commit 22a1006598882fd6efe8109b1563cdb55166faac Author: gustavooth Date: Thu Jul 23 22:56:30 2026 -0300 Initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5bb2d9a --- /dev/null +++ b/.gitignore @@ -0,0 +1,41 @@ +# Environment +.env + +# WordPress core +wp-includes/ +wp-admin/ +wp-content/languages/ +wp-content/upgrade/ +wp-content/backup-*/ + +# Default WordPress themes (not part of project) +wp-content/themes/twentytwentyfive/ +wp-content/themes/twentytwentyfour/ +wp-content/themes/twentytwentythree/ + +# Default WordPress plugins (not part of project) +wp-content/plugins/fluentform/ +wp-content/plugins/hello.php +wp-content/plugins/index.php + +# Project plugins/themes vendor deps +wp-content/plugins/*/vendor/ +wp-content/themes/*/node_modules/ +wp-content/themes/*/vendor/ + +# Logs +*.log +wp-content/debug.log + +# OS +.DS_Store +Thumbs.db + +# IDE +.idea/ +.vscode/ +*.swp +*.swo + +# Docker +docker-compose.override.yml \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..10d5736 --- /dev/null +++ b/README.md @@ -0,0 +1,121 @@ +# Gustavo Portfolio — Tema WordPress + +Tema WordPress clássico para o portfólio e blog de **Gustavo Oliveira** — [gustavoo.me](https://gustavoo.me) + +## Screenshot + +![Theme Screenshot](wp-content/themes/gustavoo-portfolio/screenshot.png) + +## Sobre + +Tema WordPress clássico desenvolvido para o portfólio pessoal e blog de Gustavo Oliveira, desenvolvedor Full Stack Sênior. O tema foi construído com foco em performance, acessibilidade e experiência do usuário, utilizando as melhores práticas do WordPress moderno. + +## Características + +- **Tema Clássico** — Template hierarchy tradicional do WordPress +- **Theme.json** — Configuração completa de estilos via theme.json (cores, tipografia, espaçamento, bordas) +- **Blocos Nativos** — Suporte completo a blocos do Gutenberg +- **Fluent Forms + FluentCRM** — Integração nativa para formulários e newsletter +- **Portfólio de Projetos** — CPT customizado para exibição de projetos +- **Newsletter Widget** — Widget personalizado para FluentCRM +- **SEO Ready** — Estrutura semântica e meta tags otimizadas +- **Performance** — Assets otimizados, fontes variáveis (Inter Variable), lazy loading nativo + +## Stack + +- **WordPress** 6.5+ +- **PHP** 8.1+ +- **Theme.json** v3 +- **Inter Variable Font** (local, self-hosted) +- **Fluent Forms** + **FluentCRM** (integração via plugin complementar) +- **Docker Compose** para desenvolvimento local + +## Estrutura do Tema + +``` +gustavoo-portfolio/ +├── assets/ +│ └── fonts/ # Inter Variable Font (woff2) +├── inc/ +│ ├── customizer.php # Personalizador do tema +│ ├── defaults.php # Configurações padrão (theme.json fallback) +│ ├── enqueue.php # Assets e estilos +│ ├── forms.php # Integração Fluent Forms +│ ├── setup.php # Configuração do tema +│ ├── template-tags.php # Template tags auxiliares +│ └── widgets.php # Widget Newsletter (FluentCRM) +├── template-parts/ # Partes de template reutilizáveis +├── 404.php +├── archive.php +├── category.php +├── comments.php +├── footer.php +├── front-page.php # Página inicial customizada +├── functions.php # Bootstrap do tema +├── header.php +├── home.php +├── index.php +├── page.php +├── search.php +├── searchform.php +├── sidebar.php +├── single.php +├── screenshot.png # Screenshot do tema (theme.png) +├── style.css # Stylesheet principal (metadados do tema) +└── theme.json # Configuração global de estilos +``` + +## Plugin Complementar + +O tema funciona em conjunto com o plugin **Gustavo Portfolio Core** (`wp-content/plugins/gustavoo-portfolio-core`), que provê: + +- **CPT Projetos** — Post type customizado para portfólio +- **Configurações Globais** — Opções de hero, sobre, projetos, blog, contato, newsletter e redes sociais +- **Seeder** — Criação automática de páginas, formulários Fluent Forms e feeds FluentCRM +- **Widget Newsletter** — Widget para exibir formulário de newsletter no sidebar + +## Desenvolvimento Local + +### Pré-requisitos + +O tema e plugin dependem dos seguintes plugins WordPress (instalados e ativados): + +- **Fluent Forms** — Formulários de contato e newsletter +- **FluentCRM** — Automação de marketing e newsletter + +```bash +# Subir ambiente Docker +docker compose up -d + +# Acessar WordPress +# http://localhost:8080 +# Usuário: admin | Senha: admin (definido no .env) + +# Instalar dependências (plugins obrigatórios) +# 1. Acesse http://localhost:8080/wp-admin +# 2. Plugins → Adicionar novo → Busque e instale: +# - Fluent Forms +# - FluentCRM +# 3. Ative ambos os plugins + +# Logs +docker compose logs -f wordpress +``` + +## Deploy + +O tema está preparado para deploy em ambientes WordPress padrão (VPS, shared hosting, WP Engine, Kinsta, etc.). + +1. Faça o build dos assets se necessário +2. Faça deploy da pasta `wp-content/themes/gustavoo-portfolio/` +3. Ative o tema no painel WordPress +4. Ative o plugin **Gustavo Portfolio Core** +5. Execute o seeder (Configurações → Portfólio → "Executar Seeder") para criar páginas e formulários padrão + +## Licença + +GNU General Public License v2 ou posterior — mesmo licença do WordPress. + +--- + +**Desenvolvido por [Gustavo Oliveira](https://gustavoo.me)** — Desenvolvedor Full Stack Sênior \ No newline at end of file diff --git a/compose.yaml b/compose.yaml new file mode 100644 index 0000000..2ceda7a --- /dev/null +++ b/compose.yaml @@ -0,0 +1,46 @@ +services: + db: + image: mariadb:11.8 + environment: + MARIADB_DATABASE: ${WORDPRESS_DB_NAME:-wordpress} + MARIADB_USER: ${WORDPRESS_DB_USER:-wordpress} + MARIADB_PASSWORD: ${WORDPRESS_DB_PASSWORD:?Defina WORDPRESS_DB_PASSWORD no arquivo .env} + MARIADB_ROOT_PASSWORD: ${MARIADB_ROOT_PASSWORD:?Defina MARIADB_ROOT_PASSWORD no arquivo .env} + volumes: + - db_data:/var/lib/mysql + healthcheck: + test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"] + interval: 10s + timeout: 5s + retries: 10 + start_period: 30s + + wordpress: + container_name: gustavoo-me + image: wordpress:7-php8.3-apache + depends_on: + db: + condition: service_healthy + ports: + - "${WORDPRESS_PORT:-8080}:80" + environment: + WORDPRESS_DB_HOST: db:3306 + WORDPRESS_DB_NAME: ${WORDPRESS_DB_NAME:-wordpress} + WORDPRESS_DB_USER: ${WORDPRESS_DB_USER:-wordpress} + WORDPRESS_DB_PASSWORD: ${WORDPRESS_DB_PASSWORD} + volumes: + - wordpress_data:/var/www/html + - type: bind + source: ./wp-content/themes + target: /var/www/html/wp-content/themes + bind: + create_host_path: false + - type: bind + source: ./wp-content/plugins + target: /var/www/html/wp-content/plugins + bind: + create_host_path: false + +volumes: + db_data: + wordpress_data: diff --git a/wp-content/plugins/.gitkeep b/wp-content/plugins/.gitkeep new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/wp-content/plugins/.gitkeep @@ -0,0 +1 @@ + diff --git a/wp-content/plugins/akismet/.htaccess b/wp-content/plugins/akismet/.htaccess new file mode 100644 index 0000000..d05c413 --- /dev/null +++ b/wp-content/plugins/akismet/.htaccess @@ -0,0 +1,34 @@ +# Only allow direct access to specific Web-available files. + +# Apache 2.2 + + Order Deny,Allow + Deny from all + + +# Apache 2.4 + + Require all denied + + +# Akismet CSS and JS + + + Allow from all + + + + Require all granted + + + +# Akismet images + + + Allow from all + + + + Require all granted + + diff --git a/wp-content/plugins/akismet/LICENSE.txt b/wp-content/plugins/akismet/LICENSE.txt new file mode 100644 index 0000000..d159169 --- /dev/null +++ b/wp-content/plugins/akismet/LICENSE.txt @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/wp-content/plugins/akismet/_inc/akismet-admin.css b/wp-content/plugins/akismet/_inc/akismet-admin.css new file mode 100644 index 0000000..dd99c52 --- /dev/null +++ b/wp-content/plugins/akismet/_inc/akismet-admin.css @@ -0,0 +1,797 @@ +body { + --akismet-color-charcoal: #272635; + --akismet-color-light-grey: #f6f7f7; + --akismet-color-mid-grey: #a7aaad; + --akismet-color-dark-grey: #646970; + --akismet-color-grey-80: #2c3338; + --akismet-color-grey-100: #101517; + --akismet-color-grey-border: #dcdcde; + --akismet-color-white: #fff; + --akismet-color-dark-green: #2d6a40; + --akismet-color-mid-green: #357b49; + --akismet-color-light-green: #4eb26a; + --akismet-color-mid-red: #e82c3f; + --akismet-color-light-blue: #256eff; + --akismet-color-notice-light-green: #dbf0e1; + --akismet-color-notice-dark-green: #69bf82; + --akismet-color-notice-light-red: #ffdbde; + --akismet-color-notice-dark-red: #ff6676; + --akismet-color-notice-yellow: #e5c133; + --akismet-color-page-bg: #fcfcfc; + --akismet-color-border: #e0e0e0; + --akismet-color-border-light: #f0f0f0; + --akismet-color-near-black: #1e1e1e; + --akismet-color-text-grey: #757575; + --akismet-color-icon: #666666; + --akismet-color-icon-hover: #333333; +} + +/* UI components */ +#akismet-plugin-container { + background-color: var(--akismet-color-page-bg); + border: 1px solid var(--akismet-color-border); + font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen-Sans', 'Ubuntu', 'Cantarell', 'Helvetica Neue', sans-serif; + -webkit-font-smoothing: antialiased; +} + +#akismet-plugin-container a { + color: var(--akismet-color-mid-green); +} + +#akismet-plugin-container a.akismet-button { + background-color: var(--akismet-color-mid-green); + color: var(--akismet-color-white); +} + +#akismet-plugin-container button:focus-visible, +#akismet-plugin-container input:focus-visible { + border: 0; + box-shadow: none; + outline: 2px solid var(--akismet-color-light-blue); +} + +#akismet-plugin-container a:focus-visible { + box-shadow: none; + outline: 2px solid var(--akismet-color-light-blue); +} + +.akismet-masthead { + border-bottom: 1px solid var(--akismet-color-border-light); + box-shadow: none; +} + +.akismet-masthead__logo { + margin: 20px 0; +} + +.akismet-section-header { + box-shadow: none; + margin-bottom: 0; +} + +.akismet-section-header__label { + color: var(--akismet-color-charcoal); + font-weight: 600; + padding-left: 0.2em; +} + +.akismet-button, +.akismet-button:hover { + border: 0; + color: var(--akismet-color-white); +} + +.akismet-button { + background-color: var(--akismet-color-mid-green); +} + +.akismet-button:hover { + background-color: var(--akismet-color-dark-green); +} + +.akismet-external-link { + display: inline-block; +} + +.akismet-external-link::after { + content: "↗"; + display: inline-block; + padding-left: 2px; + text-decoration: none; + vertical-align: middle; +} + +/* Need this specificity to override the existing header rule */ +.akismet-new-snapshot h3.akismet-new-snapshot__header { + background: none; + font-size: 13px; + color: var(--akismet-color-charcoal); + text-align: left; + text-transform: none; +} + +.akismet-new-snapshot__number { + color: var(--akismet-color-charcoal); + display: block; + font-size: 32px; + font-weight: 400; + letter-spacing: -1px; + line-height: 1.5em; + text-align: left; +} + +.akismet-new-snapshot li.akismet-new-snapshot__item { + color: var(--akismet-color-dark-grey); + font-size: 13px; + text-align: left; + text-transform: none; +} + +.akismet-masthead__logo-link { + min-height: 50px; +} + +.akismet-masthead__back-link-container { + margin-top: 16px; + margin-bottom: 2px; +} + +/* Need this specificity to override the existing link rule */ +#akismet-plugin-container a.akismet-masthead__back-link { + background-image: url(img/arrow-left.svg); + background-position: left; + background-repeat: no-repeat; + background-size: 16px; + color: var(--akismet-color-charcoal); + font-weight: 400; + padding-left: 20px; + text-decoration: none; +} + +#akismet-plugin-container a.akismet-masthead__back-link:hover { + text-decoration: underline; +} + +.akismet-new-snapshot__item { + border-top: 1px solid var(--akismet-color-border-light); + border-left: 1px solid var(--akismet-color-border-light); + padding: 1em; +} + +.akismet-new-snapshot li:first-child { + border-left: none; +} + +.akismet-new-snapshot__list { + display: flex; + margin-bottom: 0; +} + +.akismet-new-snapshot__item { + flex: 1 0 33.33%; + margin-bottom: 0; + padding-left: 1.5em; + padding-right: 1.5em; +} + +.akismet-new-snapshot__chart { + padding: 1em; +} + +.akismet-stats-footer { + align-items: center; + border-radius: 0 0 7px 7px; + border-top: 1px solid var(--akismet-color-border-light); + display: flex; + justify-content: space-between; + outline-offset: -2px; + padding: 1em 1.5em; + text-decoration: none; +} + +.akismet-stats-footer:hover { + text-decoration: underline; +} + +.akismet-stats-footer:focus { + border-radius: 0 0 7px 7px; + box-shadow: none; +} + +.akismet-stats-footer:focus-visible { + box-shadow: none; + outline: 2px solid var(--akismet-color-light-blue); +} + +.akismet-box { + border: 0; +} + +.akismet-box:not(:first-child) { + margin-top: 1rem; +} + +.akismet-box, +.akismet-card { + border: 1px solid var(--akismet-color-border); + border-radius: 8px; + overflow: hidden; +} + +.akismet-card { + margin: 16px auto 0 auto; +} + +.akismet-lower { + padding-top: 0; +} + +.akismet-lower .inside { + padding: 0; +} + +.akismet-section-header__label { + margin: 0; +} + +.akismet-settings__row { + border-bottom: 1px solid var(--akismet-color-border-light); + display: block; + padding: 1em 1.5em; +} + +.akismet-settings__row-input { + margin-left: auto; +} + +.akismet-settings__row-title { + font-weight: 500; + font-size: 1em; + margin: 0; + margin-bottom: 1em; +} + +.akismet-settings__row-description { + margin-top: 0.5em; +} + +.akismet-card-actions { + display: flex; + justify-content: flex-end; + padding: 1em; +} + +.akismet-card-actions__secondary-action { + align-self: center; + margin-inline-end: auto; + margin-inline-start: 6px; +} + +.akismet-settings__row label { + padding-bottom: 1em; +} + +.akismet-settings__row-note { + font-size: 0.9em; + margin-top: 0.4em; +} + +.akismet-settings__row-note abbr { + cursor: help; +} + +.akismet-settings__row input[type="checkbox"], +.akismet-settings__row input[type="radio"] { + accent-color: var(--akismet-color-mid-green); + box-shadow: none; + flex-shrink: 0; + margin: 2px 0 0 0; +} + +.akismet-settings__row input[type="checkbox"] { + margin-top: 1px; + vertical-align: top; + -webkit-appearance: checkbox; +} + +.akismet-settings__row input[type="radio"] { + -webkit-appearance: radio; +} + +/* Fix up misbehaving wp-admin styles in Chrome (from forms and colors stylesheets) */ +.akismet-settings__row input[type="checkbox"]:checked:before { + content: ''; +} + +.akismet-settings__row input[type="radio"]:checked:before { + background: none; +} + +.akismet-settings__row input[type="checkbox"]:checked:hover, +.akismet-settings__row input[type="radio"]:checked:hover { + accent-color: var(--akismet-color-mid-green); +} + +.akismet-button:disabled { + background-color: var(--akismet-color-mid-grey); + color: var(--akismet-color-white); + cursor: arrow; +} + +.akismet-awaiting-stats, +.akismet-account { + padding: 0 1rem 1rem 1rem; + margin: 0; +} + +.akismet-account { + display: grid; + grid-template-columns: auto 1fr; + margin-inline-start: 2px; + margin-top: 0.25em; + padding-bottom: 0; + row-gap: 0.25em; +} + +.akismet-account__label { + font-weight: 500; + padding-bottom: 1em; + padding-inline-end: 1em; +} + +.akismet-account__value { + margin: 0; + padding-bottom: 1em; +} + +.akismet-settings__row-input-label { + align-items: center; + display: flex; +} + +.akismet-settings__row-label-text { + padding-left: 0.5em; + margin-top: 2px; +} + +.akismet-alert { + border-left: 8px solid; + border-radius: 8px; + margin: 20px 0; + padding: 0.2em 1em; +} + +.akismet-alert__heading { + font-size: 1em; +} + +.akismet-alert.is-good { + background-color: var(--akismet-color-notice-light-green); + border-left-color: var(--akismet-color-notice-dark-green); +} + +.akismet-alert.is-neutral { + background-color: var(--akismet-color-white); + border-left-color: var(--akismet-color-dark-grey); +} + +.akismet-alert.is-bad { + background-color: var(--akismet-color-notice-light-red); + border-left-color: var(--akismet-color-notice-dark-red); +} + +.akismet-alert.is-commercial { + background-color: var(--akismet-color-white); + border-color: var(--akismet-color-mid-grey); + border-bottom-width: 1px; + border-left-color: var(--akismet-color-notice-yellow); + display: flex; + padding-bottom: 1em; +} + +#akismet-plugin-container .akismet-alert.is-good a, +#akismet-plugin-container .akismet-alert.is-bad a { + /* For better contrast - green isn't great */ + color: var(--akismet-color-grey-80); +} + +.akismet-alert-header { + font-size: 16px; + margin-bottom: 0.5em; +} + +.akismet-alert-button-wrapper { + align-self: center; + margin-left: 2em; + min-width: 120px; +} + +.akismet-alert-info { + text-wrap: pretty; + margin: 0.5em 0; +} + +/* Setup */ +.akismet-setup-instructions__heading { + font-size: 1.375rem; + font-weight: 700; + padding-block-end: 0; +} + +h3.akismet-setup-instructions__subheading { + color: var(--akismet-color-dark-grey); + font-size: 1rem; + font-weight: 400; + line-height: 1.5; + margin: 0 0 1.25rem; + padding-block-start: 1rem; +} + +.akismet-setup-instructions__feature-list { + list-style: none; + margin: 1rem 0.5rem 1.5rem; + max-width: 640px; + padding: 0 1rem; +} + +.akismet-setup-instructions__feature { + align-items: start; + display: flex; + margin-block-end: 1rem; + text-align: left; +} + +.akismet-setup-instructions__icon { + height: 20px; + width: 20px; +} + +.akismet-setup-instructions__body { + flex: 1; + padding-inline-start: 0.5rem; +} + +.akismet-setup-instructions__title { + color: #1d2327; + font-size: 1rem; + font-weight: 600; + line-height: 1.3; + margin: 0; + text-align: left; +} + +p.akismet-setup-instructions__text { + color: var(--akismet-color-grey-80); + font-size: 0.875rem; + line-height: 1.5; + margin: 0.25rem 0 0; + padding: 0; + text-align: left; +} + +.akismet-setup-instructions__button, +.akismet-setup-instructions__button:hover, +.akismet-setup-instructions__button:visited { + font-size: 1rem; + margin-inline-start: 1.5rem; +} + +.akismet-setup__connection { + background: var(--akismet-color-light-grey); + border: 1px solid var(--akismet-color-grey-border); + border-radius: 8px; + margin: 1rem 1rem 2rem 1rem; + padding: 1rem; +} + +.akismet-setup__connection-action:not(:last-child) { + margin-bottom: 1rem; +} + +.akismet-setup__connection-user { + display: flex; +} + +.akismet-setup__connection-avatar { + align-items: center; + display: flex; + gap: 12px; + margin-bottom: 12px; +} + +.akismet-setup__connection-avatar-image { + border-radius: 50%; +} + +.akismet-setup__connection-account-name { + color: var(--akismet-color-charcoal); + font-size: 0.9rem; + overflow-wrap: anywhere; +} + +.akismet-setup__connection-account-email { + margin-top: 0.1rem; + overflow-wrap: anywhere; +} + +.akismet-setup__connection-action { + margin-left: auto; +} + +.akismet-setup__connection-button { + text-align: center; + width: 100%; +} + +p.akismet-setup__connection-action-intro, +p.akismet-setup__connection-action-description { + color: var(--akismet-color-dark-grey); + font-size: 0.875rem; + padding: 0; +} + +p.akismet-setup__connection-action-intro { + margin: 0 0 1rem 0; +} + +p.akismet-setup__connection-action-description { + margin: 1rem 0 0; +} + +/* API key field with copy button */ +.akismet-api-key-wrapper { + position: relative; + display: inline-flex; + align-items: center; +} + +.akismet-api-key-wrapper input { + padding-right: 36px; +} + +.akismet-api-key-copy { + position: absolute; + right: 4px; + background: none; + border: none; + cursor: pointer; + padding: 4px; + color: var(--akismet-color-icon); + display: flex; + align-items: center; +} + +.akismet-api-key-copy:hover { + color: var(--akismet-color-icon-hover); +} + +/* Setup - API key input */ +.akismet-enter-api-key-box { + margin: 1.5rem 0; +} + +.akismet-enter-api-key-box__reveal { + background: none; + border: 0; + color: var(--akismet-color-mid-green); + cursor: pointer; + text-decoration: underline; +} + +.akismet-enter-api-key-box__form-wrapper { + display: none; + margin-top: 1.5rem; +} + +.akismet-enter-api-key-box__input-wrapper { + box-sizing: border-box; + display: flex; + flex-wrap: nowrap; + padding: 0 1.5rem; + width: 100%; +} + +.akismet-enter-api-key-box__key-input { + flex-grow: 1; + margin-right: 1rem; +} + +h3.akismet-enter-api-key-box__header { + padding-top: 0; + padding-bottom: 1em; + text-align: left; +} + +/* Notices > Activation (shown on edit-comments.php) */ +#akismet-setup-prompt { + background: none; + border: none; + margin: 0; + padding: 0; + width: 100%; +} + +.akismet-activate { + align-items: center; + /* background-image is defined via an inline style in class.akismet-admin.php */ + background-color: var(--akismet-color-light-grey); + background-position: calc(100% - 1em) center; + background-repeat: no-repeat; + background-size: 140px; + border: 1px solid var(--akismet-color-mid-green); + border-left-width: 4px; + display: flex; + justify-content: space-between; + margin: 15px 0; + min-height: 60px; + overflow: hidden; + padding: 5px 160px 5px 5px; + position: relative; +} + +.akismet-activate__button, +.akismet-activate__button:hover, +.akismet-activate__button:visited { + margin: 0 1em; +} + +.akismet-activate__description { + color: var(--akismet-color-charcoal); + flex-grow: 1; + font-size: 16px; + font-weight: 600; + margin: 0 auto; + text-align: center; + text-wrap: pretty; +} + +/* Compatible plugins section */ +.akismet-compatible-plugins__content { + padding: 0 1.5em 1.5em 1.5em; +} + +.akismet-compatible-plugins__intro { + margin: 0; +} + +.akismet-compatible-plugins__section-header-label { + display: block; +} + +.akismet-compatible-plugins__section-header-label-text { + padding-right: 0.5em; +} + +.akismet-compatible-plugins__list { + display: grid; + grid-template-columns: 1fr; + gap: 10px; + margin: 1.5em 0 1em 0; + padding: 0; +} + +.akismet-compatible-plugins__card { + border: 1px solid var(--akismet-color-border-light); + border-radius: 4px; + padding: 1em; + display: flex; + align-items: center; +} + +.akismet-compatible-plugins__card-logo { + padding: 0 1.5em 0 0; + object-fit: contain; + width: 36px; + height: 36px; +} + +.akismet-compatible-plugins__card-detail { + display: flex; + flex: 1; + justify-content: space-between; + align-items: center; +} + +.akismet-compatible-plugins__card-title { + font-size: 1.2em; + margin-top: 0; + margin-bottom: 0; +} + +.akismet-compatible-plugins__docs { + margin-top: 0; +} + +.akismet-compatible-plugins__show-more { + all: unset; + cursor: pointer; + display: flex; + justify-content: space-between; + position: relative; + width: 100%; +} + +/* Generates the show/hide chevron */ +.akismet-compatible-plugins__show-more::after { + align-self: center; + border-bottom: 2px solid black; + border-right: 2px solid black; + content: ""; + height: 8px; + transform: rotate(45deg); + transition: transform 0.2s ease; + width: 8px; +} + +.akismet-compatible-plugins__list.is-expanded + .akismet-compatible-plugins__show-more::after { + align-self: end; + transform: rotate(225deg); +} + +/* Gutenberg medium breakpoint */ +@media screen and (max-width: 782px) { + .akismet-new-snapshot__list { + display: block; + } + + .akismet-new-snapshot__number { + float: right; + font-size: 20px; + font-weight: 500; + margin-top: -16px; + } + + .akismet-new-snapshot__header { + font-size: 14px; + font-weight: 500; + } + + .akismet-new-snapshot__text { + font-size: 12px; + } + + .akismet-settings__row input[type="checkbox"], + .akismet-settings__row input[type="radio"] { + height: 24px; + width: 24px; + } + + .akismet-settings__row-label-text { + padding-left: 0.8em; + } + + .akismet-settings__row input[type="checkbox"], + .akismet-settings__row input[type="radio"] { + margin-top: 0; + } + + .akismet-activate { + background-size: 120px; + padding-right: 134px; + } + + .akismet-activate__button { + white-space: normal; + } + + .akismet-activate__description { + font-size: 14px; + margin-right: 1em; + } +} + +/* Gutenberg small breakpoint */ +@media screen and (max-width: 600px) { + .akismet-compatible-plugins__list { + gap: 8px; + } + + .akismet-activate__button, + .akismet-activate__button:hover { + font-size: 13px; + } + + .akismet-activate__description { + display: none; + } +} \ No newline at end of file diff --git a/wp-content/plugins/akismet/_inc/akismet-admin.js b/wp-content/plugins/akismet/_inc/akismet-admin.js new file mode 100644 index 0000000..513bf3e --- /dev/null +++ b/wp-content/plugins/akismet/_inc/akismet-admin.js @@ -0,0 +1,69 @@ +document.addEventListener( 'DOMContentLoaded', function() { + // Prevent aggressive iframe caching in Firefox + var statsIframe = document.getElementById( 'stats-iframe' ); + if ( statsIframe ) { + statsIframe.contentWindow.location.href = statsIframe.src; + } + + initCompatiblePluginsShowMoreToggle(); + initApiKeyCopyButton(); +} ); + +function initApiKeyCopyButton() { + const button = document.querySelector( '.akismet-api-key-copy' ); + if ( ! button ) { + return; + } + + button.addEventListener( 'click', function() { + const input = document.getElementById( 'key' ); + if ( ! input || ! input.value ) { + return; + } + + if ( navigator.clipboard && navigator.clipboard.writeText ) { + navigator.clipboard.writeText( input.value ).then( function() { + const svg = button.querySelector( 'svg' ); + const original = svg.innerHTML; + svg.innerHTML = ''; + setTimeout( function() { + svg.innerHTML = original; + }, 2000 ); + } ).catch( function() { + input.select(); + document.execCommand( 'copy' ); + } ); + } else { + input.select(); + document.execCommand( 'copy' ); + } + } ); +} + +function initCompatiblePluginsShowMoreToggle() { + const section = document.querySelector( '.akismet-compatible-plugins' ); + const list = document.querySelector( '.akismet-compatible-plugins__list' ); + const button = document.querySelector( '.akismet-compatible-plugins__show-more' ); + + if ( ! section || ! list || ! button ) { + return; + } + + function isElementInViewport( element ) { + const rect = element.getBoundingClientRect(); + return rect.top >= 0 && rect.bottom <= window.innerHeight; + } + + function toggleCards() { + list.classList.toggle( 'is-expanded' ); + const isExpanded = list.classList.contains( 'is-expanded' ); + button.textContent = isExpanded ? button.dataset.labelOpen : button.dataset.labelClosed; + button.setAttribute( 'aria-expanded', isExpanded.toString() ); + + if ( ! isExpanded && ! isElementInViewport( section ) ) { + section.scrollIntoView( { block: 'start' } ); + } + } + + button.addEventListener( 'click', toggleCards ); +} diff --git a/wp-content/plugins/akismet/_inc/akismet-frontend.js b/wp-content/plugins/akismet/_inc/akismet-frontend.js new file mode 100644 index 0000000..b866ab1 --- /dev/null +++ b/wp-content/plugins/akismet/_inc/akismet-frontend.js @@ -0,0 +1,393 @@ +/** + * Observe how the user enters content into the comment form in order to determine whether it's a bot or not. + * + * Note that no actual input is being saved here, only counts and timings between events. + */ + +( function() { + // Passive event listeners are guaranteed to never call e.preventDefault(), + // but they're not supported in all browsers. Use this feature detection + // to determine whether they're available for use. + var supportsPassive = false; + + try { + var opts = Object.defineProperty( {}, 'passive', { + get : function() { + supportsPassive = true; + } + } ); + + window.addEventListener( 'testPassive', null, opts ); + window.removeEventListener( 'testPassive', null, opts ); + } catch ( e ) {} + + function init() { + var input_begin = ''; + + var keydowns = {}; + var lastKeyup = null; + var lastKeydown = null; + var keypresses = []; + + var modifierKeys = []; + var correctionKeys = []; + + var lastMouseup = null; + var lastMousedown = null; + var mouseclicks = []; + var mouseclickCoordinates = []; + + var mousemoveTimer = null; + var lastMousemoveX = null; + var lastMousemoveY = null; + var mousemoveStart = null; + var mousemoves = []; + + var touchmoveCountTimer = null; + var touchmoveCount = 0; + + var lastTouchEnd = null; + var lastTouchStart = null; + var touchEvents = []; + + var scrollCountTimer = null; + var scrollCount = 0; + + var correctionKeyCodes = [ 'Backspace', 'Delete', 'ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight', 'Home', 'End', 'PageUp', 'PageDown' ]; + var modifierKeyCodes = [ 'Shift', 'CapsLock' ]; + + var forms = document.querySelectorAll( 'form[method=post]' ); + + for ( var i = 0; i < forms.length; i++ ) { + var form = forms[i]; + + var formAction = form.getAttribute( 'action' ); + + // Ignore forms that POST directly to other domains; these could be things like payment forms. + if ( formAction ) { + // Check that the form is posting to an external URL, not a path. + if ( formAction.indexOf( 'http://' ) == 0 || formAction.indexOf( 'https://' ) == 0 ) { + if ( formAction.indexOf( 'http://' + window.location.hostname + '/' ) != 0 && formAction.indexOf( 'https://' + window.location.hostname + '/' ) != 0 ) { + continue; + } + } + } + + form.addEventListener( 'submit', function () { + var ak_bkp = prepare_array_for_request( keypresses ); + var ak_bmc = prepare_array_for_request( mouseclicks ); + var ak_bte = prepare_array_for_request( touchEvents ); + var ak_bmm = prepare_array_for_request( mousemoves ); + var ak_bcc = prepare_array_for_request( mouseclickCoordinates ); + + var input_fields = { + // When did the user begin entering any input? + 'bib': input_begin, + + // When was the form submitted? + 'bfs': Date.now(), + + // How many keypresses did they make? + 'bkpc': keypresses.length, + + // How quickly did they press a sample of keys, and how long between them? + 'bkp': ak_bkp, + + // How quickly did they click the mouse, and how long between clicks? + 'bmc': ak_bmc, + + // How many mouseclicks did they make? + 'bmcc': mouseclicks.length, + + // When did they press modifier keys (like Shift or Capslock)? + 'bmk': modifierKeys.join( ';' ), + + // When did they correct themselves? e.g., press Backspace, or use the arrow keys to move the cursor back + 'bck': correctionKeys.join( ';' ), + + // How many times did they move the mouse? + 'bmmc': mousemoves.length, + + // How many times did they move around using a touchscreen? + 'btmc': touchmoveCount, + + // How many times did they scroll? + 'bsc': scrollCount, + + // How quickly did they perform touch events, and how long between them? + 'bte': ak_bte, + + // How many touch events were there? + 'btec' : touchEvents.length, + + // How quickly did they move the mouse, and how long between moves? + 'bmm' : ak_bmm, + + // Click coordinates + 'bcc' : ak_bcc + }; + + var akismet_field_prefix = 'ak_'; + + if ( this.getElementsByClassName ) { + // Check to see if we've used an alternate field name prefix. We store this as an attribute of the container around some of the Akismet fields. + var possible_akismet_containers = this.getElementsByClassName( 'akismet-fields-container' ); + + for ( var containerIndex = 0; containerIndex < possible_akismet_containers.length; containerIndex++ ) { + var container = possible_akismet_containers.item( containerIndex ); + + if ( container.getAttribute( 'data-prefix' ) ) { + akismet_field_prefix = container.getAttribute( 'data-prefix' ); + break; + } + } + } + + for ( var field_name in input_fields ) { + var field = document.createElement( 'input' ); + field.setAttribute( 'type', 'hidden' ); + field.setAttribute( 'name', akismet_field_prefix + field_name ); + field.setAttribute( 'value', input_fields[ field_name ] ); + this.appendChild( field ); + } + }, supportsPassive ? { passive: true } : false ); + + form.addEventListener( 'keydown', function ( e ) { + // If you hold a key down, some browsers send multiple keydown events in a row. + // Ignore any keydown events for a key that hasn't come back up yet. + if ( e.key in keydowns ) { + return; + } + + var keydownTime = ( new Date() ).getTime(); + keydowns[ e.key ] = [ keydownTime ]; + + if ( ! input_begin ) { + input_begin = keydownTime; + } + + // In some situations, we don't want to record an interval since the last keypress -- for example, + // on the first keypress, or on a keypress after focus has changed to another element. Normally, + // we want to record the time between the last keyup and this keydown. But if they press a + // key while already pressing a key, we want to record the time between the two keydowns. + + var lastKeyEvent = Math.max( lastKeydown, lastKeyup ); + + if ( lastKeyEvent ) { + keydowns[ e.key ].push( keydownTime - lastKeyEvent ); + } + + lastKeydown = keydownTime; + }, supportsPassive ? { passive: true } : false ); + + form.addEventListener( 'keyup', function ( e ) { + if ( ! ( e.key in keydowns ) ) { + // This key was pressed before this script was loaded, or a mouseclick happened during the keypress, or... + return; + } + + var keyupTime = ( new Date() ).getTime(); + + if ( 'TEXTAREA' === e.target.nodeName || 'INPUT' === e.target.nodeName ) { + if ( -1 !== modifierKeyCodes.indexOf( e.key ) ) { + modifierKeys.push( keypresses.length - 1 ); + } else if ( -1 !== correctionKeyCodes.indexOf( e.key ) ) { + correctionKeys.push( keypresses.length - 1 ); + } else { + // ^ Don't record timings for keys like Shift or backspace, since they + // typically get held down for longer than regular typing. + + var keydownTime = keydowns[ e.key ][0]; + + var keypress = []; + + // Keypress duration. + keypress.push( keyupTime - keydownTime ); + + // Amount of time between this keypress and the previous keypress. + if ( keydowns[ e.key ].length > 1 ) { + keypress.push( keydowns[ e.key ][1] ); + } + + keypresses.push( keypress ); + } + } + + delete keydowns[ e.key ]; + + lastKeyup = keyupTime; + }, supportsPassive ? { passive: true } : false ); + + form.addEventListener( "focusin", function ( e ) { + lastKeydown = null; + lastKeyup = null; + keydowns = {}; + }, supportsPassive ? { passive: true } : false ); + + form.addEventListener( "focusout", function ( e ) { + lastKeydown = null; + lastKeyup = null; + keydowns = {}; + }, supportsPassive ? { passive: true } : false ); + } + + document.addEventListener( 'mousedown', function ( e ) { + lastMousedown = ( new Date() ).getTime(); + + var mouseclickCoordinate = []; + + var rect = e.target.getBoundingClientRect(); + var relativeX = e.clientX - rect.left; + var relativeY = e.clientY - rect.top; + + // Pixel offset of the click within the target element. + mouseclickCoordinate.push( Math.round( relativeX ) ); + mouseclickCoordinate.push( Math.round( relativeY ) ); + + // Percentage offset of the click within the target element. + mouseclickCoordinate.push( rect.width > 0 ? Math.round( relativeX / rect.width * 100 ) : 0 ); + mouseclickCoordinate.push( rect.height > 0 ? Math.round( relativeY / rect.height * 100 ) : 0 ); + + mouseclickCoordinates.push( mouseclickCoordinate ); + }, supportsPassive ? { passive: true } : false ); + + document.addEventListener( 'mouseup', function ( e ) { + if ( ! lastMousedown ) { + // If the mousedown happened before this script was loaded, but the mouseup happened after... + return; + } + + var now = ( new Date() ).getTime(); + + var mouseclick = []; + mouseclick.push( now - lastMousedown ); + + if ( lastMouseup ) { + mouseclick.push( lastMousedown - lastMouseup ); + } + + mouseclicks.push( mouseclick ); + + lastMouseup = now; + + // If the mouse has been clicked, don't record this time as an interval between keypresses. + lastKeydown = null; + lastKeyup = null; + keydowns = {}; + }, supportsPassive ? { passive: true } : false ); + + document.addEventListener( 'mousemove', function ( e ) { + if ( mousemoveTimer ) { + clearTimeout( mousemoveTimer ); + mousemoveTimer = null; + } + else { + mousemoveStart = ( new Date() ).getTime(); + lastMousemoveX = e.offsetX; + lastMousemoveY = e.offsetY; + } + + mousemoveTimer = setTimeout( function ( theEvent, originalMousemoveStart ) { + var now = ( new Date() ).getTime() - 500; // To account for the timer delay. + + var mousemove = []; + mousemove.push( now - originalMousemoveStart ); + mousemove.push( + Math.round( + Math.sqrt( + Math.pow( theEvent.offsetX - lastMousemoveX, 2 ) + + Math.pow( theEvent.offsetY - lastMousemoveY, 2 ) + ) + ) + ); + + if ( mousemove[1] > 0 ) { + // If there was no measurable distance, then it wasn't really a move. + mousemoves.push( mousemove ); + } + + mousemoveStart = null; + mousemoveTimer = null; + }, 500, e, mousemoveStart ); + }, supportsPassive ? { passive: true } : false ); + + document.addEventListener( 'touchmove', function ( e ) { + if ( touchmoveCountTimer ) { + clearTimeout( touchmoveCountTimer ); + } + + touchmoveCountTimer = setTimeout( function () { + touchmoveCount++; + }, 500 ); + }, supportsPassive ? { passive: true } : false ); + + document.addEventListener( 'touchstart', function ( e ) { + lastTouchStart = ( new Date() ).getTime(); + }, supportsPassive ? { passive: true } : false ); + + document.addEventListener( 'touchend', function ( e ) { + if ( ! lastTouchStart ) { + // If the touchstart happened before this script was loaded, but the touchend happened after... + return; + } + + var now = ( new Date() ).getTime(); + + var touchEvent = []; + touchEvent.push( now - lastTouchStart ); + + if ( lastTouchEnd ) { + touchEvent.push( lastTouchStart - lastTouchEnd ); + } + + touchEvents.push( touchEvent ); + + lastTouchEnd = now; + + // Don't record this time as an interval between keypresses. + lastKeydown = null; + lastKeyup = null; + keydowns = {}; + }, supportsPassive ? { passive: true } : false ); + + document.addEventListener( 'scroll', function ( e ) { + if ( scrollCountTimer ) { + clearTimeout( scrollCountTimer ); + } + + scrollCountTimer = setTimeout( function () { + scrollCount++; + }, 500 ); + }, supportsPassive ? { passive: true } : false ); + } + + /** + * For the timing/coordinate data that is collected, don't send more than `limit` data points in the request. + * Choose a random slice and send those, with each batch separated by semicolons and the items in each batch + * separated by commas. + */ + function prepare_array_for_request( a, limit ) { + if ( ! limit ) { + limit = 100; + } + + var rv = ''; + + if ( a.length > 0 ) { + var random_starting_point = Math.max( 0, Math.floor( Math.random() * a.length - limit ) ); + + for ( var i = 0; i < limit && i < a.length; i++ ) { + var entry = a[ random_starting_point + i ]; + rv += entry.join( ',' ) + ';'; + } + } + + return rv; + } + + if ( document.readyState !== 'loading' ) { + init(); + } else { + document.addEventListener( 'DOMContentLoaded', init ); + } +})(); \ No newline at end of file diff --git a/wp-content/plugins/akismet/_inc/akismet.css b/wp-content/plugins/akismet/_inc/akismet.css new file mode 100644 index 0000000..be279a2 --- /dev/null +++ b/wp-content/plugins/akismet/_inc/akismet.css @@ -0,0 +1,462 @@ +.wp-admin.jetpack_page_akismet-key-config, .wp-admin.settings_page_akismet-key-config { + background-color:#f3f6f8; +} + +#submitted-on { + position: relative; +} +#the-comment-list .author .akismet-user-comment-count { + display: inline; +} +#the-comment-list .author a span { + text-decoration: none; + color: #999; +} +#the-comment-list .author a span.akismet-span-link { + text-decoration: inherit; + color: inherit; +} +#the-comment-list .akismet_remove_url { + margin-left: 3px; + color: #999; + padding: 2px 3px 2px 0; +} +#the-comment-list .akismet_remove_url:hover { + color: #A7301F; + font-weight: bold; + padding: 2px 2px 2px 0; +} +#dashboard_recent_comments .akismet-status { + display: none; +} +.akismet-status { + float: right; +} +.akismet-status a { + color: #AAA; + font-style: italic; +} +table.comments td.comment p a { + text-decoration: underline; +} +table.comments td.comment p a:after { + content: attr(href); + color: #aaa; + display: inline-block; /* Show the URL without the link's underline extending under it. */ + padding: 0 1ex; /* Because it's inline block, we can't just use spaces in the content: attribute to separate it from the link text. */ +} +.mshot-arrow { + width: 0; + height: 0; + border-top: 10px solid transparent; + border-bottom: 10px solid transparent; + border-right: 10px solid #5C5C5C; + position: absolute; + left: -6px; + top: 91px; +} +.mshot-container { + background: #5C5C5C; + position: absolute; + top: -94px; + padding: 7px; + width: 450px; + height: 338px; + z-index: 20000; + border-radius: 6px; +} +.akismet-mshot { + position: absolute; + z-index: 100; +} +.akismet-mshot .mshot-image { + margin: 0; + height: 338px; + width: 450px; +} +.checkforspam { + display: inline-block !important; +} + +.checkforspam-spinner { + display: inline-block; + margin-top: 7px; +} + +.akismet-right { + float: right; +} + +.akismet-card .akismet-right { + margin: 1em 0; +} + +.akismet-new-snapshot { + margin-top: 1em; + text-align: center; + background: #fff; +} + +.akismet-new-snapshot h3 { + background: #f5f5f5; + color: #888; + font-size: 11px; + margin: 0; +} + +.akismet-new-snapshot ul li { + color: #999; + font-size: 11px; + text-transform: uppercase; + box-sizing: border-box; +} + + +.akismet-settings th:first-child { + vertical-align: top; + padding-top: 15px; +} + +.akismet-settings th.akismet-api-key { + vertical-align: middle; + padding-top: 0; +} + +.akismet-settings span.akismet-note { + float: left; + padding-left: 23px; + font-size: 75%; + margin-top: -10px; +} + +.jetpack_page_akismet-key-config #wpcontent, .settings_page_akismet-key-config #wpcontent { + padding-left: 0; +} + +.akismet-masthead { + background-color:#fff; + text-align:center; + box-shadow:0 1px 0 rgba(200,215,225,0.5),0 1px 2px #e9eff3 +} + +@media (max-width: 45rem) { + .akismet-masthead { + padding:0 1.25rem + } +} + +.akismet-masthead__inside-container { + padding:.375rem 0; + margin:0 auto; + width:100%; + max-width:45rem; + text-align: left; +} +.akismet-masthead__logo-container { + padding:.3125rem 0 0 +} +.akismet-masthead__logo-link { + display:inline-block; + outline:none; + vertical-align:middle +} +.akismet-masthead__logo-link:focus { + line-height:0; + box-shadow:0 0 0 2px #78dcfa +} +.akismet-masthead__logo-link+code { + margin:0 10px; + padding:5px 9px; + border-radius:2px; + background:#e6ecf1; + color:#647a88 +} +.akismet-masthead__links { + display:flex; + flex-flow:row wrap; + flex:2 50%; + justify-content:flex-end; + margin:0 +} +@media (max-width: 480px) { + .akismet-masthead__links { + padding-right:.625rem + } +} +.akismet-masthead__link-li { + margin:0; + padding:0 +} +.akismet-masthead__link { + font-style:normal; + color:#0087be; + padding:.625rem; + display:inline-block +} +.akismet-masthead__link:visited { + color:#0087be +} +.akismet-masthead__link:active,.akismet-masthead__link:hover { + color:#00aadc +} +.akismet-masthead__link:hover { + text-decoration:underline +} +.akismet-masthead__link .dashicons { + display:none +} +@media (max-width: 480px) { + .akismet-masthead__link:hover,.akismet-masthead__link:active { + text-decoration:none + } + .akismet-masthead__link .dashicons { + display:block; + font-size:1.75rem + } + .akismet-masthead__link span+span { + display:none + } +} +.akismet-masthead__link-li:last-of-type .akismet-masthead__link { + padding-right:0 +} + +.akismet-lower { + margin: 0 auto; + text-align: left; + max-width: 45rem; + padding: 1.5rem; +} + +.akismet-lower .notice { + margin-bottom: 2rem; +} + +.akismet-card { + margin-top: 1rem; + margin-bottom: 0; + position: relative; + box-sizing: border-box; + background: white; +} + +.akismet-card:after, .akismet-card .inside:after, .akismet-masthead__logo-container:after { + content: "."; + display: block; + height: 0; + clear: both; + visibility: hidden; +} + +.akismet-card .inside { + padding: 1.5rem; + padding-top: 1rem; +} + +.akismet-card .akismet-card-actions { + margin-top: 1rem; +} + +.jetpack_page_akismet-key-config .update-nag, .settings_page_akismet-key-config .update-nag { + display: none; +} + +.akismet-masthead .akismet-right { + line-height: 2.125rem; + font-size: 0.9rem; +} + +.akismet-box { + box-sizing: border-box; + background: white; + border: 1px solid rgba(200, 215, 225, 0.5); +} + +.akismet-box h2, .akismet-box h3 { + padding: 1.5rem 1.5rem .5rem 1.5rem; + margin: 0; +} + +.akismet-box p { + padding: 0 1.5rem 1.5rem 1.5rem; + margin: 0; +} + +.akismet-box p:after { + content: "."; + display: block; + height: 0; + clear: both; + visibility: hidden; +} + +.akismet-box .akismet-right { + padding-right: 1.5rem; +} + +.akismet-boxes .akismet-box { + margin-bottom: 0; + padding: 0; + margin-top: -1px; +} + +.akismet-boxes .akismet-box:last-child { + margin-bottom: 1.5rem; +} + +.akismet-boxes .akismet-box:first-child { + margin-top: 1.5rem; +} + +.akismet-box .centered { + text-align: center; +} + +.akismet-button, .akismet-button:hover, .akismet-button:visited { + background: white; + border-color: #c8d7e1; + border-style: solid; + border-width: 1px 1px 2px; + color: #2e4453; + cursor: pointer; + display: inline-block; + margin: 0; + outline: 0; + overflow: hidden; + font-size: 14px; + font-weight: 500; + text-overflow: ellipsis; + text-decoration: none; + vertical-align: top; + box-sizing: border-box; + line-height: 21px; + border-radius: 4px; + padding: 7px 14px 9px; + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; +} + +.akismet-button:hover { + border-color: #a8bece; +} + +.akismet-button:active { + border-width: 2px 1px 1px; +} + +.akismet-is-primary, .akismet-is-primary:hover, .akismet-is-primary:visited { + background: #00aadc; + border-color: #0087be; + color: white; +} + +.akismet-is-primary:hover, .akismet-is-primary:focus { + border-color: #005082; +} + +.akismet-is-primary:hover { + border-color: #005082; +} + +.akismet-section-header { + position: relative; + margin: 0 auto 0.625rem auto; + padding: 1rem; + box-sizing: border-box; + box-shadow: 0 0 0 1px rgba(200, 215, 225, 0.5), 0 1px 2px #e9eff3; + background: #ffffff; + width: 100%; + padding-top: 0.6875rem; + padding-bottom: 0.6875rem; + display: flex; +} + +.akismet-section-header__label { + display: flex; + align-items: center; + flex-grow: 1; + line-height: 1.75rem; + position: relative; + font-size: 0.875rem; + color: #4f748e; +} + +.akismet-section-header__actions { + line-height: 1.75rem; +} + +.akismet-setup-instructions form { + padding-bottom: 1.5rem; +} + +.akismet-setup-instructions > a.akismet-button { + display: inline-block; + margin-bottom: 1.5rem; +} + +div.error.akismet-usage-limit-alert { + padding: 25px 45px 25px 15px; + display: flex; + align-items: center; +} + +#akismet-plugin-container .akismet-usage-limit-alert { + margin: 0 auto 0.625rem auto; + box-sizing: border-box; + box-shadow: 0 0 0 1px rgba(200, 215, 225, 0.5), 0 1px 2px #e9eff3; + border: none; + border-left: 4px solid #d63638; +} + +.akismet-usage-limit-alert .akismet-usage-limit-logo { + width: 38px; + min-width: 38px; + height: 38px; + border-radius: 20px; + margin-right: 18px; + background: black; + position: relative; +} + +.akismet-usage-limit-alert .akismet-usage-limit-logo img { + position: absolute; + width: 22px; + left: 8px; + top: 10px; +} + +.akismet-usage-limit-alert .akismet-usage-limit-text { + flex-grow: 1; + margin-right: 18px; +} + +.akismet-usage-limit-alert h3 { + line-height: 1.3; + margin: 0; +} + +.akismet-usage-limit-alert .akismet-usage-limit-cta { + border-color: none; + text-align: right; +} + +#akismet-plugin-container .akismet-usage-limit-cta a { + color: #d63638; + background: #fafafa; +} + +@media (max-width: 550px) { + div.error.akismet-usage-limit-alert { + display: block; + } + + .akismet-usage-limit-alert .akismet-usage-limit-logo, + .akismet-usage-limit-alert .akismet-usage-limit-text { + margin-bottom: 15px; + } + + .akismet-usage-limit-alert .akismet-usage-limit-cta { + text-align: left; + } +} \ No newline at end of file diff --git a/wp-content/plugins/akismet/_inc/akismet.js b/wp-content/plugins/akismet/_inc/akismet.js new file mode 100644 index 0000000..4b43bec --- /dev/null +++ b/wp-content/plugins/akismet/_inc/akismet.js @@ -0,0 +1,397 @@ +jQuery( function ( $ ) { + var mshotRemovalTimer = null; + var mshotRetryTimer = null; + var mshotTries = 0; + var mshotRetryInterval = 1000; + var mshotEnabledLinkSelector = 'a[id^="author_comment_url"], tr.pingback td.column-author a:first-of-type, td.comment p a'; + + var preloadedMshotURLs = []; + + $('.akismet-status').each(function () { + var thisId = $(this).attr('commentid'); + $(this).prependTo('#comment-' + thisId + ' .column-comment'); + }); + $('.akismet-user-comment-count').each(function () { + var thisId = $(this).attr('commentid'); + $(this).insertAfter('#comment-' + thisId + ' .author strong:first').show(); + }); + + akismet_enable_comment_author_url_removal(); + + $( '#the-comment-list' ).on( 'click', '.akismet_remove_url', function () { + var thisId = $(this).attr('commentid'); + var data = { + action: 'comment_author_deurl', + _wpnonce: WPAkismet.comment_author_url_nonce, + id: thisId + }; + $.ajax({ + url: ajaxurl, + type: 'POST', + data: data, + beforeSend: function () { + // Removes "x" link + $("a[commentid='"+ thisId +"']").hide(); + // Show temp status + $("#author_comment_url_"+ thisId).html( $( '' ).text( WPAkismet.strings['Removing...'] ) ); + }, + success: function (response) { + if (response) { + // Show status/undo link + $("#author_comment_url_"+ thisId) + .attr('cid', thisId) + .addClass('akismet_undo_link_removal') + .html( + $( '' ).text( WPAkismet.strings['URL removed'] ) + ) + .append( ' ' ) + .append( + $( '' ) + .text( WPAkismet.strings['(undo)'] ) + .addClass( 'akismet-span-link' ) + ); + } + } + }); + + return false; + }).on( 'click', '.akismet_undo_link_removal', function () { + var thisId = $(this).attr('cid'); + var thisUrl = $(this).attr('href'); + var data = { + action: 'comment_author_reurl', + _wpnonce: WPAkismet.comment_author_url_nonce, + id: thisId, + url: thisUrl + }; + $.ajax({ + url: ajaxurl, + type: 'POST', + data: data, + beforeSend: function () { + // Show temp status + $("#author_comment_url_"+ thisId).html( $( '' ).text( WPAkismet.strings['Re-adding...'] ) ); + }, + success: function (response) { + if (response) { + // Add "x" link + $("a[commentid='"+ thisId +"']").show(); + // Show link. Core strips leading http://, so let's do that too. + $("#author_comment_url_"+ thisId).removeClass('akismet_undo_link_removal').text( thisUrl.replace( /^http:\/\/(www\.)?/ig, '' ) ); + } + } + }); + + return false; + }); + + // Show a preview image of the hovered URL. Applies to author URLs and URLs inside the comments. + if ( "enable_mshots" in WPAkismet && WPAkismet.enable_mshots ) { + $( '#the-comment-list' ).on( 'mouseover', mshotEnabledLinkSelector, function () { + clearTimeout( mshotRemovalTimer ); + + if ( $( '.akismet-mshot' ).length > 0 ) { + if ( $( '.akismet-mshot:first' ).data( 'link' ) == this ) { + // The preview is already showing for this link. + return; + } + else { + // A new link is being hovered, so remove the old preview. + $( '.akismet-mshot' ).remove(); + } + } + + clearTimeout( mshotRetryTimer ); + + var linkUrl = $( this ).attr( 'href' ); + + if ( preloadedMshotURLs.indexOf( linkUrl ) !== -1 ) { + // This preview image was already preloaded, so begin with a retry URL so the user doesn't see the placeholder image for the first second. + mshotTries = 2; + } + else { + mshotTries = 1; + } + + var mShot = $( '
' ); + mShot.data( 'link', this ); + mShot.data( 'url', linkUrl ); + + mShot.find( 'img' ).on( 'load', function () { + $( '.akismet-mshot' ).data( 'pending-request', false ); + } ); + + var offset = $( this ).offset(); + + mShot.offset( { + left : Math.min( $( window ).width() - 475, offset.left + $( this ).width() + 10 ), // Keep it on the screen if the link is near the edge of the window. + top: offset.top + ( $( this ).height() / 2 ) - 101 // 101 = top offset of the arrow plus the top border thickness + } ); + + $( 'body' ).append( mShot ); + + mshotRetryTimer = setTimeout( retryMshotUntilLoaded, mshotRetryInterval ); + } ).on( 'mouseout', 'a[id^="author_comment_url"], tr.pingback td.column-author a:first-of-type, td.comment p a', function () { + mshotRemovalTimer = setTimeout( function () { + clearTimeout( mshotRetryTimer ); + + $( '.akismet-mshot' ).remove(); + }, 200 ); + } ); + + var preloadDelayTimer = null; + + $( window ).on( 'scroll resize', function () { + clearTimeout( preloadDelayTimer ); + + preloadDelayTimer = setTimeout( preloadMshotsInViewport, 500 ); + } ); + + preloadMshotsInViewport(); + } + + /** + * The way mShots works is if there was no screenshot already recently generated for the URL, + * it returns a "loading..." image for the first request. Then, some subsequent request will + * receive the actual screenshot, but it's unknown how long it will take. So, what we do here + * is continually re-request the mShot, waiting a second after every response until we get the + * actual screenshot. + */ + function retryMshotUntilLoaded() { + clearTimeout( mshotRetryTimer ); + + var imageWidth = $( '.akismet-mshot img' ).get(0).naturalWidth; + + if ( imageWidth == 0 ) { + // It hasn't finished loading yet the first time. Check again shortly. + setTimeout( retryMshotUntilLoaded, mshotRetryInterval ); + } + else if ( imageWidth == 400 ) { + // It loaded the preview image. + + if ( mshotTries == 20 ) { + // Give up if we've requested the mShot 20 times already. + return; + } + + if ( ! $( '.akismet-mshot' ).data( 'pending-request' ) ) { + $( '.akismet-mshot' ).data( 'pending-request', true ); + + mshotTries++; + + $( '.akismet-mshot .mshot-image' ).attr( 'src', akismet_mshot_url( $( '.akismet-mshot' ).data( 'url' ), mshotTries ) ); + } + + mshotRetryTimer = setTimeout( retryMshotUntilLoaded, mshotRetryInterval ); + } + else { + // All done. + } + } + + function preloadMshotsInViewport() { + var windowWidth = $( window ).width(); + var windowHeight = $( window ).height(); + + $( '#the-comment-list' ).find( mshotEnabledLinkSelector ).each( function ( index, element ) { + var linkUrl = $( this ).attr( 'href' ); + + // Don't attempt to preload an mshot for a single link twice. + if ( preloadedMshotURLs.indexOf( linkUrl ) !== -1 ) { + // The URL is already preloaded. + return true; + } + + if ( typeof element.getBoundingClientRect !== 'function' ) { + // The browser is too old. Return false to stop this preloading entirely. + return false; + } + + var rect = element.getBoundingClientRect(); + + if ( rect.top >= 0 && rect.left >= 0 && rect.bottom <= windowHeight && rect.right <= windowWidth ) { + akismet_preload_mshot( linkUrl ); + $( this ).data( 'akismet-mshot-preloaded', true ); + } + } ); + } + + $( '.checkforspam.enable-on-load' ).on( 'click', function( e ) { + if ( $( this ).hasClass( 'ajax-disabled' ) ) { + // Akismet hasn't been configured yet. Allow the user to proceed to the button's link. + return; + } + + e.preventDefault(); + + if ( $( this ).hasClass( 'button-disabled' ) ) { + window.location.href = $( this ).data( 'success-url' ).replace( '__recheck_count__', 0 ).replace( '__spam_count__', 0 ); + return; + } + + $('.checkforspam').addClass('button-disabled').addClass( 'checking' ); + $('.checkforspam-spinner').addClass( 'spinner' ).addClass( 'is-active' ); + + akismet_check_for_spam(0, 100); + }).removeClass( 'button-disabled' ); + + var spam_count = 0; + var recheck_count = 0; + + function akismet_check_for_spam(offset, limit) { + var check_for_spam_buttons = $( '.checkforspam' ); + + var nonce = check_for_spam_buttons.data( 'nonce' ); + + // We show the percentage complete down to one decimal point so even queues with 100k + // pending comments will show some progress pretty quickly. + var percentage_complete = Math.round( ( recheck_count / check_for_spam_buttons.data( 'pending-comment-count' ) ) * 1000 ) / 10; + + // Update the progress counter on the "Check for Spam" button. + $( '.checkforspam' ).text( check_for_spam_buttons.data( 'progress-label' ).replace( '%1$s', percentage_complete ) ); + + $.post( + ajaxurl, + { + 'action': 'akismet_recheck_queue', + 'offset': offset, + 'limit': limit, + 'nonce': nonce + }, + function(result) { + if ( 'error' in result ) { + // An error is only returned in the case of a missing nonce, so we don't need the actual error message. + window.location.href = check_for_spam_buttons.data( 'failure-url' ); + return; + } + + recheck_count += result.counts.processed; + spam_count += result.counts.spam; + + if (result.counts.processed < limit) { + window.location.href = check_for_spam_buttons.data( 'success-url' ).replace( '__recheck_count__', recheck_count ).replace( '__spam_count__', spam_count ); + } + else { + // Account for comments that were caught as spam and moved out of the queue. + akismet_check_for_spam(offset + limit - result.counts.spam, limit); + } + } + ); + } + + if ( "start_recheck" in WPAkismet && WPAkismet.start_recheck ) { + $( '.checkforspam:first' ).click(); + } + + if ( typeof MutationObserver !== 'undefined' ) { + // Dynamically add the "X" next the the author URL links when a comment is quick-edited. + var comment_list_container = document.getElementById( 'the-comment-list' ); + + if ( comment_list_container ) { + var observer = new MutationObserver( function ( mutations ) { + for ( var i = 0, _len = mutations.length; i < _len; i++ ) { + if ( mutations[i].addedNodes.length > 0 ) { + akismet_enable_comment_author_url_removal(); + + // Once we know that we'll have to check for new author links, skip the rest of the mutations. + break; + } + } + } ); + + observer.observe( comment_list_container, { attributes: true, childList: true, characterData: true } ); + } + } + + function akismet_enable_comment_author_url_removal() { + $( '#the-comment-list' ) + .find( 'tr.comment, tr[id ^= "comment-"]' ) + .find( '.column-author a[href^="http"]:first' ) // Ignore mailto: links, which would be the comment author's email. + .each(function () { + if ( $( this ).parent().find( '.akismet_remove_url' ).length > 0 ) { + return; + } + + var linkHref = $(this).attr( 'href' ); + + // Ignore any links to the current domain, which are diagnostic tools, like the IP address link + // or any other links another plugin might add. + var currentHostParts = document.location.href.split( '/' ); + var currentHost = currentHostParts[0] + '//' + currentHostParts[2] + '/'; + + if ( linkHref.indexOf( currentHost ) != 0 ) { + var thisCommentId = $(this).parents('tr:first').attr('id').split("-"); + + $(this) + .attr("id", "author_comment_url_"+ thisCommentId[1]) + .after( + $( 'x' ) + .attr( 'commentid', thisCommentId[1] ) + .attr( 'title', WPAkismet.strings['Remove this URL'] ) + ); + } + }); + } + + /** + * Generate an mShot URL if given a link URL. + * + * @param string linkUrl + * @param int retry If retrying a request, the number of the retry. + * @return string The mShot URL; + */ + function akismet_mshot_url( linkUrl, retry ) { + var mshotUrl = '//s0.wp.com/mshots/v1/' + encodeURIComponent( linkUrl ) + '?w=900'; + + if ( retry > 1 ) { + mshotUrl += '&r=' + encodeURIComponent( retry ); + } + + mshotUrl += '&source=akismet'; + + return mshotUrl; + } + + /** + * Begin loading an mShot preview of a link. + * + * @param string linkUrl + */ + function akismet_preload_mshot( linkUrl ) { + var img = new Image(); + img.src = akismet_mshot_url( linkUrl ); + + preloadedMshotURLs.push( linkUrl ); + } + + $( '.akismet-could-be-primary' ).each( function () { + var form = $( this ).closest( 'form' ); + + form.data( 'initial-state', form.serialize() ); + + form.on( 'change keyup', function () { + var self = $( this ); + var submit_button = self.find( '.akismet-could-be-primary' ); + + if ( self.serialize() != self.data( 'initial-state' ) ) { + submit_button.addClass( 'akismet-is-primary' ); + } + else { + submit_button.removeClass( 'akismet-is-primary' ); + } + } ); + } ); + + /** + * Shows the Enter API key form + */ + $( '.akismet-enter-api-key-box__reveal' ).on( 'click', function ( e ) { + e.preventDefault(); + + var div = $( '.akismet-enter-api-key-box__form-wrapper' ); + div.show(); + div.find( 'input[name=key]' ).focus(); + + $( this ).hide(); + } ); +}); diff --git a/wp-content/plugins/akismet/_inc/fonts/inter.css b/wp-content/plugins/akismet/_inc/fonts/inter.css new file mode 100644 index 0000000..b34e411 --- /dev/null +++ b/wp-content/plugins/akismet/_inc/fonts/inter.css @@ -0,0 +1,68 @@ +/* NOAUTORTL */ +@font-face { + font-family: 'Inter'; + font-style: normal; + font-weight: 400; + font-display: swap; + src: url("https://s0.wp.com/i/fonts/inter/Inter-Regular.woff2?v=3.19") format("woff2"), + url("https://s0.wp.com/i/fonts/inter/Inter-Regular.woff?v=3.19") format("woff"); +} +@font-face { + font-family: 'Inter'; + font-style: italic; + font-weight: 400; + font-display: swap; + src: url("https://s0.wp.com/i/fonts/inter/Inter-Italic.woff2?v=3.19") format("woff2"), + url("https://s0.wp.com/i/fonts/inter/Inter-Italic.woff?v=3.19") format("woff"); +} + +@font-face { + font-family: 'Inter'; + font-style: normal; + font-weight: 500; + font-display: swap; + src: url("https://s0.wp.com/i/fonts/inter/Inter-Medium.woff2?v=3.19") format("woff2"), + url("https://s0.wp.com/i/fonts/inter/Inter-Medium.woff?v=3.19") format("woff"); +} +@font-face { + font-family: 'Inter'; + font-style: italic; + font-weight: 500; + font-display: swap; + src: url("https://s0.wp.com/i/fonts/inter/Inter-MediumItalic.woff2?v=3.19") format("woff2"), + url("https://s0.wp.com/i/fonts/inter/Inter-MediumItalic.woff?v=3.19") format("woff"); +} + +@font-face { + font-family: 'Inter'; + font-style: normal; + font-weight: 600; + font-display: swap; + src: url("https://s0.wp.com/i/fonts/inter/Inter-SemiBold.woff2?v=3.19") format("woff2"), + url("https://s0.wp.com/i/fonts/inter/Inter-SemiBold.woff?v=3.19") format("woff"); +} +@font-face { + font-family: 'Inter'; + font-style: italic; + font-weight: 600; + font-display: swap; + src: url("https://s0.wp.com/i/fonts/inter/Inter-SemiBoldItalic.woff2?v=3.19") format("woff2"), + url("https://s0.wp.com/i/fonts/inter/Inter-SemiBoldItalic.woff?v=3.19") format("woff"); +} + +@font-face { + font-family: 'Inter'; + font-style: normal; + font-weight: 700; + font-display: swap; + src: url("https://s0.wp.com/i/fonts/inter/Inter-Bold.woff2?v=3.19") format("woff2"), + url("https://s0.wp.com/i/fonts/inter/Inter-Bold.woff?v=3.19") format("woff"); +} +@font-face { + font-family: 'Inter'; + font-style: italic; + font-weight: 700; + font-display: swap; + src: url("https://s0.wp.com/i/fonts/inter/Inter-BoldItalic.woff2?v=3.19") format("woff2"), + url("https://s0.wp.com/i/fonts/inter/Inter-BoldItalic.woff?v=3.19") format("woff"); +} diff --git a/wp-content/plugins/akismet/_inc/img/akismet-activation-banner-elements.png b/wp-content/plugins/akismet/_inc/img/akismet-activation-banner-elements.png new file mode 100644 index 0000000..6b7d546 Binary files /dev/null and b/wp-content/plugins/akismet/_inc/img/akismet-activation-banner-elements.png differ diff --git a/wp-content/plugins/akismet/_inc/img/akismet-refresh-logo.svg b/wp-content/plugins/akismet/_inc/img/akismet-refresh-logo.svg new file mode 100644 index 0000000..f5b5d2c --- /dev/null +++ b/wp-content/plugins/akismet/_inc/img/akismet-refresh-logo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/wp-content/plugins/akismet/_inc/img/akismet-refresh-logo@2x.png b/wp-content/plugins/akismet/_inc/img/akismet-refresh-logo@2x.png new file mode 100644 index 0000000..15c3db3 Binary files /dev/null and b/wp-content/plugins/akismet/_inc/img/akismet-refresh-logo@2x.png differ diff --git a/wp-content/plugins/akismet/_inc/img/arrow-left.svg b/wp-content/plugins/akismet/_inc/img/arrow-left.svg new file mode 100644 index 0000000..823da27 --- /dev/null +++ b/wp-content/plugins/akismet/_inc/img/arrow-left.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/wp-content/plugins/akismet/_inc/img/copy.svg b/wp-content/plugins/akismet/_inc/img/copy.svg new file mode 100644 index 0000000..2e59315 --- /dev/null +++ b/wp-content/plugins/akismet/_inc/img/copy.svg @@ -0,0 +1,3 @@ + diff --git a/wp-content/plugins/akismet/_inc/img/logo-a-2x.png b/wp-content/plugins/akismet/_inc/img/logo-a-2x.png new file mode 100644 index 0000000..087144a Binary files /dev/null and b/wp-content/plugins/akismet/_inc/img/logo-a-2x.png differ diff --git a/wp-content/plugins/akismet/_inc/rtl/akismet-admin-rtl.css b/wp-content/plugins/akismet/_inc/rtl/akismet-admin-rtl.css new file mode 100644 index 0000000..eb3a211 --- /dev/null +++ b/wp-content/plugins/akismet/_inc/rtl/akismet-admin-rtl.css @@ -0,0 +1,799 @@ +/* This file was automatically generated on Apr 09 2026 23:28:55 */ + +body { + --akismet-color-charcoal: #272635; + --akismet-color-light-grey: #f6f7f7; + --akismet-color-mid-grey: #a7aaad; + --akismet-color-dark-grey: #646970; + --akismet-color-grey-80: #2c3338; + --akismet-color-grey-100: #101517; + --akismet-color-grey-border: #dcdcde; + --akismet-color-white: #fff; + --akismet-color-dark-green: #2d6a40; + --akismet-color-mid-green: #357b49; + --akismet-color-light-green: #4eb26a; + --akismet-color-mid-red: #e82c3f; + --akismet-color-light-blue: #256eff; + --akismet-color-notice-light-green: #dbf0e1; + --akismet-color-notice-dark-green: #69bf82; + --akismet-color-notice-light-red: #ffdbde; + --akismet-color-notice-dark-red: #ff6676; + --akismet-color-notice-yellow: #e5c133; + --akismet-color-page-bg: #fcfcfc; + --akismet-color-border: #e0e0e0; + --akismet-color-border-light: #f0f0f0; + --akismet-color-near-black: #1e1e1e; + --akismet-color-text-grey: #757575; + --akismet-color-icon: #666666; + --akismet-color-icon-hover: #333333; +} + +/* UI components */ +#akismet-plugin-container { + background-color: var(--akismet-color-page-bg); + border: 1px solid var(--akismet-color-border); + font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen-Sans', 'Ubuntu', 'Cantarell', 'Helvetica Neue', sans-serif; + -webkit-font-smoothing: antialiased; +} + +#akismet-plugin-container a { + color: var(--akismet-color-mid-green); +} + +#akismet-plugin-container a.akismet-button { + background-color: var(--akismet-color-mid-green); + color: var(--akismet-color-white); +} + +#akismet-plugin-container button:focus-visible, +#akismet-plugin-container input:focus-visible { + border: 0; + box-shadow: none; + outline: 2px solid var(--akismet-color-light-blue); +} + +#akismet-plugin-container a:focus-visible { + box-shadow: none; + outline: 2px solid var(--akismet-color-light-blue); +} + +.akismet-masthead { + border-bottom: 1px solid var(--akismet-color-border-light); + box-shadow: none; +} + +.akismet-masthead__logo { + margin: 20px 0; +} + +.akismet-section-header { + box-shadow: none; + margin-bottom: 0; +} + +.akismet-section-header__label { + color: var(--akismet-color-charcoal); + font-weight: 600; + padding-right: 0.2em; +} + +.akismet-button, +.akismet-button:hover { + border: 0; + color: var(--akismet-color-white); +} + +.akismet-button { + background-color: var(--akismet-color-mid-green); +} + +.akismet-button:hover { + background-color: var(--akismet-color-dark-green); +} + +.akismet-external-link { + display: inline-block; +} + +.akismet-external-link::after { + content: "↗"; + display: inline-block; + padding-right: 2px; + text-decoration: none; + vertical-align: middle; +} + +/* Need this specificity to override the existing header rule */ +.akismet-new-snapshot h3.akismet-new-snapshot__header { + background: none; + font-size: 13px; + color: var(--akismet-color-charcoal); + text-align: right; + text-transform: none; +} + +.akismet-new-snapshot__number { + color: var(--akismet-color-charcoal); + display: block; + font-size: 32px; + font-weight: 400; + letter-spacing: -1px; + line-height: 1.5em; + text-align: right; +} + +.akismet-new-snapshot li.akismet-new-snapshot__item { + color: var(--akismet-color-dark-grey); + font-size: 13px; + text-align: right; + text-transform: none; +} + +.akismet-masthead__logo-link { + min-height: 50px; +} + +.akismet-masthead__back-link-container { + margin-top: 16px; + margin-bottom: 2px; +} + +/* Need this specificity to override the existing link rule */ +#akismet-plugin-container a.akismet-masthead__back-link { + background-image: url(../img/arrow-left.svg); + background-position: right; + background-repeat: no-repeat; + background-size: 16px; + color: var(--akismet-color-charcoal); + font-weight: 400; + padding-right: 20px; + text-decoration: none; +} + +#akismet-plugin-container a.akismet-masthead__back-link:hover { + text-decoration: underline; +} + +.akismet-new-snapshot__item { + border-top: 1px solid var(--akismet-color-border-light); + border-right: 1px solid var(--akismet-color-border-light); + padding: 1em; +} + +.akismet-new-snapshot li:first-child { + border-right: none; +} + +.akismet-new-snapshot__list { + display: flex; + margin-bottom: 0; +} + +.akismet-new-snapshot__item { + flex: 1 0 33.33%; + margin-bottom: 0; + padding-right: 1.5em; + padding-left: 1.5em; +} + +.akismet-new-snapshot__chart { + padding: 1em; +} + +.akismet-stats-footer { + align-items: center; + border-radius: 0 0 7px 7px; + border-top: 1px solid var(--akismet-color-border-light); + display: flex; + justify-content: space-between; + outline-offset: -2px; + padding: 1em 1.5em; + text-decoration: none; +} + +.akismet-stats-footer:hover { + text-decoration: underline; +} + +.akismet-stats-footer:focus { + border-radius: 0 0 7px 7px; + box-shadow: none; +} + +.akismet-stats-footer:focus-visible { + box-shadow: none; + outline: 2px solid var(--akismet-color-light-blue); +} + +.akismet-box { + border: 0; +} + +.akismet-box:not(:first-child) { + margin-top: 1rem; +} + +.akismet-box, +.akismet-card { + border: 1px solid var(--akismet-color-border); + border-radius: 8px; + overflow: hidden; +} + +.akismet-card { + margin: 16px auto 0 auto; +} + +.akismet-lower { + padding-top: 0; +} + +.akismet-lower .inside { + padding: 0; +} + +.akismet-section-header__label { + margin: 0; +} + +.akismet-settings__row { + border-bottom: 1px solid var(--akismet-color-border-light); + display: block; + padding: 1em 1.5em; +} + +.akismet-settings__row-input { + margin-right: auto; +} + +.akismet-settings__row-title { + font-weight: 500; + font-size: 1em; + margin: 0; + margin-bottom: 1em; +} + +.akismet-settings__row-description { + margin-top: 0.5em; +} + +.akismet-card-actions { + display: flex; + justify-content: flex-end; + padding: 1em; +} + +.akismet-card-actions__secondary-action { + align-self: center; + margin-inline-end: auto; + margin-inline-start: 6px; +} + +.akismet-settings__row label { + padding-bottom: 1em; +} + +.akismet-settings__row-note { + font-size: 0.9em; + margin-top: 0.4em; +} + +.akismet-settings__row-note abbr { + cursor: help; +} + +.akismet-settings__row input[type="checkbox"], +.akismet-settings__row input[type="radio"] { + accent-color: var(--akismet-color-mid-green); + box-shadow: none; + flex-shrink: 0; + margin: 2px 0 0 0; +} + +.akismet-settings__row input[type="checkbox"] { + margin-top: 1px; + vertical-align: top; + -webkit-appearance: checkbox; +} + +.akismet-settings__row input[type="radio"] { + -webkit-appearance: radio; +} + +/* Fix up misbehaving wp-admin styles in Chrome (from forms and colors stylesheets) */ +.akismet-settings__row input[type="checkbox"]:checked:before { + content: ''; +} + +.akismet-settings__row input[type="radio"]:checked:before { + background: none; +} + +.akismet-settings__row input[type="checkbox"]:checked:hover, +.akismet-settings__row input[type="radio"]:checked:hover { + accent-color: var(--akismet-color-mid-green); +} + +.akismet-button:disabled { + background-color: var(--akismet-color-mid-grey); + color: var(--akismet-color-white); + cursor: arrow; +} + +.akismet-awaiting-stats, +.akismet-account { + padding: 0 1rem 1rem 1rem; + margin: 0; +} + +.akismet-account { + display: grid; + grid-template-columns: auto 1fr; + margin-inline-start: 2px; + margin-top: 0.25em; + padding-bottom: 0; + row-gap: 0.25em; +} + +.akismet-account__label { + font-weight: 500; + padding-bottom: 1em; + padding-inline-end: 1em; +} + +.akismet-account__value { + margin: 0; + padding-bottom: 1em; +} + +.akismet-settings__row-input-label { + align-items: center; + display: flex; +} + +.akismet-settings__row-label-text { + padding-right: 0.5em; + margin-top: 2px; +} + +.akismet-alert { + border-right: 8px solid; + border-radius: 8px; + margin: 20px 0; + padding: 0.2em 1em; +} + +.akismet-alert__heading { + font-size: 1em; +} + +.akismet-alert.is-good { + background-color: var(--akismet-color-notice-light-green); + border-right-color: var(--akismet-color-notice-dark-green); +} + +.akismet-alert.is-neutral { + background-color: var(--akismet-color-white); + border-right-color: var(--akismet-color-dark-grey); +} + +.akismet-alert.is-bad { + background-color: var(--akismet-color-notice-light-red); + border-right-color: var(--akismet-color-notice-dark-red); +} + +.akismet-alert.is-commercial { + background-color: var(--akismet-color-white); + border-color: var(--akismet-color-mid-grey); + border-bottom-width: 1px; + border-right-color: var(--akismet-color-notice-yellow); + display: flex; + padding-bottom: 1em; +} + +#akismet-plugin-container .akismet-alert.is-good a, +#akismet-plugin-container .akismet-alert.is-bad a { + /* For better contrast - green isn't great */ + color: var(--akismet-color-grey-80); +} + +.akismet-alert-header { + font-size: 16px; + margin-bottom: 0.5em; +} + +.akismet-alert-button-wrapper { + align-self: center; + margin-right: 2em; + min-width: 120px; +} + +.akismet-alert-info { + text-wrap: pretty; + margin: 0.5em 0; +} + +/* Setup */ +.akismet-setup-instructions__heading { + font-size: 1.375rem; + font-weight: 700; + padding-block-end: 0; +} + +h3.akismet-setup-instructions__subheading { + color: var(--akismet-color-dark-grey); + font-size: 1rem; + font-weight: 400; + line-height: 1.5; + margin: 0 0 1.25rem; + padding-block-start: 1rem; +} + +.akismet-setup-instructions__feature-list { + list-style: none; + margin: 1rem 0.5rem 1.5rem; + max-width: 640px; + padding: 0 1rem; +} + +.akismet-setup-instructions__feature { + align-items: start; + display: flex; + margin-block-end: 1rem; + text-align: right; +} + +.akismet-setup-instructions__icon { + height: 20px; + width: 20px; +} + +.akismet-setup-instructions__body { + flex: 1; + padding-inline-start: 0.5rem; +} + +.akismet-setup-instructions__title { + color: #1d2327; + font-size: 1rem; + font-weight: 600; + line-height: 1.3; + margin: 0; + text-align: right; +} + +p.akismet-setup-instructions__text { + color: var(--akismet-color-grey-80); + font-size: 0.875rem; + line-height: 1.5; + margin: 0.25rem 0 0; + padding: 0; + text-align: right; +} + +.akismet-setup-instructions__button, +.akismet-setup-instructions__button:hover, +.akismet-setup-instructions__button:visited { + font-size: 1rem; + margin-inline-start: 1.5rem; +} + +.akismet-setup__connection { + background: var(--akismet-color-light-grey); + border: 1px solid var(--akismet-color-grey-border); + border-radius: 8px; + margin: 1rem 1rem 2rem 1rem; + padding: 1rem; +} + +.akismet-setup__connection-action:not(:last-child) { + margin-bottom: 1rem; +} + +.akismet-setup__connection-user { + display: flex; +} + +.akismet-setup__connection-avatar { + align-items: center; + display: flex; + gap: 12px; + margin-bottom: 12px; +} + +.akismet-setup__connection-avatar-image { + border-radius: 50%; +} + +.akismet-setup__connection-account-name { + color: var(--akismet-color-charcoal); + font-size: 0.9rem; + overflow-wrap: anywhere; +} + +.akismet-setup__connection-account-email { + margin-top: 0.1rem; + overflow-wrap: anywhere; +} + +.akismet-setup__connection-action { + margin-right: auto; +} + +.akismet-setup__connection-button { + text-align: center; + width: 100%; +} + +p.akismet-setup__connection-action-intro, +p.akismet-setup__connection-action-description { + color: var(--akismet-color-dark-grey); + font-size: 0.875rem; + padding: 0; +} + +p.akismet-setup__connection-action-intro { + margin: 0 0 1rem 0; +} + +p.akismet-setup__connection-action-description { + margin: 1rem 0 0; +} + +/* API key field with copy button */ +.akismet-api-key-wrapper { + position: relative; + display: inline-flex; + align-items: center; +} + +.akismet-api-key-wrapper input { + padding-left: 36px; +} + +.akismet-api-key-copy { + position: absolute; + left: 4px; + background: none; + border: none; + cursor: pointer; + padding: 4px; + color: var(--akismet-color-icon); + display: flex; + align-items: center; +} + +.akismet-api-key-copy:hover { + color: var(--akismet-color-icon-hover); +} + +/* Setup - API key input */ +.akismet-enter-api-key-box { + margin: 1.5rem 0; +} + +.akismet-enter-api-key-box__reveal { + background: none; + border: 0; + color: var(--akismet-color-mid-green); + cursor: pointer; + text-decoration: underline; +} + +.akismet-enter-api-key-box__form-wrapper { + display: none; + margin-top: 1.5rem; +} + +.akismet-enter-api-key-box__input-wrapper { + box-sizing: border-box; + display: flex; + flex-wrap: nowrap; + padding: 0 1.5rem; + width: 100%; +} + +.akismet-enter-api-key-box__key-input { + flex-grow: 1; + margin-left: 1rem; +} + +h3.akismet-enter-api-key-box__header { + padding-top: 0; + padding-bottom: 1em; + text-align: right; +} + +/* Notices > Activation (shown on edit-comments.php) */ +#akismet-setup-prompt { + background: none; + border: none; + margin: 0; + padding: 0; + width: 100%; +} + +.akismet-activate { + align-items: center; + /* background-image is defined via an inline style in class.akismet-admin.php */ + background-color: var(--akismet-color-light-grey); + background-position: calc(100% - (100% - 1em)) center; + background-repeat: no-repeat; + background-size: 140px; + border: 1px solid var(--akismet-color-mid-green); + border-right-width: 4px; + display: flex; + justify-content: space-between; + margin: 15px 0; + min-height: 60px; + overflow: hidden; + padding: 5px 5px 5px 160px; + position: relative; +} + +.akismet-activate__button, +.akismet-activate__button:hover, +.akismet-activate__button:visited { + margin: 0 1em; +} + +.akismet-activate__description { + color: var(--akismet-color-charcoal); + flex-grow: 1; + font-size: 16px; + font-weight: 600; + margin: 0 auto; + text-align: center; + text-wrap: pretty; +} + +/* Compatible plugins section */ +.akismet-compatible-plugins__content { + padding: 0 1.5em 1.5em 1.5em; +} + +.akismet-compatible-plugins__intro { + margin: 0; +} + +.akismet-compatible-plugins__section-header-label { + display: block; +} + +.akismet-compatible-plugins__section-header-label-text { + padding-left: 0.5em; +} + +.akismet-compatible-plugins__list { + display: grid; + grid-template-columns: 1fr; + gap: 10px; + margin: 1.5em 0 1em 0; + padding: 0; +} + +.akismet-compatible-plugins__card { + border: 1px solid var(--akismet-color-border-light); + border-radius: 4px; + padding: 1em; + display: flex; + align-items: center; +} + +.akismet-compatible-plugins__card-logo { + padding: 0 0 0 1.5em; + object-fit: contain; + width: 36px; + height: 36px; +} + +.akismet-compatible-plugins__card-detail { + display: flex; + flex: 1; + justify-content: space-between; + align-items: center; +} + +.akismet-compatible-plugins__card-title { + font-size: 1.2em; + margin-top: 0; + margin-bottom: 0; +} + +.akismet-compatible-plugins__docs { + margin-top: 0; +} + +.akismet-compatible-plugins__show-more { + all: unset; + cursor: pointer; + display: flex; + justify-content: space-between; + position: relative; + width: 100%; +} + +/* Generates the show/hide chevron */ +.akismet-compatible-plugins__show-more::after { + align-self: center; + border-bottom: 2px solid black; + border-left: 2px solid black; + content: ""; + height: 8px; + transform: rotate(-45deg); + transition: transform 0.2s ease; + width: 8px; +} + +.akismet-compatible-plugins__list.is-expanded + .akismet-compatible-plugins__show-more::after { + align-self: end; + transform: rotate(-225deg); +} + +/* Gutenberg medium breakpoint */ +@media screen and (max-width: 782px) { + .akismet-new-snapshot__list { + display: block; + } + + .akismet-new-snapshot__number { + float: left; + font-size: 20px; + font-weight: 500; + margin-top: -16px; + } + + .akismet-new-snapshot__header { + font-size: 14px; + font-weight: 500; + } + + .akismet-new-snapshot__text { + font-size: 12px; + } + + .akismet-settings__row input[type="checkbox"], + .akismet-settings__row input[type="radio"] { + height: 24px; + width: 24px; + } + + .akismet-settings__row-label-text { + padding-right: 0.8em; + } + + .akismet-settings__row input[type="checkbox"], + .akismet-settings__row input[type="radio"] { + margin-top: 0; + } + + .akismet-activate { + background-size: 120px; + padding-left: 134px; + } + + .akismet-activate__button { + white-space: normal; + } + + .akismet-activate__description { + font-size: 14px; + margin-left: 1em; + } +} + +/* Gutenberg small breakpoint */ +@media screen and (max-width: 600px) { + .akismet-compatible-plugins__list { + gap: 8px; + } + + .akismet-activate__button, + .akismet-activate__button:hover { + font-size: 13px; + } + + .akismet-activate__description { + display: none; + } +} \ No newline at end of file diff --git a/wp-content/plugins/akismet/_inc/rtl/akismet-rtl.css b/wp-content/plugins/akismet/_inc/rtl/akismet-rtl.css new file mode 100644 index 0000000..d59e13a --- /dev/null +++ b/wp-content/plugins/akismet/_inc/rtl/akismet-rtl.css @@ -0,0 +1,464 @@ +/* This file was automatically generated on Oct 30 2025 21:26:42 */ + +.wp-admin.jetpack_page_akismet-key-config, .wp-admin.settings_page_akismet-key-config { + background-color:#f3f6f8; +} + +#submitted-on { + position: relative; +} +#the-comment-list .author .akismet-user-comment-count { + display: inline; +} +#the-comment-list .author a span { + text-decoration: none; + color: #999; +} +#the-comment-list .author a span.akismet-span-link { + text-decoration: inherit; + color: inherit; +} +#the-comment-list .akismet_remove_url { + margin-right: 3px; + color: #999; + padding: 2px 0 2px 3px; +} +#the-comment-list .akismet_remove_url:hover { + color: #A7301F; + font-weight: bold; + padding: 2px 0 2px 2px; +} +#dashboard_recent_comments .akismet-status { + display: none; +} +.akismet-status { + float: left; +} +.akismet-status a { + color: #AAA; + font-style: italic; +} +table.comments td.comment p a { + text-decoration: underline; +} +table.comments td.comment p a:after { + content: attr(href); + color: #aaa; + display: inline-block; /* Show the URL without the link's underline extending under it. */ + padding: 0 1ex; /* Because it's inline block, we can't just use spaces in the content: attribute to separate it from the link text. */ +} +.mshot-arrow { + width: 0; + height: 0; + border-top: 10px solid transparent; + border-bottom: 10px solid transparent; + border-left: 10px solid #5C5C5C; + position: absolute; + right: -6px; + top: 91px; +} +.mshot-container { + background: #5C5C5C; + position: absolute; + top: -94px; + padding: 7px; + width: 450px; + height: 338px; + z-index: 20000; + border-radius: 6px; +} +.akismet-mshot { + position: absolute; + z-index: 100; +} +.akismet-mshot .mshot-image { + margin: 0; + height: 338px; + width: 450px; +} +.checkforspam { + display: inline-block !important; +} + +.checkforspam-spinner { + display: inline-block; + margin-top: 7px; +} + +.akismet-right { + float: left; +} + +.akismet-card .akismet-right { + margin: 1em 0; +} + +.akismet-new-snapshot { + margin-top: 1em; + text-align: center; + background: #fff; +} + +.akismet-new-snapshot h3 { + background: #f5f5f5; + color: #888; + font-size: 11px; + margin: 0; +} + +.akismet-new-snapshot ul li { + color: #999; + font-size: 11px; + text-transform: uppercase; + box-sizing: border-box; +} + + +.akismet-settings th:first-child { + vertical-align: top; + padding-top: 15px; +} + +.akismet-settings th.akismet-api-key { + vertical-align: middle; + padding-top: 0; +} + +.akismet-settings span.akismet-note { + float: right; + padding-right: 23px; + font-size: 75%; + margin-top: -10px; +} + +.jetpack_page_akismet-key-config #wpcontent, .settings_page_akismet-key-config #wpcontent { + padding-right: 0; +} + +.akismet-masthead { + background-color:#fff; + text-align:center; + box-shadow:0 1px 0 rgba(200,215,225,0.5),0 1px 2px #e9eff3 +} + +@media (max-width: 45rem) { + .akismet-masthead { + padding:0 1.25rem + } +} + +.akismet-masthead__inside-container { + padding:.375rem 0; + margin:0 auto; + width:100%; + max-width:45rem; + text-align: right; +} +.akismet-masthead__logo-container { + padding:.3125rem 0 0 +} +.akismet-masthead__logo-link { + display:inline-block; + outline:none; + vertical-align:middle +} +.akismet-masthead__logo-link:focus { + line-height:0; + box-shadow:0 0 0 2px #78dcfa +} +.akismet-masthead__logo-link+code { + margin:0 10px; + padding:5px 9px; + border-radius:2px; + background:#e6ecf1; + color:#647a88 +} +.akismet-masthead__links { + display:flex; + flex-flow:row wrap; + flex:2 50%; + justify-content:flex-end; + margin:0 +} +@media (max-width: 480px) { + .akismet-masthead__links { + padding-left:.625rem + } +} +.akismet-masthead__link-li { + margin:0; + padding:0 +} +.akismet-masthead__link { + font-style:normal; + color:#0087be; + padding:.625rem; + display:inline-block +} +.akismet-masthead__link:visited { + color:#0087be +} +.akismet-masthead__link:active,.akismet-masthead__link:hover { + color:#00aadc +} +.akismet-masthead__link:hover { + text-decoration:underline +} +.akismet-masthead__link .dashicons { + display:none +} +@media (max-width: 480px) { + .akismet-masthead__link:hover,.akismet-masthead__link:active { + text-decoration:none + } + .akismet-masthead__link .dashicons { + display:block; + font-size:1.75rem + } + .akismet-masthead__link span+span { + display:none + } +} +.akismet-masthead__link-li:last-of-type .akismet-masthead__link { + padding-left:0 +} + +.akismet-lower { + margin: 0 auto; + text-align: right; + max-width: 45rem; + padding: 1.5rem; +} + +.akismet-lower .notice { + margin-bottom: 2rem; +} + +.akismet-card { + margin-top: 1rem; + margin-bottom: 0; + position: relative; + box-sizing: border-box; + background: white; +} + +.akismet-card:after, .akismet-card .inside:after, .akismet-masthead__logo-container:after { + content: "."; + display: block; + height: 0; + clear: both; + visibility: hidden; +} + +.akismet-card .inside { + padding: 1.5rem; + padding-top: 1rem; +} + +.akismet-card .akismet-card-actions { + margin-top: 1rem; +} + +.jetpack_page_akismet-key-config .update-nag, .settings_page_akismet-key-config .update-nag { + display: none; +} + +.akismet-masthead .akismet-right { + line-height: 2.125rem; + font-size: 0.9rem; +} + +.akismet-box { + box-sizing: border-box; + background: white; + border: 1px solid rgba(200, 215, 225, 0.5); +} + +.akismet-box h2, .akismet-box h3 { + padding: 1.5rem 1.5rem .5rem 1.5rem; + margin: 0; +} + +.akismet-box p { + padding: 0 1.5rem 1.5rem 1.5rem; + margin: 0; +} + +.akismet-box p:after { + content: "."; + display: block; + height: 0; + clear: both; + visibility: hidden; +} + +.akismet-box .akismet-right { + padding-left: 1.5rem; +} + +.akismet-boxes .akismet-box { + margin-bottom: 0; + padding: 0; + margin-top: -1px; +} + +.akismet-boxes .akismet-box:last-child { + margin-bottom: 1.5rem; +} + +.akismet-boxes .akismet-box:first-child { + margin-top: 1.5rem; +} + +.akismet-box .centered { + text-align: center; +} + +.akismet-button, .akismet-button:hover, .akismet-button:visited { + background: white; + border-color: #c8d7e1; + border-style: solid; + border-width: 1px 1px 2px; + color: #2e4453; + cursor: pointer; + display: inline-block; + margin: 0; + outline: 0; + overflow: hidden; + font-size: 14px; + font-weight: 500; + text-overflow: ellipsis; + text-decoration: none; + vertical-align: top; + box-sizing: border-box; + line-height: 21px; + border-radius: 4px; + padding: 7px 14px 9px; + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; +} + +.akismet-button:hover { + border-color: #a8bece; +} + +.akismet-button:active { + border-width: 2px 1px 1px; +} + +.akismet-is-primary, .akismet-is-primary:hover, .akismet-is-primary:visited { + background: #00aadc; + border-color: #0087be; + color: white; +} + +.akismet-is-primary:hover, .akismet-is-primary:focus { + border-color: #005082; +} + +.akismet-is-primary:hover { + border-color: #005082; +} + +.akismet-section-header { + position: relative; + margin: 0 auto 0.625rem auto; + padding: 1rem; + box-sizing: border-box; + box-shadow: 0 0 0 1px rgba(200, 215, 225, 0.5), 0 1px 2px #e9eff3; + background: #ffffff; + width: 100%; + padding-top: 0.6875rem; + padding-bottom: 0.6875rem; + display: flex; +} + +.akismet-section-header__label { + display: flex; + align-items: center; + flex-grow: 1; + line-height: 1.75rem; + position: relative; + font-size: 0.875rem; + color: #4f748e; +} + +.akismet-section-header__actions { + line-height: 1.75rem; +} + +.akismet-setup-instructions form { + padding-bottom: 1.5rem; +} + +.akismet-setup-instructions > a.akismet-button { + display: inline-block; + margin-bottom: 1.5rem; +} + +div.error.akismet-usage-limit-alert { + padding: 25px 15px 25px 45px; + display: flex; + align-items: center; +} + +#akismet-plugin-container .akismet-usage-limit-alert { + margin: 0 auto 0.625rem auto; + box-sizing: border-box; + box-shadow: 0 0 0 1px rgba(200, 215, 225, 0.5), 0 1px 2px #e9eff3; + border: none; + border-right: 4px solid #d63638; +} + +.akismet-usage-limit-alert .akismet-usage-limit-logo { + width: 38px; + min-width: 38px; + height: 38px; + border-radius: 20px; + margin-left: 18px; + background: black; + position: relative; +} + +.akismet-usage-limit-alert .akismet-usage-limit-logo img { + position: absolute; + width: 22px; + right: 8px; + top: 10px; +} + +.akismet-usage-limit-alert .akismet-usage-limit-text { + flex-grow: 1; + margin-left: 18px; +} + +.akismet-usage-limit-alert h3 { + line-height: 1.3; + margin: 0; +} + +.akismet-usage-limit-alert .akismet-usage-limit-cta { + border-color: none; + text-align: left; +} + +#akismet-plugin-container .akismet-usage-limit-cta a { + color: #d63638; + background: #fafafa; +} + +@media (max-width: 550px) { + div.error.akismet-usage-limit-alert { + display: block; + } + + .akismet-usage-limit-alert .akismet-usage-limit-logo, + .akismet-usage-limit-alert .akismet-usage-limit-text { + margin-bottom: 15px; + } + + .akismet-usage-limit-alert .akismet-usage-limit-cta { + text-align: right; + } +} \ No newline at end of file diff --git a/wp-content/plugins/akismet/abilities/class-akismet-ability-comment-check.php b/wp-content/plugins/akismet/abilities/class-akismet-ability-comment-check.php new file mode 100644 index 0000000..7f96506 --- /dev/null +++ b/wp-content/plugins/akismet/abilities/class-akismet-ability-comment-check.php @@ -0,0 +1,224 @@ + 'object', + 'properties' => array( + 'comment_author' => array( + 'type' => 'string', + 'description' => __( 'Name of the comment author.', 'akismet' ), + ), + 'comment_author_email' => array( + 'type' => 'string', + 'description' => __( 'Email address of the comment author.', 'akismet' ), + 'format' => 'email', + ), + 'comment_author_url' => array( + 'type' => 'string', + 'description' => __( 'URL/website of the comment author.', 'akismet' ), + 'format' => 'uri', + ), + 'comment_content' => array( + 'type' => 'string', + 'description' => __( 'The comment content/text.', 'akismet' ), + ), + 'comment_type' => array( + 'type' => 'string', + 'description' => __( 'The comment type (e.g., "comment", "trackback", "pingback").', 'akismet' ), + 'default' => 'comment', + ), + 'comment_post_ID' => array( + 'type' => 'integer', + 'description' => __( 'The ID of the post the comment is being submitted to.', 'akismet' ), + ), + 'permalink' => array( + 'type' => 'string', + 'description' => __( 'The permanent link to the post or page.', 'akismet' ), + 'format' => 'uri', + ), + 'user_ip' => array( + 'type' => 'string', + 'description' => __( 'IP address of the commenter.', 'akismet' ), + ), + 'user_agent' => array( + 'type' => 'string', + 'description' => __( 'User agent string of the web browser submitting the comment.', 'akismet' ), + ), + 'referrer' => array( + 'type' => 'string', + 'description' => __( 'The HTTP_REFERER header.', 'akismet' ), + ), + 'user_role' => array( + 'type' => 'string', + 'description' => __( 'The user role of the comment author if logged in.', 'akismet' ), + ), + ), + 'additionalProperties' => false, + ); + } + + /** + * Get the output schema. + * + * @return array The output schema. + */ + protected function get_output_schema(): array { + return array( + 'type' => 'object', + 'properties' => array( + 'success' => array( + 'type' => 'boolean', + 'description' => __( 'Whether the check was successfully performed.', 'akismet' ), + ), + 'is_spam' => array( + 'type' => 'boolean', + 'description' => __( 'Whether the comment is identified as spam.', 'akismet' ), + ), + 'pro_tip' => array( + 'type' => 'string', + 'description' => __( 'Optional recommendation from Akismet (e.g., "discard" for obvious spam).', 'akismet' ), + ), + 'guid' => array( + 'type' => 'string', + 'description' => __( 'Unique identifier for this check, used for webhooks and updates.', 'akismet' ), + ), + 'error' => array( + 'type' => 'string', + 'description' => __( 'Error message if the check could not be completed.', 'akismet' ), + ), + 'debug_help' => array( + 'type' => 'string', + 'description' => __( 'Debug information to help troubleshoot issues.', 'akismet' ), + ), + ), + 'additionalProperties' => false, + ); + } + + /** + * Get the ability configuration. + * + * @return array The ability configuration. + */ + public function get_config(): array { + return array( + 'label' => $this->get_label(), + 'description' => $this->get_description(), + 'category' => Akismet_Abilities::CATEGORY_SLUG, + 'input_schema' => $this->get_input_schema(), + 'output_schema' => $this->get_output_schema(), + 'execute_callback' => array( $this, 'execute' ), + 'permission_callback' => array( $this, 'current_user_has_permission' ), + 'meta' => array( + 'annotations' => array( + 'readonly' => true, + 'destructive' => false, + 'idempotent' => false, + ), + 'mcp' => array( + 'public' => ( get_option( 'akismet_enable_mcp_access' ) === '1' ), + 'type' => 'tool', + ), + 'show_in_rest' => true, + ), + ); + } + + /** + * Execute callback for the comment-check ability. + * + * @param array|null $input The comment data to check. + * @return array|WP_Error The spam check result or error. + */ + public function execute( ?array $input = null ) { + // Check for required API key. + if ( ! Akismet::get_api_key() ) { + return new WP_Error( + 'akismet_not_configured', + __( 'Akismet is not configured. Please enter an API key.', 'akismet' ) + ); + } + + // Perform the comment check. + $result = Akismet::comment_check( $input ); + + if ( ! $result ) { + return new WP_Error( + 'comment_check_failed', + __( 'Failed to check comment with Akismet API.', 'akismet' ) + ); + } + + // Build response array. + $response = array( + 'success' => true, + 'is_spam' => $result->is_spam, + ); + + // Include optional fields if present. + if ( isset( $result->pro_tip ) ) { + $response['pro_tip'] = $result->pro_tip; + } + + if ( isset( $result->guid ) ) { + $response['guid'] = $result->guid; + } + + if ( isset( $result->error ) ) { + $response['error'] = $result->error; + } + + if ( isset( $result->debug_help ) ) { + $response['debug_help'] = $result->debug_help; + } + + return $response; + } +} diff --git a/wp-content/plugins/akismet/abilities/class-akismet-ability-get-stats.php b/wp-content/plugins/akismet/abilities/class-akismet-ability-get-stats.php new file mode 100644 index 0000000..3e701a2 --- /dev/null +++ b/wp-content/plugins/akismet/abilities/class-akismet-ability-get-stats.php @@ -0,0 +1,202 @@ + array( 'object', 'null' ), + 'properties' => array( + 'interval' => array( + 'type' => 'string', + 'description' => __( 'The time interval for stats. Options: "6-months", "all", or "60-days".', 'akismet' ), + 'enum' => array( '6-months', 'all', '60-days' ), + 'default' => '6-months', + ), + ), + 'additionalProperties' => false, + ); + } + + /** + * Get the output schema. + * + * @return array The output schema. + */ + protected function get_output_schema(): array { + return array( + 'type' => 'object', + 'properties' => array( + 'success' => array( + 'type' => 'boolean', + 'description' => __( 'Whether the stats were successfully retrieved.', 'akismet' ), + ), + 'spam' => array( + 'type' => 'integer', + 'description' => __( 'Total number of spam comments blocked.', 'akismet' ), + ), + 'ham' => array( + 'type' => 'integer', + 'description' => __( 'Total number of legitimate comments approved.', 'akismet' ), + ), + 'missed_spam' => array( + 'type' => 'integer', + 'description' => __( 'Number of spam comments that were missed.', 'akismet' ), + ), + 'false_positives' => array( + 'type' => 'integer', + 'description' => __( 'Number of legitimate comments incorrectly marked as spam.', 'akismet' ), + ), + 'accuracy' => array( + 'type' => 'number', + 'description' => __( 'Accuracy percentage of spam detection.', 'akismet' ), + ), + 'time_saved' => array( + 'type' => 'integer', + 'description' => __( 'Estimated time saved by Akismet blocking spam, in seconds.', 'akismet' ), + ), + 'breakdown' => array( + 'type' => 'object', + 'description' => __( 'Monthly breakdown of statistics.', 'akismet' ), + 'additionalProperties' => array( + 'type' => 'object', + 'properties' => array( + 'spam' => array( + 'type' => 'integer', + 'description' => __( 'Total number of spam comments blocked in this period.', 'akismet' ), + ), + 'ham' => array( + 'type' => 'integer', + 'description' => __( 'Total number of legitimate comments approved in this period.', 'akismet' ), + ), + 'missed_spam' => array( + 'type' => 'integer', + 'description' => __( 'Number of spam comments that were missed in this period.', 'akismet' ), + ), + 'false_positives' => array( + 'type' => 'integer', + 'description' => __( 'Number of legitimate comments incorrectly marked as spam in this period.', 'akismet' ), + ), + 'da' => array( + 'type' => 'string', + 'description' => __( 'Date for this period.', 'akismet' ), + ), + ), + ), + ), + 'interval' => array( + 'type' => 'string', + 'description' => __( 'The time interval for these stats.', 'akismet' ), + ), + 'error' => array( + 'type' => 'string', + 'description' => __( 'Error message if the operation could not be completed.', 'akismet' ), + ), + ), + 'additionalProperties' => false, + ); + } + + /** + * Get the ability configuration. + * + * @return array The ability configuration. + */ + public function get_config(): array { + return array( + 'label' => $this->get_label(), + 'description' => $this->get_description(), + 'category' => Akismet_Abilities::CATEGORY_SLUG, + 'input_schema' => $this->get_input_schema(), + 'output_schema' => $this->get_output_schema(), + 'execute_callback' => array( $this, 'execute' ), + 'permission_callback' => array( $this, 'current_user_has_permission' ), + 'meta' => array( + 'annotations' => array( + 'readonly' => true, + 'destructive' => false, + 'idempotent' => true, + ), + 'mcp' => array( + 'public' => ( get_option( 'akismet_enable_mcp_access' ) === '1' ), + 'type' => 'tool', + ), + 'show_in_rest' => true, + ), + ); + } + + /** + * Execute callback for the get-stats ability. + * + * @param array|null $input The input parameters with optional interval. + * @return array|WP_Error The stats data or error. + */ + public function execute( ?array $input = null ) { + // Get interval from input or use default. + $interval = isset( $input['interval'] ) ? $input['interval'] : '6-months'; + + // Fetch stats from Akismet API. + $data = Akismet::get_stats( $interval ); + + if ( ! $data ) { + return new WP_Error( + 'stats_fetch_failed', + __( 'Failed to retrieve stats from Akismet API.', 'akismet' ) + ); + } + + // Build response with data from API (already properly typed by get_stats). + return array_merge( + array( + 'success' => true, + 'interval' => $interval, + ), + (array) $data + ); + } +} diff --git a/wp-content/plugins/akismet/abilities/class-akismet-ability.php b/wp-content/plugins/akismet/abilities/class-akismet-ability.php new file mode 100644 index 0000000..3767aba --- /dev/null +++ b/wp-content/plugins/akismet/abilities/class-akismet-ability.php @@ -0,0 +1,60 @@ +get_ability_name(), + $this->get_config() + ); + } + + // phpcs:disable Generic.CodeAnalysis.UnusedFunctionParameter.Found -- Base class default, subclasses use $input. + /** + * Permission callback for any ability that uses this trait. + * + * @param array|null $input The input parameters (unused). + * @return bool Whether the current user can use this ability. + */ + public function current_user_has_permission( ?array $input = null ): bool { + // phpcs:enable Generic.CodeAnalysis.UnusedFunctionParameter.Found + return current_user_can( 'moderate_comments' ); + } +} diff --git a/wp-content/plugins/akismet/abilities/interface-akismet-ability.php b/wp-content/plugins/akismet/abilities/interface-akismet-ability.php new file mode 100644 index 0000000..305e451 --- /dev/null +++ b/wp-content/plugins/akismet/abilities/interface-akismet-ability.php @@ -0,0 +1,62 @@ +protect your blog from spam. Akismet Anti-spam keeps your site protected even while you sleep. To get started: activate the Akismet plugin and then go to your Akismet Settings page to set up your API key. +Version: 5.7 +Requires at least: 5.8 +Requires PHP: 7.2 +Author: Automattic - Anti-spam Team +Author URI: https://automattic.com/wordpress-plugins/ +License: GPLv2 or later +Text Domain: akismet +*/ + +/* +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +Copyright 2005-2025 Automattic, Inc. +*/ + +// Make sure we don't expose any info if called directly +if ( ! function_exists( 'add_action' ) ) { + echo 'Hi there! I\'m just a plugin, not much I can do when called directly.'; + exit; +} + +define( 'AKISMET_VERSION', '5.7' ); +define( 'AKISMET__MINIMUM_WP_VERSION', '5.8' ); +define( 'AKISMET__PLUGIN_DIR', plugin_dir_path( __FILE__ ) ); +define( 'AKISMET_DELETE_LIMIT', 10000 ); + +register_activation_hook( __FILE__, array( 'Akismet', 'plugin_activation' ) ); +register_deactivation_hook( __FILE__, array( 'Akismet', 'plugin_deactivation' ) ); + +require_once AKISMET__PLUGIN_DIR . 'class.akismet.php'; +require_once AKISMET__PLUGIN_DIR . 'class.akismet-widget.php'; +require_once AKISMET__PLUGIN_DIR . 'class.akismet-rest-api.php'; +require_once AKISMET__PLUGIN_DIR . 'class-akismet-compatible-plugins.php'; + +add_action( 'init', array( 'Akismet', 'init' ) ); + +add_action( 'rest_api_init', array( 'Akismet_REST_API', 'init' ) ); + +add_action( 'init', array( 'Akismet_Compatible_Plugins', 'init' ) ); + +if ( function_exists( 'wp_get_connectors' ) ) { + require_once AKISMET__PLUGIN_DIR . 'class-akismet-connector.php'; + add_action( 'init', array( 'Akismet_Connector', 'init' ) ); +} + +/** + * Conditionally loads for a WordPress 6.9+ installation, which has + * access to the core Abilities API. Only register abilities if Akismet + * is set up with an API key (either predefined or configured). + */ +if ( function_exists( 'wp_register_ability' ) ) { + require_once AKISMET__PLUGIN_DIR . 'class-akismet-abilities.php'; + add_action( + 'init', + function () { + if ( Akismet::get_api_key() ) { + Akismet_Abilities::init(); + } + } + ); +} + +if ( is_admin() || ( defined( 'WP_CLI' ) && WP_CLI ) ) { + require_once AKISMET__PLUGIN_DIR . 'class.akismet-admin.php'; + add_action( 'init', array( 'Akismet_Admin', 'init' ) ); +} + +// add wrapper class around deprecated akismet functions that are referenced elsewhere +require_once AKISMET__PLUGIN_DIR . 'wrapper.php'; + +if ( defined( 'WP_CLI' ) && WP_CLI ) { + require_once AKISMET__PLUGIN_DIR . 'class.akismet-cli.php'; +} diff --git a/wp-content/plugins/akismet/changelog.txt b/wp-content/plugins/akismet/changelog.txt new file mode 100644 index 0000000..9a54099 --- /dev/null +++ b/wp-content/plugins/akismet/changelog.txt @@ -0,0 +1,550 @@ +=== Akismet Anti-spam === + +== Archived Changelog Entries == + +This file contains older changelog entries, so we can keep the size of the standard WordPress readme.txt file reasonable. +For the latest changes, please see the "Changelog" section of the [readme.txt file](https://plugins.svn.wordpress.org/akismet/trunk/readme.txt). + += 4.2.5 = +*Release Date - 11 July 2022* + +* Fixed a bug that added unnecessary comment history entries after comment rechecks. +* Added a notice that displays when WP-Cron is disabled and might be affecting comment rechecks. + += 4.2.4 = +*Release Date - 20 May 2022* + +* Improved translator instructions for comment history. +* Bumped the "Tested up to" tag to WP 6.0. + += 4.2.3 = +*Release Date - 25 April 2022* + +* Improved compatibility with Fluent Forms +* Fixed missing translation domains +* Updated stats URL. +* Improved accessibility of elements on the config page. + += 4.2.2 = +*Release Date - 24 January 2022* + +* Improved compatibility with Formidable Forms +* Fixed a bug that could cause issues when multiple contact forms appear on one page. +* Updated delete_comment and deleted_comment actions to pass two arguments to match WordPress core since 4.9.0. +* Added a filter that allows comment types to be excluded when counting users' approved comments. + += 4.2.1 = +*Release Date - 1 October 2021* + +* Fixed a bug causing AMP validation to fail on certain pages with forms. + += 4.2 = +*Release Date - 30 September 2021* + +* Added links to additional information on API usage notifications. +* Reduced the number of network requests required for a comment page when running Akismet. +* Improved compatibility with the most popular contact form plugins. +* Improved API usage buttons for clarity on what upgrade is needed. + += 4.1.12 = +*Release Date - 3 September 2021* + +* Fixed "Use of undefined constant" notice. +* Improved styling of alert notices. + += 4.1.11 = +*Release Date - 23 August 2021* + +* Added support for Akismet API usage notifications on Akismet settings and edit-comments admin pages. +* Added support for the deleted_comment action when bulk-deleting comments from Spam. + += 4.1.10 = +*Release Date - 6 July 2021* + +* Simplified the code around checking comments in REST API and XML-RPC requests. +* Updated Plus plan terminology in notices to match current subscription names. +* Added `rel="noopener"` to the widget link to avoid warnings in Google Lighthouse. +* Set the Akismet JavaScript as deferred instead of async to improve responsiveness. +* Improved the preloading of screenshot popups on the edit comments admin page. + += 4.1.9 = +*Release Date - 2 March 2021* + +* Improved handling of pingbacks in XML-RPC multicalls + += 4.1.8 = +*Release Date - 6 January 2021* + +* Fixed missing fields in submit-spam and submit-ham calls that could lead to reduced accuracy. +* Fixed usage of deprecated jQuery function. + += 4.1.7 = +*Release Date - 22 October 2020* + +* Show the "Set up your Akismet account" banner on the comments admin screen, where it's relevant to mention if Akismet hasn't been configured. +* Don't use wp_blacklist_check when the new wp_check_comment_disallowed_list function is available. + += 4.1.6 = +*Release Date - 4 June 2020* + +* Disable "Check for Spam" button until the page is loaded to avoid errors with clicking through to queue recheck endpoint directly. +* Added filter "akismet_enable_mshots" to allow disabling screenshot popups on the edit comments admin page. + += 4.1.5 = +*Release Date - 29 April 2020* + +* Based on user feedback, we have dropped the in-admin notice explaining the availability of the "privacy notice" option in the AKismet settings screen. The option itself is available, but after displaying the notice for the last 2 years, it is now considered a known fact. +* Updated the "Requires at least" to WP 4.6, based on recommendations from https://wp-info.org/tools/checkplugini18n.php?slug=akismet +* Moved older changelog entries to a separate file to keep the size of this readme reasonable, also based on recommendations from https://wp-info.org/tools/checkplugini18n.php?slug=akismet + += 4.1.4 = +*Release Date - 17 March 2020* + +* Only redirect to the Akismet setup screen upon plugin activation if the plugin was activated manually from within the plugin-related screens, to help users with non-standard install workflows, like WP-CLI. +* Update the layout of the initial setup screen to be more readable on small screens. +* If no API key has been entered, don't run code that expects an API key. +* Improve the readability of the comment history entries. +* Don't modify the comment form HTML if no API key has been set. + += 4.1.3 = +*Release Date - 31 October 2019* + +* Prevented an attacker from being able to cause a user to unknowingly recheck their Pending comments for spam. +* Improved compatibility with Jetpack 7.7+. +* Updated the plugin activation page to use consistent language and markup. +* Redirecting users to the Akismet connnection/settings screen upon plugin activation, in an effort to make it easier for people to get setup. + += 4.1.2 = +*Release Date - 14 May 2019* + +* Fixed a conflict between the Akismet setup banner and other plugin notices. +* Reduced the number of API requests made by the plugin when attempting to verify the API key. +* Include additional data in the pingback pre-check API request to help make the stats more accurate. +* Fixed a bug that was enabling the "Check for Spam" button when no comments were eligible to be checked. +* Improved Akismet's AMP compatibility. + += 4.1.1 = +*Release Date - 31 January 2019* + +* Fixed the "Setup Akismet" notice so it resizes responsively. +* Only highlight the "Save Changes" button in the Akismet config when changes have been made. +* The count of comments in your spam queue shown on the dashboard show now always be up-to-date. + += 4.1 = +*Release Date - 12 November 2018* + +* Added a WP-CLI method for retrieving stats. +* Hooked into the new "Personal Data Eraser" functionality from WordPress 4.9.6. +* Added functionality to clear outdated alerts from Akismet.com. + += 4.0.8 = +*Release Date - 19 June 2018* + +* Improved the grammar and consistency of the in-admin privacy related notes (notice and config). +* Revised in-admin explanation of the comment form privacy notice to make its usage clearer. +* Added `rel="nofollow noopener"` to the comment form privacy notice to improve SEO and security. + += 4.0.7 = +*Release Date - 28 May 2018* + +* Based on user feedback, the link on "Learn how your comment data is processed." in the optional privacy notice now has a `target` of `_blank` and opens in a new tab/window. +* Updated the in-admin privacy notice to use the term "comment" instead of "contact" in "Akismet can display a notice to your users under your comment forms." +* Only show in-admin privacy notice if Akismet has an API Key configured + += 4.0.6 = +*Release Date - 26 May 2018* + +* Moved away from using `empty( get_option() )` to instantiating a variable to be compatible with older versions of PHP (5.3, 5.4, etc). + += 4.0.5 = +*Release Date - 26 May 2018* + +* Corrected version number after tagging. Sorry... + += 4.0.4 = +*Release Date - 26 May 2018* + +* Added a hook to provide Akismet-specific privacy information for a site's privacy policy. +* Added tools to control the display of a privacy related notice under comment forms. +* Fixed HTML in activation failure message to close META and HEAD tag properly. +* Fixed a bug that would sometimes prevent Akismet from being correctly auto-configured. + += 4.0.3 = +*Release Date - 19 February 2018* + +* Added a scheduled task to remove entries in wp_commentmeta that no longer have corresponding comments in wp_comments. +* Added a new `akismet_batch_delete_count` action to the batch delete methods for people who'd like to keep track of the numbers of records being processed by those methods. + += 4.0.2 = +*Release Date - 18 December 2017* + +* Fixed a bug that could cause Akismet to recheck a comment that has already been manually approved or marked as spam. +* Fixed a bug that could cause Akismet to claim that some comments are still waiting to be checked when no comments are waiting to be checked. + += 4.0.1 = +*Release Date - 6 November 2017* + +* Fixed a bug that could prevent some users from connecting Akismet via their Jetpack connection. +* Ensured that any pending Akismet-related events are unscheduled if the plugin is deactivated. +* Allow some JavaScript to be run asynchronously to avoid affecting page render speeds. + += 4.0 = +*Release Date - 19 September 2017* + +* Added REST API endpoints for configuring Akismet and retrieving stats. +* Increased the minimum supported WordPress version to 4.0. +* Added compatibility with comments submitted via the REST API. +* Improved the progress indicator on the "Check for Spam" button. + += 3.3.4 = +*Release Date - 3 August 2017* + +* Disabled Akismet's debug log output by default unless AKISMET_DEBUG is defined. +* URL previews now begin preloading when the mouse moves near them in the comments section of wp-admin. +* When a comment is caught by the Comment Blacklist, Akismet will always allow it to stay in the trash even if it is spam as well. +* Fixed a bug that was preventing an error from being shown when a site can't reach Akismet's servers. + += 3.3.3 = +*Release Date - 13 July 2017* + +* Reduced amount of bandwidth used by the URL Preview feature. +* Improved the admin UI when the API key is manually pre-defined for the site. +* Removed a workaround for WordPress installations older than 3.3 that will improve Akismet's compatibility with other plugins. +* The number of spam blocked that is displayed on the WordPress dashboard will now be more accurate and updated more frequently. +* Fixed a bug in the Akismet widget that could cause PHP warnings. + += 3.3.2 = +*Release Date - 10 May 2017* + +* Fixed a bug causing JavaScript errors in some browsers. + += 3.3.1 = +*Release Date - 2 May 2017* + +* Improve performance by only requesting the akismet_comment_nonce option when absolutely necessary. +* Fixed two bugs that could cause PHP warnings. +* Fixed a bug that was preventing the "Remove author URL" feature from working after a comment was edited using "Quick Edit." +* Fixed a bug that was preventing the URL preview feature from working after a comment was edited using "Quick Edit." + += 3.3 = +*Release Date - 23 February 2017* + +* Updated the Akismet admin pages with a new clean design. +* Fixed bugs preventing the `akismet_add_comment_nonce` and `akismet_update_alert` wrapper functions from working properly. +* Fixed bug preventing the loading indicator from appearing when re-checking all comments for spam. +* Added a progress indicator to the "Check for Spam" button. +* Added a success message after manually rechecking the Pending queue for spam. + += 3.2 = +*Release Date - 6 September 2016* + +* Added a WP-CLI module. You can now check comments and recheck the moderation queue from the command line. +* Stopped using the deprecated jQuery function `.live()`. +* Fixed a bug in `remove_comment_author_url()` and `add_comment_author_url()` that could generate PHP notices. +* Fixed a bug that could cause an infinite loop for sites with very very very large comment IDs. +* Fixed a bug that could cause the Akismet widget title to be blank. + += 3.1.11 = +*Release Date - 12 May 2016* + +* Fixed a bug that could cause the "Check for Spam" button to skip some comments. +* Fixed a bug that could prevent some spam submissions from being sent to Akismet. +* Updated all links to use https:// when possible. +* Disabled Akismet debug logging unless WP_DEBUG and WP_DEBUG_LOG are both enabled. + += 3.1.10 = +*Release Date - 1 April 2016* + +* Fixed a bug that could cause comments caught as spam to be placed in the Pending queue. +* Fixed a bug that could have resulted in comments that were caught by the core WordPress comment blacklist not to have a corresponding History entry. +* Fixed a bug that could have caused avoidable PHP warnings in the error log. + += 3.1.9 = +*Release Date - 28 March 2016* + +* Add compatibility with Jetpack so that Jetpack can automatically configure Akismet settings when appropriate. +* Fixed a bug preventing some comment data from being sent to Akismet. + += 3.1.8 = +*Release Date - 4 March 2016* + +* Fixed a bug preventing Akismet from being used with some plugins that rewrite admin URLs. +* Reduced the amount of bandwidth used on Akismet API calls +* Reduced the amount of space Akismet uses in the database +* Fixed a bug that could cause comments caught as spam to be placed in the Pending queue. + += 3.1.7 = +*Release Date - 4 January 2016* + +* Added documentation for the 'akismet_comment_nonce' filter. +* The post-install activation button is now accessible to screen readers and keyboard-only users. +* Fixed a bug that was preventing the "Remove author URL" feature from working in WordPress 4.4 + += 3.1.6 = +*Release Date - 14 December 2015* + +* Improve the notices shown after activating Akismet. +* Update some strings to allow for the proper plural forms in all languages. + += 3.1.5 = +*Release Date - 13 October 2015* + +* Closes a potential XSS vulnerability. + += 3.1.4 = +*Release Date - 24 September 2015* + +* Fixed a bug that was preventing some users from automatically connecting using Jetpack if they didn't have a current Akismet subscription. +* Fixed a bug that could cause comments caught as spam to be placed in the Pending queue. +* Error messages and instructions have been simplified to be more understandable. +* Link previews are enabled for all links inside comments, not just the author's website link. + += 3.1.3 = +*Release Date - 6 July 2015* + +* Notify users when their account status changes after previously being successfully set up. This should help any users who are seeing blank Akismet settings screens. + += 3.1.2 = +*Release Date - 7 June 2015* + +* Reduced the amount of space Akismet uses in the commentmeta table. +* Fixed a bug where some comments with quotes in the author name weren't getting history entries +* Pre-emptive security improvements to ensure that the Akismet plugin can't be used by attackers to compromise a WordPress installation. +* Better UI for the key entry field: allow whitespace to be included at the beginning or end of the key and strip it out automatically when the form is submitted. +* When deactivating the plugin, notify the Akismet API so the site can be marked as inactive. +* Clearer error messages. + += 3.1.1 = +*Release Date - 17th March, 2015* + +* Improvements to the "Remove comment author URL" JavaScript +* Include the pingback pre-check from the 2.6 branch. + += 3.1 = +*Release Date - 11th March, 2015* + +* Use HTTPS by default for all requests to Akismet. +* Fix for a situation where Akismet might strip HTML from a comment. + += 3.0.4 = +*Release Date - 11th December, 2014* + +* Fix to make .htaccess compatible with Apache 2.4. +* Fix to allow removal of https author URLs. +* Fix to avoid stripping part of the author URL when removing and re-adding. +* Removed the "Check for Spam" button from the "Trash" and "Approved" queues, where it would have no effect. +* Allow automatic API key configuration when Jetpack is installed and connected to a WordPress.com account + += 3.0.3 = +*Release Date - 3rd November, 2014* + +* Fix for sending the wrong data to delete_comment action that could have prevented old spam comments from being deleted. +* Added a filter to disable logging of Akismet debugging information. +* Added a filter for the maximum comment age when deleting old spam comments. +* Added a filter for the number per batch when deleting old spam comments. +* Removed the "Check for Spam" button from the Spam folder. + += 3.0.2 = +*Release Date - 18th August, 2014* + +* Performance improvements. +* Fixed a bug that could truncate the comment data being sent to Akismet for checking. + += 3.0.1 = +*Release Date - 9th July, 2014* + +* Removed dependency on PHP's fsockopen function +* Fix spam/ham reports to work when reported outside of the WP dashboard, e.g., from Notifications or the WP app +* Remove jQuery dependency for comment form JavaScript +* Remove unnecessary data from some Akismet comment meta +* Suspended keys will now result in all comments being put in moderation, not spam. + += 3.0.0 = +*Release Date - 15th April, 2014* + +* Move Akismet to Settings menu +* Drop Akismet Stats menu +* Add stats snapshot to Akismet settings +* Add Akismet subscription details and status to Akismet settings +* Add contextual help for each page +* Improve Akismet setup to use Jetpack to automate plugin setup +* Fix "Check for Spam" to use AJAX to avoid page timing out +* Fix Akismet settings page to be responsive +* Drop legacy code +* Tidy up CSS and Javascript +* Replace the old discard setting with a new "discard pervasive spam" feature. + += 2.6.0 = +*Release Date - 18th March, 2014* + +* Add ajax paging to the check for spam button to handle large volumes of comments +* Optimize javascript and add localization support +* Fix bug in link to spam comments from right now dashboard widget +* Fix bug with deleting old comments to avoid timeouts dealing with large volumes of comments +* Include X-Pingback-Forwarded-For header in outbound WordPress pingback verifications +* Add pre-check for pingbacks, to stop spam before an outbound verification request is made + += 2.5.9 = +*Release Date - 1st August, 2013* + +* Update 'Already have a key' link to redirect page rather than depend on javascript +* Fix some non-translatable strings to be translatable +* Update Activation banner in plugins page to redirect user to Akismet config page + += 2.5.8 = +*Release Date - 20th January, 2013* + +* Simplify the activation process for new users +* Remove the reporter_ip parameter +* Minor preventative security improvements + += 2.5.7 = +*Release Date - 13th December, 2012* + +* FireFox Stats iframe preview bug +* Fix mshots preview when using https +* Add .htaccess to block direct access to files +* Prevent some PHP notices +* Fix Check For Spam return location when referrer is empty +* Fix Settings links for network admins +* Fix prepare() warnings in WP 3.5 + += 2.5.6 = +*Release Date - 26th April, 2012* + +* Prevent retry scheduling problems on sites where wp_cron is misbehaving +* Preload mshot previews +* Modernize the widget code +* Fix a bug where comments were not held for moderation during an error condition +* Improve the UX and display when comments are temporarily held due to an error +* Make the Check For Spam button force a retry when comments are held due to an error +* Handle errors caused by an invalid key +* Don't retry comments that are too old +* Improve error messages when verifying an API key + += 2.5.5 = +*Release Date - 11th January, 2012* + +* Add nonce check for comment author URL remove action +* Fix the settings link + += 2.5.4 = +*Release Date - 5th January, 2012* + +* Limit Akismet CSS and Javascript loading in wp-admin to just the pages that need it +* Added author URL quick removal functionality +* Added mShot preview on Author URL hover +* Added empty index.php to prevent directory listing +* Move wp-admin menu items under Jetpack, if it is installed +* Purge old Akismet comment meta data, default of 15 days + += 2.5.3 = +*Release Date - 8th Febuary, 2011* + +* Specify the license is GPL v2 or later +* Fix a bug that could result in orphaned commentmeta entries +* Include hotfix for WordPress 3.0.5 filter issue + += 2.5.2 = +*Release Date - 14th January, 2011* + +* Properly format the comment count for author counts +* Look for super admins on multisite installs when looking up user roles +* Increase the HTTP request timeout +* Removed padding for author approved count +* Fix typo in function name +* Set Akismet stats iframe height to fixed 2500px. Better to have one tall scroll bar than two side by side. + += 2.5.1 = +*Release Date - 17th December, 2010* + +* Fix a bug that caused the "Auto delete" option to fail to discard comments correctly +* Remove the comment nonce form field from the 'Akismet Configuration' page in favor of using a filter, akismet_comment_nonce +* Fixed padding bug in "author" column of posts screen +* Added margin-top to "cleared by ..." badges on dashboard +* Fix possible error when calling akismet_cron_recheck() +* Fix more PHP warnings +* Clean up XHTML warnings for comment nonce +* Fix for possible condition where scheduled comment re-checks could get stuck +* Clean up the comment meta details after deleting a comment +* Only show the status badge if the comment status has been changed by someone/something other than Akismet +* Show a 'History' link in the row-actions +* Translation fixes +* Reduced font-size on author name +* Moved "flagged by..." notification to top right corner of comment container and removed heavy styling +* Hid "flagged by..." notification while on dashboard + += 2.5.0 = +*Release Date - 7th December, 2010* + +* Track comment actions under 'Akismet Status' on the edit comment screen +* Fix a few remaining deprecated function calls ( props Mike Glendinning ) +* Use HTTPS for the stats IFRAME when wp-admin is using HTTPS +* Use the WordPress HTTP class if available +* Move the admin UI code to a separate file, only loaded when needed +* Add cron retry feature, to replace the old connectivity check +* Display Akismet status badge beside each comment +* Record history for each comment, and display it on the edit page +* Record the complete comment as originally submitted in comment_meta, to use when reporting spam and ham +* Highlight links in comment content +* New option, "Show the number of comments you've approved beside each comment author." +* New option, "Use a nonce on the comment form." + += 2.4.0 = +*Release Date - 23rd August, 2010* + +* Spell out that the license is GPLv2 +* Fix PHP warnings +* Fix WordPress deprecated function calls +* Fire the delete_comment action when deleting comments +* Move code specific for older WP versions to legacy.php +* General code clean up + += 2.3.0 = +*Release Date - 5th June, 2010* + +* Fix "Are you sure" nonce message on config screen in WPMU +* Fix XHTML compliance issue in sidebar widget +* Change author link; remove some old references to WordPress.com accounts +* Localize the widget title (core ticket #13879) + += 2.2.9 = +*Release Date - 2nd June, 2010* + +* Eliminate a potential conflict with some plugins that may cause spurious reports + += 2.2.8 = +*Release Date - 27th May, 2010* + +* Fix bug in initial comment check for ipv6 addresses +* Report comments as ham when they are moved from spam to moderation +* Report comments as ham when clicking undo after spam +* Use transition_comment_status action when available instead of older actions for spam/ham submissions +* Better diagnostic messages when PHP network functions are unavailable +* Better handling of comments by logged-in users + += 2.2.7 = +*Release Date - 17th December, 2009* + +* Add a new AKISMET_VERSION constant +* Reduce the possibility of over-counting spam when another spam filter plugin is in use +* Disable the connectivity check when the API key is hard-coded for WPMU + += 2.2.6 = +*Release Date - 20th July, 2009* + +* Fix a global warning introduced in 2.2.5 +* Add changelog and additional readme.txt tags +* Fix an array conversion warning in some versions of PHP +* Support a new WPCOM_API_KEY constant for easier use with WordPress MU + += 2.2.5 = +*Release Date - 13th July, 2009* + +* Include a new Server Connectivity diagnostic check, to detect problems caused by firewalls + += 2.2.4 = +*Release Date - 3rd June, 2009* + +* Fixed a key problem affecting the stats feature in WordPress MU +* Provide additional blog information in Akismet API calls diff --git a/wp-content/plugins/akismet/class-akismet-abilities.php b/wp-content/plugins/akismet/class-akismet-abilities.php new file mode 100644 index 0000000..e7a09c3 --- /dev/null +++ b/wp-content/plugins/akismet/class-akismet-abilities.php @@ -0,0 +1,91 @@ + 'Akismet', + 'description' => __( 'Abilities for spam protection and comment moderation with Akismet.', 'akismet' ), + ) + ); + } + + /** + * Register all Akismet abilities. + * + * @return void + */ + public static function register_abilities() { + if ( ! function_exists( 'wp_register_ability' ) ) { + return; + } + + $abilities = array( + Akismet_Ability_Get_Stats::class, + Akismet_Ability_Comment_Check::class, + ); + + foreach ( $abilities as $ability_class ) { + new $ability_class(); + } + } +} diff --git a/wp-content/plugins/akismet/class-akismet-compatible-plugins.php b/wp-content/plugins/akismet/class-akismet-compatible-plugins.php new file mode 100644 index 0000000..4b7212b --- /dev/null +++ b/wp-content/plugins/akismet/class-akismet-compatible-plugins.php @@ -0,0 +1,309 @@ + $data ) { + $path = $data['path']; + // Skip if not installed. + if ( ! isset( $all_plugins[ $path ] ) ) { + continue; + } + // Check activation: per-site or network-wide (multisite). + $site_active = is_plugin_active( $path ); + $network_active = is_multisite() && is_plugin_active_for_network( $path ); + if ( $site_active || $network_active ) { + $active_compatible_plugins[ $slug ] = $data; + } + } + + return $active_compatible_plugins; + } + + /** + * Initializes action hooks for the class. + * + * @return void + */ + public static function init(): void { + add_action( 'activated_plugin', array( static::class, 'handle_plugin_change' ), true ); + add_action( 'deactivated_plugin', array( static::class, 'handle_plugin_change' ), true ); + } + + /** + * Handles plugin activation and deactivation events. + * + * @param string $plugin The path to the main plugin file from plugins directory. + * @return void + */ + public static function handle_plugin_change( string $plugin ): void { + $cached_plugins = static::get_cached_plugins(); + + /** + * Terminate if nothing's cached. + */ + if ( false === $cached_plugins ) { + return; + } + + $plugin_change_should_invalidate_cache = in_array( $plugin, array_column( $cached_plugins, 'path' ) ); + + /** + * Purge the cache if the plugin is activated or deactivated. + */ + if ( $plugin_change_should_invalidate_cache ) { + static::purge_cache(); + } + } + + /** + * Gets plugins that are compatible with Akismet from the Akismet API. + * + * @param bool $bypass_cache Whether to bypass the cache and fetch fresh data. + * @return array + */ + private static function get_compatible_plugins( bool $bypass_cache = false ): array { + // Return cached result if present (false => cache miss; empty array is valid). + $cached_plugins = static::get_cached_plugins(); + + if ( false !== $cached_plugins && ! $bypass_cache ) { + return $cached_plugins; + } + + $response = wp_remote_get( + self::COMPATIBLE_PLUGIN_ENDPOINT + ); + + $sanitized = static::validate_compatible_plugin_response( $response ); + + if ( false === $sanitized ) { + return array(); + } + + /** + * Sets local static associative array of plugin data keyed by plugin slug. + */ + $compatible_plugins = array(); + + foreach ( $sanitized as $plugin ) { + $compatible_plugins[ $plugin['slug'] ] = $plugin; + } + + static::set_cached_plugins( $compatible_plugins ); + + return $compatible_plugins; + } + + /** + * Validates a response object from the Compatible Plugins API. + * + * @param array|WP_Error $response + * @return array|false + */ + private static function validate_compatible_plugin_response( $response ) { + /** + * Terminates the function if the response is a WP_Error object. + */ + if ( is_wp_error( $response ) ) { + return false; + } + + /** + * The response returned is an array of header + body string data. + * This pops off the body string for processing. + */ + $response_body = wp_remote_retrieve_body( $response ); + + if ( empty( $response_body ) ) { + return false; + } + + $plugins = json_decode( $response_body, true ); + + if ( false === is_array( $plugins ) ) { + return false; + } + + foreach ( $plugins as $plugin ) { + if ( ! is_array( $plugin ) ) { + /** + * Skips to the next iteration if for some reason the plugin is not an array. + */ + continue; + } + + // Ensure that the plugin config read in from the API has all the required fields. + $plugin_key_count = count( + array_intersect_key( $plugin, array_flip( static::COMPATIBLE_PLUGIN_FIELDS ) ) + ); + + $does_not_have_all_required_fields = ! ( + $plugin_key_count === count( static::COMPATIBLE_PLUGIN_FIELDS ) + ); + + if ( $does_not_have_all_required_fields ) { + return false; + } + + if ( false === static::has_valid_plugin_path( $plugin['path'] ) ) { + return false; + } + } + + return static::sanitize_compatible_plugin_response( $plugins ); + } + + /** + * Validates a plugin path format. + * + * The path should be in the format of 'plugin-name/plugin-name.php'. + * Allows alphanumeric characters, dashes, underscores, and optional dots in folder names. + * + * @param string $path + * @return bool + */ + private static function has_valid_plugin_path( string $path ): bool { + return preg_match( '/^[a-zA-Z0-9._-]+\/[a-zA-Z0-9_-]+\.php$/', $path ) === 1; + } + + /** + * Sanitizes a response object from the Compatible Plugins API. + * + * @param array $plugins + * @return array + */ + private static function sanitize_compatible_plugin_response( array $plugins = array() ): array { + foreach ( $plugins as $key => $plugin ) { + $plugins[ $key ] = array_map( 'sanitize_text_field', $plugin ); + $plugins[ $key ]['help_url'] = sanitize_url( $plugins[ $key ]['help_url'] ); + $plugins[ $key ]['logo'] = sanitize_url( $plugins[ $key ]['logo'] ); + } + + return $plugins; + } + + /** + * @param array $plugins + * @return bool + */ + private static function set_cached_plugins( array $plugins ): bool { + $_blog_id = (int) get_current_blog_id(); + + return set_transient( + static::CACHE_KEY . "_$_blog_id", + $plugins, + DAY_IN_SECONDS + ); + } + + /** + * Attempts to get cached compatible plugins. + * + * @return mixed|false + */ + private static function get_cached_plugins() { + $_blog_id = (int) get_current_blog_id(); + + return get_transient( + static::CACHE_KEY . "_$_blog_id" + ); + } + + /** + * Purges the cache for the compatible plugins. + * + * @return bool + */ + private static function purge_cache(): bool { + $_blog_id = (int) get_current_blog_id(); + + return delete_transient( + static::CACHE_KEY . "_$_blog_id" + ); + } +} diff --git a/wp-content/plugins/akismet/class-akismet-connector.php b/wp-content/plugins/akismet/class-akismet-connector.php new file mode 100644 index 0000000..55bf206 --- /dev/null +++ b/wp-content/plugins/akismet/class-akismet-connector.php @@ -0,0 +1,147 @@ +get_route() ) { + return $response; + } + + if ( 'POST' !== $request->get_method() && 'PUT' !== $request->get_method() ) { + return $response; + } + + $data = $response->get_data(); + if ( ! is_array( $data ) || ! array_key_exists( 'wordpress_api_key', $data ) ) { + return $response; + } + + $key = $data['wordpress_api_key']; + if ( ! is_string( $key ) || '' === $key ) { + return $response; + } + + if ( Akismet::KEY_STATUS_INVALID === Akismet::verify_key( $key ) ) { + update_option( 'wordpress_api_key', '' ); + $data['wordpress_api_key'] = ''; + $response->set_data( $data ); + } + + return $response; + } + + /** + * Set the isConnected status for the Akismet connector based on actual key validity. + * + * @param array $data Script module data. + * @return array + */ + public static function set_connected_status( $data ) { + if ( ! isset( $data['connectors']['akismet']['authentication'] ) ) { + return $data; + } + + $key = Akismet::get_api_key(); + + if ( empty( $key ) ) { + $data['connectors']['akismet']['authentication']['isConnected'] = false; + return $data; + } + + $is_connected = get_transient( 'akismet_connector_key_status' ); + + if ( false === $is_connected ) { + $is_connected = Akismet::verify_key( $key ); + + // Don't cache failures (e.g. network timeouts) so we retry on the next page load. + if ( Akismet::KEY_STATUS_FAILED !== $is_connected ) { + set_transient( 'akismet_connector_key_status', $is_connected, DAY_IN_SECONDS ); + } + } + + $data['connectors']['akismet']['authentication']['isConnected'] = ( Akismet::KEY_STATUS_VALID === $is_connected ); + + return $data; + } + + /** + * Clear the connector key status cache so it doesn't serve stale data. + */ + public static function invalidate_key_status_cache() { + delete_transient( 'akismet_connector_key_status' ); + } + + /** + * Register the Akismet connector with an is_active callback so the + * connectors page can detect Akismet as active when installed as a mu-plugin. + * + * We re-register the full connector rather than patching the core one + * so that Akismet still has a connector even if core removes its own. + * + * @see https://github.com/WordPress/gutenberg/pull/76994 + * + * @param WP_Connector_Registry $registry Connector registry instance. + */ + public static function register_connector( $registry ) { + if ( method_exists( $registry, 'is_registered' ) && $registry->is_registered( 'akismet' ) ) { + $registry->unregister( 'akismet' ); + } + + $registry->register( + 'akismet', + array( + 'name' => __( 'Akismet Anti-spam', 'akismet' ), + 'description' => __( 'Protect your site from spam.', 'akismet' ), + 'type' => 'spam_filtering', + 'plugin' => array( + 'file' => 'akismet/akismet.php', + 'is_active' => function () { + return defined( 'AKISMET_VERSION' ); + }, + ), + 'authentication' => array( + 'method' => 'api_key', + 'credentials_url' => 'https://akismet.com/get/', + 'setting_name' => 'wordpress_api_key', + 'constant_name' => 'WPCOM_API_KEY', + ), + ) + ); + } +} diff --git a/wp-content/plugins/akismet/class.akismet-admin.php b/wp-content/plugins/akismet/class.akismet-admin.php new file mode 100644 index 0000000..919d6ee --- /dev/null +++ b/wp-content/plugins/akismet/class.akismet-admin.php @@ -0,0 +1,1657 @@ + array( + 'href' => true, + 'title' => true, + ), + 'b' => array(), + 'code' => array(), + 'del' => array( + 'datetime' => true, + ), + 'em' => array(), + 'i' => array(), + 'q' => array( + 'cite' => true, + ), + 'strike' => array(), + 'strong' => array(), + ); + + /** + * List of pages where activation banner should be displayed. + * + * @var array + */ + private static $activation_banner_pages = array( + 'edit-comments.php', + 'options-discussion.php', + 'plugins.php', + ); + + public static function init() { + if ( ! self::$initiated ) { + self::init_hooks(); + } + + if ( isset( $_POST['action'] ) && $_POST['action'] == 'enter-key' ) { + self::enter_api_key(); + } + } + + public static function init_hooks() { + // The standalone stats page was removed in 3.0 for an all-in-one config and stats page. + // Redirect any links that might have been bookmarked or in browser history. + if ( isset( $_GET['page'] ) && 'akismet-stats-display' == $_GET['page'] ) { + wp_safe_redirect( esc_url_raw( self::get_page_url( 'stats' ) ), 301 ); + die; + } + + self::$initiated = true; + + add_action( 'admin_init', array( 'Akismet_Admin', 'admin_init' ) ); + add_action( 'admin_menu', array( 'Akismet_Admin', 'admin_menu' ), 5 ); // Priority 5, so it's called before Jetpack's admin_menu. + add_action( 'admin_notices', array( 'Akismet_Admin', 'display_notice' ) ); + add_action( 'admin_enqueue_scripts', array( 'Akismet_Admin', 'load_resources' ) ); + add_action( 'activity_box_end', array( 'Akismet_Admin', 'dashboard_stats' ) ); + add_action( 'rightnow_end', array( 'Akismet_Admin', 'rightnow_stats' ) ); + add_action( 'manage_comments_nav', array( 'Akismet_Admin', 'check_for_spam_button' ) ); + add_action( 'admin_action_akismet_recheck_queue', array( 'Akismet_Admin', 'recheck_queue' ) ); + add_action( 'wp_ajax_akismet_recheck_queue', array( 'Akismet_Admin', 'recheck_queue' ) ); + add_action( 'wp_ajax_comment_author_deurl', array( 'Akismet_Admin', 'remove_comment_author_url' ) ); + add_action( 'wp_ajax_comment_author_reurl', array( 'Akismet_Admin', 'add_comment_author_url' ) ); + add_action( 'jetpack_auto_activate_akismet', array( 'Akismet_Admin', 'connect_jetpack_user' ) ); + + add_filter( 'plugin_action_links', array( 'Akismet_Admin', 'plugin_action_links' ), 10, 2 ); + add_filter( 'comment_row_actions', array( 'Akismet_Admin', 'comment_row_action' ), 10, 2 ); + + add_filter( 'plugin_action_links_' . plugin_basename( plugin_dir_path( __FILE__ ) . 'akismet.php' ), array( 'Akismet_Admin', 'admin_plugin_settings_link' ) ); + + add_filter( 'wxr_export_skip_commentmeta', array( 'Akismet_Admin', 'exclude_commentmeta_from_export' ), 10, 3 ); + + add_filter( 'all_plugins', array( 'Akismet_Admin', 'modify_plugin_description' ) ); + + // priority=1 because we need ours to run before core's comment anonymizer runs, and that's registered at priority=10 + add_filter( 'wp_privacy_personal_data_erasers', array( 'Akismet_Admin', 'register_personal_data_eraser' ), 1 ); + } + + public static function admin_init() { + if ( get_option( 'Activated_Akismet' ) ) { + delete_option( 'Activated_Akismet' ); + if ( ! headers_sent() ) { + $admin_url = self::get_page_url( 'init' ); + wp_redirect( $admin_url ); + } + } + + add_meta_box( 'akismet-status', __( 'Comment History', 'akismet' ), array( 'Akismet_Admin', 'comment_status_meta_box' ), 'comment', 'normal' ); + + if ( function_exists( 'wp_add_privacy_policy_content' ) ) { + wp_add_privacy_policy_content( + __( 'Akismet', 'akismet' ), + __( 'We collect information about visitors who comment on Sites that use our Akismet Anti-spam service. The information we collect depends on how the User sets up Akismet for the Site, but typically includes the commenter\'s IP address, user agent, referrer, and Site URL (along with other information directly provided by the commenter such as their name, username, email address, and the comment itself).', 'akismet' ) + ); + } + + if ( ! Akismet::predefined_api_key() ) { + register_setting( + 'connectors', + 'wordpress_api_key', + array( + 'type' => 'string', + 'label' => __( 'Akismet API Key', 'akismet' ), + 'description' => __( 'API key for Akismet.', 'akismet' ), + 'default' => '', + 'show_in_rest' => true, + 'sanitize_callback' => 'sanitize_text_field', + ) + ); + } + } + + public static function admin_menu() { + if ( self::is_jetpack_active() ) { + add_action( 'jetpack_admin_menu', array( 'Akismet_Admin', 'load_menu' ) ); + } else { + self::load_menu(); + } + } + + /** + * Check if Jetpack is active. + * + * @return bool True if Jetpack class exists, false otherwise. + */ + public static function is_jetpack_active(): bool { + return class_exists( 'Jetpack' ); + } + + public static function admin_head() { + if ( ! current_user_can( 'manage_options' ) ) { + return; + } + } + + public static function admin_plugin_settings_link( $links ) { + $settings_link = '' . __( 'Settings', 'akismet' ) . ''; + array_unshift( $links, $settings_link ); + return $links; + } + + public static function load_menu() { + if ( self::is_jetpack_active() ) { + $hook = add_submenu_page( 'jetpack', __( 'Akismet Anti-spam', 'akismet' ), __( 'Akismet Anti-spam', 'akismet' ), 'manage_options', 'akismet-key-config', array( 'Akismet_Admin', 'display_page' ) ); + } else { + $hook = add_options_page( __( 'Akismet Anti-spam', 'akismet' ), __( 'Akismet Anti-spam', 'akismet' ), 'manage_options', 'akismet-key-config', array( 'Akismet_Admin', 'display_page' ) ); + } + + if ( $hook ) { + add_action( "load-$hook", array( 'Akismet_Admin', 'admin_help' ) ); + } + } + + public static function load_resources() { + global $hook_suffix; + + if ( in_array( + $hook_suffix, + apply_filters( + 'akismet_admin_page_hook_suffixes', + array_merge( + array( + 'index.php', // dashboard + 'comment.php', + 'post.php', + 'settings_page_akismet-key-config', + 'jetpack_page_akismet-key-config', + ), + self::$activation_banner_pages + ) + ) + ) ) { + $akismet_css_path = is_rtl() ? '_inc/rtl/akismet-rtl.css' : '_inc/akismet.css'; + wp_register_style( 'akismet', plugin_dir_url( __FILE__ ) . $akismet_css_path, array(), self::get_asset_file_version( $akismet_css_path ) ); + wp_enqueue_style( 'akismet' ); + + wp_register_style( 'akismet-font-inter', plugin_dir_url( __FILE__ ) . '_inc/fonts/inter.css', array(), self::get_asset_file_version( '_inc/fonts/inter.css' ) ); + wp_enqueue_style( 'akismet-font-inter' ); + + $akismet_admin_css_path = is_rtl() ? '_inc/rtl/akismet-admin-rtl.css' : '_inc/akismet-admin.css'; + wp_register_style( 'akismet-admin', plugin_dir_url( __FILE__ ) . $akismet_admin_css_path, array(), self::get_asset_file_version( $akismet_admin_css_path ) ); + wp_enqueue_style( 'akismet-admin' ); + + wp_add_inline_style( 'akismet-admin', self::get_inline_css() ); + + wp_register_script( 'akismet.js', plugin_dir_url( __FILE__ ) . '_inc/akismet.js', array( 'jquery' ), self::get_asset_file_version( '_inc/akismet.js' ) ); + wp_enqueue_script( 'akismet.js' ); + + wp_register_script( 'akismet-admin.js', plugin_dir_url( __FILE__ ) . '_inc/akismet-admin.js', array(), self::get_asset_file_version( '/_inc/akismet-admin.js' ) ); + wp_enqueue_script( 'akismet-admin.js' ); + + $inline_js = array( + 'comment_author_url_nonce' => wp_create_nonce( 'comment_author_url_nonce' ), + 'strings' => array( + 'Remove this URL' => __( 'Remove this URL', 'akismet' ), + 'Removing...' => __( 'Removing...', 'akismet' ), + 'URL removed' => __( 'URL removed', 'akismet' ), + '(undo)' => __( '(undo)', 'akismet' ), + 'Re-adding...' => __( 'Re-adding...', 'akismet' ), + ), + 'manage_akismet_url' => admin_url( 'admin.php?page=akismet-key-config' ), + ); + + if ( isset( $_GET['akismet_recheck'] ) && is_string( $_GET['akismet_recheck'] ) && wp_verify_nonce( $_GET['akismet_recheck'], 'akismet_recheck' ) ) { + $inline_js['start_recheck'] = true; + } + + if ( apply_filters( 'akismet_enable_mshots', true ) ) { + $inline_js['enable_mshots'] = true; + } + + wp_localize_script( 'akismet.js', 'WPAkismet', $inline_js ); + } + } + + /** + * Add help to the Akismet page + * + * @return false if not the Akismet page + */ + public static function admin_help() { + $current_screen = get_current_screen(); + + // Screen Content + if ( current_user_can( 'manage_options' ) ) { + if ( ! Akismet::get_api_key() || ( isset( $_GET['view'] ) && $_GET['view'] == 'start' ) ) { + // setup page + $current_screen->add_help_tab( + array( + 'id' => 'overview', + 'title' => __( 'Overview', 'akismet' ), + 'content' => + '

' . esc_html__( 'Akismet Setup', 'akismet' ) . '

' . + '

' . esc_html__( 'Akismet filters out spam, so you can focus on more important things.', 'akismet' ) . '

' . + '

' . esc_html__( 'On this page, you are able to set up the Akismet plugin.', 'akismet' ) . '

', + ) + ); + + $current_screen->add_help_tab( + array( + 'id' => 'setup-signup', + 'title' => __( 'New to Akismet', 'akismet' ), + 'content' => + '

' . esc_html__( 'Akismet Setup', 'akismet' ) . '

' . + '

' . esc_html__( 'You need to enter an API key to activate the Akismet service on your site.', 'akismet' ) . '

' . + /* translators: %s: a link to the signup page with the text 'Akismet.com'. */ + '

' . sprintf( __( 'Sign up for an account on %s to get an API Key.', 'akismet' ), 'Akismet.com' ) . '

', + ) + ); + + $current_screen->add_help_tab( + array( + 'id' => 'setup-manual', + 'title' => __( 'Enter an API Key', 'akismet' ), + 'content' => + '

' . esc_html__( 'Akismet Setup', 'akismet' ) . '

' . + '

' . esc_html__( 'If you already have an API key', 'akismet' ) . '

' . + '
    ' . + '
  1. ' . esc_html__( 'Copy and paste the API key into the text field.', 'akismet' ) . '
  2. ' . + '
  3. ' . esc_html__( 'Click the Use this Key button.', 'akismet' ) . '
  4. ' . + '
', + ) + ); + } elseif ( isset( $_GET['view'] ) && $_GET['view'] == 'stats' ) { + // stats page + $current_screen->add_help_tab( + array( + 'id' => 'overview', + 'title' => __( 'Overview', 'akismet' ), + 'content' => + '

' . esc_html__( 'Akismet Stats', 'akismet' ) . '

' . + '

' . esc_html__( 'Akismet filters out spam, so you can focus on more important things.', 'akismet' ) . '

' . + '

' . esc_html__( 'On this page, you are able to view stats on spam filtered on your site.', 'akismet' ) . '

', + ) + ); + } else { + // configuration page + $current_screen->add_help_tab( + array( + 'id' => 'overview', + 'title' => __( 'Overview', 'akismet' ), + 'content' => + '

' . esc_html__( 'Akismet Configuration', 'akismet' ) . '

' . + '

' . esc_html__( 'Akismet filters out spam, so you can focus on more important things.', 'akismet' ) . '

' . + '

' . esc_html__( 'On this page, you are able to update your Akismet settings and view spam stats.', 'akismet' ) . '

', + ) + ); + + $current_screen->add_help_tab( + array( + 'id' => 'settings', + 'title' => __( 'Settings', 'akismet' ), + 'content' => + '

' . esc_html__( 'Akismet Configuration', 'akismet' ) . '

' . + ( Akismet::predefined_api_key() ? '' : '

' . esc_html__( 'API Key', 'akismet' ) . ' - ' . esc_html__( 'Enter/remove an API key.', 'akismet' ) . '

' ) . + '

' . esc_html__( 'Comments', 'akismet' ) . ' - ' . esc_html__( 'Show the number of approved comments beside each comment author in the comments list page.', 'akismet' ) . '

' . + '

' . esc_html__( 'Strictness', 'akismet' ) . ' - ' . esc_html__( 'Choose to either discard the worst spam automatically or to always put all spam in spam folder.', 'akismet' ) . '

', + ) + ); + + if ( ! Akismet::predefined_api_key() ) { + $current_screen->add_help_tab( + array( + 'id' => 'account', + 'title' => __( 'Account', 'akismet' ), + 'content' => + '

' . esc_html__( 'Akismet Configuration', 'akismet' ) . '

' . + '

' . esc_html__( 'Subscription Type', 'akismet' ) . ' - ' . esc_html__( 'The Akismet subscription plan', 'akismet' ) . '

' . + '

' . esc_html__( 'Status', 'akismet' ) . ' - ' . esc_html__( 'The subscription status - active, cancelled or suspended', 'akismet' ) . '

', + ) + ); + } + } + } + + // Help Sidebar + $current_screen->set_help_sidebar( + '

' . esc_html__( 'For more information:', 'akismet' ) . '

' . + + '

' . esc_html__( 'Akismet FAQ', 'akismet' ) . '

' . + '

' . esc_html__( 'Akismet Support', 'akismet' ) . '

' + ); + } + + public static function enter_api_key() { + if ( ! current_user_can( 'manage_options' ) ) { + die( __( 'Cheatin’ uh?', 'akismet' ) ); + } + + if ( empty( $_POST['_wpnonce'] ) || ! is_string( $_POST['_wpnonce'] ) || ! wp_verify_nonce( $_POST['_wpnonce'], self::NONCE ) ) { + return false; + } + + foreach ( array( 'akismet_strictness', 'akismet_show_user_comments_approved', 'akismet_enable_mcp_access' ) as $option ) { + update_option( $option, isset( $_POST[ $option ] ) && (int) $_POST[ $option ] == 1 ? '1' : '0' ); + } + + if ( ! empty( $_POST['akismet_comment_form_privacy_notice'] ) ) { + self::set_form_privacy_notice_option( $_POST['akismet_comment_form_privacy_notice'] ); + } else { + self::set_form_privacy_notice_option( 'hide' ); + } + + if ( Akismet::predefined_api_key() ) { + return false; // shouldn't have option to save key if already defined + } + + $new_key = preg_replace( '/[^a-f0-9]/i', '', $_POST['key'] ); + $old_key = Akismet::get_api_key(); + + if ( empty( $new_key ) ) { + if ( ! empty( $old_key ) ) { + delete_option( 'wordpress_api_key' ); + self::$notices[] = 'new-key-empty'; + } + } elseif ( $new_key != $old_key ) { + self::save_key( $new_key ); + } + + return true; + } + + public static function save_key( $api_key ) { + $key_status = Akismet::verify_key( $api_key ); + + if ( $key_status == 'valid' ) { + $akismet_user = self::get_akismet_user( $api_key ); + + if ( $akismet_user ) { + if ( $akismet_user->status === Akismet::USER_STATUS_ACTIVE ) { + update_option( 'wordpress_api_key', $api_key ); + } + + if ( $akismet_user->status == Akismet::USER_STATUS_ACTIVE ) { + self::$notices['status'] = 'new-key-valid'; + } elseif ( $akismet_user->status == Akismet::USER_STATUS_NO_SUB ) { + self::$notices['status'] = 'no-sub'; + } else { + self::$notices['status'] = $akismet_user->status; + } + } else { + self::$notices['status'] = 'new-key-invalid'; + } + } elseif ( in_array( $key_status, array( 'invalid', 'failed' ) ) ) { + // When verify-key returns 'invalid', it could be truly invalid OR suspended. + // Check get-subscription to distinguish between these cases. + $akismet_user = self::get_akismet_user( $api_key ); + + if ( $akismet_user && isset( $akismet_user->status ) && $akismet_user->status === Akismet::USER_STATUS_SUSPENDED ) { + self::$notices['status'] = Akismet::USER_STATUS_SUSPENDED; + } else { + self::$notices['status'] = 'new-key-' . $key_status; + } + } + } + + public static function dashboard_stats() { + if ( did_action( 'rightnow_end' ) ) { + return; // We already displayed this info in the "Right Now" section + } + + if ( ! $count = get_option( 'akismet_spam_count' ) ) { + return; + } + + global $submenu; + + echo '

' . esc_html( _x( 'Spam', 'comments', 'akismet' ) ) . '

'; + + echo '

' . sprintf( + /* translators: 1: Akismet website URL, 2: Comments page URL, 3: Number of spam comments. */ + _n( + 'Akismet has protected your site from %3$s spam comment.', + 'Akismet has protected your site from %3$s spam comments.', + $count, + 'akismet' + ), + 'https://akismet.com/wordpress/?utm_source=akismet_plugin&utm_campaign=plugin_static_link&utm_medium=in_plugin&utm_content=dashboard_stats', + esc_url( add_query_arg( array( 'page' => 'akismet-admin' ), admin_url( isset( $submenu['edit-comments.php'] ) ? 'edit-comments.php' : 'edit.php' ) ) ), + number_format_i18n( $count ) + ) . '

'; + } + + // WP 2.5+ + public static function rightnow_stats() { + if ( $count = get_option( 'akismet_spam_count' ) ) { + $intro = sprintf( + /* translators: 1: Akismet website URL, 2: Number of spam comments. */ + _n( + 'Akismet has protected your site from %2$s spam comment already. ', + 'Akismet has protected your site from %2$s spam comments already. ', + $count, + 'akismet' + ), + 'https://akismet.com/wordpress/?utm_source=akismet_plugin&utm_campaign=plugin_static_link&utm_medium=in_plugin&utm_content=dashboard_stats', + number_format_i18n( $count ) + ); + } else { + /* translators: %s: Akismet website URL. */ + $intro = sprintf( __( 'Akismet blocks spam from getting to your blog. ', 'akismet' ), 'https://akismet.com/wordpress/?utm_source=akismet_plugin&utm_campaign=plugin_static_link&utm_medium=in_plugin&utm_content=dashboard_stats' ); + } + + $link = add_query_arg( array( 'comment_status' => 'spam' ), admin_url( 'edit-comments.php' ) ); + + if ( $queue_count = self::get_spam_count() ) { + $queue_text = sprintf( + /* translators: 1: Number of comments, 2: Comments page URL. */ + _n( + 'There’s %1$s comment in your spam queue right now.', + 'There are %1$s comments in your spam queue right now.', + $queue_count, + 'akismet' + ), + number_format_i18n( $queue_count ), + esc_url( $link ) + ); + } else { + /* translators: %s: Comments page URL. */ + $queue_text = sprintf( __( "There’s nothing in your spam queue at the moment.", 'akismet' ), esc_url( $link ) ); + } + + $text = $intro . '
' . $queue_text; + echo "

$text

\n"; + } + + public static function check_for_spam_button( $comment_status ) { + // The "Check for Spam" button should only appear when the page might be showing + // a comment with comment_approved=0, which means an un-trashed, un-spammed, + // not-yet-moderated comment. + if ( 'all' != $comment_status && 'moderated' != $comment_status ) { + return; + } + + if ( ! current_user_can( 'moderate_comments' ) ) { + return; + } + + $link = ''; + + $comments_count = wp_count_comments(); + + echo ''; + echo '
'; + + $classes = array( + 'button', + 'button-secondary', + 'checkforspam', + 'button-disabled', // Disable button until the page is loaded + ); + + if ( $comments_count->moderated > 0 ) { + $classes[] = 'enable-on-load'; + + if ( ! Akismet::get_api_key() ) { + $link = self::get_page_url(); + $classes[] = 'ajax-disabled'; + } + } + + echo '' . esc_html__( 'Check for Spam', 'akismet' ) . ''; + echo ''; + } + + public static function recheck_queue() { + global $wpdb; + + Akismet::fix_scheduled_recheck(); + + if ( ! ( isset( $_GET['recheckqueue'] ) || ( isset( $_REQUEST['action'] ) && 'akismet_recheck_queue' == $_REQUEST['action'] ) ) ) { + return; + } + + if ( empty( $_POST['nonce'] ) || ! is_string( $_POST['nonce'] ) || ! wp_verify_nonce( $_POST['nonce'], 'akismet_check_for_spam' ) || ! current_user_can( 'moderate_comments' ) ) { + wp_send_json( + array( + 'error' => __( 'You don’t have permission to do that.', 'akismet' ), + ) + ); + return; + } + + $result_counts = self::recheck_queue_portion( empty( $_POST['offset'] ) ? 0 : $_POST['offset'], empty( $_POST['limit'] ) ? 100 : $_POST['limit'] ); + + if ( defined( 'DOING_AJAX' ) && DOING_AJAX ) { + wp_send_json( + array( + 'counts' => $result_counts, + ) + ); + } else { + $redirect_to = isset( $_SERVER['HTTP_REFERER'] ) ? $_SERVER['HTTP_REFERER'] : admin_url( 'edit-comments.php' ); + wp_safe_redirect( $redirect_to ); + exit; + } + } + + public static function recheck_queue_portion( $start = 0, $limit = 100 ) { + global $wpdb; + + $paginate = ''; + + if ( $limit <= 0 ) { + $limit = 100; + } + + if ( $start < 0 ) { + $start = 0; + } + + $moderation = $wpdb->get_col( $wpdb->prepare( "SELECT * FROM {$wpdb->comments} WHERE comment_approved = '0' LIMIT %d OFFSET %d", $limit, $start ) ); + + $result_counts = array( + 'processed' => is_countable( $moderation ) ? count( $moderation ) : 0, + 'spam' => 0, + 'ham' => 0, + 'error' => 0, + ); + + foreach ( $moderation as $comment_id ) { + $api_response = Akismet::recheck_comment( $comment_id, 'recheck_queue' ); + + if ( 'true' === $api_response ) { + ++$result_counts['spam']; + } elseif ( 'false' === $api_response ) { + ++$result_counts['ham']; + } else { + ++$result_counts['error']; + } + } + + return $result_counts; + } + + // Adds an 'x' link next to author URLs, clicking will remove the author URL and show an undo link + public static function remove_comment_author_url() { + if ( ! empty( $_POST['id'] ) && check_admin_referer( 'comment_author_url_nonce' ) ) { + $comment_id = intval( $_POST['id'] ); + $comment = get_comment( $comment_id, ARRAY_A ); + if ( $comment && current_user_can( 'edit_comment', $comment['comment_ID'] ) ) { + $comment['comment_author_url'] = ''; + do_action( 'comment_remove_author_url' ); + print( wp_update_comment( $comment ) ); + die(); + } + } + } + + public static function add_comment_author_url() { + if ( ! empty( $_POST['id'] ) && ! empty( $_POST['url'] ) && check_admin_referer( 'comment_author_url_nonce' ) ) { + $comment_id = intval( $_POST['id'] ); + $comment = get_comment( $comment_id, ARRAY_A ); + if ( $comment && current_user_can( 'edit_comment', $comment['comment_ID'] ) ) { + $comment['comment_author_url'] = esc_url( $_POST['url'] ); + do_action( 'comment_add_author_url' ); + print( wp_update_comment( $comment ) ); + die(); + } + } + } + + public static function comment_row_action( $a, $comment ) { + $akismet_result = get_comment_meta( $comment->comment_ID, 'akismet_result', true ); + if ( ! $akismet_result && get_comment_meta( $comment->comment_ID, 'akismet_skipped', true ) ) { + $akismet_result = 'skipped'; // Akismet chose to skip the comment-check request. + } + + $akismet_error = get_comment_meta( $comment->comment_ID, 'akismet_error', true ); + $user_result = get_comment_meta( $comment->comment_ID, 'akismet_user_result', true ); + $comment_status = wp_get_comment_status( $comment->comment_ID ); + $desc = null; + if ( $akismet_error ) { + $desc = __( 'Awaiting spam check', 'akismet' ); + } elseif ( ! $user_result || $user_result == $akismet_result ) { + // Show the original Akismet result if the user hasn't overridden it, or if their decision was the same + if ( $akismet_result == 'true' && $comment_status != 'spam' && $comment_status != 'trash' ) { + $desc = __( 'Flagged as spam by Akismet', 'akismet' ); + } elseif ( $akismet_result == 'false' && $comment_status == 'spam' ) { + $desc = __( 'Cleared by Akismet', 'akismet' ); + } + } else { + $who = get_comment_meta( $comment->comment_ID, 'akismet_user', true ); + if ( $user_result == 'true' ) { + /* translators: %s: Username. */ + $desc = sprintf( __( 'Flagged as spam by %s', 'akismet' ), $who ); + } else { + /* translators: %s: Username. */ + $desc = sprintf( __( 'Un-spammed by %s', 'akismet' ), $who ); + } + } + + // add a History item to the hover links, just after Edit + if ( $akismet_result && is_array( $a ) ) { + $b = array(); + foreach ( $a as $k => $item ) { + $b[ $k ] = $item; + if ( + $k == 'edit' + || $k == 'unspam' + ) { + $b['history'] = ' ' . esc_html__( 'History', 'akismet' ) . ''; + } + } + + $a = $b; + } + + if ( $desc ) { + echo '' . esc_html( $desc ) . ''; + } + + $show_user_comments_option = get_option( 'akismet_show_user_comments_approved' ); + + if ( $show_user_comments_option === false ) { + // Default to active if the user hasn't made a decision. + $show_user_comments_option = '1'; + } + + $show_user_comments = apply_filters( 'akismet_show_user_comments_approved', $show_user_comments_option ); + $show_user_comments = $show_user_comments === 'false' ? false : $show_user_comments; // option used to be saved as 'false' / 'true' + + if ( $show_user_comments ) { + $comment_count = Akismet::get_user_comments_approved( $comment->user_id, $comment->comment_author_email, $comment->comment_author, $comment->comment_author_url ); + $comment_count = intval( $comment_count ); + echo ''; + } + + return $a; + } + + public static function comment_status_meta_box( $comment ) { + $history = Akismet::get_comment_history( $comment->comment_ID ); + + if ( $history ) { + foreach ( $history as $row ) { + $message = ''; + + if ( ! empty( $row['message'] ) ) { + // Old versions of Akismet stored the message as a literal string in the commentmeta. + // New versions don't do that for two reasons: + // 1) Save space. + // 2) The message can be translated into the current language of the blog, not stuck + // in the language of the blog when the comment was made. + $message = esc_html( $row['message'] ); + } elseif ( ! empty( $row['event'] ) ) { + // If possible, use a current translation. + switch ( $row['event'] ) { + case 'recheck-spam': + $message = esc_html( __( 'Akismet re-checked and caught this comment as spam.', 'akismet' ) ); + break; + case 'check-spam': + $message = esc_html( __( 'Akismet caught this comment as spam.', 'akismet' ) ); + break; + case 'recheck-ham': + $message = esc_html( __( 'Akismet re-checked and cleared this comment.', 'akismet' ) ); + break; + case 'check-ham': + $message = esc_html( __( 'Akismet cleared this comment.', 'akismet' ) ); + break; + case 'check-ham-pending': + $message = esc_html( __( 'Akismet provisionally cleared this comment.', 'akismet' ) ); + break; + case 'wp-blacklisted': + case 'wp-disallowed': + $message = sprintf( + /* translators: The placeholder is a WordPress PHP function name. */ + esc_html( __( 'Comment was caught by %s.', 'akismet' ) ), + function_exists( 'wp_check_comment_disallowed_list' ) ? 'wp_check_comment_disallowed_list' : 'wp_blacklist_check' + ); + break; + case 'report-spam': + if ( isset( $row['user'] ) ) { + /* translators: The placeholder is a username. */ + $message = esc_html( sprintf( __( '%s reported this comment as spam.', 'akismet' ), $row['user'] ) ); + } elseif ( ! $message ) { + $message = esc_html( __( 'This comment was reported as spam.', 'akismet' ) ); + } + break; + case 'report-ham': + if ( isset( $row['user'] ) ) { + /* translators: The placeholder is a username. */ + $message = esc_html( sprintf( __( '%s reported this comment as not spam.', 'akismet' ), $row['user'] ) ); + } elseif ( ! $message ) { + $message = esc_html( __( 'This comment was reported as not spam.', 'akismet' ) ); + } + break; + case 'cron-retry-spam': + $message = esc_html( __( 'Akismet caught this comment as spam during an automatic retry.', 'akismet' ) ); + break; + case 'cron-retry-ham': + $message = esc_html( __( 'Akismet cleared this comment during an automatic retry.', 'akismet' ) ); + break; + case 'check-error': + if ( isset( $row['meta'], $row['meta']['response'] ) ) { + /* translators: The placeholder is an error response returned by the API server. */ + $message = sprintf( esc_html( __( 'Akismet was unable to check this comment (response: %s) but will automatically retry later.', 'akismet' ) ), '' . esc_html( $row['meta']['response'] ) . '' ); + } else { + $message = esc_html( __( 'Akismet was unable to check this comment but will automatically retry later.', 'akismet' ) ); + } + break; + case 'recheck-error': + if ( isset( $row['meta'], $row['meta']['response'] ) ) { + /* translators: The placeholder is an error response returned by the API server. */ + $message = sprintf( esc_html( __( 'Akismet was unable to recheck this comment (response: %s).', 'akismet' ) ), '' . esc_html( $row['meta']['response'] ) . '' ); + } else { + $message = esc_html( __( 'Akismet was unable to recheck this comment.', 'akismet' ) ); + } + break; + case 'webhook-spam': + $message = esc_html( __( 'Akismet caught this comment as spam and updated its status via webhook.', 'akismet' ) ); + break; + case 'webhook-ham': + $message = esc_html( __( 'Akismet cleared this comment and updated its status via webhook.', 'akismet' ) ); + break; + case 'webhook-spam-noaction': + $message = esc_html( __( 'Akismet determined this comment was spam during a recheck. It did not update the comment status because it had already been modified by another user or plugin.', 'akismet' ) ); + break; + case 'webhook-ham-noaction': + $message = esc_html( __( 'Akismet cleared this comment during a recheck. It did not update the comment status because it had already been modified by another user or plugin.', 'akismet' ) ); + break; + case 'akismet-skipped': + $message = esc_html( __( 'This comment was not sent to Akismet when it was submitted because it was caught by something else.', 'akismet' ) ); + break; + case 'akismet-skipped-disallowed': + $message = esc_html( __( 'This comment was not sent to Akismet when it was submitted because it was caught by the comment disallowed list.', 'akismet' ) ); + break; + default: + if ( preg_match( '/^status-changed/', $row['event'] ) ) { + // Half of these used to be saved without the dash after 'status-changed'. + // See https://plugins.trac.wordpress.org/changeset/1150658/akismet/trunk + $new_status = preg_replace( '/^status-changed-?/', '', $row['event'] ); + /* translators: The placeholder is a short string (like 'spam' or 'approved') denoting the new comment status. */ + $message = sprintf( esc_html( __( 'Comment status was changed to %s', 'akismet' ) ), '' . esc_html( $new_status ) . '' ); + } elseif ( preg_match( '/^status-/', $row['event'] ) ) { + $new_status = preg_replace( '/^status-/', '', $row['event'] ); + + if ( isset( $row['user'] ) ) { + /* translators: %1$s is a username; %2$s is a short string (like 'spam' or 'approved') denoting the new comment status. */ + $message = sprintf( esc_html( __( '%1$s changed the comment status to %2$s.', 'akismet' ) ), esc_html( $row['user'] ), '' . esc_html( $new_status ) . '' ); + } + } + break; + } + } + + if ( ! empty( $message ) ) { + echo '

'; + + if ( isset( $row['time'] ) ) { + $time = gmdate( 'D d M Y @ h:i:s a', (int) $row['time'] ) . ' GMT'; + + /* translators: The placeholder is an amount of time, like "7 seconds" or "3 days" returned by the function human_time_diff(). */ + $time_html = '' . sprintf( esc_html__( '%s ago', 'akismet' ), human_time_diff( $row['time'] ) ) . ''; + + printf( + /* translators: %1$s is a human-readable time difference, like "3 hours ago", and %2$s is an already-translated phrase describing how a comment's status changed, like "This comment was reported as spam." */ + esc_html( __( '%1$s - %2$s', 'akismet' ) ), + // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped + $time_html, + // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped + $message + ); // esc_html() is done above so that we can use HTML in $message. + } else { + // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped + echo $message; // esc_html() is done above so that we can use HTML in $message. + } + + echo '

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

'; + echo esc_html( __( 'No comment history.', 'akismet' ) ); + echo '

'; + } + } + + public static function plugin_action_links( $links, $file ) { + if ( $file == plugin_basename( plugin_dir_url( __FILE__ ) . '/akismet.php' ) ) { + $links[] = '' . esc_html__( 'Settings', 'akismet' ) . ''; + } + + return $links; + } + + // Total spam in queue + // get_option( 'akismet_spam_count' ) is the total caught ever + public static function get_spam_count( $type = false ) { + global $wpdb; + + if ( ! $type ) { // total + $count = wp_cache_get( 'akismet_spam_count', 'widget' ); + if ( false === $count ) { + $count = wp_count_comments(); + $count = $count->spam; + wp_cache_set( 'akismet_spam_count', $count, 'widget', 3600 ); + } + return $count; + } elseif ( 'comments' == $type || 'comment' == $type ) { // comments + $type = ''; + } + + return (int) $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(comment_ID) FROM {$wpdb->comments} WHERE comment_approved = 'spam' AND comment_type = %s", $type ) ); + } + + // Check connectivity between the WordPress blog and Akismet's servers. + // Returns an associative array of server IP addresses, where the key is the IP address, and value is true (available) or false (unable to connect). + public static function check_server_ip_connectivity() { + + $servers = $ips = array(); + + // Some web hosts may disable this function + if ( function_exists( 'gethostbynamel' ) ) { + + $ips = gethostbynamel( 'rest.akismet.com' ); + if ( $ips && is_array( $ips ) && count( $ips ) ) { + $api_key = Akismet::get_api_key(); + + foreach ( $ips as $ip ) { + $response = Akismet::verify_key( $api_key, $ip ); + // even if the key is invalid, at least we know we have connectivity + if ( $response == 'valid' || $response == 'invalid' ) { + $servers[ $ip ] = 'connected'; + } else { + $servers[ $ip ] = $response ? $response : 'unable to connect'; + } + } + } + } + + return $servers; + } + + // Simpler connectivity check + public static function check_server_connectivity( $cache_timeout = 86400 ) { + + $debug = array(); + $debug['PHP_VERSION'] = PHP_VERSION; + $debug['WORDPRESS_VERSION'] = $GLOBALS['wp_version']; + $debug['AKISMET_VERSION'] = AKISMET_VERSION; + $debug['AKISMET__PLUGIN_DIR'] = AKISMET__PLUGIN_DIR; + $debug['SITE_URL'] = site_url(); + $debug['HOME_URL'] = home_url(); + + $servers = get_option( 'akismet_available_servers' ); + if ( ( time() - get_option( 'akismet_connectivity_time' ) < $cache_timeout ) && $servers !== false ) { + $servers = self::check_server_ip_connectivity(); + update_option( 'akismet_available_servers', $servers ); + update_option( 'akismet_connectivity_time', time() ); + } + + if ( wp_http_supports( array( 'ssl' ) ) ) { + $response = wp_remote_get( 'https://rest.akismet.com/1.1/test' ); + } else { + $response = wp_remote_get( 'http://rest.akismet.com/1.1/test' ); + } + + $debug['gethostbynamel'] = function_exists( 'gethostbynamel' ) ? 'exists' : 'not here'; + $debug['Servers'] = $servers; + $debug['Test Connection'] = $response; + + Akismet::log( $debug ); + + if ( $response && 'connected' == wp_remote_retrieve_body( $response ) ) { + return true; + } + + return false; + } + + // Check the server connectivity and store the available servers in an option. + public static function get_server_connectivity( $cache_timeout = 86400 ) { + return self::check_server_connectivity( $cache_timeout ); + } + + /** + * Find out whether any comments in the Pending queue have not yet been checked by Akismet. + * + * @return bool + */ + public static function are_any_comments_waiting_to_be_checked() { + return ! ! get_comments( + array( + // Exclude comments that are not pending. This would happen if someone manually approved or spammed a comment + // that was waiting to be checked. The akismet_error meta entry will eventually be removed by the cron recheck job. + 'status' => 'hold', + + // This is the commentmeta that is saved when a comment couldn't be checked. + 'meta_key' => 'akismet_error', + + // We only need to know whether at least one comment is waiting for a check. + 'number' => 1, + ) + ); + } + + public static function get_page_url( $page = 'config' ) { + + $args = array( 'page' => 'akismet-key-config' ); + + if ( $page == 'stats' ) { + $args = array( + 'page' => 'akismet-key-config', + 'view' => 'stats', + ); + } elseif ( $page == 'delete_key' ) { + $args = array( + 'page' => 'akismet-key-config', + 'view' => 'start', + 'action' => 'delete-key', + '_wpnonce' => wp_create_nonce( self::NONCE ), + ); + } elseif ( $page === 'init' ) { + $args = array( + 'page' => 'akismet-key-config', + 'view' => 'start', + ); + } + + return add_query_arg( $args, menu_page_url( 'akismet-key-config', false ) ); + } + + /** + * Get Akismet user subscription information. + * + * @param string $api_key The Akismet API key. + * @return object|false Object with subscription info, or false if key is invalid or has no subscription. + * + * The returned object contains these properties: + * - account_id (int|false): WordPress.com user ID, or false if unavailable. + * - status (string): Account status - 'active', 'no-sub', 'cancelled', 'suspended', 'missing', or 'notice'. + * - account_name (string): Subscription plan display name. + * - account_type (string): Account type slug. + * - next_billing_date (int|false): Unix timestamp of next billing date, or false if none. + * - limit_reached (bool): Whether the usage limit has been reached. + */ + public static function get_akismet_user( $api_key ) { + $request_args = array( + 'key' => $api_key, + 'blog' => get_option( 'home' ), + ); + + $request_args = apply_filters( 'akismet_request_args', $request_args, 'get-subscription' ); + + $subscription_verification = Akismet::http_post( Akismet::build_query( $request_args ), 'get-subscription' ); + + $akismet_user = false; + + if ( ! empty( $subscription_verification[1] ) ) { + if ( 'invalid' !== $subscription_verification[1] ) { + $decoded = json_decode( $subscription_verification[1] ); + if ( is_object( $decoded ) ) { + $akismet_user = $decoded; + } + } + } + + return $akismet_user; + } + + public static function get_stats( $api_key ) { + $stat_totals = array(); + + foreach ( array( '6-months', 'all' ) as $interval ) { + $request_args = array( + 'blog' => get_option( 'home' ), + 'key' => $api_key, + 'from' => $interval, + ); + + $request_args = apply_filters( 'akismet_request_args', $request_args, 'get-stats' ); + + $response = Akismet::http_post( Akismet::build_query( $request_args ), 'get-stats' ); + + if ( ! empty( $response[1] ) ) { + $data = json_decode( $response[1] ); + /* + * The json decoded response should be an object. If it's not an object, something's wrong, and the data + * shouldn't be added to the stats_totals array. + */ + if ( is_object( $data ) ) { + $stat_totals[ $interval ] = $data; + } + } + } + + return $stat_totals; + } + + public static function verify_wpcom_key( $api_key, $user_id, $extra = array() ) { + $request_args = array_merge( + array( + 'user_id' => $user_id, + 'api_key' => $api_key, + 'get_account_type' => 'true', + ), + $extra + ); + + $request_args = apply_filters( 'akismet_request_args', $request_args, 'verify-wpcom-key' ); + + $akismet_account = Akismet::http_post( Akismet::build_query( $request_args ), 'verify-wpcom-key' ); + + if ( ! empty( $akismet_account[1] ) ) { + $akismet_account = json_decode( $akismet_account[1] ); + } + + Akismet::log( compact( 'akismet_account' ) ); + + return $akismet_account; + } + + public static function connect_jetpack_user() { + + if ( $jetpack_user = self::get_jetpack_user() ) { + if ( isset( $jetpack_user['user_id'] ) && isset( $jetpack_user['api_key'] ) ) { + $akismet_user = self::verify_wpcom_key( $jetpack_user['api_key'], $jetpack_user['user_id'], array( 'action' => 'connect_jetpack_user' ) ); + + if ( is_object( $akismet_user ) ) { + self::save_key( $akismet_user->api_key ); + return in_array( $akismet_user->status, array( Akismet::USER_STATUS_ACTIVE, Akismet::USER_STATUS_NO_SUB ) ); + } + } + } + + return false; + } + + public static function display_alert() { + Akismet::view( + 'notice', + array( + 'type' => 'alert', + 'code' => (int) get_option( 'akismet_alert_code' ), + 'msg' => get_option( 'akismet_alert_msg' ), + ) + ); + } + + public static function get_usage_limit_alert_data() { + return array( + 'type' => 'usage-limit', + 'code' => (int) get_option( 'akismet_alert_code' ), + 'msg' => get_option( 'akismet_alert_msg' ), + 'api_calls' => get_option( 'akismet_alert_api_calls' ), + 'usage_limit' => get_option( 'akismet_alert_usage_limit' ), + 'upgrade_plan' => get_option( 'akismet_alert_upgrade_plan' ), + 'upgrade_url' => get_option( 'akismet_alert_upgrade_url' ), + 'upgrade_type' => get_option( 'akismet_alert_upgrade_type' ), + 'upgrade_via_support' => get_option( 'akismet_alert_upgrade_via_support' ) === 'true', + 'recommended_plan_name' => get_option( 'akismet_alert_recommended_plan_name' ), + ); + } + + public static function display_usage_limit_alert() { + Akismet::view( 'notice', self::get_usage_limit_alert_data() ); + } + + public static function display_spam_check_warning() { + Akismet::fix_scheduled_recheck(); + + if ( wp_next_scheduled( 'akismet_schedule_cron_recheck' ) > time() && self::are_any_comments_waiting_to_be_checked() ) { + /* + * The 'akismet_display_cron_disabled_notice' filter can be used to control whether the WP-Cron disabled notice is displayed. + */ + if ( defined( 'DISABLE_WP_CRON' ) && DISABLE_WP_CRON && apply_filters( 'akismet_display_cron_disabled_notice', true ) ) { + Akismet::view( 'notice', array( 'type' => 'spam-check-cron-disabled' ) ); + } else { + /* translators: The Akismet configuration page URL. */ + $link_text = apply_filters( 'akismet_spam_check_warning_link_text', sprintf( __( 'Please check your Akismet configuration and contact your web host if problems persist.', 'akismet' ), esc_url( self::get_page_url() ) ) ); + Akismet::view( + 'notice', + array( + 'type' => 'spam-check', + 'link_text' => $link_text, + ) + ); + } + } + } + + public static function display_api_key_warning() { + Akismet::view( 'notice', array( 'type' => 'plugin' ) ); + } + + public static function display_page() { + if ( ! Akismet::get_api_key() || ( isset( $_GET['view'] ) && $_GET['view'] == 'start' ) ) { + self::display_start_page(); + } elseif ( isset( $_GET['view'] ) && $_GET['view'] == 'stats' ) { + self::display_stats_page(); + } else { + self::display_configuration_page(); + } + } + + public static function display_start_page() { + if ( isset( $_GET['action'] ) ) { + if ( $_GET['action'] == 'delete-key' ) { + if ( isset( $_GET['_wpnonce'] ) && is_string( $_GET['_wpnonce'] ) && wp_verify_nonce( $_GET['_wpnonce'], self::NONCE ) ) { + delete_option( 'wordpress_api_key' ); + } + } + } + + $api_key = Akismet::get_api_key(); + $existing_key_is_valid = ! ( + self::get_notice_by_key( 'status' ) === self::NOTICE_EXISTING_KEY_INVALID + ); + + if ( $api_key && $existing_key_is_valid ) { + self::display_configuration_page(); + return; + } + + // the user can choose to auto connect their API key by clicking a button on the akismet done page + // if jetpack, get verified api key by using connected wpcom user id + // if no jetpack, get verified api key by using an akismet token + + $akismet_user = false; + + if ( isset( $_GET['token'] ) && preg_match( '/^(\d+)-[0-9a-f]{20}$/', $_GET['token'] ) ) { + $akismet_user = self::verify_wpcom_key( '', '', array( 'token' => $_GET['token'] ) ); + } + + if ( false === $akismet_user ) { + $jetpack_user = self::get_jetpack_user(); + + if ( is_array( $jetpack_user ) ) { + $akismet_user = self::verify_wpcom_key( $jetpack_user['api_key'], $jetpack_user['user_id'] ); + } + } + + if ( isset( $_GET['action'] ) ) { + if ( $_GET['action'] == 'save-key' ) { + if ( is_object( $akismet_user ) ) { + self::save_key( $akismet_user->api_key ); + self::display_configuration_page(); + return; + } + } + } + + Akismet::view( 'start', compact( 'akismet_user' ) ); + + /* + // To see all variants when testing. + $akismet_user->status = Akismet::USER_STATUS_NO_SUB; + Akismet::view( 'start', compact( 'akismet_user' ) ); + $akismet_user->status = Akismet::USER_STATUS_CANCELLED; + Akismet::view( 'start', compact( 'akismet_user' ) ); + $akismet_user->status = Akismet::USER_STATUS_SUSPENDED; + Akismet::view( 'start', compact( 'akismet_user' ) ); + $akismet_user->status = 'other'; + Akismet::view( 'start', compact( 'akismet_user' ) ); + $akismet_user = false; + */ + } + + public static function display_stats_page() { + Akismet::view( 'stats' ); + } + + public static function display_configuration_page() { + $api_key = Akismet::get_api_key(); + $akismet_user = self::get_akismet_user( $api_key ); + + if ( ! $akismet_user ) { + // This could happen if the user's key became invalid after it was previously valid and successfully set up. + self::$notices['status'] = self::NOTICE_EXISTING_KEY_INVALID; + self::display_start_page(); + return; + } + + $stat_totals = self::get_stats( $api_key ); + + // If unset, create the new strictness option using the old discard option to determine its default. + // If the old option wasn't set, default to discarding the blatant spam. + if ( get_option( 'akismet_strictness' ) === false ) { + add_option( 'akismet_strictness', ( get_option( 'akismet_discard_month' ) === 'false' ? '0' : '1' ) ); + } + + // Sync the local "Total spam blocked" count with the authoritative count from the server. + if ( isset( $stat_totals['all'], $stat_totals['all']->spam ) ) { + update_option( 'akismet_spam_count', $stat_totals['all']->spam ); + } + + $notices = array(); + + if ( empty( self::$notices ) ) { + if ( ! empty( $stat_totals['all'] ) && isset( $stat_totals['all']->time_saved ) && $akismet_user->status == Akismet::USER_STATUS_ACTIVE && $akismet_user->account_type == 'free-api-key' ) { + + $time_saved = false; + + if ( $stat_totals['all']->time_saved > 1800 ) { + $total_in_minutes = round( $stat_totals['all']->time_saved / 60 ); + $total_in_hours = round( $total_in_minutes / 60 ); + $total_in_days = round( $total_in_hours / 8 ); + $cleaning_up = __( 'Cleaning up spam takes time.', 'akismet' ); + + if ( $total_in_days > 1 ) { + /* translators: %s: Number of days. */ + $time_saved = $cleaning_up . ' ' . sprintf( _n( 'Akismet has saved you %s day!', 'Akismet has saved you %s days!', $total_in_days, 'akismet' ), number_format_i18n( $total_in_days ) ); + } elseif ( $total_in_hours > 1 ) { + /* translators: %s: Number of hours. */ + $time_saved = $cleaning_up . ' ' . sprintf( _n( 'Akismet has saved you %d hour!', 'Akismet has saved you %d hours!', $total_in_hours, 'akismet' ), $total_in_hours ); + } elseif ( $total_in_minutes >= 30 ) { + /* translators: %s: Number of minutes. */ + $time_saved = $cleaning_up . ' ' . sprintf( _n( 'Akismet has saved you %d minute!', 'Akismet has saved you %d minutes!', $total_in_minutes, 'akismet' ), $total_in_minutes ); + } + } + + $notices[] = array( + 'type' => 'active-notice', + 'time_saved' => $time_saved, + ); + } + } + + if ( ! Akismet::predefined_api_key() && ! isset( self::$notices['status'] ) && in_array( $akismet_user->status, array( Akismet::USER_STATUS_CANCELLED, Akismet::USER_STATUS_SUSPENDED, Akismet::USER_STATUS_MISSING, Akismet::USER_STATUS_NO_SUB ) ) ) { + $notices[] = array( 'type' => $akismet_user->status ); + } + + $alert_code = get_option( 'akismet_alert_code' ); + if ( isset( Akismet::$limit_notices[ $alert_code ] ) ) { + $notices[] = self::get_usage_limit_alert_data(); + } elseif ( $alert_code > 0 ) { + $notices[] = array( + 'type' => 'alert', + 'code' => (int) get_option( 'akismet_alert_code' ), + 'msg' => get_option( 'akismet_alert_msg' ), + ); + } + + /* + * To see all variants when testing. + * + * You may also want to comment out the akismet_view_arguments filter in Akismet::view() + * to ensure that you can see all of the notices (e.g. suspended, active-notice). + */ + // $notices[] = array( 'type' => 'active-notice', 'time_saved' => 'Cleaning up spam takes time. Akismet has saved you 1 minute!' ); + // $notices[] = array( 'type' => 'plugin' ); + // $notices[] = array( 'type' => 'notice', 'notice_header' => 'This is the notice header.', 'notice_text' => 'This is the notice text.' ); + // $notices[] = array( 'type' => 'missing-functions' ); + // $notices[] = array( 'type' => 'servers-be-down' ); + // $notices[] = array( 'type' => Akismet::USER_STATUS_CANCELLED ); + // $notices[] = array( 'type' => Akismet::USER_STATUS_SUSPENDED ); + // $notices[] = array( 'type' => Akismet::USER_STATUS_MISSING ); + // $notices[] = array( 'type' => Akismet::USER_STATUS_NO_SUB ); + // $notices[] = array( 'type' => 'new-key-valid' ); + // $notices[] = array( 'type' => 'new-key-invalid' ); + // $notices[] = array( 'type' => 'existing-key-invalid' ); + // $notices[] = array( 'type' => 'new-key-failed' ); + // $notices[] = array( 'type' => 'usage-limit', 'api_calls' => '15000', 'usage_limit' => '10000', 'upgrade_plan' => 'Enterprise', 'upgrade_url' => 'https://akismet.com/account/', 'code' => 10502 ); + // $notices[] = array( 'type' => 'usage-limit', 'api_calls' => '15000', 'usage_limit' => '10000', 'upgrade_type' => 'qty', 'upgrade_plan' => 'Business', 'upgrade_url' => 'https://akismet.com/account/', 'code' => 10504, 'recommended_plan_name' => 'Akismet Pro (500)' ); + // $notices[] = array( 'type' => 'usage-limit', 'api_calls' => '15000', 'usage_limit' => '10000', 'upgrade_type' => 'qty', 'upgrade_plan' => 'Business', 'upgrade_url' => 'https://akismet.com/pricing/', 'code' => 10508 ); + // $notices[] = array( 'type' => 'spam-check', 'link_text' => 'Link text.' ); + // $notices[] = array( 'type' => 'spam-check-cron-disabled' ); + // $notices[] = array( 'type' => 'alert', 'code' => 123 ); + // $notices[] = array( 'type' => 'alert', 'code' => Akismet::ALERT_CODE_COMMERCIAL ); + + Akismet::log( compact( 'stat_totals', 'akismet_user' ) ); + Akismet::view( 'config', compact( 'api_key', 'akismet_user', 'stat_totals', 'notices' ) ); + } + + public static function display_notice() { + global $hook_suffix; + + if ( in_array( $hook_suffix, array( 'jetpack_page_akismet-key-config', 'settings_page_akismet-key-config' ) ) ) { + // This page manages the notices and puts them inline where they make sense. + return; + } + + // To see notice variants while testing. + // Akismet::view( 'notice', array( 'type' => 'spam-check-cron-disabled' ) ); + // Akismet::view( 'notice', array( 'type' => 'spam-check' ) ); + // Akismet::view( 'notice', array( 'type' => 'alert', 'code' => 123, 'msg' => 'Message' ) ); + // Akismet::view( 'notice', array( 'type' => 'usage-limit', 'api_calls' => '15000', 'usage_limit' => '10000', 'upgrade_plan' => 'Enterprise', 'upgrade_url' => 'https://akismet.com/account/', 'code' => 10502 ) ); + // Akismet::view( 'notice', array( 'type' => 'usage-limit', 'api_calls' => '15000', 'usage_limit' => '10000', 'upgrade_type' => 'qty', 'upgrade_plan' => 'Business', 'upgrade_url' => 'https://akismet.com/account/', 'code' => 10504, 'recommended_plan_name' => 'Akismet Pro (500)' ) ); + // Akismet::view( 'notice', array( 'type' => 'usage-limit', 'api_calls' => '15000', 'usage_limit' => '10000', 'upgrade_type' => 'qty', 'upgrade_plan' => 'Business', 'upgrade_url' => 'https://akismet.com/pricing/', 'code' => 10508 ) ); + + if ( in_array( $hook_suffix, array( 'edit-comments.php' ) ) && (int) get_option( 'akismet_alert_code' ) > 0 ) { + Akismet::verify_key( Akismet::get_api_key() ); // verify that the key is still in alert state + + $alert_code = get_option( 'akismet_alert_code' ); + if ( isset( Akismet::$limit_notices[ $alert_code ] ) ) { + self::display_usage_limit_alert(); + } elseif ( $alert_code > 0 ) { + self::display_alert(); + } + } elseif ( in_array( $hook_suffix, self::$activation_banner_pages, true ) && ! Akismet::get_api_key() ) { + // Show the "Set Up Akismet" banner on the comments and plugin pages if no API key has been set. + self::display_api_key_warning(); + } elseif ( $hook_suffix == 'edit-comments.php' && wp_next_scheduled( 'akismet_schedule_cron_recheck' ) ) { + self::display_spam_check_warning(); + } + + if ( isset( $_GET['akismet_recheck_complete'] ) ) { + $recheck_count = (int) $_GET['recheck_count']; + $spam_count = (int) $_GET['spam_count']; + + if ( $recheck_count === 0 ) { + $message = __( 'There were no comments to check. Akismet will only check comments awaiting moderation.', 'akismet' ); + } else { + /* translators: %s: Number of comments. */ + $message = sprintf( _n( 'Akismet checked %s comment.', 'Akismet checked %s comments.', $recheck_count, 'akismet' ), number_format( $recheck_count ) ); + $message .= ' '; + + if ( $spam_count === 0 ) { + $message .= __( 'No comments were caught as spam.', 'akismet' ); + } else { + /* translators: %s: Number of comments. */ + $message .= sprintf( _n( '%s comment was caught as spam.', '%s comments were caught as spam.', $spam_count, 'akismet' ), number_format( $spam_count ) ); + } + } + + echo '

' . esc_html( $message ) . '

'; + } elseif ( isset( $_GET['akismet_recheck_error'] ) ) { + echo '

' . esc_html( __( 'Akismet could not recheck your comments for spam.', 'akismet' ) ) . '

'; + } + } + + public static function display_status() { + if ( ! self::get_server_connectivity() ) { + Akismet::view( 'notice', array( 'type' => 'servers-be-down' ) ); + } elseif ( ! empty( self::$notices ) ) { + foreach ( self::$notices as $index => $type ) { + if ( is_object( $type ) ) { + $notice_header = $notice_text = ''; + + if ( property_exists( $type, 'notice_header' ) ) { + $notice_header = wp_kses( $type->notice_header, self::$allowed ); + } + + if ( property_exists( $type, 'notice_text' ) ) { + $notice_text = wp_kses( $type->notice_text, self::$allowed ); + } + + if ( property_exists( $type, 'status' ) ) { + $type = wp_kses( $type->status, self::$allowed ); + Akismet::view( 'notice', compact( 'type', 'notice_header', 'notice_text' ) ); + + unset( self::$notices[ $index ] ); + } + } else { + Akismet::view( 'notice', compact( 'type' ) ); + + unset( self::$notices[ $index ] ); + } + } + } + } + + /** + * Gets a specific notice by key. + * + * @param $key + * @return mixed + */ + private static function get_notice_by_key( $key ) { + return self::$notices[ $key ] ?? null; + } + + /** + * Gets a Jetpack user. + * + * @return array|false + */ + private static function get_jetpack_user() { + if ( ! self::is_jetpack_active() ) { + return false; + } + + if ( defined( 'JETPACK__VERSION' ) && version_compare( JETPACK__VERSION, '7.7', '<' ) ) { + // For version of Jetpack prior to 7.7. + Jetpack::load_xml_rpc_client(); + } + + $xml = new Jetpack_IXR_ClientMulticall( array( 'user_id' => get_current_user_id() ) ); + + $xml->addCall( 'wpcom.getUserID' ); + $xml->addCall( 'akismet.getAPIKey' ); + $xml->query(); + + Akismet::log( compact( 'xml' ) ); + + if ( ! $xml->isError() ) { + $responses = $xml->getResponse(); + if ( ( is_countable( $responses ) ? count( $responses ) : 0 ) > 1 ) { + // Due to a quirk in how Jetpack does multi-calls, the response order + // can't be trusted to match the call order. It's a good thing our + // return values can be mostly differentiated from each other. + $first_response_value = array_shift( $responses[0] ); + $second_response_value = array_shift( $responses[1] ); + + // If WPCOM ever reaches 100 billion users, this will fail. :-) + if ( preg_match( '/^[a-f0-9]{12}$/i', $first_response_value ) ) { + $api_key = $first_response_value; + $user_id = (int) $second_response_value; + } else { + $api_key = $second_response_value; + $user_id = (int) $first_response_value; + } + + return compact( 'api_key', 'user_id' ); + } + } + return false; + } + + /** + * Some commentmeta isn't useful in an export file. Suppress it (when supported). + * + * @param bool $exclude + * @param string $key The meta key + * @param object $meta The meta object + * @return bool Whether to exclude this meta entry from the export. + */ + public static function exclude_commentmeta_from_export( $exclude, $key, $meta ) { + if ( + in_array( + $key, + array( + 'akismet_as_submitted', + 'akismet_delay_moderation_email', + 'akismet_delayed_moderation_email', + 'akismet_rechecking', + 'akismet_schedule_approval_fallback', + 'akismet_schedule_email_fallback', + 'akismet_skipped_microtime', + ) + ) + ) { + return true; + } + + return $exclude; + } + + /** + * When Akismet is active, remove the "Activate Akismet" step from the plugin description. + */ + public static function modify_plugin_description( $all_plugins ) { + if ( isset( $all_plugins['akismet/akismet.php'] ) ) { + if ( Akismet::get_api_key() ) { + $all_plugins['akismet/akismet.php']['Description'] = __( 'Used by millions, Akismet is quite possibly the best way in the world to protect your blog from spam. Your site is fully configured and being protected, even while you sleep.', 'akismet' ); + } else { + $all_plugins['akismet/akismet.php']['Description'] = __( 'Used by millions, Akismet is quite possibly the best way in the world to protect your blog from spam. It keeps your site protected even while you sleep. To get started, just go to your Akismet Settings page to set up your API key.', 'akismet' ); + } + } + + return $all_plugins; + } + + private static function set_form_privacy_notice_option( $state ) { + if ( in_array( $state, array( 'display', 'hide' ) ) ) { + update_option( 'akismet_comment_form_privacy_notice', $state ); + } + } + + public static function register_personal_data_eraser( $erasers ) { + $erasers['akismet'] = array( + 'eraser_friendly_name' => __( 'Akismet', 'akismet' ), + 'callback' => array( 'Akismet_Admin', 'erase_personal_data' ), + ); + + return $erasers; + } + + /** + * When a user requests that their personal data be removed, Akismet has a duty to discard + * any personal data we store outside of the comment itself. Right now, that is limited + * to the copy of the comment we store in the akismet_as_submitted commentmeta. + * + * FWIW, this information would be automatically deleted after 15 days. + * + * @param $email_address string The email address of the user who has requested erasure. + * @param $page int This function can (and will) be called multiple times to prevent timeouts, + * so this argument is used for pagination. + * @return array + * @see https://developer.wordpress.org/plugins/privacy/adding-the-personal-data-eraser-to-your-plugin/ + */ + public static function erase_personal_data( $email_address, $page = 1 ) { + $items_removed = false; + + $number = 50; + $page = (int) $page; + + $comments = get_comments( + array( + 'author_email' => $email_address, + 'number' => $number, + 'paged' => $page, + 'order_by' => 'comment_ID', + 'order' => 'ASC', + ) + ); + + foreach ( (array) $comments as $comment ) { + $comment_as_submitted = get_comment_meta( $comment->comment_ID, 'akismet_as_submitted', true ); + + if ( $comment_as_submitted ) { + delete_comment_meta( $comment->comment_ID, 'akismet_as_submitted' ); + $items_removed = true; + } + } + + // Tell core if we have more comments to work on still + $done = ( is_countable( $comments ) ? count( $comments ) : 0 ) < $number; + + return array( + 'items_removed' => $items_removed, + 'items_retained' => false, // always false in this example + 'messages' => array(), // no messages in this example + 'done' => $done, + ); + } + + /** + * Return an array of HTML elements that are allowed in a notice. + * + * @return array + */ + public static function get_notice_kses_allowed_elements() { + return self::$allowed; + } + + /** + * Return a version to append to the URL of an asset file (e.g. CSS and images). + * + * @param string $relative_path Relative path to asset file + * @return string + */ + public static function get_asset_file_version( $relative_path ) { + + $full_path = AKISMET__PLUGIN_DIR . $relative_path; + + // If the AKISMET_VERSION contains a lower-case letter, it's a development version (e.g. 5.3.1a2). + // Use the file modified time in development. + if ( preg_match( '/[a-z]/', AKISMET_VERSION ) && file_exists( $full_path ) ) { + return filemtime( $full_path ); + } + + // Otherwise, use the AKISMET_VERSION. + return AKISMET_VERSION; + } + + /** + * Return inline CSS for Akismet admin. + * + * @return string + */ + protected static function get_inline_css(): string { + global $hook_suffix; + + // Hide excess compatible plugins when there are lots. + $inline_css = ' + .akismet-compatible-plugins__card:nth-child(n+' . esc_attr( Akismet_Compatible_Plugins::DEFAULT_VISIBLE_PLUGIN_COUNT + 1 ) . ') { + display: none; + } + + .akismet-compatible-plugins__list.is-expanded .akismet-compatible-plugins__card:nth-child(n+' . esc_attr( Akismet_Compatible_Plugins::DEFAULT_VISIBLE_PLUGIN_COUNT + 1 ) . ') { + display: flex; + } + '; + + // Enqueue the Akismet activation banner background separately so we can + // include the right path to the image. Shown on edit-comments.php and plugins.php. + if ( in_array( $hook_suffix, self::$activation_banner_pages, true ) ) { + $activation_banner_url = esc_url( + plugin_dir_url( __FILE__ ) . '_inc/img/akismet-activation-banner-elements.png' + ); + $inline_css .= '.akismet-activate {' . PHP_EOL . + 'background-image: url(' . $activation_banner_url . ');' . PHP_EOL . + '}'; + } + + return $inline_css; + } +} diff --git a/wp-content/plugins/akismet/class.akismet-cli.php b/wp-content/plugins/akismet/class.akismet-cli.php new file mode 100644 index 0000000..e623cd5 --- /dev/null +++ b/wp-content/plugins/akismet/class.akismet-cli.php @@ -0,0 +1,186 @@ +... + * : The ID(s) of the comment(s) to check. + * + * [--noaction] + * : Don't change the status of the comment. Just report what Akismet thinks it is. + * + * ## EXAMPLES + * + * wp akismet check 12345 + * + * @alias comment-check + */ + public function check( $args, $assoc_args ) { + foreach ( $args as $comment_id ) { + if ( isset( $assoc_args['noaction'] ) ) { + // Check the comment, but don't reclassify it. + $api_response = Akismet::check_db_comment( $comment_id, 'wp-cli' ); + } else { + $api_response = Akismet::recheck_comment( $comment_id, 'wp-cli' ); + } + + if ( 'true' === $api_response ) { + /* translators: %d: Comment ID. */ + WP_CLI::line( sprintf( __( 'Comment #%d is spam.', 'akismet' ), $comment_id ) ); + } elseif ( 'false' === $api_response ) { + /* translators: %d: Comment ID. */ + WP_CLI::line( sprintf( __( 'Comment #%d is not spam.', 'akismet' ), $comment_id ) ); + } elseif ( false === $api_response ) { + /* translators: %d: Comment ID. */ + WP_CLI::error( __( 'Failed to connect to Akismet.', 'akismet' ) ); + } elseif ( is_wp_error( $api_response ) ) { + /* translators: %d: Comment ID. */ + WP_CLI::warning( sprintf( __( 'Comment #%d could not be checked.', 'akismet' ), $comment_id ) ); + } + } + } + + /** + * Recheck all comments in the Pending queue. + * + * ## EXAMPLES + * + * wp akismet recheck_queue + * + * @alias recheck-queue + */ + public function recheck_queue() { + $batch_size = 100; + $start = 0; + + $total_counts = array(); + + do { + $result_counts = Akismet_Admin::recheck_queue_portion( $start, $batch_size ); + + if ( $result_counts['processed'] > 0 ) { + foreach ( $result_counts as $key => $count ) { + if ( ! isset( $total_counts[ $key ] ) ) { + $total_counts[ $key ] = $count; + } else { + $total_counts[ $key ] += $count; + } + } + $start += $batch_size; + $start -= $result_counts['spam']; // These comments will have been removed from the queue. + } + } while ( $result_counts['processed'] > 0 ); + + /* translators: %d: Number of comments. */ + WP_CLI::line( sprintf( _n( 'Processed %d comment.', 'Processed %d comments.', $total_counts['processed'], 'akismet' ), number_format( $total_counts['processed'] ) ) ); + + /* translators: %d: Number of comments. */ + WP_CLI::line( sprintf( _n( '%d comment moved to Spam.', '%d comments moved to Spam.', $total_counts['spam'], 'akismet' ), number_format( $total_counts['spam'] ) ) ); + + if ( $total_counts['error'] ) { + /* translators: %d: Number of comments. */ + WP_CLI::line( sprintf( _n( '%d comment could not be checked.', '%d comments could not be checked.', $total_counts['error'], 'akismet' ), number_format( $total_counts['error'] ) ) ); + } + } + + /** + * Fetches stats from the Akismet API. + * + * ## OPTIONS + * + * [] + * : The time period for which to retrieve stats. + * --- + * default: all + * options: + * - days + * - months + * - all + * --- + * + * [--format=] + * : Allows overriding the output of the command when listing connections. + * --- + * default: table + * options: + * - table + * - json + * - csv + * - yaml + * - count + * --- + * + * [--summary] + * : When set, will display a summary of the stats. + * + * ## EXAMPLES + * + * wp akismet stats + * wp akismet stats all + * wp akismet stats days + * wp akismet stats months + * wp akismet stats all --summary + */ + public function stats( $args, $assoc_args ) { + $api_key = Akismet::get_api_key(); + + if ( empty( $api_key ) ) { + WP_CLI::error( __( 'API key must be set to fetch stats.', 'akismet' ) ); + } + + switch ( $args[0] ) { + case 'days': + $interval = '60-days'; + break; + case 'months': + $interval = '6-months'; + break; + default: + $interval = 'all'; + break; + } + + $request_args = array( + 'blog' => get_option( 'home' ), + 'key' => $api_key, + 'from' => $interval, + ); + + $request_args = apply_filters( 'akismet_request_args', $request_args, 'get-stats' ); + + $response = Akismet::http_post( Akismet::build_query( $request_args ), 'get-stats' ); + + if ( empty( $response[1] ) ) { + WP_CLI::error( __( 'Currently unable to fetch stats. Please try again.', 'akismet' ) ); + } + + $response_body = json_decode( $response[1], true ); + + if ( is_null( $response_body ) ) { + WP_CLI::error( __( 'Stats response could not be decoded.', 'akismet' ) ); + } + + if ( isset( $assoc_args['summary'] ) ) { + $keys = array( + 'spam', + 'ham', + 'missed_spam', + 'false_positives', + 'accuracy', + 'time_saved', + ); + + WP_CLI\Utils\format_items( $assoc_args['format'], array( $response_body ), $keys ); + } else { + $stats = $response_body['breakdown']; + WP_CLI\Utils\format_items( $assoc_args['format'], $stats, array_keys( end( $stats ) ) ); + } + } +} diff --git a/wp-content/plugins/akismet/class.akismet-rest-api.php b/wp-content/plugins/akismet/class.akismet-rest-api.php new file mode 100644 index 0000000..328bdb6 --- /dev/null +++ b/wp-content/plugins/akismet/class.akismet-rest-api.php @@ -0,0 +1,623 @@ + WP_REST_Server::READABLE, + 'permission_callback' => array( 'Akismet_REST_API', 'privileged_permission_callback' ), + 'callback' => array( 'Akismet_REST_API', 'get_key' ), + ), + array( + 'methods' => WP_REST_Server::EDITABLE, + 'permission_callback' => array( 'Akismet_REST_API', 'privileged_permission_callback' ), + 'callback' => array( 'Akismet_REST_API', 'set_key' ), + 'args' => array( + 'key' => array( + 'required' => true, + 'type' => 'string', + 'sanitize_callback' => array( 'Akismet_REST_API', 'sanitize_key' ), + 'description' => __( 'A 12-character Akismet API key. Available at akismet.com/account', 'akismet' ), + ), + ), + ), + array( + 'methods' => WP_REST_Server::DELETABLE, + 'permission_callback' => array( 'Akismet_REST_API', 'privileged_permission_callback' ), + 'callback' => array( 'Akismet_REST_API', 'delete_key' ), + ), + ) + ); + + register_rest_route( + 'akismet/v1', + '/settings/', + array( + array( + 'methods' => WP_REST_Server::READABLE, + 'permission_callback' => array( 'Akismet_REST_API', 'privileged_permission_callback' ), + 'callback' => array( 'Akismet_REST_API', 'get_settings' ), + ), + array( + 'methods' => WP_REST_Server::EDITABLE, + 'permission_callback' => array( 'Akismet_REST_API', 'privileged_permission_callback' ), + 'callback' => array( 'Akismet_REST_API', 'set_boolean_settings' ), + 'args' => array( + 'akismet_strictness' => array( + 'required' => false, + 'type' => 'boolean', + 'description' => __( 'If true, Akismet will automatically discard the worst spam automatically rather than putting it in the spam folder.', 'akismet' ), + ), + 'akismet_show_user_comments_approved' => array( + 'required' => false, + 'type' => 'boolean', + 'description' => __( 'If true, show the number of approved comments beside each comment author in the comments list page.', 'akismet' ), + ), + 'akismet_enable_mcp_access' => array( + 'required' => false, + 'type' => 'boolean', + 'description' => __( 'If true, allow MCP clients to access Akismet data and functionality.', 'akismet' ), + ), + ), + ), + ) + ); + + register_rest_route( + 'akismet/v1', + '/stats', + array( + 'methods' => WP_REST_Server::READABLE, + 'permission_callback' => array( 'Akismet_REST_API', 'privileged_permission_callback' ), + 'callback' => array( 'Akismet_REST_API', 'get_stats' ), + 'args' => array( + 'interval' => array( + 'required' => false, + 'type' => 'string', + 'sanitize_callback' => array( 'Akismet_REST_API', 'sanitize_interval' ), + 'description' => __( 'The time period for which to retrieve stats. Options: 60-days, 6-months, all', 'akismet' ), + 'default' => 'all', + ), + ), + ) + ); + + register_rest_route( + 'akismet/v1', + '/stats/(?P[\w+])', + array( + 'args' => array( + 'interval' => array( + 'description' => __( 'The time period for which to retrieve stats. Options: 60-days, 6-months, all', 'akismet' ), + 'type' => 'string', + ), + ), + array( + 'methods' => WP_REST_Server::READABLE, + 'permission_callback' => array( 'Akismet_REST_API', 'privileged_permission_callback' ), + 'callback' => array( 'Akismet_REST_API', 'get_stats' ), + ), + ) + ); + + register_rest_route( + 'akismet/v1', + '/alert', + array( + array( + 'methods' => WP_REST_Server::READABLE, + 'permission_callback' => array( 'Akismet_REST_API', 'remote_call_permission_callback' ), + 'callback' => array( 'Akismet_REST_API', 'get_alert' ), + 'args' => array( + 'key' => array( + 'required' => false, + 'type' => 'string', + 'sanitize_callback' => array( 'Akismet_REST_API', 'sanitize_key' ), + 'description' => __( 'A 12-character Akismet API key. Available at akismet.com/account', 'akismet' ), + ), + ), + ), + array( + 'methods' => WP_REST_Server::EDITABLE, + 'permission_callback' => array( 'Akismet_REST_API', 'remote_call_permission_callback' ), + 'callback' => array( 'Akismet_REST_API', 'set_alert' ), + 'args' => array( + 'key' => array( + 'required' => false, + 'type' => 'string', + 'sanitize_callback' => array( 'Akismet_REST_API', 'sanitize_key' ), + 'description' => __( 'A 12-character Akismet API key. Available at akismet.com/account', 'akismet' ), + ), + ), + ), + array( + 'methods' => WP_REST_Server::DELETABLE, + 'permission_callback' => array( 'Akismet_REST_API', 'remote_call_permission_callback' ), + 'callback' => array( 'Akismet_REST_API', 'delete_alert' ), + 'args' => array( + 'key' => array( + 'required' => false, + 'type' => 'string', + 'sanitize_callback' => array( 'Akismet_REST_API', 'sanitize_key' ), + 'description' => __( 'A 12-character Akismet API key. Available at akismet.com/account', 'akismet' ), + ), + ), + ), + ) + ); + + register_rest_route( + 'akismet/v1', + '/webhook', + array( + 'methods' => WP_REST_Server::CREATABLE, + 'callback' => array( 'Akismet_REST_API', 'receive_webhook' ), + 'permission_callback' => array( 'Akismet_REST_API', 'remote_call_permission_callback' ), + ) + ); + } + + /** + * Get the current Akismet API key. + * + * @param WP_REST_Request $request + * @return WP_Error|WP_REST_Response + */ + public static function get_key( $request = null ) { + return rest_ensure_response( Akismet::get_api_key() ); + } + + /** + * Set the API key, if possible. + * + * @param WP_REST_Request $request + * @return WP_Error|WP_REST_Response + */ + public static function set_key( $request ) { + if ( defined( 'WPCOM_API_KEY' ) ) { + return rest_ensure_response( new WP_Error( 'hardcoded_key', __( 'This site\'s API key is hardcoded and cannot be changed via the API.', 'akismet' ), array( 'status' => 409 ) ) ); + } + + $new_api_key = $request->get_param( 'key' ); + + if ( ! self::key_is_valid( $new_api_key ) ) { + return rest_ensure_response( new WP_Error( 'invalid_key', __( 'The value provided is not a valid and registered API key.', 'akismet' ), array( 'status' => 400 ) ) ); + } + + update_option( 'wordpress_api_key', $new_api_key ); + + return self::get_key(); + } + + /** + * Unset the API key, if possible. + * + * @param WP_REST_Request $request + * @return WP_Error|WP_REST_Response + */ + public static function delete_key( $request ) { + if ( defined( 'WPCOM_API_KEY' ) ) { + return rest_ensure_response( new WP_Error( 'hardcoded_key', __( 'This site\'s API key is hardcoded and cannot be deleted.', 'akismet' ), array( 'status' => 409 ) ) ); + } + + delete_option( 'wordpress_api_key' ); + + return rest_ensure_response( true ); + } + + /** + * Get the Akismet settings. + * + * @param WP_REST_Request $request + * @return WP_Error|WP_REST_Response + */ + public static function get_settings( $request = null ) { + return rest_ensure_response( + array( + 'akismet_strictness' => ( get_option( 'akismet_strictness', '1' ) === '1' ), + 'akismet_show_user_comments_approved' => ( get_option( 'akismet_show_user_comments_approved', '1' ) === '1' ), + 'akismet_enable_mcp_access' => ( get_option( 'akismet_enable_mcp_access', '0' ) === '1' ), + ) + ); + } + + /** + * Update the Akismet settings. + * + * @param WP_REST_Request $request + * @return WP_Error|WP_REST_Response + */ + public static function set_boolean_settings( $request ) { + foreach ( array( + 'akismet_strictness', + 'akismet_show_user_comments_approved', + 'akismet_enable_mcp_access', + ) as $setting_key ) { + + $setting_value = $request->get_param( $setting_key ); + if ( is_null( $setting_value ) ) { + // This setting was not specified. + continue; + } + + // From 4.7+, WP core will ensure that these are always boolean + // values because they are registered with 'type' => 'boolean', + // but we need to do this ourselves for prior versions. + $setting_value = self::parse_boolean( $setting_value ); + + update_option( $setting_key, $setting_value ? '1' : '0' ); + } + + return self::get_settings(); + } + + /** + * Parse a numeric or string boolean value into a boolean. + * + * @param mixed $value The value to convert into a boolean. + * @return bool The converted value. + */ + public static function parse_boolean( $value ) { + switch ( $value ) { + case true: + case 'true': + case '1': + case 1: + return true; + + case false: + case 'false': + case '0': + case 0: + return false; + + default: + return (bool) $value; + } + } + + /** + * Get the Akismet stats for a given time period. + * + * Possible `interval` values: + * - all + * - 60-days + * - 6-months + * + * @param WP_REST_Request $request + * @return WP_Error|WP_REST_Response + */ + public static function get_stats( $request ) { + $api_key = Akismet::get_api_key(); + + $interval = $request->get_param( 'interval' ); + + $stat_totals = array(); + + $request_args = array( + 'blog' => get_option( 'home' ), + 'key' => $api_key, + 'from' => $interval, + ); + + $request_args = apply_filters( 'akismet_request_args', $request_args, 'get-stats' ); + + $response = Akismet::http_post( Akismet::build_query( $request_args ), 'get-stats' ); + + if ( ! empty( $response[1] ) ) { + $stat_totals[ $interval ] = json_decode( $response[1] ); + } + + return rest_ensure_response( $stat_totals ); + } + + /** + * Get the current alert code and message. Alert codes are used to notify the site owner + * if there's a problem, like a connection issue between their site and the Akismet API, + * invalid requests being sent, etc. + * + * @param WP_REST_Request $request + * @return WP_Error|WP_REST_Response + */ + public static function get_alert( $request ) { + return rest_ensure_response( + array( + 'code' => get_option( 'akismet_alert_code' ), + 'message' => get_option( 'akismet_alert_msg' ), + ) + ); + } + + /** + * Update the current alert code and message by triggering a call to the Akismet server. + * + * @param WP_REST_Request $request + * @return WP_Error|WP_REST_Response + */ + public static function set_alert( $request ) { + delete_option( 'akismet_alert_code' ); + delete_option( 'akismet_alert_msg' ); + + // Make a request so the most recent alert code and message are retrieved. + Akismet::verify_key( Akismet::get_api_key() ); + + return self::get_alert( $request ); + } + + /** + * Clear the current alert code and message. + * + * @param WP_REST_Request $request + * @return WP_Error|WP_REST_Response + */ + public static function delete_alert( $request ) { + delete_option( 'akismet_alert_code' ); + delete_option( 'akismet_alert_msg' ); + + return self::get_alert( $request ); + } + + private static function key_is_valid( $key ) { + $request_args = array( + 'key' => $key, + 'blog' => get_option( 'home' ), + ); + + $request_args = apply_filters( 'akismet_request_args', $request_args, 'verify-key' ); + + $response = Akismet::http_post( Akismet::build_query( $request_args ), 'verify-key' ); + + if ( $response[1] == 'valid' ) { + return true; + } + + return false; + } + + public static function privileged_permission_callback() { + return current_user_can( 'manage_options' ); + } + + /** + * For calls that Akismet.com makes to the site to clear outdated alert codes, use the API key for authorization. + */ + public static function remote_call_permission_callback( $request ) { + $local_key = Akismet::get_api_key(); + + return $local_key && ( strtolower( $request->get_param( 'key' ) ?? '' ) === strtolower( $local_key ) ); + } + + public static function sanitize_interval( $interval, $request, $param ) { + $interval = trim( $interval ); + + $valid_intervals = array( '60-days', '6-months', 'all' ); + + if ( ! in_array( $interval, $valid_intervals ) ) { + $interval = 'all'; + } + + return $interval; + } + + public static function sanitize_key( $key, $request, $param ) { + return trim( $key ); + } + + /** + * Process a webhook request from the Akismet servers. + * + * @param WP_REST_Request $request + * @return WP_Error|WP_REST_Response + */ + public static function receive_webhook( $request ) { + Akismet::log( array( 'Webhook request received', $request->get_body() ) ); + + /** + * The request body should look like this: + * array( + * 'key' => '1234567890abcd', + * 'endpoint' => '[comment-check|submit-ham|submit-spam]', + * 'comments' => array( + * array( + * 'guid' => '[...]', + * 'result' => '[true|false]', + * 'comment_author' => '[...]', + * [...] + * ), + * array( + * 'guid' => '[...]', + * [...], + * ), + * [...] + * ) + * ) + * + * Multiple comments can be included in each request, and the only truly required + * field for each is the guid, although it would be friendly to include also + * comment_post_ID, comment_parent, and comment_author_email, if possible to make + * searching easier. + */ + + // The response will include statuses for the result of each comment that was supplied. + $response = array( + 'comments' => array(), + ); + + $endpoint = $request->get_param( 'endpoint' ); + + switch ( $endpoint ) { + case 'comment-check': + $webhook_comments = $request->get_param( 'comments' ); + + if ( ! is_array( $webhook_comments ) ) { + return rest_ensure_response( new WP_Error( 'malformed_request', __( 'The \'comments\' parameter must be an array.', 'akismet' ), array( 'status' => 400 ) ) ); + } + + foreach ( $webhook_comments as $webhook_comment ) { + $guid = $webhook_comment['guid']; + + if ( ! $guid ) { + // Without the GUID, we can't be sure that we're matching the right comment. + // We'll make it a rule that any comment without a GUID is ignored intentionally. + continue; + } + + // Search on the fields that are indexed in the comments table, plus the GUID. + // The GUID is the only thing we really need to search on, but comment_meta + // is not indexed in a useful way if there are many many comments. This + // should help narrow it down first. + $queryable_fields = array( + 'comment_post_ID' => 'post_id', + 'comment_parent' => 'parent', + 'comment_author_email' => 'author_email', + ); + + $query_args = array(); + $query_args['status'] = 'any'; + $query_args['meta_key'] = 'akismet_guid'; + $query_args['meta_value'] = $guid; + + foreach ( $queryable_fields as $queryable_field => $wp_comment_query_field ) { + if ( isset( $webhook_comment[ $queryable_field ] ) ) { + $query_args[ $wp_comment_query_field ] = $webhook_comment[ $queryable_field ]; + } + } + + $comments_query = new WP_Comment_Query( $query_args ); + $comments = $comments_query->comments; + + if ( ! $comments ) { + // Unexpected, although the comment could have been deleted since being submitted. + Akismet::log( 'Webhook failed: no matching comment found.' ); + + $response['comments'][ $guid ] = array( + 'status' => 'error', + 'message' => __( 'Could not find matching comment.', 'akismet' ), + ); + + continue; + } if ( count( $comments ) > 1 ) { + // Two comments shouldn't be able to match the same GUID. + Akismet::log( 'Webhook failed: multiple matching comments found.', $comments ); + + $response['comments'][ $guid ] = array( + 'status' => 'error', + 'message' => __( 'Multiple comments matched request.', 'akismet' ), + ); + + continue; + } else { + // We have one single match, as hoped for. + Akismet::log( 'Found matching comment.', $comments ); + + $comment = $comments[0]; + + $current_status = wp_get_comment_status( $comment ); + + $result = $webhook_comment['result']; + + if ( 'true' == $result ) { + Akismet::log( 'Comment should be spam' ); + + // The comment should be classified as spam. + if ( 'spam' != $current_status ) { + // The comment is not classified as spam. If Akismet was the one to act on it, move it to spam. + if ( Akismet::last_comment_status_change_came_from_akismet( $comment->comment_ID ) ) { + Akismet::log( 'Comment is not spam; marking as spam.' ); + + wp_spam_comment( $comment ); + Akismet::update_comment_history( $comment->comment_ID, '', 'webhook-spam' ); + } else { + Akismet::log( 'Comment is not spam, but it has already been manually handled by some other process.' ); + Akismet::update_comment_history( $comment->comment_ID, '', 'webhook-spam-noaction' ); + } + } + } elseif ( 'false' == $result ) { + Akismet::log( 'Comment should be ham' ); + + // The comment should be classified as ham. + if ( 'spam' == $current_status ) { + Akismet::log( 'Comment is spam.' ); + + // The comment is classified as spam. If Akismet was the one to label it as spam, unspam it. + if ( Akismet::last_comment_status_change_came_from_akismet( $comment->comment_ID ) ) { + Akismet::log( 'Akismet marked it as spam; unspamming.' ); + + wp_unspam_comment( $comment ); + + akismet::update_comment_history( $comment->comment_ID, '', 'webhook-ham' ); + } else { + Akismet::log( 'Comment is not spam, but it has already been manually handled by some other process.' ); + Akismet::update_comment_history( $comment->comment_ID, '', 'webhook-ham-noaction' ); + } + } else if ( 'unapproved' == $current_status ) { + Akismet::log( 'Comment is pending.' ); + + // The comment is in Pending. If Akismet was the one to put it there, approve it (but only if the site + // settings dictate that). + if ( Akismet::last_comment_status_change_came_from_akismet( $comment->comment_ID ) ) { + Akismet::log( 'Akismet marked it as Pending; approving.' ); + + if ( check_comment( $comment->comment_author, $comment->comment_author_email, $comment->comment_author_url, $comment->comment_content, $comment->comment_author_IP, $comment->comment_agent, $comment->comment_type ) ) { + wp_set_comment_status( $comment->comment_ID, 1 ); + } + + akismet::update_comment_history( $comment->comment_ID, '', 'webhook-ham' ); + } else { + Akismet::log( 'Comment is not spam, but it has already been manually handled by some other process.' ); + Akismet::update_comment_history( $comment->comment_ID, '', 'webhook-ham-noaction' ); + } + } + + $moderation_email_was_delayed = get_comment_meta( $comment->comment_ID, 'akismet_delayed_moderation_email', true ); + + if ( $moderation_email_was_delayed ) { + Akismet::log( 'Moderation email was delayed for comment #' . $comment->comment_ID . '; sending now.' ); + + delete_comment_meta( $comment->comment_ID, 'akismet_delayed_moderation_email' ); + wp_new_comment_notify_moderator( $comment->comment_ID ); + wp_new_comment_notify_postauthor( $comment->comment_ID ); + } + + delete_comment_meta( $comment->comment_ID, 'akismet_delay_moderation_email' ); + } + + $response['comments'][ $guid ] = array( 'status' => 'success' ); + } + } + + break; + case 'submit-ham': + case 'submit-spam': + // Nothing to do for submit-ham or submit-spam. + break; + default: + // Unsupported endpoint. + break; + } + + /** + * Allow plugins to do things with a successfully processed webhook request, like logging. + * + * @since 5.3.2 + * + * @param WP_REST_Request $request The REST request object. + */ + do_action( 'akismet_webhook_received', $request ); + + Akismet::log( 'Done processing webhook.' ); + + return rest_ensure_response( $response ); + } +} diff --git a/wp-content/plugins/akismet/class.akismet-widget.php b/wp-content/plugins/akismet/class.akismet-widget.php new file mode 100644 index 0000000..0634eed --- /dev/null +++ b/wp-content/plugins/akismet/class.akismet-widget.php @@ -0,0 +1,177 @@ + __( 'Display the number of spam comments Akismet has caught', 'akismet' ) ) + ); + } + + /** + * Outputs the widget settings form + * + * @param array $instance The widget options + */ + public function form( $instance ) { + if ( $instance && isset( $instance['title'] ) ) { + $title = $instance['title']; + } else { + $title = __( 'Spam Blocked', 'akismet' ); + } + ?> + +

+ + +

+ + + + + + + + 'FIRST_MONTH_OVER_LIMIT', + 10502 => 'SECOND_MONTH_OVER_LIMIT', + 10504 => 'THIRD_MONTH_APPROACHING_LIMIT', + 10508 => 'THIRD_MONTH_OVER_LIMIT', + 10516 => 'FOUR_PLUS_MONTHS_OVER_LIMIT', + ); + + private static $last_comment = ''; + private static $initiated = false; + private static $last_comment_result = null; + private static $comment_as_submitted_allowed_keys = array( + 'blog' => '', + 'blog_charset' => '', + 'blog_lang' => '', + 'blog_ua' => '', + 'comment_agent' => '', + 'comment_author' => '', + 'comment_author_IP' => '', + 'comment_author_email' => '', + 'comment_author_url' => '', + 'comment_content' => '', + 'comment_date_gmt' => '', + 'comment_tags' => '', + 'comment_type' => '', + 'guid' => '', + 'is_test' => '', + 'permalink' => '', + 'reporter' => '', + 'site_domain' => '', + 'submit_referer' => '', + 'submit_uri' => '', + 'user_ID' => '', + 'user_agent' => '', + 'user_id' => '', + 'user_ip' => '', + ); + + public static function init() { + if ( ! self::$initiated ) { + self::init_hooks(); + } + } + + /** + * Initializes WordPress hooks + */ + private static function init_hooks() { + self::$initiated = true; + + add_action( 'wp_insert_comment', array( 'Akismet', 'auto_check_update_meta' ), 10, 2 ); + add_action( 'wp_insert_comment', array( 'Akismet', 'schedule_email_fallback' ), 10, 2 ); + add_action( 'wp_insert_comment', array( 'Akismet', 'schedule_approval_fallback' ), 10, 2 ); + + add_filter( 'preprocess_comment', array( 'Akismet', 'auto_check_comment' ), 1 ); + add_filter( 'rest_pre_insert_comment', array( 'Akismet', 'rest_auto_check_comment' ), 1 ); + + add_action( 'comment_form', array( 'Akismet', 'load_form_js' ) ); + add_action( 'do_shortcode_tag', array( 'Akismet', 'load_form_js_via_filter' ), 10, 4 ); + + add_action( 'akismet_scheduled_delete', array( 'Akismet', 'delete_old_comments' ) ); + add_action( 'akismet_scheduled_delete', array( 'Akismet', 'delete_old_comments_meta' ) ); + add_action( 'akismet_scheduled_delete', array( 'Akismet', 'delete_orphaned_commentmeta' ) ); + add_action( 'akismet_schedule_cron_recheck', array( 'Akismet', 'cron_recheck' ) ); + + add_action( 'akismet_email_fallback', array( 'Akismet', 'email_fallback' ), 10, 3 ); + add_action( 'akismet_approval_fallback', array( 'Akismet', 'approval_fallback' ), 10, 3 ); + + add_action( 'comment_form', array( 'Akismet', 'add_comment_nonce' ), 1 ); + add_action( 'comment_form', array( 'Akismet', 'output_custom_form_fields' ) ); + add_filter( 'script_loader_tag', array( 'Akismet', 'set_form_js_async' ), 10, 3 ); + + add_filter( 'notify_moderator', array( 'Akismet', 'disable_emails_if_unreachable' ), 1000, 2 ); + add_filter( 'notify_post_author', array( 'Akismet', 'disable_emails_if_unreachable' ), 1000, 2 ); + + add_filter( 'pre_comment_approved', array( 'Akismet', 'last_comment_status' ), 10, 2 ); + + add_action( 'transition_comment_status', array( 'Akismet', 'transition_comment_status' ), 10, 3 ); + + // Run this early in the pingback call, before doing a remote fetch of the source uri + add_action( 'xmlrpc_call', array( 'Akismet', 'pre_check_pingback' ), 10, 3 ); + + // Jetpack compatibility + add_filter( 'jetpack_options_whitelist', array( 'Akismet', 'add_to_jetpack_options_whitelist' ) ); + add_filter( 'jetpack_contact_form_html', array( 'Akismet', 'inject_custom_form_fields' ) ); + add_filter( 'jetpack_contact_form_akismet_values', array( 'Akismet', 'prepare_custom_form_values' ) ); + + // Gravity Forms + add_filter( 'gform_get_form_filter', array( 'Akismet', 'inject_custom_form_fields' ) ); + add_filter( 'gform_akismet_fields', array( 'Akismet', 'prepare_custom_form_values' ) ); + + // Contact Form 7 + add_filter( 'wpcf7_form_elements', array( 'Akismet', 'append_custom_form_fields' ) ); + add_filter( 'wpcf7_akismet_parameters', array( 'Akismet', 'prepare_custom_form_values' ) ); + + // Formidable Forms + add_filter( 'frm_filter_final_form', array( 'Akismet', 'inject_custom_form_fields' ) ); + add_filter( 'frm_akismet_values', array( 'Akismet', 'prepare_custom_form_values' ) ); + + // Fluent Forms + /* + * The Fluent Forms hook names were updated in version 5.0.0. The last version that supported + * the original hook names was 4.3.25, and version 4.3.25 was tested up to WordPress version 6.1. + * + * The legacy hooks are fired before the new hooks. See + * https://github.com/fluentform/fluentform/commit/cc45341afcae400f217470a7bbfb15efdd80454f + * + * The legacy Fluent Forms hooks will be removed when Akismet no longer supports WordPress version 6.1. + * This will provide compatibility with previous versions of Fluent Forms for a reasonable amount of time. + */ + add_filter( 'fluentform_form_element_start', array( 'Akismet', 'output_custom_form_fields' ) ); + add_filter( 'fluentform_akismet_fields', array( 'Akismet', 'prepare_custom_form_values' ), 10, 2 ); + // Current Fluent Form hooks. + add_filter( 'fluentform/form_element_start', array( 'Akismet', 'output_custom_form_fields' ) ); + add_filter( 'fluentform/akismet_fields', array( 'Akismet', 'prepare_custom_form_values' ), 10, 2 ); + + add_action( 'update_option_wordpress_api_key', array( 'Akismet', 'updated_option' ), 10, 2 ); + add_action( 'add_option_wordpress_api_key', array( 'Akismet', 'added_option' ), 10, 2 ); + + add_action( 'comment_form_after', array( 'Akismet', 'display_comment_form_privacy_notice' ) ); + } + + public static function get_api_key() { + return apply_filters( 'akismet_get_api_key', defined( 'WPCOM_API_KEY' ) ? constant( 'WPCOM_API_KEY' ) : get_option( 'wordpress_api_key' ) ); + } + + /** + * Exchange the API key for a token that can only be used to access stats pages. + * + * @return string + */ + public static function get_access_token() { + static $access_token = null; + + if ( is_null( $access_token ) ) { + $request_args = array( 'api_key' => self::get_api_key() ); + + $request_args = apply_filters( 'akismet_request_args', $request_args, 'token' ); + + $response = self::http_post( self::build_query( $request_args ), 'token' ); + + $access_token = $response[1]; + } + + return $access_token; + } + + public static function check_key_status( $key, $ip = null ) { + $request_args = array( + 'key' => $key, + 'blog' => get_option( 'home' ), + ); + + $request_args = apply_filters( 'akismet_request_args', $request_args, 'verify-key' ); + + return self::http_post( self::build_query( $request_args ), 'verify-key', $ip ); + } + + public static function verify_key( $key, $ip = null ) { + // Shortcut for obviously invalid keys. + if ( strlen( $key ) != 12 ) { + return 'invalid'; + } + + $response = self::check_key_status( $key, $ip ); + + if ( $response[1] != 'valid' && $response[1] != 'invalid' ) { + return 'failed'; + } + + return $response[1]; + } + + public static function deactivate_key( $key ) { + $request_args = array( + 'key' => $key, + 'blog' => get_option( 'home' ), + ); + + $request_args = apply_filters( 'akismet_request_args', $request_args, 'deactivate' ); + + $response = self::http_post( self::build_query( $request_args ), 'deactivate' ); + + if ( $response[1] != 'deactivated' ) { + return 'failed'; + } + + return $response[1]; + } + + /** + * Get spam protection statistics from Akismet API. + * + * @param string $interval Time interval for stats: '6-months', 'all', or '60-days'. + * @param string $api_key Optional. API key to use. Defaults to stored key. + * @return object|false Stats data object on success, false on failure. + */ + public static function get_stats( $interval = '6-months', $api_key = null ) { + if ( is_null( $api_key ) ) { + $api_key = self::get_api_key(); + } + + if ( ! $api_key ) { + return false; + } + + $request_args = array( + 'blog' => get_option( 'home' ), + 'key' => $api_key, + 'from' => $interval, + ); + + $request_args = apply_filters( 'akismet_request_args', $request_args, 'get-stats' ); + + $response = self::http_post( self::build_query( $request_args ), 'get-stats' ); + + if ( empty( $response[1] ) ) { + return false; + } + + $data = json_decode( $response[1] ); + + if ( ! is_object( $data ) ) { + return false; + } + + // Ensure proper types for numeric fields. + if ( isset( $data->spam ) ) { + $data->spam = (int) $data->spam; + } + if ( isset( $data->ham ) ) { + $data->ham = (int) $data->ham; + } + if ( isset( $data->missed_spam ) ) { + $data->missed_spam = (int) $data->missed_spam; + } + if ( isset( $data->false_positives ) ) { + $data->false_positives = (int) $data->false_positives; + } + if ( isset( $data->accuracy ) ) { + $data->accuracy = (float) $data->accuracy; + } + if ( isset( $data->time_saved ) ) { + $data->time_saved = (int) $data->time_saved; + } + + // Ensure proper types for breakdown data. + if ( isset( $data->breakdown ) && is_object( $data->breakdown ) ) { + foreach ( $data->breakdown as $period => $stats ) { + if ( ! is_object( $stats ) ) { + continue; + } + + if ( isset( $stats->spam ) ) { + $stats->spam = (int) $stats->spam; + } + if ( isset( $stats->ham ) ) { + $stats->ham = (int) $stats->ham; + } + if ( isset( $stats->missed_spam ) ) { + $stats->missed_spam = (int) $stats->missed_spam; + } + if ( isset( $stats->false_positives ) ) { + $stats->false_positives = (int) $stats->false_positives; + } + } + } + + return $data; + } + + /** + * Check comment data for spam via Akismet API. + * + * @param array $comment_data Array of comment data to check. + * @param string $api_key Optional. API key to use. Defaults to stored key. + * @return object|false Result object on success, false on failure. + */ + public static function comment_check( $comment_data, $api_key = null ) { + if ( is_null( $api_key ) ) { + $api_key = self::get_api_key(); + } + + if ( ! $api_key ) { + return false; + } + + // Build the request array with required and optional fields. + $request = array_merge( + array( + 'blog' => get_option( 'home' ), + 'blog_lang' => get_locale(), + 'blog_charset' => get_option( 'blog_charset' ), + 'user_ip' => self::get_ip_address(), + 'user_agent' => self::get_user_agent(), + ), + $comment_data + ); + + $request = apply_filters( 'akismet_request_args', $request, 'comment-check' ); + + $response = self::http_post( self::build_query( $request ), 'comment-check' ); + + if ( empty( $response[1] ) ) { + return false; + } + + // Build result object. + $result = (object) array( + 'is_spam' => ( 'true' === $response[1] ), + ); + + // Include additional response headers if present. + if ( isset( $response[0]['x-akismet-pro-tip'] ) ) { + $result->pro_tip = $response[0]['x-akismet-pro-tip']; + } + + if ( isset( $response[0]['x-akismet-guid'] ) ) { + $result->guid = $response[0]['x-akismet-guid']; + } + + if ( isset( $response[0]['x-akismet-error'] ) ) { + $result->error = $response[0]['x-akismet-error']; + } + + if ( isset( $response[0]['x-akismet-debug-help'] ) ) { + $result->debug_help = $response[0]['x-akismet-debug-help']; + } + + return $result; + } + + /** + * Add the akismet option to the Jetpack options management whitelist. + * + * @param array $options The list of whitelisted option names. + * @return array The updated whitelist + */ + public static function add_to_jetpack_options_whitelist( $options ) { + $options[] = 'wordpress_api_key'; + return $options; + } + + /** + * When the akismet option is updated, run the registration call. + * + * This should only be run when the option is updated from the Jetpack/WP.com + * API call, and only if the new key is different than the old key. + * + * @param mixed $old_value The old option value. + * @param mixed $value The new option value. + */ + public static function updated_option( $old_value, $value ) { + // Not an API call + if ( ! class_exists( 'WPCOM_JSON_API_Update_Option_Endpoint' ) ) { + return; + } + // Only run the registration if the old key is different. + if ( $old_value !== $value ) { + self::verify_key( $value ); + } + } + + /** + * Treat the creation of an API key the same as updating the API key to a new value. + * + * @param mixed $option_name Will always be "wordpress_api_key", until something else hooks in here. + * @param mixed $value The option value. + */ + public static function added_option( $option_name, $value ) { + if ( 'wordpress_api_key' === $option_name ) { + return self::updated_option( '', $value ); + } + } + + public static function rest_auto_check_comment( $commentdata ) { + return self::auto_check_comment( $commentdata, 'rest_api' ); + } + + /** + * Check a comment for spam. + * + * @param array $commentdata + * @param string $context What kind of request triggered this comment check? Possible values are 'default', 'rest_api', and 'xml-rpc'. + * @return array|WP_Error Either the $commentdata array with additional entries related to its spam status + * or a WP_Error, if it's a REST API request and the comment should be discarded. + */ + public static function auto_check_comment( $commentdata, $context = 'default' ) { + // If no key is configured, then there's no point in doing any of this. + if ( ! self::get_api_key() ) { + return $commentdata; + } + + if ( ! isset( $commentdata['comment_meta'] ) ) { + $commentdata['comment_meta'] = array(); + } + + self::$last_comment_result = null; + + // Skip the Akismet check if the comment matches the Disallowed Keys list. + if ( function_exists( 'wp_check_comment_disallowed_list' ) ) { + $comment_author = isset( $commentdata['comment_author'] ) ? $commentdata['comment_author'] : ''; + $comment_author_email = isset( $commentdata['comment_author_email'] ) ? $commentdata['comment_author_email'] : ''; + $comment_author_url = isset( $commentdata['comment_author_url'] ) ? $commentdata['comment_author_url'] : ''; + $comment_content = isset( $commentdata['comment_content'] ) ? $commentdata['comment_content'] : ''; + $comment_author_ip = isset( $commentdata['comment_author_IP'] ) ? $commentdata['comment_author_IP'] : ''; + $comment_agent = isset( $commentdata['comment_agent'] ) ? $commentdata['comment_agent'] : ''; + + if ( wp_check_comment_disallowed_list( $comment_author, $comment_author_email, $comment_author_url, $comment_content, $comment_author_ip, $comment_agent ) ) { + $commentdata['akismet_result'] = 'skipped'; + $commentdata['comment_meta']['akismet_result'] = 'skipped'; + + $commentdata['akismet_skipped_microtime'] = microtime( true ); + $commentdata['comment_meta']['akismet_skipped_microtime'] = $commentdata['akismet_skipped_microtime']; + + self::set_last_comment( $commentdata ); + + return $commentdata; + } + } + + $comment = $commentdata; + + $comment['user_ip'] = self::get_ip_address(); + $comment['user_agent'] = self::get_user_agent(); + $comment['referrer'] = self::get_referer(); + $comment['blog'] = get_option( 'home' ); + $comment['blog_lang'] = get_locale(); + $comment['blog_charset'] = get_option( 'blog_charset' ); + $comment['permalink'] = get_permalink( $comment['comment_post_ID'] ); + + if ( ! empty( $comment['user_ID'] ) ) { + $comment['user_role'] = self::get_user_roles( $comment['user_ID'] ); + } + + /** See filter documentation in init_hooks(). */ + $akismet_nonce_option = apply_filters( 'akismet_comment_nonce', get_option( 'akismet_comment_nonce' ) ); + $comment['akismet_comment_nonce'] = 'inactive'; + if ( $akismet_nonce_option == 'true' || $akismet_nonce_option == '' ) { + $comment['akismet_comment_nonce'] = 'failed'; + if ( isset( $_POST['akismet_comment_nonce'] ) && is_string( $_POST['akismet_comment_nonce'] ) && wp_verify_nonce( $_POST['akismet_comment_nonce'], 'akismet_comment_nonce_' . $comment['comment_post_ID'] ) ) { + $comment['akismet_comment_nonce'] = 'passed'; + } + + // comment reply in wp-admin + if ( isset( $_POST['_ajax_nonce-replyto-comment'] ) && check_ajax_referer( 'replyto-comment', '_ajax_nonce-replyto-comment' ) ) { + $comment['akismet_comment_nonce'] = 'passed'; + } + } + + if ( self::is_test_mode() ) { + $comment['is_test'] = 'true'; + } + + foreach ( $_POST as $key => $value ) { + if ( is_string( $value ) ) { + $comment[ "POST_{$key}" ] = $value; + } + } + + foreach ( $_SERVER as $key => $value ) { + if ( ! is_string( $value ) ) { + continue; + } + + if ( preg_match( '/^HTTP_COOKIE/', $key ) ) { + continue; + } + + // Send any potentially useful $_SERVER vars, but avoid sending junk we don't need. + if ( preg_match( '/^(HTTP_|REMOTE_ADDR|REQUEST_URI|DOCUMENT_URI)/', $key ) ) { + $comment[ "$key" ] = $value; + } + } + + $post = get_post( $comment['comment_post_ID'] ); + + if ( ! is_null( $post ) ) { + // $post can technically be null, although in the past, it's always been an indicator of another plugin interfering. + $comment['comment_post_modified_gmt'] = $post->post_modified_gmt; + + // Tags and categories are important context in which to consider the comment. + $comment['comment_context'] = array(); + + $tag_names = wp_get_post_tags( $post->ID, array( 'fields' => 'names' ) ); + + if ( $tag_names && ! is_wp_error( $tag_names ) ) { + foreach ( $tag_names as $tag_name ) { + $comment['comment_context'][] = $tag_name; + } + } + + $category_names = wp_get_post_categories( $post->ID, array( 'fields' => 'names' ) ); + + if ( $category_names && ! is_wp_error( $category_names ) ) { + foreach ( $category_names as $category_name ) { + $comment['comment_context'][] = $category_name; + } + } + } + + // Set the webhook callback URL. The Akismet servers may make a request to this URL + // if a comment's spam status changes. + $comment['callback'] = get_rest_url( null, 'akismet/v1/webhook' ); + + /** + * Filter the data that is used to generate the request body for the API call. + * + * @since 5.3.1 + * + * @param array $comment An array of request data. + * @param string $endpoint The API endpoint being requested. + */ + $comment = apply_filters( 'akismet_request_args', $comment, 'comment-check' ); + + $response = self::http_post( self::build_query( $comment ), 'comment-check' ); + + do_action( 'akismet_comment_check_response', $response ); + + $commentdata['comment_as_submitted'] = array_intersect_key( $comment, self::$comment_as_submitted_allowed_keys ); + + // Also include any form fields we inject into the comment form, like ak_js + foreach ( $_POST as $key => $value ) { + if ( is_string( $value ) && strpos( $key, 'ak_' ) === 0 ) { + $commentdata['comment_as_submitted'][ 'POST_' . $key ] = $value; + } + } + + $commentdata['akismet_result'] = $response[1]; + + if ( 'true' === $response[1] || 'false' === $response[1] ) { + $commentdata['comment_meta']['akismet_result'] = $response[1]; + } else { + $commentdata['comment_meta']['akismet_error'] = time(); + } + + if ( isset( $response[0]['x-akismet-pro-tip'] ) ) { + $commentdata['akismet_pro_tip'] = $response[0]['x-akismet-pro-tip']; + $commentdata['comment_meta']['akismet_pro_tip'] = $response[0]['x-akismet-pro-tip']; + } + + if ( isset( $response[0]['x-akismet-guid'] ) ) { + $commentdata['akismet_guid'] = $response[0]['x-akismet-guid']; + $commentdata['comment_meta']['akismet_guid'] = $response[0]['x-akismet-guid']; + + if ( 'false' === $response[1] ) { + // If Akismet has indicated that there is more processing to be done before this comment + // can be fully classified, delay moderation emails until that processing is complete. + if ( isset( $response[0]['X-akismet-recheck-after'] ) ) { + // Prevent this comment from reaching Active status (keep in Pending) until + // it's finished being checked. + $commentdata['comment_approved'] = '0'; + self::$last_comment_result = '0'; + + // Indicate that we should schedule a fallback so that if the site never receives a + // followup from Akismet, the emails will still be sent. We don't schedule it here + // because we don't yet have the comment ID. Add an extra minute to ensure that the + // fallback email isn't sent while the recheck or webhook call is happening. + $delay = $response[0]['X-akismet-recheck-after'] * 2; + + $commentdata['comment_meta']['akismet_schedule_approval_fallback'] = $delay; + + // If this commentmeta is present, we'll prevent the moderation email from sending once. + $commentdata['comment_meta']['akismet_delay_moderation_email'] = true; + + self::log( 'Delaying moderation email for comment from ' . $commentdata['comment_author'] . ' for ' . $delay . ' seconds' ); + + $commentdata['comment_meta']['akismet_schedule_email_fallback'] = $delay; + } + } + } + + $commentdata['comment_meta']['akismet_as_submitted'] = $commentdata['comment_as_submitted']; + + if ( isset( $response[0]['x-akismet-error'] ) ) { + // An error occurred that we anticipated (like a suspended key) and want the user to act on. + // Send to moderation. + self::$last_comment_result = '0'; + } elseif ( 'true' == $response[1] ) { + // akismet_spam_count will be incremented later by comment_is_spam() + self::$last_comment_result = 'spam'; + + $discard = ( isset( $commentdata['akismet_pro_tip'] ) && $commentdata['akismet_pro_tip'] === 'discard' && self::allow_discard() ); + + do_action( 'akismet_spam_caught', $discard ); + + if ( $discard ) { + // The spam is obvious, so we're bailing out early. + // akismet_result_spam() won't be called so bump the counter here + if ( $incr = apply_filters( 'akismet_spam_count_incr', 1 ) ) { + update_option( 'akismet_spam_count', get_option( 'akismet_spam_count' ) + $incr ); + } + + if ( 'rest_api' === $context ) { + return new WP_Error( 'akismet_rest_comment_discarded', __( 'Comment discarded.', 'akismet' ) ); + } elseif ( 'xml-rpc' === $context ) { + // If this is a pingback that we're pre-checking, the discard behavior is the same as the normal spam response behavior. + return $commentdata; + } else { + // Redirect back to the previous page, or failing that, the post permalink, or failing that, the homepage of the blog. + $redirect_to = isset( $_SERVER['HTTP_REFERER'] ) ? $_SERVER['HTTP_REFERER'] : ( $post ? get_permalink( $post ) : home_url() ); + wp_safe_redirect( esc_url_raw( $redirect_to ) ); + die(); + } + } elseif ( 'rest_api' === $context ) { + // The way the REST API structures its calls, we can set the comment_approved value right away. + $commentdata['comment_approved'] = 'spam'; + } + } + + // if the response is neither true nor false, hold the comment for moderation and schedule a recheck + if ( 'true' != $response[1] && 'false' != $response[1] ) { + if ( ! current_user_can( 'moderate_comments' ) ) { + // Comment status should be moderated + self::$last_comment_result = '0'; + } + + $commentdata['comment_meta']['akismet_delay_moderation_email'] = true; + + if ( ! wp_next_scheduled( 'akismet_schedule_cron_recheck' ) ) { + wp_schedule_single_event( time() + 1200, 'akismet_schedule_cron_recheck' ); + do_action( 'akismet_scheduled_recheck', 'invalid-response-' . $response[1] ); + } + } + + // Delete old comments daily + if ( ! wp_next_scheduled( 'akismet_scheduled_delete' ) ) { + wp_schedule_event( time(), 'daily', 'akismet_scheduled_delete' ); + } + + self::set_last_comment( $commentdata ); + self::fix_scheduled_recheck(); + + return $commentdata; + } + + public static function get_last_comment() { + return self::$last_comment; + } + + public static function set_last_comment( $comment ) { + if ( is_null( $comment ) ) { + // This never happens in our code. + self::$last_comment = null; + } else { + // We filter it here so that it matches the filtered comment data that we'll have to compare against later. + // wp_filter_comment expects comment_author_IP + self::$last_comment = wp_filter_comment( + array_merge( + array( 'comment_author_IP' => self::get_ip_address() ), + $comment + ) + ); + } + } + + // this fires on wp_insert_comment. we can't update comment_meta when auto_check_comment() runs + // because we don't know the comment ID at that point. + public static function auto_check_update_meta( $id, $comment ) { + // wp_insert_comment() might be called in other contexts, so make sure this is the same comment + // as was checked by auto_check_comment + if ( is_object( $comment ) && ! empty( self::$last_comment ) && is_array( self::$last_comment ) ) { + if ( self::matches_last_comment_by_id( $id ) ) { + // normal result: true or false + if ( isset( self::$last_comment['akismet_result'] ) && self::$last_comment['akismet_result'] == 'true' ) { + self::update_comment_history( $comment->comment_ID, '', 'check-spam' ); + if ( $comment->comment_approved != 'spam' ) { + self::update_comment_history( + $comment->comment_ID, + '', + 'status-changed-' . $comment->comment_approved + ); + } + } elseif ( isset( self::$last_comment['akismet_result'] ) && self::$last_comment['akismet_result'] == 'false' ) { + if ( get_comment_meta( $comment->comment_ID, 'akismet_schedule_approval_fallback', true ) ) { + self::update_comment_history( $comment->comment_ID, '', 'check-ham-pending' ); + } else { + self::update_comment_history( $comment->comment_ID, '', 'check-ham' ); + } + + // Status could be spam or trash, depending on the WP version and whether this change applies: + // https://core.trac.wordpress.org/changeset/34726 + if ( $comment->comment_approved == 'spam' || $comment->comment_approved == 'trash' ) { + if ( function_exists( 'wp_check_comment_disallowed_list' ) ) { + if ( wp_check_comment_disallowed_list( $comment->comment_author, $comment->comment_author_email, $comment->comment_author_url, $comment->comment_content, $comment->comment_author_IP, $comment->comment_agent ) ) { + self::update_comment_history( $comment->comment_ID, '', 'wp-disallowed' ); + } else { + self::update_comment_history( $comment->comment_ID, '', 'status-changed-' . $comment->comment_approved ); + } + } else { + self::update_comment_history( $comment->comment_ID, '', 'status-changed-' . $comment->comment_approved ); + } + } + } elseif ( isset( self::$last_comment['akismet_result'] ) && 'skipped' == self::$last_comment['akismet_result'] ) { + // The comment wasn't sent to Akismet because it matched the disallowed comment keys. + self::update_comment_history( $comment->comment_ID, '', 'wp-disallowed' ); + self::update_comment_history( $comment->comment_ID, '', 'akismet-skipped-disallowed' ); + } else if ( ! isset( self::$last_comment['akismet_result'] ) ) { + // Add a generic skipped history item. + self::update_comment_history( $comment->comment_ID, '', 'akismet-skipped' ); + } else { + // abnormal result: error + self::update_comment_history( + $comment->comment_ID, + '', + 'check-error', + array( 'response' => substr( self::$last_comment['akismet_result'], 0, 50 ) ) + ); + } + } + } + } + + /** + * After the comment has been inserted, we have access to the comment ID. Now, we can + * schedule the fallback moderation/notification emails using the comment ID instead + * of relying on a lookup of the GUID in the commentmeta table. + * + * @param int $id The comment ID. + * @param object $comment The comment object. + */ + public static function schedule_email_fallback( $id, $comment ) { + self::log( 'Checking whether to schedule_email_fallback for comment #' . $id ); + + // If the moderation/notification emails for this comment were delayed + $email_delay = get_comment_meta( $id, 'akismet_schedule_email_fallback', true ); + + if ( $email_delay ) { + delete_comment_meta( $id, 'akismet_schedule_email_fallback' ); + + wp_schedule_single_event( time() + $email_delay, 'akismet_email_fallback', array( $id ) ); + + self::log( 'Scheduled email fallback for ' . ( time() + $email_delay ) . ' for comment #' . $id ); + } else { + self::log( 'No need to schedule_email_fallback for comment #' . $id ); + } + } + + /** + * Send out the notification emails if they were previously delayed while waiting + * for a recheck or webhook. + * + * @param int $comment_ID The comment ID. + */ + public static function email_fallback( $comment_id ) { + self::log( 'In email fallback for comment #' . $comment_id ); + + if ( get_comment_meta( $comment_id, 'akismet_delayed_moderation_email', true ) ) { + self::log( 'Triggering notification emails for comment #' . $comment_id . '. They will be sent if comment is not spam.' ); + + delete_comment_meta( $comment_id, 'akismet_delayed_moderation_email' ); + wp_new_comment_notify_moderator( $comment_id ); + wp_new_comment_notify_postauthor( $comment_id ); + } else { + self::log( 'No need to send fallback email for comment #' . $comment_id ); + } + + delete_comment_meta( $comment_id, 'akismet_delay_moderation_email' ); + } + + /** + * After the comment has been inserted, we have access to the comment ID. Now, we can + * schedule the fallback moderation/notification emails using the comment ID instead + * of relying on a lookup of the GUID in the commentmeta table. + * + * @param int $id The comment ID. + * @param object $comment The comment object. + */ + public static function schedule_approval_fallback( $id, $comment ) { + self::log( 'Checking whether to schedule_approval_fallback for comment #' . $id ); + + // If the moderation/notification emails for this comment were delayed + $approval_delay = get_comment_meta( $id, 'akismet_schedule_approval_fallback', true ); + + if ( $approval_delay ) { + delete_comment_meta( $id, 'akismet_schedule_approval_fallback' ); + + wp_schedule_single_event( time() + $approval_delay, 'akismet_approval_fallback', array( $id ) ); + + self::log( 'Scheduled approval fallback for ' . ( time() + $approval_delay ) . ' for comment #' . $id ); + } else { + self::log( 'No need to schedule_approval_fallback for comment #' . $id ); + } + } + + /** + * If no other process has approved or spammed this comment since it was put in pending, approve it. + * + * @param int $comment_ID The comment ID. + */ + public static function approval_fallback( $comment_id ) { + self::log( 'In approval fallback for comment #' . $comment_id ); + + if ( wp_get_comment_status( $comment_id ) == 'unapproved' ) { + if ( self::last_comment_status_change_came_from_akismet( $comment_id ) ) { + $comment = get_comment( $comment_id ); + + if ( ! $comment ) { + self::log( 'Comment #' . $comment_id . ' no longer exists.' ); + } else if ( check_comment( $comment->comment_author, $comment->comment_author_email, $comment->comment_author_url, $comment->comment_content, $comment->comment_author_IP, $comment->comment_agent, $comment->comment_type ) ) { + self::log( 'Approving comment #' . $comment_id ); + + wp_set_comment_status( $comment_id, 1 ); + } else { + self::log( 'Not approving comment #' . $comment_id . ' because it does not pass check_comment()' ); + } + + self::update_comment_history( $comment->comment_ID, '', 'check-ham' ); + } else { + self::log( 'No need to fallback approve comment #' . $comment_id . ' because it was not last modified by Akismet.' ); + + $history = self::get_comment_history( $comment_id ); + + if ( ! empty( $history ) ) { + $most_recent_history_event = $history[0]; + + error_log( 'Comment history: ' . print_r( $history, true ) ); + } + } + } else { + self::log( 'No need to fallback approve comment #' . $comment_id . ' because it is not pending.' ); + } + } + + public static function delete_old_comments() { + global $wpdb; + + /** + * Determines how many comments will be deleted in each batch. + * + * @param int The default, as defined by AKISMET_DELETE_LIMIT. + */ + $delete_limit = apply_filters( 'akismet_delete_comment_limit', defined( 'AKISMET_DELETE_LIMIT' ) ? AKISMET_DELETE_LIMIT : 10000 ); + $delete_limit = max( 1, intval( $delete_limit ) ); + + /** + * Determines how many days a comment will be left in the Spam queue before being deleted. + * + * @param int The default number of days. + */ + $delete_interval = apply_filters( 'akismet_delete_comment_interval', 15 ); + $delete_interval = max( 1, intval( $delete_interval ) ); + + while ( $comment_ids = $wpdb->get_col( $wpdb->prepare( "SELECT comment_id FROM {$wpdb->comments} WHERE DATE_SUB(NOW(), INTERVAL %d DAY) > comment_date_gmt AND comment_approved = 'spam' LIMIT %d", $delete_interval, $delete_limit ) ) ) { + if ( empty( $comment_ids ) ) { + return; + } + + $wpdb->queries = array(); + + $comments = array(); + + foreach ( $comment_ids as $comment_id ) { + $comments[ $comment_id ] = get_comment( $comment_id ); + + do_action( 'delete_comment', $comment_id, $comments[ $comment_id ] ); + do_action( 'akismet_batch_delete_count', __FUNCTION__ ); + } + + // Prepared as strings since comment_id is an unsigned BIGINT, and using %d will constrain the value to the maximum signed BIGINT. + $format_string = implode( ', ', array_fill( 0, is_countable( $comment_ids ) ? count( $comment_ids ) : 0, '%s' ) ); + + $wpdb->query( $wpdb->prepare( "DELETE FROM {$wpdb->comments} WHERE comment_id IN ( " . $format_string . ' )', $comment_ids ) ); + $wpdb->query( $wpdb->prepare( "DELETE FROM {$wpdb->commentmeta} WHERE comment_id IN ( " . $format_string . ' )', $comment_ids ) ); + + foreach ( $comment_ids as $comment_id ) { + do_action( 'deleted_comment', $comment_id, $comments[ $comment_id ] ); + unset( $comments[ $comment_id ] ); + } + + clean_comment_cache( $comment_ids ); + do_action( 'akismet_delete_comment_batch', is_countable( $comment_ids ) ? count( $comment_ids ) : 0 ); + } + + if ( apply_filters( 'akismet_optimize_table', ( mt_rand( 1, 5000 ) == 11 ), $wpdb->comments ) ) { // lucky number + $wpdb->query( "OPTIMIZE TABLE {$wpdb->comments}" ); + } + } + + public static function delete_old_comments_meta() { + global $wpdb; + + $interval = apply_filters( 'akismet_delete_commentmeta_interval', 15 ); + + // enforce a minimum of 1 day + $interval = absint( $interval ); + if ( $interval < 1 ) { + $interval = 1; + } + + // akismet_as_submitted meta values are large, so expire them + // after $interval days regardless of the comment status + while ( $comment_ids = $wpdb->get_col( $wpdb->prepare( "SELECT m.comment_id FROM {$wpdb->commentmeta} as m INNER JOIN {$wpdb->comments} as c USING(comment_id) WHERE m.meta_key = 'akismet_as_submitted' AND DATE_SUB(NOW(), INTERVAL %d DAY) > c.comment_date_gmt LIMIT 10000", $interval ) ) ) { + if ( empty( $comment_ids ) ) { + return; + } + + $wpdb->queries = array(); + + foreach ( $comment_ids as $comment_id ) { + delete_comment_meta( $comment_id, 'akismet_as_submitted' ); + do_action( 'akismet_batch_delete_count', __FUNCTION__ ); + } + + do_action( 'akismet_delete_commentmeta_batch', is_countable( $comment_ids ) ? count( $comment_ids ) : 0 ); + } + + if ( apply_filters( 'akismet_optimize_table', ( mt_rand( 1, 5000 ) == 11 ), $wpdb->commentmeta ) ) { // lucky number + $wpdb->query( "OPTIMIZE TABLE {$wpdb->commentmeta}" ); + } + } + + // Clear out comments meta that no longer have corresponding comments in the database + public static function delete_orphaned_commentmeta() { + global $wpdb; + + $last_meta_id = 0; + $start_time = isset( $_SERVER['REQUEST_TIME_FLOAT'] ) ? $_SERVER['REQUEST_TIME_FLOAT'] : microtime( true ); + $max_exec_time = max( ini_get( 'max_execution_time' ) - 5, 3 ); + + while ( $commentmeta_results = $wpdb->get_results( $wpdb->prepare( "SELECT m.meta_id, m.comment_id, m.meta_key FROM {$wpdb->commentmeta} as m LEFT JOIN {$wpdb->comments} as c USING(comment_id) WHERE c.comment_id IS NULL AND m.meta_id > %d ORDER BY m.meta_id LIMIT 1000", $last_meta_id ) ) ) { + if ( empty( $commentmeta_results ) ) { + return; + } + + $wpdb->queries = array(); + + $commentmeta_deleted = 0; + + foreach ( $commentmeta_results as $commentmeta ) { + if ( 'akismet_' == substr( $commentmeta->meta_key, 0, 8 ) ) { + delete_comment_meta( $commentmeta->comment_id, $commentmeta->meta_key ); + do_action( 'akismet_batch_delete_count', __FUNCTION__ ); + ++$commentmeta_deleted; + } + + $last_meta_id = $commentmeta->meta_id; + } + + do_action( 'akismet_delete_commentmeta_batch', $commentmeta_deleted ); + + // If we're getting close to max_execution_time, quit for this round. + if ( microtime( true ) - $start_time > $max_exec_time ) { + return; + } + } + + if ( apply_filters( 'akismet_optimize_table', ( mt_rand( 1, 5000 ) == 11 ), $wpdb->commentmeta ) ) { // lucky number + $wpdb->query( "OPTIMIZE TABLE {$wpdb->commentmeta}" ); + } + } + + // how many approved comments does this author have? + public static function get_user_comments_approved( $user_id, $comment_author_email, $comment_author, $comment_author_url ) { + global $wpdb; + + /** + * Which comment types should be ignored when counting a user's approved comments? + * + * Some plugins add entries to the comments table that are not actual + * comments that could have been checked by Akismet. Allow these comments + * to be excluded from the "approved comment count" query in order to + * avoid artificially inflating the approved comment count. + * + * @param array $comment_types An array of comment types that won't be considered + * when counting a user's approved comments. + * + * @since 4.2.2 + */ + $excluded_comment_types = apply_filters( 'akismet_excluded_comment_types', array() ); + + $comment_type_where = ''; + + if ( is_array( $excluded_comment_types ) && ! empty( $excluded_comment_types ) ) { + $excluded_comment_types = array_unique( $excluded_comment_types ); + + foreach ( $excluded_comment_types as $excluded_comment_type ) { + $comment_type_where .= $wpdb->prepare( ' AND comment_type <> %s ', $excluded_comment_type ); + } + } + + if ( ! empty( $user_id ) ) { + return (int) $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM {$wpdb->comments} WHERE user_id = %d AND comment_approved = 1" . $comment_type_where, $user_id ) ); + } + + if ( ! empty( $comment_author_email ) ) { + return (int) $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM {$wpdb->comments} WHERE comment_author_email = %s AND comment_author = %s AND comment_author_url = %s AND comment_approved = 1" . $comment_type_where, $comment_author_email, $comment_author, $comment_author_url ) ); + } + + return 0; + } + + /** + * Get the full comment history for a given comment, as an array in reverse chronological order. + * Each entry will have an 'event', a 'time', and possibly a 'message' member (if the entry is old enough). + * Some entries will also have a 'user' or 'meta' member. + * + * @param int $comment_id The relevant comment ID. + * @return array|bool An array of history events, or false if there is no history. + */ + public static function get_comment_history( $comment_id ) { + $history = get_comment_meta( $comment_id, 'akismet_history', false ); + if ( empty( $history ) || empty( $history[0] ) ) { + return false; + } + + /* + // To see all variants when testing. + $history[] = array( 'time' => 445856401, 'message' => 'Old versions of Akismet stored the message as a literal string in the commentmeta.', 'event' => null ); + $history[] = array( 'time' => 445856402, 'event' => 'recheck-spam' ); + $history[] = array( 'time' => 445856403, 'event' => 'check-spam' ); + $history[] = array( 'time' => 445856404, 'event' => 'recheck-ham' ); + $history[] = array( 'time' => 445856405, 'event' => 'check-ham' ); + $history[] = array( 'time' => 445856405, 'event' => 'check-ham-pending' ); + $history[] = array( 'time' => 445856406, 'event' => 'wp-blacklisted' ); + $history[] = array( 'time' => 445856406, 'event' => 'wp-disallowed' ); + $history[] = array( 'time' => 445856407, 'event' => 'report-spam' ); + $history[] = array( 'time' => 445856408, 'event' => 'report-spam', 'user' => 'sam' ); + $history[] = array( 'message' => 'sam reported this comment as spam (hardcoded message).', 'time' => 445856400, 'event' => 'report-spam', 'user' => 'sam' ); + $history[] = array( 'time' => 445856409, 'event' => 'report-ham', 'user' => 'sam' ); + $history[] = array( 'message' => 'sam reported this comment as ham (hardcoded message).', 'time' => 445856400, 'event' => 'report-ham', 'user' => 'sam' ); // + $history[] = array( 'time' => 445856410, 'event' => 'cron-retry-spam' ); + $history[] = array( 'time' => 445856411, 'event' => 'cron-retry-ham' ); + $history[] = array( 'time' => 445856412, 'event' => 'check-error' ); // + $history[] = array( 'time' => 445856413, 'event' => 'check-error', 'meta' => array( 'response' => 'The server was taking a nap.' ) ); + $history[] = array( 'time' => 445856414, 'event' => 'recheck-error' ); // Should not generate a message. + $history[] = array( 'time' => 445856415, 'event' => 'recheck-error', 'meta' => array( 'response' => 'The server was taking a nap.' ) ); + $history[] = array( 'time' => 445856416, 'event' => 'status-changedtrash' ); + $history[] = array( 'time' => 445856417, 'event' => 'status-changedspam' ); + $history[] = array( 'time' => 445856418, 'event' => 'status-changedhold' ); + $history[] = array( 'time' => 445856419, 'event' => 'status-changedapprove' ); + $history[] = array( 'time' => 445856420, 'event' => 'status-changed-trash' ); + $history[] = array( 'time' => 445856421, 'event' => 'status-changed-spam' ); + $history[] = array( 'time' => 445856422, 'event' => 'status-changed-hold' ); + $history[] = array( 'time' => 445856423, 'event' => 'status-changed-approve' ); + $history[] = array( 'time' => 445856424, 'event' => 'status-trash', 'user' => 'sam' ); + $history[] = array( 'time' => 445856425, 'event' => 'status-spam', 'user' => 'sam' ); + $history[] = array( 'time' => 445856426, 'event' => 'status-hold', 'user' => 'sam' ); + $history[] = array( 'time' => 445856427, 'event' => 'status-approve', 'user' => 'sam' ); + $history[] = array( 'time' => 445856427, 'event' => 'webhook-spam' ); + $history[] = array( 'time' => 445856427, 'event' => 'webhook-ham' ); + $history[] = array( 'time' => 445856427, 'event' => 'webhook-spam-noaction' ); + $history[] = array( 'time' => 445856427, 'event' => 'webhook-ham-noaction' ); + */ + + // Validate history entries to guard against malformed data. + // In one case, serialized data was returned in $entry instead of an array. + $history = array_filter( + $history, + function ( $entry ) { + return is_array( $entry ) && isset( $entry['time'] ) && is_numeric( $entry['time'] ); + } + ); + + usort( $history, 'Akismet::_cmp_time' ); + + return $history; + } + + /** + * Log an event for a given comment, storing it in comment_meta. + * + * @param int $comment_id The ID of the relevant comment. + * @param string $message The string description of the event. No longer used. + * @param string $event The event code. + * @param array $meta Metadata about the history entry. e.g., the user that reported or changed the status of a given comment. + */ + public static function update_comment_history( $comment_id, $message, $event = null, $meta = null ) { + global $current_user; + + $user = ''; + + $event = array( + 'time' => self::_get_microtime(), + 'event' => $event, + ); + + if ( is_object( $current_user ) && isset( $current_user->user_login ) ) { + $event['user'] = $current_user->user_login; + } + + if ( ! empty( $meta ) ) { + $event['meta'] = $meta; + } + + // $unique = false so as to allow multiple values per comment + $r = add_comment_meta( $comment_id, 'akismet_history', $event, false ); + } + + public static function check_db_comment( $id, $recheck_reason = 'recheck_queue' ) { + global $wpdb; + + if ( ! self::get_api_key() ) { + return new WP_Error( 'akismet-not-configured', __( 'Akismet is not configured. Please enter an API key.', 'akismet' ) ); + } + + $c = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM {$wpdb->comments} WHERE comment_ID = %d", $id ), ARRAY_A ); + + if ( ! $c ) { + return new WP_Error( 'invalid-comment-id', __( 'Comment not found.', 'akismet' ) ); + } + + $c['user_ip'] = $c['comment_author_IP']; + $c['user_agent'] = $c['comment_agent']; + $c['referrer'] = ''; + $c['blog'] = get_option( 'home' ); + $c['blog_lang'] = get_locale(); + $c['blog_charset'] = get_option( 'blog_charset' ); + $c['permalink'] = get_permalink( $c['comment_post_ID'] ); + $c['recheck_reason'] = $recheck_reason; + + $c['user_role'] = ''; + if ( ! empty( $c['user_ID'] ) ) { + $c['user_role'] = self::get_user_roles( $c['user_ID'] ); + } + + if ( self::is_test_mode() ) { + $c['is_test'] = 'true'; + } + + $c = apply_filters( 'akismet_request_args', $c, 'comment-check' ); + + $response = self::http_post( self::build_query( $c ), 'comment-check' ); + + if ( ! empty( $response[1] ) ) { + return $response[1]; + } + + return false; + } + + public static function recheck_comment( $id, $recheck_reason = 'recheck_queue' ) { + add_comment_meta( $id, 'akismet_rechecking', true ); + + $api_response = self::check_db_comment( $id, $recheck_reason ); + + if ( is_wp_error( $api_response ) ) { + // Invalid comment ID. + } elseif ( 'true' === $api_response ) { + wp_set_comment_status( $id, 'spam' ); + update_comment_meta( $id, 'akismet_result', 'true' ); + delete_comment_meta( $id, 'akismet_error' ); + delete_comment_meta( $id, 'akismet_delay_moderation_email' ); + delete_comment_meta( $id, 'akismet_delayed_moderation_email' ); + delete_comment_meta( $id, 'akismet_schedule_approval_fallback' ); + delete_comment_meta( $id, 'akismet_schedule_email_fallback' ); + self::update_comment_history( $id, '', 'recheck-spam' ); + } elseif ( 'false' === $api_response ) { + update_comment_meta( $id, 'akismet_result', 'false' ); + delete_comment_meta( $id, 'akismet_error' ); + delete_comment_meta( $id, 'akismet_delay_moderation_email' ); + delete_comment_meta( $id, 'akismet_delayed_moderation_email' ); + delete_comment_meta( $id, 'akismet_schedule_approval_fallback' ); + delete_comment_meta( $id, 'akismet_schedule_email_fallback' ); + self::update_comment_history( $id, '', 'recheck-ham' ); + } else { + // abnormal result: error + update_comment_meta( $id, 'akismet_result', 'error' ); + self::update_comment_history( + $id, + '', + 'recheck-error', + array( 'response' => substr( $api_response, 0, 50 ) ) + ); + } + + delete_comment_meta( $id, 'akismet_rechecking' ); + + return $api_response; + } + + public static function transition_comment_status( $new_status, $old_status, $comment ) { + + if ( $new_status == $old_status ) { + return; + } + + if ( 'spam' === $new_status || 'spam' === $old_status ) { + // Clear the cache of the "X comments in your spam queue" count on the dashboard. + wp_cache_delete( 'akismet_spam_count', 'widget' ); + } + + // we don't need to record a history item for deleted comments + if ( $new_status == 'delete' ) { + return; + } + + if ( ! current_user_can( 'edit_post', $comment->comment_post_ID ) && ! current_user_can( 'moderate_comments' ) ) { + return; + } + + if ( defined( 'WP_IMPORTING' ) && WP_IMPORTING == true ) { + return; + } + + // if this is present, it means the status has been changed by a re-check, not an explicit user action + if ( get_comment_meta( $comment->comment_ID, 'akismet_rechecking' ) ) { + return; + } + + if ( function_exists( 'getallheaders' ) ) { + $request_headers = getallheaders(); + + foreach ( $request_headers as $header => $value ) { + if ( strtolower( $header ) == 'x-akismet-webhook' ) { + // This change is due to a webhook request. + return; + } + } + } + + // Assumption alert: + // We want to submit comments to Akismet only when a moderator explicitly spams or approves it - not if the status + // is changed automatically by another plugin. Unfortunately WordPress doesn't provide an unambiguous way to + // determine why the transition_comment_status action was triggered. And there are several different ways by which + // to spam and unspam comments: bulk actions, ajax, links in moderation emails, the dashboard, and perhaps others. + // We'll assume that this is an explicit user action if certain POST/GET variables exist. + if ( + // status=spam: Marking as spam via the REST API or... + // status=unspam: I'm not sure. Maybe this used to be used instead of status=approved? Or the UI for removing from spam but not approving has been since removed?... + // status=approved: Unspamming via the REST API (Calypso) or... + ( isset( $_POST['status'] ) && in_array( $_POST['status'], array( 'spam', 'unspam', 'approved' ) ) ) + // spam=1: Clicking "Spam" underneath a comment in wp-admin and allowing the AJAX request to happen. + || ( isset( $_POST['spam'] ) && (int) $_POST['spam'] == 1 ) + // unspam=1: Clicking "Not Spam" underneath a comment in wp-admin and allowing the AJAX request to happen. Or, clicking "Undo" after marking something as spam. + || ( isset( $_POST['unspam'] ) && (int) $_POST['unspam'] == 1 ) + // comment_status=spam/unspam: It's unclear where this is happening. + || ( isset( $_POST['comment_status'] ) && in_array( $_POST['comment_status'], array( 'spam', 'unspam' ) ) ) + // action=spam: Choosing "Mark as Spam" from the Bulk Actions dropdown in wp-admin (or the "Spam it" link in notification emails). + // action=unspam: Choosing "Not Spam" from the Bulk Actions dropdown in wp-admin. + // action=spamcomment: Following the "Spam" link below a comment in wp-admin (not allowing AJAX request to happen). + // action=unspamcomment: Following the "Not Spam" link below a comment in wp-admin (not allowing AJAX request to happen). + || ( isset( $_GET['action'] ) && in_array( $_GET['action'], array( 'spam', 'unspam', 'spamcomment', 'unspamcomment' ) ) ) + // action=editedcomment: Editing a comment via wp-admin (and possibly changing its status). + || ( isset( $_POST['action'] ) && in_array( $_POST['action'], array( 'editedcomment' ) ) ) + // for=jetpack: Moderation via the WordPress app, Calypso, anything powered by the Jetpack connection. + || ( isset( $_GET['for'] ) && ( 'jetpack' == $_GET['for'] ) && ( ! defined( 'IS_WPCOM' ) || ! IS_WPCOM ) ) + // Certain WordPress.com API requests + || ( defined( 'REST_API_REQUEST' ) && REST_API_REQUEST ) + // WordPress.org REST API requests + || ( defined( 'REST_REQUEST' ) && REST_REQUEST ) + ) { + if ( $new_status == 'spam' && ( $old_status == 'approved' || $old_status == 'unapproved' || ! $old_status ) ) { + return self::submit_spam_comment( $comment->comment_ID ); + } elseif ( $old_status == 'spam' && ( $new_status == 'approved' || $new_status == 'unapproved' ) ) { + return self::submit_nonspam_comment( $comment->comment_ID ); + } + } + + self::update_comment_history( $comment->comment_ID, '', 'status-' . $new_status ); + } + + public static function submit_spam_comment( $comment_id ) { + global $wpdb, $current_user, $current_site; + + $comment_id = (int) $comment_id; + + $comment = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM {$wpdb->comments} WHERE comment_ID = %d", $comment_id ), ARRAY_A ); + + if ( ! $comment ) { + // it was deleted + return; + } + + if ( 'spam' != $comment['comment_approved'] ) { + return; + } + + self::update_comment_history( $comment_id, '', 'report-spam' ); + + // If the user hasn't configured Akismet, there's nothing else to do at this point. + if ( ! self::get_api_key() ) { + return; + } + + // use the original version stored in comment_meta if available + $as_submitted = self::sanitize_comment_as_submitted( get_comment_meta( $comment_id, 'akismet_as_submitted', true ) ); + + if ( $as_submitted && is_array( $as_submitted ) && isset( $as_submitted['comment_content'] ) ) { + $comment = array_merge( $comment, $as_submitted ); + } + + $comment['blog'] = get_option( 'home' ); + $comment['blog_lang'] = get_locale(); + $comment['blog_charset'] = get_option( 'blog_charset' ); + $comment['permalink'] = get_permalink( $comment['comment_post_ID'] ); + + if ( is_object( $current_user ) ) { + $comment['reporter'] = $current_user->user_login; + } + + if ( is_object( $current_site ) ) { + $comment['site_domain'] = $current_site->domain; + } + + $comment['user_role'] = ''; + if ( ! empty( $comment['user_ID'] ) ) { + $comment['user_role'] = self::get_user_roles( $comment['user_ID'] ); + } + + if ( self::is_test_mode() ) { + $comment['is_test'] = 'true'; + } + + $post = get_post( $comment['comment_post_ID'] ); + + if ( ! is_null( $post ) ) { + $comment['comment_post_modified_gmt'] = $post->post_modified_gmt; + } + + $comment['comment_check_response'] = self::last_comment_check_response( $comment_id ); + + $comment = apply_filters( 'akismet_request_args', $comment, 'submit-spam' ); + + $response = self::http_post( self::build_query( $comment ), 'submit-spam' ); + + update_comment_meta( $comment_id, 'akismet_user_result', 'true' ); + + if ( $comment['reporter'] ) { + update_comment_meta( $comment_id, 'akismet_user', $comment['reporter'] ); + } + + do_action( 'akismet_submit_spam_comment', $comment_id, $response[1] ); + } + + public static function submit_nonspam_comment( $comment_id ) { + global $wpdb, $current_user, $current_site; + + $comment_id = (int) $comment_id; + + $comment = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM {$wpdb->comments} WHERE comment_ID = %d", $comment_id ), ARRAY_A ); + + if ( ! $comment ) { + // it was deleted + return; + } + + self::update_comment_history( $comment_id, '', 'report-ham' ); + + // If the user hasn't configured Akismet, there's nothing else to do at this point. + if ( ! self::get_api_key() ) { + return; + } + + // use the original version stored in comment_meta if available + $as_submitted = self::sanitize_comment_as_submitted( get_comment_meta( $comment_id, 'akismet_as_submitted', true ) ); + + if ( $as_submitted && is_array( $as_submitted ) && isset( $as_submitted['comment_content'] ) ) { + $comment = array_merge( $comment, $as_submitted ); + } + + $comment['blog'] = get_option( 'home' ); + $comment['blog_lang'] = get_locale(); + $comment['blog_charset'] = get_option( 'blog_charset' ); + $comment['permalink'] = get_permalink( $comment['comment_post_ID'] ); + $comment['user_role'] = ''; + + if ( is_object( $current_user ) ) { + $comment['reporter'] = $current_user->user_login; + } + + if ( is_object( $current_site ) ) { + $comment['site_domain'] = $current_site->domain; + } + + if ( ! empty( $comment['user_ID'] ) ) { + $comment['user_role'] = self::get_user_roles( $comment['user_ID'] ); + } + + if ( self::is_test_mode() ) { + $comment['is_test'] = 'true'; + } + + $post = get_post( $comment['comment_post_ID'] ); + + if ( ! is_null( $post ) ) { + $comment['comment_post_modified_gmt'] = $post->post_modified_gmt; + } + + $comment['comment_check_response'] = self::last_comment_check_response( $comment_id ); + + $comment = apply_filters( 'akismet_request_args', $comment, 'submit-ham' ); + + $response = self::http_post( self::build_query( $comment ), 'submit-ham' ); + + update_comment_meta( $comment_id, 'akismet_user_result', 'false' ); + + if ( $comment['reporter'] ) { + update_comment_meta( $comment_id, 'akismet_user', $comment['reporter'] ); + } + + do_action( 'akismet_submit_nonspam_comment', $comment_id, $response[1] ); + } + + public static function cron_recheck() { + global $wpdb; + + $api_key = self::get_api_key(); + + $status = self::verify_key( $api_key ); + if ( get_option( 'akismet_alert_code' ) || $status == 'invalid' ) { + // since there is currently a problem with the key, reschedule a check for 6 hours hence + wp_schedule_single_event( time() + 21600, 'akismet_schedule_cron_recheck' ); + do_action( 'akismet_scheduled_recheck', 'key-problem-' . get_option( 'akismet_alert_code' ) . '-' . $status ); + return false; + } + + delete_option( 'akismet_available_servers' ); + + $comment_errors = $wpdb->get_col( "SELECT comment_id FROM {$wpdb->commentmeta} WHERE meta_key = 'akismet_error' LIMIT 100" ); + + foreach ( (array) $comment_errors as $comment_id ) { + // if the comment no longer exists, or is too old, remove the meta entry from the queue to avoid getting stuck + $comment = get_comment( $comment_id ); + + if ( + ! $comment // Comment has been deleted + || strtotime( $comment->comment_date_gmt ) < strtotime( '-15 days' ) // Comment is too old. + || $comment->comment_approved !== '0' // Comment is no longer in the Pending queue + ) { + delete_comment_meta( $comment_id, 'akismet_error' ); + delete_comment_meta( $comment_id, 'akismet_delay_moderation_email' ); + delete_comment_meta( $comment_id, 'akismet_delayed_moderation_email' ); + delete_comment_meta( $comment_id, 'akismet_schedule_approval_fallback' ); + delete_comment_meta( $comment_id, 'akismet_schedule_email_fallback' ); + continue; + } + + add_comment_meta( $comment_id, 'akismet_rechecking', true ); + $status = self::check_db_comment( $comment_id, 'retry' ); + + $event = ''; + if ( $status == 'true' ) { + $event = 'cron-retry-spam'; + } elseif ( $status == 'false' ) { + $event = 'cron-retry-ham'; + } + + // If we got back a legit response then update the comment history + // other wise just bail now and try again later. No point in + // re-trying all the comments once we hit one failure. + if ( ! empty( $event ) ) { + delete_comment_meta( $comment_id, 'akismet_error' ); + self::update_comment_history( $comment_id, '', $event ); + update_comment_meta( $comment_id, 'akismet_result', $status ); + // make sure the comment status is still pending. if it isn't, that means the user has already moved it elsewhere. + $comment = get_comment( $comment_id ); + if ( $comment && 'unapproved' == wp_get_comment_status( $comment_id ) ) { + if ( $status == 'true' ) { + wp_spam_comment( $comment_id ); + } elseif ( $status == 'false' ) { + // comment is good, but it's still in the pending queue. depending on the moderation settings + // we may need to change it to approved. + if ( check_comment( $comment->comment_author, $comment->comment_author_email, $comment->comment_author_url, $comment->comment_content, $comment->comment_author_IP, $comment->comment_agent, $comment->comment_type ) ) { + wp_set_comment_status( $comment_id, 1 ); + } elseif ( get_comment_meta( $comment_id, 'akismet_delayed_moderation_email', true ) ) { + wp_new_comment_notify_moderator( $comment_id ); + wp_new_comment_notify_postauthor( $comment_id ); + } + } + } + + delete_comment_meta( $comment_id, 'akismet_delay_moderation_email' ); + delete_comment_meta( $comment_id, 'akismet_delayed_moderation_email' ); + } else { + // If this comment has been pending moderation for longer than MAX_DELAY_BEFORE_MODERATION_EMAIL, + // send a moderation email now. + if ( ( intval( gmdate( 'U' ) ) - strtotime( $comment->comment_date_gmt ) ) < self::MAX_DELAY_BEFORE_MODERATION_EMAIL ) { + delete_comment_meta( $comment_id, 'akismet_delay_moderation_email' ); + delete_comment_meta( $comment_id, 'akismet_delayed_moderation_email' ); + + wp_new_comment_notify_moderator( $comment_id ); + wp_new_comment_notify_postauthor( $comment_id ); + } + + delete_comment_meta( $comment_id, 'akismet_rechecking' ); + wp_schedule_single_event( time() + 1200, 'akismet_schedule_cron_recheck' ); + do_action( 'akismet_scheduled_recheck', 'check-db-comment-' . $status ); + + return; + } + + delete_comment_meta( $comment_id, 'akismet_rechecking' ); + } + + $remaining = $wpdb->get_var( "SELECT COUNT(*) FROM {$wpdb->commentmeta} WHERE meta_key = 'akismet_error'" ); + + if ( $remaining && ! wp_next_scheduled( 'akismet_schedule_cron_recheck' ) ) { + wp_schedule_single_event( time() + 1200, 'akismet_schedule_cron_recheck' ); + do_action( 'akismet_scheduled_recheck', 'remaining' ); + } + } + + public static function fix_scheduled_recheck() { + $future_check = wp_next_scheduled( 'akismet_schedule_cron_recheck' ); + if ( ! $future_check ) { + return; + } + + if ( get_option( 'akismet_alert_code' ) > 0 ) { + return; + } + + $check_range = time() + 1200; + if ( $future_check > $check_range ) { + wp_clear_scheduled_hook( 'akismet_schedule_cron_recheck' ); + wp_schedule_single_event( time() + 300, 'akismet_schedule_cron_recheck' ); + do_action( 'akismet_scheduled_recheck', 'fix-scheduled-recheck' ); + } + } + + public static function add_comment_nonce( $post_id ) { + /** + * To disable the Akismet comment nonce, add a filter for the 'akismet_comment_nonce' tag + * and return any string value that is not 'true' or '' (empty string). + * + * Don't return boolean false, because that implies that the 'akismet_comment_nonce' option + * has not been set and that Akismet should just choose the default behavior for that + * situation. + */ + + if ( ! self::get_api_key() ) { + return; + } + + $akismet_comment_nonce_option = apply_filters( 'akismet_comment_nonce', get_option( 'akismet_comment_nonce' ) ); + + if ( $akismet_comment_nonce_option == 'true' || $akismet_comment_nonce_option == '' ) { + echo '

'; + wp_nonce_field( 'akismet_comment_nonce_' . $post_id, 'akismet_comment_nonce', false ); + echo '

'; + } + } + + public static function is_test_mode() { + return defined( 'AKISMET_TEST_MODE' ) && AKISMET_TEST_MODE; + } + + public static function allow_discard() { + if ( defined( 'DOING_AJAX' ) && DOING_AJAX ) { + return false; + } + if ( is_user_logged_in() ) { + return false; + } + + return ( get_option( 'akismet_strictness' ) === '1' ); + } + + public static function get_ip_address() { + return isset( $_SERVER['REMOTE_ADDR'] ) ? $_SERVER['REMOTE_ADDR'] : null; + } + + /** + * Using the unique values that we assign, do we consider these two comments + * to be the same instance of a comment? + * + * The only fields that matter in $comment1 and $comment2 are akismet_guid and akismet_skipped_microtime. + * We set both of these during the comment-check call, and if the comment has been saved to the DB, + * we save them as comment meta and add them back into the comment array before comparing the comments. + * + * @param mixed $comment1 A comment object or array. + * @param mixed $comment2 A comment object or array. + * @return bool Whether the two comments should be treated as the same comment. + */ + private static function comments_match( $comment1, $comment2 ) { + $comment1 = (array) $comment1; + $comment2 = (array) $comment2; + + if ( ! empty( $comment1['akismet_guid'] ) && ! empty( $comment2['akismet_guid'] ) ) { + // If the comment got sent to the API and got a response, it will have a GUID. + + return ( $comment1['akismet_guid'] == $comment2['akismet_guid'] ); + } else if ( ! empty( $comment1['akismet_skipped_microtime'] ) && ! empty( $comment2['akismet_skipped_microtime'] ) ) { + // It won't have a GUID if it didn't get sent to the API because it matched the disallowed list, + // but it should have a microtimestamp to use here for matching against the comment DB entry it matches. + return ( strval( $comment1['akismet_skipped_microtime'] ) == strval( $comment2['akismet_skipped_microtime'] ) ); + } + + return false; + } + + /** + * Does the supplied comment match the details of the one most recently stored in self::$last_comment? + * + * @param array $comment + * @return bool Whether the comment supplied as an argument is a match for the one we have stored in $last_comment. + */ + public static function matches_last_comment( $comment ) { + if ( ! self::$last_comment ) { + return false; + } + + return self::comments_match( $comment, self::$last_comment ); + } + + /** + * Because of the order of operations, we don't always know the comment ID of the comment that we're checking, + * so we have to be able to match the comment we cached locally with the comment from the DB. + * + * @param int $comment_id + * @return bool Whether the comment represented by $comment_id is a match for the one we have stored in $last_comment. + */ + public static function matches_last_comment_by_id( $comment_id ) { + return self::matches_last_comment( self::get_fields_for_comment_matching( $comment_id ) ); + } + + /** + * Given a comment ID, retrieve the values that we use for matching comments together. + * + * @param int $comment_id + * @return array An array containing akismet_guid and akismet_skipped_microtime. Either or both may be falsy, but we hope that at least one is a string. + */ + public static function get_fields_for_comment_matching( $comment_id ) { + return array( + 'akismet_guid' => get_comment_meta( $comment_id, 'akismet_guid', true ), + 'akismet_skipped_microtime' => get_comment_meta( $comment_id, 'akismet_skipped_microtime', true ), + ); + } + + private static function get_user_agent() { + return isset( $_SERVER['HTTP_USER_AGENT'] ) ? $_SERVER['HTTP_USER_AGENT'] : null; + } + + private static function get_referer() { + return isset( $_SERVER['HTTP_REFERER'] ) ? $_SERVER['HTTP_REFERER'] : null; + } + + // return a comma-separated list of role names for the given user + public static function get_user_roles( $user_id ) { + $comment_user = null; + $roles = false; + + if ( ! class_exists( 'WP_User' ) ) { + return false; + } + + if ( $user_id > 0 ) { + $comment_user = new WP_User( $user_id ); + if ( isset( $comment_user->roles ) ) { + $roles = implode( ',', $comment_user->roles ); + } + } + + if ( is_multisite() && is_super_admin( $user_id ) ) { + if ( empty( $roles ) ) { + $roles = 'super_admin'; + } else { + $comment_user->roles[] = 'super_admin'; + $roles = implode( ',', $comment_user->roles ); + } + } + + return $roles; + } + + // filter handler used to return a spam result to pre_comment_approved + public static function last_comment_status( $approved, $comment ) { + if ( is_null( self::$last_comment_result ) ) { + // We didn't have reason to store the result of the last check. + return $approved; + } + + // Only do this if it's the correct comment. + if ( ! self::matches_last_comment( $comment ) ) { + self::log( "comment_is_spam mismatched comment, returning unaltered $approved" ); + return $approved; + } + + if ( 'trash' === $approved ) { + // If the last comment we checked has had its approval set to 'trash', + // then it failed the comment blacklist check. Let that blacklist override + // the spam check, since users have the (valid) expectation that when + // they fill out their blacklists, comments that match it will always + // end up in the trash. + return $approved; + } + + // bump the counter here instead of when the filter is added to reduce the possibility of overcounting + if ( $incr = apply_filters( 'akismet_spam_count_incr', 1 ) ) { + update_option( 'akismet_spam_count', get_option( 'akismet_spam_count' ) + $incr ); + } + + return self::$last_comment_result; + } + + /** + * If Akismet is temporarily unreachable, we don't want to "spam" the blogger or post author + * with emails for comments that will be automatically cleared or spammed on the next retry. + * + * @param bool $maybe_notify Whether the notification email will be sent. + * @param int $comment_id The ID of the relevant comment. + * @return bool Whether the notification email should still be sent. + */ + public static function disable_emails_if_unreachable( $maybe_notify, $comment_id ) { + if ( $maybe_notify ) { + if ( get_comment_meta( $comment_id, 'akismet_delay_moderation_email', true ) ) { + self::log( 'Disabling notification email for comment #' . $comment_id ); + + update_comment_meta( $comment_id, 'akismet_delayed_moderation_email', true ); + delete_comment_meta( $comment_id, 'akismet_delay_moderation_email' ); + + // If we want to prevent the email from sending another time, we'll have to reset + // the akismet_delay_moderation_email commentmeta. + + return false; + } + } + + return $maybe_notify; + } + + /** + * Comparison function for sorting activity history entries by time. + * + * Used as a callback for usort() to sort activity entries in descending + * chronological order. Includes defensive validation to handle malformed + * data. + * + * @param mixed $a First comparison value (expected: array with 'time' key). + * @param mixed $b Second comparison value (expected: array with 'time' key). + * @return int Returns -1 if $a > $b, 1 if $a < $b, 0 if equal or both invalid. + */ + public static function _cmp_time( $a, $b ) { + // Validate entries to guard against malformed data. + // Third-party integrations may pass invalid data types. + $a_valid = is_array( $a ) && isset( $a['time'] ) && is_numeric( $a['time'] ); + $b_valid = is_array( $b ) && isset( $b['time'] ) && is_numeric( $b['time'] ); + + if ( $a_valid && $b_valid ) { + return (float) $b['time'] <=> (float) $a['time']; + } + + // Push invalid entries to the end of the sorted array. + if ( $a_valid && ! $b_valid ) { + return -1; + } + + if ( ! $a_valid && $b_valid ) { + return 1; + } + + // Both invalid; maintain relative order. + return 0; + } + + public static function _get_microtime() { + $mtime = explode( ' ', microtime() ); + return $mtime[1] + $mtime[0]; + } + + /** + * Make a POST request to the Akismet API. + * + * @param string $request The body of the request. + * @param string $path The path for the request. + * @param string $ip The specific IP address to hit. + * @return array A two-member array consisting of the headers and the response body, both empty in the case of a failure. + */ + public static function http_post( $request, $path, $ip = null ) { + + $akismet_ua = sprintf( 'WordPress/%s | Akismet/%s', $GLOBALS['wp_version'], constant( 'AKISMET_VERSION' ) ); + $akismet_ua = apply_filters( 'akismet_ua', $akismet_ua ); + + $host = self::API_HOST; + $api_key = self::get_api_key(); + + if ( $api_key ) { + $request = add_query_arg( 'api_key', $api_key, $request ); + } + + $http_host = $host; + // use a specific IP if provided + // needed by Akismet_Admin::check_server_connectivity() + if ( $ip && long2ip( ip2long( $ip ) ) ) { + $http_host = $ip; + } + + $http_args = array( + 'body' => $request, + 'headers' => array( + 'Content-Type' => 'application/x-www-form-urlencoded; charset=' . get_option( 'blog_charset' ), + 'Host' => $host, + 'User-Agent' => $akismet_ua, + ), + 'httpversion' => '1.0', + 'timeout' => 15, + ); + + $akismet_url = $http_akismet_url = "http://{$http_host}/1.1/{$path}"; + + /** + * Try SSL first; if that fails, try without it and don't try it again for a while. + */ + + $ssl = $ssl_failed = false; + + // Check if SSL requests were disabled fewer than X hours ago. + $ssl_disabled = get_option( 'akismet_ssl_disabled' ); + + if ( $ssl_disabled && $ssl_disabled < ( time() - 60 * 60 * 24 ) ) { // 24 hours + $ssl_disabled = false; + delete_option( 'akismet_ssl_disabled' ); + } elseif ( $ssl_disabled ) { + do_action( 'akismet_ssl_disabled' ); + } + + if ( ! $ssl_disabled && ( $ssl = wp_http_supports( array( 'ssl' ) ) ) ) { + $akismet_url = set_url_scheme( $akismet_url, 'https' ); + + do_action( 'akismet_https_request_pre' ); + } + + $response = wp_remote_post( $akismet_url, $http_args ); + + self::log( compact( 'akismet_url', 'http_args', 'response' ) ); + + if ( $ssl && is_wp_error( $response ) ) { + do_action( 'akismet_https_request_failure', $response ); + + // Intermittent connection problems may cause the first HTTPS + // request to fail and subsequent HTTP requests to succeed randomly. + // Retry the HTTPS request once before disabling SSL for a time. + $response = wp_remote_post( $akismet_url, $http_args ); + + self::log( compact( 'akismet_url', 'http_args', 'response' ) ); + + if ( is_wp_error( $response ) ) { + $ssl_failed = true; + + do_action( 'akismet_https_request_failure', $response ); + + do_action( 'akismet_http_request_pre' ); + + // Try the request again without SSL. + $response = wp_remote_post( $http_akismet_url, $http_args ); + + self::log( compact( 'http_akismet_url', 'http_args', 'response' ) ); + } + } + + if ( is_wp_error( $response ) ) { + do_action( 'akismet_request_failure', $response ); + + return array( '', '' ); + } + + if ( $ssl_failed ) { + // The request failed when using SSL but succeeded without it. Disable SSL for future requests. + update_option( 'akismet_ssl_disabled', time() ); + + do_action( 'akismet_https_disabled' ); + } + + $simplified_response = array( $response['headers'], $response['body'] ); + + $alert_code_check_paths = array( + 'verify-key', + 'comment-check', + 'get-stats', + ); + + if ( in_array( $path, $alert_code_check_paths ) ) { + self::update_alert( $simplified_response ); + } + + return $simplified_response; + } + + // given a response from an API call like check_key_status(), update the alert code options if an alert is present. + public static function update_alert( $response ) { + $alert_option_prefix = 'akismet_alert_'; + $alert_header_prefix = 'x-akismet-alert-'; + $alert_header_names = array( + 'code', + 'msg', + 'api-calls', + 'usage-limit', + 'upgrade-plan', + 'upgrade-url', + 'upgrade-type', + 'upgrade-via-support', + 'recommended-plan-name', + ); + + foreach ( $alert_header_names as $alert_header_name ) { + $value = null; + if ( isset( $response[0][ $alert_header_prefix . $alert_header_name ] ) ) { + $value = $response[0][ $alert_header_prefix . $alert_header_name ]; + } + + $option_name = $alert_option_prefix . str_replace( '-', '_', $alert_header_name ); + if ( $value != get_option( $option_name ) ) { + if ( ! $value ) { + delete_option( $option_name ); + } else { + update_option( $option_name, $value ); + } + } + } + } + + /** + * Mark akismet-frontend.js as deferred. Because nothing depends on it, it can run at any time + * after it's loaded, and the browser won't have to wait for it to load to continue + * parsing the rest of the page. + */ + public static function set_form_js_async( $tag, $handle, $src ) { + if ( 'akismet-frontend' !== $handle ) { + return $tag; + } + + return preg_replace( '/^ + loadCss(); + + wp_enqueue_script('dompurify', fluentCrmMix('libs/purify/purify.min.js'), [], $this->version, true); + + $inlineCss = Helper::generateThemePrefCss(); + wp_add_inline_style('fluentcrm_app_global', $inlineCss); + + remove_action('admin_print_scripts', 'print_emoji_detection_script'); + + add_filter('tiny_mce_plugins', function ($plugins) { + if (is_array($plugins)) { + return array_diff($plugins, array('wpemoji')); + } + return array(); + }); + + wp_localize_script('fluentcrm_admin_app_boot', 'fcAdmin', $this->getAdminVars()); + } + + public function getAdminVars() + { + $app = FluentCrm(); + + $tags = Tag::orderBy('title', 'ASC')->get(); + $formattedTags = []; + foreach ($tags as $tag) { + $formattedTags[] = [ + 'id' => strval($tag->id), + 'title' => $tag->title, + 'slug' => $tag->slug + ]; + } + + $lists = Lists::orderBy('title', 'ASC')->get(); + $formattedLists = []; + foreach ($lists as $list) { + $formattedLists[] = [ + 'id' => strval($list->id), + 'title' => $list->title, + 'slug' => $list->slug + ]; + } + + $currentUser = wp_get_current_user(); + + $activatedFeatures = Helper::getActivatedFeatures(); + + $postTypes = get_post_types(['public' => true], 'objects'); + unset($postTypes['attachment']); + + $formattedPostTypes = []; + + foreach ($postTypes as $postTypeName => $postType) { + $formattedPostTypes[] = [ + 'id' => $postTypeName, + 'title' => $postType->label + ]; + } + + $blockEditorUrl = site_url('?fluent_crm_block_editor=1'); + + if (current_user_can('edit_posts')) { + $blockEditorUrl = admin_url('post-new.php?fluent_crm_block_editor=1'); + } + + $existingSettings = get_option('fluentcrm-global-settings'); + $data = array( + 'business_settings' => [ + 'business_name' => Arr::get($existingSettings, 'business_settings.business_name'), + 'business_email' => Arr::get($existingSettings, 'business_settings.admin_email'), + 'business_address' => Arr::get($existingSettings, 'business_settings.business_address') + ], + 'images_url' => fluentCrmMix('images'), + 'ajaxurl' => admin_url('admin-ajax.php'), + 'ajax_nonce' => wp_create_nonce('fluentcrm_ajax_nonce'), + 'admin_url' => admin_url('admin.php?page=fluentcrm-admin#/'), + 'site_url' => site_url('/'), + 'slug' => FLUENTCRM, + 'rest' => $this->getRestInfo($app), + /** + * Filters the list of countries in FluentCRM. + * + * This filter allows you to modify the list of countries used in the application. + * + * @param array An array of countries. + */ + 'countries' => apply_filters('fluent_crm/countries', []), + 'contact_types' => fluentcrm_contact_types(), + 'purchase_providers' => Helper::getPurchaseHistoryProviders(), + /** + * Filters the form submission providers in FluentCRM. + * + * This filter allows you to modify the list of form submission providers. + * + * @param array An array of form submission providers. + */ + 'form_submission_providers' => apply_filters('fluent_crm/form_submission_providers', []), + /** + * Filters the list of support ticket providers in FluentCRM. + * + * This filter allows you to modify the array of support ticket providers. + * + * @param array An array of support ticket providers. + */ + 'support_tickets_providers' => apply_filters('fluentcrm-support_tickets_providers', []), + 'activity_types' => fluentcrm_activity_types(), + 'profile_sections' => Helper::getProfileSections(), + 'globalSmartCodes' => Helper::getGlobalSmartCodes(), + 'extendedSmartCodes' => Helper::getExtendedSmartCodes(), + 'addons' => $activatedFeatures, + 'email_template_designs' => Helper::getEmailDesignTemplates(), + 'contact_prefixes' => Helper::getContactPrefixes(), + 'contact_custom_fields' => fluentcrm_get_custom_contact_fields(), + 'server_time' => current_time('mysql'), + // Only users who can manage settings can hit the repair endpoint + // (SettingsPolicy → fcrm_manage_settings), so only they get a real + // health signal. Everyone else gets `true` — and the health check is + // skipped via short-circuit — so the SPA never fires a repair the + // server would just reject. For permitted users this is a cheap + // cached read (one fc_meta option) on the happy path; when it is + // false, app.js fires a one-off background repair on boot. + 'db_index_health_ok' => !PermissionManager::currentUserCan('fcrm_manage_settings') || !DbPerformanceService::hasBrokenIndex(false), + 'crm_pro_url' => 'https://fluentcrm.com/?utm_source=plugin&utm_medium=admin&utm_campaign=promo', + /** + * Determine if request verification is required in FluentCRM. + * + * This filter allows you to specify whether request verification is required. + * By default, it is set to false. + * + * @param bool Whether request verification is required. Default false. + */ + 'require_verify_request' => apply_filters('fluentcrm_is_require_verify', false), + 'trans' => TransStrings::getStrings(), + 'has_fluentsmtp' => defined('FLUENTMAIL'), + /** + * Determine if FluentMail suggestion should be disabled in FluentCRM. + * + * This filter allows customization of the FluentMail suggestion feature in FluentCRM. + * + * @return bool True if FluentMail suggestion is disabled, false otherwise. + */ + 'disable_fluentmail_suggest' => apply_filters('fluent_crm/fluentmail_suggest', defined('FLUENTMAIL')), + 'verified_senders' => $this->getVerifiedSenders(), + 'has_smart_link' => $this->hasSmartLink(), + 'auth' => [ + 'permissions' => PermissionManager::currentUserPermissions(), + 'first_name' => $currentUser->first_name, + 'last_name' => $currentUser->last_name, + 'email' => $currentUser->user_email, + 'avatar' => fluentcrmGetAvatarHtml($currentUser->user_email, $currentUser->display_name, 128), + 'user_id' => $currentUser->ID + ], + 'is_rtl' => fluentcrm_is_rtl(), + 'icons' => [ + 'trigger_icon' => 'fc-icon-trigger', + ], + /** + * Define the funnel category icons in FluentCRM. + * + * This filter allows you to change the icons used for different funnel categories in FluentCRM. + * + * @param array An associative array where the keys are funnel categories and values are arrays with: + * - 'svg' => (string) Inline SVG markup (highest priority) + * - 'icon' => (string) CSS icon class name (fallback) + */ + 'funnel_cat_icons' => apply_filters('fluent_crm/funnel_icons', [ + 'crm' => ['svg' => $this->getFunnelCatSvgIcon('crm')], + 'wordpresstriggers' => ['svg' => $this->getFunnelCatSvgIcon('wordpresstriggers')], + 'woocommerce' => ['svg' => $this->getFunnelCatSvgIcon('woocommerce')], + 'lifterlms' => ['svg' => $this->getFunnelCatSvgIcon('lifterlms')], + 'easydigitaldownloads' => ['icon' => 'fc-icon-edd'], + 'learndash' => ['svg' => $this->getFunnelCatSvgIcon('learndash')], + 'memberpress' => ['svg' => $this->getFunnelCatSvgIcon('memberpress')], + // Keep both keys for backward compatibility with existing filter integrations. + 'paidmembershippro' => ['svg' => $this->getFunnelCatSvgIcon('paidmembershipspro')], + 'paidmembershipspro' => ['svg' => $this->getFunnelCatSvgIcon('paidmembershipspro')], + 'restrictcontentpro' => ['icon' => 'fc-icon-restric_content'], + 'tutorlms' => ['svg' => $this->getFunnelCatSvgIcon('tutorlms')], + 'wishlistmember' => ['svg' => $this->getFunnelCatSvgIcon('wishlistmember')], + 'surecart' => ['svg' => $this->getFunnelCatSvgIcon('surecart')], + 'fluentforms' => ['svg' => $this->getFunnelCatSvgIcon('fluentforms')], + 'fluentboards' => ['svg' => $this->getFunnelCatSvgIcon('fluentboards')], + 'community' => ['svg' => $this->getFunnelCatSvgIcon('community')], + ]), + 'advanced_filter_options' => Helper::getAdvancedFilterOptions(), + /** + * Modify the advanced filter suggestions in FluentCRM. + * + * This filter allows you to modify the suggestions provided for the advanced filter. + * @return array Modified array of suggestions for the advanced filter. + */ + 'advanced_filter_suggestions' => apply_filters('fluentcrm_advanced_filter_suggestions', []), + /** + * Define the commerce provider in FluentCRM. + * + * This filter allows you to change the commerce provider used in FluentCRM. + * + * @param string The current commerce provider. Default is an empty string. + */ + 'commerce_provider' => apply_filters('fluentcrm_commerce_provider', ''), + /** + * Define the currency sign used in FluentCRM. + * + * This filter allows you to change the currency sign used in the FluentCRM plugin. + * + * @param string The current currency sign. Default is an empty string. + */ + 'commerce_currency_sign' => apply_filters('fluentcrm_currency_sign', ''), + 'disable_time_diff' => Helper::isExperimentalEnabled('classic_date_time'), + 'wp_date_time_format' => $this->getDefaultDateTimeFormatForMoment(), + 'disable_ai' => Helper::isExperimentalEnabled('disable_visual_ai'), + 'app_version' => FLUENTCRM_PLUGIN_VERSION, + 'available_tags' => $formattedTags, + 'available_lists' => $formattedLists, + 'available_funnel_label_colors' => Helper::funnelLabelColors(), + 'available_contact_statuses' => fluentcrm_subscriber_statuses(true), + 'available_contact_editable_statuses' => fluentcrm_subscriber_editable_statuses(true), + 'available_sms_statuses' => fluentcrm_subscriber_sms_statuses(true), + 'available_contact_types' => fluentcrm_contact_types(true), + 'available_custom_fields' => fluentcrm_get_option('contact_custom_fields', []), + 'contact_sample_csv' => fluentCrmMix('sample.csv'), + 'global_email_footer' => Helper::getEmailFooterContent(), + 'experimentals' => Helper::getExperimentalSettings(), + 'publicPostTypes' => $formattedPostTypes, + 'has_woo' => defined('WC_PLUGIN_FILE'), + 'debugs' => [ + '_fc_last_automation_processor' => get_option('_fc_last_funnel_processor_ran'), + '_fcrm_last_scheduler' => fluentCrmGetOptionCache('_fcrm_last_scheduler'), + '_fcrm_last_scheduler_for_sms' => fluentCrmGetOptionCache('_fcrm_last_scheduler_for_sms'), + ], + /** + * Determine the custom contact bulk actions in FluentCRM. + * + * This filter allows you to add or modify the bulk actions available for contacts in the FluentCRM admin interface. + * + * @param array An array of custom bulk actions for contacts. + */ + 'custom_contact_bulk_actions' => apply_filters('fluent_crm/custom_contact_bulk_actions', []), + 'crm_editor_frame' => $blockEditorUrl, + ); + + if (Arr::get($activatedFeatures, 'company_module')) { + $data['company_categories'] = Helper::companyCategories(); + $data['company_types'] = Helper::companyTypes(); + $data['company_profile_sections'] = Helper::getCompanyProfileSections(); + $data['company_custom_fields'] = fluentcrm_get_custom_company_fields(); + } + /** + * Filter the admin variables for FluentCRM. + * + * This filter allows modification of the admin variables used in FluentCRM. + * + * @param array $data The array of admin variables. + * @return array The filtered array of admin variables. + */ + return apply_filters('fluent_crm/admin_vars', $data); + } + + public function loadCss() + { + $isRtl = fluentcrm_is_rtl(); + + $v3Css = 'admin/css/app3.css'; + $appGlobalCss = 'admin/css/app_global.css'; + $vendorStyle = 'admin/css/style.css'; + + if ($isRtl) { + // Keep loading the base bundles in RTL mode; dedicated *-rtl build files + // are not produced in this Vite pipeline. RTL-specific tweaks live in + // admin_rtl.css and are loaded as an additive override below. + // $appGlobalCss = 'admin/css/app_global-rtl.css'; // TODO:: The CSS file does not exist. + wp_enqueue_style('fluentcrm_app_rtl', fluentCrmMix('admin/css/admin_rtl.css'), [], $this->version); + // style.css works for both LTR and RTL — Element Plus handles RTL natively via dir="rtl" + } + + // Legacy fluentcrm-admin.css eliminated — all styles now in app3.css. + // Register empty handle for backward compatibility (add-ons may depend on it). + wp_register_style('fluentcrm_admin_app', false); + wp_enqueue_style('fluentcrm_admin_app'); + + wp_enqueue_style('fluentcrm_app_global', fluentCrmMix($appGlobalCss), array(), $this->version); + wp_enqueue_style('fluentcrm_admin_app1', fluentCrmMix($v3Css), array(), $this->version); + // style.css is a build-only artifact (merged Vue/Element Plus chunk CSS). + // In dev mode, Vite injects this CSS via HMR automatically. + // Element Plus CSS is wrapped in @layer, so overrides always win regardless of load order. + if (!\FluentCrm\App\Vite::underDevelopment()) { + wp_enqueue_style('fluentcrm_vendor', fluentCrmMix($vendorStyle), array(), $this->version); + } + } + + protected function getRestInfo($app) + { + $ns = $app->config->get('app.rest_namespace'); + $v = $app->config->get('app.rest_version'); + + $restUrl = rest_url($ns . '/' . $v); + $restUrl = rtrim($restUrl, '/\\'); + return [ + 'base_url' => esc_url_raw(rest_url()), + 'url' => $restUrl, + 'nonce' => wp_create_nonce('wp_rest'), + 'namespace' => $ns, + 'version' => $v, + ]; + } + + public function emailBuilderBlockInit() + { + if (function_exists('wp_enqueue_media')) { + // Editor default styles. + add_filter('user_can_richedit', '__return_true'); + wp_tinymce_inline_scripts(); + wp_enqueue_editor(); + wp_enqueue_media(); + } + } + + private function getMenuIcon() + { + return 'data:image/svg+xml;base64,' . base64_encode(''); + } + + private function getVerifiedSenders() + { + $verifiedSenders = []; + if (defined('FLUENTMAIL')) { + $smtpSettings = get_option('fluentmail-settings', []); + $mappings = (array) Arr::get($smtpSettings, 'mappings', []); + if ($mappings) { + $verifiedSenders = array_keys($mappings); + } + } + + /** + * Determine the list of verified email senders in FluentCRM. + * + * This filter allows modification of the array of verified email senders. + * + * @param array $verifiedSenders An array of verified email senders. + * @return array Filtered array of verified email senders. + */ + return apply_filters('fluent_crm/verfied_email_senders', $verifiedSenders); + } + + private function hasSmartLink() + { + if (!defined('FLUENTCAMPAIGN')) { + return false; + } + + global $wpdb; + $table_name = $wpdb->prefix . 'fc_smart_links'; + $query = $wpdb->prepare('SHOW TABLES LIKE %s', $wpdb->esc_like($table_name)); + + // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared + if ($wpdb->get_var($query) == $table_name) { + return true; + } + + return false; + } + + private function getDefaultDateTimeFormatForMoment() + { + + $phpFormat = get_option('date_format') . ' ' . get_option('time_format'); + + $replacements = [ + 'A' => 'A', // for the sake of escaping below + 'a' => 'a', // for the sake of escaping below + 'B' => '', // Swatch internet time (.beats), no equivalent + 'c' => 'YYYY-MM-DD[T]HH:mm:ssZ', // ISO 8601 + 'D' => 'ddd', + 'd' => 'DD', + 'e' => 'zz', // deprecated since version 1.6.0 of moment.js + 'F' => 'MMMM', + 'G' => 'H', + 'g' => 'h', + 'H' => 'HH', + 'h' => 'hh', + 'I' => '', // Daylight Saving Time? => moment().isDST(); + 'i' => 'mm', + 'j' => 'D', + 'L' => '', // Leap year? => moment().isLeapYear(); + 'l' => 'dddd', + 'M' => 'MMM', + 'm' => 'MM', + 'N' => 'E', + 'n' => 'M', + 'O' => 'ZZ', + 'o' => 'YYYY', + 'P' => 'Z', + 'r' => 'ddd, DD MMM YYYY HH:mm:ss ZZ', // RFC 2822 + 'S' => 'o', + 's' => 'ss', + 'T' => 'z', // deprecated since version 1.6.0 of moment.js + 't' => '', // days in the month => moment().daysInMonth(); + 'U' => 'X', + 'u' => 'SSSSSS', // microseconds + 'v' => 'SSS', // milliseconds (from PHP 7.0.0) + 'W' => 'W', // for the sake of escaping below + 'w' => 'e', + 'Y' => 'YYYY', + 'y' => 'YY', + 'Z' => '', // time zone offset in minutes => moment().zone(); + 'z' => 'DDD', + ]; + + // Converts escaped characters. + foreach ($replacements as $from => $to) { + $replacements['\\' . $from] = '[' . $from . ']'; + } + + $format = strtr($phpFormat, $replacements); + + /** + * Determine the date and time format used in FluentCRM. + * + * This filter allows you to modify the date and time format used in FluentCRM. + * + * @param string $format The current date and time format. + * @return string The modified date and time format. + */ + return apply_filters('fluent_crm/moment_date_time_format', $format); + } + + private function unloadOtherScripts() + { + /** + * Determine whether to skip the no-conflict mode in FluentCRM. + * + * This filter allows you to skip the no-conflict mode by returning true. + * By default, it returns false, meaning the no-conflict mode is not skipped. + * + * @return bool Whether to skip the no-conflict mode. Default is false. + */ + $isSkip = apply_filters('fluent_crm/skip_no_conflict', false); + if ($isSkip) { + return; + } + + /** + * Define the list of approved slugs for FluentCRM assets. + * + * This filter allows modification of the list of slugs that are approved for FluentCRM assets. + * + * @param array $approvedSlugs An array of approved slugs for FluentCRM assets. + */ + $approvedSlugs = apply_filters('fluent_crm_asset_listed_slugs', [ + '\/gutenberg\/' + ]); + $approvedSlugs[] = 'fluent-crm'; + $approvedSlugs = array_unique($approvedSlugs); + $approvedSlugs = implode('|', $approvedSlugs); + + $pluginUrl = str_replace(['http:', 'https:'], '', plugins_url()); + + add_filter('script_loader_src', function ($src, $handle) use ($approvedSlugs, $pluginUrl) { + if (!$src) { + return $src; + } + + $willSkip = (strpos($src, $pluginUrl) !== false) && !preg_match('/' . $approvedSlugs . '/', $src); + if ($willSkip) { + return false; + } + return $src; + }, 1, 2); + + add_action('wp_print_scripts', function () { + global $wp_scripts; + if (!$wp_scripts) { + return; + } + + /** + * Define the list of approved slugs for FluentCRM assets. + * + * This filter allows modification of the list of slugs that are approved for FluentCRM assets. + * + * @param array $approvedSlugs An array of approved slugs for FluentCRM assets. + */ + $approvedSlugs = apply_filters('fluent_crm_asset_listed_slugs', [ + '\/gutenberg\/' + ]); + + $approvedSlugs[] = 'fluent-crm'; + + $approvedSlugs = array_unique($approvedSlugs); + + $approvedSlugs = implode('|', $approvedSlugs); + + $pluginUrl = plugins_url(); + + $pluginUrl = str_replace(['http:', 'https:'], '', $pluginUrl); + + foreach ($wp_scripts->queue as $script) { + if (empty($wp_scripts->registered[$script]) || empty($wp_scripts->registered[$script]->src)) { + continue; + } + + $src = $wp_scripts->registered[$script]->src; + $isMatched = (strpos($src, $pluginUrl) !== false) && !preg_match('/' . $approvedSlugs . '/', $src); + if (!$isMatched) { + continue; + } + + wp_dequeue_script($wp_scripts->registered[$script]->handle); + } + }, 1); + } + + private function getFunnelCatSvgIcon($key) + { + $icons = [ + 'crm' => '', + 'surecart' => '', + 'fluentboards' => '', + 'community' => '', + 'fluentforms' => '', + 'learndash' => '', + 'lifterlms' => '', + 'paidmembershipspro' => ' + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +', + 'wordpresstriggers' => '', + 'woocommerce' => '', + 'tutorlms' => '', + 'memberpress' => '', + 'wishlistmember' => '' + ]; + + return isset($icons[$key]) ? $icons[$key] : ''; + } +} diff --git a/wp-content/plugins/fluent-crm/app/Hooks/Handlers/AutoSubscribeHandler.php b/wp-content/plugins/fluent-crm/app/Hooks/Handlers/AutoSubscribeHandler.php new file mode 100644 index 0000000..46cc9de --- /dev/null +++ b/wp-content/plugins/fluent-crm/app/Hooks/Handlers/AutoSubscribeHandler.php @@ -0,0 +1,381 @@ +getRegistrationSettings(); + + if (Arr::get($settings, 'status') != 'yes') { + + $user = get_user_by('ID', $userId); + $contact = Subscriber::where('email', $user->user_email)->first(); + if ($contact && $contact->user_id != $user->ID) { + fluentCrmDb()->table('fc_subscribers') + ->where('id', $contact->id) + ->update([ + 'user_id' => $user->ID + ]); + } + + return false; + } + + $subscriberData = FunnelHelper::prepareUserData($userId); + if ($listId = Arr::get($settings, 'target_list')) { + $subscriberData['lists'] = [$listId]; + } + + if ($tags = Arr::get($settings, 'target_tags')) { + $subscriberData['tags'] = $tags; + } + + $isDoubleOptin = Arr::get($settings, 'double_optin') == 'yes'; + + if ($isDoubleOptin) { + $subscriberData['status'] = 'pending'; + } else { + $subscriberData['status'] = 'subscribed'; + } + + $contact = FunnelHelper::createOrUpdateContact($subscriberData); + + if (!$contact) { + return false; + } + + if ($contact->status == 'pending' && $subscriberData['status'] == 'pending') { + $contact->sendDoubleOptinEmail(); + } + + add_action('updated_user_meta', function ($meta_id, $userId, $meta_key, $_meta_value) use ($contact) { + if ($userId == $contact->user_id && ($meta_key == 'first_name' || $meta_key == 'last_name') && $_meta_value) { + if ($contact->{$meta_key} != $_meta_value) { + fluentCrmDb()->table('fc_subscribers') + ->where('id', $contact->id) + ->update([ + $meta_key => $_meta_value + ]); + } + } + }, 10, 4); + + } + + public function addSubscribeCheckbox($buttonHtml) + { + + $settings = (new AutoSubscribe())->getCommentSettings(); + + /** + * Determine the settings for the comment form subscribe feature in FluentCRM. + * + * This filter allows modification of the settings used for the comment form subscribe feature in FluentCRM. + * + * @param array $settings The current settings for the comment form subscribe feature. + * @return array The modified settings for the comment form subscribe feature. + * @since 2.7.0 + * + */ + $settings = apply_filters('fluent_crm/comment_form_subscribe_settings', $settings); + + if (Arr::get($settings, 'status') != 'yes') { + return $buttonHtml; + } + + if (Arr::get($settings, 'show_only_new') == 'yes') { + if ($userId = get_current_user_id()) { + $user = get_user_by('ID', $userId); + $contact = Subscriber::where('user_id', $userId)->orWhere('email', $user->user_email)->first(); + if ($contact && $contact->status == 'subscribed') { + return $buttonHtml; + } + } + } + + $label = Arr::get($settings, 'checkbox_label'); + if (!$label) { + $label = __('Subscribe to newsletter', 'fluent-crm'); + } + + $checkedTag = ''; + + if (Arr::get($settings, 'auto_checked') == 'yes') { + $checkedTag = 'checked="true"'; + } + + $html = ''; + + return $html . $buttonHtml; + } + + public function handleCommentPost($commentId, $isApproved, $commentData) + { + // is this a spam comment? + if ($isApproved === 'spam') { + return false; + } + + if (defined('WC_PLUGIN_FILE') && Arr::get($commentData, 'comment_type') == 'review') { + do_action('fluentcrm_woo_review_comment_post', $commentId, $isApproved, $commentData); + } + + $isChecked = Arr::get($_REQUEST, 'wp-comment-fc-consent') == 'yes'; + if (!$isChecked) { + return false; + } + + $subscriberData = [ + 'full_name' => Arr::get($commentData, 'comment_author'), + 'email' => Arr::get($commentData, 'comment_author_email'), + 'ip_address' => Arr::get($commentData, 'comment_author_IP') + ]; + + if ($userId = Arr::get($commentData, 'user_id')) { + $subscriberData['user_id'] = $userId; + } + + $subscriberData = array_filter($subscriberData); + + $settings = (new AutoSubscribe())->getCommentSettings(); + + if ($listId = Arr::get($settings, 'target_list')) { + $subscriberData['lists'] = [$listId]; + } + + if ($tags = Arr::get($settings, 'target_tags')) { + $subscriberData['tags'] = $tags; + } + + $isDoubleOptin = Arr::get($settings, 'double_optin') == 'yes'; + + if ($isDoubleOptin) { + $subscriberData['status'] = 'pending'; + } + + $contact = FunnelHelper::createOrUpdateContact($subscriberData); + + if (!$contact) { + return false; + } + + if (!$contact->country) { + // get CF Country from request header: CF-IPCountry + $countryCode = sanitize_text_field($_SERVER['HTTP_CF_IPCOUNTRY'] ?? ''); + if ($countryCode && preg_match('/^[A-Z]{2}$/', $countryCode) && $countryCode !== 'XX') { + $contact->country = $countryCode; + $contact->save(); + } + } + + + if ($contact->status == 'pending') { + $contact->sendDoubleOptinEmail(); + } + + return true; + } + + public function syncUserUpdate($userId, $oldData, $newData = []) + { + + if (is_multisite() && is_network_admin()) { + return false; + } + + if (!empty($newData['user_pass'])) { + $user = get_user_by('ID', $userId); + (new Cleanup())->handleUserPasswordChanged($user); + } + + if (!Helper::isUserSyncEnabled()) { + return false; + } + + // check if user email has been changed + $user = get_user_by('ID', $userId); + + if ($user->user_email != $oldData->user_email) { + // email has been changed + $oldSubscriber = Subscriber::where('email', $oldData->user_email)->first(); + + // check if a contact is exist with the new email id + $newSubscriber = Subscriber::where('email', $user->user_email)->first(); + + if ($newSubscriber) { + fluentCrmDb()->table('fc_subscribers') + ->where('id', $oldSubscriber->id) + ->update([ + 'user_id' => '' + ]); + $oldSubscriber = false; + } + + if ($oldSubscriber) { + $updateData = [ + 'email' => $user->user_email, + 'hash' => md5($user->user_email), + 'updated_at' => current_time('mysql'), + 'user_id' => $user->ID + ]; + + if ($user->first_name) { + $updateData['first_name'] = $user->first_name; + } + + if ($user->last_name) { + $updateData['last_name'] = $user->last_name; + } + + return fluentCrmDb()->table('fc_subscribers') + ->where('id', $oldSubscriber->id) + ->update($updateData); + } + } + + // we just have to change the first name and lastname + $updateData = Helper::getWPMapUserInfo($user); + + unset($updateData['email']); + + if (!$updateData) { + return false; + } + + $updateData['updated_at'] = current_time('mysql'); + + return fluentCrmDb()->table('fc_subscribers') + ->where('email', $user->user_email) + ->update($updateData); + } + + public function maybeDeleteContact($userId, $reassignId, $user) + { + if (is_multisite() && is_network_admin()) { + return false; + } + + if (!Helper::isContactDeleteOnUserDeleteEnabled()) { + return false; + } + + $subscriber = Subscriber::where('user_id', $userId)->first(); + if (!$subscriber) { + $subscriber = Subscriber::where('email', $user->user_email)->first(); + } + + if (!$subscriber) { + return false; + } + + return Helper::deleteContacts([$subscriber->id]); + } + + public function syncWooAddressUpdate($userId, $addressType) + { + if ($addressType != 'billing') { + return; + } + + $customer = new \WC_Customer($userId); + + if (!$customer || !$customer->get_id()) { + return; + } + + $user = get_user_by('ID', $userId); + $contact = Subscriber::where('email', $user->user_email)->first(); + + $addressData = $customer->get_billing(); + + $updateData = [ + 'user_id' => $userId, + 'address_line_1' => $addressData['address_1'], + 'address_line_2' => $addressData['address_2'], + 'city' => $addressData['city'], + 'state' => $addressData['state'], + 'country' => $addressData['country'], + 'postal_code' => $addressData['postcode'] + ]; + + if ($contact) { + $contact->fill($updateData); + $dirty = $contact->getDirty(); + if ($dirty) { + fluentCrmDb()->table('fc_subscribers') + ->where('id', $contact->id) + ->update($dirty); + $contact = Subscriber::find($contact->id); + do_action('fluent_crm/contact_updated', $contact, $dirty); + } + } else { + FluentCrmApi('contacts')->createOrUpdate($updateData); + } + } + + public function maybeAddCountryToProfile($userLogin, $wpUser) + { + // get CF Country from request header: CF-IPCountry + $countryCode = sanitize_text_field($_SERVER['HTTP_CF_IPCOUNTRY'] ?? ''); + + if (!$countryCode || !preg_match('/^[A-Z]{2}$/', $countryCode) || $countryCode === 'XX') { + return; + } + + $contact = Subscriber::where('email', $wpUser->user_email)->first(); + if (!$contact || $contact->country) { + return; + } + + $updateData = [ + 'country' => $countryCode + ]; + + if (empty($contact->user_id) || (int) $contact->user_id === (int) $wpUser->ID) { + $updateData['user_id'] = $wpUser->ID; + } + + fluentCrmDb()->table('fc_subscribers') + ->where('id', $contact->id) + ->update($updateData); + } + +} diff --git a/wp-content/plugins/fluent-crm/app/Hooks/Handlers/CampaignGuard.php b/wp-content/plugins/fluent-crm/app/Hooks/Handlers/CampaignGuard.php new file mode 100644 index 0000000..226a769 --- /dev/null +++ b/wp-content/plugins/fluent-crm/app/Hooks/Handlers/CampaignGuard.php @@ -0,0 +1,60 @@ +send('The campaign is not available anymore.'); + } + + $status = $campaign->status; + + if (!in_array($status, ['draft', 'pending', 'incomplete', 'purged', 'scheduled'])) { + $message = __('The campaign has been locked and not modifiable due to it\'s current status', 'fluent-crm'); + $message .= ": {$status}."; + $this->send($message); + } + + return; + } + + public function checkIsWorking($campaign) + { + if (!$campaign) { + $this->send('The campaign is not available anymore.'); + } + + $status = $campaign->status; + + if ($status == 'working') { + $message = __("The campaign has been locked and not deletable due to it's current status", "fluent-crm"); + $message .= ": {$status}."; + $this->send($message); + } + + return; + } + + protected function send($message) + { + FluentCrm('response')->sendError([ + 'status' => self::FORBIDDEN_CODE, + 'message' => "

{$message}

" + ], self::FORBIDDEN_CODE); + } +} diff --git a/wp-content/plugins/fluent-crm/app/Hooks/Handlers/Cleanup.php b/wp-content/plugins/fluent-crm/app/Hooks/Handlers/Cleanup.php new file mode 100644 index 0000000..9b5da9f --- /dev/null +++ b/wp-content/plugins/fluent-crm/app/Hooks/Handlers/Cleanup.php @@ -0,0 +1,373 @@ +delete(); + CampaignUrlMetric::whereIn('subscriber_id', $subscriberIds)->delete(); + SubscriberMeta::whereIn('subscriber_id', $subscriberIds)->delete(); + SubscriberNote::whereIn('subscriber_id', $subscriberIds)->delete(); + SubscriberPivot::whereIn('subscriber_id', $subscriberIds)->delete(); + FunnelMetric::whereIn('subscriber_id', $subscriberIds)->delete(); + FunnelSubscriber::whereIn('subscriber_id', $subscriberIds)->delete(); + + if (defined('FLUENTCAMPAIGN_DIR_FILE')) { + \FluentCampaign\App\Models\SequenceTracker::whereIn('subscriber_id', $subscriberIds)->delete(); + } + + if (Helper::isExperimentalEnabled('company_module')) { + Company::whereIn('owner_id', $subscriberIds) + ->update([ + 'owner_id' => NULL + ]); + } + + } + + /** + * Cleanup related data of a campaign. + * + * @param int $campaignId + */ + public function deleteCampaignAssets($campaignId) + { + // Idempotent backstop — Campaign::deleteCampaignData() already removes + // these in the normal delete flow, but we keep this here so any + // future caller that fires fluent_crm/campaign_deleted without + // running deleteCampaignData() first still gets a clean teardown. + CampaignEmail::where('campaign_id', $campaignId)->delete(); + CampaignUrlMetric::where('campaign_id', $campaignId)->delete(); + } + + /** + * Cleanup related data of a list. + * + * @param int $listId + */ + public function deleteListAssets($listId) + { + SubscriberPivot::where('object_type', 'FluentCrm\App\Models\Lists')->where('object_id', $listId)->delete(); + } + + /** + * Cleanup related data of a tag. + * + * @param int $listId + */ + public function deleteTagAssets($listId) + { + SubscriberPivot::where('object_type', 'FluentCrm\App\Models\Tag')->where('object_id', $listId)->delete(); + } + + /** + * Cancel Future Emails. + * + * @param \FluentCrm\App\Models\Subscriber $subscriber + */ + public function handleUnsubscribe($subscriber) + { + // Per-statement try/catch: a row-lock deadlock against the mailer + // workers on the CampaignEmail update should not also block the + // FunnelSubscriber / SequenceTracker cancellations. The next status + // transition (or a manual retry) will reconcile any rows we miss. + try { + CampaignEmail::where('subscriber_id', $subscriber->id) + ->whereIn('status', ['pending', 'scheduled', 'draft', 'processing', 'scheduling']) + ->update([ + 'status' => 'cancelled' + ]); + } catch (\Exception $e) { + Helper::debugLog('handleUnsubscribe', 'CampaignEmail cancel deferred: ' . $e->getMessage(), 'extended'); + } + + try { + FunnelSubscriber::where('subscriber_id', $subscriber->id) + ->where('status', 'active') + ->whereDoesntHave('funnel', function ($query) { + $query->where('trigger_name', 'fluent_crm/subscriber_status_changed'); + }) + ->update([ + 'status' => 'cancelled' + ]); + } catch (\Exception $e) { + Helper::debugLog('handleUnsubscribe', 'FunnelSubscriber cancel deferred: ' . $e->getMessage(), 'extended'); + } + + if (defined('FLUENTCAMPAIGN')) { + try { + \FluentCampaign\App\Models\SequenceTracker::where('subscriber_id', $subscriber->id) + ->where('status', 'active') + ->update([ + 'status' => 'cancelled' + ]); + } catch (\Exception $e) { + Helper::debugLog('handleUnsubscribe', 'SequenceTracker cancel deferred: ' . $e->getMessage(), 'extended'); + } + } + } + + /** + * Change the future emails email_address of a provided contact. + * + * @param \FluentCrm\App\Models\Subscriber $subscriber + */ + public function handleContactEmailChanged($subscriber) + { + CampaignEmail::where('subscriber_id', $subscriber->id) + ->whereIn('status', ['draft', 'scheduled']) + ->update([ + 'email_address' => $subscriber->email + ]); + } + + + /** + * @param $userId int + * @param $resign int|null + * @param $deletedUser \WP_User + * @return bool + */ + public function handleUserDelete($userId, $resign, $deletedUser) + { + $settings = Helper::getComplianceSettings(); + if ($settings['delete_contact_on_user'] !== 'yes') { + return false; + } + + $subscriber = Subscriber::where('user_id', $userId)->first(); + + if (!$subscriber && $deletedUser) { + $subscriber = Subscriber::where('email', $deletedUser->user_email)->first(); + } + + if (!$subscriber) { + return false; + } + + // delete the subscriber now; + Helper::deleteContacts([$subscriber->id]); + + return true; + } + + public function attachCrmExporter($exporters) + { + $settings = Helper::getComplianceSettings(); + if ($settings['personal_data_export'] !== 'yes') { + return $exporters; + } + + $exporters['fluent-crm'] = [ + 'exporter_friendly_name' => __('FluentCRM Data', 'fluent-crm'), + 'callback' => [$this, 'exportPersonalDataWP'], + ]; + + return $exporters; + + } + + public function exportPersonalDataWP($user_email, $page = 1) + { + $subscriber = Subscriber::where('email', $user_email)->first(); + + if (!$subscriber) { + return [ + 'data' => [], + 'done' => true + ]; + } + + $customerFields = $subscriber->custom_fields(); + $mainFields = $subscriber->toArray(); + + $data = [ + 'group_id' => 'fluent-crm-contact', + 'group_label' => __('FluentCRM Data', 'fluent-crm'), + 'item_id' => 'crm-contact', + 'data' => [] + ]; + + foreach ($mainFields as $fieldKey => $fieldValue) { + if ($fieldValue) { + $data['data'][] = [ + 'name' => $fieldKey, + 'value' => $fieldValue + ]; + } + } + + foreach ($customerFields as $fieldKey => $customerField) { + $data['data'][] = [ + 'name' => $fieldKey, + 'value' => $customerField + ]; + } + + return [ + 'data' => [$data], + 'done' => true, + ]; + } + + public function handleCompanyDelete($id) + { + /* + * Remove Company ID from all connected subscribers + */ + Subscriber::where('company_id', $id)->update([ + 'company_id' => NULL + ]); + + fluentCrmDb()->table('fc_subscriber_pivot') + ->where('object_id', $id) + ->where('object_type', 'FluentCrm\App\Models\Company') + ->delete(); + + // Delete company notes + CompanyNote::where('subscriber_id', $id)->delete(); + } + + public function handleUserPasswordChanged($user) + { + $contact = Subscriber::where('email', $user->user_email) + ->first(); + + if (!$contact) { + return false; + } + + $exist = SubscriberMeta::where('subscriber_id', $contact->id) + ->where('key', '_secure_managed_hash') + ->first(); + + if (!$exist) { + return false; + } + + $hash = md5(wp_generate_uuid4() . '_' . $contact->id . '_' . '_' . time() . '__' . $contact->id); + $exist->value = $hash; + $exist->updated_at = current_time('mysql'); + $exist->save(); + + return true; + } + + public function archiveCampaignAssets($campaign) + { + if ($campaign->type != 'campaign' || fluentcrm_get_campaign_meta($campaign->id, '_cached_email_body', true)) { + return; + } + + // We will create email body and then cache it for future use + $rawTemplates = [ + 'raw_html', + 'visual_builder', + 'raw_classic' + ]; + + if (in_array($campaign->design_template, $rawTemplates)) { + $emailBody = $campaign->email_body; + } else { + $emailBody = (new BlockParser())->parse($campaign->email_body); + } + + fluentcrm_update_campaign_meta($campaign->id, '_cached_email_body', $emailBody); + return true; + } + + public static function maybeRemoveOldScheuledActionLogs() + { + $group_slug = 'fluent-crm'; + $days_old = 7; + + global $wpdb; + + // Get the timestamp for 7 days ago + $cutoff_date = gmdate('Y-m-d H:i:s', strtotime("-{$days_old} days")); + + // Get the group ID + $group_id = $wpdb->get_var($wpdb->prepare( + "SELECT group_id FROM {$wpdb->prefix}actionscheduler_groups WHERE slug = %s", + $group_slug + )); + + if (!$group_id) { + return false; // Group not found + } + + // Delete old actions and their associated logs + $deleted = $wpdb->query($wpdb->prepare(" + DELETE a, l + FROM {$wpdb->prefix}actionscheduler_actions a + LEFT JOIN {$wpdb->prefix}actionscheduler_logs l ON a.action_id = l.action_id + WHERE a.group_id = %d + AND a.status IN ('complete', 'failed') + AND a.scheduled_date_gmt < %s", $group_id, $cutoff_date)); + + // Clean up orphaned claims + $wpdb->query(" + DELETE c + FROM {$wpdb->prefix}actionscheduler_claims c + LEFT JOIN {$wpdb->prefix}actionscheduler_actions a ON c.claim_id = a.claim_id + WHERE a.action_id IS NULL"); + + return $deleted; + } + + public function SyncSubscriberDeleteSettings($fromKey, $value) + { + if ($fromKey == 'compliance_settings') { + $option = Meta::where('key', 'user_syncing_settings') + ->where('object_type', 'option') + ->first(); + + if ($option) { + $settings = $option->value; + + if ($settings['delete_contact_on_user_delete'] != $value) { + $settings['delete_contact_on_user_delete'] = $value; + $option->value = $settings; + $option->save(); + } + } + } else { + $complianceSettings = get_option('_fluentcrm_compliance_settings'); + if ($complianceSettings) { + $complianceSettings['delete_contact_on_user'] = $value; + update_option('_fluentcrm_compliance_settings', $complianceSettings, 'no'); + } + } + } +} diff --git a/wp-content/plugins/fluent-crm/app/Hooks/Handlers/ContactActivityLogger.php b/wp-content/plugins/fluent-crm/app/Hooks/Handlers/ContactActivityLogger.php new file mode 100644 index 0000000..3ad686e --- /dev/null +++ b/wp-content/plugins/fluent-crm/app/Hooks/Handlers/ContactActivityLogger.php @@ -0,0 +1,169 @@ +ID, '_last_login', current_time('mysql')); + $this->trackActivityByUser($user, 'login'); + } + + public function trackActivityByUser($user, $type = '') + { + if (is_numeric($user)) { + $user = get_user_by('ID', $user); + } + if (!$user || empty($user->user_email)) { + return; + } + + $subscriber = Subscriber::where('email', $user->user_email)->first(); + + if (!$subscriber) { + return; + } + + $this->trackActivityBySubscriber($subscriber); + + if ($type == 'login') { + fluentcrm_update_subscriber_meta($subscriber->id, '_last_login', current_time('mysql')); + } + + return true; + } + + public function trackActivityBySubscriber($subscriber) + { + if (!$subscriber) { + return; + } + + if (is_numeric($subscriber)) { + $subscriber = Subscriber::where('id', $subscriber)->first(); + } + + if (!$subscriber) { + return; + } + + if ($subscriber->last_activity && strtotime($subscriber->last_activity) > (current_time('timestamp') - 3600)) { + return; + } + + $data = [ + 'last_activity' => current_time('mysql') + ]; + + if (!$subscriber->ip && fluentCrmWillTrackIp()) { + $ip = FluentCrm('request')->getIp(fluentCrmWillAnonymizeIp()); + if ($ip != '127.0.0.1') { + $data['ip'] = $ip; + } + } + + return fluentCrmDb()->table('fc_subscribers') + ->where('id', $subscriber->id) + ->update($data); + + } + + public function trackEmailOpenAnonymously($campaignEmaillModel) + { + if (!$campaignEmaillModel->campaign_id) { + return; + } + + // check if the campaign exist + global $wpdb; + $exists = $wpdb->get_var( + $wpdb->prepare( + "SELECT 1 FROM {$wpdb->prefix}fc_campaigns WHERE id = %d LIMIT 1", + $campaignEmaillModel->campaign_id + ) + ); + + if (!$exists) { + return; + } + + + $existingMetaModel = fluentcrm_get_campaign_meta($campaignEmaillModel->campaign_id, '_ano_open_count', false); + if ($existingMetaModel) { + global $wpdb; + $wpdb->query($wpdb->prepare( + "UPDATE {$wpdb->prefix}fc_meta SET value = value + 1 WHERE id = %d", + $existingMetaModel->id + )); + } else { + // we creating new one + Meta::create([ + 'key' => '_ano_open_count', + 'value' => 1, + 'object_id' => $campaignEmaillModel->campaign_id, + 'object_type' => 'FluentCrm\App\Models\Campaign' + ]); + } + + return true; + } + + public function trackEmailClickAnonymously($url, $campaign) + { + $existingMetaModel = fluentcrm_get_campaign_meta($campaign->id, '_ano_url_clicks', false); + + $url = (string)$url; + + if ($existingMetaModel) { + $urls = is_array($existingMetaModel->value) ? $existingMetaModel->value : []; + if (isset($urls[$url])) { + $urls[$url] = (int)$urls[$url] + 1; + } else { + $urls[$url] = 1; + } + + $existingMetaModel->value = $urls; + $existingMetaModel->save(); + } else { + Meta::create([ + 'key' => '_ano_url_clicks', + 'value' => [ + $url => 1 + ], + 'object_id' => $campaign->id, + 'object_type' => 'FluentCrm\App\Models\Campaign' + ]); + } + + return true; + } +} diff --git a/wp-content/plugins/fluent-crm/app/Hooks/Handlers/CountryNames.php b/wp-content/plugins/fluent-crm/app/Hooks/Handlers/CountryNames.php new file mode 100644 index 0000000..0818cd3 --- /dev/null +++ b/wp-content/plugins/fluent-crm/app/Hooks/Handlers/CountryNames.php @@ -0,0 +1,1038 @@ +names = [ + [ + 'code' => 'AF', + 'title' => __('Afghanistan', 'fluent-crm') + ], + [ + 'code' => 'AX', + 'title' => __('Åland Islands', 'fluent-crm') + ], + [ + 'code' => 'AL', + 'title' => __('Albania', 'fluent-crm') + ], + [ + 'code' => 'DZ', + 'title' => __('Algeria', 'fluent-crm') + ], + [ + 'code' => 'AS', + 'title' => __('American Samoa', 'fluent-crm') + ], + [ + 'code' => 'AD', + 'title' => __('Andorra', 'fluent-crm') + ], + [ + 'code' => 'AO', + 'title' => __('Angola', 'fluent-crm') + ], + [ + 'code' => 'AI', + 'title' => __('Anguilla', 'fluent-crm') + ], + [ + 'code' => 'AQ', + 'title' => __('Antarctica', 'fluent-crm') + ], + [ + 'code' => 'AG', + 'title' => __('Antigua and Barbuda', 'fluent-crm') + ], + [ + 'code' => 'AR', + 'title' => __('Argentina', 'fluent-crm') + ], + [ + 'code' => 'AM', + 'title' => __('Armenia', 'fluent-crm') + ], + [ + 'code' => 'AW', + 'title' => __('Aruba', 'fluent-crm') + ], + [ + 'code' => 'AU', + 'title' => __('Australia', 'fluent-crm') + ], + [ + 'code' => 'AT', + 'title' => __('Austria', 'fluent-crm') + ], + [ + 'code' => 'AZ', + 'title' => __('Azerbaijan', 'fluent-crm') + ], + [ + 'code' => 'BS', + 'title' => __('Bahamas', 'fluent-crm') + ], + [ + 'code' => 'BH', + 'title' => __('Bahrain', 'fluent-crm') + ], + [ + 'code' => 'BD', + 'title' => __('Bangladesh', 'fluent-crm') + ], + [ + 'code' => 'BB', + 'title' => __('Barbados', 'fluent-crm') + ], + [ + 'code' => 'BY', + 'title' => __('Belarus', 'fluent-crm') + ], + [ + 'code' => 'BE', + 'title' => __('Belgium', 'fluent-crm') + ], + [ + 'code' => 'PW', + 'title' => __('Belau', 'fluent-crm') + ], + [ + 'code' => 'BZ', + 'title' => __('Belize', 'fluent-crm') + ], + [ + 'code' => 'BJ', + 'title' => __('Benin', 'fluent-crm') + ], + [ + 'code' => 'BM', + 'title' => __('Bermuda', 'fluent-crm') + ], + [ + 'code' => 'BT', + 'title' => __('Bhutan', 'fluent-crm') + ], + [ + 'code' => 'BO', + 'title' => __('Bolivia', 'fluent-crm') + ], + [ + 'code' => 'BQ', + 'title' => __('Bonaire, Saint Eustatius and Saba', 'fluent-crm') + ], + [ + 'code' => 'BA', + 'title' => __('Bosnia and Herzegovina', 'fluent-crm') + ], + [ + 'code' => 'BW', + 'title' => __('Botswana', 'fluent-crm') + ], + [ + 'code' => 'BV', + 'title' => __('Bouvet Island', 'fluent-crm') + ], + [ + 'code' => 'BR', + 'title' => __('Brazil', 'fluent-crm') + ], + [ + 'code' => 'IO', + 'title' => __('British Indian Ocean Territory', 'fluent-crm') + ], + [ + 'code' => 'BN', + 'title' => __('Brunei', 'fluent-crm') + ], + [ + 'code' => 'BG', + 'title' => __('Bulgaria', 'fluent-crm') + ], + [ + 'code' => 'BF', + 'title' => __('Burkina Faso', 'fluent-crm') + ], + [ + 'code' => 'BI', + 'title' => __('Burundi', 'fluent-crm') + ], + [ + 'code' => 'KH', + 'title' => __('Cambodia', 'fluent-crm') + ], + [ + 'code' => 'CM', + 'title' => __('Cameroon', 'fluent-crm') + ], + [ + 'code' => 'CA', + 'title' => __('Canada', 'fluent-crm') + ], + [ + 'code' => 'CV', + 'title' => __('Cape Verde', 'fluent-crm') + ], + [ + 'code' => 'KY', + 'title' => __('Cayman Islands', 'fluent-crm') + ], + [ + 'code' => 'CF', + 'title' => __('Central African Republic', 'fluent-crm') + ], + [ + 'code' => 'TD', + 'title' => __('Chad', 'fluent-crm') + ], + [ + 'code' => 'CL', + 'title' => __('Chile', 'fluent-crm') + ], + [ + 'code' => 'CN', + 'title' => __('China', 'fluent-crm') + ], + [ + 'code' => 'CX', + 'title' => __('Christmas Island', 'fluent-crm') + ], + [ + 'code' => 'CC', + 'title' => __('Cocos (Keeling) Islands', 'fluent-crm') + ], + [ + 'code' => 'CO', + 'title' => __('Colombia', 'fluent-crm') + ], + [ + 'code' => 'KM', + 'title' => __('Comoros', 'fluent-crm') + ], + [ + 'code' => 'CG', + 'title' => __('Congo (Brazzaville)', 'fluent-crm') + ], + [ + 'code' => 'CD', + 'title' => __('Congo (Kinshasa)', 'fluent-crm') + ], + [ + 'code' => 'CK', + 'title' => __('Cook Islands', 'fluent-crm') + ], + [ + 'code' => 'CR', + 'title' => __('Costa Rica', 'fluent-crm') + ], + [ + 'code' => 'HR', + 'title' => __('Croatia', 'fluent-crm') + ], + [ + 'code' => 'CU', + 'title' => __('Cuba', 'fluent-crm') + ], + [ + 'code' => 'CW', + 'title' => __('Curaçao', 'fluent-crm') + ], + [ + 'code' => 'CY', + 'title' => __('Cyprus', 'fluent-crm') + ], + [ + 'code' => 'CZ', + 'title' => __('Czechia (Czech Republic)', 'fluent-crm') + ], + [ + 'code' => 'DK', + 'title' => __('Denmark', 'fluent-crm') + ], + [ + 'code' => 'DJ', + 'title' => __('Djibouti', 'fluent-crm') + ], + [ + 'code' => 'DM', + 'title' => __('Dominica', 'fluent-crm') + ], + [ + 'code' => 'DO', + 'title' => __('Dominican Republic', 'fluent-crm') + ], + [ + 'code' => 'EC', + 'title' => __('Ecuador', 'fluent-crm') + ], + [ + 'code' => 'EG', + 'title' => __('Egypt', 'fluent-crm') + ], + [ + 'code' => 'SV', + 'title' => __('El Salvador', 'fluent-crm') + ], + [ + 'code' => 'GQ', + 'title' => __('Equatorial Guinea', 'fluent-crm') + ], + [ + 'code' => 'ER', + 'title' => __('Eritrea', 'fluent-crm') + ], + [ + 'code' => 'EE', + 'title' => __('Estonia', 'fluent-crm') + ], + [ + 'code' => 'ET', + 'title' => __('Ethiopia', 'fluent-crm') + ], + [ + 'code' => 'FK', + 'title' => __('Falkland Islands', 'fluent-crm') + ], + [ + 'code' => 'FO', + 'title' => __('Faroe Islands', 'fluent-crm') + ], + [ + 'code' => 'FJ', + 'title' => __('Fiji', 'fluent-crm') + ], + [ + 'code' => 'FI', + 'title' => __('Finland', 'fluent-crm') + ], + [ + 'code' => 'FR', + 'title' => __('France', 'fluent-crm') + ], + [ + 'code' => 'GF', + 'title' => __('French Guiana', 'fluent-crm') + ], + [ + 'code' => 'PF', + 'title' => __('French Polynesia', 'fluent-crm') + ], + [ + 'code' => 'TF', + 'title' => __('French Southern Territories', 'fluent-crm') + ], + [ + 'code' => 'GA', + 'title' => __('Gabon', 'fluent-crm') + ], + [ + 'code' => 'GM', + 'title' => __('Gambia', 'fluent-crm') + ], + [ + 'code' => 'GE', + 'title' => __('Georgia', 'fluent-crm') + ], + [ + 'code' => 'DE', + 'title' => __('Germany', 'fluent-crm') + ], + [ + 'code' => 'GH', + 'title' => __('Ghana', 'fluent-crm') + ], + [ + 'code' => 'GI', + 'title' => __('Gibraltar', 'fluent-crm') + ], + [ + 'code' => 'GR', + 'title' => __('Greece', 'fluent-crm') + ], + [ + 'code' => 'GL', + 'title' => __('Greenland', 'fluent-crm') + ], + [ + 'code' => 'GD', + 'title' => __('Grenada', 'fluent-crm') + ], + [ + 'code' => 'GP', + 'title' => __('Guadeloupe', 'fluent-crm') + ], + [ + 'code' => 'GU', + 'title' => __('Guam', 'fluent-crm') + ], + [ + 'code' => 'GT', + 'title' => __('Guatemala', 'fluent-crm') + ], + [ + 'code' => 'GG', + 'title' => __('Guernsey', 'fluent-crm') + ], + [ + 'code' => 'GN', + 'title' => __('Guinea', 'fluent-crm') + ], + [ + 'code' => 'GW', + 'title' => __('Guinea-Bissau', 'fluent-crm') + ], + [ + 'code' => 'GY', + 'title' => __('Guyana', 'fluent-crm') + ], + [ + 'code' => 'HT', + 'title' => __('Haiti', 'fluent-crm') + ], + [ + 'code' => 'HM', + 'title' => __('Heard Island and McDonald Islands', 'fluent-crm') + ], + [ + 'code' => 'HN', + 'title' => __('Honduras', 'fluent-crm') + ], + [ + 'code' => 'HK', + 'title' => __('Hong Kong', 'fluent-crm') + ], + [ + 'code' => 'HU', + 'title' => __('Hungary', 'fluent-crm') + ], + [ + 'code' => 'IS', + 'title' => __('Iceland', 'fluent-crm') + ], + [ + 'code' => 'IN', + 'title' => __('India', 'fluent-crm') + ], + [ + 'code' => 'ID', + 'title' => __('Indonesia', 'fluent-crm') + ], + [ + 'code' => 'IR', + 'title' => __('Iran', 'fluent-crm') + ], + [ + 'code' => 'IQ', + 'title' => __('Iraq', 'fluent-crm') + ], + [ + 'code' => 'IE', + 'title' => __('Ireland', 'fluent-crm') + ], + [ + 'code' => 'IM', + 'title' => __('Isle of Man', 'fluent-crm') + ], + [ + 'code' => 'IL', + 'title' => __('Israel', 'fluent-crm') + ], + [ + 'code' => 'IT', + 'title' => __('Italy', 'fluent-crm') + ], + [ + 'code' => 'CI', + 'title' => __('Ivory Coast', 'fluent-crm') + ], + [ + 'code' => 'JM', + 'title' => __('Jamaica', 'fluent-crm') + ], + [ + 'code' => 'JP', + 'title' => __('Japan', 'fluent-crm') + ], + [ + 'code' => 'JE', + 'title' => __('Jersey', 'fluent-crm') + ], + [ + 'code' => 'JO', + 'title' => __('Jordan', 'fluent-crm') + ], + [ + 'code' => 'KZ', + 'title' => __('Kazakhstan', 'fluent-crm') + ], + [ + 'code' => 'KE', + 'title' => __('Kenya', 'fluent-crm') + ], + [ + 'code' => 'KI', + 'title' => __('Kiribati', 'fluent-crm') + ], + [ + 'code' => 'KW', + 'title' => __('Kuwait', 'fluent-crm') + ], + [ + 'code' => 'XK', + 'title' => __('Kosovo', 'fluent-crm') + ], + [ + 'code' => 'KG', + 'title' => __('Kyrgyzstan', 'fluent-crm') + ], + [ + 'code' => 'LA', + 'title' => __('Laos', 'fluent-crm') + ], + [ + 'code' => 'LV', + 'title' => __('Latvia', 'fluent-crm') + ], + [ + 'code' => 'LB', + 'title' => __('Lebanon', 'fluent-crm') + ], + [ + 'code' => 'LS', + 'title' => __('Lesotho', 'fluent-crm') + ], + [ + 'code' => 'LR', + 'title' => __('Liberia', 'fluent-crm') + ], + [ + 'code' => 'LY', + 'title' => __('Libya', 'fluent-crm') + ], + [ + 'code' => 'LI', + 'title' => __('Liechtenstein', 'fluent-crm') + ], + [ + 'code' => 'LT', + 'title' => __('Lithuania', 'fluent-crm') + ], + [ + 'code' => 'LU', + 'title' => __('Luxembourg', 'fluent-crm') + ], + [ + 'code' => 'MO', + 'title' => __('Macao', 'fluent-crm') + ], + [ + 'code' => 'MK', + 'title' => __('North Macedonia', 'fluent-crm') + ], + [ + 'code' => 'MG', + 'title' => __('Madagascar', 'fluent-crm') + ], + [ + 'code' => 'MW', + 'title' => __('Malawi', 'fluent-crm') + ], + [ + 'code' => 'MY', + 'title' => __('Malaysia', 'fluent-crm') + ], + [ + 'code' => 'MV', + 'title' => __('Maldives', 'fluent-crm') + ], + [ + 'code' => 'ML', + 'title' => __('Mali', 'fluent-crm') + ], + [ + 'code' => 'MT', + 'title' => __('Malta', 'fluent-crm') + ], + [ + 'code' => 'MH', + 'title' => __('Marshall Islands', 'fluent-crm') + ], + [ + 'code' => 'MQ', + 'title' => __('Martinique', 'fluent-crm') + ], + [ + 'code' => 'MR', + 'title' => __('Mauritania', 'fluent-crm') + ], + [ + 'code' => 'MU', + 'title' => __('Mauritius', 'fluent-crm') + ], + [ + 'code' => 'YT', + 'title' => __('Mayotte', 'fluent-crm') + ], + [ + 'code' => 'MX', + 'title' => __('Mexico', 'fluent-crm') + ], + [ + 'code' => 'FM', + 'title' => __('Micronesia', 'fluent-crm') + ], + [ + 'code' => 'MD', + 'title' => __('Moldova', 'fluent-crm') + ], + [ + 'code' => 'MC', + 'title' => __('Monaco', 'fluent-crm') + ], + [ + 'code' => 'MN', + 'title' => __('Mongolia', 'fluent-crm') + ], + [ + 'code' => 'ME', + 'title' => __('Montenegro', 'fluent-crm') + ], + [ + 'code' => 'MS', + 'title' => __('Montserrat', 'fluent-crm') + ], + [ + 'code' => 'MA', + 'title' => __('Morocco', 'fluent-crm') + ], + [ + 'code' => 'MZ', + 'title' => __('Mozambique', 'fluent-crm') + ], + [ + 'code' => 'MM', + 'title' => __('Myanmar', 'fluent-crm') + ], + [ + 'code' => 'NA', + 'title' => __('Namibia', 'fluent-crm') + ], + [ + 'code' => 'NR', + 'title' => __('Nauru', 'fluent-crm') + ], + [ + 'code' => 'NP', + 'title' => __('Nepal', 'fluent-crm') + ], + [ + 'code' => 'NL', + 'title' => __('Netherlands', 'fluent-crm') + ], + [ + 'code' => 'NC', + 'title' => __('New Caledonia', 'fluent-crm') + ], + [ + 'code' => 'NZ', + 'title' => __('New Zealand', 'fluent-crm') + ], + [ + 'code' => 'NI', + 'title' => __('Nicaragua', 'fluent-crm') + ], + [ + 'code' => 'NE', + 'title' => __('Niger', 'fluent-crm') + ], + [ + 'code' => 'NG', + 'title' => __('Nigeria', 'fluent-crm') + ], + [ + 'code' => 'NU', + 'title' => __('Niue', 'fluent-crm') + ], + [ + 'code' => 'NF', + 'title' => __('Norfolk Island', 'fluent-crm') + ], + [ + 'code' => 'MP', + 'title' => __('Northern Mariana Islands', 'fluent-crm') + ], + [ + 'code' => 'KP', + 'title' => __('North Korea', 'fluent-crm') + ], + [ + 'code' => 'NO', + 'title' => __('Norway', 'fluent-crm') + ], + [ + 'code' => 'OM', + 'title' => __('Oman', 'fluent-crm') + ], + [ + 'code' => 'PK', + 'title' => __('Pakistan', 'fluent-crm') + ], + [ + 'code' => 'PS', + 'title' => __('Palestinian Territory', 'fluent-crm') + ], + [ + 'code' => 'PA', + 'title' => __('Panama', 'fluent-crm') + ], + [ + 'code' => 'PG', + 'title' => __('Papua New Guinea', 'fluent-crm') + ], + [ + 'code' => 'PY', + 'title' => __('Paraguay', 'fluent-crm') + ], + [ + 'code' => 'PE', + 'title' => __('Peru', 'fluent-crm') + ], + [ + 'code' => 'PH', + 'title' => __('Philippines', 'fluent-crm') + ], + [ + 'code' => 'PN', + 'title' => __('Pitcairn', 'fluent-crm') + ], + [ + 'code' => 'PL', + 'title' => __('Poland', 'fluent-crm') + ], + [ + 'code' => 'PT', + 'title' => __('Portugal', 'fluent-crm') + ], + [ + 'code' => 'PR', + 'title' => __('Puerto Rico', 'fluent-crm') + ], + [ + 'code' => 'QA', + 'title' => __('Qatar', 'fluent-crm') + ], + [ + 'code' => 'RE', + 'title' => __('Reunion', 'fluent-crm') + ], + [ + 'code' => 'RO', + 'title' => __('Romania', 'fluent-crm') + ], + [ + 'code' => 'RU', + 'title' => __('Russia', 'fluent-crm') + ], + [ + 'code' => 'RW', + 'title' => __('Rwanda', 'fluent-crm') + ], + [ + 'code' => 'BL', + 'title' => __('Saint Barthélemy', 'fluent-crm') + ], + [ + 'code' => 'SH', + 'title' => __('Saint Helena', 'fluent-crm') + ], + [ + 'code' => 'KN', + 'title' => __('Saint Kitts and Nevis', 'fluent-crm') + ], + [ + 'code' => 'LC', + 'title' => __('Saint Lucia', 'fluent-crm') + ], + [ + 'code' => 'MF', + 'title' => __('Saint Martin (French part)', 'fluent-crm') + ], + [ + 'code' => 'SX', + 'title' => __('Saint Martin (Dutch part)', 'fluent-crm') + ], + [ + 'code' => 'PM', + 'title' => __('Saint Pierre and Miquelon', 'fluent-crm') + ], + [ + 'code' => 'VC', + 'title' => __('Saint Vincent and the Grenadines', 'fluent-crm') + ], + [ + 'code' => 'SM', + 'title' => __('San Marino', 'fluent-crm') + ], + [ + 'code' => 'ST', + 'title' => __('São Tomé and Príncipe', 'fluent-crm') + ], + [ + 'code' => 'SA', + 'title' => __('Saudi Arabia', 'fluent-crm') + ], + [ + 'code' => 'SN', + 'title' => __('Senegal', 'fluent-crm') + ], + [ + 'code' => 'RS', + 'title' => __('Serbia', 'fluent-crm') + ], + [ + 'code' => 'SC', + 'title' => __('Seychelles', 'fluent-crm') + ], + [ + 'code' => 'SL', + 'title' => __('Sierra Leone', 'fluent-crm') + ], + [ + 'code' => 'SG', + 'title' => __('Singapore', 'fluent-crm') + ], + [ + 'code' => 'SK', + 'title' => __('Slovakia', 'fluent-crm') + ], + [ + 'code' => 'SI', + 'title' => __('Slovenia', 'fluent-crm') + ], + [ + 'code' => 'SB', + 'title' => __('Solomon Islands', 'fluent-crm') + ], + [ + 'code' => 'SO', + 'title' => __('Somalia', 'fluent-crm') + ], + [ + 'code' => 'ZA', + 'title' => __('South Africa', 'fluent-crm') + ], + [ + 'code' => 'GS', + 'title' => __('South Georgia/Sandwich Islands', 'fluent-crm') + ], + [ + 'code' => 'KR', + 'title' => __('South Korea', 'fluent-crm') + ], + [ + 'code' => 'SS', + 'title' => __('South Sudan', 'fluent-crm') + ], + [ + 'code' => 'ES', + 'title' => __('Spain', 'fluent-crm') + ], + [ + 'code' => 'LK', + 'title' => __('Sri Lanka', 'fluent-crm') + ], + [ + 'code' => 'SD', + 'title' => __('Sudan', 'fluent-crm') + ], + [ + 'code' => 'SR', + 'title' => __('Suriname', 'fluent-crm') + ], + [ + 'code' => 'SJ', + 'title' => __('Svalbard and Jan Mayen', 'fluent-crm') + ], + [ + 'code' => 'SZ', + 'title' => __('Swaziland', 'fluent-crm') + ], + [ + 'code' => 'SE', + 'title' => __('Sweden', 'fluent-crm') + ], + [ + 'code' => 'CH', + 'title' => __('Switzerland', 'fluent-crm') + ], + [ + 'code' => 'SY', + 'title' => __('Syria', 'fluent-crm') + ], + [ + 'code' => 'TW', + 'title' => __('Taiwan', 'fluent-crm') + ], + [ + 'code' => 'TJ', + 'title' => __('Tajikistan', 'fluent-crm') + ], + [ + 'code' => 'TZ', + 'title' => __('Tanzania', 'fluent-crm') + ], + [ + 'code' => 'TH', + 'title' => __('Thailand', 'fluent-crm') + ], + [ + 'code' => 'TL', + 'title' => __('Timor-Leste', 'fluent-crm') + ], + [ + 'code' => 'TG', + 'title' => __('Togo', 'fluent-crm') + ], + [ + 'code' => 'TK', + 'title' => __('Tokelau', 'fluent-crm') + ], + [ + 'code' => 'TO', + 'title' => __('Tonga', 'fluent-crm') + ], + [ + 'code' => 'TT', + 'title' => __('Trinidad and Tobago', 'fluent-crm') + ], + [ + 'code' => 'TN', + 'title' => __('Tunisia', 'fluent-crm') + ], + [ + 'code' => 'TR', + 'title' => __('Turkey', 'fluent-crm') + ], + [ + 'code' => 'TM', + 'title' => __('Turkmenistan', 'fluent-crm') + ], + [ + 'code' => 'TC', + 'title' => __('Turks and Caicos Islands', 'fluent-crm') + ], + [ + 'code' => 'TV', + 'title' => __('Tuvalu', 'fluent-crm') + ], + [ + 'code' => 'UG', + 'title' => __('Uganda', 'fluent-crm') + ], + [ + 'code' => 'UA', + 'title' => __('Ukraine', 'fluent-crm') + ], + [ + 'code' => 'AE', + 'title' => __('United Arab Emirates', 'fluent-crm') + ], + [ + 'code' => 'GB', + 'title' => __('United Kingdom (UK)', 'fluent-crm') + ], + [ + 'code' => 'US', + 'title' => __('United States (US)', 'fluent-crm') + ], + [ + 'code' => 'UM', + 'title' => __('United States (US) Minor Outlying Islands', 'fluent-crm') + ], + [ + 'code' => 'UY', + 'title' => __('Uruguay', 'fluent-crm') + ], + [ + 'code' => 'UZ', + 'title' => __('Uzbekistan', 'fluent-crm') + ], + [ + 'code' => 'VU', + 'title' => __('Vanuatu', 'fluent-crm') + ], + [ + 'code' => 'VA', + 'title' => __('Vatican', 'fluent-crm') + ], + [ + 'code' => 'VE', + 'title' => __('Venezuela', 'fluent-crm') + ], + [ + 'code' => 'VN', + 'title' => __('Vietnam', 'fluent-crm') + ], + [ + 'code' => 'VG', + 'title' => __('Virgin Islands (British)', 'fluent-crm') + ], + [ + 'code' => 'VI', + 'title' => __('Virgin Islands (US)', 'fluent-crm') + ], + [ + 'code' => 'WF', + 'title' => __('Wallis and Futuna', 'fluent-crm') + ], + [ + 'code' => 'EH', + 'title' => __('Western Sahara', 'fluent-crm') + ], + [ + 'code' => 'WS', + 'title' => __('Samoa', 'fluent-crm') + ], + [ + 'code' => 'YE', + 'title' => __('Yemen', 'fluent-crm') + ], + [ + 'code' => 'ZM', + 'title' => __('Zambia', 'fluent-crm') + ], + [ + 'code' => 'ZW', + 'title' => __('Zimbabwe', 'fluent-crm') + ], + ]; + } + + /** + * Get an array of all the country names. + * + * @return array + */ + public function get() + { + if (!$this->names) { + $this->setNames(); + } + + return $this->names; + } +} diff --git a/wp-content/plugins/fluent-crm/app/Hooks/Handlers/DeactivationHandler.php b/wp-content/plugins/fluent-crm/app/Hooks/Handlers/DeactivationHandler.php new file mode 100644 index 0000000..17c20fc --- /dev/null +++ b/wp-content/plugins/fluent-crm/app/Hooks/Handlers/DeactivationHandler.php @@ -0,0 +1,29 @@ +filterTemplateData($templateData); + $templateData['email_body'] = $emailBody; + + $view = FluentCrm('view'); + $emailBody = $view->make('emails.block_editor.Template', $templateData); + $emailBody = $emailBody->__toString(); + + $emogrifier = new Emogrifier($emailBody); + $emogrifier->disableInvisibleNodeRemoval(); + return $emogrifier->emogrify(); + } + + /** + * @param string $emailBody + * @param array $templateData + * @param \FluentCrm\App\Models\Campaign $campaign + * @return string + */ + public function addPlainTemplate($emailBody, $templateData, $campaign) + { + $templateData = $this->filterTemplateData($templateData); + + $view = FluentCrm('view'); + $emailBody = $view->make('emails.plain.Template', $templateData); + $emailBody = $emailBody->__toString(); + + $emogrifier = new Emogrifier($emailBody); + $emogrifier->disableInvisibleNodeRemoval(); + return $emogrifier->emogrify(); + } + + /** + * @param string $emailBody + * @param array $templateData + * @param \FluentCrm\App\Models\Campaign $campaign + * @return string + */ + public function addSimpleTemplate($emailBody, $templateData, $campaign) + { + if (empty($templateData['config']['body_bg_color'])) { + $templateData['config']['body_bg_color'] = '#FAFAFA'; + } + + if (empty($templateData['config']['content_bg_color'])) { + $templateData['config']['content_bg_color'] = '#ffffff'; + } + + $templateData = $this->filterTemplateData($templateData); + + $view = FluentCrm('view'); + $emailBody = $view->make('emails.simple.Template', $templateData); + $emailBody = $emailBody->__toString(); + $emogrifier = new Emogrifier($emailBody); + $emogrifier->disableInvisibleNodeRemoval(); + return $emogrifier->emogrify(); + } + + /** + * @param string $emailBody + * @param array $templateData + * @param \FluentCrm\App\Models\Campaign $campaign + * @return string + */ + public function addClassicTemplate($emailBody, $templateData, $campaign) + { + if (empty($templateData['config']['content_bg_color'])) { + $templateData['config']['content_bg_color'] = '#ffffff'; + } + + $templateData = $this->filterTemplateData($templateData); + + $view = FluentCrm('view'); + $emailBody = $view->make('emails.classic.Template', $templateData); + $emailBody = $emailBody->__toString(); + + $emogrifier = new Emogrifier($emailBody); + $emogrifier->disableInvisibleNodeRemoval(); + return $emogrifier->emogrify(); + } + + /** + * @param string $emailBody + * @param array $templateData + * @param \FluentCrm\App\Models\Campaign $campaign + * @return string + */ + public function addRawClassicTemplate($emailBody, $templateData, $campaign) + { + $templateData = $this->filterTemplateData($templateData); + + $configDefault = [ + 'content_width' => '', + 'content_padding' => '', + 'headings_font_family' => '', + 'text_color' => '', + 'link_color' => '', + 'body_bg_color' => '', + 'content_bg_color' => '', + 'footer_text_color' => '', + 'content_font_family' => "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'", + 'paragraph_color' => '', + 'paragraph_font_size' => '', + 'paragraph_font_family' => '', + 'paragraph_line_height' => '', + 'headings_color' => '' + ]; + + $templateData['config'] = wp_parse_args($templateData['config'], $configDefault); + + $view = FluentCrm('view'); + $emailBody = $view->make('emails.raw_classic.Template', $templateData); + $emailBody = $emailBody->__toString(); + $emogrifier = new Emogrifier($emailBody); + $emogrifier->disableInvisibleNodeRemoval(); + return $emogrifier->emogrify(); + } + + public function addWebPreviewTemplate($emailBody, $templateData, $campaign) + { + $templateData = $this->filterTemplateData($templateData); + + $configDefault = [ + 'content_width' => '', + 'content_padding' => '', + 'headings_font_family' => '', + 'text_color' => '', + 'link_color' => '', + 'body_bg_color' => '', + 'content_bg_color' => '', + 'footer_text_color' => '', + 'content_font_family' => "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'", + 'paragraph_color' => '', + 'paragraph_font_size' => '', + 'paragraph_font_family' => '', + 'paragraph_line_height' => '', + 'headings_color' => '' + ]; + + $templateData['config'] = wp_parse_args($templateData['config'], $configDefault); + + $view = FluentCrm('view'); + $emailBody = $view->make('emails.web_preview.Template', $templateData); + $emailBody = $emailBody->__toString(); + $emogrifier = new Emogrifier($emailBody); + $emogrifier->disableInvisibleNodeRemoval(); + return $emogrifier->emogrify(); + } + + private function filterTemplateData($templateData) + { + $footerConfig = Arr::get($templateData, 'footer_config', []); + $disableFooter = Arr::get($footerConfig, 'disable_footer'); + if ($disableFooter !== 'yes' && $disableFooter !== 'no') { + $disableFooter = Arr::get($templateData, 'config.disable_footer'); + } + + if ($disableFooter == 'yes') { + $templateData['footer_text'] = ''; + } else { + $style = 'font-size: 13px; color: #202020;'; + if ($footerConfig) { + $fontSize = Arr::get($footerConfig, 'font_size', 13) . 'px'; + $color = sanitize_hex_color(Arr::get($footerConfig, 'font_color', '#202020')) ?: '#202020'; + $backgroundColor = Arr::get($footerConfig, 'background_color', 'transparent'); + $paddingRaw = Arr::get($footerConfig, 'footer_padding'); + $safeBackgroundColor = sanitize_hex_color($backgroundColor); + if ($backgroundColor === 'transparent') { + $safeBackgroundColor = 'transparent'; + } + + $safePadding = 20; + if ($paddingRaw !== null && $paddingRaw !== '') { + $safePadding = min(80, max(0, intval($paddingRaw))); + } + + $style = "font-size: {$fontSize}; color: {$color};"; + if ($safeBackgroundColor) { + $style .= " background-color: {$safeBackgroundColor};"; + } + $style .= " padding: {$safePadding}px;"; + $templateData['footer_text'] = Sanitize::sanitizeFooterHtml($footerConfig['footer_content'] ?? ''); + } + + if($templateData['footer_text']) { + $templateData['footer_text'] = "
{$templateData['footer_text']}
"; + } + } + + return $templateData; + } + +} diff --git a/wp-content/plugins/fluent-crm/app/Hooks/Handlers/EventTrackingHandler.php b/wp-content/plugins/fluent-crm/app/Hooks/Handlers/EventTrackingHandler.php new file mode 100644 index 0000000..24fadfc --- /dev/null +++ b/wp-content/plugins/fluent-crm/app/Hooks/Handlers/EventTrackingHandler.php @@ -0,0 +1,369 @@ + __('Event Tracking', 'fluent-crm'), + 'value' => 'event_tracking', + 'children' => $this->getConditionItems() + ]; + + return $groups; + }); + } + + public function getEventTrackingKeyOptions($options = []) + { + $items = EventTracker::select(['event_key']) + ->groupBy('event_key') + ->orderBy('event_key', 'ASC') + ->get(); + + $formattedItems = []; + + foreach ($items as $item) { + $formattedItems[] = [ + 'id' => $item->event_key, + 'title' => $item->event_key + ]; + } + + return $formattedItems; + } + + public function applyEventTrackingFilter($query, $filters) + { + if (!Helper::isExperimentalEnabled('event_tracking')) { + return $query; + } + + foreach ($filters as $filter) { + if (!array_key_exists('value', $filter) || $filter['value'] === '') { + continue; + } + + $relation = 'trackingEvents'; + + $filterProp = $filter['property']; + + if ($filterProp == 'event_tracking_key') { + $operator = $filter['operator']; + $values = $filter['value']; + if ($operator == 'not_in') { + $query->whereDoesntHave($relation, function ($q) use ($values) { + $q->whereIn('event_key', $values); + }); + } else { + $query->whereHas($relation, function ($q) use ($values) { + $q->whereIn('event_key', $values); + }); + } + continue; + } + + if ($filterProp == 'event_tracking_title') { + $operator = $filter['operator']; + + if ($operator == '=') { + $query->whereHas($relation, function ($q) use ($filter) { + $q->where('title', $filter['value']); + }); + } else if ($operator == '!=') { + $query->whereDoesntHave($relation, function ($q) use ($filter) { + $q->where('title', $filter['value']); + }); + } else if ($operator == 'contains') { + $query->whereHas($relation, function ($q) use ($filter) { + $q->where('title', 'LIKE', '%' . $filter['value'] . '%'); + }); + } else if ($operator == 'not_contains') { + $query->whereDoesntHave($relation, function ($q) use ($filter) { + $q->where('title', 'LIKE', '%' . $filter['value'] . '%'); + }); + } + continue; + } + + if ($filterProp == 'event_tracking_value') { + + $eventKey = Arr::get($filter, 'extra_value'); + if (!$eventKey) { + continue; + } + + $operator = $filter['operator']; + + if ($operator == '=') { + $query->whereHas($relation, function ($q) use ($filter, $eventKey) { + $q->where('value', $filter['value']) + ->where('event_key', $eventKey); + }); + } else if ($operator == '!=') { + $query->whereDoesntHave($relation, function ($q) use ($filter, $eventKey) { + $q->where('value', $filter['value']) + ->where('event_key', $eventKey); + }); + } else if (in_array($operator, ['<', '>'])) { + + $query->whereHas($relation, function ($q) use ($filter, $eventKey, $operator) { + $q->where('value', $operator, (int)$filter['value']) + ->where('event_key', $eventKey); + }); + } else if ($operator == 'contains') { + $query->whereHas($relation, function ($q) use ($filter, $eventKey) { + $q->where('value', 'LIKE', '%' . $filter['value'] . '%') + ->where('event_key', $eventKey); + }); + } else if ($operator == 'not_contains') { + $query->whereDoesntHave($relation, function ($q) use ($filter, $eventKey) { + $q->where('value', 'LIKE', '%' . $filter['value'] . '%') + ->where('event_key', $eventKey); + }); + } + continue; + } + + if ($filterProp == 'event_tracking_key_count') { + + $eventKey = Arr::get($filter, 'extra_value'); + if (!$eventKey) { + continue; + } + + $operator = $filter['operator']; + + if ($operator == '=') { + $query->whereHas($relation, function ($q) use ($filter, $eventKey) { + $q->where('counter', $filter['value']) + ->where('event_key', $eventKey); + }); + } else if ($operator == '!=') { + $query->whereDoesntHave($relation, function ($q) use ($filter, $eventKey) { + $q->where('counter', $filter['value']) + ->where('event_key', $eventKey); + }); + } else if (in_array($operator, ['<', '>'])) { + + $query->whereHas($relation, function ($q) use ($filter, $eventKey, $operator) { + $q->where('counter', $operator, (int)$filter['value']) + ->where('event_key', $eventKey); + }); + } + + continue; + } + } + + return $query; + } + + public function trackEventActivity($data, $repeatable = true) + { + return FluentCrmApi('event_tracker')->track($data, $repeatable); + } + + public function addSubscriberInfoWidgets($widgets, $subscriber) + { + if (!Helper::isExperimentalEnabled('event_tracking')) { + return $widgets; + } + + $events = EventTracker::where('subscriber_id', $subscriber->id) + ->orderBy('updated_at', 'DESC') + ->paginate(); + + if ($events->isEmpty()) { + return $widgets; + } + + $html = '
    '; + foreach ($events as $event) { + $html .= '
  • '; + $html .= '

    ' . esc_html($event->title) . '

    '; + if ($event->value) { + $html .= '

    ' . wp_kses_post($event->value) . '

    '; + } + $html .= ''; + $html .= '
  • '; + } + $html .= '
'; + + $widgets['event_tracking'] = [ + 'title' => __('Event Tracking', 'fluent-crm'), + 'content' => $html, + 'has_pagination' => $events->total() > $events->perPage(), + 'total' => $events->total(), + 'per_page' => $events->perPage(), + 'current_page' => $events->currentPage() + ]; + + return $widgets; + } + + public function addEventTrackingFilterOptions($groups) + { + if (!Helper::isExperimentalEnabled('event_tracking')) { + return $groups; + } + + $groups['event_tracking'] = [ + 'label' => __('Event Tracking', 'fluent-crm'), + 'value' => 'event_tracking', + 'children' => $this->getConditionItems() + ]; + + return $groups; + } + + public function addEventTrackingConditionOptions($items) + { + if (!Helper::isExperimentalEnabled('event_tracking')) { + return $items; + } + + return [ + [ + 'label' => __('Event Tracking', 'fluent-crm'), + 'value' => 'event_tracking', + 'children' => $this->getConditionItems() + ], + [ + 'label' => __('Contact Segment', 'fluent-crm'), + 'value' => 'segment', + 'children' => [ + [ + 'label' => __('Type', 'fluent-crm'), + 'value' => 'contact_type', + 'type' => 'selections', + 'component' => 'options_selector', + 'option_key' => 'contact_types', + 'is_multiple' => false, + 'is_singular_value' => true + ], + [ + 'label' => __('Tags', 'fluent-crm'), + 'value' => 'tags', + 'type' => 'selections', + 'component' => 'options_selector', + 'option_key' => 'tags', + 'is_multiple' => true, + ], + [ + 'label' => __('Lists', 'fluent-crm'), + 'value' => 'lists', + 'type' => 'selections', + 'component' => 'options_selector', + 'option_key' => 'lists', + 'is_multiple' => true, + ] + ], + ] + ]; + } + + private function getConditionItems() + { + return [ + [ + 'label' => __('Event Key', 'fluent-crm'), + 'value' => 'event_tracking_key', + 'type' => 'selections', + 'component' => 'ajax_selector', + 'option_key' => 'event_tracking_keys', + 'is_multiple' => true, + 'custom_operators' => [ + 'in' => 'in', + 'not_in' => 'not in' + ], + 'creatable' => true, + 'experimental_cache' => true, + 'help' => __('Match one or more tracking events for your contacts.', 'fluent-crm') + ], + [ + 'label' => __('Event Occurrence Count', 'fluent-crm'), + 'value' => 'event_tracking_key_count', + 'type' => 'composite_optioned_compare', + 'help' => __('The provided value for your selected event will be matched with the event occurrence count', 'fluent-crm'), + 'ajax_selector' => [ + 'label' => __('For Event Key', 'fluent-crm'), + 'option_key' => 'event_tracking_keys', + 'experimental_cache' => true, + 'is_multiple' => false, + 'placeholder' => __('Select Event Key', 'fluent-crm') + ], + 'value_config' => [ + 'label' => __('Event Count', 'fluent-crm'), + 'type' => 'input_text', + 'data_type' => 'number', + 'placeholder' => __('Event Value', 'fluent-crm') + ], + 'custom_operators' => [ + '=' => 'equal', + '!=' => 'not equal', + '>' => 'greater than', + '<' => 'less than' + ], + ], + [ + 'label' => __('Event Value', 'fluent-crm'), + 'value' => 'event_tracking_value', + 'type' => 'composite_optioned_compare', + 'help' => __('The compare value will be matched with selected event & last recorded value of the selected event key', 'fluent-crm'), + 'ajax_selector' => [ + 'label' => __('For Event Key', 'fluent-crm'), + 'option_key' => 'event_tracking_keys', + 'experimental_cache' => true, + 'is_multiple' => false, + 'placeholder' => __('Select Event Key', 'fluent-crm') + ], + 'value_config' => [ + 'label' => __('Compare Value', 'fluent-crm'), + 'type' => 'input_text', + 'placeholder' => __('Event Value', 'fluent-crm'), + 'data_type' => 'number', + ], + 'custom_operators' => [ + '=' => 'equal', + '!=' => 'not equal', + 'contains' => 'includes', + 'not_contains' => 'does not include', + '>' => 'greater than', + '<' => 'less than' + ], + ], + [ + 'label' => __('Event Title', 'fluent-crm'), + 'value' => 'event_tracking_title', + 'type' => 'text', + 'help' => __('Match by tracking event title', 'fluent-crm') + ], + ]; + } +} diff --git a/wp-content/plugins/fluent-crm/app/Hooks/Handlers/ExternalPages.php b/wp-content/plugins/fluent-crm/app/Hooks/Handlers/ExternalPages.php new file mode 100644 index 0000000..e817a91 --- /dev/null +++ b/wp-content/plugins/fluent-crm/app/Hooks/Handlers/ExternalPages.php @@ -0,0 +1,1788 @@ + 'unsubscribePage', + 'manage_subscription' => 'manageSubscription', + 'confirmation' => 'confirmationPage', // ?fluentcrm=1&route=confirmation&s_id={subscriber.id}&hash={subscriber.hash} + 'open' => 'trackEmailOpen', // ?fluentcrm=1&route=open&_e_hash=kandkaskdja + 'bounce_handler' => 'bounceHandler', // ?fluentcrm=1&route=bounce_handler&provider=ses&retry=1 + 'contact' => 'handleContactWebhook', // POST ?fluentcrm=1&route=contact&hash=khkhjkhjkhjkhkhkh + 'bnu' => 'handleBenchmarkUrl', // GET ?fluentcrm=1&route=bnu&aid=${sequence_id} + 'smart_url' => 'SmartUrlHandler', + 'webhook' => 'handleGeneralWebhook', // ?fluentcrm=1&route=webhook&handler=handler_name + 'email_preview' => 'handlePreviewEmail', + 'general' => 'handleGeneralRequest' + ]; + + protected function getRoute() + { + $this->request = FluentCrm('request'); + + if ($this->request->has('fluentcrm')) { + $route = $this->request->get('route'); + if ($route && isset($this->validRoutes[sanitize_text_field($route)])) { + return $this->validRoutes[sanitize_text_field($route)]; + } + } + } + + public function route() + { + if (!isset($_GET['fluentcrm'])) { + return false; + } + + if ($route = $this->getRoute()) { + do_action('litespeed_control_set_nocache', 'nocache due to fluentcrm dynamic data'); + $this->{$route}(); + } + } + + public function bounceHandler() + { + $provider = sanitize_text_field($this->request->get('provider')); + + if ($provider == 'ses') { + $this->bounceHandlerSES(); + } + } + + public function bounceHandlerSES() + { + // check bounce key + $sesBounceKey = fluentcrm_get_option('_fc_bounce_key'); + $verifyKey = Arr::get($_REQUEST, 'verify_key'); + + if (!$sesBounceKey || $verifyKey !== $sesBounceKey) { + wp_send_json([ + 'status' => 422, + 'message' => __('verify_key verification failed', 'fluent-crm') + ], 422); + } + + $postdata = \file_get_contents('php://input'); + + if (!$postdata) { + wp_send_json([ + 'status' => 423, + 'message' => __('Empty request body', 'fluent-crm') + ], 422); + } + + $postdata = \json_decode($postdata, true); + + if (!$postdata || !is_array($postdata)) { + wp_send_json([ + 'status' => 423, + 'message' => __('Invalid JSON payload', 'fluent-crm') + ], 422); + } + + $notificationType = Arr::get($postdata, 'notificationType'); + + if (!$notificationType) { + $notificationType = Arr::get($postdata, 'Type'); + } + + if ($notificationType == 'SubscriptionConfirmation') { + $subscribeUrl = esc_url_raw($postdata['SubscribeURL'] ?? ''); + // Only confirm subscriptions from AWS SNS domains to prevent SSRF + $host = wp_parse_url($subscribeUrl, PHP_URL_HOST); + if ($host && preg_match('/\.amazonaws\.com(\.cn)?$/i', $host)) { + \wp_remote_get($subscribeUrl); + } + wp_send_json([ + 'status' => 200, + 'message' => __('success', 'fluent-crm') + ], 200); + } + + // SNS wraps SES notifications in a Message envelope + if (empty($postdata['notificationType']) && !empty($postdata['Message'])) { + $postdata = json_decode($postdata['Message'], true); + $notificationType = Arr::get($postdata, 'notificationType', $notificationType); + } + + if ($notificationType == 'Bounce') { + $bounce = Arr::get($postdata, 'bounce', []); + $bouncedRecipients = Arr::get($bounce, 'bouncedRecipients', []); + + if (!$bouncedRecipients) { + wp_send_json(['status' => 200, 'message' => __('No recipients', 'fluent-crm')], 200); + } + + $bounceType = Arr::get($bounce, 'bounceType'); + $bounceSubType = Arr::get($bounce, 'bounceSubType'); + $isHardBounce = $bounceType === 'Permanent'; + // Sender-side failures — the recipient's mailbox is fine, so we must + // not penalise the subscriber. See SES bounce sub-type reference. + $senderFaultSubTypes = ['MessageTooLarge', 'ContentRejected', 'AttachmentRejected']; + $isSenderFault = !$isHardBounce && in_array($bounceSubType, $senderFaultSubTypes, true); + + foreach ($bouncedRecipients as $bouncedRecipient) { + $email = $this->extractEmail(Arr::get($bouncedRecipient, 'emailAddress', '')); + if (!$email) { + continue; + } + + $reason = Arr::get($bouncedRecipient, 'diagnosticCode', ''); + + if ($isHardBounce) { + $this->recordUnsubscribe([ + 'email' => $email, + 'reason' => $reason, + 'status' => 'bounced' + ]); + } else if (!$isSenderFault) { + $this->recordSoftBounce([ + 'email' => $email, + 'reason' => $reason + ]); + } + } + } else if ($notificationType == 'Complaint') { + $complaint = Arr::get($postdata, 'complaint', []); + $complainedRecipients = Arr::get($complaint, 'complainedRecipients', []); + + if (!$complainedRecipients) { + wp_send_json(['status' => 200, 'message' => __('No recipients', 'fluent-crm')], 200); + } + + foreach ($complainedRecipients as $complainedRecipient) { + $email = $this->extractEmail(Arr::get($complainedRecipient, 'emailAddress', '')); + if (!$email) { + continue; + } + + $reason = Arr::get($complainedRecipient, 'diagnosticCode'); + if (!$reason) { + $reason = 'SES complaint received as: ' . Arr::get($complaint, 'complaintFeedbackType'); + } + + $this->recordUnsubscribe([ + 'email' => $email, + 'reason' => $reason, + 'status' => 'complained' + ]); + } + } + + wp_send_json([ + 'status' => 200, + 'message' => __('success', 'fluent-crm') + ], 200); + } + + public function recordUnsubscribe($data) + { + if (!empty($data['email']) && is_email($data['email'])) { + $subscriber = Subscriber::where('email', $data['email'])->first(); + if ($subscriber) { + $oldStatus = $subscriber->status; + if ($oldStatus == $data['status']) { + return false; + } + + $subscriber = $subscriber->updateStatus($data['status']); + $key = 'reason'; + + if ($data['status'] == 'unsubscribed') { + $key = 'unsubscribe_reason'; + } + + fluentcrm_update_subscriber_meta($subscriber->id, $key, $data['reason']); + } else { + $willStore = apply_filters('fluent_crm/bounced_email_store', true); + if ($willStore) { + $contactData = Arr::only($data, ['email', 'status']); + if (!isset($contactData['created_at'])) { + $contactData['created_at'] = current_time('mysql'); + } + + $key = 'reason'; + + if ($data['status'] == 'unsubscribed') { + $key = 'unsubscribe_reason'; + } + + $contact = Subscriber::store($contactData); + fluentcrm_update_subscriber_meta($contact->id, $key, $data['reason']); + } + } + return true; + } + + return false; + } + + public function recordSoftBounce($data) + { + if (!empty($data['email']) && is_email($data['email'])) { + $email = sanitize_text_field($data['email']); + $subscriber = Subscriber::where('email', $email)->first(); + + if (!$subscriber) { + return false; + } + + $existingCount = fluentcrm_get_subscriber_meta($subscriber->id, '_soft_bounce_count', 0); + if (!$existingCount) { + $existingCount = 0; + } + + /** + * Modify the soft bounce limit. + * + * This filter allows you to change the default soft bounce limit. + * + * @param int The default soft bounce limit. Default is 5. + * @since 2.7.0 + * + */ + $softCountLimit = apply_filters('fluent_crm/soft_bounce_limit', 5); + + if ($existingCount < $softCountLimit) { + fluentcrm_update_subscriber_meta($subscriber->id, '_soft_bounce_count', ($existingCount + 1)); + } else { + $oldStatus = $subscriber->status; + if ($oldStatus != 'bounced') { + $subscriber = $subscriber->updateStatus('bounced'); + fluentcrm_update_subscriber_meta($subscriber->id, 'reason', $data['reason']); + } + } + + return true; + } + + return false; + } + + public function unsubscribePage() + { + + nocache_headers(); + + $campaignEmailId = $this->request->get('ce_id'); + $managedSecureHash = $this->request->get('secure_hash'); + $subscriber = null; + + if ($managedSecureHash) { + $subscriber = fluentCrmApi('contacts')->getContactByManagedSecureHash($managedSecureHash); + } + + // check if this is a POST request + if ($this->request->method() == 'POST') { + // This is List-Unsubscribe request + if ($subscriber && $subscriber->status != 'unsubscribed') { + $campaignEmail = null; + if ($campaignEmailId) { + $campaignEmail = CampaignEmail::where('id', (int) $campaignEmailId) + ->where('subscriber_id', $subscriber->id) + ->first(); + } + + do_action('fluent_crm/before_contact_unsubscribe_from_email', $subscriber, $campaignEmail, 'from_header'); + + $subscriber = $subscriber->updateStatus('unsubscribed'); + + fluentcrm_update_subscriber_meta($subscriber->id, 'unsubscribe_reason', 'Unsubscribe From List Header'); + if ($campaignEmail) { + CampaignUrlMetric::maybeInsert([ + 'campaign_id' => $campaignEmail->campaign_id, + 'subscriber_id' => $campaignEmail->subscriber_id, + 'type' => 'unsubscribe' + ]); + } + } + + wp_send_json_success([ + 'message' => __("You've successfully unsubscribed from our email list.", 'fluent-crm') + ], 200); + return; + } + + if (!$subscriber) { + $this->unsubscribeRequestForm(); + return; + } + + setcookie("fc_hash_secure", $subscriber->getSecureHash(), time() + 7776000, COOKIEPATH, COOKIE_DOMAIN, is_ssl(), true); /* expire in 90 days */ + + if ($campaignEmailId) { + $campaignEmailId = (int)$campaignEmailId; + $campaignEmail = CampaignEmail::where('id', $campaignEmailId)->first(); + if (!$campaignEmail || !$subscriber || $campaignEmail->subscriber_id != $subscriber->id) { + $this->unsubscribeRequestForm(); + return; + } + } else { + $campaignEmail = (object)[ + 'id' => 0 + ]; + } + + $businessSettings = fluentcrmGetGlobalSettings('business_settings', []); + + $this->loadAssets(); + + $absEmail = $this->hideEmail($subscriber->email); + $absEmailHash = md5($absEmail); + + /** + * Define the unsubscribe texts displayed on the unsubscribe page in FluentCRM. + * + * @param array { + * An array of texts to be displayed on the unsubscribe page. + * + * @type string $heading The heading text. + * @type string $heading_description The description text under the heading. + * @type string $email_label The label for the email address input field. + * @type string $reason_label The label for the reason input field. + * @type string $button_text The text for the unsubscribe button. + * } + * @param object $subscriber The subscriber object. + * @since 2.7.0 + * + */ + $texts = apply_filters('fluent_crm/unsubscribe_texts', [ + 'heading' => __('Unsubscribe', 'fluent-crm'), + 'heading_description' => __('We\'re sorry to see you go!', 'fluent-crm'), + 'email_label' => __('Your Email Address', 'fluent-crm'), + 'reason_label' => __('Please let us know a reason', 'fluent-crm'), + 'button_text' => __('Unsubscribe', 'fluent-crm') + ], $subscriber); + + $complianceSettings = Helper::getComplianceSettings(); + $data = [ + 'business' => $businessSettings, + 'campaign_email' => $campaignEmail, + 'subscriber' => $subscriber, + 'mask_email' => $absEmail, + 'abs_hash' => $absEmailHash, + 'combined_hash' => md5($subscriber->email . $absEmail), + 'reasons' => $this->unsubscribeReasons(), + 'secure_hash' => fluentCrmGetContactManagedHash($subscriber->id), + 'texts' => $texts, + 'one_click_unsubscribe' => Arr::get($complianceSettings, 'one_click_unsubscribe') + ]; + + add_action('wp_loaded', function () use ($data) { + fluentCrm('view')->render('external.unsubscribe', $data); + exit(); + }, 1); + } + + public function unsubscribeRequestForm() + { + do_action('fluent_crm/doing_unsubscribe_request_form'); + + $businessSettings = fluentcrmGetGlobalSettings('business_settings', []); + $this->loadAssets(); + $data = [ + 'business' => $businessSettings, + ]; + + add_action('wp_loaded', function () use ($data) { + fluentCrm('view')->render('external.unsubscribe_request_form', $data); + exit(); + }, 1); + } + + public function manageSubscriptionRequestForm() + { + do_action('fluent_crm/doing_unsubscribe_request_form'); + + $businessSettings = fluentcrmGetGlobalSettings('business_settings', []); + $this->loadAssets(); + $data = [ + 'business' => $businessSettings, + ]; + + add_action('wp_loaded', function () use ($data) { + fluentCrm('view')->render('external.manage_subscription_request_form', $data); + exit(); + }, 1); + } + + public function handleUnsubscribeRequestAjax() + { + $email = Arr::get($_REQUEST, 'email'); + + if (!$email || !is_email($email)) { + wp_send_json_error([ + 'message' => __('Please provide a valid email address', 'fluent-crm') + ], 422); + } + + $subscriber = Subscriber::where('email', $email)->first(); + + if (!$subscriber || $subscriber->status != 'subscribed') { + // Use the same success response to prevent email enumeration + wp_send_json_success([ + 'message' => __("If this email exists in our system, we've sent a confirmation link to your inbox.", 'fluent-crm') + ]); + } + + // Let's send unsubscribe email with link + $data = [ + 'business' => fluentcrmGetGlobalSettings('business_settings', []), + 'unsubscribe_url' => add_query_arg(array_filter([ + 'fluentcrm' => 1, + 'route' => 'unsubscribe', + 'secure_hash' => fluentCrmGetContactManagedHash($subscriber->id) + ]), site_url('/')), + 'subscriber' => $subscriber + ]; + + $emailBody = (string)fluentCrm('view')->make('external.unsubscribe_request_email', $data); + $emailSubject = __('Confirm your unsubscribe Request', 'fluent-crm'); + + do_action('fluent_crm/before_unsubscribe_request_email', $subscriber, $data); + + Mailer::send([ + 'to' => [ + 'email' => $subscriber->email, + 'name' => $subscriber->full_name + ], + 'subject' => $emailSubject, + 'body' => $emailBody + ]); + + wp_send_json_success([ + 'message' => __("We've sent an email to your inbox that contains a link to unsubscribe from our mailing list. Please check your email address and unsubscribe.", 'fluent-crm') + ]); + } + + public function handleManageSubRequestAjax() + { + + $email = Arr::get($_REQUEST, 'email'); + + if (!$email || !is_email($email)) { + wp_send_json_error([ + 'message' => __('Please provide a valid email address', 'fluent-crm') + ], 422); + } + + $subscriber = Subscriber::where('email', $email)->first(); + + if (!$subscriber) { + // Use the same success response to prevent email enumeration + wp_send_json_success([ + 'message' => __("If this email exists in our system, we've sent a confirmation link to your inbox.", 'fluent-crm') + ]); + } + + // Let's send manage subscription email with link + $data = [ + 'business' => fluentcrmGetGlobalSettings('business_settings', []), + 'manage_subscription_url' => add_query_arg(array_filter([ + 'fluentcrm' => 1, + 'route' => 'manage_subscription', + 'ce_id' => $subscriber->id, + 'secure_hash' => fluentCrmGetContactManagedHash($subscriber->id) + ]), site_url('/')), + 'subscriber' => $subscriber + ]; + + $emailBody = (string)fluentCrm('view')->make('external.manage_sub_request_email', $data); + $emailSubject = __('Your Email preferences URL', 'fluent-crm'); + + do_action('fluent_crm/before_manage_sub_request_email', $subscriber, $data); + + Mailer::send([ + 'to' => [ + 'email' => $subscriber->email, + 'name' => $subscriber->full_name + ], + 'subject' => $emailSubject, + 'body' => $emailBody + ]); + + wp_send_json_success([ + 'message' => __("We've sent an email to your inbox that contains a link to email management from. Please check your email address to get the link.", 'fluent-crm') + ]); + } + + public function unsubscribeReasons() + { + /** + * Define the list of unsubscribe reasons in FluentCRM. + * + * This filter allows modification of the reasons provided to users when they choose to unsubscribe from emails. + * + * @param array { + * An associative array of unsubscribe reasons. + * + * @type string $no_longer Reason for no longer wanting to receive emails. + * @type string $never_signed_up Reason for never signing up for the email list. + * @type string $emails_inappropriate Reason for finding the emails inappropriate. + * @type string $emails_spam Reason for considering the emails as spam. + * @type string $other Reason for other, with a prompt to fill in the reason. + * } + * @since 2.5.1 + * + */ + return apply_filters('fluent_crm/unsubscribe_reasons', [ + 'no_longer' => __('I no longer want to receive these emails', 'fluent-crm'), + 'never_signed_up' => __('I never signed up for this email list', 'fluent-crm'), + 'emails_inappropriate' => __('The emails are inappropriate', 'fluent-crm'), + 'emails_spam' => __('The emails are spam', 'fluent-crm'), + 'other' => __('Other (fill in reason below)', 'fluent-crm') + ]); + } + + public function handleUnsubscribe() + { + $request = FluentCrm('request'); + $data = $request->all(); + + $subscriber = null; + + if ($secureHash = $request->get('secure_hash')) { + $subscriber = fluentCrmApi('contacts')->getContactByManagedSecureHash($secureHash); + } + + if (!$subscriber) { + wp_send_json_error([ + 'message' => __('Sorry, No email found based on your data', 'fluent-crm') + ], 422); + } + + $oldStatus = $subscriber->status; + + + $emailId = intval($request->get('_e_id')); + if ($emailId) { + $campaignEmail = CampaignEmail::find($emailId); + } else { + $campaignEmail = null; + } + + do_action('fluent_crm/before_contact_unsubscribe_from_email', $subscriber, $campaignEmail, 'web_ui'); + + + if ($oldStatus != 'unsubscribed') { + $subscriber = $subscriber->updateStatus('unsubscribed'); + + /** + * Fires when a subscriber is unsubscribed from Web UI + * @param Subscriber $subscriber + * @param array $data Unsubscribe data from Web UI Form + */ + do_action('fluent_crm/subscriber_unsubscribed_from_web_ui', $subscriber, $data); + } + + + $reason = sanitize_text_field($request->get('reason')); + + if ($reason == 'other') { + if ($otherReason = $request->get('other_reason')) { + $reason = sanitize_text_field($otherReason); + } + } else if ($reason) { + $reasons = $this->unsubscribeReasons(); + if (isset($reasons[$reason])) { + $reason = sanitize_text_field($reasons[$reason]); + } + } + + fluentcrm_update_subscriber_meta($subscriber->id, 'unsubscribe_reason', $reason); + + if ($campaignEmail) { + CampaignUrlMetric::maybeInsert([ + 'campaign_id' => $campaignEmail->campaign_id, + 'subscriber_id' => $campaignEmail->subscriber_id, + 'type' => 'unsubscribe', + 'ip_address' => FluentCrm()->request->getIp(fluentCrmWillAnonymizeIp()) + ]); + } + + $redirect = Arr::get(Helper::getGlobalEmailSettings(), 'unsubscribe_redirect', ''); + if (!$redirect) { + $redirect = false; + } + + if ($redirect) { + /** + * Determine the redirect URL for a campaign email in FluentCRM. + * + * This filter allows you to modify the redirect URL for a campaign email. + * + * @param string $redirect The redirect URL. + * @param object $subscriber The subscriber object. + * @since 2.7.40 + * + */ + $redirect = apply_filters('fluent_crm/parse_campaign_email_text', $redirect, $subscriber); + $redirect = str_replace(['&', '+'], ['&', '%2B'], $redirect); + } + + if (!$reason) { + $reason = 'n/a'; + } + SubscriberNote::create([ + 'subscriber_id' => $subscriber->id, + 'type' => 'system_log', + 'title' => __('Unsubscribed', 'fluent-crm'), + /* translators: 1: IP address of the subscriber (may be anonymized), 2: unsubscribe reason */ + 'description' => wp_kses(sprintf(__('Subscriber unsubscribed from IP Address: %1$s
Reason: %2$s', 'fluent-crm'), + esc_html(FluentCrm()->request->getIp(fluentCrmWillAnonymizeIp())), + esc_html($reason) + ), + array('br' => array()) + ) + ]); + + $message = __("You've successfully unsubscribed from our email list.", 'fluent-crm'); + wp_send_json_success([ + /** + * Determine the unsubscribe response message in FluentCRM. + * + * This filter allows modification of the message displayed to the user + * when they unsubscribe from a mailing list. + * + * @param string $message The default unsubscribe response message. + * @param object $subscriber The subscriber object containing subscriber or contact details. + * @since 2.7.0 + * + */ + 'message' => apply_filters('fluent_crm/unsub_response_message', $message, $subscriber), + /** + * Determine the URL to which the user is redirected after unsubscribing in FluentCRM. + * + * @param string $redirect The default redirect URL. + * @param object $subscriber The subscriber or contact object. + * @since 2.7.0 + * + */ + 'redirect_url' => apply_filters('fluent_crm/unsub_redirect_url', $redirect, $subscriber) + ], 200); + } + + private function trackEmailOpen() + { + $mailHash = sanitize_text_field($this->request->get('_e_hash')); + $emailId = (int)$this->request->get('_e_id'); + + $isAnonymous = isset($_REQUEST['ano']); + + if ($emailId) { + $email = CampaignEmail::where('id', $emailId)->first(); + } else { + $email = CampaignEmail::where('email_hash', $mailHash)->first(); + } + + if ($email && $email->email_hash != $mailHash) { + $email = null; + } + + if ($email) { + if ($isAnonymous) { + do_action('fluent_crm/email_opened_anonymously', $email); + } else { + $updated = fluentCrmDb()->table('fc_campaign_emails') + ->where('id', $email->id) + ->where('is_open', 0) + ->update([ + 'is_open' => 1, + 'updated_at' => current_time('mysql') + ]); // returns affected rows + + if ($updated) { + do_action('fluent_crm/email_opened', $email); + } + } + } + + if (ini_get('ignore_user_abort')) { + ignore_user_abort(true); + } + + //turn off gzip compression + if (function_exists('apache_setenv')) { + @apache_setenv('no-gzip', 1); + } + + @ini_set('zlib.output_compression', 'Off'); + // we are sending 1x1 pixel transparent gif image + header('Content-Encoding: none'); + header('Content-Type: image/gif'); + header('Content-Length: 43'); + header('Cache-Control: private, no-cache, no-cache=Set-Cookie, proxy-revalidate'); + header('Expires: Wed, 11 Jan 2000 12:59:00 GMT'); + header('Last-Modified: Wed, 11 Jan 2006 12:59:00 GMT'); + header('Pragma: no-cache'); + // Transparent 1x1 GIF as hex format + $image = base64_decode('R0lGODlhAQABAJAAAP8AAAAAACH5BAUQAAAALAAAAAABAAEAAAICBAEAOw=='); + + // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Intentionally outputting raw GIF bytes for the tracking pixel response + echo $image; + exit; + } + + public function confirmationPage() + { + + $hash = sanitize_text_field($this->request->get('hash')); + + if (!$hash) { + return; + } + + nocache_headers(); + + $secureHash = sanitize_text_field($this->request->get('secure_hash')); + $subscriber = false; + + if ($secureHash) { + $subscriber = fluentCrmApi('contacts')->getContactBySecureHash($secureHash); + } + + if (!$subscriber) { + $body = __('Sorry! Your confirmation url is not valid', 'fluent-crm'); + } else { + if (!is_user_logged_in()) { + $secureHash = fluentCrmGetContactSecureHash($subscriber->id); + setcookie("fc_hash_secure", $secureHash, time() + 7776000, COOKIEPATH, COOKIE_DOMAIN, is_ssl(), true); /* expire in 90 days */ + } + + do_action('fluent_crm/track_activity_by_subscriber', $subscriber); + + if ($subscriber->status != 'subscribed') { + + $subscriber = $subscriber->updateStatus('subscribed'); + + do_action('fluentcrm_process_contact_jobs', $subscriber); + + /** + * Fires when a contact is subscribed after double opt-in confirmation + * @param Subscriber $subscriber + */ + do_action('fluent_crm/subscriber_confirmed_via_double_optin', $subscriber); + + SubscriberNote::create([ + 'subscriber_id' => $subscriber->id, + 'type' => 'system_log', + 'title' => __('Subscriber double opt-in confirmed', 'fluent-crm'), + 'description' => __('Subscriber confirmed double opt-in from IP Address:', 'fluent-crm') . ' ' . $this->request->getIp() + ]); + + /** + * Determine whether to use cookies for FluentCRM. + * + * This filter allows you to control whether cookies should be used for FluentCRM. It is used in conjunction with the `setcookie` function when a user is not logged in. + * + * @param bool Whether to use cookies. Default true. + * @since 2.7.0 + * + */ + if (!is_user_logged_in() && apply_filters('fluent_crm/will_use_cookie', true)) { + setcookie("fc_hash_secure", fluentCrmGetContactSecureHash($subscriber->id), time() + 7776000, COOKIEPATH, COOKIE_DOMAIN, is_ssl(), true); /* expire in 90 days */ + } + } + + $listIdOfSubscriber = Helper::latestListIdOfSubscriber($subscriber->id); + + $config = null; + if ($listIdOfSubscriber) { + $globalDoubleOptin = fluentcrm_get_list_meta($listIdOfSubscriber, 'global_double_optin'); + if ($globalDoubleOptin && $globalDoubleOptin->value == 'no') { + $meta = fluentcrm_get_meta($listIdOfSubscriber, 'FluentCrm\App\Models\Lists', 'double_optin_settings', []); + $config = $meta ? $meta->value : null; + } + } + + if (!$config) { + $config = Helper::getDoubleOptinSettings(); + } + + /** + * Determine the double opt-in options configuration in FluentCRM. + * + * This filter allows modification of the double opt-in options configuration. + * + * @param array $config The double opt-in options configuration array. + * @param object $subscriber The subscriber object. + * @return array The modified double opt-in options configuration array. + * @since 2.6.0 + * + */ + $config = apply_filters('fluent_crm/double_optin_options', $config, $subscriber); + + if (Arr::get($config, 'after_confirmation_type') == 'redirect' && $url = Arr::get($config, 'after_conf_redirect_url')) { + /** + * Determine the campaign email text URL in FluentCRM. + * + * This filter allows you to modify the URL used in the campaign email text. + * + * @param string $url The original URL. + * @param object $subscriber The subscriber object. + * @return string The filtered URL. + * @since 2.7.0 + * + */ + $url = apply_filters('fluent_crm/parse_campaign_email_text', $url, $subscriber); + if ($url) { + $url = trim($url); + $url = str_replace(['&', '+'], ['&', '%2B'], $url); + wp_redirect($url, 307); + exit(); + } + } + + /** + * Determine the campaign email text after confirmation message in FluentCRM. + * + * This filter allows modification of the campaign email text after the confirmation message. + * + * @param string $after_confirm_message The message displayed after confirmation. + * @param object $subscriber The subscriber object. + * @since 2.7.0 + * + */ + $body = apply_filters('fluent_crm/parse_campaign_email_text', $config['after_confirm_message'], $subscriber); + + /** + * Filter the confirmation text before it is parsed in FluentCRM. + * + * This filter allows you to modify the CRM text before it is parsed and processed. + * + * @param string $body The text to be parsed. + * @param object $subscriber The subscriber object containing subscriber data. + * @since 2.7.0 + * + */ + $body = apply_filters('fluent_crm/parse_extended_crm_text', $body, $subscriber); + + } + + wp_enqueue_style( + 'fluentcrm_unsubscribe', + fluentCrmMix('public/public_pref.css'), + [], + FLUENTCRM_PLUGIN_VERSION + ); + + $businessSettings = fluentcrmGetGlobalSettings('business_settings', []); + + $data = [ + 'body' => $body, + 'subscriber' => $subscriber, + 'business' => $businessSettings + ]; + add_action('wp_loaded', function () use ($data) { + fluentCrm('view')->render('external.confirmation', $data); + exit(); + }, 1); + } + + public function handleContactWebhook() + { + if ($this->request->method() != 'POST') { + wp_send_json_error([ + 'message' => __('Webhook must need to be as POST Method', 'fluent-crm'), + 'type' => 'invalid_request_method' + ], 200); + } + + $postData = $this->request->get(); + + if (empty($postData['email'])) { + $postData = Helper::parseArrayOrJson($this->request->getContent(), []); + } + + if (empty($hash = $this->request->get('hash'))) { + wp_send_json_error([ + 'message' => __('Invalid Webhook URL', 'fluent-crm'), + 'type' => 'invalid_webhook_url' + ], 200); + } + + $webhook = Webhook::where('key', $hash)->first(); + + if (!$webhook) { + wp_send_json_error([ + 'message' => __('Invalid Webhook Hash', 'fluent-crm'), + 'type' => 'invalid_webhook_hash' + ], 200); + } + + /** + * Manage the incoming webhook data in FluentCRM. + * + * This filter allows modification of the incoming webhook data before it is processed. + * + * @param array $postData The incoming webhook data. + * @param string $webhook The webhook identifier. + * @param object $request The request object containing the webhook data. + * + * @return array The filtered webhook data. + * @since 2.5.95 + * + */ + $postData = apply_filters('fluent_crm/incoming_webhook_data', $postData, $webhook, $this->request); + + if ($keyBy = Arr::get($postData, '_key_by')) { + if ($keyBy == 'hash' && $hash = Arr::get($postData, '_key_by_value')) { + $exist = Subscriber::where('hash', $hash)->first(); + if ($exist && empty($postData['email'])) { + $postData['email'] = $exist->email; + } + } + } + + $validator = FluentCrm('validator')->make($postData, [ + 'email' => 'required|email' + ])->validate(); + + if ($validator->fails()) { + wp_send_json_error([ + 'message' => __('Validation failed.', 'fluent-crm'), + 'errors' => $validator->errors(), + 'type' => 'email_validation_failed' + ], 200); + } + + if (isset($postData['names'])) { + $postData['first_name'] = Arr::get($postData['names'], 'first_name', ''); + $postData['last_name'] = Arr::get($postData['names'], 'last_name', ''); + } + + if (isset($postData['full_name'])) { + $postData = Subscriber::explodeFullName($postData); + } + + $subscriberModel = new Subscriber; + + $mainFields = Arr::only($postData, $subscriberModel->getFillable()); + + foreach ($mainFields as $fieldKey => $value) { + if (is_array($value)) { + $mainFields[$fieldKey] = map_deep($value, 'sanitize_text_field'); + } else { + $mainFields[$fieldKey] = wp_unslash(sanitize_text_field($value)); + } + } + + + $customValues = []; + $customColumns = array_map(function ($field) { + return $field['slug']; + }, fluentcrm_get_option('contact_custom_fields', [])); + + if ($customColumns) { + $customValues = []; + foreach (Arr::only($postData, $customColumns) as $itemKey => $value) { + if (is_string($value)) { + $customValues[$itemKey] = sanitize_textarea_field($value); + } else { + $customValues[$itemKey] = map_deep($value, 'sanitize_textarea_field'); + } + } + } + + $tags = array_filter((array)Arr::get($postData, 'tags', [])); + $lists = array_filter((array)Arr::get($postData, 'lists', [])); + + if (!$tags) { + $tags = Arr::get($webhook->value, 'tags', []); + } + + if (!$lists) { + $lists = Arr::get($webhook->value, 'lists', []); + } + + + $companies = Helper::maybeParseAndFilterWebhookData($webhook, $postData, 'companies'); + $defaultStatus = Arr::get($webhook->value, 'status', ''); + + $extraData = [ + 'detach_tags' => Arr::get($postData, 'detach_tags', []), + 'detach_lists' => Arr::get($postData, 'detach_lists', []), + 'tags' => $tags, + 'lists' => $lists, + 'companies' => $companies, + 'status' => $defaultStatus + ]; + + $data = array_merge( + $mainFields, + $customValues, + $extraData + ); + + $data = array_filter($data); + + /** + * Manage the contact data for the webhook. + * + * This filter allows modification of the contact data before it is processed by the webhook. + * + * @param array $data The contact data to be filtered. + * @param array $postData The original post data received from the webhook. + * @param string $webhook The identifier for the webhook. + * @since 2.5.1 + * + */ + $data = apply_filters('fluent_crm/webhook_contact_data', $data, $postData, $webhook); + + $forceUpdate = (!empty($data['status']) && $data['status'] != Arr::get($webhook->value, 'status', '')) || $data['status'] == 'subscribed'; + + $user = get_user_by('email', $data['email']); + + if ($user) { + $data['user_id'] = $user->ID; + } + + $subscriber = FluentCrmApi('contacts')->createOrUpdate($data, $forceUpdate); + + if ($subscriber->status == 'pending') { + $subscriber->sendDoubleOptinEmail(); + } + + $message = $subscriber->wasRecentlyCreated ? 'created' : 'updated'; + wp_send_json_success([ + 'message' => $message, + 'id' => $subscriber->id, + 'type' => 'success' + ], 200); + } + + public function handleBenchmarkUrl() + { + $benchmarkActionId = intval(Arr::get($_REQUEST, 'aid')); + if ($benchmarkActionId) { + /** + * Fires when a benchmark linked is clicked + * @param int $benchmarkActionId + * @param Subscriber|false Current Contact Object or false if not available + */ + do_action('fluencrm_benchmark_link_clicked', $benchmarkActionId, fluentcrm_get_current_contact()); + } + } + + public function manageSubscription() + { + $contactId = (int)$_GET['ce_id']; + $subscriber = false; + + $managedSecureHash = sanitize_text_field(Arr::get($_REQUEST, 'secure_hash')); + + if ($managedSecureHash) { + $subscriber = fluentCrmApi('contacts')->getContactByManagedSecureHash($managedSecureHash); + if ($subscriber && $subscriber->id != $contactId) { + return; + } + } + + if (!$subscriber) { + $this->manageSubscriptionRequestForm(); + return; + } + + $emailSettings = Helper::getGlobalEmailSettings(); + if (Arr::get($emailSettings, 'show_on_page') == 'yes' && Arr::get($emailSettings, 'pref_form') == 'yes' && !empty(Arr::get($emailSettings, 'pref_general')) && Arr::get($emailSettings, 'pref_page_id')) { + $pageId = Arr::get($emailSettings, 'pref_page_id'); + $pageUrl = get_permalink($pageId); + if ($pageUrl) { + if(!is_user_logged_in()) { + setcookie("fc_hash_secure", $subscriber->getSecureHash(), time() + 7776000, COOKIEPATH, COOKIE_DOMAIN, is_ssl(), true); /* expire in 90 days */ + } + $pageUrl = add_query_arg('_signed_at', time(), $pageUrl); + wp_redirect($pageUrl); + exit(); + } + } + + $this->loadAssets(); + + $managedSecureHash = fluentCrmGetContactManagedHash($subscriber->id); + add_action('wp_loaded', function () use ($subscriber, $managedSecureHash) { + echo $this->getManageSubscriptionHtml($subscriber, $managedSecureHash); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped + exit(); + }, 1); + } + + public function getManageSubscriptionHtml($subscriber, $secureHash = '') + { + $absEmail = $this->hideEmail($subscriber->email); + + $absEmailHash = md5($absEmail); + + $subscribedLists = $subscriber->lists->keyBy('id'); + + $businessSettings = fluentcrmGetGlobalSettings('business_settings', []); + + $lists = Helper::getPublicLists(); + + $listOptions = []; + foreach ($lists as $list) { + $listOptions[] = [ + 'id' => strval($list->id), + 'label' => $list->title, + 'selected' => isset($subscribedLists[$list->id]) + ]; + } + + return fluentCrm('view')->make('external.manage_subscription', [ + 'subscriber' => $subscriber, + 'abs_email' => $absEmail, + 'abs_hash' => $absEmailHash, + 'subscribed_lists' => $subscribedLists, + 'list_options' => $listOptions, + 'business' => $businessSettings, + 'secure_hash' => $secureHash + ]); + } + + public function handleManageSubPref() + { + $secureHash = Arr::get($_REQUEST, '_secure_hash'); + + $absHash = Arr::get($_REQUEST, '_abs_hash'); + $email = Arr::get($_REQUEST, 'email'); + $originalHash = Arr::get($_REQUEST, '_original_hash'); + + $subscriber = null; + if ($secureHash) { + $subscriber = fluentCrmApi('contacts')->getContactByManagedSecureHash($secureHash); + } + + if (!$subscriber) { + wp_send_json_error([ + 'message' => __('Sorry! No subscriber found in the database', 'fluent-crm') + ], 422); + return; + } + + $addedLists = []; + $detachedListIds = []; + $publicLists = Helper::getPublicLists(); + if ($publicLists) { + $lists = Arr::get($_REQUEST, 'lists', []); + $alreadyListIds = array_keys($subscriber->lists->keyBy('id')->toArray()); + $publicListIds = array_values(array_keys($publicLists->keyBy('id')->toArray())); + $addedListIds = array_values(array_intersect($lists, $publicListIds)); + if ($alreadyListIds) { + $addedListIds = array_diff($addedListIds, $alreadyListIds); + } + $detachedListIds = array_values(array_diff($publicListIds, $lists)); + + $addedLists = $addedListIds; + } + + if ($absHash != md5($email)) { + // Email has been changed + if (!is_email($email)) { + wp_send_json_error([ + 'message' => __('Email is not valid. Please provide a valid email', 'fluent-crm') + ], 422); + } + + // Check if unique + $exist = Subscriber::where('email', $email)->where('id', '!=', $subscriber->id)->first(); + if ($exist) { + wp_send_json_error([ + 'message' => __('The new email has been used to another account. Please use a new email address', 'fluent-crm') + ], 422); + } + + $subscriber->status = 'pending'; + $subscriber->email = $email; + $subscriber->first_name = sanitize_text_field(Arr::get($_REQUEST, 'first_name', '')); + $subscriber->last_name = sanitize_text_field(Arr::get($_REQUEST, 'last_name', '')); + $subscriber->save(); + $subscriber->sendDoubleOptinEmail(); + + if ($addedLists) { + $subscriber->attachLists($addedLists); + } + if ($detachedListIds) { + $subscriber->detachLists($detachedListIds); + } + + wp_send_json_success([ + 'message' => sprintf( + /* translators: %s: the new email address to which confirmation was sent */ + esc_html__('A confirmation email has been sent to %s. Please confirm your email address to resubscribe with changed email address', 'fluent-crm'), + esc_html(sanitize_email($email)) + ) + ], 200); + return; + } + + // Just update the info + $subscriber->first_name = sanitize_text_field(Arr::get($_REQUEST, 'first_name', '')); + $subscriber->last_name = sanitize_text_field(Arr::get($_REQUEST, 'last_name', '')); + $subscriber->save(); + + if ($addedLists) { + $subscriber->attachLists($addedLists); + } + + if ($detachedListIds) { + $subscriber->detachLists($detachedListIds); + } + + if ($subscriber->status != 'subscribed') { + $subscriber->sendDoubleOptinEmail(); + wp_send_json_success([ + 'message' => sprintf( + /* translators: %s: the email address to which confirmation was sent */ + esc_html__('A confirmation email has been sent to %s. Please confirm your email address to resubscribe', 'fluent-crm'), + esc_html(sanitize_email($email)) + ) + ], 200); + } + + wp_send_json_success([ + 'message' => __('Your provided information has been successfully updated', 'fluent-crm') + ], 200); + } + + public function SmartUrlHandler() + { + if (isset($_REQUEST['slug'])) { + do_action('fluentcrm_smartlink_clicked', sanitize_text_field($_REQUEST['slug'])); + } + } + + private function hideEmail($email) + { + list($first, $last) = explode('@', $email); + if (strlen($first) > 2) { + $first = str_replace(substr($first, 2), str_repeat('*', strlen($first) - 2), $first); + } + $last = explode('.', $last); + $last_domain = str_replace(substr($last['0'], '1'), str_repeat('*', strlen($last['0']) - 1), $last['0']); + array_shift($last); + return $first . '@' . $last_domain . '.' . implode('.', $last); + } + + private function loadAssets() + { + if (defined('CT_VERSION')) { + // oxygen page compatibility + remove_action('wp_head', 'oxy_print_cached_css', 999999); + } + + wp_enqueue_style( + 'fluentcrm_public_pref', + fluentCrmMix('public/public_pref.css'), + [], + FLUENTCRM_PLUGIN_VERSION + ); + + wp_enqueue_script( + 'fluentcrm_public_pref', + fluentCrmMix('public/public_pref.js'), + ['jquery'], + FLUENTCRM_PLUGIN_VERSION + ); + + $complianceSettings = Helper::getComplianceSettings(); + + wp_localize_script('fluentcrm_public_pref', 'fluentcrm_public_pref', [ + 'ajaxurl' => admin_url('admin-ajax.php'), + /** + * Determine if auto unsubscribe should be enabled in FluentCRM. + * + * This filter allows customization of the auto unsubscribe behavior. + * + * @param bool|string Default value is 'no'. Can be overridden to 'yes' to enable auto unsubscribe. + * @return bool|string Filtered value to determine if auto unsubscribe should be enabled. + * @since 2.8.34 + * + */ + 'auto_unsubscribe' => apply_filters('fluent_crm/will_auto_unsubscribe', Arr::get($complianceSettings, 'one_click_unsubscribe', 'no')) + ]); + } + + private function extractEmail($from_email) + { + $bracket_pos = strpos($from_email, '<'); + if (false !== $bracket_pos) { + $from_email = substr($from_email, $bracket_pos + 1); + $from_email = str_replace('>', '', $from_email); + $from_email = trim($from_email); + } + + if (is_email($from_email)) { + return $from_email; + } + return false; + } + + public function handleBackgroundProcessCallback() + { + $callbackName = sanitize_text_field(Arr::get($_REQUEST, 'callback_name')); + $nonce = Arr::get($_REQUEST, 'nonce'); + + if (!$callbackName || !$nonce || !wp_verify_nonce($nonce, 'fluentcrm_callback_for_background')) { + die('Security Check Failed'); + } + $data = Arr::get($_REQUEST, 'payload', []); + do_action($callbackName, $data); + echo 'success'; + die(); + } + + public function handleGeneralWebhook() + { + $data = $_REQUEST; + + $handler = sanitize_text_field(Arr::get($_REQUEST, 'handler')); + + do_action('fluentcrm_webhook_to_' . $handler, $data); + + wp_send_json([ + 'message' => __('No Action found', 'fluent-crm'), + 'action' => 'fluentcrm_webhook_to_' . $handler + ]); + } + + public function handlePreviewEmail() + { + + nocache_headers(); + + $emailHash = sanitize_text_field(Arr::get($_REQUEST, '_e_hash')); + + if (!$emailHash) { + // Maybe it's the Campaign Share Email + $newsLetterShareId = sanitize_text_field(Arr::get($_REQUEST, 'fc_newsletter')); + + if ($newsLetterShareId) { + $this->showNewesLetterView($newsLetterShareId); + return; + } + } + + $email = CampaignEmail::where('email_hash', $emailHash)->with(['campaign', 'subscriber'])->first(); + + $businessSettings = fluentcrmGetGlobalSettings('business_settings', []); + + if (!$email || !$email->campaign || !$email->subscriber) { + fluentCrm('view')->render('external.view_on_browser', [ + 'business' => $businessSettings, + 'email_heading' => '', + 'email' => null, + 'email_body' => '

Sorry, web preview could not be loaded

', + 'cssAssets' => [ + fluentCrmMix('public/public_pref.css') . '?version=' . FLUENTCRM_PLUGIN_VERSION + ], + 'footer_text' => '' + ]); + exit(); + } + + if ($email->campaign && Arr::get($email->campaign->settings, 'template_config')) { + $templateConfig = wp_parse_args($email->campaign->settings['template_config'], Helper::getTemplateConfig($email->campaign->design_template, false)); + } else { + $templateConfig = Helper::getTemplateConfig('', false); + } + + $rawTemplates = [ + 'raw_html', + 'visual_builder', + 'raw_classic' + ]; + + if (in_array($email->campaign->design_template, $rawTemplates)) { + $emailBody = $email->campaign->email_body; + } else { + $emailBody = (new BlockParser($email->subscriber))->parse($email->campaign->email_body); + } + + /** + * Email Footer Text For WebUI + * @param string $footerText + * @param CampaignEmail $email + */ + $footerText = Helper::getEmailFooterContent($email->campaign); + + /** + * Determine the footer text of the web email in FluentCRM. + * + * This filter allows you to modify the footer text of the web email. + * + * @param string $footerText The current footer text. + * @param array $email The email data array. + * + * @return string The modified footer text. + * @since 2.8.40 + * + */ + $footerText = apply_filters('fluent_crm/web_email_footer_text', $footerText, $email); + + $emailSubject = $email->email_subject; + $preHeader = ($email->campaign) ? $email->campaign->email_pre_header : ''; + + $subscriber = $email->subscriber; + if ($subscriber) { + /** + * Determine the campaign email body content text in FluentCRM. + * + * This filter allows modification of the campaign email text before it is sent to the subscriber. + * + * @param string $emailBody The email body content. + * @param object $subscriber The subscriber object containing subscriber details. + * + * @return string The filtered email body content. + * @since 2.8.02 + * + */ + $emailBody = apply_filters('fluent_crm/parse_campaign_email_text', $emailBody, $subscriber); + /** + * Determine the footer text of a campaign email in FluentCRM. + * + * This filter allows you to modify the footer text of a campaign email before it is sent to the subscriber. + * + * @param string $footerText The footer text of the campaign email. + * @param object $subscriber The subscriber object containing subscriber details. + * + * @return string The modified footer text. + * @since 2.8.02 + * + */ + $footerText = apply_filters('fluent_crm/parse_campaign_email_text', $footerText, $subscriber); + /** + * Determine the campaign email subject text in FluentCRM. + * + * This filter allows you to modify the email subject text for a campaign. + * + * @param string $emailSubject The original email subject text. + * @param object $subscriber The subscriber object. + * + * @return string The filtered email subject text. + * @since 2.8.02 + * + */ + $emailSubject = apply_filters('fluent_crm/parse_campaign_email_text', $emailSubject, $subscriber); + /** + * Determine the Email pre-header text of a campaign email in FluentCRM. + * + * This filter allows you to modify the pre-header text of a campaign email before it is sent to the subscriber. + * + * @param string $preHeader The pre-header text of the campaign email. + * @param object $subscriber The subscriber object containing subscriber details. + * + * @return string The filtered pre-header text. + * @since 2.8.02 + * + */ + $preHeader = apply_filters('fluent_crm/parse_campaign_email_text', $preHeader, $subscriber); + } + + $emailFooterConfig = Helper::getFooterConfig($email->campaign); + $emailFooterConfig['footer_content'] = $footerText; + + $templateData = [ + 'preHeader' => $preHeader, + 'email_body' => $emailBody, + 'footer_text' => $footerText, + 'footer_config' => $emailFooterConfig, + 'config' => $templateConfig + ]; + + if ($email->campaign->design_template == 'visual_builder') { + /** + * Determine the email design template content in the visual builder in FluentCRM. + * + * @param string $emailBody The email body content. + * @param array $templateData The template data. + * @param object $email->campaign The email campaign object. + * @param object $email->subscriber The email subscriber object. + * @since 2.7.40 + * + */ + $content = apply_filters('fluent_crm/email-design-template-visual_builder', + $emailBody, + $templateData, + $email->campaign, + $email->subscriber + ); + $footerText = ''; + } elseif ($email->campaign->design_template == 'raw_html') { + $content = $emailBody; + $footerText = ''; + } else { + /** + * Apply the campaign design template for the web preview. + * + * Single render pass — passes the original parsed email body directly to the + * design template handler. No intermediate web_preview pass is applied, which + * prevented duplicate footers and double Emogrifier runs for block_editor, + * simple, plain, classic, and raw_classic templates. + * + * @param string $emailBody The original parsed email body content. + * @param array $templateData Template data including footer_config so filterTemplateData() + * applies custom footer styling and sanitization correctly. + * @param object $email->campaign The campaign object. + * @param object $subscriber The subscriber object. + * @since 2.8.40 + * + */ + $content = apply_filters( + 'fluent_crm/email-design-template-' . $email->campaign->design_template, + $emailBody, + $templateData, + $email->campaign, + $subscriber + ); + } + + $preViewUrl = site_url('?fluentcrm=1&route=email_preview&_e_hash=' . $email->email_hash); + $content = str_replace(['##web_preview_url##', '{{crm_global_email_footer}}', '{{crm_preheader_text}}'], [$preViewUrl, $footerText, $preHeader], $content); + + if (Str::contains($content, ['##crm.', '{{crm.'])) { + /** + * Determine the Smartcode text content before it is parsed in FluentCRM. + * + * This filter allows you to modify the Smartcode text content before it is parsed. + * + * @param string $content The Smartcode text content to be parsed. + * @param object $subscriber The subscriber object associated with the email. + * @since 2.8.40 + * + */ + $content = apply_filters('fluent_crm/parse_extended_crm_text', $content, $subscriber); + } + + $content = str_replace(['https://fonts.googleapis.com/css2', 'https://fonts.googleapis.com/css'], 'https://fonts.bunny.net/css', $content); + + $data = [ + 'business' => $businessSettings, + 'email_heading' => $emailSubject, + 'email' => $email, + 'email_body' => [ + 'rendered' => $content + ], + 'cssAssets' => [ + fluentCrmMix('public/public_pref.css') . '?version=' . FLUENTCRM_PLUGIN_VERSION + ] + ]; + + /** + * Determine the data used to view an email in the browser in FluentCRM. + * + * This filter allows modification of the data that is used when viewing an email in the browser. + * + * @param array $data The data to be used for viewing the email in the browser. + * @param object $email The email object containing the email details. + * @since 2.7.40 + * + */ + $data = apply_filters('fluent_crm/email_view_on_browser_data', $data, $email); + + fluentCrm('view')->render('external.view_on_browser', $data); + exit(); + } + + public function showNewesLetterView($nsHash) + { + nocache_headers(); + $meta = Meta::where('object_type', 'FluentCrm\App\Models\Campaign') + ->where('key', '_campaign_share_id') + ->where('value', $nsHash) + ->first(); + + + $businessSettings = fluentcrmGetGlobalSettings('business_settings', []); + + $campaign = null; + + if ($meta) { + $campaign = Campaign::withoutGlobalScope('type')->find($meta->object_id); + } + + if (!$campaign) { + fluentCrm('view')->render('external.view_on_browser', [ + 'business' => $businessSettings, + 'email_heading' => '', + 'email' => null, + 'email_body' => '

Sorry, web preview could not be loaded

', + 'cssAssets' => [ + fluentCrmMix('public/public_pref.css') . '?version=' . FLUENTCRM_PLUGIN_VERSION + ], + 'footer_text' => '' + ]); + exit(); + } + + $emailBody = fluentcrm_get_campaign_meta($campaign->id, '_cached_email_body', true); + + if (!$emailBody) { + // Let's generate the email body + $rawTemplates = [ + 'raw_html', + 'visual_builder', + 'raw_classic' + ]; + + if (in_array($campaign->design_template, $rawTemplates)) { + $emailBody = $campaign->email_body; + } else { + $emailBody = (new BlockParser(fluentcrm_get_current_contact()))->parse($campaign->email_body); + } + } + + $emailSubject = $campaign->subject; + + + $subscriber = fluentcrm_get_current_contact(); + + /** + * Determine the campaign email subject text in FluentCRM. + * + * This filter allows you to modify the email subject text for a campaign. + * + * @param string $emailSubject The original email subject text. + * @param object $subscriber The subscriber object. + * + * @return string The filtered email subject text. + * @since 2.8.44 + * + */ + $emailSubject = apply_filters('fluent_crm/parse_campaign_email_text', $emailSubject, $subscriber); + /** + * Determine the campaign email text in FluentCRM. + * + * This filter allows modification of the campaign email text before it is sent to the subscriber. + * + * @param string $emailBody The email body content. + * @param object $subscriber The subscriber object. + * @since 2.8.44 + * + */ + $emailBody = apply_filters('fluent_crm/parse_campaign_email_text', $emailBody, $subscriber); + + $templateConfig = wp_parse_args($campaign->settings['template_config'], Helper::getTemplateConfig($campaign->design_template, false)); + + $templateData = [ + 'preHeader' => '', + 'email_body' => $emailBody, + 'footer_text' => '', + 'config' => $templateConfig + ]; + + + if ($campaign->design_template == 'visual_builder' || $campaign->design_template == 'raw_html') { + $content = $emailBody; + if ($campaign->design_template == 'visual_builder') { + /** + * Determine the email design template in the visual builder in FluentCRM. + * + * This filter allows customization of the email design template in the visual builder. + * + * @param string $content The current content of the email design template. + * @param array $templateData Data related to the email template. + * @param object $campaign The campaign object. + * @param object $subscriber The subscriber object. + * + * @return string The filtered email design template content. + * @since 2.8.44 + * + */ + $content = apply_filters('fluent_crm/email-design-template-visual_builder', + $content, + $templateData, + $campaign, + $subscriber + ); + } + } else { + /** + * Determine the email design template content for various template types in FluentCRM. + * + * This filter allows modification of the email design template content before it is sent. + * + * @param string $content The email content after applying the design template. + * @param string $emailBody The original email body content. + * @param array $templateData An array of data used in the email template. + * @param object $campaign The campaign object containing campaign details. + * @param object $subscriber The subscriber object containing subscriber details. + * @since 2.8.44 + * + */ + $content = apply_filters('fluent_crm/email-design-template-' . $campaign->design_template, + $emailBody, + $templateData, + $campaign, + $subscriber + ); + } + + $content = str_replace(['https://fonts.googleapis.com/css2', 'https://fonts.googleapis.com/css'], 'https://fonts.bunny.net/css', $content); + + $data = [ + 'business' => $businessSettings, + 'email_heading' => $emailSubject, + 'email' => null, + 'email_body' => [ + 'rendered' => $content + ], + 'cssAssets' => [ + fluentCrmMix('public/public_pref.css') . '?version=' . FLUENTCRM_PLUGIN_VERSION + ] + ]; + + /** + * Determine the full email newsletter data in FluentCRM. + * + * This filter allows modification of the email newsletter data before it is processed. + * + * @param array $data The email newsletter data. + * @since 2.8.44 + * + */ + $data = apply_filters('fluent_crm/email_newsletter_data', $data); + + fluentCrm('view')->render('external.view_on_browser', $data); + exit(); + } + + public function handleGeneralRequest() + { + $data = $_REQUEST; + $handler = sanitize_text_field(Arr::get($_REQUEST, 'handler')); + + if ($handler) { + do_action('fluent_crm/handle_frontend_for_' . $handler, $data); + } + + } +} diff --git a/wp-content/plugins/fluent-crm/app/Hooks/Handlers/FluentBlockEditorHandler.php b/wp-content/plugins/fluent-crm/app/Hooks/Handlers/FluentBlockEditorHandler.php new file mode 100644 index 0000000..13c774a --- /dev/null +++ b/wp-content/plugins/fluent-crm/app/Hooks/Handlers/FluentBlockEditorHandler.php @@ -0,0 +1,2264 @@ + 'Email-Body', + 'public' => false, + 'show_in_rest' => true, + 'supports' => ['editor', 'thumbnail'] + ]); + + if (isset($_REQUEST['fluent_crm_block_editor'])) { + // Require authentication + if (!is_user_logged_in()) { + $scheme = is_ssl() ? 'https://' : 'http://'; + $current_url = $scheme . (isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : '') . (isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : ''); + wp_safe_redirect(wp_login_url(esc_url_raw($current_url))); + exit; + } + + // Determine required capability based on context + $block_type = isset($_REQUEST['block_type']) ? sanitize_text_field(wp_unslash($_REQUEST['block_type'])) : ''; + $required_cap = self::getRequiredCapability($block_type); + /** + * Filter the required capability for opening the FluentCRM block editor. + * + * @param string $required_cap Default capability derived from block type. + * @param string $block_type The requested block type. + * @param array $request Raw request array for advanced checks. + */ + $required_cap = apply_filters('fluent_crm/block_editor_required_cap', $required_cap, $block_type, $_REQUEST); + + if (!PermissionManager::currentUserCan($required_cap)) { + status_header(403); + wp_die(__('Sorry, you are not allowed to access this page.', 'fluent-crm'), 403); + } + + // Optional nonce enforcement (off by default to preserve existing links) + $require_nonce = (bool)apply_filters('fluent_crm/block_editor_require_nonce', false, $block_type, $_REQUEST); + if ($require_nonce) { + $nonce = isset($_REQUEST['_fcrm_nonce']) ? sanitize_text_field(wp_unslash($_REQUEST['_fcrm_nonce'])) : ''; + if (!$nonce || !wp_verify_nonce($nonce, 'fcrm_block_editor')) { + status_header(403); + wp_die(__('Invalid or missing security token.', 'fluent-crm'), 403); + } + } + if (!defined('IFRAME_REQUEST')) { + define('IFRAME_REQUEST', true); + } + + remove_action('enqueue_block_editor_assets', 'wp_enqueue_editor_block_directory_assets'); + add_action('fluent_crm/block_editor_head', function () { + $asset_file = FLUENTCRM_PLUGIN_PATH . 'assets/guten-editor/index.asset.php'; + $asset = file_exists($asset_file) ? require($asset_file) : []; + $version = !empty($asset['version']) ? $asset['version'] : FLUENTCRM_PLUGIN_VERSION; + $url = FLUENTCRM_PLUGIN_URL . 'assets/guten-editor/index.css'; + ?> + + initializeEditor($_REQUEST); // phpcs:ignore WordPress.Security.NonceVerification.Recommended + + + $actionHook = 'template_redirect'; + if(is_admin()) { + $actionHook = 'admin_init'; + } + + add_action($actionHook, function () { + $this->renderPage(); + exit(200); + }, -1000); + + } + }, 2); + + // REST route for autosave + add_action('rest_api_init', function () { + register_rest_route('fluent-crm/v1', '/editor-autosave', [ + 'methods' => 'POST', + 'callback' => [$this, 'handleEditorAutosave'], + 'permission_callback' => function () { + // Basic capability check – refined per payload inside handler + return is_user_logged_in(); + } + ]); + + register_rest_route('fluent-crm/v2', '/editor/cart-products', [ + 'methods' => 'GET', + 'callback' => [$this, 'handleCartProductsListing'], + 'permission_callback' => function () { + return is_user_logged_in() && PermissionManager::currentUserCan('fcrm_read_emails'); + } + ]); + + // Native REST routes for editor pattern CRUD (wp_block-compatible format) + $patternPermission = function () { + return is_user_logged_in() && PermissionManager::currentUserCan('fcrm_manage_email_templates'); + }; + + register_rest_route('fluent-crm/v2', '/editor-patterns', [ + 'methods' => \WP_REST_Server::READABLE, + 'callback' => [$this, 'handleEditorPatternsList'], + 'permission_callback' => $patternPermission + ]); + + register_rest_route('fluent-crm/v2', '/editor-patterns', [ + 'methods' => \WP_REST_Server::CREATABLE, + 'callback' => [$this, 'handleEditorPatternCreate'], + 'permission_callback' => $patternPermission + ]); + + register_rest_route('fluent-crm/v2', '/editor-patterns/(?P\d+)', [ + 'methods' => \WP_REST_Server::READABLE, + 'callback' => [$this, 'handleEditorPatternGet'], + 'permission_callback' => $patternPermission + ]); + + register_rest_route('fluent-crm/v2', '/editor-patterns/(?P\d+)', [ + 'methods' => \WP_REST_Server::EDITABLE, + 'callback' => [$this, 'handleEditorPatternUpdate'], + 'permission_callback' => $patternPermission + ]); + + register_rest_route('fluent-crm/v2', '/editor-patterns/(?P\d+)', [ + 'methods' => \WP_REST_Server::DELETABLE, + 'callback' => [$this, 'handleEditorPatternDelete'], + 'permission_callback' => function () { + return is_user_logged_in() && PermissionManager::currentUserCan('fcrm_manage_email_delete'); + } + ]); + + register_rest_route('fluent-crm/v2', '/editor-pattern-categories', [ + 'methods' => \WP_REST_Server::READABLE, + 'callback' => [$this, 'handleEditorPatternCategories'], + 'permission_callback' => $patternPermission + ]); + + register_rest_route('fluent-crm/v2', '/editor-pattern-categories', [ + 'methods' => \WP_REST_Server::CREATABLE, + 'callback' => [$this, 'handleEditorPatternCategoryCreate'], + 'permission_callback' => $patternPermission + ]); + }); + } + + /** + * Register FluentCRM conditional visibility attributes in PHP. + * + * WordPress validates dynamic block attributes against the server-side + * block schema during render REST requests, so this also runs when the + * render request contains FluentCRM's conditional visibility attributes. + * + * @param array $args Block registration arguments. + * @param string $name Block name. + * @return array + */ + public function registerConditionalVisibilityAttributes($args, $name) + { + $shouldRegister = $this->shouldRegisterConditionalVisibilityAttributes(); + + if (!apply_filters('fluent_crm/block_editor_register_conditional_visibility_attributes', $shouldRegister, $name, $args)) { + return $args; + } + + $skipBlocks = [ + 'fluentcrm/conditional-group', + 'fluent-crm/conditional-content' + ]; + + if (in_array($name, $skipBlocks, true)) { + return $args; + } + + if (!isset($args['attributes']) || !is_array($args['attributes'])) { + $args['attributes'] = []; + } + + $attributes = apply_filters('fluent_crm/block_editor_conditional_visibility_attributes', [ + 'fcrmConditionType' => [ + 'type' => 'string', + 'default' => '' + ], + 'fcrmTagIds' => [ + 'type' => 'array', + 'default' => [] + ] + ], $name, $args); + + foreach ((array)$attributes as $attributeName => $attributeConfig) { + if (!isset($args['attributes'][$attributeName])) { + $args['attributes'][$attributeName] = $attributeConfig; + } + } + + return $args; + } + + /** + * Check if the current request needs FluentCRM conditional visibility + * attributes in server-side block schemas. + * + * @return bool + */ + private function shouldRegisterConditionalVisibilityAttributes() + { + if (isset($_REQUEST['fluent_crm_block_editor'])) { + return true; + } + + if (!isset($_REQUEST['attributes'])) { + return false; + } + + $attributes = wp_unslash($_REQUEST['attributes']); + + if (is_array($attributes)) { + return array_key_exists('fcrmConditionType', $attributes) || array_key_exists('fcrmTagIds', $attributes); + } + + if (is_string($attributes)) { + return strpos($attributes, 'fcrmConditionType') !== false || strpos($attributes, 'fcrmTagIds') !== false; + } + + return false; + } + + /** + * Handle autosave requests for the block editor iframe. + * Creates or updates campaign / recurring campaign / template records. + * If block_type is empty and id/bid is 0, nothing will be created. + * + * @param \WP_REST_Request $request + * @return \WP_REST_Response|\WP_Error + */ + public function handleEditorAutosave(\WP_REST_Request $request) + { + $params = $request->get_json_params(); + if (!$params) { + $params = $request->get_body_params(); + } + + $blockType = isset($params['block_type']) ? sanitize_text_field($params['block_type']) : ''; + $entityId = isset($params['id']) ? (int)$params['id'] : 0; + $title = isset($params['title']) ? wp_strip_all_tags($params['title']) : ''; + $content = isset($params['content']) ? $params['content'] : ''; + $prevUpdatedAt = isset($params['prev_updated_at']) ? sanitize_text_field($params['prev_updated_at']) : ''; + $hash = isset($params['hash']) ? sanitize_text_field($params['hash']) : ''; + + // Basic guard: if block type empty and no existing entity, do not create + if (empty($blockType) && !$entityId) { + return new \WP_REST_Response([ + 'status' => 'skipped', + 'message' => __('No block_type provided; nothing created.', 'fluent-crm') + ], 200); + } + + // Capability check + $required_cap = self::getRequiredCapability($blockType); + $required_cap = apply_filters('fluent_crm/block_editor_required_cap', $required_cap, $blockType, $params); + if (!PermissionManager::currentUserCan($required_cap)) { + return new \WP_Error('forbidden', __('You do not have permission to autosave this item', 'fluent-crm'), ['status' => 403]); + } + + // Resolve model + field mapping + $model = null; + $contentField = null; + $titleField = null; + if ($blockType === 'campaign') { + $model = Campaign::class; + $contentField = 'email_body'; + $titleField = 'title'; + } elseif ($blockType === 'email_body_in_funnel') { + $model = FunnelCampaign::class; + $contentField = 'email_body'; + $titleField = 'title'; + } elseif ($blockType === 'recurring_campaign') { + if (defined('FLUENTCAMPAIGN')) { + $model = \FluentCampaign\App\Models\RecurringCampaign::class; + } + // Free-tier: $model stays null; the find block below uses withoutGlobalScopes() fallback. + $contentField = 'email_body'; + $titleField = 'title'; + } elseif ($blockType === 'sequence_mail') { + if (defined('FLUENTCAMPAIGN')) { + $model = \FluentCampaign\App\Models\SequenceMail::class; + } + // Free-tier: $model stays null; the find block below uses withoutGlobalScopes() fallback. + $contentField = 'email_body'; + $titleField = 'title'; + } elseif ($blockType === 'recurring_mail') { + if (defined('FLUENTCAMPAIGN')) { + $model = \FluentCampaign\App\Models\RecurringMail::class; + } + $contentField = 'email_body'; + $titleField = 'title'; + } elseif ($blockType === 'template') { + $model = Template::class; + $contentField = 'post_content'; + $titleField = 'post_title'; + } else { + return new \WP_Error('invalid_block_type', __('Unsupported block_type', 'fluent-crm'), ['status' => 400]); + } + + // Guard: recurring_campaign requires Pro; without it $model stays null — skip gracefully + if ($model === null) { + return new \WP_REST_Response([ + 'status' => 'skipped', + 'message' => __('Feature not available.', 'fluent-crm') + ], 200); + } + + $now = current_time('mysql'); + $created = false; + $record = null; + + if (!$entityId) { + return new \WP_REST_Response([ + 'status' => 'skipped', + 'message' => __('No entity ID provided.', 'fluent-crm') + ], 200); + } + + if ($entityId) { + if ($model) { + $record = $model::find($entityId); + } else { + // recurring_campaign on free tier: fc_campaigns with type=recurring_campaign. + // sequence_mail on free tier: fc_campaigns with type=sequence_mail. + // Bypass the Campaign global scope so the type filter does not exclude it. + $record = Campaign::withoutGlobalScopes()->find($entityId); + } + if (!$record) { + return new \WP_Error('not_found', __('Entity not found', 'fluent-crm'), ['status' => 404]); + } + // Conflict detection (always compare with model's updated_at mapping) + if (!empty($prevUpdatedAt) && isset($record->updated_at) && $prevUpdatedAt && $prevUpdatedAt !== $record->updated_at) { + return new \WP_REST_Response([ + 'status' => 'conflict', + 'id' => $entityId, + 'server_updated_at' => $record->updated_at, + 'message' => __('The item was modified elsewhere.', 'fluent-crm') + ], 200); + } + } + + // Update existing record if needed + $dirty = false; + if ($blockType === 'template') { + // For templates we ONLY save post_content using core wp_update_post for proper cache + hooks + if ($record->{$contentField} !== $content) { + + $postArr = [ + 'ID' => $entityId, + 'post_content' => $content, + 'post_modified' => current_time('mysql'), + 'post_modified_gmt' => gmdate('Y-m-d H:i:s') + ]; + $result = wp_update_post($postArr, true); + if (is_wp_error($result)) { + return new \WP_Error('update_failed', $result->get_error_message(), ['status' => 500]); + } + $dirty = true; + // Reload record to get fresh timestamps and updated content + $record = Template::find($entityId); + } + } else { + if ($record->{$contentField} !== $content) { + $record->{$contentField} = $content; + $dirty = true; + } + // Even though frontend no longer sends title for autosave, keep defensive logic (but campaign/recurring only) + if ($title && $record->{$titleField} !== $title) { + $record->{$titleField} = $title; + $dirty = true; + } + if ($dirty) { + try { + $record->save(); + } catch (\Exception $e) { + return new \WP_Error('update_failed', $e->getMessage(), ['status' => 500]); + } + } + } + + $updatedAt = isset($record->updated_at) ? $record->updated_at : $now; + + return new \WP_REST_Response([ + 'status' => $created ? 'created' : ($dirty ? 'ok' : 'noop'), + 'id' => $entityId, + 'hash' => $hash, + 'updated_at' => $updatedAt, + 'saved_at' => $now, + 'created' => $created + ], 200); + } + + public function handleCartProductsListing(\WP_REST_Request $request) + { + $fallback = [ + 'products' => [], + 'product' => null, + 'taxonomies' => [] + ]; + + $providerResponse = apply_filters('fluent_crm/cart_products_preview_data', null, $request); + if (is_array($providerResponse)) { + return new \WP_REST_Response(wp_parse_args($providerResponse, $fallback), 200); + } + + if (!defined('FLUENTCART_VERSION')) { + return new \WP_REST_Response($fallback, 200); + } + + $perPage = max(1, min(20, absint($request->get_param('per_page') ?: 3))); + $order = strtolower((string)$request->get_param('order')) === 'asc' ? 'ASC' : 'DESC'; + $productId = absint($request->get_param('product_id')); + $search = sanitize_text_field((string)$request->get_param('search')); + if (!$productId) { + $searchProductId = 0; + if (preg_match('/^\s*(?:id|product_id)\s*:\s*(\d+)\s*$/i', $search, $matches)) { + $searchProductId = absint($matches[1]); + } elseif (preg_match('/^\s*#\s*(\d+)\s*$/', $search, $matches)) { + $searchProductId = absint($matches[1]); + } + + if ($searchProductId) { + $productId = $searchProductId; + $search = ''; + } + } + + $taxType = sanitize_text_field((string)$request->get_param('taxType')); + $products = CartProductData::getProducts([ + 'perPage' => $perPage, + 'order' => $order, + 'orderBy' => sanitize_key((string)$request->get_param('orderby')), + 'taxType' => $taxType, + 'search' => $search, + 'productId' => $productId, + ], 'medium'); + + $selectedProduct = ($productId && !empty($products[0])) ? $products[0] : null; + + $terms = get_terms([ + 'taxonomy' => 'product-categories', + 'hide_empty' => false + ]); + $termOptions = []; + if (!is_wp_error($terms) && is_array($terms)) { + foreach ($terms as $term) { + $termOptions[] = [ + 'value' => (string)$term->term_id, + 'label' => $term->name + ]; + } + } + + $response = [ + 'products' => $products, + 'product' => $selectedProduct, + 'taxonomies' => [ + 'product' => [ + 'terms' => [ + 'product_cat' => $termOptions + ] + ] + ] + ]; + + return new \WP_REST_Response( + apply_filters('fluent_crm/cart_products_preview_response', $response, $request), + 200 + ); + } + + /* + |-------------------------------------------------------------------------- + | Editor init: initializeEditor(), resolveEntityContent(), + | getOrCreateDummyPost(), prepareEditorBootData(), setupEditorHooks() + |-------------------------------------------------------------------------- + */ + + public function initializeEditor($data = []) + { + do_action('litespeed_control_set_nocache', 'fluentcrm api request'); + // set no cache headers + nocache_headers(); + + $context = Arr::get($data, 'block_type'); // campaign or template etc .. + $this->unregisterDefaultBlockPatterns($context, $data); + + // Double-check permissions inside renderer based on context + $required_cap = self::getRequiredCapability($context); + $required_cap = apply_filters('fluent_crm/block_editor_required_cap', $required_cap, $context, $data); + $hasAccess = PermissionManager::currentUserCan($required_cap); + + $entity = $this->resolveEntityContent($context, $data); + + if (!$hasAccess) { + echo '

' . esc_html__('Sorry, you do not have access to this page.', 'fluent-crm') . '

'; + exit(200); + } + + add_filter('should_load_separate_core_block_assets', '__return_false', 20); + show_admin_bar(false); + + global $post; + $post = $this->getOrCreateDummyPost($entity['title'], $entity['content']); + + $this->prepareEditorBootData($context, $data, $entity); + $this->setupEditorHooks($post); + } + + /** + * Resolve entity content from the database based on block_type and bid. + * + * @param string $context The block_type (campaign, recurring_campaign, template). + * @param array $data The request data. + * @return array With keys: content, title, id, updatedAt, availableTags. + */ + private function resolveEntityContent($context, $data) + { + $post_content = '

'; + $post_title = 'Demo Title'; + $recordId = null; + $recordUpdatedAt = current_time('mysql'); + $availableTags = []; + + if ($context == 'campaign') { + $campaignId = (int)Arr::get($data, 'bid'); + if ($campaignId && $campaignId != 'undefined') { + $campaign = Campaign::find($campaignId); + if ($campaign) { + $post_content = $campaign->email_body; + $post_title = $campaign->title; + $recordId = $campaign->id; + $recordUpdatedAt = isset($campaign->updated_at) ? $campaign->updated_at : $recordUpdatedAt; + } + } + } + + if ($context == 'email_body_in_funnel') { + $campaignId = (int)Arr::get($data, 'bid'); + if ($campaignId && $campaignId != 'undefined') { + $campaign = FunnelCampaign::find($campaignId); + if ($campaign) { + $post_content = $campaign->email_body; + $post_title = $campaign->title; + $recordId = $campaign->id; + $recordUpdatedAt = isset($campaign->updated_at) ? $campaign->updated_at : $recordUpdatedAt; + } + } + } + + // Pro Required for Recurring Campaigns + if (defined('FLUENTCAMPAIGN')) { + + if ($context == 'recurring_campaign') { + $campaign = null; + $campaignId = (int)Arr::get($data, 'bid'); + if ($campaignId && $campaignId != 'undefined') { + $campaign = \FluentCampaign\App\Models\RecurringCampaign::find($campaignId); + } + // Fallback for free-tier Email Sequences stored in fc_campaigns with type=recurring_campaign. + if (!$campaign && $campaignId) { + $campaign = Campaign::withoutGlobalScopes()->find($campaignId); + } + if ($campaign) { + $post_content = $campaign->email_body; + $post_title = $campaign->title; + $recordId = $campaign->id; + $recordUpdatedAt = isset($campaign->updated_at) ? $campaign->updated_at : $recordUpdatedAt; + } + } + + if ($context == 'recurring_mail') { + $mailId = (int)Arr::get($data, 'bid'); + if ($mailId && $mailId != 'undefined') { + $mail = \FluentCampaign\App\Models\RecurringMail::find($mailId); + if ($mail) { + $post_content = $mail->email_body; + $post_title = $mail->title; + $recordId = $mail->id; + $recordUpdatedAt = isset($mail->updated_at) ? $mail->updated_at : $recordUpdatedAt; + } + } + } + + if ($context == 'sequence_mail') { + $mail = null; + $mailId = (int)Arr::get($data, 'bid'); + if ($mailId && $mailId != 'undefined') { + $mail = \FluentCampaign\App\Models\SequenceMail::find($mailId); + } + // Fallback for free-tier Email Sequences stored in fc_campaigns with type=sequence_mail. + if (!$mail && $mailId) { + $mail = Campaign::withoutGlobalScopes()->find($mailId); + } + if ($mail) { + $post_content = $mail->email_body; + $post_title = $mail->title; + $recordId = $mail->id; + $recordUpdatedAt = isset($mail->updated_at) ? $mail->updated_at : $recordUpdatedAt; + } + } + } + + if ($context == 'template') { + $templateId = (int)Arr::get($data, 'bid'); + if ($templateId && $templateId != 'undefined') { + $template = Template::find($templateId); + if ($template) { + $post_content = $template->post_content; + $post_title = $template->post_title; + $recordId = $templateId; + $recordUpdatedAt = isset($template->updated_at) ? $template->updated_at : $recordUpdatedAt; + } + } + } + + if ($context == 'email_pattern') { + $patternId = (int)Arr::get($data, 'bid'); + if ($patternId && $patternId != 'undefined') { + $pattern = Meta::where('object_type', 'email_pattern')->where('id', $patternId)->first(); + if ($pattern) { + $post_content = Arr::get($pattern->value, 'content', ''); + $post_title = Arr::get($pattern->value, 'title', ''); + $recordId = $pattern->id; + $recordUpdatedAt = $pattern->updated_at ?: $recordUpdatedAt; + } + } + } + + try { + $availableTags = Tag::select(['id', 'title'])->orderBy('title', 'ASC')->get(); + } catch (\Throwable $e) { + $availableTags = []; + } + + if (!$post_content) { + $post_content = '

'; + } + + return [ + 'content' => $post_content, + 'title' => $post_title, + 'id' => $recordId, + 'updatedAt' => $recordUpdatedAt, + 'availableTags' => $availableTags, + ]; + } + + /** + * Retrieve or create the fcrm-dummy post used as a simulated post for the editor. + * + * @param string $title + * @param string $content + * @return \WP_Post + */ + private function getOrCreateDummyPost($title, $content) + { + $firstPost = fluentCrmDb()->table('posts') + ->where('post_type', 'fcrm-dummy') + ->first(); + + if ($firstPost) { + $simulatedPost = get_post($firstPost->ID); + $simulatedPost->post_content = $content; + $simulatedPost->post_title = $title; + } else { + $newPostId = wp_insert_post(array( + 'post_title' => $title, + 'post_content' => $content, + 'post_type' => 'fcrm-dummy', + 'post_status' => 'draft', + )); + + $simulatedPost = get_post($newPostId); + } + + return $simulatedPost; + } + + /* + * ----------------------------------------------------------------------- + * Native REST handlers for editor patterns (wp_block-compatible format). + * These bypass WPFluent's response pipeline so core-data gets raw responses. + * ----------------------------------------------------------------------- + */ + + public function handleEditorPatternsList() + { + $patterns = Meta::where('object_type', 'email_pattern') + ->orderBy('id', 'desc') + ->get(); + + $categoryMap = $this->getPatternCategoryMap(); + $items = []; + foreach ($patterns as $pattern) { + $items[] = $this->formatMetaAsWpBlock($pattern, $categoryMap); + } + + return new \WP_REST_Response($items, 200); + } + + public function handleEditorPatternGet(\WP_REST_Request $request) + { + $id = (int) $request->get_param('id'); + $pattern = Meta::where('object_type', 'email_pattern')->where('id', $id)->first(); + + if (!$pattern) { + return new \WP_Error('not_found', __('Pattern not found', 'fluent-crm'), ['status' => 404]); + } + + $categoryMap = $this->getPatternCategoryMap(); + return new \WP_REST_Response($this->formatMetaAsWpBlock($pattern, $categoryMap), 200); + } + + public function handleEditorPatternCreate(\WP_REST_Request $request) + { + $params = $request->get_json_params(); + if (empty($params)) { + $params = $request->get_body_params(); + } + + $title = Arr::get($params, 'title', ''); + if (is_array($title)) { + $title = Arr::get($title, 'raw', ''); + } + $title = sanitize_text_field($title); + + $content = Arr::get($params, 'content', ''); + if (is_array($content)) { + $content = Arr::get($content, 'raw', ''); + } + $content = wp_kses_post($content); + + if (!$title) { + $title = __('Untitled Pattern', 'fluent-crm'); + } + + $meta = Arr::get($params, 'meta', []); + $syncStatus = is_array($meta) ? sanitize_text_field(Arr::get($meta, 'wp_pattern_sync_status', '')) : ''; + + $categoryIds = (array) Arr::get($params, 'wp_pattern_category', []); + $categoryName = $this->resolvePatternCategoryName($categoryIds); + + $slug = 'fluentcrm/' . sanitize_title($title . '-' . uniqid()); + + $pattern = Meta::create([ + 'object_type' => 'email_pattern', + 'object_id' => get_current_user_id(), + 'key' => $slug, + 'value' => [ + 'title' => $title, + 'content' => $content, + 'category' => $categoryName, + 'description' => '', + 'sync_status' => $syncStatus, + ], + ]); + + $categoryMap = $this->getPatternCategoryMap(); + return new \WP_REST_Response($this->formatMetaAsWpBlock($pattern, $categoryMap), 201); + } + + public function handleEditorPatternUpdate(\WP_REST_Request $request) + { + $id = (int) $request->get_param('id'); + $pattern = Meta::where('object_type', 'email_pattern')->where('id', $id)->first(); + + if (!$pattern) { + return new \WP_Error('not_found', __('Pattern not found', 'fluent-crm'), ['status' => 404]); + } + + $params = $request->get_json_params(); + if (empty($params)) { + $params = $request->get_body_params(); + } + + $value = $pattern->value; + + $title = Arr::get($params, 'title'); + if ($title !== null) { + if (is_array($title)) { + $title = Arr::get($title, 'raw', ''); + } + $value['title'] = sanitize_text_field($title); + } + + $content = Arr::get($params, 'content'); + if ($content !== null) { + if (is_array($content)) { + $content = Arr::get($content, 'raw', ''); + } + $value['content'] = wp_kses_post($content); + } + + $meta = Arr::get($params, 'meta', []); + if (is_array($meta) && isset($meta['wp_pattern_sync_status'])) { + $value['sync_status'] = sanitize_text_field($meta['wp_pattern_sync_status']); + } + + $categoryIds = Arr::get($params, 'wp_pattern_category'); + if ($categoryIds !== null) { + $value['category'] = $this->resolvePatternCategoryName((array) $categoryIds); + } + + $pattern->value = $value; + $pattern->save(); + + $categoryMap = $this->getPatternCategoryMap(); + return new \WP_REST_Response($this->formatMetaAsWpBlock($pattern, $categoryMap), 200); + } + + public function handleEditorPatternDelete(\WP_REST_Request $request) + { + $id = (int) $request->get_param('id'); + $pattern = Meta::where('object_type', 'email_pattern')->where('id', $id)->first(); + + if (!$pattern) { + return new \WP_Error('not_found', __('Pattern not found', 'fluent-crm'), ['status' => 404]); + } + + $response = $this->formatMetaAsWpBlock($pattern, $this->getPatternCategoryMap()); + $pattern->delete(); + + return new \WP_REST_Response($response, 200); + } + + public function handleEditorPatternCategories() + { + $categories = Meta::where('object_type', 'email_pattern_category') + ->orderBy('id', 'asc') + ->get(); + + $items = []; + foreach ($categories as $cat) { + $items[] = [ + 'id' => (int) $cat->id, + 'count' => 0, + 'name' => Arr::get($cat->value, 'name', $cat->key), + 'slug' => $cat->key, + 'parent' => 0, + ]; + } + + return new \WP_REST_Response($items, 200); + } + + public function handleEditorPatternCategoryCreate(\WP_REST_Request $request) + { + $params = $request->get_json_params(); + if (empty($params)) { + $params = $request->get_body_params(); + } + + $name = sanitize_text_field(Arr::get($params, 'name', '')); + if (!$name) { + return new \WP_Error('missing_name', __('Category name is required', 'fluent-crm'), ['status' => 400]); + } + + $slug = sanitize_title($name); + $existing = Meta::where('object_type', 'email_pattern_category')->where('key', $slug)->first(); + + if ($existing) { + return new \WP_REST_Response([ + 'id' => (int) $existing->id, + 'count' => 0, + 'name' => Arr::get($existing->value, 'name', $existing->key), + 'slug' => $existing->key, + 'parent' => 0, + ], 200); + } + + $category = Meta::create([ + 'object_type' => 'email_pattern_category', + 'object_id' => 0, + 'key' => $slug, + 'value' => ['name' => $name], + ]); + + return new \WP_REST_Response([ + 'id' => (int) $category->id, + 'count' => 0, + 'name' => $name, + 'slug' => $slug, + 'parent' => 0, + ], 201); + } + + private function formatMetaAsWpBlock($meta, $categoryMap = []) + { + $value = $meta->value; + $title = Arr::get($value, 'title', ''); + $content = Arr::get($value, 'content', ''); + $syncStatus = Arr::get($value, 'sync_status', 'unsynced'); + $category = Arr::get($value, 'category', ''); + + $categoryIds = []; + if ($category) { + $catSlug = sanitize_title($category); + if (isset($categoryMap[$catSlug])) { + $categoryIds[] = (int) $categoryMap[$catSlug]; + } + } + + return [ + 'id' => (int) $meta->id, + 'date' => $meta->created_at ?: gmdate('Y-m-d\TH:i:s'), + 'date_gmt' => $meta->created_at ?: gmdate('Y-m-d\TH:i:s'), + 'modified' => $meta->updated_at ?: gmdate('Y-m-d\TH:i:s'), + 'modified_gmt' => $meta->updated_at ?: gmdate('Y-m-d\TH:i:s'), + 'slug' => $meta->key, + 'status' => 'publish', + 'type' => 'wp_block', + 'link' => '', + 'title' => ['raw' => $title], + 'content' => ['raw' => $content, 'protected' => false], + 'meta' => new \stdClass(), + 'wp_pattern_sync_status' => $syncStatus ?: '', + 'wp_pattern_category' => $categoryIds, + ]; + } + + private function getPatternCategoryMap() + { + $categories = Meta::where('object_type', 'email_pattern_category')->get(); + $map = []; + foreach ($categories as $cat) { + $map[$cat->key] = $cat->id; + } + return $map; + } + + private function resolvePatternCategoryName($categoryIds) + { + if (empty($categoryIds)) { + return ''; + } + $categoryIds = array_map('intval', $categoryIds); + $category = Meta::where('object_type', 'email_pattern_category') + ->whereIn('id', $categoryIds) + ->first(); + + return $category ? Arr::get($category->value, 'name', $category->key) : ''; + } + + /** + * Build the editorBootData array that is injected into the iframe as window.fcrmEditorBoot. + * + * @param string $context + * @param array $data + * @param array $entity Return value from resolveEntityContent(). + */ + private function prepareEditorBootData($context, $data, $entity) + { + $canSave = (!empty($context) && $context !== '0'); + // If block_type null/empty and bid == 0 we won't create anything (flag can_save false) + if (empty($context) && (int)Arr::get($data, 'bid') === 0) { + $canSave = false; + } + // New templates (id=0) should not autosave until they are explicitly created. + if ($context === 'template' && (int)Arr::get($data, 'bid') === 0) { + $canSave = false; + } + $hideBackBtn = self::parseBoolParam(Arr::get($data, 'hideBackBtn', Arr::get($data, 'hide_back_btn', false))); + $hideNextBtn = self::parseBoolParam(Arr::get($data, 'hideNextBtn', Arr::get($data, 'hide_next_btn', false))); + $hideSaveBtn = self::parseBoolParam(Arr::get($data, 'hideSaveBtn', Arr::get($data, 'hide_save_btn', false))); + $disableAutosave = self::parseBoolParam(Arr::get($data, 'disableAutosave', Arr::get($data, 'disable_autosave', false))); + if ($disableAutosave) { + $canSave = false; + } + // Load user-saved email patterns from the database + $savedPatternData = \FluentCrm\App\Http\Controllers\EmailPatternController::getEditorPatterns(); + $savedPatterns = $savedPatternData['patterns']; + $savedPatternCategories = $savedPatternData['categories']; + + $customPatterns = apply_filters('fluent_crm/block_editor_custom_patterns', $savedPatterns, $context, $data); + if (!is_array($customPatterns)) { + $customPatterns = []; + } + $customPatternCategories = apply_filters('fluent_crm/block_editor_custom_pattern_categories', $savedPatternCategories, $context, $data); + if (!is_array($customPatternCategories)) { + $customPatternCategories = []; + } + $designTemplate = isset($data['design_template']) ? sanitize_text_field($data['design_template']) : ''; + $features = $this->getEditorFeatures($context); + $this->editorBootData = [ + 'entity' => [ + 'id' => $entity['id'], + 'block_type' => $context, + 'title' => $entity['title'], + 'content' => $entity['content'], + 'updated_at' => $entity['updatedAt'], + ], + 'can_save' => $canSave, + 'autosave' => [ + 'endpoint' => rest_url('fluent-crm/v1/editor-autosave'), + 'nonce' => wp_create_nonce('wp_rest') + ], + 'fcrm_ui' => isset($data['fcrm_ui']) ? sanitize_text_field($data['fcrm_ui']) : '', + 'compose_nav' => [ + 'hideBackBtn' => $hideBackBtn, + 'hideNextBtn' => $hideNextBtn, + 'hideSaveBtn' => $hideSaveBtn + ], + 'email_template_designs' => \FluentCrm\App\Services\Helper::getEmailDesignTemplates(), + 'current_design_template' => $designTemplate, + 'available_tags' => $entity['availableTags'], + 'global_email_footer' => \FluentCrm\App\Services\Helper::getEmailFooterContent(), + 'more_menu' => [ + 'help_url' => apply_filters('fluent_crm/block_editor_help_url', 'https://fluentcrm.com/docs/'), + 'patterns_url' => apply_filters('fluent_crm/block_editor_patterns_url', admin_url('edit.php?post_type=wp_block')), + 'hide_welcome_guide' => (bool)apply_filters('fluent_crm/block_editor_hide_welcome_guide', true), + 'hide_manage_patterns' => (bool)apply_filters('fluent_crm/block_editor_hide_manage_patterns', true), + 'replace_native_help' => (bool)apply_filters('fluent_crm/block_editor_replace_native_help', true), + ], + 'patterns' => [ + 'unregister_all' => (bool)apply_filters('fluent_crm/block_editor_unregister_all_patterns', true, $context, $data), + 'custom' => array_values($customPatterns), + 'categories' => array_values($customPatternCategories), + ], + 'ai_writing' => $this->getAiWritingConfig(), + 'features' => $features, + ]; + } + + /** + * Get editor feature flags based on content type. + * This is the single source of truth for which UI elements show per context. + * + * @param string $context block_type: campaign, template, email_pattern, recurring_campaign, sequence_mail, email_body_in_funnel + * @return array + */ + private function getEditorFeatures($context) + { + // Full email editing features (default for campaigns, templates, etc.) + $emailDefaults = [ + 'email_style_settings' => true, + 'email_footer' => true, + 'email_preview' => true, + 'save_as_template' => true, + 'browse_templates' => true, + 'smartcodes' => true, + 'design_switcher' => true, + 'save_draft' => true, + ]; + + // Minimal features for non-email content (patterns, snippets, etc.) + $minimalDefaults = [ + 'email_style_settings' => false, + 'email_footer' => false, + 'email_preview' => true, + 'save_as_template' => false, + 'browse_templates' => false, + 'smartcodes' => false, + 'design_switcher' => false, + 'switch_editor' => false, + 'create_pattern' => false, + 'save_draft' => true, + 'sidebar_panel_title' => __('Pattern Info', 'fluent-crm'), + 'sidebar_content' => '

' . __('Editing Pattern', 'fluent-crm') . '

' + . '

' . __('Patterns are reusable block layouts that can be inserted into any email. Changes here will apply to all future emails that use this pattern.', 'fluent-crm') . '

' + . '

' . __('Synced patterns stay linked — editing here updates everywhere. Unsynced patterns are copied on insert.', 'fluent-crm') . '

', + ]; + + $presets = [ + 'campaign' => $emailDefaults, + 'template' => $emailDefaults, + 'recurring_campaign' => $emailDefaults, + 'recurring_mail' => $emailDefaults, + 'sequence_mail' => $emailDefaults, + 'email_body_in_funnel' => $emailDefaults, + 'email_pattern' => $minimalDefaults, + ]; + + $features = isset($presets[$context]) ? $presets[$context] : $emailDefaults; + + return apply_filters('fluent_crm/block_editor_features', $features, $context); + } + + private function getAiWritingConfig() + { + $credentials = get_option('_fluent_ai_creds', []); + $preferences = fluentcrm_get_option('_ai_writing_settings', []); + + if (!is_array($credentials)) { + $credentials = []; + } + + $provider = isset($credentials['provider']) ? $credentials['provider'] : ''; + $hasApiKey = !empty($credentials['api_key']) || $provider === 'wordpress'; + + return [ + 'enabled' => ( + is_array($credentials) + && is_array($preferences) + && isset($preferences['is_enabled']) + && $preferences['is_enabled'] === 'yes' + && $hasApiKey + && !empty($credentials['provider']) + && !empty($credentials['model']) + ), + ]; + } + + /** + * Register wp_enqueue_scripts callbacks, post-locking filters, and other editor hooks. + * + * @param \WP_Post $post + */ + private function setupEditorHooks($post) + { + $enqueueHook = 'wp_enqueue_scripts'; + + add_action($enqueueHook, function () use ($post) { + wp_enqueue_script('postbox', admin_url('js/postbox.min.js'), array('jquery-ui-sortable'), false, 1); + wp_enqueue_editor(); + wp_enqueue_script('wp-tinymce'); + wp_enqueue_style('dashicons'); + wp_enqueue_style('media'); + wp_enqueue_style('admin-menu'); + wp_enqueue_style('admin-bar'); + wp_enqueue_style('l10n'); + + wp_add_inline_script( + 'wp-api-fetch', + \sprintf( + 'wp.apiFetch.use( wp.apiFetch.createPreloadingMiddleware( %s ) );', + wp_json_encode( + array( + '/wp/v2/fcrm-dummy/' . $post->ID . '?context=edit' => array( + 'body' => array( + 'id' => $post->ID, + 'title' => array('raw' => $post->post_title), + 'content' => array( + 'block_format' => 1, + 'raw' => $post->post_content, + ), + 'excerpt' => array('raw' => ''), + 'date' => '', + 'date_gmt' => '', + 'modified' => '', + 'modified_gmt' => '', + 'link' => home_url('/'), + 'guid' => array(), + 'parent' => 0, + 'menu_order' => 0, + 'author' => 0, + 'featured_media' => 0, + 'comment_status' => 'closed', + 'ping_status' => 'closed', + 'template' => '', + 'meta' => array(), + '_links' => array(), + 'type' => 'fcrm-dummy', + 'status' => 'pending', // pending is the best state to remove draft saving possibilities. + 'slug' => '', + 'generated_slug' => '', + 'permalink_template' => home_url('/'), + ), + ), + ) + ) + ), + 'after' + ); + }, 11); + + add_action($enqueueHook, function ($hook) use ($post) { + // Gutenberg requires the post-locking functions defined within: + // See `show_post_locked_dialog` and `get_post_metadata` filters below. + include_once ABSPATH . 'wp-admin/includes/post.php'; + $this->enqueueEditorAssets($hook, $post); + }); + + // Disable post locking dialogue. + add_filter('show_post_locked_dialog', '__return_false'); + + // Everyone can richedit! This avoids a case where a page can be cached where a user can't richedit. + $GLOBALS['wp_rich_edit'] = true; + add_filter('user_can_richedit', '__return_true', 1000); + + // This prevents other logged-in users taking a lock of the post on the front-end. + add_filter('get_post_metadata', function ($value, $post_id, $meta_key) { + if ($meta_key !== '_edit_lock') { + return $value; + } + return time() . ':' . get_current_user_id(); + }, 10, 3); + + // Disable Jetpack Blocks for now. + add_filter('jetpack_gutenberg', '__return_false'); + } + + /* + |-------------------------------------------------------------------------- + | Assets: enqueueEditorAssets(), enqueueEditorScripts(), + | enqueueEditorStyles(), enqueueCustomEditorAssets() + |-------------------------------------------------------------------------- + */ + + private function enqueueEditorAssets($hook, $post) + { + + $initial_edits = array( + 'title' => $post->post_title, + 'content' => $post->post_content, + 'excerpt' => $post->post_excerpt, + ); + + $editor_settings = $this->getEditorSettings($post); + + $init_script = <<post_type, + $post->ID, + wp_json_encode($editor_settings), + wp_json_encode($initial_edits) + ); + wp_add_inline_script('wp-edit-post', $script); + + $this->enqueueEditorScripts($post); + $this->enqueueEditorStyles(); + $this->enqueueCustomEditorAssets(); + } + + /** + * Enqueue media, tinymce, and postbox init scripts. + * + * @param \WP_Post $post + */ + private function enqueueEditorScripts($post) + { + wp_enqueue_media( + array( + 'post' => null + ) + ); + + add_filter('user_can_richedit', '__return_true'); + wp_tinymce_inline_scripts(); + wp_enqueue_editor(); + wp_enqueue_script('wp-tinymce'); + } + + /** + * Enqueue wp-edit-post, block library styles, and editor format library assets. + */ + private function enqueueEditorStyles() + { + wp_enqueue_style('wp-edit-post'); + + /* + These styles are usually registered by Gutenberg and register properly when the user is signed in. + However, if the use is not registered they are not added. For now, include them, but this isn't a good long term strategy + + See: https://github.com/WordPress/wporg-gutenberg/issues/26 + */ + wp_enqueue_style('wp-block-library'); + wp_enqueue_style('wp-block-image'); + wp_enqueue_style('wp-block-group'); + wp_enqueue_style('wp-block-heading'); + wp_enqueue_style('wp-block-button'); + wp_enqueue_style('wp-block-paragraph'); + wp_enqueue_style('wp-block-separator'); + wp_enqueue_style('wp-block-columns'); + wp_enqueue_style('wp-block-row'); + wp_enqueue_style('wp-block-cover'); + wp_enqueue_style('wp-block-spacer'); + + wp_register_style('fluent_crm_editor_styles', FLUENTCRM_PLUGIN_URL . 'assets/guten-editor/style.css', false, FLUENTCRM_PLUGIN_VERSION, 'all'); + + add_action('fluent_enqueue_block_editor_assets', 'wp_enqueue_editor_format_library_assets'); + + /** + * Fires after block assets have been enqueued for the editing interface. + * + * Call `add_action` on any hook before 'admin_enqueue_scripts'. + * + * In the function call you supply, simply use `wp_enqueue_script` and + * `wp_enqueue_style` to add your functionality to the Gutenberg editor. + * + * @since 0.4.0 + */ + do_action('fluent_enqueue_block_editor_assets'); + } + + /** + * Enqueue fcrm_editor_custom JS, inline block editor config, smartcodes, and boot data. + */ + private function enqueueCustomEditorAssets() + { + $editor_asset_file = FLUENTCRM_PLUGIN_PATH . 'assets/guten-editor/index.asset.php'; + $editor_asset = file_exists($editor_asset_file) ? require($editor_asset_file) : []; + $editor_deps = !empty($editor_asset['dependencies']) ? $editor_asset['dependencies'] : ['react', 'wp-edit-post', 'wp-plugins']; + $editor_version = !empty($editor_asset['version']) ? $editor_asset['version'] : FLUENTCRM_PLUGIN_VERSION; + if (!in_array('wp-edit-post', $editor_deps)) { + $editor_deps[] = 'wp-edit-post'; + } + if (!in_array('wp-reusable-blocks', $editor_deps)) { + $editor_deps[] = 'wp-reusable-blocks'; + } + wp_enqueue_script('fcrm_editor_custom', FLUENTCRM_PLUGIN_URL . 'assets/guten-editor/index.js', $editor_deps, $editor_version, true); + + $availableDesigns = Helper::getEmailDesignTemplates(); + $availableDesigns = array_filter($availableDesigns, function ($design) { + return !empty($design['use_gutenberg']); + }); + $designPresetPayloads = []; + foreach ($availableDesigns as $designKey => $design) { + $designPresetPayloads[$designKey] = $design; + $designPresetPayloads[$designKey]['config'] = [ + 'design_template' => Arr::get($design, 'id', $designKey) + ]; + } + + $blockEditorConfig = [ + 'modules' => [ + 'hasWooCommerce' => defined('WC_PLUGIN_FILE'), + 'hasFluentCampaign' => defined('FLUENTCAMPAIGN'), + 'hasFluentCart' => defined('FLUENTCART_VERSION') + ], + 'endpoints' => [ + 'products' => apply_filters('fluent_crm/block_editor_products_endpoint', 'fluent-crm/v2/campaigns-pro/products'), + 'cartProducts' => apply_filters('fluent_crm/block_editor_cart_products_endpoint', 'fluent-crm/v2/editor/cart-products'), + 'tags' => apply_filters('fluent_crm/block_editor_tags_endpoint', 'fluent-crm/v2/reports/options?fields=tags') + ], + 'fontSizes' => BlockEditorHelper::getDefaultPreset('font-size'), + 'spacingPresets' => BlockEditorHelper::getDefaultPreset('spacing'), + 'defaultDesignConfig' => Helper::getTemplateConfig(false), + // Keep the shared design metadata intact for legacy consumers while ensuring + // Gutenberg preset clicks only change the selected design template. + 'designTemplatePresets' => $designPresetPayloads, + 'default_design_template' => Helper::getDefaultEmailTemplate() + ]; + + wp_add_inline_script( + 'fcrm_editor_custom', + 'window.fcrmBlockEditorConfig = ' . wp_json_encode($blockEditorConfig) . ';', + 'before' + ); + + // Inject smartcodes into the iframe window + $globalSmartCodes = Helper::getGlobalSmartCodes(); + $extendedSmartCodes = Helper::getExtendedSmartCodes(); + $transStrings = TransStringsGuten::getStrings(); + wp_add_inline_script( + 'fcrm_editor_custom', + 'window.fcAdmin = window.fcAdmin || {};' . + 'window.fcAdmin.globalSmartCodes = ' . wp_json_encode($globalSmartCodes) . ';' . + 'window.fcAdmin.extendedSmartCodes = ' . wp_json_encode($extendedSmartCodes) . ';' . + 'window.fcAdmin.trans = Object.assign({}, window.fcAdmin.trans || {}, ' . wp_json_encode($transStrings) . ');', + 'before' + ); + + if (!empty($this->editorBootData)) { + wp_add_inline_script( + 'fcrm_editor_custom', + 'window.fcrmEditorBoot = ' . wp_json_encode($this->editorBootData) . ';', + 'before' + ); + } + } + + /* + |-------------------------------------------------------------------------- + | Settings: getEditorSettings(), getAllowedBlocks(), getEditorStyleSheets(), + | getResolvedAssets(), getDefaultEditorStyles() + |-------------------------------------------------------------------------- + */ + + private function getEditorSettings($post) + { + // Run pattern cleanup here as well (late) so theme/plugin init hooks cannot re-add defaults. + $context = Arr::get($this->editorBootData, 'entity.block_type', ''); + $requestData = is_array($_REQUEST) ? $_REQUEST : []; + $this->unregisterDefaultBlockPatterns($context, $requestData); + + // Media settings. + $max_upload_size = wp_max_upload_size(); + if (!$max_upload_size) { + $max_upload_size = 0; + } + + $lock_details = array( + 'isLocked' => false, + 'user' => '', + ); + + $allowedBlocks = $this->getAllowedBlocks(); + + $blockStyleDefaults = BlockEditorHelper::getStyleDefauls(); + + + $editor_settings = array( + 'maxUploadFileSize' => $max_upload_size, + 'allowedMimeTypes' => get_allowed_mime_types(), + 'postLock' => $lock_details, + 'postLockUtils' => array( + 'nonce' => wp_create_nonce('lock-post_' . $post->ID), + 'unlockNonce' => wp_create_nonce('update-post_' . $post->ID), + 'ajaxUrl' => admin_url('admin-ajax.php'), + ), + '__experimentalFeatures' => array( + 'appearanceTools' => true, + 'useRootPaddingAwareAlignments' => false, + 'border' => [ + 'color' => 1, + 'radius' => 1, + 'style' => 1, + 'width' => 1, + ], + 'color' => [ + 'background' => true, + 'button' => 1, + 'caption' => 1, + 'customDuotone' => 0, + 'defaultDuotone' => 0, + 'defaultGradients' => 0, + 'defaultPalette' => true, + 'duotone' => [], + 'gradients' => [], + 'heading' => 1, + 'link' => 1, + 'palette' => [ + 'default' => $blockStyleDefaults['color'], + ], + 'text' => true, + ], + 'dimensions' => [ + 'units' => ['px'], + 'defaultAspectRatios' => false, + 'aspectRatio' => false, + 'minHeight' => 1, + ], + 'shadow' => [ + 'defaultPresets' => true, + 'presets' => [ + 'default' => [ + [ + 'name' => 'Natural', + 'slug' => 'natural', + 'shadow' => '6px 6px 9px rgba(0, 0, 0, 0.2)', + ], + [ + 'name' => 'Deep', + 'slug' => 'deep', + 'shadow' => '12px 12px 50px rgba(0, 0, 0, 0.4)', + ], + [ + 'name' => 'Sharp', + 'slug' => 'sharp', + 'shadow' => '6px 6px 0px rgba(0, 0, 0, 0.2)', + ], + [ + 'name' => 'Outlined', + 'slug' => 'outlined', + 'shadow' => '6px 6px 0px -3px rgba(255, 255, 255, 1), 6px 6px rgba(0, 0, 0, 1)', + ], + [ + 'name' => 'Crisp', + 'slug' => 'crisp', + 'shadow' => '6px 6px 0px rgba(0, 0, 0, 1)', + ], + ], + ], + ], + 'spacing' => [ + 'blockGap' => 1, + 'margin' => 1, + 'padding' => 1, + 'units' => ['px'], + 'defaultSpacingSizes' => true, + 'spacingScale' => [ + 'default' => [ + 'operator' => '*', + 'increment' => 1.5, + 'steps' => 7, + 'mediumStep' => 24, + 'unit' => 'px', + ], + ], + 'spacingSizes' => [ + 'default' => $blockStyleDefaults['spacing'], + ] + ], + 'typography' => [ + 'defaultFontSizes' => true, + 'dropCap' => false, + 'fontFamilies' => [ + 'default' => $blockStyleDefaults['font-family'] + ], + 'fontSizes' => [ + 'default' => $blockStyleDefaults['font-size'] + ], + 'fontStyle' => true, + 'fontWeight' => true, + 'letterSpacing' => true, + 'textAlign' => true, + 'textDecoration' => true, + 'textTransform' => true, + 'writingMode' => false, + 'units' => ['px'], + 'fluid' => false + ], + 'blocks' => [ + 'core/button' => [ + 'border' => [ + 'radius' => true, + ] + ], + 'core/buttons' => [ + 'border' => [ + 'radius' => false, + ], + 'spacing' => [ + 'blockGap' => false, + ], + 'layout' => false, + 'contentRole' => false + ], + 'core/image' => [ + 'lightbox' => [ + 'allowEditing' => true, + ] + ], + 'core/pullquote' => [ + 'border' => [ + 'color' => true, + 'radius' => true, + 'style' => true, + 'width' => true, + ] + ], + 'core/paragraph' => [ + 'spacing' => [ + 'margin' => 1, + 'padding' => 1, + ] + ], + 'core/columns' => [ + // disable block gap for columns block, but keep for inner column blocks + 'spacing' => [ + 'blockGap' => array( + '__experimentalDefault' => '20px', + 'sides' => array( + 'horizontal' + ) + ), + 'defaultSpacingSizes' => false, + ], + 'border' => [ + 'color' => true, + 'radius' => false, + 'style' => true, + 'width' => true, + ], + 'shadow' => false, + ], + 'core/group' => [ + '__experimentalSettings' => false, + 'shadow' => false, + 'dimensions' => [ + 'minHeight' => false + ], + 'spacing' => [ + 'blockGap' => false, + 'margin' => true, + 'padding' => true, + ], + 'position' => [ + 'sticky' => false, + ], + 'layout' => [ + 'allowSizingOnChildren' => false, + 'contentSize' => false + ] + ], + 'core/row' => [ + '__experimentalSettings' => false, + 'shadow' => false, + 'dimensions' => [ + 'minHeight' => false + ], + 'spacing' => [ + 'blockGap' => true, + 'margin' => true, + 'padding' => true, + ], + 'position' => [ + 'sticky' => false, + ] + ] + ], + 'layout' => [ + 'contentSize' => 'var(--theme-block-max-width)', + 'wideSize' => 'var(--theme-block-wide-max-width)', + ], + 'background' => [ + 'backgroundImage' => 1, + 'backgroundSize' => 1, + ], + 'position' => [ + 'sticky' => 0, + ] + ), + '__experimentalDiscussionSettings' => [ + 'avatarURL' => 'https://secure.gravatar.com/avatar/?s=96&f=y&r=g', + 'commentOrder' => 'asc', + 'commentsPerPage' => '50', + 'defaultCommentsPage' => 'newest', + 'defaultCommentStatus' => 'open', + 'pageComments' => '', + 'threadComments' => '1', + 'threadCommentsDepth' => '5' + ], + '__unstableGalleryWithImageBlocks' => false, + '__unstableIsBlockBasedTheme' => false, + 'enableCustomUnits' => false, + 'fontSizes' => [ + [ + 'name' => 'Small', + 'size' => 'var(--fcom-font-size-small)', + 'slug' => 'small' + ], + [ + 'name' => 'Medium', + 'size' => 'var(--fcom-font-size-medium)', + 'slug' => 'medium' + ], + [ + 'name' => 'Large', + 'size' => 'var(--fcom-font-size-large)', + 'slug' => 'large' + ], + [ + 'name' => 'Larger', + 'size' => 'var(--fcom-font-size-larger)', + 'slug' => 'larger' + ], + [ + 'name' => 'XX-Large', + 'size' => 'var(--fcom-font-size-xxlarge)', + 'slug' => 'xxlarge' + ] + ], + 'fullscreenMode' => 1, + 'enableCustomSpacing' => 1, + 'enableCustomLineHeight' => 1, + 'enableCustomFields' => false, + 'disablePostFormats' => true, + 'disableLayoutStyles' => true, + 'disableCustomSpacingSizes' => false, + 'disableCustomGradients' => 1, + 'alignWide' => true, + 'disableCustomFontSizes' => false, + 'disableCustomColors' => false, + 'canUpdateBlockBindings' => false, + 'bodyPlaceholder' => __('Start writing your email...', 'fluent-crm'), + 'allowedBlockTypes' => $allowedBlocks, + 'gradients' => [], + 'imageDefaultSize' => 'large', + 'imageEditing' => true, + 'isRTL' => is_rtl(), + 'autosaveInterval' => 999, + 'localAutosaveInterval' => 999, + 'richEditingEnabled' => true, + 'spacingSizes' => [ + [ + 'name' => '2X-Small', + 'size' => '0.44rem', + 'slug' => '20' + ], + [ + 'name' => 'X-Small', + 'size' => '0.67rem', + 'slug' => '30' + ], + [ + 'name' => 'Small', + 'size' => '1rem', + 'slug' => '40' + ], + [ + 'name' => 'Medium', + 'size' => '1.5rem', + 'slug' => '50' + ], + [ + 'name' => 'Large', + 'size' => '2.25rem', + 'slug' => '60' + ], + [ + 'name' => 'X-Large', + 'size' => '3.38rem', + 'slug' => '70' + ], + [ + 'name' => '2X-Large', + 'size' => '5.06rem', + 'slug' => '80' + ] + ], + 'titlePlaceholder' => __('Add Lesson title', 'fluent-crm') + ); + + $editor_settings['styles'] = $this->getEditorStyleSheets(); + $editor_settings['__unstableResolvedAssets'] = $this->getResolvedAssets(); + $editor_settings['defaultEditorStyles'] = $this->getDefaultEditorStyles(); + $editor_settings['imageSizes'] = $this->getAvailableImageSizes(); + + $editor_settings['__experimentalBlockPatterns'] = array_values((array)apply_filters( + 'fluent_crm/block_editor_custom_patterns', + [], + $context, + $requestData + )); + $editor_settings['__experimentalBlockPatternCategories'] = array_values((array)apply_filters( + 'fluent_crm/block_editor_custom_pattern_categories', + [], + $context, + $requestData + )); + + $editor_settings = apply_filters('fluent_crm/block_editor_settings', $editor_settings); + return $editor_settings; + } + + /** + * Return the allowed blocks array for the editor. + * + * @return array + */ + private function getAllowedBlocks() + { + $allowedBlocks = [ + 'core/block', + 'core/buttons', + 'core/button', + 'core/code', + 'core/columns', + 'core/column', + 'core/footnotes', + 'core/freeform', + 'core/group', + 'core/row', + 'core/heading', + 'core/html', + 'core/image', + 'core/list', + 'core/list-item', + 'core/missing', + 'core/paragraph', + 'core/preformatted', + 'core/pullquote', + 'core/quote', + 'core/rss', + 'core/separator', + 'core/spacer', + 'core/table', + 'core/verse', + 'core/freeform', + 'fluentcrm/conditional-group' + ]; + + if (defined('WC_PLUGIN_FILE')) { + $allowedBlocks[] = 'fluentcrm/woo-product'; + $allowedBlocks[] = 'fluent-crm/woo-product'; + } + + if (defined('FLUENTCAMPAIGN')) { + $allowedBlocks[] = 'fluent-crm/latest-posts'; + if (defined('WC_PLUGIN_FILE')) { + $allowedBlocks[] = 'fluent-crm/woo-products'; + } + } + + if (defined('FLUENTCART_VERSION')) { + $allowedBlocks[] = 'fluent-crm/cart-products'; + $allowedBlocks[] = 'fluent-crm/cart-product'; + } + + $aiConfig = $this->getAiWritingConfig(); + if (!empty($aiConfig['enabled'])) { + $allowedBlocks[] = 'fluent-crm/ai-writer'; + } + + $allowedBlocks = apply_filters('fluent_crm/new_editor_allowed_block_types', $allowedBlocks); + $allowedBlocks = array_values(array_unique($allowedBlocks)); + + return $allowedBlocks; + } + + /** + * Return the `styles` array for the editor settings (the massive CSS). + * + * @return array + */ + private function getEditorStyleSheets() + { + $defaultPresets = BlockEditorHelper::getStyleDefaultPresets(); + $dynamicCss = BlockEditorHelper::getDynamicCssForEditor(); + + return [ + [ + '__unstableType' => 'presets', + 'css' => ':root{' . $defaultPresets . '--wp--preset--aspect-ratio--square: 1;--wp--preset--aspect-ratio--4-3: 4/3;--wp--preset--aspect-ratio--3-4: 3/4;--wp--preset--aspect-ratio--3-2: 3/2;--wp--preset--aspect-ratio--2-3: 2/3;--wp--preset--aspect-ratio--16-9: 16/9;--wp--preset--aspect-ratio--9-16: 9/16;--wp--preset--color--theme-palette-color-1: var(--theme-palette-color-1);--wp--preset--color--theme-palette-color-2: var(--theme-palette-color-2);--wp--preset--color--theme-palette-color-3: var(--theme-palette-color-3);--wp--preset--color--theme-palette-color-4: var(--theme-palette-color-4);--wp--preset--color--theme-palette-color-5: var(--theme-palette-color-5);--wp--preset--color--theme-palette-color-6: var(--theme-palette-color-6);--wp--preset--color--theme-palette-color-7: var(--theme-palette-color-7);--wp--preset--color--theme-palette-color-8: var(--theme-palette-color-8);--wp--preset--gradient--vivid-cyan-blue-to-vivid-purple: linear-gradient(135deg,rgba(6,147,227,1) 0%,rgb(155,81,224) 100%);--wp--preset--gradient--light-green-cyan-to-vivid-green-cyan: linear-gradient(135deg,rgb(122,220,180) 0%,rgb(0,208,130) 100%);--wp--preset--gradient--luminous-vivid-amber-to-luminous-vivid-orange: linear-gradient(135deg,rgba(252,185,0,1) 0%,rgba(255,105,0,1) 100%);--wp--preset--gradient--luminous-vivid-orange-to-vivid-red: linear-gradient(135deg,rgba(255,105,0,1) 0%,rgb(207,46,46) 100%);--wp--preset--gradient--very-light-gray-to-cyan-bluish-gray: linear-gradient(135deg,rgb(238,238,238) 0%,rgb(169,184,195) 100%);--wp--preset--gradient--cool-to-warm-spectrum: linear-gradient(135deg,rgb(74,234,220) 0%,rgb(151,120,209) 20%,rgb(207,42,186) 40%,rgb(238,44,130) 60%,rgb(251,105,98) 80%,rgb(254,248,76) 100%);--wp--preset--gradient--blush-light-purple: linear-gradient(135deg,rgb(255,206,236) 0%,rgb(152,150,240) 100%);--wp--preset--gradient--blush-bordeaux: linear-gradient(135deg,rgb(254,205,165) 0%,rgb(254,45,45) 50%,rgb(107,0,62) 100%);--wp--preset--gradient--luminous-dusk: linear-gradient(135deg,rgb(255,203,112) 0%,rgb(199,81,192) 50%,rgb(65,88,208) 100%);--wp--preset--gradient--pale-ocean: linear-gradient(135deg,rgb(255,245,203) 0%,rgb(182,227,212) 50%,rgb(51,167,181) 100%);--wp--preset--gradient--electric-grass: linear-gradient(135deg,rgb(202,248,128) 0%,rgb(113,206,126) 100%);--wp--preset--gradient--midnight: linear-gradient(135deg,rgb(2,3,129) 0%,rgb(40,116,252) 100%);--wp--preset--gradient--juicy-peach: linear-gradient(to right, #ffecd2 0%, #fcb69f 100%);--wp--preset--gradient--young-passion: linear-gradient(to right, #ff8177 0%, #ff867a 0%, #ff8c7f 21%, #f99185 52%, #cf556c 78%, #b12a5b 100%);--wp--preset--gradient--true-sunset: linear-gradient(to right, #fa709a 0%, #fee140 100%);--wp--preset--gradient--morpheus-den: linear-gradient(to top, #30cfd0 0%, #330867 100%);--wp--preset--gradient--plum-plate: linear-gradient(135deg, #667eea 0%, #764ba2 100%);--wp--preset--gradient--aqua-splash: linear-gradient(15deg, #13547a 0%, #80d0c7 100%);--wp--preset--gradient--love-kiss: linear-gradient(to top, #ff0844 0%, #ffb199 100%);--wp--preset--gradient--new-retrowave: linear-gradient(to top, #3b41c5 0%, #a981bb 49%, #ffc8a9 100%);--wp--preset--gradient--plum-bath: linear-gradient(to top, #cc208e 0%, #6713d2 100%);--wp--preset--gradient--high-flight: linear-gradient(to right, #0acffe 0%, #495aff 100%);--wp--preset--gradient--teen-party: linear-gradient(-225deg, #FF057C 0%, #8D0B93 50%, #321575 100%);--wp--preset--gradient--fabled-sunset: linear-gradient(-225deg, #231557 0%, #44107A 29%, #FF1361 67%, #FFF800 100%);--wp--preset--gradient--arielle-smile: radial-gradient(circle 248px at center, #16d9e3 0%, #30c7ec 47%, #46aef7 100%);--wp--preset--gradient--itmeo-branding: linear-gradient(180deg, #2af598 0%, #009efd 100%);--wp--preset--gradient--deep-blue: linear-gradient(to right, #6a11cb 0%, #2575fc 100%);--wp--preset--gradient--strong-bliss: linear-gradient(to right, #f78ca0 0%, #f9748f 19%, #fd868c 60%, #fe9a8b 100%);--wp--preset--gradient--sweet-period: linear-gradient(to top, #3f51b1 0%, #5a55ae 13%, #7b5fac 25%, #8f6aae 38%, #a86aa4 50%, #cc6b8e 62%, #f18271 75%, #f3a469 87%, #f7c978 100%);--wp--preset--gradient--purple-division: linear-gradient(to top, #7028e4 0%, #e5b2ca 100%);--wp--preset--gradient--cold-evening: linear-gradient(to top, #0c3483 0%, #a2b6df 100%, #6b8cce 100%, #a2b6df 100%);--wp--preset--gradient--mountain-rock: linear-gradient(to right, #868f96 0%, #596164 100%);--wp--preset--gradient--desert-hump: linear-gradient(to top, #c79081 0%, #dfa579 100%);--wp--preset--gradient--ethernal-constance: linear-gradient(to top, #09203f 0%, #537895 100%);--wp--preset--gradient--happy-memories: linear-gradient(-60deg, #ff5858 0%, #f09819 100%);--wp--preset--gradient--grown-early: linear-gradient(to top, #0ba360 0%, #3cba92 100%);--wp--preset--gradient--morning-salad: linear-gradient(-225deg, #B7F8DB 0%, #50A7C2 100%);--wp--preset--gradient--night-call: linear-gradient(-225deg, #AC32E4 0%, #7918F2 48%, #4801FF 100%);--wp--preset--gradient--mind-crawl: linear-gradient(-225deg, #473B7B 0%, #3584A7 51%, #30D2BE 100%);--wp--preset--gradient--angel-care: linear-gradient(-225deg, #FFE29F 0%, #FFA99F 48%, #FF719A 100%);--wp--preset--gradient--juicy-cake: linear-gradient(to top, #e14fad 0%, #f9d423 100%);--wp--preset--gradient--rich-metal: linear-gradient(to right, #d7d2cc 0%, #304352 100%);--wp--preset--gradient--mole-hall: linear-gradient(-20deg, #616161 0%, #9bc5c3 100%);--wp--preset--gradient--cloudy-knoxville: linear-gradient(120deg, #fdfbfb 0%, #ebedee 100%);--wp--preset--gradient--soft-grass: linear-gradient(to top, #c1dfc4 0%, #deecdd 100%);--wp--preset--gradient--saint-petersburg: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);--wp--preset--gradient--everlasting-sky: linear-gradient(135deg, #fdfcfb 0%, #e2d1c3 100%);--wp--preset--gradient--kind-steel: linear-gradient(-20deg, #e9defa 0%, #fbfcdb 100%);--wp--preset--gradient--over-sun: linear-gradient(60deg, #abecd6 0%, #fbed96 100%);--wp--preset--gradient--premium-white: linear-gradient(to top, #d5d4d0 0%, #d5d4d0 1%, #eeeeec 31%, #efeeec 75%, #e9e9e7 100%);--wp--preset--gradient--clean-mirror: linear-gradient(45deg, #93a5cf 0%, #e4efe9 100%);--wp--preset--gradient--wild-apple: linear-gradient(to top, #d299c2 0%, #fef9d7 100%);--wp--preset--gradient--snow-again: linear-gradient(to top, #e6e9f0 0%, #eef1f5 100%);--wp--preset--gradient--confident-cloud: linear-gradient(to top, #dad4ec 0%, #dad4ec 1%, #f3e7e9 100%);--wp--preset--gradient--glass-water: linear-gradient(to top, #dfe9f3 0%, white 100%);--wp--preset--gradient--perfect-white: linear-gradient(-225deg, #E3FDF5 0%, #FFE6FA 100%);--wp--preset--font-size--small: var(--fcom-font-size-small);--wp--preset--font-size--medium: var(--fcom-font-size-medium);--wp--preset--font-size--large: var(--fcom-font-size-large);--wp--preset--font-size--x-large: 42px;--wp--preset--font-size--larger: var(--fcom-font-size-larger);--wp--preset--font-size--xxlarge: var(--fcom-font-size-xxlarge);--wp--preset--shadow--natural: 6px 6px 9px rgba(0, 0, 0, 0.2);--wp--preset--shadow--deep: 12px 12px 50px rgba(0, 0, 0, 0.4);--wp--preset--shadow--sharp: 6px 6px 0px rgba(0, 0, 0, 0.2);--wp--preset--shadow--outlined: 6px 6px 0px -3px rgba(255, 255, 255, 1), 6px 6px rgba(0, 0, 0, 1);--wp--preset--shadow--crisp: 6px 6px 0px rgba(0, 0, 0, 1);}', + 'isGlobalStyles' => true + ], + [ + '__unstableType' => 'presets', + 'css' => '.has-theme-palette-color-1-color{color: var(--wp--preset--color--theme-palette-color-1) !important;}.has-theme-palette-color-2-color{color: var(--wp--preset--color--theme-palette-color-2) !important;}.has-theme-palette-color-3-color{color: var(--wp--preset--color--theme-palette-color-3) !important;}.has-theme-palette-color-4-color{color: var(--wp--preset--color--theme-palette-color-4) !important;}.has-theme-palette-color-5-color{color: var(--wp--preset--color--theme-palette-color-5) !important;}.has-theme-palette-color-6-color{color: var(--wp--preset--color--theme-palette-color-6) !important;}.has-theme-palette-color-7-color{color: var(--wp--preset--color--theme-palette-color-7) !important;}.has-theme-palette-color-8-color{color: var(--wp--preset--color--theme-palette-color-8) !important;}.has-theme-palette-color-1-background-color{background-color: var(--wp--preset--color--theme-palette-color-1) !important;}.has-theme-palette-color-2-background-color{background-color: var(--wp--preset--color--theme-palette-color-2) !important;}.has-theme-palette-color-3-background-color{background-color: var(--wp--preset--color--theme-palette-color-3) !important;}.has-theme-palette-color-4-background-color{background-color: var(--wp--preset--color--theme-palette-color-4) !important;}.has-theme-palette-color-5-background-color{background-color: var(--wp--preset--color--theme-palette-color-5) !important;}.has-theme-palette-color-6-background-color{background-color: var(--wp--preset--color--theme-palette-color-6) !important;}.has-theme-palette-color-7-background-color{background-color: var(--wp--preset--color--theme-palette-color-7) !important;}.has-theme-palette-color-8-background-color{background-color: var(--wp--preset--color--theme-palette-color-8) !important;}.has-theme-palette-color-1-border-color{border-color: var(--wp--preset--color--theme-palette-color-1) !important;}.has-theme-palette-color-2-border-color{border-color: var(--wp--preset--color--theme-palette-color-2) !important;}.has-theme-palette-color-3-border-color{border-color: var(--wp--preset--color--theme-palette-color-3) !important;}.has-theme-palette-color-4-border-color{border-color: var(--wp--preset--color--theme-palette-color-4) !important;}.has-theme-palette-color-5-border-color{border-color: var(--wp--preset--color--theme-palette-color-5) !important;}.has-theme-palette-color-6-border-color{border-color: var(--wp--preset--color--theme-palette-color-6) !important;}.has-theme-palette-color-7-border-color{border-color: var(--wp--preset--color--theme-palette-color-7) !important;}.has-theme-palette-color-8-border-color{border-color: var(--wp--preset--color--theme-palette-color-8) !important;}.has-vivid-cyan-blue-to-vivid-purple-gradient-background{background: var(--wp--preset--gradient--vivid-cyan-blue-to-vivid-purple) !important;}.has-light-green-cyan-to-vivid-green-cyan-gradient-background{background: var(--wp--preset--gradient--light-green-cyan-to-vivid-green-cyan) !important;}.has-luminous-vivid-amber-to-luminous-vivid-orange-gradient-background{background: var(--wp--preset--gradient--luminous-vivid-amber-to-luminous-vivid-orange) !important;}.has-luminous-vivid-orange-to-vivid-red-gradient-background{background: var(--wp--preset--gradient--luminous-vivid-orange-to-vivid-red) !important;}.has-very-light-gray-to-cyan-bluish-gray-gradient-background{background: var(--wp--preset--gradient--very-light-gray-to-cyan-bluish-gray) !important;}.has-cool-to-warm-spectrum-gradient-background{background: var(--wp--preset--gradient--cool-to-warm-spectrum) !important;}.has-blush-light-purple-gradient-background{background: var(--wp--preset--gradient--blush-light-purple) !important;}.has-blush-bordeaux-gradient-background{background: var(--wp--preset--gradient--blush-bordeaux) !important;}.has-luminous-dusk-gradient-background{background: var(--wp--preset--gradient--luminous-dusk) !important;}.has-pale-ocean-gradient-background{background: var(--wp--preset--gradient--pale-ocean) !important;}.has-electric-grass-gradient-background{background: var(--wp--preset--gradient--electric-grass) !important;}.has-midnight-gradient-background{background: var(--wp--preset--gradient--midnight) !important;}.has-juicy-peach-gradient-background{background: var(--wp--preset--gradient--juicy-peach) !important;}.has-young-passion-gradient-background{background: var(--wp--preset--gradient--young-passion) !important;}.has-true-sunset-gradient-background{background: var(--wp--preset--gradient--true-sunset) !important;}.has-morpheus-den-gradient-background{background: var(--wp--preset--gradient--morpheus-den) !important;}.has-plum-plate-gradient-background{background: var(--wp--preset--gradient--plum-plate) !important;}.has-aqua-splash-gradient-background{background: var(--wp--preset--gradient--aqua-splash) !important;}.has-love-kiss-gradient-background{background: var(--wp--preset--gradient--love-kiss) !important;}.has-new-retrowave-gradient-background{background: var(--wp--preset--gradient--new-retrowave) !important;}.has-plum-bath-gradient-background{background: var(--wp--preset--gradient--plum-bath) !important;}.has-high-flight-gradient-background{background: var(--wp--preset--gradient--high-flight) !important;}.has-teen-party-gradient-background{background: var(--wp--preset--gradient--teen-party) !important;}.has-fabled-sunset-gradient-background{background: var(--wp--preset--gradient--fabled-sunset) !important;}.has-arielle-smile-gradient-background{background: var(--wp--preset--gradient--arielle-smile) !important;}.has-itmeo-branding-gradient-background{background: var(--wp--preset--gradient--itmeo-branding) !important;}.has-deep-blue-gradient-background{background: var(--wp--preset--gradient--deep-blue) !important;}.has-strong-bliss-gradient-background{background: var(--wp--preset--gradient--strong-bliss) !important;}.has-sweet-period-gradient-background{background: var(--wp--preset--gradient--sweet-period) !important;}.has-purple-division-gradient-background{background: var(--wp--preset--gradient--purple-division) !important;}.has-cold-evening-gradient-background{background: var(--wp--preset--gradient--cold-evening) !important;}.has-mountain-rock-gradient-background{background: var(--wp--preset--gradient--mountain-rock) !important;}.has-desert-hump-gradient-background{background: var(--wp--preset--gradient--desert-hump) !important;}.has-ethernal-constance-gradient-background{background: var(--wp--preset--gradient--ethernal-constance) !important;}.has-happy-memories-gradient-background{background: var(--wp--preset--gradient--happy-memories) !important;}.has-grown-early-gradient-background{background: var(--wp--preset--gradient--grown-early) !important;}.has-morning-salad-gradient-background{background: var(--wp--preset--gradient--morning-salad) !important;}.has-night-call-gradient-background{background: var(--wp--preset--gradient--night-call) !important;}.has-mind-crawl-gradient-background{background: var(--wp--preset--gradient--mind-crawl) !important;}.has-angel-care-gradient-background{background: var(--wp--preset--gradient--angel-care) !important;}.has-juicy-cake-gradient-background{background: var(--wp--preset--gradient--juicy-cake) !important;}.has-rich-metal-gradient-background{background: var(--wp--preset--gradient--rich-metal) !important;}.has-mole-hall-gradient-background{background: var(--wp--preset--gradient--mole-hall) !important;}.has-cloudy-knoxville-gradient-background{background: var(--wp--preset--gradient--cloudy-knoxville) !important;}.has-soft-grass-gradient-background{background: var(--wp--preset--gradient--soft-grass) !important;}.has-saint-petersburg-gradient-background{background: var(--wp--preset--gradient--saint-petersburg) !important;}.has-everlasting-sky-gradient-background{background: var(--wp--preset--gradient--everlasting-sky) !important;}.has-kind-steel-gradient-background{background: var(--wp--preset--gradient--kind-steel) !important;}.has-over-sun-gradient-background{background: var(--wp--preset--gradient--over-sun) !important;}.has-premium-white-gradient-background{background: var(--wp--preset--gradient--premium-white) !important;}.has-clean-mirror-gradient-background{background: var(--wp--preset--gradient--clean-mirror) !important;}.has-wild-apple-gradient-background{background: var(--wp--preset--gradient--wild-apple) !important;}.has-snow-again-gradient-background{background: var(--wp--preset--gradient--snow-again) !important;}.has-confident-cloud-gradient-background{background: var(--wp--preset--gradient--confident-cloud) !important;}.has-glass-water-gradient-background{background: var(--wp--preset--gradient--glass-water) !important;}.has-perfect-white-gradient-background{background: var(--wp--preset--gradient--perfect-white) !important;}.has-small-font-size{font-size: var(--wp--preset--font-size--small) !important;}.has-medium-font-size{font-size: var(--wp--preset--font-size--medium) !important;}.has-large-font-size{font-size: var(--wp--preset--font-size--large) !important;}.has-x-large-font-size{font-size: var(--wp--preset--font-size--x-large) !important;}.has-larger-font-size{font-size: var(--wp--preset--font-size--larger) !important;}.has-xxlarge-font-size{font-size: var(--wp--preset--font-size--xxlarge) !important;}', + 'isGlobalStyles' => true + ], + [ + '__unstableType' => 'theme', + 'css' => ':root { --wp--style--global--content-size: var(--theme-block-max-width);--wp--style--global--wide-size: var(--theme-block-wide-max-width); }:where(body) { margin: 0; }.wp-site-blocks > .alignleft { float: left; margin-right: 2em; }.wp-site-blocks > .alignright { float: right; margin-left: 2em; }.wp-site-blocks > .aligncenter { justify-content: center; margin-left: auto; margin-right: auto; }:where(.wp-site-blocks) > * { margin-block-start: var(--theme-content-spacing); margin-block-end: 0; }:where(.wp-site-blocks) > :first-child { margin-block-start: 0; }:where(.wp-site-blocks) > :last-child { margin-block-end: 0; }:root { --wp--style--block-gap: var(--theme-content-spacing); }:root :where(.is-layout-flow) > :first-child{margin-block-start: 0;}:root :where(.is-layout-flow) > :last-child{margin-block-end: 0;}:root :where(.is-layout-flow) > *{margin-block-start: var(--theme-content-spacing);margin-block-end: 0;}:root :where(.is-layout-constrained) > :first-child{margin-block-start: 0;}:root :where(.is-layout-constrained) > :last-child{margin-block-end: 0;}:root :where(.is-layout-constrained) > *{margin-block-start: var(--theme-content-spacing);margin-block-end: 0;}:root :where(.is-layout-flex){gap: var(--theme-content-spacing);}:root :where(.is-layout-grid){gap: var(--theme-content-spacing);}.is-layout-flow > .alignleft{float: left;margin-inline-start: 0;margin-inline-end: 2em;}.is-layout-flow > .alignright{float: right;margin-inline-start: 2em;margin-inline-end: 0;}.is-layout-flow > .aligncenter{margin-left: auto !important;margin-right: auto !important;}.is-layout-constrained > .alignleft{float: left;margin-inline-start: 0;margin-inline-end: 2em;}.is-layout-constrained > .alignright{float: right;margin-inline-start: 2em;margin-inline-end: 0;}.is-layout-constrained > .aligncenter{margin-left: auto !important;margin-right: auto !important;}.is-layout-constrained > :where(:not(.alignleft):not(.alignright):not(.alignfull)){max-width: var(--wp--style--global--content-size);margin-left: auto !important;margin-right: auto !important;}.is-layout-constrained > .alignwide{max-width: var(--wp--style--global--wide-size);}body .is-layout-flex{display: flex;}.is-layout-flex{flex-wrap: wrap;align-items: center;}.is-layout-flex > :is(*, div){margin: 0;}body .is-layout-grid{display: grid;}.is-layout-grid > :is(*, div){margin: 0;}body{padding-top: 0px;padding-right: 0px;padding-bottom: 0px;padding-left: 0px;}', + 'isGlobalStyles' => true + ], + [ + '__unstableType' => 'user', + 'css' => " :root{--theme-block-max-width: 700px;--global-calc-content-width: 700px;--theme-block-wide-max-width: 820px;--theme-font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, Helvetica, Arial, sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\";--theme-font-weight: 400;--theme-text-transform: none;--theme-text-decoration: none;--theme-font-size: 16px;--theme-line-height: 1.60;--theme-letter-spacing: 0em;--theme-button-font-weight: 500;--theme-button-font-size: 16px;--theme-palette-color-1: #4F46E5;--theme-palette-color-2: #7C3AED;--theme-palette-color-3: #1F2937;--theme-palette-color-4: #374151;--theme-palette-color-5: #6B7280;--theme-palette-color-6: #9CA3AF;--theme-palette-color-7: #E5E7EB;--theme-palette-color-8: #ffffff;--theme-text-color: var(--fcom-primary-text, #19283a);--theme-link-initial-color: var(--theme-palette-color-1);--theme-link-hover-color: var(--theme-palette-color-2);--theme-selection-text-color: #ffffff;--theme-selection-background-color: var(--theme-palette-color-1);--theme-border-color: var(--theme-palette-color-5);--theme-headings-color: var(--theme-palette-color-4);--theme-content-spacing: 1.5em;--theme-button-min-height: 40px;--theme-button-shadow: none;--theme-button-transform: none;--theme-button-text-initial-color: #ffffff;--theme-button-text-hover-color: #ffffff;--theme-button-background-initial-color: var(--theme-palette-color-1);--theme-button-background-hover-color: var(--theme-palette-color-2);--theme-button-border: none;--theme-button-padding: 5px 20px;--theme-normal-container-max-width: 1290px;--theme-content-vertical-spacing: 60px;--theme-container-edge-spacing: 90vw;--theme-narrow-container-max-width: 750px;--theme-wide-offset: 130px;--fcom-font-size-small: 16px;--fcom-font-size-medium: 18px;--fcom-font-size-large: 22px;--fcom-font-size-larger: 26px;--fcom-font-size-xxlarge: 32px;--wp--preset--spacing--20: 0.44rem;--wp--preset--spacing--30: 0.67rem;--wp--preset--spacing--40: 1rem;--wp--preset--spacing--50: 1.5rem;--wp--preset--spacing--60: 2.25rem;--wp--preset--spacing--70: 3.38rem;--wp--preset--spacing--80: 5.06rem}body .has-theme-palette-color-1-color{color:var(--theme-palette-color-1)}body .has-theme-palette-color-2-color{color:var(--theme-palette-color-2)}body .has-theme-palette-color-3-color{color:var(--theme-palette-color-3)}body .has-theme-palette-color-4-color{color:var(--theme-palette-color-4)}body .has-theme-palette-color-5-color{color:var(--theme-palette-color-5)}body .has-theme-palette-color-6-color{color:var(--theme-palette-color-6)}body .has-theme-palette-color-7-color{color:var(--theme-palette-color-7)}body .has-theme-palette-color-8-color{color:var(--theme-palette-color-8)}body .has-theme-palette-color-1-background-color{background-color:var(--theme-palette-color-1)}body .has-theme-palette-color-2-background-color{background-color:var(--theme-palette-color-2)}body .has-theme-palette-color-3-background-color{background-color:var(--theme-palette-color-3)}body .has-theme-palette-color-4-background-color{background-color:var(--theme-palette-color-4)}body .has-theme-palette-color-5-background-color{background-color:var(--theme-palette-color-5)}body .has-theme-palette-color-6-background-color{background-color:var(--theme-palette-color-6)}body .has-theme-palette-color-7-background-color{background-color:var(--theme-palette-color-7)}body .has-theme-palette-color-8-background-color{background-color:var(--theme-palette-color-8)}body .has-small-font-size{font-size:var(--fcom-font-size-small)}body .has-medium-font-size{font-size:var(--fcom-font-size-medium)}body .has-large-font-size{font-size:var(--fcom-font-size-large)}body .has-larger-font-size{font-size:var(--fcom-font-size-larger)}body .has-xxlarge-font-size{font-size:var(--fcom-font-size-xxlarge)}body .is-root-container>.alignfull{margin-inline:var(--has-wide, -20px)}body .is-root-container>.wp-block.alignleft{margin-inline-start:calc((100% - min(var(--theme-block-max-width),100%))/2)}body .is-root-container>.wp-block.alignright{margin-inline-end:calc((100% - min(var(--theme-block-max-width),100%))/2)}body :root .wp-element-button{font-family:var(--theme-button-font-family, var(--theme-font-family));font-size:var(--theme-button-font-size);font-weight:var(--theme-button-font-weight);font-style:var(--theme-button-font-style);line-height:var(--theme-button-line-height);letter-spacing:var(--theme-button-letter-spacing);text-transform:var(--theme-button-text-transform);-webkit-text-decoration:var(--theme-button-text-decoration);text-decoration:var(--theme-button-text-decoration)}body :root .wp-block-button[style*=font-weight] .wp-element-button{font-weight:inherit}body .wp-block-columns:last-child{margin-bottom:0}body .has-drop-cap:not(:focus):first-letter{font-size:5.8em;font-weight:700;margin:.1em .12em .05em 0}body figcaption{text-align:center;margin-block:.5em 0}body .wp-block-code,body .wp-block-verse,body .wp-block-preformatted{box-sizing:border-box;tab-size:4;padding:15px 20px;border-radius:3px;background:var(--theme-palette-color-7)}body blockquote{margin-inline:0}body blockquote:where(:not(.is-style-plain)):where(:not(.has-text-align-center):not(.has-text-align-right)){border-inline-start:4px solid var(--theme-palette-color-1)}body blockquote:where(:not(.is-style-plain)).has-text-align-center{padding-block:1.5em;border-block:3px solid var(--theme-palette-color-1)}body blockquote:where(:not(.is-style-plain)).has-text-align-right{border-inline-end:4px solid var(--theme-palette-color-1)}body blockquote:where(:not(.is-style-plain):not(.has-text-align-center):not(.has-text-align-right)){padding-inline-start:1.5em}body blockquote.has-text-align-right{padding-inline-end:1.5em}body blockquote p:last-child{margin-bottom:0}body blockquote cite{font-size:14px}body .wp-block-list{padding-left:30px}body .wp-block-pullquote{position:relative;padding:70px;text-align:initial;border-width:10px;border-style:solid;border-color:var(--theme-palette-color-1)}body .wp-block-pullquote blockquote{border:0;padding:0;margin:0;position:relative;isolation:isolate}body .wp-block-pullquote blockquote p{margin-top:0;margin-bottom:1em}body .wp-block-pullquote blockquote p:last-child{margin-bottom:0}body .wp-block-pullquote blockquote cite{font-size:16px;font-weight:500}body [data-align=left] .wp-block-pullquote,body [data-align=right] .wp-block-pullquote{max-width:50%;margin-top:.3em;margin-bottom:.3em}body .wp-block-table table{border-width:1px}body .wp-block-table table:not(.has-border-color) thead,body .wp-block-table table:not(.has-border-color) tfoot,body .wp-block-table table:not(.has-border-color) td,body .wp-block-table table:not(.has-border-color) th{border-color:var(--theme-table-border-color, var(--theme-border-color))}body .wp-block-table th:not([class*=has-text-align]){text-align:inherit}body .wp-block-table.is-style-stripes{border:0}body .wp-block-button.is-style-outline .wp-element-button{padding:var(--theme-button-padding);border:2px solid;border-color:var(--theme-button-background-initial-color)}body .wp-block-button.is-style-outline .wp-element-button:not(.has-text-color){color:var(--theme-button-background-initial-color)}body .wp-block-button.is-style-outline .wp-element-button:hover{color:var(--theme-button-text-hover-color);border-color:var(--theme-button-background-hover-color);background-color:var(--theme-button-background-hover-color)}body .wp-block-separator{border:none;margin-inline:auto;color:var(--theme-form-field-border-initial-color)}body .wp-block-separator:not(:where(.is-style-wide,.is-style-dots,.alignfull,.alignwide)){max-width:100px !important}body .wp-block-separator:not(.is-style-dots){height:2px;background-color:currentColor}body :root :where(p.has-background,.wp-block-group.has-background){padding:30px;box-sizing:border-box}body h1.has-background,body h2.has-background,body h3.has-background,body h4.has-background,body h5.has-background,body h6.has-background{padding:1.25em 2.375em}body{background-color:var(--fcom-primary-bg, white);background-image:none;font-family:var(--theme-font-family);line-height:var(--theme-line-height)}body .is-root-container{font-size:var(--theme-font-size, 16px);font-family:var(--theme-font-family, -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, Helvetica, Arial, sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\")}.block-editor-iframe__html.is-zoomed-out .block-editor-iframe__body{padding:20px 30px}.editor-visual-editor__post-title-wrapper.edit-post-visual-editor__post-title-wrapper{margin-top:0px !important;padding-top:0;margin-bottom:30px;position:relative}.editor-visual-editor__post-title-wrapper.edit-post-visual-editor__post-title-wrapper h1{font-size:32px;font-weight:700}h1{--theme-font-weight: 700;--theme-font-size: 40px;--theme-line-height: 1.5}h2{--theme-font-weight: 700;--theme-font-size: 35px;--theme-line-height: 1.5}h3{--theme-font-weight: 700;--theme-font-size: 30px;--theme-line-height: 1.5}h4{--theme-font-weight: 700;--theme-font-size: 25px;--theme-line-height: 1.5}h5{--theme-font-weight: 700;--theme-font-size: 20px;--theme-line-height: 1.5}h6{--theme-font-weight: 700;--theme-font-size: 16px;--theme-line-height: 1.5}.wp-block-pullquote{--theme-font-family: Georgia;--theme-font-weight: 600;--theme-font-size: 25px}pre,code,samp,kbd{--theme-font-family: monospace;--theme-font-weight: 400;--theme-font-size: 16px}figcaption{--theme-font-size: 14px}li::marker{color:#959595}.editor-styles-wrapper{--true: initial;--false: ;--wp--style--global--content-size: var(--theme-block-max-width);--wp--style--global--wide-size: var(--theme-block-wide-max-width);box-sizing:border-box;border:var(--has-boxed, var(--theme-boxed-content-border));padding:var(--has-boxed, var(--theme-boxed-content-spacing));box-shadow:var(--has-boxed, var(--theme-boxed-content-box-shadow));border-radius:var(--has-boxed, var(--theme-boxed-content-border-radius));margin-inline:auto;margin-block:var(--has-boxed, 20px);width:calc(100% - 40px);max-width:100%}:is(.is-layout-flow,.is-layout-constrained)>*:where(:not(h1,h2,h3,h4,h5,h6)){margin-block-start:0;margin-block-end:var(--theme-content-spacing)}:is(.is-layout-flow,.is-layout-constrained) :where(h1,h2,h3,h4,h5,h6){margin-block-end:calc(var(--has-theme-content-spacing, 1)*(.3em + 10px))}:root{color:var(--theme-text-color)}a{color:var(--theme-link-initial-color)}.block-editor-block-list__layout.is-root-container>.alignwide{max-width:var(--theme-block-wide-max-width);box-sizing:border-box}.is-root-container{padding:0 20px}\n" + ], + [ + 'css' => file_exists(FLUENTCRM_PLUGIN_PATH . 'assets/guten-editor/index.css') + ? file_get_contents(FLUENTCRM_PLUGIN_PATH . 'assets/guten-editor/index.css') + : '', + '__unstableType' => 'user' + ], + [ + 'css' => $dynamicCss, + '__unstableType' => 'user' + ] + ]; + } + + /** + * Return the `__unstableResolvedAssets` for the editor settings. + * + * @return array + */ + private function getResolvedAssets() + { + $resolvedStyles = [ + 'wp-components-css' => includes_url('/css/dist/components/style.min.css'), + 'wp-preferences-css' => includes_url('/css/dist/preferences/style.min.css'), + 'wp-block-editor-css' => includes_url('/css/dist/block-editor/style.min.css'), + 'wp-reusable-blocks-css' => includes_url('/css/dist/reusable-blocks/style.min.css'), + 'wp-patterns-css' => includes_url('/css/dist/patterns/style.min.css'), + 'wp-editor-css' => includes_url('/css/dist/editor/style.min.css'), + 'wp-block-library-css' => includes_url('/css/dist/block-library/style.min.css'), + 'wp-block-editor-content-css' => includes_url('/css/dist/block-editor/content.min.css'), + 'wp-edit-blocks-css' => includes_url('/css/dist/block-library/editor.min.css'), + ]; + + global $wp_version; + $cssFiles = ''; + foreach ($resolvedStyles as $name => $file) { + $cssFiles .= "\n"; + } + + return [ + 'scripts' => '', + 'styles' => $cssFiles + ]; + } + + /** + * Return the `defaultEditorStyles` for the editor settings. + * + * @return array + */ + private function getDefaultEditorStyles() + { + return [ + [ + 'css' => ':root{--wp-admin-theme-color:#007cba;--wp-admin-theme-color--rgb:0, 124, 186;--wp-admin-theme-color-darker-10:#006ba1;--wp-admin-theme-color-darker-10--rgb:0, 107, 161;--wp-admin-theme-color-darker-20:#005a87;--wp-admin-theme-color-darker-20--rgb:0, 90, 135;--wp-admin-border-width-focus:2px;--wp-block-synced-color:#7a00df;--wp-block-synced-color--rgb:122, 0, 223;--wp-bound-block-color:var(--wp-block-synced-color);}@media (min-resolution:192dpi){:root{--wp-admin-border-width-focus:1.5px;}}body{font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen-Sans,Ubuntu,Cantarell,Helvetica Neue,sans-serif;font-size:18px;line-height:1.5;--wp--style--block-gap:2em;}p{line-height:1.8;}.editor-post-title__block{font-size:2.5em;font-weight:800;margin-bottom:1em;margin-top:2em;}' + ] + ]; + } + + /* + |-------------------------------------------------------------------------- + | Render: renderPage() + |-------------------------------------------------------------------------- + */ + + protected function renderPage() + { + add_action('fluent_crm/new_block_editor_footer', function () { + wp_underscore_playlist_templates(); + if (function_exists('wp_script_modules') && method_exists(wp_script_modules(), 'print_import_map')) { + wp_script_modules()->print_import_map(); + } + wp_print_footer_scripts(); + wp_print_media_templates(); + }); + + add_action('fluent_crm_block_editor/head', 'wp_enqueue_scripts', 1); + add_action('fluent_crm_block_editor/head', 'wp_resource_hints', 2); + add_action('fluent_crm_block_editor/head', 'wp_preload_resources', 1); + add_action('fluent_crm_block_editor/head', 'wp_print_styles', 8); + add_action('fluent_crm_block_editor/head', 'wp_print_head_scripts', 9); + + $this->unloadOtherScripts(); + ?> + + > + + FluentCRM Block Editor + + + + + + + + +
+
+
+ + + + buildApprovedSlugsPattern(); + + $pluginUrl = str_replace(['http:', 'https:'], '', plugins_url()); + $themesUrl = str_replace(['http:', 'https:'], '', get_theme_root_uri()); + + add_filter('script_loader_src', function ($src, $handle) use ($approvedSlugsPattern, $pluginUrl, $themesUrl) { + if (!$src) { + return $src; + } + + if ($this->isThirdPartyAsset($src, $approvedSlugsPattern, $pluginUrl, $themesUrl)) { + return false; + } + + return $src; + }, 1, 2); + + add_filter('style_loader_src', function ($src, $handle) use ($approvedSlugsPattern, $pluginUrl, $themesUrl) { + if (!$src) { + return $src; + } + + if ($this->isThirdPartyAsset($src, $approvedSlugsPattern, $pluginUrl, $themesUrl)) { + return false; + } + + return $src; + }, 1, 2); + + add_action('wp_print_scripts', function () use ($approvedSlugsPattern, $pluginUrl, $themesUrl) { + global $wp_scripts; + if (!$wp_scripts) { + return; + } + + foreach ($wp_scripts->queue as $script) { + if (empty($wp_scripts->registered[$script]) || empty($wp_scripts->registered[$script]->src)) { + continue; + } + + $src = $wp_scripts->registered[$script]->src; + + if (!$this->isThirdPartyAsset($src, $approvedSlugsPattern, $pluginUrl, $themesUrl)) { + continue; + } + + wp_dequeue_script($wp_scripts->registered[$script]->handle); + } + }, 1); + + add_action('wp_print_styles', function () { + $isSkip = apply_filters('fluent_crm_editor/skip_no_conflict', false, 'styles'); + + if ($isSkip) { + return; + } + + // Dequeue theme.json global styles that aren't caught by URL-based filtering + wp_dequeue_style('global-styles'); + wp_dequeue_style('global-styles-css-custom-properties'); + + global $wp_styles; + if (!$wp_styles) { + return; + } + + $approvedSlugs = apply_filters('fluent_crm_editor/asset_listed_slugs', [ + '\/gutenberg\/', + ]); + + $approvedSlugs[] = '\/fluent-crm\/'; + + $approvedSlugs = array_unique($approvedSlugs); + $approvedSlugs = implode('|', $approvedSlugs); + + $pluginUrl = plugins_url(); + $themeUrl = get_theme_root_uri(); + + $pluginUrl = str_replace(['http:', 'https:'], '', $pluginUrl); + $themeUrl = str_replace(['http:', 'https:'], '', $themeUrl); + + foreach ($wp_styles->queue as $script) { + + if (empty($wp_styles->registered[$script]) || empty($wp_styles->registered[$script]->src)) { + continue; + } + + $src = $wp_styles->registered[$script]->src; + $pluginMatched = (strpos($src, $pluginUrl) !== false) && !preg_match('/' . $approvedSlugs . '/', $src); + $themeMatched = (strpos($src, $themeUrl) !== false) && !preg_match('/' . $approvedSlugs . '/', $src); + + if (!$pluginMatched && !$themeMatched) { + continue; + } + + wp_dequeue_style($wp_styles->registered[$script]->handle); + } + }, 999999); + } + + /** + * Build the regex pattern string from approved asset slugs. + * + * @return string + */ + private function buildApprovedSlugsPattern() + { + /** + * Define the list of approved slugs for FluentCRM assets. + * + * This filter allows modification of the list of slugs that are approved for FluentCRM assets. + * + * @param array $approvedSlugs An array of approved slugs for FluentCRM assets. + */ + $approvedSlugs = apply_filters('fluent_crm_editor/asset_listed_slugs', [ + '\/gutenberg\/', + ]); + $approvedSlugs[] = 'fluent-crm'; + $approvedSlugs = array_unique($approvedSlugs); + + return implode('|', $approvedSlugs); + } + + /** + * Check if an asset src is a third-party plugin/theme asset that should be blocked. + * + * @param string $src + * @param string $pattern + * @param string $pluginUrl + * @param string $themesUrl + * @return bool + */ + private function isThirdPartyAsset($src, $pattern, $pluginUrl, $themesUrl) + { + $pluginMatched = (strpos($src, $pluginUrl) !== false) && !preg_match('/' . $pattern . '/', $src); + if ($pluginMatched) { + return true; + } + + $themeMatched = (strpos($src, $themesUrl) !== false) && !preg_match('/' . $pattern . '/', $src); + if ($themeMatched) { + return true; + } + + return false; + } + + protected function unregisterDefaultBlockPatterns($context = '', $data = []) + { + // Only affect FluentCRM iframe editor requests, never global wp-admin editors. + if (!isset($_REQUEST['fluent_crm_block_editor'])) { + return; + } + + $shouldUnregister = (bool)apply_filters('fluent_crm/block_editor_unregister_all_patterns', true, $context, $data); + if (!$shouldUnregister) { + return; + } + + // Prevent late core/theme pattern registration hooks from repopulating defaults. + foreach ([ + '_register_core_block_patterns_and_categories', + '_register_theme_block_patterns', + '_register_remote_theme_patterns' + ] as $callback) { + remove_action('init', $callback, 9); + remove_action('init', $callback, 10); + } + + add_filter('should_load_remote_block_patterns', '__return_false', 999); + remove_theme_support('core-block-patterns'); + + if (class_exists('\WP_Block_Patterns_Registry')) { + $registry = \WP_Block_Patterns_Registry::get_instance(); + $patterns = method_exists($registry, 'get_all_registered') ? $registry->get_all_registered() : []; + foreach (array_keys($patterns) as $patternName) { + if (method_exists($registry, 'unregister')) { + $registry->is_registered($patternName) && $registry->unregister($patternName); + } + } + } + + if (class_exists('\WP_Block_Pattern_Categories_Registry')) { + $categoryRegistry = \WP_Block_Pattern_Categories_Registry::get_instance(); + $categories = method_exists($categoryRegistry, 'get_all_registered') ? $categoryRegistry->get_all_registered() : []; + foreach (array_keys($categories) as $categoryName) { + if (method_exists($categoryRegistry, 'unregister')) { + $categoryRegistry->is_registered($categoryName) && $categoryRegistry->unregister($categoryName); + } + } + } + } + + /* + |-------------------------------------------------------------------------- + | Helpers: getRequiredCapability(), parseBoolParam(), getAvailableImageSizes() + |-------------------------------------------------------------------------- + */ + + /** + * Map a block type to the required FluentCRM capability. + * + * @param string $blockType + * @return string + */ + private static function getRequiredCapability($blockType) + { + if (in_array($blockType, ['campaign', 'email_body_in_funnel', 'recurring_campaign', 'recurring_mail', 'sequence_mail'], true)) { + return 'fcrm_manage_emails'; + } + if ($blockType === 'template') { + return 'fcrm_manage_email_templates'; + } + return 'fcrm_read_emails'; + } + + /** + * Parse a loosely-typed boolean parameter from a request value. + * + * @param mixed $value + * @return bool + */ + private static function parseBoolParam($value) + { + if (is_bool($value)) { + return $value; + } + if (is_numeric($value)) { + return ((int)$value) === 1; + } + $value = strtolower(trim((string)$value)); + if ($value === '1' || $value === 'true' || $value === 'yes' || $value === 'on') { + return true; + } + if ($value === '0' || $value === 'false' || $value === 'no' || $value === 'off') { + return false; + } + return false; + } + + /** + * Get available image sizes for the editor. + * + * @return array + */ + private function getAvailableImageSizes() + { + $size_names = apply_filters( + 'image_size_names_choose', + array( + 'thumbnail' => __('Thumbnail', 'fluent-crm'), + 'medium' => __('Medium', 'fluent-crm'), + 'large' => __('Large', 'fluent-crm'), + 'full' => __('Full Size', 'fluent-crm'), + ) + ); + $all_sizes = array(); + foreach ($size_names as $size_slug => $size_name) { + $all_sizes[] = array( + 'slug' => $size_slug, + 'name' => $size_name, + ); + } + return $all_sizes; + } +} diff --git a/wp-content/plugins/fluent-crm/app/Hooks/Handlers/FluentBlockPatternHandler.php b/wp-content/plugins/fluent-crm/app/Hooks/Handlers/FluentBlockPatternHandler.php new file mode 100644 index 0000000..6169c1d --- /dev/null +++ b/wp-content/plugins/fluent-crm/app/Hooks/Handlers/FluentBlockPatternHandler.php @@ -0,0 +1,68 @@ + 'fcrm-email', + 'label' => __('FluentCRM Email', 'fluent-crm'), + 'description' => __('Reusable email sections for FluentCRM editor.', 'fluent-crm') + ]; + + return $categories; + } + + public function addCustomPatterns($patterns) + { + $patterns[] = [ + 'name' => 'fcrm/intro-cta', + 'title' => __('Intro + CTA', 'fluent-crm'), + 'categories' => ['fcrm-email'], + 'keywords' => ['intro', 'cta'], + 'content' => '

' . esc_html__('Welcome to our newsletter', 'fluent-crm') . '

' . esc_html__('Share your main message here in one short paragraph.', 'fluent-crm') . '

' + ]; + + $patterns[] = [ + 'name' => 'fcrm/two-button-row', + 'title' => __('Two Button Row', 'fluent-crm'), + 'categories' => ['fcrm-email'], + 'keywords' => ['buttons', 'actions'], + 'content' => '

' . esc_html__('Choose an action:', 'fluent-crm') . '

' + ]; + + $patterns[] = [ + 'name' => 'fcrm/feature-list', + 'title' => __('Feature List', 'fluent-crm'), + 'categories' => ['fcrm-email'], + 'keywords' => ['list', 'features'], + 'content' => '

' . esc_html__('Why people choose us', 'fluent-crm') . '

  • ' . esc_html__('Fast setup', 'fluent-crm') . '
  • ' . esc_html__('Simple workflow', 'fluent-crm') . '
  • ' . esc_html__('Better conversion', 'fluent-crm') . '
' + ]; + + $patterns[] = [ + 'name' => 'fcrm/event-reminder', + 'title' => __('Event Reminder', 'fluent-crm'), + 'categories' => ['fcrm-email'], + 'keywords' => ['event', 'reminder'], + 'content' => '

' . esc_html__('Reminder: Upcoming Event', 'fluent-crm') . '

' . esc_html__('Date: Monday, 10:00 AM', 'fluent-crm') . '
' . esc_html__('Location: Online', 'fluent-crm') . '

' + ]; + + $patterns[] = [ + 'name' => 'fcrm/simple-footer-note', + 'title' => __('Simple Footer Note', 'fluent-crm'), + 'categories' => ['fcrm-email'], + 'keywords' => ['footer', 'note'], + 'content' => '

' . esc_html__('Need help? Reply to this email and our team will assist you.', 'fluent-crm') . '

' + ]; + + return $patterns; + } +} + diff --git a/wp-content/plugins/fluent-crm/app/Hooks/Handlers/FluentConditionalContentBlockHandler.php b/wp-content/plugins/fluent-crm/app/Hooks/Handlers/FluentConditionalContentBlockHandler.php new file mode 100644 index 0000000..5d2f0c0 --- /dev/null +++ b/wp-content/plugins/fluent-crm/app/Hooks/Handlers/FluentConditionalContentBlockHandler.php @@ -0,0 +1,223 @@ + 'show_if_user_logged_in', + 'show_if_public_users' => 'show_if_user_not_logged_in', + 'show_if_tag_exists' => 'show_if_tag_exist', + 'show_if_tag_not_exists' => 'show_if_tag_not_exist', + ]; + + public function register() + { + add_action('init', [$this, 'registerBlock']); + add_action('enqueue_block_editor_assets', [$this, 'enqueueEditorAssets']); + } + + /** + * Register the block with a render callback. + * The JS save() still writes HTML into post_content for storage, + * but the render_callback fully controls frontend output. + */ + public function registerBlock() + { + if (!function_exists('register_block_type')) { + return; + } + + register_block_type(self::BLOCK_NAME, [ + 'api_version' => 3, + 'editor_script' => 'fluent-crm-conditional-content-block', + 'attributes' => [ + 'condition_type' => [ + 'type' => 'string', + 'default' => self::DEFAULT_CONDITION, + ], + 'tag_ids' => [ + 'type' => 'array', + 'default' => [], + ], + ], + 'supports' => [ + 'align' => ['wide', 'full'], + 'anchor' => true, + 'html' => false, + ], + 'render_callback' => [$this, 'renderBlock'], + ]); + } + + + public function enqueueEditorAssets() + { + // The iframe editor already registers its own conditional block implementation. + if (isset($_REQUEST['fluent_crm_block_editor'])) { + return; + } + + $handle = 'fluent-crm-conditional-content-block'; + + wp_register_script( + $handle, + fluentCrmMix('public/conditional-content-block.js'), + ['wp-blocks', 'wp-block-editor', 'wp-components', 'wp-element', 'wp-i18n'], + FLUENTCRM_PLUGIN_VERSION, + true + ); + + $tags = Tag::select(['id', 'title'])->orderBy('title', 'ASC')->get(); + + wp_localize_script($handle, 'fcrmConditionalContentConfig', [ + 'hasPro' => defined('FLUENTCAMPAIGN'), + 'tags' => $tags + ]); + + wp_set_script_translations($handle, 'fluent-crm'); + } + + + /** + * Render callback. Receives block attributes, the rendered inner blocks + * as $content, and the WP_Block instance. + * + * @param array $attributes + * @param string $content Inner blocks already rendered. + * @param \WP_Block $block + * @return string + */ + public function renderBlock($attributes, $content, $block) + { + if (!$this->passesCondition($attributes)) { + return ''; + } + + // no inner blocks placed at all — nothing to render. + // Checking $block->inner_blocks (parsed block data) is the authoritative source of truth + // and avoids inspecting the rendered HTML string, which loses semantic information. + if (count($block->inner_blocks) === 0) { + return ''; + } + + // inner blocks exist but all rendered to nothing — for example a Query Loop + // with no results, a dynamic block gated by its own conditions, or a plugin-restricted + // block. Avoids outputting an empty wrapper div in those cases. + // trim() on the raw HTML string is intentional: any real element (iframe, video, image, + // paragraph, etc.) produces a non-empty string. wp_strip_all_tags() is deliberately + // avoided here because it removes HTML tags and would incorrectly treat media-only + // content (iframes, videos, images) as empty. + if (trim($content) === '') { + return ''; + } + + $wrapperAttributes = get_block_wrapper_attributes([ + 'class' => 'fc-cond-section', + ]); + + return sprintf( + '
%2$s
', + $wrapperAttributes, + $content + ); + } + + /** + * Decide whether the current visitor passes the condition. + */ + private function passesCondition($attrs) + { + $condition = $this->normalizeConditionType( + isset($attrs['condition_type']) ? $attrs['condition_type'] : self::DEFAULT_CONDITION + ); + + $tagIds = isset($attrs['tag_ids']) && is_array($attrs['tag_ids']) + ? array_values(array_filter(array_map('intval', $attrs['tag_ids']))) + : []; + + switch ($condition) { + case 'show_if_user_logged_in': + return is_user_logged_in(); + + case 'show_if_user_not_logged_in': + return !is_user_logged_in(); + + case 'show_if_tag_exist': + if (empty($tagIds)) { + return false; + } + return $this->contactHasAnyTag($tagIds); + + case 'show_if_tag_not_exist': + if (empty($tagIds)) { + return true; + } + return !$this->contactHasAnyTag($tagIds); + } + + return false; + } + + /** + * Check if the current contact has any of the given tag IDs. + */ + private function contactHasAnyTag(array $tagIds) + { + $contact = $this->getCurrentContact(); + + if (!$contact) { + return false; + } + + return $contact->hasAnyTagId($tagIds); + } + + /** + * Resolve the current contact once per request. + */ + private function getCurrentContact() + { + static $resolved = null; + static $cached = false; + + if ($cached) { + return $resolved; + } + + $cached = true; + + $contact = fluentcrm_get_current_contact(); + + if ($contact) { + $resolved = $contact->load('tags'); + } + + return $resolved; + } + + /** + * Map legacy keys to current condition keys, mirroring the JS side. + */ + private function normalizeConditionType($value) + { + $value = trim((string)$value); + + if (!$value) { + return self::DEFAULT_CONDITION; + } + + return isset($this->legacyMap[$value]) ? $this->legacyMap[$value] : $value; + } + +} diff --git a/wp-content/plugins/fluent-crm/app/Hooks/Handlers/FormSubmissions.php b/wp-content/plugins/fluent-crm/app/Hooks/Handlers/FormSubmissions.php new file mode 100644 index 0000000..df4e9c7 --- /dev/null +++ b/wp-content/plugins/fluent-crm/app/Hooks/Handlers/FormSubmissions.php @@ -0,0 +1,266 @@ + __('Form Submissions (Fluent Forms)', 'fluent-crm'), + 'name' => __('Fluent Forms', 'fluent-crm') + ]; + } + return $providers; + } + + public function getFluentFormSubmissions($data, $subscriber) + { + if (!defined('FLUENTFORM')) { + return $data; + } + + $app = fluentCrm(); + $page = intval($app->request->get('page', 1)); + $per_page = intval($app->request->get('per_page', 10)); + + $query = fluentCrmDb()->table('fluentform_submissions') + ->select([ + 'fluentform_submissions.id', + 'fluentform_submissions.form_id', + 'fluentform_forms.title', + 'fluentform_submissions.status', + 'fluentform_submissions.created_at' + ]) + ->join('fluentform_forms', 'fluentform_forms.id', '=', 'fluentform_submissions.form_id') + ->where(function ($query) use ($subscriber) { + $query->where('fluentform_submissions.response', 'LIKE', '%' . $subscriber->email . '%'); + if ($subscriber->user_id) { + $query->orWhere('fluentform_submissions.user_id', $subscriber->user_id); + } + }); + + $total = $query->count(); + + $submissions = $query + ->limit($per_page) + ->offset($per_page * ($page - 1)) + ->orderBy('fluentform_submissions.id', 'desc') + ->get(); + + $formattedSubmissions = []; + foreach ($submissions as $submission) { + $submissionUrl = admin_url('admin.php?page=fluent_forms&route=entries&form_id=' . $submission->form_id . '#/entries/' . $submission->id); + $actionUrl = '#' . $submission->id . ''; + + $badgeClass = 'fcrm_badge'; + + if ($submission->status === 'read') { + $badgeClass .= ' fcrm_badge_success'; + } else if ($submission->status === 'unread') { + $badgeClass .= ' fcrm_badge_warning'; + } + + $formattedSubmissions[] = [ + '__id' => $submission->id, + 'id' => $actionUrl, + 'title' => $submission->title, + 'Status' => '' . $submission->status . '', + 'Submitted At' => '' . $submission->created_at . '', + 'action' => 'view' + ]; + } + + return [ + 'total' => $total, + 'data' => $formattedSubmissions, + 'columns_config' => [ + 'id' => [ + 'label' => __('ID', 'fluent-crm'), + 'width' => '100px' + ], + 'title' => [ + 'label' => __('Form Title', 'fluent-crm') + ], + 'Status' => [ + 'label' => __('Status', 'fluent-crm'), + 'width' => '100px' + ], + 'Submitted At' => [ + 'label' => __('Submitted At', 'fluent-crm'), + 'width' => '180px' + ], + 'action' => [ + 'quick_action' => true, + 'label' => __('Action', 'fluent-crm'), + 'width' => '100px' + ] + ] + ]; + } + + public function getFluentFormSubmissionDetails($dataView, $params) + { + $submissionId = (int)Arr::get($params, '__id'); + + if (!$submissionId) { + $dataView['content_html'] = '

' . __('No submission found', 'fluent-crm') . '

'; + return $dataView; + } + + $submission = Submission::with(['form'])->find($submissionId); + if (!$submission || !$submission->form) { + $dataView['content_html'] = '

' . __('No submission found', 'fluent-crm') . '

'; + return $dataView; + } + + $form = $submission->form; + + if (!Acl::hasPermission('fluentform_entries_viewer', $form->id)) { + $dataView['title'] = __('Permission Denied', 'fluent-crm'); + $dataView['content_html'] = '

' . __('You do not have permission to view this submission.', 'fluent-crm') . '

'; + return $dataView; + } + + $submittedData = json_decode($submission->response, true); + $html = 'Submission Details

{all_data}
'; + if ($submission->payment_status) { + $html .= '

Payment Details

'; + $html .= '{payment.receipt}'; + } + + $html .= '

Additional Details:

'; + $html .= '
    '; + $html .= '
  • Source URL: {submission.source_url}
  • '; + $html .= '
  • Serial #: {submission.serial_number}
  • '; + $html .= '
  • Browser: {submission.browser} / {submission.device}
  • '; + $html .= '
  • Date: {submission.created_at}
  • '; + $html .= '
'; + + $body = ShortCodeParser::parse( + $html, + $submission->id, + $submittedData, + $form, + false, + true + ); + + $dataView['title'] = sprintf(__('Submission #%d - %s', 'fluent-crm'), $submission->id, $form->title); + $dataView['content_html'] = '
' . $body . '
'; + + $dataView['footer_content'] = 'View in FluentForms'; + + return $dataView; + + } + + public function parseEditorCodes($code, $form, $keys) + { + $contact = FluentCrmApi('contacts')->getCurrentContact(true, true); + + $providedKey = $keys[0]; + + // maybe has fallback value + $dynamicKey = explode('|', $providedKey); + $fallBack = ''; + if (count($dynamicKey) > 1) { + $fallBack = $dynamicKey[1]; + } + $ref = $dynamicKey[0]; + + if (!$contact) { + return $fallBack; + } + + $validMainProps = (new Subscriber)->getFillable(); + $validMainProps[] = 'id'; + + if (in_array($ref, $validMainProps)) { + if ($contact->{$ref}) { + return $contact->{$ref}; + } + + return $fallBack; + } + + // Maybe it's a custom field + $customData = $contact->custom_fields(); + + if ($customData && !empty($customData[$ref])) { + $value = $customData[$ref]; + if (is_array($value)) { + return implode(',', $value); + } + + return $customData[$ref]; + } + + $listMaps = [ + 'list_ids' => 'id', + 'list_titles' => 'title', + 'list_slugs' => 'slug' + ]; + + $tagMaps = [ + 'tag_ids' => 'id', + 'tag_titles' => 'title', + 'tag_slugs' => 'slug' + ]; + + + if (isset($listMaps[$ref])) { + $listProps = []; + foreach ($contact->lists as $list) { + $listProps[] = $list->{$listMaps[$ref]}; + } + if ($listProps) { + return trim(implode(', ', $listProps)); + } + } else if (isset($tagMaps[$ref])) { + $tagProps = []; + foreach ($contact->tags as $tag) { + $tagProps[] = $tag->{$tagMaps[$ref]}; + } + if ($tagProps) { + return trim(implode(', ', $tagProps)); + } + } + + return $fallBack; + } +} diff --git a/wp-content/plugins/fluent-crm/app/Hooks/Handlers/FunnelHandler.php b/wp-content/plugins/fluent-crm/app/Hooks/Handlers/FunnelHandler.php new file mode 100644 index 0000000..df71a17 --- /dev/null +++ b/wp-content/plugins/fluent-crm/app/Hooks/Handlers/FunnelHandler.php @@ -0,0 +1,530 @@ +funnelItemsRegistered) { + return; + } + + $this->funnelItemsRegistered = true; + + $this->initBlockActions(); + $this->initBenchMarkBlocks(); + $this->initTriggers(); + + if (!defined('FLUENTCAMPAIGN_DIR_FILE')) { + new \FluentCrm\App\Services\Funnel\ProFunnelItems(); + } + } + + public function registerEarlyActiveTriggers() + { + $this->registerActiveTriggers(true); + } + + public function registerActiveTriggers($onlyRegisteredArgFilters = false) + { + $triggers = get_option($this->settingsKey, []); + $triggers = array_unique($triggers); + + if (!$triggers) { + return; + } + + foreach ($triggers as $triggerName) { + if ($this->shouldSkipEddTriggerRegistration($triggerName)) { + continue; + } + + if (isset($this->registeredFunnelTriggers[$triggerName])) { + continue; + } + + /* + * Early registration is only safe when the trigger's arg-count filter is + * already registered. Otherwise the priority 20 pass will register it + * after handle() has initialized the core trigger filters. + */ + $argNumFilterName = 'fluentcrm_funnel_arg_num_' . $triggerName; + if ($onlyRegisteredArgFilters && !has_filter($argNumFilterName)) { + continue; + } + + $argNum = apply_filters($argNumFilterName, 1); + add_action($triggerName, function () use ($triggerName, $argNum) { + $this->mapTriggers($triggerName, func_get_args(), $argNum); + }, 10, $argNum); + + $this->registeredFunnelTriggers[$triggerName] = true; + } + + /* + * EDD also exposes edd_complete_purchase after a successful payment. + * Keep the existing fallback, but attach it only once and only after the + * main EDD payment-status trigger has been registered. + */ + if ( + isset($this->registeredFunnelTriggers['edd_update_payment_status']) && + empty($this->registeredTriggerFallbacks['edd_complete_purchase']) + ) { + add_action('edd_complete_purchase', function ($paymentId) { + $this->mapTriggers('edd_update_payment_status', [$paymentId, 'complete', 'pending'], 3); + }); + + $this->registeredTriggerFallbacks['edd_complete_purchase'] = true; + } + } + + /** + * Skip stored EDD automation hooks when the active EDD install is unsupported. + * + * Existing EDD funnel data should remain stored, but EDD runtime dispatch must + * not be registered unless the site is running EDD 3 or newer. + * + * @param string $triggerName + * @return bool + */ + private function shouldSkipEddTriggerRegistration($triggerName) + { + if (Helper::isEdd3()) { + return false; + } + + return in_array($triggerName, [ + 'edd_update_payment_status', + 'edd_sl_post_set_status', + 'edd_recurring_add_subscription_payment', + 'edd_subscription_status_change', + 'edd_fc_order_refunded_simulation' + ], true); + } + + public function handle() + { + $this->registerFunnelItems(); + + add_action('fluent_crm_process_automation', function () { + if ($this->funnelFired) { + return; + } + + $this->funnelFired = true; + + if (!$this->acquireFunnelProcessorLock()) { + return; + } + + try { + (new FunnelProcessor())->followUpSequenceActions(); + } finally { + $this->releaseFunnelProcessorLock(); + } + }); + } + + private function mapTriggers($triggerName, $originalArgs, $argNumber) + { + $triggerNameBase = $triggerName; + + $funnels = Funnel::where('status', 'published') + ->where('trigger_name', $triggerNameBase) + ->get(); + + foreach ($funnels as $funnel) { + ob_start(); + /** + * Automation Funnel Start Trigger from specific action + * @param Funnel $funnel + * @param array $originalArgs Original Arguments from the trigger + */ + do_action("fluentcrm_funnel_start_{$triggerName}", $funnel, $originalArgs); + $maybeErrors = ob_get_clean(); + } + + $benchMarks = FunnelSequence::where('type', 'benchmark') + ->where('action_name', $triggerNameBase) + ->whereHas('funnel', function ($q) { + return $q->where('status', 'published'); + }) + ->orderBy('id', 'ASC') + ->get(); + + foreach ($benchMarks as $benchMark) { + ob_start(); + /** + * Automation Funnel's Benchmark Start Trigger from specific action trigger + * @param Funnel $funnel + * @param array $originalArgs Original Arguments from the trigger + */ + do_action("fluentcrm_funnel_benchmark_start_{$triggerName}", $benchMark, $originalArgs); + $maybeErrors = ob_get_clean(); + } + } + + /** + * Claim the funnel-processor lock so two runners can't process the same + * queue concurrently. Backed by an atomic conditional UPDATE on wp_options + * (Helper::acquireDbLock) on every environment — not wp_cache_add(), which + * is not atomic under all object-cache drop-ins (e.g. LiteSpeed) and would + * let concurrent runners all acquire the lock. See Helper::acquireDbLock(). + */ + private function acquireFunnelProcessorLock() + { + return Helper::acquireDbLock($this->lockKey, $this->lockTimeout); + } + + private function releaseFunnelProcessorLock() + { + Helper::releaseDbLock($this->lockKey); + } + + public function resetFunnelIndexes() + { + $funnels = Funnel::select('trigger_name') + ->where('status', 'published') + ->groupBy('trigger_name') + ->get(); + + $funnelArrays = []; + foreach ($funnels as $funnel) { + $funnelArrays[] = $funnel->trigger_name; + } + + $sequenceMetrics = FunnelSequence::select('action_name') + ->where('status', 'published') + ->where('type', 'benchmark') + ->whereHas('funnel', function ($q) { + return $q->where('status', 'published'); + }) + ->groupBy('action_name') + ->get(); + + foreach ($sequenceMetrics as $sequenceMetric) { + $funnelArrays[] = $sequenceMetric->action_name; + } + + update_option($this->settingsKey, array_unique($funnelArrays), 'yes'); + } + + private function initTriggers() + { + new UserRegistrationTrigger(); + new FluentFormSubmissionTrigger(); + if (defined('FLUENTFORMPRO')) { + new FluentFormSubscriptionPaymentReceivedTrigger(); + new FluentFormSubscriptionCancelledTrigger(); + } + } + + private function initBlockActions() + { + if (Helper::isCompanyEnabled()) { + new ApplyCompanyAction(); + new DetachCompanyAction(); + } + new ApplyListAction(); + new ApplyTagAction(); + new DetachListAction(); + new DetachTagAction(); + new WaitTimeAction(); + new SendEmailAction(); + } + + private function initBenchMarkBlocks() + { + new ListAppliedBenchmark(); + new TagAppliedBenchmark(); + new RemoveFromListBenchmark(); + new RemoveFromTagBenchmark(); + } + + public function resumeSubscriberFunnels($subscriber, $oldStatus) + { + $funnelSubscribers = FunnelSubscriber::where('status', 'pending') + ->with(['funnel']) + ->where('subscriber_id', $subscriber->id) + ->whereHas('funnel', function ($query) { + return $query->where('status', 'published'); + }) + ->get(); + + $funnelProcessorClass = new FunnelProcessor(); + + foreach ($funnelSubscribers as $funnelSubscriber) { + $funnel = $funnelSubscriber->funnel; + + if (!$funnel || $funnel->status != 'published') { + continue; + } + + $funnelProcessorClass->resumeFunnelSubscriber($funnel, $subscriber, $funnelSubscriber); + } + } + + public function saveSequences() + { + check_ajax_referer('fluentcrm_ajax_nonce', '_nonce'); + + $hasPermission = PermissionManager::currentUserCan('fcrm_write_funnels'); + + if (!$hasPermission) { + wp_send_json([ + 'message' => __('Sorry, You do not have permission to do this action', 'fluent-crm') + ], 422); + } + + $request = FluentCrm('request'); + $data = $request->all(); + + $data['sequences'] = wp_unslash(Arr::get($data, 'sequences')); + + $funnel = FunnelHelper::saveFunnelSequence($data['funnel_id'], $data); + + wp_send_json([ + 'sequences' => FunnelHelper::getFunnelSequences($funnel, true), + 'message' => __('Sequence successfully updated', 'fluent-crm') + ]); + } + + public function exportFunnel() + { + check_ajax_referer('fluentcrm_ajax_nonce', '_nonce'); + + $permission = 'manage_options'; + if (!current_user_can($permission)) { + die('You do not have permission'); + } + + $funnelId = intval($_REQUEST['funnel_id']); + $funnel = Funnel::findOrFail($funnelId); + /** + * Determine the funnel editor details based on the funnel's trigger name. + * + * The dynamic portion of the hook name, `$funnel->trigger_name`, refers to the trigger name of the funnel. + * + * @param object $funnel The funnel object containing the editor details. + * @since 2.0.0 + * + */ + $funnel = apply_filters('fluentcrm_funnel_editor_details_' . $funnel->trigger_name, $funnel); + + $funnel->labels = $funnel->getFormattedLabels(); + + $funnel->sequences = FunnelHelper::getFunnelSequences($funnel, true); + + $funnel->site_hash = md5(site_url()); + $funnel->export_date = gmdate('Y-m-d H:i:s'); + + header('Content-disposition: attachment; filename=' . sanitize_title($funnel->title, 'funnel', 'display') . '-' . $funnelId . '.json'); + header('Content-type: application/json'); + echo json_encode($funnel); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped + exit(); + } + + public function saveEmailAction() + { + check_ajax_referer('fluentcrm_ajax_nonce', '_nonce'); + + $hasPermission = PermissionManager::currentUserCan('fcrm_write_funnels'); + + if (!$hasPermission) { + wp_send_json([ + 'message' => __('Sorry, You do not have permission to do this action', 'fluent-crm') + ], 422); + } + + $request = FluentCrm('request'); + $funnelId = $request->get('funnel_id'); + $funnel = Funnel::findOrFail($funnelId); + + $settings = Helper::parseArrayOrJson($request->get('action_data')); + + $settings['action_name'] = 'send_custom_email'; + + $funnelCampaign = Arr::get($settings, 'campaign', []); + + $funnelCampaignId = Arr::get($funnelCampaign, 'id'); + + $data = Arr::only($funnelCampaign, array_keys(FunnelCampaign::getMock())); + $data['settings']['mailer_settings'] = Arr::get($settings, 'mailer_settings', []); + + $type = 'created'; + + if ($funnelCampaignId && $funnel->id == Arr::get($data, 'parent_id')) { + // We have this campaign + $data['settings'] = \maybe_serialize($data['settings']); + $data['type'] = 'funnel_email_campaign'; + $data['title'] = $funnel->title . ' (' . $funnel->id . ')'; + FunnelCampaign::where('id', $funnelCampaignId)->update($data); + $type = 'updated'; + } else { + $data['parent_id'] = $funnel->id; + $data['type'] = 'funnel_email_campaign'; + $data['title'] = $funnel->title . ' (' . $funnel->id . ')'; + $campaign = FunnelCampaign::create($data); + $funnelCampaignId = $campaign->id; + } + + if (Arr::get($funnelCampaign, 'design_template') == 'visual_builder') { + $design = Arr::get($funnelCampaign, '_visual_builder_design', []); + fluentcrm_update_campaign_meta($funnelCampaignId, '_visual_builder_design', $design); + } else { + fluentcrm_delete_campaign_meta($funnelCampaignId, '_visual_builder_design'); + } + + $refCampaign = FunnelCampaign::find($funnelCampaignId); + + wp_send_json([ + 'type' => $type, + 'reference_campaign' => $funnelCampaignId, + 'campaign' => Arr::only($refCampaign->toArray(), array_keys(FunnelCampaign::getMock())) + ], 200); + } + + public function saveCampaignEmail() + { + check_ajax_referer('fluentcrm_ajax_nonce', '_nonce'); + + $hasPermission = PermissionManager::currentUserCan('fcrm_manage_emails'); + + if (!$hasPermission) { + wp_send_json([ + 'message' => __('Sorry, You do not have permission to do this action', 'fluent-crm') + ], 422); + } + + $request = FluentCrm('request'); + $id = $request->get('campaign_id'); + + $data = Helper::parseArrayOrJson($request->get('action_data')); + + if (empty($data)) { + wp_send_json([ + 'message' => __('Invalid Data', 'fluent-crm') + ], 422); + } + + $updateData = Arr::only($data, [ + 'title', + 'slug', + 'template_id', + 'email_subject', + 'email_pre_header', + 'email_body', + 'utm_status', + 'utm_source', + 'utm_medium', + 'utm_campaign', + 'utm_term', + 'utm_content', + 'scheduled_at', + 'design_template' + ]); + + if (!empty($data['settings'])) { + $updateData['settings'] = $data['settings']; + } + + $updateData = Sanitize::campaign($updateData); + + $campaign = Campaign::findOrFail($id); + + $campaign->fill($updateData)->save(); + + $nextStep = Arr::get($data, 'next_step'); + + if ($nextStep) { + do_action('fluent_crm/update_campaign_compose', $data, $campaign); + fluentcrm_update_campaign_meta($id, '_next_config_step', $nextStep); + } + + wp_send_json([ + 'campaign' => $campaign + ], 200); + } +} diff --git a/wp-content/plugins/fluent-crm/app/Hooks/Handlers/Integrations.php b/wp-content/plugins/fluent-crm/app/Hooks/Handlers/Integrations.php new file mode 100644 index 0000000..024e85a --- /dev/null +++ b/wp-content/plugins/fluent-crm/app/Hooks/Handlers/Integrations.php @@ -0,0 +1,40 @@ +init(); + } + + if(defined('FLUENTCART_VERSION')) { + (new \FluentCrm\App\Services\ExternalIntegrations\FluentCart\FluentCart())->init(); + } + + /* + * Oxygen Editor Integration + */ + if (defined('CT_VERSION')) { + require_once FLUENTCRM_PLUGIN_PATH . 'app/Services/ExternalIntegrations/Oxygen/oxy_init.php'; + } + + (new EventTrackingHandler())->register(); + + if(defined('BRICKS_VERSION')) { + (new BricksBuilderIntegration())->register(); + } + } +} diff --git a/wp-content/plugins/fluent-crm/app/Hooks/Handlers/PrefFormHandler.php b/wp-content/plugins/fluent-crm/app/Hooks/Handlers/PrefFormHandler.php new file mode 100644 index 0000000..500ae3d --- /dev/null +++ b/wp-content/plugins/fluent-crm/app/Hooks/Handlers/PrefFormHandler.php @@ -0,0 +1,821 @@ +getCurrentContact(true, true); + + if (!$subscriber) { + return $noContactContent; + } + + /** + * Determine the preference form labels in FluentCRM. + * + * This filter allows modification of the labels used in the preference form. + * + * @since 2.5.95 + * + * @param array { + * An associative array of labels. + * + * @type string $first_name Label for the first name field. + * @type string $last_name Label for the last name field. + * @type string $prefix Label for the title field. + * @type string $email Label for the email field. + * @type string $phone Label for the phone/mobile field. + * @type string $dob Label for the date of birth field. + * @type string $address_line_1 Label for the address line 1 field. + * @type string $address_line_2 Label for the address line 2 field. + * @type string $city Label for the city field. + * @type string $state Label for the state field. + * @type string $postal_code Label for the ZIP code field. + * @type string $country Label for the country field. + * @type string $update Label for the update info button. + * @type string $address_heading Label for the address information section. + * @type string $list_label Label for the mailing list groups section. + * } + */ + $labels = apply_filters('fluent_crm/pref_labels', [ + 'first_name' => __('First Name', 'fluent-crm'), + 'last_name' => __('Last Name', 'fluent-crm'), + 'prefix' => __('Title', 'fluent-crm'), + 'email' => __('Email', 'fluent-crm'), + 'phone' => __('Phone/Mobile', 'fluent-crm'), + 'dob' => __('Date of Birth', 'fluent-crm'), + 'address_line_1' => __('Address Line 1', 'fluent-crm'), + 'address_line_2' => __('Address Line 2', 'fluent-crm'), + 'city' => __('City', 'fluent-crm'), + 'state' => __('State', 'fluent-crm'), + 'postal_code' => __('ZIP Code', 'fluent-crm'), + 'country' => __('Country', 'fluent-crm'), + 'update' => __('Update info', 'fluent-crm'), + 'address_heading' => __('Address Information', 'fluent-crm'), + 'list_label' => __('Mailing List Groups', 'fluent-crm'), + 'custom_fields' => __('Custom Fields', 'fluent-crm') + ]); + + $formFields = $this->getFormFields($settings, $subscriber, $labels, false); + + $listOptions = []; + $lists = Helper::getPublicLists(); + if ($lists) { + foreach ($lists as $list) { + $listOptions[strval($list->id)] = $list->title; + } + + $formattedLists = []; + foreach ($subscriber->lists as $list) { + $formattedLists[] = $list->id; + } + + $formFields['lists'] = [ + 'type' => 'checkboxes', + 'name' => 'lists', + 'container_class' => 'fc_inline_checkboxes', + 'options' => $listOptions, + 'value' => $formattedLists, + 'id' => 'mailing_lists', + 'label' => Arr::get($labels, 'list_label', 'Mailing List Groups'), + ]; + } + + /** + * Determine the preference form fields against a subscriber or contact data in FluentCRM. + * + * This filter allows modification of the preference form fields before they are displayed. + * + * @since 2.5.95 + * + * @param array $formFields The current form fields. + * @param object $subscriber The subscriber object. + * @return array Modified form fields. + */ + $formFields = apply_filters('fluent_crm/pref_form_fields', $formFields, $subscriber); + + $formFields[] = [ + 'type' => 'hidden', + 'atts' => [ + 'name' => 'action', + 'value' => 'fluent_crm_account_form' + ] + ]; + + if (isset($_REQUEST['_fc_secure_hash'])) { + $hash = sanitize_text_field($_REQUEST['_fc_secure_hash']); + if($hash) { + $formFields[] = [ + 'type' => 'hidden', + 'atts' => [ + 'name' => '_fc_hash_secure', + 'value' => $hash + ] + ]; + } + } + + wp_enqueue_style( + 'fluentcrm_public_pref', + fluentCrmMix('public/public_pref.css'), + [], + FLUENTCRM_PLUGIN_VERSION + ); + + wp_enqueue_script('fluentcrm_public_pref', fluentCrmMix('public/public_pref.js'), ['jquery'], FLUENTCRM_PLUGIN_VERSION, true); + + wp_localize_script('fluentcrm_public_pref', 'fluentcrm_sub_pref', [ + 'ajaxurl' => admin_url('admin-ajax.php') + ]); + + return (string) fluentCrm('view')->make('external.pref_form', [ + 'fields' => $formFields, + 'submitBtn' => [ + 'container_class' => 'fc_pref_submit', + 'btn_text' => __('Update info', 'fluent-crm'), + 'atts' => [ + 'type' => 'submit', + 'id' => 'fluentcrm_preferences_submit', + 'class' => 'btn fc_pref_submit' + ] + ], + 'subscriber' => $subscriber + ]); + } + + public function handleDynamicContentShortCode($atts, $text = '') + { + if(!$text) { + return ''; + } + + $defaults = [ + 'hide_for_guest' => 'no' + ]; + + $atts = shortcode_atts($defaults, $atts, 'fluentcrm_content'); + + $subscriber = FluentCrmApi('contacts')->getCurrentContact(true, true); + + if(!$subscriber) { + if($atts['hide_for_guest'] == 'yes') { + return ''; + } + return preg_replace_callback('/({{|##)+(.*?)(}}|##)/', function ($matches) { + if(isset($matches[2])) { + $token = $matches[2]; + $tokens = explode('|', $token); + if(isset($tokens[1])) { + return $tokens[1]; + } + } + return ''; + }, $text); + } + + return \FluentCrm\App\Services\Libs\Parser\Parser::parse($text, $subscriber); + } + + public function handleAjax() + { + if (!isset($_POST['_fc_nonce']) || !wp_verify_nonce($_POST['_fc_nonce'], 'fluent_crm_account_form_fields')) { + wp_send_json_error([ + 'message' => __('Sorry, your nonce did not verify.', 'fluent-crm') + ], 422); + } + + $settings = Helper::getGlobalEmailSettings(); + + if (Arr::get($settings, 'pref_form') != 'yes' || empty(Arr::get($settings, 'pref_general'))) { + wp_send_json_error([ + 'message' => __('Sorry! You cannot update your profile.', 'fluent-crm') + ], 422); + } + + if (isset($_REQUEST['_fc_hash_secure']) && !is_user_logged_in()) { + $hash = sanitize_text_field($_REQUEST['_fc_hash_secure']); + if ($hash) { + $_COOKIE['fc_hash_secure'] = $hash; + } + } + + $subscriber = FluentCrmApi('contacts')->getCurrentContact(false, true); + + if (!$subscriber) { + wp_send_json_error([ + 'message' => __('Sorry! You cannot update your profile.', 'fluent-crm') + ], 422); + } + + $validInputs = $this->getFormFields($settings, $subscriber, [], true); + + $validKeys = array_keys($validInputs); + + $validData = Arr::only($_REQUEST, $validKeys); + if (empty($validData['email'])) { + $validData['email'] = $subscriber->email; + } + + $errors = []; + foreach ($validInputs as $key => $input) { + if (Arr::get($input, 'required') && empty($validData[$key])) { + $errors[] = $key . ' is required'; + } + + // Handle array values for multi-select and checkboxes + if (isset($validData[$key]) && is_array($validData[$key])) { + $validData[$key] = array_map('sanitize_text_field', $validData[$key]); + } else { + $validData[$key] = sanitize_text_field(Arr::get($validData, $key, '')); + } + } + + if (!empty($validData['date_of_birth']) && !$this->isValidDate($validData['date_of_birth'])) { + $errors[] = 'date_of_birth'; + } + + if ($errors) { + wp_send_json_error([ + 'message' => __('Please fill up all required fields', 'fluent-crm'), + 'errors' => $errors, + 'inputs' => $validData + ], 422); + } + + // Handle custom fields + $enabledCustomFieldSlugs = Arr::get($settings, 'pref_custom', []); + $allCustomFields = (new CustomFields)->getGlobalFields()['fields']; + + if (!empty($allCustomFields) && !empty($enabledCustomFieldSlugs)) { + foreach ($allCustomFields as $field) { + $fieldKey = $field['slug']; + + // Only process fields that are enabled in pref_custom + if (!in_array($fieldKey, $enabledCustomFieldSlugs)) { + continue; + } + + if (isset($validData[$fieldKey])) { + $value = $validData[$fieldKey]; + + // Handle different field types + switch ($field['type']) { + case 'checkbox': + if (is_array($value)) { + $value = array_map('sanitize_text_field', $value); + } + break; + + + case 'select-multi': + + if (is_array($value)) { + $value = array_map('sanitize_text_field', $value); + } else { + $value = []; + } + break; + + case 'number': + $value = floatval($value); + break; + + case 'textarea': + $value = isset($_POST[$fieldKey]) ? sanitize_textarea_field($_POST[$fieldKey]) : ''; + break; + + case 'date': + $value = sanitize_text_field($value); + break; + + default: + $value = sanitize_text_field($value); + } + + // Update the meta with proper type + $subscriber->updateMeta($field['slug'], $value, 'custom_field'); + unset($validData[$fieldKey]); // Remove from main data + } + } + } + + $subscriber->fill($validData); + + $updateData = $subscriber->getDirty(); + + if($updateData) { + $subscriber->save(); + } + + if (isset($_REQUEST['lists'])) { + $publicLists = Helper::getPublicLists(); + $publicListIds = []; + foreach ($publicLists as $publicList) { + $publicListIds[] = $publicList->id; + } + + $selectedListIds = map_deep($_REQUEST['lists'], 'intval'); + $attachLists = []; + $detachLists = []; + + foreach ($subscriber->lists as $list) { + if (!in_array($list->id, $publicListIds)) { + continue; + } + + if (!in_array($list->id, $selectedListIds)) { + $detachLists[] = $list->id; + } + } + + foreach ($selectedListIds as $selectedListId) { + if (in_array($selectedListId, $publicListIds)) { + $attachLists[] = $selectedListId; + } + } + + if ($attachLists) { + $subscriber->attachLists($attachLists); + } + + if ($detachLists) { + $subscriber->detachLists($detachLists); + } + + } else { + $listIds = $subscriber->lists()->get()->pluck('id')->toArray(); + $subscriber->detachLists($listIds); + } + + do_action('fluent_crm/pref_form_self_contact_updated', $subscriber, $_REQUEST); + + if ($updateData) { + do_action('fluentcrm_contact_updated', $subscriber, $updateData); + do_action('fluent_crm/contact_updated', $subscriber, $updateData); + } + + wp_send_json_success([ + 'message' => __('Your information has been updated', 'fluent-crm'), + 'data' => $validData + ], 200); + } + + private function getFormFields($settings, $subscriber, $labels, $inputOnly = false) + { + $generalFields = Arr::get($settings, 'pref_general'); + $customFields = Arr::get($settings, 'pref_custom'); + + $formFields = []; + + if (array_intersect($generalFields, ['first_name', 'last_name'])) { + + $nameFields = []; + + if (in_array('prefix', $generalFields)) { + $nameFields['prefix'] = [ + 'type' => 'select', + 'name' => 'prefix', + 'container_class' => 'fc_name_prefix', + 'id' => 'fc_name_prefix', + 'label' => Arr::get($labels, 'prefix', 'Prefix'), + 'placeholder' => '--', + 'options' => Helper::getContactPrefixes(true), + 'value' => $subscriber->prefix + ]; + } + + if (in_array('first_name', $generalFields)) { + $nameFields['first_name'] = [ + 'type' => 'input', + 'name' => 'first_name', + 'id' => 'fc_first_name', + 'atts' => [ + 'type' => 'text', + 'placeholder' => __('First Name', 'fluent-crm') + ], + 'required' => true, + 'label' => Arr::get($labels, 'first_name', 'First Name'), + 'value' => $subscriber->first_name + ]; + } + + if (in_array('last_name', $generalFields)) { + $nameFields['last_name'] = [ + 'type' => 'input', + 'name' => 'last_name', + 'id' => 'fc_last_name', + 'atts' => [ + 'type' => 'text', + 'placeholder' => __('Last Name', 'fluent-crm') + ], + 'required' => true, + 'label' => Arr::get($labels, 'last_name', 'Last Name'), + 'value' => $subscriber->last_name + ]; + } + + $formFields['name'] = [ + 'type' => 'container', + 'container_class' => 'fc_names fc_' . count($nameFields) . '_col', + 'fields' => $nameFields + ]; + } + + $formFields[] = [ + 'type' => 'raw_html', + 'html' => '
' + ]; + + $formFields['email'] = [ + 'type' => 'input', + 'name' => 'email', + 'id' => 'fc_email', + 'atts' => [ + 'type' => 'email', + 'placeholder' => __('Email', 'fluent-crm'), + 'disabled' => true + ], + 'required' => true, + 'label' => Arr::get($labels, 'email', 'Email'), + 'value' => $subscriber->email + ]; + + if (in_array('phone', $generalFields)) { + $formFields['phone'] = [ + 'type' => 'input', + 'name' => 'phone', + 'id' => 'fc_phone', + 'atts' => [ + 'type' => 'tel', + 'placeholder' => __('Phone', 'fluent-crm') + ], + 'required' => false, + 'label' => Arr::get($labels, 'phone', 'Phone/Mobile'), + 'value' => $subscriber->phone + ]; + } + + $formFields[] = [ + 'type' => 'raw_html', + 'html' => '
' + ]; + + if (in_array('date_of_birth', $generalFields)) { + $formFields['date_of_birth'] = [ + 'type' => 'date_dropdowns', + 'name' => 'date_of_birth', + 'id' => 'fc_date_of_birth', + 'required' => false, + 'label' => Arr::get($labels, 'dob', 'Date of Birth'), + 'value' => $subscriber->date_of_birth + ]; + } + + if (in_array('address_fields', $generalFields)) { + $formFields[] = [ + 'type' => 'raw_html', + 'html' => '

' . Arr::get($labels, 'address_heading', 'Address Information') . '

' + ]; + + /** + * Filter to modify the list of country names for the Preference Form Field in FluentCRM. + * + * This filter allows you to modify the list of country names used in FluentCRM. + * + * @since 2.7.0 + * + * @param array An array of country names. + */ + $countryNames = apply_filters('fluent_crm/countries', []); + + $formattedCountries = []; + foreach ($countryNames as $country) { + $formattedCountries[$country['code']] = $country['title']; + } + + $formFields['address'] = [ + 'type' => 'container', + 'container_class' => 'fc_addresses fc_2_col', + 'fields' => [ + 'address_line_1' => [ + 'type' => 'input', + 'name' => 'address_line_1', + 'id' => 'fc_address_line_1', + 'atts' => [ + 'type' => 'text', + 'placeholder' => __('Address Line 1', 'fluent-crm') + ], + 'label' => Arr::get($labels, 'address_line_1', 'Address Line 1'), + 'value' => $subscriber->address_line_1 + ], + 'address_line_2' => [ + 'type' => 'input', + 'name' => 'address_line_2', + 'id' => 'fc_address_line_2', + 'atts' => [ + 'type' => 'text', + 'placeholder' => __('Address Line 2', 'fluent-crm') + ], + 'label' => Arr::get($labels, 'address_line_2', 'Address Line 2'), + 'value' => $subscriber->address_line_2 + ], + 'city' => [ + 'type' => 'input', + 'name' => 'city', + 'id' => 'fc_address_city', + 'atts' => [ + 'type' => 'text', + 'placeholder' => __('City', 'fluent-crm') + ], + 'label' => Arr::get($labels, 'city', 'City'), + 'value' => $subscriber->city + ], + 'state' => [ + 'type' => 'input', + 'name' => 'state', + 'id' => 'fc_address_state', + 'atts' => [ + 'type' => 'text', + 'placeholder' => __('State', 'fluent-crm') + ], + 'label' => Arr::get($labels, 'state', 'State'), + 'value' => $subscriber->state + ], + 'postal_code' => [ + 'type' => 'input', + 'name' => 'postal_code', + 'id' => 'fc_address_postal_code', + 'atts' => [ + 'type' => 'text', + 'placeholder' => __('Zip Code', 'fluent-crm') + ], + 'label' => Arr::get($labels, 'postal_code', 'Zip Code'), + 'value' => $subscriber->postal_code + ], + 'country' => [ + 'type' => 'select', + 'name' => 'country', + 'id' => 'fc_address_country', + 'placeholder' => __('Select Country', 'fluent-crm'), + 'label' => Arr::get($labels, 'country', 'Country'), + 'value' => $subscriber->country, + 'options' => $formattedCountries + ], + ] + ]; + } + + // Add custom fields section + if (!empty($customFields)) { + $allCustomFields = (new CustomFields)->getGlobalFields()['fields']; + $enabledCustomFields = []; + + // Filter custom fields based on pref_custom settings + foreach ($allCustomFields as $field) { + if (in_array($field['slug'], $customFields)) { + $enabledCustomFields[] = $field; + } + } + + if (!empty($enabledCustomFields)) { + $formFields[] = [ + 'type' => 'raw_html', + 'html' => '

' + ]; + + // Group fields by their group attribute + $groupedFields = []; + $ungroupedFields = []; + + foreach ($enabledCustomFields as $field) { + if (!empty($field['group'])) { + $group = $field['group']; + if (!isset($groupedFields[$group])) { + $groupedFields[$group] = []; + } + $groupedFields[$group][] = $field; + } else { + $ungroupedFields[] = $field; + } + } + + // Add ungrouped fields first + if (!empty($ungroupedFields)) { + $ungroupedContainer = [ + 'type' => 'container', + 'container_class' => 'fc_custom_fields fc_2_col', + 'fields' => [] + ]; + + foreach ($ungroupedFields as $field) { + $fieldType = $field['type']; + $fieldKey = $field['slug']; + $fieldConfig = $this->getCustomFieldConfig($field, $subscriber); + $ungroupedContainer['fields'][$fieldKey] = $fieldConfig; + } + + $formFields['custom_fields_ungrouped'] = $ungroupedContainer; + } + + // Create containers for each group + foreach ($groupedFields as $groupName => $fields) { + $customFieldsContainer = [ + 'type' => 'container', + 'container_class' => 'fc_custom_fields fc_2_col fc_custom_field_group_box', + 'fields' => [] + ]; + + $customFieldsContainer['fields']['group_heading'] = [ + 'type' => 'raw_html', + 'html' => '
' . esc_html($groupName) . '
' + ]; + + foreach ($fields as $field) { + $fieldType = $field['type']; + $fieldKey = $field['slug']; + $fieldConfig = $this->getCustomFieldConfig($field, $subscriber); + $customFieldsContainer['fields'][$fieldKey] = $fieldConfig; + } + + $formFields['custom_fields_' . sanitize_title($groupName)] = $customFieldsContainer; + } + } + } + + + if (!$inputOnly) { + return $formFields; + } + + return $this->parseInputs($formFields); + } + + private function parseInputs($fields) + { + $inputFields = []; + + $inputTypes = ['hidden', 'input', 'checkboxes', 'select', 'radio', 'date', 'date_dropdowns', 'textarea', 'select-multi', 'custom_date', 'custom_date_time']; + + foreach ($fields as $inputKey => $field) { + $type = Arr::get($field, 'type'); + if ($type == 'container') { + $inputFields = array_merge($this->parseInputs($field['fields']), $inputFields); + } else if (in_array($type, $inputTypes)) { + $inputFields[$inputKey] = $field; + } + } + + return $inputFields; + } + + private function getCustomFieldConfig($field, $subscriber) + { + $fieldType = $field['type']; + $fieldKey = $field['slug']; + + $fieldConfig = [ + 'type' => 'input', + 'name' => $fieldKey, + 'id' => 'fc_' . $fieldKey, + 'label' => $field['label'], + 'required' => !empty($field['required']), + 'value' => $subscriber->getMeta($field['slug'], 'custom_field') + ]; + + // Add field-specific configurations + switch ($fieldType) { + case 'text': + $fieldConfig['type'] = 'input'; + $fieldConfig['atts'] = [ + 'type' => 'text', + 'placeholder' => $field['label'], + 'class' => 'fc_input_control' + ]; + break; + + case 'textarea': + $fieldConfig['type'] = 'textarea'; + $fieldConfig['atts'] = [ + 'placeholder' => $field['label'], + 'class' => 'fc_input_control', + 'name' => $fieldKey + ]; + break; + + case 'number': + $fieldConfig['type'] = 'input'; + $fieldConfig['atts'] = [ + 'type' => 'number', + 'placeholder' => $field['label'], + 'class' => 'fc_input_control' + ]; + break; + + case 'select-one': + $fieldConfig['type'] = 'select'; + $fieldConfig['options'] = array_combine($field['options'], $field['options']); + $fieldConfig['placeholder'] = $field['label']; + $fieldConfig['atts'] = [ + 'class' => 'fc_input_control select-one' + ]; + break; + + case 'select-multi': + $fieldConfig['type'] = 'select-multi'; + $fieldConfig['options'] = array_combine($field['options'], $field['options']); + $fieldConfig['value'] = is_array($fieldConfig['value']) ? $fieldConfig['value'] : []; + $fieldConfig['name'] = $fieldKey . '[]'; + $fieldConfig['atts'] = [ + 'class' => 'fc_input_control select-multi', + 'multiple' => 'multiple' + ]; + break; + + case 'radio': + $fieldConfig['type'] = 'radio'; + $fieldConfig['options'] = array_combine($field['options'], $field['options']); + $fieldConfig['atts'] = [ + 'class' => 'fc_input_control' + ]; + break; + + case 'checkbox': + $fieldConfig['type'] = 'checkboxes'; + $fieldConfig['options'] = is_array($field['options']) ? $field['options'] : []; + $fieldConfig['value'] = is_array($fieldConfig['value']) ? $fieldConfig['value'] : []; + $fieldConfig['atts'] = [ + 'class' => 'fc_input_control' + ]; + break; + + case 'date': + $fieldConfig['type'] = 'custom_date'; + $fieldConfig['atts'] = [ + 'type' => 'date', + 'class' => 'fc_date_item fc_input_control', + 'data-format' => 'YYYY-MM-DD', + 'placeholder' => $field['label'], + 'data-template' => 'DD - MM - YYYY' + ]; + break; + + case 'date_time': + $fieldConfig['type'] = 'custom_date_time'; + $fieldConfig['atts'] = [ + 'type' => 'text', + 'class' => 'fc_date_item fc_input_control', + 'data-format' => 'YYYY-MM-DD HH:mm:ss', + 'placeholder' => $field['label'], + 'data-template' => 'DD - MM - YYYY HH:mm' + ]; + break; + } + + return $fieldConfig; + } + + /** + * Check if a string is a valid calendar date in Y-m-d format. + * + * @param string $ymd Date string (e.g. 2024-02-31). + * @return bool + */ + private function isValidDate($ymd) + { + if (!is_string($ymd) || !preg_match('/^(\d{4})-(\d{2})-(\d{2})$/', $ymd, $parts)) { + return false; + } + return checkdate((int) $parts[2], (int) $parts[3], (int) $parts[1]); + } + +} diff --git a/wp-content/plugins/fluent-crm/app/Hooks/Handlers/PurchaseHistory.php b/wp-content/plugins/fluent-crm/app/Hooks/Handlers/PurchaseHistory.php new file mode 100644 index 0000000..0a07115 --- /dev/null +++ b/wp-content/plugins/fluent-crm/app/Hooks/Handlers/PurchaseHistory.php @@ -0,0 +1,795 @@ +id The ID of the subscriber. + */ + $stats = apply_filters('fluent_crm/contact_purchase_stat_' . $commerceProvider, [], $subscriber->id); + if (!$stats) { + return false; + } + + $html = '
    '; + foreach ($stats as $stat) { + $html .= '
  • ' . $stat['title'] . ' ' . $stat['value'] . '
  • '; + } + $html .= '
'; + + return [ + 'title' => __('Customer Summary', 'fluent-crm'), + 'content' => $html + ]; + } + + if (defined('WC_PLUGIN_FILE')) { + $summary = $this->getWooCustomerSummary($subscriber); + if ($summary) { + return [ + 'title' => __('Customer Summary', 'fluent-crm'), + 'content' => $summary + ]; + } + return false; + } + + if (Helper::isEdd3()) { + + $customer = fluentCrmDb()->table('edd_customers') + ->where('email', $subscriber->email); + if ($subscriber->user_id) { + $customer = $customer->orWhere('user_id', $subscriber->user_id); + } + $customer = $customer->first(); + if (!$customer) { + return false; + } + + $summaryData = [ + 'order_count' => $customer->purchase_count, + 'lifetime_value' => number_format($customer->purchase_value, 2), + 'avg_value' => ($customer->purchase_count) ? round($customer->purchase_value / $customer->purchase_count, 2) : 'n/a', + 'stat_avg_count' => 0, + 'stat_avg_spend' => 0, + 'stat_avg_value' => 0, + 'currency_sign' => edd_currency_symbol(), + 'first_order_date' => $customer->date_created + ]; + + $html = $this->formatSummaryData($summaryData, true); + + return [ + 'title' => __('Customer Summary', 'fluent-crm'), + 'content' => $html + ]; + } + + return false; + } + + public function wooOrders($data, $subscriber) + { + if (!defined('WC_PLUGIN_FILE')) { + return $data; + } + + $hasRecount = defined('FLUENTCAMPAIGN') && \FluentCampaign\App\Services\Commerce\Commerce::isEnabled('woo'); + + $app = fluentCrm(); + + if ($hasRecount && $app->request->get('will_recount') == 'yes') { + (new \FluentCampaign\App\Services\Integrations\WooCommerce\DeepIntegration)->syncCustomerBySubscriber($subscriber); + } + + $page = (int)$app->request->get('page', 1); + $per_page = (int)$app->request->get('per_page', 10); + + $sort_by = sanitize_sql_orderby($app->request->get('sort_by', 'id')); + $sort_type = sanitize_sql_orderby($app->request->get('sort_type', 'DESC')); + + $valid_columns = ['id', 'date_created', 'total_amount']; + $valid_directions = ['ASC', 'DESC']; + + if (!in_array($sort_by, $valid_columns)) { + $sort_by = 'id'; + } + if (!in_array(strtoupper($sort_type), $valid_directions)) { + $sort_type = 'DESC'; + } + + $orders = $this->getWooOrders($subscriber, $sort_by, $sort_type); + $totalOrders = count($orders); + $orders = array_slice($orders, ($page - 1) * $per_page, $per_page); + + $formattedOrders = []; + + foreach ($orders as $order) { + $item_count = $order->get_item_count() - $order->get_item_count_refunded(); + $actionsHtml = ' + + + + '; + $date = ''.'#' . $order->get_order_number().''.wc_format_datetime($order->get_date_created()).''; + + $status = ''. Helper::getStatusText($order->get_status()) .''; + + $formattedOrders[] = [ + 'date' => wp_kses_post($date), + 'status' => wp_kses_post($status), + /* translators: 1: formatted order total (with currency), 2: number of items */ + 'total' => wp_kses_post(sprintf(_n('%1$s for %2$s item', '%1$s for %2$s items', $item_count, 'fluent-crm'), $order->get_formatted_order_total(), $item_count)), + 'action' => $actionsHtml, + ]; + } + /** + * Determine the WooCommerce purchase history sidebar HTML in FluentCRM. + * + * This filter allows customization of the HTML content displayed in the WooCommerce purchase sidebar. + * + * @since 2.7.0 + * + * @param string The current HTML content of the sidebar. + * @param object $subscriber The subscriber object containing subscriber data. + * @param int $page The current page identifier as an integer. + */ + $sidebarHtml = apply_filters('fluent_crm/woo_purchase_sidebar_html', '', $subscriber, $page); + + return [ + 'data' => $formattedOrders, + 'sidebar_html' => $sidebarHtml, + 'total' => $totalOrders, + 'has_recount' => $hasRecount, + 'columns_config' => [ + 'date' => [ + 'label' => __('Date', 'fluent-crm'), + 'sortable' => true, + 'key' => 'date_created_gmt' + ], + 'status' => [ + 'label' => __('Status', 'fluent-crm'), + ], + 'total' => [ + 'label' => __('Total', 'fluent-crm'), + 'width' => '160px', + 'sortable' => true, + 'key' => 'total_amount' + ], + 'actions' => [ + 'label' => __('', 'fluent-crm'), + 'width' => '50px' + ] + ] + ]; + } + + public function getWooCustomerSummary($subscriber) + { + $customerQuery = fluentCrmDb()->table('wc_customer_lookup') + ->where('email', $subscriber->email); + + if ($subscriber->user_id) { + $customerQuery = $customerQuery->orWhere('user_id', $subscriber->user_id); + } + + $customer = $customerQuery->first(); + + if ($customer) { + $statuses = wc_get_is_paid_statuses(); + $statuses = array_map(function ($status) { + return 'wc-' . $status; + }, $statuses); + + $orderStats = fluentCrmDb()->table('wc_order_stats') + ->where('customer_id', $customer->customer_id) + ->whereIn('status', $statuses) + ->get(); + + if ($orderStats->isEmpty()) { + return false; + } + + $lifetimeValue = 0; + $orderIds = []; + + $firstOrderDate = null; + $lastOrderDate = null; + + foreach ($orderStats as $order) { + if (!$firstOrderDate) { + $firstOrderDate = $order->date_created; + } + + if (!$lastOrderDate) { + $lastOrderDate = $order->date_created; + } + + if (strtotime($order->date_created) < strtotime($firstOrderDate)) { + $firstOrderDate = $order->date_created; + } + + if (strtotime($order->date_created) > strtotime($lastOrderDate)) { + $lastOrderDate = $order->date_created; + } + + $lifetimeValue += $order->total_sales; + $orderIds[] = $order->order_id; + } + + $orderIds = array_unique($orderIds); + + $orderCount = count($orderIds); + + $data_store = \WC_Data_Store::load('report-customers-stats'); + $stat = $data_store->get_data(); + + $avg_value = $orderCount > 0 ? round($lifetimeValue / $orderCount, 2) : 0; + + $summaryData = [ + 'order_count' => $orderCount, + 'lifetime_value' => $lifetimeValue, + 'avg_value' => $avg_value, + 'stat_avg_count' => $stat->avg_orders_count, + 'stat_avg_spend' => $stat->avg_total_spend, + 'stat_avg_value' => $stat->avg_avg_order_value, + 'currency_sign' => get_woocommerce_currency_symbol(), + 'last_order_date' => $lastOrderDate, + 'first_order_date' => $firstOrderDate, + ]; + + return $this->formatSummaryData($summaryData, true); + } + + return false; + } + + /** + * Return EDD 3 order history for the subscriber purchase-history panel. + */ + public function eddOrders($data, $subscriber) + { + if (!Helper::isEdd3()) { + return $data; + } + + $app = fluentCrm(); + $page = (int)$app->request->get('page', 1); + set_query_var('paged', $page); + + $hasRecount = defined('FLUENTCAMPAIGN') && \FluentCampaign\App\Services\Commerce\Commerce::isEnabled('edd'); + + if ($hasRecount && $app->request->get('will_recount') == 'yes') { + (new \FluentCampaign\App\Services\Integrations\Edd\DeepIntegration)->syncCustomerBySubscriber($subscriber); + } + + $sort_by = sanitize_sql_orderby($app->request->get('sort_by', 'id')); + $sort_type = sanitize_sql_orderby($app->request->get('sort_type', 'DESC')); + $per_page = (int)$app->request->get('per_page', 10); + $customer = new \EDD_Customer($subscriber->email); + + if (!$customer || !$customer->id) { + return $data; + } + + $lasOrderData = ''; + + /* + * EDD 3 stores orders in the edd_orders table. Legacy edd_payment posts + * are intentionally not queried because EDD 2 is no longer supported. + */ + $totalCount = fluentCrmDb()->table('edd_orders') + ->where('customer_id', $customer->id) + ->count(); + + if (!$totalCount) { + return $data; + } + + $valid_columns = ['id', 'date_created', 'total']; + $valid_directions = ['ASC', 'DESC']; + + if (!in_array($sort_by, $valid_columns)) { + $sort_by = 'id'; + } + if (!in_array(strtoupper($sort_type), $valid_directions)) { + $sort_type = 'DESC'; + } + + $orders = fluentCrmDb()->table('edd_orders') + ->where('customer_id', $customer->id) + ->orderBy($sort_by, $sort_type) + ->limit($per_page) + ->offset(($page - 1) * $per_page) + ->get(); + + $formattedOrders = []; + + foreach ($orders as $order) { + $orderActionHtml = ' + + + + '; + $date = ''.'#' . $order->id .''.date_i18n(get_option('date_format'), strtotime($order->date_created)).''; + + $status = ''. Helper::getStatusText($order->status) .''; + + $formattedOrders[] = [ + 'date' => $date, + 'status' => $status, + 'total' => edd_currency_filter(edd_format_amount($order->total)), + 'action' => $orderActionHtml + ]; + } + + if (!$orders->isEmpty()) { + $lasOrderData = date_i18n(get_option('date_format'), strtotime($orders[0]->date_created)); + } + + /** + * Determine the HTML content displayed in the EDD purchase history sidebar for a subscriber in FluentCRM. + * + * This filter allows customization of the HTML content that appears in the EDD purchase + * history sidebar for a given subscriber on a specific page. + * + * @since 2.7.0 + * + * @param string $beforeHtml The HTML content to be displayed before the purchase history. + * @param object $subscriber The subscriber object. + * @param int $page The current page identifier as an integer. + */ + $beforeHtml = apply_filters('fluent_crm/edd_purchase_sidebar_html', '', $subscriber, $page); + +// if (!$beforeHtml && $subscriber->user_id && $page == 1 && $formattedOrders) { +// $summaryData = [ +// 'order_count' => $customer->purchase_count, +// 'lifetime_value' => $customer->purchase_value, +// 'avg_value' => ($customer->purchase_count) ? round($customer->purchase_value / $customer->purchase_count, 2) : 'n/a', +// 'stat_avg_count' => 0, +// 'stat_avg_spend' => 0, +// 'stat_avg_value' => 0, +// 'currency_sign' => edd_currency_symbol(), +// 'last_order_date' => $lasOrderData +// ]; +// $beforeHtml = $this->formatSummaryData($summaryData); +// } + + return [ + 'data' => $formattedOrders, + 'total' => $totalCount, + 'sidebar_html' => $beforeHtml, + 'after_html' => '

' . esc_html__('View Customer Profile', 'fluent-crm') . '

', + 'has_recount' => $hasRecount, + 'columns_config' => [ + 'order' => [ + 'label' => __('Order', 'fluent-crm'), + 'width' => '100px', + 'sortable' => true, + 'key' => 'id' + ], + 'date' => [ + 'label' => __('Date', 'fluent-crm'), + 'sortable' => true, + 'key' => 'edd_orders' + ], + 'status' => [ + 'label' => __('Status', 'fluent-crm'), + 'width' => '140px', + 'sortable' => false + ], + 'total' => [ + 'label' => __('Total', 'fluent-crm'), + 'width' => '120px', + 'sortable' => true, + 'key' => 'total' + ], + 'action' => [ + 'label' => __('Actions', 'fluent-crm'), + 'width' => '100px', + 'sortable' => false + ] + ] + ]; + } + + public function payformSubmissions($data, $subscriber) + { + if (!defined('WPPAYFORM_VERSION')) { + return $data; + } + $app = fluentCrm(); + $page = intval($app->request->get('page', 1)); + $per_page = intval($app->request->get('per_page', 10)); + $query = fluentCrmDb()->table('wpf_submissions') + ->select([ + 'wpf_submissions.id', + 'wpf_submissions.form_id', + 'wpf_submissions.currency', + 'wpf_submissions.payment_status', + 'wpf_submissions.payment_total', + 'wpf_submissions.payment_method', + 'wpf_submissions.created_at', + 'posts.post_title', + 'wpf_subscriptions.recurring_amount', + ]) + ->join('posts', 'posts.ID', '=', 'wpf_submissions.form_id') + ->leftJoin('wpf_subscriptions', 'wpf_subscriptions.submission_id', '=', 'wpf_submissions.id') + ->where(function ($query) use ($subscriber) { + $query->where('wpf_submissions.customer_email', '=', $subscriber->email); + if ($subscriber->user_id) { + $query->orWhere('wpf_submissions.user_id', '=', $subscriber->user_id); + } + }) + // ->where('wpf_submissions.payment_total', '>', 0) + ->limit($per_page) + ->offset($per_page * ($page - 1)) + ->orderBy('wpf_submissions.id', 'desc'); + + $total = $query->count(); + $submissions = $query->get(); + $formattedSubmissions = []; + foreach ($submissions as $submission) { + $submissionUrl = admin_url('admin.php?page=wppayform.php#/edit-form/' . $submission->form_id . '/entries/' . $submission->id . '/view'); + $actionUrl = ' + + + + '; + $paymentStatus = ''. \FluentCrm\App\Services\Helper::getStatusText($submission->payment_status) .''; + $formattedSubmissions[] = [ + 'id' => '#' . $submission->id, + 'post_title' => $submission->post_title, + 'recurring_amount' => $submission->recurring_amount ? wpPayFormFormatMoney($submission->recurring_amount, $subscriber->form_id) : wpPayFormFormatMoney($submission->payment_total, $subscriber->form_id), + 'payment_status' => $paymentStatus, + 'payment_method' => $submission->payment_method, + 'created_at' => $submission->created_at, + 'action' => $actionUrl + ]; + } + + return [ + 'total' => $total, + 'data' => $formattedSubmissions, + 'columns_config' => [ + 'id' => [ 'label' => __('ID', 'fluent-crm'), 'width' => '100px', 'sortable' => false, 'key' => 'id'], + 'post_title' => [ 'label' => __('Form Title', 'fluent-crm'), 'sortable' => false, 'key' => 'post_title'], + 'recurring_amount' => [ 'label' => __('Payment Total', 'fluent-crm'), 'sortable' => false, 'key' => 'recurring_amount'], + 'payment_status' => [ 'label' => __('Payment Status', 'fluent-crm'), 'sortable' => false, 'key' => 'payment_status'], + 'payment_method' => [ 'label' => __('Payment Method', 'fluent-crm'), 'sortable' => false, 'key' => 'payment_method'], + 'created_at' => [ 'label' => __('Submitted At', 'fluent-crm'), 'sortable' => false, 'key' => 'created_at'] + ] + ]; + + } + + public function formatSummaryData($data, $bodyOnly = false) + { + $blocks = []; + if (!empty($data['first_order_date'])) { + $blocks['Customer Since'] = gmdate(get_option('date_format'), strtotime($data['first_order_date'])); + } + + if (!empty($data['last_order_date'])) { + $blocks['Last Order'] = gmdate(get_option('date_format'), strtotime($data['last_order_date'])); + } + + $blocks['Order Count (paid)'] = $data['order_count'] . $this->getPercentChangeHtml($data['order_count'], $data['stat_avg_count']); + $blocks['Lifetime Value'] = $data['currency_sign'] . $data['lifetime_value']; + $blocks['AOV'] = $data['currency_sign'] . $data['avg_value'] . $this->getPercentChangeHtml($data['avg_value'], $data['stat_avg_value']); + + + $html = '

' . esc_html__("Customer Summary", "fluent-crm") . '

'; + + $body = ''; + + + $body .= '
    '; + foreach ($blocks as $title => $block) { + $body .= '
  • ' . $title . '' . $block . '
  • '; + } + + if (!empty($data['purchased_products'])) { + $body .= '
  • ' . esc_html__("Purchased Products", "fluent-crm") . '
  • '; + } + + $body .= '
'; + + if ($bodyOnly) { + return $body; + } + + return $body . '
'; + } + + private function getPercentChangeHtml($value, $refValue) + { + if (!$refValue || !$value) { + return ''; + } + $change = $value - $refValue; + $percentChange = absint(ceil($change / $refValue * 100)); + if ($change >= 0) { + return '' . $percentChange . '%' . ''; + } else { + return '' . $percentChange . '%' . ''; + } + } + + + private function getWooOrders($subscriber, $sort_by, $sort_type) + { + $email = $subscriber->email; + + $user = get_user_by('email', $email); + + // check HPOS is enabled or not + if (get_option('woocommerce_custom_orders_table_enabled') === 'yes') { + // high performance order is enabled + if ($user) { + $hposOrders = fluentCrmDb()->table('wc_orders') + ->where('status', '!=', 'trash') + ->select(['id']) + ->where(function ($query) use ($user) { + $query->where('customer_id', $user->ID) + ->orWhere(function ($query) use ($user) { + $query->where('billing_email', $user->user_email) + ->where('customer_id', 0); + }); + }) + ->orderBy($sort_by, $sort_type) + ->get(); + } else { + $hposOrders = fluentCrmDb()->table('wc_orders') + ->select(['id']) + ->where('billing_email', $email) + ->where('customer_id', 0) + ->orderBy($sort_by, $sort_type) + ->get(); + } + + if ($hposOrders->isEmpty()) { + return []; + } + + $orders = []; + foreach ($hposOrders as $hposOrder) { + $order = wc_get_order($hposOrder->id); + if ($order) { + $orders[$hposOrder->id] = $order; + } + } + + return array_values($orders); + } + + + $orders = []; + // Get all orders by user id + $storeUseId = $user ? $user->ID : false; + + if ($storeUseId) { + $userOrders = wc_get_orders([ + 'customer_id' => $storeUseId, + 'limit' => -1, + 'orderby' => $sort_by, + 'order' => $sort_type, + ]); + + // Sort orders by total amount manually + if ($sort_by === 'total_amount') { + $this->wooSortOrdersByTotalAmount($userOrders, $sort_type); + } + + foreach ($userOrders as $order) { + $orders[$order->get_id()] = $order; + } + } + + // get orders by billing email + $guestOrders = wc_get_orders([ + 'customer' => $email, + 'limit' => -1, + 'orderby' => $sort_by, + 'order' => $sort_type, + ]); + + if ($sort_by === 'total_amount') { + $this->wooSortOrdersByTotalAmount($userOrders, $sort_type); + } + + foreach ($guestOrders as $order) { + $userId = $order->get_user_id(); + if ($userId && $storeUseId != $userId) { + continue; + } + $orders[$order->get_id()] = $order; + } + + + return array_values($orders); + } + + /** + * Sorts an array of WooCommerce orders by their total amount. + * + * This method sorts an array of WooCommerce order objects based on the total order amount. + * The sorting can be done in either ascending ('ASC') or descending ('DESC') order, + * as specified by the $sort_type parameter. + * + * @param array $orders Array of WooCommerce order objects to be sorted. + * @param string $sort_type Specifies the sorting order. + * Accepts 'ASC' for ascending or 'DESC' for descending. + * + * @return void The $orders array is sorted in place. + */ + public function wooSortOrdersByTotalAmount(&$orders, $sort_type) { + usort($orders, function ($a, $b) use ($sort_type) { + $a_total = (float)$a->get_total(); + $b_total = (float)$b->get_total(); + + return $sort_type === 'ASC' ? $a_total <=> $b_total : $b_total <=> $a_total; + }); + } + + public function pmproOrders($data, $subscriber) + { + if (!defined('PMPRO_VERSION')) { + return $data; + } + + if (!defined('FLUENTCAMPAIGN')) { + return $data; + } + + $customer = fluentCrmDb()->table('users')->where('user_email', $subscriber->email)->first(); + + if (!$customer) { + return false; + } + + if (!$subscriber->user_id || $subscriber->user_id != $customer->ID) { + $subscriber->user_id = $customer->ID; + $subscriber->save(); + } + + $app = fluentCrm(); + + $page = (int)$app->request->get('page', 1); + $per_page = (int)$app->request->get('per_page', 10); + + $sort_by = sanitize_sql_orderby($app->request->get('sort_by', 'ID')); + $sort_type = sanitize_sql_orderby($app->request->get('sort_type', 'DESC')); + + $valid_columns = ['ID', 'date', 'modified']; + $valid_directions = ['ASC', 'DESC']; + + if (!in_array($sort_by, $valid_columns)) { + $sort_by = 'ID'; + } + if (!in_array(strtoupper($sort_type), $valid_directions)) { + $sort_type = 'DESC'; + } + + // Fetch the array of MemberOrder OBJECTS + $user_order_objects = \MemberOrder::get_orders([ + 'user_id' => $subscriber->user_id, + 'status' => 'success', + 'orderby' => $sort_by, + 'order' => $sort_type + ]); + + // Create a new, simple array to hold the data for JSON conversion + $formattedOrders = []; + + $totalOrders = count($user_order_objects); + $orders = array_slice($user_order_objects, ($page - 1) * $per_page, $per_page); + + + if (!empty($orders)) { + // Loop through each PHP object and extract its data into a simple array + foreach ($orders as $order) { + // Construct the URL using WordPress's admin_url() function + $order_page_url = admin_url('admin.php?page=pmpro-orders&order=' . $order->id); + $level = pmpro_getLevel($order->membership_id); + $actionsHtml = 'View Order'; + $status = ''. \FluentCrm\App\Services\Helper::getStatusText($order->status) .''; + $formattedOrders[] = [ + 'order_code' => '#' . $order->code, + 'membership_level_name' => $level ? $level->name : null, + 'status' => $status, + 'total' => $order->total, + 'date' => gmdate('j F, Y', $order->timestamp), + 'gateway' => $order->gateway, + 'actions' => $actionsHtml + ]; + } + } + + return [ + 'data' => $formattedOrders, + 'sidebar_html' => '', + 'total' => $totalOrders, + 'has_recount' => false, + 'columns_config' => [ + 'order_code' => [ + 'label' => __('Order Code', 'fluent-crm'), + 'width' => '120px', + 'sortable' => false, + 'key' => 'id' + ], + 'membership_level_name' => [ + 'label' => __('Membership Level', 'fluent-crm'), + 'sortable' => false, + ], + 'date' => [ + 'label' => __('Date', 'fluent-crm'), + 'sortable' => false, + 'key' => 'date_created_gmt' + ], + 'status' => [ + 'label' => __('Status', 'fluent-crm'), + 'width' => '100px' + ], + 'total' => [ + 'label' => __('Total', 'fluent-crm'), + 'width' => '130px', + 'sortable' => false, + 'key' => 'total_amount' + ], + 'gateway' => [ + 'label' => __('Gateway', 'fluent-crm'), + 'width' => '120px', + 'sortable' => false, + ], + 'actions' => [ + 'label' => __('Actions', 'fluent-crm'), + 'width' => '100px' + ] + ] + ]; + } + + +} diff --git a/wp-content/plugins/fluent-crm/app/Hooks/Handlers/RedirectionHandler.php b/wp-content/plugins/fluent-crm/app/Hooks/Handlers/RedirectionHandler.php new file mode 100644 index 0000000..bc44408 --- /dev/null +++ b/wp-content/plugins/fluent-crm/app/Hooks/Handlers/RedirectionHandler.php @@ -0,0 +1,184 @@ +url_token = $data['fch']; + } + + $isAnonymousClick = isset($data['ano']); + + $redirectUrl = trim($this->trackUrlClick($mailId, $urlData, $isAnonymousClick)); + $redirectUrl = htmlspecialchars_decode($redirectUrl); + + if (!$redirectUrl) { + wp_redirect(home_url(), 307); + exit; + } + + // remove zero width space + $redirectUrl = str_replace(["\xE2\x80\x8B", '%E2%80%8B'], '', $redirectUrl); + do_action('fluentcrm_email_url_click', $redirectUrl, $mailId, $urlData); + wp_redirect($redirectUrl, 307); + exit; + } + + public function trackUrlClick($mailId, $urlData, $isAnonymousClick = false) + { + if (!$mailId) { + return $urlData->url; + } + + $campaignEmail = CampaignEmail::with(['subscriber'])->find($mailId); + + if (!$campaignEmail || !$campaignEmail->subscriber) { + return $urlData->url; + } + + $campaign = fluentCrmGetFromCache('campaign_' . $campaignEmail->campaign_id, function () use ($campaignEmail) { + return Campaign::withoutGlobalScopes()->find($campaignEmail->campaign_id); + }); + + if (!$campaign) { + return $urlData->url; + } + + // Require valid fch token before recording any tracking data. + // Missing or invalid token = redirect but don't record metrics. + // This prevents analytics poisoning via forged or guessed mid values. + if (empty($urlData->url_token) || substr($campaignEmail->email_hash, 0, 8) !== $urlData->url_token) { + return $urlData->url; + } + + if (!$campaignEmail->is_open && !$isAnonymousClick) { + do_action('fluent_crm/email_opened', $campaignEmail); + } + + if (!$isAnonymousClick) { + CampaignUrlMetric::maybeInsert([ + 'url_id' => $urlData->id, + 'campaign_id' => $campaignEmail->campaign_id, + 'subscriber_id' => $campaignEmail->subscriber_id, + 'type' => 'click', + 'ip_address' => FluentCrm('request')->getIp(fluentCrmWillAnonymizeIp()) + ]); + } + + $url = $urlData->url; + + $url = str_replace('&', '&', $url); + $url = esc_url_raw($url); + + $isSmartUrl = strpos($url, 'route=smart_url'); + + $tokenVerified = false; + + /** + * Filter whether to use cookies for FluentCRM redirection. + * + * This filter allows you to control whether cookies should be used for tracking + * FluentCRM redirection. By default, it is set to true. + * + * @param bool Whether to use cookies for redirection. Default true. + * @since 2.8.44 + * + */ + if (apply_filters('fluent_crm/will_use_cookie', true) && !empty($urlData->url_token)) { + // validate the URL token here + if (substr($campaignEmail->email_hash, 0, 8) === $urlData->url_token) { + $tokenVerified = true; + $secureHash = fluentCrmGetContactSecureHash($campaignEmail->subscriber_id); + setcookie("fc_hash_secure", $secureHash, time() + 7776000, COOKIEPATH, COOKIE_DOMAIN, is_ssl(), true); /* expire in 90 days */ + $_COOKIE['fc_hash_secure'] = $secureHash; + } + + if ($campaignEmail->campaign_id) { + setcookie("fc_cid", $campaignEmail->campaign_id, time() + 2419200, COOKIEPATH, COOKIE_DOMAIN, is_ssl(), true); /* expire in 28 days */ + } + } + + do_action('fluent_crm/email_url_clicked', $campaignEmail, $urlData); + + $args = $campaign->getUtmParams(); + + if (!$isAnonymousClick) { + $campaignEmail->click_counter += 1; + $campaignEmail->is_open = 1; + $campaignEmail->save(); + } else { + do_action('fluent_crm/anonymous_email_url_clicked', $url, $campaign, $campaignEmail); + } + + do_action('fluent_crm/track_activity_by_subscriber', $campaignEmail->subscriber); + + if ($isSmartUrl) { + // this is a smart URL + $url_components = wp_parse_url($url); + parse_str($url_components['query'], $params); + + if (!empty($params['slug'])) { + $subscriber = $campaignEmail->subscriber; + + $signedHash = Arr::get($_REQUEST, 'signed_hash'); + $isSecure = $tokenVerified && $signedHash && \FluentCrm\App\Services\Helper::verifySmartUrlHash($campaignEmail->email_hash, $signedHash); + + if ($isSecure) { + do_action('fluent_crm/smart_link_verified', $subscriber); + } + + do_action('fluentcrm_smartlink_clicked_direct', sanitize_text_field($params['slug']), $subscriber, $campaignEmail); + } + } + + if (strpos($urlData->url, 'route=bnu') !== false) { + $url_components = wp_parse_url($url); + parse_str($url_components['query'], $params); + if (!empty($params['aid'])) { + $benchmarkActionId = intval($params['aid']); + // Note: hook name has a known typo (missing 't' in 'fluent') — kept for backward compatibility with Pro + do_action('fluencrm_benchmark_link_clicked', $benchmarkActionId, $campaignEmail->subscriber); + } + $args['bnu_timer_' . time()] = time(); + } + + if ($args) { + $url = add_query_arg($args, $url); + } + + return $url; + } +} diff --git a/wp-content/plugins/fluent-crm/app/Hooks/Handlers/Scheduler.php b/wp-content/plugins/fluent-crm/app/Hooks/Handlers/Scheduler.php new file mode 100644 index 0000000..a76a0fb --- /dev/null +++ b/wp-content/plugins/fluent-crm/app/Hooks/Handlers/Scheduler.php @@ -0,0 +1,594 @@ +handle(); + + nocache_headers(); + wp_send_json_success([ + 'message' => 'success', + 'timestamp' => time() + ]); + }); + + // For Multi Threaded Emails Internal Ajax. Same as above — the atomic + // lock inside MultiThreadHandler::isSystemOk() guards against concurrent + // runners, so we call the handler directly and let the loser bail at the + // lock. The experimental-flag check stays here to avoid constructing the + // handler at all when multi-threading is disabled. + add_action('wp_ajax_nopriv_fluentcrm-post-multi-thread-send-now', function () { + if (Helper::isExperimentalEnabled('multi_threading_emails')) { + (new MultiThreadHandler())->handle(); + } + + nocache_headers(); + wp_send_json_success([ + 'message' => 'success', + 'timestamp' => time() + ]); + }); + + add_action('fluentcrm_scheduled_every_minute_tasks', array(__CLASS__, 'process')); + add_action('fluentcrm_scheduled_hourly_tasks', array(__CLASS__, 'processHourly')); + add_action('fluentcrm_scheduled_five_minute_tasks', array(__CLASS__, 'processFiveMinutes')); + add_action('fluentcrm_process_contact_jobs', array(__CLASS__, 'processForSubscriber'), 999, 1); + add_action('fluentcrm_scheduled_weekly_tasks', array(__CLASS__, 'processWeekly')); + add_action('fluent_crm_send_multi_thread_emails', array(__CLASS__, 'processMultiThreadEmails'), 10); + + add_action('fluent_crm_cancel_multi_thread_mailing', function () { + as_unschedule_all_actions('fluent_crm_send_multi_thread_emails'); + return true; + }); + + /* + * Clean up schedule that means removing from database- tasks by action scheduler + * Clean up before last 7 days logs generated by action scheduler + * this action will be triggered daily and will remove all the logs generated before 7 days + */ + add_action('fluent_crm_ascheduler_runs_daily', function () { + Cleanup::maybeRemoveOldScheuledActionLogs(); + }); + + } + + public static function process() + { + wp_raise_memory_limit('admin'); + + // In-process re-entrance guard (cheap; complements the cross-process lock below). + if (did_action('fluentcrm_process_scheduled_tasks_init')) { + return false; + } + + // Atomic cross-process mutex. Prevents concurrent AS + WP-Cron + AJAX + // runners from all reaching Handler->handle() at the same time. The + // downstream BaseHandler also has its own lock — this outer guard + // avoids wasted PHP bootstraps for the loser of the race. + if (!self::acquireLock('minute_scheduler', 90)) { + return false; + } + + try { + // _fcrm_last_scheduler stays as the success-timestamp signal used + // by the WP-Cron fallback in register() to detect a stalled Action + // Scheduler. It is no longer the gate that prevents re-entry — + // that role belongs to the atomic lock above. + fluentCrmSetOptionCache('_fcrm_last_scheduler', time(), 50); + do_action('fluentcrm_process_scheduled_tasks_init'); + + (new Handler)->handle(); + } finally { + self::releaseLock('minute_scheduler'); + } + + return true; + } + + /** + * Browser-ping fallback for the every-minute task. + * + * Triggered from the admin app's periodic ping (ReportingController::ping, + * fired ~every 50s while any CRM page is open). It is a TRUE last-resort + * fallback: it only takes over when Action Scheduler (and the WP-Cron + * fallback) have stalled, detected by the same _fcrm_last_scheduler + * freshness signal the WP-Cron fallback in register() uses. When AS is + * healthy this returns after a single option read, so it is safe to call on + * every ping and for every admin who has the dashboard open — it does NOT + * run cron more often than once per minute on a healthy site. + * + * All concurrency safety lives in process(): its atomic cross-process lock + * means that even with many tabs/users pinging at once, at most one runner + * sends emails, and the _fcrm_last_scheduler stamp written there throttles + * takeovers to roughly once per minute. This only advances the minute task + * (the email-sending pipeline); the heavier hourly/five-minute tasks keep + * their own WP-Cron/AS schedules. + * + * @return bool True if it took over and ran the minute task, false otherwise. + */ + public static function maybeProcessFromBrowserPing() + { + // Action Scheduler owns this task; only step in when it has actually + // stalled. Same 70s threshold as the WP-Cron fallback in register(). + $lastScheduler = fluentCrmGetOptionCache('_fcrm_last_scheduler'); + if ($lastScheduler && (time() - $lastScheduler) <= 70) { + return false; + } + + return self::process(); + } + + public static function processForSubscriber($subscriber) + { + if (!is_object($subscriber) || empty($subscriber->id)) { + return false; + } + + if (!defined('FLUENTCRM_DOING_BULK_IMPORT')) { + // @todo: Implement this immediately + (new Handler)->processSubscriberEmail($subscriber->id); + } + + return true; + } + + public static function processHourly() + { + // Atomic mutex. Closes the duplicate-event leak in markArchiveCampaigns(): + // without this, two concurrent hourly runners both pass the SELECT, + // both UPDATE rows to 'archived' (idempotent), and both fire + // fluent_crm/campaign_archived for the same campaign — causing + // listeners (webhooks, metrics, notifications) to fire twice. + if (!self::acquireLock('hourly_scheduler', 300)) { + return; + } + + try { + self::markArchiveCampaigns(); + self::maybeCleanupCsvFiles(); + do_action('fluent_crm_process_automation'); + } finally { + self::releaseLock('hourly_scheduler'); + } + } + + + public static function markArchiveCampaigns() + { + // get the scheduled or working campaigns where scheduled_at is five minutes ago + $campaigns = Campaign::whereIn('status', ['working', 'scheduled']) + ->whereDoesntHave('emails', function ($query) { + $query->whereIn('status', ['scheduling', 'pending', 'scheduled', 'processing', 'draft']); + return $query; + }) + ->withoutGlobalScope('type') + ->whereIn('type', fluentCrmAutoProcessCampaignTypes()) + ->where('scheduled_at', '<', gmdate('Y-m-d H:i:s', current_time('timestamp') - 300)) + ->get(); + + if (!$campaigns->isEmpty()) { + + Campaign::whereIn('id', array_unique($campaigns->pluck('id')->toArray())) + ->withoutGlobalScope('type') + ->update([ + 'status' => 'archived' + ]); + + foreach ($campaigns as $campaign) { + do_action('fluent_crm/campaign_archived', $campaign); + } + + return true; + } + + return false; + } + + /** + * @return void + */ + public static function processWeekly() + { + (new Maintenance())->maybeProcessData(); + + // Clear email_body from historical 'sent' rows to reclaim disk space. + // Loop a LIMIT-bounded UPDATE so each statement's row-lock footprint + // stays small (an unbounded UPDATE on a multi-million-row table holds + // locks for minutes and stalls report/dashboard SELECTs) while still + // draining the full backlog in this tick. Going direct to $wpdb skips + // ORM overhead on what is effectively the same repeated statement. + try { + global $wpdb; + $table = $wpdb->prefix . 'fc_campaign_emails'; + $chunkSize = 50000; + $maxIterations = 100; // safety cap — up to ~5M rows per weekly tick + + for ($i = 0; $i < $maxIterations; $i++) { + $affected = (int) $wpdb->query( + "UPDATE {$table} SET email_body = '' WHERE status = 'sent' AND email_body != '' LIMIT {$chunkSize}" + ); + + if ($affected < $chunkSize || fluentCrmIsMemoryExceeded()) { + break; + } + } + } catch (\Exception $e) { + Helper::debugLog('processWeekly', 'email_body cleanup deferred: ' . $e->getMessage(), 'extended'); + } + } + + /** + * Discover and process pending campaigns. + * + * Called by cron/Action Scheduler. Handles housekeeping (stale email reset), + * finds campaigns ready to process, and kicks off processing. For continuous + * processing, use processCampaignById() via the AJAX handler. + * + * @return bool + */ + public static function processFiveMinutes() + { + // Cheap time-based pre-check — skips the lock-acquire round trip when + // the function is called more frequently than the work needs to run. + $lastRun = fluentCrmGetOptionCache('_fcrm_last_five_minutes_run', 30); + if ($lastRun && (time() - $lastRun) < 60) { + return false; + } + + // Atomic mutex. The throttle above is non-atomic so two near-simultaneous + // callers can both pass it; the lock guarantees that only one actually + // proceeds into discovery + processing. + if (!self::acquireLock('five_minute_scheduler', 180)) { + return false; + } + + try { + fluentCrmSetOptionCache('_fcrm_last_five_minutes_run', time(), 60); + + self::resetStaleProcessingEmails(100, 'processFiveMinutes'); + + $cutOutTime = gmdate('Y-m-d H:i:s', current_time('timestamp') + 360); + + $campaigns = Campaign::whereIn('status', ['pending-scheduled', 'processing']) + ->withoutGlobalScope('type') + ->whereIn('type', fluentCrmAutoProcessCampaignTypes()) + ->orderBy('scheduled_at', 'ASC') + ->where('scheduled_at', '<=', $cutOutTime) + ->limit(2) + ->get(); + + if ($campaigns->isEmpty()) { + do_action('fluent_crm_process_automation'); + do_action('fluentcrm_scheduled_hourly_tasks'); + return false; + } + + $firstCampaign = $campaigns->first(); + + if ($firstCampaign->status == 'pending-scheduled') { + $firstCampaign->status = 'processing'; + $firstCampaign->save(); + } + + $result = self::processCampaignById($firstCampaign->id); + + // If first campaign is done and there are more queued, chain the next one. + // Skip if memory is low (aborted) to avoid cascading failures. + if (!$result && count($campaigns) > 1 && !fluentCrmIsMemoryExceeded()) { + // Verify first campaign actually finished (not just aborted) + $firstCampaign = Campaign::withoutGlobalScope('type')->find($firstCampaign->id); + if ($firstCampaign && $firstCampaign->status != 'processing') { + $nextCampaign = $campaigns->last(); + if ($nextCampaign->status == 'pending-scheduled') { + $nextCampaign->status = 'processing'; + $nextCampaign->save(); + } + self::fireCampaignProcessingChain($nextCampaign->id); + } + } + + return $result; + } finally { + self::releaseLock('five_minute_scheduler'); + } + } + + /** + * Reset rows stuck in 'processing' back to 'pending' so they get re-claimed. + * + * An unbounded mass UPDATE on (status='processing' AND updated_at < cutoff) + * locks a wide range and deadlocks against the row-level SELECT ... FOR + * UPDATE claims that the mailer Handler / MultiThreadHandler hold while + * sending. We instead drain in bounded chunks by primary key. + * + * We deliberately do NOT order the SELECT: ORDER BY id would push MySQL + * onto PRIMARY (full id-walk looking for sparse matches on a multi-million + * row table) instead of the (status, scheduled_at) index, which contains + * only the small currently-'processing' slice. Each chunk drains rows out + * of the predicate, so the next iteration naturally finds different rows + * without an explicit order. + * + * Any deadlock that still slips through is harmless — remaining rows will + * be picked up on the next caller's tick. + * + * @param int $maxAgeSeconds Rows older than this (in 'processing') get reset. + * @param string $callerContext Used in the deferred-log message. + * @return int Number of rows reset back to pending. + */ + public static function resetStaleProcessingEmails($maxAgeSeconds = 100, $callerContext = '') + { + try { + // If a sender lock is still fresh, a batch is likely active or just + // yielded. Resetting 'processing' rows during that window risks + // requeueing work owned by the live sender and increases row-lock + // contention with SELECT ... FOR UPDATE / sent-status updates. + if (self::hasFreshEmailSenderLock($maxAgeSeconds)) { + return 0; + } + + $staleCutoff = gmdate('Y-m-d H:i:s', current_time('timestamp') - (int) $maxAgeSeconds); + $chunkSize = 200; + $maxChunks = 50; // up to 10k rows per call; subsequent calls drain the rest + $recovered = 0; + + for ($i = 0; $i < $maxChunks; $i++) { + $staleIds = CampaignEmail::where('status', 'processing') + ->where('updated_at', '<', $staleCutoff) + ->limit($chunkSize) + ->pluck('id') + ->toArray(); + + if (empty($staleIds)) { + break; + } + + $updated = CampaignEmail::whereIn('id', $staleIds) + ->where('status', 'processing') + ->update([ + 'status' => 'pending' + ]); + + if ($updated === false) { + global $wpdb; + Helper::debugLog($callerContext ?: 'resetStaleProcessingEmails', 'Stale email reset deferred: ' . $wpdb->last_error, 'extended'); + break; + } + + $recovered += (int) $updated; + + if (count($staleIds) < $chunkSize || fluentCrmIsMemoryExceeded()) { + break; + } + } + + if ($recovered) { + Helper::debugLog($callerContext ?: 'resetStaleProcessingEmails', 'Recovered ' . $recovered . ' stale processing emails older than ' . (int) $maxAgeSeconds . ' seconds', 'extended'); + } + + return $recovered; + } catch (\Exception $e) { + Helper::debugLog($callerContext ?: 'resetStaleProcessingEmails', 'Stale email reset deferred: ' . $e->getMessage(), 'extended'); + return 0; + } + } + + /** + * Avoid stale-row recovery while a sender still appears active. + * + * Sender locks are refreshed by BaseHandler::refreshLock() between claimed + * batches. We check all sender lock keys because regular, multi-threaded, + * and CLI senders can all own rows in fc_campaign_emails. + * + * @param int $maxAgeSeconds + * @return bool + */ + private static function hasFreshEmailSenderLock($maxAgeSeconds) + { + // Use at least 60 seconds so a very small caller-provided stale window + // does not make recovery race an otherwise healthy sender. + $freshWindow = max(60, (int) $maxAgeSeconds); + + // Compare everything against one timestamp for consistent decisions + // across all sender lock keys checked below. + $now = time(); + + foreach (['fluentcrm_is_sending_emails', 'fluentcrm_is_sending_multi_emails', 'fluentcrm_is_sending_cli_emails'] as $lockKey) { + // Read the lock straight from its wp_options row. BaseHandler's + // acquireLock()/refreshLock() store the timestamp there via + // Helper::acquireDbLock()/refreshDbLock() on every environment, so we + // must NOT use getInstantOption() here: on object-cache sites it reads + // the fc_instant_options group, which the DB lock never writes to, and + // would miss a live sender — letting recovery reset its rows. + $lockedAt = Helper::getDbLockTimestamp($lockKey); + + // A non-empty timestamp inside the freshness window means a sender + // appears active, so stale recovery should defer to the next tick. + if ($lockedAt && ($now - $lockedAt) <= $freshWindow) { + return true; + } + } + + // No fresh sender lock was found. Recovery may safely inspect stale rows. + return false; + } + + /** + * Process a specific campaign by ID. + * + * Can be called directly from the AJAX handler for continuous chaining + * without re-discovering campaigns or running housekeeping. + * + * @param int $campaignId + * @return bool True if more processing is needed, false if done. + */ + public static function processCampaignById($campaignId) + { + // Per-campaign scheduler lock. processCampaignById has two entry points + // — the AJAX self-trigger fluentcrm-post-campaigns-emails-processing + // (which bypasses processFiveMinutes' scheduler-level lock entirely) + // and processFiveMinutes itself (which holds five_minute_scheduler). + // Without this guard, fireCampaignProcessingChain could pile up + // overlapping AJAX requests for the same campaign that all reach + // CampaignProcessor and bail at its per-campaign lock — wasted PHP + // bootstraps. Lock name is per-campaign so different campaigns still + // process in parallel. TTL matches the set_time_limit(120) below. + $lockName = 'campaign_chain_' . (int)$campaignId; + if (!self::acquireLock($lockName, 120)) { + return false; + } + + try { + if (function_exists('set_time_limit')) { + @set_time_limit(120); + } + + $campaign = Campaign::withoutGlobalScope('type')->find($campaignId); + if (!$campaign) { + return false; + } + + $campaignProcessingChunk = (int)apply_filters('fluent_crm/five_minute_campaign_processing_chunk', 20, $campaign); + if ($campaignProcessingChunk < 1) { + $campaignProcessingChunk = 1; + } + + $runTime = fluentCrmMaxRunTime() - 5; + $campaign = (new CampaignProcessor($campaignId))->processEmails($campaignProcessingChunk, $runTime); + + if (fluentCrmIsMemoryExceeded()) { + return false; + } + + if ($campaign && $campaign->status == 'processing') { + self::fireCampaignProcessingChain($campaignId); + return true; + } + + return false; + } finally { + self::releaseLock($lockName); + } + } + + /** + * Fire a background AJAX request to continue processing a specific campaign. + * + * @param int $campaignId + */ + private static function fireCampaignProcessingChain($campaignId) + { + $url = add_query_arg([ + 'action' => 'fluentcrm-post-campaigns-emails-processing', + 'campaign_id' => $campaignId, + 'time' => time() + ], admin_url('admin-ajax.php')); + + \FluentCrm\App\Services\Libs\Mailer\Handler::fireNonBlockingRequest($url, [ + 'retry' => 1 + ]); + } + + public static function maybeCleanupCsvFiles() + { + $dir = FileSystem::getDir(); + + // loop through files in directory + foreach (glob($dir . '/fluentcrm-*.csv') as $filename) { + // check if file was created before last 30 minutes + if (time() - filectime($filename) >= 1800) { + wp_delete_file($filename); // delete file + } + } + } + + public static function processMultiThreadEmails() + { + (new MultiThreadHandler())->handle(); + return true; + } + + /** + * Atomically claim a scheduler-level lock so two runners can't enter the + * same critical section concurrently (e.g. Action Scheduler + WP-Cron + * minute ticks landing in the same second). + * + * Backed by a conditional UPDATE on wp_options keyed off a timestamp + * (Helper::acquireDbLock). The UPDATE succeeds only if the row is unclaimed + * or its stored timestamp is older than $ttl, so a crashed runner's lock + * self-recovers after the TTL. This is used on every environment — we no + * longer take a wp_cache_add() fast path, because that primitive is not + * atomic under all object-cache drop-ins (e.g. LiteSpeed), which let + * concurrent runners all acquire the same lock. See Helper::acquireDbLock(). + * + * @param string $name Lock identifier appended to the option key. + * @param int $ttl Seconds before a held lock is considered abandoned. + * @return bool True if the lock was acquired by this process. + */ + private static function acquireLock($name, $ttl) + { + return Helper::acquireDbLock('_fluentcrm_lock_' . $name, $ttl); + } + + /** + * Release a scheduler-level lock previously acquired by acquireLock(). + * Safe to call even if the lock was not held by this process — the worst + * case is freeing the slot a tick early. + */ + private static function releaseLock($name) + { + Helper::releaseDbLock('_fluentcrm_lock_' . $name); + } +} diff --git a/wp-content/plugins/fluent-crm/app/Hooks/Handlers/SetupWizard.php b/wp-content/plugins/fluent-crm/app/Hooks/Handlers/SetupWizard.php new file mode 100644 index 0000000..e375788 --- /dev/null +++ b/wp-content/plugins/fluent-crm/app/Hooks/Handlers/SetupWizard.php @@ -0,0 +1,161 @@ +setup_wizard(); + } + } + + /** + * Show the setup wizard + */ + public function setup_wizard() + { + add_filter('user_can_richedit', '__return_true'); + + if (!function_exists('media_handle_upload')) { + require_once(ABSPATH . 'wp-admin/includes/image.php'); + require_once(ABSPATH . 'wp-admin/includes/file.php'); + require_once(ABSPATH . 'wp-admin/includes/media.php'); + } + + + if (current_user_can('upload_files')) { + wp_enqueue_script('media-upload'); + } + add_thickbox(); + + wp_enqueue_editor(); + + + if (function_exists('wp_enqueue_media')) { + wp_enqueue_media(); + } + + + // Inject Vite HMR client — mirrors AdminMenu::loadCssJs(). + // Without this, Vue '; +}); + +/* + * MCP — Register abilities for the WordPress Abilities API. + * + * Lazy-register guard: + * - On WP < 6.9 (no Abilities API in core) OR sites without the WP MCP Adapter + * plugin active, `wp_register_ability` is undefined — we skip silently. + * - The opt-out option `fluent_crm_mcp_enabled` (default 'yes') lets admins + * disable the entire MCP surface from Settings → MCP without uninstalling + * the adapter. + * + * See `app/Modules/MCP/MCPInit.php` for the registration logic. + */ +add_action('init', function () { + if (!function_exists('wp_register_ability')) { + return; + } + + if (fluentcrm_get_option('mcp_enabled', 'yes') !== 'yes') { + return; + } + + (new \FluentCrm\App\Modules\MCP\MCPInit())->init(); +}, 5); diff --git a/wp-content/plugins/fluent-crm/app/Hooks/filters.php b/wp-content/plugins/fluent-crm/app/Hooks/filters.php new file mode 100644 index 0000000..f815d80 --- /dev/null +++ b/wp-content/plugins/fluent-crm/app/Hooks/filters.php @@ -0,0 +1,86 @@ +addFilter('fluent_crm/countries', 'CountryNames@get'); + +(new \FluentCrm\App\Hooks\Handlers\EmailDesignTemplates())->register(); + +$app->addFilter('fluent_crm/purchase_history_woocommerce', 'PurchaseHistory@wooOrders', 10, 2); +$app->addFilter('fluent_crm/purchase_history_edd', 'PurchaseHistory@eddOrders', 10, 2); +$app->addFilter('fluent_crm/purchase_history_payform', 'PurchaseHistory@payformSubmissions', 10, 2); +$app->addFilter('fluent_crm/purchase_history_pmpro', 'PurchaseHistory@pmproOrders', 10, 2); + +// Fluent Forms Integration +(new \FluentCrm\App\Hooks\Handlers\FormSubmissions())->register(); + +add_filter('fluent_crm/parse_campaign_email_text', function ($text, $subscriber) { + return \FluentCrm\App\Services\Libs\Parser\Parser::parse($text, $subscriber); +}, 10, 2); + +$app->addFilter('fluent_crm/parse_extended_crm_text', function ($text, $subscriber) { + if (!$subscriber) { + return $text; + } + + return \FluentCrm\App\Services\Libs\Parser\Parser::parseCrmValue($text, $subscriber); +}, 10, 2); + +$app->addFilter('comment_form_submit_field', 'AutoSubscribeHandler@addSubscribeCheckbox', 10, 1); +$app->addFilter('wp_privacy_personal_data_exporters', 'Cleanup@attachCrmExporter'); + + +$app->addFilter('wp_privacy_personal_data_exporters', 'Cleanup@attachCrmExporter'); +$app->addFilter('fluent_crm/block_editor_unregister_all_patterns', 'FluentBlockPatternHandler@shouldUnregisterAllPatterns', 10, 3); +$app->addFilter('fluent_crm/block_editor_custom_pattern_categories', 'FluentBlockPatternHandler@addCustomPatternCategories', 10, 1); +$app->addFilter('fluent_crm/block_editor_custom_patterns', 'FluentBlockPatternHandler@addCustomPatterns', 10, 1); + +/* + * deprecated Hooks + * @todo: Remove this by January 2023 + */ +add_filter('fluentcrm_parse_campaign_email_text', function ($text, $subscriber) { + if (!$subscriber) { + return $text; + } + + _deprecated_hook('fluentcrm_parse_campaign_email_text', '2.6.6', 'fluent_crm/parse_campaign_email_text', 'Use fluent_crm/parse_campaign_email_text filter hook instead'); + + return \FluentCrm\App\Services\Libs\Parser\Parser::parse($text, $subscriber); +}, 10, 2); + +$app->addFilter('fluentcrm_email-design-template-plain', function ($emailBody, $templateData, $campaign) { + _deprecated_hook('fluentcrm_email-design-template-plain', '2.6.6', 'fluent_crm/email-design-template-plain', 'Use fluent_crm/email-design-template-plain filter hook instead'); + return (new \FluentCrm\App\Hooks\Handlers\EmailDesignTemplates())->addPlainTemplate($emailBody, $templateData, $campaign); +}, 10, 3); + +$app->addFilter('fluentcrm_email-design-template-simple', function ($emailBody, $templateData, $campaign) { + _deprecated_hook('fluentcrm_email-design-template-simple', '2.6.6', 'fluent_crm/email-design-template-simple', 'Use fluent_crm/email-design-template-simple filter hook instead'); + return (new \FluentCrm\App\Hooks\Handlers\EmailDesignTemplates())->addSimpleTemplate($emailBody, $templateData, $campaign); +}, 10, 3); + +$app->addFilter('fluentcrm_email-design-template-classic', function ($emailBody, $templateData, $campaign) { + _deprecated_hook('fluentcrm_email-design-template-classic', '2.6.6', 'fluent_crm/email-design-template-classic', 'Use fluent_crm/email-design-template-classic filter hook instead'); + return (new \FluentCrm\App\Hooks\Handlers\EmailDesignTemplates())->addClassicTemplate($emailBody, $templateData, $campaign); +}, 10, 3); + +$app->addFilter('fluentcrm_email-design-template-raw_classic', function ($emailBody, $templateData, $campaign) { + _deprecated_hook('fluentcrm_email-design-template-raw_classic', '2.6.6', 'fluent_crm/email-design-template-raw_classic', 'Use fluent_crm/email-design-template-raw_classic filter hook instead'); + return (new \FluentCrm\App\Hooks\Handlers\EmailDesignTemplates())->addRawClassicTemplate($emailBody, $templateData, $campaign); +}, 10, 3); + +$app->addFilter('fluentcrm_email-design-template-web_preview', function ($emailBody, $templateData, $campaign) { + _deprecated_hook('fluentcrm_email-design-template-web_preview', '2.6.6', 'fluent_crm/email-design-template-web_preview', 'Use fluent_crm/email-design-template-web_preview filter hook instead'); + return (new \FluentCrm\App\Hooks\Handlers\EmailDesignTemplates())->addWebPreviewTemplate($emailBody, $templateData, $campaign); +}, 10, 3); + +/* + * + */ diff --git a/wp-content/plugins/fluent-crm/app/Http/Controllers/ActivityLogController.php b/wp-content/plugins/fluent-crm/app/Http/Controllers/ActivityLogController.php new file mode 100644 index 0000000..b4899d5 --- /dev/null +++ b/wp-content/plugins/fluent-crm/app/Http/Controllers/ActivityLogController.php @@ -0,0 +1,41 @@ +get('search')); + + $logs = ActivityLog::orderBy('id', 'DESC'); + + if (!empty($search)) { + $logs = $logs->where('action', 'LIKE', "%{$search}%") + ->orWhere('description', 'LIKE', "%{$search}%"); + } + + $logs = $logs->paginate($request->per_page ?: 20); + + return [ + 'logs' => $logs + ]; + } + + public function deleteAll(Request $request) + { + ActivityLog::where('id', '>', 0)->delete(); + + return [ + 'message' => __('All activity logs have been deleted', 'fluent-crm') + ]; + } +} diff --git a/wp-content/plugins/fluent-crm/app/Http/Controllers/AiController.php b/wp-content/plugins/fluent-crm/app/Http/Controllers/AiController.php new file mode 100644 index 0000000..0b762dd --- /dev/null +++ b/wp-content/plugins/fluent-crm/app/Http/Controllers/AiController.php @@ -0,0 +1,1208 @@ + ['wordpress'], + 'open_ai' => ['auto', 'gpt-5.5', 'gpt-5.4', 'gpt-5.4-mini', 'gpt-5.4-nano', 'gpt-4.1', 'gpt-4o', 'gpt-4o-mini'], + 'claude' => ['auto', 'claude-opus-4-7', 'claude-sonnet-4-6', 'claude-haiku-4-5-20251001', 'claude-opus-4-6'], + 'gemini' => ['auto', 'gemini-3.5-flash', 'gemini-3.1-pro-preview', 'gemini-3-flash-preview', 'gemini-3.1-flash-lite', 'gemini-2.5-flash', 'gemini-2.5-pro', 'gemini-2.5-flash-lite'], + ]; + + private $autoProviderModels = [ + 'open_ai' => 'gpt-5.4', + 'claude' => 'claude-sonnet-4-6', + 'gemini' => 'gemini-3.5-flash', + 'wordpress' => 'wordpress', + ]; + + public function getSettings(Request $request) + { + $settings = $this->getSavedSettings(); + + // Mask the API key for frontend display + if (!empty($settings['api_key'])) { + $settings['api_key'] = '****' . substr($settings['api_key'], -4); + } + + global $wp_version; + $hasWordPressAi = (intval(explode('.', $wp_version)[0]) >= 7); + $connectorsUrl = admin_url('options-connectors.php'); + + return $this->sendSuccess([ + 'settings' => $settings, + 'has_wordpress_ai' => $hasWordPressAi, + 'connectors_url' => $connectorsUrl, + ]); + } + + public function saveSettings(Request $request) + { + $data = $request->get('settings', []); + + $isEnabled = sanitize_text_field(Arr::get($data, 'is_enabled', 'no')); + $provider = $this->normalizeProvider(sanitize_text_field(Arr::get($data, 'provider', ''))); + $model = sanitize_text_field(Arr::get($data, 'model', 'auto')); + $apiKey = sanitize_text_field(Arr::get($data, 'api_key', '')); + $customPrompt = sanitize_textarea_field(Arr::get($data, 'custom_prompt', '')); + + if ($isEnabled !== 'yes') { + $isEnabled = 'no'; + } + + global $wp_version; + $hasWordPressAi = (intval(explode('.', $wp_version)[0]) >= 7); + if ($provider === 'wordpress' && !$hasWordPressAi) { + return $this->sendError([ + 'message' => __('WordPress AI is only supported in WordPress 7.0 or higher.', 'fluent-crm'), + ], 422); + } + + if (!$model) { + $model = 'auto'; + } + + $validProviders = array_keys($this->providerModels); + if ($provider && !in_array($provider, $validProviders, true)) { + return $this->sendError([ + 'message' => __('Invalid AI provider selected.', 'fluent-crm'), + ], 422); + } + + $existingCredentials = $this->getSavedCredentials(); + + // Handle API key: if masked value is sent back, keep existing; if empty, clear it. + $plainApiKey = Arr::get($existingCredentials, 'api_key', ''); + if (empty($apiKey)) { + $plainApiKey = ''; + } elseif (strpos($apiKey, '****') !== 0) { + $plainApiKey = $apiKey; + } + + $credentials = [ + 'provider' => $provider, + 'model' => $model, + 'api_key' => $plainApiKey, + 'created_by' => 'fluent_crm', + ]; + + $preferences = [ + 'is_enabled' => $isEnabled, + 'custom_prompt' => $customPrompt, + ]; + + update_option($this->credentialsOptionKey, $credentials, false); + fluentcrm_update_option($this->writingSettingsOptionKey, $preferences); + + return $this->sendSuccess([ + 'message' => __('AI configuration saved successfully.', 'fluent-crm'), + ]); + } + + public function getModels(Request $request) + { + $data = $request->get('settings', []); + $provider = $this->normalizeProvider(sanitize_text_field(Arr::get($data, 'provider', ''))); + + $validProviders = array_keys($this->providerModels); + if (!$provider || !in_array($provider, $validProviders, true)) { + return $this->sendError([ + 'message' => __('Invalid AI provider selected.', 'fluent-crm'), + ], 422); + } + + return $this->sendSuccess([ + 'models' => $this->formatModelOptions($provider), + ]); + } + + public function testConnection(Request $request) + { + $data = $request->get('settings', []); + $provider = $this->normalizeProvider(sanitize_text_field(Arr::get($data, 'provider', ''))); + $model = sanitize_text_field(Arr::get($data, 'model', 'auto')); + $apiKey = sanitize_text_field(Arr::get($data, 'api_key', '')); + + if (!$provider || !$model) { + return $this->sendError([ + 'message' => __('Please select a provider and model first.', 'fluent-crm'), + ], 422); + } + + $validProviders = array_keys($this->providerModels); + if (!in_array($provider, $validProviders, true)) { + return $this->sendError([ + 'message' => __('Invalid AI provider selected.', 'fluent-crm'), + ], 422); + } + + // If the API key is masked, use the saved one + if (!$apiKey || strpos($apiKey, '****') === 0) { + $savedSettings = $this->getSavedSettings(); + $apiKey = Arr::get($savedSettings, 'api_key', ''); + } + + if ($provider !== 'wordpress' && !$apiKey) { + return $this->sendError([ + 'message' => __('Please enter an API key.', 'fluent-crm'), + ], 422); + } + + $resolvedModel = $this->resolveModel($provider, $model); + if (!$resolvedModel) { + return $this->sendError([ + 'message' => __('AI model is not configured. Please set it in Settings.', 'fluent-crm'), + ], 422); + } + + $result = $this->callProviderApi($provider, $resolvedModel, $apiKey, 'Say "Connection successful" in exactly two words.', '', 15); + + if (is_wp_error($result)) { + return $this->sendError([ + 'message' => $result->get_error_message(), + ], 422); + } + + return $this->sendSuccess([ + 'message' => __('Connection successful! Your API key is valid.', 'fluent-crm'), + ]); + } + + public function generate(Request $request) + { + $action = sanitize_text_field($request->get('action', '')); + $content = sanitize_textarea_field($request->get('content', '')); + $tone = sanitize_text_field($request->get('tone', '')); + $customPrompt = sanitize_textarea_field($request->get('custom_prompt', '')); + + $validActions = ['rewrite', 'shorten', 'expand', 'fix_grammar', 'custom']; + + if (!in_array($action, $validActions, true)) { + return $this->sendError([ + 'message' => __('Invalid action specified.', 'fluent-crm'), + ], 422); + } + + if (empty($content) && $action !== 'custom') { + return $this->sendError([ + 'message' => __('No content provided to process.', 'fluent-crm'), + ], 422); + } + + if ($action === 'custom' && empty($content) && empty($customPrompt)) { + return $this->sendError([ + 'message' => __('Please provide a prompt or select some text.', 'fluent-crm'), + ], 422); + } + + $settings = $this->getSavedSettings(); + + if (Arr::get($settings, 'is_enabled') !== 'yes') { + return $this->sendError([ + 'message' => __('AI features are not enabled. Please configure AI in Settings.', 'fluent-crm'), + ], 422); + } + + $provider = Arr::get($settings, 'provider', ''); + $apiKey = Arr::get($settings, 'api_key', ''); + if ($provider !== 'wordpress' && !$apiKey) { + return $this->sendError([ + 'message' => __('AI API key is not configured. Please add it in Settings.', 'fluent-crm'), + ], 422); + } + + $model = Arr::get($settings, 'model', ''); + + $validProviders = array_keys($this->providerModels); + if (!$provider || !in_array($provider, $validProviders, true)) { + return $this->sendError([ + 'message' => __('AI provider is not configured. Please set it in Settings.', 'fluent-crm'), + ], 422); + } + + if (!$model) { + return $this->sendError([ + 'message' => __('AI model is not configured. Please set it in Settings.', 'fluent-crm'), + ], 422); + } + + $resolvedModel = $this->resolveModel($provider, $model); + if (!$resolvedModel) { + return $this->sendError([ + 'message' => __('AI model is not configured. Please set it in Settings.', 'fluent-crm'), + ], 422); + } + + $userPrompt = $this->buildUserPrompt($action, $content, $customPrompt); + + $result = $this->callProviderApi($provider, $resolvedModel, $apiKey, $userPrompt, $this->getSystemPrompt($tone, $settings), 30); + + if (is_wp_error($result)) { + return $this->sendError([ + 'message' => $result->get_error_message(), + ], 422); + } + + return $this->sendSuccess([ + 'content' => sanitize_textarea_field($result), + ]); + } + + /** + * Generate a complete email body from a user prompt for campaign editors. + */ + public function generateEmailBody(Request $request) + { + $prompt = sanitize_textarea_field($request->get('prompt', '')); + $tone = sanitize_text_field($request->get('tone', 'friendly')); + $audience = sanitize_text_field($request->get('audience', '')); + $length = sanitize_text_field($request->get('length', 'medium')); + $cta = sanitize_text_field($request->get('cta', '')); + $context = Helper::parseArrayOrJson($request->get('context', [])); + + if (!$prompt) { + return $this->sendError([ + 'message' => __('Please provide a prompt to generate the email body.', 'fluent-crm'), + ], 422); + } + + if (!in_array($tone, ['friendly', 'professional', 'casual', 'persuasive', 'educational'], true)) { + $tone = 'friendly'; + } + + if (!in_array($length, ['short', 'medium', 'long'], true)) { + $length = 'medium'; + } + + $settings = $this->getSavedSettings(); + $aiConfig = $this->validateAiGenerationConfig($settings); + + if (is_wp_error($aiConfig)) { + return $this->sendError([ + 'message' => $aiConfig->get_error_message(), + ], 422); + } + + $promptData = [ + 'prompt' => $prompt, + 'tone' => $tone, + 'audience' => $audience, + 'length' => $length, + 'cta' => $cta, + 'context' => $this->sanitizePromptContext($context), + ]; + + $result = $this->callProviderApi( + $aiConfig['provider'], + $aiConfig['model'], + $aiConfig['api_key'], + $this->buildEmailBodyUserPrompt($promptData), + $this->getEmailBodySystemPrompt($settings), + 45 + ); + + if (is_wp_error($result)) { + return $this->sendError([ + 'message' => $result->get_error_message(), + ], 422); + } + + $generated = $this->parseGeneratedEmailBody($result); + + /** + * Filter generated AI email body content before returning it to the editor. + * + * @param array $generated Generated subject suggestions, preheader, and body HTML. + * @param array $promptData Sanitized prompt inputs and editor context. + * @param string $result Raw AI provider response. + */ + $generated = apply_filters('fluent_crm/ai_email_body_generated_content', $generated, $promptData, $result); + + if (!is_array($generated)) { + $generated = [ + 'subject_suggestions' => [], + 'preview_text' => '', + 'email_body' => '', + ]; + } + + return $this->sendSuccess([ + 'email_body' => $this->sanitizeGeneratedEmailBody(Arr::get($generated, 'email_body', ''), Arr::get($promptData, 'context.output_format', '')), + 'subject_suggestions' => array_values(array_map('sanitize_text_field', Arr::get($generated, 'subject_suggestions', []))), + 'preview_text' => sanitize_text_field(Arr::get($generated, 'preview_text', '')), + 'provider' => $aiConfig['provider'], + 'model' => $aiConfig['model'], + ]); + } + + private function sanitizeGeneratedEmailBody($emailBody, $outputFormat) + { + $emailBody = (string) $emailBody; + + if ($outputFormat === 'gutenberg_blocks' && function_exists('filter_block_content')) { + return filter_block_content($emailBody); + } + + return wp_kses_post($emailBody); + } + + /** + * Generate or return a cached markdown summary for a contact profile. + */ + public function contactSummary(Request $request) + { + $subscriberId = intval($request->get('subscriber_id', 0)); + $generate = $request->get('generate') == 'yes'; + $regenerate = $request->get('regenerate') == 'yes'; + $locale = $this->getContactSummaryLocale(); + + if (!$subscriberId) { + return $this->sendError([ + 'message' => __('Invalid contact selected.', 'fluent-crm'), + ], 422); + } + + $subscriber = Subscriber::with(['tags', 'lists'])->find($subscriberId); + + if (!$subscriber) { + return $this->sendError([ + 'message' => __('Subscriber not found', 'fluent-crm'), + ], 404); + } + + $metaKey = '_ai_contact_summary'; + $cachedSummary = fluentcrm_get_subscriber_meta($subscriberId, $metaKey, []); + + if (!$regenerate && $this->isCachedContactSummaryForLocale($cachedSummary, $locale)) { + return $this->sendSuccess([ + 'summary' => $cachedSummary, + 'cached' => true, + ]); + } + + if (!$generate && !$regenerate) { + return $this->sendSuccess([ + 'summary' => [], + 'cached' => false, + ]); + } + + $settings = $this->getSavedSettings(); + + if (Arr::get($settings, 'is_enabled') !== 'yes') { + return $this->sendError([ + 'message' => __('AI features are not enabled. Please configure AI in Settings.', 'fluent-crm'), + ], 422); + } + + $provider = Arr::get($settings, 'provider', ''); + $apiKey = Arr::get($settings, 'api_key', ''); + if ($provider !== 'wordpress' && !$apiKey) { + return $this->sendError([ + 'message' => __('AI API key is not configured. Please add it in Settings.', 'fluent-crm'), + ], 422); + } + + $model = Arr::get($settings, 'model', ''); + + $validProviders = array_keys($this->providerModels); + if (!$provider || !in_array($provider, $validProviders, true)) { + return $this->sendError([ + 'message' => __('AI provider is not configured. Please set it in Settings.', 'fluent-crm'), + ], 422); + } + + if (!$model) { + return $this->sendError([ + 'message' => __('AI model is not configured. Please set it in Settings.', 'fluent-crm'), + ], 422); + } + + $resolvedModel = $this->resolveModel($provider, $model); + if (!$resolvedModel) { + return $this->sendError([ + 'message' => __('AI model is not configured. Please set it in Settings.', 'fluent-crm'), + ], 422); + } + + $context = $this->buildContactSummaryContext($subscriber, $locale); + $result = $this->callProviderApi( + $provider, + $resolvedModel, + $apiKey, + $this->buildContactSummaryPrompt($context, $locale), + $this->getContactSummarySystemPrompt($settings, $locale), + 45 + ); + + if (is_wp_error($result)) { + return $this->sendError([ + 'message' => $result->get_error_message(), + ], 422); + } + + $summary = [ + 'content' => sanitize_textarea_field($result), + 'generated_at' => fluentCrmTimestamp(), + 'provider' => $provider, + 'model' => $model, + 'locale' => $locale, + 'counts' => Arr::get($context, 'counts', []), + ]; + + fluentcrm_update_subscriber_meta($subscriberId, $metaKey, $summary); + + return $this->sendSuccess([ + 'summary' => $summary, + 'cached' => false, + ]); + } + + /** + * Resolve the WordPress site locale used for AI contact summaries. + * + * The contact summary must follow Settings > General > Site Language, not the + * current admin user's profile language. The fallback keeps cache comparison + * deterministic if WordPress returns an empty locale for any reason. + * + * @return string Sanitized WordPress locale, for example en_US or bn_BD. + */ + private function getContactSummaryLocale() + { + $locale = sanitize_text_field((string) get_locale()); + + return $locale ?: 'en_US'; + } + + /** + * Check whether a cached AI contact summary can be reused for the site locale. + * + * New summaries store their generation locale and are reusable only when it + * matches the current site locale. Older cached summaries did not store a + * locale and were generated from English-only prompts, so they are treated as + * valid only while the current site locale is English. + * + * @param array $summary Cached subscriber meta summary payload. + * @param string $locale Current sanitized WordPress site locale. + * + * @return bool True when the cached summary can be shown without regenerating. + */ + private function isCachedContactSummaryForLocale($summary, $locale) + { + if (!is_array($summary) || empty($summary['content'])) { + return false; + } + + $cachedLocale = sanitize_text_field(Arr::get($summary, 'locale', '')); + + if ($cachedLocale) { + return $cachedLocale === $locale; + } + + // Legacy cached summaries were generated from English-only prompts. + return $this->isEnglishLocale($locale); + } + + /** + * Determine whether a WordPress locale belongs to the English language family. + * + * Used only for legacy AI summary cache compatibility, where existing cached + * records have no explicit locale but were produced by English prompts. + * + * @param string $locale WordPress locale to inspect. + * + * @return bool True for locales beginning with en, such as en_US or en_GB. + */ + private function isEnglishLocale($locale) + { + return strtolower(substr((string) $locale, 0, 2)) === 'en'; + } + + private function getSystemPrompt($tone = '', $settings = []) + { + $prompt = 'You are an email copywriting assistant. Write like a real human — natural, conversational, and warm. ' + . 'Avoid AI-sounding patterns: no em dashes, no "I hope this email finds you well", no "In today\'s fast-paced world", no "leverage", no "streamline", no "I\'d be happy to". ' + . 'Use short sentences. Use simple words. Write the way people actually talk in emails. ' + . 'Return ONLY the improved text in markdown format. No explanations, preamble, or wrapping code blocks.' . "\n\n" + . 'You can use these smartcode placeholders to personalize the email: ' + . '{{contact.first_name}} (recipient first name), ' + . '{{contact.last_name}} (recipient last name), ' + . '{{contact.full_name}} (recipient full name), ' + . '{{contact.email}} (recipient email), ' + . '{{crm.business_name}} (sender business name), ' + . '{{crm.business_address}} (sender business address). ' + . 'Use these smartcodes where appropriate to make emails feel personal. Keep existing smartcodes in the text intact.'; + + if ($tone) { + $prompt .= ' Use a ' . strtolower($tone) . ' tone throughout.'; + } + + $customSystemPrompt = trim(Arr::get($settings, 'custom_prompt', '')); + if ($customSystemPrompt) { + $prompt .= "\n\nAdditional instructions: " . $customSystemPrompt; + } + + return $prompt; + } + + private function buildUserPrompt($action, $content, $customPrompt = '') + { + switch ($action) { + case 'rewrite': + return "Rewrite the following email text while keeping the same meaning:\n\n" . $content; + case 'shorten': + return "Make the following email text shorter and more concise:\n\n" . $content; + case 'expand': + return "Expand the following email text with more detail and engagement:\n\n" . $content; + case 'fix_grammar': + return "Fix grammar, spelling, and punctuation in the following text:\n\n" . $content; + case 'custom': + return $customPrompt . "\n\nText:\n" . $content; + default: + return $content; + } + } + + private function validateAiGenerationConfig($settings) + { + if (Arr::get($settings, 'is_enabled') !== 'yes') { + return new \WP_Error('ai_disabled', __('AI features are not enabled. Please configure AI in Settings.', 'fluent-crm')); + } + + $provider = Arr::get($settings, 'provider', ''); + $apiKey = Arr::get($settings, 'api_key', ''); + if ($provider !== 'wordpress' && !$apiKey) { + return new \WP_Error('missing_api_key', __('AI API key is not configured. Please add it in Settings.', 'fluent-crm')); + } + + $model = Arr::get($settings, 'model', ''); + + $validProviders = array_keys($this->providerModels); + if (!$provider || !in_array($provider, $validProviders, true)) { + return new \WP_Error('missing_provider', __('AI provider is not configured. Please set it in Settings.', 'fluent-crm')); + } + + if (!$model) { + return new \WP_Error('missing_model', __('AI model is not configured. Please set it in Settings.', 'fluent-crm')); + } + + $resolvedModel = $this->resolveModel($provider, $model); + if (!$resolvedModel) { + return new \WP_Error('missing_model', __('AI model is not configured. Please set it in Settings.', 'fluent-crm')); + } + + return [ + 'provider' => $provider, + 'model' => $resolvedModel, + 'api_key' => $apiKey, + ]; + } + + private function getEmailBodySystemPrompt($settings = []) + { + $prompt = 'You are an expert email marketing copywriter for FluentCRM users. ' + . 'Generate a complete email body that is ready to insert into an email editor. ' + . 'Use real, specific copy based only on the user prompt. Do not invent discounts, dates, guarantees, scarcity, purchase history, or personal facts. ' + . 'Use FluentCRM smartcodes sparingly when helpful, such as {{contact.first_name}}, {{contact.full_name}}, and {{crm.business_name}}. ' + . 'Match the requested output_format exactly: gutenberg_blocks must return valid WordPress block markup using paragraph, heading, list, and button blocks; classic_html must return clean rich HTML fragments; raw_html must return clean raw HTML fragments. ' + . 'For HTML outputs, use only these tags when needed: h2, h3, p, ul, ol, li, strong, em, a, br. Do not include html, head, body, style, script, table, img, or wrapper div tags. ' + . 'Return ONLY valid JSON with this exact shape: {"subject_suggestions":["..."],"preview_text":"...","email_body":"..."}. No markdown fences, no explanations.'; + + $customSystemPrompt = trim(Arr::get($settings, 'custom_prompt', '')); + if ($customSystemPrompt) { + $prompt .= "\n\nAdditional brand instructions: " . $customSystemPrompt; + } + + /** + * Filter the AI email body system prompt. + * + * @param string $prompt System prompt sent to the configured AI provider. + * @param array $settings Saved AI settings. + */ + return apply_filters('fluent_crm/ai_email_body_system_prompt', $prompt, $settings); + } + + private function buildEmailBodyUserPrompt($promptData) + { + $lengthMap = [ + 'short' => 'Short: about 2-3 short paragraphs or one heading plus a few bullets.', + 'medium' => 'Medium: about 4-6 short paragraphs or sections.', + 'long' => 'Long: a more detailed email with clear sections and supporting bullets.', + ]; + + $prompt = "Create a complete marketing email body from this brief.\n\n" + . 'Goal: ' . Arr::get($promptData, 'prompt') . "\n" + . 'Tone: ' . Arr::get($promptData, 'tone') . "\n" + . 'Length: ' . Arr::get($lengthMap, Arr::get($promptData, 'length'), $lengthMap['medium']) . "\n"; + + if ($audience = Arr::get($promptData, 'audience')) { + $prompt .= 'Audience: ' . $audience . "\n"; + } + + if ($cta = Arr::get($promptData, 'cta')) { + $prompt .= 'Primary CTA: ' . $cta . "\n"; + } + + $context = Arr::get($promptData, 'context', []); + if ($context) { + $prompt .= "\nEditor context:\n" . wp_json_encode($context, JSON_PRETTY_PRINT); + } + + $prompt .= "\n\nOutput rules:\n" . $this->getEmailBodyOutputRules(Arr::get($context, 'output_format', 'classic_html')); + + /** + * Filter the AI email body user prompt. + * + * @param string $prompt User prompt sent to the configured AI provider. + * @param array $promptData Sanitized prompt inputs and editor context. + */ + return apply_filters('fluent_crm/ai_email_body_user_prompt', $prompt, $promptData); + } + + private function getEmailBodyOutputRules($outputFormat) + { + if ($outputFormat === 'gutenberg_blocks') { + return 'Set email_body to WordPress Gutenberg block markup only. Example shape:

Headline

followed by

Copy

and
  • Point
. Use a button block for the primary CTA when a CTA exists.'; + } + + if ($outputFormat === 'raw_html') { + return 'Set email_body to raw HTML fragments suitable for a raw HTML editor. Use semantic HTML and include links for CTAs when appropriate. Do not include WordPress block comments.'; + } + + return 'Set email_body to clean rich HTML suitable for a classic WYSIWYG email editor. Do not include WordPress block comments.'; + } + + private function sanitizePromptContext($context) + { + if (!is_array($context)) { + return []; + } + + $allowed = ['design_template', 'editor_type', 'output_format', 'campaign_type', 'has_existing_body']; + $sanitized = []; + + foreach ($allowed as $key) { + if (isset($context[$key]) && is_scalar($context[$key])) { + $sanitized[$key] = sanitize_text_field((string) $context[$key]); + } + } + + return $sanitized; + } + + private function parseGeneratedEmailBody($result) + { + $decoded = json_decode(trim($result), true); + + if (!is_array($decoded) && preg_match('/\{.*\}/s', $result, $matches)) { + $decoded = json_decode($matches[0], true); + } + + if (!is_array($decoded)) { + return [ + 'subject_suggestions' => [], + 'preview_text' => '', + 'email_body' => $result, + ]; + } + + $subjects = Arr::get($decoded, 'subject_suggestions', []); + if (!is_array($subjects)) { + $subjects = $subjects ? [$subjects] : []; + } + + return [ + 'subject_suggestions' => array_slice($subjects, 0, 5), + 'preview_text' => Arr::get($decoded, 'preview_text', ''), + 'email_body' => Arr::get($decoded, 'email_body', ''), + ]; + } + + private function getContactSummarySystemPrompt($settings = [], $locale = '') + { + $prompt = 'You are a CRM assistant creating an internal contact summary for sales and support teams. ' + . 'Use only the supplied contact data. Do not invent purchases, courses, tickets, emails, dates, or recommendations. ' + . 'If a section has no data, say it is not available. ' + . 'Do not repeat basic contact details like name, email, status, or created date unless directly relevant to a decision. ' + . 'Return markdown only in the requested WordPress site language. Use decision-focused headings, concise bullets, and a final section equivalent to "Suggested next action" in that language.'; + + if ($locale) { + $prompt .= "\n\nRequested WordPress site locale: " . $locale . ". Write the entire summary in this site language."; + } + + $customSystemPrompt = trim(Arr::get($settings, 'custom_prompt', '')); + if ($customSystemPrompt) { + $prompt .= "\n\nAdditional business context: " . $customSystemPrompt; + } + + /** + * Filter the AI contact summary system prompt. + * + * @param string $prompt System prompt sent to the configured AI provider. + * @param array $settings Saved AI settings. + * @param string $locale WordPress site locale requested for the summary output. + */ + return apply_filters('fluent_crm/ai_contact_summary_system_prompt', $prompt, $settings, $locale); + } + + private function buildContactSummaryPrompt($context, $locale = '') + { + $languageInstruction = $locale + ? 'Write the entire summary in the WordPress site language for locale ' . $locale . '. ' + : 'Write the entire summary in the WordPress site language. '; + + $prompt = "Summarize this contact for a CRM user who already sees the basic profile on screen. Focus on what they should understand or do next. Include email engagement, purchase history, course or membership history when present, support ticket history when present, risk signals, opportunities, and suggested next action. Do not create a Contact Details section. " . $languageInstruction . "Translate the explanatory prose and headings, but keep contact names, company names, product names, email subjects, URLs, IDs, tag names, list names, order numbers, and other source data values unchanged.\n\nContact context:\n" . wp_json_encode($context, JSON_PRETTY_PRINT); + + /** + * Filter the AI contact summary user prompt. + * + * @param string $prompt User prompt sent to the configured AI provider. + * @param array $context Structured contact context used for summary generation. + * @param string $locale WordPress site locale requested for the summary output. + */ + return apply_filters('fluent_crm/ai_contact_summary_user_prompt', $prompt, $context, $locale); + } + + private function buildContactSummaryContext($subscriber, $locale = '') + { + $context = [ + 'contact' => [ + 'id' => (int) $subscriber->id, + 'name' => $subscriber->full_name, + 'email' => $subscriber->email, + 'status' => $subscriber->status, + 'created_at' => $subscriber->created_at, + 'lists' => $this->pluckTitles($subscriber->lists), + 'tags' => $this->pluckTitles($subscriber->tags), + ], + 'language' => [ + 'site_locale' => $locale, + ], + 'emails' => $this->getEmailSummaryContext($subscriber->id), + 'purchase_history' => $this->getPurchaseSummaryContext($subscriber), + 'support_tickets' => $this->getSupportTicketSummaryContext($subscriber), + 'counts' => [], + ]; + + $context['counts'] = [ + 'emails' => Arr::get($context, 'emails.total', 0), + 'purchase_providers' => count(Arr::get($context, 'purchase_history.providers', [])), + 'support_providers' => count(Arr::get($context, 'support_tickets.providers', [])), + ]; + + return $context; + } + + private function getEmailSummaryContext($subscriberId) + { + $emails = CampaignEmail::where('subscriber_id', $subscriberId) + ->orderBy('id', 'DESC') + ->limit(20) + ->get(); + + $items = []; + $opened = 0; + $clicked = 0; + + foreach ($emails as $email) { + if ($email->is_open) { + $opened++; + } + + if ($email->click_counter) { + $clicked++; + } + + $items[] = [ + 'subject' => wp_strip_all_tags($email->email_subject), + 'status' => $email->status, + 'sent_at' => $email->scheduled_at ?: $email->created_at, + 'opened' => (bool) $email->is_open, + 'click_counter' => intval($email->click_counter), + ]; + } + + return [ + 'total' => CampaignEmail::where('subscriber_id', $subscriberId)->count(), + 'recent_count' => count($items), + 'opened' => $opened, + 'clicked' => $clicked, + 'recent' => $items, + ]; + } + + private function getPurchaseSummaryContext($subscriber) + { + $providers = []; + foreach (Helper::getPurchaseHistoryProviders() as $providerKey => $provider) { + $providerKey = sanitize_key($providerKey); + if (!$providerKey) { + continue; + } + + $data = apply_filters('fluent_crm/purchase_history_' . $providerKey, [ + 'orders' => [], + 'total' => 0, + ], $subscriber); + + $providers[$providerKey] = [ + 'title' => sanitize_text_field(Arr::get($provider, 'title', $providerKey)), + 'total' => intval(Arr::get($data, 'total', 0)), + 'orders' => $this->normalizeSummaryRows(Arr::get($data, 'orders', []), 10), + ]; + } + + return [ + 'providers' => $providers, + ]; + } + + private function getSupportTicketSummaryContext($subscriber) + { + $providers = []; + $supportProviders = apply_filters('fluentcrm-support_tickets_providers', []); + + foreach ($supportProviders as $providerKey => $provider) { + $providerKey = sanitize_key($providerKey); + if (!$providerKey) { + continue; + } + + $data = apply_filters('fluentcrm-get_support_tickets_' . $providerKey, [ + 'data' => [], + 'total' => 0, + ], $subscriber); + + $providers[$providerKey] = [ + 'title' => sanitize_text_field(Arr::get($provider, 'title', $providerKey)), + 'total' => intval(Arr::get($data, 'total', 0)), + 'tickets' => $this->normalizeSummaryRows(Arr::get($data, 'data', []), 10), + ]; + } + + return [ + 'providers' => $providers, + ]; + } + + private function normalizeSummaryRows($rows, $limit = 10) + { + if (!$rows) { + return []; + } + + if (is_object($rows) && method_exists($rows, 'toArray')) { + $rows = $rows->toArray(); + } + + if (!is_array($rows)) { + return []; + } + + $normalized = []; + foreach (array_slice($rows, 0, $limit) as $row) { + $row = (array) $row; + $item = []; + + foreach ($row as $key => $value) { + if (is_scalar($value) || $value === null) { + $item[sanitize_key($key)] = sanitize_text_field(wp_strip_all_tags((string) $value)); + } + } + + if ($item) { + $normalized[] = $item; + } + } + + return $normalized; + } + + private function pluckTitles($items) + { + $titles = []; + foreach ($items as $item) { + if (!empty($item->title)) { + $titles[] = $item->title; + } + } + + return $titles; + } + + private function callProviderApi($provider, $model, $apiKey, $userPrompt, $systemPrompt = '', $timeout = 30) + { + switch ($provider) { + case 'open_ai': + return $this->callOpenAi($model, $apiKey, $userPrompt, $systemPrompt, $timeout); + case 'claude': + return $this->callClaude($model, $apiKey, $userPrompt, $systemPrompt, $timeout); + case 'gemini': + return $this->callGemini($model, $apiKey, $userPrompt, $systemPrompt, $timeout); + case 'wordpress': + return $this->callWordPress($model, $userPrompt, $systemPrompt, $timeout); + default: + return new \WP_Error('invalid_provider', __('Invalid AI provider.', 'fluent-crm')); + } + } + + private function callWordPress($model, $userPrompt, $systemPrompt, $timeout) + { + $filtered = apply_filters('fluent_crm/wordpress_ai_generate', null, $userPrompt, $systemPrompt, $model, $timeout); + if ($filtered !== null) { + return $filtered; + } + + if (function_exists('wp_ai_client_prompt')) { + $prompt = wp_ai_client_prompt($userPrompt); + if ($systemPrompt) { + $prompt->using_system_instruction($systemPrompt); + } + if ($prompt->is_supported_for_text_generation()) { + $result = $prompt->generate_text(); + if (is_wp_error($result)) { + return $result; + } + if (empty($result)) { + return new \WP_Error('empty_response', __('No content generated by WordPress AI client. Please try again.', 'fluent-crm')); + } + return $result; + } else { + return new \WP_Error('not_supported', __('WordPress AI client is not configured or supported on this site.', 'fluent-crm')); + } + } + + return new \WP_Error( + 'wordpress_ai_not_supported', + __('WordPress AI Client functions are not available on this WordPress installation. Please ensure you have an AI provider plugin or WordPress AI Core features enabled.', 'fluent-crm') + ); + } + + private function callOpenAi($model, $apiKey, $userPrompt, $systemPrompt, $timeout) + { + $messages = []; + if ($systemPrompt) { + $messages[] = ['role' => 'system', 'content' => $systemPrompt]; + } + $messages[] = ['role' => 'user', 'content' => $userPrompt]; + + $response = wp_remote_post('https://api.openai.com/v1/chat/completions', [ + 'timeout' => $timeout, + 'headers' => [ + 'Authorization' => 'Bearer ' . $apiKey, + 'Content-Type' => 'application/json', + ], + 'body' => wp_json_encode([ + 'model' => $model, + 'messages' => $messages, + 'max_completion_tokens' => 2048, + ]), + ]); + + if (is_wp_error($response)) { + return new \WP_Error('api_error', __('Failed to connect to OpenAI: ', 'fluent-crm') . $response->get_error_message()); + } + + $code = wp_remote_retrieve_response_code($response); + $body = json_decode(wp_remote_retrieve_body($response), true); + + if ($code !== 200) { + $errorMessage = Arr::get($body, 'error.message', __('Unknown error from OpenAI.', 'fluent-crm')); + return new \WP_Error('api_error', $errorMessage); + } + + $content = Arr::get($body, 'choices.0.message.content', ''); + if (empty($content)) { + return new \WP_Error('empty_response', __('No content generated. Please try again.', 'fluent-crm')); + } + + return $content; + } + + private function callClaude($model, $apiKey, $userPrompt, $systemPrompt, $timeout) + { + $data = [ + 'model' => $model, + 'max_tokens' => 2048, + 'messages' => [ + ['role' => 'user', 'content' => $userPrompt], + ], + ]; + + if ($systemPrompt) { + $data['system'] = $systemPrompt; + } + + $response = wp_remote_post('https://api.anthropic.com/v1/messages', [ + 'timeout' => $timeout, + 'headers' => [ + 'x-api-key' => $apiKey, + 'anthropic-version' => '2023-06-01', + 'Content-Type' => 'application/json', + ], + 'body' => wp_json_encode($data), + ]); + + if (is_wp_error($response)) { + return new \WP_Error('api_error', __('Failed to connect to Claude: ', 'fluent-crm') . $response->get_error_message()); + } + + $code = wp_remote_retrieve_response_code($response); + $body = json_decode(wp_remote_retrieve_body($response), true); + + if ($code !== 200) { + $errorMessage = Arr::get($body, 'error.message', __('Unknown error from Claude.', 'fluent-crm')); + return new \WP_Error('api_error', $errorMessage); + } + + $content = Arr::get($body, 'content.0.text', ''); + if (empty($content)) { + return new \WP_Error('empty_response', __('No content generated. Please try again.', 'fluent-crm')); + } + + return $content; + } + + private function callGemini($model, $apiKey, $userPrompt, $systemPrompt, $timeout) + { + $url = 'https://generativelanguage.googleapis.com/v1beta/models/' . $model . ':generateContent'; + + $data = [ + 'contents' => [ + ['parts' => [['text' => $userPrompt]]], + ], + 'generationConfig' => [ + 'maxOutputTokens' => 2048, + ], + ]; + + if ($systemPrompt) { + $data['system_instruction'] = ['parts' => [['text' => $systemPrompt]]]; + } + + $response = wp_remote_post($url, [ + 'timeout' => $timeout, + 'headers' => [ + 'Content-Type' => 'application/json', + 'x-goog-api-key' => $apiKey, + ], + 'body' => wp_json_encode($data), + ]); + + if (is_wp_error($response)) { + return new \WP_Error('api_error', __('Failed to connect to Gemini: ', 'fluent-crm') . $response->get_error_message()); + } + + $code = wp_remote_retrieve_response_code($response); + $body = json_decode(wp_remote_retrieve_body($response), true); + + if ($code !== 200) { + $errorMessage = Arr::get($body, 'error.message', __('Unknown error from Gemini.', 'fluent-crm')); + return new \WP_Error('api_error', $errorMessage); + } + + $content = Arr::get($body, 'candidates.0.content.parts.0.text', ''); + if (empty($content)) { + return new \WP_Error('empty_response', __('No content generated. Please try again.', 'fluent-crm')); + } + + return $content; + } + + private function getSavedSettings() + { + $defaults = [ + 'is_enabled' => 'no', + 'provider' => '', + 'api_key' => '', + 'model' => 'auto', + 'custom_prompt' => '', + ]; + + $settings = array_merge( + $this->getSavedPreferences(), + $this->getSavedCredentials() + ); + + if (!is_array($settings)) { + return $defaults; + } + + return wp_parse_args($settings, $defaults); + } + + private function getSavedCredentials() + { + $credentials = get_option($this->credentialsOptionKey, []); + + if (!is_array($credentials)) { + $credentials = []; + } + + $provider = $this->normalizeProvider(Arr::get($credentials, 'provider', '')); + $model = sanitize_text_field(Arr::get($credentials, 'model', 'auto')); + + return [ + 'provider' => $provider, + 'model' => $model ?: 'auto', + 'api_key' => sanitize_text_field(Arr::get($credentials, 'api_key', '')), + 'created_by' => sanitize_text_field(Arr::get($credentials, 'created_by', '')), + ]; + } + + private function getSavedPreferences() + { + $preferences = fluentcrm_get_option($this->writingSettingsOptionKey, []); + + if (!is_array($preferences)) { + $preferences = []; + } + + return [ + 'is_enabled' => sanitize_text_field(Arr::get($preferences, 'is_enabled', 'no')) === 'yes' ? 'yes' : 'no', + 'custom_prompt' => sanitize_textarea_field(Arr::get($preferences, 'custom_prompt', '')), + ]; + } + + private function normalizeProvider($provider) + { + $provider = sanitize_key($provider); + + return $provider === 'openai' ? 'open_ai' : $provider; + } + + private function resolveModel($provider, $model) + { + $model = $model ?: 'auto'; + + if ($model !== 'auto') { + return $model; + } + + return Arr::get($this->autoProviderModels, $provider, ''); + } + + private function formatModelOptions($provider) + { + $models = []; + + foreach (Arr::get($this->providerModels, $provider, []) as $model) { + $models[] = [ + 'value' => $model, + 'label' => $model === 'auto' ? __('Auto', 'fluent-crm') : $model, + ]; + } + + return $models; + } +} diff --git a/wp-content/plugins/fluent-crm/app/Http/Controllers/CampaignAnalyticsController.php b/wp-content/plugins/fluent-crm/app/Http/Controllers/CampaignAnalyticsController.php new file mode 100644 index 0000000..7f33837 --- /dev/null +++ b/wp-content/plugins/fluent-crm/app/Http/Controllers/CampaignAnalyticsController.php @@ -0,0 +1,489 @@ +findOrFail($campaignId); + $clickStatus = $campaign->settings['click_tracker'] ?? ''; + $openStatus = $campaign->settings['open_tracker'] ?? ''; + + if ($clickStatus === '') { + $clickStatus = fluentcrmTrackClicking(); + } + + if ($openStatus === '') { + $openStatus = fluentcrmTrackEmailOpen(); + } + + $links = array_values($campaignUrlMetric->getLinksReport($campaign)); + + return $this->sendSuccess([ + 'links' => $links, + 'click_status' => $clickStatus, + 'open_status' => $openStatus + ]); + } + + public function getRevenueReport(Request $request, $campaignId) + { + $limit = intval($request->get('per_page', 10)); + $offset = (intval($request->get('page', 1)) - 1) * $limit; + + $sources = $this->getActiveRevenueSources(); + $multiSource = count($sources) > 1; + + if (empty($sources)) { + return [ + 'orders' => [], + 'labels' => $this->getRevenueLabels(false), + 'total' => 0 + ]; + } + + // Build a single newest-first index across every active commerce source so + // pagination spans them all. Within each source, ids stay in DB-newest order. + $index = []; + foreach ($sources as $source) { + foreach ($this->getAttributedOrderIds($source, $campaignId) as $orderId) { + $index[] = ['source' => $source, 'order_id' => (int) $orderId]; + } + } + + $totalOrders = count($index); + $pageEntries = array_slice($index, $offset, $limit); + + $orders = []; + foreach ($pageEntries as $entry) { + $row = $this->formatRevenueRow($entry['source'], $entry['order_id'], $multiSource); + if ($row) { + $orders[] = $row; + } + } + + return [ + 'orders' => $orders, + 'labels' => $this->getRevenueLabels($multiSource), + 'total' => $totalOrders + ]; + } + + public function getRevenueReSyncReport(Request $request, $campaignId) + { + $sources = $this->getActiveRevenueSources(); + if (empty($sources)) { + return [ + 'message' => __('No revenue found for this campaign', 'fluent-crm') + ]; + } + + $revenueData = ['orderIds' => []]; + $primaryCurrency = null; + + foreach ($sources as $source) { + $sourceData = $this->reSyncSourceRevenue($source, $campaignId); + foreach ($sourceData['orderIds'] as $oid) { + if (!in_array($oid, $revenueData['orderIds'])) { + $revenueData['orderIds'][] = $oid; + } + } + foreach ($sourceData['totals'] as $currency => $cents) { + if (!isset($revenueData[$currency])) { + $revenueData[$currency] = 0; + if ($primaryCurrency === null) { + $primaryCurrency = $currency; + } + } + $revenueData[$currency] += $cents; + } + } + + if (empty($revenueData['orderIds'])) { + return [ + 'message' => __('No order found to re-sync', 'fluent-crm') + ]; + } + + fluentcrm_update_campaign_meta($campaignId, '_campaign_revenue', $revenueData); + + $primaryTotal = $primaryCurrency ? $revenueData[$primaryCurrency] : 0; + + return [ + 'message' => __('Revenue has been re-synced successfully', 'fluent-crm'), + 'total' => number_format($primaryTotal / 100, 2) + ]; + } + + /** + * Active commerce sources that participate in campaign revenue attribution. + * Order matters: it determines display precedence within the merged report. + */ + protected function getActiveRevenueSources() + { + $sources = []; + if (defined('WC_PLUGIN_FILE')) { + $sources[] = 'woo'; + } + if (Helper::isEdd3()) { + $sources[] = 'edd'; + } + if (defined('FLUENTCART_VERSION')) { + $sources[] = 'fct'; + } + return $sources; + } + + /** + * Lightweight index query — returns just order IDs attributed to this campaign, + * newest-first per source. Used both for paginated report rendering and re-sync. + */ + protected function getAttributedOrderIds($source, $campaignId) + { + if ($source === 'woo') { + if (Helper::isWooHposEnabled()) { + return fluentCrmDb()->table('wc_orders_meta') + ->where('meta_key', '_fc_cid') + ->where('meta_value', $campaignId) + ->orderBy('id', 'DESC') + ->get() + ->pluck('order_id') + ->map(function ($orderId) { + return intval($orderId); + }) + ->all(); + } + return fluentCrmDb()->table('postmeta') + ->where('meta_key', '_fc_cid') + ->where('meta_value', $campaignId) + ->orderBy('meta_id', 'DESC') + ->get() + ->pluck('post_id') + ->map(function ($orderId) { + return intval($orderId); + }) + ->all(); + } + + if ($source === 'edd') { + /* + * EDD 3 writes order attribution meta to edd_ordermeta via the + * order meta API. Do not read legacy postmeta/edd_payment records. + */ + return fluentCrmDb()->table('edd_ordermeta') + ->where('meta_key', '_fc_cid') + ->where('meta_value', $campaignId) + ->orderBy('meta_id', 'DESC') + ->get() + ->pluck('edd_order_id') + ->map(function ($orderId) { + return intval($orderId); + }) + ->all(); + } + + if ($source === 'fct') { + return fluentCrmDb()->table('fct_order_meta') + ->where('meta_key', '_fc_cid') + ->where('meta_value', $campaignId) + ->orderBy('id', 'DESC') + ->get() + ->pluck('order_id') + ->map(function ($orderId) { + return intval($orderId); + }) + ->all(); + } + + return []; + } + + /** + * Sum NET revenue per currency for one source — i.e. only orders in a successful + * (paid/completed) status, with refunded amounts subtracted. Returns + * `['orderIds' => [int...], 'totals' => ['usd' => cents, ...]]`. + * Orders that net to zero or below (fully refunded, cancelled, pending) are skipped + * so they don't pollute the order list with non-revenue rows. + */ + protected function reSyncSourceRevenue($source, $campaignId) + { + $result = ['orderIds' => [], 'totals' => []]; + $orderIds = $this->getAttributedOrderIds($source, $campaignId); + if (!$orderIds) { + return $result; + } + + if ($source === 'woo') { + $paidStatuses = function_exists('wc_get_is_paid_statuses') ? wc_get_is_paid_statuses() : ['processing', 'completed']; + $currency = strtolower(get_woocommerce_currency()); + foreach ($orderIds as $orderId) { + $order = wc_get_order($orderId); + if (!$order || !$order->get_id()) { + continue; + } + if (!in_array($order->get_status(), $paidStatuses, true)) { + continue; + } + $netCents = intval(((float) $order->get_total() - (float) $order->get_total_refunded()) * 100); + if ($netCents <= 0) { + continue; + } + $result['orderIds'][] = (int) $order->get_id(); + $result['totals'][$currency] = ($result['totals'][$currency] ?? 0) + $netCents; + } + return $result; + } + + if ($source === 'edd') { + // EDD 3 keeps canonical status and refund data in order tables. + $completeStatuses = ['complete', 'completed', 'partially_refunded']; + foreach ($orderIds as $orderId) { + $payment = new \EDD_Payment($orderId); + if (!$payment || !$payment->ID) { + continue; + } + if (!in_array($payment->status, $completeStatuses, true)) { + continue; + } + $netTotal = function_exists('edd_get_order_total') + ? edd_get_order_total($payment->ID) + : $payment->total; + $netCents = intval(((float) $netTotal) * 100); + if ($netCents <= 0) { + continue; + } + $currency = strtolower(edd_get_payment_currency_code($payment->ID) ?: 'usd'); + $result['orderIds'][] = (int) $payment->ID; + $result['totals'][$currency] = ($result['totals'][$currency] ?? 0) + $netCents; + } + return $result; + } + + if ($source === 'fct') { + // Canonical "successful" set: paid, partially_paid, partially_refunded. + // Net revenue subtracts total_refund below so partial refunds still contribute. + $successStatuses = \FluentCart\App\Helpers\Status::getOrderPaymentSuccessStatuses(); + $orders = \FluentCart\App\Models\Order::query() + ->whereIn('id', $orderIds) + ->whereIn('payment_status', $successStatuses) + ->get(); + foreach ($orders as $order) { + $netCents = (int) $order->total_amount - (int) ($order->total_refund ?? 0); + if ($netCents <= 0) { + continue; + } + $currency = strtolower($order->currency ?: 'usd'); + $result['orderIds'][] = (int) $order->id; + $result['totals'][$currency] = ($result['totals'][$currency] ?? 0) + $netCents; + } + return $result; + } + + return $result; + } + + /** + * Render a single order row for the merged revenue table. The `source` key + * is added when more than one commerce platform is contributing data. + */ + protected function formatRevenueRow($source, $orderId, $multiSource) + { + $row = null; + if ($source === 'woo') { + $row = $this->formatWooOrderRow($orderId); + } else if ($source === 'edd') { + $row = $this->formatEddOrderRow($orderId); + } else if ($source === 'fct') { + $row = $this->formatFluentCartOrderRow($orderId); + } + + if (!$row) { + return null; + } + + if ($multiSource) { + $row = ['source' => $this->getSourceLabel($source)] + $row; + } + + return $row; + } + + protected function getSourceLabel($source) + { + $labels = [ + 'woo' => 'WooCommerce', + 'edd' => 'EDD', + 'fct' => 'FluentCart', + ]; + return $labels[$source] ?? $source; + } + + protected function getRevenueLabels($multiSource) + { + $labels = [ + 'order' => '#', + 'title' => __('Customer', 'fluent-crm'), + 'status' => __('Status', 'fluent-crm'), + 'date' => __('Date', 'fluent-crm'), + 'total' => __('Total', 'fluent-crm'), + 'action' => __('View', 'fluent-crm'), + ]; + if ($multiSource) { + $labels = ['source' => __('Source', 'fluent-crm')] + $labels; + } + return $labels; + } + + protected function formatWooOrderRow($orderId) + { + $order = wc_get_order($orderId); + if (!$order || !$order->get_id()) { + return null; + } + + /* translators: 1: billing first name, 2: billing last name */ + $buyer = trim(sprintf(_x('%1$s %2$s', 'full name', 'fluent-crm'), $order->get_billing_first_name(), $order->get_billing_last_name())); + + $order_timestamp = $order->get_date_created() ? $order->get_date_created()->getTimestamp() : ''; + + if (!$order_timestamp) { + $show_date = '–'; + } else if ($order_timestamp > strtotime('-1 day', time()) && $order_timestamp <= time()) { + $show_date = sprintf( + /* translators: %s: human-readable time difference */ + _x('%s ago', '%s = human-readable time difference', 'fluent-crm'), + human_time_diff($order->get_date_created()->getTimestamp(), time()) + ); + } else { + /** + * Determine the date format for displaying the order creation date in the WooCommerce admin in FluentCRM. + * + * @param string The date format to be used. Default is 'M j, Y'. + * @param string The context for the date format. Default is 'woocommerce'. + * @since 2.2.0 + */ + $show_date = $order->get_date_created()->date_i18n(apply_filters('woocommerce_admin_order_date_format', __('M j, Y', 'fluent-crm'))); + } + + $editUrl = admin_url('post.php?post=' . absint($order->get_id()) . '&action=edit'); + + return [ + 'order' => '#' . esc_html($order->get_order_number()), + 'title' => '' . esc_html($buyer) . '', + 'status' => wc_get_order_status_name($order->get_status()), + 'date' => $show_date, + 'total' => $order->get_formatted_order_total(), + 'action' => '' . esc_html__('View', 'fluent-crm') . '', + ]; + } + + protected function formatEddOrderRow($orderId) + { + $payment = new \EDD_Payment($orderId); + if (!$payment || !$payment->ID) { + return null; + } + + $orderActionHtml = '' . esc_html__('View', 'fluent-crm') . ''; + $amount = !empty($payment->total) ? $payment->total : 0; + $customer_id = edd_get_payment_customer_id($payment->ID); + + if (!empty($customer_id)) { + $customer = new \EDD_Customer($customer_id); + $customerName = '' . esc_html($customer->name) . ''; + } else { + $email = edd_get_payment_user_email($payment->ID); + $customerName = '' . esc_html__('(customer missing)', 'fluent-crm') . ''; + } + + return [ + 'order' => '#' . $payment->number, + 'title' => $customerName, + 'status' => $payment->status_nicename, + 'date' => date_i18n(get_option('date_format'), strtotime($payment->date)), + 'total' => edd_currency_filter(edd_format_amount($amount), edd_get_payment_currency_code($payment->ID)), + 'action' => $orderActionHtml, + ]; + } + + protected function formatFluentCartOrderRow($orderId) + { + $order = \FluentCart\App\Models\Order::with('customer')->find($orderId); + if (!$order) { + return null; + } + + $customerName = ''; + if ($order->customer) { + $customerName = trim($order->customer->first_name . ' ' . $order->customer->last_name); + if (!$customerName) { + $customerName = $order->customer->email; + } + } + + $orderUrl = admin_url('admin.php?page=fluent-cart#/orders/' . $order->id . '/view'); + + return [ + 'order' => '#' . ($order->invoice_no ?: $order->id), + 'title' => '' . esc_html($customerName) . '', + 'status' => esc_html(\FluentCrm\App\Services\Helper::getStatusText($order->status)), + 'date' => date_i18n(get_option('date_format'), strtotime($order->created_at)), + 'total' => \FluentCart\App\Helpers\Helper::toDecimal($order->total_amount, true, $order->currency), + 'action' => '' . esc_html__('View', 'fluent-crm') . '', + ]; + } + + public function getUnsubscribers(Request $request, $campaignId) + { + $unsubscribes = CampaignUrlMetric::with('subscriber') + ->where('campaign_id', $campaignId) + ->where('type', 'unsubscribe') + ->paginate(); + + foreach ($unsubscribes as $unsubscribe) { + $unsubscribe->subscriber->reason = $unsubscribe->subscriber->unsubscribeReason(); + } + + return [ + 'unsubscribes' => $unsubscribes + ]; + } + + public function getSegmentedContacts(Request $request, $campaignId) + { + $campaign = Campaign::findOrFail($campaignId); + $contactsModel = $campaign->getSubscribersModel(); + + $search = $request->getSafe('search', 'sanitize_text_field'); + + if ($search) { + $contactsModel->searchBy($search); + } + + if ($orderBy = $request->getSafe('sort_by', 'sanitize_sql_orderby', 'id')) { + $orderType = $request->getSafe('sort_type', 'sanitize_sql_orderby', 'desc'); + $contactsModel->orderBy($orderBy, $orderType); + } + + $contacts = $contactsModel->with(['lists', 'tags'])->paginate(); + + return [ + 'subscribers' => $contacts + ]; + } +} diff --git a/wp-content/plugins/fluent-crm/app/Http/Controllers/CampaignController.php b/wp-content/plugins/fluent-crm/app/Http/Controllers/CampaignController.php new file mode 100644 index 0000000..513c369 --- /dev/null +++ b/wp-content/plugins/fluent-crm/app/Http/Controllers/CampaignController.php @@ -0,0 +1,1810 @@ +get('searchBy', '')); + $status = $request->get('statuses', []); + $status = is_array($status) ? array_map('sanitize_key', $status) : []; + + $order = strtoupper($request->get('sort_type', '')); + $order = in_array($order, ['ASC', 'DESC'], true) ? $order : 'DESC'; + + $orderBy = sanitize_key($request->get('sort_by', '')); + // Re-key `with` to a flat, integer-indexed list and sanitize each value. + // Legitimate callers always send a plain list of names (e.g. with[]=stats); + // discarding any caller-supplied string keys closes the relation-name + // injection (a request shaped like with[]=stats) without restricting + // which names are allowed, so no existing core/add-on caller is affected. + $with = array_values(array_map('sanitize_key', (array) $request->get('with', []))); + + $labels = $request->get('labels', []); + $labels = is_array($labels) ? array_map('intval', $labels) : []; + $labels = array_filter($labels); // labels are id + + if (empty($orderBy)) { + $orderBy = 'created_at'; + } + + $campaignQuery = Campaign::when($status, function ($query) use ($status) { + return $query->whereIn('status', $status); + })->when($search, function ($query) use ($search) { + return $query->where('title', 'LIKE', "%$search%"); + }) + ->orderBy($orderBy, ($order == 'ASC') ? 'ASC' : 'DESC'); + + if (!empty($labels)) { + $campaignQuery->whereHas('labelsTerm', function ($query) use ($labels) { + $query->whereIn('term_id', $labels); + }); + } + + $campaigns = $campaignQuery->paginate(); + if (in_array('stats', $with)) { + $campaignIds = $campaigns->pluck('id')->toArray(); + + if ($campaignIds) { + // Batch email stats in a single GROUP BY query + $emailStats = fluentCrmDb()->table('fc_campaign_emails') + ->select('campaign_id') + ->selectRaw('COUNT(*) as total') + ->selectRaw("SUM(CASE WHEN status = 'sent' THEN 1 ELSE 0 END) as sent") + ->selectRaw("SUM(CASE WHEN is_open = 1 THEN 1 ELSE 0 END) as views") + ->selectRaw("SUM(CASE WHEN click_counter IS NOT NULL THEN 1 ELSE 0 END) as clicks") + ->whereIn('campaign_id', $campaignIds) + ->groupBy('campaign_id') + ->get() + ->keyBy('campaign_id'); + + // Batch unsubscribe counts + $unsubCounts = fluentCrmDb()->table('fc_campaign_url_metrics') + ->select('campaign_id') + ->selectRaw('COUNT(DISTINCT subscriber_id) as total') + ->where('type', 'unsubscribe') + ->whereIn('campaign_id', $campaignIds) + ->groupBy('campaign_id') + ->get() + ->keyBy('campaign_id'); + + // Batch meta (next_step, revenue, anonymous tracking) + $metaItems = fluentCrmDb()->table('fc_meta') + ->whereIn('object_id', $campaignIds) + ->where('object_type', 'FluentCrm\App\Models\Campaign') + ->whereIn('key', ['_next_config_step', '_campaign_revenue', '_ano_open_count', '_ano_url_clicks']) + ->get(); + + $metaMap = []; + foreach ($metaItems as $meta) { + $metaMap[$meta->object_id][$meta->key] = $meta->value; + } + + // Batch labels: get relations then load all labels at once + $labelRelations = fluentCrmDb()->table('fc_term_relations') + ->whereIn('object_id', $campaignIds) + ->where('object_type', 'FluentCrm\App\Models\Campaign') + ->get(); + + $labelIdsByCampaign = []; + $allLabelIds = []; + foreach ($labelRelations as $rel) { + $labelIdsByCampaign[$rel->object_id][] = $rel->term_id; + $allLabelIds[] = $rel->term_id; + } + + $allLabels = []; + if ($allLabelIds) { + $allLabels = fluentCrmDb()->table('fc_terms') + ->whereIn('id', array_unique($allLabelIds)) + ->where('taxonomy_name', 'global_label') + ->get() + ->keyBy('id'); + } + + foreach ($campaigns as $campaign) { + $stat = $emailStats[$campaign->id] ?? null; + $unsub = $unsubCounts[$campaign->id] ?? null; + $campMeta = $metaMap[$campaign->id] ?? []; + + // Views: use anonymous count if open tracking is anonymous + if ($campaign->getOpenTrackingStatus(false) === 'anonymous') { + $views = (int) ($campMeta['_ano_open_count'] ?? 0); + } else { + $views = $stat ? (int) $stat->views : 0; + } + + // Clicks: use anonymous aggregated clicks if click tracking is anonymous + if ($campaign->getClickTrackingStatus(false) === 'anonymous') { + $clickData = $campMeta['_ano_url_clicks'] ?? null; + $clicks = 0; + if ($clickData) { + $clickItems = maybe_unserialize($clickData); + if (is_array($clickItems)) { + $clicks = array_sum($clickItems); + } + } + } else { + $clicks = $stat ? (int) $stat->clicks : 0; + } + + $stats = [ + 'total' => $stat ? (int) $stat->total : 0, + 'sent' => $stat ? (int) $stat->sent : 0, + 'views' => $views, + 'clicks' => $clicks, + 'unsubscribers' => $unsub ? (int) $unsub->total : 0, + ]; + + // Revenue from batch meta + $revenueRaw = $campMeta['_campaign_revenue'] ?? null; + if ($revenueRaw) { + $data = (array) maybe_unserialize($revenueRaw); + foreach ($data as $currency => $cents) { + if ($cents && $currency !== 'orderIds') { + $stats['revenue'] = [ + 'label' => __('Revenue', 'fluent-crm') . ' (' . $currency . ')', + 'total' => number_format($cents / 100, 2), + 'currency' => $currency + ]; + } + } + } + + $campaign->stats = $stats; + $campaign->next_step = $campMeta['_next_config_step'] ?? false; + + // Labels from batch + $campaignLabelIds = $labelIdsByCampaign[$campaign->id] ?? []; + $campaign->labels = array_values(array_filter(array_map(function ($labelId) use ($allLabels) { + $label = $allLabels[$labelId] ?? null; + if (!$label) { + return null; + } + $settings = maybe_unserialize($label->settings); + return [ + 'id' => $label->id, + 'slug' => $label->slug, + 'title' => $label->title, + 'color' => is_array($settings) ? ($settings['color'] ?? '') : '' + ]; + }, $campaignLabelIds))); + } + } + } + + return [ + 'campaigns' => $campaigns + ]; + } + + public function create(Request $request) + { + $title = $request->get('title'); + if ($title !== null && $title !== '') { + $data = $this->validate($request->only('title'), [ + 'title' => 'required|unique:fc_campaigns', + ]); + $data['title'] = sanitize_text_field($data['title']); + } else { + $defaultTitle = __('Untitled', 'fluent-crm'); + $data['title'] = $this->ensureUniqueDefaultTitle($defaultTitle); + } + + $campaign = Campaign::create($data)->load([ + 'template', 'subjects' + ]); + + do_action('fluent_crm/campaign_created', $campaign); + + return $this->sendSuccess( + $campaign + ); + } + + /** + * Return a unique title for default "Untitled" (e.g. Untitled, Untitled 2, ...). + */ + protected function ensureUniqueDefaultTitle($baseTitle) + { + $title = sanitize_text_field($baseTitle); + if (!Campaign::where('title', $title)->exists()) { + return $title; + } + $count = 2; + while (Campaign::where('title', $title . ' ' . $count)->exists()) { + $count++; + } + return $title . ' ' . $count; + } + + public function campaign(Request $request, $id) + { + if ($request->exists('viewCampaign')) { + $campaign = Campaign::findOrFail($id); + $emails = $campaign->emails()->with('subscriber')->paginate(); + return $this->sendSuccess(['campaign' => $campaign, 'emails' => $emails]); + } + + // Re-key `with` to a flat, integer-indexed list and sanitize each value + // before it reaches Campaign::with(). The vulnerability was that a request + // shaped like with[]=subjects put attacker input in the array KEY, + // which array_map('sanitize_key', ...) never touched, so it flowed into + // with() as a relation name and surfaced verbatim in the reflected + // "relationship not found" exception (XSS). Discarding keys with array_values + // closes that path, and sanitize_key keeps each value to [a-z0-9_-] so a + // value can't carry markup either. This keeps the exact value-sanitization the + // endpoint already had and does not restrict which relations are allowed, so + // no core/add-on caller is broken. + $with = array_values(array_map('sanitize_key', (array) $request->get('with', []))); + if ($with) { + $campaign = Campaign::with($with)->find($id); + } else { + $campaign = Campaign::findOrFail($id); + } + + /** + * Determine the email campaign data in FluentCRM. + * + * This filter allows modification of the email campaign data before it is used. + * + * @param array $campaign The email campaign data. + * @since 2.6.51 + * + */ + $campaign = apply_filters('fluent_crm/campaign_data', $campaign); + + $templates = Template::emailTemplates() + ->select(['ID', 'post_title']) + ->orderBy('ID', 'desc') + ->get(); + + $campaign->server_time = current_time('mysql'); + + return $this->sendSuccess(compact('campaign', 'templates')); + } + + public function campaignEmails(Request $request, $campaignId) + { + $filterType = $request->get('filter_type'); + $search = $request->getSafe('search', 'sanitize_text_field'); + + $campaign = Campaign::withoutGlobalScope('type')->findOrFail($campaignId); + + $emailsQuery = CampaignEmail::with(['subscriber'])->where('campaign_id', $campaign->id); + + if ($search) { + $emailsQuery->whereHas('subscriber', function ($q) use ($search) { + $q->searchBy($search); + }); + } + + $filterType = in_array($filterType, ['click', 'view', 'unopened', 'failed']) ? $filterType : ''; + + if ($filterType == 'click') { + $emailsQuery = $emailsQuery->whereNotNull('click_counter') + ->orderBy('click_counter', 'DESC'); + } else if ($filterType == 'view') { + $emailsQuery = $emailsQuery->where('is_open', '>', 0) + ->orderBy('is_open', 'DESC'); + } else if ($filterType == 'unopened') { + $emailsQuery = $emailsQuery->where('is_open', '==', 0) + ->orderBy('is_open', 'DESC'); + } else if ($filterType == 'failed') { + $emailsQuery = $emailsQuery->where('status', 'failed') + ->orderBy('id', 'DESC'); + } + + $emails = $emailsQuery->paginate(); + + $data = [ + 'emails' => $emails, + 'failed_counts' => CampaignEmail::where('campaign_id', $campaign->id)->where('status', 'failed')->count() + ]; + + if ($request->get('with_campaign')) { + $campaign->open_tracking_status = $campaign->getOpenTrackingStatus(); + $campaign->click_tracking_status = $campaign->getClickTrackingStatus(); + $data['campaign'] = $campaign; + } + + return $data; + } + + public function updateSingleCampaignSimulate(Request $request) + { + $id = intval($request->get('campaign_id')); + return $this->update($request, $id); + } + + public function update(Request $request, $id) + { + $data = $this->validate($this->request->except(['action']), [ + "title" => "required|unique:fc_campaigns,title,{$id},id", + ]); + + $updateData = Arr::only($data, [ + 'title', + 'slug', + 'template_id', + 'email_subject', + 'email_pre_header', + 'email_body', + 'utm_status', + 'utm_source', + 'utm_medium', + 'utm_campaign', + 'utm_term', + 'utm_content', + 'scheduled_at', + 'design_template' + ]); + + if (!empty($data['settings'])) { + $updateData['settings'] = $data['settings']; + + if (!empty($data['settings']['template_config']['design_template'])) { + $updateData['design_template'] = $data['settings']['template_config']['design_template']; + } + } + + $updateData = Sanitize::campaign($updateData); + + $campaign = Campaign::findOrFail($id); + + $campaignSubjects = []; + + if (isset($data['update_subjects'])) { + $campaignSubjects = Arr::get($data, 'subjects', []); + + // Validate A/B subjects before saving campaign fields to avoid partial step updates. + $validSubjects = array_filter((array)$campaignSubjects, function ($subject) { + return !empty($subject['key']) && trim((string)Arr::get($subject, 'value', '')); + }); + + if (!empty($campaignSubjects) && count($validSubjects) < 2) { + return $this->sendError([ + 'message' => __('Please provide at least two Subject Lines for A/B Test.', 'fluent-crm') + ], 422); + } + } + + $campaign->fill($updateData)->save(); + + if (isset($data['update_subjects'])) { + $campaign->syncSubjects($campaignSubjects); + $campaign = Campaign::with(['subjects'])->find($id); + } else { + $campaign = Campaign::findOrFail($id); + } + + $nextStep = Arr::get($data, 'next_step'); + + if ($nextStep) { + + if ($nextStep == 1) { + do_action('fluent_crm/update_campaign_compose', $data, $campaign); + } else if ($nextStep == 2) { + $footerDisabled = Arr::get(Helper::getFooterConfig($campaign), 'disable_footer') === 'yes'; + if (($footerDisabled || $campaign->design_template === 'visual_builder') && !Helper::hasComplianceText($campaign->email_body)) { + return $this->sendError([ + 'compliance_failed' => true, + 'message' => '##crm.manage_subscription_url## or ##crm.unsubscribe_url## or {{crm_global_email_footer}} string is required for compliance. Please include unsubscription or manage subscription link.
Please go to the previous screen and add the unsubscribe link' + ]); + } + do_action('fluent_crm/update_campaign_subjects', $data, $campaign); + } + + fluentcrm_update_campaign_meta($id, '_next_config_step', $nextStep); + } + + do_action('fluent_crm/campaign_data_updated', $campaign, $data); + + return $this->sendSuccess([ + 'campaign' => $campaign + ]); + } + + public function updateStep(Request $request, $id) + { + $step = intval($request->get('next_step')); + fluentcrm_update_campaign_meta($id, '_next_config_step', $step); + return [ + 'message' => __('step saved', 'fluent-crm') + ]; + } + + public function validateRecipientsSelection(Request $request) + { + $items = $request->get('items'); + $campaignId = absint($request->get('campaign_id')); + $campaign = Campaign::findOrFail($campaignId); + $subscribersIds = $campaign->getSubscribeIdsByList($items); + + if (!$subscribersIds) { + return $this->sendError([ + 'message' => __('Sorry! No subscribers found based on your selection', 'fluent-crm'), + 'count' => 0 + ]); + } + + $settings = $campaign->settings; + $settings['subscribers'] = $items; + $campaign->settings = $settings; + $campaign->save(); + + return $this->sendSuccess([ + 'count' => count($subscribersIds) + ]); + } + + public function draftRecipients(Request $request, $campaignId) + { + $campaign = Campaign::findOrFail($campaignId); + + + $subscribersSettings = [ + 'subscribers' => $request->get('subscribers'), + 'excludedSubscribers' => $request->get('excludedSubscribers'), + 'sending_filter' => $request->get('sending_filter', 'list_tag'), + 'dynamic_segment' => $request->get('dynamic_segment'), + 'advanced_filters' => Helper::parseArrayOrJson($request->get('advanced_filters')) + ]; + + // Sanitize the inputs + $sanitizedSubscribersSettings['sending_filter'] = sanitize_text_field($subscribersSettings['sending_filter']); + + + $count = (new Campaign())->getSubscriberIdsCountBySegmentSettings($subscribersSettings); + + if (!$count) { + return $this->sendError([ + 'message' => __('Sorry no subscribers found based on your selection', 'fluent-crm') + ]); + } + + $campaign->campaign_emails()->delete(); + $campaign->status = 'draft'; + $campaign->recipients_count = $count; + $campaign->settings = wp_parse_args($subscribersSettings, $campaign->settings); + $campaign->save(); + fluentcrm_update_campaign_meta($campaign->id, '_recipient_processed', 0); + fluentcrm_update_campaign_meta($campaign->id, '_last_recipient_id', 0); + + do_action('fluent_crm/campaign_recipients_query_updated', $campaign); + + return [ + 'message' => __('Recipient settings has been updated', 'fluent-crm'), + 'count' => $count + ]; + } + + public function recipientsCount(Request $request, $campaignId) + { + $campaign = Campaign::withoutGlobalScope('type')->findOrFail($campaignId); + $preProcessedStatuses = [ + 'draft', + 'processing', + 'pending-scheduled' + ]; + + if (in_array($campaign->status, $preProcessedStatuses)) { + $count = $campaign->getSubscribersModel()->count(); + } else { + $count = $campaign->recipients_count; + } + + return [ + 'estimated_count' => $count + ]; + } + + + /* + * TODO: This method is currently not in use. We will keep it for reference for now. We will remove in the immediate next version + * Found since v3.0.0 + */ + // public function subscribe(Request $request, $campaignId) + // { + // $startTime = microtime(true); + // $campaign = Campaign::findOrFail($campaignId); + + // do_action('fluentcrm_campaign_status_active', $campaign); + + // $page = (int)$this->request->get('page', 1); + // /** + // * Determine the number of subscribers to process per request in FluentCRM Email Campaign. + // * + // * This filter allows you to modify the number of subscribers that are processed + // * in a single request when handling email campaigns. + // * + // * @param int The number of subscribers to process per request. Default is 90. + // * @since 2.7.0 + // * + // */ + // $limit = (int)apply_filters('fluent_crm/process_subscribers_per_request', 90); + + // $subscribersSettings = [ + // 'subscribers' => $request->get('subscribers'), + // 'excludedSubscribers' => $request->get('excludedSubscribers'), + // 'sending_filter' => $request->get('sending_filter', 'list_tag'), + // 'dynamic_segment' => $request->get('dynamic_segment'), + // 'advanced_filters' => Helper::parseArrayOrJson($request->get('advanced_filters')) + // ]; + + // $offset = 0; + // $runTime = 40; + + // if ($page == 1) { + // $runTime = 15; + // $campaign->campaign_emails()->delete(); + // $campaign->settings = wp_parse_args($subscribersSettings, $campaign->settings); + // $campaign->save(); + // fluentcrm_update_campaign_meta($campaign->id, '_recipient_processed', 0); + // } else { + // $offset = (int)fluentcrm_get_campaign_meta($campaign->id, '_recipient_processed', true); + // } + + // $subscribeStatus = $campaign->subscribeBySegment($subscribersSettings, $limit, $offset); + // fluentcrm_update_campaign_meta($campaign->id, '_recipient_processed', $campaign->recipients_count); + + // $willRun = true; + + // while ($willRun && ((microtime(true) - $startTime) < $runTime) && !fluentCrmIsMemoryExceeded()) { + // $campaign = Campaign::findOrFail($campaignId); + // $willRun = !!$subscribeStatus['result']; + + // if ($willRun) { + // $subscribeStatus = $campaign->subscribeBySegment($subscribersSettings, $limit, $campaign->recipients_count); + // fluentcrm_update_campaign_meta($campaign->id, '_recipient_processed', $campaign->recipients_count); + // } + // } + + // $hasMore = !!$subscribeStatus['result']; + + // if (!$hasMore) { + // $campaign = Campaign::findOrFail($campaignId); + // fluentcrm_update_campaign_meta($campaign->id, '_recipient_processed', 0); + // if (!$campaign->recipients_count) { + // return $this->sendError([ + // 'message' => __('Sorry, No subscribers found based on your filters', 'fluent-crm') + // ]); + // } + // $campaign->maybeDeleteDuplicates(); + // } + + // if ($subscribeStatus['total_items']) { + // return $this->sendSuccess([ + // 'has_more' => $hasMore, + // 'count' => $campaign->recipients_count, + // 'total_items' => $subscribeStatus['total_items'], + // 'page_total' => ceil($subscribeStatus['total_items'] / $limit), + // 'next_page' => $page + 1, + // 'execution_time' => microtime(true) - $startTime, + // 'memory' => fluentCrmIsMemoryExceeded(), + // 'memory_limit' => fluentCrmGetMemoryLimit(), + // 'memory_usage' => memory_get_usage(true) + // ]); + // } + + // if ($campaign->recipients_count) { + // return [ + // 'has_more' => false, + // 'count' => $campaign->recipients_count + // ]; + // } + + // return $this->sendError([ + // 'message' => __('Sorry, No subscribers found based on your filters', 'fluent-crm') + // ]); + // } + + public function getContactEstimation(Request $request) + { + $start_time = microtime(true); + + $filterType = $request->get('sending_filter', 'list_tag'); + + $subscribersSettings = [ + 'sending_filter' => $filterType + ]; + + if ($filterType == 'list_tag') { + $subscribersSettings['subscribers'] = $request->get('subscribers', []); + $subscribersSettings['excludedSubscribers'] = $request->get('excludedSubscribers', []); + } else if ($filterType == 'dynamic_segment') { + $subscribersSettings['dynamic_segment'] = $request->get('dynamic_segment', []); + } else if ($filterType == 'advanced_filters') { + $subscribersSettings['advanced_filters'] = Helper::parseArrayOrJson($request->get('advanced_filters')); + } else { + return [ + 'count' => 0 + ]; + } + + $count = (new Campaign())->getSubscriberIdsCountBySegmentSettings($subscribersSettings); + + return [ + 'count' => $count, + 'execution_time' => microtime(true) - $start_time + ]; + } + + public function deleteCampaignEmails(Request $request, $campaignId) + { + $selectionIds = array_filter($request->get('email_ids'), 'intval'); + + if ($selectionIds) { + CampaignEmail::where('campaign_id', $campaignId) + ->whereIn('id', $selectionIds) + ->delete(); + } + + $newCount = CampaignEmail::where('campaign_id', $campaignId) + ->count(); + + Campaign::where('id', $campaignId)->update([ + 'recipients_count' => $newCount + ]); + + return $this->sendSuccess([ + 'message' => __('Selected emails are deleted', 'fluent-crm'), + 'recipients_count' => $newCount + ]); + } + + public function schedule(Request $request, $campaignId) + { + $scheduleAt = $request->get('scheduled_at'); + $campaign = Campaign::findOrFail($campaignId); + + if ($campaign->status != 'draft') { + return $this->sendError([ + 'message' => __('Campaign status is not in draft status. Please reload the page', 'fluent-crm') + ], 422); + } + + if (!$campaign->recipients_count) { + return $this->sendError([ + 'message' => __('No recipients found for this campaign. Please add recipients first.', 'fluent-crm') + ], 422); + } + + // Wrap email deletion + campaign update in a transaction so a crash + // between the two doesn't leave the campaign in an inconsistent state + fluentCrmDb()->beginTransaction(); + + try { + // Remove Emails if there has any pre-processed + CampaignEmail::where('campaign_id', $campaignId)->delete(); + fluentcrm_update_campaign_meta($campaign->id, '_recipient_processed', 0); + fluentcrm_update_campaign_meta($campaign->id, '_last_recipient_id', 0); + + if ($scheduleAt) { + $sendingType = $request->get('sending_type', 'schedule'); + if ($sendingType == 'range_schedule') { + $isInvalid = true; + if (is_array($scheduleAt) && count($scheduleAt) == 2) { + $scheduleStartAt = sanitize_text_field($scheduleAt[0]); + + if ($scheduleStartAt && strtotime($scheduleStartAt) < current_time('timestamp')) { + $scheduleStartAt = current_time('mysql'); + } + + $scheduleEndAt = sanitize_text_field($scheduleAt[1]); + + if ($scheduleEndAt && $scheduleEndAt && strtotime($scheduleStartAt) < strtotime($scheduleEndAt)) { + $isInvalid = false; + } + + $scheduleAt = [$scheduleStartAt, $scheduleEndAt]; + } + + if ($isInvalid) { + fluentCrmDb()->rollBack(); + return $this->sendError([ + 'message' => __('Invalid schedule date range', 'fluent-crm') + ], 422); + } + + $settings = $campaign->settings; + $settings['sending_type'] = 'range_schedule'; + $settings['schedule_range'] = [strtotime($scheduleAt[0]), strtotime($scheduleAt[1])]; + + $data = [ + 'status' => 'pending-scheduled', + 'updated_at' => fluentCrmTimestamp(), + 'scheduled_at' => $scheduleAt[0], + 'recipients_count' => 0, + 'settings' => $settings + ]; + + } else { + $scheduleAt = sanitize_text_field($scheduleAt); + if (!$scheduleAt) { + fluentCrmDb()->rollBack(); + return $this->sendError([ + 'message' => __('Invalid schedule date', 'fluent-crm') + ], 422); + } + + $settings = $campaign->settings; + $settings['sending_type'] = 'schedule'; + + $data = [ + 'status' => 'pending-scheduled', + 'updated_at' => fluentCrmTimestamp(), + 'scheduled_at' => $scheduleAt, + 'recipients_count' => 0, + 'settings' => $settings + ]; + } + + $message = __('Your campaign email has been scheduled', 'fluent-crm'); + + } else { + $message = __('Email Sending will be started soon', 'fluent-crm'); + $settings = $campaign->settings; + $settings['sending_type'] = 'instant'; + $data = [ + 'status' => 'processing', + 'updated_at' => fluentCrmTimestamp(), + 'scheduled_at' => fluentCrmTimestamp(), + 'recipients_count' => 0, + 'settings' => $settings + ]; + } + + $data['settings']['click_tracker'] = fluentcrmTrackClicking(); + $data['settings']['open_tracker'] = fluentcrmTrackEmailOpen(); + + $data['settings'] = maybe_serialize($data['settings']); + + // Guard: only transition from 'draft' to prevent race conditions + $updated = Campaign::where('id', $campaignId) + ->where('status', 'draft') + ->update($data); + + if (!$updated) { + fluentCrmDb()->rollBack(); + return $this->sendError([ + 'message' => __('Campaign is no longer in draft status. Please reload the page.', 'fluent-crm') + ], 422); + } + + fluentCrmDb()->commit(); + } catch (\Throwable $e) { + fluentCrmDb()->rollBack(); + return $this->sendError([ + 'message' => __('Failed to schedule campaign. Please try again.', 'fluent-crm') + ], 500); + } + + if (!$scheduleAt) { + $url = add_query_arg([ + 'action' => 'fluentcrm-post-campaigns-emails-processing', + 'campaign_id' => $campaignId, + 'time' => time() + ], admin_url('admin-ajax.php')); + + \FluentCrm\App\Services\Libs\Mailer\Handler::fireNonBlockingRequest($url, [ + 'retry' => 1 + ]); + } + + $campaign = Campaign::findOrFail($campaignId); + + fluentcrm_update_campaign_meta($campaign->id, '_campaign_sent_by', get_current_user_id()); + + if ($scheduleAt) { + do_action('fluent_crm/campaign_scheduled', $campaign, $campaign->scheduled_at); + } else { + do_action('fluent_crm/campaign_set_send_now', $campaign); + } + + return $this->sendSuccess([ + 'campaign' => $campaign, + 'message' => $message, + 'current_timestamp' => fluentCrmTimestamp() + ]); + } + + public function processingStat(Request $request, $campaignId) + { + $campaign = Campaign::withoutGlobalScope('type') + ->with(['subjects']) + ->whereIn('type', fluentCrmAutoProcessCampaignTypes()) + ->findOrFail($campaignId); + + if ($campaign->status == 'pending-scheduled' && (strtotime($campaign->scheduled_at) - current_time('timestamp')) < 360) { + $campaign->status = 'processing'; + $campaign->recipients_count = 0; + $campaign->save(); + do_action('fluent_crm/campaign_processing_start', $campaign); + } + + if ($campaign->status != 'processing') { + if ($campaign->status == 'scheduled' && current_time('timestamp') - strtotime($campaign->scheduled_at) > 300) { + if (Scheduler::markArchiveCampaigns()) { + $campaign = Campaign::withoutGlobalScope('type') + ->with(['subjects']) + ->findOrFail($campaignId); + } + } + + return [ + 'reload' => true, + 'campaign' => $campaign + ]; + } + + // This is the processing status + $processor = (new CampaignProcessor($campaign->id)); + $processingChunk = (int)apply_filters('fluent_crm/campaign_processing_stat_chunk', 30, $campaign); + if ($processingChunk < 1) { + $processingChunk = 1; + } + + $processingRunTime = (int)apply_filters('fluent_crm/campaign_processing_stat_runtime_seconds', 10, $campaign); + if ($processingRunTime < 1) { + $processingRunTime = 1; + } + + $processedCampaign = $processor->processEmails($processingChunk, $processingRunTime); + + $didRun = false; + if ($processedCampaign) { + $campaign = $processedCampaign; + $campaign->load('subjects'); + $didRun = true; + } + + $campaign->scheduling_range = $campaign->rangedScheduleDates(); + + return [ + 'campaign' => $campaign, + 'didRun' => $didRun, + 'scheduling_method' => $processor->getSchedulingMethod() + ]; + } + + public function sendTestEmail() + { + $isTest = $this->request->get('test_campaign') == 'yes'; + + add_action('wp_mail_failed', function ($wpError) { + Helper::debugLog( + 'Test Email failed', + $wpError->get_error_message(), + 'error' + ); + }, 10, 1); + + if ($isTest) { + $campaign = (object)$this->request->get('campaign'); + $emailSubject = $campaign->email_subject; + + if (empty($campaign->settings)) { + $campaign->settings = [ + 'template_config' => [] + ]; + } + + if (!empty($campaign->subjects) && is_array($campaign->subjects)) { + $validSubjects = array_filter($campaign->subjects, function ($subject) { + return trim((string)Arr::get((array)$subject, 'value', '')); + }); + + if ($validSubjects) { + // Quick Test only verifies deliverability, so use the first configured A/B subject. + $subject = reset($validSubjects); + $emailSubject = Arr::get((array)$subject, 'value', $emailSubject); + } + } + + $campaignEmail = (object)[ + 'email_subject' => $emailSubject, + 'email_pre_header' => $campaign->email_pre_header, + 'email_body' => $campaign->email_body + ]; + } else { + $campaignId = $this->request->get('campaign_id'); + $campaignEmail = CampaignEmail::where('campaign_id', $campaignId)->first(); + $campaign = Campaign::findOrFail($campaignId); + if (!$campaignEmail) { + $campaignEmail = (object)[ + 'email_subject' => $campaign->email_subject, + 'email_pre_header' => $campaign->email_pre_header, + 'email_body' => $campaign->email_body + ]; + } + } + + $email = $this->request->getSafe('email', 'sanitize_email', ''); + + if (!$email) { + $user = get_user_by('ID', get_current_user_id()); + $email = $user->user_email; + } + + $emailBody = $campaignEmail->email_body; + + $subscriber = Subscriber::where('email', $email)->first(); + if (!$subscriber) { + $subscriber = Subscriber::where('status', 'subscribed')->first(); + } + + if (!$subscriber) { + return $this->sendError([ + 'message' => __('No subscriber found to send test. Please add atleast one contact as subscribed status', 'fluent-crm') + ]); + } + + $designTemplate = $campaign->design_template; + + $rawTemplates = [ + 'raw_html', + 'visual_builder', + 'raw_classic' + ]; + + if (in_array($designTemplate, $rawTemplates)) { + $emailBody = $campaign->email_body; + } else { + $emailBody = (new BlockParser($subscriber))->parse($emailBody); + } + + $emailFooterConfig = Helper::getFooterConfig($campaign); + $emailFooter = Arr::get($emailFooterConfig, 'footer_content', ''); + + $emailSubject = $campaignEmail->email_subject; + + $preHeader = (!empty($campaign->email_pre_header)) ? $campaign->email_pre_header : ''; + + if ($subscriber) { + /** + * Determine the email campaign body text before it is sent. + * + * This filter allows you to modify the email body content for a campaign before it is sent to the subscriber. + * + * @param string $emailBody The email body content. + * @param object $subscriber The subscriber object. + * + * @return string The filtered email body content. + * @since 2.7.0 + * + */ + $emailBody = apply_filters('fluent_crm/parse_campaign_email_text', $emailBody, $subscriber); + /** + * Determine the email footer text for a campaign. + * + * This filter allows you to modify the email footer text for a campaign before it is sent to a subscriber. + * + * @param string $emailFooter The email footer text. + * @param object $subscriber The subscriber object. + * @since 2.7.0 + * + */ + $emailFooter = apply_filters('fluent_crm/parse_campaign_email_text', $emailFooter, $subscriber); + + $emailFooterConfig['footer_content'] = $emailFooter; + + /** + * Determine the email campaign subject text. + * + * This filter allows you to modify the email subject text for a campaign. + * + * @param string $emailSubject The email subject text. + * @param object $subscriber The subscriber object. + * @since 2.7.0 + * + */ + $emailSubject = apply_filters('fluent_crm/parse_campaign_email_text', $emailSubject, $subscriber); + /** + * Determine the pre-header text of the campaign email. + * + * This filter allows you to modify the pre-header text of the campaign email before it is sent to the subscriber. + * + * @param string $preHeader The pre-header text of the campaign email. + * @param object $subscriber The subscriber object containing subscriber details. + * + * @return string The filtered pre-header text. + * @since 2.7.0 + * + */ + $preHeader = apply_filters('fluent_crm/parse_campaign_email_text', $preHeader, $subscriber); + } + + $templateData = [ + 'preHeader' => $preHeader, + 'email_body' => $emailBody, + 'footer_text' => $emailFooter, + 'footer_config' => $emailFooterConfig, + 'config' => wp_parse_args(Arr::get($campaign->settings, 'template_config', []), Helper::getTemplateConfig($campaign->design_template)) + ]; + + /** + * Determine the email body content based on the design template type. + * + * This filter allows modification of the email body content based on the specified design template. + * + * @param string $emailBody The email body content. + * @param array $templateData The data used for the email template. + * @param object $campaign The campaign object. + * @param object $subscriber The subscriber object. + * @since 2.5.1 + * + */ + $emailBody = apply_filters( + 'fluent_crm/email-design-template-' . $campaign->design_template, + $emailBody, + $templateData, + $campaign, + $subscriber + ); + + + $emailBody = str_replace('{{crm_global_email_footer}}', $emailFooter, $emailBody); + $emailBody = str_replace('{{crm_preheader_text}}', $preHeader, $emailBody); + + $data = [ + 'to' => [ + 'email' => $email, + 'name' => $subscriber->full_name + ], + 'subject' => 'TEST: ' . $emailSubject, + 'body' => $emailBody, + 'headers' => Helper::getMailHeadersFromSettings(Arr::get($campaign->settings, 'mailer_settings', [])) + ]; + + Helper::maybeDisableEmojiOnEmail(); + $result = Mailer::send($data, $subscriber, null, true); + + return [ + 'message' => sprintf( + __('Test email successfully sent to %1$s, The dynamic tags may not be replaced in the test email', 'fluent-crm'), + $email + ), + 'result' => $result + ]; + } + + /** + * Render the editor/draft email preview iframe. + * + * Route: POST /campaigns/email-preview-html + * Used by resources/admin/Pieces/EmailElements/EmailPreview.vue. + * For sent email-history previews, see previewEmail(). + * + * @return array + */ + public function getEmailPreviewBody() + { + if (!defined('FLUENTCRM_PREVIEWING_EMAIL')) { + define('FLUENTCRM_PREVIEWING_EMAIL', true); + } + + $campaignId = $this->request->get('campaign_id'); + + if ($campaignId) { + $campaignId = (int)$campaignId; + $campaign = Campaign::withoutGlobalScope('type')->with(['subjects'])->findOrfail($campaignId); + } else { + $campaign = $this->request->get('campaign', []); + if (isset($campaign['post_content'])) { + $campaign['email_body'] = $campaign['post_content']; + } + + if (isset($campaign['post_excerpt'])) { + $campaign['email_pre_header'] = sanitize_text_field($campaign['post_excerpt']); + } + + $campaign = (object)$campaign; + } + + $emailBody = $campaign->email_body; + + if ($this->request->get('contact_id')) { + $subscriber = Subscriber::find($this->request->getSafe('contact_id', 'sanitize_text_field')); + } else { + $subscriber = fluentcrm_get_current_contact(); + } + + if (!$subscriber) { + $subscriber = Subscriber::where('status', 'subscribed')->first(); + } + + if ($this->request->get('disable_subscriber') == 'yes') { + $subscriber = null; + } + + $designTemplate = $campaign->design_template; + + if (!$designTemplate) { + $designTemplate = 'plain'; + } + + $rawTemplates = [ + 'raw_html', + 'visual_builder', + 'raw_classic' + ]; + + if (in_array($designTemplate, $rawTemplates)) { + $emailBody = wp_unslash($campaign->email_body); + } else { + $emailBody = (new BlockParser($subscriber))->parse($emailBody); + } + + if (empty($campaign->settings)) { + $campaign->settings = []; + } + + $emailFooterConfig = Helper::getFooterConfig($campaign); + $emailFooter = Arr::get($emailFooterConfig, 'footer_content', ''); + + if ($subscriber) { + /** + * Determine the campaign email body content text. + * + * This filter allows you to modify the email body content before it is sent to the subscriber. + * + * @param string $emailBody The original email body content. + * @param object $subscriber The subscriber object containing subscriber details. + * + * @return string The filtered email body content. + * @since 2.7.0 + * + */ + $emailBody = apply_filters('fluent_crm/parse_campaign_email_text', $emailBody, $subscriber); + /** + * Determine the campaign email footer text. + * + * This filter allows you to modify the email footer content before it is sent to the subscriber. + * + * @param string $emailFooter The original email footer content. + * @param object $subscriber The subscriber object containing subscriber details. + * + * @return string The filtered email footer content. + * @since 2.7.0 + * + */ + $emailFooter = apply_filters('fluent_crm/parse_campaign_email_text', $emailFooter, $subscriber); + } + + $preHeader = (!empty($campaign->email_pre_header)) ? $campaign->email_pre_header : ''; + + if ($preHeader && $subscriber) { + /** + * Determine the campaign email Pre-header text before sending. + * + * This filter allows you to modify the email pre-header content for a campaign before it is sent to the subscriber. + * + * @param string $emailBody The email body content. + * @param object $subscriber The subscriber object. + * @since 2.7.0 + * + */ + $preHeader = apply_filters('fluent_crm/parse_campaign_email_text', $preHeader, $subscriber); + } + + $emailFooterConfig['footer_content'] = $emailFooter; + + $templateData = [ + 'preHeader' => $preHeader, + 'email_body' => $emailBody, + 'footer_text' => $emailFooter, + 'footer_config' => $emailFooterConfig, + 'config' => wp_parse_args(Arr::get($campaign->settings, 'template_config', []), Helper::getTemplateConfig($campaign->design_template)) + ]; + + /** + * Determine the email body content based on the design template type. + * + * This filter allows modification of the email body content based on the specified design template. + * + * @param string $emailBody The email body content. + * @param array $templateData The data used for the email template. + * @param object $campaign The campaign object. + * @param object $subscriber The subscriber object. + * @since 2.5.1 + * + */ + $emailBody = apply_filters( + 'fluent_crm/email-design-template-' . $designTemplate, + $emailBody, + $templateData, + $campaign, + $subscriber + ); + + if (Str::contains($emailBody, ['{{crm', '##crm'])) { + $emailBody = str_replace(['{{crm_global_email_footer}}', '{{crm_preheader_text}}'], [$emailFooter, $preHeader], $emailBody); + if (Str::contains($emailBody, ['##crm.', '{{crm.'])) { + /** + * Determine the email body content including a specific Smartcode for a subscriber. + * + * This filter allows modification of the email body content including a smartcode before it is sent to a subscriber. + * + * @param string $emailBody The email body content. + * @param object $subscriber The subscriber object. + * + * @return string The filtered email body content. + * @since 2.7.0 + * + */ + $emailBody = apply_filters('fluent_crm/parse_extended_crm_text', $emailBody, $subscriber); + } + } + + $emailBody = str_replace(['https://fonts.googleapis.com/css2', 'https://fonts.googleapis.com/css'], 'https://fonts.bunny.net/css', $emailBody); + + return [ + 'preview_html' => $emailBody, + 'subjects' => !empty($campaign->subjects) ? $campaign->subjects : [] + ]; + } + + public function unsubscribe() + { + $campaignId = $this->request->get('campaign_id'); + + $subscriberIds = (array)$this->request->get('subscriber_ids'); + + $campaign = Campaign::findOrFail($campaignId); + + $campaign->unsubscribe($subscriberIds); + + return $this->sendSuccess(compact('campaign')); + } + + public function delete(Request $request, $campaignId) + { + $campaign = Campaign::findOrFail($campaignId); + + $campaign->deleteCampaignData(); + $campaign->delete(); + do_action('fluent_crm/campaign_deleted', $campaignId); + + return $this->send(['success' => true]); + } + + public function handleBulkAction(Request $request) + { + $actionName = $request->getSafe('action_name', 'sanitize_text_field', ''); + $campaignIds = array_map('intval', (array)$request->get('campaign_ids', [])); + $selectAllCampaigns = filter_var($request->get('select_all'), FILTER_VALIDATE_BOOLEAN); + $campaignIds = array_map(function ($id) { + return (int)$id; + }, $campaignIds); + + $campaignIds = array_unique(array_filter($campaignIds)); + + if ($selectAllCampaigns) { + $campaignIds = Campaign::pluck('id')->toArray(); + } + + if (!$campaignIds) { + return $this->sendError([ + 'message' => __('Please provide campaign IDs', 'fluent-crm') + ]); + } + + if ($actionName == 'apply_labels') { + // labels are coming as array of ids from request + $newLabelIds = (array)$request->get('labels', []); + $newLabelIds = array_map('intval', $newLabelIds); + $newLabelIds = array_unique(array_filter($newLabelIds)); + + if (!$newLabelIds) { + return $this->sendError([ + 'message' => __('Please provide labels', 'fluent-crm') + ]); + } + + $campaigns = Campaign::whereIn('id', $campaignIds)->get(); + + foreach ($campaigns as $campaign) { + $campaign->attachLabels($newLabelIds); + } + + return $this->sendSuccess([ + 'message' => __('Labels has been applied successfully', 'fluent-crm'), + ]); + } + + if ($actionName == 'delete_campaigns') { + $campaigns = Campaign::whereIn('id', $campaignIds)->get(); + foreach ($campaigns as $campaign) { + $campaignId = $campaign->id; + $campaign->deleteCampaignData(); + $campaign->delete(); + do_action('fluent_crm/campaign_deleted', $campaignId); + } + + return $this->sendSuccess([ + 'message' => __('Selected Campaigns have been deleted permanently', 'fluent-crm'), + ]); + } + + return $this->sendError([ + 'message' => __('invalid bulk action', 'fluent-crm') + ]); + } + + public function createTemplate() + { + $templateId = $this->request->get('template_id'); + $campaignId = $this->request->get('campaign_id'); + $campaign = Campaign::findOrFail($campaignId); + + $template = Template::emailTemplates()->find($templateId); + if (!$template) { + return $this->sendError([ + 'message' => __('Template not found', 'fluent-crm') + ], 404); + } + + $template = Template::emailTemplates()->find($templateId); + + if (!$template) { + return $this->sendError([ + 'message' => __('Template not found', 'fluent-crm') + ], 404); + } + + return $this->send([ + 'id' => Template::create([ + 'post_type' => fluentcrmCampaignTemplateCPTSlug(), + 'post_content' => $template->post_content + ])->ID + ]); + } + + /** + * Render a sent/scheduled email-history preview with metadata and click stats. + * + * Route: GET /campaigns/emails/{email_id}/preview + * Used by contact profile email history, campaign email rows, and all-emails preview. + * The body renderer is CampaignEmail::previewData(); keep its template handling aligned + * with getEmailPreviewBody() so raw/classic and Gutenberg previews stay consistent. + * + * @param Request $request + * @param int $emailId + * @return array + */ + public function previewEmail(Request $request, $emailId) + { + if (!defined('FLUENTCRM_PREVIEWING_EMAIL')) { + define('FLUENTCRM_PREVIEWING_EMAIL', true); + } + + $email = CampaignEmail::findOrFail($emailId); + + $emailData = $email->previewData(); + $emailData['clicks'] = $email->getClicks(); + + return $this->sendSuccess([ + 'info' => $email, + 'email' => $emailData + ]); + } + + public function getCampaignStatus(Request $request, $campaignId) + { + $campaign = Campaign::withoutGlobalScope('type') + ->with(['subjects']) + ->whereIn('type', fluentCrmAutoProcessCampaignTypes()) + ->findOrFail($campaignId); + + if ($campaign->status == 'processing' || $campaign->status == 'pending-scheduled') { + return [ + 'current_timestamp' => fluentCrmTimestamp(), + 'stat' => [], + 'campaign' => $campaign, + 'sent_count' => 0, + 'analytics' => (object)[], + 'subject_analytics' => (object)[] + ]; + } + + if ($campaign->status == 'scheduled' && $campaign->scheduled_at) { + if (strtotime($campaign->scheduled_at) < strtotime(current_time('mysql'))) { + $campaign->status = 'working'; + $campaign->save(); + } + } + + $ranged = null; + + if ($campaign->status == 'working') { + $ranged = $campaign->rangedScheduleDates(); + if (!$ranged) { + // Keep this status-polling GET endpoint read-mostly. It used to + // reset stale 'processing' campaign emails here, but this route is + // called every few seconds from the campaign screen. Doing queue + // recovery from this hot polling path can collide with the sender's + // row claims/updates and produce avoidable InnoDB deadlocks. Stale + // email recovery now stays in Scheduler::resetStaleProcessingEmails(). + + // Detect a stalled send cycle. Do NOT key this off the sending + // lock: on sites with an external object cache the lock lives in + // the fc_instant_options cache group (not wp_options, so the old + // get_option() read missed it entirely) and auto-expires after + // ~80s, so a 140s threshold would never see it. Instead key off + // _last_called, which Handler::isSystemOk() stamps in the fc_meta + // "option" store on every lock-winning run — persistent, has no + // TTL, and is identical on object-cache and DB-only sites. + $lastCalled = (int)fluentcrm_get_option('fluentcrm_is_sending_emails_last_called'); + if (!$lastCalled || (time() - $lastCalled) > 140) { + // No send activity for >140s while the campaign is still + // 'working' — treat as stuck. Throttle the re-fire so frequent + // status polling doesn't spam loopback requests; one kick per + // 60s is enough for a healthy run to resume and refresh + // _last_called (which then clears this condition). + $lastRefire = fluentCrmGetOptionCache('_fcrm_last_stuck_refire', 200); + if (!$lastRefire || (time() - $lastRefire) > 60) { + fluentCrmSetOptionCache('_fcrm_last_stuck_refire', time(), 200); + + // Deliberately do NOT force-clear the lock here. If sending + // has truly stalled for >140s the lock is already well past + // its ~80s timeout, so the re-fired handler's acquireLock() + // steals it on its own (DB: expired-timestamp UPDATE; object + // cache: the key has already TTL'd away). Clearing it + // explicitly would only change anything while a sender is + // still holding a *fresh* lock — i.e. a long run under a + // raised maximumProcessingTime — and releasing it there would + // let a second sender run concurrently (rate-limit overshoot). + // Let acquireLock() be the single arbiter of ownership. + wp_remote_post(admin_url('admin-ajax.php'), [ + 'sslverify' => false, + 'blocking' => false, + 'cookies' => array(), + 'body' => [ + 'campaign_id' => $campaignId, + 'retry' => 1, + 'time' => time(), + 'action' => 'fluentcrm-post-campaigns-send-now' + ] + ]); + } + } + } + } + + $analytics = []; + $subjectsAnalytics = []; + + $sentCount = CampaignEmail::select('id') + ->where('campaign_id', $campaignId) + ->where('status', 'sent') + ->count(); + + if ($campaign->status == 'working') { + + $campaign->scheduling_range = $ranged; + + $processingCount = CampaignEmail::select('id') + ->where('campaign_id', $campaignId) + ->where('status', 'processing') + ->count(); + + // Do not reset stale 'processing' rows from campaign status polling. + // If there are still rows in processing, report that state and let the + // scheduler-owned recovery path decide when it is safe to requeue them. + if (!$processingCount && $sentCount) { + $futureCount = CampaignEmail::select('id') + ->where('campaign_id', $campaignId) + ->whereIn('status', ['pending', 'scheduled', 'paused', 'processing', 'draft']) + ->count(); + + if (!$futureCount) { + Campaign::withoutGlobalScope('type')->where('id', $campaign->id)->update([ + 'status' => 'archived', + 'updated_at' => current_time('mysql') + ]); + $campaign = Campaign::withoutGlobalScope('type')->with(['subjects'])->findOrFail($campaignId); + + do_action('fluent_crm/campaign_archived', $campaign); + } + } + } + + if ($campaign->status == 'archived') { + $campaignUrlMetric = new CampaignUrlMetric(); + $analytics = $campaignUrlMetric->getCampaignAnalytics($campaign); + + if (isset($analytics['open']) && $analytics['open']['total'] > $sentCount) { + $analytics['open']['total'] = $sentCount; + } + + if (isset($analytics['click']) && $analytics['click']['total'] > $sentCount) { + $analytics['click']['total'] = $sentCount; + } + + $subjectsAnalytics = $campaignUrlMetric->getSubjectStats($campaign); + + $campaign->open_tracking_status = $campaign->getOpenTrackingStatus(); + $campaign->click_tracking_status = $campaign->getClickTrackingStatus(); + } + + $stat = CampaignEmail::select('status', fluentCrmDb()->raw('count(*) as total')) + ->where('campaign_id', $campaignId) + ->groupBy('status') + ->get(); + + //attaching who sent the campaign + $campaignSentBy = $this->getCampaignSentData($campaign->id); + $campaign->sent_by = $campaignSentBy; + + return $this->sendSuccess([ + 'current_timestamp' => fluentCrmTimestamp(), + 'stat' => $stat, + 'campaign' => $campaign, + 'sent_count' => $sentCount, + 'analytics' => (object)$analytics, + 'subject_analytics' => (object)$subjectsAnalytics + ], 200); + } + + public function getOverviewStats(Request $request, CampaignUrlMetric $campaignUrlMetric, $campaignId) + { + $campaign = Campaign::withoutGlobalScope('type') + ->whereIn('type', fluentCrmAutoProcessCampaignTypes()) + ->findOrFail($campaignId); + + $sentCount = CampaignEmail::select('id') + ->where('campaign_id', $campaignId) + ->where('status', 'sent') + ->count(); + + + $analytics = $campaignUrlMetric->getCampaignAnalytics($campaignId); + + if (isset($analytics['open']) && $analytics['open']['total'] > $sentCount) { + $analytics['open']['total'] = $sentCount; + } + + if (isset($analytics['click']) && $analytics['click']['total'] > $sentCount) { + $analytics['click']['total'] = $sentCount; + } + + $stat = CampaignEmail::select('status', fluentCrmDb()->raw('count(*) as total')) + ->where('campaign_id', $campaignId) + ->groupBy('status') + ->get(); + + return [ + 'sent_count' => $sentCount, + 'stat' => $stat, + 'analytics' => $analytics + ]; + } + + public function pauseCampaign(Request $request, $id) + { + $campaign = Campaign::findOrFail($id); + + if ($campaign->status != 'working') { + return $this->sendError([ + 'message' => __('You can only pause a campaign if it is on "Working" state, Please reload this page', 'fluent-crm') + ]); + } + + $campaign->status = 'paused'; + $campaign->save(); + + CampaignEmail::where('campaign_id', $campaign->id) + ->whereIn('status', ['scheduled', 'pending', 'scheduling']) + ->update([ + 'status' => 'paused' + ]); + + $campaign = Campaign::findOrFail($id); + + return [ + 'message' => __('Campaign has been successfully marked as paused', 'fluent-crm'), + 'campaign' => $campaign + ]; + } + + public function resumeCampaign(Request $request, $id) + { + $campaign = Campaign::findOrFail($id); + + if ($campaign->status != 'paused') { + return $this->sendError([ + 'message' => __('You can only resume a campaign if it is on "paused" state, Please reload this page', 'fluent-crm') + ]); + } + + $campaign->status = 'working'; + $campaign->save(); + + CampaignEmail::where('campaign_id', $campaign->id) + ->where('status', 'paused') + ->update([ + 'status' => 'scheduled', + 'scheduled_at' => current_time('mysql') + ]); + + return [ + 'message' => __('Campaign has been successfully resumed', 'fluent-crm'), + 'campaign' => Campaign::findOrFail($id) + ]; + } + + public function updateCampaignTitle(Request $request, $id) + { + $campaign = Campaign::findOrFail($id); + $campaign->title = sanitize_text_field($request->get('title')); + $campaign->save(); + + if ($campaign->status == 'scheduled') { + $newTime = $request->get('scheduled_at'); + if ($newTime != $campaign->scheduled_at) { + $campaign->scheduled_at = $newTime; + $campaign->save(); + CampaignEmail::where('campaign_id', $campaign->id) + ->whereNotIn('status', ['sent', 'failed', 'bounced']) + ->update([ + 'status' => 'scheduled', + 'scheduled_at' => $newTime + ]); + } + } + + return [ + 'message' => __('Campaign has been updated', 'fluent-crm'), + 'campaign' => Campaign::findOrFail($id) + ]; + } + + public function duplicateCampaign(Request $request, $id) + { + $oldCampaign = Campaign::findOrFail($id); + $newCampaign = [ + 'title' => __('[Duplicate] ', 'fluent-crm') . $oldCampaign->title, + 'slug' => $oldCampaign->slug . '-' . time(), + 'email_body' => $oldCampaign->email_body, + 'status' => 'draft', + 'template_id' => $oldCampaign->template_id, + 'email_subject' => $oldCampaign->email_subject, + 'email_pre_header' => $oldCampaign->email_pre_header, + 'utm_status' => $oldCampaign->utm_status, + 'utm_source' => $oldCampaign->utm_source, + 'utm_medium' => $oldCampaign->utm_medium, + 'utm_campaign' => $oldCampaign->utm_campaign, + 'utm_term' => $oldCampaign->utm_term, + 'utm_content' => $oldCampaign->utm_content, + 'design_template' => $oldCampaign->design_template, + 'created_by' => get_current_user_id(), + 'settings' => $oldCampaign->settings + ]; + $labelIds = $oldCampaign->getFormattedLabels()->pluck('id')->toArray(); + + $campaign = Campaign::create($newCampaign); + $campaign->attachLabels($labelIds); + + $campaign->duplicateSubjects($oldCampaign); + + do_action('fluent_crm/campaign_duplicated', $campaign, $oldCampaign); + + return [ + 'campaign' => $campaign, + 'message' => __('Campaign has been successfully duplicated', 'fluent-crm') + ]; + + } + + public function unSchedule(Request $request, $id) + { + $campaign = Campaign::withoutGlobalScope('type') + ->whereIn('type', fluentCrmAutoProcessCampaignTypes()) + ->findOrFail($id); + + $validStatuses = [ + 'scheduled', + 'pending-scheduled', + 'processing' + ]; + + if (!in_array($campaign->status, $validStatuses)) { + return $this->sendError([ + 'message' => __('You can only un-schedule a campaign if it is on "scheduled" state, Please reload this page', 'fluent-crm') + ]); + } + + if ($campaign->status == 'processing' && strtotime($campaign->scheduled_at) < current_time('timestamp')) { + return $this->sendError([ + 'message' => __('You can only un-schedule a campaign if it is on "scheduled" state, Please reload this page', 'fluent-crm') + ]); + } + + $campaign->status = 'draft'; + $campaign->save(); + + // check if there has any emails, if yes then delete all of them + CampaignEmail::where('campaign_id', $campaign->id) + ->delete(); + + CampaignEmail::withoutGlobalScope('type')->where('campaign_id', $campaign->id) + ->whereIn('status', ['scheduled', 'scheduling']) + ->delete(); + + return [ + 'message' => __('Campaign has been successfully un-scheduled', 'fluent-crm') + ]; + } + + + public function getShareUrl($id) + { + $campaign = Campaign::withoutGlobalScope('type') + ->findOrFail($id); + + return [ + 'sharable_url' => $campaign->getShareableUrl() + ]; + } + + public function updateLabels(Request $request, $funnel_id) + { + $funnel = Campaign::findOrFail($funnel_id); + $action = $request->getSafe('action', 'sanitize_text_field'); + $labelIds = $request->get('label_ids'); + + if (!is_array($labelIds)) { + $labelIds = [$labelIds]; + } + + $labelIds = array_map('intval', $labelIds); + $labelIds = array_unique(array_filter($labelIds)); + + if ($action == 'detach') { + $funnel->detachLabels($labelIds); + } + + return $this->sendSuccess([ + 'message' => __('Labels has been updated', 'fluent-crm') + ]); + + } + + private function getCampaignSentData($campaignId) + { + $campaignSentById = fluentcrm_get_campaign_meta($campaignId, '_campaign_sent_by', true); + $user = get_userdata($campaignSentById); + if ($user) { + return $user->display_name . ' (' . $user->user_email . ')'; + } + return false; + } +} diff --git a/wp-content/plugins/fluent-crm/app/Http/Controllers/CompanyController.php b/wp-content/plugins/fluent-crm/app/Http/Controllers/CompanyController.php new file mode 100644 index 0000000..54d3218 --- /dev/null +++ b/wp-content/plugins/fluent-crm/app/Http/Controllers/CompanyController.php @@ -0,0 +1,924 @@ + $request->getSafe('sort_by', 'sanitize_sql_orderby', 'id'), + 'order' => $request->getSafe('sort_order', 'sanitize_sql_orderby', 'DESC') + ]; + + $companies = Company::orderBy($order['by'], $order['order']) + ->with(['owner']) + ->searchBy($request->getSafe('search', 'sanitize_text_field')); + + $inlineFilters = $request->get('inline_filters', []); + + if ($inlineFilters && is_array($inlineFilters)) { + $inlineFilters = array_filter($inlineFilters); + + foreach ($inlineFilters as $key => $values) { + if (!is_array($values)) { + continue; + } + $values = array_map('sanitize_text_field', $values); + + if ($key == 'company_categories') { + $companies->whereIn('industry', $values); + } else if ($key == 'company_types') { + $companies->whereIn('type', $values); + } + } + } + + $companies = $companies->paginate(); + + foreach ($companies as $company) { + $company->contacts_count = $company->getContactsCount(); + } + + return [ + 'companies' => $companies + ]; + } + + public function searchCompanies(Request $request) + { + $search = $request->getSafe('search', 'sanitize_text_field'); + $companies = Company::orderBy('name', 'ASC') + ->searchBy($search); + + $subscriberId = $request->getSafe('subscriber_id', 'intval'); + + if ($subscriberId) { + $companies = $companies->doesnthave('subscribers', 'and', function ($query) use ($subscriberId) { + $query->where('fc_subscribers.id', $subscriberId); + }); + } + + $companies = $companies->limit(50)->get(); + + $formatted = []; + + $values = (array)$request->get('values', []); + + $pushedIds = []; + + foreach ($companies as $company) { + $pushedIds[] = $company->id; + $formatted[] = [ + 'id' => $company->id, + 'name' => $company->name, + 'email' => $company->email, + 'logo' => $company->logo, + 'phone' => $company->phone, + 'website' => $company->website + ]; + } + + if ($values && $newIds = array_diff($values, $pushedIds)) { + $newItems = Company::whereIn('id', $newIds) + ->get(); + foreach ($newItems as $item) { + $formatted[] = [ + 'id' => $item->id, + 'name' => $item->name, + 'email' => $item->email, + 'logo' => $item->logo, + 'phone' => $item->phone, + 'website' => $item->website + ]; + } + } + + return [ + 'results' => $formatted, + 'has_more' => Company::count() >= 50 + ]; + } + + public function searchUnattachedContacts(Request $request) + { + $search = $request->getSafe('search', 'sanitize_text_field'); + $companyId = $request->getSafe('company_id', 'intval', ''); + + $contacts = Subscriber::orderBy('id', 'DESC') + ->searchBy($search) + ->whereDoesntHave('companies', function ($query) use ($companyId) { + $query->where('fc_companies.id', $companyId); + }) + ->limit($request->getSafe('limit', 'intval', 20)) + ->get(); + + return [ + 'results' => $contacts + ]; + } + + public function attachSubscribers(Request $request) + { + $subscriberIds = $request->get('subscriber_ids'); + $companyIds = $request->get('company_ids'); + + $result = FluentCrmApi('companies')->attachContactsByIds($subscriberIds, $companyIds); + + if (!$result) { + return $this->sendError('Invalid data', 422); + } + + return [ + 'message' => __('Selected Companies have been attached successfully', 'fluent-crm'), + 'companies' => $result['companies'] + ]; + } + + public function detachSubscribers(Request $request) + { + $subscriberIds = $request->get('subscriber_ids'); + $companyIds = $request->get('company_ids'); + + $result = FluentCrmApi('companies')->detachContactsByIds($subscriberIds, $companyIds); + + if (!$result) { + return $this->sendError('Invalid data', 422); + } + $result['message'] = __('Company has been successfully detached', 'fluent-crm'); + + return $result; + } + + /** + * Find a company. + */ + public function find(Request $request, $id) + { + + $findBy = $request->getSafe('find_by', 'sanitize_text_field', 'id'); + $findByValue = $request->getSafe('find_by_value', 'sanitize_text_field'); + + $customFindBys = ['name', 'email', 'phone']; + + if (in_array($findBy, $customFindBys)) { + $company = Company::where($findBy, $findByValue)->first(); + if (!$company) { + return $this->sendError('Company not found', 422); + } + } else { + $company = Company::findOrFail($id); + } + + $company->load(['owner']); + if ($company->owner) { + $company->owner->stats = $company->owner->stats(); + } + + $company->contacts_count = $company->getContactsCount(); + + return [ + 'company' => $company + ]; + } + + /** + * Store a company. + * @param Request $request + * @return \WP_REST_Response | array + */ + public function create(Request $request) + { + $allData = $request->all(); + + $allData = $this->validate($allData, [ + 'name' => 'required|unique:fc_companies,name' + ]); + + $data = $this->getSanitizedData($allData); + + if (empty($data['logo']) && !empty($allData['website']) && Helper::isExperimentalEnabled('company_auto_logo')) { + $data['logo'] = $this->getLogoWebsiteUrl($allData['website']); + } + + $company = FluentCrmApi('companies')->createOrUpdate($data); + + if ($contactId = $request->getSafe('intended_contact_id', 'intval')) { + $contact = Subscriber::find($contactId); + if ($contact) { + $contact->attachCompanies([$company->id]); + if (!$contact->company_id) { + $contact->company_id = $company->id; + $contact->save(); + } + } + } + + return [ + 'message' => __('Company has been created successfully', 'fluent-crm'), + 'company' => $company + ]; + } + + public function update(Request $request, $id = 0) + { + if ($id == 0) { + return $this->create($request); + } + + $company = Company::findOrFail($id); + + $allData = $request->all(); + + $name = sanitize_text_field($allData['name']); + + if (Company::where('id', '!=', $id)->where('name', $name)->first()) { + return $this->sendError([ + 'message' => __('Company name already exists. Please use a different company name', 'fluent-crm') + ], 422); + } + + $data = $this->getSanitizedData($allData); + + $company = FluentCrmApi('companies')->createOrUpdate($data); + + return [ + 'message' => __('Company has been updated', 'fluent-crm'), + 'company' => $company + ]; + + } + + public function updateProperty() + { + $column = $this->request->getSafe('property', 'sanitize_text_field'); + $value = $this->request->getSafe('value', 'sanitize_text_field'); + $companyIds = $this->request->get('companies'); + + if (!is_array($companyIds)) { + $companyIds = [$companyIds]; + } + $companyIds = array_map('intval', $companyIds); + $companyIds = array_filter($companyIds); + + $validColumns = ['type', 'logo', 'owner_id', 'refetch_logo']; + $types = Helper::companyTypes(); + $statuses = Helper::companyTypes(); + + $this->validate([ + 'column' => $column, + 'value' => $value, + 'company_ids' => $companyIds + ], [ + 'column' => 'required', + 'value' => 'required', + 'company_ids' => 'required' + ]); + + if (!in_array($column, $validColumns)) { + return $this->sendError([ + 'message' => __('Column is not valid', 'fluent-crm') + ]); + } + + if ($column == 'type' && !in_array($value, $types)) { + return $this->sendError([ + 'message' => __('Value is not valid', 'fluent-crm') + ]); + } else if ($column == 'status' && !in_array($value, $statuses)) { + return $this->sendError([ + 'message' => __('Value is not valid', 'fluent-crm') + ]); + } + + $companies = Company::whereIn('id', $companyIds)->get(); + + foreach ($companies as $company) { + + if ($column == 'refetch_logo') { + $newLogo = $this->getLogoWebsiteUrl($company->website); + if ($newLogo) { + $company->logo = $newLogo; + $company->save(); + return [ + 'message' => __('Logo has been updated successfully', 'fluent-crm'), + 'updated_logo' => $newLogo + ]; + } + + return $this->sendError([ + 'message' => __('Sorry, we could not find the logo from website. Please upload manually', 'fluent-crm') + ]); + } + + $oldValue = $company->{$column}; + if ($oldValue != $value) { + $company->{$column} = $value; + $company->save(); + if (in_array($column, ['type', 'status', 'owner_id'])) { + do_action('fluent_crm/company_' . $column . '_to_' . $value, $company, $oldValue); + } + } + } + + return $this->sendSuccess([ + 'message' => __('Company successfully updated', 'fluent-crm') + ]); + } + + public function delete(Request $request, $id) + { + $company = Company::findOrFail($id); + do_action('fluent_crm/before_company_delete', $company); + $company->delete(); + do_action('fluent_crm/company_deleted', $id); + + return [ + 'message' => __('Company has been deleted successfully', 'fluent-crm') + ]; + } + + public function handleBulkActions(Request $request) + { + $actionName = sanitize_text_field($request->get('action_name', '')); + + $companyIds = array_map('intval', $request->get('company_ids', [])); + $companyIds = array_filter($companyIds); + $lastId = $request->get('last_id', 0); + + if (!$companyIds) { + + + $companyQuery = Company::orderBy('id', 'ASC') + ->searchBy($request->getSafe('search', 'sanitize_text_field')); + + $inlineFilters = $request->get('company_query.inline_filters', []); + + if ($inlineFilters && is_array($inlineFilters)) { + $inlineFilters = array_filter($inlineFilters); + + foreach ($inlineFilters as $key => $values) { + if (!is_array($values)) { + continue; + } + $values = array_map('sanitize_text_field', $values); + + if ($key == 'company_categories') { + $companyQuery->whereIn('industry', $values); + } else if ($key == 'company_types') { + $companyQuery->whereIn('type', $values); + } + } + } + $companyQuery = $companyQuery->limit(50) + ->where('id', '>', $lastId); + } else { + $companyQuery = Company::whereIn('id', $companyIds); + } + + $companies = $companyQuery->get(); + if ($companies->isEmpty()) { + return [ + 'is_completed' => true, + 'completed_companies' => 0, + 'message' => __('All companies have been processed', 'fluent-crm') + ]; + } + $companyIds = $companyQuery->pluck('id')->toArray(); + $lastCompanyId = end($companyIds); + + if ($actionName == 'delete_companies') { + foreach ($companies as $company) { + $id = $company->id; + do_action('fluent_crm/before_company_delete', $company); + $company->delete(); + do_action('fluent_crm/company_deleted', $id); + } + + return $this->sendSuccess([ + 'last_company_id' => $lastCompanyId, + 'completed_companies' => count($companyIds), + 'message' => __('Selected Companies have been deleted permanently', 'fluent-crm'), + ]); + } elseif ($actionName == 'change_company_status') { + $newStatus = sanitize_text_field($request->get('new_status', '')); + if (!$newStatus) { + return $this->sendError([ + 'message' => __('Please select status', 'fluent-crm') + ]); + } + + foreach ($companies as $company) { + $oldStatus = $company->status; + if ($oldStatus != $newStatus) { + $company->status = $newStatus; + $company->save(); + do_action('fluent_crm/company_status_to_' . $newStatus, $company, $oldStatus); + } + } + + return [ + 'last_company_id' => $lastCompanyId, + 'completed_companies' => count($companyIds), + 'message' => __('Status has been changed for the selected companies', 'fluent-crm') + ]; + } else if ($actionName == 'change_company_type') { + $newType = sanitize_text_field($request->get('new_status', '')); + if (!$newType) { + return $this->sendError([ + 'message' => __('Please select new type', 'fluent-crm') + ]); + } + foreach ($companies as $company) { + $oldType = $company->type; + if ($oldType != $newType) { + $company->type = $newType; + $company->save(); + do_action('fluent_crm/company_type_to_' . $newType, $company, $oldType); + } + } + + return [ + 'last_company_id' => $lastCompanyId, + 'completed_companies' => count($companyIds), + 'message' => __('Company Type has been updated for the selected companies', 'fluent-crm') + ]; + } else if ($actionName == 'change_company_category') { + $newCategory = sanitize_text_field($request->get('new_status', '')); + if (!$newCategory) { + return $this->sendError([ + 'message' => __('Please select new category', 'fluent-crm') + ]); + } + foreach ($companies as $company) { + $oldCategory = $company->industry; + if ($oldCategory != $newCategory) { + $company->industry = $newCategory; + $company->save(); + do_action('fluent_crm/company_category_to_' . $newCategory, $company, $oldCategory); + } + } + + return [ + 'last_company_id' => $lastCompanyId, + 'completed_companies' => count($companyIds), + 'message' => __('Company Category has been updated for the selected companies', 'fluent-crm') + ]; + } + + return [ + 'last_company_id' => $lastCompanyId, + 'completed_companies' => count($companyIds), + 'message' => __('Selected bulk action has been successfully completed', 'fluent-crm') + ]; + } + + private function getSanitizedData($allData) + { + $rules = [ + 'name' => 'required' + ]; + + if (Arr::get($allData, 'website')) { + $allData['website'] = $this->makeHttpUrl($allData['website']); + $rules['website'] = 'url'; + } + + if (Arr::get($allData, 'linkedin_url')) { + $allData['linkedin_url'] = $this->makeHttpUrl($allData['linkedin_url']); + $rules['linkedin_url'] = 'url'; + } + + if (Arr::get($allData, 'facebook_url')) { + $allData['facebook_url'] = $this->makeHttpUrl($allData['facebook_url']); + $rules['facebook_url'] = 'url'; + } + + if (Arr::get($allData, 'twitter_url')) { + $allData['twitter_url'] = $this->makeHttpUrl($allData['twitter_url']); + $rules['twitter_url'] = 'url'; + } + + $allData = $this->validate($allData, $rules); + + $data = Sanitize::company($allData); + + return Arr::only($data, array_keys($allData)); + } + + private function makeHttpUrl($url) + { + if (!$url) { + return $url; + } + $parsed_url = wp_parse_url($url); + if (!$parsed_url || empty($parsed_url['scheme'])) { + $url = 'https://' . $url; + } + + return $url; + } + + /** + * Returns true only if the URL resolves to a public, routable IP address. + * Blocks private/reserved ranges to prevent SSRF attacks. + */ + private function isSSRFSafeUrl($url) + { + $parsed = wp_parse_url($url); + if (!$parsed || empty($parsed['host'])) { + return false; + } + + $scheme = strtolower($parsed['scheme'] ?? ''); + if (!in_array($scheme, ['http', 'https'])) { + return false; + } + + $host = $parsed['host']; + // Strip IPv6 brackets if present + $host = trim($host, '[]'); + + // If it looks like a raw IP, validate directly; otherwise resolve the hostname + if (filter_var($host, FILTER_VALIDATE_IP)) { + $ip = $host; + } else { + $ip = gethostbyname($host); + // gethostbyname() returns the original string on failure + if ($ip === $host && !filter_var($ip, FILTER_VALIDATE_IP)) { + return false; + } + } + + // Reject private, loopback, link-local, and other reserved ranges + return (bool) filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE); + } + + private function getLogoWebsiteUrl($url) + { + if (!$url) { + return NULL; + } + + $url = $this->makeHttpUrl($url); + + if (!$this->isSSRFSafeUrl($url)) { + return NULL; + } + + $response = wp_remote_get($url, [ + 'sslverify' => false, // Disable SSL verification to avoid 403 Forbidden error + 'timeout' => 10, // Set a timeout of 10 seconds + 'user-agent' => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3' // Set a User-Agent header to avoid 403 Forbidden error + ]); + + // Check for errors in the response + if (is_wp_error($response)) { + return NULL; + } + + // Extract the HTML content from the response + $html = wp_remote_retrieve_body($response); + + preg_match('/isSSRFSafeUrl($logoUrl)) { + return NULL; + } + + $uploadDir = wp_upload_dir(); // Get the uploads directory + + $filename = md5($url . time()) . '-' . basename($logoUrl); // Get the filename from the URL + $filepath = $uploadDir['basedir'] . '/fluentcrm/' . $filename; // Combine the uploads directory path with the filename + + // Download the image using wp_remote_get() and save it to the uploads directory + $image = wp_remote_get($logoUrl, [ + 'timeout' => 10, // Set a timeout of 10 seconds + 'sslverify' => false // Disable SSL verification to avoid 403 Forbidden error + ]); + + if (!is_wp_error($image)) { + // Check if the downloaded file is actually an image + $headers = wp_remote_retrieve_headers($image); + $imageBody = wp_remote_retrieve_body($image); + if (defined('FILEINFO_MIME_TYPE') && class_exists('\finfo')) { + $finfo = new \finfo(FILEINFO_MIME_TYPE); + $content_type = $finfo->buffer($imageBody); + } else { + $content_type = wp_remote_retrieve_header($headers, 'content-type'); + if (!$content_type) { + $content_type = Arr::get($headers, 'content-type'); + } + + if (strpos($content_type, 'image/') !== 0) { + return null; + } + + // Temporary file to validate the image + $tmpFilePath = tempnam(sys_get_temp_dir(), 'tmpimg'); + file_put_contents($tmpFilePath, $imageBody); + $imgSize = getimagesize($tmpFilePath); + wp_delete_file($tmpFilePath); + if (!$imgSize) { + return null; + } + } + + if (strpos($content_type, 'image/') === 0) { + global $wp_filesystem; + if (!$wp_filesystem) { + require_once(ABSPATH . '/wp-admin/includes/file.php'); + WP_Filesystem(); + } + + FileSystem::setCustomUploadDir([ + 'baseurl' => $uploadDir['baseurl'], + 'basedir' => $uploadDir['basedir'], + ]); + + $wp_filesystem->put_contents($filepath, $imageBody); + // Return the URL of the saved image + return $uploadDir['baseurl'] . FLUENTCRM_UPLOAD_DIR . '/' . $filename; + } else { + // If the downloaded file is not an image, delete the file and return null + wp_delete_file($filepath); + } + } + } + + // If no logo URL is found, or if an error occurs, or if the downloaded file is not an image, return null + return NULL; + } + + public function getNotes() + { + $companyId = $this->request->get('id'); + $search = $this->request->get('search'); + $includeId = intval($this->request->get('include_id', 0)); + + $notes = CompanyNote::where('subscriber_id', $companyId); + + if (!empty($search)) { + global $wpdb; + $notes = $notes->where('title', 'LIKE', '%' . $wpdb->esc_like(sanitize_text_field($search)) . '%'); + } + + $notes = $notes->orderBy('id', 'DESC') + ->paginate(); + + foreach ($notes as $note) { + $note->added_by = $note->createdBy(); + } + $fields['fields'] = Helper::getNoteSyncFields(); + + $response = [ + 'notes' => $notes, + 'fields' => $fields + ]; + + if ($includeId) { + $noteIds = (new Collection($notes->items()))->pluck('id')->toArray(); + if (!in_array($includeId, $noteIds)) { + $includedNote = CompanyNote::where('id', $includeId) + ->where('subscriber_id', $companyId) + ->first(); + if ($includedNote) { + $includedNote->added_by = $includedNote->createdBy(); + $response['included_note'] = $includedNote; + } + } + } + + return $this->sendSuccess($response); + } + + public function addNote(Request $request, $id) + { + $company = Company::findOrFail($id); + $note = $this->validate($request->get('note'), [ + 'title' => 'required', + 'description' => 'required', + 'type' => 'required', + 'created_at' => 'nullable|date' + ]); + + if (empty($note['created_at'])) { + $note['created_at'] = current_time('mysql'); + } + + $note['subscriber_id'] = $id; + + $note = Sanitize::contactNote($note); + + $subscriberNote = CompanyNote::create(wp_unslash($note)); + + /** + * Subscriber's Note Added + * + * @param SubscriberNote $subscriberNote Note Model. + * @param Subscriber $subscriber Contact Model. + * @param array $note Contact Note Data Array. + * @since 1.0 + */ + do_action('fluent_crm/company_note_added', $subscriberNote, $company, $note); + + return $this->sendSuccess([ + 'note' => $subscriberNote, + 'message' => __('Note has been successfully added', 'fluent-crm') + ]); + } + + public function updateNote(Request $request, $id, $noteId) + { + $company = Company::findOrFail($id); + + $note = $this->validate($request->get('note'), [ + 'title' => 'required', + 'description' => 'required', + 'type' => 'required', + 'created_at' => 'sometimes|date' + ]); + + $note = Arr::only(wp_unslash($note), ['title', 'description', 'type', 'created_at']); + + if (empty($note['created_at'])) { + unset($note['created_at']); + } + + $note = Sanitize::contactNote($note); + + $companyNote = CompanyNote::findOrFail($noteId); + $companyNote->fill($note); + $companyNote->save(); + + /** + * Subscriber's Note Updated + * + * @param CompanyNote $companyNote Note Model. + * @param Company $company Contact Model. + * @param array $note Contact Note Data Array. + * @since 1.0 + */ + do_action('fluent_crm/company_note_updated', $companyNote, $company, $note); + + return $this->sendSuccess([ + 'note' => $companyNote, + 'message' => __('Note successfully updated', 'fluent-crm') + ]); + } + + public function deleteNote($id, $noteId) + { + $company = Company::findOrFail($id); + CompanyNote::where('id', $noteId)->delete(); + + /** + * Subscriber's Note Delete + * + * @param int $noteId Note ID. + * @param Company $company Company Model. + * @since 1.0 + */ + do_action('fluent_crm/company_note_deleted', $noteId, $company); + + return $this->sendSuccess([ + 'message' => __('Note successfully deleted', 'fluent-crm') + ]); + } + + public function bulkDeleteNotes(Request $request, $id) + { + $company = Company::findOrFail($id); + $noteIds = array_filter(array_map('intval', (array) $request->get('note_ids', []))); + + if (empty($noteIds)) { + return $this->sendError([ + 'message' => __('No note IDs provided', 'fluent-crm') + ]); + } + + if (count($noteIds) > 200) { + return $this->sendError([ + 'message' => __('Too many notes selected. Please delete 200 or fewer notes at a time.', 'fluent-crm') + ]); + } + + // Scope delete to this company so users cannot delete notes belonging to other companies. + $deletableNoteIds = CompanyNote::where('subscriber_id', $company->id) + ->whereIn('id', $noteIds) + ->pluck('id') + ->toArray(); + + $deletedCount = 0; + if ($deletableNoteIds) { + $deletedCount = CompanyNote::whereIn('id', $deletableNoteIds)->delete(); + + foreach ($deletableNoteIds as $deletedNoteId) { + do_action('fluent_crm/company_note_deleted', $deletedNoteId, $company); + } + } + + return $this->sendSuccess([ + 'message' => sprintf( + /* translators: %d: number of deleted notes */ + _n('%d note deleted', '%d notes deleted', $deletedCount, 'fluent-crm'), + $deletedCount + ) + ]); + } + + public function getCustomGlobalFields(CustomCompanyField $model) + { + return $this->sendSuccess( + $model->getGlobalFields( + $this->request->get('with', []) + ) + ); + } + + public function saveCustomGlobalFields(CustomCompanyField $model) + { + $fields = $model->saveGlobalFields( + Helper::parseArrayOrJson($this->request->get('fields')) + ); + + return $this->sendSuccess([ + 'fields' => $fields, + 'message' => __('Fields saved successfully!', 'fluent-crm') + ]); + } + + public function updateCustomFieldGroupName(CustomCompanyField $model) + { + $oldName = sanitize_text_field($this->request->get('old_name')); + $newName = sanitize_text_field($this->request->get('new_name')); + $updatedCustomFields = $model->updateGroupName($oldName, $newName); + + return $this->sendSuccess([ + 'fields' => $updatedCustomFields, + 'message' => __('Group name updated successfully!', 'fluent-crm') + ]); + } + + public function getCompanyExternalView(Request $request, $companyId) + { + $company = Company::findOrFail($companyId); + $sectionId = $request->get('section_provider'); + + return apply_filters('fluent_crm/company_profile_section_' . $sectionId, [ + 'heading' => '', + 'content_html' => '' + ], $company); + } + + public function saveExternalViewData(Request $request, $companyId) + { + $company = Company::findOrFail($companyId); + $sectionId = $request->get('section_provider'); + + $response = apply_filters('fluent_crm/company_profile_section_save_' . $sectionId, '', $request->get('data', []), $company); + + if (!$response) { + return $this->sendError([ + 'message' => __('Handler could not be found.', 'fluent-crm') + ]); + } + + return $response; + } +} diff --git a/wp-content/plugins/fluent-crm/app/Http/Controllers/Controller.php b/wp-content/plugins/fluent-crm/app/Http/Controllers/Controller.php new file mode 100644 index 0000000..324e129 --- /dev/null +++ b/wp-content/plugins/fluent-crm/app/Http/Controllers/Controller.php @@ -0,0 +1,103 @@ +app = App::getInstance(); + $this->request = $this->app['request']; + $this->response = $this->app['response']; + } + + public function validate($data, $rules, $messages = []) + { + $validator = new Validator($data, $rules, $messages); + + if ($validator->validate()->fails()) { + // Sanitize validation error messages before returning them + $errors = $validator->errors(); + if (is_array($errors)) { + array_walk_recursive($errors, function (&$value) { + if (is_string($value)) { + $value = sanitize_text_field($value); + } + }); + } + + // phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped -- Sanitization is already done above + throw new ValidationException( + esc_html__('Unprocessable Entity!', 'fluent-crm'), + 422, + null, + $errors + ); + } + + return $data; + } + + public function send($data = null, $code = 200) + { + return $this->response->send($data, $code); + } + + public function sendSuccess($data = null, $code = 200) + { + return $this->response->sendSuccess($data, $code); + } + + public function sendError($data = null, $code = 422) + { + return $this->response->sendError($data, $code); + } + + public function validationErrors($data = null, $code = 422) + { + if ($data instanceof ValidationException) { + $data = $data->errors(); + } + + // Sanitize error payload before sending the response to prevent unescaped output + if (is_array($data)) { + array_walk_recursive($data, function (&$value) { + if (is_string($value)) { + $value = sanitize_text_field($value); + } + }); + } elseif (is_string($data)) { + $data = sanitize_text_field($data); + } + + return $this->sendError($data, $code); + } +} diff --git a/wp-content/plugins/fluent-crm/app/Http/Controllers/CsvController.php b/wp-content/plugins/fluent-crm/app/Http/Controllers/CsvController.php new file mode 100644 index 0000000..e76d2c8 --- /dev/null +++ b/wp-content/plugins/fluent-crm/app/Http/Controllers/CsvController.php @@ -0,0 +1,494 @@ +validate($this->request->files(), [ + 'file' => 'mimetypes:' . implode(',', fluentcrmCsvMimes()) + ], [ + 'file.mimetypes' => __('The file must be a valid CSV.', 'fluent-crm') + ]); + + $delimeter = $request->get('delimiter', 'comma'); + + if ($delimeter == 'comma') { + $delimeter = ','; + } else { + $delimeter = ';'; + } + + $uploadedFiles = FileSystem::put($files); + + try { + $csv = $this->getCsvReader(FileSystem::get($uploadedFiles[0]['file'])); + $csv->setDelimiter($delimeter); + $headers = $csv->fetchOne(); + } catch (\Exception $exception) { + return $this->sendError([ + 'message' => $exception->getMessage() + ]); + } + + if (count($headers) != count(array_unique($headers))) { + return $this->sendError([ + 'message' => __('Looks like your csv has same name header multiple times. Please fix your csv first and remove any duplicate header column', 'fluent-crm') + ]); + } + + + if ($request->get('type') == 'company') { + $mappables = Company::mappables(); + } else { + $mappables = Subscriber::mappables(); + } + + $headerItems = array_values(array_filter($headers)); + $subscriberColumns = array_keys($mappables); + + $maps = []; + + $customFields = fluentcrm_get_custom_contact_fields(); + + $fieldsMap = []; + if ($customFields) { + foreach ($customFields as $field) { + $fieldsMap[$field['slug']] = $field['label']; + } + } + + foreach ($headerItems as $headerItem) { + $tableMap = (in_array($headerItem, $subscriberColumns)) ? $headerItem : null; + + if (!$tableMap) { + $santizedItem = str_replace(' ', '_', strtolower($headerItem)); + if (in_array($santizedItem, $subscriberColumns)) { + $tableMap = $santizedItem; + } + } + + if (!empty($fieldsMap) && in_array($headerItem, $fieldsMap)) { + $tableMap = array_search($headerItem, $fieldsMap); + } + + $maps[] = [ + 'csv' => $headerItem, + 'table' => $tableMap + ]; + } + + if ($request->get('type') == 'company') { + /** + * Determine the columns of the company table in FluentCRM. + * + * This filter allows you to modify the columns of the company table in the CSV export. + * + * @since 2.8.0 + * + * @param array $subscriberColumns An array of default subscriber columns. + */ + $columns = apply_filters( + 'fluent_crm/company_table_columns', $subscriberColumns + ); + } else { + /** + * Determine the columns of the subscriber table in FluentCRM. + * + * This filter allows you to modify the columns displayed in the subscriber table. + * + * @since 2.8.0 + * + * @param array $subscriberColumns An array of default subscriber table columns. + */ + $columns = apply_filters( + 'fluent_crm/subscriber_table_columns', $subscriberColumns + ); + } + + return $this->send([ + 'file' => $uploadedFiles[0]['file'], + 'headers' => $headerItems, + 'fields' => $mappables, + 'columns' => $columns, + 'map' => $maps + ]); + } + + public function import() + { + $inputs = $this->request->only([ + 'map', 'tags', 'lists', 'file', 'update', 'new_status', 'double_optin_email', 'import_silently', 'force_update_status' + ]); + + if (Arr::get($inputs, 'import_silently') == 'yes') { + if (!defined('FLUENTCRM_DISABLE_TAG_LIST_EVENTS')) { + define('FLUENTCRM_DISABLE_TAG_LIST_EVENTS', true); + } + } + + $forceStatusChange = Arr::get($inputs, 'force_update_status') == 'yes'; + + $delimeter = $this->request->get('delimiter', 'comma'); + + if ($delimeter == 'comma') { + $delimeter = ','; + } else { + $delimeter = ';'; + } + + $status = $inputs['new_status']; + + try { + $reader = $this->getCsvReader(FileSystem::get($inputs['file'])); + $reader->setDelimiter($delimeter); + + if (method_exists($reader, 'getRecords')) { + $aHeaders = $reader->fetchOne(0); + + $allRecords = $reader->getRecords($aHeaders); + + if (!is_array($allRecords)) { + $allRecords = iterator_to_array($allRecords, true); + } + + unset($allRecords[0]); + $allRecords = array_values($allRecords); + } else { + $aHeaders = $reader->fetchOne(0); + $allRecords = $reader->fetchAssoc($aHeaders); + if (!is_array($allRecords)) { + $allRecords = iterator_to_array($allRecords, true); + } + + unset($allRecords[0]); + + $allRecords = array_values($allRecords); + } + } catch (\Exception $exception) { + return $this->sendError([ + 'message' => $exception->getMessage() + ]); + } + + + $page = $this->request->get('importing_page', 1); + + $processPerRequest = apply_filters('fluent_crm/csv_import_contact_limit_per_request', 100); + + $offset = ($page - 1) * $processPerRequest; + $records = array_slice($allRecords, $offset, $processPerRequest); + + + $customFieldKeys = $this->customFieldKeys(); + $subscribers = []; + $skipped = []; + + $isCompanyEnabled = Helper::isCompanyEnabled(); + + foreach ($records as $record) { + if (!array_filter($record)) { + continue; + } + + $subscriber = [ + 'custom_values' => [] + ]; + foreach ($inputs['map'] as $map) { + if (!$map['table']) { + continue; + } + if (isset($map['csv'], $map['table'])) { + if (in_array($map['table'], ['tags', 'lists'])) { + //if tags or lists are mapped to be imported + if ($map['table'] == 'tags') { + $subscriber['tags'] = !empty($record[$map['csv']]) ? explode(',', $record[$map['csv']]) : []; + } else { + $subscriber['lists'] = !empty($record[$map['csv']]) ? explode(',', $record[$map['csv']]) : []; + } + } + else if (in_array($map['table'], $customFieldKeys)) { + $subscriber['custom_values'][$map['table']] = $record[$map['csv']]; + } else { + $subscriber[$map['table']] = $record[$map['csv']]; + } + } + } + + if (!array_key_exists('email', $subscriber)) { + return $this->sendError(['email' => __('The email field is required.', 'fluent-crm')], 422); + } + + $subscriber['email'] = is_string($subscriber['email']) ? trim($subscriber['email']) : $subscriber['email']; + + if ($subscriber['email'] && is_email($subscriber['email'])) { + + if (isset($subscriber['company_id']) && $subscriber['company_id'] && $isCompanyEnabled) { + $companyNameOrId = $subscriber['company_id']; + if (is_string($companyNameOrId)) { + $company = Company::query()->firstOrCreate([ + 'name' => $subscriber['company_id'] + ], [ + 'name' => $subscriber['company_id'] + ]); + + if ($company) { + $subscriber['company_id'] = $company->id; + } else { + unset($subscriber['company_id']); + } + } else { + $company = Company::find($subscriber['company_id']); + if (!$company) { + unset($subscriber['company_id']); + } + } + } + + $subscribers[] = Sanitize::contact($subscriber); + } else { + $skipped[] = $subscriber; + } + } + + if (!isset($inputs['tags'])) { + $inputs['tags'] = []; + } + + if (!isset($inputs['lists'])) { + $inputs['lists'] = []; + } + + $sendDoubleOptin = Arr::get($inputs, 'double_optin_email') == 'yes'; + + $result = Subscriber::import( + $subscribers, $inputs['tags'], $inputs['lists'], $inputs['update'], $status, $sendDoubleOptin, $forceStatusChange, 'csv' + ); + + $totalSkipped = count($result['skips']) + count($skipped); + + $completed = $offset + count($records); + $totalCount = count($allRecords); + $hasMore = $completed < $totalCount; + if (!$hasMore) { + FileSystem::delete($inputs['file']); + } + + return $this->sendSuccess([ + 'total' => $totalCount, + 'completed' => $completed, + 'total_page' => ceil($totalCount / $processPerRequest), + 'skipped' => $totalSkipped, + 'invalid_contacts' => $skipped, + 'skipped_contacts' => $result['skips'], + 'invalid_email_counts' => count($skipped), + 'inserted' => count($result['inserted']), + 'updated' => count($result['updated']), + 'has_more' => $hasMore, + 'last_page' => $page, + 'tags' => $inputs['tags'], + 'lists' => $inputs['lists'], + 'offset' => $offset, + 'result' => $result + ]); + } + + public function importCompanies() + { + $inputs = $this->request->only([ + 'map', 'file', 'update', 'create_owner' + ]); + + $delimeter = $this->request->get('delimiter', 'comma'); + + if ($delimeter == 'comma') { + $delimeter = ','; + } else { + $delimeter = ';'; + } + + try { + $reader = $this->getCsvReader(FileSystem::get($inputs['file'])); + $reader->setDelimiter($delimeter); + + if (method_exists($reader, 'getRecords')) { + $aHeaders = $reader->fetchOne(0); + + $allRecords = $reader->getRecords($aHeaders); + + if (!is_array($allRecords)) { + $allRecords = iterator_to_array($allRecords, true); + } + + unset($allRecords[0]); + $allRecords = array_values($allRecords); + } else { + $aHeaders = $reader->fetchOne(0); + $allRecords = $reader->fetchAssoc($aHeaders); + if (!is_array($allRecords)) { + $allRecords = iterator_to_array($allRecords, true); + } + + unset($allRecords[0]); + + $allRecords = array_values($allRecords); + } + } catch (\Exception $exception) { + return $this->sendError([ + 'message' => $exception->getMessage() + ]); + } + + $page = $this->request->get('importing_page', 1); + $processPerRequest = 100; + $offset = ($page - 1) * $processPerRequest; + $records = array_slice($allRecords, $offset, $processPerRequest); + + $willCreateOwner = $this->request->get('create_owner') == 'yes'; + $willUpdate = $this->request->get('update') == 'yes'; + + $customFields = fluentcrm_get_custom_company_fields(); + + $companies = []; + $skipped = []; + foreach ($records as $record) { + if (!array_filter($record)) { + continue; + } + + $company = []; + foreach ($inputs['map'] as $map) { + if (!$map['table']) { + continue; + } + if (isset($map['csv'], $map['table'])) { + $company[$map['table']] = trim($record[$map['csv']]); + } + } + + if (empty($company['name'])) { + return $this->sendError(['email' => __('The company name field is required.', 'fluent-crm')], 422); + } + + if (!$willUpdate) { + // check if exists + if (Company::where('name', $company['name'])->first()) { + $skipped[] = $company; + continue; + } + } + + + if ($customFields) { + $customValues = []; + + foreach ($company as $dataKey => $dataValue) { + if (strpos($dataKey, '_custom_') === 0) { + $customKey = str_replace('_custom_', '', $dataKey); + $customValues[$customKey] = $dataValue; + unset($company[$dataKey]); + } + } + + $company['custom_values'] = $customValues; + } + + $company = Sanitize::company($company); + + if (!empty($company['owner_email']) && is_email($company['owner_email'])) { + $ownerEmail = sanitize_email($company['owner_email']); + } else { + $ownerEmail = null; + } + + if ($ownerEmail) { + $owner = FluentCrmApi('contacts')->getContact($ownerEmail); + if ($owner) { + $company['owner_id'] = $owner->id; + } else if ($willCreateOwner) { + $owner = FluentCrmApi('contacts')->createOrUpdate([ + 'full_name' => sanitize_text_field(Arr::get($company, 'owner_name')), + 'email' => $ownerEmail, + 'status' => 'subscribed' + ]); + + if ($owner) { + $company['owner_id'] = $owner->id; + } + } + } + + $createdCompany = FluentCrmApi('companies')->createOrUpdate($company); + $companies[] = $createdCompany; + } + + $completed = $offset + count($companies); + $totalCount = count($allRecords); + $hasMore = $completed < $totalCount; + if (!$hasMore) { + FileSystem::delete($inputs['file']); + } + + return $this->sendSuccess([ + 'total' => $totalCount, + 'completed' => count($companies), + 'total_page' => ceil($totalCount / $processPerRequest), + 'skipped' => count($skipped), + 'has_more' => $hasMore, + 'last_page' => $page, + 'offset' => $offset + ]); + } + + protected function customFieldKeys() + { + $fields = fluentcrm_get_option('contact_custom_fields', []); + $keys = []; + foreach ($fields as $field) { + $keys[] = $field['slug']; + } + return $keys; + } + + private function getCsvReader($file) + { + if (!class_exists(' \League\Csv\Reader')) { + include FLUENTCRM_PLUGIN_PATH . 'app/Services/Libs/csv/autoload.php'; + } + + return \League\Csv\Reader::createFromString($file); + } +} diff --git a/wp-content/plugins/fluent-crm/app/Http/Controllers/CustomContactFieldsController.php b/wp-content/plugins/fluent-crm/app/Http/Controllers/CustomContactFieldsController.php new file mode 100644 index 0000000..fc12156 --- /dev/null +++ b/wp-content/plugins/fluent-crm/app/Http/Controllers/CustomContactFieldsController.php @@ -0,0 +1,51 @@ +sendSuccess( + $model->getGlobalFields( + $this->request->get('with', []) + ) + ); + } + + public function saveGlobalFields(CustomContactField $model) + { + $fields = $model->saveGlobalFields( + Helper::parseArrayOrJson($this->request->get('fields')) + ); + + return $this->sendSuccess([ + 'fields' => $fields, + 'message' => __('Fields saved successfully!', 'fluent-crm') + ]); + } + + public function updateGroupName(CustomContactField $model) + { + $oldName = sanitize_text_field($this->request->get('old_name')); + $newName = sanitize_text_field($this->request->get('new_name')); + $updatedCustomFields = $model->updateGroupName($oldName, $newName); + + return $this->sendSuccess([ + 'fields' => $updatedCustomFields, + 'message' => __('Group name updated successfully!', 'fluent-crm') + ]); + } +} diff --git a/wp-content/plugins/fluent-crm/app/Http/Controllers/DashboardController.php b/wp-content/plugins/fluent-crm/app/Http/Controllers/DashboardController.php new file mode 100644 index 0000000..5db2231 --- /dev/null +++ b/wp-content/plugins/fluent-crm/app/Http/Controllers/DashboardController.php @@ -0,0 +1,268 @@ +getCounts(); + + $nextMinuteTask = Helper::getNextMinuteTaskTimeStamp(); + + $notices = []; + + if ((time() - $nextMinuteTask) > 120) { + $notices[] = '
Attention: Looks like the scheduled cron jobs are not running timely. Please consider setup server side cron. Click here to check the status
'; + } + + $systemTips = ''; + $emailsCount = Arr::get($overallStats, 'email_sent.count', 0); + if ($emailsCount > 400000) { + $lastEmail = CampaignEmail::orderBy('id', 'ASC')->first(); + if ($lastEmail && strtotime($lastEmail->created_at) < strtotime('-120 days')) { + $emailsCount = number_format($emailsCount, 0); + $sysBody = '
'; + /* translators: %s: number of emails in the database */ + $sysBody .= '

' . sprintf(__('You have %s email history in the database. Consider cleaning up old email history to speed up your next email campaign.', 'fluent-crm'), $emailsCount) . '

'; + $sysBody .= '' . __('View Data Cleanup', 'fluent-crm') . ''; + $sysBody .= '
'; + $systemTips = [ + 'title' => __('Database Cleanup Suggestion', 'fluent-crm'), + 'body' => $sysBody, + ]; + } + } + + /** + * Define the FluentCRM dashboard notices. + * + * This filter allows modification of the notices displayed on the FluentCRM dashboard. + * + * @since 2.8.40 + * + * @param array $notices An array of notices to be displayed on the dashboard. + */ + $notices = apply_filters('fluent_crm/dashboard_notices', $notices); + + /** + * Define the dashboard data for FluentCRM. + * + * @since 2.9.23 + * + * @param array { + * The dashboard data array. + * + * @type array $stats Overall statistics. + * @type array $sales Sales statistics. + * @type array $dashboard_notices Notices to be displayed on the dashboard. + * @type array $onboarding Onboarding statistics. + * @type array $quick_links Quick links for the dashboard. + * @type array $ff_config FluentForm configuration. + * @type array $recommendation Recommendations for the user. + * @type array $system_tips System tips for the user. + * } + */ + return apply_filters('fluent_crm/dashboard_data', [ + 'stats' => $overallStats, + /** + * Determine the FluentCRMsales statistics data. + * + * This filter allows modification of the sales statistics data before it is used. + * + * @since 2.7.0 + * + * @param array An array of sales statistics data. + */ + 'sales' => apply_filters('fluent_crm/sales_stats', []), + 'dashboard_notices' => $notices, + 'onboarding' => $stats->getOnboardingStat(), + 'quick_links' => $stats->getQuickLinks(), + 'ff_config' => [ + 'is_installed' => defined('FLUENTFORM'), + 'create_form_link' => admin_url('admin.php?page=fluent_forms#add=1') + ], + 'recommendation' => $this->recommendation(), + 'system_tips' => $systemTips, + 'recent_contacts' => $stats->getRecentContacts(3), + 'active_automations' => $stats->getActiveAutomations(3), + 'recent_campaigns' => $stats->getRecentCampaigns(3), + 'triggers' => $this->getTriggers() + ]); + } + + private function recommendation() + { + if (defined('FLUENTCAMPAIGN')) { + return false; + } + + $recommendations = []; + + if (defined('WC_PLUGIN_FILE')) { + $recommendations[] = [ + 'provider' => 'WooCommerce', + 'title' => __('Do more with WooCommerce + FluentCRM', 'fluent-crm'), + 'description' => __('Integrate FluentCRM with WooCommerce and segment your customers by purchase behavior, send super targeted emails, onboarding emails, cross promotions and many more.', 'fluent-crm'), + 'btn_text' => __('Upgrade to Pro', 'fluent-crm'), + 'learn_more' => 'https://fluentcrm.com/integrations/woocommerce-marketing-automation/', + 'base_title' => __('Supercharge your WooCommerce store by upgrading FluentCRM Pro', 'fluent-crm') + ]; + $recommendations[] = [ + 'provider' => 'WooCommerce', + 'title' => __('Do more with WooCommerce + FluentCRM', 'fluent-crm'), + 'description' => __('Integrate FluentCRM with WooCommerce and segment your customers by purchase behavior, send super targeted emails, onboarding emails, cross promotions and many more.', 'fluent-crm'), + 'btn_text' => __('Upgrade to Pro', 'fluent-crm'), + 'learn_more' => 'https://fluentcrm.com/integrations/woocommerce-marketing-automation/', + 'base_title' => __('Supercharge your WooCommerce store by upgrading FluentCRM Pro', 'fluent-crm') + ]; + } + + if (Helper::isEdd3()) { + $recommendations[] = [ + 'provider' => 'EDD', + 'title' => __('Do more with EDD + FluentCRM', 'fluent-crm'), + 'description' => __('Integrate FluentCRM with Easy Digital Downloads and segment your customers by purchase behavior, send super targeted emails, onboarding emails, cross promotions and many more.', 'fluent-crm'), + 'btn_text' => __('Upgrade to Pro', 'fluent-crm'), + 'learn_more' => 'https://fluentcrm.com/integrations/easy-digital-downloads-integration-fluentcrm/', + 'base_title' => __('Supercharge your Digital Downloads store by upgrading FluentCRM Pro', 'fluent-crm') + ]; + } + + if (defined('LLMS_PLUGIN_FILE')) { + $recommendations[] = [ + 'provider' => 'LifterLMS', + 'title' => __('Do more with LifterLMS + FluentCRM', 'fluent-crm'), + 'description' => __('Integrate LifterLMS with FluentCRM and segment your students by courses, send super targeted emails, onboarding emails, cross promote more courses and many more.', 'fluent-crm'), + 'learn_more' => 'https://fluentcrm.com/integrations/lifterlms/', + 'btn_text' => __('Upgrade to Pro', 'fluent-crm'), + 'base_title' => __('Supercharge your LMS by upgrading FluentCRM Pro', 'fluent-crm') + ]; + $recommendations[] = [ + 'provider' => 'LifterLMS', + 'title' => __('Do more with LifterLMS + FluentCRM', 'fluent-crm'), + 'description' => __('Integrate LifterLMS with FluentCRM and segment your students by courses, send super targeted emails, onboarding emails, cross promote more courses and many more.', 'fluent-crm'), + 'learn_more' => 'https://fluentcrm.com/integrations/lifterlms/', + 'btn_text' => __('Upgrade to Pro', 'fluent-crm'), + 'base_title' => __('Supercharge your LMS by upgrading FluentCRM Pro', 'fluent-crm') + ]; + } else if (defined('LEARNDASH_VERSION')) { + $recommendations[] = [ + 'provider' => 'LearnDash', + 'title' => __('Do more with LearnDash + FluentCRM', 'fluent-crm'), + 'description' => __('Integrate LearnDash with FluentCRM and segment your students by courses, send super targeted emails, onboarding emails, cross promote more courses and many more.', 'fluent-crm'), + 'learn_more' => 'https://fluentcrm.com/integrations/learndash-integration-fluentcrm/', + 'btn_text' => __('Upgrade to Pro', 'fluent-crm'), + 'base_title' => __('Supercharge your LMS by upgrading FluentCRM Pro', 'fluent-crm') + ]; + $recommendations[] = [ + 'provider' => 'LearnDash', + 'title' => __('Do more with LearnDash + FluentCRM', 'fluent-crm'), + 'description' => __('Integrate LearnDash with FluentCRM and segment your students by courses, send super targeted emails, onboarding emails, cross promote more courses and many more.', 'fluent-crm'), + 'learn_more' => 'https://fluentcrm.com/integrations/learndash-integration-fluentcrm/', + 'btn_text' => __('Upgrade to Pro', 'fluent-crm'), + 'base_title' => __('Supercharge your LMS by upgrading FluentCRM Pro', 'fluent-crm') + ]; + } else if (defined('TUTOR_VERSION')) { + $recommendations[] = [ + 'provider' => 'TutorLMS', + 'title' => __('Do more with TutorLMS + FluentCRM', 'fluent-crm'), + 'description' => __('Integrate TutorLMS with FluentCRM and segment your students by courses, send super targeted emails, onboarding emails, cross promote more courses and many more.', 'fluent-crm'), + 'btn_text' => __('Upgrade to Pro', 'fluent-crm'), + 'learn_more' => 'https://fluentcrm.com/docs/tutorlms-integration-with-fluentcrm/', + 'base_title' => __('Supercharge your LMS by upgrading FluentCRM Pro', 'fluent-crm') + ]; + } else if (defined('LP_PLUGIN_FILE')) { + $recommendations[] = [ + 'provider' => 'LearnPress', + 'title' => __('Do more with LearnPress + FluentCRM', 'fluent-crm'), + 'description' => __('Integrate LearnPress with FluentCRM and segment your students by courses, send super targeted emails, onboarding emails, cross promote more courses and many more.', 'fluent-crm'), + 'btn_text' => __('Upgrade to Pro', 'fluent-crm'), + 'learn_more' => 'https://fluentcrm.com/docs/learpress-integration-with-fluentcrm/', + 'base_title' => __('Supercharge your LMS by upgrading FluentCRM Pro', 'fluent-crm') + ]; + } + + if (defined('PMPRO_VERSION')) { + $recommendations[] = [ + 'provider' => 'PaidMembership Pro', + 'title' => __('Do more with PaidMembership Pro + FluentCRM', 'fluent-crm'), + 'description' => __('Integrate PaidMembership Pro with FluentCRM and segment your members by membership levels, send super targeted emails, onboarding emails, cross promote more levels and many more.', 'fluent-crm'), + 'btn_text' => __('Upgrade to Pro', 'fluent-crm'), + 'base_title' => __('Supercharge your Membership Site by upgrading FluentCRM Pro', 'fluent-crm') + ]; + } else if (defined('WLM3_PLUGIN_VERSION')) { + $recommendations[] = [ + 'provider' => 'Wishlist Member', + 'title' => __('Do more with Wishlist Member + FluentCRM', 'fluent-crm'), + 'description' => __('Integrate Wishlist Member with FluentCRM and segment your members by membership levels, send super targeted emails, onboarding emails, cross promote more levels and many more.', 'fluent-crm'), + 'btn_text' => __('Upgrade to Pro', 'fluent-crm'), + 'base_title' => __('Supercharge your Membership Site by upgrading FluentCRM Pro', 'fluent-crm') + ]; + } else if (defined('MEPR_PLUGIN_NAME')) { + $recommendations[] = [ + 'provider' => 'MemberPress', + 'title' => __('Do more with MemberPress + FluentCRM', 'fluent-crm'), + 'description' => __('Integrate MemberPress with FluentCRM and segment your members by membership levels, send super targeted emails, onboarding emails, cross promote more levels and many more.', 'fluent-crm'), + 'btn_text' => __('Upgrade to Pro', 'fluent-crm'), + 'base_title' => __('Supercharge your Membership Site by upgrading FluentCRM Pro', 'fluent-crm') + ]; + } else if (class_exists('\Restrict_Content_Pro')) { + $recommendations[] = [ + 'provider' => 'Restrict Content Pro', + 'title' => __('Do more with Restrict Content Pro + FluentCRM', 'fluent-crm'), + 'description' => __('Integrate Restrict Content Pro with FluentCRM and segment your members by membership levels, send super targeted emails, onboarding emails, cross promote more levels and many more.', 'fluent-crm'), + 'btn_text' => __('Upgrade to Pro', 'fluent-crm'), + 'base_title' => __('Supercharge your Membership Site by upgrading FluentCRM Pro', 'fluent-crm') + ]; + } + + if (defined('BP_REQUIRED_PHP_VERSION') && function_exists('\buddypress')) { + $title = defined('BP_PLATFORM_VERSION') ? 'BuddyBoss' : 'BuddyPress'; + $recommendations[] = [ + 'provider' => $title, + /* translators: %s: plugin name (BuddyBoss or BuddyPress) */ + 'title' => sprintf(__('Do more with %s + FluentCRM', 'fluent-crm'), $title), + /* translators: %s: plugin name (BuddyBoss or BuddyPress) */ + 'description' => sprintf(__('Integrate %s with FluentCRM and segment your members by different group, send super targeted emails, onboarding emails, cross promote more groups and many more.', 'fluent-crm'), $title), + 'btn_text' => __('Upgrade to Pro', 'fluent-crm'), + 'base_title' => __('Supercharge your Community Site by upgrading FluentCRM Pro', 'fluent-crm') + ]; + } + + if (!$recommendations) { + return false; + } + + return $recommendations[array_rand($recommendations)]; + + } + + private function getTriggers() + { + /** + * Determine the list of funnel triggers in FluentCRM. + * + * This filter allows you to modify the array of funnel triggers. + * + * @since 1.0.0 + * + * @param array An array of funnel triggers. + */ + return apply_filters('fluentcrm_funnel_triggers', []); + } +} diff --git a/wp-content/plugins/fluent-crm/app/Http/Controllers/DocsController.php b/wp-content/plugins/fluent-crm/app/Http/Controllers/DocsController.php new file mode 100644 index 0000000..0e16195 --- /dev/null +++ b/wp-content/plugins/fluent-crm/app/Http/Controllers/DocsController.php @@ -0,0 +1,195 @@ +getDocsPerChunk($this->restApi . 'docs?per_page=100', 'fluentcrm_all_docs'); + $moreDocs = $this->getDocsPerChunk($this->restApi . 'docs?per_page=100&offset=100', 'fluentcrm_all_docs_2'); + + if ($moreDocs) { + $formattedDocs = array_merge($formattedDocs, $moreDocs); + } + + return [ + 'docs' => $formattedDocs + ]; + } + + public function getDoc($docId) + { + $request = wp_remote_get($this->restApi . 'docs/' . $docId); + + if (is_wp_error($request)) { + return [ + 'content' => 'sorry, we could not fetch the doc at this moment. Please try again', + 'is_error' => true + ]; + } + + $doc = json_decode(wp_remote_retrieve_body($request), true); + + return [ + 'title' => sanitize_text_field($doc['title']['rendered']), + 'content' => links_add_target(Helper::sanitizeHtml($doc['content']['rendered'])), + 'link' => esc_url($doc['link']), + 'id' => $doc['id'] + ]; + } + + public function getAddons(Request $request) + { + $canAutoInstallToolkit = (bool) apply_filters('fluent_toolkit/can_auto_install', false); + $toolkitPluginFile = 'fluent-toolkit/fluent-toolkit.php'; + $toolkitLoaded = defined('FLUENT_TOOLKIT_VERSION'); + $toolkitPluginExists = $this->isPluginInstalled($toolkitPluginFile); + $toolkitActionText = __('Get FluentHub from GitHub', 'fluent-crm'); + + if ($canAutoInstallToolkit) { + $toolkitActionText = $toolkitPluginExists ? __('Activate FluentHub', 'fluent-crm') : __('Install FluentHub', 'fluent-crm'); + } + + $addOns = [ + 'fluentform' => [ + 'title' => __('Fluent Forms', 'fluent-crm'), + 'logo' => fluentCrmMix('images/fluentform.png'), + 'is_installed' => defined('FLUENTFORM'), + 'learn_more_url' => 'https://wordpress.org/plugins/fluentform/', + 'settings_url' => admin_url('admin.php?page=fluent_forms'), + 'action_text' => $this->isPluginInstalled('fluent-form/fluent-form.php') ? __('Activate Fluent Forms', 'fluent-crm') : __('Install Fluent Forms', 'fluent-crm'), + 'description' => __('Collect leads and build any type of forms, accept payments, connect with your CRM with the Fastest Contact Form Builder Plugin for WordPress', 'fluent-crm') + ], + 'fluentsmtp' => [ + 'title' => __('Fluent SMTP', 'fluent-crm'), + 'logo' => fluentCrmMix('images/fluent-smtp.svg'), + 'is_installed' => defined('FLUENTMAIL'), + 'learn_more_url' => 'https://wordpress.org/plugins/fluent-smtp/', + 'settings_url' => admin_url('options-general.php?page=fluent-mail#/'), + 'action_text' => $this->isPluginInstalled('fluent-smtp/fluent-smtp.php') ? __('Activate Fluent SMTP', 'fluent-crm') : __('Install Fluent SMTP', 'fluent-crm'), + 'description' => __('The Ultimate SMTP and SES Plugin for WordPress. Connect with any SMTP, SendGrid, Mailgun, SES, Sendinblue, PepiPost, Google, Microsoft and more.', 'fluent-crm') + ], + 'fluent-support' => [ + 'title' => __('Fluent Support', 'fluent-crm'), + 'logo' => fluentCrmMix('images/fluent-support.svg'), + 'is_installed' => defined('FLUENT_SUPPORT_VERSION'), + 'learn_more_url' => 'https://wordpress.org/plugins/fluent-support/', + 'settings_url' => admin_url('admin.php?page=fluent-support#/'), + 'action_text' => $this->isPluginInstalled('fluent-support/fluent-support.php') ? __('Activate Fluent Support', 'fluent-crm') : __('Install Fluent Support', 'fluent-crm'), + 'description' => __('WordPress Helpdesk and Customer Support Ticket Plugin. Provide awesome support and manage customer queries right from your WordPress dashboard.', 'fluent-crm') + ], + 'fluent-cart' => [ + 'title' => __('Fluent Cart', 'fluent-crm'), + 'logo' => fluentCrmMix('images/fluent-cart-dark.svg'), + 'is_installed' => defined('FLUENTCART_VERSION'), + 'learn_more_url' => 'https://wordpress.org/plugins/fluent-cart/', + 'settings_url' => admin_url('admin.php?page=fluent-cart#/'), + 'action_text' => $this->isPluginInstalled('fluent-cart/fluent-cart.php') ? __('Activate Fluent Cart', 'fluent-crm') : __('Install Fluent Cart', 'fluent-crm'), + 'description' => __('WordPress eCommerce and Shopping Cart Plugin. Build an online store and manage products, orders, and customers right from your WordPress dashboard.', 'fluent-crm') + ], + 'fluent-boards' => [ + 'title' => __('Fluent Boards', 'fluent-crm'), + 'logo' => fluentCrmMix('images/fluent-boards.svg'), + 'is_installed' => defined('FLUENT_BOARDS'), + 'learn_more_url' => 'https://wordpress.org/plugins/fluent-boards/', + 'settings_url' => admin_url('admin.php?page=fluent-boards#/'), + 'action_text' => $this->isPluginInstalled('fluent-boards/fluent-boards.php') ? __('Activate Fluent Boards', 'fluent-crm') : __('Install Fluent Boards', 'fluent-crm'), + 'description' => __('WordPress Project Management and Collaboration Plugin. Manage projects, tasks, and team collaboration right from your WordPress dashboard.', 'fluent-crm') + ], + 'fluent-community' => [ + 'title' => __('Fluent Community', 'fluent-crm'), + 'logo' => fluentCrmMix('images/fluent-community.svg'), + 'is_installed' => defined('FLUENT_COMMUNITY_PLUGIN_VERSION'), + 'learn_more_url' => 'https://wordpress.org/plugins/fluent-community/', + 'settings_url' => admin_url('admin.php?page=fluent-community#/'), + 'action_text' => $this->isPluginInstalled('fluent-community/fluent-community.php') ? __('Activate Fluent Community', 'fluent-crm') : __('Install Fluent Community', 'fluent-crm'), + 'description' => __('WordPress Forum and Community Plugin. Build a thriving online community and discussion forum right from your WordPress dashboard.', 'fluent-crm') + ], + 'fluent-booking' => [ + 'title' => __('Fluent Booking', 'fluent-crm'), + 'logo' => fluentCrmMix('images/fluent-booking.svg'), + 'is_installed' => defined('FLUENT_BOOKING_VERSION'), + 'learn_more_url' => 'https://wordpress.org/plugins/fluent-booking/', + 'settings_url' => admin_url('admin.php?page=fluent-booking#/'), + 'action_text' => $this->isPluginInstalled('fluent-booking/fluent-booking.php') ? __('Activate Fluent Booking', 'fluent-crm') : __('Install Fluent Booking', 'fluent-crm'), + 'description' => __('WordPress Appointment Booking Plugin. Manage appointments, bookings, and customer scheduling right from your WordPress dashboard.', 'fluent-crm') + ], + 'fluent-toolkit' => [ + 'title' => __('FluentHub', 'fluent-crm'), + 'logo' => fluentCrmMix('images/fluent-toolkit.svg'), + 'is_installed' => $toolkitLoaded, + 'learn_more_url' => 'https://github.com/WPManageNinja/fluent-toolkit', + 'settings_url' => admin_url('admin.php?page=fluent-toolkit'), + 'action_text' => $toolkitActionText, + 'install_route' => $canAutoInstallToolkit ? 'mcp/install-adapter' : '', + 'install_url' => $canAutoInstallToolkit ? '' : 'https://github.com/WPManageNinja/fluent-toolkit', + 'description' => __('FluentCRM ships AI agent tools, but they only become available once FluentHub is installed and active.', 'fluent-crm') + ] + ]; + + $data = [ + 'addons' => $addOns + ]; + + if (in_array('experimental_features', $request->get('with', []))) { + $data['experimental_features'] = Helper::getExperimentalSettings(); + } + + return $data; + } + + private function isPluginInstalled($plugin) + { + return file_exists(WP_PLUGIN_DIR . '/' . $plugin); + } + + private function getDocsPerChunk($url, $chunkKey) + { + return fluentCrmGetFromCache($chunkKey, function () use ($url) { + $request = wp_remote_get($url); + + if (is_wp_error($request)) { + return []; + } + + $docs = json_decode(wp_remote_retrieve_body($request), true); + + $formattedDocs = []; + + foreach ($docs as $doc) { + + if (empty($doc['title'])) { + continue; + } + + $primaryCategory = Arr::get($doc, 'taxonomy_info.doc_category.0', ['value' => 'none', 'label' => 'Other']); + $formattedDocs[] = [ + 'title' => sanitize_text_field($doc['title']['rendered']), + 'content' => links_add_target(Helper::sanitizeHtml($doc['content']['rendered'])), + 'link' => esc_url($doc['link']), + 'category' => wp_kses_post_deep($primaryCategory) + ]; + } + + return $formattedDocs; + }, WEEK_IN_SECONDS); + } + +} diff --git a/wp-content/plugins/fluent-crm/app/Http/Controllers/EmailPatternController.php b/wp-content/plugins/fluent-crm/app/Http/Controllers/EmailPatternController.php new file mode 100644 index 0000000..23712f0 --- /dev/null +++ b/wp-content/plugins/fluent-crm/app/Http/Controllers/EmailPatternController.php @@ -0,0 +1,485 @@ +objectType) + ->orderBy('id', 'desc'); + + if ($search = $request->getSafe('search', 'sanitize_text_field')) { + $query->where('value', 'LIKE', '%' . $search . '%'); + } + + $patterns = $query->paginate(); + + $formattedPatterns = []; + foreach ($patterns as $pattern) { + $formattedPatterns[] = $this->formatPattern($pattern); + } + + return $this->sendSuccess([ + 'patterns' => [ + 'data' => $formattedPatterns, + 'total' => $patterns->total() + ] + ]); + } + + public function show(Request $request, $id) + { + $pattern = Meta::where('object_type', $this->objectType) + ->where('id', $id) + ->firstOrFail(); + + return $this->sendSuccess([ + 'pattern' => $this->formatPattern($pattern) + ]); + } + + /** + * Return patterns in wp_block REST format for the editor middleware. + */ + public function indexWpFormat(Request $request) + { + $patterns = Meta::where('object_type', $this->objectType) + ->orderBy('id', 'desc') + ->get(); + + $categoryMap = $this->getCategoryMap(); + $formatted = []; + foreach ($patterns as $pattern) { + $formatted[] = $this->formatAsWpBlock($pattern, $categoryMap); + } + + return $formatted; + } + + public function store(Request $request) + { + $this->validate($request->all(), [ + 'title' => 'required|string', + 'content' => 'required|string', + ]); + + $title = sanitize_text_field($request->get('title')); + $content = wp_kses_post($request->get('content')); + $category = sanitize_text_field($request->get('category', '')); + $description = sanitize_text_field($request->get('description', '')); + $syncStatus = sanitize_text_field($request->get('sync_status', 'unsynced')); + + $slug = 'fluentcrm/' . sanitize_title($title . '-' . uniqid()); + + $pattern = Meta::create([ + 'object_type' => $this->objectType, + 'object_id' => get_current_user_id(), + 'key' => $slug, + 'value' => [ + 'title' => $title, + 'content' => $content, + 'category' => $category, + 'description' => $description, + 'sync_status' => $syncStatus, + ], + ]); + + return $this->sendSuccess([ + 'message' => __('Pattern saved successfully', 'fluent-crm'), + 'pattern' => $this->formatPattern($pattern), + ]); + } + + /** + * Store a pattern from wp_block format (called by editor middleware). + */ + public function storeWpFormat(Request $request) + { + $title = $request->get('title', ''); + if (is_array($title)) { + $title = Arr::get($title, 'raw', ''); + } + $title = sanitize_text_field($title); + + $content = $request->get('content', ''); + if (is_array($content)) { + $content = Arr::get($content, 'raw', ''); + } + $content = wp_kses_post($content); + + if (!$title && !$content) { + return $this->sendError([ + 'message' => __('Title or content is required', 'fluent-crm') + ]); + } + + if (!$title) { + $title = __('Untitled Pattern', 'fluent-crm'); + } + + $meta = $request->get('meta', []); + $syncStatus = Arr::get($meta, 'wp_pattern_sync_status', ''); + $syncStatus = sanitize_text_field($syncStatus); + + $categoryIds = (array) $request->get('wp_pattern_category', []); + $categoryName = $this->resolveCategoryName($categoryIds); + + $slug = 'fluentcrm/' . sanitize_title($title . '-' . uniqid()); + + $pattern = Meta::create([ + 'object_type' => $this->objectType, + 'object_id' => get_current_user_id(), + 'key' => $slug, + 'value' => [ + 'title' => $title, + 'content' => $content, + 'category' => $categoryName, + 'description' => '', + 'sync_status' => $syncStatus, + ], + ]); + + $categoryMap = $this->getCategoryMap(); + return $this->formatAsWpBlock($pattern, $categoryMap); + } + + public function update(Request $request, $id) + { + $pattern = Meta::where('object_type', $this->objectType) + ->where('id', $id) + ->firstOrFail(); + + $value = $pattern->value; + + if ($title = $request->get('title')) { + if (is_array($title)) { + $title = Arr::get($title, 'raw', ''); + } + $value['title'] = sanitize_text_field($title); + } + + if ($request->has('content')) { + $content = $request->get('content'); + if (is_array($content)) { + $content = Arr::get($content, 'raw', ''); + } + $value['content'] = wp_kses_post($content); + } + + $value['category'] = sanitize_text_field($request->get('category', '')); + + if ($request->has('wp_pattern_category')) { + $categoryIds = (array) $request->get('wp_pattern_category', []); + $value['category'] = $this->resolveCategoryName($categoryIds); + } + + if ($request->has('description')) { + $value['description'] = sanitize_text_field($request->get('description')); + } + + if ($request->exists('sync_status')) { + $value['sync_status'] = sanitize_text_field($request->get('sync_status')); + } + + $meta = $request->get('meta', []); + if (is_array($meta) && isset($meta['wp_pattern_sync_status'])) { + $value['sync_status'] = sanitize_text_field($meta['wp_pattern_sync_status']); + } + + if ($title = $request->get('title')) { + if (is_array($title)) { + $title = Arr::get($title, 'raw', ''); + } + if ($title) { + $pattern->key = 'fluentcrm/' . sanitize_title($title . '-' . $pattern->id); + } + } + + $pattern->value = $value; + $pattern->save(); + + return $this->sendSuccess([ + 'message' => __('Pattern updated successfully', 'fluent-crm'), + 'pattern' => $this->formatPattern($pattern), + ]); + } + + public function delete(Request $request, $id) + { + Meta::where('object_type', $this->objectType) + ->where('id', $id) + ->firstOrFail() + ->delete(); + + return $this->sendSuccess([ + 'message' => __('Pattern deleted successfully', 'fluent-crm'), + ]); + } + + public function handleBulkAction(Request $request) + { + $actionName = sanitize_text_field($request->get('action_name')); + + if ($actionName !== 'delete_patterns') { + return $this->sendError([ + 'message' => __('Invalid action', 'fluent-crm') + ]); + } + + $query = Meta::where('object_type', $this->objectType); + + if (filter_var($request->get('select_all'), FILTER_VALIDATE_BOOLEAN)) { + $search = $request->getSafe('search', 'sanitize_text_field', ''); + if ($search !== '') { + $query->where('value', 'LIKE', '%' . $search . '%'); + } + } else { + $patternIds = array_map('intval', (array) $request->get('pattern_ids', [])); + if (empty($patternIds)) { + return $this->sendError([ + 'message' => __('No patterns selected', 'fluent-crm') + ]); + } + $query->whereIn('id', $patternIds); + } + + $count = $query->delete(); + + return $this->sendSuccess([ + 'message' => sprintf(__('%d pattern(s) deleted successfully', 'fluent-crm'), $count) + ]); + } + + /** + * CRUD for pattern categories (stored as fc_meta with separate object_type). + */ + public function getCategories() + { + // Collect unique category names from all patterns + $patterns = Meta::where('object_type', $this->objectType)->get(); + $categories = []; + foreach ($patterns as $pattern) { + $cat = Arr::get($pattern->value, 'category', ''); + if ($cat && !in_array($cat, $categories)) { + $categories[] = $cat; + } + } + + sort($categories); + + return $this->sendSuccess([ + 'categories' => $categories + ]); + } + + public function storeCategory(Request $request) + { + $name = sanitize_text_field($request->get('name', '')); + if (!$name) { + return $this->sendError(['message' => __('Category name is required', 'fluent-crm')]); + } + + $slug = sanitize_title($name); + + // Check for existing + $existing = Meta::where('object_type', $this->categoryObjectType) + ->where('key', $slug) + ->first(); + + if ($existing) { + return $this->formatCategoryAsWpTerm($existing); + } + + $category = Meta::create([ + 'object_type' => $this->categoryObjectType, + 'object_id' => 0, + 'key' => $slug, + 'value' => ['name' => $name], + ]); + + return $this->formatCategoryAsWpTerm($category); + } + + public function deleteCategory(Request $request, $id) + { + Meta::where('object_type', $this->categoryObjectType) + ->where('id', $id) + ->firstOrFail() + ->delete(); + + return $this->sendSuccess([ + 'message' => __('Category deleted successfully', 'fluent-crm'), + ]); + } + + /** + * Format a pattern Meta record as a wp_block REST response. + */ + private function formatAsWpBlock($meta, $categoryMap = []) + { + $value = $meta->value; + $title = Arr::get($value, 'title', ''); + $content = Arr::get($value, 'content', ''); + $syncStatus = Arr::get($value, 'sync_status', 'unsynced'); + $category = Arr::get($value, 'category', ''); + + $categoryIds = []; + if ($category) { + $catSlug = sanitize_title($category); + if (isset($categoryMap[$catSlug])) { + $categoryIds[] = (int) $categoryMap[$catSlug]; + } + } + + return [ + 'id' => (int) $meta->id, + 'date' => $meta->created_at ? $meta->created_at : gmdate('Y-m-d\TH:i:s'), + 'date_gmt' => $meta->created_at ? $meta->created_at : gmdate('Y-m-d\TH:i:s'), + 'modified' => $meta->updated_at ? $meta->updated_at : gmdate('Y-m-d\TH:i:s'), + 'modified_gmt' => $meta->updated_at ? $meta->updated_at : gmdate('Y-m-d\TH:i:s'), + 'slug' => $meta->key, + 'status' => 'publish', + 'type' => 'wp_block', + 'link' => '', + 'title' => ['raw' => $title], + 'content' => ['raw' => $content, 'protected' => false], + 'meta' => new \stdClass(), + 'wp_pattern_sync_status' => $syncStatus ?: '', + 'wp_pattern_category' => $categoryIds, + ]; + } + + private function formatCategoryAsWpTerm($meta) + { + $value = $meta->value; + + return [ + 'id' => (int) $meta->id, + 'count' => 0, + 'name' => Arr::get($value, 'name', $meta->key), + 'slug' => $meta->key, + 'parent' => 0, + ]; + } + + private function formatPattern($meta) + { + $value = $meta->value; + + return [ + 'id' => (int) $meta->id, + 'slug' => $meta->key, + 'title' => Arr::get($value, 'title', ''), + 'content' => Arr::get($value, 'content', ''), + 'category' => Arr::get($value, 'category', ''), + 'description' => Arr::get($value, 'description', ''), + 'sync_status' => Arr::get($value, 'sync_status', 'unsynced'), + 'created_at' => $meta->created_at ? (string) $meta->created_at : '', + 'updated_at' => $meta->updated_at ? (string) $meta->updated_at : '', + ]; + } + + /** + * Build slug → id map for all pattern categories. + */ + private function getCategoryMap() + { + $categories = Meta::where('object_type', $this->categoryObjectType)->get(); + $map = []; + foreach ($categories as $cat) { + $map[$cat->key] = $cat->id; + } + return $map; + } + + /** + * Resolve category IDs back to a single category name. + */ + private function resolveCategoryName($categoryIds) + { + if (empty($categoryIds)) { + return ''; + } + + $categoryIds = array_map('intval', $categoryIds); + $category = Meta::where('object_type', $this->categoryObjectType) + ->whereIn('id', $categoryIds) + ->first(); + + if ($category) { + return Arr::get($category->value, 'name', $category->key); + } + + return ''; + } + + /** + * Get patterns formatted for the block editor boot data. + */ + public static function getEditorPatterns() + { + $patterns = Meta::where('object_type', 'email_pattern') + ->orderBy('id', 'desc') + ->get(); + + $editorPatterns = []; + $categories = []; + $seenCategories = []; + + foreach ($patterns as $pattern) { + $value = $pattern->value; + $title = Arr::get($value, 'title', ''); + $content = Arr::get($value, 'content', ''); + $category = Arr::get($value, 'category', ''); + $description = Arr::get($value, 'description', ''); + + if (!$content) { + continue; + } + + $patternCategories = []; + if ($category) { + $catSlug = sanitize_title($category); + $patternCategories[] = $catSlug; + if (!isset($seenCategories[$catSlug])) { + $seenCategories[$catSlug] = true; + $categories[] = [ + 'name' => $catSlug, + 'label' => $category, + ]; + } + } + + // Always include in the general fluentcrm-patterns category + $patternCategories[] = 'fluentcrm-patterns'; + + $editorPatterns[] = [ + 'name' => $pattern->key, + 'title' => $title, + 'content' => $content, + 'description' => $description, + 'categories' => $patternCategories, + 'keywords' => ['fluentcrm', 'email'], + ]; + } + + // Always add the root category + array_unshift($categories, [ + 'name' => 'fluentcrm-patterns', + 'label' => __('My Patterns', 'fluent-crm'), + ]); + + return [ + 'patterns' => $editorPatterns, + 'categories' => $categories, + ]; + } +} diff --git a/wp-content/plugins/fluent-crm/app/Http/Controllers/FormsController.php b/wp-content/plugins/fluent-crm/app/Http/Controllers/FormsController.php new file mode 100644 index 0000000..a90bde7 --- /dev/null +++ b/wp-content/plugins/fluent-crm/app/Http/Controllers/FormsController.php @@ -0,0 +1,464 @@ + false, + 'forms' => (object)[ + 'data' => [], + 'total' => 0 + ] + ]; + } + + // Now let's find the forms which are connected with Fluent Forms + $connectFeedForms = fluentCrmDb()->table('fluentform_form_meta') + ->where('meta_key', 'fluentcrm_feeds') + ->select(['form_id', 'id', 'value']) + ->groupBy('form_id') + ->get(); + + + $formIds = []; + $connectedFormIds = []; + foreach ($connectFeedForms as $form) { + $formIds[] = $form->form_id; + $settings = json_decode($form->value, true); + $connectedFormIds[$form->form_id] = [ + 'feed_id' => $form->id, + 'settings' => $settings + ]; + } + // Now let's get forms ids from funnel + $fluentFormFunnels = Funnel::where('trigger_name', 'fluentform_submission_inserted') + ->get(); + + $connectedFunnelIds = []; + foreach ($fluentFormFunnels as $funnel) { + $formId = Arr::get($funnel->settings, 'form_id'); + if ($formId) { + $connectedFunnelIds[$formId] = $funnel->id; + $formIds[] = $formId; + } + } + + $formIds = array_unique($formIds); + $page = $request->get('page', 1); + $limit = $request->get('per_page', 10); + $offset = ($page - 1) * $limit; + + $forms = []; + + + if ($formIds) { + $crmBaseUrl = fluentcrm_menu_url_base(); + + $search = sanitize_text_field($request->get('search', '')); + + $allFormsQuery = fluentCrmDb()->table('fluentform_forms') + ->whereIn('id', $formIds); + + if ($search) { + $allFormsQuery->where('title', 'LIKE', '%' . $search . '%'); + } + $allForms = $allFormsQuery->orderBy('id', 'DESC') + ->limit($limit) + ->offset($offset) + ->get(); + + foreach ($allForms as $form) { + $funnelUrl = ''; + $feedUrl = ''; + $associateTags = []; + $associateList = ''; + if (isset($connectedFunnelIds[$form->id])) { + $funnelUrl = $crmBaseUrl . 'funnel/' . $connectedFunnelIds[$form->id] . '/edit'; + } + if (isset($connectedFormIds[$form->id])) { + $feedUrl = admin_url('admin.php?page=fluent_forms&form_id=' . $form->id . '&route=settings&sub_route=form_settings#/all-integrations/' . $connectedFormIds[$form->id]['feed_id'] . '/fluentcrm'); + $tagIds = Arr::get($connectedFormIds[$form->id], 'settings.tag_ids'); + if ($tagIds) { + $tags = Tag::whereIn('id', $tagIds)->get(); + foreach ($tags as $tag) { + $associateTags[] = $tag->title; + } + } + $listId = Arr::get($connectedFormIds[$form->id], 'settings.list_id'); + if ($listId && $list = Lists::find($listId)) { + $associateList = $list->title; + } + } + + $forms[] = [ + 'id' => $form->id, + 'title' => $form->title, + 'status' => $form->status, + 'created_at' => $form->created_at, + 'funnel_url' => $funnelUrl, + 'feed_url' => $feedUrl, + 'associate_tags' => implode(', ', $associateTags), + 'associate_lists' => $associateList, + 'shortcode' => '[fluentform id="' . $form->id . '"]', + 'edit_url' => admin_url('admin.php?page=fluent_forms&route=editor&form_id=' . $form->id), + 'preview_url' => site_url('?fluent_forms_pages=1&design_mode=1&preview_id=' . $form->id) + ]; + } + } + + $total = count($formIds); + + return [ + 'installed' => true, + 'forms' => [ + 'data' => $forms, + 'page' => $page, + 'per_page' => $limit, + 'total' => $total, + 'last_page' => ceil($total / $limit) + ] + ]; + } + + public function create(Request $request) + { + $form = $this->validate($request->all(), [ + 'template_id' => 'required', + 'title' => 'required|unique:fluentform_forms', + 'selected_tags' => 'required', + 'selected_list' => 'required' + ]); + $template = $this->getSelectedTemplate($form['template_id']); + $now = current_time('mysql'); + $formData = [ + 'title' => $form['title'], + 'status' => 'published', + 'type' => 'form', + 'created_by' => get_current_user_id(), + 'created_at' => $now, + 'updated_at' => $now, + 'form_fields' => $template['form_fields'] + ]; + + $formId = fluentCrmDb()->table('fluentform_forms')->insertGetId($formData); + + if ($template['custom_css']) { + fluentCrmDb()->table('fluentform_form_meta') + ->insert([ + 'form_id' => $formId, + 'meta_key' => '_custom_form_css', + 'value' => $template['custom_css'] + ]); + } + + $defaultSettings = (new \FluentForm\App\Modules\Form\Form(wpFluentForm()))->getFormsDefaultSettings(); + + if ($form['double_optin']) { + $defaultSettings['confirmation']['messageToShow'] = __('Please check your inbox to confirm your subscription', 'fluent-crm'); + } else { + $defaultSettings['confirmation']['messageToShow'] = __('You are successfully subscribed to our email list', 'fluent-crm'); + } + fluentCrmDb()->table('fluentform_form_meta') + ->insert(array( + 'form_id' => $formId, + 'meta_key' => 'formSettings', + 'value' => json_encode($defaultSettings) + )); + + $feedDefaults = [ + 'name' => __('FluentCRM Integration Feed', 'fluent-crm'), + 'first_name' => '', + 'last_name' => '', + 'email' => 'email', + 'other_fields' => [ + [ + 'item_value' => '', + 'label' => '' + ] + ], + 'list_id' => $form['selected_list'], + 'tag_ids' => $form['selected_tags'], + 'skip_if_exists' => false, + 'double_opt_in' => $form['double_optin'], + 'conditionals' => [ + 'conditions' => [], + 'status' => false, + 'type' => 'all' + ], + 'enabled' => true, + 'status' => true + ]; + if (is_array($template['map_fields'])) { + $feedDefaults = wp_parse_args($template['map_fields'], $feedDefaults); + } + $feedData = [ + 'meta_key' => 'fluentcrm_feeds', + 'form_id' => $formId, + 'value' => \json_encode($feedDefaults) + ]; + + $createdFeedId = fluentCrmDb()->table('fluentform_form_meta') + ->insertGetId($feedData); + + do_action('fluentform/inserted_new_form', $formId, $formData); + do_action('fluentcrm_created_new_fluentform', $formId, $formData); + + $feedUrl = admin_url('admin.php?page=fluent_forms&form_id=' . $formId . '&route=settings&sub_route=form_settings#/all-integrations/' . $createdFeedId . '/fluentcrm'); + + return [ + 'message' => __('Form has been created', 'fluent-crm'), + 'created_form' => [ + 'id' => $formId, + 'shortcode' => '[fluentform id="' . $formId . '"]', + 'feed_url' => $feedUrl, + 'edit_url' => admin_url('admin.php?page=fluent_forms&route=editor&form_id=' . $formId), + 'preview_url' => site_url('?fluent_forms_pages=1&design_mode=1&preview_id=' . $formId) + ] + ]; + } + + public function getTemplates() + { + $templates = [ + 'inline_subscribe' => [ + 'label' => __('Inline Opt-in Form', 'fluent-crm'), + 'image' => fluentCrmMix('images/forms/form_1.svg'), + 'id' => 'inline_subscribe', + 'form_fields' => '{"fields":[{"index":1,"element":"input_email","attributes":{"type":"email","name":"email","value":"","id":"","class":"extra_spaced","placeholder":"Email Address"},"settings":{"container_class":"","label":"","label_placement":"","help_message":"","admin_field_label":"Email Address","validation_rules":{"required":{"value":true,"message":"This field is required"},"email":{"value":true,"message":"This field must contain a valid email"}},"conditional_logics":{"type":"any","status":false,"conditions":[{"field":"","value":"","operator":""}]},"is_unique":"no","unique_validation_message":"Email address need to be unique."},"editor_options":{"title":"Email Address","icon_class":"ff-edit-email","template":"inputText"},"uniqElKey":"el_1601142291509"}],"submitButton":{"uniqElKey":"el_1524065200616","element":"button","attributes":{"type":"submit","class":""},"settings":{"align":"left","button_style":"default","container_class":"top_merged","help_message":"","background_color":"#409EFF","button_size":"md","color":"#ffffff","button_ui":{"type":"default","text":"Subscribe","img_url":""},"normal_styles":{"backgroundColor":"#409EFF","borderColor":"#409EFF","color":"#ffffff","borderRadius":"","minWidth":""},"hover_styles":{"backgroundColor":"#ffffff","borderColor":"#409EFF","color":"#409EFF","borderRadius":"","minWidth":""},"current_state":"normal_styles"},"editor_options":{"title":"Submit Button"}}}', + 'custom_css' => $this->getFormCss('inline_subscribe'), + 'map_fields' => [ + 'email' => 'email' + ] + ], + 'simple_optin' => [ + 'label' => __('Simple Opt-in Form', 'fluent-crm'), + 'image' => fluentCrm('url.assets') . 'images/forms/form_2.svg', + 'id' => 'simple_optin', + 'form_fields' => '{"fields":[{"index":1,"element":"input_email","attributes":{"type":"email","name":"email","value":"","id":"","class":"","placeholder":"Your Email Address"},"settings":{"container_class":"","label":"","label_placement":"","help_message":"","admin_field_label":"Email Address","validation_rules":{"required":{"value":true,"message":"This field is required"},"email":{"value":true,"message":"This field must contain a valid email"}},"conditional_logics":[],"is_unique":"no","unique_validation_message":"Email address need to be unique."},"editor_options":{"title":"Email Address","icon_class":"ff-edit-email","template":"inputText"},"uniqElKey":"el_16011431576720.7540920979222681"}],"submitButton":{"uniqElKey":"el_1524065200616","element":"button","attributes":{"type":"submit","class":""},"settings":{"align":"left","button_style":"default","container_class":"","help_message":"","background_color":"#409EFF","button_size":"md","color":"#ffffff","button_ui":{"type":"default","text":"Subscribe To Newsletter","img_url":""},"normal_styles":{"backgroundColor":"#409EFF","borderColor":"#409EFF","color":"#ffffff","borderRadius":"","minWidth":""},"hover_styles":{"backgroundColor":"#ffffff","borderColor":"#409EFF","color":"#409EFF","borderRadius":"","minWidth":""},"current_state":"normal_styles"},"editor_options":{"title":"Submit Button"}}}', + 'custom_css' => '', + 'map_fields' => [ + 'email' => 'email' + ] + ], + 'with_name_subscribe' => [ + 'label' => __('Subscription Form', 'fluent-crm'), + 'image' => fluentCrm('url.assets') . 'images/forms/form_3.svg', + 'id' => 'with_name_subscribe', + 'form_fields' => '{"fields":[{"index":0,"element":"input_name","attributes":{"name":"names","data-type":"name-element"},"settings":{"container_class":"","admin_field_label":"Name","conditional_logics":{"type":"any","status":false,"conditions":[{"field":"","value":"","operator":""}]},"label_placement":""},"fields":{"first_name":{"element":"input_text","attributes":{"type":"text","name":"first_name","value":"","id":"","class":"","placeholder":"First Name"},"settings":{"container_class":"","label":"First Name","help_message":"","visible":true,"validation_rules":{"required":{"value":false,"message":"This field is required"}},"conditional_logics":[]},"editor_options":{"template":"inputText"}},"middle_name":{"element":"input_text","attributes":{"type":"text","name":"middle_name","value":"","id":"","class":"","placeholder":"","required":false},"settings":{"container_class":"","label":"Middle Name","help_message":"","error_message":"","visible":false,"validation_rules":{"required":{"value":false,"message":"This field is required"}},"conditional_logics":[]},"editor_options":{"template":"inputText"}},"last_name":{"element":"input_text","attributes":{"type":"text","name":"last_name","value":"","id":"","class":"","placeholder":"Last Name","required":false},"settings":{"container_class":"","label":"Last Name","help_message":"","error_message":"","visible":true,"validation_rules":{"required":{"value":false,"message":"This field is required"}},"conditional_logics":[]},"editor_options":{"template":"inputText"}}},"editor_options":{"title":"Name Fields","element":"name-fields","icon_class":"ff-edit-name","template":"nameFields"},"uniqElKey":"el_1570866006692"},{"index":1,"element":"input_email","attributes":{"type":"email","name":"email","value":"","id":"","class":"","placeholder":"Email Address"},"settings":{"container_class":"","label":"Email","label_placement":"","help_message":"","admin_field_label":"","validation_rules":{"required":{"value":true,"message":"This field is required"},"email":{"value":true,"message":"This field must contain a valid email"}},"conditional_logics":{"type":"any","status":false,"conditions":[{"field":"","value":"","operator":""}]},"is_unique":"no","unique_validation_message":"Email address need to be unique."},"editor_options":{"title":"Email Address","icon_class":"ff-edit-email","template":"inputText"},"uniqElKey":"el_1570866012914"}],"submitButton":{"uniqElKey":"el_1524065200616","element":"button","attributes":{"type":"submit","class":""},"settings":{"align":"left","button_style":"default","container_class":"","help_message":"","background_color":"#409EFF","button_size":"md","color":"#ffffff","button_ui":{"type":"default","text":"Subscribe","img_url":""},"normal_styles":{"backgroundColor":"#409EFF","borderColor":"#409EFF","color":"#ffffff","borderRadius":"","minWidth":""},"hover_styles":{"backgroundColor":"#ffffff","borderColor":"#409EFF","color":"#409EFF","borderRadius":"","minWidth":""},"current_state":"normal_styles"},"editor_options":{"title":"Submit Button"}}}', + 'custom_css' => '', + 'map_fields' => [ + 'email' => 'email', + 'first_name' => '{inputs.names.first_name}', + 'last_name' => '{inputs.names.last_name}' + ] + ] + ]; + /** + * Define the form templates for FluentCRM Forms(Fluent Forms). + * + * This filter allows customization of the Fluent Forms templates used in FluentCRM. + * + * @param array { + * An array of form templates. + * + * @type array $inline_subscribe { + * Inline Opt-in Form template. + * @type string $label The label for the form. + * @type string $image The URL of the form image. + * @type string $id The ID of the form. + * @type string $form_fields The JSON string of form fields. + * @type string $custom_css The custom CSS for the form. + * @type array $map_fields The mapping of form fields. + * } + * @type array $simple_optin { + * Simple Opt-in Form template. + * @type string $label The label for the form. + * @type string $image The URL of the form image. + * @type string $id The ID of the form. + * @type string $form_fields The JSON string of form fields. + * @type string $custom_css The custom CSS for the form. + * @type array $map_fields The mapping of form fields. + * } + * @type array $with_name_subscribe { + * Subscription Form template. + * @type string $label The label for the form. + * @type string $image The URL of the form image. + * @type string $id The ID of the form. + * @type string $form_fields The JSON string of form fields. + * @type string $custom_css The custom CSS for the form. + * @type array $map_fields The mapping of form fields. + * } + * } + * @since 2.7.0 + * + */ + return apply_filters('fluent_crm/ff_form_templates', [ + 'templates' => $templates + ]); + } + + private function getFormCss($name) + { + $css = ''; + if ($name == 'inline_subscribe') { + $css = '.fluent_form_FF_ID { + position: relative; +} +.fluent_form_FF_ID .top_merged.ff_submit_btn_wrapper { + position: absolute; + top: 5px; + right: 5px; +} +.fluent_form_FF_ID .extra_spaced { + padding: 12px 15px !important; +}'; + } + return $css; + } + + private function getSelectedTemplate($templateId) + { + $templates = $this->getTemplates(); + if (isset($templates['templates'][$templateId])) { + return $templates['templates'][$templateId]; + } + $templatesArray = array_values($templates['templates']); + return $templatesArray[0]; + } + + public function getEntries(Request $request, $id) + { + if (!defined('FLUENTFORM')) { + return $this->sendError([ + 'message' => __('Fluent Forms is not installed', 'fluent-crm'), + 'entries' => [] + ]); + } + + if (!Acl::hasPermission('fluentform_entries_viewer', $id)) { + return $this->sendError([ + 'message' => __('You do not have permission to view these entries', 'fluent-crm'), + 'entries' => [] + ]); + } + + // Check if form exists + $form = fluentCrmDb()->table('fluentform_forms') + ->where('id', $id) + ->first(); + + if (!$form) { + return $this->sendError([ + 'message' => __('Form not found', 'fluent-crm'), + 'entries' => [] + ]); + } + + $page = $request->get('page', 1); + $limit = $request->get('per_page', 10); + $offset = ($page - 1) * $limit; + $search = sanitize_text_field($request->get('search', '')); + + // Get total count + $totalQuery = fluentCrmDb()->table('fluentform_submissions') + ->where('form_id', $id); + + if ($search) { + $totalQuery->where(function ($query) use ($search) { + $query->where('response', 'LIKE', '%' . $search . '%') + ->orWhere('status', 'LIKE', '%' . $search . '%'); + }); + } + + // Get entries + $entriesQuery = fluentCrmDb()->table('fluentform_submissions') + ->where('form_id', $id); + + if ($search) { + $entriesQuery->where(function ($query) use ($search) { + $query->where('response', 'LIKE', '%' . $search . '%') + ->orWhere('status', 'LIKE', '%' . $search . '%'); + }); + } + + $total = $totalQuery->count(); + + $entries = $entriesQuery->orderBy('id', 'DESC') + ->limit($limit) + ->offset($offset) + ->get(); + + // Format entries + $formattedEntries = []; + foreach ($entries as $entry) { + $response = json_decode($entry->response, true); + + $formattedEntries[] = [ + 'id' => $entry->id, + 'serial_number' => $entry->serial_number, + 'status' => $entry->status, + 'created_at' => $entry->created_at, + 'response' => $response, + 'user_id' => $entry->user_id, + 'browser' => $entry->browser, + 'device' => $entry->device, + 'ip' => $entry->ip, + 'entry_url' => admin_url('admin.php?page=fluent_forms&form_id=' . $id . '&route=entries#/entries/' . $entry->id) + ]; + } + + return [ + 'entries' => [ + 'data' => $formattedEntries, + 'page' => $page, + 'per_page' => $limit, + 'total' => $total, + 'last_page' => ceil($total / $limit) + ], + 'form' => [ + 'id' => $form->id, + 'title' => $form->title + ] + ]; + } + + public function getEntry(Request $request, $formId, $id) + { + $dataView = apply_filters('fluent_crm/dynamic_contact_item_view_fluentform', [ + 'content_html' => 'No data found' + ], [ + '__id' => $id + ]); + + return [ + 'entry' => $dataView + ]; + } +} diff --git a/wp-content/plugins/fluent-crm/app/Http/Controllers/FunnelController.php b/wp-content/plugins/fluent-crm/app/Http/Controllers/FunnelController.php new file mode 100644 index 0000000..99a19c8 --- /dev/null +++ b/wp-content/plugins/fluent-crm/app/Http/Controllers/FunnelController.php @@ -0,0 +1,1873 @@ +maybeMigrateDB(); + + $orderBy = $request->getSafe('sort_by', 'sanitize_sql_orderby', 'id'); + $orderType = $request->getSafe('sort_type', 'sanitize_sql_orderby', 'DESC'); + + $labelIds = $this->sanitizeFilterIds($request->get('labels')); // labels are id + $tagIds = $this->sanitizeFilterIds($request->get('tags')); // tags are id + $listIds = $this->sanitizeFilterIds($request->get('lists')); // lists are id + $allowedStatuses = ['published', 'draft']; + $statusFilter = array_intersect( + (array) $request->get('statuses', []), + $allowedStatuses + ); + + $funnelQuery = Funnel::withCount('subscribers') + ->orderBy($orderBy, $orderType); + + if (!Helper::isEdd3()) { + /* + * EDD 2 is no longer supported, so hide existing EDD automations + * without deleting stored funnel data from the database. + */ + $funnelQuery->whereNotIn('trigger_name', [ + 'edd_update_payment_status', + 'edd_sl_post_set_status', + 'edd_recurring_add_subscription_payment', + 'edd_subscription_status_change', + 'edd_fc_order_refunded_simulation' + ]); + } + + if ($search = $request->getSafe('search', 'sanitize_text_field')) { + global $wpdb; + $searchTerm = '%%' . $wpdb->esc_like($search) . '%%'; + $funnelQuery->where(function ($query) use ($searchTerm) { + $query->where('title', 'LIKE', $searchTerm) + ->orWhere('trigger_name', 'LIKE', $searchTerm); + }); + } + + if (!empty($labelIds)) { + $funnelQuery->whereHas('labelsTerm', function ($query) use ($labelIds) { + $query->whereIn('term_id', $labelIds); + }); + } + + $segmentFilters = array_filter([ + 'tags' => $tagIds, + 'lists' => $listIds + ]); + + if ($segmentFilters) { + $matchingFunnelIds = $this->getFunnelIdsMatchingSegments($segmentFilters); + foreach (array_keys($segmentFilters) as $segmentKey) { + $funnelQuery->whereIn('id', $matchingFunnelIds[$segmentKey] ?: [0]); + } + } + + if (!empty($statusFilter)) { + $funnelQuery->whereIn('status', $statusFilter); + } + + $funnels = $funnelQuery->paginate(); + $with = $this->request->get('with', []); + + $funnelIds = $funnels->pluck('id')->toArray(); + $inProgressCounts = $this->getInProgressSubscriberCounts($funnelIds); + + // Batch fetch descriptions from fc_meta + $descriptions = Meta::whereIn('object_id', $funnelIds) + ->where('object_type', 'FluentCrm\App\Models\Funnel') + ->where('key', 'description') + ->get() + ->keyBy('object_id'); + + // Batch fetch labels via term_relations + labels + $termRelations = TermRelation::whereIn('object_id', $funnelIds) + ->where('object_type', 'FluentCrm\App\Models\Funnel') + ->get() + ->groupBy('object_id'); + + $allLabelIds = $termRelations->flatten()->pluck('term_id')->unique()->toArray(); + $allLabels = !empty($allLabelIds) ? Label::whereIn('id', $allLabelIds)->get()->keyBy('id') : []; + + foreach ($funnels as $funnel) { + $funnel->in_progress_subscribers_count = $inProgressCounts[(int) $funnel->id] ?? 0; + + $meta = $descriptions[$funnel->id] ?? null; + $funnel->description = $meta ? $meta->value : ''; + + $funnelTerms = $termRelations[$funnel->id] ?? []; + $funnel->labels = []; + $formattedLabels = []; + foreach ($funnelTerms as $term) { + $label = $allLabels[$term->term_id] ?? null; + if ($label) { + $formattedLabels[] = [ + 'id' => $label->id, + 'slug' => $label->slug, + 'title' => $label->title, + 'color' => $label->settings['color'] ?? '' + ]; + } + } + $funnel->labels = $formattedLabels; + } + + $data = [ + 'funnels' => $funnels + ]; + + if (in_array('triggers', $with)) { + $data['triggers'] = $this->getTriggers(); + } + + return $data; + } + + /** + * Count contacts currently inside each automation. + * + * @param array $funnelIds Funnel IDs to count. + * @return array + */ + private function getInProgressSubscriberCounts($funnelIds) + { + $funnelIds = array_filter(array_map('intval', (array) $funnelIds)); + + if (!$funnelIds) { + return []; + } + + $inProgressRows = FunnelSubscriber::select([ + 'funnel_id', + fluentCrmDb()->raw('COUNT(id) as total') + ]) + ->whereIn('funnel_id', $funnelIds) + ->whereIn('status', ['active', 'waiting']) + ->groupBy('funnel_id') + ->get(); + + $counts = []; + + foreach ($inProgressRows as $row) { + $counts[(int) $row->funnel_id] = (int) $row->total; + } + + return $counts; + } + + /** + * Sanitize array request values that contain model IDs. + * + * @param mixed $ids Request value. + * @return array + */ + private function sanitizeFilterIds($ids) + { + return is_array($ids) ? array_unique(array_filter(array_map('intval', $ids))) : []; + } + + /** + * Get automation IDs that reference selected tags/lists in one trigger/sequence scan. + * + * @param array $segmentFilters Selected tag/list IDs keyed by settings name. + * @return array + */ + private function getFunnelIdsMatchingSegments($segmentFilters) + { + $triggerMap = [ + 'tags' => ['fluentcrm_contact_added_to_tags', 'fluentcrm_contact_removed_from_tags'], + 'lists' => ['fluentcrm_contact_added_to_lists', 'fluentcrm_contact_removed_from_lists'] + ]; + $sequenceMap = [ + 'tags' => ['add_contact_to_tag', 'detach_contact_from_tag', 'fluentcrm_contact_added_to_tags', 'fluentcrm_contact_removed_from_tags'], + 'lists' => ['add_contact_to_list', 'detach_contact_from_list', 'fluentcrm_contact_added_to_lists', 'fluentcrm_contact_removed_from_lists'] + ]; + $funnelIds = array_fill_keys(array_keys($segmentFilters), []); + $triggerNames = []; + $sequenceActionNames = []; + + foreach (array_keys($segmentFilters) as $segmentKey) { + $triggerNames = array_merge($triggerNames, $triggerMap[$segmentKey]); + $sequenceActionNames = array_merge($sequenceActionNames, $sequenceMap[$segmentKey]); + } + + // Match automation triggers first, keeping results grouped by segment so combined filters can be intersected later. + $triggerFunnels = Funnel::whereIn('trigger_name', array_values(array_unique($triggerNames))) + ->get(['id', 'trigger_name', 'settings']); + + foreach ($triggerFunnels as $funnel) { + foreach ($segmentFilters as $segmentKey => $selectedIds) { + if (!in_array($funnel->trigger_name, $triggerMap[$segmentKey], true)) { + continue; + } + + if ($this->hasMatchingSegmentSettings($funnel->settings, $segmentKey, $selectedIds)) { + $funnelIds[$segmentKey][] = (int) $funnel->id; + } + } + } + + // Match related automation actions and benchmarks in one chunked scan instead of scanning once per segment. + FunnelSequence::whereIn('action_name', array_values(array_unique($sequenceActionNames))) + ->select(['id', 'funnel_id', 'action_name', 'settings']) + ->chunkById(500, function ($chunk) use (&$funnelIds, $segmentFilters, $sequenceMap) { + foreach ($chunk as $sequence) { + foreach ($segmentFilters as $segmentKey => $selectedIds) { + if (!in_array($sequence->action_name, $sequenceMap[$segmentKey], true)) { + continue; + } + + if ($this->hasMatchingSegmentSettings($sequence->settings, $segmentKey, $selectedIds)) { + $funnelIds[$segmentKey][] = (int) $sequence->funnel_id; + } + } + } + }); + + foreach ($funnelIds as $segmentKey => $ids) { + $funnelIds[$segmentKey] = array_values(array_unique($ids)); + } + + return $funnelIds; + } + + /** + * Check if serialized funnel settings contain any selected tag/list IDs. + * + * @param array $settings Funnel or sequence settings. + * @param string $settingsKey Settings key to read. + * @param array $selectedIds Selected tag/list IDs. + * @return bool + */ + private function hasMatchingSegmentSettings($settings, $settingsKey, $selectedIds) + { + $settingsIds = Arr::get((array) $settings, $settingsKey, []); + + if (!is_array($settingsIds) || !$settingsIds) { + return false; + } + + $settingsIds = array_filter(array_map(function ($item) { + if (is_array($item)) { + return (int) Arr::get($item, 'id'); + } + + return (int) $item; + }, $settingsIds)); + + return (bool) array_intersect($settingsIds, $selectedIds); + } + + public function getFunnel(Request $request, $funnelId) + { + $with = $request->get('with', []); + $funnel = Funnel::findOrFail($funnelId); + + if (defined('MEPR_PLUGIN_NAME')) { + // Maybe trigger name changed + $migrationMaps = [ + 'recurring-transaction-expired' => 'mepr-event-transaction-expired' + ]; + + if (isset($migrationMaps[$funnel->trigger_name])) { + $funnel->trigger_name = $migrationMaps[$funnel->trigger_name]; + $funnel->save(); + } + } + + $triggers = $this->getTriggers(); + if (isset($triggers[$funnel->trigger_name])) { + $funnel->trigger = $triggers[$funnel->trigger_name]; + } + + /** + * Determine the funnel editor details based on the funnel trigger name. + * + * The dynamic portion of the hook name, `$funnel->trigger_name`, refers to the trigger name of the funnel. + * + * @param object $funnel The funnel object containing the editor details. + * @since 1.0.0 + * + */ + $funnel = apply_filters('fluentcrm_funnel_editor_details_' . $funnel->trigger_name, $funnel); + + $funnel->description = $funnel->getMeta('description'); + $inProgressCounts = $this->getInProgressSubscriberCounts([$funnel->id]); + $funnel->in_progress_subscribers_count = $inProgressCounts[(int) $funnel->id] ?? 0; + + if (!$funnel->settings) { + $funnel->settings = (object)[]; + } + + $data = [ + 'funnel' => $funnel + ]; + + if (in_array('blocks', $with)) { + $data['blocks'] = $this->getBlocks($funnel); + } + + if (in_array('block_fields', $with)) { + $data['block_fields'] = $this->getBlockFields($funnel); + /** + * Determine the smart codes for a funnel based on the context. + * + * This filter allows modification of the context smart codes used in a funnel based on the funnel's trigger name. + * + * @param array An array of context smart codes. + * @param string $funnel ->trigger_name The name of the funnel trigger. + * @param object $funnel The funnel object. + * @since 2.5.7 + * + */ + $data['composer_context_codes'] = apply_filters('fluent_crm_funnel_context_smart_codes', [], $funnel->trigger_name, $funnel); + } + + if (in_array('funnel_sequences', $with)) { + FunnelHelper::maybeMigrateConditions($funnel->id); + $data['funnel_sequences'] = $this->getFunnelSequences($funnel, true); + } + + return $data; + } + + public function create(Request $request) + { + try { + $funnel = $this->validate($request->get('funnel'), [ + 'trigger_name' => 'required' + ]); + + $description = sanitize_textarea_field(Arr::get($funnel, 'description')); + + $funnelData = Arr::only($funnel, ['title', 'trigger_name']); + + $funnelData['title'] = sanitize_text_field($funnelData['title']); + + + if (empty($funnelData['title'])) { + $allTriggers = $this->getTriggers(); + $label = Arr::get($allTriggers, $funnelData['trigger_name'] . '.label', 'Unknown Automation'); + $funnelData['title'] = $label . ' (Created at ' . gmdate('Y-m-d') . ')'; + } + + $funnelData['status'] = 'draft'; + $funnelData['settings'] = []; + $funnelData['conditions'] = []; + $funnelData['created_by'] = get_current_user_id(); + + $funnelData = Sanitize::funnel($funnelData); + $funnel = Funnel::create($funnelData); + + if ($description) { + $funnel->updateMeta('description', $description); + } + + return [ + 'funnel' => $funnel, + 'message' => __('Automation has been created. Please configure now', 'fluent-crm') + ]; + } catch (ValidationException $e) { + return $this->validationErrors($e); + } + } + + public function delete(Request $request, $funnelId) + { + $funnel = Funnel::findOrFail($funnelId); + + $sequences = FunnelSequence::where('funnel_id', $funnelId)->get(); + foreach ($sequences as $deletingSequence) { + do_action('fluentcrm_funnel_sequence_deleting_' . $deletingSequence->action_name, $deletingSequence, $funnel); + $deletingSequence->delete(); + } + + $labelIds = TermRelation::where('object_id', $funnel->id) + ->where('object_type', Funnel::class) + ->pluck('term_id') + ->toArray(); + if (!empty($labelIds)) { + $funnel->detachLabels($labelIds); + } + + FunnelSubscriber::where('funnel_id', $funnelId)->delete(); + FunnelMetric::where('funnel_id', $funnelId)->delete(); + + $funnel->deleteMeta('description'); + $funnel->deleteMeta('funnel_label'); + $funnel->delete(); + + (new FunnelHandler())->resetFunnelIndexes(); + + return [ + 'message' => __('Automation has been deleted', 'fluent-crm') + ]; + } + + public function getTriggersRest() + { + return [ + 'triggers' => $this->getTriggers() + ]; + } + + public function changeTrigger(Request $request, $funnelId) + { + $data = $request->only(['title', 'trigger_name']); + + $this->validate($data, [ + 'trigger_name' => 'required', + 'title' => 'required' + ]); + + $funnel = Funnel::findOrFail($funnelId); + + if ($funnel->trigger_name == $data['trigger_name']) { + return $this->sendError([ + 'message' => __('Trigger name is same', 'fluent-crm') + ]); + } + + $funnel->trigger_name = sanitize_text_field($data['trigger_name']); + $funnel->title = sanitize_text_field($data['title']); + + $funnel->settings = []; + $funnel->conditions = []; + $funnel->save(); + + /** + * Determine the funnel editor details based on the funnel's trigger name in FluentCRM. + * + * The dynamic portion of the hook name, `$funnel->trigger_name`, refers to the trigger name of the funnel. + * + * @param object $funnel The funnel object containing the editor details. + * @since 2.3.1 + * + */ + $funnel = apply_filters('fluentcrm_funnel_editor_details_' . $funnel->trigger_name, $funnel); + + return [ + 'message' => __('Automation trigger has been successfully updated', 'fluent-crm'), + 'funnel' => $funnel + ]; + + } + + private function getTriggers() + { + /** + * Determine the list of funnel triggers in FluentCRM. + * + * This filter allows you to modify the array of funnel triggers. + * + * @param array An array of funnel triggers. + * @since 1.0.0 + * + */ + return apply_filters('fluentcrm_funnel_triggers', []); + } + + private function getBlocks($funnel) + { + /** + * Determine the funnel blocks. + * + * This filter allows modification of the funnel blocks. + * + * @param array An array of funnel blocks. + * @param mixed $funnel The funnel object or data. + * @since 1.0.0 + * + */ + return apply_filters('fluentcrm_funnel_blocks', [], $funnel); + } + + private function getBlockFields($funnel) + { + /** + * Determine the funnel block fields. + * + * This filter allows modification of the funnel block fields. + * + * @param array The current funnel block fields. + * @param object $funnel The funnel object. + * @since 1.0.0 + * + */ + return apply_filters('fluentcrm_funnel_block_fields', [], $funnel); + } + + public function getFunnelSequences($funnel, $isFiltered = false) + { + $sequences = FunnelHelper::getFunnelSequences($funnel, $isFiltered); + $formattedSequences = []; + $childs = []; + + foreach ($sequences as $sequence) { + if ($sequence['type'] == 'conditional') { + $sequence['children'] = [ + 'yes' => [], + 'no' => [] + ]; + } else if ($sequence['type'] == 'benchmark') { + // @todo: we may delete this mid 2023 + if (empty($sequence['settings']['can_enter'])) { + $sequence['settings']['can_enter'] = 'yes'; + } + } + + if ($parentId = Arr::get($sequence, 'parent_id')) { + if (!isset($childs[$parentId]['yes'])) { + $childs[$parentId]['yes'] = []; + } + if (!isset($childs[$parentId]['no'])) { + $childs[$parentId]['no'] = []; + } + $childs[$parentId][$sequence['condition_type']][] = $sequence; + } else { + $formattedSequences[$sequence['id']] = $sequence; + } + } + + if ($childs) { + foreach ($childs as $sequenceId => $children) { + if (isset($formattedSequences[$sequenceId])) { + $formattedSequences[$sequenceId]['children'] = $children; + } + } + } + + return array_values($formattedSequences); + } + + public function saveSequencesFallback(Request $request) + { + $funnelId = intval($request->get('funnel_id')); + return $this->saveSequences($request, $funnelId); + } + + public function saveSequences(Request $request, $funnelId) + { + $data = $request->all(); + + $funnel = FunnelHelper::saveFunnelSequence($funnelId, $data); + + return [ + 'sequences' => $this->getFunnelSequences($funnel, true), + 'message' => __('Sequence successfully updated', 'fluent-crm') + ]; + } + + public function getSubscribers(Request $request, $funnelId) + { + + $funnel = Funnel::findOrFail($funnelId); + + $search = $request->getSafe('search', 'sanitize_text_field', ''); + $status = $request->getSafe('status', 'sanitize_text_field', ''); + + $funnelSubscribersQuery = FunnelSubscriber::with([ + 'subscriber', + 'last_sequence', + 'next_sequence_item', + 'metrics' => function ($query) use ($funnelId) { + $query->where('funnel_id', $funnelId); + } + ]) + ->orderBy('id', 'DESC') + ->where('funnel_id', $funnelId); + + if ($search) { + $funnelSubscribersQuery->whereHas('subscriber', function ($q) use ($search) { + $q->searchBy($search); + }); + } + + $sequenceId = (int)$request->get('sequence_id'); + if ($sequenceId) { + $funnelSubscribersQuery->whereHas('metrics', function ($q) use ($sequenceId) { + $q->where('sequence_id', $sequenceId); + }); + } + + if ($status && $status !== 'all') { + $funnelSubscribersQuery->where('status', $status); + } + + $funnelSubscribers = $funnelSubscribersQuery->paginate(); + + $data = [ + 'funnel_subscribers' => $funnelSubscribers + ]; + + $with = $request->get('with', []); + + if (in_array('funnel', $with)) { + $data['funnel'] = $funnel; + } + + if (in_array('sequences', $with)) { + $sequences = FunnelSequence::where('funnel_id', $funnelId) + ->orderBy('sequence', 'ASC') + ->get(); + $formattedSequences = []; + foreach ($sequences as $sequence) { + $formattedSequences[] = $sequence; + } + $data['sequences'] = $formattedSequences; + } + + return $data; + } + + public function getSubscriberReporting(Request $request, $funnelId, $contactId) + { + Funnel::findOrFail($funnelId); + Subscriber::findOrFail($contactId); + + $funnelSubscriber = FunnelSubscriber::with([ + 'last_sequence', + 'next_sequence_item', + 'metrics' => function ($query) use ($funnelId) { + $query->where('funnel_id', $funnelId); + } + ]) + ->where('funnel_id', $funnelId) + ->where('subscriber_id', $contactId) + ->first(); + + $sequences = FunnelSequence::where('funnel_id', $funnelId) + ->orderBy('sequence', 'ASC') + ->get(); + + $formattedSequences = []; + foreach ($sequences as $sequence) { + $formattedSequences[] = $sequence; + } + + return [ + 'funnel_subscriber' => $funnelSubscriber, + 'sequences' => $formattedSequences + ]; + } + + public function getAllActivities(Request $request) + { + $search = $request->getSafe('search', 'sanitize_text_field', ''); + $status = $request->getSafe('status', 'sanitize_text_field', ''); + + $funnelSubscribersQuery = FunnelSubscriber::with([ + 'subscriber', + 'last_sequence', + 'next_sequence_item', + 'funnel.actions' => function ($query) { + $query->orderBy('sequence', 'ASC'); + } + ]) + ->orderBy('id', 'DESC'); + + if ($search) { + $funnelSubscribersQuery->whereHas('subscriber', function ($q) use ($search) { + $q->searchBy($search); + }); + } + + if ($status) { + $funnelSubscribersQuery->where('status', $status); + } + + $funnelSubscribers = $funnelSubscribersQuery->paginate(); + + $funnelIds = $funnelSubscribers->pluck('funnel_id')->unique()->values()->toArray(); + $subscriberIds = $funnelSubscribers->pluck('subscriber_id')->unique()->values()->toArray(); + + $allMetrics = FunnelMetric::whereIn('funnel_id', $funnelIds) + ->whereIn('subscriber_id', $subscriberIds) + ->get() + ->groupBy(function ($metric) { + return $metric->funnel_id . '_' . $metric->subscriber_id; + }); + + foreach ($funnelSubscribers as $funnelSubscriber) { + $key = $funnelSubscriber->funnel_id . '_' . $funnelSubscriber->subscriber_id; + $funnelSubscriber->metrics = $allMetrics[$key] ?? []; + } + + return [ + 'activities' => $funnelSubscribers + ]; + } + + public function removeBulkSubscribers(Request $request) + { + $funnel_subscriber_ids = $request->get('funnel_subscriber_ids', []); + + $funnel_subscriber_ids = array_map('intval', $funnel_subscriber_ids); + + if (!$funnel_subscriber_ids) { + return $this->sendError([ + 'message' => __('Please provide automation subscriber IDs', 'fluent-crm') + ]); + } + + $items = FunnelSubscriber::whereIn('id', $funnel_subscriber_ids)->get(); + + foreach ($items as $item) { + FunnelMetric::where('funnel_id', $item->funnel_id) + ->where('subscriber_id', $item->subscriber_id) + ->delete(); + } + + FunnelSubscriber::whereIn('id', $funnel_subscriber_ids)->delete(); + + return [ + 'message' => __('Selected subscribers have been removed from this automation', 'fluent-crm') + ]; + } + + public function report(Request $request, Reporting $reporting, $funnelId) + { + return [ + 'stats' => $reporting->funnelStat($funnelId) + ]; + } + + public function updateFunnelProperty(Request $request, $funnelId) + { + $funnel = Funnel::findOrFail($funnelId); + $newStatus = $request->getSafe('status', 'sanitize_text_field'); + + $allowedStatuses = ['draft', 'published']; + if (!in_array($newStatus, $allowedStatuses, true)) { + return $this->sendError([ + 'message' => __('Invalid status value', 'fluent-crm') + ]); + } + + if ($funnel->status == $newStatus) { + return $this->sendError([ + 'message' => __('Automation already has the same status', 'fluent-crm') + ]); + } + + $funnel->status = $newStatus; + $funnel->save(); + + return [ + /* translators: %s: subscription status */ + 'message' => sprintf(esc_html__('Status has been updated to %s', 'fluent-crm'), $newStatus) + ]; + } + + public function handleBulkAction(Request $request) + { + $actionName = $request->getSafe('action_name', 'sanitize_text_field', ''); + + $funnelIds = array_map('intval', (array)$request->get('funnel_ids', [])); + + $funnelIds = array_unique(array_filter($funnelIds)); + + if (!$funnelIds) { + return $this->sendError([ + 'message' => __('Please provide automation IDs', 'fluent-crm') + ]); + } + + if ($actionName == 'change_funnel_status') { + $newStatus = sanitize_text_field($request->get('status', '')); + if (!$newStatus) { + return $this->sendError([ + 'message' => __('Please select status', 'fluent-crm') + ]); + } + + $funnels = Funnel::whereIn('id', $funnelIds)->get(); + + foreach ($funnels as $funnel) { + $oldStatus = $funnel->status; + if ($oldStatus != $newStatus) { + $funnel->status = $newStatus; + $funnel->save(); + } + } + + (new FunnelHandler())->resetFunnelIndexes(); + + return [ + 'message' => __('Status has been changed for the selected automations', 'fluent-crm') + ]; + } + + if ($actionName == 'delete_funnels') { + + $funnels = Funnel::whereIn('id', $funnelIds)->get(); + + foreach ($funnels as $funnel) { + $sequences = FunnelSequence::where('funnel_id', $funnel->id)->get(); + + $labelIds = TermRelation::where('object_id', $funnel->id) + ->where('object_type', Funnel::class) + ->pluck('term_id') + ->toArray(); + if (!empty($labelIds)) { + $funnel->detachLabels($labelIds); + } + + foreach ($sequences as $deletingSequence) { + do_action('fluentcrm_funnel_sequence_deleting_' . $deletingSequence->action_name, $deletingSequence, $funnel); + $deletingSequence->delete(); + } + FunnelSubscriber::where('funnel_id', $funnel->id)->delete(); + FunnelMetric::where('funnel_id', $funnel->id)->delete(); + + $funnel->deleteMeta('funnel_label'); + $funnel->deleteMeta('description'); + $funnel->delete(); + } + + (new FunnelHandler())->resetFunnelIndexes(); + + return [ + 'message' => __('Selected automations have been deleted permanently', 'fluent-crm'), + ]; + + } + + if ($actionName == 'apply_labels') { + $newLabelIds = $request->get('labels'); // labels are id + $newLabelIds = is_array($newLabelIds) ? array_map('intval', $newLabelIds) : []; + + $newLabelIds = array_unique(array_filter($newLabelIds)); + + if (!$newLabelIds) { + return $this->sendError([ + 'message' => __('Please provide labels', 'fluent-crm') + ]); + } + + $funnels = Funnel::whereIn('id', $funnelIds)->get(); + + foreach ($funnels as $funnel) { + $funnel->attachLabels($newLabelIds); + } + + return [ + 'message' => __('Labels has been applied successfully', 'fluent-crm'), + ]; + } + + return $this->sendError([ + 'message' => __('invalid bulk action', 'fluent-crm') + ]); + } + + public function cloneFunnel(Request $request, $funnelId) + { + $oldFunnel = Funnel::findOrFail($funnelId); + + $newFunnelData = [ + 'title' => __('[Copy] ', 'fluent-crm') . $oldFunnel->title, + 'trigger_name' => $oldFunnel->trigger_name, + 'status' => 'draft', + 'conditions' => $oldFunnel->conditions, + 'settings' => $oldFunnel->settings, + 'created_by' => get_current_user_id() + ]; + $labelIds = $oldFunnel->getFormattedLabels()->pluck('id')->toArray(); + + $funnel = Funnel::create($newFunnelData); + $funnel->attachLabels($labelIds); + + $sequences = FunnelHelper::getFunnelSequences($oldFunnel, true); + + $sequenceIds = []; + $cDelay = 0; + $delay = 0; + + $childs = []; + $oldNewMaps = []; + + foreach ($sequences as $index => $sequence) { + $oldId = $sequence['id']; + unset($sequence['id']); + unset($sequence['created_at']); + unset($sequence['updated_at']); + + // it's creatable + $sequence['funnel_id'] = $funnel->id; + $sequence['status'] = 'published'; + $sequence['conditions'] = []; + $sequence['sequence'] = $index + 1; + $sequence['c_delay'] = $cDelay; + $sequence['delay'] = $delay; + $delay = 0; + + $actionName = $sequence['action_name']; + + if ($actionName == 'fluentcrm_wait_times') { + $delay = FunnelHelper::getDelayInSecond($sequence['settings']); + $cDelay += $delay; + } + + /** + * Determine the funnel sequence before saving. + * + * This filter allows modification of the funnel sequence before it is saved. + * + * @param array $sequence The sequence data to be saved. + * @param array $funnel The funnel data associated with the sequence. + * + * @return array The modified sequence data. + * @since 1.1.4 + * + */ + $sequence = apply_filters('fluentcrm_funnel_sequence_saving_' . $sequence['action_name'], $sequence, $funnel); + if (Arr::get($sequence, 'type') == 'benchmark') { + $delay = $sequence['delay']; + } + + $sequence['created_by'] = get_current_user_id(); + + $parentId = Arr::get($sequence, 'parent_id'); + + if ($parentId) { + $childs[$parentId][] = $sequence; + } else { + $createdSequence = FunnelSequence::create($sequence); + do_action('fluent_crm/sequence_created_' . $createdSequence->action_name, $createdSequence); + $sequenceIds[] = $createdSequence->id; + $oldNewMaps[$oldId] = $createdSequence->id; + } + } + + if ($childs) { + foreach ($childs as $oldParentId => $childBlocks) { + foreach ($childBlocks as $childBlock) { + $newParentId = Arr::get($oldNewMaps, $oldParentId); + if ($newParentId) { + $childBlock['parent_id'] = $newParentId; + $createdSequence = FunnelSequence::create($childBlock); + $sequenceIds[] = $createdSequence->id; + } + } + } + } + + FunnelHelper::maybeMigrateConditions($funnel->id); + (new FunnelHandler())->resetFunnelIndexes(); + + return [ + 'message' => __('Automation has been successfully cloned', 'fluent-crm'), + 'funnel' => $funnel + ]; + } + + public function importFunnel(Request $request) + { + $funnelArray = $request->get('funnel'); + $sequences = Helper::parseArrayOrJson($request->get('sequences')); + + if (!is_array($funnelArray) || empty($funnelArray['trigger_name'])) { + return $this->sendError([ + 'message' => __('Invalid automation data. Please provide a valid automation with a trigger name.', 'fluent-crm') + ]); + } + + if (!is_array($sequences)) { + $sequences = []; + } + + $funnel = $this->createFunnelFromData($funnelArray, $sequences); + + return [ + 'message' => __('Automation has been successfully imported', 'fluent-crm'), + 'funnel' => $funnel + ]; + + } + + public function deleteSubscribers(Request $request, $funnelId) + { + $funnel = Funnel::findOrFail($funnelId); + $ids = $request->get('subscriber_ids'); + $ids = is_array($ids) ? array_map('intval', $ids) : []; + $ids = array_unique(array_filter($ids)); + + if (!$ids) { + return $this->sendError([ + 'message' => __('subscriber_ids parameter is required', 'fluent-crm') + ]); + } + + FunnelHelper::removeSubscribersFromFunnel($funnelId, $ids); + + return [ + 'message' => __('Subscriber has been removed from this automation', 'fluent-crm') + ]; + } + + public function subscriberAutomations(Request $request, $subscriberId) + { + $automations = FunnelSubscriber::where('subscriber_id', $subscriberId) + ->with([ + 'funnel', + 'last_sequence', + 'next_sequence_item' + ]) + ->orderBy('id', 'DESC') + ->paginate(); + + return [ + 'automations' => $automations + ]; + } + + public function updateSubscriptionStatus(Request $request, $funnelId, $subscriberId) + { + $status = $request->getSafe('status', 'sanitize_text_field'); + + $allowedStatuses = ['active', 'completed', 'cancelled']; + if (!$status || !in_array($status, $allowedStatuses, true)) { + return $this->sendError([ + 'message' => __('Invalid subscription status', 'fluent-crm') + ]); + } + + $funnelSubscriber = FunnelSubscriber::where('funnel_id', $funnelId) + ->where('subscriber_id', $subscriberId) + ->first(); + + if (!$funnelSubscriber) { + return $this->sendError([ + 'message' => __('No Corresponding report found', 'fluent-crm') + ]); + } + + if ($funnelSubscriber->status == 'completed') { + return $this->sendError([ + 'message' => __('The status already completed state', 'fluent-crm') + ]); + } + + $funnelSubscriber->status = $status; + + if ($status == 'active' && !$funnelSubscriber->next_execution_time) { + $funnelSubscriber->next_execution_time = gmdate('Y-m-d H:i:s', current_time('timestamp') + 60); + } + + $funnelSubscriber->save(); + + return [ + /* translators: %s: subscription status */ + 'message' => sprintf(esc_html__('Status has been updated to %s', 'fluent-crm'), $status) + ]; + } + + public function forceAdvanceSubscriber(Request $request, $funnelId, $subscriberId) + { + $funnelSubscriber = FunnelSubscriber::where('funnel_id', intval($funnelId)) + ->where('subscriber_id', intval($subscriberId)) + ->first(); + + if (!$funnelSubscriber) { + return $this->sendError([ + 'message' => __('No corresponding subscriber found in this automation', 'fluent-crm') + ]); + } + + if (in_array($funnelSubscriber->status, ['completed', 'cancelled', 'pending'])) { + return $this->sendError([ + 'message' => sprintf( + /* translators: %s: subscriber status */ + esc_html__('Cannot advance a subscriber with status: %s', 'fluent-crm'), + $funnelSubscriber->status + ) + ]); + } + + $targetSequenceId = intval($request->get('sequence_id')); + $targetSequence = FunnelSequence::where('id', $targetSequenceId) + ->where('funnel_id', intval($funnelId)) + ->first(); + + if (!$targetSequence) { + return $this->sendError([ + 'message' => __('Target sequence not found', 'fluent-crm') + ]); + } + + $processor = new FunnelProcessor(); + + // If waiting on benchmark, record skip metric for the current benchmark + if ($funnelSubscriber->status === 'waiting') { + $benchmarkSeq = FunnelSequence::find($funnelSubscriber->next_sequence_id); + if ($benchmarkSeq) { + FunnelMetric::updateOrCreate( + [ + 'funnel_id' => intval($funnelId), + 'sequence_id' => $benchmarkSeq->id, + 'subscriber_id' => intval($subscriberId), + ], + [ + 'benchmark_value' => 0, + 'benchmark_currency' => 'USD', + 'status' => 'skipped', + 'notes' => __('Manually skipped by admin', 'fluent-crm'), + ] + ); + FunnelHelper::changeFunnelSubSequenceStatus($funnelSubscriber->id, $benchmarkSeq->id, 'skipped'); + } + } + + // Advance to the target sequence + // Find the sequence just before the target so SequencePoints includes the target in its query + $prevSequence = FunnelSequence::where('funnel_id', intval($funnelId)) + ->where('sequence', '<', $targetSequence->sequence) + ->orderBy('sequence', 'DESC') + ->first(); + + $funnelSubscriber->last_sequence_id = $prevSequence ? $prevSequence->id : 0; + $funnelSubscriber->next_sequence_id = $targetSequence->id; + $funnelSubscriber->next_sequence = $targetSequence->sequence; + $funnelSubscriber->status = 'active'; + $funnelSubscriber->next_execution_time = current_time('mysql'); + $funnelSubscriber->save(); + + $processor->processFunnelAction($funnelSubscriber); + + $funnelSubscriber = FunnelSubscriber::where('id', $funnelSubscriber->id) + ->with(['last_sequence', 'next_sequence_item']) + ->first(); + + return [ + 'message' => __('Subscriber has been advanced', 'fluent-crm'), + 'funnel_subscriber' => $funnelSubscriber + ]; + } + + private function maybeMigrateDB() + { + // Temp + Funnel::whereNull('trigger_name') + ->where('status', 'draft') + ->whereNull('created_by') + ->delete(); + + + $sequence = \FluentCrm\App\Models\FunnelSequence::first(); + $isMigrated = false; + global $wpdb; + if ($sequence) { + $attributes = $sequence->getAttributes(); + if (isset($attributes['parent_id'])) { + $isMigrated = true; + } + } else { + $isMigrated = $wpdb->get_col($wpdb->prepare("SELECT * FROM information_schema.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND COLUMN_NAME='parent_id' AND TABLE_NAME=%s", $wpdb->prefix . 'fc_funnel_sequences')); + } + + if (!$isMigrated) { + $sequenceTable = $wpdb->prefix . 'fc_funnel_sequences'; + // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared + $wpdb->query("ALTER TABLE {$sequenceTable} ADD COLUMN `parent_id` bigint NOT NULL DEFAULT '0', ADD `condition_type` varchar(192) NULL AFTER `parent_id`"); + } + } + + public function getEmailReports(Request $request, $funnelId) + { + $funnel = Funnel::findOrFail($funnelId); + $emailSequences = FunnelSequence::where('funnel_id', $funnel->id) + ->orderBy('sequence', 'ASC') + ->where('action_name', 'send_custom_email') + ->get(); + + $campaignIds = []; + foreach ($emailSequences as $emailSequence) { + $refId = Arr::get($emailSequence->settings, 'reference_campaign'); + if ($refId) { + $campaignIds[] = $refId; + } + } + + $campaigns = FunnelCampaign::whereIn('id', array_unique($campaignIds))->get()->keyBy('id'); + + foreach ($emailSequences as $emailSequence) { + $refId = Arr::get($emailSequence->settings, 'reference_campaign'); + $campaign = $refId ? ($campaigns[$refId] ?? null) : null; + + if ($campaign) { + $emailSequence->campaign = [ + 'subject' => $campaign->email_subject, + 'id' => $campaign->id, + 'stats' => $campaign->stats(), + 'status' => $campaign->status, + 'open_tracking_status' => $campaign->getOpenTrackingStatus(), + 'click_tracking_status' => $campaign->getClickTrackingStatus() + ]; + } else { + $emailSequence->campaign = null; + } + } + + return [ + 'email_sequences' => $emailSequences + ]; + } + + public function saveEmailActionFallback(Request $request) + { + $funnelId = intval($request->get('funnel_id')); + return $this->saveEmailAction($request, $funnelId); + } + + public function saveEmailAction(Request $request, $funnelId) + { + $funnel = Funnel::findOrFail($funnelId); + + $settings = Helper::parseArrayOrJson($request->get('action_data')); + $settings['action_name'] = 'send_custom_email'; + + $funnelCampaign = Arr::get($settings, 'campaign', []); + + $funnelCampaignId = Arr::get($funnelCampaign, 'id'); + + $data = Arr::only($funnelCampaign, array_keys(FunnelCampaign::getMock())); + $data['settings']['mailer_settings'] = Arr::get($settings, 'mailer_settings', []); + + $type = 'created'; + + if ($funnelCampaignId && $funnel->id == Arr::get($data, 'parent_id')) { + // We have this campaign + $data['settings'] = \maybe_serialize($data['settings']); + $data['type'] = 'funnel_email_campaign'; + $data['title'] = $funnel->title . ' (' . $funnel->id . ')'; + FunnelCampaign::where('id', $funnelCampaignId)->update($data); + $type = 'updated'; + } else { + $data['parent_id'] = $funnel->id; + $data['type'] = 'funnel_email_campaign'; + $data['title'] = $funnel->title . ' (' . $funnel->id . ')'; + $campaign = FunnelCampaign::create($data); + $funnelCampaignId = $campaign->id; + } + + if (Arr::get($funnelCampaign, 'design_template') == 'visual_builder') { + $design = Arr::get($funnelCampaign, '_visual_builder_design', []); + fluentcrm_update_campaign_meta($funnelCampaignId, '_visual_builder_design', $design); + } else { + fluentcrm_delete_campaign_meta($funnelCampaignId, '_visual_builder_design'); + } + + $refCampaign = FunnelCampaign::find($funnelCampaignId); + + return [ + 'type' => $type, + 'reference_campaign' => $funnelCampaignId, + 'campaign' => Arr::only($refCampaign->toArray(), array_keys(FunnelCampaign::getMock())) + ]; + } + + public function getSyncableContactCounts(Request $request, $funnelId) + { + $funnel = Funnel::findOrFail($funnelId); + $latestAction = \FluentCrm\App\Models\FunnelSequence::where('funnel_id', $funnelId) + ->orderBy('sequence', 'DESC') + ->first(); + + if (!$latestAction) { + return [ + 'syncable_count' => 0 + ]; + } + + $count = \FluentCrm\App\Models\FunnelSubscriber::where('funnel_id', $funnel->id) + ->with(['subscriber']) + ->where('status', 'completed') + ->whereHas('subscriber', function ($q) { + $q->where('status', 'subscribed'); + }) + ->whereHas('last_sequence', function ($q) use ($latestAction) { + $q->where('action_name', '!=', 'end_this_funnel') + ->where('id', '!=', $latestAction->id) + ->where('sequence', '<', $latestAction->sequence); + })->count(); + + return [ + 'syncable_count' => $count + ]; + } + + public function syncNewSteps(Request $request, $funnelId) + { + $funnel = Funnel::findOrFail($funnelId); + + if ($funnel->status != 'published') { + return $this->sendError([ + 'message' => __('Automation status needs to be published', 'fluent-crm') + ]); + } + + if (!defined('FLUENTCAMPAIGN_DIR_FILE')) { + return $this->sendError([ + 'message' => __('This feature requires the latest version of FluentCRM Pro', 'fluent-crm') + ]); + } + + $cleanup = new \FluentCampaign\App\Hooks\Handlers\Cleanup(); + + if (!method_exists($cleanup, 'syncAutomationSteps')) { + return $this->sendError([ + 'message' => __('This feature requires the latest version of FluentCRM Pro', 'fluent-crm') + ]); + } + + $result = $cleanup->syncAutomationSteps($funnel); + + if (is_wp_error($result)) { + return $this->sendError($result->get_error_messages()); + } + + return [ + 'message' => __('Synced successfully', 'fluent-crm') + ]; + } + + public function getTemplates() + { + $templates = fluentCrmPersistentCache('funnel_remote_templates', function () { + return $this->getDynamicTemplates(); + }, 60 * 60 * 24); // 24 hours + + $allowedTemplates = $this->filterTemplates($templates); + + return [ + 'templates' => $allowedTemplates, + 'all' => $templates, + 'cats' => $this->allowedCategories() + ]; + } + + public function filterTemplates($templates) + { + $allowedCategories = $this->allowedCategories(); + $filteredTemplates = []; + + foreach ($templates as $template) { + if (empty($template['dependencies'])) { + $filteredTemplates[] = $template; + continue; + } + $diff = array_diff($template['dependencies'], $allowedCategories); + if (!$diff) { + $filteredTemplates[] = $template; + } + } + + return $filteredTemplates; + } + + public function allowedCategories() + { + $categories = []; + + if (defined('FLUENTFORM')) { + $categories[] = 'fluentforms'; + } + + if (defined('MEPR_PLUGIN_NAME')) { + $categories[] = 'memberpress'; + } + + if (defined('FLUENT_BOARDS')) { + $categories[] = 'fluent-boards'; + } + + if (defined('FLUENT_SUPPORT')) { + $categories[] = 'fluent-support'; + } + + if (defined('FLUENT_BOOKING_VERSION')) { + $categories[] = 'fluent-booking'; + } + + if (defined('WC_PLUGIN_FILE')) { + $categories[] = 'woocommerce'; + } + + if (defined('WCS_INIT_TIMESTAMP')) { + $categories[] = 'wcs'; + } + + if (Helper::isEdd3()) { + $categories[] = 'edd'; + } + + if (defined('LIFTERLMS_VERSION')) { + $categories[] = 'lifterlms'; + } + + if (defined('TUTOR_VERSION')) { + $categories[] = 'tutor'; + } + + if (defined('LEARNDASH_VERSION')) { + $categories[] = 'learndash'; + } + + if (defined('SURECART_PLUGIN_FILE')) { + $categories[] = 'surecart'; + } + + if (Helper::isExperimentalEnabled('abandoned_cart')) { + $categories[] = 'woo_abandon_carts'; + } + + if (defined('FLUENTCAMPAIGN_DIR_FILE')) { + $categories[] = 'fluentcrm_pro'; + } + + return $categories; + + } + + public function createFromTemplate(Request $request) + { + $template = $request->get('template'); + + $templateData = $this->getFunnelData($template['content']); + + if (empty($templateData) || !isset($templateData['sequences'])) { + return $this->sendError([ + 'message' => __('Could not load template data. The template URL may be unavailable or not allowed.', 'fluent-crm') + ]); + } + + $funnelArray = $templateData; + $sequences = $templateData['sequences']; + + $funnel = $this->createFunnelFromData($funnelArray, $sequences); + + return [ + 'funnel' => $funnel, + 'message' => __('Automation has been created from template', 'fluent-crm') + ]; + + } + + private function createFunnelFromData($funnelArray, $sequences) + { + $funnelArray = Sanitize::funnel($funnelArray); + + $newFunnelData = [ + 'title' => Arr::get($funnelArray, 'title'), + 'trigger_name' => Arr::get($funnelArray, 'trigger_name'), + 'status' => 'draft', + 'conditions' => Arr::get($funnelArray, 'conditions', []), + 'settings' => Arr::get($funnelArray, 'settings'), + 'created_by' => get_current_user_id() + ]; + + $funnel = Funnel::create($newFunnelData); + + $funnelLabels = Arr::get($funnelArray, 'labels', []); + if (isset($funnelLabels) && !empty($funnelLabels)) { + $newLabels = []; + foreach ($funnelLabels as $key => $funnelLabel) { + // Validate required fields + if (!isset($funnelLabel['slug'], $funnelLabel['title'])) { + continue; // Skip invalid labels + } + + $existLabel = Label::where('taxonomy_name', 'global_label')->where('slug', $funnelLabel['slug'])->first(); + if (!$existLabel) { + $labelData = [ + 'slug' => sanitize_text_field($funnelLabel['slug']), + 'title' => sanitize_text_field($funnelLabel['title']), + ]; + $color = sanitize_hex_color($funnelLabel['color']); + + $labelData['settings'] = [ + 'color' => $color + ]; + + $existLabel = Label::create($labelData); + } + + $newLabels[] = $existLabel->id; + } + $funnel->attachLabels($newLabels); + } + + $sequenceIds = []; + $cDelay = 0; + $delay = 0; + + $childs = []; + $oldNewMaps = []; + + foreach ($sequences as $index => $sequence) { + $oldId = $sequence['id']; + unset($sequence['id']); + unset($sequence['created_at']); + unset($sequence['updated_at']); + // it's creatable + $sequence['funnel_id'] = $funnel->id; + $sequence['status'] = 'published'; + $sequence['conditions'] = []; + $sequence['sequence'] = $index + 1; + $sequence['c_delay'] = $cDelay; + $sequence['delay'] = $delay; + $delay = 0; + + $actionName = $sequence['action_name']; + + if ($actionName == 'fluentcrm_wait_times') { + $delay = FunnelHelper::getDelayInSecond($sequence['settings']); + $cDelay += $delay; + } + /** + * Determine the funnel sequence before saving. + * + * This filter allows modification of the funnel sequence before it is saved. + * + * @param array $sequence The sequence data to be saved. + * @param array $funnel The funnel data associated with the sequence. + * + * @return array The modified sequence data. + * @since 2.9.20 + * + */ + $sequence = apply_filters('fluentcrm_funnel_sequence_saving_' . $sequence['action_name'], $sequence, $funnel); + + if (Arr::get($sequence, 'type') == 'benchmark') { + $delay = $sequence['delay']; + } + + $sequence['created_by'] = get_current_user_id(); + + $parentId = Arr::get($sequence, 'parent_id'); + + if ($parentId) { + $childs[$parentId][] = $sequence; + } else { + $createdSequence = FunnelSequence::create($sequence); + do_action('fluent_crm/sequence_created_' . $createdSequence->action_name, $createdSequence); + + $sequenceIds[] = $createdSequence->id; + $oldNewMaps[$oldId] = $createdSequence->id; + } + } + + if ($childs) { + foreach ($childs as $oldParentId => $childBlocks) { + foreach ($childBlocks as $childBlock) { + $newParentId = Arr::get($oldNewMaps, $oldParentId); + if ($newParentId) { + $childBlock['parent_id'] = $newParentId; + $createdSequence = FunnelSequence::create($childBlock); + $sequenceIds[] = $createdSequence->id; + } + } + } + } + + (new FunnelHandler())->resetFunnelIndexes(); + FunnelHelper::maybeMigrateConditions($funnel->id); + + return $funnel; + } + + public function temporaryStaticTemplates() + { + return \FluentCrm\App\Services\Funnel\StaticTemplates::get(); + } + + public function getDynamicTemplates() + { + $restBase = defined('FC_TEMPLATE_API_DOMAIN') ? FC_TEMPLATE_API_DOMAIN : 'https://fluentcrm.com'; + $restApi = $restBase . '/wp-json/wp/v2/automation-templates?per_page=50'; + + $response = wp_remote_get($restApi, [ + 'sslverify' => true, + ]); + + if (is_wp_error($response)) { + // Handle error + error_log($response->get_error_message()); + return []; + } + + $body = wp_remote_retrieve_body($response); + $templateLists = json_decode($body, true); + + if (json_last_error() !== JSON_ERROR_NONE) { + // Handle JSON error + error_log('JSON Decode Error: ' . json_last_error_msg()); + return []; + } + + if (!is_array($templateLists)) { + return []; + } + + $formattedTemplates = []; + foreach ($templateLists as $template) { + + if (!$template['template_json']) { + // Skip if no template json + continue; + } + $formattedTemplates[] = [ + 'id' => $template['id'], + 'title' => $template['title']['rendered'], +// 'description' => $template['excerpt']['rendered'], + 'short_description' => $template['short_description'], + 'type' => $template['template_type'], + 'dependencies' => $template['plugin_dependencies'], + 'content' => $template['template_json'], + 'link' => $template['link'], + 'media' => $template['_links']['wp:attachment'][0]['href'] ?? '', + 'status' => $template['status'], + ]; + } + + return $formattedTemplates; + } + + public function getFunnelData($jsonUrl) + { + $allowedHosts = ['fluentcrm.com', 'www.fluentcrm.com', 'wpmanageninja.com', 'www.wpmanageninja.com']; + if (defined('FC_TEMPLATE_API_DOMAIN')) { + $configuredHost = wp_parse_url(FC_TEMPLATE_API_DOMAIN, PHP_URL_HOST); + if ($configuredHost) { + $allowedHosts[] = $configuredHost; + } + } + $parsedUrl = wp_parse_url($jsonUrl); + $host = isset($parsedUrl['host']) ? strtolower($parsedUrl['host']) : ''; + + if (!in_array($host, $allowedHosts, true)) { + return []; + } + + $request = wp_remote_get($jsonUrl, [ + 'sslverify' => true, + ]); + + if (is_wp_error($request)) { + return []; + } + return json_decode(wp_remote_retrieve_body($request), true); + } + + public function sendTestWebhook(Request $request) + { + + $this->validate($request->all(), [ + 'data.remote_url' => 'required|url', + ], [ + 'data.remote_url.required' => __('Remote URL is required', 'fluent-crm') + ]); + + $payloadData = $request->get('data', []); + + $bodyDataType = sanitize_text_field(Arr::get($payloadData, 'body_data_type', '')); + $bodyDataValues = Arr::get($payloadData, 'body_data_values', []); + $headerType = sanitize_text_field(Arr::get($payloadData, 'header_type', '')); + $headerData = Arr::get($payloadData, 'header_data', []); + $sendingMethod = sanitize_text_field(Arr::get($payloadData, 'sending_method', '')); + $requestFormat = sanitize_text_field(Arr::get($payloadData, 'request_format', '')); + $remoteUrl = sanitize_text_field(Arr::get($payloadData, 'remote_url', '')); + + if (!is_array($bodyDataValues)) { + $bodyDataValues = []; + } + + if (!is_array($headerData)) { + $headerData = []; + } + + + $user = get_user_by('ID', get_current_user_id()); + $email = $user->user_email; + + $subscriber = Subscriber::where('email', $email) + ->orWhere('status', 'subscribed') + ->first(); + + if (!$subscriber) { + return $this->sendError([ + 'message' => __('No subscriber found to send test webhook. Please add at least one contact with subscribed status.', 'fluent-crm') + ]); + } + + $headers = $this->prepareHeaders($headerType, $headerData, $subscriber); + $body = $this->prepareBody($bodyDataType, $bodyDataValues, $subscriber); + + $isJson = 'no'; + if ($requestFormat == 'json' && $sendingMethod == 'POST') { + $isJson = 'yes'; + $headers['Content-Type'] = 'application/json; charset=utf-8'; + } + + if ($sendingMethod == 'GET') { + $remoteUrl = add_query_arg($body, $remoteUrl); + } + + $data = [ + 'payload' => [ + 'body' => ($sendingMethod == 'POST') ? $body : null, + 'method' => $sendingMethod, + 'headers' => $headers, + /** + * Determine whether to verify SSL for FluentCRM webhook requests. + * + * This filter allows you to control whether SSL verification should be performed + * when making webhook requests in FluentCRM. + * + * @param bool Whether to verify SSL. Default false. + * @since 2.9.25 + * + */ + 'sslverify' => apply_filters('fluent_crm/webhook_ssl_verify', true) + ], + 'remote_url' => $remoteUrl, + 'is_json' => $isJson + ]; + + if ($data['is_json'] == 'yes') { + $data['payload']['body'] = json_encode($data['payload']['body']); + } + + $response = wp_remote_request($data['remote_url'], $data['payload']); + + if (is_wp_error($response)) { + return $this->sendError([ + 'message' => __('Test Webhook failed to send', 'fluent-crm') . ': ' . $response->get_error_message() + ]); + } + + return [ + 'message' => __('Test Webhook has been sent successfully', 'fluent-crm') + ]; + } + + private function prepareHeaders($headerType, $headerData, $subscriber) + { + $headers = []; + if ($headerType === 'with_headers') { + foreach ($headerData as $item) { + $dataKey = sanitize_text_field(Arr::get($item, 'data_key', '')); + $dataValue = sanitize_text_field(Arr::get($item, 'data_value', '')); + + if (empty($dataKey) || empty($dataValue)) { + continue; + } + $dataKey = str_replace(' ', '-', $dataKey); + $headers[$dataKey] = Parser::parse($dataValue, $subscriber); + } + } + return $headers; + } + + private function prepareBody($bodyDataType, $bodyDataValues, $subscriber) + { + $body = []; + if ($bodyDataType === 'subscriber_data') { + $body = $subscriber->toArray(); + $body['custom_field'] = $subscriber->custom_fields(); + } else { + foreach ($bodyDataValues as $item) { + $dataKey = sanitize_text_field(Arr::get($item, 'data_key', '')); + $dataValue = sanitize_text_field(Arr::get($item, 'data_value', '')); + + if (empty($dataKey) || empty($dataValue)) { + continue; + } + $body[$dataKey] = Parser::parse($dataValue, $subscriber); + } + } + return $body; + } + + public function updateFunnelTitle(Request $request, $funnelId) + { + $funnel = Funnel::findOrFail($funnelId); + $newTitle = $request->getSafe('title', 'sanitize_text_field'); + + if ($funnel->title == $newTitle) { + return $this->sendError([ + 'message' => __('Automation already has the same title', 'fluent-crm') + ]); + } + + $funnel->title = $newTitle; + $funnel->save(); + + return [ + /* translators: %s: the new funnel title */ + 'message' => sprintf(esc_html__('Title has been updated to %s', 'fluent-crm'), $newTitle) + ]; + } + + public function updateLabels(Request $request, $funnel_id) + { + $funnel = Funnel::findOrFail($funnel_id); + $action = $request->getSafe('action', 'sanitize_text_field'); + $labelIds = $request->get('label_ids'); + + if (!is_array($labelIds)) { + $labelIds = [$labelIds]; + } + + $labelIds = is_array($labelIds) ? array_map('intval', $labelIds) : []; + $labelIds = array_unique(array_filter($labelIds)); + + if ($action == 'sync') { + $funnel->syncLabels($labelIds); + } elseif ($action == 'attach') { + $funnel->attachLabels($labelIds); + } else { + $funnel->detachLabels($labelIds); + } + + return [ + 'message' => __('Labels has been updated', 'fluent-crm') + ]; + + } + + +} diff --git a/wp-content/plugins/fluent-crm/app/Http/Controllers/GlobalLabelController.php b/wp-content/plugins/fluent-crm/app/Http/Controllers/GlobalLabelController.php new file mode 100644 index 0000000..8306c39 --- /dev/null +++ b/wp-content/plugins/fluent-crm/app/Http/Controllers/GlobalLabelController.php @@ -0,0 +1,149 @@ +get(); + return [ + 'labels' => $labels + ]; + + } + + public function create(Request $request) + { + $data = Arr::get($request->all(), 'label'); + + // sanitize the data + $labelData = [ + 'slug' => sanitize_text_field($data['slug']), + 'title' => sanitize_text_field($data['title']), + ]; + $color = sanitize_hex_color($data['color']); + + $labelData['settings'] = [ + 'color' => $color + ]; + + $label = Label::create($labelData); + + return [ + 'label' => $label, + 'message' => __('Label has been created successfully', 'fluent-crm') + ]; + } + + public function update(Request $request, $id) + { + $data = Arr::get($request->all(), 'label'); + + $label = Label::findOrFail($id); + + // sanitize the data + $labelData = [ + 'slug' => sanitize_text_field($data['slug']), + 'title' => sanitize_text_field($data['title']), + ]; + $color = sanitize_hex_color($data['color']); + + $labelData['settings'] = [ + 'color' => $color + ]; + + $label->update($labelData); + + return [ + 'label' => $label, + 'message' => __('Labels have been updated successfully', 'fluent-crm') + ]; + } + + + public function delete(Request $request, $id) + { + $label = Label::findOrFail($id); + if ($label) { + $label->delete(); + } + + return [ + 'message' => __('Label has been deleted successfully', 'fluent-crm') + ]; + } + + public function deleteLabel(Request $request) + { + $funnelId = $request->getSafe('funnel_id', 'intval'); + $labelSlug = $request->getSafe('label_slug'); + $action = $request->getSafe('action'); + + if (!$labelSlug) { + return [ + 'message' => __('Please provide label slug', 'fluent-crm') + ]; + } + + switch ($action) { + case 'delete_from_funnel': + $this->deleteLabelFromFunnel($funnelId, $labelSlug); + return [ + 'message' => __('Removed from funnel successfully', 'fluent-crm') + ]; + case 'delete_from_funnel_label': + $this->deleteLabelFromFunnelLabel($labelSlug); + return [ + 'message' => __('Label has been deleted successfully', 'fluent-crm') + ]; + default: + return [ + 'message' => __('Invalid Action', 'fluent-crm') + ]; + } + } + + protected function deleteLabelFromFunnel($funnelId, $slug) + { + $funnel = Funnel::findOrFail($funnelId); + $label = Label::where('slug', $slug)->first(); + + if (!$label) { + return; + } + + $funnel->detachLabels([$label->id]); + } + + protected function deleteLabelFromFunnelLabel($slug) + { + $label = Label::where('slug', $slug)->first(); + + if (!$label) { + return; + } + + TermRelation::where('term_id', $label->id) + ->where('object_type', Funnel::class) + ->delete(); + + $label->delete(); + } +} diff --git a/wp-content/plugins/fluent-crm/app/Http/Controllers/ImporterController.php b/wp-content/plugins/fluent-crm/app/Http/Controllers/ImporterController.php new file mode 100644 index 0000000..61b18ef --- /dev/null +++ b/wp-content/plugins/fluent-crm/app/Http/Controllers/ImporterController.php @@ -0,0 +1,386 @@ + [ + 'label' => __('CSV File', 'fluent-crm'), + 'logo' => fluentCrmMix('images/csv.svg'), + 'disabled' => false + ], + 'users' => [ + 'label' => __('WordPress Users', 'fluent-crm'), + 'logo' => fluentCrmMix('images/wordpress.svg'), + 'disabled' => false + ] + ]); + + if (defined('FLUENTCART_VERSION')) { + $drivers['fluent_cart'] = [ + 'label' => __('FluentCart', 'fluent-crm'), + 'logo' => fluentCrmMix('images/fluent-cart-dark.svg'), + 'disabled' => false + ]; + } + + if ($proDrivers = $this->getProDrivers()) { + $drivers = array_merge($drivers, $proDrivers); + } + + return [ + 'drivers' => $drivers + ]; + } + + public function getDriver(Request $request, $driver) + { + if ($driver == 'users') { + return $this->processUserDriver($request); + } + + /** + * Determine the import driver response (CSV). + * + * This filter allows modification of the import driver response based on the specified driver. + * + * @since 2.7.0 + * + * @param bool The response to be filtered or not. Default false. + * @param object $request The request object containing import data. + */ + $response = apply_filters('fluent_crm/get_import_driver_' . $driver, false, $request); + + if (!$response || is_wp_error($response)) { + $message = __('Sorry no driver found for this import', 'fluent-crm'); + if (is_wp_error($response)) { + $message = $response->get_error_message(); + } + return $this->sendError([ + 'message' => $message + ]); + } + + return $response; + } + + public function importData(Request $request, $driver) + { + $config = $request->get('config', []); + $page = $request->getSafe('importing_page', 'intval', 1); + + if ($driver == 'users') { + return $this->processUserImport($config, $page); + } + + /** + * Determine the response after importing data using a specific driver (CSV). + * + * This filter allows you to modify the response after the import process + * using a specified driver. + * + * @since 2.7.0 + * + * @param bool The response to be filtered or not. Default false. + * @param array $config The configuration array for the import process. + * @param int $page The current page number being processed. + */ + $response = apply_filters('fluent_crm/post_import_driver_' . $driver, false, $config, $page); + + if (!$response || is_wp_error($response)) { + $message = __('Sorry no driver found for this import', 'fluent-crm'); + if (is_wp_error($response)) { + $message = $response->get_error_message(); + } + return $this->sendError([ + 'message' => $message + ]); + } + + return $response; + } + + private function processUserDriver($request) + { + $summary = $request->get('summary'); + + if ($summary) { + $config = $request->get('config'); + + $userQuery = new \WP_User_Query([ + 'role__in' => Arr::get($config, 'roles'), + 'number' => 5, + 'fields' => ['ID', 'display_name', 'user_email'], + ]); + + $users = $userQuery->get_results(); + $total = $userQuery->get_total(); + + $formattedUsers = []; + + foreach ($users as $user) { + $formattedUsers[] = [ + 'name' => $user->display_name, + 'email' => $user->user_email + ]; + } + + return $this->send([ + 'import_info' => [ + 'subscribers' => $formattedUsers, + 'total' => $total, + 'has_list_config' => true, + 'has_tag_config' => true, + 'has_status_config' => true, + 'has_update_config' => true, + 'has_silent_config' => true + ]]); + } + + if (!function_exists('get_editable_roles')) { + require_once(ABSPATH . '/wp-admin/includes/user.php'); + } + $roles = \get_editable_roles(); + + $formattedRoles = []; + + foreach ($roles as $roleKey => $role) { + $formattedRoles[] = [ + 'id' => $roleKey, + 'label' => $role['name'] + ]; + } + + $infoSvg = ''; + + return [ + 'config' => [ + 'roles' => [] + ], + 'fields' => [ + 'roles' => [ + 'label' => __('Select User Roles', 'fluent-crm'), + 'inline_help' => $infoSvg . ' ' . __('Please check the user roles that you want to import as contact', 'fluent-crm'), + 'type' => 'checkbox-group', + 'options' => $formattedRoles, + 'has_all_selector' => true, + 'all_selector_label' => __('All', 'fluent-crm') + ] + ], + 'labels' => [ + 'step_2' => __('Next [Review Data]', 'fluent-crm'), + 'step_3' => __('Import Users Now', 'fluent-crm') + ] + ]; + } + + private function processUsers($users, $inputs) + { + $subscribers = []; + foreach ($users as $user) { + $subscriber = Helper::getWPMapUserInfo($user); + $subscriber['source'] = 'wp_users'; + if (isset($subscriber['email']) && $subscriber['email']) { + $subscribers[] = $subscriber; + } + } + + $sendDoubleOptin = Arr::get($inputs, 'double_optin_email') == 'yes'; + + return Subscriber::import( + $subscribers, + Arr::get($inputs, 'tags', []), + Arr::get($inputs, 'lists', []), + Arr::get($inputs, 'update', ''), + Arr::get($inputs, 'status', ''), + $sendDoubleOptin + ); + } + + private function processUserImport($config, $page) + { + $inputs = Arr::only($config, [ + 'map', 'tags', 'lists', 'roles', 'update', 'status', 'double_optin_email', 'import_silently' + ]); + + + /** + * Determine the number of subscribers to process per request while importing. + * + * This filter allows you to modify the number of subscribers that are processed in each request. + * + * @since 2.7.0 + * + * @param int $limit The number of subscribers to process per request. Default is 100. + */ + $limit = apply_filters('fluent_crm/import_users_limit_per_request', 100); + + $userQuery = new \WP_User_Query([ + 'role__in' => $inputs['roles'], + 'number' => $limit, + 'offset' => ($page - 1) * $limit + ]); + + if (Arr::get($inputs, 'import_silently') == 'yes') { + if (!defined('FLUENTCRM_DISABLE_TAG_LIST_EVENTS')) { + define('FLUENTCRM_DISABLE_TAG_LIST_EVENTS', true); + } + } + + $total = $userQuery->get_total(); + $users = $userQuery->get_results(); + if ($users) { + $this->processUsers($users, $inputs); + } + + $hasRecords = !!count($users); + + return $this->sendSuccess([ + 'page_total' => ceil($total / $limit), + 'record_total' => $total, + 'has_more' => $hasRecords, + 'current_page' => $page, + 'next_page' => $page + 1 + ]); + } + + private function getProDrivers() + { + $drivers = []; + + if (defined('FLUENTCAMPAIGN')) { + return $drivers; + } + + if (defined('LLMS_PLUGIN_FILE')) { + $drivers['lifterlms'] = [ + 'label' => __('LifterLMS', 'fluent-crm'), + 'logo' => fluentCrmMix('images/lifterlms.png'), + 'disabled' => true, + 'disabled_message' => __('Import LifterLMS students by course and groups then segment by associate tags. This is a pro feature. Please upgrade to activate this feature', 'fluent-crm') + ]; + } + + if (defined('LEARNDASH_VERSION')) { + $drivers['learndash'] = [ + 'label' => __('LearnDash', 'fluent-crm'), + 'logo' => fluentCrmMix('images/learndash.png'), + 'disabled' => true, + 'disabled_message' => __('Import LearnDash students by course and groups then segment by associate tags. This is a pro feature. Please upgrade to activate this feature', 'fluent-crm') + ]; + } + + if (defined('TUTOR_VERSION')) { + $drivers['tutorlms'] = [ + 'label' => __('TutorLMS', 'fluent-crm'), + 'logo' => fluentCrmMix('images/tutorlms.jpg'), + 'disabled' => true, + 'disabled_message' => __('Import TutorLMS students by course then segment by associate tags. This is a pro feature. Please upgrade to activate this feature', 'fluent-crm') + ]; + } + + if (defined('PMPRO_VERSION')) { + $drivers['pmpro'] = [ + 'label' => __('Paid Membership Pro', 'fluent-crm'), + 'logo' => fluentCrmMix('images/pmpro.png'), + 'disabled' => true, + 'disabled_message' => __('Import Paid Membership Pro members by membership levels then segment by associate tags. This is a pro feature. Please upgrade to activate this feature', 'fluent-crm') + ]; + } + + if (defined('WLM3_PLUGIN_VERSION')) { + $drivers['wishlist_member'] = [ + 'label' => __('Wishlist member', 'fluent-crm'), + 'logo' => fluentCrmMix('images/wishlist_member.png'), + 'disabled' => true, + 'disabled_message' => __('Import Wishlist members by membership levels then segment by associate tags. This is a pro feature. Please upgrade to activate this feature', 'fluent-crm') + ]; + } + + if (class_exists('\Restrict_Content_Pro')) { + $drivers['rcp'] = [ + 'label' => __('Restrict Content Pro', 'fluent-crm'), + 'logo' => fluentCrmMix('images/rcp.png'), + 'disabled' => true, + 'disabled_message' => __('Import Restrict Content Pro members by membership levels then segment by associate tags. This is a pro feature. Please upgrade to activate this feature', 'fluent-crm') + ]; + } + + if (defined('BP_REQUIRED_PHP_VERSION') && function_exists('\buddypress')) { + + $pluginName = 'BuddyPress'; + $logo = fluentCrmMix('images/buddypress.png'); + + if (defined('BP_PLATFORM_VERSION')) { + $pluginName = 'BuddyBoss'; + $logo = fluentCrmMix('images/buddyboss.svg'); + } + + $drivers['buddypress'] = [ + 'label' => $pluginName, + 'logo' => $logo, + 'disabled' => true, + /* translators: %s: plugin name */ + 'disabled_message' => sprintf(__('Import %s members by member groups and member types then segment by associate tags. This is a pro feature. Please upgrade to activate this feature', 'fluent-crm'), $pluginName) + ]; + } + + if (defined('LP_PLUGIN_FILE')) { + $drivers['learnpress'] = [ + 'label' => __('LearnPress', 'fluent-crm'), + 'logo' => fluentCrmMix('images/learnpress.png'), + 'disabled' => true, + 'disabled_message' => __('Import LearnPress students by course then segment by associate tags. This is a pro feature. Please upgrade to activate this feature', 'fluent-crm') + ]; + } + + return $drivers; + } +} diff --git a/wp-content/plugins/fluent-crm/app/Http/Controllers/ListsController.php b/wp-content/plugins/fluent-crm/app/Http/Controllers/ListsController.php new file mode 100644 index 0000000..3b40998 --- /dev/null +++ b/wp-content/plugins/fluent-crm/app/Http/Controllers/ListsController.php @@ -0,0 +1,260 @@ +get('with', []); + + $order = [ + 'by' => $request->getSafe('sort_by', 'sanitize_sql_orderby', 'id'), + 'order' => $request->getSafe('sort_order', 'sanitize_sql_orderby', 'DESC') + ]; + $paginatedLists = Lists::orderBy($order['by'], $order['order']) + ->searchBy($request->getSafe('search')) + ->paginate(); + $lists = $paginatedLists->items(); + + if (!$request->get('exclude_counts')) { + foreach ($lists as $list) { + $list->totalCount = $list->totalCount(); + $list->subscribersCount = $list->countByStatus('subscribed'); + } + } + + $data = [ + 'lists' => $lists, + 'pagination' => [ + 'total' => $paginatedLists->total(), + ] + ]; + + if ($request->get('all_lists')) { + $allLists = Lists::get(); + $formattedLists = []; + foreach ($allLists as $list) { + $formattedLists[] = [ + 'id' => strval($list->id), + 'title' => $list->title, + 'slug' => $list->slug, + 'description' => $list->description + ]; + } + $data['all_lists'] = $formattedLists; + } + + return $this->send($data); + } + + /** + * Find a list. + * + * @param \FluentCrm\Framework\Http\Request\Request $request + * @param int $id + * @return \WP_REST_Response + */ + public function find(Request $request, $id) + { + return $this->send(Lists::find($id)); + } + + + /** + * Store a list. + * + * @param \FluentCrm\Framework\Http\Request\Request $request + * @return \WP_REST_Response + */ + public function create(Request $request) + { + $allData = $request->all(); + + if (empty($allData['slug'])) { + if ($allData['title']) { + $allData['slug'] = sanitize_text_field($allData['title']); + } + } + + $data = $this->validate($allData, [ + 'title' => 'required', + 'slug' => "required|unique:fc_lists,slug" + ]); + + $list = Lists::create([ + 'title' => sanitize_text_field($allData['title']), + 'slug' => sanitize_title($data['slug'], 'display'), + 'description' => sanitize_textarea_field(Arr::get($allData, 'description')) + ]); + + do_action('fluentcrm_list_created', $list->id); + + do_action('fluent_crm/list_created', $list); + + return $this->send([ + 'lists' => $list, + 'item' => $list, + 'message' => __('Successfully saved the list.', 'fluent-crm') + ]); + } + + + /** + * Store a list. + * + * @param \FluentCrm\Framework\Http\Request\Request $request + * @param $id int + * @return \WP_REST_Response + */ + public function update(Request $request, $id) + { + $allData = $this->validate($request->all(), [ + 'title' => 'required' + ]); + + if(!empty($allData['slug'])) { + $allData['slug'] = Helper::slugify($allData['title']); + } + + if ($id == 0 && $request->get('update_by') == 'slug' && !empty($allData['slug'])) { + + $list = Lists::where('slug', $allData['slug'])->first(); + if (!$list) { + return $this->sendError([ + 'message' => __('List could not be found', 'fluent-crm') + ]); + } + + $id = $list->id; + } else { + $list = Lists::findOrFail($id); + if(empty($allData['slug'])) { + $allData['slug'] = $list->slug; + } + } + + if (Lists::where('slug', $allData['slug'])->where('id', '!=', $id)->first()) { + return $this->sendError([ + 'message' => __('Provided slug already exists in another list', 'fluent-crm') + ]); + } + + $list = Lists::where('id', $id)->update([ + 'title' => sanitize_text_field($allData['title']), + 'slug' => $allData['slug'], + 'description' => sanitize_textarea_field(Arr::get($allData, 'description')), + ]); + + do_action('fluentcrm_list_updated', $id); + + do_action('fluent_crm/list_updated', $list); + + return $this->send([ + 'lists' => $list, + 'message' => __('Successfully saved the list.', 'fluent-crm'), + ]); + } + + /** + * Bulk store lists. + * + * @param \FluentCrm\Framework\Http\Request\Request $request + * @return \WP_REST_Response + */ + public function storeBulk(Request $request) + { + $lists = $request->get('lists', []); + if (empty($lists)) { + $lists = $this->request->get('items', []); + } + + $createdIds = []; + foreach ($lists as $list) { + if (empty($list['title'])) { + continue; + } + + if (empty($list['slug'])) { + $list['slug'] = Helper::slugify($list['title']); + } + + $list = Lists::updateOrCreate( + ['slug' => sanitize_title($list['slug'], 'display')], + ['title' => sanitize_text_field($list['title'])] + ); + + $createdIds[] = $list->id; + + if($list->wasRecentlyCreated) { + do_action('fluentcrm_list_created', $list->id); + do_action('fluent_crm/list_created', $list); + } else { + do_action('fluentcrm_list_updated', $list->id); + do_action('fluent_crm/list_updated', $list); + } + } + + return $this->sendSuccess([ + 'message' => __('Provided Lists have been successfully created', 'fluent-crm'), + 'ids' => $createdIds + ]); + } + + /** + * Delete a list + * + * @param \FluentCrm\Framework\Http\Request\Request $request + * @param int $id + * @return \WP_REST_Response + */ + public function remove(Request $request, $id) + { + Lists::where('id', $id)->delete(); + do_action('fluent_crm/list_deleted', $id); + do_action('fluentcrm_list_deleted', $id); + + return $this->send([ + 'message' => __('Successfully removed the list.', 'fluent-crm') + ]); + } + + public function handleBulkAction(Request $request) + { + $listIds = array_map('intval', (array)$request->get('listIds', [])); + + $listIds = array_unique(array_filter($listIds)); + + foreach ($listIds as $listId) { + Lists::where('id', $listId)->delete(); + do_action('fluent_crm/list_deleted', $listId); + do_action('fluentcrm_list_deleted', $listId); + } + + return $this->sendSuccess([ + 'message' => __('Selected Lists have been removed permanently', 'fluent-crm'), + ]); + + } +} diff --git a/wp-content/plugins/fluent-crm/app/Http/Controllers/MCPSettingsController.php b/wp-content/plugins/fluent-crm/app/Http/Controllers/MCPSettingsController.php new file mode 100644 index 0000000..bf588a2 --- /dev/null +++ b/wp-content/plugins/fluent-crm/app/Http/Controllers/MCPSettingsController.php @@ -0,0 +1,431 @@ +isAdapterPresent(); + $toolkitInstalled = $this->isToolkitPresent(); + $adapterRuntimeAvailable = $this->isAdapterRuntimeAvailable(); + $standaloneActive = is_plugin_active(self::ADAPTER_PLUGIN_FILE) && $adapterRuntimeAvailable; + $toolkitActive = $this->isToolkitLoaded(); + $toolkitAdapterActive = $toolkitActive && $this->isToolkitAdapterAvailable(); + $adapterActive = $standaloneActive || $toolkitAdapterActive; + $adapterProvider = $standaloneActive ? 'plugin' : ($toolkitAdapterActive ? 'toolkit' : ''); + $abilitiesAvailable = function_exists('wp_register_ability'); + $canAutoInstall = (bool) apply_filters('fluent_toolkit/can_auto_install', false); + + $toolsCount = $abilitiesAvailable ? $this->countAbilities() : 0; + + $currentUser = wp_get_current_user(); + + // Detect a local dev environment heuristically. Self-signed/local + // certs trip Node's TLS validation in the npx proxy that Claude + // Desktop uses; if we know the user is on dev, we pre-bake the + // workaround into the generated snippet. The Vue page also exposes + // a manual toggle for edge cases. + $isLocalDev = self::detectLocalDevEnvironment(); + + return [ + 'adapter_installed' => $adapterInstalled || $toolkitInstalled, + 'adapter_active' => $adapterActive, + 'adapter_provider' => $adapterProvider, + 'standalone_adapter_installed' => $adapterInstalled, + 'toolkit_installed' => $toolkitInstalled, + 'toolkit_active' => $toolkitActive, + 'toolkit_adapter_available' => $toolkitAdapterActive, + 'adapter_runtime_available' => $adapterRuntimeAvailable, + 'adapter_version' => $this->detectAdapterVersion(), + 'toolkit_version' => $this->detectToolkitVersion(), + 'abilities_api_loaded' => $abilitiesAvailable, + 'endpoint_url' => MCPInit::getEndpointUrl(), + 'tools_count' => $toolsCount, + 'mcp_enabled' => fluentcrm_get_option('mcp_enabled', 'yes') === 'yes', + 'pro_active' => defined('FLUENTCAMPAIGN'), + 'app_passwords_url' => admin_url('profile.php#application-passwords-section'), + 'plugins_url' => admin_url('plugins.php'), + 'can_auto_install_adapter' => $canAutoInstall, + 'toolkit_download_url' => 'https://github.com/WPManageNinja/fluent-toolkit', + 'current_user_login' => $currentUser ? $currentUser->user_login : '', + 'is_local_dev' => $isLocalDev, + ]; + } + + /** + * Toggle the kill-switch. Stored as a FluentCRM option so the lazy-register + * guard in app/Hooks/actions.php picks it up on the next request. + */ + public function toggle(Request $request) + { + $value = $request->get('mcp_enabled'); + $enabled = is_string($value) ? ($value === 'yes' || $value === 'true' || $value === '1') : (bool) $value; + + fluentcrm_update_option('mcp_enabled', $enabled ? 'yes' : 'no'); + + return [ + 'ok' => true, + 'mcp_enabled' => $enabled, + 'message' => $enabled + ? __('MCP tools enabled. New requests will see the FluentCRM abilities.', 'fluent-crm') + : __('MCP tools disabled. The adapter will no longer report FluentCRM abilities.', 'fluent-crm'), + ]; + } + + /** + * One-click adapter install. Free can only explain the missing dependency; + * Pro may opt in to the FluentHub background installer via hooks. + */ + public function installAdapter() + { + if (!current_user_can('install_plugins')) { + return $this->sendError([ + 'message' => __('Sorry! you do not have permission to install plugins', 'fluent-crm'), + ]); + } + + $canAutoInstall = (bool) apply_filters('fluent_toolkit/can_auto_install', false); + if (!$canAutoInstall) { + return $this->sendError([ + 'message' => __('Please install FluentHub from GitHub, then reload this page to connect FluentCRM with AI agents.', 'fluent-crm'), + 'toolkit_download_url' => 'https://github.com/WPManageNinja/fluent-toolkit', + ]); + } + + do_action('fluent_toolkit/do_auto_install'); + + wp_clean_plugins_cache(); + + if (!function_exists('is_plugin_active')) { + require_once ABSPATH . 'wp-admin/includes/plugin.php'; + } + + $toolkitInstalled = $this->isToolkitPresent(); + $toolkitActive = $this->isToolkitLoaded(); + $adapterRuntimeAvailable = $this->isAdapterRuntimeAvailable(); + $toolkitAdapterAvailable = $toolkitActive && $this->isToolkitAdapterAvailable(); + $isInstalled = $this->isAdapterPresent() || $toolkitInstalled; + $isActive = (is_plugin_active(self::ADAPTER_PLUGIN_FILE) && $adapterRuntimeAvailable) || $toolkitAdapterAvailable; + + if ($isInstalled && $isActive) { + $message = __('FluentHub installed and activated. Reload the page to register FluentCRM MCP tools.', 'fluent-crm'); + } elseif ($toolkitInstalled && $toolkitActive) { + $message = __('FluentHub is installed and active, but this version does not include the bundled MCP adapter yet. Please update FluentHub when the MCP-ready build is available, then reload this page.', 'fluent-crm'); + } elseif ($toolkitInstalled) { + $message = __('FluentHub is installed but could not be activated automatically. Please activate FluentHub from the Plugins page, then reload this page.', 'fluent-crm'); + } else { + $message = __('Could not install FluentHub automatically. Please install FluentHub manually, then reload this page.', 'fluent-crm'); + } + + return [ + 'is_installed' => $isInstalled, + 'adapter_active' => $isActive, + 'toolkit_active' => $toolkitActive, + 'toolkit_adapter_available' => $toolkitAdapterAvailable, + 'message' => $message, + ]; + } + + /** + * Generate a copy-paste config snippet for the requested client. + * + * Every client uses WordPress Application Passwords — built into WP 5.6+, + * no extra plugin needed. Direct HTTP clients (Claude Code, Cursor, + * generic) carry credentials via Basic Auth header; the + * @automattic/mcp-wordpress-remote stdio bridge that Claude Desktop uses + * accepts the username/password directly via WP_API_USERNAME and + * WP_API_PASSWORD env vars and handles encoding itself. + * + * Placeholders used here are stable strings the Vue page substitutes via + * regex when the user fills the credentials inputs. + */ + public function getConfigSnippet(Request $request) + { + $client = sanitize_key((string) $request->get('client', 'claude-code')); + $endpoint = MCPInit::getEndpointUrl(); + // Optional override from the Settings UI checkbox. When the user + // explicitly says "I'm on local dev" we add the TLS-bypass env var to + // Claude Desktop's snippet; when they say "no" we omit it even if + // auto-detection thinks otherwise. + $forceLocalDev = $request->get('local_dev'); + if ($forceLocalDev === 'yes' || $forceLocalDev === '1' || $forceLocalDev === 'true') { + $isLocalDev = true; + } elseif ($forceLocalDev === 'no' || $forceLocalDev === '0' || $forceLocalDev === 'false') { + $isLocalDev = false; + } else { + $isLocalDev = self::detectLocalDevEnvironment(); + } + + // The Vue page replaces these tokens with the user's real values. + // Keep them stable + distinct so the regex stays simple. + $basicPlaceholder = ''; + $usernamePlaceholder = ''; + $passwordPlaceholder = ''; + + $appPasswordsUrl = admin_url('profile.php#application-passwords-section'); + + switch ($client) { + case 'codex': + $snippet = sprintf( + "Settings → Connect to a custom MCP\n\nName: fluent-crm\nTransport: Streamable HTTP ← click this tab first\n\nURL: %s\n\nHeader:\n Key: Authorization\n Value: Basic %s\n\nClick Save.", + $endpoint, + $basicPlaceholder + ); + $instructions = sprintf( + /* translators: %s: link to WP user profile application passwords section */ + __('Open OpenAI Codex → Settings → Connect to a custom MCP. Click the "Streamable HTTP" tab. Generate a WordPress Application Password from %s, then paste username + app password into the inputs above — the Value field will auto-fill with the encoded Basic auth string.', 'fluent-crm'), + $appPasswordsUrl + ); + break; + + case 'cursor': + $snippet = wp_json_encode([ + 'mcpServers' => [ + 'fluent-crm' => [ + 'url' => $endpoint, + 'type' => 'http', + 'headers' => [ + 'Authorization' => 'Basic ' . $basicPlaceholder, + ], + ], + ], + ], JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES); + $instructions = __('Cursor speaks HTTP MCP natively. Fill in your username and application password above and the snippet will be ready to paste into Cursor → Settings → MCP. Restart Cursor afterwards.', 'fluent-crm'); + break; + + case 'generic': + $snippet = sprintf( + "URL: %s\nAuth: Authorization: Basic %s\n\n# Quick test (curl handles the base64 for you)\ncurl -s -u '%s:%s' \\\n -X POST %s \\\n -H 'Content-Type: application/json' \\\n -d '{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"tools/list\"}'", + $endpoint, + $basicPlaceholder, + $usernamePlaceholder, + $passwordPlaceholder, + $endpoint + ); + $instructions = __('Use the URL + Basic Auth header with any HTTP MCP client. The endpoint speaks the standard MCP protocol — initialize, tools/list, tools/call — over JSON-RPC.', 'fluent-crm'); + break; + + case 'claude-desktop': + // Claude Desktop cannot speak HTTP MCP directly yet — it + // routes through @automattic/mcp-wordpress-remote, which + // accepts WP_API_USERNAME / WP_API_PASSWORD plain (proxy + // does the encoding). No JWT plugin needed. + $env = [ + 'WP_API_URL' => $endpoint, + 'WP_API_USERNAME' => $usernamePlaceholder, + 'WP_API_PASSWORD' => $passwordPlaceholder, + 'OAUTH_ENABLED' => 'false', + ]; + + if ($isLocalDev) { + // Self-signed certs trip Node's bundled CA store. Trust + // the connection wholesale for local dev — the proxy + // only ever talks to one URL the user explicitly chose, + // so the practical risk is bounded. + $env['NODE_TLS_REJECT_UNAUTHORIZED'] = '0'; + } + + $snippet = wp_json_encode([ + 'mcpServers' => [ + 'fluent-crm' => [ + 'command' => 'npx', + 'args' => ['-y', '@automattic/mcp-wordpress-remote@latest'], + 'env' => $env, + ], + ], + ], JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES); + + $localDevNote = $isLocalDev + ? ' ' . __('Local dev mode is on, so NODE_TLS_REJECT_UNAUTHORIZED is included — the npx proxy needs it to talk to self-signed Valet/MAMP/Local SSL.', 'fluent-crm') + : ''; + $instructions = __('Paste into ~/Library/Application Support/Claude/claude_desktop_config.json (macOS) or %APPDATA%\\Claude\\claude_desktop_config.json (Windows), then restart Claude Desktop.', 'fluent-crm') . $localDevNote; + break; + + case 'claude-code': + default: + $snippet = sprintf( + "claude mcp add \\\n --transport http \\\n fluent-crm %s \\\n --header \"Authorization: Basic %s\"", + $endpoint, + $basicPlaceholder + ); + $instructions = __('Fill in your username and application password above, then paste the command into your terminal. Run `claude` and the FluentCRM tools will appear under MCP servers.', 'fluent-crm'); + $client = 'claude-code'; + break; + } + + return [ + 'client' => $client, + 'snippet' => $snippet, + 'instructions' => $instructions, + 'endpoint' => $endpoint, + 'app_passwords_url' => $appPasswordsUrl, + 'is_local_dev' => $isLocalDev, + ]; + } + + /** + * Heuristic check for "we're running on a local development install." + * + * Tested in order: + * 1. Hostname ends in a dev TLD (.test, .lab, .local, .localhost) + * 2. Hostname is literally `localhost` + * 3. Host resolves to a private/loopback IP range + * + * Filterable via `fluent_crm/mcp_is_local_dev` so operators can override + * detection on edge cases (a public-facing site on `.local`, an internal + * tool that needs the dev-mode behavior anyway, etc.). + * + * @return bool + */ + private static function detectLocalDevEnvironment() + { + $host = wp_parse_url(home_url(), PHP_URL_HOST); + $host = strtolower((string) $host); + + $isDev = false; + + $devTlds = ['.test', '.lab', '.local', '.localhost', '.docker', '.dev']; + foreach ($devTlds as $tld) { + $len = strlen($tld); + if ($len > 0 && substr($host, -$len) === $tld) { + $isDev = true; + break; + } + } + + if (!$isDev && ($host === 'localhost' || $host === '127.0.0.1' || $host === '::1')) { + $isDev = true; + } + + if (!$isDev && filter_var($host, FILTER_VALIDATE_IP)) { + // Private IP ranges per RFC 1918. + $isPrivate = !filter_var($host, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE); + if ($isPrivate) { + $isDev = true; + } + } + + /** + * Override the local-dev detection. Useful when the heuristic gets + * it wrong (e.g. a public site on a `.local` mDNS hostname). + * + * @since 2.10.0 + * + * @param bool $isDev Whether the install looks like local dev. + * @param string $host The detected hostname. + */ + return (bool) apply_filters('fluent_crm/mcp_is_local_dev', $isDev, $host); + } + + // --------------------------------------------------------------------- + // helpers + // --------------------------------------------------------------------- + + private function isAdapterPresent() + { + return $this->isPluginPresent(self::ADAPTER_PLUGIN_FILE); + } + + private function isToolkitPresent() + { + return $this->isToolkitLoaded() || $this->isPluginPresent(self::TOOLKIT_PLUGIN_FILE); + } + + private function detectAdapterVersion() + { + return $this->detectPluginVersion(self::ADAPTER_PLUGIN_FILE); + } + + private function detectToolkitVersion() + { + if ($this->isToolkitLoaded()) { + return (string) FLUENT_TOOLKIT_VERSION; + } + + return $this->detectPluginVersion(self::TOOLKIT_PLUGIN_FILE); + } + + private function isToolkitLoaded() + { + return defined('FLUENT_TOOLKIT_VERSION'); + } + + private function isPluginPresent($pluginFile) + { + if (!function_exists('get_plugins')) { + require_once ABSPATH . 'wp-admin/includes/plugin.php'; + } + $plugins = get_plugins(); + return isset($plugins[$pluginFile]); + } + + private function detectPluginVersion($pluginFile) + { + if (!function_exists('get_plugins')) { + require_once ABSPATH . 'wp-admin/includes/plugin.php'; + } + $plugins = get_plugins(); + if (!isset($plugins[$pluginFile])) { + return null; + } + return $plugins[$pluginFile]['Version'] ?? null; + } + + private function isToolkitAdapterAvailable() + { + if (!$this->isToolkitLoaded()) { + return false; + } + + if (class_exists('\FluentToolkit\Mcp\AdapterBootstrap') && method_exists('\FluentToolkit\Mcp\AdapterBootstrap', 'available')) { + return (bool) \FluentToolkit\Mcp\AdapterBootstrap::available(); + } + + return $this->isAdapterRuntimeAvailable(); + } + + private function isAdapterRuntimeAvailable() + { + return defined('WP_MCP_VERSION') + && class_exists('\WP\MCP\Core\McpAdapter') + && function_exists('wp_register_ability'); + } + + private function countAbilities() + { + $count = count(AbilitiesRegistrar::getDefinitions()); + + // Pro tools are pushed onto the names list via the + // `fluent_crm/mcp_ability_names` filter in MCPInit. + $names = apply_filters('fluent_crm/mcp_ability_names', array_keys(AbilitiesRegistrar::getDefinitions())); + if (is_array($names)) { + $count = count(array_unique($names)); + } + return $count; + } +} diff --git a/wp-content/plugins/fluent-crm/app/Http/Controllers/MigratorController.php b/wp-content/plugins/fluent-crm/app/Http/Controllers/MigratorController.php new file mode 100644 index 0000000..4be5825 --- /dev/null +++ b/wp-content/plugins/fluent-crm/app/Http/Controllers/MigratorController.php @@ -0,0 +1,208 @@ + $this->getMigrators() + ]; + } + + public function verifyCredential(Request $request) + { + $driver = $request->get('driver'); + + $driverClassName = $this->getDriverClass($driver); + + if (!$driverClassName) { + return $this->sendError([ + 'message' => __('Sorry no driver found for the selected CRM', 'fluent-crm') + ]); + } + + $credential = $request->get('credential', []); + + $driverClass = new $driverClassName; + + $result = $driverClass->verifyCredentials($credential); + + if (is_wp_error($result)) { + return $this->sendError([ + 'message' => $result->get_error_message(), + ], 422); + } + + return [ + 'message' => __('Your provided API key is valid', 'fluent-crm') + ]; + } + + public function getListTagMappings(Request $request) + { + $driver = $request->get('driver'); + + $driverClassName = $this->getDriverClass($driver); + + if (!$driverClassName) { + return $this->sendError([ + 'message' => __('Sorry no driver found for the selected CRM', 'fluent-crm') + ]); + } + + $credential = $request->get('credential', []); + + $result = (new $driverClassName)->getListTagMappings($request->all()); + if (is_wp_error($result)) { + return $this->sendError([ + 'message' => $result->get_error_message(), + ], 422); + } + + return [ + 'options' => $result + ]; + } + + + public function getImportSummary(Request $request) + { + $driver = $request->get('driver'); + $driverClassName = $this->getDriverClass($driver); + + if (!$driverClassName) { + return $this->sendError([ + 'message' => __('Sorry no driver found for the selected CRM', 'fluent-crm') + ]); + } + + $credential = $request->get('credential', []); + $mapSettings = $request->get('map_settings', []); + + + $summary = (new $driverClassName)->getSummary($request->all()); + + if (is_wp_error($summary)) { + return $this->sendError([ + 'message' => $summary->get_error_message(), + ], 422); + } + + return [ + 'import_summary' => $summary + ]; + } + + public function handleImport(Request $request) + { + if (!defined('FLUENTCRM_DOING_BULK_IMPORT')) { + define('FLUENTCRM_DOING_BULK_IMPORT', true); + } + + $driver = $request->get('driver'); + $driverClassName = $this->getDriverClass($driver); + + if (!$driverClassName) { + return $this->sendError([ + 'message' => __('Sorry no driver found for the selected CRM', 'fluent-crm') + ]); + } + + $summary = (new $driverClassName)->runImport($request->all()); + + if (is_wp_error($summary)) { + return $this->sendError([ + 'message' => $summary->get_error_message(), + ], 422); + } + + return [ + 'import_info' => $summary + ]; + } + + private function getDriverClass($driver) + { + if ($driver == 'mailchimp') { + return MailChimpMigrator::class; + } else if ($driver == 'ConvertKit') { + return ConvertKitMigrator::class; + } else if ($driver == 'MailerLite') { + return MailerLiteMigrator::class; + } else if ($driver == 'Drip') { + return DripMigrator::class; + } else if ($driver == 'ActiveCampaign') { + return ActiveCampaignMigrator::class; + } + + /** + * Filter the migrator driver class. + * + * This filter allows you to modify the migrator driver class. + * + * @since 2.7.0 + * + * @param mixed $class The current migrator driver class. Default null. + * @param string $driver The driver name. + */ + return apply_filters('fluent_crm/migrator_driver_class', null, $driver); + } + + private function getMigrators() + { + /** + * Filter the list of available SaaS migrators. + * + * This filter allows modification of the list of available SaaS migrators + * by adding, removing, or modifying the migrators. + * + * @since 2.7.0 + * + * @param array $migrators { + * An associative array of migrators. + * + * @type array $mailchimp { + * Information about the MailChimp migrator. + * } + * @type array $ConvertKit { + * Information about the ConvertKit migrator. + * } + * @type array $MailerLite { + * Information about the MailerLite migrator. + * } + * @type array $Drip { + * Information about the Drip migrator. + * } + * @type array $ActiveCampaign { + * Information about the ActiveCampaign migrator. + * } + * } + */ + return apply_filters('fluent_crm/saas_migrators', [ + 'mailchimp' => (new MailChimpMigrator())->getInfo(), + 'ConvertKit' => (new ConvertKitMigrator())->getInfo(), + 'MailerLite' => (new MailerLiteMigrator())->getInfo(), + 'Drip' => (new DripMigrator())->getInfo(), + 'ActiveCampaign' => (new ActiveCampaignMigrator())->getInfo() + ]); + } + +} diff --git a/wp-content/plugins/fluent-crm/app/Http/Controllers/OptionsController.php b/wp-content/plugins/fluent-crm/app/Http/Controllers/OptionsController.php new file mode 100644 index 0000000..b5bd538 --- /dev/null +++ b/wp-content/plugins/fluent-crm/app/Http/Controllers/OptionsController.php @@ -0,0 +1,1043 @@ +request->get('fields')) { + $options = array_unique(explode(',', $fileds)); + + $response = []; + + foreach ($options as $method) { + // Only invoke this controller's own zero-argument option providers. + // Gating on method_exists() alone let the `fields` param call ANY + // method on the controller: ones that require a Request argument + // (getAjaxOptions/getTaxonomyTerms/getCascadeSelections) fataled with + // an ArgumentCountError, inherited framework helpers ran unintentionally, + // and `index` recursed into itself. Restricting by shape (public, + // non-static, no required args, declared on this class) admits every + // legitimate option getter — current or future — without an explicit + // name list, so no caller can be silently broken. + if ($method === 'index' || !method_exists($this, $method)) { + continue; + } + + $reflection = new \ReflectionMethod($this, $method); + + if ( + !$reflection->isPublic() || + $reflection->isStatic() || + $reflection->getNumberOfRequiredParameters() > 0 || + $reflection->getDeclaringClass()->getName() !== self::class + ) { + continue; + } + + $result = $this->{$method}(); + if (is_array($result)) { + $response = array_merge($response, $result); + } + } + + return [ + 'options' => $response + ]; + } + + throw new \Exception('Missing requested fields field.', 422); + } + + /** + * Include the countries options. + * + * @return array + */ + public function countries() + { + /** + * Determine the list of countries in FluentCRM. + * + * This filter allows you to modify the list of countries used in FluentCRM. + * + * @param array An array of countries. + * @since 2.7.0 + * + */ + $countries = apply_filters('fluent_crm/countries', []); + $formattedCountries = []; + foreach ($countries as $country) { + $country['id'] = $country['code']; + $country['slug'] = $country['code']; + $formattedCountries[] = $country; + } + return [ + 'countries' => $formattedCountries + ]; + } + + /** + * Include all the lists. + * + * @return array + */ + public function lists() + { + $lists = Lists::select(['id', 'slug', 'title'])->orderBy('title', 'ASC')->get(); + + $withCount = (array)$this->request->get('with_count', []); + + if ($withCount && in_array('lists', $withCount, true)) { + $subscribedCounts = $this->getSubscribedCountByListIds($lists->pluck('id')->all()); + + foreach ($lists as $list) { + $listId = (int)$list->id; + $list->subscribersCount = isset($subscribedCounts[$listId]) ? $subscribedCounts[$listId] : 0; + } + } + + return [ + 'lists' => $lists + ]; + } + + private function getSubscribedCountByListIds($listIds = []) + { + $listIds = array_unique(array_filter(array_map('intval', (array)$listIds))); + + if (!$listIds) { + return []; + } + + $countRows = fluentCrmDb()->table('fc_subscriber_pivot') + ->select([ + 'fc_subscriber_pivot.object_id', + fluentCrmDb()->raw('count(*) as total') + ]) + ->join('fc_subscribers', 'fc_subscribers.id', '=', 'fc_subscriber_pivot.subscriber_id') + ->where('fc_subscriber_pivot.object_type', Lists::class) + ->whereIn('fc_subscriber_pivot.object_id', $listIds) + ->where('fc_subscribers.status', 'subscribed') + ->groupBy('fc_subscriber_pivot.object_id') + ->get(); + + $counts = []; + foreach ($countRows as $countRow) { + $counts[(int)$countRow->object_id] = (int)$countRow->total; + } + + return $counts; + } + + /** + * Include all the tags. + * + * @return array + */ + public function tags() + { + $tags = Tag::select(['id', 'slug', 'title'])->orderBy('title', 'ASC')->get(); + foreach ($tags as $tag) { + $tag->value = strval($tag->id); + $tag->label = $tag->title; + } + return [ + 'tags' => $tags + ]; + } + + /** + * Include all the Campaigns. + * + * @return array + */ + public function campaigns() + { + return [ + 'campaigns' => Campaign::select('id', 'title')->orderBy('id', 'DESC')->get() + ]; + } + + /** + * Include all the EmailSequences. + * + * @return array + */ + public function email_sequences() + { + $sequences = []; + + if (defined('FLUENTCAMPAIGN')) { + $sequences = \FluentCampaign\App\Models\Sequence::select('id', 'title')->orderBy('id', 'DESC')->get(); + } + + return [ + 'email_sequences' => $sequences + ]; + } + + /** + * Include all the Automation Funnels. + * + * @return array + */ + public function automation_funnels() + { + $funnels = Funnel::select('id', 'title', 'status') + ->orderBy('id', 'DESC')->get(); + + foreach ($funnels as $funnel) { + $funnel->title .= ' (' . $funnel->status . ')'; + } + + return [ + 'automation_funnels' => $funnels + ]; + } + + /** + * Include all the Companies. + * + * @return array + */ + public function companies() + { + return [ + 'companies' => Company::select('id', 'name as title')->orderBy('id', 'DESC')->get() + ]; + } + + /** + * Include subscriber statuses. + * + * @return array + */ + public function statuses() + { + return [ + 'statuses' => fluentcrm_subscriber_statuses(true) + ]; + } + + /** + * Include subscribers' sms statuses. + * + * @return array + */ + public function sms_statuses() + { + /** + * sms statuses are static data and no db call is happening here + * also available in fcAdmin data in frontend + * + */ + return [ + 'sms_statuses' => fluentcrm_subscriber_sms_statuses(true) + ]; + } + + /** + * Include subscriber editable statuses. + * + * @return array + */ + public function editable_statuses() + { + return [ + 'editable_statuses' => fluentcrm_subscriber_editable_statuses(true) + ]; + } + + /** + * Include subscriber Contact Types. + * + * @return array + */ + public function contact_types() + { + return [ + 'contact_types' => fluentcrm_contact_types(true) + ]; + } + + /** + * Include the sample csv url. + * + * @return array + */ + public function sampleCsv() + { + return [ + 'sampleCsv' => $this->app['url.assets'] . 'sample.csv' + ]; + } + + public function segments() + { + /** + * Determine the dynamic segments in FluentCRM. + * + * This filter allows you to modify the dynamic segments used in FluentCRM. + * + * @param array An array of dynamic segments. + * @since 1.0.0 + * + */ + $segments = apply_filters('fluentcrm_dynamic_segments', []); + + return [ + 'segments' => $segments + ]; + } + + public function roles() + { + if (!function_exists('get_editable_roles')) { + require_once(ABSPATH . '/wp-admin/includes/user.php'); + } + + return [ + 'roles' => \get_editable_roles() + ]; + } + + public function user_roles_options() + { + $roles = $this->roles(); + + $formattedRoles = []; + foreach ($roles['roles'] as $role => $roleData) { + $formattedRoles[] = [ + 'id' => $role, + 'title' => $roleData['name'], + 'slug' => $role + ]; + } + + return [ + 'user_roles_options' => $formattedRoles + ]; + } + + public function profile_sections() + { + return [ + 'profile_sections' => Helper::getProfileSections() + ]; + } + + public function custom_fields() + { + return [ + 'custom_fields' => fluentcrm_get_option('contact_custom_fields', []) + ]; + } + + public function getAjaxOptions(Request $request) + { + $optionKey = $request->getSafe('option_key'); + $search = $request->getSafe('search'); + $includedIds = $request->getSafe('values'); + + $options = []; + + if ($optionKey == 'woo_categories') { + // woocommerce categories + if (defined('WC_PLUGIN_FILE')) { + $cat_args = array( + 'taxonomy' => 'product_cat', + 'orderby' => 'name', + 'order' => 'ASC', + 'hide_empty' => false, + 'search' => $search, + 'number' => 50 + ); + $product_categories = get_terms($cat_args); + + $pushedIds = []; + foreach ($product_categories as $category) { + $options[] = [ + 'id' => $category->term_id, + 'title' => $category->name + ]; + $pushedIds[] = $category->term_id; + } + + if (empty($includedIds)) { + $includedIds = $pushedIds; + } + $includedIds = array_diff($includedIds, $pushedIds); + + if ($includedIds) { + $cat_args = array( + 'taxonomy' => 'product_cat', + 'orderby' => 'name', + 'order' => 'ASC', + 'hide_empty' => false, + 'include' => $includedIds + ); + $product_categories = get_terms($cat_args); + foreach ($product_categories as $category) { + $options[] = [ + 'id' => $category->term_id, + 'title' => $category->name + ]; + } + } + } + + return [ + 'options' => $options + ]; + } + + $wooProductKeys = ['woo_products', 'product_selector_woo', 'product_selector_woo_order']; + + if (in_array($optionKey, $wooProductKeys)) { + if (defined('WC_PLUGIN_FILE')) { + + $args = [ + 'limit' => 50, + 'orderby' => 'date', + 'order' => 'DESC', + 's' => $search + ]; + + $pushedIds = []; + + $subOptionKey = $request->getSafe('sub_option_key', 'sanitize_text_field', []); + if (!empty($subOptionKey)) { + $args['type'] = $subOptionKey; + } + + $products = wc_get_products($args); + + foreach ($products as $product) { + $productId = $product->get_id(); + $options[] = [ + 'id' => $productId, + 'title' => $product->get_name() + ]; + $pushedIds[] = $productId; + } + + if (empty($includedIds)) { + $includedIds = $pushedIds; + } else { + $includedIds = (array)$includedIds; + } + + $includedIds = array_diff($includedIds, $pushedIds); + + if ($includedIds) { + $products = wc_get_products([ + 'orderby' => 'date', + 'order' => 'DESC', + 'include' => $includedIds + ]); + foreach ($products as $product) { + $productId = $product->get_id(); + $options[] = [ + 'id' => $productId, + 'title' => $product->get_name() + ]; + } + } + } + + return [ + 'options' => $options + ]; + + } + + if ($optionKey == 'edd_products' || $optionKey == 'product_selector_edd') { + if (Helper::isEdd3() && defined('FLUENTCAMPAIGN')) { + $options = \FluentCampaign\App\Services\Integrations\Edd\Helper::getProducts(); + } + + return [ + 'options' => $options + ]; + + } + + if ($optionKey == 'voxel_products' || $optionKey == 'product_selector_voxel') { + $pushedIds = []; + $args = array( + 'post_type' => 'product', + 'post_status' => 'publish', + 'posts_per_page' => 20 + ); + + if ($search) { + $args['s'] = $search; + } + + $query = new \WP_Query($args); + $products = $query->posts; + + foreach ($products as $product) { + $options[] = [ + 'id' => $product->ID, + 'title' => $product->post_title + ]; + $pushedIds[] = $product->ID; + } + + if ($includedIds) { + $includedIds = array_diff($includedIds, $pushedIds); + if ($includedIds) { + $args = array( + 'post_type' => 'product', + 'post_status' => 'publish', + 'post__in' => $includedIds + ); + $query = new \WP_Query($args); + $products = $query->posts; + + foreach ($products as $product) { + $options[] = [ + 'id' => $product->ID, + 'title' => $product->post_title + ]; + } + } + } + + return [ + 'options' => $options + ]; + } + + if ($optionKey == 'voxel_product_types') { + $formattedTypes = []; + + if (class_exists('\Voxel\Product_Type')) { + $product_types = \Voxel\Product_Type::get_all(); + + foreach ($product_types as $product_type) { + $formattedTypes[] = [ + 'id' => $product_type->get_key(), + 'title' => $product_type->get_label() + ]; + } + } + + return [ + 'options' => $formattedTypes + ]; + } + + if ($optionKey == 'campaigns' || $optionKey == 'funnels' || $optionKey == 'email_sequences') { + + if ($optionKey == 'campaigns') { + $objectModel = Campaign::select(['id', 'title', 'status'])->where('status', '!=', 'draft'); + } else if ($optionKey == 'funnels') { + $objectModel = Funnel::select(['id', 'title', 'status']); + } else if ($optionKey == 'email_sequences') { + if (!defined('FLUENTCAMPAIGN')) { + return [ + 'options' => [] + ]; + } + $objectModel = \FluentCampaign\App\Models\Sequence::select(['id', 'title', 'status']); + } else { + return [ + 'options' => [] + ]; + } + + $items = $objectModel + ->when($search, function ($query) use ($search) { + return $query->where('title', 'LIKE', "%$search%"); + }) + ->limit(20) + ->orderBy('id', 'DESC') + ->get(); + + $pushedIds = []; + + foreach ($items as $item) { + $options[] = [ + 'id' => $item->id, + 'title' => $item->title . ' - ' . $item->id + ]; + $pushedIds[] = $item->id; + } + + if (!$includedIds) { + return [ + 'options' => $options + ]; + } + + $includedIds = (array)$includedIds; + + $includedIds = array_diff($includedIds, $pushedIds); + if ($includedIds) { + + if ($optionKey == 'campaigns') { + $objectModel = Campaign::select(['id', 'title', 'status']); + } else if ($optionKey == 'funnels') { + $objectModel = Funnel::select(['id', 'title', 'status']); + } else if ($optionKey == 'email_sequences') { + $objectModel = \FluentCampaign\App\Models\Sequence::select(['id', 'title', 'status']); + } else { + return [ + 'options' => $options + ]; + } + + $items = $objectModel->whereIn('id', $includedIds)->get(); + foreach ($items as $item) { + $options[] = [ + 'id' => $item->id, + 'title' => $item->title . ' - ' . $item->id + ]; + } + } + + return [ + 'options' => $options + ]; + } + + if ($optionKey == 'companies') { + if (!Helper::isCompanyEnabled()) { + return [ + 'options' => [] + ]; + } + + $companies = Company::select(['id', 'name']) + ->searchBy($search) + ->limit(20) + ->orderBy('id', 'DESC') + ->get(); + + $pushedIds = []; + foreach ($companies as $company) { + $options[] = [ + 'id' => $company->id, + 'title' => $company->name + ]; + $pushedIds[] = $company->id; + } + + if (empty($includedIds)) { + $includedIds = $pushedIds; + } + $includedIds = array_diff($includedIds, $pushedIds); + + if ($includedIds) { + $companies = Company::select(['id', 'name']) + ->whereIn('id', $includedIds) + ->get(); + foreach ($companies as $company) { + $options[] = [ + 'id' => $company->id, + 'title' => $company->name + ]; + } + } + + return [ + 'options' => $options + ]; + } + + if ($optionKey == 'post_type') { + // we need to verify the post type access permission here + if (!current_user_can('edit_posts')) { + return [ + 'options' => [] + ]; + } + + $postType = $request->getSafe('sub_option_key', 'sanitize_text_field'); + if (!$postType) { + return [ + 'options' => [] + ]; + } + + $args = [ + 'post_type' => $postType, + 'posts_per_page' => 20 + ]; + + if ($search) { + $args['s'] = $search; + } + + $posts = get_posts($args); + + $formattedPosts = []; + if (!is_wp_error($posts)) { + foreach ($posts as $post) { + $formattedPosts[$post->ID] = [ + 'id' => strval($post->ID), + 'title' => $post->post_title + ]; + } + } + + if (!$includedIds) { + return [ + 'options' => array_values($formattedPosts) + ]; + } + + $includedIds = (array)$includedIds; + + $includedIds = array_diff($includedIds, array_keys($formattedPosts)); + if ($includedIds) { + $posts = get_posts([ + 'post_type' => $postType, + 'post__in' => $includedIds + ]); + foreach ($posts as $post) { + $formattedPosts[$post->ID] = [ + 'id' => strval($post->ID), + 'title' => $post->post_title + ]; + } + } + + return [ + 'options' => array_values($formattedPosts) + ]; + } + + if ($optionKey == 'company_industries') { + $companyCategories = Helper::companyCategories(); + + $formattedCategories = []; + foreach ($companyCategories as $category) { + $formattedCategories[] = [ + 'id' => $category, + 'title' => $category + ]; + } + + return [ + 'options' => $formattedCategories + ]; + } + + if ($optionKey == 'company_types') { + $companyTypes = Helper::companyTypes(); + + $formattedTypes = []; + foreach ($companyTypes as $type) { + $formattedTypes[] = [ + 'id' => $type, + 'title' => $type + ]; + } + + return [ + 'options' => $formattedTypes + ]; + } + + if ($optionKey == 'users') { + + if (!current_user_can('list_users')) { + return [ + 'options' => [] + ]; + } + + $users = Helper::searchWPUsers($search); + + $usersWithLessFields = []; + + foreach ($users as $user) { + $usersWithLessFields[] = [ + 'id' => $user->ID, + 'user_email' => $user->user_email, + 'name' => $user->display_name ?? $user->user_email, + 'title' => $user->display_name . ' (' . $user->user_email . ')' + ]; + } + + return [ + 'options' => $usersWithLessFields + ]; + } + + + return [ + /** + * Determine the AJAX options for FluentCRM. + * + * This filter allows modification of the AJAX options based on the provided option key, search term, and included IDs. + * + * @param array The options array to be filtered. + * @param string $search The search term used to filter the options. + * @param array $includedIds The IDs to be included in the options. + * @since 2.5.9 + * + */ + 'options' => apply_filters('fluentcrm_ajax_options_' . $optionKey, [], $search, $includedIds) + ]; + } + + public function getTaxonomyTerms(Request $request) + { + $taxonomy = $request->get('taxonomy'); + $search = $request->get('search'); + $includeIds = (array)$request->get('values', []); + + $args = [ + 'taxonomy' => $taxonomy, + 'hide_empty' => false, + 'number' => 20 + ]; + + if ($search) { + $args['search'] = $search; + } + + $terms = get_terms($args); + + $formattedTerms = []; + if (!is_wp_error($terms)) { + foreach ($terms as $term) { + $formattedTerms[$term->term_id] = [ + 'id' => strval($term->term_id), + 'title' => $term->name + ]; + } + } + + if ($includeIds && $formattedTerms) { + $includeIds = array_diff($includeIds, array_keys($formattedTerms)); + if ($includeIds) { + $includedTerms = get_terms([ + 'taxonomy' => $taxonomy, + 'hide_empty' => false, + 'include' => $includeIds + ]); + + if (!is_wp_error($includedTerms)) { + foreach ($includedTerms as $includedTerm) { + $formattedTerms[$includedTerm->term_id] = [ + 'id' => strval($includedTerm->term_id), + 'title' => $includedTerm->name + ]; + } + } + } + } + + return [ + 'options' => array_values($formattedTerms) + ]; + + } + + public function getCascadeSelections(Request $request) + { + $provider = $request->get('provider'); + + /** + * Determine the cascade selection options for a given provider. + * + * The dynamic portion of the hook name, `$provider`, refers to the specific provider for which the options are being filtered. + * + * @param array { + * An array of options for the cascade selection. + * + * @type array $options The options for the selection. + * @type bool $has_more Whether there are more options available. + * } + * @param array $request The request data. + * @since 2.9.23 + * + */ + return apply_filters('fluent_crm/cascade_selection_options_' . $provider, [ + 'options' => [], + 'has_more' => true + ], $request->all()); + } + + + /** + * Search contacts, email campaigns (by title), automations (by title), and companies (by name). + * Scope limits which types are queried for faster results. + * + * GET global-search?search=...&scope=all|subscribers|campaigns|funnels|companies + */ + public function search() + { + $search = trim(sanitize_text_field($this->request->get('search', ''))); + $scope = trim(sanitize_text_field($this->request->get('scope', 'all'))); + $validScopes = ['all', 'subscribers', 'campaigns', 'funnels', 'companies', 'subscriber_notes']; + if (!in_array($scope, $validScopes, true)) { + $scope = 'all'; + } + + $limit = (int)apply_filters('fluent_crm/global_search_result_limit', 100); + if ($limit <= 0) { + $limit = 100; + } + global $wpdb; + $searchLike = $search ? '%' . $wpdb->esc_like($search) . '%' : '%'; + + if (empty($search)) { + return $this->sendSuccess([ + 'subscribers' => [], + 'campaigns' => [], + 'funnels' => [] + ]); + } + + $canReadContacts = PermissionManager::currentUserCan('fcrm_read_contacts'); + $canReadEmails = PermissionManager::currentUserCan('fcrm_read_emails'); + $canReadFunnels = PermissionManager::currentUserCan('fcrm_read_funnels'); + $canReadCompanies = Helper::isCompanyEnabled() && PermissionManager::currentUserCan('fcrm_manage_contact_cats'); + + $subscribers = []; + $campaigns = []; + $funnels = []; + $companies = []; + $subscriberNotes = []; + + if ($search !== '') { + $querySubscribers = ($scope === 'all' || $scope === 'subscribers') && $canReadContacts; + $queryCampaigns = ($scope === 'all' || $scope === 'campaigns') && $canReadEmails; + $queryFunnels = ($scope === 'all' || $scope === 'funnels') && $canReadFunnels; + $queryCompanies = ($scope === 'all' || $scope === 'companies') && $canReadCompanies; + $queryNotes = $scope === 'subscriber_notes' && $canReadContacts; + + if ($querySubscribers) { + $queryArgs = [ + 'with' => [], + 'filter_type' => 'simple', + 'search' => $search, + 'sort_by' => 'id', + 'sort_type' => 'DESC', + 'custom_fields' => false, + 'limit' => $limit, + ]; + $subscribersResult = (new ContactsQuery($queryArgs))->get(); + $collection = is_array($subscribersResult) ? collect($subscribersResult) : $subscribersResult; + $subscribers = $collection->map(function ($s) { + return [ + 'id' => $s->id, + 'email' => $s->email, + 'first_name' => $s->first_name ?? '', + 'last_name' => $s->last_name ?? '', + 'full_name' => $s->full_name, + 'photo' => $s->photo, + ]; + })->values()->all(); + } + + if ($queryCampaigns) { + $campaigns = Campaign::select('id', 'title', 'status') + ->where('title', 'LIKE', $searchLike) + ->orderBy('id', 'DESC') + ->take($limit) + ->get(); + } + + if ($queryFunnels) { + $funnels = Funnel::select('id', 'title', 'status') + ->where('title', 'LIKE', $searchLike) + ->orderBy('id', 'DESC') + ->take($limit) + ->get(); + } + + if ($queryCompanies) { + $companies = Company::select('id', 'name', 'logo') + ->where('name', 'LIKE', $searchLike) + ->orderBy('id', 'DESC') + ->take($limit) + ->get() + ->map(function ($c) { + return [ + 'id' => $c->id, + 'name' => $c->name, + 'logo' => $c->logo ?? '', + ]; + }) + ->values() + ->all(); + } + + if ($queryNotes) { + $subscriberNotes = SubscriberNote::with(['subscriber' => function ($q) { + $q->select('id', 'email', 'first_name', 'last_name'); + }]) + ->where(function ($q) use ($searchLike) { + $q->where('title', 'LIKE', $searchLike) + ->orWhere('description', 'LIKE', $searchLike); + }) + ->orderBy('id', 'DESC') + ->take($limit) + ->get() + ->map(function ($note) { + $subscriber = $note->subscriber; + return [ + 'id' => $note->id, + 'subscriber_id' => $note->subscriber_id, + 'title' => $note->title, + 'created_at' => $note->created_at, + 'subscriber_name' => $subscriber ? $subscriber->full_name : '', + 'subscriber_email' => $subscriber ? $subscriber->email : '', + ]; + }) + ->values() + ->all(); + } + } + + $response = [ + 'subscribers' => $subscribers, + 'campaigns' => $campaigns, + 'funnels' => $funnels, + ]; + if (Helper::isCompanyEnabled()) { + $response['companies'] = $companies; + } + + if ($scope === 'subscriber_notes') { + $response['subscriber_notes'] = $subscriberNotes; + } + + return $this->sendSuccess($response); + } +} diff --git a/wp-content/plugins/fluent-crm/app/Http/Controllers/PurchaseHistoryController.php b/wp-content/plugins/fluent-crm/app/Http/Controllers/PurchaseHistoryController.php new file mode 100644 index 0000000..27c689c --- /dev/null +++ b/wp-content/plugins/fluent-crm/app/Http/Controllers/PurchaseHistoryController.php @@ -0,0 +1,57 @@ +sendSuccess([ + 'providers' => Helper::getPurchaseHistoryProviders() + ]); + } + + public function getOrders() + { + $provider = $this->request->getSafe('provider'); + $subscriberId = $this->request->getSafe('id', 'intval'); + $subscriber = Subscriber::findOrFail($subscriberId); + + /** + * Determine the purchase history data for a specific provider in FluentCRM. + * + * The dynamic portion of the hook name, `$provider`, refers to the purchase history provider. + * + * @since 1.0.0 + * + * @param array { + * The purchase history data. + * + * @type array $orders List of orders. + * @type int $total Total number of orders. + * } + * @param object $subscriber The subscriber object. + */ + $data = apply_filters('fluent_crm/purchase_history_'.$provider, [ + 'orders' => [], + 'total' => 0 + ], $subscriber); + + return $this->sendSuccess([ + 'orders' => $data + ]); + } + +} diff --git a/wp-content/plugins/fluent-crm/app/Http/Controllers/ReportingController.php b/wp-content/plugins/fluent-crm/app/Http/Controllers/ReportingController.php new file mode 100644 index 0000000..67bf78a --- /dev/null +++ b/wp-content/plugins/fluent-crm/app/Http/Controllers/ReportingController.php @@ -0,0 +1,597 @@ +get('date_range') ?: ['', '']; + $tagId = intval($request->get('tag_id', 0)); + $listId = intval($request->get('list_id', 0)); + $compareType = sanitize_text_field($request->get('compare_type', '')); + $compareRange = $request->get('compare_range', []); + + $currentStats = $reporting->getSubscribersGrowth($from, $to, $tagId, $listId); + + $currentFrom = $from ?: gmdate('Y-m-d', strtotime('-30 days')); + $currentTo = $to ?: gmdate('Y-m-d', strtotime('+1 day')); + + $dataSets = [ + [ + 'label' => __('Current Range', 'fluent-crm'), + 'data' => $currentStats, + 'range' => [$currentFrom, $currentTo], + 'backgroundColor' => '#335CFF', + 'borderColor' => '#335CFF', + 'fill' => true, + ], + ]; + + if ($compareType && $compareType !== 'no_comparison') { + $compRange = $this->resolveCompareRange($compareType, $compareRange, $currentFrom, $currentTo); + if ($compRange) { + $compareStats = $reporting->getSubscribersGrowth($compRange[0], $compRange[1], $tagId, $listId); + $dataSets[] = [ + 'label' => __('Compare Range', 'fluent-crm'), + 'data' => $compareStats, + 'range' => $compRange, + 'backgroundColor' => '#1FC16B', + 'borderColor' => '#1FC16B', + 'fill' => true, + ]; + } + } + + return $this->sendSuccess([ + 'data_sets' => $dataSets, + 'current_range' => [$currentFrom, $currentTo], + ]); + } + + /** + * Calculate comparison date range based on type. + * + * @param string $type + * @param array $compareRange + * @param string $from + * @param string $to + * @return array|false + */ + private function resolveCompareRange($type, $compareRange, $from, $to) + { + $fromTs = strtotime($from); + $toTs = strtotime($to); + $diffDays = (int)(($toTs - $fromTs) / 86400); + + switch ($type) { + case 'previous_period': + return [ + gmdate('Y-m-d', $fromTs - ($diffDays + 1) * 86400), + gmdate('Y-m-d', $fromTs - 86400), + ]; + case 'previous_month': + $newFrom = gmdate('Y-m-d', strtotime($from . ' -1 month')); + return [$newFrom, gmdate('Y-m-d', strtotime($newFrom) + $diffDays * 86400)]; + case 'previous_quarter': + $newFrom = gmdate('Y-m-d', strtotime($from . ' -3 months')); + return [$newFrom, gmdate('Y-m-d', strtotime($newFrom) + $diffDays * 86400)]; + case 'previous_year': + $newFrom = gmdate('Y-m-d', strtotime($from . ' -12 months')); + return [$newFrom, gmdate('Y-m-d', strtotime($newFrom) + $diffDays * 86400)]; + case 'custom': + if (is_array($compareRange) && count(array_filter($compareRange)) >= 2) { + return [ + sanitize_text_field($compareRange[0]), + sanitize_text_field($compareRange[1]), + ]; + } + return false; + default: + return false; + } + } + + public function getEmailSentStats(Request $request, Reporting $reporting) + { + list($from, $to) = $request->get('date_range') ?: ['', '']; + $campaignId = intval($request->get('campaign_id', 0)); + return $this->sendSuccess([ + 'stats' => $reporting->getEmailStats($from, $to, 'sent', $campaignId) + ]); + } + + public function getEmailOpenStats(Request $request, Reporting $reporting) + { + list($from, $to) = $request->get('date_range') ?: ['', '']; + $campaignId = intval($request->get('campaign_id', 0)); + return $this->sendSuccess([ + 'stats' => $reporting->getEmailOpenStats($from, $to, $campaignId) + ]); + } + + public function getEmailClickStats(Request $request, Reporting $reporting) + { + list($from, $to) = $request->get('date_range') ?: ['', '']; + $campaignId = intval($request->get('campaign_id', 0)); + return $this->sendSuccess([ + 'stats' => $reporting->getEmailClickStats($from, $to, $campaignId) + ]); + } + + public function getEmailUnsubStats(Request $request, Reporting $reporting) + { + list($from, $to) = $request->get('date_range') ?: ['', '']; + return $this->sendSuccess([ + 'stats' => $reporting->getUnsubscribeStats($from, $to) + ]); + } + + public function getEmailPerformance(Request $request, Reporting $reporting) + { + $dateRange = Arr::get($request->all(), 'date_range', []); + + if (!empty($dateRange[0]) && !empty($dateRange[1])) { + $from = sanitize_text_field($dateRange[0]); + $to = sanitize_text_field($dateRange[1]); + } else { + $days = intval(Arr::get($request->all(), 'days')); + + if ($days > 0) { + $from = '-' . $days . ' days'; + } elseif ($request->exists('days')) { + // days=0 means "All Time" + $from = '2000-01-01'; + } else { + $from = null; // default: -30 days + } + + $to = null; + } + + return $this->sendSuccess([ + 'stats' => $reporting->getEmailPerformance($from, $to) + ]); + } + + public function getEmails(Request $request) + { + $status = sanitize_text_field($request->get('status', '')); + $search = sanitize_text_field($request->get('search', '')); + $selectedTypes = array_values(array_filter(array_map('sanitize_text_field', (array)$request->get('types', [])))); + $types = CampaignEmail::expandEmailTypes($selectedTypes); + + $emails = CampaignEmail::orderBy('scheduled_at', 'DESC') + ->with('subscriber', 'campaign') + ->when($search, function ($q) use ($search) { + return $this->applyEmailSearchFilter($q, $search); + }) + ->when($status, function ($q) use ($status) { + return $q->where('status', $status); + }) + ->when($types, function ($q) use ($types) { + return $q->whereIn('email_type', $types); + }) + ->paginate(); + + $statuses = null; + $emailTypes = null; + + if ($request->get('page') == 1 && !$search) { + $statuses = CampaignEmail::select('status') + ->selectRaw('count(id) as total') + ->when($types, function ($q) use ($types) { + return $q->whereIn('email_type', $types); + }) + ->groupBy('status') + ->get() + ->keyBy('status') + ->map(function ($status) { + return $status->total; + }); + + $typeCounts = CampaignEmail::select('email_type') + ->selectRaw('count(id) as total') + ->whereNotNull('email_type') + ->when($status, function ($q) use ($status) { + return $q->where('status', $status); + }) + ->groupBy('email_type') + ->get() + ->reduce(function ($carry, $emailType) { + $canonicalType = CampaignEmail::normalizeEmailType($emailType->email_type); + + if (!isset($carry[$canonicalType])) { + $carry[$canonicalType] = [ + 'id' => $canonicalType, + 'label' => CampaignEmail::resolveEmailTypeLabel($canonicalType), + 'count' => 0, + ]; + } + + $carry[$canonicalType]['count'] += (int)$emailType->total; + + return $carry; + }, []); + + $orderedTypes = []; + foreach (array_keys(CampaignEmail::getEmailTypeLabels()) as $canonicalType) { + if (!empty($typeCounts[$canonicalType])) { + $orderedTypes[] = $typeCounts[$canonicalType]; + } + } + + $emailTypes = array_values($orderedTypes); + } + + return [ + 'emails' => $emails, + 'statuses' => $statuses, + 'types' => $emailTypes + ]; + } + + /** + * Apply the search filter to email activity queries. + * + * Search is scoped to fields the table actually exposes so users can find + * rows by subject, source campaign title, recipient email, or related + * contact email without triggering extra broad scans on large datasets. + * + * @param \FluentCrm\Framework\Database\Orm\Builder $query + * @param string $search + * @return \FluentCrm\Framework\Database\Orm\Builder + */ + private function applyEmailSearchFilter($query, $search) + { + global $wpdb; + + $escapedSearch = $wpdb->esc_like($search); + $containsLike = '%' . $escapedSearch . '%'; + $emailLike = strpos($search, '@') !== false ? $escapedSearch . '%' : $containsLike; + + return $query->where(function ($subQuery) use ($containsLike, $emailLike) { + $subQuery->where('email_subject', 'LIKE', $containsLike) + ->orWhere('email_address', 'LIKE', $emailLike) + ->orWhereHas('campaign', function ($campaignQuery) use ($containsLike) { + $campaignQuery->where('title', 'LIKE', $containsLike); + }) + ->orWhereHas('subscriber', function ($subscriberQuery) use ($emailLike) { + $subscriberQuery->where('email', 'LIKE', $emailLike); + }); + }); + } + + public function deleteEmails(Request $request) + { + $emailIds = $request->get('email_ids'); + CampaignEmail::whereIn('id', $emailIds) + ->delete(); + + return [ + 'message' => __('Selected emails have been deleted', 'fluent-crm') + ]; + } + + public function getContactsByStatus() + { + $statuses = fluentCrmDb()->table('fc_subscribers') + ->select(fluentCrmDb()->raw('status, COUNT(id) as count')) + ->groupBy('status') + ->get(); + + $defaultOrder = [ + 'subscribed', + 'unsubscribed', + 'pending', + 'bounced', + 'complained', + 'spammed', + 'transactional' + ]; + $defaultStats = array_fill_keys($defaultOrder, 0); + $total = 0; + + foreach ($statuses as $row) { + $count = (int)$row->count; + $status = sanitize_text_field($row->status); + $total += $count; + + if (array_key_exists($status, $defaultStats)) { + $defaultStats[$status] = $count; + } + } + + $result = []; + foreach ($defaultOrder as $status) { + $result[] = [ + 'status' => $status, + 'count' => $defaultStats[$status], + ]; + } + + return $this->sendSuccess([ + 'stats' => $result, + 'total' => $total, + ]); + } + + public function getContactsByTags(Request $request) + { + $limit = intval($request->get('per_page', 20)); + + $tags = Tag::select(['fc_tags.id', 'fc_tags.title']) + ->selectRaw('COUNT(subscriber_id) as contact_count') + ->leftJoin('fc_subscriber_pivot', function ($join) { + $join->on('fc_tags.id', '=', 'fc_subscriber_pivot.object_id') + ->where('fc_subscriber_pivot.object_type', '=', 'FluentCrm\App\Models\Tag'); + }) + ->groupBy('fc_tags.id', 'fc_tags.title') + ->orderByDesc('contact_count') + ->paginate($limit); + + return $this->sendSuccess([ + 'tags' => $tags, + ]); + } + + public function getContactsByLists(Request $request) + { + $limit = intval($request->get('per_page', 20)); + + $lists = Lists::select(['fc_lists.id', 'fc_lists.title']) + ->selectRaw('COUNT(subscriber_id) as contact_count') + ->leftJoin('fc_subscriber_pivot', function ($join) { + $join->on('fc_lists.id', '=', 'fc_subscriber_pivot.object_id') + ->where('fc_subscriber_pivot.object_type', '=', 'FluentCrm\App\Models\Lists'); + }) + ->groupBy('fc_lists.id', 'fc_lists.title') + ->orderByDesc('contact_count') + ->paginate($limit); + + return $this->sendSuccess([ + 'lists' => $lists, + ]); + } + + public function getContactsByCountry() + { + $countries = fluentCrmDb()->table('fc_subscribers') + ->select(fluentCrmDb()->raw('UPPER(TRIM(country)) as country_code, COUNT(id) as contact_count')) + ->whereNotNull('country') + ->whereRaw("TRIM(country) != ''") + ->groupBy(fluentCrmDb()->raw('UPPER(TRIM(country))')) + ->orderByDesc('contact_count') + ->get(); + + $result = []; + foreach ($countries as $row) { + $result[] = [ + 'country_code' => $row->country_code, + 'contact_count' => (int) $row->contact_count, + ]; + } + + return $this->sendSuccess([ + 'countries' => $result, + ]); + } + + public function getCampaignsList(Request $request) + { + $limit = intval($request->get('per_page', 15)); + + $campaigns = Campaign::where('status', 'archived') + ->orderBy('updated_at', 'DESC') + ->paginate($limit); + + foreach ($campaigns as $campaign) { + $campaign->stats = $campaign->stats(); + } + + return $this->sendSuccess([ + 'campaigns' => $campaigns, + ]); + } + + public function getAutomationReports(Request $request) + { + $limit = intval($request->get('per_page', 15)); + + $funnels = Funnel::where('status', 'published') + ->orderBy('created_at', 'DESC') + ->paginate($limit); + + $totalSubscribers = 0; + $totalCompleted = 0; + $totalInProgress = 0; + + foreach ($funnels as $funnel) { + $funnel->total_subscribers = FunnelSubscriber::where('funnel_id', $funnel->id) + ->distinct() + ->count('subscriber_id'); + + $funnel->completed_count = FunnelSubscriber::where('funnel_id', $funnel->id) + ->where('status', 'completed') + ->count(); + + $funnel->in_progress_count = FunnelSubscriber::where('funnel_id', $funnel->id) + ->where('status', 'active') + ->count(); + + // Last run time + $lastRun = FunnelSubscriber::where('funnel_id', $funnel->id) + ->whereNotNull('last_executed_time') + ->orderByDesc('last_executed_time') + ->first(); + $funnel->last_run_at = $lastRun ? $lastRun->last_executed_time : null; + + // Recent 3 subscribers who entered + $recentEntries = FunnelSubscriber::where('funnel_id', $funnel->id) + ->with(['subscriber' => function ($q) { + $q->select(['id', 'first_name', 'last_name', 'email', 'avatar']); + }]) + ->orderByDesc('created_at') + ->limit(3) + ->get(); + + $funnel->recent_subscribers = $recentEntries->map(function ($entry) { + if (!$entry->subscriber) { + return null; + } + return [ + 'id' => $entry->subscriber->id, + 'name' => trim($entry->subscriber->first_name . ' ' . $entry->subscriber->last_name), + 'email' => $entry->subscriber->email, + 'avatar' => $entry->subscriber->avatar, + 'entered_at' => $entry->created_at, + ]; + })->filter()->values(); + + $totalSubscribers += $funnel->total_subscribers; + $totalCompleted += $funnel->completed_count; + $totalInProgress += $funnel->in_progress_count; + } + + // Top 5 automations by total subscribers (most triggered) + $topAutomations = Funnel::where('status', 'published') + ->get() + ->map(function ($funnel) { + $funnel->trigger_count = FunnelSubscriber::where('funnel_id', $funnel->id) + ->count(); + return $funnel; + }) + ->sortByDesc('trigger_count') + ->take(5) + ->values() + ->map(function ($funnel) { + return [ + 'id' => $funnel->id, + 'title' => $funnel->title, + 'trigger_name' => $funnel->trigger_name, + 'trigger_count' => $funnel->trigger_count, + ]; + }); + + $overview = [ + 'total' => Funnel::where('status', 'published')->count(), + 'subscribers' => $totalSubscribers, + 'completed' => $totalCompleted, + 'in_progress' => $totalInProgress, + ]; + + return $this->sendSuccess([ + 'automations' => $funnels, + 'overview' => $overview, + 'top_automations' => $topAutomations, + ]); + } + + public function getAutomationStepReport(Request $request, Reporting $reporting, $id) + { + $id = intval($id); + $funnel = Funnel::findOrFail($id); + + $stats = $reporting->funnelStat($funnel->id); + + return $this->sendSuccess([ + 'funnel' => $funnel, + 'stats' => $stats, + ]); + } + + public function getCampaignOptions(Request $request) + { + global $wpdb; + + $search = sanitize_text_field($request->get('search', '')); + $limit = intval($request->get('per_page', 50)); + + $query = Campaign::select(['id', 'title']) + ->where('status', 'archived'); + + if ($search) { + $query->where('title', 'LIKE', '%' . $wpdb->esc_like($search) . '%'); + } + + $options = $query->orderBy('updated_at', 'DESC') + ->limit($limit) + ->get(); + + return $this->sendSuccess([ + 'options' => $options, + ]); + } + + public function getAdvancedReportProviders() + { + return [ + /** + * Determine the advanced report providers for FluentCRM. + * + * This filter allows you to modify the list of advanced report providers. + * + * @since 1.0.0 + * + * @param array An array of advanced report providers. + */ + 'providers' => apply_filters('fluent_crm/advanced_report_providers', []) + ]; + } + + public function getRecentTags(Request $request) + { + $limit = intval($request->get('per_page', 5)); + + $tags = Tag::select(['fc_tags.id', 'fc_tags.title', 'fc_tags.created_at']) + ->selectRaw('COUNT(subscriber_id) as contact_count') + ->leftJoin('fc_subscriber_pivot', function ($join) { + $join->on('fc_tags.id', '=', 'fc_subscriber_pivot.object_id') + ->where('fc_subscriber_pivot.object_type', '=', 'FluentCrm\App\Models\Tag'); + }) + ->groupBy('fc_tags.id', 'fc_tags.title', 'fc_tags.created_at') + ->orderByDesc('fc_tags.created_at') + ->limit($limit) + ->get(); + + return $this->sendSuccess([ + 'tags' => $tags, + ]); + } + + public function ping() + { + // Browser-driven cron fallback: while an admin has any CRM page open, + // the app pings this endpoint ~every 50s. If Action Scheduler (and the + // WP-Cron fallback) have stalled, take over the every-minute email task + // here. No-ops in a single option read when scheduling is healthy, and + // is fully locked/throttled internally — safe across tabs and users. + Scheduler::maybeProcessFromBrowserPing(); + + return [ + 'message' => 'pong' + ]; + } +} diff --git a/wp-content/plugins/fluent-crm/app/Http/Controllers/SettingsController.php b/wp-content/plugins/fluent-crm/app/Http/Controllers/SettingsController.php new file mode 100644 index 0000000..ac3fb1f --- /dev/null +++ b/wp-content/plugins/fluent-crm/app/Http/Controllers/SettingsController.php @@ -0,0 +1,1183 @@ +get('settings_keys', []); + + $returnSettings = []; + foreach ($keys as $key) { + if ($key == 'email_settings') { + $returnSettings[$key] = Helper::getGlobalEmailSettings(); + } else if (isset($existingSettings[$key])) { + + if ($key == 'business_settings') { + $existingSettings[$key] = fluentcrmGetGlobalSettings('business_settings'); + } + + $returnSettings[$key] = $existingSettings[$key]; + } + } + + return $returnSettings; + } + + public function save(Request $request) + { + $settings = (array)$request->get('settings', []); + + $existingSettings = get_option('fluentcrm-global-settings'); + + if (!$existingSettings) { + $existingSettings = []; + } + + foreach ($settings as $settingsKey => $setting) { + $existingSettings[$settingsKey] = $setting; + + if ($settingsKey == 'email_settings') { + $emailFooter = Arr::get($setting, 'email_footer'); + if (!Helper::hasComplianceText($emailFooter)) { + return $this->sendError([ + 'message' => __('##crm.manage_subscription_url## or ##crm.unsubscribe_url## string is required for compliance. Please include unsubscription or manage subscription link', 'fluent-crm') + ]); + } + } + } + + update_option( 'fluentcrm-global-settings', $existingSettings ); + + return $this->sendSuccess([ + 'message' => __('Settings Updated', 'fluent-crm') + ]); + } + + public function getDoubleOptinSettings(Request $request) + { + //check if list id comes + $listId = $request->get('list_id', null); + $doubleOptinSettings = null; + //if list id sent then it is double optin setup of a list + if ($listId) { + $meta = fluentcrm_get_list_meta($listId, 'double_optin_settings'); + $doubleOptinSettings = $meta ? $meta->value : null; + } + + //if no double optin setup of list found or this is global + if (!$doubleOptinSettings) { + $doubleOptinSettings = Helper::getDoubleOptinSettings(); + if (empty($doubleOptinSettings['tag_based_redirect'])) { + $doubleOptinSettings['tag_based_redirect'] = 'no'; + $doubleOptinSettings['tag_redirects'] = [ + [ + 'field_key' => [], + 'field_value' => '' + ] + ]; + } + } + + $data = [ + 'settings' => $doubleOptinSettings + ]; + + if ($listId) { + $globalDoubleOptin = fluentcrm_get_list_meta($listId, 'global_double_optin'); + $data['global_double_optin'] = $globalDoubleOptin ? $globalDoubleOptin->value : 'yes'; + } + + if (in_array('settings_fields', $request->get('with', []))) { + + $designTemplates = Helper::getEmailDesignTemplates(); + + $designTemplates = Arr::only($designTemplates, ['simple', 'plain', 'classic', 'raw_classic']); + + $data['settings_fields'] = [ + 'design_template' => [ + 'type' => 'image-radio', + 'label' => __('Design Template', 'fluent-crm'), + 'help' => __('Email Design Template for this double-optin email', 'fluent-crm'), + 'options' => $designTemplates + ], + 'email_subject' => [ + 'type' => 'input-text-popper', + 'placeholder' => __('Optin Email Subject', 'fluent-crm'), + 'label' => __('Email Subject', 'fluent-crm'), + 'help' => __('Your double-optin email subject', 'fluent-crm') + ], + 'email_pre_header' => [ + 'type' => 'input-text-popper', + 'placeholder' => __('Optin Email Pre Header', 'fluent-crm'), + 'label' => __('Email Pre Header', 'fluent-crm'), + 'help' => __('Your double-optin email pre header', 'fluent-crm') + ], + 'email_body' => [ + 'type' => 'wp-editor', + 'placeholder' => __('Double-Optin Email Body', 'fluent-crm'), + 'label' => __('Email Body', 'fluent-crm'), + 'help' => __('Provide Email Body for the double-optin', 'fluent-crm'), + 'inline_help' => __('Use #activate_link# for plain url or {{crm.activate_button|Confirm Subscription}} for default button', 'fluent-crm') + ], + 'confirmation_html_viewer' => [ + 'type' => 'html-viewer', + 'heading' => __('After Confirmation Actions', 'fluent-crm'), + 'info' => __('Please provide details after a contact confirms double opt-in from email', 'fluent-crm') + ], + 'after_confirmation_type' => [ + 'type' => 'input-radio', + 'label' => __('After Confirmation Type', 'fluent-crm'), + 'help' => __('Please select what will happen once a contact confirms double opt-in', 'fluent-crm'), + 'options' => [ + [ + 'id' => 'message', + 'label' => __('Show Message', 'fluent-crm') + ], + [ + 'id' => 'redirect', + 'label' => __('Redirect to an URL', 'fluent-crm') + ] + ] + ], + 'after_confirm_message' => [ + 'type' => 'wp-editor', + 'placeholder' => __('After Confirmation Message', 'fluent-crm'), + 'label' => __('After Confirmation Message', 'fluent-crm'), + 'help' => __('This message will be shown after a subscriber confirm subscription', 'fluent-crm'), + 'dependency' => [ + 'depends_on' => 'after_confirmation_type', + 'operator' => '=', + 'value' => 'message' + ] + ], + 'after_conf_redirect_url' => [ + 'type' => 'input-text-popper', + 'placeholder' => __('Redirect URL', 'fluent-crm'), + 'label' => __('Redirect URL', 'fluent-crm'), + 'help' => __('Please provide redirect URL after confirmation', 'fluent-crm'), + 'dependency' => [ + 'depends_on' => 'after_confirmation_type', + 'operator' => '=', + 'value' => 'redirect' + ] + ], + 'tag_based_redirect' => [ + 'type' => 'inline-checkbox', + 'checkbox_label' => (defined('FLUENTCAMPAIGN')) ? __('Enable Tag based double optin redirect', 'fluent-crm') : __('Enable Tag based double optin redirect (Require FluentCRM Pro)', 'fluent-crm'), + 'true_label' => 'yes', + 'false_label' => 'no', + 'disabled' => !defined('FLUENTCAMPAIGN') + ], + 'tag_redirects' => [ + 'label' => __('Configure your redirect URLs based on tags (Will redirect to the provided URL if any selected tag matches the contact)', 'fluent-crm'), + 'type' => 'form-many-drop-down-mapper', + 'local_label' => __('Targeted Tags', 'fluent-crm'), + 'local_placeholder' => __('Select Tags', 'fluent-crm'), + 'remote_label' => __('Redirect URL (After Double Optin Confirmation)', 'fluent-crm'), + 'field_option_selector' => [ + 'option_key' => 'tags', + 'is_multiple' => true + ], + 'remote_field_type' => 'input-text-popper', + 'remote_field' => [ + 'placeholder' => __('Redirect URL', 'fluent-crm') + ], + 'help' => __('User will be redirected to the URL which matches based on the tags at first match', 'fluent-crm'), + 'dependency' => [ + 'depends_on' => 'tag_based_redirect', + 'operator' => '=', + 'value' => 'yes' + ], + 'manage_serial' => true + ] + ]; + } + + return $this->sendSuccess($data); + } + + public function saveDoubleOptinSettings(Request $request) + { + $settings = wp_unslash($request->get('settings')); + $listId = $request->get('list_id'); + $globalDoubleOptin = sanitize_text_field($request->get('global_double_optin')); + + $this->validate($settings, [ + 'email_subject' => 'required', + 'email_body' => 'required', + 'after_confirm_message' => 'required' + ], [ + 'email_subject.required' => __('Email Subject is required', 'fluent-crm'), + 'email_body.required' => __('Email Body is required', 'fluent-crm'), + 'after_confirm_message.required' => __('After Confirmation Message is required', 'fluent-crm') + ]); + + // let's check if message contains #activate_link# or {{crm.activate_button + $emailBody = $settings['email_body']; + + if ( + strpos($emailBody, '#activate_link#') === false && + strpos($emailBody, '{{crm.activate_button') === false + ) { + return $this->sendError([ + 'message' => __('Email Body need to contains activation link', 'fluent-crm') + ]); + } + + if ($listId) { + fluentcrm_update_list_meta($listId, 'double_optin_settings', $settings); + if ($globalDoubleOptin) { + fluentcrm_update_list_meta($listId, 'global_double_optin', $globalDoubleOptin); + } + } else { + fluentcrm_update_option('double_optin_settings', $settings); + } + + return $this->sendSuccess([ + 'message' => __('Double Opt-in settings has been updated', 'fluent-crm') + ] + ); + } + + public function TestRequestResolver(Request $request) + { + return [ + 'message' => __('Valid', 'fluent-crm'), + 'params' => $request->all() + ]; + } + + public function resetDB(Request $request) + { + if (!current_user_can('manage_options')) { + return $this->sendError([ + 'message' => __('Sorry, You do not have admin permission to reset database', 'fluent-crm') + ]); + } + + if (!defined('FLUENTCRM_IS_DEV_FEATURES') || !FLUENTCRM_IS_DEV_FEATURES) { + return $this->sendError([ + 'message' => __('Development mode is not activated. So you cannot use this feature. You can define "FLUENTCRM_IS_DEV_FEATURES" in your wp-config to enable this feature', 'fluent-crm') + ]); + } + + $tables = [ + 'fc_campaign_emails', + 'fc_campaigns', + 'fc_campaign_url_metrics', + 'fc_funnel_metrics', + 'fc_funnels', + 'fc_funnel_sequences', + 'fc_funnel_subscribers', + 'fc_lists', + 'fc_meta', + 'fc_subscriber_meta', + 'fc_subscriber_notes', + 'fc_subscriber_pivot', + 'fc_subscribers', + 'fc_tags', + 'fc_url_stores' + ]; + + if (defined('FLUENTCAMPAIGN_PLUGIN_URL')) { + $tables[] = 'fc_sequence_tracker'; + } + + global $wpdb; + foreach ($tables as $table) { + // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared + $wpdb->query("DROP TABLE IF EXISTS " . $wpdb->prefix . $table); + } + // All tables are delete now let's run the migration + (new ActivationHandler)->handle(false); + + if (defined('FLUENTCAMPAIGN_PLUGIN_URL')) { + \FluentCampaign\App\Migration\Migrate::run(false); + } + + $options = [ + '_fluentcrm_commerce_modules' + ]; + + foreach ($options as $option) { + delete_option($option); + } + + return [ + 'message' => __('All FluentCRM Database Tables have been reset', 'fluent-crm'), + 'tables' => $tables + ]; + } + + public function getBounceConfigs() + { + $securityCode = fluentcrm_get_option('_fc_bounce_key'); + if (!$securityCode) { + $securityCode = 'fcrm_' . substr(md5(wp_generate_uuid4()), 0, 14); // first 14 digit + fluentcrm_update_option('_fc_bounce_key', $securityCode); + } + + $bounceSettings = [ + 'ses' => [ + 'label' => __('Amazon SES', 'fluent-crm'), + 'webhook_url' => site_url('index.php?fluentcrm=1&route=bounce_handler&provider=ses&verify_key=' . $securityCode), + 'doc_url' => 'https://fluentcrm.com/docs/bounce-handler-with-amazon-ses/', + 'input_title' => __('Amazon SES Bounce Handler URL', 'fluent-crm'), + 'input_info' => __('Please use this bounce handler url in your Amazon SES + SNS settings', 'fluent-crm') + ], + 'tosend' => [ + 'label' => __('ToSend', 'fluent-crm'), + 'webhook_url' => get_rest_url(null, 'fluent-crm/v2/public/bounce_handler/tosend/handle/' . $securityCode), + 'doc_url' => 'https://fluentcrm.com/docs/bounce-handling-with-tosend/', + 'input_title' => __('ToSend Bounce Handler Webhook URL', 'fluent-crm'), + 'input_info' => __('Please paste this URL into your ToSend\'s Webhook settings to enable Bounce Handling with FluentCRM. Select both Bounce and Complaint events.', 'fluent-crm') + ], + 'mailgun' => [ + 'label' => __('Mailgun', 'fluent-crm'), + 'webhook_url' => get_rest_url(null, 'fluent-crm/v2/public/bounce_handler/mailgun/handle/' . $securityCode), + 'doc_url' => 'https://fluentcrm.com/docs/bounce-handling-with-mailgun/', + 'input_title' => __('Mailgun Bounce Handler Webhook URL', 'fluent-crm'), + 'input_info' => __('Please paste this URL into your Mailgun\'s Webhook settings to enable Bounce Handling with FluentCRM', 'fluent-crm') + ], + 'pepipost' => [ + 'label' => __('PepiPost', 'fluent-crm'), + 'webhook_url' => get_rest_url(null, 'fluent-crm/v2/public/bounce_handler/pepipost/handle/' . $securityCode), + 'doc_url' => 'https://fluentcrm.com/docs/bounce-handling-with-pepipost/', + 'input_title' => __('PepiPost Bounce Handler Webhook URL', 'fluent-crm'), + 'input_info' => __('Please paste this URL into your PepiPost\'s Webhook settings to enable Bounce Handling with FluentCRM', 'fluent-crm') + ], + 'postmark' => [ + 'label' => __('PostMark', 'fluent-crm'), + 'webhook_url' => get_rest_url(null, 'fluent-crm/v2/public/bounce_handler/postmark/handle/' . $securityCode), + 'doc_url' => 'https://fluentcrm.com/docs/bounce-handling-with-postmark/', + 'input_title' => __('PostMark Bounce Handler Webhook URL', 'fluent-crm'), + 'input_info' => __('Please paste this URL into your PostMark\'s Webhook settings to enable Bounce Handling with FluentCRM', 'fluent-crm') + ], + 'sendgrid' => [ + 'label' => __('SendGrid', 'fluent-crm'), + 'webhook_url' => get_rest_url(null, 'fluent-crm/v2/public/bounce_handler/sendgrid/handle/' . $securityCode), + 'doc_url' => 'https://fluentcrm.com/docs/bounce-handling-with-sendgrid/', + 'input_title' => __('SendGrid Bounce Handler Webhook URL', 'fluent-crm'), + 'input_info' => __('Please paste this URL into your SendGrid\'s Webhook settings to enable Bounce Handling with FluentCRM', 'fluent-crm') + ], + 'sparkpost' => [ + 'label' => __('SparkPost', 'fluent-crm'), + 'webhook_url' => get_rest_url(null, 'fluent-crm/v2/public/bounce_handler/sparkpost/handle/' . $securityCode), + 'doc_url' => 'https://fluentcrm.com/docs/bounce-handling-with-sparkpost/', + 'input_title' => __('SparkPost Bounce Handler Webhook URL', 'fluent-crm'), + 'input_info' => __('Please paste this URL into your SparkPost\'s Webhook settings to enable Bounce Handling with FluentCRM', 'fluent-crm') + ], + 'elasticemail' => [ + 'label' => __('Elastic Email', 'fluent-crm'), + 'webhook_url' => get_rest_url(null, 'fluent-crm/v2/public/bounce_handler/elasticemail/handle/' . $securityCode), + 'doc_url' => 'https://fluentcrm.com/docs/bounce-handling-with-elastic-email/', + 'input_title' => __('Elastic Email Bounce Handler Webhook URL', 'fluent-crm'), + 'input_info' => __('Please paste this URL into your Elastic Email\'s Webhook settings to enable Bounce Handling with FluentCRM', 'fluent-crm') + ], + 'postalserver' => [ + 'label' => __('Postal Server', 'fluent-crm'), + 'webhook_url' => get_rest_url(null, 'fluent-crm/v2/public/bounce_handler/postalserver/handle/' . $securityCode), + 'doc_url' => 'https://fluentcrm.com/docs/bounce-handling-with-postal-server/', + 'input_title' => __('Postal Server Bounce Handler Webhook URL', 'fluent-crm'), + 'input_info' => __('Please paste this URL into your Postal Server\'s Webhook settings to enable Bounce Handling with FluentCRM. Please select only MessageBounced & MessageDeliveryFailed event', 'fluent-crm') + ], + 'smtp2go' => [ + 'label' => __('SMTP2Go', 'fluent-crm'), + 'webhook_url' => get_rest_url(null, 'fluent-crm/v2/public/bounce_handler/smtp2go/handle/' . $securityCode), + 'doc_url' => 'https://fluentcrm.com/docs/bounce-handling-with-smtp2go/', + 'input_title' => __('SMTP2Go Bounce Handler Webhook URL', 'fluent-crm'), + 'input_info' => __('Please paste this URL into your SMTP2Go\'s Webhook settings to enable Bounce Handling with FluentCRM', 'fluent-crm') + ], + 'brevo' => [ + 'label' => __('Brevo (ex Sendinblue)', 'fluent-crm'), + 'webhook_url' => get_rest_url(null, 'fluent-crm/v2/public/bounce_handler/brevo/handle/' . $securityCode), + 'doc_url' => 'https://fluentcrm.com/docs/bounce-handling-with-brevo/', + 'input_title' => __('Brevo Bounce Handler Webhook URL', 'fluent-crm'), + 'input_info' => __('Please paste this URL into your Brevo\'s Webhook settings to enable Bounce Handling with FluentCRM', 'fluent-crm') + ], + ]; + + $data = [ + /** + * Determine FluentCRM Bounce Handler settings. + * + * This filter allows modification of the bounce handler settings. + * + * @param array $bounceSettings The current bounce settings. + * @param string $securityCode The security code for the bounce handler. + * @since 2.5.95 + * + */ + 'bounce_settings' => apply_filters('fluent_crm/bounce_handlers', $bounceSettings, $securityCode) + ]; + + if (defined('FLUENTMAIL')) { + $smtpSettings = get_option('fluentmail-settings', []); + if (!$smtpSettings || !count($smtpSettings['connections'])) { + $data['fluentsmtp_info'] = [ + 'configured' => false + ]; + } else { + $data['fluentsmtp_info'] = [ + 'configured' => true, + 'verified_senders' => array_keys($smtpSettings['mappings']) + ]; + } + $data['fluentsmtp_info']['config_url'] = admin_url('options-general.php?page=fluent-mail#/connections'); + } else { + $data['fluentsmtp_info'] = false; + } + + return $data; + } + + public function getAutoSubscribeSettings(Request $request) + { + $autoSubscribeService = new AutoSubscribe(); + + $roleBasedTaggingClass = new RoleBasedTagging(); + + $data = [ + 'registration_setting' => $autoSubscribeService->getRegistrationSettings(), + 'comment_settings' => $autoSubscribeService->getCommentSettings(), + 'user_syncing_settings' => $autoSubscribeService->getUserSyncSettings(), + 'role_based_tagging_settings' => $roleBasedTaggingClass->getSettings(true), + 'date_time_settings' => [ + 'classic_date_time' => Arr::get(Helper::getExperimentalSettings(), 'classic_date_time', 'no') + ] + ]; + + $with = $request->get('with', []); + if (in_array('fields', $with)) { + $data['registration_fields'] = $autoSubscribeService->getRegistrationFields(); + $data['comment_fields'] = $autoSubscribeService->getCommentFields(); + $data['user_syncing_fields'] = $autoSubscribeService->getUserSyncFields(); + $data['role_based_tagging_settings_fields'] = $roleBasedTaggingClass->getFields(); + } + + if (defined('WC_PLUGIN_FILE')) { + $data['woo_checkout_fields'] = $autoSubscribeService->getWooCheckoutFields(); + $data['woo_checkout_settings'] = $autoSubscribeService->getWooCheckoutSettings(); + } + + if (defined('FLUENTCART_VERSION')) { + $data['fluent_cart_checkout_fields'] = $autoSubscribeService->getFluentCartCheckoutFields(); + $data['fluent_cart_checkout_settings'] = $autoSubscribeService->getFluentCartCheckoutSettings(); + } + + return $data; + } + + public function saveAutoSubscribeSettings(Request $request) + { + $registrationSettings = $request->get('registration_setting', []); + $commentSettings = $request->get('comment_settings', []); + $userSyncSettings = $request->get('user_syncing_settings', []); + $dateTimeSettings = $request->get('date_time_settings', []); + + + fluentcrm_update_option('user_registration_subscribe_settings', $registrationSettings); + fluentcrm_update_option('comment_form_subscribe_settings', $commentSettings); + fluentcrm_update_option('user_syncing_settings', $userSyncSettings); + + if (is_array($dateTimeSettings) && array_key_exists('classic_date_time', $dateTimeSettings)) { + $experimentalSettings = Helper::getExperimentalSettings(); + $experimentalSettings['classic_date_time'] = sanitize_text_field($dateTimeSettings['classic_date_time']) === 'yes' ? 'yes' : 'no'; + update_option('_fluentcrm_experimental_settings', $experimentalSettings, 'yes'); + } + + if (defined('FLUENTCAMPAIGN_PLUGIN_VERSION')) { + $roleBasedSettings = $request->get('role_based_tagging_settings', []); + fluentcrm_update_option('role_based_tagging_settings', $roleBasedSettings); + } + + if (defined('WC_PLUGIN_FILE') && defined('FLUENTCAMPAIGN_DIR_FILE')) { + $wooCheckoutSettings = $request->get('woo_checkout_settings'); + fluentcrm_update_option('woo_checkout_form_subscribe_settings', $wooCheckoutSettings); + fluentCrmSetCache('woo_checkout_form_subscribe_settings', $wooCheckoutSettings, 86400); + } + + if (defined('FLUENTCART_VERSION')) { + $fluentCartCheckoutSettings = (new AutoSubscribe())->sanitizeFluentCartCheckoutSettings( + $request->get('fluent_cart_checkout_settings', []) + ); + fluentcrm_update_option('fluent_cart_checkout_form_subscribe_settings', $fluentCartCheckoutSettings); + } + + return [ + 'message' => __('Settings has been updated', 'fluent-crm') + ]; + } + + public function getCronStatus() + { + $events = []; + + $nextRun = Helper::getNextMinuteTaskTimeStamp(); + + $events[] = (object)array( + 'hook' => 'fluentcrm_scheduled_every_minute_tasks', + 'is_overdue' => (time() - $nextRun) > 30, + 'human_name' => __('Scheduled Email Sending Tasks', 'fluent-crm'), + 'next_run' => human_time_diff($nextRun, time()), + 'interval' => 60 + ); + + $nextFiverMinutesRun = wp_next_scheduled('fluentcrm_scheduled_five_minute_tasks'); + $events[] = (object)array( + 'hook' => 'fluentcrm_scheduled_hourly_tasks', + 'is_overdue' => ($nextFiverMinutesRun - time()) < -60, + 'human_name' => __('Scheduled Email Processing', 'fluent-crm'), + 'next_run' => human_time_diff($nextFiverMinutesRun, time()), + 'interval' => 300 + ); + + $nextHourlyRun = wp_next_scheduled('fluentcrm_scheduled_hourly_tasks'); + $events[] = (object)array( + 'hook' => 'fluentcrm_scheduled_hourly_tasks', + 'is_overdue' => ($nextHourlyRun - time()) < -120, + 'human_name' => __('Scheduled Automation Tasks', 'fluent-crm'), + 'next_run' => human_time_diff($nextHourlyRun, time()), + 'interval' => 3600 + ); + + + return [ + 'cron_events' => $events, + 'server' => [ + 'memory_limit' => intval(fluentCrmGetMemoryLimit() / 1048576) . 'MB', + 'usage_percent' => fluentCrmGetMemoryUsagePercentage(), + 'max_execution_time' => fluentCrmMaxRunTime() . ' seconds', + 'has_server_cron' => defined('DISABLE_WP_CRON') && DISABLE_WP_CRON + ] + ]; + } + + public function runCron(Request $request) + { + $hookName = $request->get('hook'); + $hookNames = [ + 'fluentcrm_scheduled_every_minute_tasks' => __('Scheduled Email Sending', 'fluent-crm'), + 'fluentcrm_scheduled_hourly_tasks' => __('Scheduled Automation Tasks', 'fluent-crm'), + 'fluentcrm_scheduled_five_minute_tasks' => __('Scheduled Email Processing', 'fluent-crm') + ]; + + if (!isset($hookNames[$hookName])) { + return $this->sendError([ + 'message' => __('The provided hook name is not valid', 'fluent-crm') + ]); + } + + do_action($hookName); + + return [ + 'message' => __('Selected CRON Event successfully ran', 'fluent-crm') + ]; + } + + /** + * Return the health of the critical DB indexes for the settings UI. + * + * Cheap by default: serves the cached snapshot unless ?fresh=1 is passed + * (e.g. to re-verify right after a repair). + * + * @param Request $request + * @return array + */ + public function getDbIndexHealth(Request $request) + { + $fromDb = $request->getSafe('fresh', 'sanitize_text_field') === '1'; + + return $this->sendSuccess([ + 'indexes' => array_values(DbPerformanceService::getIndexHealth($fromDb)) + ]); + } + + /** + * Repair any missing/broken critical DB indexes. + * + * Runs inline (the ALTERs are idempotent and prefer a non-blocking online + * build) behind a short transient lock so concurrent admin tabs/users can't + * trigger overlapping ALTERs on the same tables. If a repair is already in + * flight we report that rather than stacking a second one. + * + * @param Request $request + * @return array + */ + public function repairDbIndexes(Request $request) + { + // Re-check from the live DB so a stale "ok" cache can't make us skip a + // genuinely needed repair (and a stale "broken" cache can't force a + // pointless ALTER pass). + if (!DbPerformanceService::hasBrokenIndex(true)) { + return $this->sendSuccess([ + 'message' => __('All database indexes are healthy.', 'fluent-crm'), + 'indexes' => array_values(DbPerformanceService::getIndexHealth(false)) + ]); + } + + $lockKey = 'fc_db_index_repairing'; + if (get_transient($lockKey)) { + // Not an error — another tab/request is already repairing. Report it + // as a benign "pending" success so the client doesn't surface a + // false failure notice while the other request finishes the work. + return $this->sendSuccess([ + 'pending' => true, + 'message' => __('A database index repair is already running.', 'fluent-crm'), + 'indexes' => array_values(DbPerformanceService::getIndexHealth(false)) + ]); + } + + set_transient($lockKey, 1, 5 * MINUTE_IN_SECONDS); + + try { + $result = DbPerformanceService::repairBrokenIndexes(); + } finally { + delete_transient($lockKey); + } + + if ($result['failed']) { + return $this->sendError([ + 'message' => __('Some database indexes could not be repaired. Please check your database user privileges or contact your host.', 'fluent-crm'), + 'failed' => $result['failed'], + 'indexes' => array_values($result['health']) + ], 422); + } + + return $this->sendSuccess([ + 'message' => __('Database indexes repaired successfully.', 'fluent-crm'), + 'repaired' => $result['repaired'], + 'indexes' => array_values($result['health']) + ]); + } + + public function getOldLogDetails(Request $request) + { + $data = $request->all(); + $this->validate($data, [ + 'days_before' => 'required|numeric|min:7', + 'selected_logs' => 'array|required' + ]); + + $selectedLogs = $data['selected_logs']; + $daysBefore = $data['days_before']; + + $refDate = gmdate('Y-m-d 00:00:01', time() - $daysBefore * 86400); + + $dataCounters = []; + if (in_array('emails', $selectedLogs)) { + $dataCounters[] = [ + 'title' => __('Email History Logs', 'fluent-crm'), + 'count' => CampaignEmail::where('created_at', '<', $refDate) + ->where('status', 'sent') + ->count() + ]; + } + + if (in_array('email_clicks', $selectedLogs)) { + $dataCounters[] = [ + 'title' => __('Email clicks', 'fluent-crm'), + 'count' => CampaignUrlMetric::where('type', 'click') + ->where('created_at', '<', $refDate) + ->count() + ]; + } + + if (in_array('email_open', $selectedLogs)) { + $dataCounters[] = [ + 'title' => __('Email Opens', 'fluent-crm'), + 'count' => CampaignUrlMetric::where('type', 'open') + ->where('created_at', '<', $refDate) + ->count() + ]; + } + + if (in_array('system_logs', $selectedLogs)) { + $dataCounters[] = [ + 'title' => __('System Logs', 'fluent-crm'), + 'count' => SystemLog::where('created_at', '<', $refDate) + ->count() + ]; + } + + if (in_array('activity_logs', $selectedLogs)) { + $dataCounters[] = [ + 'title' => __('Activity Logs', 'fluent-crm'), + 'count' => ActivityLog::where('created_at', '<', $refDate) + ->count() + ]; + } + + return [ + 'log_counts' => $dataCounters + ]; + } + + public function removeOldLogs(Request $request) + { + $data = $request->all(); + $this->validate($data, [ + 'days_before' => 'required|numeric|min:7', + 'selected_logs' => 'array|required' + ]); + + $selectedLogs = $data['selected_logs']; + $daysBefore = $data['days_before']; + + $perChunk = 10000; // Deleting 10,000 per chunk + $hasMore = false; + + $refDate = gmdate('Y-m-d 00:00:01', time() - $daysBefore * 86400); + if (in_array('emails', $selectedLogs)) { + + $campaignIds = CampaignEmail::where('created_at', '<', $refDate) + ->where('status', 'sent') + ->groupBy('campaign_id') + ->pluck('campaign_id'); + + foreach ($campaignIds->toArray() as $campaignId) { + fluentcrm_update_campaign_meta($campaignId, '_data_trunked', 'yes'); + } + + CampaignEmail::where('created_at', '<', $refDate) + ->where('status', 'sent') + ->limit($perChunk) + ->delete(); + + $hasMore = CampaignEmail::where('created_at', '<', $refDate) + ->where('status', 'sent') + ->exists(); + } + + $urlMetricsTypes = []; + if (in_array('email_clicks', $selectedLogs)) { + $urlMetricsTypes[] = 'click'; + } + if (in_array('email_open', $selectedLogs)) { + $urlMetricsTypes[] = 'open'; + } + + if ($urlMetricsTypes) { + CampaignUrlMetric::whereIn('type', $urlMetricsTypes) + ->where('created_at', '<', $refDate) + ->limit($perChunk) + ->delete(); + + if (!$hasMore) { + $hasMore = CampaignUrlMetric::whereIn('type', $urlMetricsTypes) + ->where('created_at', '<', $refDate) + ->exists(); + } + + } + + if (in_array('system_logs', $selectedLogs)) { + SystemLog::where('created_at', '<', $refDate) + ->limit($perChunk) + ->delete(); + + if (!$hasMore) { + $hasMore = SystemLog::where('created_at', '<', $refDate) + ->exists(); + } + + } + + if (in_array('activity_logs', $selectedLogs)) { + ActivityLog::where('created_at', '<', $refDate) + ->limit($perChunk) + ->delete(); + + if (!$hasMore) { + $hasMore = ActivityLog::where('created_at', '<', $refDate) + ->exists(); + } + + } + + return [ + /* translators: %d is the number of days; used to indicate how old the deleted logs were. */ + 'message' => sprintf(__('Logs older than %d days have been deleted successfully', 'fluent-crm'), $daysBefore), + 'has_more' => $hasMore + ]; + } + + public function deleteRestKey(Request $request) + { + $data = $request->all(); + $this->validate($data, [ + 'user_id' => 'required', + 'uuid' => 'required' + ]); + + $data['user_id'] = intval($data['user_id']); + $data['uuid'] = sanitize_text_field($data['uuid']); + + if (!get_user_meta($data['user_id'], '_fcrm_has_role', true)) { + return $this->sendError([ + 'message' => __('Sorry, the provided user does not have FluentCRM access', 'fluent-crm') + ]); + } + + if (!current_user_can('manage_options')) { + return $this->sendError([ + 'message' => __('Sorry, You do not have permission to delete REST API', 'fluent-crm') + ]); + } + + $deleted = \WP_Application_Passwords::delete_application_password($data['user_id'], $data['uuid']); + + if ($deleted) { + $applicationUsers = fluentcrm_get_option('_rest_api_users', []); + + foreach ($applicationUsers[$data['user_id']] as $index => $uuid) { + if ($uuid == $data['uuid']) { + array_splice($applicationUsers[$data['user_id']], $index, 1); + } + } + + fluentcrm_update_option('_rest_api_users', $applicationUsers); + } + + if (!$deleted) { + return $this->sendError([ + 'message' => __('Something is wrong', 'fluent-crm') + ]); + } + + return [ + 'message' => __('API Key has been successfully deleted', 'fluent-crm') + ]; + } + + public function getRestKeys(Request $request) + { + $query = new \WP_User_Query(array( + 'meta_key' => '_fcrm_has_role', + 'meta_value' => 1, + 'meta_compare' => '=', + 'number' => 200 + )); + + $managers = []; + + foreach ($query->get_results() as $user) { + if (user_can($user, 'manage_options')) { + continue; + } + + $managers[] = [ + 'id' => $user->ID, + 'full_name' => $user->first_name . ' - ' . $user->last_name, + 'email' => $user->user_email + ]; + } + + $applicationUsers = fluentcrm_get_option('_rest_api_users', []); + $restApps = []; + + if ($applicationUsers) { + $userIds = array_keys($applicationUsers); + $restUsers = get_users([ + 'include' => $userIds, + 'number' => 20 + ]); + + foreach ($restUsers as $restUser) { + $applicationUUIDs = $applicationUsers[$restUser->ID]; + $passwords = get_user_meta($restUser->ID, '_application_passwords', true); + + $crmApps = []; + + foreach ($passwords as $password) { + if (in_array($password['uuid'], $applicationUUIDs)) { + $crmApps[] = [ + 'uuid' => $password['uuid'], + 'name' => $password['name'], + 'created' => gmdate('Y-m-d H:i:s', $password['created']) + ]; + } + } + + if (!$crmApps) { + continue; + } + + $restApps[] = [ + 'id' => $restUser->ID, + 'first_name' => $restUser->first_name, + 'last_name' => $restUser->last_name, + 'email' => $restUser->user_email, + 'api_keys' => $crmApps, + 'manage_url' => admin_url('user-edit.php?user_id=' . $restUser->ID . '#application-passwords-section') + ]; + } + } + + + return [ + 'managers' => $managers, + 'rest_keys' => $restApps + ]; + } + + public function createRestKey(Request $request) + { + $data = $request->all(); + $this->validate($data, [ + 'api_name' => 'required', + 'api_user_id' => 'required' + ]); + + $data['api_user_id'] = intval($data['api_user_id']); + $data['api_name'] = sanitize_text_field($data['api_name']); + + // check if the provided user has FluentCRM Access + if (!get_user_meta($data['api_user_id'], '_fcrm_has_role', true)) { + return $this->sendError([ + 'message' => __('Sorry, the provided user does not have FluentCRM access', 'fluent-crm') + ]); + } + + if (!current_user_can('manage_options')) { + return $this->sendError([ + 'message' => __('Sorry, You do not have permission to create REST API', 'fluent-crm') + ]); + } + + $user = get_user_by('ID', $data['api_user_id']); + + if (is_wp_error($user)) { + return $this->sendError([ + 'message' => __('Sorry, the provided user does not have FluentCRM access', 'fluent-crm') + ]); + } + + $prepared = (object)[ + 'name' => $data['api_name'] + ]; + + $created = \WP_Application_Passwords::create_new_application_password($user->ID, wp_slash((array)$prepared)); + + if (is_wp_error($created)) { + return $this->sendError([ + 'message' => $created->get_error_message() + ]); + } + + + $password = $created[0]; + $item = \WP_Application_Passwords::get_user_application_password($user->ID, $created[1]['uuid']); + + $item['info'] = [ + 'api_password' => \WP_Application_Passwords::chunk_password($password), + 'api_username' => $user->user_login, + ]; + + $uuid = $item['uuid']; + + $applicationUsers = fluentcrm_get_option('_rest_api_users', []); + + if (!isset($applicationUsers[$user->ID])) { + $applicationUsers[$user->ID] = []; + } + $applicationUsers[$user->ID][] = $uuid; + + fluentcrm_update_option('_rest_api_users', $applicationUsers); + + return [ + 'item' => $item, + 'message' => __('API Key has been successfully created', 'fluent-crm') + ]; + } + + public function getIntegrations(Request $request) + { + $withFields = in_array('fields', $request->get('with', [])); + + /** + * Determine the deep integration providers for FluentCRM. + * + * This filter allows modification of the deep integration providers used in FluentCRM such as Woocommerce, Easy Digital Downloads, etc. + * + * @param array An array of deep integration providers. + * @param bool $withFields Whether to include fields in the integration providers. + * @since 2.5.1 + * + */ + $deepIntegrationProviders = apply_filters('fluentcrm_deep_integration_providers', [], $withFields); + + return [ + 'integrations' => $deepIntegrationProviders + ]; + } + + public function saveIntegration(Request $request) + { + $provider = $request->get('provider'); + $action = $request->get('action'); + $data = $request->all(); + + if ($action == 'sync') { + /** + * Determine whether to allow deep integration sync for a specific provider. + * + * This filter allows you to modify the result of the deep integration sync for a given provider. + * + * @param mixed The result of the integration sync. Default false. Expected to be a boolean. + * @param array $data The data to be synced. + * @since 2.5.1 + * + */ + $result = apply_filters('fluentcrm_deep_integration_sync_' . $provider, false, $data); + } else { + /** + * Determine the result of saving deep integration settings for a specific provider. + * + * The dynamic portion of the hook name, `$provider`, refers to the specific integration provider. + * + * @param mixed The result of the save operation. Default false. Expected to be a boolean. + * @param array $data The data being saved. + * @since 2.5.1 + * + */ + $result = apply_filters('fluentcrm_deep_integration_save_' . $provider, false, $data); + } + + if ($result) { + if (is_wp_error($result)) { + return $this->sendError([ + 'message' => $result->get_error_message() + ]); + } + return $result; + } + + return $this->sendError([ + 'message' => __('Sorry, the provided provider does not exist', 'fluent-crm') + ]); + } + + public function getComplianceSettings(Request $request) + { + return [ + 'settings' => Helper::getComplianceSettings() + ]; + } + + public function updateComplianceSettings(Request $request) + { + $data = Arr::only($request->all(), array_keys(Helper::getComplianceSettings())); + $validValues = [ + 'yes', + 'no', + 'anonymous' + ]; + + foreach ($data as $key => $datum) { + if (!in_array($datum, $validValues)) { + $data[$key] = ''; + } else { + $data[$key] = $datum; + } + } + + update_option('_fluentcrm_compliance_settings', $data, 'no'); + + do_action('fluent_crm/sync_subscriber_delete_setting', 'compliance_settings', $data['delete_contact_on_user']); + + return [ + 'message' => __('Settings has been successfully updated', 'fluent-crm'), + 'settings' => $data + ]; + } + + public function getExperimentalSettings(Request $request) + { + return [ + 'settings' => Helper::getExperimentalSettings() + ]; + } + + public function updateExperimentalSettings(Request $request) + { + + $data = Arr::only($request->all(), array_keys(Helper::getExperimentalSettings())); + + foreach ($data as $key => $datum) { + if ($key === 'campaign_ids' && is_array($datum)) { + $data[$key] = array_map('intval', $datum); + } elseif ($key === 'frontend_portal_page_id') { + $data[$key] = absint($datum); + } elseif ($key === 'frontend_portal_slug') { + $data[$key] = sanitize_title($datum); + } elseif ($key === 'frontend_portal_render_type') { + $renderType = sanitize_text_field($datum); + $data[$key] = in_array($renderType, ['standalone', 'shortcode'], true) ? $renderType : 'standalone'; + } else { + $data[$key] = sanitize_text_field($datum); + } + } + + if (Arr::get($data, 'frontend_portal') === 'yes' && empty($data['frontend_portal_slug'])) { + $data['frontend_portal_slug'] = 'fluentcrm'; + } + + if (Arr::get($data, 'company_module') == 'yes') { + require_once(FLUENTCRM_PLUGIN_PATH . 'database/migrations/CompaniesMigrator.php'); + \FluentCrmMigrations\CompaniesMigrator::migrate(); + } + + if (Arr::get($data, 'event_tracking') == 'yes') { + require_once(FLUENTCRM_PLUGIN_PATH . 'database/migrations/SubscriberEventTracking.php'); + \FluentCrmMigrations\SubscriberEventTracking::migrate(); + } + + if (Arr::get($data, 'activity_log') == 'yes') { + require_once(FLUENTCRM_PLUGIN_PATH . 'database/migrations/ActivityLogsMigrator.php'); + \FluentCrmMigrations\ActivityLogsMigrator::migrate(); + } + + update_option('_fluentcrm_experimental_settings', $data, 'yes'); + + return [ + 'message' => __('Settings has been updated', 'fluent-crm') + ]; + } + + public function getCampaigns(Request $request) + { + $campaigns = Campaign::orderBy('id', 'DESC')->get(); + + return [ + 'campaigns' => $campaigns + ]; + } + +} diff --git a/wp-content/plugins/fluent-crm/app/Http/Controllers/SetupController.php b/wp-content/plugins/fluent-crm/app/Http/Controllers/SetupController.php new file mode 100644 index 0000000..ff6c843 --- /dev/null +++ b/wp-content/plugins/fluent-crm/app/Http/Controllers/SetupController.php @@ -0,0 +1,382 @@ +get('install_fluentform', 'no'); + + if ($installFluentForm == 'yes' && !defined('FLUENTFORM')) { + $this->installFluentForm(); + } + + if ($request->get('install_fluentcart', 'no') === 'yes' && !defined('FLUENTCART_VERSION')) { + $this->installFluentCart(); + } + + $optinEmail = $request->get('optin_email', 'no'); + if ($optinEmail && is_email($optinEmail)) { + $this->shareEmail($optinEmail); + } + + $shareEssential = $request->get('share_essentials', 'no'); + if ($shareEssential == 'yes') { + fluentcrm_update_option('_fluentcrm_share_essential', $shareEssential); + } + + return $this->sendSuccess([ + 'message' => __('Installation has been completed', 'fluent-crm') + ]); + } + + public function handleFluentFormInstall() + { + if (!current_user_can('install_plugins')) { + return $this->sendError([ + 'message' => __('Sorry! you do not have permission to install plugin', 'fluent-crm') + ]); + } + $this->installFluentForm(); + return [ + 'ff_config' => [ + 'is_installed' => defined('FLUENTFORM'), + 'create_form_link' => admin_url('admin.php?page=fluent_forms#add=1') + ], + 'is_installed' => defined('FLUENTFORM'), + 'message' => __('Fluent Forms has been installed and activated', 'fluent-crm') + ]; + } + + public function handleFluentBoardsInstall() + { + if (!current_user_can('install_plugins')) { + return $this->sendError([ + 'message' => __('Sorry! you do not have permission to install plugin', 'fluent-crm') + ]); + } + $this->installFluentBoards(); + return [ + 'message' => __('Fluent Boards has been installed and activated', 'fluent-crm'), + 'is_installed' => defined('FLUENT_BOARDS'), + ]; + } + + public function handleFluentCommunityInstall() + { + if (!current_user_can('install_plugins')) { + return $this->sendError([ + 'message' => __('Sorry! you do not have permission to install plugin', 'fluent-crm') + ]); + } + $this->installFluentCommunity(); + return [ + 'message' => __('Fluent Community has been installed and activated', 'fluent-crm'), + 'is_installed' => defined('FLUENT_COMMUNITY_PLUGIN_VERSION'), + ]; + } + + public function handleFluentBookingInstall() + { + if (!current_user_can('install_plugins')) { + return $this->sendError([ + 'message' => __('Sorry! you do not have permission to install plugin', 'fluent-crm') + ]); + } + $this->installFluentBooking(); + return [ + 'message' => __('Fluent Booking has been installed and activated', 'fluent-crm'), + 'is_installed' => defined('FLUENT_BOOKING_VERSION'), + ]; + } + + public function handleFluentCartInstall() + { + if (!current_user_can('install_plugins')) { + return $this->sendError([ + 'message' => __('Sorry! you do not have permission to install plugin', 'fluent-crm') + ]); + } + $this->installFluentCart(); + return [ + 'message' => __('FluentCart has been installed and activated', 'fluent-crm'), + 'is_installed' => defined('FLUENTCART_VERSION'), + ]; + } + + public function handleFluentSmtpInstall() + { + if (!current_user_can('install_plugins')) { + return $this->sendError([ + 'message' => __('Sorry! you do not have permission to install plugin', 'fluent-crm') + ]); + } + + $this->installFluentSMTP(); + + return [ + 'is_installed' => defined('FLUENTMAIL'), + 'config_url' => admin_url('options-general.php?page=fluent-mail#/'), + 'message' => __('FluentSMTP plugin has been installed and activated successfully', 'fluent-crm') + ]; + + } + + + + public function handleFluentSupportInstall() + { + if (!current_user_can('install_plugins')) { + return $this->sendError([ + 'message' => __('Sorry! you do not have permission to install plugin', 'fluent-crm') + ]); + } + + $plugin_id = 'fluent-support'; + $plugin = [ + 'name' => __('Fluent Support', 'fluent-crm'), + 'repo-slug' => 'fluent-support', + 'file' => 'fluent-support.php', + ]; + $this->backgroundInstaller($plugin, $plugin_id); + + return [ + 'is_installed' => defined('FLUENT_SUPPORT_VERSION'), + 'message' => __('Fluent Support plugin has been installed and activated successfully', 'fluent-crm') + ]; + + } + + private function shareEmail($optinEmail) + { + $user = get_user_by('ID', get_current_user_id()); + $data = [ + 'answers' => [ + 'website' => site_url(), + 'email' => $optinEmail, + 'first_name' => $user->first_name, + 'last_name' => $user->last_name, + 'name' => $user->display_name + ], + 'questions' => [ + 'website' => 'website', + 'first_name' => 'first_name', + 'last_name' => 'last_name', + 'email' => 'email', + 'name' => 'name' + ], + 'user' => [ + 'email' => $optinEmail + ], + 'fb_capture' => 1, + 'form_id' => 54 + ]; + + $url = add_query_arg($data, 'https://wpmanageninja.com/'); + + wp_remote_post($url); + } + + private function installFluentForm() + { + $plugin_id = 'fluentform'; + $plugin = [ + 'name' => __('Fluent Forms', 'fluent-crm'), + 'repo-slug' => 'fluentform', + 'file' => 'fluentform.php', + ]; + $this->backgroundInstaller($plugin, $plugin_id); + } + + private function installFluentBoards() + { + $plugin_id = 'fluent-boards'; + $plugin = [ + 'name' => __('Fluent Boards', 'fluent-crm'), + 'repo-slug' => 'fluent-boards', + 'file' => 'fluent-boards.php', + ]; + $this->backgroundInstaller($plugin, $plugin_id); + } + private function installFluentCommunity() + { + $plugin_id = 'fluent-community'; + $plugin = [ + 'name' => __('Fluent Community', 'fluent-crm'), + 'repo-slug' => 'fluent-community', + 'file' => 'fluent-community.php', + ]; + $this->backgroundInstaller($plugin, $plugin_id); + } + + private function installFluentBooking() + { + $plugin_id = 'fluent-booking'; + $plugin = [ + 'name' => __('Fluent Booking', 'fluent-crm'), + 'repo-slug' => 'fluent-booking', + 'file' => 'fluent-booking.php', + ]; + $this->backgroundInstaller($plugin, $plugin_id); + } + + private function installFluentCart() + { + $plugin_id = 'fluent-cart'; + $plugin = [ + 'name' => __('FluentCart', 'fluent-crm'), + 'repo-slug' => 'fluent-cart', + 'file' => 'fluent-cart.php', + ]; + $this->backgroundInstaller($plugin, $plugin_id); + } + + private function installFluentSMTP() + { + $plugin_id = 'fluent-smtp'; + $plugin = [ + 'name' => __('FluentSMTP', 'fluent-crm'), + 'repo-slug' => 'fluent-smtp', + 'file' => 'fluent-smtp.php', + ]; + $this->backgroundInstaller($plugin, $plugin_id); + } + + private function backgroundInstaller($plugin_to_install, $plugin_id) + { + if (!empty($plugin_to_install['repo-slug'])) { + require_once ABSPATH . 'wp-admin/includes/file.php'; + require_once ABSPATH . 'wp-admin/includes/plugin-install.php'; + require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php'; + require_once ABSPATH . 'wp-admin/includes/plugin.php'; + + WP_Filesystem(); + + $skin = new \Automatic_Upgrader_Skin(); + $upgrader = new \WP_Upgrader($skin); + $installed_plugins = array_reduce(array_keys(\get_plugins()), array($this, 'associate_plugin_file'), array()); + $plugin_slug = $plugin_to_install['repo-slug']; + $plugin_file = isset($plugin_to_install['file']) ? $plugin_to_install['file'] : $plugin_slug . '.php'; + $installed = false; + $activate = false; + + // See if the plugin is installed already. + if (isset($installed_plugins[$plugin_file])) { + $installed = true; + $activate = !is_plugin_active($installed_plugins[$plugin_file]); + } + + // Install this thing! + if (!$installed) { + // Suppress feedback. + ob_start(); + + try { + $plugin_information = plugins_api( + 'plugin_information', + array( + 'slug' => $plugin_slug, + 'fields' => array( + 'short_description' => false, + 'sections' => false, + 'requires' => false, + 'rating' => false, + 'ratings' => false, + 'downloaded' => false, + 'last_updated' => false, + 'added' => false, + 'tags' => false, + 'homepage' => false, + 'donate_link' => false, + 'author_profile' => false, + 'author' => false, + ), + ) + ); + + if (is_wp_error($plugin_information)) { + throw new \Exception($plugin_information->get_error_message()); + } + + $package = $plugin_information->download_link; + $download = $upgrader->download_package($package); + + if (is_wp_error($download)) { + throw new \Exception($download->get_error_message()); + } + + $working_dir = $upgrader->unpack_package($download, true); + + if (is_wp_error($working_dir)) { + throw new \Exception($working_dir->get_error_message()); + } + + $result = $upgrader->install_package( + array( + 'source' => $working_dir, + 'destination' => WP_PLUGIN_DIR, + 'clear_destination' => false, + 'abort_if_destination_exists' => false, + 'clear_working' => true, + 'hook_extra' => array( + 'type' => 'plugin', + 'action' => 'install', + ), + ) + ); + + if (is_wp_error($result)) { + throw new \Exception($result->get_error_message()); + } + + $activate = true; + } catch (\Exception $e) { + } + + // Discard feedback. + ob_end_clean(); + } + + wp_clean_plugins_cache(); + + // Activate this thing. + if ($activate) { + try { + $result = activate_plugin($installed ? $installed_plugins[$plugin_file] : $plugin_slug . '/' . $plugin_file); + + if (is_wp_error($result)) { + throw new \Exception($result->get_error_message()); + } + } catch (\Exception $e) { + } + } + } + } + + private function associate_plugin_file($plugins, $key) + { + $path = explode('/', $key); + $filename = end($path); + $plugins[$filename] = $key; + return $plugins; + } +} diff --git a/wp-content/plugins/fluent-crm/app/Http/Controllers/SubscriberController.php b/wp-content/plugins/fluent-crm/app/Http/Controllers/SubscriberController.php new file mode 100644 index 0000000..95be56b --- /dev/null +++ b/wp-content/plugins/fluent-crm/app/Http/Controllers/SubscriberController.php @@ -0,0 +1,1912 @@ +request->get('filter_type', 'simple'); + + $with = ['tags', 'lists']; + + if (Helper::isCompanyEnabled()) { + $with[] = 'company'; + $with[] = 'companies'; + } + + if ($filterType == 'advanced') { + $queryArgs = [ + 'with' => $with, + 'filter_type' => 'advanced', + 'filters_groups_raw' => Helper::parseArrayOrJson($this->request->get('advanced_filters')), + 'search' => trim(sanitize_text_field($this->request->get('search', ''))), + 'sort_by' => sanitize_sql_orderby($this->request->get('sort_by', 'id')), + 'sort_type' => sanitize_sql_orderby($this->request->get('sort_type', 'DESC')), + 'has_commerce' => $this->request->get('has_commerce'), + 'custom_fields' => $this->request->get('custom_fields') == 'true', + 'company_ids' => $this->request->get('company_ids', []), + ]; + } else { + $queryArgs = [ + 'with' => $with, + 'filter_type' => 'simple', + 'search' => trim(sanitize_text_field($this->request->get('search', ''))), + 'sort_by' => sanitize_sql_orderby($this->request->get('sort_by', 'id')), + 'sort_type' => sanitize_sql_orderby($this->request->get('sort_type', 'DESC')), + 'has_commerce' => $this->request->get('has_commerce'), + 'custom_fields' => $this->request->get('custom_fields') == 'true', + 'tags' => $this->request->get('tags', []), + 'statuses' => $this->request->get('statuses', []), + 'sms_statuses' => $this->request->get('sms_statuses', []), + 'lists' => $this->request->get('lists', []), + 'company_ids' => $this->request->get('company_ids', []), + ]; + } + + $subscribers = (new ContactsQuery($queryArgs))->paginate(); + + return $this->sendSuccess([ + 'subscribers' => $subscribers, + 'custom' => $this->request->get('custom_fields') + ]); + } + + /** + * Find a subscriber by id + * + * @return \WP_REST_Response $object + */ + public function show() + { + $with = $this->request->get('with', []); + + $contactId = $this->request->get('id'); + + $defaultWith = ['tags', 'lists']; + + if (Helper::isCompanyEnabled()) { + $defaultWith[] = 'companies'; + } + + $subscriber = false; + if ($contactId) { + $subscriber = Subscriber::with($defaultWith)->find($contactId); + } else if ($byEmail = $this->request->get('get_by_email')) { + $subscriber = Subscriber::with($defaultWith)->where('email', sanitize_email($byEmail))->first(); + } + + if (!$subscriber) { + return $this->sendError([ + 'message' => __('Subscriber not found', 'fluent-crm') + ]); + } + + if (in_array('commerce_stat', $with)) { + $subscriber->commerce_stat = []; + /** + * Determine the commerce provider for FluentCRM. + * + * This filter allows you to modify the commerce provider used in FluentCRM. + * + * @param string The current commerce provider. + * @since 2.5.1 + * + */ + $commerceProvider = apply_filters('fluentcrm_commerce_provider', ''); + if ($commerceProvider) { + /** + * Determine the purchase statistics for a specific subscriber and commerce provider. + * + * This filter allows modification of the purchase statistics for a given subscriber + * based on the specified commerce provider. + * + * @param array The current purchase statistics for the subscriber. + * @param int $subscriber_id The ID of the subscriber. + * + * @return array Modified purchase statistics for the subscriber. + * @since 2.7.0 + * + */ + $subscriber->commerce_stat = apply_filters('fluent_crm/contact_purchase_stat_' . $commerceProvider, [], $subscriber->id); + } + } + + if ($wpUser = $subscriber->getWpUser()) { + $subscriber->user_edit_url = get_edit_user_link($wpUser->ID); + $subscriber->user_roles = array_values($wpUser->roles); + } + + if (in_array('stats', $with)) { + $subscriber->stats = $subscriber->stats(); + } + + if (in_array('subscriber.custom_values', $with)) { + $subscriber->custom_values = (object)$subscriber->custom_fields(); + } + + if ($subscriber->date_of_birth == '0000-00-00' || empty($subscriber->date_of_birth)) { + $subscriber->date_of_birth = ''; + } + + if ($subscriber->status == 'unsubscribed') { + $subscriber->unsubscribe_reason = $subscriber->unsubscribeReason(); + $subscriber->unsubscribe_date = $subscriber->unsubscribeReasonDate(); + } else if ($subscriber->status == 'bounced' || $subscriber->status == 'complained') { + $subscriber->unsubscribe_reason = $subscriber->unsubscribeReason('reason'); + $subscriber->unsubscribe_date = $subscriber->unsubscribeReasonDate('reason'); + } + + $data = [ + 'subscriber' => $subscriber + ]; + + if (in_array('custom_fields', $with)) { + $data['custom_fields'] = fluentcrm_get_option('contact_custom_fields', []); + } + + + return $this->sendSuccess($data); + } + + public function updateProperty() + { + $column = $this->request->getSafe('property', 'sanitize_text_field'); + $value = $this->request->getSafe('value', 'sanitize_text_field'); + + $subscriberIds = $this->request->get('subscribers'); + + if (!is_array($subscriberIds)) { + $subscriberIds = [$subscriberIds]; // say, this is single value, convert to array + } + + $subscriberIds = array_map('intval', $subscriberIds); + $subscriberIds = array_unique(array_filter($subscriberIds)); + + $validColumns = ['status', 'contact_type', 'avatar', 'company_id', 'sms_status']; + $subscriberStatuses = fluentcrm_subscriber_statuses(); + $leadStatuses = fluentcrm_contact_types(); + $smsStatuses = apply_filters('fluentcrm_sms_statuses', [ + 'sms_subscribed', + 'sms_unsubscribed', + 'sms_pending', + 'sms_bounced' + ]); + + $this->validate([ + 'column' => $column, + 'subscriber_ids' => $subscriberIds + ], [ + 'column' => 'required', + 'subscriber_ids' => 'required' + ]); + + if (!in_array($column, $validColumns)) { + return $this->sendError([ + 'message' => __('Column is not valid', 'fluent-crm') + ]); + } + + if ($column == 'status' && !in_array($value, $subscriberStatuses)) { + return $this->sendError([ + 'message' => __('Value is not valid', 'fluent-crm') + ]); + } else if ($column == 'contact_type' && !isset($leadStatuses[$value])) { + return $this->sendError([ + 'message' => __('Value is not valid', 'fluent-crm') + ]); + } else if ($column == 'company_id') { + Company::findOrFail($value); // just a check + } else if ($column == 'sms_status' && !in_array($value, $smsStatuses)) { + return $this->sendError([ + 'message' => __('Value is not valid', 'fluent-crm') + ]); + } + + $subscribers = Subscriber::whereIn('id', $subscriberIds)->get(); + + foreach ($subscribers as $subscriber) { + $oldValue = $subscriber->{$column}; + if ($oldValue != $value) { + $subscriber->{$column} = $value; + $subscriber->save(); + if (in_array($column, ['status', 'contact_type', 'sms_status'])) { + do_action('fluentcrm_subscriber_' . $column . '_to_' . $value, $subscriber, $oldValue); + + if ($column == 'status') { + /** + * Contact's Status has been changed + * + * @param Subscriber $subscriber Subscriber Model. + * @param string $oldStatus Old Status. + * @since 1.0 + * + */ + do_action('fluent_crm/subscriber_status_changed', $subscriber, $oldValue, $value); + } else if ($column == 'sms_status') { + /** + * Contact's SMS Status has been changed + * + * @param Subscriber $subscriber Subscriber Model. + * @param string $oldStatus Old SMS Status. + * @since 3.0.0 + * + */ + do_action('fluent_crm/subscriber_sms_status_changed', $subscriber, $oldValue, $value); + } + + } + if ($column == 'avatar') { + do_action('fluent_crm/subscriber_avatar_update', $subscriber, $oldValue); + } + } + } + + return $this->sendSuccess([ + 'message' => __('Subscribers successfully updated', 'fluent-crm') + ]); + } + + public function deleteSubscriber(Request $request, $id) + { + $subscriber = Subscriber::findOrFail($id); + + Helper::deleteContacts([$subscriber->id]); + + return $this->sendSuccess([ + 'message' => __('Selected Subscriber has been deleted successfully', 'fluent-crm') + ]); + } + + public function deleteSubscribers(Request $request) + { + $subscriberIds = $request->get('subscribers'); + + $this->validate( + ['subscriber_ids' => $subscriberIds], + ['subscriber_ids' => 'required'] + ); + + Helper::deleteContacts($subscriberIds); + + return $this->sendSuccess([ + 'message' => __('Selected Subscribers have been deleted', 'fluent-crm') + ]); + } + + /** + * Tag a subscriber with Tags or Lists. + */ + public function tagger() + { + $model = $this->resolveModel(); + + $subscribers = $this->request->get('subscribers'); + $attachments = $this->attachments($model, 'attach'); + $detachments = $this->attachments($model, 'detach'); + + $type = $this->request->get('type'); + + foreach ($subscribers as $subscriberId) { + $subscriber = Subscriber::find($subscriberId); + if ($attachments) { + if ($type == 'tags') { + $subscriber->attachTags($attachments); + } else { + $subscriber->attachLists($attachments); + } + } + + if ($detachments) { + if ($type == 'tags') { + $subscriber->detachTags($detachments); + } else { + $subscriber->detachLists($detachments); + } + } + } + + return $this->sendSuccess([ + 'message' => __('Successfully updated the ', 'fluent-crm') . _n('subscriber', 'subscribers', count($subscribers), 'fluent-crm') . '.', + 'subscribers' => Subscriber::with('tags', 'lists')->whereIn('id', $subscribers)->get() + ]); + } + + /** + * Store a subscriber. + * + * @return array + */ + public function store(Request $request) + { + $forceUpdate = $request->get('__force_update') == 'yes'; + + if (!$forceUpdate) { + $data = $this->validate($request->all(), [ + 'email' => 'required|email|unique:fc_subscribers', + 'status' => 'required' + ], [ + 'email.unique' => __('Provided email already assigned to another subscriber.', 'fluent-crm') + ]); + } else { + $data = $this->validate($request->all(), [ + 'email' => 'required|email', + 'status' => 'required' + ]); + } + + unset($data['__force_update']); + + $data = Sanitize::contact($data); + + $user = get_user_by('email', $data['email']); + + if ($user) { + $data['user_id'] = $user->ID; + } else { + $data['user_id'] = ''; + } + + if ($this->isNew()) { + $data['created_at'] = current_time('mysql'); + + $contact = FluentCrmApi('contacts')->createOrUpdate($data, false, false); + + /** + * new Contact has been created + * + * @param Subscriber $contact Subscriber Model. + * @param array $data Original raw subscriber. + * @since 3.30.2 + */ +// do_action('fluentcrm_contact_created', $contact); // @deprecated since 2.8.0. Use fluent_crm/contact_created instead +// do_action('fluent_crm/contact_created', $contact); + // no need these action hooks because those are already in the updateOrCreate method in Subscriber model + + $double_optin = filter_var($request->get('double_optin'), FILTER_VALIDATE_BOOLEAN); + + if ($double_optin) { + $contact->sendDoubleOptinEmail(); + } + + return [ + 'message' => __('Successfully added the subscriber.', 'fluent-crm'), + 'contact' => $contact, + 'action_type' => 'created' + ]; + + } else if ($forceUpdate) { + $contact = FluentCrmApi('contacts')->createOrUpdate($data, false, false); + + if ($contact && $contact->status == 'pending') { + $contact->sendDoubleOptinEmail(); + } + + return $this->sendSuccess([ + 'message' => __('Contact has been successfully updated.', 'fluent-crm'), + 'contact' => $contact, + 'action_type' => 'updated' + ]); + } + + return $this->sendError([ + 'message' => __('Sorry, contact already exists', 'fluent-crm') + ], 422); + } + + public function bulkAddUpdate(Request $request) + { + $contacts = Helper::parseArrayOrJson($request->get('contacts')); + $invalids = []; + $created = []; + $updated = []; + + $double_optin = filter_var($request->get('double_optin'), FILTER_VALIDATE_BOOLEAN); + $forceUpdate = filter_var($request->get('force_update'), FILTER_VALIDATE_BOOLEAN); + + foreach ($contacts as $contact) { + $contactData = Sanitize::contact($contact); + if (empty($contactData['email']) || !is_email($contactData['email'])) { + $invalids[] = $contactData; + continue; + } + + $contactData['tags'] = Arr::get($contact, 'tags', []); + $contactData['lists'] = Arr::get($contact, 'lists', []); + $createdContact = FluentCrmApi('contacts')->createOrUpdate($contactData, $forceUpdate, false); + + if (!$createdContact) { + $invalids[] = $contactData; + continue; + } + + if ($createdContact->status == 'pending' && $double_optin) { + $createdContact->sendDoubleOptinEmail(); + } + + if ($contact->wasRecentlyCreated) { + $created[] = [ + 'id' => $createdContact->id, + 'email' => $createdContact->email, + 'status' => $createdContact->status, + ]; + } else { + $updated[] = [ + 'id' => $createdContact->id, + 'email' => $createdContact->email, + 'status' => $createdContact->status, + ]; + } + } + + return [ + 'message' => __('Successfully added/updated the subscribers.', 'fluent-crm'), + 'created' => $created, + 'updated' => $updated, + 'invalids' => $invalids + ]; + } + + public function updateSubscriber(Request $request, $id) + { + $subscriber = Subscriber::findOrFail($id); + $originalData = Helper::parseArrayOrJson($request->get('subscriber')); + + if (!$originalData) { + $originalData = $request->all(); + } + + $data = []; + if (isset($originalData['email'])) { + $data = $this->validate($originalData, [ + 'email' => 'required|email|unique:fc_subscribers,email,' . $id, + ], [ + 'email.unique' => __('Provided email already assigned to another subscriber.', 'fluent-crm') + ]); + } else { + $data = $originalData; + } + + if (isset($data['email'])) { + // Maybe update user id + $user = get_user_by('email', $data['email']); + /** + * Determine whether to update the WordPress user email on change. + * + * This filter allows you to control whether the WordPress user email should be updated + * when there is a change in the FluentCRM subscriber email. + * + * @param bool Whether to update the WordPress user email on change. Default false. + * @since 2.9.25 + * + */ + if (!$user && apply_filters('fluentcrm_update_wp_user_email_on_change', false)) { + $user = get_user_by('ID', $data['user_id']); + } + + $data['user_id'] = $user ? $user->ID : NULL; + } + + if (!empty($data['user_id'])) { + $data['user_id'] = (int)$data['user_id']; + } + + if (isset($data['date_of_birth']) && empty($data['date_of_birth'])) { + $data['date_of_birth'] = NULL; + } + + $validData = Sanitize::contact($data); + + unset($validData['created_at']); + unset($validData['last_activity']); + $customValues = Arr::get($originalData, 'custom_values', []); + + $oldEmail = $subscriber->email; + + $oldSubscriber = clone $subscriber; + + $subscriber->fill($validData); + + $dirtyFields = $subscriber->getDirty(); + + if ($dirtyFields) { + $subscriber->save(); + } + + if ($customValues) { + $originalCustomFields = $subscriber->custom_fields(); + $subscriber->syncCustomFieldValues($customValues, true); + $dirtyCustomValues = $oldSubscriber->custom_fields(); + + do_action('fluent_crm/contact_updated_with_changes', $subscriber, $dirtyCustomValues, $originalCustomFields, ['source' => 'web', 'type' => 'custom_fields_only']); + } + + if ($tags = Arr::get($originalData, 'attach_tags', [])) { + $subscriber->attachTags($tags); + } + + if ($lists = Arr::get($originalData, 'attach_lists', [])) { + $subscriber->attachLists($lists); + } + + if ($detachTags = Arr::get($originalData, 'detach_tags', [])) { + $subscriber->detachTags($detachTags); + } + + if ($detachLists = Arr::get($originalData, 'detach_lists', [])) { + $subscriber->detachLists($detachLists); + } + + if ($dirtyFields) { + + if (isset($dirtyFields['email'])) { + /** + * Contact's Email address has been updated + * + * @param Subscriber $subscriber Subscriber Model. + * @param string $oldEmail Old Email Address. + * @since 1.0 + * + */ + do_action('fluent_crm/contact_email_changed', $subscriber, $oldEmail); + } + + do_action('fluentcrm_contact_updated', $subscriber, $dirtyFields); + do_action('fluent_crm/contact_updated', $subscriber, $dirtyFields); + + do_action('fluent_crm/contact_updated_with_changes', $subscriber, $dirtyFields, $oldSubscriber, ['source' => 'web', 'type' => 'all_fields']); + + } + + return $this->sendSuccess([ + 'message' => __('Subscriber successfully updated', 'fluent-crm'), + 'contact' => $subscriber, + 'isDirty' => !!$dirtyFields, + 'values' => $customValues + ], 200); + } + + /** + * Resolve the appropriate model e.g. Tag or, Lists + * + * @return string + */ + private function resolveModel() + { + $type = $this->request->type; + + return 'FluentCrm\App\Models\\' . ($type === 'tags' ? 'Tag' : 'Lists'); + } + + /** + * Get the attachment options e.g. attach or, detach + * + * @param \FluentCrm\App\Models\Model $model + * @param string $type + * @return array + */ + private function attachments($model, $type = 'attach') + { + $attachments = $this->request->get($type, []); + $findBy = sanitize_text_field($this->request->get('find_by', 'slug')); + + if ($attachments) { + $items = $model::select('id')->whereIn($findBy, $attachments)->get(); + + if (!$items->isEmpty()) { + return array_map(function ($item) { + return $item['id']; + }, $items->toArray()); + } + } + + return []; + } + + /** + * Handles if subscriber already exists. + * + * @return bool + */ + private function isNew() + { + $subscriber = Subscriber::where( + 'email', $this->request->getSafe('email', 'sanitize_email', '') + )->first(); + + if ($subscriber) { + return false; + } + + return true; + } + + public function emails(Request $request, $subscriberId) + { + $filter = sanitize_text_field(Arr::get($request->get(), 'filter')); + + $emailsQuery = CampaignEmail::where('subscriber_id', $subscriberId) + ->orderBy('id', 'DESC'); + + // Apply filter if present + if ($filter == 'open') { + $emailsQuery->where('is_open', '1'); + } elseif ($filter == 'click') { + $emailsQuery->whereNotNull('click_counter'); + } elseif ($filter == 'unopened') { + $emailsQuery->where('is_open', '==', 0); + } + + $emails = $emailsQuery->paginate(); + $tab = Arr::get($request->all(), 'tab', 'crm'); + + if (defined('FLUENTMAIL_PLUGIN_FILE') && $tab == 'fluentsmtp') { + $getLogsByCurrentUser = Subscriber::where('id', $subscriberId)->pluck('email')->first(); + + $emails = []; + + if (!empty($getLogsByCurrentUser)) { + $emails = fluentMailDb()->table(FLUENT_MAIL_DB_PREFIX . 'email_logs') + ->where('to', 'LIKE', '%' . $getLogsByCurrentUser . '%') + ->orderBy('id', 'DESC') + ->paginate(); + + // Format the email log results + $emails['data'] = $this->formatResult($emails['data']); + } + } + + /** + * Determine and retrieve emails for a subscriber. + * + * This filter allows modifying the list of emails associated with a subscriber. + * + * @param array { + * Array containing the email data. + * + * @type array $emails List of email records or paginated results. + * } + * @param int $subscriberId The ID of the subscriber. + * + * @return array Filtered email data for the subscriber. + * @since 1.0.0 + * + */ + return apply_filters('fluentcrm_contact_emails', [ + 'emails' => $emails + ], $subscriberId); + } + + protected function formatResult($result) + { + $result = is_array($result) ? $result : func_get_args(); + + foreach ($result as $key => $row) { + $result[$key] = array_map('maybe_unserialize', (array)$row); + $result[$key]['id'] = (int)$result[$key]['id']; + $result[$key]['retries'] = (int)$result[$key]['retries']; + $result[$key]['from'] = htmlspecialchars($result[$key]['from']); + $result[$key]['subject'] = wp_kses_post(wp_unslash($result[$key]['subject'])); + } + + return $result; + } + + public function deleteEmails(Request $request, $subscriberId) + { + $emailIds = array_map('intval', $request->get('email_ids')); + CampaignEmail::where('subscriber_id', $subscriberId) + ->whereIn('id', $emailIds) + ->delete(); + return [ + 'message' => __('Selected emails have been deleted', 'fluent-crm') + ]; + } + + public function getNotes() + { + $subscriberId = $this->request->get('id'); + $search = $this->request->get('search'); + $includeId = intval($this->request->get('include_id', 0)); + + $notes = SubscriberNote::where('subscriber_id', $subscriberId); + + if (!empty($search)) { + global $wpdb; + $notes = $notes->where('title', 'LIKE', '%' . $wpdb->esc_like(sanitize_text_field($search)) . '%'); + } + + $notes = $notes->orderBy('id', 'DESC') + ->paginate(); + + foreach ($notes as $note) { + $note->added_by = $note->createdBy(); + } + $fields['fields'] = Helper::getNoteSyncFields(); + + $response = [ + 'notes' => $notes, + 'fields' => $fields + ]; + + if ($includeId) { + $noteIds = (new Collection($notes->items()))->pluck('id')->toArray(); + if (!in_array($includeId, $noteIds)) { + $includedNote = SubscriberNote::where('id', $includeId) + ->where('subscriber_id', $subscriberId) + ->first(); + if ($includedNote) { + $includedNote->added_by = $includedNote->createdBy(); + $response['included_note'] = $includedNote; + } + } + } + + return $this->sendSuccess($response); + } + + public function addNote(Request $request, $id) + { + $subscriber = Subscriber::findOrFail($id); + + $note = $this->validate($request->get('note'), [ + 'title' => 'required', + 'description' => 'required', + 'type' => 'required', + 'created_at' => 'nullable|date' + ]); + + if (empty($note['created_at'])) { + $note['created_at'] = current_time('mysql'); + } + + /** + * Parse the Subscriber's Note Description. + * + * @param string $note ['description'] The subscriber's note description. + * @param object $subscriber The subscriber object. + * @since 2.8.44 + * + */ + $note['description'] = apply_filters('fluent_crm/parse_campaign_email_text', $note['description'], $subscriber); + + $note['subscriber_id'] = $id; + + $note = Sanitize::contactNote($note); + + $subscriberNote = SubscriberNote::create(wp_unslash($note)); + + /** + * Subscriber's Note Added + * + * @param SubscriberNote $subscriberNote Note Model. + * @param Subscriber $subscriber Contact Model. + * @param array $note Contact Note Data Array. + * @since 1.0 + */ + do_action('fluent_crm/note_added', $subscriberNote, $subscriber, $note); + + return $this->sendSuccess([ + 'note' => $subscriberNote, + 'message' => __('Note successfully added', 'fluent-crm') + ]); + } + + public function updateNote(Request $request, $id, $noteId) + { + $subscriber = Subscriber::findOrFail($id); + + $note = $this->validate($request->get('note'), [ + 'title' => 'required', + 'description' => 'required', + 'type' => 'required', + 'created_at' => 'sometimes|date' + ]); + + $note = Arr::only(wp_unslash($note), ['title', 'description', 'type', 'created_at']); + + if (empty($note['created_at'])) { + unset($note['created_at']); + } + + /** + * Parse the campaign email text for Subscriber's Note Description. + * + * This filter allows you to modify the campaign email text before it is processed for a Subscriber's Note Description. + * + * @param string $note ['description'] Subscriber's Note Description from parsed campaign email text. + * @param object $subscriber The subscriber object data. + * @since 2.8.44 + * + */ + $note['description'] = apply_filters('fluent_crm/parse_campaign_email_text', $note['description'], $subscriber); + + $note = Sanitize::contactNote($note); + + $subsciberNote = SubscriberNote::find($noteId); + $subsciberNote->fill($note); + $subsciberNote->save(); + + /** + * Subscriber's Note Updated + * + * @param SubscriberNote $subscriberNote Note Model. + * @param Subscriber $subscriber Contact Model. + * @param array $note Contact Note Data Array. + * @since 1.0 + */ + do_action('fluent_crm/note_updated', $subsciberNote, $subscriber, $note); + + return $this->sendSuccess([ + 'note' => $subsciberNote, + 'message' => __('Note successfully updated', 'fluent-crm') + ]); + } + + public function deleteNote($id, $noteId) + { + $subscriber = Subscriber::findOrFail($id); + SubscriberNote::where('id', $noteId)->delete(); + + /** + * Subscriber's Note Delete + * + * @param SubscriberNote $subscriberNote Note Model. + * @param Subscriber $subscriber Contact Model. + * @since 1.0 + */ + do_action('fluent_crm/note_delete', $noteId, $subscriber); + + return $this->sendSuccess([ + 'message' => __('Note successfully deleted', 'fluent-crm') + ]); + } + + public function bulkDeleteNotes(Request $request, $id) + { + $subscriber = Subscriber::findOrFail($id); + $noteIds = array_filter(array_map('intval', (array) $request->get('note_ids', []))); + + if (empty($noteIds)) { + return $this->sendError([ + 'message' => __('No note IDs provided', 'fluent-crm') + ]); + } + + if (count($noteIds) > 200) { + return $this->sendError([ + 'message' => __('Too many notes selected. Please delete 200 or fewer notes at a time.', 'fluent-crm') + ]); + } + + // Scope delete to this subscriber so users cannot delete notes belonging to other contacts. + $deletableNoteIds = SubscriberNote::where('subscriber_id', $subscriber->id) + ->whereIn('id', $noteIds) + ->pluck('id') + ->toArray(); + + $deletedCount = 0; + if ($deletableNoteIds) { + $deletedCount = SubscriberNote::whereIn('id', $deletableNoteIds)->delete(); + + foreach ($deletableNoteIds as $deletedNoteId) { + do_action('fluent_crm/note_delete', $deletedNoteId, $subscriber); + } + } + + return $this->sendSuccess([ + 'message' => sprintf( + /* translators: %d: number of deleted notes */ + _n('%d note deleted', '%d notes deleted', $deletedCount, 'fluent-crm'), + $deletedCount + ) + ]); + } + + public function getFormSubmissions() + { + $provider = $this->request->get('provider'); + $subscriberId = intval($this->request->get('id')); + $subscriber = Subscriber::findOrFail($subscriberId); + + /** + * Filter the form submissions data for a specific provider. + * + * The dynamic portion of the hook name, `$provider`, refers to the form provider. + * + * @param array { + * An array of form submissions data. + * + * @type array $data The form submissions data. + * @type int $total The total number of form submissions. + * } + * @param object $subscriber The subscriber object. + * @since 2.5.1 + * + */ + $data = apply_filters('fluentcrm_get_form_submissions_' . $provider, [ + 'data' => [], + 'total' => 0 + ], $subscriber); + + return $this->sendSuccess([ + 'submissions' => $data + ]); + } + + public function getSupportTickets() + { + $provider = $this->request->get('provider'); + $subscriberId = intval($this->request->get('id')); + $subscriber = Subscriber::where('id', $subscriberId)->first(); + + /** + * Determine the support tickets data for a specific provider and subscriber. + * + * The dynamic portion of the hook name, `$provider`, refers to the support ticket provider. + * + * @param array { + * An array of support tickets data. + * + * @type array $data The support tickets data. + * @type int $total The total number of support tickets. + * } + * @param object $subscriber The subscriber object. + * @since 2.5.1 + * + */ + $data = apply_filters('fluentcrm-get_support_tickets_' . $provider, [ + 'data' => [], + 'total' => 0 + ], $subscriber); + + $data['columns_config'] = [ + 'id' => [ + 'label' => __('ID', 'fluent-crm'), + 'width' => '100px' + ], + 'status' => [ + 'label' => 'Status', + 'width' => '120px' + ], + 'Submitted at' => [ + 'label' => 'Submitted at', + 'width' => '150px' + ], + 'action' => [ + 'label' => 'Action', + 'width' => '150px' + ] + ]; + + return $this->sendSuccess([ + 'tickets' => $data + ]); + } + + public function sendDoubleOptinEmail(Request $request, $id) + { + $subscriber = Subscriber::findOrFail($id); + + if ($subscriber->status == 'subscribed') { + return $this->sendError([ + 'message' => __('Contact Already Subscribed', 'fluent-crm') + ]); + } + + $subscriber->sendDoubleOptinEmail(); + + return $this->sendSuccess([ + 'message' => __('Double OptIn email has been sent', 'fluent-crm') + ]); + } + + public function getTemplateMock(Request $request, $id) + { + $emailMock = CustomEmailCampaign::getMock(); + $emailMock['title'] = __('Custom Email to Contact', 'fluent-crm'); + return [ + 'email_mock' => $emailMock + ]; + } + + public function sendCustomEmail(Request $request, $contactId) + { + $contact = Subscriber::findOrFail($contactId); + + $validStatuses = ['subscribed', 'transactional']; + + if (!in_array($contact->status, $validStatuses)) { + return $this->sendError([ + 'message' => __('Subscriber\'s status need to be subscribed.', 'fluent-crm') + ]); + } + + add_action('wp_mail_failed', function ($wpError) { + Helper::debugLog( + 'Custom Email failed', + $wpError->get_error_message(), + 'error' + ); + }, 10, 1); + + $newCampaign = $request->get('campaign'); + unset($newCampaign['id']); + + $newCampaign = Sanitize::campaign($newCampaign); + + $campaign = CustomEmailCampaign::create($newCampaign); + + $campaign->subscribe([$contactId], [ + 'status' => 'scheduled', + 'scheduled_at' => current_time('mysql') + ]); + + do_action('fluentcrm_process_contact_jobs', $contact); + + return [ + 'message' => __('Custom Email has been successfully sent', 'fluent-crm') + ]; + } + + public function getExternalView(Request $request, $subscriberId) + { + $subscriber = Subscriber::findOrFail($subscriberId); + $sectionId = $request->get('section_provider'); + + /** + * Filter the profile section content for a specific section ID. + * + * The dynamic portion of the hook name, `$sectionId`, refers to the ID of the profile section. + * + * @param array { + * An array of profile section data. + * + * @type string $heading The heading of the profile section. + * @type string $content_html The HTML content of the profile section. + * } + * @param object $subscriber The subscriber object. + * @since 2.5.1 + * + */ + return apply_filters('fluencrm_profile_section_' . $sectionId, [ + 'heading' => '', + 'content_html' => '' + ], $subscriber); + } + + public function saveExternalViewData(Request $request, $subscriberId) + { + $subscriber = Subscriber::findOrFail($subscriberId); + $sectionId = $request->get('section_provider'); + + /** + * Filter the data being saved for a specific profile section. + * + * This filter allows modifying the data before saving it for a profile section + * identified by the `$sectionId`. + * + * @param mixed The data to be saved for the profile section. Defaults to an empty string. + * @param array The input data received from the request. Defaults to an empty array. + * @param object $subscriber The subscriber object for which the profile section is being updated. + * + * @return mixed Filtered data to be saved for the profile section. + * @since 2.8.44 + * + */ + $response = apply_filters('fluencrm_profile_section_save_' . $sectionId, '', $request->get('data', []), $subscriber); + + if (!$response) { + return $this->sendError([ + 'message' => __('Handled could not be found.', 'fluent-crm') + ]); + } + + return $response; + } + + public function handleBulkActions(Request $request) + { + $actionName = sanitize_text_field($request->get('action_name', '')); + $doingAllBulk = $request->get('is_all') == 'yes'; + + if ($doingAllBulk) { + + $contactQuery = $request->get('contact_query', []); + + $filterType = Arr::get($contactQuery, 'filter_type', 'simple'); + + $with = []; + + if ($filterType == 'advanced') { + + $rawGroup = json_decode(Arr::get($contactQuery, 'advanced_filters', ''), true); + + if (!$rawGroup || !is_array($rawGroup)) { + return $this->sendError([ + 'message' => __('Invalid Advanced Filters', 'fluent-crm') + ]); + } + + $queryArgs = [ + 'with' => $with, + 'filter_type' => 'advanced', + 'filters_groups_raw' => $rawGroup, + 'search' => trim(Arr::get($contactQuery, 'search', '')), + 'sort_by' => 'id', + 'sort_type' => 'ASC', + 'has_commerce' => false, + 'custom_fields' => false, + 'company_ids' => Arr::get($contactQuery, 'company_ids', []), + ]; + } else { + $queryArgs = [ + 'with' => $with, + 'filter_type' => 'simple', + 'search' => trim(Arr::get($contactQuery, 'search', '')), + 'sort_by' => 'id', + 'sort_type' => 'ASC', + 'has_commerce' => false, + 'custom_fields' => false, + 'tags' => Arr::get($contactQuery, 'tags', []), + 'statuses' => Arr::get($contactQuery, 'statuses', []), + 'lists' => Arr::get($contactQuery, 'lists', []), + 'company_ids' => Arr::get($contactQuery, 'company_ids', []), + ]; + } + + $subscribersModel = (new ContactsQuery($queryArgs))->getModel(); + $lastId = $request->get('last_id', 0); + $bulkActionLimit = (int)apply_filters('fluent_crm/contact_bulk_action_limit', 400, $request); + if ($bulkActionLimit < 1) { + $bulkActionLimit = 1; + } + + $subscribersModel = $subscribersModel->select(['id']) + ->limit($bulkActionLimit) + ->where('id', '>', $lastId) + ->get(); + + if ($subscribersModel->isEmpty()) { + return [ + 'is_completed' => true, + 'completed_contacts' => 0, + 'message' => __('All contacts have been processed', 'fluent-crm') + ]; + } + + $subscriberIds = $subscribersModel->pluck('id')->toArray(); + + } else { + $subscriberIds = array_map('intval', $request->get('subscriber_ids', [])); + $subscriberIds = array_filter($subscriberIds); + } + + $lastContactId = end($subscriberIds); + + if (!$subscriberIds) { + return $this->sendError([ + 'message' => __('Subscribers selection is required', 'fluent-crm') + ]); + } + + if ($actionName == 'delete_contacts') { + Helper::deleteContacts($subscriberIds); + return $this->sendSuccess([ + 'completed_contacts' => count($subscriberIds), + 'last_contact_id' => $lastContactId, + 'message' => __('Selected Contacts have been deleted permanently', 'fluent-crm'), + ]); + } elseif ($actionName == 'send_double_optin') { + Helper::sendDoubleOptin($subscriberIds); + return $this->sendSuccess([ + 'last_contact_id' => $lastContactId, + 'completed_contacts' => count($subscriberIds), + 'message' => __('Double optin sent to selected contacts', 'fluent-crm'), + ]); + } elseif ($actionName == 'add_to_email_sequence') { + if (!defined('FLUENTCAMPAIGN')) { + return $this->sendError([ + 'message' => __('This action requires FluentCRM Pro', 'fluent-crm') + ]); + } + + $sequenceId = (int)$request->get('new_status', ''); + + if (!$sequenceId) { + return $this->sendError([ + 'message' => __('Invalid Email Sequence ID', 'fluent-crm') + ]); + } + + $sequence = \FluentCampaign\App\Models\Sequence::findOrFail($sequenceId); + + $validSubscribers = Subscriber::whereIn('id', $subscriberIds) + ->whereDoesntHave('sequences', function ($q) use ($sequenceId) { + $q->where('fc_campaigns.id', $sequenceId); + }) + ->where('status', 'subscribed') + ->get(); + + if ($validSubscribers->isEmpty()) { + if ($doingAllBulk) { + return $this->sendSuccess([ + 'last_contact_id' => $lastContactId, + 'completed_contacts' => count($subscriberIds), + 'message' => __('No valid active subscribers found for this chunk', 'fluent-crm') + ]); + } + return $this->sendError([ + 'message' => __('No valid active subscribers found for this sequence', 'fluent-crm') + ]); + } + + $sequence->subscribe($validSubscribers); + return [ + 'last_contact_id' => $lastContactId, + 'completed_contacts' => count($subscriberIds), + /* translators: 1. Number of subscribers */ + 'message' => sprintf(__('%d subscribers have been attached to the selected email sequence', 'fluent-crm'), count($validSubscribers)) + ]; + + } elseif ($actionName == 'add_to_company') { + $companyId = (int)$request->get('new_status', ''); + + if (!$companyId) { + return $this->sendError([ + 'message' => __('Invalid Company ID', 'fluent-crm') + ]); + } + + $company = Company::findOrFail($companyId); + + $validSubscribers = Subscriber::whereIn('id', $subscriberIds) + ->whereDoesntHave('companies', function ($q) use ($companyId) { + $q->where('fc_companies.id', $companyId); + }) + ->get(); + + if ($validSubscribers->isEmpty()) { + + if ($doingAllBulk) { + return $this->sendSuccess([ + 'last_contact_id' => $lastContactId, + 'completed_contacts' => count($subscriberIds), + 'message' => __('No valid active subscribers found for this chunk', 'fluent-crm') + ]); + } + + return $this->sendError([ + 'message' => __('No valid active subscribers found for this company', 'fluent-crm') + ]); + } + + foreach ($validSubscribers as $contact) { + if ($contact) { + $contact->attachCompanies([$company->id]); + if (!$contact->company_id) { + $contact->company_id = $company->id; + $contact->save(); + } + } + } + return [ + 'last_contact_id' => $lastContactId, + 'completed_contacts' => count($subscriberIds), + /* translators: %d is the number of subscribers */ + 'message' => sprintf(__('%d subscribers have been attached to the selected company', 'fluent-crm'), count($validSubscribers)) + ]; + + } elseif ($actionName == 'remove_from_company') { + $companyId = (int)$request->get('new_status', ''); + + if (!$companyId) { + return $this->sendError([ + 'message' => __('Invalid Company ID', 'fluent-crm') + ]); + } + + $company = Company::findOrFail($companyId); + + $validSubscribers = Subscriber::whereIn('id', $subscriberIds) + ->whereHas('companies', function ($q) use ($companyId) { + $q->where('fc_companies.id', $companyId); + }) + ->get(); + + if ($validSubscribers->isEmpty()) { + + if ($doingAllBulk) { + return $this->sendSuccess([ + 'last_contact_id' => $lastContactId, + 'completed_contacts' => count($subscriberIds), + 'message' => __('No valid active subscribers found for this chunk', 'fluent-crm') + ]); + } + + return $this->sendError([ + 'message' => __('No valid active subscribers found for this company', 'fluent-crm') + ]); + } + + foreach ($validSubscribers as $contact) { + if ($contact) { + $contact->detachCompanies([$company->id]); + if ($contact->company_id == $company->id) { + $contact->company_id = null; + $contact->save(); + } + } + } + return [ + 'last_contact_id' => $lastContactId, + 'completed_contacts' => count($subscriberIds), + /* translators: %d is the number of subscribers */ + 'message' => sprintf(__('%d subscribers have been detached from the selected company', 'fluent-crm'), count($validSubscribers)) + ]; + + } elseif ($actionName == 'change_contact_status') { + $newStatus = sanitize_text_field($request->get('new_status', '')); + if (!$newStatus) { + return $this->sendError([ + 'message' => __('Please select status', 'fluent-crm') + ]); + } + + $subscribers = Subscriber::whereIn('id', $subscriberIds)->get(); + + foreach ($subscribers as $subscriber) { + $oldStatus = $subscriber->status; + if ($oldStatus != $newStatus) { + $subscriber->updateStatus($newStatus); + } + } + + return [ + 'last_contact_id' => $lastContactId, + 'completed_contacts' => count($subscriberIds), + 'message' => __('Status has been changed for the selected subscribers', 'fluent-crm') + ]; + } elseif ($actionName == 'add_to_automation') { + if (!defined('FLUENTCAMPAIGN')) { + return $this->sendError([ + 'message' => __('This action requires FluentCRM Pro', 'fluent-crm') + ]); + } + + $automationId = (int)$request->get('new_status', ''); + + if (!$automationId) { + return $this->sendError([ + 'message' => __('Invalid Automation Funnel ID', 'fluent-crm') + ]); + } + + $automation = Funnel::findOrFail($automationId); + + $validSubscribers = Subscriber::whereIn('id', $subscriberIds) + ->whereDoesntHave('funnels', function ($q) use ($automationId) { + $q->where('fc_funnels.id', $automationId); + }) + ->get(); + + if ($validSubscribers->isEmpty()) { + + if ($doingAllBulk) { + return $this->sendSuccess([ + 'last_contact_id' => $lastContactId, + 'completed_contacts' => count($subscriberIds), + 'message' => __('No valid active subscribers found for this chunk', 'fluent-crm') + ]); + } + + return $this->sendError([ + 'message' => __('No valid active subscribers found for this funnel', 'fluent-crm') + ]); + } + + foreach ($validSubscribers as $subscriber) { + (new FunnelProcessor())->startFunnelSequence($automation, [], [ + 'source_trigger_name' => 'fcrm_manual_attach' + ], $subscriber); + } + + return [ + 'last_contact_id' => $lastContactId, + 'completed_contacts' => count($subscriberIds), + /* translators: %d is the number of subscribers */ + 'message' => sprintf(__('%d subscribers have been attached to the selected automation funnel', 'fluent-crm'), count($validSubscribers)), + 'subscribers' => $validSubscribers + ]; + } else if ($actionName == 'change_contact_type') { + $subscribers = Subscriber::whereIn('id', $subscriberIds)->get(); + $newType = sanitize_text_field($request->get('new_status', '')); + if (!$newType) { + return $this->sendError([ + 'message' => __('Please select new type', 'fluent-crm') + ]); + } + foreach ($subscribers as $subscriber) { + $oldType = $subscriber->contact_type; + if ($oldType != $newType) { + $subscriber->contact_type = $newType; + $subscriber->save(); + do_action('fluent_crm/subscriber_contact_type_to_' . $newType, $subscriber, $oldType); + } + } + + return [ + 'last_contact_id' => $lastContactId, + 'completed_contacts' => count($subscriberIds), + 'message' => __('Contact Type has been updated for the selected subscribers', 'fluent-crm') + ]; + } else if ($actionName == 'update_custom_fields') { + $customField = $request->get('custom_field'); + $customFieldKey = Arr::get($customField, 'key'); + $customFieldValue = Arr::get($customField, 'value'); + $subscribers = Subscriber::whereIn('id', $subscriberIds)->get(); + + if (empty($customFieldKey)) { + return $this->sendError([ + 'message' => __('Please provide a valid custom field key', 'fluent-crm') + ]); + } + + foreach ($subscribers as $subscriber) { + $existField = SubscriberMeta::where('key', $customFieldKey) + ->where('subscriber_id', $subscriber->id) + ->first(); + + // check if exists + if ($existField) { + if ($existField->value == $customFieldValue) { + continue; + } + $existField->fill(['value' => $customFieldValue])->save(); + } else { + $customFieldMeta = new SubscriberMeta(); + $customFieldMeta->fill([ + 'subscriber_id' => $subscriber->id, + 'object_type' => 'custom_field', + 'key' => $customFieldKey, + 'value' => $customFieldValue, + 'created_by' => get_current_user_id() + ]); + $customFieldMeta->save(); + } + } + + return [ + 'last_contact_id' => $lastContactId, + 'completed_contacts' => count($subscriberIds), + 'message' => __('Custom Fields has been updated for the selected subscribers', 'fluent-crm') + ]; + } + + $validActions = [ + 'add_to_tags' => 'attachTags', + 'add_to_lists' => 'attachLists', + 'remove_from_tags' => 'detachTags', + 'remove_from_lists' => 'detachLists' + ]; + + if (!isset($validActions[$actionName])) { + $response = $this->sendError([ + 'message' => __('Selected Action is not valid', 'fluent-crm') + ]); + + /** + * Filter the result of a bulk action performed on FluentCRM contacts. + * + * The dynamic portion of the hook name, `$actionName`, refers to the specific bulk action being performed. + * + * @param mixed $response The initial response for the bulk action. Can be modified by the filter. + * @param array $subscriberIds An array of subscriber IDs targeted by the bulk action. + * @param array $request ->all() The full request data as an associative array. + * + * @return mixed Filtered response for the bulk action. + * @since 2.9.0 + * + */ + $result = apply_filters('fluent_crm/contact_bulk_action_' . $actionName, $response, $subscriberIds, $request->all()); + + if (is_array($result)) { + $result['last_contact_id'] = $lastContactId; + $result['completed_contacts'] = count($subscriberIds); + } + + return $result; + } + + $options = $request->get('action_options', []); + + $options = array_map(function ($id) { + return intval($id); + }, $options); + + $options = array_filter($options); + + if (!$options) { + return $this->sendError([ + 'message' => __('Please provide bulk options', 'fluent-crm') + ]); + } + + $method = $validActions[$actionName]; + + $subscribers = Subscriber::whereIn('id', $subscriberIds)->get(); + + foreach ($subscribers as $subscriber) { + $subscriber->{$method}($options); + } + + return [ + 'last_contact_id' => $lastContactId, + 'completed_contacts' => count($subscriberIds), + 'message' => __('Selected bulk action has been successfully completed', 'fluent-crm') + ]; + } + + public function getPrevNextIds(Request $request) + { + $filterType = $this->request->get('filter_type'); + + $currentId = (int)$this->request->get('current_id'); + + if (!$filterType || !$currentId) { + return $this->sendError([ + 'message' => __('filter_type and current_id are required', 'fluent-crm') + ]); + } + + $sortType = sanitize_sql_orderby($this->request->get('sort_type', 'DESC')); + $prevSortType = ($sortType == 'DESC') ? 'ASC' : 'DESC'; + + if ($filterType == 'advanced') { + $queryArgs = [ + 'filter_type' => 'advanced', + 'filters_groups_raw' => Helper::parseArrayOrJson($this->request->get('advanced_filters')), + 'search' => trim(sanitize_text_field($this->request->get('search', ''))), + 'sort_by' => 'id', + 'sort_type' => $sortType, + 'with' => [] + ]; + } else { + $queryArgs = [ + 'filter_type' => 'simple', + 'search' => trim(sanitize_text_field($this->request->get('search', ''))), + 'sort_by' => 'id', + 'sort_type' => $sortType, + 'tags' => $this->request->get('tags', []), + 'statuses' => $this->request->get('statuses', []), + 'lists' => $this->request->get('lists', []), + 'with' => [] + ]; + } + + $prevQueryArgs = $queryArgs; + + $prevQueryArgs['sort_type'] = ($sortType == 'DESC') ? 'ASC' : 'DESC'; + + $prevItems = (new ContactsQuery($prevQueryArgs)) + ->getModel() + ->select(['id']) + ->limit(10) + ->where('id', ($sortType == 'DESC') ? '>' : '<', $currentId) + ->get(); + + $nextItems = (new ContactsQuery($queryArgs)) + ->getModel() + ->select(['id']) + ->limit(10) + ->where('id', ($sortType == 'DESC') ? '<' : '>', $currentId) + ->get(); + + $formattedNext = []; + foreach ($nextItems as $nextItem) { + $formattedNext[] = $nextItem->id; + } + + $formattedPrev = []; + foreach ($prevItems as $prevItem) { + $formattedPrev[] = $prevItem->id; + } + + return [ + 'navigation' => [ + 'next' => $formattedNext, + 'prev' => $formattedPrev + ], + 'has_next' => count($formattedNext) == 10, + 'has_prev' => count($formattedPrev) == 10 + ]; + + } + + public function searchContacts(Request $request) + { + $search = trim($request->getSafe('search', 'sanitize_text_field', '')); + $limit = absint($request->get('limit', 20)); + if (!$limit) { + $limit = 20; + } + $offset = absint($request->get('offset', 0)); + + $loadDefault = $request->get('load_default'); + $loadDefault = in_array($loadDefault, [true, 1, '1', 'true', 'yes'], true); + + $contacts = []; + + if ($search) { + $subscribers = Subscriber::searchBy($search)->offset($offset)->limit($limit)->get(); + foreach ($subscribers as $subscriber) { + $contacts[$subscriber->id] = [ + 'first_name' => $subscriber->first_name, + 'last_name' => $subscriber->last_name, + 'full_name' => $subscriber->full_name, + 'email' => $subscriber->email, + 'id' => (string)$subscriber->id, + 'photo' => $subscriber->photo + ]; + } + } else if ($loadDefault) { + $subscribers = Subscriber::orderBy('id', 'DESC')->offset($offset)->limit($limit)->get(); + foreach ($subscribers as $subscriber) { + $contacts[$subscriber->id] = [ + 'first_name' => $subscriber->first_name, + 'last_name' => $subscriber->last_name, + 'full_name' => $subscriber->full_name, + 'email' => $subscriber->email, + 'id' => (string)$subscriber->id, + 'photo' => $subscriber->photo + ]; + } + } + + $values = (array)$request->get('values', []); + + if ($values) { + $pushedIds = array_keys($contacts); + $includedIds = array_diff($values, $pushedIds); + if ($includedIds) { + $subscribers = Subscriber::whereIn('id', $includedIds)->get(); + foreach ($subscribers as $subscriber) { + $contacts[$subscriber->id] = [ + 'first_name' => $subscriber->first_name, + 'last_name' => $subscriber->last_name, + 'full_name' => $subscriber->full_name, + 'email' => $subscriber->email, + 'id' => (string)$subscriber->id, + 'photo' => $subscriber->photo + ]; + } + } + } + + return [ + 'contacts' => (object)$contacts + ]; + } + + public function getInfoWidgets(Request $request, $subscriber) + { + if (is_numeric($subscriber)) { + $subscriber = Subscriber::findOrFail($subscriber); + } + + + if ($byWidget = $request->get('by_widget')) { + /** + * Filter the subscriber info widget. + * + * This filter allows modification of the subscriber info widget based on the widget type. + * + * @param array The array of widgets. + * @param object $subscriber The subscriber object data. + * @since 2.8.40 + * + */ + $widgets = apply_filters('fluent_crm/subscriber_info_widget_' . $byWidget, [], $subscriber); + $widgets = array_values($widgets); + + if (isset($widgets[0])) { + $widget = $widgets[0]; + } else { + $widget = [ + 'content' => 'No content found' + ]; + } + + return [ + 'widget' => $widget + ]; + } + + $commerce = (new PurchaseHistory())->getCommerceStatWidget($subscriber); + + /** + * Filter the top widgets for a subscriber. + * + * This filter allows modification of the top widgets displayed for a subscriber. + * + * @param array The array of top widgets. + * @param object $subscriber The subscriber object. + * @since 2.8.0 + * + */ + $topWidgets = array_filter(apply_filters('fluent_crm/subscriber_top_widgets', array_filter([$commerce]), $subscriber)); + + /** + * Filter the subscriber info widgets. + * + * This filter allows modification of the subscriber info widgets. + * + * @param array An array of existing widgets. + * @param object $subscriber The subscriber object. + * @since 2.8.0 + * + */ + $otherWidgets = apply_filters('fluent_crm/subscriber_info_widgets', [], $subscriber); + + return [ + 'widgets' => [ + 'top_widgets' => $topWidgets, + 'other_widgets' => $otherWidgets, + 'widgets_count' => count($topWidgets) + count($otherWidgets) + ] + ]; + } + + public function getTrackingEvents(Request $request, $subscriberId) + { + if (!Helper::isExperimentalEnabled('event_tracking')) { + return $this->sendError([ + 'message' => __('Event Tracker is not enabled', 'fluent-crm'), + 'error_code' => 'not_enabled' + ]); + } + + $subscriber = Subscriber::findOrFail($subscriberId); + $events = EventTracker::where('subscriber_id', $subscriber->id) + ->orderBy('id', 'DESC') + ->paginate(); + + return [ + 'events' => $events + ]; + } + + public function trackEvent(Request $request) + { + $data = $request->all(); + + $this->validate($data, [ + 'event_key' => 'required', + 'title' => 'required' + ]); + + $isUnique = $request->get('repeatable', true); + $result = FluentCrmApi('event_tracker')->track($data, $isUnique); + + if (is_wp_error($result)) { + return $this->sendError([ + 'message' => $result->get_error_message(), + 'error_code' => $result->get_error_code() + ]); + } + + return $this->sendSuccess([ + 'message' => __('Event has been tracked', 'fluent-crm'), + 'id' => $result->id + ]); + } + + public function getUrlMetrics(Request $request, $id) + { + $sort_by = sanitize_sql_orderby($this->request->get('sort_by', 'id')); + $sort_type = sanitize_sql_orderby($this->request->get('sort_type', 'DESC')); + $subscriber = Subscriber::findOrFail($id); + + $urlActivityQuery = CampaignUrlMetric::with('url_stores') + ->where('subscriber_id', $subscriber->id) + ->where('type', 'click'); + + // Apply custom sorting if provided + if (!empty($sort_by) && !empty($sort_type)) { + $urlActivityQuery->orderBy($sort_by, $sort_type); + } else { + $urlActivityQuery->orderBy('id', 'DESC'); + } + + $urlActivity = $urlActivityQuery->paginate(); + + $urlMetrics = $urlActivity->toArray(); + + if (!empty($urlMetrics['data'])) { + $urlMetrics['data'] = $this->formatUrlActivityData($urlMetrics['data']); + } + + return [ + 'urlMetrics' => $urlMetrics + ]; + } + + public function formatUrlActivityData($data) + { + $result = []; + foreach ($data as $item) { + $result[] = [ + 'url' => $item['url_stores']['url'], + 'count' => $item['counter'] + ]; + } + return $result; + } + + public function getDynamicItemView(Request $request, $subscriberId) + { + $subscriber = Subscriber::findOrFail($subscriberId); + $provider = (string)$request->get('provider'); + $params = $request->get('params', []); + + /** + * Filter the dynamic item view for a specific provider. + * + * The dynamic portion of the hook name, `$provider`, refers to the specific provider for which the view is being fetched. + * + * @param array { + * An array containing the dynamic item view data. + * + * 'type' => (string) The type of the dynamic item. + * 'title' => (string) The title of the dynamic item. + * 'content_html' => (string) The HTML content of the dynamic item. + * 'footer_content' => (string) The footer content associated with the dynamic item. + * + * } + * @param object $subscriber The subscriber object. + * @param array $params Additional parameters passed in the request. + * @since 3.0.0 + * + */ + + $data = apply_filters('fluent_crm/dynamic_contact_item_view_' . $provider, [ + 'type' => 'html', + 'title' => 'no title', + 'content_html' => 'sorry, no content found', + 'footer_content' => '' + ], $params, $subscriber); + + return [ + 'data_view' => $data + ]; + } +} diff --git a/wp-content/plugins/fluent-crm/app/Http/Controllers/SystemLogController.php b/wp-content/plugins/fluent-crm/app/Http/Controllers/SystemLogController.php new file mode 100644 index 0000000..4539f53 --- /dev/null +++ b/wp-content/plugins/fluent-crm/app/Http/Controllers/SystemLogController.php @@ -0,0 +1,308 @@ +getSearchTerm($request); + + $logs = $this->getLogsQuery($search); + + $logs = $logs->paginate($request->per_page ?: 20); + + return [ + 'logs' => $logs + ]; + } + + /** + * Stream system logs as CSV without loading all rows into memory. + * + * @param \FluentCrm\Framework\Http\Request\Request $request + * @return void + */ + public function export(Request $request) + { + $range = $this->getExportRange($request); + $startDate = $this->getExportStartDate($range); + $search = $this->getSearchTerm($request); + $chunkSize = $this->getExportChunkSize(); + $lastId = PHP_INT_MAX; + + $this->prepareCsvDownload($this->getExportFilename($range)); + + $output = fopen('php://output', 'w'); + + if (!$output) { + exit; + } + + fwrite($output, "\xEF\xBB\xBF"); + fputcsv($output, $this->getCsvHeaders(), ',', '"', '\\'); + + do { + $logs = $this->getLogsQuery($search, $startDate) + ->where('id', '<', $lastId) + ->select(['id', 'created_at', 'title', 'description']) + ->limit($chunkSize) + ->get(); + + $count = count($logs); + + foreach ($logs as $log) { + $lastId = (int) $log->id; + fputcsv($output, $this->formatCsvLogRow($log), ',', '"', '\\'); + } + + fflush($output); + + if (function_exists('flush')) { + flush(); + } + + if (connection_aborted()) { + break; + } + } while ($count === $chunkSize); + + fclose($output); + exit; + } + + public function deleteAll(Request $request) + { + SystemLog::where('id', '>', 0)->delete(); + + return [ + 'message' => __('All logs have been deleted', 'fluent-crm') + ]; + } + + /** + * @param string $search + * @param string|null $startDate + * @return mixed + */ + private function getLogsQuery($search = '', $startDate = null) + { + global $wpdb; + + $logs = SystemLog::orderBy('id', 'DESC'); + + if ($startDate) { + $logs = $logs->where('created_at', '>=', $startDate); + } + + if ($search !== '') { + $searchLike = '%' . $wpdb->esc_like($search) . '%'; + $logs = $logs->where(function ($query) use ($searchLike) { + $query->where('title', 'LIKE', $searchLike) + ->orWhere('description', 'LIKE', $searchLike); + }); + } + + return $logs; + } + + /** + * @param \FluentCrm\Framework\Http\Request\Request $request + * @return string + */ + private function getSearchTerm(Request $request) + { + $search = $request->get('search', ''); + + if (!is_scalar($search)) { + return ''; + } + + return trim(sanitize_text_field($search)); + } + + /** + * @param \FluentCrm\Framework\Http\Request\Request $request + * @return int|string + */ + private function getExportRange(Request $request) + { + $range = $request->get('range', 'all'); + + if (!is_scalar($range)) { + return 'all'; + } + + return $this->normalizeExportRange(sanitize_text_field($range)); + } + + /** + * @param mixed $range + * @return int|string + */ + private function normalizeExportRange($range) + { + $range = is_scalar($range) ? (string) $range : 'all'; + + if ($range === 'all') { + return 'all'; + } + + $range = intval($range); + $allowedRanges = [7, 15, 30]; + + return in_array($range, $allowedRanges, true) ? $range : 'all'; + } + + /** + * @param int|string $range + * @param int|null $currentTimestamp + * @return string|null + */ + private function getExportStartDate($range, $currentTimestamp = null) + { + $range = $this->normalizeExportRange($range); + + if ($range === 'all') { + return null; + } + + if (!$currentTimestamp) { + $currentTimestamp = current_time('timestamp'); + } + + return gmdate('Y-m-d H:i:s', $currentTimestamp - ($range * 86400)); + } + + /** + * @return array + */ + private function getCsvHeaders() + { + return ['ID', 'Date & Time', 'Title', 'Description']; + } + + /** + * @param object $log + * @return array + */ + private function formatCsvLogRow($log) + { + return [ + (int) $log->id, + $this->sanitizeCsvCell($log->created_at), + $this->sanitizeCsvCell($log->title), + $this->sanitizeCsvCell($this->plainText($log->description)) + ]; + } + + /** + * @param int|string $range + * @return string + */ + private function getExportFilename($range) + { + $range = $this->normalizeExportRange($range); + $rangePart = ($range === 'all') ? 'all' : 'last-' . $range . '-days'; + + return 'fluent-crm-system-logs-' . $rangePart . '-' . gmdate('Y-m-d-His') . '.csv'; + } + + /** + * Prevent spreadsheet formula execution while preserving visible values. + * + * @param mixed $value + * @return string + */ + private function sanitizeCsvCell($value) + { + if ($value === null) { + return ''; + } + + if ($value instanceof \DateTimeInterface) { + $value = $value->format('Y-m-d H:i:s'); + } else { + $value = is_scalar($value) ? (string) $value : wp_json_encode($value); + } + + if ($value !== '' && preg_match('/^[=+\-@\t\r]/', $value)) { + $value = "'" . $value; + } + + return $value; + } + + /** + * @param mixed $value + * @return string + */ + private function plainText($value) + { + if ($value === null) { + return ''; + } + + $value = is_scalar($value) ? (string) $value : wp_json_encode($value); + + return trim(html_entity_decode(strip_tags($value), ENT_QUOTES, 'UTF-8')); + } + + /** + * @return int + */ + private function getExportChunkSize() + { + $chunkSize = (int) apply_filters('fluent_crm/system_logs_export_chunk_size', 1000); + + if ($chunkSize < 100) { + return 100; + } + + if ($chunkSize > 5000) { + return 5000; + } + + return $chunkSize; + } + + /** + * @param string $filename + * @return void + */ + private function prepareCsvDownload($filename) + { + if (function_exists('set_time_limit')) { + // Shared hosts may still enforce web server timeouts; this only removes PHP's timer. + @set_time_limit(0); + } + + while (ob_get_level()) { + if (!@ob_end_clean()) { + break; + } + } + + nocache_headers(); + header('Content-Type: text/csv; charset=utf-8'); + header('Content-Disposition: attachment; filename="' . sanitize_file_name($filename) . '"'); + header('X-Content-Type-Options: nosniff'); + } +} diff --git a/wp-content/plugins/fluent-crm/app/Http/Controllers/TagsController.php b/wp-content/plugins/fluent-crm/app/Http/Controllers/TagsController.php new file mode 100644 index 0000000..bf18ca0 --- /dev/null +++ b/wp-content/plugins/fluent-crm/app/Http/Controllers/TagsController.php @@ -0,0 +1,258 @@ + $request->getSafe('sort_by', 'sanitize_sql_orderby', 'id'), + 'order' => $request->getSafe('sort_order', 'sanitize_sql_orderby', 'DESC') + ]; + + $tags = Tag::orderBy($order['by'], $order['order']) + ->searchBy($request->getSafe('search')) + ->paginate(); + + if (!$request->get('exclude_counts')) { + foreach ($tags as $tag) { + $tag->subscribersCount = $tag->countByStatus('subscribed'); + } + } + + $data = [ + 'tags' => $tags + ]; + + if ($request->get('all_tags')) { + $allTags = Tag::get(); + $formattedTags = []; + foreach ($allTags as $tag) { + $formattedTags[] = [ + 'id' => strval($tag->id), + 'title' => $tag->title, + 'slug' => $tag->slug, + 'description' => $tag->description + ]; + } + $data['all_tags'] = $formattedTags; + } + + return $data; + } + + /** + * Find a tag. + */ + public function find($id) + { + return $this->send([ + 'tag' => Tag::find($id) + ]); + } + + /** + * Store a tag. + * @param \FluentCrm\Framework\Http\Request\Request $request + * @return \WP_REST_Response + */ + public function create(Request $request) + { + $allData = $request->all(); + + if (empty($allData['slug'])) { + $allData['slug'] = Helper::slugify($allData['title']); + } else { + $allData['slug'] = sanitize_text_field($allData['slug']); + } + + $allData = $this->validate($allData, [ + 'title' => 'required', + 'slug' => "required|unique:fc_tags,slug" + ]); + + $tag = Tag::create([ + 'title' => sanitize_text_field($allData['title']), + 'slug' => $allData['slug'], + 'description' => sanitize_textarea_field(Arr::get($allData, 'description')) + ]); + + do_action('fluentcrm_tag_created', $tag->id); + + do_action('fluent_crm/tag_created', $tag); + + return $this->sendSuccess([ + 'lists' => $tag, + 'item' => $tag, + 'message' => __('Successfully saved the tag.', 'fluent-crm') + ]); + } + + /** + * Store a tag. + * @param \FluentCrm\Framework\Http\Request\Request $request + * @param $id int Tag ID + * @return \WP_REST_Response + */ + public function store(Request $request, $id) + { + $allData = $this->validate($request->all(), [ + 'title' => 'required' + ]); + + if (empty($allData['slug'])) { + $allData['slug'] = Helper::slugify($allData['title']); + } + + if ($id == 0 && $request->get('update_by') == 'slug' && !empty($allData['slug'])) { + + $tag = Tag::where('slug', $allData['slug'])->first(); + if (!$tag) { + return $this->sendError([ + 'message' => __('Tag could not be found', 'fluent-crm') + ]); + } + $id = $tag->id; + } else { + $tag = Tag::findOrFail($id); + if (empty($allData['slug'])) { + $allData['slug'] = $tag->slug; + } + } + + if (Tag::where('slug', $allData['slug'])->where('id', '!=', $id)->first()) { + return $this->sendError([ + 'message' => __('Provided slug already exists in another tag', 'fluent-crm') + ]); + } + + $tag = Tag::where('id', $id)->update([ + 'title' => sanitize_text_field($allData['title']), + 'slug' => $allData['slug'], + 'description' => sanitize_textarea_field(Arr::get($allData, 'description')), + ]); + + do_action('fluentcrm_tag_updated', $id); + + do_action('fluent_crm/tag_updated', $tag); + + return $this->sendSuccess([ + 'lists' => $tag, + 'message' => __('Successfully saved the tag.', 'fluent-crm') + ]); + } + + /** + * Store a tag. + */ + public function storeBulk() + { + $tags = $this->request->get('tags', []); + + if (!$tags) { + $tags = $this->request->get('items', []); + } + + $createdIds = []; + + foreach ($tags as $tag) { + if (empty($tag['title'])) { + continue; + } + + if (empty($tag['slug'])) { + $tag['slug'] = Helper::slugify($tag['title']); + } + + $tag = Tag::updateOrCreate( + ['slug' => sanitize_title($tag['slug'], 'display')], + ['title' => sanitize_text_field($tag['title'])] + ); + + $createdIds[] = $tag->id; + + if ($tag->wasRecentlyCreated) { + do_action('fluentcrm_tag_created', $tag->id); + do_action('fluent_crm/tag_created', $tag); + } else { + do_action('fluentcrm_tag_updated', $tag->id); + do_action('fluent_crm/tag_updated', $tag); + } + + } + + return $this->sendSuccess([ + 'message' => __('Successfully saved the tags.', 'fluent-crm'), + 'ids' => $createdIds + ]); + } + + /** + * Delete a tag by id + * + * @param \FluentCrm\Framework\Http\Request\Request $request + * @param $tagId + * @return \WP_REST_Response $object + */ + public function remove(Request $request, $tagId) + { + $tag = Tag::find($tagId); + + if (!$tag) { + return $this->sendError([ + 'message' => __('Tag not found', 'fluent-crm') + ], 404); + } + + $tag->delete(); + + do_action('fluentcrm_tag_deleted', $tagId); + do_action('fluent_crm/tag_deleted', $tagId); + + return $this->sendSuccess([ + 'message' => __('Successfully removed the tag.', 'fluent-crm') + ]); + } + + + public function handleBulkAction(Request $request) + { + $tagIds = array_map('intval', (array)$request->get('tagIds', [])); + + $tagIds = array_unique(array_filter($tagIds)); + + if ($tagIds) { + foreach ($tagIds as $tagId) { + Tag::where('id', $tagId)->delete(); + do_action('fluentcrm_tag_deleted', $tagId); + + do_action('fluent_crm/tag_deleted', $tagId); + } + } + + return $this->sendSuccess([ + 'message' => __('Selected Tags have been removed permanently', 'fluent-crm'), + ]); + + } +} diff --git a/wp-content/plugins/fluent-crm/app/Http/Controllers/TemplateController.php b/wp-content/plugins/fluent-crm/app/Http/Controllers/TemplateController.php new file mode 100644 index 0000000..cbeea93 --- /dev/null +++ b/wp-content/plugins/fluent-crm/app/Http/Controllers/TemplateController.php @@ -0,0 +1,703 @@ +getSafe('order', 'sanitize_sql_orderby', 'desc'); + $orderBy = $request->getSafe('orderBy', 'sanitize_sql_orderby', 'ID'); + + $templatesQuery = Template::emailTemplates( + $request->get('types', ['publish', 'draft']) + ); + + if ($search = $request->getSafe('search')) { + $templatesQuery->where('post_title', 'LIKE', '%' . $search . '%'); + } + + // Order the query results and paginate + $templates = $templatesQuery + ->orderBy($orderBy, $order) + ->paginate(); + + foreach ($templates as $template) { + $template->design_template = get_post_meta($template->ID, '_design_template', true); + } + + return $this->sendSuccess([ + 'templates' => $templates + ]); + } + + public function template(Request $request, $templateId = 0) + { + $template = Template::find($templateId); + + if ($template) { + $editType = get_post_meta($template->ID, '_edit_type', true); + if (!$editType) { + $editType = 'html'; + } + + $designTemplate = get_post_meta($template->ID, '_design_template', true); + $templateConfig = get_post_meta($template->ID, '_template_config', true); + + if(!$templateConfig || !is_array($templateConfig)) { + $templateConfig = []; + } + + $footerSettings = get_post_meta($template->ID, '_footer_settings', true); + $normalizedSettings = $this->normalizeTemplateSettings([ + 'template_config' => $templateConfig, + 'footer_settings' => $footerSettings + ], $designTemplate); + + $templateData = [ + 'post_title' => $template->post_title, + 'post_content' => $template->post_content, + 'post_excerpt' => $template->post_excerpt, + 'email_subject' => get_post_meta($template->ID, '_email_subject', true), + 'edit_type' => $editType, + 'design_template' => $designTemplate, + 'settings' => $normalizedSettings + ]; + + /** + * Filter the template data before editing. + * + * @since 2.6.51 + * + * @param array $templateData The data of the template being edited. + * @param object $template The template object. + */ + $templateData = apply_filters('fluent_crm/editing_template_data', $templateData, $template); + + } else { + $defaultTemplate = Helper::getDefaultEmailTemplate(); + $normalizedSettings = $this->normalizeTemplateSettings([ + 'template_config' => Helper::getTemplateConfig($defaultTemplate), + 'footer_settings' => [] + ], $defaultTemplate); + + $templateData = [ + 'post_title' => '', + 'post_content' => '', + 'post_excerpt' => '', + 'email_subject' => '', + 'edit_type' => 'html', + 'design_template' => $defaultTemplate, + 'settings' => $normalizedSettings + ]; + } + + return $this->sendSuccess([ + 'template' => $templateData + ]); + } + + public function create(Request $request) + { + if($templateId = $request->get('template_id')) { + return $this->update($request, $templateId); + } + + $templateData = Helper::parseArrayOrJson($this->request->get('template')); + + $designTemplate = Arr::get($templateData, 'design_template'); + if (!$designTemplate) { + $designTemplate = Helper::getDefaultEmailTemplate(); + $templateData['design_template'] = $designTemplate; + } + + $templateData['settings'] = $this->normalizeTemplateSettings(Arr::get($templateData, 'settings', []), $designTemplate); + + $postData = Arr::only($templateData, [ + 'post_title', + 'post_content', + 'post_excerpt' + ]); + + if(empty($postData['post_title'])) { + $postData['post_title'] = 'Email Template @ '.current_time('mysql'); + } + + if (empty($templateData['email_subject'])) { + $templateData['email_subject'] = $postData['post_title']; + } + + if(empty($postData['post_excerpt'])) { + $postData['post_excerpt'] = ''; + } + + $postData['post_modified'] = current_time('mysql'); + $postData['post_modified_gmt'] = gmdate('Y-m-d H:i:s'); + $postData['post_date'] = current_time('mysql'); + $postData['post_date_gmt'] = gmdate('Y-m-d H:i:s'); + $postData['post_type'] = fluentcrmTemplateCPTSlug(); + + $templateId = wp_insert_post($postData); + + update_post_meta($templateId, '_email_subject', Arr::get($templateData, 'email_subject')); + update_post_meta($templateId, '_edit_type', Arr::get($templateData, 'edit_type')); + update_post_meta($templateId, '_template_config', Arr::get($templateData, 'settings.template_config', [])); + update_post_meta($templateId, '_footer_settings', Arr::get($templateData, 'settings.footer_settings', [])); + update_post_meta($templateId, '_design_template', $designTemplate); + + do_action('fluent_crm/email_template_created', $templateId, $templateData); + + return $this->sendSuccess([ + 'message' => __('Template successfully created', 'fluent-crm'), + 'template_id' => $templateId + ]); + } + + public function duplicate($templateId) + { + $template = Template::findOrFail($templateId); + + $postData = [ + 'post_title' => __('[Duplicate] ', 'fluent-crm') . $template['post_title'], + 'post_content' => $template['post_content'], + 'post_excerpt' => $template['post_excerpt'], + 'post_modified' => current_time('mysql'), + 'post_modified_gmt' => gmdate('Y-m-d H:i:s'), + 'post_date' => current_time('mysql'), + 'post_date_gmt' => gmdate('Y-m-d H:i:s'), + 'post_type' => fluentcrmTemplateCPTSlug(), + ]; + + $newTemplateId = wp_insert_post($postData); + + // Meta fields to copy over + $metaKeys = [ + '_email_subject', + '_edit_type', + '_template_config', + '_design_template', + '_footer_settings' + ]; + + // Update post meta in a loop + $this->copyMetaFields($templateId, $newTemplateId, $metaKeys); + + do_action('fluent_crm/email_template_duplicated', $newTemplateId, $template); + + return $this->sendSuccess([ + 'message' => __('Template successfully duplicated', 'fluent-crm'), + 'template_id' => $newTemplateId + ]); + } + + /** + * Helper method to copy meta fields from one post to another + */ + protected function copyMetaFields($oldPostId, $newPostId, $metaKeys) + { + foreach ($metaKeys as $metaKey) { + update_post_meta($newPostId, $metaKey, get_post_meta($oldPostId, $metaKey, true)); + } + } + + public function update(Request $request, $id) + { + $oldTemplate = Template::findOrFail($id); + + $templateData = Helper::parseArrayOrJson($this->request->get('template')); + $designTemplate = Arr::get($templateData, 'design_template'); + if (!$designTemplate) { + $designTemplate = get_post_meta($id, '_design_template', true) ?: Helper::getDefaultEmailTemplate(); + $templateData['design_template'] = $designTemplate; + } + + $templateData['settings'] = $this->normalizeTemplateSettings(Arr::get($templateData, 'settings', []), $designTemplate); + + $footerSettings = Arr::get($templateData, 'settings.footer_settings'); + if($footerSettings) { + if (($footerSettings['custom_footer'] == 'yes') && !Helper::hasComplianceText($footerSettings['footer_content'])) { + return $this->sendError([ + 'message' => __('##crm.manage_subscription_url## or ##crm.unsubscribe_url## string is required for compliance. Please include unsubscription or manage subscription link', 'fluent-crm') + ]); + } + } + + if(empty($templateData['post_title'])) { + $templateData['post_title'] = 'Email template created at '.gmdate('Y-m-d H:i'); + } + + if(empty($templateData['email_subject'])) { + $templateData['email_subject'] = 'Email template created at '.gmdate('Y-m-d H:i'); + } + + $postData = Arr::only($templateData, [ + 'post_title', + 'post_content', + 'post_excerpt' + ]); + + + + $postData['post_modified'] = current_time('mysql'); + $postData['post_modified_gmt'] = gmdate('Y-m-d H:i:s'); + Template::where('ID', $id)->update($postData); + + update_post_meta($id, '_email_subject', Arr::get($templateData, 'email_subject')); + update_post_meta($id, '_edit_type', Arr::get($templateData, 'edit_type')); + update_post_meta($id, '_design_template', Arr::get($templateData, 'design_template')); + update_post_meta($id, '_template_config', Arr::get($templateData, 'settings.template_config', [])); + update_post_meta($id, '_footer_settings', Arr::get($templateData, 'settings.footer_settings', [])); + + $template = Template::findOrFail($id); + + do_action('fluent_crm/email_template_updated', $templateData, $template); + + return $this->sendSuccess([ + 'message' => __('Template successfully updated', 'fluent-crm'), + 'template_id' => $id + ]); + } + + public function handleBulkAction(Request $request) + { + $actionName = sanitize_text_field($request->get('action_name', '')); + + $templateIds = array_map('intval', (array)$request->get('template_ids', [])); + + $templateIds = array_unique(array_filter($templateIds)); + + $selectAllTemplates = filter_var($request->get('select_all'), FILTER_VALIDATE_BOOLEAN); + + if ($selectAllTemplates) { + $templateIds = Template::pluck('id')->toArray(); + } + + $templateIds = array_filter($templateIds); + if ($actionName == 'change_template_status') { + $newStatus = sanitize_text_field($request->get('status', '')); + if (!$newStatus) { + return $this->sendError([ + 'message' => __('Please select status', 'fluent-crm') + ]); + } + + $templates = Template::whereIn('ID', $templateIds)->get(); + + foreach ($templates as $template) { + $oldStatus = $template->post_status; + if ($oldStatus != $newStatus) { + $template->post_status = $newStatus; + $template->save(); + } + } + + return [ + 'message' => __('Status has been changed for the selected templates', 'fluent-crm') + ]; + } else if ($actionName == 'delete_templates') { + $templates = Template::whereIn('id', $templateIds)->get(); + + foreach ($templates as $template) { + wp_delete_post($template->ID, true); + } + + return $this->sendSuccess([ + 'message' => __('Selected Templates have been deleted permanently', 'fluent-crm'), + ]); + } + + return [ + 'message' => __('invalid bulk action', 'fluent-crm') + ]; + } + + public function delete(Request $request, $id) + { + $template = Template::findOrFail($id); + + wp_delete_post($template->ID, true); + + return $this->sendSuccess([ + 'message' => __('The template has been deleted successfully.', 'fluent-crm') + ]); + } + + public function render() + { + $rendered = Template::findOrFail( + $this->request->get('ID') + )->render(); + + return $this->sendSuccess($rendered); + } + + public function allTemplates() + { + return $this->sendSuccess([ + 'templates' => Template::emailTemplates(['publish'])->orderBy('ID', 'desc')->get(), + 'smartcodes' => $this->smartCodes() + ]); + } + + public function getSmartCodes() + { + return $this->sendSuccess([ + 'smartcodes' => $this->smartCodes() + ]); + } + + protected function smartCodes() + { + return Helper::getGlobalSmartCodes(); + } + + public function setGlobalStyle(Request $request) + { + $settings = $request->get('config', []); + + foreach ($settings as $settingKey => $setting) { + $settings[$settingKey] = sanitize_text_field($setting); + } + + fluentcrm_update_option('global_email_style_config', $settings); + + return [ + 'message' => __('Global style settings have been updated', 'fluent-crm') + ]; + } + + /** + * Fetches built-in templates from cached locally + * cached for 24 hours, then refreshed + * @return + */ + public function getBuiltInTemplates() + { + $templates = fluentCrmPersistentCache('email_remote_templates', function () { + return $this->loadRemoteTemplates(); + }, 60 * 60 * 24); // 24 hours + + // Return a success response with the formatted templates + return $this->sendSuccess([ + 'templates' => $templates + ]); + } + + /** + * Downloads a single built-in template file and returns it without saving + * it as a local email template. + * + * @param \FluentCrm\Framework\Http\Request\Request $request + * @return \FluentCrm\Framework\Http\Response\Response + */ + public function getBuiltInTemplate(Request $request) + { + $fileUrl = esc_url_raw($request->get('file', '')); + + if (!$fileUrl || !$this->isAllowedRemoteTemplateUrl($fileUrl)) { + return $this->sendError([ + 'message' => __('Invalid template source URL', 'fluent-crm') + ]); + } + + $response = wp_remote_get($fileUrl, [ + 'sslverify' => true, + 'timeout' => 20, + 'redirection' => 0, + 'limit_response_size' => 1024 * 1024 + ]); + + if (is_wp_error($response)) { + return $this->sendError([ + 'message' => __('Unable to download the selected template. Please try again.', 'fluent-crm') + ]); + } + + $responseCode = wp_remote_retrieve_response_code($response); + if ($responseCode < 200 || $responseCode >= 300) { + return $this->sendError([ + 'message' => __('Unable to download the selected template. Please try again.', 'fluent-crm') + ]); + } + + $templateData = Helper::parseArrayOrJson(wp_remote_retrieve_body($response)); + + if (Arr::get($templateData, 'is_fc_template') !== 'yes') { + return $this->sendError([ + 'message' => __('The selected file is not a valid FluentCRM template.', 'fluent-crm') + ]); + } + + $template = $this->formatRemoteTemplateData($templateData); + + $hasVisualBuilderDesign = $template['design_template'] === 'visual_builder' && !empty($template['_visual_builder_design']); + + if (!$template['post_content'] && !$hasVisualBuilderDesign) { + return $this->sendError([ + 'message' => __('The selected template does not have any email content.', 'fluent-crm') + ]); + } + + return $this->sendSuccess([ + 'message' => __('Template has been inserted', 'fluent-crm'), + 'template' => $template + ]); + } + + /** + * Restricts direct template downloads to trusted FluentCRM template hosts. + * + * @param string $url + * @return bool + */ + protected function isAllowedRemoteTemplateUrl($url) + { + $parsedUrl = wp_parse_url($url); + + if (empty($parsedUrl['scheme']) || empty($parsedUrl['host']) || $parsedUrl['scheme'] !== 'https') { + return false; + } + + $allowedHosts = [ + 'fluentcrm.com', + 'www.fluentcrm.com', + 'wpmanageninja.com', + 'www.wpmanageninja.com' + ]; + + if (defined('FC_TEMPLATE_API_DOMAIN')) { + $configuredHost = wp_parse_url(FC_TEMPLATE_API_DOMAIN, PHP_URL_HOST); + if ($configuredHost) { + $allowedHosts[] = strtolower($configuredHost); + } + } + + return in_array(strtolower($parsedUrl['host']), array_unique($allowedHosts), true); + } + + /** + * Normalizes remote JSON to the local template shape without creating a WP post. + * + * @param array $templateData + * @return array + */ + protected function formatRemoteTemplateData($templateData) + { + $designTemplate = sanitize_text_field(Arr::get($templateData, 'design_template')); + if (!$designTemplate) { + $designTemplate = Helper::getDefaultEmailTemplate(); + } + + $normalizedSettings = $this->normalizeTemplateSettings(Arr::get($templateData, 'settings', []), $designTemplate); + + return [ + 'post_title' => sanitize_text_field(Arr::get($templateData, 'post_title', '')), + 'post_content' => Arr::get($templateData, 'post_content', ''), + 'post_excerpt' => sanitize_textarea_field(Arr::get($templateData, 'post_excerpt', '')), + 'email_subject' => sanitize_text_field(Arr::get($templateData, 'email_subject', '')), + 'edit_type' => sanitize_text_field(Arr::get($templateData, 'edit_type', 'html')), + 'design_template' => $designTemplate, + 'settings' => $normalizedSettings, + '_visual_builder_design' => Arr::get($templateData, '_visual_builder_design') + ]; + } + + /** + * Normalize template settings with legacy footer disable compatibility. + * + * @param array $settings + * @return array + */ + protected function normalizeTemplateSettings($settings, $designTemplate = '') + { + $templateConfig = Arr::get($settings, 'template_config', []); + if (!is_array($templateConfig)) { + $templateConfig = []; + } + + if (!$this->templateSupportsContentPadding($designTemplate)) { + unset($templateConfig['content_padding']); + } elseif (!isset($templateConfig['content_padding'])) { + $templateConfig['content_padding'] = 20; + } + + $footerSettings = Arr::get($settings, 'footer_settings', []); + if (!is_array($footerSettings)) { + $footerSettings = []; + } + + $hasExplicitDisableFooter = array_key_exists('disable_footer', $footerSettings); + $hasExplicitCustomFooter = array_key_exists('custom_footer', $footerSettings); + + $disableFooter = Arr::get($footerSettings, 'disable_footer'); + if ($disableFooter !== 'yes' && $disableFooter !== 'no') { + $legacyDisable = Arr::get($templateConfig, 'disable_footer'); + $disableFooter = ($legacyDisable === 'yes' || $legacyDisable === 'no') ? $legacyDisable : 'no'; + } + + $customFooter = Arr::get($footerSettings, 'custom_footer'); + if ($customFooter !== 'yes' && $customFooter !== 'no') { + $legacyFooterContent = Arr::get($footerSettings, 'footer_content', ''); + $customFooter = (is_string($legacyFooterContent) && trim(wp_strip_all_tags($legacyFooterContent))) + ? 'yes' + : 'no'; + } + + $footerSettings = wp_parse_args($footerSettings, [ + 'custom_footer' => 'no', + 'footer_content' => '', + 'disable_footer' => 'no', + 'font_size' => 13, + 'font_color' => '#202020', + 'background_color' => 'transparent', + 'footer_padding' => 20 + ]); + + // Footer content is user-editable from a raw text mode; sanitize before persistence. + $footerSettings['footer_content'] = Sanitize::sanitizeFooterHtml(Arr::get($footerSettings, 'footer_content', '')); + + $footerSettings['disable_footer'] = $disableFooter; + $footerSettings['custom_footer'] = $customFooter; + + // Legacy imported templates may carry disable_footer in template_config without + // explicit footer settings. Treat those as Global Footer instead of hidden footer. + $isLegacyImportedDisabled = ( + !$hasExplicitDisableFooter && + !$hasExplicitCustomFooter && + $footerSettings['disable_footer'] === 'yes' && + Arr::get($templateConfig, 'disable_footer') === 'yes' && + $footerSettings['custom_footer'] !== 'yes' && + !trim(wp_strip_all_tags(Arr::get($footerSettings, 'footer_content', ''))) + ); + + if ($isLegacyImportedDisabled) { + $footerSettings['disable_footer'] = 'no'; + $footerSettings['custom_footer'] = 'no'; + } + + // Keep legacy key in sync during transition to avoid regressions in old readers. + $templateConfig['disable_footer'] = $footerSettings['disable_footer']; + + return [ + 'template_config' => $templateConfig, + 'footer_settings' => $footerSettings + ]; + } + + /** + * Raw classic editor templates only support font family and footer flags. + * + * @param string $designTemplate + * @return bool + */ + protected function templateSupportsContentPadding($designTemplate) + { + if ($designTemplate === 'raw_classic') { + return false; + } + + $templates = Helper::getEmailDesignTemplates(); + $template = Arr::get($templates, $designTemplate, []); + + return Arr::get($template, 'template_type') !== 'classic_editor'; + } + + /** + * Fetches and formats email templates from a remote FluentCRM API endpoint. + * This method makes an HTTP request to retrieve email templates from FluentCRM's public API. + * It processes the response and formats the templates into a standardized structure. + * @throws \WP_Error Logs error message if the API request fails + * @return array + * @access public + */ + + public function loadRemoteTemplates() + { + $restBase = defined('FC_TEMPLATE_API_DOMAIN') ? FC_TEMPLATE_API_DOMAIN : 'https://fluentcrm.com'; + $restApi = $restBase.'/wp-json/wp/v2/email-templates?per_page=50'; + + // Make a GET request to retrieve CRM templates + $response = wp_remote_get($restApi, [ + 'sslverify' => false, + ]); + + // Check if the request resulted in an error + if (is_wp_error($response)) { + // Handle error + error_log($response->get_error_message()); + return []; + } + + // Decode the JSON response from the request + $templateLists = json_decode(wp_remote_retrieve_body($response), true); + + if (!is_array($templateLists)) { + return []; + } + + $formattedTemplates = []; + + foreach ($templateLists as $template) { + if (!$template['template_json']) { + // Skip if no template json + continue; + } + $mediaURL = ''; + if ($template['featured_media'] != 0) { + $mediaURL = $this->getMediaURL($template['featured_media'], $restApi); + } + $formattedTemplates[] = [ + 'id' => $template['id'], + 'title' => $template['title']['rendered'], + 'content' => $template['template_json'], + 'short_description' => $template['short_description'], + 'link' => $template['link'], + 'media_url' => $mediaURL, + 'status' => $template['status'], + 'cover_image' => $template['cover_image'], + ]; + } + + return $formattedTemplates; + } + + + /** + * Retrieves the full source URL of a media item. + * + * @param int $mediaID Media item ID. + * @param string $restAPI The base URL of the REST API. + * + * @return string Full source URL of the media item. + */ + public function getMediaURL($mediaID, $restAPI) { + $request = wp_remote_get($restAPI.'media/'.$mediaID, [ + 'sslverify' => false, + ]); + + // Check for request errors + if (is_wp_error($request)) { + return ''; + } + + $image = json_decode($request['body'], true); + $img = Arr::get($image, 'source_url'); + + return $img; + } +} diff --git a/wp-content/plugins/fluent-crm/app/Http/Controllers/UsersController.php b/wp-content/plugins/fluent-crm/app/Http/Controllers/UsersController.php new file mode 100644 index 0000000..80c2b5b --- /dev/null +++ b/wp-content/plugins/fluent-crm/app/Http/Controllers/UsersController.php @@ -0,0 +1,131 @@ +getSafe('roles', 'sanitize_text_field', []); + $limit = $request->limit ?: 5; + $fields = $request->fields ?: ['ID', 'display_name', 'user_email']; + + $userQuery = new \WP_User_Query([ + 'role__in' => $roles, + 'number' => $limit, + 'fields' => $fields, + ]); + + $users = $userQuery->get_results(); + + $total = $userQuery->get_total(); + + return $this->send([ + 'users' => $users, + 'total' => $total + ]); + } + + public function import(Request $request) + { + $inputs = $request->only([ + 'map', 'tags', 'lists', 'roles', 'update', 'new_status', 'double_optin_email', 'import_silently' + ]); + + /** + * Filter the number of subscribers to process per request while importing users in FluentCRM. + * + * This filter allows you to modify the number of subscribers that are processed + * in a single request when processing subscribers in FluentCRM. + * + * @param int $limit The number of subscribers to process per request. Default is 100. + */ + $limit = apply_filters('fluent_crm/process_subscribers_per_request', 100); + $page = absint($request->get('page', 1)); + + $userQuery = new \WP_User_Query([ + 'role__in' => Arr::get($inputs, 'roles', []), + 'number' => $limit, + 'offset' => ($page - 1) * $limit + ]); + + if (Arr::get($inputs, 'import_silently') == 'yes') { + if(!defined('FLUENTCRM_DISABLE_TAG_LIST_EVENTS')) { + define('FLUENTCRM_DISABLE_TAG_LIST_EVENTS', true); + } + } + + $total = $userQuery->get_total(); + $users = $userQuery->get_results(); + if($users) { + $this->processUsers($users, $inputs); + } + + $hasRecords = !!count($users); + + return $this->sendSuccess([ + 'message' => __('Processing', 'fluent-crm'), + 'page_total' => ceil($total / $limit), + 'record_total' => $total, + 'has_more' => $hasRecords, + 'current_page' => $page, + 'next_page' => $page + 1 + ]); + + } + + private function processUsers($users, $inputs) + { + $subscribers = []; + foreach ($users as $user) { + $subscriber = Helper::getWPMapUserInfo($user); + $subscriber['source'] = 'wp_users'; + if ($subscriber['email']) { + $subscribers[] = Sanitize::contact($subscriber); + } + } + + $sendDoubleOptin = Arr::get($inputs, 'double_optin_email') == 'yes'; + + return Subscriber::import( + $subscribers, + Arr::get($inputs, 'tags', []), + Arr::get($inputs, 'lists', []), + Arr::get($inputs, 'update'), + Arr::get($inputs, 'new_status'), + $sendDoubleOptin + ); + } + + public function roles() + { + if (!function_exists('get_editable_roles')) { + require_once(ABSPATH . '/wp-admin/includes/user.php'); + } + $roles = \get_editable_roles(); + + return [ + 'roles' => $roles + ]; + } +} diff --git a/wp-content/plugins/fluent-crm/app/Http/Controllers/WebhookBounceController.php b/wp-content/plugins/fluent-crm/app/Http/Controllers/WebhookBounceController.php new file mode 100644 index 0000000..ba2d102 --- /dev/null +++ b/wp-content/plugins/fluent-crm/app/Http/Controllers/WebhookBounceController.php @@ -0,0 +1,87 @@ +validServices)) { + /** + * Filter the bounce handling response for a specific service. + * + * The dynamic portion of the hook name, `$serviceName`, refers to the name of the email service. This is a custom bounce handler. + * + * @since 2.5.95 + * + * @param array { + * The response data. + * + * @type int $success Indicates if the bounce handling was successful (0 or 1). + * @type string $message The message associated with the bounce handling. + * @type string $service The name of the email service. + * @type string $result The result of the bounce handling. + * @type int $time The timestamp when the bounce was handled. + * } + * @param object $request The request object. + * @param string $securityCode The security code for the request. + */ + return apply_filters('fluent_crm_handle_bounce_' . $serviceName, [ + 'success' => 0, + 'message' => '', + 'service' => $serviceName, + 'result' => '', + 'time' => time() + ], $request, $securityCode); + } + + if (!hash_equals($this->getSecurityCode(), $securityCode)) { + return $this->getError(); + } + + $result = (new Webhook())->handle($serviceName, $request); + + return [ + 'success' => 1, + 'message' => 'recorded', + 'service' => $serviceName, + 'result' => $result, + 'time' => time() + ]; + + } + + private function getSecurityCode() + { + $code = fluentcrm_get_option('_fc_bounce_key'); + + if (!$code) { + $code = 'fcrm_' . substr(md5(wp_generate_uuid4()), 0, 14); + fluentcrm_update_option('_fc_bounce_key', $code); + } + + return $code; + } + + private function getError() + { + return [ + 'status' => false, + 'message' => __('Invalid Data or Security Code', 'fluent-crm') + ]; + } +} diff --git a/wp-content/plugins/fluent-crm/app/Http/Controllers/WebhookController.php b/wp-content/plugins/fluent-crm/app/Http/Controllers/WebhookController.php new file mode 100644 index 0000000..043fd0f --- /dev/null +++ b/wp-content/plugins/fluent-crm/app/Http/Controllers/WebhookController.php @@ -0,0 +1,113 @@ +getFields(); + $search = $request->getSafe('search'); + + $webhooks = $webhook->latest()->get()->toArray(); + + if (!empty($search)) { + $search = strtolower($search); + $webhooks = array_map(function ($row) use ($search) { + $value = isset($row['value']) && is_array($row['value']) ? $row['value'] : []; + $name = strtolower((string)($value['name'] ?? '')); + + if ($name !== '' && Str::contains($name, $search)) { + return $row; + } + return null; + }, $webhooks); + } + + $rows = []; + foreach ($webhooks as $row) { + if ($row) { + $rows[] = $row; + } + } + + + $response = [ + 'webhooks' => $rows, + 'fields' => $fields['fields'], + 'custom_fields' => $fields['custom_fields'], + 'lists' => Lists::get(), + 'tags' => Tag::get() + ]; + + if (Helper::isCompanyEnabled()) { + $response['companies'] = Company::get(); + } + + return $response; + } + + public function create(Request $request, Webhook $webhook) + { + $data = $request->all(); + + $validatedData = $this->validate($data, [ + 'name' => 'required', + 'status' => 'required' + ]); + + $webhook = $webhook->store($validatedData); + + return [ + 'id' => $webhook->id, + 'webhook' => $webhook->value, + 'webhooks' => $webhook->latest()->get(), + 'message' => __('Successfully created the WebHook', 'fluent-crm') + ]; + } + + public function update(Request $request, Webhook $webhook, $id) + { + $existingWebhook = $webhook->find($id); + + if (!$existingWebhook) { + return $this->sendError([ + 'message' => __('Webhook not found', 'fluent-crm') + ], 404); + } + + $existingWebhook->saveChanges($request->all()); + + return [ + 'webhooks' => $webhook->latest()->get(), + 'message' => __('Successfully updated the webhook', 'fluent-crm') + ]; + } + + public function delete(Webhook $webhook, $id) + { + $webhook->where('id', $id)->delete(); + + return [ + 'webhooks' => $webhook->latest()->get(), + 'message' => __('Successfully deleted the webhook', 'fluent-crm') + ]; + } +} diff --git a/wp-content/plugins/fluent-crm/app/Http/Policies/AiPolicy.php b/wp-content/plugins/fluent-crm/app/Http/Policies/AiPolicy.php new file mode 100644 index 0000000..d97abec --- /dev/null +++ b/wp-content/plugins/fluent-crm/app/Http/Policies/AiPolicy.php @@ -0,0 +1,38 @@ +currentUserCan('fcrm_manage_settings'); + } + + public function saveSettings(Request $request) + { + return $this->currentUserCan('fcrm_manage_settings'); + } + + public function testConnection(Request $request) + { + return $this->currentUserCan('fcrm_manage_settings'); + } + + public function generate(Request $request) + { + return $this->currentUserCan('fcrm_manage_emails'); + } + + public function generateEmailBody(Request $request) + { + return $this->currentUserCan('fcrm_manage_emails'); + } + + public function contactSummary(Request $request) + { + return $this->currentUserCan('fcrm_read_contacts'); + } +} diff --git a/wp-content/plugins/fluent-crm/app/Http/Policies/BasePolicy.php b/wp-content/plugins/fluent-crm/app/Http/Policies/BasePolicy.php new file mode 100644 index 0000000..e93e7e3 --- /dev/null +++ b/wp-content/plugins/fluent-crm/app/Http/Policies/BasePolicy.php @@ -0,0 +1,33 @@ +currentUserCan('manage_options'); + } + + public function currentUserCan($permission) + { + return PermissionManager::currentUserCan($permission); + } +} diff --git a/wp-content/plugins/fluent-crm/app/Http/Policies/CampaignPolicy.php b/wp-content/plugins/fluent-crm/app/Http/Policies/CampaignPolicy.php new file mode 100644 index 0000000..3981185 --- /dev/null +++ b/wp-content/plugins/fluent-crm/app/Http/Policies/CampaignPolicy.php @@ -0,0 +1,44 @@ +method() == 'GET') { + return $this->currentUserCan('fcrm_read_emails'); + } + + return $this->currentUserCan('fcrm_manage_emails'); + } + + public function delete(Request $request) + { + return $this->currentUserCan('fcrm_manage_email_delete'); + } + + public function deleteCampaignEmails(Request $request) + { + return $this->currentUserCan('fcrm_manage_email_delete'); + } + + public function handleBulkAction(Request $request) + { + return $this->currentUserCan('fcrm_manage_email_delete'); + } +} diff --git a/wp-content/plugins/fluent-crm/app/Http/Policies/CompanyPolicy.php b/wp-content/plugins/fluent-crm/app/Http/Policies/CompanyPolicy.php new file mode 100644 index 0000000..e5817bf --- /dev/null +++ b/wp-content/plugins/fluent-crm/app/Http/Policies/CompanyPolicy.php @@ -0,0 +1,66 @@ +isEnabled() && $this->currentUserCan('fcrm_manage_contact_cats'); + } + + public function delete(Request $request) + { + return $this->isEnabled() && $this->currentUserCan('fcrm_manage_contact_cats_delete'); + } + + /** + * Check user permission for bulk company actions. + * + * The delete bulk action permanently removes companies, so it must require + * the stronger delete permission while other bulk updates keep the manage + * permission used by the company module. + * + * @param \FluentCrm\Framework\Http\Request\Request $request + * @return Boolean + */ + public function handleBulkActions(Request $request) + { + $actionName = sanitize_text_field($request->get('action_name', '')); + + if ($actionName == 'delete_companies') { + return $this->delete($request); + } + + return $this->verifyRequest($request); + } + + public function detachSubscribers(Request $request) + { + return $this->verifyRequest($request); + } + + public function bulkDeleteNotes(Request $request) + { + return $this->verifyRequest($request); + } + + public function deleteSubscribes(Request $request) + { + return $this->detachSubscribers($request); + } +} diff --git a/wp-content/plugins/fluent-crm/app/Http/Policies/CustomFieldsPolicy.php b/wp-content/plugins/fluent-crm/app/Http/Policies/CustomFieldsPolicy.php new file mode 100644 index 0000000..8310c7c --- /dev/null +++ b/wp-content/plugins/fluent-crm/app/Http/Policies/CustomFieldsPolicy.php @@ -0,0 +1,32 @@ +currentUserCan('fcrm_manage_settings'); + } + + //TODO: masiur vai + public function getLabels(Request $request) + { + return true; + } +} diff --git a/wp-content/plugins/fluent-crm/app/Http/Policies/EmailPatternPolicy.php b/wp-content/plugins/fluent-crm/app/Http/Policies/EmailPatternPolicy.php new file mode 100644 index 0000000..88b8012 --- /dev/null +++ b/wp-content/plugins/fluent-crm/app/Http/Policies/EmailPatternPolicy.php @@ -0,0 +1,27 @@ +method() == 'GET') { + return $this->currentUserCan('fcrm_read_emails'); + } + + return $this->currentUserCan('fcrm_manage_emails'); + } + + public function delete(Request $request) + { + return $this->currentUserCan('fcrm_manage_email_delete'); + } + + public function handleBulkAction(Request $request) + { + return $this->currentUserCan('fcrm_manage_email_delete'); + } +} diff --git a/wp-content/plugins/fluent-crm/app/Http/Policies/FormsPolicy.php b/wp-content/plugins/fluent-crm/app/Http/Policies/FormsPolicy.php new file mode 100644 index 0000000..52f0fda --- /dev/null +++ b/wp-content/plugins/fluent-crm/app/Http/Policies/FormsPolicy.php @@ -0,0 +1,27 @@ +currentUserCan('fcrm_manage_forms'); + } +} diff --git a/wp-content/plugins/fluent-crm/app/Http/Policies/FunnelPolicy.php b/wp-content/plugins/fluent-crm/app/Http/Policies/FunnelPolicy.php new file mode 100644 index 0000000..f40ad61 --- /dev/null +++ b/wp-content/plugins/fluent-crm/app/Http/Policies/FunnelPolicy.php @@ -0,0 +1,53 @@ +method() == 'GET') { + return $this->currentUserCan('fcrm_read_funnels'); + } + + return $this->currentUserCan('fcrm_write_funnels'); + } + + public function delete(Request $request) + { + return $this->currentUserCan('fcrm_delete_funnels'); + } + + public function handleBulkAction(Request $request) + { + if ($request->get('action_name') == 'delete_funnels') { + return $this->currentUserCan('fcrm_delete_funnels'); + } + + return $this->currentUserCan('fcrm_write_funnels'); + } + + public function removeBulkSubscribers(Request $request) + { + return $this->currentUserCan('fcrm_delete_funnels'); + } + + public function deleteSubscribers(Request $request) + { + return $this->currentUserCan('fcrm_delete_funnels'); + } +} diff --git a/wp-content/plugins/fluent-crm/app/Http/Policies/ImportUserPolicy.php b/wp-content/plugins/fluent-crm/app/Http/Policies/ImportUserPolicy.php new file mode 100644 index 0000000..1a98753 --- /dev/null +++ b/wp-content/plugins/fluent-crm/app/Http/Policies/ImportUserPolicy.php @@ -0,0 +1,24 @@ +currentUserCan('fcrm_manage_contacts'); + } +} diff --git a/wp-content/plugins/fluent-crm/app/Http/Policies/ListPolicy.php b/wp-content/plugins/fluent-crm/app/Http/Policies/ListPolicy.php new file mode 100644 index 0000000..50ed2c6 --- /dev/null +++ b/wp-content/plugins/fluent-crm/app/Http/Policies/ListPolicy.php @@ -0,0 +1,42 @@ +currentUserCan('fcrm_manage_contact_cats'); + } + + + /** + * Check user permission for delete lists + * @param \FluentCrm\Framework\Http\Request\Request $request + * @return Boolean + */ + public function remove(Request $request) + { + return $this->currentUserCan('fcrm_manage_contact_cats_delete'); + } + + public function handleBulkAction(Request $request) + { + return $this->currentUserCan('fcrm_manage_contact_cats_delete'); + } +} diff --git a/wp-content/plugins/fluent-crm/app/Http/Policies/PublicPolicy.php b/wp-content/plugins/fluent-crm/app/Http/Policies/PublicPolicy.php new file mode 100644 index 0000000..71894ca --- /dev/null +++ b/wp-content/plugins/fluent-crm/app/Http/Policies/PublicPolicy.php @@ -0,0 +1,26 @@ +currentUserCan('fcrm_view_dashboard'); + } + + public function getEmails(Request $request) + { + return $this->currentUserCan('fcrm_read_emails'); + } + + public function deleteEmails(Request $request) + { + return $this->currentUserCan('fcrm_manage_email_delete'); + } +} diff --git a/wp-content/plugins/fluent-crm/app/Http/Policies/SettingsPolicy.php b/wp-content/plugins/fluent-crm/app/Http/Policies/SettingsPolicy.php new file mode 100644 index 0000000..1bb026c --- /dev/null +++ b/wp-content/plugins/fluent-crm/app/Http/Policies/SettingsPolicy.php @@ -0,0 +1,12 @@ +currentUserCan('fcrm_manage_settings'); + } +} diff --git a/wp-content/plugins/fluent-crm/app/Http/Policies/SubscriberPolicy.php b/wp-content/plugins/fluent-crm/app/Http/Policies/SubscriberPolicy.php new file mode 100644 index 0000000..3e3fa84 --- /dev/null +++ b/wp-content/plugins/fluent-crm/app/Http/Policies/SubscriberPolicy.php @@ -0,0 +1,76 @@ +method() == 'GET') { + return $this->currentUserCan('fcrm_read_contacts'); + } + + return $this->currentUserCan('fcrm_manage_contacts'); + } + + public function deleteSubscriber(Request $request) + { + return $this->currentUserCan('fcrm_manage_contacts_delete'); + } + + public function deleteSubscribers(Request $request) + { + return $this->currentUserCan('fcrm_manage_contacts_delete'); + } + + public function deleteNote(Request $request) + { + return $this->currentUserCan('fcrm_manage_contacts_delete'); + } + + public function bulkDeleteNotes(Request $request) + { + return $this->currentUserCan('fcrm_manage_contacts_delete'); + } + + public function deleteEmails(Request $request) + { + return $this->currentUserCan('fcrm_manage_email_delete'); + } + + public function handleBulkActions(Request $request) + { + $actionName = $request->get('action_name'); + + if (!$actionName) { + return $this->currentUserCan('fcrm_manage_contacts'); + } + + + $actionMaps = [ + 'add_to_email_sequence' => 'fcrm_manage_emails', + 'add_to_automation' => 'fcrm_write_funnels', + 'delete_contacts' => 'fcrm_manage_contacts_delete' + ]; + + if (isset($actionMaps[$actionName])) { + return $this->currentUserCan($actionMaps[$actionName]); + } + + return $this->currentUserCan('fcrm_manage_contacts'); + } +} diff --git a/wp-content/plugins/fluent-crm/app/Http/Policies/TagPolicy.php b/wp-content/plugins/fluent-crm/app/Http/Policies/TagPolicy.php new file mode 100644 index 0000000..c08b2f6 --- /dev/null +++ b/wp-content/plugins/fluent-crm/app/Http/Policies/TagPolicy.php @@ -0,0 +1,42 @@ +currentUserCan('fcrm_manage_contact_cats'); + } + + /** + * Check user permission for delete tags + * @param \FluentCrm\Framework\Http\Request\Request $request + * @return Boolean + */ + public function remove(Request $request) + { + return $this->currentUserCan('fcrm_manage_contact_cats_delete'); + } + + public function handleBulkAction(Request $request) + { + return $this->currentUserCan('fcrm_manage_contact_cats_delete'); + } + +} diff --git a/wp-content/plugins/fluent-crm/app/Http/Policies/TemplatePolicy.php b/wp-content/plugins/fluent-crm/app/Http/Policies/TemplatePolicy.php new file mode 100644 index 0000000..0eb3d9b --- /dev/null +++ b/wp-content/plugins/fluent-crm/app/Http/Policies/TemplatePolicy.php @@ -0,0 +1,41 @@ +currentUserCan('fcrm_manage_email_templates'); + } + + public function getBuiltInTemplate(Request $request) + { + return $this->currentUserCan('fcrm_manage_email_templates'); + } + + public function delete(Request $request) + { + return $this->currentUserCan('fcrm_manage_email_delete'); + } + + public function handleBulkAction(Request $request) + { + return $this->currentUserCan('fcrm_manage_email_delete'); + } +} diff --git a/wp-content/plugins/fluent-crm/app/Http/Policies/UsersPolicy.php b/wp-content/plugins/fluent-crm/app/Http/Policies/UsersPolicy.php new file mode 100644 index 0000000..ed296a5 --- /dev/null +++ b/wp-content/plugins/fluent-crm/app/Http/Policies/UsersPolicy.php @@ -0,0 +1,26 @@ +currentUserCan('fcrm_manage_settings'); + } +} diff --git a/wp-content/plugins/fluent-crm/app/Http/Routes/api.php b/wp-content/plugins/fluent-crm/app/Http/Routes/api.php new file mode 100644 index 0000000..32b3f98 --- /dev/null +++ b/wp-content/plugins/fluent-crm/app/Http/Routes/api.php @@ -0,0 +1,515 @@ +prefix('tags')->withPolicy('TagPolicy')->group(function ($router) { + + $router->get('/', [TagsController::class, 'index']); + $router->post('/', [TagsController::class, 'create']); + + $router->get('{id}', [TagsController::class, 'find'])->int('id'); + $router->put('{id}', [TagsController::class, 'store'])->int('id'); + $router->delete('{id}', [TagsController::class, 'remove'])->int('id'); + $router->post('do-bulk-action', [TagsController::class, 'handleBulkAction']); + + $router->post('/bulk', [TagsController::class, 'storeBulk']); + +}); + +/* + * /lists endpoints + */ +$router->prefix('lists')->withPolicy('ListPolicy')->group(function ($router) { + + $router->get('/', [ListsController::class, 'index']); + $router->post('/', [ListsController::class, 'create']); + + $router->get('{id}', [ListsController::class, 'find'])->int('id'); + $router->put('{id}', [ListsController::class, 'update'])->int('id'); + $router->delete('/{id}', [ListsController::class, 'remove'])->int('id'); + $router->post('do-bulk-action', [ListsController::class, 'handleBulkAction']); + + $router->post('/bulk', [ListsController::class, 'storeBulk']); + +}); + +/* + * Global search: contacts + email campaigns + automations in one call. + * Each Permission is checked in the controller. + */ +$router->get('global-search', [OptionsController::class, 'search']); + +/* + * /subscribers endpoints + */ +$router->prefix('subscribers')->withPolicy('SubscriberPolicy')->group(function ($router) { + + $router->get('/', [SubscriberController::class, 'index']); + $router->post('/', [SubscriberController::class, 'store']); + $router->put('subscribers-property', [SubscriberController::class, 'updateProperty']); + $router->delete('/', [SubscriberController::class, 'deleteSubscribers']); + $router->post('sync-segments', [SubscriberController::class, 'tagger']); + $router->post('do-bulk-action', [SubscriberController::class, 'handleBulkActions']); + $router->get('prev-next-ids', [SubscriberController::class, 'getPrevNextIds']); + + $router->get('{id}', [SubscriberController::class, 'show'])->int('id'); + $router->delete('{id}', [SubscriberController::class, 'deleteSubscriber'])->int('id'); + + $router->put('{id}', [SubscriberController::class, 'updateSubscriber'])->int('id'); + $router->get('{id}/emails', [SubscriberController::class, 'emails'])->int('id'); + $router->get('{id}/emails/template-mock', [SubscriberController::class, 'getTemplateMock'])->int('id'); + $router->post('{id}/emails/send', [SubscriberController::class, 'sendCustomEmail'])->int('id'); + $router->delete('{id}/emails', [SubscriberController::class, 'deleteEmails'])->int('id'); + $router->get('{id}/purchase-history', [PurchaseHistoryController::class, 'getOrders'])->int('id'); + $router->get('{id}/form-submissions', [SubscriberController::class, 'getFormSubmissions'])->int('id'); + $router->get('{id}/support-tickets', [SubscriberController::class, 'getSupportTickets'])->int('id'); + $router->post('{id}/send-double-optin', [SubscriberController::class, 'sendDoubleOptinEmail'])->int('id'); + + $router->get('{id}/notes', [SubscriberController::class, 'getNotes'])->int('id'); + $router->post('{id}/notes', [SubscriberController::class, 'addNote'])->int('id'); + $router->put('{id}/notes/{note_id}', [SubscriberController::class, 'updateNote'])->int('id')->int('note_id'); + $router->delete('{id}/notes/{note_id}', [SubscriberController::class, 'deleteNote'])->int('id')->int('note_id'); + $router->post('{id}/notes/bulk-delete', [SubscriberController::class, 'bulkDeleteNotes'])->int('id'); + $router->get('{id}/external_view', [SubscriberController::class, 'getExternalView'])->int('id'); + $router->post('{id}/external_view', [SubscriberController::class, 'saveExternalViewData'])->int('id'); + $router->get('{id}/info-widgets', [SubscriberController::class, 'getInfoWidgets'])->int('id'); + $router->get('{id}/dynamic-item-view', [SubscriberController::class, 'getDynamicItemView'])->int('id'); + + $router->get('search-contacts', [SubscriberController::class, 'searchContacts']); + + $router->get('{id}/tracking-events', [SubscriberController::class, 'getTrackingEvents'])->int('id'); + $router->post('track-event', [SubscriberController::class, 'trackEvent']); + + $router->get('{id}/url-metrics', [SubscriberController::class, 'getUrlMetrics'])->int('id'); + + $router->post('bulk-add-update', [SubscriberController::class, 'bulkAddUpdate']); + +}); + +$router->prefix('campaigns')->withPolicy('CampaignPolicy')->group(function ($router) { + + $router->get('/', [CampaignController::class, 'campaigns']); + $router->post('/', [CampaignController::class, 'create']); + $router->post('/send-test-email', [CampaignController::class, 'sendTestEmail']); + // Editor/draft preview iframe: renders a campaign payload or campaign_id without email-history metadata. + $router->post('/email-preview-html', [CampaignController::class, 'getEmailPreviewBody']); + // Sent/scheduled email history preview: used from contact profile, campaign emails, and all emails. + $router->get('emails/{email_id}/preview', [CampaignController::class, 'previewEmail'])->int('email_id'); + + $router->post('estimated-contacts', [CampaignController::class, 'getContactEstimation']); + $router->post('update-single-campaign', [CampaignController::class, 'updateSingleCampaignSimulate']); + + $router->get('{id}', [CampaignController::class, 'campaign'])->int('id'); + $router->put('{id}', [CampaignController::class, 'update'])->int('id'); + $router->post('{id}/step', [CampaignController::class, 'updateStep'])->int('id'); + + $router->post('{id}/pause', [CampaignController::class, 'pauseCampaign'])->int('id'); + $router->post('{id}/duplicate', [CampaignController::class, 'duplicateCampaign'])->int('id'); + $router->post('{id}/resume', [CampaignController::class, 'resumeCampaign'])->int('id'); + $router->put('{id}/title', [CampaignController::class, 'updateCampaignTitle'])->int('id'); + $router->delete('{id}', [CampaignController::class, 'delete'])->int('id'); + + $router->post('do-bulk-action', [CampaignController::class, 'handleBulkAction'])->int('id'); + + // todo: delete this endpoint '{id}/subscribe' in future since it is not in use anywhere. We will keep it for reference for now. We will remove in the immediate next version + // $router->post('{id}/subscribe', [CampaignController::class, 'subscribe'])->int('id'); + $router->post('{id}/draft-recipients', [CampaignController::class, 'draftRecipients'])->int('id'); + $router->get('{id}/estimated-recipients-count', [CampaignController::class, 'recipientsCount'])->int('id'); + + $router->get('{id}/emails', [CampaignController::class, 'campaignEmails'])->int('id'); + $router->delete('{id}/emails', [CampaignController::class, 'deleteCampaignEmails'])->int('id'); + $router->post('{id}/schedule', [CampaignController::class, 'schedule'])->int('id'); + $router->post('{id}/un-schedule', [CampaignController::class, 'unSchedule'])->int('id'); + $router->get('{id}/processing-stat', [CampaignController::class, 'processingStat'])->int('id'); + + $router->get('{id}/share-url', [CampaignController::class, 'getShareUrl'])->int('id'); + + + $router->get('{id}/status', [CampaignController::class, 'getCampaignStatus'])->int('id'); + $router->get('{id}/overview_stats', [CampaignController::class, 'getOverviewStats'])->int('id'); + $router->get('{id}/link-report', [CampaignAnalyticsController::class, 'getLinksReport'])->int('id'); + $router->get('{id}/revenues', [CampaignAnalyticsController::class, 'getRevenueReport'])->int('id'); + $router->post('{id}/revenues/resync', [CampaignAnalyticsController::class, 'getRevenueReSyncReport'])->int('id'); + $router->get('{id}/unsubscribers', [CampaignAnalyticsController::class, 'getUnsubscribers'])->int('id'); + + $router->get('{id}/contacts-by-segment', [CampaignAnalyticsController::class, 'getSegmentedContacts'])->int('id'); + + $router->put('{id}/update-labels', [CampaignController::class, 'updateLabels'])->int('id'); +}); + +$router->prefix('templates')->withPolicy('TemplatePolicy')->group(function ($router) { + + $router->get('/', [TemplateController::class, 'templates']); + $router->get('/all', [TemplateController::class, 'allTemplates']); + $router->get('/smartcodes', [TemplateController::class, 'getSmartCodes']); + $router->post('/', [TemplateController::class, 'create']); + + $router->get('{id}', [TemplateController::class, 'template'])->int('id'); + $router->put('{id}', [TemplateController::class, 'update'])->int('id'); + $router->post('/duplicate/{id}', [TemplateController::class, 'duplicate'])->int('id'); + $router->delete('{id}', [TemplateController::class, 'delete'])->int('id'); + $router->post('do-bulk-action', [TemplateController::class, 'handleBulkAction']); + + $router->post('set-global-style', [TemplateController::class, 'setGlobalStyle']); + $router->post('built-in-template', [TemplateController::class, 'getBuiltInTemplate']); + $router->get('/built-in-templates', [TemplateController::class, 'getBuiltInTemplates']); + +}); + +/* + * Email Patterns Route + */ +$router->prefix('email-patterns')->withPolicy('EmailPatternPolicy')->group(function ($router) { + $router->get('/', [EmailPatternController::class, 'index']); + $router->post('/', [EmailPatternController::class, 'store']); + $router->get('{id}', [EmailPatternController::class, 'show'])->int('id'); + $router->put('{id}', [EmailPatternController::class, 'update'])->int('id'); + $router->delete('{id}', [EmailPatternController::class, 'delete'])->int('id'); + $router->post('do-bulk-action', [EmailPatternController::class, 'handleBulkAction']); + + // wp_block-compatible endpoints for editor middleware interception + $router->get('/wp-format', [EmailPatternController::class, 'indexWpFormat']); + $router->post('/wp-format', [EmailPatternController::class, 'storeWpFormat']); + + // Pattern categories + $router->get('/categories', [EmailPatternController::class, 'getCategories']); + $router->post('/categories', [EmailPatternController::class, 'storeCategory']); + $router->delete('/categories/{id}', [EmailPatternController::class, 'deleteCategory'])->int('id'); +}); + +/* + * Funnels Route + */ +$router->prefix('funnels')->withPolicy('FunnelPolicy')->group(function ($router) { + + $router->get('/', [FunnelController::class, 'funnels']); + $router->post('/', [FunnelController::class, 'create']); + $router->get('templates', [FunnelController::class, 'getTemplates']); + $router->post('create-from-template', [FunnelController::class, 'createFromTemplate']); + $router->post('import', [FunnelController::class, 'importFunnel']); + + $router->get('all-activities', [FunnelController::class, 'getAllActivities']); + $router->post('remove-bulk-subscribers', [FunnelController::class, 'removeBulkSubscribers']); + + $router->get('triggers', [FunnelController::class, 'getTriggersRest']); + + $router->get('subscriber/{subscriber_id}/automations', [FunnelController::class, 'subscriberAutomations']); + + $router->post('funnel/save-funnel-sequences', [FunnelController::class, 'saveSequencesFallback']); + $router->post('funnel/save-email-action-fallback', [FunnelController::class, 'saveEmailActionFallback']); + + $router->get('{id}', [FunnelController::class, 'getFunnel'])->int('id'); + $router->post('{id}/clone', [FunnelController::class, 'cloneFunnel'])->int('id'); + $router->put('{id}', [FunnelController::class, 'updateFunnelProperty'])->int('id'); + $router->put('{id}/change-trigger', [FunnelController::class, 'changeTrigger'])->int('id'); + $router->post('{id}/sequences', [FunnelController::class, 'saveSequences'])->int('id'); + $router->put('funnel/{id}/title', [FunnelController::class, 'updateFunnelTitle'])->int('id'); + + $router->post('{id}/sequences/save-email-action', [FunnelController::class, 'saveEmailAction'])->int('id'); + + $router->get('{id}/subscribers', [FunnelController::class, 'getSubscribers'])->int('id'); + $router->get('{id}/subscribers/{contact_id}', [FunnelController::class, 'getSubscriberReporting'])->int('id')->int('contact_id'); + + $router->delete('{id}/subscribers', [FunnelController::class, 'deleteSubscribers'])->int('id'); + $router->delete('{id}', [FunnelController::class, 'delete'])->int('id'); + $router->get('{id}/report', [FunnelController::class, 'report'])->int('id'); + $router->post('do-bulk-action', [FunnelController::class, 'handleBulkAction']); + + + $router->get('{id}/email_reports', [FunnelController::class, 'getEmailReports'])->int('id'); + $router->put('{id}/subscribers/{subscriber_id}/status', [FunnelController::class, 'updateSubscriptionStatus'])->int('id')->int('subscriber_id'); + $router->post('{id}/subscribers/{subscriber_id}/advance', [FunnelController::class, 'forceAdvanceSubscriber'])->int('id')->int('subscriber_id'); + + $router->get('{id}/syncable-counts', [FunnelController::class, 'getSyncableContactCounts'])->int('id'); + $router->post('{id}/sync-new-steps', [FunnelController::class, 'syncNewSteps'])->int('id'); + + $router->post('send-test-webhook', [FunnelController::class, 'sendTestWebhook']); + + $router->put('{id}/update-labels', [FunnelController::class, 'updateLabels'])->int('id'); + +}); + +/* + * Reporting Route + */ +$router->prefix('reports')->withPolicy('ReportPolicy')->group(function ($router) { + + $router->get('dashboard-stats', [DashboardController::class, 'getStats']); + $router->get('subscribers', [ReportingController::class, 'getContactGrowth']); + $router->get('email-sents', [ReportingController::class, 'getEmailSentStats']); + $router->get('email-opens', [ReportingController::class, 'getEmailOpenStats']); + $router->get('email-clicks', [ReportingController::class, 'getEmailClickStats']); + $router->get('email-unsubs', [ReportingController::class, 'getEmailUnsubStats']); + $router->get('email-performance', [ReportingController::class, 'getEmailPerformance']); + + $router->get('options', [OptionsController::class, 'index']); + $router->get('ajax-options', [OptionsController::class, 'getAjaxOptions']); + $router->get('taxonomy-terms', [OptionsController::class, 'getTaxonomyTerms']); + $router->get('cascade_selections', [OptionsController::class, 'getCascadeSelections']); + + $router->get('emails', [ReportingController::class, 'getEmails']); + $router->delete('emails', [ReportingController::class, 'deleteEmails']); + + $router->get('advanced-providers', [ReportingController::class, 'getAdvancedReportProviders']); + + $router->get('contacts-by-status', [ReportingController::class, 'getContactsByStatus']); + $router->get('contacts-by-tags', [ReportingController::class, 'getContactsByTags']); + $router->get('contacts-by-lists', [ReportingController::class, 'getContactsByLists']); + $router->get('contacts-by-country', [ReportingController::class, 'getContactsByCountry']); + $router->get('recent-tags', [ReportingController::class, 'getRecentTags']); + $router->get('campaigns-list', [ReportingController::class, 'getCampaignsList']); + $router->get('campaign-options', [ReportingController::class, 'getCampaignOptions']); + $router->get('automations', [ReportingController::class, 'getAutomationReports']); + $router->get('automations/{id}/steps', [ReportingController::class, 'getAutomationStepReport']); + + $router->get('ping', [ReportingController::class, 'ping']); + +}); + +$router->prefix('setting')->withPolicy('SettingsPolicy')->group(function ($router) { + + $router->get('/', [SettingsController::class, 'get']); + $router->put('/', [SettingsController::class, 'save']); + $router->post('complete-installation', [SetupController::class, 'CompleteWizard']); + $router->get('double-optin', [SettingsController::class, 'getDoubleOptinSettings']); + $router->put('double-optin', [SettingsController::class, 'saveDoubleOptinSettings']); + + $router->post('install-fluentform', [SetupController::class, 'handleFluentFormInstall']); + $router->post('install-fluentsmtp', [SetupController::class, 'handleFluentSmtpInstall']); + $router->post('install-fluent-support', [SetupController::class, 'handleFluentSupportInstall']); + $router->post('install-fluent-boards', [SetupController::class, 'handleFluentBoardsInstall']); + $router->post('install-fluent-community', [SetupController::class, 'handleFluentCommunityInstall']); + $router->post('install-fluent-cart', [SetupController::class, 'handleFluentCartInstall']); + $router->post('install-fluent-booking', [SetupController::class, 'handleFluentBookingInstall']); + + $router->get('bounce_configs', [SettingsController::class, 'getBounceConfigs']); + + $router->get('auto_subscribe_settings', [SettingsController::class, 'getAutoSubscribeSettings']); + $router->post('auto_subscribe_settings', [SettingsController::class, 'saveAutoSubscribeSettings']); + + $router->get('test', [SettingsController::class, 'TestRequestResolver']); + $router->put('test', [SettingsController::class, 'TestRequestResolver']); + $router->post('test', [SettingsController::class, 'TestRequestResolver']); + $router->delete('test', [SettingsController::class, 'TestRequestResolver']); + + $router->post('reset_db', [SettingsController::class, 'resetDB']); + $router->get('old_logs', [SettingsController::class, 'getOldLogDetails']); + $router->delete('old_logs', [SettingsController::class, 'removeOldLogs']); + + $router->get('cron_status', [SettingsController::class, 'getCronStatus']); + $router->post('run_cron', [SettingsController::class, 'runCron']); + + $router->get('db-index-health', [SettingsController::class, 'getDbIndexHealth']); + $router->post('db-index-health/repair', [SettingsController::class, 'repairDbIndexes']); + + $router->get('rest-keys', [SettingsController::class, 'getRestKeys']); + $router->post('rest-keys', [SettingsController::class, 'createRestKey']); + $router->delete('rest-keys', [SettingsController::class, 'deleteRestKey']); + + + $router->get('integrations', [SettingsController::class, 'getIntegrations']); + $router->post('integrations', [SettingsController::class, 'saveIntegration']); + + $router->get('compliance', [SettingsController::class, 'getComplianceSettings']); + $router->post('compliance', [SettingsController::class, 'updateComplianceSettings']); + + $router->get('experiments', [SettingsController::class, 'getExperimentalSettings']); + $router->post('experiments', [SettingsController::class, 'updateExperimentalSettings']); + $router->get('experiments/campaigns', [SettingsController::class, 'getCampaigns']); + + $router->get('system-logs', [SystemLogController::class, 'index']); + $router->get('system-logs/export', [SystemLogController::class, 'export']); + $router->delete('system-logs/reset', [SystemLogController::class, 'deleteAll']); + + // will be added in future + // $router->get('activity-logs', [ActivityLogController::class, 'index']); + // $router->get('activity-logs/reset', [ActivityLogController::class, 'deleteAll']); + + $router->get('abandon-cart', [AbandonCartSettingsController::class, 'getSettings']); + $router->post('abandon-cart', [AbandonCartSettingsController::class, 'saveSettings']); + +}); + +$router->prefix('ai')->withPolicy('AiPolicy')->group(function ($router) { + $router->get('settings', [AiController::class, 'getSettings']); + $router->post('settings', [AiController::class, 'saveSettings']); + $router->post('models', [AiController::class, 'getModels']); + $router->post('test', [AiController::class, 'testConnection']); + $router->post('generate', [AiController::class, 'generate']); + $router->post('generate-email-body', [AiController::class, 'generateEmailBody']); + $router->post('contact-summary', [AiController::class, 'contactSummary']); +}); + +/* + * MCP settings endpoints — Settings → MCP admin page (MCP_PLAN.md § 13). + */ +$router->prefix('mcp')->withPolicy('SettingsPolicy')->group(function ($router) { + $router->get('status', [MCPSettingsController::class, 'status']); + $router->post('toggle', [MCPSettingsController::class, 'toggle']); + $router->post('install-adapter', [MCPSettingsController::class, 'installAdapter']); + $router->get('config-snippet', [MCPSettingsController::class, 'getConfigSnippet']); +}); + +$router->prefix('abandon-carts')->withPolicy('FunnelPolicy')->group(function ($router) { + $router->get('/', [AbandonCartController::class, 'getCarts']); + $router->post('bulk-delete', [AbandonCartController::class, 'handleBulkDeleteCart']); + $router->get('report-summary', [AbandonCartController::class, 'getReportSummary']); +}); + +$router->prefix('custom-fields')->withPolicy('CustomFieldsPolicy')->group(function ($router) { + $router->get('contacts', [CustomContactFieldsController::class, 'getGlobalFields']); + $router->put('contacts', [CustomContactFieldsController::class, 'saveGlobalFields']); + $router->put('contacts/update_group_name', [CustomContactFieldsController::class, 'updateGroupName']); +}); + +$router->prefix('labels')->withPolicy('CustomFieldsPolicy')->group(function ($router) { + $router->get('/', [GlobalLabelController::class, 'getlabels']); + $router->post('/', [GlobalLabelController::class, 'create']); + $router->put('{id}', [GlobalLabelController::class, 'update'])->int('id'); + $router->delete('{id}', [GlobalLabelController::class, 'delete'])->int('id'); +}); + +$router->prefix('webhooks')->withPolicy('WebhookPolicy')->group(function ($router) { + $router->get('/', [WebhookController::class, 'index']); + $router->post('/', [WebhookController::class, 'create']); + $router->put('/{id}', [WebhookController::class, 'update'])->int('id'); + $router->delete('/{id}', [WebhookController::class, 'delete'])->int('id'); +}); + +/* + * Users + */ +$router->prefix('users')->withPolicy('UsersPolicy')->group(function ($router) { + + $router->get('/', [UsersController::class, 'index']); + $router->get('/roles', [UsersController::class, 'roles']); + +}); + +/* + * Import + */ +$router->prefix('import')->withPolicy('ImportUserPolicy')->group(function ($router) { + + $router->post('csv-upload', [CsvController::class, 'upload']); + $router->post('csv-import', [CsvController::class, 'import']); + + $router->post('users', [UsersController::class, 'import']); + + $router->get('drivers', [ImporterController::class, 'getDrivers']); + $router->get('drivers/{driver}', [ImporterController::class, 'getDriver'])->alphaNumDash('driver'); + $router->post('drivers/{driver}', [ImporterController::class, 'importData'])->alphaNumDash('driver'); + +}); + + +/* + * Fluent Forms Wrapper + */ +$router->prefix('forms')->withPolicy('FormsPolicy')->group(function ($router) { + $router->get('/', [FormsController::class, 'index']); + $router->post('/', [FormsController::class, 'create']); + $router->get('templates', [FormsController::class, 'getTemplates']); + $router->get('{id}/entries', [FormsController::class, 'getEntries'])->int('id'); + $router->get('{form_id}/entries/{id}', [FormsController::class, 'getEntry'])->int('form_id')->int('id'); +}); + + +/* + * Fluent Forms Wrapper + */ +$router->prefix('docs')->withPolicy('ReportPolicy')->group(function ($router) { + $router->get('/', [DocsController::class, 'index']); + $router->get('/{doc_id}', [DocsController::class, 'getDoc'])->int('doc_id'); + $router->get('/addons', [DocsController::class, 'getAddons']); +}); + +/* + * Public EndPoints + */ +$router->prefix('public')->withPolicy('PublicPolicy')->group(function ($router) { + + $router->any('bounce_handler/{service_name}/handle/{security_code}', [WebhookBounceController::class, 'handleBounce']) + ->alphaNumDash('service_name') + ->alphaNumDash('security_code'); + + $router->any('bounce_handler/{service_name}/{security_code}', [WebhookBounceController::class, 'handleBounce']) + ->alphaNumDash('service_name') + ->alphaNumDash('security_code'); + +}); + + +$router->prefix('migrators')->withPolicy('SettingsPolicy')->group(function ($router) { + $router->get('/', [MigratorController::class, 'getDrivers']); + $router->post('/verify-cred', [MigratorController::class, 'verifyCredential']); + $router->get('/list-tag-mappings', [MigratorController::class, 'getListTagMappings']); + + $router->post('/summary', [MigratorController::class, 'getImportSummary']); + $router->post('/import', [MigratorController::class, 'handleImport']); +}); + +$router->prefix('companies')->withPolicy('CompanyPolicy')->group(function ($router) { + $router->get('/', [CompanyController::class, 'index']); + $router->post('/', [CompanyController::class, 'create']); + $router->get('/{id}', [CompanyController::class, 'find'])->int('id'); + $router->put('/{id}', [CompanyController::class, 'update'])->int('id'); + $router->delete('/{id}', [CompanyController::class, 'delete'])->int('id'); + + $router->get('/search', [CompanyController::class, 'searchCompanies']); + $router->get('/search-unattached-contacts', [CompanyController::class, 'searchUnattachedContacts']); + $router->put('companies-property', [CompanyController::class, 'updateProperty']); + $router->post('attach-subscribers', [CompanyController::class, 'attachSubscribers']); + $router->post('detach-subscribers', [CompanyController::class, 'detachSubscribers']); + $router->post('do-bulk-action', [CompanyController::class, 'handleBulkActions']); + + $router->get('{id}/notes', [CompanyController::class, 'getNotes'])->int('id'); + $router->post('{id}/notes', [CompanyController::class, 'addNote'])->int('id'); + $router->put('{id}/notes/{note_id}', [CompanyController::class, 'updateNote'])->int('id')->int('note_id'); + $router->delete('{id}/notes/{note_id}', [CompanyController::class, 'deleteNote'])->int('id')->int('note_id'); + $router->post('{id}/notes/bulk-delete', [CompanyController::class, 'bulkDeleteNotes'])->int('id'); + + $router->post('csv-import', [CsvController::class, 'importCompanies']); + + $router->get('custom-fields', [CompanyController::class, 'getCustomGlobalFields']); + $router->put('custom-fields', [CompanyController::class, 'saveCustomGlobalFields']); + $router->put('custom-fields/update_group_name', [CompanyController::class, 'updateCustomFieldGroupName']); + + $router->get('{id}/custom_tab_view', [CompanyController::class, 'getCompanyExternalView'])->int('id'); +}); diff --git a/wp-content/plugins/fluent-crm/app/Http/Routes/routes.php b/wp-content/plugins/fluent-crm/app/Http/Routes/routes.php new file mode 100644 index 0000000..ab7d04c --- /dev/null +++ b/wp-content/plugins/fluent-crm/app/Http/Routes/routes.php @@ -0,0 +1,8 @@ +namespace('FluentCrm\App\Http\Controllers')->group(function($router) { + require_once __DIR__ . '/api.php'; +}); diff --git a/wp-content/plugins/fluent-crm/app/Models/ActivityLog.php b/wp-content/plugins/fluent-crm/app/Models/ActivityLog.php new file mode 100644 index 0000000..695c9f2 --- /dev/null +++ b/wp-content/plugins/fluent-crm/app/Models/ActivityLog.php @@ -0,0 +1,70 @@ + 'array', // Automatically decodes JSON to array + // Use 'object' instead of 'array' if you prefer stdClass objects + ]; + + public static function boot() + { + parent::boot(); + + static::creating(function ($model) { + if (empty($model->created_at)) { + $model->created_at = fluentCrmTimestamp(); + } + + if (empty($model->activity_by)) { + $model->activity_by = 0; + } + + $model->updated_at = fluentCrmTimestamp(); + }); + + static::updated(function ($model) { + $model->updated_at = fluentCrmTimestamp(); + }); + } + + public function getActivityByEmailAttribute() + { + $user = User::where('ID', $this->activity_by)->first(); + if (!$user) { + return null; + } + return $user->display_name . ' (' . $user->user_email . ')'; + } + +} diff --git a/wp-content/plugins/fluent-crm/app/Models/Campaign.php b/wp-content/plugins/fluent-crm/app/Models/Campaign.php new file mode 100644 index 0000000..99ea5bb --- /dev/null +++ b/wp-content/plugins/fluent-crm/app/Models/Campaign.php @@ -0,0 +1,1013 @@ +design_template ? $model->design_template : Helper::getDefaultEmailTemplate(); + $model->email_body = $model->email_body ?: ''; + $model->status = $model->status ?: 'draft'; + $model->type = static::$type; + $model->design_template = $defaultTemplate; + $model->slug = $model->slug ?: sanitize_title($model->title, '', 'preview'); + $model->created_by = $model->created_by ?: get_current_user_id(); + $model->settings = $model->settings ?: [ + 'mailer_settings' => [ + 'from_name' => '', + 'from_email' => '', + 'reply_to_name' => '', + 'reply_to_email' => '', + 'is_custom' => 'no' + ], + 'subscribers' => [ + [ + 'list' => 'all', + 'tag' => 'all' + ] + ], + 'excludedSubscribers' => [ + [ + 'list' => null, + 'tag' => null + ] + ], + 'sending_filter' => 'list_tag', + 'dynamic_segment' => [ + 'id' => '', + 'slug' => '' + ], + 'advanced_filters' => [[]], + 'template_config' => Helper::getTemplateConfig($defaultTemplate), + 'sending_type' => 'instant', + 'is_transactional' => 'no' + ]; + }); + + static::addGlobalScope('type', function ($builder) { + $builder->where('type', '=', static::$type); + }); + } + + public function setSlugAttribute($slug) + { + $this->attributes['slug'] = \sanitize_title($slug, '', 'preview'); + } + + public function setSettingsAttribute($settings) + { + $this->attributes['settings'] = \maybe_serialize($settings); + } + + public function getSettingsAttribute($settings) + { + $settings = \maybe_unserialize($settings); + $settings = is_array($settings) ? $settings : []; + $templateConfig = Arr::get($settings, 'template_config', []); + + $defaultConfig = Helper::getTemplateConfig($this->design_template, false); + $templateConfig = wp_parse_args($templateConfig, $defaultConfig); + $templateConfig['design_template'] = $this->design_template; + $footerDefaults = [ + 'disable_footer' => 'no', + 'custom_footer' => 'no', + 'footer_content' => '', + 'font_size' => 13, + 'font_color' => '#202020', + 'background_color' => 'transparent', + 'footer_padding' => 20 + ]; + + $footerSettings = Arr::get($settings, 'footer_settings', []); + $footerSettings = is_array($footerSettings) ? $footerSettings : []; + + // Backward compatibility: older imports may only carry disable_footer in template_config. + if (!isset($footerSettings['disable_footer'])) { + $legacyDisable = Arr::get($templateConfig, 'disable_footer'); + if ($legacyDisable === 'yes' || $legacyDisable === 'no') { + $footerSettings['disable_footer'] = $legacyDisable; + } + } + + if (!isset($footerSettings['custom_footer'])) { + $legacyFooterContent = Arr::get($footerSettings, 'footer_content', ''); + if (is_string($legacyFooterContent) && trim(wp_strip_all_tags($legacyFooterContent))) { + $footerSettings['custom_footer'] = 'yes'; + } + } + + $footerSettings = wp_parse_args($footerSettings, $footerDefaults); + $footerSettings['disable_footer'] = ($footerSettings['disable_footer'] === 'yes') ? 'yes' : 'no'; + $footerSettings['custom_footer'] = ($footerSettings['custom_footer'] === 'yes') ? 'yes' : 'no'; + $settings['footer_settings'] = $footerSettings; + $templateConfig['disable_footer'] = $footerSettings['disable_footer']; + $settings['template_config'] = $templateConfig; + + $mailerDefaults = [ + 'from_name' => '', + 'from_email' => '', + 'reply_to_name' => '', + 'reply_to_email' => '', + 'is_custom' => 'no' + ]; + + $mailerSettings = Arr::get($settings, 'mailer_settings', []); + $mailerSettings = wp_parse_args($mailerSettings, $mailerDefaults); + $settings['mailer_settings'] = $mailerSettings; + + return $settings; + } + + public function getRecipientsCountAttribute($recipientsCount) + { + return (int)$recipientsCount; + } + + public function getRenderedBodyAttribute() + { + return (new Template)->render($this->body); + } + + // Now using a single subject, get the first one + public function getSubjectAttribute() + { + if ($firstSubject = $this->subjects()->first()) { + return $firstSubject->value; + } + return $this->email_subject; + } + + public function syncSubjects($subjects) + { + $validSubjectIds = []; + foreach ($subjects as $subject) { + if (empty($subject['value']) || empty($subject['key'])) { + continue; + } + if (empty($subject['id'])) { + $data = Arr::only($subject, ['key', 'value']); + $data['object_id'] = $this->id; + $inserted = Subject::create($data); + $validSubjectIds[] = $inserted->id; + } else { + $subjectItem = Subject::where('id', intval($subject['id'])) + ->where('object_id', $this->id) + ->first(); + + if ($subjectItem) { + $subjectItem->fill(Arr::only($subject, ['key', 'value']))->save(); + $validSubjectIds[] = $subjectItem->id; + } + } + } + + if ($validSubjectIds) { + // remove old subjects + Subject::whereNotIn('id', $validSubjectIds) + ->where('object_id', $this->id) + ->delete(); + } else { + Subject::where('object_id', $this->id) + ->delete(); + } + + return $this->subjects(); + } + + public function duplicateSubjects(Campaign $campaign) + { + + $subjects = $campaign->subjects; + if (!$subjects) { + return; + } + + $formattedSubjects = []; + foreach ($subjects as $subject) { + $formattedSubjects[] = [ + 'key' => $subject->key, + 'value' => $subject['value'] + ]; + } + if ($formattedSubjects) { + $this->syncSubjects($formattedSubjects); + } + } + + public function scopeOfType($query, $status) + { + return $query->where('status', $status); + } + + public function scopeArchived($query) + { + return $query->where('status', 0); + } + + + /** + * @return \FluentCrm\Framework\Database\Orm\Relations\BelongsTo + */ + public function template() + { + return $this->belongsTo(__NAMESPACE__ . '\Template', 'template_id', 'ID'); + } + + /** + * One2Many: Campaign has many emails + * + * @return \FluentCrm\Framework\Database\Orm\Relations\hasMany + */ + public function emails() + { + return $this->hasMany( + __NAMESPACE__ . '\CampaignEmail', 'campaign_id', 'id' + ); + } + + /** + * TODO: emails should be filtered by status (draft, queue e.t.c.) + * One2Many: Campaign has many emails + * @return \FluentCrm\Framework\Database\Orm\Relations\hasMany + */ + public function campaign_emails() + { + return $this->hasMany( + __NAMESPACE__ . '\CampaignEmail', 'campaign_id', 'id' + )->where('email_type', 'campaign'); + } + + /** + * One2Many: Campaign has many subjects + * @return \FluentCrm\Framework\Database\Orm\Relations\hasMany + */ + public function subjects() + { + return $this->hasMany(__NAMESPACE__ . '\Subject', 'object_id', 'id'); + } + + /** + * Add one or more subscribers to the campaign by list with filtering + * @param $settings + * @param bool $limit + * @param int $offset + * @return array + */ + public function subscribeBySegment($settings, $limit = false, $offset = 0) + { + $model = $this->getSubscribersModel($settings); + + $totalCount = $model->count(); + + if ($limit) { + $model->limit($limit); + } + + if ($offset) { + $model->offset($offset); + } + + $result = $this->subscribe($model, [], true); + + return [ + 'result' => ($result) ? $result : 0, + 'total_subscribed' => count($result), + 'total_items' => $totalCount + ]; + } + + public function getSubscribersModel($settings = false) + { + if (!$settings) { + $settings = $this->settings; + } + + $filterType = Arr::get($settings, 'sending_filter', 'list_tag'); + + if ($filterType == 'list_tag') { + $subscriberModel = $this->getSubscribeIdsByListModel($settings['subscribers'], 'subscribed'); + if ($excludeItems = Arr::get($settings, 'excludedSubscribers')) { + $formattedExcludedItems = []; + foreach ($excludeItems as $item) { + if (empty($item['list']) && empty($item['tag'])) { + continue; + } + $formattedExcludedItems[] = $item; + } + + if ($formattedExcludedItems) { + $excludedModel = $this->getSubscribeIdsByListModel($excludeItems, 'subscribed'); + $excludedModel->select('id'); + $subscriberModel->whereNotIn('id', $excludedModel->getQuery()); + } + } + + return $subscriberModel; + } + + if ($filterType == 'dynamic_segment') { + $segmentSettings = Arr::get($settings, 'dynamic_segment', []); + $segmentSettings['offset'] = 0; + $segmentSettings['limit'] = false; + + /** + * Filter the dynamic segment details based on the segment slug. + * + * This filter allows you to modify the details of a dynamic segment. + * + * @param array The details of the dynamic segment. + * @param int $segmentSettings ['id'] The ID of the segment. + * @param array { + * Additional context for the segment. + * + * @type bool Whether to include the model in the context. + * } + * + * @return array Modified segment details. + * @since 2.5.93 + * + */ + $segmentDetails = apply_filters('fluentcrm_dynamic_segment_' . $segmentSettings['slug'], [], $segmentSettings['id'], [ + 'model' => true + ]); + + if (!empty($segmentDetails['model'])) { + $model = $segmentDetails['model']; + $model->where('status', 'subscribed'); + return $model; + } + + return null; + } + + if ($filterType == 'advanced_filters') { + $query = new ContactsQuery([ + 'with' => [], + 'filter_type' => 'advanced', + 'contact_status' => 'subscribed', + 'filters_groups_raw' => $settings['advanced_filters'] + ]); + + return $query->getModel(); + } + + return null; + } + + public function getSubscriberIdsBySegmentSettings($settings, $limit = false, $offset = 0) + { + $model = $this->getSubscribersModel($settings); + + if (!$model) { + return [ + 'subscriber_ids' => [], + 'total_count' => 0 + ]; + } + + $totalCount = $model->count(); + + if ($limit) { + $model->limit($limit); + } + + if ($offset) { + $model->offset($offset); + } + + return [ + 'subscriber_ids' => $model->get()->pluck('id')->toArray(), + 'total_count' => $totalCount + ]; + } + + public function getSubscriberIdsCountBySegmentSettings($settings, $status = 'subscribed') + { + $model = $this->getSubscribersModel($settings); + if ($model) { + return $model->count(); + } + + return 0; + } + + + /** + * @param $query + * @param $ids + * @param $table + * @param $objectType + * @return mixed + */ + private function getSubQueryForLisTorTagFilter($query, $ids, $table, $objectType) + { + $prefix = 'fc_'; + + return $query->from($prefix . $table) + ->join( + $prefix . 'subscriber_pivot', + $prefix . 'subscriber_pivot.object_id', + '=', + $prefix . $table . '.id' + ) + ->where($prefix . 'subscriber_pivot.object_type', $objectType) + ->whereIn($prefix . $table . '.id', $ids) + ->groupBy($prefix . 'subscriber_pivot.subscriber_id') + ->select($prefix . 'subscriber_pivot.subscriber_id'); + } + + /** + * Get subscribers ids to by list with tag filtering + * @param array $items + * @param string $status contact status + * @param int|boolean $limit limit + * @param int $offset contact offset + * @return array + */ + public function getSubscribeIdsByList($items, $status = 'subscribed', $limit = false, $offset = 0) + { + $model = $this->getSubscribeIdsByListModel($items, $status, $limit, $offset); + $results = $model->get(); + $ids = []; + + foreach ($results as $result) { + $ids[] = $result->id; + } + + return $ids; + } + + /** + * Get subscribers count to by list with tag filtering + * @param array $items + * @param string $status contact status + * @param int|boolean $limit limit + * @param int $offset contact offset + * @return int + */ + public function getSubscribeIdsByListCount($items, $status = 'subscribed', $limit = false, $offset = 0) + { + $model = $this->getSubscribeIdsByListModel($items, $status, $limit, $offset); + return $model->count(); + } + + public function getSubscribeIdsByListModel($items, $status = 'subscribed', $limit = false, $offset = 0) + { + + $query = Subscriber::where('status', $status); + + $queryGroups = []; + + $willSkip = false; + + $hasListFilter = false; + $tagIds = []; + foreach ($items as $item) { + $listId = $item['list']; + $tagId = $item['tag']; + if (!$listId || !$tagId) { + continue; + } + + if ($listId == 'all' && $tagId == 'all') { + $willSkip = true; + } else if ($listId == 'all') { + $queryGroups[] = ['tag_id' => $tagId]; + $tagIds[] = $tagId; + } else if ($tagId == 'all') { + $hasListFilter = true; + $queryGroups[] = ['list_id' => $listId]; + } else { + $hasListFilter = true; + $tagIds[] = $tagId; + $queryGroups[] = [ + 'list_id' => $listId, + 'tag_id' => $tagId + ]; + } + } + + if (!$willSkip && !$hasListFilter && $tagIds) { + $query->filterByTags($tagIds); + } else if (!$willSkip && $queryGroups) { + $query->where(function ($innerQuery) use ($queryGroups) { + $type = 'where'; + foreach ($queryGroups as $queryGroup) { + $innerQuery->{$type}(function ($q) use ($queryGroup, $innerQuery) { + foreach ($queryGroup as $type => $id) { + if ($type == 'tag_id') { + $q->whereIn('id', function ($query) use ($id) { + return $this->getSubQueryForLisTorTagFilter($query, [$id], 'tags', 'FluentCrm\App\Models\Tag'); + }); + } else if ($type == 'list_id') { + $q->whereIn('id', function ($query) use ($id) { + return $this->getSubQueryForLisTorTagFilter($query, [$id], 'lists', 'FluentCrm\App\Models\Lists'); + }); + } + } + }); + + $type = 'orWhere'; + } + }); + } + + if ($limit) { + $query->limit($limit)->offset($offset); + } + + return $query; + + } + + /** + * Add one or more subscribers to the campaign + * @param array $subscriberIds + * @param array $emailArgs extra campaign_email args + * @param bool $isModel if the $subscriberIds is collection or not + * @return array + */ + public function subscribe($subscriberIds, $emailArgs = [], $isModel = false) + { + $updateIds = []; + + $mailHeaders = Helper::getMailHeadersFromSettings(Arr::get($this->settings, 'mailer_settings', [])); + + if ($isModel) { + $subscribers = $subscriberIds; + } else { + $subscribers = Subscriber::whereIn('id', $subscriberIds)->get(); + } + + $validStatuses = ['subscribed', 'transactional']; + + foreach ($subscribers as $subscriber) { + if (!in_array($subscriber->status, $validStatuses)) { + continue; // We don't want to send emails to non-subscribed members + } + + $time = fluentCrmTimestamp(); + $email = [ + 'campaign_id' => $this->id, + 'status' => $this->status, + 'subscriber_id' => $subscriber->id, + 'email_address' => $subscriber->email, + 'email_headers' => $mailHeaders, + 'email_hash' => Helper::generateEmailHash(), + 'created_at' => $time, + 'updated_at' => $time + ]; + + $subjectItem = $this->guessEmailSubject(); + $emailSubject = $this->email_subject; + + if ($subjectItem && !empty($subjectItem->value)) { + $emailSubject = $subjectItem->value; + $email['email_subject_id'] = $subjectItem->id; + } + + /** + * Filter the campaign email subject text. + * + * This filter allows you to modify the email subject text for a campaign. + * + * @param string $emailSubject The original email subject text. + * @param object $subscriber The subscriber object. + * + * @return string The filtered email subject text. + */ + $email['email_subject'] = apply_filters('fluent_crm/parse_campaign_email_text', $emailSubject, $subscriber); + + $email['email_body'] = $this->email_body; + + if ($emailArgs) { + $email = wp_parse_args($emailArgs, $email); + } + + $inserted = CampaignEmail::create($email); + + $subscriber->campaign_id = $this->id; + $subscriber->email_id = $inserted->id; + + $updateIds[] = $inserted->id; + } + + $emailCount = $this->getEmailCount(); + if ($emailCount != $this->recipients_count) { + $this->recipients_count = $emailCount; + $this->save(); + } + + return $updateIds; + } + + /** + * Remove one or more subscribers from the campaign + * @param array $subscriberIds + * @return bool + */ + public function unsubscribe($subscriberIds) + { + $result = $this->emails()->whereIn('subscriber_id', $subscriberIds)->delete(); + + $this->recipients_count = $this->emails()->count(); + + $this->save(); + + return $result; + } + + /** + * Guess the subject by probability formula + * @return Model Object or null + */ + public function guessEmailSubject() + { + // Cache subjects per campaign to avoid repeated DB queries during batch processing. + // The weighted random selection still runs per call for proper A/B distribution. + static $subjectsCache = []; + + if (isset($subjectsCache[$this->id])) { + $subjects = $subjectsCache[$this->id]; + } else { + $subjects = $this->subjects()->get(); + $subjectsCache[$this->id] = $subjects; + } + + if ($subjects->isEmpty()) { + return null; + } + + $priorities = $subjects->pluck('key')->toArray(); + $count = count($priorities); + $num = wp_rand(0, array_sum($priorities)); + + $i = $n = 0; + while ($i < $count) { + $n += $priorities[$i]; + if ($n >= $num) break; + $i++; + } + + return isset($subjects[$i]) ? $subjects[$i] : null; + } + + public function getParsedText($text, $subscriber) + { + return Parser::parse($text, $subscriber); + } + + public function filterDuplicateSubscribers($subscriberIds, $subscribers) + { + $existingIds = CampaignEmail::where('campaign_id', $this->id) + ->whereIn('subscriber_id', $subscribers->pluck('id')->toArray()) + ->get()->pluck('subscriber_id')->toArray(); + + return $subscribers->filter(function ($subscriber) use ($existingIds) { + return !in_array($subscriber->id, $existingIds); + }); + } + + public function archive() + { + $this->status = 0; + $this->save(); + return $this; + } + + public function getUtmParams() + { + if ($this->utm_status) { + return array_filter([ + 'utm_source' => $this->utm_source, + 'utm_medium' => $this->utm_medium, + 'utm_campaign' => $this->utm_campaign, + 'utm_term' => $this->utm_term, + 'utm_content' => $this->utm_content + ]); + } + + return []; + } + + public function stats() + { + $totalEmails = CampaignEmail::where('campaign_id', $this->id) + ->count(); + + $totalSent = CampaignEmail::where('campaign_id', $this->id) + ->where('status', 'sent') + ->count(); + + if ($this->getOpenTrackingStatus(false) === 'anonymous') { + $views = fluentcrm_get_campaign_meta($this->id, '_ano_open_count', true); + if (!$views) { + $views = 0; + } + } else { + $views = CampaignEmail::where('campaign_id', $this->id) + ->where('is_open', 1) + ->count(); + } + + if ($this->getClickTrackingStatus(false) === 'anonymous') { + $clickItems = fluentcrm_get_campaign_meta($this->id, '_ano_url_clicks', true); + $clicks = 0; + if ($clickItems && is_array($clickItems)) { + $clicks = array_sum($clickItems); + } + } else { + $clicks = CampaignEmail::where('campaign_id', $this->id) + ->whereNotNull('click_counter') + ->count(); + } + + $unSubscribed = CampaignUrlMetric::where('campaign_id', $this->id) + ->where('type', 'unsubscribe') + ->distinct() + ->count('subscriber_id'); + + $revenue = fluentcrm_get_campaign_meta($this->id, '_campaign_revenue'); + + $stats = [ + 'total' => $totalEmails, + 'sent' => $totalSent, + 'clicks' => $clicks, + 'views' => $views, + 'unsubscribers' => $unSubscribed + ]; + + if ($revenue && $revenue->value) { + $data = (array)$revenue->value; + foreach ($data as $currency => $cents) { + if ($cents && $currency !== 'orderIds') { + $stats['revenue'] = [ + 'label' => __('Revenue', 'fluent-crm') . ' (' . $currency . ')', + 'total' => number_format($cents / 100, 2), + 'currency' => $currency + ]; + } + } + } + + return $stats; + + } + + public function getEmailCount() + { + return fluentCrmDb()->table('fc_campaign_emails') + ->where('campaign_id', $this->id) + ->count(); + } + + public function maybeDeleteDuplicates() + { + global $wpdb; + $table = $wpdb->prefix . 'fc_campaign_emails'; + + // Quick check: do any duplicates exist? Most campaigns won't have any. + // Exclude NULL subscriber_ids — SQL NULL != NULL so the self-join can't match them. + $hasDuplicates = $wpdb->get_var($wpdb->prepare( + "SELECT 1 FROM {$table} WHERE campaign_id = %d AND subscriber_id IS NOT NULL GROUP BY subscriber_id HAVING COUNT(*) > 1 LIMIT 1", + $this->id + )); + + if (!$hasDuplicates) { + return $this; + } + + // Delete duplicates, keeping the row with the lowest id per subscriber. + $deleted = $wpdb->query($wpdb->prepare( + "DELETE e1 FROM {$table} e1 + INNER JOIN {$table} e2 + ON e1.campaign_id = e2.campaign_id + AND e1.subscriber_id = e2.subscriber_id + AND e1.id > e2.id + WHERE e1.campaign_id = %d + AND e1.subscriber_id IS NOT NULL", + $this->id + )); + + if ($deleted) { + $emailCount = $this->getEmailCount(); + if ($emailCount != $this->recipients_count) { + $this->recipients_count = $emailCount; + $this->save(); + } + } + + return $this; + } + + public function getHash() + { + $hash = fluentcrm_get_campaign_meta($this->id, '_campaign_hash', true); + + if ($hash) { + return $hash; + } + + $hash = md5(wp_rand(100, 10000) . '_' . $this->id . '_' . $this->title . '_' . time() . '_' . wp_generate_uuid4()); + $hash = str_replace('e', 'd', $hash); + fluentcrm_update_campaign_meta($this->id, '_campaign_hash', $hash); + + return $hash; + } + + public function deleteCampaignData() + { + CampaignEmail::where('campaign_id', $this->id)->delete(); + CampaignUrlMetric::where('campaign_id', $this->id)->delete(); + + Meta::where('object_id', $this->id) + ->where('object_type', 'FluentCrm\App\Models\Campaign') + ->delete(); + + return $this; + } + + public function rangedScheduleDates() + { + $settings = $this->settings; + + if (Arr::get($settings, 'sending_type') != 'range_schedule') { + return null; + } + + + $ranges = Arr::get($settings, 'schedule_range', ['', '']); + + if (!$ranges) { + return null; + } + + return [ + 'start' => gmdate('Y-m-d H:i:s', $ranges[0]), + 'end' => gmdate('Y-m-d H:i:s', $ranges[1]) + ]; + } + + public function getEmailScheduleAt() + { + $settings = $this->settings; + + if (Arr::get($settings, 'sending_type') != 'range_schedule') { + return $this->scheduled_at; + } + + // this is a range selector + $ranges = Arr::get($settings, 'schedule_range', [$this->scheduled_at, $this->scheduled_at]); + + $timeStamp = random_int($ranges[0], $ranges[1]); + + if ($timeStamp < current_time('timestamp')) { + $timeStamp = current_time('timestamp') + 60; + } + + return gmdate('Y-m-d H:i:s', $timeStamp); + } + + public function getShareableUrl() + { + $shareId = fluentcrm_get_campaign_meta($this->id, '_campaign_share_id', true); + if (!$shareId) { + $shareId = md5($this->getHash() . '_' . $this->id . '_' . time()); + fluentcrm_update_campaign_meta($this->id, '_campaign_share_id', $shareId); + } + + return add_query_arg([ + 'fluentcrm' => 1, + 'route' => 'email_preview', + 'fc_newsletter' => $shareId + ], site_url()); + } + + public function labelsTerm() + { + return $this->belongsToMany(Label::class, 'fc_term_relations', 'object_id', 'term_id') + ->wherePivot('object_type', __CLASS__); + } + + public function labels() + { + $labelIds = TermRelation::where('object_id', $this->id) + ->where('object_type', __CLASS__) + ->pluck('term_id') + ->toArray(); + return Label::whereIn('id', $labelIds)->get(); + } + + public function getFormattedLabels() + { + $labels = $this->labels(); + return $labels->map(function ($label) { + return [ + 'id' => $label->id, + 'slug' => $label->slug, + 'title' => $label->title, + 'color' => $label->settings['color'] ?? '' + ]; + }); + } + + public function attachLabels($labelIds) + { + $labelIds = is_array($labelIds) ? $labelIds : [$labelIds]; + + if (empty($labelIds)) { + return $this; + } + + $existingLabelIds = TermRelation::where('object_id', $this->id) + ->where('object_type', __CLASS__) + ->pluck('term_id') + ->toArray(); + + $newLabelIds = array_diff($labelIds, $existingLabelIds); + + if (!empty($newLabelIds)) { + foreach ($newLabelIds as $labelId) { + TermRelation::create([ + 'object_id' => $this->id, + 'object_type' => __CLASS__, + 'term_id' => $labelId + ]); + } + } + + return $this; + } + + public function detachLabels($labelIds) + { + $labelIds = is_array($labelIds) ? $labelIds : [$labelIds]; + + if (empty($labelIds)) { + return $this; + } + + TermRelation::where('object_id', $this->id) + ->where('object_type', __CLASS__) + ->whereIn('term_id', $labelIds) + ->delete(); + + return $this; + } + + public function getOpenTrackingStatus($globalFallback = true) + { + $settings = $this->settings; + if (isset($settings['open_tracker'])) { + $status = $settings['open_tracker']; + return $status; + } + + if ($globalFallback) { + $status = fluentcrmTrackEmailOpen(); + return $status; + } + + return null; + } + + public function getClickTrackingStatus($globalFallback = true) + { + $settings = $this->settings; + if (isset($settings['click_tracker'])) { + $status = $settings['click_tracker']; + return $status; + } + + if ($globalFallback) { + $status = fluentcrmTrackClicking(); + return $status; + } + + return null; + } + +} diff --git a/wp-content/plugins/fluent-crm/app/Models/CampaignEmail.php b/wp-content/plugins/fluent-crm/app/Models/CampaignEmail.php new file mode 100644 index 0000000..a68d4c5 --- /dev/null +++ b/wp-content/plugins/fluent-crm/app/Models/CampaignEmail.php @@ -0,0 +1,667 @@ +> + */ + public static function getEmailTypeAliases() + { + return [ + 'funnel_email_campaign' => ['funnel_email_campaign', 'automation'], + 'recurring_campaign' => ['recurring_campaign', 'recurring_email_campaign'], + 'custom_email_campaign' => ['custom_email_campaign', 'custom_email'], + 'campaign' => ['campaign'], + 'sequence' => ['sequence', 'email_sequence', 'sequence_email'], + ]; + } + + /** + * Map canonical email types to the user-facing labels used in reporting. + * + * @return array + */ + public static function getEmailTypeLabels() + { + return [ + 'funnel_email_campaign' => __('Automation', 'fluent-crm'), + 'recurring_campaign' => __('Recurring Campaign', 'fluent-crm'), + 'custom_email_campaign' => __('Custom Email', 'fluent-crm'), + 'campaign' => __('Campaign', 'fluent-crm'), + 'sequence' => __('Sequence', 'fluent-crm'), + ]; + } + + /** + * Resolve the canonical email type key for filtering and reporting. + * + * @param string|null $emailType + * @return string + */ + public static function normalizeEmailType($emailType) + { + $emailType = sanitize_text_field((string)$emailType); + + foreach (static::getEmailTypeAliases() as $canonicalType => $aliases) { + if (in_array($emailType, $aliases, true)) { + return $canonicalType; + } + } + + return $emailType ?: 'campaign'; + } + + /** + * Expand selected canonical types into the raw slugs stored in email rows. + * + * @param array $selectedTypes + * @return array + */ + public static function expandEmailTypes(array $selectedTypes) + { + $expandedTypes = []; + $aliases = static::getEmailTypeAliases(); + + foreach ($selectedTypes as $selectedType) { + $canonicalType = static::normalizeEmailType($selectedType); + $expandedTypes = array_merge($expandedTypes, $aliases[$canonicalType] ?? [$canonicalType]); + } + + return array_values(array_unique($expandedTypes)); + } + + /** + * Resolve the human-readable email type label for reports and tables. + * + * @return string + */ + public function getEmailTypeLabelAttribute() + { + return static::resolveEmailTypeLabel($this->email_type); + } + + /** + * Convert a stored email type slug into a stable UI label. + * + * @param string|null $emailType + * @return string + */ + public static function resolveEmailTypeLabel($emailType) + { + $emailType = static::normalizeEmailType($emailType); + $labels = static::getEmailTypeLabels(); + + if (isset($labels[$emailType])) { + return $labels[$emailType]; + } + + return ucwords(str_replace(['_', '-'], ' ', $emailType ?: 'campaign')); + } + + /** + * One2One: CampaignEmail belongs to one Campaign + * @return Model + */ + public function campaign() + { + return $this->belongsTo( + __NAMESPACE__ . '\Campaign', 'campaign_id', 'id' + )->withoutGlobalScope('type'); + } + + /** + * One2One: CampaignEmail belongs to one Subscriber + * @return Model + */ + public function subscriber() + { + return $this->belongsTo( + __NAMESPACE__ . '\Subscriber', 'subscriber_id', 'id' + ); + } + + /** + * One2One: CampaignEmail belongs to one Subject + * + * Note: The email_subject_id will be inserted by calculating the prioroty + * from subjects table where the subjects are related to a parent Campaign. + * So, when creating a campaign email, there will be an option to select a + * subject from a list and that list will contain subjects related to the + * parent campaign because a campaign can have many subjects and the campaign + * email will get only one from that list by calculating the priority from subjects. + * + * @return Model + */ + public function subject() + { + return $this->belongsTo( + __NAMESPACE__ . '\Subject', 'email_subject_id', 'id' + ); + } + + public function markAs($status) + { + $this->status = $status; + $this->save(); + return $this; + } + + public function markAsSent($status = 'sent') + { + return $this->markAs($status); + } + + public function markAsFailed($status = 'failed') + { + return $this->markAs($status); + } + + /** + * Data for the email to be sent + * @return array + */ + public function data() + { + $email_subject = $this->getEmailSubject(); + $email_body = $this->getEmailBody(); + $headers = Helper::getMailHeader($this->email_headers); + + return [ + 'to' => [ + 'email' => $this->email_address, + 'name' => ($this->subscriber) ? $this->subscriber->full_name : '' + ], + 'headers' => $headers, + 'subject' => $email_subject, + 'body' => $email_body, + 'campaign_id' => $this->campaign_id, + 'id' => $this->id, + 'subscriber_id' => $this->subscriber_id + ]; + } + + /** + * Build preview data for one queued/sent email row. + * + * Route: GET /campaigns/emails/{email_id}/preview via CampaignController::previewEmail(). + * This is the contact/campaign/all-emails history preview, not the draft editor preview + * route (POST /campaigns/email-preview-html). Keep the body rendering rules aligned with + * CampaignController::getEmailPreviewBody(): raw/classic-builder templates must bypass + * BlockParser, while block-editor templates should continue through BlockParser. + * + * @return array + */ + public function previewData() + { + $emailSettings = fluentcrmGetGlobalSettings('email_settings', []); + + $campaign = $this->campaign; + $subscriber = $this->subscriber; + + $emailBody = ($this->email_body) ? $this->email_body : (($campaign) ? $campaign->email_body : ''); + + $designTemplate = ($campaign) ? $campaign->design_template : ''; + + if (!$designTemplate) { + $designTemplate = 'plain'; + } + + $rawTemplates = [ + 'raw_html', + 'visual_builder', + 'raw_classic' + ]; + + if (in_array($designTemplate, $rawTemplates, true) || ($this->is_parsed && $this->email_body)) { + $emailBody = wp_unslash($emailBody); + } else { + $emailBody = (new BlockParser($subscriber))->parse($emailBody); + } + + /** + * Determine the campaign email body content text. + * + * This filter allows you to modify the email body content before it is sent to the subscriber. + * + * @param string $emailBody The email body content. + * @param object $this->subscriber The subscriber object. + * @since 2.7.0 + * + */ + $emailBody = apply_filters('fluent_crm/parse_campaign_email_text', $emailBody, $subscriber); + + $templateConfig = wp_parse_args( + ($campaign) ? Arr::get($campaign->settings, 'template_config', []) : [], + Helper::getTemplateConfig($designTemplate) + ); + + $emailFooterConfig = ($campaign) ? Helper::getFooterConfig($campaign) : []; + $footerText = Arr::get($emailFooterConfig, 'footer_content', ''); + + if ($subscriber) { + $subscriber->campaign_id = $this->campaign_id; + /** + * Determine the footer text of a campaign email for previewing. + * + * This filter allows you to modify the footer text of a campaign email before it is sent to the subscriber. + * + * @param string $footerText The footer text of the campaign email. + * @param object $subscriber The subscriber object. + * @since 2.7.0 + * + */ + $footerText = apply_filters('fluent_crm/parse_campaign_email_text', $footerText, $subscriber); + } + + $preHeader = ($campaign) ? $campaign->email_pre_header : ''; + + if ($preHeader && $subscriber) { + /** + * Filter the pre-header text of a campaign email for previewing. + * + * @param string $preHeader The pre-header text of the campaign email. + * @param object $subscriber The subscriber object. + * @since 2.7.0 + * + */ + $preHeader = apply_filters('fluent_crm/parse_campaign_email_text', $preHeader, $subscriber); + } + + $emailFooterConfig['footer_content'] = $footerText; + + /** + * Filter the email body content using a specific email design template. + * + * This filter allows customization of the email body content by applying a specific design template. + * + * @param string $emailBody The original email body content before applying the design template. + * @param array { + * Contextual information for the email design template. + * + * @type string $preHeader The pre-header text for the email, if available. + * @type string $email_body The original email body content. + * @type string $footer_text The footer text for the email, if any. + * @type array $config Configuration settings for the email template. + * } + * @param object|null $this->campaign The campaign object, if available. + * @param object|null $this->subscriber The subscriber object, if available. + * @since 1.0.0 + * + */ + $email_body = apply_filters( + 'fluent_crm/email-design-template-' . $designTemplate, + $emailBody, + [ + 'preHeader' => $preHeader, + 'email_body' => $emailBody, + 'footer_text' => $footerText, + 'footer_config' => $emailFooterConfig, + 'config' => $templateConfig + ], + $campaign, + $subscriber + ); + + + if (Str::contains($email_body, ['##crm.', '{{crm.'])) { + /** + * Filter the email body content for a campaign email and parse SmartCodes. + * + * This filter allows customization of the email body content before it is sent to the subscriber. There are FluentCRM-specific SmartCodes. + * + * @param string $email_body The email body content to be filtered. + * @param object $this ->subscriber The subscriber object containing subscriber details. + * @since 2.7.0 + * + */ + $email_body = apply_filters('fluent_crm/parse_extended_crm_text', $email_body, $subscriber); + } + + $preViewUrl = site_url('?fluentcrm=1&route=email_preview&_e_hash=' . $this->email_hash); + $email_body = str_replace(['##web_preview_url##', '{{crm_global_email_footer}}', '{{crm_preheader_text}}'], [$preViewUrl, $footerText, $preHeader], $email_body); + + + $email_body = str_replace(['https://fonts.googleapis.com/css2', 'https://fonts.googleapis.com/css'], 'https://fonts.bunny.net/css', $email_body); + + return [ + 'to' => [ + 'email' => $this->email_address, + 'name' => ($subscriber) ? $subscriber->full_name : '' + ], + 'from' => [ + 'name' => Arr::get($emailSettings, 'from_name'), + 'email' => Arr::get($emailSettings, 'from_email') + ], + 'reply' => null, + 'subject' => $this->email_subject, + 'body' => $email_body, + 'campaign_id' => $this->campaign_id, + 'id' => $this->id, + 'subscriber_id' => $this->subscriber_id + ]; + } + + public function getEmailSubject() + { + return $this->email_subject; + } + + public function getEmailBody() + { + $subscriber = $this->subscriber; + + if ($subscriber) { + $subscriber->email_id = $this->id; + } + + $designTemplate = 'classic'; + + $campaign = $this->campaign; + + if ($campaign) { + $designTemplate = $campaign->design_template; + } + + if ($this->is_parsed && !$this->email_body && $campaign && $campaign->email_body) { + // Recover unsent queue rows that were marked parsed after their body was cleared. + $this->is_parsed = 0; + } + + if (!$this->is_parsed) { + $rawTemplates = [ + 'raw_html', + 'visual_builder', + 'raw_classic' + ]; + + $emailBody = ($campaign) ? $campaign->email_body : $this->email_body; + + // Don't cache URL map if body has conditional blocks or merge tags inside href URLs + $canCache = !Helper::hasConditionOnString($emailBody) + && !preg_match('/href=["\'][^"\']*\{\{/', $emailBody); + + if (in_array($designTemplate, $rawTemplates)) { + $emailBody = $this->campaign->email_body; + } else { + $emailBody = $this->getParsedEmailBody(); + } + + $emailBody = str_replace(['https://fonts.googleapis.com/css2', 'https://fonts.googleapis.com/css'], 'https://fonts.bunny.net/css', $emailBody); + + $emailBody = apply_filters('fluent_crm/parse_campaign_email_text', $emailBody, $subscriber); + + $emailBody = apply_filters('fluentcrm_email_body_text', $emailBody, $subscriber, $this); + + if ($campaign && $trackingType = $campaign->getClickTrackingStatus()) { + $campaignUrls = $this->getCampaignUrls($emailBody, $canCache); + if ($campaignUrls) { + if ($trackingType === 'anonymous') { + $emailBody = Helper::attachAnonymousUrls($emailBody, $campaignUrls, $this->id, $this->email_hash); + } else { + $emailBody = Helper::attachUrls($emailBody, $campaignUrls, $this->id, $this->email_hash); + } + } + } + + $this->email_body = $emailBody; + $this->is_parsed = 1; + // Not saved to DB here — the parsed body is kept in memory for Mailer::send(). + // BaseHandler's mark-as-sent UPDATE persists is_parsed=1 and clears email_body. + // On rare retry (process crash), the email re-parses from campaign body which + // is correct. This avoids a ~15ms LONGTEXT write per email during bulk sends. + } + + $emailFooterConfig = []; + $footerText = ''; + if ($subscriber) { + $subscriber->campaign_id = $this->campaign_id; + + static $footerConfigCache = []; + $cacheKey = $this->campaign_id ?: 0; + if (isset($footerConfigCache[$cacheKey])) { + $emailFooterConfig = $footerConfigCache[$cacheKey]; + } else { + $emailFooterConfig = Helper::getFooterConfig($campaign); + $footerConfigCache[$cacheKey] = $emailFooterConfig; + } + $footerText = Arr::get($emailFooterConfig, 'footer_content', ''); + + if ($footerText) { + /** + * Filter the footer text of the campaign email. + * + * This filter allows you to modify the footer text of the campaign email before it is sent to the subscriber. + * + * @param string $footerText The footer text of the campaign email. + * @param object $subscriber The subscriber object. + * @since 2.7.0 + * + */ + $footerText = apply_filters('fluent_crm/parse_campaign_email_text', $footerText, $subscriber); + + $preViewUrl = site_url('?fluentcrm=1&route=email_preview&_e_hash=' . $this->email_hash); + $footerText = str_replace('##web_preview_url##', $preViewUrl, $footerText); + $emailFooterConfig['footer_content'] = $footerText; + } + } + + static $templateConfigCache = []; + $templateCacheKey = $this->campaign_id ?: 0; + if (isset($templateConfigCache[$templateCacheKey])) { + $templateConfig = $templateConfigCache[$templateCacheKey]; + } else { + if ($this->campaign && Arr::get($this->campaign->settings, 'template_config')) { + $templateConfig = wp_parse_args($this->campaign->settings['template_config'], Helper::getTemplateConfig($this->campaign->design_template)); + } else { + $templateConfig = Helper::getTemplateConfig(); + } + $templateConfigCache[$templateCacheKey] = $templateConfig; + } + + $preHeader = ($this->campaign) ? $this->campaign->email_pre_header : ''; + + if ($preHeader && $subscriber) { + /** + * Filter the pre-header text of a campaign email. + * + * This filter allows you to modify the pre-header text of a campaign email before it is sent. + * + * @param string $preHeader The pre-header text of the campaign email. + * @param object $subscriber The subscriber object containing subscriber details. + * @since 2.7.0 + * + */ + $preHeader = apply_filters('fluent_crm/parse_campaign_email_text', $preHeader, $subscriber); + } + + $footerUrls = $this->getCampaignUrls($footerText, false); + + if ($footerUrls) { + $trackingType = $campaign ? $campaign->getClickTrackingStatus() : null; + if ($trackingType === 'anonymous') { + $footerText = Helper::attachAnonymousUrls($footerText, $footerUrls, $this->id, $this->email_hash); + } else { + $footerText = Helper::attachUrls($footerText, $footerUrls, $this->id, $this->email_hash); + } + } + + $templateData = [ + 'preHeader' => $preHeader, + 'email_body' => $this->email_body, + 'footer_text' => $footerText, + 'config' => $templateConfig, + 'footer_config' => $emailFooterConfig, + ]; + + /** + * Filter the email design template content. + * + * This filter allows customization of the email design template content based on the template type. + * + * @param string $this ->email_body The original email body content. + * @param array $templateData The data used for the template. + * @param object $this ->campaign The campaign object. + * @param object $this ->subscriber The subscriber object. + * @since 1.0.0 + * + */ + $content = apply_filters( + 'fluent_crm/email-design-template-' . $designTemplate, + $this->email_body, + $templateData, + $this->campaign, + $this->subscriber + ); + + $preViewUrl = site_url('?fluentcrm=1&route=email_preview&_e_hash=' . $this->email_hash); + $content = str_replace(['##web_preview_url##', '{{crm_global_email_footer}}', '{{crm_preheader_text}}'], [$preViewUrl, $footerText, $preHeader], $content); + + if (Str::contains($content, ['##crm.', '{{crm'])) { + /** + * Filter the content to parse extended CRM text such as SmartCodes. + * + * This filter allows you to modify the content by parsing extended CRM text. There are FluentCRM-specific SmartCodes available. + * + * @param string $content The content to be filtered. + * @param object $subscriber The subscriber object. + * @since 2.7.0 + * + */ + $content = apply_filters('fluent_crm/parse_extended_crm_text', $content, $subscriber); + } + + return Helper::injectTrackerPixel($content, $this->email_hash, $this->id); + } + + private function getParsedEmailBody() + { + if (!$this->campaign_id || !$this->campaign) { + // return (new BlockParser($this->subscriber))->parse($this->email_body); + return (new BlockParser($this->subscriber))->parse($this->email_body); + } + + static $parsedEmailBody = []; + $originalBody = $this->campaign->email_body; + + $hasConditions = Helper::hasConditionOnString($originalBody); + + if (isset($parsedEmailBody[$this->campaign_id]) && !$hasConditions) { + return $parsedEmailBody[$this->campaign_id]; + } + + if ($this->campaign->status == 'archived' && !$hasConditions) { + $cachedEmailBody = fluentcrm_get_campaign_meta($this->campaign_id, '_cached_email_body', true); + if ($cachedEmailBody) { + $parsedEmailBody[$this->campaign_id] = $cachedEmailBody; + return $parsedEmailBody[$this->campaign_id]; + } + } + + $rawTemplates = [ + 'raw_html', + 'visual_builder', + 'raw_classic' + ]; + + if (in_array($this->campaign->design_template, $rawTemplates)) { + $emailBody = $originalBody; + } else { + // $emailBody = (new BlockParser($this->subscriber))->parse($originalBody); + $emailBody = (new BlockParser($this->subscriber))->parse($originalBody); + } + + if ($hasConditions) { + return $emailBody; + } + + $parsedEmailBody[$this->campaign_id] = $emailBody; + return $emailBody; + } + + public function getCampaignUrls($emailBody, $cached = false) + { + $trackingType = fluentcrmTrackClicking(); + + if (!$trackingType) { + return []; + } + + if (!$cached || !$this->campaign_id) { + return Helper::urlReplaces($emailBody); + } + + static $campaignUrls = []; + if (isset($campaignUrls[$this->campaign_id])) { + return $campaignUrls[$this->campaign_id]; + } + + $campaignUrls[$this->campaign_id] = Helper::urlReplaces($emailBody); + return $campaignUrls[$this->campaign_id]; + } + + public function getClicks() + { + return fluentCrmDb()->table('fc_campaign_url_metrics') + ->select(['fc_campaign_url_metrics.counter', 'fc_url_stores.url', 'fc_campaign_url_metrics.id']) + ->where('type', 'click') + ->where('fc_campaign_url_metrics.subscriber_id', $this->subscriber_id) + ->where('fc_campaign_url_metrics.campaign_id', $this->campaign_id) + ->join('fc_url_stores', 'fc_url_stores.id', '=', 'fc_campaign_url_metrics.url_id') + ->get(); + } + + public function getSubjectCount($campaignId) + { + return static::select( + 'fc_campaign_emails.email_subject_id', + fluentCrmDb()->raw('count(*) as total'), + 'fc_meta.value', + 'fc_meta.key' + ) + ->where('fc_campaign_emails.campaign_id', $campaignId) + ->groupBy('fc_campaign_emails.email_subject_id') + ->join('fc_meta', 'fc_meta.id', '=', 'fc_campaign_emails.email_subject_id') + ->get(); + } + + public function getOpenCount($subjectId) + { + return static::where('email_subject_id', $subjectId) + ->where('is_open', '>', 0) + ->count(); + } + + public function setEmailHeadersAttribute($headers) + { + $this->attributes['email_headers'] = \maybe_serialize($headers); + } + + public function getEmailHeadersAttribute($settings) + { + return \maybe_unserialize($settings); + } +} diff --git a/wp-content/plugins/fluent-crm/app/Models/CampaignUrlMetric.php b/wp-content/plugins/fluent-crm/app/Models/CampaignUrlMetric.php new file mode 100644 index 0000000..1ce162d --- /dev/null +++ b/wp-content/plugins/fluent-crm/app/Models/CampaignUrlMetric.php @@ -0,0 +1,324 @@ +belongsTo(__NAMESPACE__ . '\Campaign', 'campaign_id', 'id') + ->withoutGlobalScope('type'); + } + + public function subscriber() + { + return $this->belongsTo(__NAMESPACE__ . '\Subscriber', 'subscriber_id', 'id'); + } + + public function url_stores() + { + return $this->belongsTo(__NAMESPACE__ . '\UrlStores', 'url_id', 'id'); + } + + public static function maybeInsert($data) + { + $query = static::where([ + 'campaign_id' => $data['campaign_id'], + 'subscriber_id' => $data['subscriber_id'], + 'type' => $data['type'] + ])->when(!empty($data['url_id']), function ($query) use ($data) { + return $query->where('url_id', $data['url_id']); + }); + + if ($instance = $query->first()) { + $instance->counter += 1; + $instance->save(); + return $instance; + } + + return static::create($data); + } + + public function getLinksReport($campaign) + { + if (is_numeric($campaign)) { + $campaign = Campaign::withoutGlobalScopes()->find($campaign); + } + + if (!$campaign) { + return []; + } + + $settings = $campaign->settings; + + $clickTracker = $settings['click_tracker'] ?? true; + + // is anonimous tracking enabled? + if ($clickTracker === 'anonymous') { + // get from meta + $links = fluentcrm_get_campaign_meta($campaign->id, '_ano_url_clicks', true); + $formattedLinks = []; + if ($links && is_array($links)) { + $index = 1; + foreach ($links as $link => $count) { + $formattedLinks[] = [ + 'id' => $index, + 'url' => esc_url_raw($link), + 'total' => $count + ]; + $index++; + } + } + + // sort by total desc + usort($formattedLinks, function ($a, $b) { + return $b['total'] <=> $a['total']; + }); + + return $this->maybeTransformSmartLinks($formattedLinks); + } + + if ($clickTracker === false) { + return []; + } + + $stats = static::select( + fluentCrmDb()->raw('count(*) as total'), + 'fc_url_stores.url', + 'fc_url_stores.id' + ) + ->where('fc_campaign_url_metrics.campaign_id', $campaign->id) + ->where('fc_campaign_url_metrics.type', 'click') + ->groupBy('fc_campaign_url_metrics.url_id') + ->join('fc_url_stores', 'fc_url_stores.id', '=', 'fc_campaign_url_metrics.url_id') + ->orderBy('total', 'DESC') + ->get()->toArray(); + + $formatedLinks = []; + + foreach ($stats as $stat) { + $url = str_replace(['&'], ['&'], $stat['url']); + $url = esc_url_raw($url); + + if (isset($formatedLinks[$url])) { + $formatedLinks[$url]['total'] += $stat['total']; + continue; + } + + $formatedLinks[$url] = [ + 'id' => $stat['id'], + 'url' => $url, + 'total' => $stat['total'] + ]; + } + + $sortedLinks = array_values($formatedLinks); + usort($sortedLinks, function ($a, $b) { + return $b['total'] <=> $a['total']; + }); + + return $this->maybeTransformSmartLinks($sortedLinks); + } + + public function getCampaignAnalytics($campaign) + { + if (is_numeric($campaign)) { + $campaign = Campaign::withoutGlobalScopes()->find($campaign); + } + + if (!$campaign) { + return []; + } + + $unsubscribeCount = CampaignUrlMetric::where('campaign_id', $campaign->id) + ->where('type', 'unsubscribe') + ->distinct() + ->count('subscriber_id'); + + $formattedStatus = []; + if ($campaign->getOpenTrackingStatus(false) === 'anonymous') { + $openCount = fluentcrm_get_campaign_meta($campaign->id, '_ano_open_count', true); + if (!$openCount) { + $openCount = 0; + } + } else { + $openCount = fluentCrmDb()->table('fc_campaign_emails') + ->where('campaign_id', $campaign->id) + ->where(function ($q) { + $q->where('is_open', 1) + ->orWhereNotNull('click_counter'); + }) + ->count(); + } + + if ($campaign->getClickTrackingStatus(false) === 'anonymous') { + $clicks = fluentcrm_get_campaign_meta($campaign->id, '_ano_url_clicks', true); + $clickCount = 0; + if ($clicks && is_array($clicks)) { + $clickCount = array_sum($clicks); + } + } else { + $clickCount = fluentCrmDb()->table('fc_campaign_emails') + ->where('campaign_id', $campaign->id) + ->whereNotNull('click_counter') + ->count(); + } + + if ($openCount) { + $formattedStatus['open'] = [ + 'total' => $openCount, + /* translators: %d: number of opens */ + 'label' => sprintf(__('Open Rate (%d)', 'fluent-crm'), $openCount), + 'type' => 'open', + 'is_percent' => true, + 'icon_class' => 'dashicons dashicons-buddicons-pm' + ]; + } + + if ($clickCount) { + $formattedStatus['click'] = [ + 'total' => $clickCount, + /* translators: %d: number of clicks */ + 'label' => sprintf(__('Click Rate (%d)', 'fluent-crm'), $clickCount), + 'type' => 'click', + 'is_percent' => true, + 'icon_class' => 'el-icon el-icon-position' + ]; + } + + if ($openCount && $clickCount) { + $formattedStatus['ctor'] = [ + 'total' => number_format(($clickCount / $openCount) * 100, 2) . '%', + 'label' => __('Click To Open Rate', 'fluent-crm'), + 'type' => 'ctor', + 'icon_class' => 'el-icon el-icon-chat-dot-square' + ]; + } + + if ($unsubscribeCount) { + $formattedStatus['unsubscribe'] = [ + 'total' => $unsubscribeCount, + /* translators: %d: number of unsubscribes */ + 'label' => sprintf(__('Unsubscribe (%d)', 'fluent-crm'), $unsubscribeCount), + 'type' => 'unsubscribe', + 'is_percent' => true, + 'icon_class' => 'el-icon el-icon-warning-outline' + ]; + } + + $revenue = fluentcrm_get_campaign_meta($campaign->id, '_campaign_revenue'); + + if ($revenue && $revenue->value) { + $data = (array)$revenue->value; + foreach ($data as $currency => $cents) { + if ($cents && $currency !== 'orderIds') { + $formattedStatus['revenue'] = [ + 'label' => __('Revenue', 'fluent-crm') . ' (' . $currency . ')', + 'type' => 'revenue', + 'total' => number_format($cents / 100, 2), + 'icon_class' => 'el-icon el-icon-money' + ]; + } + } + } + + return $formattedStatus; + } + + public function getSubjectStats($campaign) + { + $subjects = $campaign->subjects()->get(); + + if ($subjects->isEmpty()) { + return []; + } + + $subjectCounts = (new CampaignEmail)->getSubjectCount($campaign->id); + + $totalClicks = 0; + $totalOpens = 0; + + foreach ($subjectCounts as $subjectCount) { + $metric = $this->getSubjectMetric( + $subjectCount->email_subject_id, $campaign->id + ); + $totalClicks += $metric['total_clicks']; + $totalOpens += $metric['total_opens']; + $subjectCount->metric = $metric; + } + + return [ + 'subjects' => $subjectCounts, + 'total_clicks' => $totalClicks, + 'total_opens' => $totalOpens + ]; + } + + private function getSubjectMetric($subjectId, $campaignId) + { + $clickMetrics = $this->getClickMetrics($campaignId, $subjectId); + + $openCount = (new CampaignEmail)->getOpenCount($subjectId); + + $clickTotal = array_sum($clickMetrics->pluck('total')->toArray()); + + return [ + 'clicks' => $clickMetrics, + 'total_clicks' => $clickTotal, + 'total_opens' => $openCount + ]; + } + + public function getClickMetrics($campaignId, $subjectId) + { + return static::select( + fluentCrmDb()->raw('count(*) as total'), + 'fc_url_stores.url' + ) + ->where('fc_campaign_url_metrics.campaign_id', $campaignId) + ->where('fc_campaign_url_metrics.type', 'click') + ->where('fc_campaign_emails.email_subject_id', $subjectId) + ->groupBy('fc_campaign_url_metrics.url_id') + ->join('fc_url_stores', 'fc_url_stores.id', '=', 'fc_campaign_url_metrics.url_id') + ->join('fc_campaign_emails', 'fc_campaign_emails.subscriber_id', '=', 'fc_campaign_url_metrics.subscriber_id') + ->orderBy('total', 'DESC') + ->get(); + } + + private function maybeTransformSmartLinks($links) + { + if (!apply_filters('fluent_crm/has_smartlink', false)) { + return $links; + } + + foreach ($links as $index => $link) { + $url = $link['url']; + if (strpos($url, 'route=smart_url&slug=') !== false) { + // this is a smart-link + $smartLink = apply_filters('fluent_crm/smartlink_by_short_url', null, $url); + if ($smartLink) { + $links[$index]['destination'] = $smartLink->target_url; + $links[$index]['title'] = $smartLink->title; + } + } + } + + return $links; + } +} diff --git a/wp-content/plugins/fluent-crm/app/Models/Company.php b/wp-content/plugins/fluent-crm/app/Models/Company.php new file mode 100644 index 0000000..5e02b79 --- /dev/null +++ b/wp-content/plugins/fluent-crm/app/Models/Company.php @@ -0,0 +1,177 @@ + __('Company Name *', 'fluent-crm'), + 'owner_email' => __('Owner Email', 'fluent-crm'), + 'owner_name' => __('Owner Name', 'fluent-crm'), + 'industry' => __('Industry', 'fluent-crm'), + 'description' => __('Company Description', 'fluent-crm'), + 'logo' => __('Company Logo URL', 'fluent-crm'), + 'type' => __('Type', 'fluent-crm'), + 'email' => __('Company Email', 'fluent-crm'), + 'phone' => __('Company Phone', 'fluent-crm'), + 'address_line_1' => __('Address Line 1', 'fluent-crm'), + 'address_line_2' => __('Address Line 2', 'fluent-crm'), + 'postal_code' => __('Postal Code', 'fluent-crm'), + 'city' => __('City', 'fluent-crm'), + 'state' => __('State', 'fluent-crm'), + 'country' => __('Country', 'fluent-crm'), + 'employees_number' => __('Employees Number', 'fluent-crm'), + 'linkedin_url' => __('LinkedIn URL', 'fluent-crm'), + 'facebook_url' => __('Facebook URL', 'fluent-crm'), + 'twitter_url' => __('Twitter URL', 'fluent-crm'), + 'website' => __('Website URL', 'fluent-crm') + ]; + } + + protected $searchable = [ + 'name', + 'phone', + 'description', + 'email' + ]; + + public static function boot() + { + parent::boot(); + + static::creating(function ($model) { + $model->hash = md5(wp_generate_uuid4() . '_' . time() . '_' . wp_rand(1000, 9999)); + }); + } + + /** + * Local scope to filter companies by search/query string + */ + public function scopeSearchBy($query, $search) + { + if ($search) { + $fields = $this->searchable; + $query->where(function ($query) use ($fields, $search) { + $query->where(array_shift($fields), 'LIKE', "%$search%"); + foreach ($fields as $field) { + $query->orWhere($field, 'LIKE', "%$search%"); + } + }); + } + + return $query; + } + + public function scopeOfType($query, $status) + { + return $query->where('type', $status); + } + + public function scopeOfIndustry($query, $status) + { + return $query->where('industry', $status); + } + + /** + * Get all of the subscribers that belongs to the company. + * + * @return \FluentCrm\Framework\Database\Orm\Relations\BelongsToMany + */ + public function subscribers() + { + return $this->belongsToMany( + __NAMESPACE__ . '\Subscriber', 'fc_subscriber_pivot', 'object_id', 'subscriber_id' + )->where('object_type', __CLASS__); + } + + public function owner() + { + return $this->belongsTo(Subscriber::class, 'owner_id', 'id'); + } + + public function getContactsCount() + { + return $this->subscribers()->count(); + } + + /** + * A Company has many notes and activities. + * + * @return \FluentCrm\Framework\Database\Orm\Relations\HasMany + */ + public function notes() + { + return $this->hasMany(CompanyNote::class, 'subscriber_id', 'id'); + } + + public function setMetaAttribute($meta) + { + $this->attributes['meta'] = \maybe_serialize($meta); + } + + public function getMetaAttribute($meta) + { + $metaData = \maybe_unserialize($meta); + + if (!$metaData) { + return [ + 'custom_values' => [] + ]; + } + + $metaDefaults = [ + 'custom_values' => [] + ]; + + return array_merge($metaDefaults, $metaData); + } + + public function getCustomValues() + { + return Arr::get($this->meta, 'custom_values', []); + } + +} diff --git a/wp-content/plugins/fluent-crm/app/Models/CompanyNote.php b/wp-content/plugins/fluent-crm/app/Models/CompanyNote.php new file mode 100644 index 0000000..439667a --- /dev/null +++ b/wp-content/plugins/fluent-crm/app/Models/CompanyNote.php @@ -0,0 +1,91 @@ +created_at)) { + $model->created_at = fluentCrmTimestamp(); + } + + $model->status = '_company_note_'; + + $model->updated_at = fluentCrmTimestamp(); + $model->created_by = $model->created_by ?: get_current_user_id(); + }); + + static::updated(function ($model) { + $model->updated_at = fluentCrmTimestamp(); + }); + + static::addGlobalScope('status', function ($builder) { + $builder->where('status', '_company_note_'); // This disguised the Company Note from SubscriberNote + }); + } + /** + * One2One: CompanyNote belongs to one Company + * @return \FluentCrm\Framework\Database\Orm\Relations\BelongsTo + */ + public function company() + { + return $this->belongsTo( + __NAMESPACE__.'\Company', 'subscriber_id', 'id' + ); + } + + public function markAs($status) + { + $this->status = $status; + $this->save(); + return $this; + } + + public function createdBy() + { + if(!$this->created_by) { + return false; + } + + $user = get_user_by('ID', $this->created_by); + + if (!$user) { + return false; + } + + return [ + 'ID' => $user->ID, + 'first_name' => $user->first_name, + 'last_name' => $user->last_name, + 'display_name' => $user->display_name + ]; + } +} diff --git a/wp-content/plugins/fluent-crm/app/Models/CustomCompanyField.php b/wp-content/plugins/fluent-crm/app/Models/CustomCompanyField.php new file mode 100644 index 0000000..92e424c --- /dev/null +++ b/wp-content/plugins/fluent-crm/app/Models/CustomCompanyField.php @@ -0,0 +1,36 @@ + 'default', + 'title' => __('Custom Company Data', 'fluent-crm') + ] + ]; + } + + return $fieldGroups; + } +} diff --git a/wp-content/plugins/fluent-crm/app/Models/CustomContactField.php b/wp-content/plugins/fluent-crm/app/Models/CustomContactField.php new file mode 100644 index 0000000..cbfea8e --- /dev/null +++ b/wp-content/plugins/fluent-crm/app/Models/CustomContactField.php @@ -0,0 +1,269 @@ +globalMetaName, []); + + if (in_array('field_types', $with)) { + $data['field_types'] = $this->getFieldTypes(); + } + + if (in_array('field_groups', $with)) { + $data['field_groups'] = $this->getFieldGroups(); + } + + return $data; + } + + public function getFieldTypes() + { + /** + * Modify the global custom contact field types for FluentCRM custom contact fields. + * + * The default field types are: 'text', 'textarea', 'number', 'single-select', 'multi-select', 'radio', 'checkbox', 'date', 'date_time'. + * + * @since 2.7.0 + * + * @param array { + * An associative array of field types. + * + * @type array $text { + * @type string $type The type of the field. + * @type string $label The label for the field. + * @type string $value_type The value type of the field. + * } + * @type array $textarea { + * @type string $type The type of the field. + * @type string $label The label for the field. + * @type string $value_type The value type of the field. + * } + * @type array $number { + * @type string $type The type of the field. + * @type string $label The label for the field. + * @type string $value_type The value type of the field. + * } + * @type array $single-select { + * @type string $type The type of the field. + * @type string $label The label for the field. + * @type string $value_type The value type of the field. + * } + * @type array $multi-select { + * @type string $type The type of the field. + * @type string $label The label for the field. + * @type string $value_type The value type of the field. + * } + * @type array $radio { + * @type string $type The type of the field. + * @type string $label The label for the field. + * @type string $value_type The value type of the field. + * } + * @type array $checkbox { + * @type string $type The type of the field. + * @type string $label The label for the field. + * @type string $value_type The value type of the field. + * } + * @type array $date { + * @type string $type The type of the field. + * @type string $label The label for the field. + * @type string $value_type The value type of the field. + * } + * @type array $date_time { + * @type string $type The type of the field. + * @type string $label The label for the field. + * @type string $value_type The value type of the field. + * } + * } + */ + return apply_filters('fluent_crm/global_field_types', [ + 'text' => [ + 'type' => 'text', + 'label' => __('Single Line Text', 'fluent-crm'), + 'value_type' => 'string' + ], + 'textarea' => [ + 'type' => 'textarea', + 'label' => __('Multi Line Text', 'fluent-crm'), + 'value_type' => 'string' + ], + 'number' => [ + 'type' => 'number', + 'label' => __('Numeric Field', 'fluent-crm'), + 'value_type' => 'numeric' + ], + 'single-select' => [ + 'type' => 'select-one', + 'label' => __('Select choice', 'fluent-crm'), + 'value_type' => 'string' + ], + 'multi-select' => [ + 'type' => 'select-multi', + 'label' => __('Multiple Select choice', 'fluent-crm'), + 'value_type' => 'array' + ], + 'radio' => [ + 'type' => 'radio', + 'label' => __('Radio Choice', 'fluent-crm'), + 'value_type' => 'string' + ], + 'checkbox' => [ + 'type' => 'checkbox', + 'label' => __('Checkboxes', 'fluent-crm'), + 'value_type' => 'array' + ], + 'date' => [ + 'type' => 'date', + 'label' => __('Date', 'fluent-crm'), + 'value_type' => 'date' + ], + 'date_time' => [ + 'type' => 'date_time', + 'label' => __('Date and Time', 'fluent-crm'), + 'value_type' => 'datetime' + ] + ]); + } + + public function saveGlobalFields($fields) + { + $slugs = []; + + foreach ($fields as $field) { + if (isset($field['slug'])) { + $slugs[] = $field['slug']; + } + } + + $formattedFields = []; + + $keys = []; + foreach ($fields as $field) { + + if (empty($field['slug'])) { + $field['slug'] = $this->generateSlug($field, $slugs); + } + + if (in_array($field['slug'], $keys)) { + continue; + } + + $keys[] = $field['slug']; + + $formattedFields[] = $field; + } + + fluentcrm_update_option($this->globalMetaName, $formattedFields); + + return $formattedFields; + } + + protected function generateSlug($field, $slugs) + { + $label = str_replace(' ', '_', $field['label']); + $label = sanitize_title($label, 'custom_field', 'view'); + $label = substr($label, 0, 25); + $originalLabel = $label; + + if (is_numeric($label)) { + $label = 'cf_' . $label; + } + + $mainColumns = array_merge( + (new Subscriber)->getFillable(), + ['id', 'updated_at'] + ); + + if (in_array($label, $mainColumns)) { + $label = 'cf_' . $label; + } + + $index = 1; + + while (in_array($label, $slugs)) { + $label = $originalLabel . '_' . $index; + $index++; + } + + return $label; + } + + public function formatCustomFieldValues($values, $fields = []) + { + if (!$values) { + return $values; + } + if (!$fields) { + $rawFields = fluentcrm_get_option($this->globalMetaName, []); + foreach ($rawFields as $field) { + $fields[$field['slug']] = $field; + } + } + + foreach ($values as $valueKey => $value) { + + $isArrayType = Arr::get($fields, $valueKey . '.type') == 'checkbox' || Arr::get($fields, $valueKey . '.type') == 'select-multi'; + + if (!is_array($value) && $isArrayType) { + $itemValues = explode(',', $value); + $trimmedvalues = []; + foreach ($itemValues as $itemValue) { + $trimmedvalues[] = trim($itemValue); + } + if ($itemValue) { + $values[$valueKey] = $trimmedvalues; + } + } + } + + return $values; + } + + public function getFieldGroups() + { + $fieldGroups = fluentcrm_get_option('contact_field_groups'); + + if (!$fieldGroups) { + $fieldGroups = [ + [ + 'slug' => 'default', + 'title' => __('Custom Profile Data', 'fluent-crm') + ] + ]; + } + return $fieldGroups; + } + + public function updateGroupName($oldName, $newName) + { + $currentCustomFields = fluentcrm_get_option($this->globalMetaName); + + $updatedCustomFields = []; + + foreach ($currentCustomFields as $customField) { + if (isset($customField['group']) && $customField['group'] == $oldName) { + $customField['group'] = $newName; + } + $updatedCustomFields[] = $customField; + } + + fluentcrm_update_option($this->globalMetaName, $updatedCustomFields); + + return $updatedCustomFields; + } +} diff --git a/wp-content/plugins/fluent-crm/app/Models/CustomEmailCampaign.php b/wp-content/plugins/fluent-crm/app/Models/CustomEmailCampaign.php new file mode 100644 index 0000000..71c0f3e --- /dev/null +++ b/wp-content/plugins/fluent-crm/app/Models/CustomEmailCampaign.php @@ -0,0 +1,45 @@ + '', + 'title' => __('Custom Email', 'fluent-crm'), + 'status' => 'published', + 'template_id' => '', + 'email_subject' => '', + 'email_pre_header' => '', + 'email_body' => '', + 'utm_status' => 0, + 'utm_source' => '', + 'utm_medium' => '', + 'utm_campaign' => '', + 'utm_term' => '', + 'utm_content' => '', + 'design_template' => $defaultTemplate, + 'settings' => (object)[ + 'template_config' => Helper::getTemplateConfig($defaultTemplate) + ] + ]; + } + +} diff --git a/wp-content/plugins/fluent-crm/app/Models/EventTracker.php b/wp-content/plugins/fluent-crm/app/Models/EventTracker.php new file mode 100644 index 0000000..892b2d8 --- /dev/null +++ b/wp-content/plugins/fluent-crm/app/Models/EventTracker.php @@ -0,0 +1,50 @@ +created_by = $model->created_by ?: get_current_user_id(); + }); + + } + + /** + * One2One: SubscriberNote belongs to one Subscriber + * @return \FluentCrm\Framework\Database\Orm\Relations\BelongsTo + */ + public function subscriber() + { + return $this->belongsTo( + __NAMESPACE__ . '\Subscriber', 'subscriber_id', 'id' + ); + } +} diff --git a/wp-content/plugins/fluent-crm/app/Models/Funnel.php b/wp-content/plugins/fluent-crm/app/Models/Funnel.php new file mode 100644 index 0000000..5d5796c --- /dev/null +++ b/wp-content/plugins/fluent-crm/app/Models/Funnel.php @@ -0,0 +1,206 @@ +type = self::$type; + }); + + static::addGlobalScope('type', function ($builder) { + $builder->where('fc_funnels.type', '=', self::$type); + }); + } + + public function scopePublished($query) + { + return $query->where('status', 'published'); + } + + public function actions() + { + return $this->hasMany( + __NAMESPACE__ . '\FunnelSequence', 'funnel_id', 'id' + ); + } + + public function subscribers() + { + return $this->hasMany( + __NAMESPACE__ . '\FunnelSubscriber', 'funnel_id', 'id' + ); + } + + public function setSettingsAttribute($settings) + { + $this->attributes['settings'] = \maybe_serialize($settings); + } + + public function getSettingsAttribute($settings) + { + return \maybe_unserialize($settings); + } + + public function setConditionsAttribute($conditions) + { + $this->attributes['conditions'] = \maybe_serialize($conditions); + } + + public function getConditionsAttribute($conditions) + { + return \maybe_unserialize($conditions); + } + + public function getSubscribersCount() + { + return $this->subscribers()->count(); + } + + public function updateMeta($key, $value) + { + fluentcrm_update_meta($this->id, __CLASS__, $key, $value); + } + + public function getMeta($key, $default = '') + { + $meta = fluentcrm_get_meta($this->id, __CLASS__, $key); + if($meta) { + return $meta->value; + } + return $default; + } + + public function deleteMeta($key) + { + fluentcrm_delete_meta($this->id, __CLASS__, $key); + } + + public function labelsTerm() + { + return $this->belongsToMany(Label::class, 'fc_term_relations', 'object_id', 'term_id') + ->wherePivot('object_type', __CLASS__); + } + + public function labels() + { + $labelIds = TermRelation::where('object_id', $this->id) + ->where('object_type', __CLASS__) + ->pluck('term_id') + ->toArray(); + return Label::whereIn('id', $labelIds)->get(); + } + + public function getFormattedLabels() + { + $labels = $this->labels(); + return $labels->map(function ($label) { + return [ + 'id' => $label->id, + 'slug' => $label->slug, + 'title' => $label->title, + 'color' => $label->settings['color'] ?? '' + ]; + }); + } + + public function attachLabels($labelIds) + { + if (!is_array($labelIds)) { + $labelIds = [$labelIds]; + } + + $existingLabelIds = TermRelation::where('object_id', $this->id) + ->where('object_type', __CLASS__) + ->pluck('term_id') + ->toArray(); + + $newLabelIds = array_diff($labelIds, $existingLabelIds); + + if (!empty($newLabelIds)) { + foreach ($newLabelIds as $labelId) { + TermRelation::create([ + 'object_id' => $this->id, + 'object_type' => __CLASS__, + 'term_id' => $labelId + ]); + } + } + + return $this; + } + + /** + * Replace existing funnel labels with the provided label IDs. + * + * @param array|int $labelIds + * @return $this + */ + public function syncLabels($labelIds) + { + if (!is_array($labelIds)) { + $labelIds = [$labelIds]; + } + + $existingLabelIds = TermRelation::where('object_id', $this->id) + ->where('object_type', __CLASS__) + ->pluck('term_id') + ->toArray(); + + $labelIds = array_unique(array_filter(array_map('intval', $labelIds))); + $labelsToDetach = array_diff($existingLabelIds, $labelIds); + $labelsToAttach = array_diff($labelIds, $existingLabelIds); + + if (!empty($labelsToDetach)) { + $this->detachLabels($labelsToDetach); + } + + if (!empty($labelsToAttach)) { + $this->attachLabels($labelsToAttach); + } + + return $this; + } + + public function detachLabels($labelIds) + { + if (!is_array($labelIds)) { + $labelIds = [$labelIds]; + } + + TermRelation::where('object_id', $this->id) + ->where('object_type', __CLASS__) + ->whereIn('term_id', $labelIds) + ->delete(); + + return $this; + } +} diff --git a/wp-content/plugins/fluent-crm/app/Models/FunnelCampaign.php b/wp-content/plugins/fluent-crm/app/Models/FunnelCampaign.php new file mode 100644 index 0000000..7fcb402 --- /dev/null +++ b/wp-content/plugins/fluent-crm/app/Models/FunnelCampaign.php @@ -0,0 +1,309 @@ + '', + 'parent_id' => '', + 'title' => __('Funnel Campaign Holder', 'fluent-crm'), + 'status' => 'published', + 'template_id' => '', + 'email_subject' => '', + 'email_pre_header' => '', + 'email_body' => '', + 'utm_status' => 0, + 'utm_source' => '', + 'utm_medium' => '', + 'utm_campaign' => '', + 'utm_term' => '', + 'utm_content' => '', + 'design_template' => $defaultTemplate, + 'settings' => (object)[ + 'template_config' => Helper::getTemplateConfig($defaultTemplate), + 'mailer_settings' => [ + 'from_name' => '', + 'from_email' => '', + 'reply_to_name' => '', + 'reply_to_email' => '', + 'is_custom' => 'no' + ] + ] + ]; + } + + public function sendToCustomAddresses($addresses = [], $args = [], $refSubscriber = false) + { + if (!$addresses) { + return; + } + $time = current_time('mysql'); + foreach ($addresses as $address) { + if (!is_email($address)) { + continue; + } + + // check if the email has any subscriber + $subscriber = Subscriber::where('email', $address)->first(); + if ($subscriber && $subscriber->status != 'subscribed') { + continue; + } + + // We have to handle manually + $emailBody = (new BlockParser($refSubscriber))->parse($this->email_body); + + $emailSubject = $this->email_subject; + + if ($refSubscriber) { + /** + * Filter the campaign email body content. + * + * This filter allows you to modify the email body content before it is sent to the subscriber. + * + * @param string $emailBody The email body content. + * @param object $refSubscriber The subscriber object reference. + * @since 2.7.0 + * + */ + $emailBody = apply_filters('fluent_crm/parse_campaign_email_text', $emailBody, $refSubscriber); + /** + * Filter the email subject text for a campaign. + * + * This filter allows you to modify the email subject text before it is sent to the subscriber. + * + * @param string $emailSubject The original email subject text. + * @param object $refSubscriber The subscriber object reference. + * + * @return string The filtered email subject text. + * @since 2.7.0 + * + */ + $emailSubject = apply_filters('fluent_crm/parse_campaign_email_text', $emailSubject, $refSubscriber); + } + + $email = [ + 'campaign_id' => $this->id, + 'email_address' => $address, + 'email_subject' => $emailSubject, + 'email_body' => $emailBody, + 'created_at' => $time, + 'updated_at' => $time, + 'is_parsed' => 1, + 'note' => __('Email Sent From Funnel', 'fluent-crm') + ]; + + if ($subscriber) { + $email['subscriber_id'] = $subscriber->id; + } + + if ($args) { + $email = wp_parse_args($email, $args); + } + + $insertId = CampaignEmail::insert($email); + $emailHash = Helper::generateEmailHash($insertId); + + CampaignEmail::where('id', $insertId) + ->update([ + 'email_hash' => $emailHash + ]); + } + } + + + /** + * Add one or more subscribers to the campaign + * @param array $subscriberIds + * @param array $emailArgs extra campaign_email args + * @param bool $isModel if the $subscriberIds is collection or not + * @return array + */ + public function subscribe($subscriberIds, $emailArgs = [], $isModel = false) + { + $updateIds = []; + + $mailHeaders = Helper::getMailHeadersFromSettings(Arr::get($this->settings, 'mailer_settings', [])); + + if ($isModel) { + $subscribers = $subscriberIds; + } else { + $subscribers = Subscriber::whereIn('id', $subscriberIds)->get(); + } + + $sendableStatuses = fluentCrmEmailSendableStatuses(); + + foreach ($subscribers as $subscriber) { + if (!in_array($subscriber->status, $sendableStatuses, true)) { + continue; // We don't want to send emails to non-subscribed members + } + + $time = fluentCrmTimestamp(); + $email = [ + 'campaign_id' => $this->id, + 'status' => $this->status, + 'subscriber_id' => $subscriber->id, + 'email_address' => $subscriber->email, + 'email_headers' => $mailHeaders, + 'created_at' => $time, + 'updated_at' => $time, + 'email_body' => '', + ]; + $subjectItem = $this->guessEmailSubject(); + $emailSubject = $this->email_subject; + + // Let's create the email body here + $rawTemplates = [ + 'raw_html', + 'visual_builder', + 'raw_classic' + ]; + $emailBody = $this->email_body; + if (!in_array($this->design_template, $rawTemplates)) { + $emailBody = (new BlockParser($subscriber))->parse($emailBody); + } + $emailBody = str_replace(['https://fonts.googleapis.com/css2', 'https://fonts.googleapis.com/css'], 'https://fonts.bunny.net/css', $emailBody); + /** + * Filter the email body content for a campaign. + * + * This filter allows you to modify the email body content before it is sent to the subscriber. + * + * @param string $emailBody The original email body content. + * @param object $subscriber The subscriber object containing subscriber details. + * + * @return string The filtered email body content. + * @since 2.8.44 + * + */ + $emailBody = apply_filters('fluent_crm/parse_campaign_email_text', $emailBody, $subscriber); + // email body creation done + + if ($subjectItem && !empty($subjectItem->value)) { + $emailSubject = $subjectItem->value; + $email['email_subject_id'] = $subjectItem->id; + } + + /** + * Filter the campaign email subject text. + * + * This filter allows you to modify the email subject text for a campaign. + * + * @param string $emailSubject The original email subject text. + * @param object $subscriber The subscriber object. + * + * @return string The filtered email subject text. + * @since 2.8.40 + * + */ + $email['email_subject'] = apply_filters('fluent_crm/parse_campaign_email_text', $emailSubject, $subscriber); + + if ($emailArgs) { + $email = wp_parse_args($emailArgs, $email); + } + + $inserted = CampaignEmail::create($email); + + $subscriber->campaign_id = $this->id; + $subscriber->email_id = $inserted->id; + + $emailHash = Helper::generateEmailHash($inserted->id); + + /** + * Filter the email body content of a campaign email. + * + * This filter allows you to modify the email body content before it is sent to the subscriber. + * + * @param string $emailBody The email body content. + * @param object $subscriber The subscriber object containing subscriber details. + * + * @return string The filtered email body content. + * @since 2.8.44 + * + */ + $emailBody = apply_filters('fluent_crm/parse_campaign_email_text', $emailBody, $subscriber); + + + $trackingType = fluentcrmTrackClicking(); + + if ($trackingType) { + $campaignUrls = Helper::urlReplaces($emailBody); + if ($campaignUrls) { + if ($trackingType === 'anonymous') { + $emailBody = Helper::attachAnonymousUrls($emailBody, $campaignUrls, $inserted->id, $emailHash); + } else { + $emailBody = Helper::attachUrls($emailBody, $campaignUrls, $inserted->id, $emailHash); + } + } + } + + + CampaignEmail::where('id', $inserted->id) + ->update([ + 'email_hash' => $emailHash, + 'email_body' => $emailBody, + 'is_parsed' => 1 + ]); + + $updateIds[] = $inserted->id; + } + + $emailCount = $this->getEmailCount(); + if ($emailCount != $this->recipients_count) { + $this->recipients_count = $emailCount; + $this->save(); + } + + return $updateIds; + } + + public function processAndSubscribe($subscriber, $refData = [], $args = []) + { + foreach ($refData as $refKey => $data) { + $subscriber->{$refKey} = $data; + } + + /* + * Note: We are not using the parse_campaign_email_text filter here + * Have a plan to remove this below commented code + */ + // We have to handle manually + // $emailBody = (new BlockParser($this->subscriber))->parse($this->email_body); + // $args['email_body'] = apply_filters('fluent_crm/parse_campaign_email_text', $emailBody, $subscriber); + // $args['email_subject'] = apply_filters('fluent_crm/parse_campaign_email_text', $this->email_subject, $subscriber); + // $args['is_parsed'] = 1; + + return $this->subscribe([$subscriber], $args, true); + } + + + public function getOpenTrackingStatus($globalFallback = true) + { + return fluentcrmTrackEmailOpen(); + } + + public function getClickTrackingStatus($globalFallback = true) + { + return fluentcrmTrackClicking(); + } + +} diff --git a/wp-content/plugins/fluent-crm/app/Models/FunnelMetric.php b/wp-content/plugins/fluent-crm/app/Models/FunnelMetric.php new file mode 100644 index 0000000..b179b49 --- /dev/null +++ b/wp-content/plugins/fluent-crm/app/Models/FunnelMetric.php @@ -0,0 +1,55 @@ +where('status', $status); + } + + public function funnel() + { + return $this->belongsTo( + __NAMESPACE__ . '\Funnel', 'funnel_id', 'id' + ); + } + + public function sequence() + { + return $this->belongsTo( + __NAMESPACE__ . '\FunnelSequence', 'sequence_id', 'id' + ); + } + + public function subscriber() + { + return $this->belongsTo( + __NAMESPACE__ . '\Subscriber', 'subscriber_id', 'id' + ); + } + +} diff --git a/wp-content/plugins/fluent-crm/app/Models/FunnelSequence.php b/wp-content/plugins/fluent-crm/app/Models/FunnelSequence.php new file mode 100644 index 0000000..daf7898 --- /dev/null +++ b/wp-content/plugins/fluent-crm/app/Models/FunnelSequence.php @@ -0,0 +1,98 @@ +settings) && is_array($model->settings)) { + $model->settings = \maybe_serialize($model->settings); + } + if (isset($model->conditions) && is_array($model->conditions)) { + $model->conditions = \maybe_serialize($model->conditions); + } + }); + } + + public function funnel() + { + return $this->belongsTo( + __NAMESPACE__ . '\Funnel', 'funnel_id', 'id' + ); + } + + public function parent() + { + return $this->belongsTo( + __NAMESPACE__ . '\FunnelSequence', 'parent_id', 'id' + ); + } + + public function children() + { + return $this->hasMany( + __NAMESPACE__ . '\FunnelSequence', 'parent_id', 'id' + ); + } + + public function setSettingsAttribute($settings) + { + if (is_array($settings)) { + $this->attributes['settings'] = \maybe_serialize($settings); + } else { + $this->attributes['settings'] = $settings; + } + } + + public function getSettingsAttribute($settings) + { + return \maybe_unserialize($settings); + } + + public function setConditionsAttribute($conditions) + { + if (is_array($conditions)) { + $this->attributes['conditions'] = \maybe_serialize($conditions); + } else { + $this->attributes['conditions'] = $conditions; + } + } + + public function getConditionsAttribute($conditions) + { + return \maybe_unserialize($conditions); + } +} diff --git a/wp-content/plugins/fluent-crm/app/Models/FunnelSubscriber.php b/wp-content/plugins/fluent-crm/app/Models/FunnelSubscriber.php new file mode 100644 index 0000000..76f6ffd --- /dev/null +++ b/wp-content/plugins/fluent-crm/app/Models/FunnelSubscriber.php @@ -0,0 +1,76 @@ +where('status', 'active'); + } + + public function funnel() + { + return $this->belongsTo( + __NAMESPACE__ . '\Funnel', 'funnel_id', 'id' + ); + } + + public function next_sequence_item() + { + return $this->belongsTo( + __NAMESPACE__ . '\FunnelSequence', 'next_sequence_id', 'id' + ); + } + + public function last_sequence() + { + return $this->belongsTo( + __NAMESPACE__ . '\FunnelSequence', 'last_sequence_id', 'id' + ); + } + + public function metrics() + { + return $this->hasMany( + __NAMESPACE__ . '\FunnelMetric', 'subscriber_id', 'subscriber_id' + ); + } + + public function subscriber() + { + return $this->belongsTo( + __NAMESPACE__ . '\Subscriber', 'subscriber_id', 'id' + ); + } + +} diff --git a/wp-content/plugins/fluent-crm/app/Models/Label.php b/wp-content/plugins/fluent-crm/app/Models/Label.php new file mode 100644 index 0000000..69e0dbc --- /dev/null +++ b/wp-content/plugins/fluent-crm/app/Models/Label.php @@ -0,0 +1,47 @@ +taxonomy_name = $model->taxonomy_name ?: 'global_label'; // default type is label + }); + + static::addGlobalScope('taxonomy_name', function (Builder $builder) { + $builder->where('taxonomy_name', '=', 'global_label'); + }); + } + + public function getSettingsAttribute($value) + { + return \maybe_unserialize($value); + } + + public function setSettingsAttribute($value) + { + $this->attributes['settings'] = maybe_serialize($value); + } + + + +} \ No newline at end of file diff --git a/wp-content/plugins/fluent-crm/app/Models/Lists.php b/wp-content/plugins/fluent-crm/app/Models/Lists.php new file mode 100644 index 0000000..57616f7 --- /dev/null +++ b/wp-content/plugins/fluent-crm/app/Models/Lists.php @@ -0,0 +1,83 @@ +searchable; + $query->where(function ($query) use ($fields, $search) { + $query->where(array_shift($fields), 'LIKE', "%$search%"); + foreach ($fields as $field) { + $query->orWhere($field, 'LIKE', "$search%"); + } + }); + } + + return $query; + } + + /** + * Many2Many: List belongs to many Subscriber + * + * @return \FluentCrm\App\Models\Base\Collection + */ + public function subscribers() + { + return $this->belongsToMany( + __NAMESPACE__.'\Subscriber', 'fc_subscriber_pivot', 'object_id', 'subscriber_id' + )->where('object_type', __CLASS__); + } + + public function totalCount() + { + return fluentCrmDb()->table('fc_subscriber_pivot') + ->where('object_type', 'FluentCrm\App\Models\Lists') + ->where('object_id', $this->id) + ->count(); + } + + public function countByStatus($status = 'subscribed') + { + return fluentCrmDb()->table('fc_subscriber_pivot') + ->where('fc_subscriber_pivot.object_type', 'FluentCrm\App\Models\Lists') + ->where('fc_subscriber_pivot.object_id', $this->id) + ->join('fc_subscribers', 'fc_subscribers.id', '=', 'fc_subscriber_pivot.subscriber_id') + ->where('fc_subscribers.status', $status) + ->count(); + } + +} diff --git a/wp-content/plugins/fluent-crm/app/Models/Meta.php b/wp-content/plugins/fluent-crm/app/Models/Meta.php new file mode 100644 index 0000000..099ca8c --- /dev/null +++ b/wp-content/plugins/fluent-crm/app/Models/Meta.php @@ -0,0 +1,41 @@ +attributes['value'] = maybe_serialize($value); + } + + public function getValueAttribute($value) + { + return maybe_unserialize($value); + } +} diff --git a/wp-content/plugins/fluent-crm/app/Models/Model.php b/wp-content/plugins/fluent-crm/app/Models/Model.php new file mode 100644 index 0000000..4d5257b --- /dev/null +++ b/wp-content/plugins/fluent-crm/app/Models/Model.php @@ -0,0 +1,68 @@ +orderBy($field, 'desc'); + } + + public function scopeNewest($query, $field = 'created_at') + { + return $query->orderBy($field, 'asc'); + } + + public function getPerPage() + { + return (isset($_REQUEST['per_page'])) ? intval($_REQUEST['per_page']) : 15; + } + + /** + * Get a fresh timestamp for the model. + * + * @return \DateTime + */ + public function freshTimestamp() + { + return new \FluentCrm\Framework\Support\DateTime(current_time('mysql')); + } + + protected function serializeDate(\DateTimeInterface $date) + { + return $date->format('Y-m-d H:i:s'); + } + + public function getTimezone() + { + return wp_timezone(); + } + + protected function asDateTime($value) + { + if (is_string($value) && Str::contains($value, 'T')) { + return new \FluentCrm\Framework\Support\DateTime($value); + } + + return parent::asDateTime($value); + } + + protected function originalIsNumericallyEquivalent($key) + { + $current = $this->attributes[$key]; + + $original = $this->original[$key]; + + return is_numeric($current) && is_numeric($original) && strcmp((string) $current, (string) $original) === 0; + } + +} diff --git a/wp-content/plugins/fluent-crm/app/Models/Subject.php b/wp-content/plugins/fluent-crm/app/Models/Subject.php new file mode 100644 index 0000000..b382123 --- /dev/null +++ b/wp-content/plugins/fluent-crm/app/Models/Subject.php @@ -0,0 +1,47 @@ +object_type = __class__; + }); + static::saving(function ($model) { + $model->object_type = __class__; + }); + + static::addGlobalScope('object_type', function ($builder) { + $builder->where('object_type', '=', __class__); + }); + } + + public function campaign() + { + return $this->belongsTo(__NAMESPACE__.'\Campaign', 'object_id', 'id') + ->withoutGlobalScope('type'); + } + + public function emails() + { + return $this->hasMany(__NAMESPACE__.'\CampaignEmail', 'email_subject_id', 'id'); + } +} diff --git a/wp-content/plugins/fluent-crm/app/Models/Subscriber.php b/wp-content/plugins/fluent-crm/app/Models/Subscriber.php new file mode 100644 index 0000000..825ee14 --- /dev/null +++ b/wp-content/plugins/fluent-crm/app/Models/Subscriber.php @@ -0,0 +1,2589 @@ +hash = md5($model->email); + }); + + static::updating(function ($model) { + if ($model->user_id && Helper::isUserSyncEnabled()) { + $user = get_user_by('email', $model->email); + + $email_mismatch = false; + + if (!$user) { + $email_mismatch = true; + $user = get_user_by('ID', $model->user_id); + } + + if ($user) { + if ($model->first_name && $model->first_name != $user->first_name) { + update_user_meta($user->ID, 'first_name', $model->first_name); + } + if ($model->last_name && $model->last_name != $user->last_name) { + update_user_meta($user->ID, 'last_name', $model->last_name); + } + + /** + * Determine whether to update the WordPress user email when there is a mismatch. + * + * This filter allows you to control whether the WordPress user email should be updated + * when there is a mismatch between the subscriber email and the WordPress user email. + * + * @param bool Whether to update the WordPress user email. Default false. + * @since 2.3.1 + * + */ + if ($email_mismatch && apply_filters('fluentcrm_update_wp_user_email_on_change', false)) { + $user->user_email = $model->email; + wp_update_user($user); + } + + $model->user_id = $user->ID; // in case user id mismatch + } + } + }); + } + + /** + * $searchable Columns in table to search + * @var array + */ + protected $searchable = [ + 'email', + 'first_name', + 'last_name', + 'address_line_1', + 'address_line_2', + 'postal_code', + 'city', + 'state', + 'country', + 'phone', + 'status' + ]; + + /** + * Local scope to filter subscribers by search/query string + * @param \FluentCrm\Framework\Database\Query\Builder $query + * @param string $search + * @param boolean $custom_fields + * @return \FluentCrm\Framework\Database\Query\Builder $query + */ + public function scopeSearchBy($query, $search, $custom_fields = false) + { + if ($search) { + $fields = $this->searchable; + $query->where(function ($query) use ($fields, $search, $custom_fields) { + $query->where(array_shift($fields), 'LIKE', "%$search%"); + + $nameArray = explode(' ', $search); + if (count($nameArray) >= 2) { + $query->orWhere(function ($q) use ($nameArray) { + $fname = array_shift($nameArray); + $lastName = implode(' ', $nameArray); + $q->where('first_name', 'LIKE', "%$fname%") + ->orWhere('last_name', 'LIKE', "%$lastName%"); + }); + } + + foreach ($fields as $field) { + $query->orWhere($field, 'LIKE', "%$search%"); + } + }); + + /** + * If contact list has custom field + * Then this block is responsible for searching by custom filed + */ + if ($custom_fields) { + $query->orWhere(function ($q) use ($search, $custom_fields) { + $q->whereHas('custom_field_meta', function ($q) use ($search) { + $q->where('value', 'LIKE', "%$search%"); + }); + }); + } + } + + return $query; + } + + /** + * Local scope to filter subscribers by search/query string + * @param \FluentCrm\Framework\Database\Query\Builder $query + * @param array $statuses + * @return \FluentCrm\Framework\Database\Query\Builder $query + */ + public function scopeFilterByStatues($query, $statuses) + { + if ($statuses) { + $query->whereIn('status', $statuses); + } + + return $query; + } + + /** + * Local scope to filter subscribers by contact type + * @param \FluentCrm\Framework\Database\Query\Builder $query + * @param array $statuses + * @return \FluentCrm\Framework\Database\Query\Builder $query + */ + public function scopeFilterByContactType($query, $type) + { + if ($type) { + $query->where('contact_type', $type); + } + + return $query; + } + + /** + * Local scope to filter subscribers by tags + * @param \FluentCrm\Framework\Database\Query\Builder $query + * @param array $keys + * @param string $filterBy id/slug + * @return \FluentCrm\Framework\Database\Query\Builder $query + */ + public function scopeFilterByTags($query, $keys, $filterBy = 'id') + { + $prefix = 'fc_'; + + $keys = Sanitize::sanitizeTagIds($keys, false); + + return $query->whereIn('id', function ($q) use ($prefix, $keys, $filterBy) { + $q->from($prefix . 'tags') + ->join( + $prefix . 'subscriber_pivot', + $prefix . 'subscriber_pivot.object_id', + '=', + $prefix . 'tags.id' + ) + ->where($prefix . 'subscriber_pivot.object_type', 'FluentCrm\App\Models\Tag') + ->whereIn($prefix . 'tags.' . $filterBy, $keys) + ->groupBy($prefix . 'subscriber_pivot.subscriber_id') + ->select($prefix . 'subscriber_pivot.subscriber_id'); + }); + } + + /** + * Local scope to filter subscribers by not in tags + * @param \FluentCrm\Framework\Database\Query\Builder $query + * @param array $keys + * @param string $filterBy id/slug + * @return \FluentCrm\Framework\Database\Query\Builder $query + */ + public function scopeFilterByNotInTags($query, $keys, $filterBy = 'id') + { + $prefix = 'fc_'; + + $keys = Sanitize::sanitizeTagIds($keys, false); + + return $query->whereNotIn('id', function ($q) use ($prefix, $keys, $filterBy) { + $q->from($prefix . 'tags') + ->join( + $prefix . 'subscriber_pivot', + $prefix . 'subscriber_pivot.object_id', + '=', + $prefix . 'tags.id' + ) + ->where($prefix . 'subscriber_pivot.object_type', 'FluentCrm\App\Models\Tag') + ->whereIn($prefix . 'tags.' . $filterBy, $keys) + ->groupBy($prefix . 'subscriber_pivot.subscriber_id') + ->select($prefix . 'subscriber_pivot.subscriber_id'); + }); + } + + /** + * Local scope to filter subscribers by lists + * @param \FluentCrm\Framework\Database\Query\Builder $query + * @param array $keys + * @param string $filterBy id/slug + * @return \FluentCrm\Framework\Database\Query\Builder $query + */ + public function scopeFilterByLists($query, $keys, $filterBy = 'id') + { + $prefix = 'fc_'; + + $keys = Sanitize::sanitizeListIds($keys, false); + + return $query->whereIn('id', function ($q) use ($prefix, $keys, $filterBy) { + $q->from($prefix . 'lists') + ->join( + $prefix . 'subscriber_pivot', + $prefix . 'subscriber_pivot.object_id', + '=', + $prefix . 'lists.id' + ) + ->where($prefix . 'subscriber_pivot.object_type', 'FluentCrm\App\Models\Lists') + ->whereIn($prefix . 'lists.' . $filterBy, $keys) + ->groupBy($prefix . 'subscriber_pivot.subscriber_id') + ->select($prefix . 'subscriber_pivot.subscriber_id'); + }); + } + + /** + * Local scope to filter subscribers by not in lists + * @param \FluentCrm\Framework\Database\Query\Builder $query + * @param array $keys + * @param string $filterBy id/slug + * @return \FluentCrm\Framework\Database\Query\Builder $query + */ + public function scopeFilterByNotInLists($query, $keys, $filterBy = 'id') + { + $prefix = 'fc_'; + + $keys = Sanitize::sanitizeListIds($keys, false); + + return $query->whereNotIn('id', function ($q) use ($prefix, $keys, $filterBy) { + $q->from($prefix . 'lists') + ->join( + $prefix . 'subscriber_pivot', + $prefix . 'subscriber_pivot.object_id', + '=', + $prefix . 'lists.id' + ) + ->where($prefix . 'subscriber_pivot.object_type', 'FluentCrm\App\Models\Lists') + ->whereIn($prefix . 'lists.' . $filterBy, $keys) + ->groupBy($prefix . 'subscriber_pivot.subscriber_id') + ->select($prefix . 'subscriber_pivot.subscriber_id'); + }); + } + + /** + * Many2Many: Subscriber belongs to many tags + * @return \FluentCrm\Framework\Database\Orm\Relations\BelongsToMany + */ + public function tags() + { + $class = __NAMESPACE__ . '\Tag'; + + return $this->belongsToMany( + $class, + 'fc_subscriber_pivot', + 'subscriber_id', + 'object_id' + ) + ->wherePivot('object_type', $class) + ->withPivot('object_type') + ->orderBy('title', 'ASC') + ->withTimestamps(); + } + + public function company() + { + return $this->belongsTo(__NAMESPACE__ . '\Company', 'company_id', 'id'); + } + + public function companies() + { + $class = __NAMESPACE__ . '\Company'; + + return $this->belongsToMany( + $class, + 'fc_subscriber_pivot', + 'subscriber_id', + 'object_id' + ) + ->wherePivot('object_type', $class) + ->withPivot('object_type') + ->withTimestamps(); + } + + /** + * Local scope to filter subscribers by companies + * @param \FluentCrm\Framework\Database\Query\Builder $query + * @param array $keys + * @param string $filterBy id/slug + * @return \FluentCrm\Framework\Database\Query\Builder $query + */ + public function scopeFilterByCompanies($query, $keys, $filterBy = 'id') + { + $prefix = 'fc_'; + + return $query->whereIn('id', function ($q) use ($prefix, $keys, $filterBy) { + $q->from($prefix . 'companies') + ->join( + $prefix . 'subscriber_pivot', + $prefix . 'subscriber_pivot.object_id', + '=', + $prefix . 'companies.id' + ) + ->where($prefix . 'subscriber_pivot.object_type', 'FluentCrm\App\Models\Company') + ->whereIn($prefix . 'companies.' . $filterBy, $keys) + ->groupBy($prefix . 'subscriber_pivot.subscriber_id') + ->select($prefix . 'subscriber_pivot.subscriber_id'); + }); + } + + /** + * Many2Many: Subscriber has many email sequences + * @return \FluentCrm\Framework\Database\Orm\Relations\BelongsToMany + */ + public function sequences() + { + $class = '\FluentCampaign\App\Models\Sequence'; + + return $this->belongsToMany( + $class, + 'fc_sequence_tracker', + 'subscriber_id', + 'campaign_id' + ) + ->withoutGlobalScopes() + ->withTimestamps(); + } + + /** + * Many2Many: Subscriber has many sequence trackers + * @return \FluentCrm\Framework\Database\Orm\Relations\HasMany + */ + public function sequence_trackers() + { + $class = '\FluentCampaign\App\Models\SequenceTracker'; + + return $this->hasMany( + $class, + 'subscriber_id', + 'id' + ); + } + + /** + * Many2Many: Subscriber has many funnels + * @return \FluentCrm\Framework\Database\Orm\Relations\BelongsToMany + */ + public function funnels() + { + $class = __NAMESPACE__ . '\Funnel'; + + return $this->belongsToMany( + $class, + 'fc_funnel_subscribers', + 'subscriber_id', + 'funnel_id' + ) + ->withoutGlobalScopes() + ->withTimestamps(); + } + + /** + * Many2Many: Subscriber has many funnel subscribers + * @return \FluentCrm\Framework\Database\Orm\Relations\hasMany + */ + public function funnel_subscribers() + { + return $this->hasMany( + __NAMESPACE__ . '\FunnelSubscriber', + 'subscriber_id', + 'id' + ); + } + + /** + * hasMany: Subscriber has many commerce items + * @return \FluentCrm\Framework\Database\Orm\Relations\HasMany + */ + public function contact_commerce() + { + $class = '\FluentCampaign\App\Services\Commerce\ContactRelationModel'; + return $this->hasMany($class, 'subscriber_id', 'id'); + } + + /** + * hasOne: Subscriber has a commerce for a specific provider + * @return \FluentCrm\Framework\Database\Orm\Relations\hasOne + */ + public function commerce_by_provider() + { + $class = '\FluentCampaign\App\Services\Commerce\ContactRelationModel'; + return $this->hasOne($class, 'subscriber_id', 'id'); + } + + + /** + * hasOne: Subscriber has many commerce items + * @return \FluentCrm\Framework\Database\Orm\Relations\HasMany + */ + public function contact_commerce_items() + { + $class = '\FluentCampaign\App\Services\Commerce\ContactRelationItemsModel'; + return $this->hasMany($class, 'subscriber_id', 'id'); + } + + public function scopeCommerceItemsItemIds($query, $provider, $method, $column, $values) + { + $query->whereHas('contact_commerce_items', $provider) + ->{$method}($column, $values); + } + + public function affiliate_wp() + { + $class = '\FluentCampaign\App\Services\Integrations\AffiliateWP\AffiliateWPModel'; + return $this->hasOne($class, 'user_id', 'user_id'); + } + + /** + * Many2Many: Subscriber belongs to many lists + * @return Model Collection + */ + public function lists() + { + $class = __NAMESPACE__ . '\Lists'; + + return $this->belongsToMany( + $class, + 'fc_subscriber_pivot', + 'subscriber_id', + 'object_id' + ) + ->wherePivot('object_type', $class) + ->withPivot('object_type') + ->orderBy('title', 'ASC') + ->withTimestamps(); + } + + + /** + * One2Many: Subscriber has to many SubscriberMeta + * @return Model Collection + */ + public function meta() + { + $class = __NAMESPACE__ . '\SubscriberMeta'; + return $this->hasMany( + $class, + 'subscriber_id', + 'id' + ); + } + + /** + * One2Many: Subscriber has to many SubscriberMeta + * @return Model Collection + */ + public function custom_field_meta() + { + $class = __NAMESPACE__ . '\SubscriberMeta'; + return $this->hasMany( + $class, + 'subscriber_id', + 'id' + )->where('object_type', '=', 'custom_field'); + } + + /** + * One2Many: Subscriber has to many Click Metrics + * @return Model Collection + */ + public function urlMetrics() + { + $class = __NAMESPACE__ . '\CampaignUrlMetric'; + + return $this->hasMany( + $class, + 'subscriber_id', + 'id' + ); + } + + /** + * A subscriber has many campaign emails. + * + * @return \FluentCrm\Framework\Database\Orm\Relations\HasMany + */ + public function campaignEmails() + { + return $this->hasMany(CampaignEmail::class, 'subscriber_id', 'id'); + } + + /** + * A subscriber has many notes and activities. + * + * @return \FluentCrm\Framework\Database\Orm\Relations\HasMany + */ + public function notes() + { + return $this->hasMany(SubscriberNote::class, 'subscriber_id', 'id'); + } + + /** + * A subscriber has many tracking events. + * + * @return \FluentCrm\Framework\Database\Orm\Relations\HasMany + */ + public function trackingEvents() + { + return $this->hasMany(EventTracker::class, 'subscriber_id', 'id'); + } + + /** + * One2Many: Subscriber has to many custom fields value + * @return array + */ + public function custom_fields() + { + $customFields = fluentcrm_get_custom_contact_fields(); + + if (!$customFields || !is_array($customFields)) { + return []; + } + + $keys = array_map(function ($item) { + return $item['slug']; + }, $customFields); + + + if (!$keys) { + return []; + } + + $items = $this->custom_field_meta()->whereIn('key', $keys)->get(); + $formattedValues = []; + foreach ($items as $item) { + $formattedValues[$item->key] = apply_filters('fluent_crm/modify_custom_field_value', $item->value); + } + return $formattedValues; + } + + /** + * Update Custom Field Values + * @param $values array of custom values + * @param bool $deleteOtherValues + * @return array of updated values + */ + public function syncCustomFieldValues($values, $deleteOtherValues = true) + { + $emptyValues = array_filter($values, function ($value) { + return $value === ''; + }); + + $updateValues = []; + + if ($deleteOtherValues) { + $deleteMetaKeys = array_keys($emptyValues); + + if ($deleteMetaKeys) { + $this->custom_field_meta()->whereIn('key', $deleteMetaKeys)->delete(); + foreach ($deleteMetaKeys as $key) { + $updateValues[$key] = ''; + } + } + } + + $newValues = array_filter($values, function ($value) { + return $value !== ''; + }); + + foreach ($newValues as $key => $value) { + $exist = $this->meta()->where('key', $key)->first(); + if ($exist) { + if ($exist->value == $value) { + continue; + } + $updateValues[$key] = $value; + $exist->fill(['value' => $value])->save(); + } else { + $meta = new SubscriberMeta(); + $meta->fill([ + 'subscriber_id' => $this->id, + 'object_type' => 'custom_field', + 'key' => $key, + 'value' => $value, + 'created_by' => get_current_user_id() + ]); + $meta->save(); + $updateValues[$key] = $value; + } + } + + if ($updateValues) { + do_action('fluentcrm_contact_custom_data_updated', $newValues, $this, $updateValues); + do_action('fluent_crm/contact_custom_data_updated', $newValues, $this, $updateValues); + } + + return $updateValues; + } + + public function stats() + { + return [ + 'emails' => CampaignEmail::where('subscriber_id', $this->id) + ->where('status', 'sent') + ->count(), + 'opens' => CampaignEmail::where('subscriber_id', $this->id) + ->where('is_open', '>', 0) + ->where('status', 'sent') + ->count(), + 'clicks' => CampaignEmail::where('subscriber_id', $this->id) + ->whereNotNull('click_counter') + ->where('status', 'sent') + ->count() + ]; + } + + /** + * Save the subscriber. + * + * @param array $data + */ + public static function store($data = []) + { + $model = static::create($data); + + if ($customValues = Arr::get($data, 'custom_values')) { + $model->syncCustomFieldValues($customValues); + } + + $tagIds = Arr::get($data, 'tags', []); + if ($tagIds) { + $model->attachTags($tagIds); + } + + $listIds = Arr::get($data, 'lists', []); + if ($listIds) { + $model->attachLists($listIds); + } + + $companyId = Arr::get($data, 'company_id'); + if ($companyId && Helper::isExperimentalEnabled('company_module')) { + $model->attachCompanies([$companyId]); + } + + return $model; + } + + /** + * Get subscriber mappable fields. + * + * @return array + */ + public static function mappables() + { + $fields = [ + 'prefix' => __('Name Prefix', 'fluent-crm'), + 'first_name' => __('First Name', 'fluent-crm'), + 'last_name' => __('Last Name', 'fluent-crm'), + 'full_name' => __('Full Name', 'fluent-crm'), + 'email' => __('Email', 'fluent-crm'), + 'contact_type' => __('Contact Type', 'fluent-crm'), + 'timezone' => __('Timezone', 'fluent-crm'), + 'address_line_1' => __('Address Line 1', 'fluent-crm'), + 'address_line_2' => __('Address Line 2', 'fluent-crm'), + 'city' => __('City', 'fluent-crm'), + 'state' => __('State', 'fluent-crm'), + 'postal_code' => __('Postal Code', 'fluent-crm'), + 'country' => __('Country', 'fluent-crm'), + 'ip' => __('IP Address', 'fluent-crm'), + 'phone' => __('Phone', 'fluent-crm'), + 'source' => __('Source', 'fluent-crm'), + 'date_of_birth' => __('Date of Birth (Y-m-d Format only)', 'fluent-crm') + ]; + + if (Helper::isCompanyEnabled()) { + $fields['company_id'] = __('Primary Company', 'fluent-crm'); + } + + return $fields; + } + + /** + * Accessor to get dynamic photo attribute + * @return string + */ + public function getPhotoAttribute() + { + if (!empty($this->attributes['avatar'])) { + return $this->attributes['avatar']; + } + + if (empty($this->attributes['email'])) { + return ''; + } + + $fallBack = ''; + if (isset($this->attributes['first_name'])) { + $fallBack = $this->attributes['first_name']; + } + + if (isset($this->attributes['last_name'])) { + $fallBack .= '+' . $this->attributes['last_name']; + } + + return fluentcrmGravatar($this->attributes['email'], $fallBack); + } + + /** + * Accessor to get dynamic full_name attribute + * @return string + */ + public function getFullNameAttribute() + { + $fname = isset($this->attributes['first_name']) ? $this->attributes['first_name'] : ''; + $lname = isset($this->attributes['last_name']) ? $this->attributes['last_name'] : ''; + return trim("{$fname} {$lname}"); + } + + /** + * Import csv/wpusers into subscribers + * @param array $data + * @param array $tags + * @param array $lists + * @param mixed $update string true/false or boolean true/false + * @param string $newStatus status for the new subscribers + * @param boolean $doubleOptin Send Double Optin Emails for new pending contacts + * @param string $source Fallback Source for New Contacts + * @return array affected records/collection + */ + public static function import($data, $tags, $lists, $update, $newStatus = '', $doubleOptin = false, $forceStatusChange = false, $source = '') + { + if (!defined('FLUENTCRM_DOING_BULK_IMPORT')) { + define('FLUENTCRM_DOING_BULK_IMPORT', true); + } + + ob_start(); + $insertables = []; + $updateables = []; + $updatedModels = new Collection; + $shouldUpdate = $update === 'true' || $update === true; + + $records = []; + + $uniqueEmails = []; + + foreach ($data as $index => $record) { + + $email = $record['email']; + + if (!filter_var($email, FILTER_VALIDATE_EMAIL) || in_array(strtolower($email), $uniqueEmails)) { + unset($data[$index]); + continue; + } + + $uniqueEmails[] = strtolower($email); + + $record = self::explodeFullName($record); + $data[$index] = $record; + + $records[] = $email; + } + + $existingSubscribers = []; + $oldSubscribers = static::whereIn('email', $records)->get(); + + foreach ($oldSubscribers as $model) { + $existingSubscribers[strtolower($model->email)] = $model; + } + + $strictStatuses = fluentcrm_strict_statues(); + + $newContactCustomFields = []; + $newRecords = []; + $skips = []; + $newLists = []; + $newTags = []; + foreach ($data as $item) { + $item['hash'] = md5($item['email']); + $lowEmail = strtolower($item['email']); + if (isset($existingSubscribers[$lowEmail])) { + if (!$forceStatusChange && $newStatus && !in_array($newStatus, $strictStatuses)) { + $item['status'] = $existingSubscribers[$lowEmail]->status; + } else if ($newStatus) { + $item['status'] = $newStatus; + } + + unset($item['source']); + + $customValues = Arr::get($item, 'custom_values'); + if ($shouldUpdate && $customValues) { + $existingSubscribers[$lowEmail]->syncCustomFieldValues($customValues, false); + } + + //if item has lists or tags that need to be processed then mapping to email + if (Arr::get($item, 'lists')) { + $existingListIdsOfUser = $existingSubscribers[$lowEmail]->lists()->get()->pluck('id')->toArray(); + $newLists[$item['email']] = Helper::getNewAttachableLists(Arr::get($item, 'lists'), $existingListIdsOfUser, $lists); + } + if (Arr::get($item, 'tags')) { + $existingTagIdsOfUser = $existingSubscribers[$lowEmail]->tags()->get()->pluck('id')->toArray(); + $newTags[$item['email']] = Helper::getNewAttachableTags(Arr::get($item, 'tags'), $existingTagIdsOfUser, $tags); + } + + unset($item['custom_values']); + unset($item['id']); + unset($item['created_at']); + unset($item['lists']); + unset($item['tags']); + + $item['updated_at'] = fluentCrmTimestamp(); + $updateables[] = array_filter($item); + } else { + if (isset($newRecords[$item['email']])) { + $skips[] = $item; + continue; + } + $extraValues = [ + 'created_at' => fluentCrmTimestamp() + ]; + if ($newStatus) { + $extraValues['status'] = $newStatus; + } + + if ($customValues = Arr::get($item, 'custom_values')) { + $newContactCustomFields[$item['email']] = $customValues; + } + + //if item has lists or tags that need to be processed then mapping to email + if (Arr::get($item, 'lists')) { + $newLists[$item['email']] = Arr::get($item, 'lists'); + } + if (Arr::get($item, 'tags')) { + $newTags[$item['email']] = Arr::get($item, 'tags'); + } + + $itemEmail = $item['email']; + + unset($item['custom_values']); + unset($item['id']); + unset($item['lists']); + unset($item['tags']); + + if (empty($item['source']) && $source) { + $item['source'] = $source; + } + + $newRecords[$itemEmail] = 1; + $insertables[] = array_filter(array_merge($item, $extraValues)); + } + } + + $insertedModels = []; + if ($insertables) { + foreach ($insertables as $insertable) { + $attachableTags = $tags; + $attachableLists = $lists; + $insertedModel = self::create($insertable); + if ($newContactCustomFields) { + if (isset($newContactCustomFields[$insertedModel->email])) { + $insertedModel->syncCustomFieldValues( + $newContactCustomFields[$insertedModel->email], + false + ); + } + } + + //checking insertable email has tags, lists that need to be created or is already have + if (!empty($newTags[$insertedModel->email])) { + $newlyCreateTagIds = Helper::createNewTags($newTags[$insertedModel->email]); + $attachableTags = array_merge($tags, $newlyCreateTagIds); + } + + if (!empty($newLists[$insertedModel->email])) { + $newlyCreateListIds = Helper::createNewLists($newLists[$insertedModel->email]); + $attachableLists = array_merge($lists, $newlyCreateListIds); + } + + if ($attachableTags || $attachableLists || $doubleOptin) { + $attachableTags && $insertedModel->attachTags($attachableTags); + $attachableLists && $insertedModel->attachLists($attachableLists); + + if ($doubleOptin && $insertedModel->status == 'pending') { + $insertedModel->sendDoubleOptinEmail(); + } + } + + + if (!empty($insertable['company_id']) && Helper::isCompanyEnabled()) { + $insertedModel->attachCompanies([$insertable['company_id']]); + } + + /* + * @deprecated since 2.8.0. Use fluent_crm/contact_created instead + */ + do_action('fluentcrm_contact_created', $insertedModel); + do_action('fluent_crm/contact_created', $insertedModel); + + $insertedModels[] = $insertedModel; + } + } + + if ($shouldUpdate) { + foreach ($updateables as $updateable) { + $existingModel = $existingSubscribers[strtolower($updateable['email'])]; + $oldStatus = $existingModel->status; + $existingModel->fill($updateable); + + $updateData = $existingModel->getDirty(); + + if ($updateData) { + $existingModel->save(); + + if (!empty($updateable['company_id']) && Helper::isCompanyEnabled()) { + $existingModel->attachCompanies([$updateable['company_id']]); + } + + if (!empty($updateable['status']) && $updateable['status'] != $oldStatus) { + $newStatus = $updateable['status']; + do_action('fluent_crm/subscriber_status_changed', $existingModel, $oldStatus, $newStatus); + do_action('fluentcrm_subscriber_status_to_' . $newStatus, $existingModel, $oldStatus); + } + + //attaching new lists, tags to subscriber + if (!empty($newLists[$updateable['email']])) { + $existingModel->attachLists($newLists[$updateable['email']]); + } + if (!empty($newTags[$updateable['email']])) { + $existingModel->attachTags($newTags[$updateable['email']]); + } + + do_action('fluentcrm_contact_updated', $existingModel, $updateData); + do_action('fluent_crm/contact_updated', $existingModel, $updateData); + } + + $updatedModels->push($existingModel); + } + } + + // Syncing Tags & Lists + if ($tags || $lists || $doubleOptin) { + if ($shouldUpdate) { + foreach ($oldSubscribers as $model) { + $tags && $model->attachTags($tags); + $lists && $model->attachLists($lists); + } + } + } + + do_action('fluentcrm_contacts_imported_bulk', $insertedModels); + do_action('fluentcrm_contacts_updated_bulk', $updatedModels); + + $errors = ob_get_clean(); + + return [ + 'inserted' => $insertedModels, + 'updated' => $updatedModels, + 'skips' => $skips, + 'errors' => $errors + ]; + } + + public function updateOrCreate($data, $forceUpdate = false, $deleteOtherValues = false, $sync = false) + { + $subscriberData = static::explodeFullName($data); + $subscriberData = array_filter(Arr::only($subscriberData, $this->getFillable())); + $tags = Arr::get($data, 'tags', []); + $lists = Arr::get($data, 'lists', []); + $companies = Arr::get($data, 'companies', []); + + $exist = static::where('email', $subscriberData['email'])->first(); + + if (empty($subscriberData['user_id'])) { + $user = get_user_by('email', $subscriberData['email']); + if ($user) { + $subscriberData['user_id'] = $user->ID; + } + } + + $isNew = true; + $oldStatus = ''; + if ($exist) { + $isNew = false; + $oldStatus = $exist->status; + } + + if (!empty($data['status'])) { + $status = $data['status']; + if ($forceUpdate) { + $subscriberData['status'] = $status; + } else if ($exist && $exist->status == 'subscribed') { + unset($subscriberData['status']); + } else if ($exist && in_array($exist->status, ['bounced', 'complained', 'spammed'])) { + unset($subscriberData['status']); + } else { + $subscriberData['status'] = $status; + } + + if ($status == 'unsubscribed') { + $subscriberData['status'] = 'unsubscribed'; + } + } + + $isSubscribed = false; + if (($exist && $exist->status != 'subscribed') && (!empty($subscriberData['status']) && $subscriberData['status'] === 'subscribed')) { + $isSubscribed = true; + } else if (!$exist && (!empty($subscriberData['status']) && $subscriberData['status'] === 'subscribed')) { + $isSubscribed = true; + } + + $dirtyFields = []; + + if ($exist) { + $oldEmail = $exist->email; + $exist->fill($subscriberData); + $dirtyFields = $exist->getDirty(); + + if ($dirtyFields) { + $exist->save(); + if (isset($dirtyFields['email'])) { + do_action('fluent_crm/contact_email_changed', $exist, $oldEmail); + } + } + } else { + if (!isset($subscriberData['created_at'])) { + $subscriberData['created_at'] = current_time('mysql'); + } + $exist = static::create($subscriberData); + $exist = $this->find($exist->id); + $exist->wasRecentlyCreated = true; + } + + $customFieldsChanges = []; + if ($customValues = Arr::get($data, 'custom_values', [])) { + $customFieldsChanges = $exist->syncCustomFieldValues($customValues, $deleteOtherValues); + } + + /* + * TODO: investigate this attachTags and attachLists method. for Masiur + */ + // Syncing Lists + if ($lists) { + $exist->attachLists($lists); + } + + // Syncing Tags + if ($tags) { + $exist->attachTags($tags); + } + + + if (Helper::isCompanyEnabled()) { + $companyId = $exist->company_id; + if (empty($companyId)) { + // Syncing Companies + $companies && $exist->attachCompanies($companies); + } else { + $exist->attachCompanies([$companyId]); + } + } + + if ($detachTags = Arr::get($data, 'detach_tags', [])) { + $exist->detachTags($detachTags); + } + + if ($detachLists = Arr::get($data, 'detach_lists', [])) { + $exist->detachLists($detachLists); + } + + if ($isNew) { + do_action('fluentcrm_contact_created', $exist); // @deprecated since 2.8.0. Use fluent_crm/contact_created instead + do_action('fluent_crm/contact_created', $exist); + } else if ($dirtyFields || $customFieldsChanges) { + do_action('fluentcrm_contact_updated', $exist, $dirtyFields); // @deprecated since 2.8.0. Use fluent_crm/contact_updated instead + do_action('fluent_crm/contact_updated', $exist, $dirtyFields); + } + + if ($isSubscribed && $exist->status == 'subscribed') { + if (!$isNew) { + do_action('fluent_crm/subscriber_status_changed', $exist, $oldStatus, $exist->status); + } + do_action('fluentcrm_subscriber_status_to_subscribed', $exist, $oldStatus); + } + + return $exist; + } + + public function sendDoubleOptinEmail() + { + $lastDoubleOptin = fluentcrm_get_subscriber_meta($this->id, '_last_double_optin_timestamp'); + if ($lastDoubleOptin && (time() - $lastDoubleOptin < 150)) { + return false; + } else { + fluentcrm_update_subscriber_meta($this->id, '_last_double_optin_timestamp', time()); + } + + return (new Handler())->sendDoubleOptInEmail($this); + } + + public static function explodeFullName($record) + { + if (!empty($record['first_name']) || !empty($record['last_name'])) { + return $record; + } + if (!empty($record['full_name'])) { + $fullNameArray = explode(' ', $record['full_name']); + $record['first_name'] = array_shift($fullNameArray); + if ($fullNameArray) { + $record['last_name'] = implode(' ', $fullNameArray); + } + unset($record['full_name']); + } + + return $record; + } + + public function attachLists($listIds) + { + if (!$listIds) { + return $this; + } + + // Guard against attaching to an unsaved Subscriber (id = 0 or unset). + // Without this, INSERT IGNORE would happily write a (0, X, 'Lists') + // garbage row and the contact_added_to_lists action would fire with + // $this->id = 0. Sanitize is also skipped because sanitizeListIds() can + // create new lists as a side effect, which we don't want to do for an + // invalid subscriber. + if (empty($this->id)) { + return $this; + } + + $listIds = Sanitize::sanitizeListIds($listIds); + $listIds = array_filter(array_map('intval', $listIds)); + + if (!$listIds) { + return $this; + } + + global $wpdb; + $pivotTable = $wpdb->prefix . 'fc_subscriber_pivot'; + $objectType = 'FluentCrm\App\Models\Lists'; + + // Per-row INSERT IGNORE. The composite unique key on + // (subscriber_id, object_id, object_type) added in the SubscriberPivot + // migration makes this race-safe: when two concurrent integrations + // (e.g. WooCommerce + WP Fusion + LearnDash hooks all firing on the + // same enrollment) call attachLists() with the same pair, the second + // INSERT IGNORE gets rows_affected = 0 and we don't fire the + // contact_added_to_lists action for that ID — preventing the + // duplicate-event leak the customer was seeing. + // Timestamps use current_time('mysql') (WordPress site timezone) to + // match the ORM's freshTimestamp() convention used by historical rows. + $now = current_time('mysql'); + $newListIds = []; + + foreach ($listIds as $listId) { + $affected = $wpdb->query($wpdb->prepare( + "INSERT IGNORE INTO {$pivotTable} (subscriber_id, object_id, object_type, created_at, updated_at) VALUES (%d, %d, %s, %s, %s)", + $this->id, $listId, $objectType, $now, $now + )); + // $wpdb->query() returns false on errors like deadlocks or lock-wait + // timeouts. PHP's `false > 0` is false, so without this check the + // failure would be silently treated as "row already existed" and we + // wouldn't fire contact_added_to_lists even when the row may have + // ultimately been written. Log and skip to surface the issue. + if ($affected === false) { + Helper::debugLog('Subscriber::attachLists pivot insert failed', $wpdb->last_error, 'error'); + continue; + } + if ($affected > 0) { + $newListIds[] = $listId; + } + } + + // Always refresh the in-memory relationship so callers reusing this + // Subscriber instance see post-write state, even on the no-op path. + // Matches the original ORM-driven attach() behavior, which loaded + // upfront for the diff and reloaded after write. + $this->load('lists'); + + if ($newListIds) { + fluentcrm_contact_added_to_lists($newListIds, $this); + + do_action('fluent_crm/contact_added_to_lists', $this, $newListIds); + } + + return $this; + } + + public function attachTags($tagIds) + { + if (!$tagIds) { + return $this; + } + + // Guard against attaching to an unsaved Subscriber — see attachLists(). + if (empty($this->id)) { + return $this; + } + + $tagIds = Sanitize::sanitizeTagIds($tagIds); + $tagIds = array_filter(array_map('intval', $tagIds)); + + if (!$tagIds) { + return $this; + } + + global $wpdb; + $pivotTable = $wpdb->prefix . 'fc_subscriber_pivot'; + $objectType = 'FluentCrm\App\Models\Tag'; + + // Per-row INSERT IGNORE — see attachLists() for the rationale. + $now = current_time('mysql'); + $newTagIds = []; + + foreach ($tagIds as $tagId) { + $affected = $wpdb->query($wpdb->prepare( + "INSERT IGNORE INTO {$pivotTable} (subscriber_id, object_id, object_type, created_at, updated_at) VALUES (%d, %d, %s, %s, %s)", + $this->id, $tagId, $objectType, $now, $now + )); + if ($affected === false) { + Helper::debugLog('Subscriber::attachTags pivot insert failed', $wpdb->last_error, 'error'); + continue; + } + if ($affected > 0) { + $newTagIds[] = $tagId; + } + } + + // Always refresh the relation — see attachLists() for rationale. + $this->load('tags'); + + if ($newTagIds) { + fluentcrm_contact_added_to_tags($newTagIds, $this); + + do_action('fluent_crm/contact_added_to_tags', $this, $newTagIds); + } + + return $this; + } + + public function attachCompanies($companyIds) + { + if (!$companyIds) { + return $this; + } + + // Guard against attaching to an unsaved Subscriber — see attachLists(). + if (empty($this->id)) { + return $this; + } + + $companyIds = array_filter(array_map('intval', $companyIds)); + + if (!$companyIds) { + return $this; + } + + global $wpdb; + $pivotTable = $wpdb->prefix . 'fc_subscriber_pivot'; + $objectType = 'FluentCrm\App\Models\Company'; + + // Per-row INSERT IGNORE — see attachLists() for the rationale. + $now = current_time('mysql'); + $newCompanyIds = []; + + foreach ($companyIds as $companyId) { + $affected = $wpdb->query($wpdb->prepare( + "INSERT IGNORE INTO {$pivotTable} (subscriber_id, object_id, object_type, created_at, updated_at) VALUES (%d, %d, %s, %s, %s)", + $this->id, $companyId, $objectType, $now, $now + )); + if ($affected === false) { + Helper::debugLog('Subscriber::attachCompanies pivot insert failed', $wpdb->last_error, 'error'); + continue; + } + if ($affected > 0) { + $newCompanyIds[] = $companyId; + } + } + + // Always refresh the relation — see attachLists() for rationale. + $this->load('companies'); + + if ($newCompanyIds) { + fluentcrm_contact_added_to_companies($newCompanyIds, $this); + } + + return $this; + } + + public function detachLists($listIds) + { + if (!$listIds) { + return $this; + } + + // Guard against detaching from an unsaved Subscriber — defensive, same + // pattern as the attach methods. With $this->id = 0 the SELECT would + // match any garbage rows the migration is supposed to have cleaned up. + if (empty($this->id)) { + return $this; + } + + $listIds = Sanitize::sanitizeListIds($listIds, false); + $listIds = array_filter(array_map('intval', $listIds)); + + if (!$listIds) { + return $this; + } + + global $wpdb; + $pivotTable = $wpdb->prefix . 'fc_subscriber_pivot'; + $objectType = 'FluentCrm\App\Models\Lists'; + + // Fresh DB read — bypass any stale relationship cached on $this — so we + // only consider list IDs that actually exist for this subscriber right now. + $placeholders = implode(',', array_fill(0, count($listIds), '%d')); + $existingListIds = $wpdb->get_col($wpdb->prepare( + "SELECT object_id FROM {$pivotTable} WHERE subscriber_id = %d AND object_type = %s AND object_id IN ({$placeholders})", + array_merge([$this->id, $objectType], $listIds) + )); + + $existingListIds = array_map('intval', $existingListIds); + $validListIds = array_values(array_intersect($listIds, $existingListIds)); + + if (!$validListIds) { + return $this; + } + + // Per-row DELETE so a concurrent detachLists() racing us against the + // same (subscriber, list) pair can be distinguished via rows_affected. + // The first DELETE removes the row and reports affected = 1; the + // second reports 0 and we skip firing the action — preventing the + // double-fire that would happen if we trusted our pre-DELETE diff. + $removedListIds = []; + foreach ($validListIds as $listId) { + $affected = $wpdb->query($wpdb->prepare( + "DELETE FROM {$pivotTable} WHERE subscriber_id = %d AND object_type = %s AND object_id = %d", + $this->id, $objectType, $listId + )); + if ($affected === false) { + Helper::debugLog('Subscriber::detachLists pivot delete failed', $wpdb->last_error, 'error'); + continue; + } + if ($affected > 0) { + $removedListIds[] = $listId; + } + } + + // Always refresh the relation — see attachLists() for rationale. + $this->load('lists'); + + if ($removedListIds) { + fluentcrm_contact_removed_from_lists($removedListIds, $this); + + do_action('fluent_crm/contact_removed_from_lists', $this, $removedListIds); + } + + return $this; + } + + public function detachTags($tagsIds) + { + if (!$tagsIds) { + return $this; + } + + // Guard against detaching from an unsaved Subscriber — see detachLists(). + if (empty($this->id)) { + return $this; + } + + $tagsIds = Sanitize::sanitizeTagIds($tagsIds, false); + $tagsIds = array_filter(array_map('intval', $tagsIds)); + + if (!$tagsIds) { + return $this; + } + + global $wpdb; + $pivotTable = $wpdb->prefix . 'fc_subscriber_pivot'; + $objectType = 'FluentCrm\App\Models\Tag'; + + // Fresh DB read — see detachLists() for rationale. + $placeholders = implode(',', array_fill(0, count($tagsIds), '%d')); + $existingTagIds = $wpdb->get_col($wpdb->prepare( + "SELECT object_id FROM {$pivotTable} WHERE subscriber_id = %d AND object_type = %s AND object_id IN ({$placeholders})", + array_merge([$this->id, $objectType], $tagsIds) + )); + + $existingTagIds = array_map('intval', $existingTagIds); + $validTagIds = array_values(array_intersect($tagsIds, $existingTagIds)); + + if (!$validTagIds) { + return $this; + } + + // Per-row DELETE with rows_affected check — see detachLists(). + $removedTagIds = []; + foreach ($validTagIds as $tagId) { + $affected = $wpdb->query($wpdb->prepare( + "DELETE FROM {$pivotTable} WHERE subscriber_id = %d AND object_type = %s AND object_id = %d", + $this->id, $objectType, $tagId + )); + if ($affected === false) { + Helper::debugLog('Subscriber::detachTags pivot delete failed', $wpdb->last_error, 'error'); + continue; + } + if ($affected > 0) { + $removedTagIds[] = $tagId; + } + } + + // Always refresh the relation — see attachLists() for rationale. + $this->load('tags'); + + if ($removedTagIds) { + fluentcrm_contact_removed_from_tags($removedTagIds, $this); + + do_action('fluent_crm/contact_removed_from_tags', $this, $removedTagIds); + } + + return $this; + } + + public function detachCompanies($companyIds) + { + if (!$companyIds) { + return $this; + } + + // Guard against detaching from an unsaved Subscriber — see detachLists(). + if (empty($this->id)) { + return $this; + } + + $companyIds = array_filter(array_map('intval', $companyIds)); + + if (!$companyIds) { + return $this; + } + + global $wpdb; + $pivotTable = $wpdb->prefix . 'fc_subscriber_pivot'; + $objectType = 'FluentCrm\App\Models\Company'; + + // Fresh DB read — see detachLists() for rationale. + $placeholders = implode(',', array_fill(0, count($companyIds), '%d')); + $existingCompanyIds = $wpdb->get_col($wpdb->prepare( + "SELECT object_id FROM {$pivotTable} WHERE subscriber_id = %d AND object_type = %s AND object_id IN ({$placeholders})", + array_merge([$this->id, $objectType], $companyIds) + )); + + $existingCompanyIds = array_map('intval', $existingCompanyIds); + $validCompanyIds = array_values(array_intersect($companyIds, $existingCompanyIds)); + + if (!$validCompanyIds) { + return $this; + } + + // Per-row DELETE with rows_affected check — see detachLists(). + $removedCompanyIds = []; + foreach ($validCompanyIds as $companyId) { + $affected = $wpdb->query($wpdb->prepare( + "DELETE FROM {$pivotTable} WHERE subscriber_id = %d AND object_type = %s AND object_id = %d", + $this->id, $objectType, $companyId + )); + if ($affected === false) { + Helper::debugLog('Subscriber::detachCompanies pivot delete failed', $wpdb->last_error, 'error'); + continue; + } + if ($affected > 0) { + $removedCompanyIds[] = $companyId; + } + } + + // Always refresh the relation — see attachLists() for rationale. + $this->load('companies'); + + if ($removedCompanyIds) { + fluentcrm_contact_removed_from_companies($removedCompanyIds, $this); + } + + return $this; + } + + public function unsubscribeReason($metaKey = 'unsubscribe_reason') + { + return fluentcrm_get_subscriber_meta($this->id, $metaKey, ''); + } + + public function unsubscribeReasonDate($metaKey = 'unsubscribe_reason') + { + $item = SubscriberMeta::where('key', $metaKey) + ->where('subscriber_id', $this->id) + ->first(); + + if ($item) { + return (string)$item->updated_at; + } + return ''; + } + + public function hasAnyTagId($tagIds) + { + if (!$tagIds || !is_array($tagIds)) { + return false; + } + + $tagIds = Sanitize::sanitizeTagIds($tagIds, false); + + $this->load('tags'); + + foreach ($this->tags as $tag) { + if (in_array($tag->id, $tagIds)) { + return true; + } + } + return false; + } + + public function hasAnyListId($listIds) + { + if (!$listIds || !is_array($listIds)) { + return false; + } + + $listIds = Sanitize::sanitizeListIds($listIds, false); + + $this->load('lists'); + + foreach ($this->lists as $list) { + if (in_array($list->id, $listIds)) { + return true; + } + } + return false; + } + + public function updateMeta($metaKey, $metaValue, $objectType) + { + $exist = $this->meta() + ->where('key', $metaKey) + ->where('object_type', $objectType) + ->first(); + + if ($exist) { + $exist->value = $metaValue; + $exist->save(); + return true; + } + $this->meta()->create([ + 'key' => $metaKey, + 'object_type' => $objectType, + 'value' => $metaValue + ]); + + return true; + } + + public function getMeta($metaKey, $objectType) + { + $exist = $this->meta() + ->where('key', $metaKey) + ->where('object_type', $objectType) + ->first(); + + if ($exist) { + return $exist->value; + } + + return false; + } + + /** + * Parse filter to set proper operator and value for the filter query for date operators + * + * @param array $filter + * @return array + */ + public static function filterParser($filter) + { + + switch ($filter['operator']) { + case 'before': + $filter['operator'] = '<'; + if (!empty($filter['value']) && strlen($filter['value']) < 11) { + $filter['value'] = $filter['value'] . ' 00:00:00'; + } + break; + + case 'after': + $filter['operator'] = '>'; + if (!empty($filter['value']) && strlen($filter['value']) < 11) { + $filter['value'] = $filter['value'] . ' 00:00:00'; + } + break; + + case 'date_equal': + case 'contains': + $filter['operator'] = 'LIKE'; + $filter['value'] = '%' . $filter['value'] . '%'; + break; + case 'not_contains': + $filter['operator'] = 'NOT LIKE'; + $filter['value'] = '%' . $filter['value'] . '%'; + break; + + case 'days_before': + $daysToSeconds = intval($filter['value']) * 24 * 60 * 60; + $filter['operator'] = '<'; + $filter['value'] = gmdate('Y-m-d', current_time('timestamp') - $daysToSeconds); + break; + + case 'days_within': + $daysToSeconds = intval($filter['value']) * 24 * 60 * 60; + $filter['operator'] = 'BETWEEN'; + $filter['value'] = [ + gmdate('Y-m-d 00:00:01', current_time('timestamp') - $daysToSeconds), + gmdate('Y-m-d') . ' 23:59:59' + ]; + break; + } + + return $filter; + } + + public static function applyGeneralFilterQuery($query, $filter, $referenceColumn = 'value') + { + + $exactOperators = ['=', '!=', '>', '<']; + + $operator = self::parseCustomFieldsFilterOperator($filter); + + if (in_array($operator, $exactOperators)) { + if ($operator == '>' || $operator == '<') { + $filter['value'] = (float)$filter['value']; + } else { + $filter['value'] = sanitize_text_field($filter['value']); + } + $query->where($referenceColumn, $operator, $filter['value']); + } else { + $filter = self::filterParser($filter); + + if ($filter['operator'] != $operator) { + $newOperator = $filter['operator']; + if ($newOperator == 'BETWEEN') { + $query->whereBetween($referenceColumn, $filter['value']); + } elseif ($newOperator == 'LIKE' || $newOperator == 'NOT LIKE') { + $query->where($referenceColumn, $newOperator, '%' . $filter['value'] . '%'); + } else { + // Date operators (before/after/date_equal/days_before/days_within) are + // already normalized by filterParser() into '<' / '>' with a datetime + // string value. A plain where() is correct here — the previous + // whereTimestamp() call was a phantom method that the WPFluent + // dynamic-where __call magic silently rewrote to + // `WHERE timestamp = ''`, producing a SQL error. + $query->where($referenceColumn, $newOperator, $filter['value']); + } + } else { + $filter['value'] = sanitize_text_field($filter['value']); + $query->where($referenceColumn, $operator, '%' . $filter['value'] . '%'); + } + } + + return $query; + } + + /** + * Dynamically build relation filter query for the Subscriber + * model. It handles purchase, lists, tags relations. + * + * @param string $relation + * @param \FluentCrm\Framework\Database\Orm\Builder|\FluentCrm\Framework\Database\Query\Builder $query + * @param string $method + * @param string $subMethod + * @param string $subField + * @param array $filter + * @param string $provider + * @return \FluentCrm\Framework\Database\Orm\Builder|\FluentCrm\Framework\Database\Query\Builder + */ + public static function buildRelationFilterQuery($relation, $query, $method, $subMethod, $subField, $filter, $provider = false) + { + if (in_array($filter['operator'], ['in_all', 'not_in_all'])) { + foreach ($filter['value'] as $item) { + $query = static::buildRelationFilterQuery($relation, $query, $method, $subMethod, $subField, ['value' => $item, 'operator' => ''], $provider); + } + } else { + $query = $query->{$method}($relation, function ($relationQuery) use ($subMethod, $subField, $filter, $provider) { + $relationQuery = $relationQuery->{$subMethod}($subField, $filter['value']); + + if ($provider) { + $relationQuery = $relationQuery->where('provider', $provider); + } + + return $relationQuery; + }); + } + + return $query; + } + + + /** + * Dynamically build relation filter query for the Subscriber model. + * It handles taxonomy query for subscribers purchase history mainly + * + * @param string $primaryRelation + * @param string $childRelation + * @param \FluentCrm\Framework\Database\Orm\Builder|\FluentCrm\Framework\Database\Query\Builder $mainQuery + * @param string $method + * @param string $subField + * @param array $itemIds + * @param $checkAll bool + * @param string $provider + * @return \FluentCrm\Framework\Database\Orm\Builder|\FluentCrm\Framework\Database\Query\Builder + */ + public static function buildChildRelationFilterQuery($primaryRelation, $childRelation, $mainQuery, $method, $subField, $itemIds, $checkAll = false, $provider = false) + { + if (!$checkAll) { + return $mainQuery->{$method}($primaryRelation, function ($query) use ($itemIds, $provider, $childRelation, $subField) { + $query->whereHas($childRelation, function ($q) use ($itemIds, $subField) { + $q->whereIn($subField, $itemIds); + return $q; + }); + + if ($provider) { + $query->where('provider', $provider); + } + + return $query; + }); + } + + return $mainQuery->{$method}($primaryRelation, function ($query) use ($itemIds, $provider, $childRelation, $subField) { + + $query->whereHas($childRelation, function ($q) use ($itemIds, $subField) { + $q->distinct()->whereIn($subField, $itemIds); + }, '=', count($itemIds)); + + if ($provider) { + $query->where('provider', $provider); + } + + return $query; + }); + } + + /** + * Builds purchase provider related filter query. It handles woo, edd filter now. + * + * @param \FluentCrm\Framework\Database\Orm\Builder|\FluentCrm\Framework\Database\Query\Builder $query + * @param array $filters + * @param string $provider + * @return \FluentCrm\Framework\Database\Orm\Builder|\FluentCrm\Framework\Database\Query\Builder + */ + public static function providerQueryBuilder($query, $filters, $provider = 'woo') + { + $filters = array_reduce($filters, function ($carry, $filter) { + if ($filter['property'] == 'purchased_items') { + $carry['contactRelationsItems'][] = $filter; + } else if (in_array($filter['property'], ['purchased_categories', 'purchased_tags', 'purchased_groups'])) { + $filter['property'] = 'commerce_taxonomies'; + $carry['itemTaxonomyRelations'][] = $filter; + } elseif ($filter['property'] == 'commerce_coupons') { + $carry['contactCommerceIn'][] = $filter; + } elseif ($filter['property'] == 'commerce_exist') { + $filter['method'] = 'whereHas'; + if ($filter['operator'] == 'not_exist') { + $filter['method'] = 'whereDoesntHave'; + } + $carry['contactCommerceCheck'][] = $filter; + } else if ($filter['property'] == 'variation_purchased') { + $filter['property'] = 'item_sub_id'; + $filter['method'] = 'whereHas'; + if ($filter['operator'] == 'not_exist') { + $filter['method'] = 'whereDoesntHave'; + } + + if (is_array($filter['value']) && $filter['value']) { + $formattedVariationIds = []; + foreach ($filter['value'] as $value) { + $ids = explode('||', $value); + if ($ids && count($ids) == 2) { + $formattedVariationIds[] = (int)$ids[1]; + } + } + $formattedVariationIds = array_values(array_unique($formattedVariationIds)); + if ($formattedVariationIds) { + $filter['value'] = $formattedVariationIds; + $carry['contactSubFieldItems'][] = $filter; + } + } + } else { + $carry['contactRelations'][] = $filter; + } + + return $carry; + }, []); + + if (array_key_exists('contactRelations', $filters)) { + $query->whereHas('contact_commerce', function ($contactCommerceQuery) use ($filters, $provider) { + foreach ($filters['contactRelations'] as $filter) { + $filter = static::filterParser($filter); + if ($filter['operator'] == 'BETWEEN') { + $contactCommerceQuery->whereBetween($filter['property'], $filter['value']); + } else { + $contactCommerceQuery->where($filter['property'], $filter['operator'], $filter['value']); + } + } + + return $contactCommerceQuery->where('provider', $provider); + }); + } + + if (array_key_exists('contactRelationsItems', $filters)) { + foreach ($filters['contactRelationsItems'] as $filter) { + if ($filter['operator'] == 'not_in_all') { + $query = static::buildChildRelationFilterQuery('commerce_by_provider', 'items', $query, 'whereDoesntHave', 'item_id', $filter['value'], true, $provider); + } else { + list($method, $subMethod) = static::parseRelationalFilterQueryMethods($filter); + $query = static::buildRelationFilterQuery('contact_commerce_items', $query, $method, $subMethod, 'item_id', $filter, $provider); + } + } + } + + if (array_key_exists('contactSubFieldItems', $filters)) { + foreach ($filters['contactSubFieldItems'] as $filter) { + $query = static::buildRelationFilterQuery('contact_commerce_items', $query, $filter['method'], 'whereIn', $filter['property'], $filter, $provider); + } + } + + if (array_key_exists('itemTaxonomyRelations', $filters)) { + $childTaxMaps = [ + 'in' => 'whereHas', + 'not_in' => 'whereDoesntHave', + 'in_all' => 'whereHas', + 'not_in_all' => 'whereDoesntHave' + ]; + + foreach ($filters['itemTaxonomyRelations'] as $filter) { + $operator = $filter['operator']; + if (!isset($childTaxMaps[$operator]) || empty($filter['value'])) { + continue; + } + $method = $childTaxMaps[$operator]; + $checkAll = in_array($operator, ['in_all', 'not_in_all']); + + if ($checkAll) { + $query = static::buildChildRelationFilterQuery('commerce_by_provider', 'taxonomies', $query, $method, 'term_taxonomy_id', $filter['value'], $checkAll, $provider); + } else { + $query = static::buildChildRelationFilterQuery('contact_commerce_items', 'taxonomies', $query, $method, 'term_taxonomy_id', $filter['value'], $checkAll, $provider); + } + + } + } + + if (array_key_exists('contactCommerceIn', $filters)) { + foreach ($filters['contactCommerceIn'] as $filter) { + $filter['value'] = (array)$filter['value']; + + $method = in_array($filter['operator'], ['in', 'in_all']) ? 'whereHas' : 'whereDoesntHave'; + + if (in_array($filter['operator'], ['in', 'not_in'])) { + $query->{$method}('contact_commerce', function ($contactCommerceQuery) use ($filter, $provider) { + $contactCommerceQuery + ->where('provider', $provider) + ->where(function ($query) use ($filter) { + $firstVal = array_shift($filter['value']); + $operator = 'LIKE'; + + $query->where($filter['property'], $operator, '%' . $firstVal . '%'); + foreach ($filter['value'] as $value) { + $query->orWhere($filter['property'], $operator, '%' . $value . '%'); + } + }); + + }); + } else { + foreach ($filter['value'] as $value) { + $query->{$method}('contact_commerce', function ($contactCommerceQuery) use ($filter, $value, $provider) { + $contactCommerceQuery + ->where('provider', $provider) + ->where($filter['property'], 'LIKE', '%' . $value . '%'); + }); + } + } + } + } + + if (array_key_exists('contactCommerceCheck', $filters)) { + foreach ($filters['contactCommerceCheck'] as $filter) { + $method = $filter['method']; + $query->{$method}('contact_commerce', function ($q) use ($provider) { + $q->where('provider', $provider); + }); + } + } + + return $query; + } + + public function buildSearchableQuery($query, $search, $operator = 'LIKE') + { + $fields = $this->searchable; + + $query->where(function ($query) use ($fields, $search, $operator) { + $query->where(array_shift($fields), $operator, $search); + + $nameArray = explode(' ', $search); + + if (count($nameArray) >= 2) { + $query->orWhere(function ($q) use ($nameArray, $operator) { + $firstName = array_shift($nameArray); + $lastName = implode(' ', $nameArray); + + $q->where('first_name', $operator, $firstName); + $q->where('last_name', $operator, $lastName); + }); + } + + foreach ($fields as $field) { + $query->orWhere($field, $operator, $search); + } + }); + + return $query; + } + + /** + * @param \FluentCrm\Framework\Database\Orm\Builder|\FluentCrm\Framework\Database\Query\Builder $query + * @param array $filters + * @return \FluentCrm\Framework\Database\Orm\Builder|\FluentCrm\Framework\Database\Query\Builder + */ + public function buildGeneralPropertiesFilterQuery($query, $filters) + { + foreach ($filters as $filter) { + + $operator = $filter['operator']; + $searchTerm = $filter['value']; + + if ($filter['operator'] == 'contains') { + if (is_array($filter['value'])) { + continue; + } + $operator = 'LIKE'; + $searchTerm = '%' . $filter['value'] . '%'; + } elseif ($filter['operator'] == 'not_contains') { + if (is_array($filter['value'])) { + continue; + } + $operator = 'NOT LIKE'; + $searchTerm = '%' . $filter['value'] . '%'; + } + + $dateFields = ['created_at', 'last_activity', 'date_of_birth']; + + if (in_array($filter['property'], $dateFields)) { + + if (empty($filter['value'])) { + continue; + } + + // created_at/last_activity are TIMESTAMP and date_of_birth is DATE. + // Comparing those columns to an empty string forces MySQL to coerce + // '' into a TIMESTAMP/DATE and throws "Incorrect TIMESTAMP value: ''". + // whereNotNull + != '0000-00-00' is enough — empty strings cannot + // legitimately be stored in these typed columns. + $query = $query->where(function ($q) use ($filter) { + $q->whereNotNull($filter['property']) + ->where($filter['property'], '!=', '0000-00-00'); + }); + + $query = self::applyGeneralFilterQuery($query, $filter, $filter['property']); + } else if ($filter['property'] == 'search') { + $query = $this->buildSearchableQuery($query, $searchTerm, $operator); + } else if ($operator == 'in') { + if (!is_array($searchTerm)) { + $searchTerm = (array)$searchTerm; + } + if ($searchTerm) { + $query = $query->whereIn($filter['property'], $searchTerm); + } + } else if ($operator == 'not_in') { + if (!is_array($searchTerm)) { + $searchTerm = (array)$searchTerm; + } + if ($searchTerm) { + $query = $query->whereNotIn($filter['property'], $searchTerm); + } + } else if ($operator == 'is_null') { + $query = $query->where(function ($q) use ($filter) { + return $q->whereNull($filter['property']) + ->orWhere($filter['property'], '=', ''); + }); + } else if ($operator == 'not_null') { + $query = $query->where(function ($q) use ($filter) { + return $q->whereNotNull($filter['property']) + ->where($filter['property'], '!=', ''); + }); + } else { + $query = $query->where($filter['property'], $operator, $searchTerm); + } + } + return $query; + } + + /** + * @param array $filter + * @return string[] + */ + public static function parseRelationalFilterQueryMethods($filter) + { + // default operator = in + $method = 'whereHas'; + $subMethod = 'whereIn'; + + switch ($filter['operator']) { + case 'not_in': + $method = 'whereDoesntHave'; + $subMethod = 'whereIn'; + + break; + case 'in_all': + $method = 'whereHas'; + $subMethod = 'where'; + + break; + case 'not_in_all': + $method = 'whereDoesntHave'; + $subMethod = 'where'; + + break; + } + + return [$method, $subMethod]; + } + + /** + * @param \FluentCrm\Framework\Database\Orm\Builder|\FluentCrm\Framework\Database\Query\Builder $query + * @param array $filters + * @return \FluentCrm\Framework\Database\Orm\Builder|\FluentCrm\Framework\Database\Query\Builder + */ + public function buildSegmentFilterQuery($query, $filters) + { + foreach ($filters as $filter) { + if (empty($filter['value'])) { + continue; + } + + $prop = $filter['property']; + + if (in_array($prop, ['tags', 'lists', 'companies'])) { + if ($filter['operator'] == 'not_in_all') { + $query->has($prop, '<', count($filter['value']), 'and', function ($query) use ($filter) { + $query->whereIn('object_id', $filter['value']); + }); + } else { + list($method, $subMethod) = static::parseRelationalFilterQueryMethods($filter); + $query = static::buildRelationFilterQuery($filter['property'], $query, $method, $subMethod, 'object_id', $filter); + } + } else if ($prop == 'user_role') { + $userRole = esc_sql($filter['value']); + + $operator = $filter['operator']; + $method = ($operator == 'in' || $operator == 'contains') ? 'whereHas' : 'whereDoesntHave'; + + $query = $query->{$method}('user', function ($userQuery) use ($userRole) { + return $userQuery->whereExists(function ($subQuery) use ($userRole) { + global $wpdb; + return $subQuery->select(fluentCrmDb()->raw(1)) + ->from('usermeta') + ->whereRaw("{$wpdb->prefix}usermeta.user_id = {$wpdb->prefix}users.ID") + ->where('usermeta.meta_key', '=', $wpdb->prefix . 'capabilities') + ->where('usermeta.meta_value', 'LIKE', '%"' . $userRole . '"%'); + }); + }); + } else if ($prop == 'company_industry') { + $operator = $filter['operator']; + $queryOperator = '>='; + if ($operator == 'not_in') { + $queryOperator = '<'; + } + + $query = $query->has('companies', $queryOperator, 1, 'and', function ($q) use ($filter) { + $values = (array)$filter['value']; + $q->whereIn('industry', $values); + }); + } else if ($prop == 'company_type') { + $queryOperator = '>='; + if ($operator == 'not_in') { + $queryOperator = '<'; + } + $query = $query->has('companies', $queryOperator, 1, 'and', function ($q) use ($filter) { + $values = (array)$filter['value']; + $q->whereIn('type', $values); + }); + } else { + $operator = $filter['operator']; + $method = ($operator == 'in' || $operator == 'contains') ? 'whereIn' : 'whereNotIn'; + + $query = $query->{$method}($prop, (array)$filter['value']); + } + } + + return $query; + } + + /** + * @param \FluentCrm\Framework\Database\Orm\Builder|\FluentCrm\Framework\Database\Query\Builder $query + * @param array $filters + * @return \FluentCrm\Framework\Database\Query\Builder + */ + public function buildCustomFieldsFilterQuery($query, $filters) + { + $filters = array_reduce($filters, function ($carry, $filter) { + $operator = $filter['operator']; + + if ($operator == 'not_in') { + $carry['notIn'][] = $filter; + } else if ($operator == '!=' || $operator == 'not_contains') { + $carry['notEqualNorExist'][] = $filter; + } else if ($operator == 'is_null' || $operator == 'not_null') { + if ($operator == 'is_null') { + $filter['method'] = 'whereDoesntHave'; + } else { + $filter['method'] = 'whereHas'; + } + + $carry['exist_or_not'][] = $filter; + } else { + if ($operator == 'in') { + $filter['operator'] = 'contains'; + } else if ($operator == 'not_in') { + $filter['operator'] = 'not_contains'; + } + $carry['regular'][] = $filter; + } + return $carry; + }, []); + + if (array_key_exists('regular', $filters)) { + foreach ($filters['regular'] as $filter) { + $query->whereHas('custom_field_meta', function ($customFieldQuery) use ($filter) { + $customFieldQuery->where('key', $filter['property']); + $operator = self::parseCustomFieldsFilterOperator($filter); + if (is_array($filter['value'])) { + $customFieldQuery->where(function ($valueQuery) use ($operator, $filter) { + $firstVal = array_shift($filter['value']); + + $valueQuery->where('value', $operator, '%' . $firstVal . '%'); + + foreach ($filter['value'] as $value) { + $valueQuery->orWhere('value', $operator, '%' . $value . '%'); + } + }); + } else { + $customFieldQuery = self::applyGeneralFilterQuery($customFieldQuery, $filter, 'value'); + } + return $customFieldQuery; + }); + } + } + + if (array_key_exists('notIn', $filters)) { + foreach ($filters['notIn'] as $filter) { + $filter['value'] = (array)$filter['value']; + + foreach ($filter['value'] as $value) { + $query->whereDoesntHave('custom_field_meta', function ($customFieldQuery) use ($value, $filter) { + $customFieldQuery->where('key', $filter['property']) + ->where('value', 'LIKE', '%' . $value . '%'); + }); + } + } + } + + if (array_key_exists('notEqualNorExist', $filters)) { + foreach ($filters['notEqualNorExist'] as $filter) { + $value = (string)trim($filter['value']); + $operator = $filter['operator']; + + if ($operator == 'not_contains') { + $operator = 'LIKE'; + $value = '%' . $value . '%'; + } else { + $operator = '='; + } + + $query->whereDoesntHave('custom_field_meta', function ($customFieldQuery) use ($value, $filter, $operator) { + $customFieldQuery->where('key', $filter['property']) + ->where('value', $operator, $value); + }); + + } + } + + if (array_key_exists('exist_or_not', $filters)) { + foreach ($filters['exist_or_not'] as $filter) { + $query->{$filter['method']}('custom_field_meta', function ($customFieldQuery) use ($filter) { + $customFieldQuery->where('key', $filter['property']); + }); + } + } + + return $query; + } + + public static function parseCustomFieldsFilterOperator($filter) + { + $operator = $filter['operator']; + + switch ($filter['operator']) { + case 'contains': + case 'in': + $operator = 'LIKE'; + break; + case 'not_contains': + case 'not_in': + $operator = 'NOT LIKE'; + break; + } + + return $operator; + } + + /** + * @param \FluentCrm\Framework\Database\Orm\Builder|\FluentCrm\Framework\Database\Query\Builder $query + * @param array $filters + * @return \FluentCrm\Framework\Database\Orm\Builder|\FluentCrm\Framework\Database\Query\Builder + */ + public function buildActivitiesFilterQuery($query, $filters) + { + foreach ($filters as $filter) { + if (empty($filter['value'])) { + if (in_array($filter['property'], ['email_opened', 'email_link_clicked'])) { + if ($filter['operator'] != 'never') { + continue; + } + } else { + continue; + } + } + + $originalValue = $filter['value']; + + $relation = 'campaignEmails'; + + $filter['where'] = [ + 'prop' => 'status', + 'value' => 'sent', + 'field' => 'scheduled_at' + ]; + + $filterProp = $filter['property']; + + if ($filterProp == 'email_opened' && $filter['operator'] == 'never') { + $query->whereDoesntHave('campaignEmails', function ($q) { + $q->where('is_open', 1); + }); + continue; + } + + if ($filterProp == 'email_link_clicked' && $filter['operator'] == 'never') { + $query->whereDoesntHave('campaignEmails', function ($q) { + $q->whereNotNull('click_counter'); + }); + continue; + } + + if ($filterProp == 'campaign_email_activity') { + $campaignId = (int)$filter['value']; + $operator = $filter['operator']; + + if ($operator == 'not_in') { + $query->whereDoesntHave('campaignEmails', function ($q) use ($campaignId) { + $q->where('campaign_id', $campaignId); + }); + } else { + $query->whereHas('campaignEmails', function ($q) use ($campaignId, $operator) { + $q->where('campaign_id', $campaignId); + if ($operator == 'clicked') { + $q->whereNotNull('click_counter'); + } else if ($operator == 'not_clicked') { + $q->whereNull('click_counter'); + } else if ($operator == 'open') { + $q->where('is_open', 1); + } else if ($operator == 'no_open') { + $q->where('is_open', '0'); + } + }); + } + continue; + } else if ($filterProp == 'automation_activity') { + + $funnelId = (int)$filter['value']; + $operator = $filter['operator']; + + if ($operator == 'not_in') { + $query->whereDoesntHave('funnel_subscribers', function ($q) use ($funnelId) { + $q->where('funnel_id', $funnelId); + }); + } else { + $query->whereHas('funnel_subscribers', function ($q) use ($funnelId, $operator) { + $q->where('funnel_id', $funnelId); + $statusItems = ['completed', 'active', 'cancelled', 'waiting']; + if (in_array($operator, $statusItems)) { + $q->where('status', $operator); + } + }); + } + + continue; + } else if ($filterProp == 'email_sequence_activity') { + + $sequenceId = (int)$filter['value']; + $operator = $filter['operator']; + + if ($operator == 'not_in') { + $query->whereDoesntHave('sequence_trackers', function ($q) use ($sequenceId) { + $q->where('campaign_id', $sequenceId); + }); + } else { + $query->whereHas('sequence_trackers', function ($q) use ($sequenceId, $operator) { + $q->where('campaign_id', $sequenceId); + $statusItems = ['completed', 'active', 'cancelled']; + if (in_array($operator, $statusItems)) { + $q->where('status', $operator); + } + }); + } + + continue; + } else if ($filterProp == 'email_opened') { + $relation = 'campaignEmails'; + $filter['where'] = [ + 'prop' => 'is_open', + 'value' => 1, + 'field' => 'updated_at' + ]; + } else if ($filterProp != 'email_sent') { + $relation = 'urlMetrics'; + $filter['where'] = [ + 'prop' => 'type', + 'value' => 'click', + 'field' => 'updated_at' + ]; + } + + $filter = static::filterParser($filter); + + $query->whereHas($relation, function ($campaignEmailQuery) use ($filter, $relation) { + $campaignEmailQuery->where($filter['where']['prop'], $filter['where']['value']); + if ($filter['operator'] == 'BETWEEN') { + $campaignEmailQuery->whereBetween($filter['where']['field'], $filter['value']); + } else { + $campaignEmailQuery->where($filter['where']['field'], $filter['operator'], $filter['value']); + } + }); + + $operator = $filter['operator']; + if ($operator == '<' || $operator == 'LIKE') { + if ($operator == 'LIKE') { + $compareValue = $originalValue . ' 23:59:59'; + } else { + $compareValue = $filter['value'] . ' 23:59:59'; + } + + $query->whereDoesntHave($relation, function ($campaignEmailQuery) use ($filter, $compareValue) { + $campaignEmailQuery->where($filter['where']['prop'], $filter['where']['value']); + $campaignEmailQuery->where($filter['where']['field'], '>', $compareValue); + }); + } + } + + return $query; + } + + public function lastActivityDate($activityName) + { + $validNames = ['email_sent', 'email_link_clicked', 'email_opened']; + if (!in_array($activityName, $validNames)) { + return false; + } + + if ($activityName == 'email_sent') { + $lastEmail = CampaignEmail::where('subscriber_id', $this->id) + ->where('status', 'sent') + ->orderBy('scheduled_at', 'DESC') + ->first(); + if ($lastEmail) { + return $lastEmail->scheduled_at; + } + return false; + } + + if ($activityName == 'email_opened') { + $lastOpen = CampaignEmail::where('subscriber_id', $this->id) + ->where('is_open', 1) + ->orderBy('updated_at', 'DESC') + ->first(); + return $lastOpen ? $lastOpen->updated_at : false; + } + + $lastActivity = CampaignUrlMetric::where('subscriber_id', $this->id) + ->where('type', 'click') + ->orderBy('updated_at', 'DESC') + ->first(); + + if ($lastActivity) { + return $lastActivity->updated_at; + } + + return false; + } + + public function user() + { + return $this->belongsTo(User::class, 'user_id', 'ID'); + } + + public function getWpUser() + { + if ($this->user_id) { + return get_user_by('ID', $this->user_id); + } + + $user = get_user_by('email', $this->email); + + if ($user) { + $this->user_id = $user->ID; + $this->save(); + + // remove the same user_id for other subscribers + self::where('user_id', $user->ID) + ->where('id', '!=', $this->id) + ->update([ + 'user_id' => NULL + ]); + } + + return $user; + } + + public function getWpUserId() + { + if ($this->user_id) { + return $this->user_id; + } + + $user = $this->getWpUser(); + + if ($user) { + return $user->ID; + } + + return null; + } + + /** + * Get the attributes that have been changed since last sync. + * + * @return array + */ + public function getDirty() + { + $dirty = []; + foreach ($this->attributes as $key => $value) { + if (!in_array($key, $this->fillable)) { + continue; + } + + if (!array_key_exists($key, $this->original)) { + $dirty[$key] = $value; + } elseif ($value !== $this->original[$key] && + !$this->originalIsNumericallyEquivalent($key)) { + $dirty[$key] = $value; + } + } + + return $dirty; + } + + public function getSecureHash() + { + return fluentCrmGetContactSecureHash($this->id); + } + + public function trackEvent($eventData, $isUnique = false) + { + $eventData['subscriber'] = $this; + return FluentCrmApi('event_tracker')->track($eventData, $isUnique); + } + + public function updateStatus($status) + { + + if ($this->status == $status) { + return $this; + } + + $oldStatus = $this->status; + $newStatus = $status; + + $this->status = $status; + $this->save(); + + do_action('fluent_crm/subscriber_status_changed', $this, $oldStatus, $newStatus); + do_action('fluentcrm_subscriber_status_to_' . $newStatus, $this, $oldStatus); + + return $this; + } +} diff --git a/wp-content/plugins/fluent-crm/app/Models/SubscriberMeta.php b/wp-content/plugins/fluent-crm/app/Models/SubscriberMeta.php new file mode 100644 index 0000000..c23db3a --- /dev/null +++ b/wp-content/plugins/fluent-crm/app/Models/SubscriberMeta.php @@ -0,0 +1,50 @@ +belongsTo( + __NAMESPACE__.'\Subscriber', 'subscriber_id', 'id' + ); + } + + public function scopeFilterByKey($query, $key) + { + if ($key) { + $query->where('key', $key); + } + + return $query; + } + + public function setValueAttribute($value) + { + $this->attributes['value'] = maybe_serialize($value); + } + + public function getValueAttribute($value) + { + return maybe_unserialize($value); + } +} diff --git a/wp-content/plugins/fluent-crm/app/Models/SubscriberNote.php b/wp-content/plugins/fluent-crm/app/Models/SubscriberNote.php new file mode 100644 index 0000000..f200061 --- /dev/null +++ b/wp-content/plugins/fluent-crm/app/Models/SubscriberNote.php @@ -0,0 +1,93 @@ +created_at)) { + $model->created_at = fluentCrmTimestamp(); + } + + $model->updated_at = fluentCrmTimestamp(); + $model->created_by = $model->created_by ?: get_current_user_id(); + }); + + static::updated(function ($model) { + $model->updated_at = fluentCrmTimestamp(); + }); + + static::addGlobalScope('status', function ($builder) { + $builder->whereNotIn('status', ['_company_note_', '_system_log_']); + }); + + } + + /** + * One2One: SubscriberNote belongs to one Subscriber + * @return \FluentCrm\Framework\Database\Orm\Relations\BelongsTo + */ + public function subscriber() + { + return $this->belongsTo( + __NAMESPACE__ . '\Subscriber', 'subscriber_id', 'id' + ); + } + + public function markAs($status) + { + $this->status = $status; + $this->save(); + return $this; + } + + public function createdBy() + { + if (!$this->created_by) { + return false; + } + + $user = User::find($this->created_by); + + if (!$user) { + return false; + } + + if (!$user) { + return false; + } + + return [ + 'ID' => $user->ID, + 'display_name' => $user->display_name, + 'photo' => $user->photo + ]; + } +} diff --git a/wp-content/plugins/fluent-crm/app/Models/SubscriberPivot.php b/wp-content/plugins/fluent-crm/app/Models/SubscriberPivot.php new file mode 100644 index 0000000..c733f9d --- /dev/null +++ b/wp-content/plugins/fluent-crm/app/Models/SubscriberPivot.php @@ -0,0 +1,105 @@ + $value) { + $query->where($filed, $value); + } + + return $query; + } + + /** + * Save an entry to the subscriber pivot table. + * + * @param array $attributes + * @return int + */ + public static function store($attributes) + { + $attributes += [ + 'created_at' => $now = current_time('mysql'), + 'updated_at' => $now + ]; + + return static::insert($attributes); + } + + /** + * Attach tags/lists to the subscriber. + * + * @param array $items + * @param int $subscriber + * @param string $type + */ + public static function attach($items, $subscriber, $type) + { + $objectIds = []; + + foreach ($items as $objectId) { + $objectIds = array_merge($objectIds, [$objectId]); + static::firstOrCreate([ + 'subscriber_id' => $subscriber, + 'object_id' => $objectId, + 'object_type' => $type + ]); + } + + if ($objectIds) { + $function = static::getFunctionName($type, __FUNCTION__); + $function($objectIds, Subscriber::find($subscriber)); + } + } + + /** + * Detach tags/lists from the subscriber. + * + * @param array $items + * @param int $subscriber + * @param string $type + */ + public static function detach($items, $subscriber, $type) + { + if ($items) { + static::where('subscriber_id', $subscriber) + ->where('object_type', $type) + ->whereIn('object_id', $items) + ->delete(); + + $function = static::getFunctionName($type, __FUNCTION__); + $function($items, Subscriber::find($subscriber)); + } + } + + private static function getFunctionName($type, $prefix) + { + $parts = explode('\\', $type); + $typeOfObject = end($parts); + $function = $typeOfObject == 'Tag' ? 'tags' : 'lists'; + + if ($prefix == 'attach') { + return "fluentcrm_contact_added_to_$function"; + } else if ($prefix == 'detach') { + return "fluentcrm_contact_removed_from_$function"; + } + } +} diff --git a/wp-content/plugins/fluent-crm/app/Models/SystemLog.php b/wp-content/plugins/fluent-crm/app/Models/SystemLog.php new file mode 100644 index 0000000..9ea89e4 --- /dev/null +++ b/wp-content/plugins/fluent-crm/app/Models/SystemLog.php @@ -0,0 +1,56 @@ +created_at)) { + $model->created_at = fluentCrmTimestamp(); + } + + if (empty($model->subscriber_id)) { + $model->subscriber_id = 0; + } + + $model->status = '_system_log_'; + + $model->updated_at = fluentCrmTimestamp(); + }); + + static::updated(function ($model) { + $model->updated_at = fluentCrmTimestamp(); + }); + + static::addGlobalScope('status', function ($builder) { + $builder->where('status', '_system_log_'); + }); + } +} diff --git a/wp-content/plugins/fluent-crm/app/Models/Tag.php b/wp-content/plugins/fluent-crm/app/Models/Tag.php new file mode 100644 index 0000000..1dbcbe1 --- /dev/null +++ b/wp-content/plugins/fluent-crm/app/Models/Tag.php @@ -0,0 +1,82 @@ +searchable; + $query->where(function ($query) use ($fields, $search) { + $query->where(array_shift($fields), 'LIKE', "%$search%"); + foreach ($fields as $field) { + $query->orWhere($field, 'LIKE', "$search%"); + } + }); + } + + return $query; + } + + /** + * Get all of the subscribers that belongs to the tag. + * + * @return \FluentCrm\Framework\Database\Orm\Relations\BelongsToMany + */ + public function subscribers() + { + return $this->belongsToMany( + __NAMESPACE__.'\Subscriber', 'fc_subscriber_pivot', 'object_id', 'subscriber_id' + )->where('object_type', __CLASS__); + } + + public function totalCount() + { + return fluentCrmDb()->table('fc_subscriber_pivot') + ->where('object_type', 'FluentCrm\App\Models\Tag') + ->where('object_id', $this->id) + ->count(); + } + + public function countByStatus($status = 'subscribed') + { + return fluentCrmDb()->table('fc_subscriber_pivot') + ->where('fc_subscriber_pivot.object_type', 'FluentCrm\App\Models\Tag') + ->where('fc_subscriber_pivot.object_id', $this->id) + ->join('fc_subscribers', 'fc_subscribers.id', '=', 'fc_subscriber_pivot.subscriber_id') + ->where('fc_subscribers.status', $status) + ->count(); + } +} diff --git a/wp-content/plugins/fluent-crm/app/Models/Template.php b/wp-content/plugins/fluent-crm/app/Models/Template.php new file mode 100644 index 0000000..8cf54e1 --- /dev/null +++ b/wp-content/plugins/fluent-crm/app/Models/Template.php @@ -0,0 +1,52 @@ +where( + 'post_type', fluentcrmTemplateCPTSlug() + )->whereIn('post_status', $types); + } + + public function scopeCampaignTemplate($query) + { + return $query->where( + 'post_type', fluentcrmCampaignTemplateCPTSlug() + )->where('post_status', 'publish'); + } + + public function campaign() + { + return $this->hasOne(__NAMESPACE__.'\\'.'Campaign', 'template_id', 'ID'); + } + + public function render($content = null) + { + $content = $content ?: $this->post_content; + + return Parser::parse($content, []); + } +} diff --git a/wp-content/plugins/fluent-crm/app/Models/TermRelation.php b/wp-content/plugins/fluent-crm/app/Models/TermRelation.php new file mode 100644 index 0000000..1a4f391 --- /dev/null +++ b/wp-content/plugins/fluent-crm/app/Models/TermRelation.php @@ -0,0 +1,24 @@ + 'array' + ]; + +} diff --git a/wp-content/plugins/fluent-crm/app/Models/UrlStores.php b/wp-content/plugins/fluent-crm/app/Models/UrlStores.php new file mode 100644 index 0000000..f0833f5 --- /dev/null +++ b/wp-content/plugins/fluent-crm/app/Models/UrlStores.php @@ -0,0 +1,83 @@ +first(); + + if ($isExist) { + $urls[$cacheKey] = $isExist->short; + return $isExist->short; + } + + $maxRetries = 3; + for ($attempt = 0; $attempt < $maxRetries; $attempt++) { + $short = self::generateRandomSlug(); + try { + self::insert([ + 'url' => $longUrl, + 'short' => $short, + 'created_at' => current_time('mysql'), + 'updated_at' => current_time('mysql') + ]); + $urls[$cacheKey] = $short; + return $short; + } catch (\Exception $e) { + if (strpos($e->getMessage(), 'Duplicate entry') !== false) { + continue; + } + throw $e; + } + } + + return ''; + } + + public static function generateRandomSlug($length = 6) + { + $chars = '0123456789abcdefghijklmnopqrstuvwxyz'; + $charsLen = strlen($chars); + + $slug = ''; + $bytes = random_bytes($length); + for ($i = 0; $i < $length; $i++) { + $slug .= $chars[ord($bytes[$i]) % $charsLen]; + } + + return $slug; + } + + public static function getRowByShort($short) + { + global $wpdb; + return $wpdb->get_row($wpdb->prepare("SELECT * FROM " . $wpdb->prefix . "fc_url_stores WHERE BINARY `short` = %s ORDER BY `id` DESC LIMIT 1", $short)); + } +} diff --git a/wp-content/plugins/fluent-crm/app/Models/User.php b/wp-content/plugins/fluent-crm/app/Models/User.php new file mode 100644 index 0000000..15494f7 --- /dev/null +++ b/wp-content/plugins/fluent-crm/app/Models/User.php @@ -0,0 +1,48 @@ +ID); + + if(!empty($this->attributes['user_email'])) { + $contact->orWhere('email', $this->attributes['user_email']); + } + + $contact = $contact->first(); + + if($contact) { + return $contact->photo; + } + + if(empty($this->attributes['user_email'])) { + return ''; + } + + return fluentcrmGravatar($this->attributes['user_email'], $this->attributes['display_name']); + } +} diff --git a/wp-content/plugins/fluent-crm/app/Models/Webhook.php b/wp-content/plugins/fluent-crm/app/Models/Webhook.php new file mode 100644 index 0000000..e585d76 --- /dev/null +++ b/wp-content/plugins/fluent-crm/app/Models/Webhook.php @@ -0,0 +1,105 @@ +where(function($query) { + $query->where('object_type', '=', 'webhook') + ->orWhere('object_type', 'LIKE', 'webhook_%'); + }); + }); + } + + public function getFields() + { + $contactFields = [ + 'fields' => [], + 'custom_fields' => [] + ]; + + foreach (Subscriber::mappables() as $key => $column) { + $contactFields['fields'][] = ['key' => $key, 'field' => $column]; + } + + foreach ((new CustomContactField)->getGlobalFields()['fields'] as $field) { + $contactFields['custom_fields'][] = ['key' => $field['slug'], 'field' => $field['label']]; + } + + return $contactFields; + } + + public function getSchema() + { + $schema = [ + 'name' => '', + 'lists' => [], + 'tags' => [], + 'url' => '', + 'status' => '' + ]; + + if (Helper::isCompanyEnabled()) { + $schema['companies'] = []; + } + + return $schema; + } + + public function store($data) + { + $key = wp_generate_uuid4(); + $webhookUrl = site_url("?fluentcrm=1&route=contact&hash={$key}"); + + return static::create([ + 'object_type' => 'webhook', + 'key' => $key, + 'value' => array_merge($data, [ + 'url' => $webhookUrl + ]), + ]); + } + + public function saveChanges($data) + { + $data['tags'] = Arr::get($data, 'tags', []); + $data['lists'] = Arr::get($data, 'lists', []); + $data['companies'] = Arr::get($data, 'companies', []); + + $this->value = array_merge( + $this->value, + array_diff_key($data, [ + 'id' => '', 'url' => '' + ]) + ); + + $this->save(); + + return $this; + } +} diff --git a/wp-content/plugins/fluent-crm/app/Modules/AbandonCart/AbCartHelper.php b/wp-content/plugins/fluent-crm/app/Modules/AbandonCart/AbCartHelper.php new file mode 100644 index 0000000..a8dad4b --- /dev/null +++ b/wp-content/plugins/fluent-crm/app/Modules/AbandonCart/AbCartHelper.php @@ -0,0 +1,247 @@ + 'no', + 'enabled_providers' => [], + 'capture_after_minutes' => 30, + 'lost_cart_days' => 10, + 'cool_off_period_days' => 10, + 'gdpr_consent' => 'no', + 'gdpr_consent_text' => 'Your email and cart are saved so we can send you email reminders about this order. {{opt_out label="No Thanks"}}', + 'disabled_user_roles' => [], + 'track_add_to_cart' => 'no', + 'add_to_cart_exclude_user_roles' => [], + 'tags_on_cart_abandoned' => [], + 'lists_on_cart_abandoned' => [], + 'tags_on_cart_lost' => [], + 'lists_on_cart_lost' => [], + 'new_contact_status' => 'transactional', + ]; + + // Merge provider-specific defaults from available drivers + foreach (DriverManager::getAvailable() as $driver) { + $providerDefaults = $driver->getProviderSettingsDefaults(); + if ($providerDefaults) { + $defaults = array_merge($defaults, $providerDefaults); + } + } + + $settings = get_option('_fc_ab_cart_settings', []); + + if (is_array($settings) && $settings) { + if ($settings['enabled'] === 'yes' && !isset($settings['enabled_providers'])) { + // backwards compatibility: if enabled but no providers selected, enable woo only as we had that. + if (defined('WC_PLUGIN_FILE')) { + $settings['enabled_providers'] = ['woo']; + } + } + + $settings = wp_parse_args($settings, $defaults); + } else { + $settings = $defaults; + } + + // Let each driver process settings (e.g. merge WC paid statuses) + foreach (DriverManager::getAvailable() as $driver) { + $settings = $driver->processSettings($settings); + } + + return $settings; + } + + public static function getSetting($key, $default = '') + { + $setting = self::getSettings(); + return Arr::get($setting, $key, $default); + } + + public static function isActive() + { + return Helper::isExperimentalEnabled('abandoned_cart'); + } + + public static function willCartTrack() + { + if (!self::isActive()) { + return false; + } + + $settings = self::getSettings(); + if ($settings['enabled'] !== 'yes') { + return false; + } + + $disableUserRoles = Arr::get($settings, 'disabled_user_roles', []); + + if (!$disableUserRoles) { + return true; + } + + $user = wp_get_current_user(); + + if (!$user) { + return true; + } + + $userRoles = array_values($user->roles); + + return !array_intersect($userRoles, $disableUserRoles); + } + + public static function getGDPRMessage() + { + $settings = self::getSettings(); + + if (Arr::get($settings, 'gdpr_consent') !== 'yes' || empty($settings['gdpr_consent_text'])) { + return ''; + } + + $text = wp_kses_post($settings['gdpr_consent_text']); + + // {{opt_out label="No Thanks"}} + return preg_replace('/{{opt_out label="([^"]+)"}}/', '$1', $text); + } + + public static function getCountAndSumByStatus($status, $dateRange = [], $dateColumn = 'created_at') + { + $query = AbandonCartModel::where('status', $status); + + if ($dateRange) { + $query = $query->whereBetween($dateColumn, $dateRange); + } + + $count = $query->count(); + $sum = 0; + + if ($count) { + $sum = $query->sum('total'); + } + + + return [$count, $sum]; + } + + public static function getSortedAutomations($provider = 'woo') + { + $triggerName = 'fc_ab_cart_simulation_' . $provider; + + $funnels = Funnel::where('trigger_name', $triggerName) + ->where('status', 'published') + ->orderBy('id', 'DESC') + ->get(); + + $formattedFunnels = []; + + foreach ($funnels as $funnel) { + $priority = Arr::get($funnel->settings, 'priority', 1); + if (isset($formattedFunnels[$priority])) { + $priority++; + } + + $formattedFunnels[$priority] = $funnel; + } + + // reverse the array to get the latest funnels first + krsort($formattedFunnels); + + return array_values($formattedFunnels); + } + + public static function getAbCartByDataProps($props = [], $statuses = ['processing', 'draft']) + { + + if (empty($props)) { + return null; + } + + if ($token = Arr::get($props, 'checkout_key')) { + $record = AbandonCartModel::where('checkout_key', $token) + ->when($statuses, function ($query) use ($statuses) { + return $query->whereIn('status', $statuses); + }) + ->first(); + + if ($record) { + return $record; + } + } + + + if ($billingEmail = Arr::get($props, 'email')) { + $record = AbandonCartModel::where('email', $billingEmail) + ->when($statuses, function ($query, $statuses) { + return $query->whereIn('status', $statuses); + }) + ->orderBy('id', 'DESC') + ->first(); + + if ($record) { + return $record; + } + } + + if ($userId = Arr::get($props, 'user_id')) { + $record = AbandonCartModel::where('user_id', $userId) + ->when($statuses, function ($query, $statuses) { + return $query->whereIn('status', $statuses); + }) + ->orderBy('id', 'DESC') + ->first(); + + if ($record) { + return $record; + } + } + + + return null; + } + + /** + * Check if the given order status is considered a "win" (i.e., completed) status for the specified driver. + * @param string $driver The driver slug (e.g., 'woo') + * @param string $orderStatus The order status to check (e.g., 'completed') + * @return bool True if it's a win status, false otherwise + */ + public static function isWinOrderStatus($driver, $orderStatus) + { + $driver = DriverManager::getDriver($driver); + if ($driver) { + return $driver->isWinOrderStatus($orderStatus); + } + + return false; + } + + /** + * @deprecated Use DriverManager::getDriver('woo')->isWithinCoolOffPeriod() instead + */ + public static function isWooWithinCoolOffPeriod($abCartModel) + { + $driver = DriverManager::getDriver('woo'); + if ($driver) { + return $driver->isWithinCoolOffPeriod($abCartModel); + } + + return false; + } + + +} diff --git a/wp-content/plugins/fluent-crm/app/Modules/AbandonCart/AbandonCart.php b/wp-content/plugins/fluent-crm/app/Modules/AbandonCart/AbandonCart.php new file mode 100644 index 0000000..852e5f1 --- /dev/null +++ b/wp-content/plugins/fluent-crm/app/Modules/AbandonCart/AbandonCart.php @@ -0,0 +1,195 @@ +init(); + }, 90); + } + + protected function init() + { + $drivers = DriverManager::getEnabled(); + + if (!$drivers) { + return false; + } + + // Boot only enabled drivers (available + toggled on in settings) + foreach ($drivers as $driver) { + $driver->register(); + $driver->registerAutomationTrigger(); + } + + // Expose abandon cart availability to the frontend via fcAdmin vars + add_filter('fluent_crm/admin_vars', function ($vars) { + $vars['has_abandon_carts'] = true; + $vars['can_read_abandon_carts'] = PermissionManager::currentUserCan('fcrm_read_funnels'); + return $vars; + }); + + // Add Abandoned Carts as a sub-item under the Reports dropdown + add_filter('fluent_crm/menu_items', function ($items) { + if (!PermissionManager::currentUserCan('fcrm_read_funnels')) { + return $items; + } + + $urlBase = fluentcrm_menu_url_base(); + $hasReportsMenu = false; + foreach ($items as &$item) { + if (!empty($item['key']) && $item['key'] === 'reports' && isset($item['sub_items'])) { + $item['sub_items'][] = [ + 'key' => 'reports_abandoned_carts', + 'label' => __('Abandoned Carts', 'fluent-crm'), + 'permalink' => $urlBase . 'reports?tab=abandoned_carts', + 'icon' => '', + ]; + $hasReportsMenu = true; + break; + } + } + unset($item); + + if (!$hasReportsMenu) { + $items[] = [ + 'key' => 'reports', + 'label' => __('Reports', 'fluent-crm'), + 'permalink' => $urlBase . 'reports?tab=abandoned_carts', + 'layout_class' => 'fc_1_col_menu', + 'sub_items' => [ + [ + 'key' => 'reports_abandoned_carts', + 'label' => __('Abandoned Carts', 'fluent-crm'), + 'permalink' => $urlBase . 'reports?tab=abandoned_carts', + 'icon' => '', + ] + ], + ]; + } + return $items; + }); + + // Run the runner + add_action('fluentcrm_scheduled_five_minute_tasks', [$this, 'maybeRunAbRunner'], 999); + + add_action('fluentcrm_scheduled_daily_tasks', [$this, 'markOldCartsAsLost'], 10); + + add_filter('fluent_crm/sales_stats', function ($stats) { + + [$recoveredCount, $recoveredRevenue] = AbCartHelper::getCountAndSumByStatus('recovered', [], 'recovered_at'); + if (!$recoveredRevenue) { + return $stats; + } + + $dateRange = [ + gmdate('Y-m-01 00:00:00', current_time('timestamp')), + gmdate('Y-m-t 23:59:59', current_time('timestamp')) + ]; + + [$thisMonth, $thisMonthRevenue] = AbCartHelper::getCountAndSumByStatus('recovered', $dateRange, 'recovered_at'); + + $stats[] = [ + 'title' => __('Cart Recovered (This Month)', 'fluent-crm'), + 'content' => DriverManager::formatPrice($thisMonthRevenue) + ]; + + $stats[] = [ + 'title' => __('Cart Recovered (All Time)', 'fluent-crm'), + 'content' => DriverManager::formatPrice($recoveredRevenue) + ]; + + return $stats; + }); + } + + public function maybeRunAbRunner() + { + static $counter = 0; + + if (!$counter) { + if (fluentCrmIsTimeOut(30)) { + return false; + } + // It's the first time. Check if there has any runner or not + $lastRunner = fluentCrmGetOptionCache('__fc_ab_runner'); + if ($lastRunner) { + $timeElapsed = time() - $lastRunner; + if ($timeElapsed < 50) { + return false; + } + + fluentCrmSetOptionCache('__fc_ab_runner', null, 50); + } + } + + fluentCrmSetOptionCache('__fc_ab_runner', time(), 50); + $counter = $counter + 1; + + // Get Draft Carts that need to be abandoned + $settings = AbCartHelper::getSettings(); + $cutMinutes = Arr::get($settings, 'capture_after_minutes', 5); + $cutDateTime = gmdate('Y-m-d H:i:s', current_time('timestamp') - ($cutMinutes * 60)); + + $enabledSlugs = DriverManager::getEnabledSlugs(); + + if (!$enabledSlugs) { + fluentCrmSetOptionCache('__fc_ab_runner', null, 50); + return false; + } + + $abCarts = AbandonCartModel::where('status', 'draft') + ->whereIn('provider', $enabledSlugs) + ->where('updated_at', '<=', $cutDateTime) + ->orderBy('id', 'DESC') + ->limit(10) + ->get(); + + if ($abCarts->isEmpty()) { + fluentCrmSetOptionCache('__fc_ab_runner', null, 50); + return false; + } + + foreach ($abCarts as $abCart) { + (new AbandonCartRunner())->runAbandonCart($abCart); + } + + fluentCrmSetOptionCache('__fc_ab_runner', null, 50); + + if (!fluentCrmIsTimeOut(40)) { + $this->maybeRunAbRunner(); + } + + return true; + } + + public function markOldCartsAsLost() + { + $settings = AbCartHelper::getSettings(); + $cutDays = Arr::get($settings, 'lost_cart_days', 15); + $cutDateTime = gmdate('Y-m-d H:i:s', current_time('timestamp') - ($cutDays * 86400)); + + AbandonCartModel::where('status', 'processing') + ->where('created_at', '<=', $cutDateTime) + ->update(['status' => 'lost']); + + } +} diff --git a/wp-content/plugins/fluent-crm/app/Modules/AbandonCart/AbandonCartController.php b/wp-content/plugins/fluent-crm/app/Modules/AbandonCart/AbandonCartController.php new file mode 100644 index 0000000..3dfd21b --- /dev/null +++ b/wp-content/plugins/fluent-crm/app/Modules/AbandonCart/AbandonCartController.php @@ -0,0 +1,236 @@ +get('query', []); + $dateRangeInput = $request->get('date_range', []); + $dateRange = $this->getDateRange($dateRangeInput); + + $enabledDrivers = DriverManager::getEnabled(); + $missingAutomations = []; + $formattedDrivers = []; + + $triggerNames = []; + foreach ($enabledDrivers as $driver) { + $triggerNames[$driver->getTriggerName()] = $driver; + } + + $activeTriggers = Funnel::query() + ->whereIn('trigger_name', array_keys($triggerNames)) + ->where('status', 'published') + ->pluck('trigger_name') + ->unique() + ->toArray(); + + foreach ($enabledDrivers as $driver) { + if (!in_array($driver->getTriggerName(), $activeTriggers, true)) { + // Include the provider slug so the UI can scope the "no automation" notice + // (doc link + starter template differ per provider). + $missingAutomations[] = [ + 'provider' => $driver->getProviderSlug(), + 'label' => $driver->getProviderLabel(), + ]; + } + + $formattedDrivers[$driver->getProviderSlug()] = [ + 'label' => $driver->getProviderLabel(), + 'logo' => $driver->getLogo() + ]; + } + + $carts = AbandonCartModel::orderBy('id', 'DESC') + ->with(['subscriber', 'automation']); + + if ($dateRange) { + $carts = $carts->whereBetween('created_at', $dateRange); + } + + $status = sanitize_text_field(Arr::get($query, 'status', '')); + $search = sanitize_text_field(Arr::get($query, 'search', '')); + + $carts = $carts->statusBy($status) + ->searchBy($search) + ->paginate(); + + return [ + 'carts' => $this->mutateCartData($carts), + 'haveAutomation' => empty($missingAutomations), + 'missingAutomations' => $missingAutomations, + 'drivers' => $formattedDrivers + ]; + } + + public function mutateCartData($carts) + { + $updatedData = $carts->getCollection()->transform(function ($cart) { + if ($cart->status == 'processing') { + $cart->recovery_url = $cart->getRecoveryUrl(); + } + + $subscriber = $cart->subscriber; + // Customer Avatar + $cart->customer_avatar = $subscriber ? $subscriber->photo : fluentcrmGravatar($cart->email, $cart->full_name); + + // Driver-specific enrichment (product images, order URL, etc.) + $driver = DriverManager::getDriver($cart->provider); + if ($driver) { + $cart = $driver->enrichCartForListing($cart); + } + + // Remove subscriber to clean up output + unset($cart->subscriber); + + return $cart; + }); + + $carts->setCollection( + $updatedData + ); + + return $carts; + } + + public function handleBulkDeleteCart(Request $request) + { + $cartIds = $request->get('cart_ids', []); + + if (!$cartIds || !is_array($cartIds)) { + return $this->sendError([ + 'message' => __('No carts selected to delete', 'fluent-crm') + ]); + } + + $cartIds = array_map('intval', $cartIds); + + $carts = AbandonCartModel::whereIn('id', $cartIds)->get(); + + foreach ($carts as $cart) { + $cart->deleteCart(); + } + + return [ + 'message' => __('Selected carts have been deleted successfully', 'fluent-crm') + ]; + } + + public function getReportSummary(Request $request) + { + $dateRangeInput = $request->get('date_range', []); + $dateRange = $this->getDateRange($dateRangeInput); + + [$recoveredCount, $recoveredRevenue] = AbCartHelper::getCountAndSumByStatus('recovered', $dateRange, 'recovered_at'); + [$processingCount, $processingRevenue] = AbCartHelper::getCountAndSumByStatus('processing', $dateRange); + [$lostCount, $lostRevenue] = AbCartHelper::getCountAndSumByStatus('lost', $dateRange); + [$draftCount, $draftRevenue] = AbCartHelper::getCountAndSumByStatus('draft', $dateRange); + [$optoutCount, $optoutRevenue] = AbCartHelper::getCountAndSumByStatus('opt_out', $dateRange); + + $recoveryRate = '0%'; + + if ($lostCount) { + $recoveryRate = number_format(($recoveredCount / ($lostCount + $recoveredCount)) * 100, 2) . '%'; + } else if ($recoveredCount) { + $recoveryRate = '100%'; + } + + return [ + 'widgets' => [ + 'recovered_revenue' => [ + 'title' => esc_html__('Recovered Revenue', 'fluent-crm'), + 'value' => DriverManager::formatPrice($recoveredRevenue), + 'count' => number_format($recoveredCount), + ], + 'processing_revenue' => [ + 'title' => esc_html__('Processing Revenue', 'fluent-crm'), + 'value' => DriverManager::formatPrice($processingRevenue), + 'count' => number_format($processingCount), + ], + 'lost_revenue' => [ + 'title' => esc_html__('Lost Revenue', 'fluent-crm'), + 'value' => DriverManager::formatPrice($lostRevenue), + 'count' => number_format($lostCount), + ], + 'draft_revenue' => [ + 'title' => esc_html__('Draft Revenue', 'fluent-crm'), + 'value' => DriverManager::formatPrice($draftRevenue), + 'count' => number_format($draftCount) + ], + 'optout_revenue' => [ + 'title' => esc_html__('Optout Revenue', 'fluent-crm'), + 'value' => DriverManager::formatPrice($optoutRevenue), + 'count' => number_format($optoutCount) + ], + 'recovery_rate' => [ + 'title' => esc_html__('Recovery Rate', 'fluent-crm'), + 'value' => $recoveryRate, + 'count' => '' + ] + ] + ]; + + } + + public function getDateRange($dateRangeInput) + { + if ($dateRangeInput) { + $dateRange = array_filter($dateRangeInput); + + $startTime = isset($dateRange[0]) ? strtotime($dateRange[0]) : false; + $endTime = isset($dateRange[1]) ? strtotime($dateRange[1]) : false; + + if (count($dateRange) != 2 || !$startTime || !$endTime || $startTime > $endTime) { + // Invalid date range, fallback to last 30 days + $startDate = gmdate('Y-m-d 00:00:01', strtotime('-30 days')); + $endDate = gmdate('Y-m-d 23:59:59'); + $dateRange = [$startDate, $endDate]; + } else { + $startDateString = $dateRange[0]; + $endDateString = $dateRange[1]; + + // Remove timezone identifiers + $startDateString = preg_replace('/\(.*\)/', '', $startDateString); + $endDateString = preg_replace('/\(.*\)/', '', $endDateString); + + try { + // Parse dates + $startDate = new \DateTime($startDateString); + $endDate = new \DateTime($endDateString); + + // Adjust times for range + $startDate->setTime(0, 0, 1); // Set time to 00:00:01 + $endDate->setTime(23, 59, 59); // Set time to 23:59:59 + + // Format for SQL or other usage + $dateRange = [ + $startDate->format("Y-m-d H:i:s"), + $endDate->format("Y-m-d H:i:s") + ]; + } catch (\Exception $e) { + // Fallback to last 30 days + $dateRange = [ + gmdate('Y-m-d 00:00:01', strtotime('-30 days')), + gmdate('Y-m-d 23:59:59') + ]; + } + } + } else { + // Default to last 30 days if no date range provided + $startDate = gmdate('Y-m-d 00:00:01', strtotime('-30 days')); + $endDate = gmdate('Y-m-d 23:59:59'); + $dateRange = [$startDate, $endDate]; + } + + return $dateRange; + } + +} diff --git a/wp-content/plugins/fluent-crm/app/Modules/AbandonCart/AbandonCartMigrator.php b/wp-content/plugins/fluent-crm/app/Modules/AbandonCart/AbandonCartMigrator.php new file mode 100644 index 0000000..6f696ea --- /dev/null +++ b/wp-content/plugins/fluent-crm/app/Modules/AbandonCart/AbandonCartMigrator.php @@ -0,0 +1,56 @@ +get_charset_collate(); + $table = $wpdb->prefix .'fc_abandoned_carts'; + + if ($wpdb->get_var("SHOW TABLES LIKE '$table'") != $table || $isForced) { + $sql = "CREATE TABLE $table ( + `id` BIGINT UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT, + `checkout_key` VARCHAR(192), + `cart_hash` VARCHAR(192), + `is_optout` TINYINT(1) DEFAULT 0, + `full_name` VARCHAR(192), + `email` VARCHAR(192), + `provider` VARCHAR(100) DEFAULT 'woo', + `user_id` BIGINT UNSIGNED NULL, + `click_counts` BIGINT UNSIGNED DEFAULT 0, + `contact_id` BIGINT UNSIGNED NULL, + `order_id` BIGINT UNSIGNED NULL, + `automation_id` BIGINT UNSIGNED NULL, + `checkout_page_id` BIGINT UNSIGNED NULL, + `status` VARCHAR(30) DEFAULT 'draft', + `subtotal` DECIMAL(10,2), + `shipping` DECIMAL(10,2), + `tax` DECIMAL(10,2), + `discounts` DECIMAL(10,2), + `fees` DECIMAL(10,2), + `total` DECIMAL(10,2), + `currency` VARCHAR(50), + `cart` LONGTEXT, + `note` TEXT, + `abandoned_at` TIMESTAMP NULL, + `recovered_at` TIMESTAMP NULL, + `created_at` TIMESTAMP NULL, + `updated_at` TIMESTAMP NULL, + KEY `status` (`status`), + KEY `checkout_key` (`checkout_key`) + ) $charsetCollate;"; + require_once(ABSPATH . 'wp-admin/includes/upgrade.php'); + dbDelta($sql); + } + } +} diff --git a/wp-content/plugins/fluent-crm/app/Modules/AbandonCart/AbandonCartModel.php b/wp-content/plugins/fluent-crm/app/Modules/AbandonCart/AbandonCartModel.php new file mode 100644 index 0000000..e2343eb --- /dev/null +++ b/wp-content/plugins/fluent-crm/app/Modules/AbandonCart/AbandonCartModel.php @@ -0,0 +1,208 @@ +checkout_key = md5(time() . wp_generate_uuid4()); + }); + } + + public function scopeProvider($query, $provider) + { + return $query->where('provider', $provider); + } + + public function scopeStatusBy($query, $status) + { + if (!$status || $status == 'all') { + return $query; + } + + return $query->where('status', $status); + } + + public function scopeSearchBy($query, $search) + { + if (!$search) { + return $query; + } + + return $query->where(function ($q) use ($search) { + $q->where('full_name', 'LIKE', '%' . $search . '%') + ->orWhere('email', 'LIKE', '%' . $search . '%'); + }); + } + + public function setCartAttribute($data) + { + $this->attributes['cart'] = \maybe_serialize($data); + } + + public function getCartAttribute($data) + { + return \maybe_unserialize($data); + } + + public function subscriber() + { + return $this->belongsTo(Subscriber::class, 'contact_id'); + } + + public function automation() + { + return $this->belongsTo(Funnel::class, 'automation_id'); + } + + public function getAddress($type = 'billing') + { + $customerData = Arr::get($this->cart, 'customer_data', []); + + if (Arr::get($customerData, 'differentShipping') != 'yes') { + $type = 'billingAddress'; + } else { + $type = 'shippingAddress'; + } + + return array_filter([ + 'address_1' => Arr::get($customerData, $type . '.address_1'), + 'address_2' => Arr::get($customerData, $type . '.address_2'), + 'city' => Arr::get($customerData, $type . '.city'), + 'state' => Arr::get($customerData, $type . '.state'), + 'postcode' => Arr::get($customerData, $type . '.postcode'), + 'country' => Arr::get($customerData, $type . '.country'), + ]); + } + + private function getAddressLineByKey($type, $key) + { + $address = $this->getAddress($type); + return Arr::get($address, $key, ''); + } + + public function getInputProp($key, $default = '') + { + $customerData = Arr::get($this->cart, 'customer_data', []); + + return Arr::get($customerData, $key, $default); + } + + public function getAddressProp($key, $addressType = 'billingAddress', $default = '') + { + $address = Arr::get($this->cart, 'customer_data.'.$addressType, []); + + return Arr::get($address, $key, $default); + } + + /* + * Get the cart items as html + * This function is called by shortcodes/mergecodes/smartcode + * e.g. {{ab_cart_woo.cart_items_table}} + */ + public function getCartItemsHtml() + { + $driver = DriverManager::getDriver($this->provider); + + if ($driver) { + return $driver->getCartItemsHtml($this); + } + + return ''; + } + + public function getRecoveryUrl() + { + $driver = DriverManager::getDriver($this->provider); + + if ($driver) { + return $driver->getRecoveryUrl($this); + } + + if ($this->status != 'processing') { + return ''; + } + + return add_query_arg([ + 'fluentcrm' => 1, + 'route' => 'general', + 'handler' => 'fc_cart_' . $this->provider, + 'fc_ab_hash' => $this->checkout_key + ], home_url()); + } + + public function deleteCart() + { + if ($this->automation_id && $this->contact_id) { + FunnelHelper::removeSubscribersFromFunnel($this->automation_id, [$this->contact_id]); + } + + $this->delete(); + } + + public function optOut() + { + if ($this->is_optout) { + return $this; + } + + $originalStatus = $this->status; + $this->is_optout = 1; + $this->status = 'opt_out'; + $this->save(); + + if (!$this->contact_id || !$this->automation_id) { + return $this; + } + + if ($originalStatus == 'processing') { + FunnelHelper::removeSubscribersFromFunnel($this->automation_id, [$this->contact_id]); + $this->automation_id = null; + $this->save(); + } + + return $this; + } +} diff --git a/wp-content/plugins/fluent-crm/app/Modules/AbandonCart/AbandonCartRunner.php b/wp-content/plugins/fluent-crm/app/Modules/AbandonCart/AbandonCartRunner.php new file mode 100644 index 0000000..e18476a --- /dev/null +++ b/wp-content/plugins/fluent-crm/app/Modules/AbandonCart/AbandonCartRunner.php @@ -0,0 +1,322 @@ +status != 'draft') { + return false; + } + + $driver = DriverManager::getDriver($abandonCart->provider); + + if (!$driver) { + $abandonCart->status = 'skipped'; + $abandonCart->note = 'No driver found for provider: ' . $abandonCart->provider; + $abandonCart->save(); + return $abandonCart; + } + + if ($driver->isWithinCoolOffPeriod($abandonCart)) { + $abandonCart->status = 'skipped'; + $abandonCart->note = 'Under Cool Off Period'; + $abandonCart->save(); + return $abandonCart; + } + + $automationData = $this->getEligibleAutomation($abandonCart); + + if (!$automationData) { + $abandonCart->status = 'skipped'; + $abandonCart->note = 'No automation found for this cart'; + $abandonCart->save(); + return $abandonCart; + } + + if (is_wp_error($automationData)) { + $abandonCart->status = 'skipped'; + $abandonCart->note = $automationData->get_error_message(); + $abandonCart->save(); + return $abandonCart; + } + + $automation = $automationData['automation']; + $contact = $automationData['contact']; + + if (!$contact) { + $contact = $this->createContactFromCart($abandonCart); + } + + // Check if exit + $existingFunnelSub = FunnelSubscriber::where('funnel_id', $automation->id) + ->where('subscriber_id', $contact->id) + ->first(); + + if ($existingFunnelSub) { + FunnelMetric::where('funnel_id', $existingFunnelSub->funnel_id) + ->where('subscriber_id', $contact->id) + ->delete(); + + $existingFunnelSub->delete(); + } + + $settings = AbCartHelper::getSettings(); + if ($attachLists = Arr::get($settings, 'lists_on_cart_abandoned', [])) { + $contact->attachLists($attachLists); + } + + if ($attachTags = Arr::get($settings, 'tags_on_cart_abandoned', [])) { + $contact->attachTags($attachTags); + } + + $abandonCart->status = 'processing'; + $abandonCart->automation_id = $automation->id; + $abandonCart->contact_id = $contact->id; + $abandonCart->abandoned_at = current_time('mysql'); + $abandonCart->save(); + + (new FunnelProcessor())->startFunnelSequence($automation, [], [ + 'source_trigger_name' => $driver->getTriggerName(), + 'source_ref_id' => $abandonCart->id + ], $contact); + + return $abandonCart; + } + + public function getEligibleAutomation(AbandonCartModel $abandonCart) + { + $automations = AbCartHelper::getSortedAutomations($abandonCart->provider); + + if (!$automations) { + return new \WP_Error('no_automation', 'No automation found for this cart'); + } + + $existingContact = fluentCrmApi('contacts')->getContact($abandonCart->email); + + $processableStatuses = ['subscribed', 'transactional']; + + if ($existingContact && !in_array($existingContact->status, $processableStatuses)) { + return new \WP_Error('contact_unsubscribed', 'Contact status is not allowed to process this cart'); + } + + $items = Arr::get($abandonCart->cart, 'cart_contents', []); + + // Use driver for provider-specific condition data extraction + $driver = DriverManager::getDriver($abandonCart->provider); + + if ($driver) { + $conditionData = $driver->extractCartConditionData($abandonCart); + $productIds = $conditionData['product_ids']; + $categoryIds = $conditionData['category_ids']; + } else { + $productIds = []; + $categoryIds = []; + foreach ($items as $item) { + $productIds[] = $item['product_id']; + } + } + + $cartData = [ + 'cart_total' => $abandonCart->total, + 'cart_items_count' => count($items), + 'cart_items' => $productIds, + 'cart_items_categories' => $categoryIds, + ]; + + $contact = $existingContact; + + foreach ($automations as $automation) { + $conditions = (array)$automation->conditions; + if (Arr::get($conditions, 'require_subscribed') === 'yes' && (!$existingContact || $existingContact->status != 'subscribed')) { + continue; + } + + $existingFunnelSub = null; + $checkActive = Arr::get($conditions, 'active_once') === 'yes'; + if ($checkActive && $existingContact) { + // check if the contact is already has an automation for this one + $existingFunnelSub = FunnelSubscriber::where('funnel_id', $automation->id) + ->where('subscriber_id', $existingContact->id) + ->first(); + + if ($existingFunnelSub && $existingFunnelSub->status == 'active') { + continue; + } + } + + $cartConditions = array_filter(Arr::get($conditions, 'cart_conditions', [])); + + if (!$cartConditions) { + return [ + 'automation' => $automation, + 'contact' => $contact + ]; + } + + if (!$contact && $this->hasContactConditions($cartConditions)) { + $contact = $this->createContactFromCart($abandonCart); + } + + if ($this->assessConditionGroups($cartConditions, $contact, $cartData)) { + return [ + 'automation' => $automation, + 'contact' => $contact + ]; + } + } + + return new \WP_Error('no_automation', 'No automation found for this cart based on condition match'); + } + + protected function createContactFromCart(AbandonCartModel $abandonCart) + { + $customData = Arr::get($abandonCart->cart, 'customer_data', []); + $cartSettings = AbCartHelper::getSettings(); + $contactData = array_filter([ + 'email' => $abandonCart->email, + 'first_name' => Arr::get($customData, 'billingAddress.first_name'), + 'last_name' => Arr::get($customData, 'billingAddress.last_name'), + 'user_id' => $abandonCart->user_id, + 'full_name' => $abandonCart->full_name, + 'status' => Arr::get($cartSettings, 'new_contact_status', 'transactional'), + 'address_line_1' => Arr::get($customData, 'billingAddress.address_1'), + 'address_line_2' => Arr::get($customData, 'billingAddress.address_2'), + 'city' => Arr::get($customData, 'billingAddress.city'), + 'state' => Arr::get($customData, 'billingAddress.state'), + 'postal_code' => Arr::get($customData, 'billingAddress.postcode'), + 'country' => Arr::get($customData, 'billingAddress.country'), + 'phone' => Arr::get($customData, 'billingAddress.phone'), + 'tags' => Arr::get($cartSettings, 'tags_on_cart_abandoned', []), + 'lists' => Arr::get($cartSettings, 'lists_on_cart_abandoned', []) + ]); + + + return fluentCrmApi('contacts')->createOrUpdate($contactData); + } + + protected function hasContactConditions($conditions) + { + $cartGroupKeys = DriverManager::getAllSmartCodeGroupKeys(); + + foreach ($conditions as $conditionGroup) { + foreach ($conditionGroup as $filterItem) { + if (count($filterItem['source']) != 2 || empty($filterItem['source'][0]) || empty($filterItem['source'][1]) || empty($filterItem['operator'])) { + continue; + } + + $provider = $filterItem['source'][0]; + + if (!in_array($provider, $cartGroupKeys)) { + return true; + } + } + } + + return false; + } + + protected function assessConditionGroups($conditionGroups, $subscriber, $cartData = []) + { + foreach ($conditionGroups as $conditions) { + $result = $this->assessConditions($conditions, $subscriber, $cartData); + if ($result) { + return true; + } + } + + return false; + } + + protected function assessConditions($conditions, $subscriber, $cartData = []) + { + if (!defined('FLUENTCAMPAIGN_DIR_FILE')) { + return true; + } + + $helperClass = 'FluentCampaign\App\Services\Funnel\Conditions\FunnelConditionHelper'; + + if (!class_exists($helperClass)) { + // Free-only: no advanced condition engine, accept all conditions + return true; + } + + $formattedGroups = $helperClass::formatConditionGroups($conditions); + + foreach ($formattedGroups as $groupName => $group) { + if ($groupName == 'subscriber') { + + if (!$subscriber) { + return false; + } + + $subscriberData = $subscriber->toArray(); + if (!ConditionAssessor::matchAllConditions($group, $subscriberData)) { + return false; + } + } else if ($groupName == 'custom_fields') { + if (!$subscriber) { + return false; + } + $customData = $subscriber->custom_fields(); + if (!ConditionAssessor::matchAllConditions($group, $customData)) { + return false; + } + } else if ($groupName == 'segment') { + if (!$subscriber) { + return false; + } + if (!$helperClass::assessSegmentConditions($group, $subscriber)) { + return false; + } + } else if ($groupName == 'activities') { + if (!$subscriber) { + return false; + } + if (!$helperClass::assessActivities($group, $subscriber)) { + return false; + } + } else if ($groupName == 'event_tracking') { + if (!$subscriber) { + return false; + } + if (!$helperClass::assessEventTrackingConditions($group, $subscriber)) { + return false; + } + } else if ($groupName == 'other') { + if (!$subscriber) { + return false; + } + foreach ($group as $condition) { + $prop = $condition['data_key']; + if (!apply_filters('fluentcrm_automation_custom_condition_assert_' . $prop, true, $condition, $subscriber, null, null)) { + return false; + } + } + } else if (DriverManager::getDriverByGroupKey($groupName)) { + if (!ConditionAssessor::matchAllConditions($group, $cartData)) { + return false; + } + } else { + if (!$subscriber) { + return false; + } + + $result = apply_filters("fluentcrm_automation_conditions_assess_$groupName", true, $group, $subscriber, null, null); + if (!$result) { + return false; + } + } + } + + return true; + } +} diff --git a/wp-content/plugins/fluent-crm/app/Modules/AbandonCart/Drivers/AbstractCartDriver.php b/wp-content/plugins/fluent-crm/app/Modules/AbandonCart/Drivers/AbstractCartDriver.php new file mode 100644 index 0000000..8a9f2ff --- /dev/null +++ b/wp-content/plugins/fluent-crm/app/Modules/AbandonCart/Drivers/AbstractCartDriver.php @@ -0,0 +1,206 @@ + [...], 'category_ids' => [...]] + */ + abstract public function extractCartConditionData(AbandonCartModel $cart); + + /** + * Enrich cart data for the admin listing API response. + * Adds product images, order URL, etc. + * + * @param AbandonCartModel $cart + * @return AbandonCartModel + */ + abstract public function enrichCartForListing(AbandonCartModel $cart); + + /** + * Return provider-specific data for the settings API response. + * e.g. WooCommerce returns order statuses. + * + * @return array + */ + public function getProviderSettingsResponse() + { + return []; + } + + /** + * Return provider-specific settings fields for the settings page. + * + * @return array + */ + public function getSettingsFields() + { + return []; + } + + /** + * Return provider-specific default settings to merge into global defaults. + * + * @return array + */ + public function getProviderSettingsDefaults() + { + return []; + } + + /** + * Apply provider-specific processing to settings after loading. + * + * @param array $settings + * @return array + */ + public function processSettings($settings) + { + return $settings; + } + + /** + * Get the trigger name for this provider's automation. + * + * @return string + */ + public function getTriggerName() + { + return 'fc_ab_cart_simulation_' . $this->getProviderSlug(); + } + + /** + * Get the handler name for cart recovery URL routing. + * + * @return string + */ + public function getHandlerName() + { + return 'fc_cart_' . $this->getProviderSlug(); + } + + /** + * Get the smart code group key for this provider. + * + * @return string + */ + public function getSmartCodeGroupKey() + { + return 'ab_cart_' . $this->getProviderSlug(); + } + + /** + * Get the base path for view templates. + * Drivers should override this to point to their own plugin's Views directory. + * + * @return string + */ + protected function getViewsBasePath() + { + return ''; + } + + /** + * Load a view template from the driver's Views directory. + * + * @param string $templateName + * @param array $data + * @return string + */ + protected function loadView($templateName, $data) + { + $basePath = $this->getViewsBasePath(); + if (!$basePath) { + return ''; + } + + extract($data, EXTR_SKIP); + ob_start(); + include $basePath . $templateName . '.php'; + return ltrim(ob_get_clean()); + } + + public function getLogo() + { + return $this->logo; + } +} diff --git a/wp-content/plugins/fluent-crm/app/Modules/AbandonCart/Drivers/DriverManager.php b/wp-content/plugins/fluent-crm/app/Modules/AbandonCart/Drivers/DriverManager.php new file mode 100644 index 0000000..be7adb6 --- /dev/null +++ b/wp-content/plugins/fluent-crm/app/Modules/AbandonCart/Drivers/DriverManager.php @@ -0,0 +1,168 @@ +getProviderSlug()] = $driver; + } + + /** + * @param string $providerSlug + * @return AbstractCartDriver|null + */ + public static function getDriver($providerSlug) + { + return static::$drivers[$providerSlug] ?? null; + } + + /** + * @return AbstractCartDriver[] + */ + public static function getAll() + { + return static::$drivers; + } + + /** + * @return AbstractCartDriver[] Only drivers whose platform is currently active + */ + public static function getAvailable() + { + return array_filter(static::$drivers, function ($driver) { + return $driver->isAvailable(); + }); + } + + /** + * @return string[] + */ + public static function getAvailableSlugs() + { + return array_keys(static::getAvailable()); + } + + /** + * @return bool + */ + public static function hasAvailableDrivers() + { + return count(static::getAvailable()) > 0; + } + + /** + * Get drivers that are both available (plugin installed) and enabled in settings. + * + * @return AbstractCartDriver[] + */ + public static function getEnabled() + { + $available = static::getAvailable(); + + $settings = AbCartHelper::getSettings(true); + + $enabledProviders = $settings['enabled_providers'] ?? []; + + if (empty($enabledProviders)) { + return []; + } + + return array_filter($available, function ($driver) use ($enabledProviders) { + return in_array($driver->getProviderSlug(), $enabledProviders); + }); + } + + /** + * @return string[] + */ + public static function getEnabledSlugs() + { + return array_keys(static::getEnabled()); + } + + /** + * Check if a specific driver is enabled + * + * @param string $providerSlug + * @return bool + */ + public static function isDriverEnabled($providerSlug) + { + return isset(static::getEnabled()[$providerSlug]); + } + + /** + * Get trigger names from all enabled drivers + * + * @return string[] + */ + public static function getEnabledTriggerNames() + { + return array_map(function ($driver) { + return $driver->getTriggerName(); + }, static::getEnabled()); + } + + /** + * Get smart code group keys from all registered drivers + * + * @return string[] + */ + public static function getAllSmartCodeGroupKeys() + { + return array_map(function ($driver) { + return $driver->getSmartCodeGroupKey(); + }, static::getAll()); + } + + /** + * Find a driver by its smart code group key (e.g. 'ab_cart_woo') + * + * @param string $groupKey + * @return AbstractCartDriver|null + */ + public static function getDriverByGroupKey($groupKey) + { + foreach (static::$drivers as $driver) { + if ($driver->getSmartCodeGroupKey() === $groupKey) { + return $driver; + } + } + + return null; + } + + /** + * Format a price using the appropriate driver, with a generic fallback + * + * @param float|string $amount + * @param string $currency + * @param string|null $providerSlug + * @return string + */ + public static function formatPrice($amount, $currency = '', $providerSlug = null) + { + if ($providerSlug) { + $driver = static::getDriver($providerSlug); + if ($driver) { + return $driver->formatPrice($amount, $currency); + } + } + + // Fall back to first available driver + $available = static::getAvailable(); + if ($available) { + $driver = reset($available); + return $driver->formatPrice($amount, $currency); + } + + return '$' . number_format((float)$amount, 2); + } +} diff --git a/wp-content/plugins/fluent-crm/app/Modules/AbandonCart/Drivers/FluentCart/FluentCartAutomationTrigger.php b/wp-content/plugins/fluent-crm/app/Modules/AbandonCart/Drivers/FluentCart/FluentCartAutomationTrigger.php new file mode 100644 index 0000000..7586829 --- /dev/null +++ b/wp-content/plugins/fluent-crm/app/Modules/AbandonCart/Drivers/FluentCart/FluentCartAutomationTrigger.php @@ -0,0 +1,270 @@ +triggerName = 'fc_ab_cart_simulation_fluent_cart'; + $this->priority = 99; + $this->actionArgNum = 1; + parent::__construct(); + } + + public function getTrigger() + { + return [ + 'category' => __('FluentCart', 'fluent-crm'), + 'label' => __('Cart Abandoned - FluentCart', 'fluent-crm'), + 'description' => __('This Funnel will be initiated when a cart has been abandoned in FluentCart', 'fluent-crm'), + 'svg' => '' + ]; + } + + public function getSettingsFields($funnel) + { + return [ + 'title' => __('Cart Abandoned - FluentCart', 'fluent-crm'), + 'sub_title' => __('This Funnel will be initiated when a cart has been abandoned in FluentCart', 'fluent-crm'), + 'fields' => [ + 'priority' => [ + 'label' => __('Priority of this abandon cart automation trigger', 'fluent-crm'), + 'type' => 'input-number', + 'placeholder' => __('Automation Priority', 'fluent-crm'), + 'inline_help' => __('If you have multiple automations for abandoned cart, you can set the priority. The higher the priority means it will match earlier. Only one abandoned cart automation will run per abandonment depending on your conditional logic.', 'fluent-crm') + ] + ] + ]; + } + + public function getFunnelSettingsDefaults() + { + return [ + 'priority' => 10 + ]; + } + + public function getFunnelConditionDefaults($funnel) + { + return [ + 'cart_conditions' => [[]], + 'active_once' => 'no', + 'require_subscribed' => 'no' + ]; + } + + public function getConditionFields($funnel) + { + if (!defined('FLUENTCAMPAIGN_DIR_FILE')) { + $cartConditionField = [ + 'type' => 'html', + 'label' => '', + 'info' => '

Conditions by Cart Items

' . __('FluentCRM Pro plugin is required to use the conditional logic for this trigger. Please install and activate FluentCRM Pro to use this feature.', 'fluent-crm') . '
' + ]; + } else { + $cartConditionField = [ + 'type' => 'condition_block_groups', + 'label' => __('Specify Matching Conditions', 'fluent-crm'), + 'inline_help' => __('Specify which contact properties need to be matched. If the conditions match then the automation will run.', 'fluent-crm'), + 'labels' => [ + 'match_type_all_label' => __('True if all conditions match', 'fluent-crm'), + 'match_type_any_label' => __('True if any of the conditions match', 'fluent-crm'), + 'data_key_label' => __('Contact Data', 'fluent-crm'), + 'condition_label' => __('Condition', 'fluent-crm'), + 'data_value_label' => __('Match Value', 'fluent-crm') + ], + 'groups' => $this->getConditionGroups($funnel), + 'add_label' => __('Add Condition to check your contact\'s properties', 'fluent-crm'), + ]; + } + + $fields = [ + 'cart_conditions' => $cartConditionField, + 'active_once' => [ + 'type' => 'yes_no_check', + 'label' => '', + 'check_label' => __('Skip this automation if the contact is already in active state.', 'fluent-crm'), + 'inline_help' => __('Enable this to prevent the automation from running multiple times for the same contact if it is currently active in this automation', 'fluent-crm') + ], + 'require_subscribed' => [ + 'type' => 'yes_no_check', + 'label' => '', + 'check_label' => __('Only run this automation for subscribed contacts', 'fluent-crm'), + 'inline_help' => __('If you enable, then it will only run this automation for subscribed contacts', 'fluent-crm') + ] + ]; + + + return $fields; + } + + public function handle($funnel, $originalArgs) + { + // do nothing here - cart processing is handled by AbandonCartRunner + } + + public function getConditionGroups($funnel) + { + $groups = [ + 'ab_cart_fluent_cart' => [ + 'label' => __('Cart Data', 'fluent-crm'), + 'value' => 'ab_cart_fluent_cart', + 'children' => [ + [ + 'label' => __('Cart Total', 'fluent-crm'), + 'value' => 'cart_total', + 'type' => 'numeric' + ], + [ + 'label' => __('Cart Items Count', 'fluent-crm'), + 'value' => 'cart_items_count', + 'type' => 'numeric' + ], + [ + 'label' => __('Cart Items', 'fluent-crm'), + 'value' => 'cart_items', + 'type' => 'selections', + 'component' => 'ajax_selector', + 'option_key' => 'fluent_cart_products', + 'is_multiple' => true, + 'help' => __('Match the products on the cart', 'fluent-crm') + ], + [ + 'label' => __('Cart Items Categories', 'fluent-crm'), + 'value' => 'cart_items_categories', + 'type' => 'selections', + 'component' => 'tax_selector', + 'taxonomy' => 'product-categories', + 'is_multiple' => true, + 'help' => __('Match the product categories on the cart', 'fluent-crm') + ], + ] + ], + 'subscriber' => [ + 'label' => __('Contact', 'fluent-crm'), + 'value' => 'subscriber', + 'children' => [ + [ + 'label' => __('First Name', 'fluent-crm'), + 'value' => 'first_name', + 'type' => 'nullable_text' + ], + [ + 'label' => __('Last Name', 'fluent-crm'), + 'value' => 'last_name', + 'type' => 'nullable_text' + ], + [ + 'label' => __('Email', 'fluent-crm'), + 'value' => 'email', + 'type' => 'extended_text' + ], + [ + 'label' => __('Country', 'fluent-crm'), + 'value' => 'country', + 'type' => 'selections', + 'component' => 'options_selector', + 'option_key' => 'countries', + 'is_multiple' => true, + 'is_singular_value' => true + ], + [ + 'label' => __('Phone', 'fluent-crm'), + 'value' => 'phone', + 'type' => 'nullable_text' + ], + [ + 'label' => __('Created At', 'fluent-crm'), + 'value' => 'created_at', + 'type' => 'dates', + ] + ], + ], + 'segment' => [ + 'label' => __('Contact Segment', 'fluent-crm'), + 'value' => 'segment', + 'children' => [ + [ + 'label' => __('Tags', 'fluent-crm'), + 'value' => 'tags', + 'type' => 'selections', + 'component' => 'options_selector', + 'option_key' => 'tags', + 'is_multiple' => true, + ], + [ + 'label' => __('Lists', 'fluent-crm'), + 'value' => 'lists', + 'type' => 'selections', + 'component' => 'options_selector', + 'option_key' => 'lists', + 'is_multiple' => true, + ], + [ + 'label' => __('WP User Role', 'fluent-crm'), + 'value' => 'user_role', + 'type' => 'selections', + 'is_singular_value' => true, + 'options' => FunnelHelper::getUserRoles(true), + 'is_multiple' => true, + ] + ], + ], + ]; + + if ($customFields = fluentcrm_get_custom_contact_fields()) { + $children = []; + foreach ($customFields as $field) { + $item = [ + 'label' => $field['label'], + 'value' => $field['slug'], + 'type' => $field['type'], + ]; + + if ($item['type'] == 'number') { + $item['type'] = 'numeric'; + } else if ($item['type'] == 'date') { + $item['type'] = 'dates'; + $item['date_type'] = 'date'; + $item['value_format'] = 'YYYY-MM-DD'; + } else if ($item['type'] == 'date_time') { + $item['type'] = 'dates'; + $item['has_time'] = 'yes'; + $item['date_type'] = 'datetime'; + $item['value_format'] = 'YYYY-MM-DD HH:mm:ss'; + } else if (isset($field['options'])) { + $item['type'] = 'selections'; + $options = $field['options']; + $formattedOptions = []; + foreach ($options as $option) { + $formattedOptions[$option] = $option; + } + $item['options'] = $formattedOptions; + $isMultiple = in_array($field['type'], ['checkbox', 'select-multi']); + $item['is_multiple'] = $isMultiple; + if ($isMultiple) { + $item['is_singular_value'] = true; + } + } else { + $item['type'] = 'extended_text'; + } + + $children[] = $item; + } + + $groups['custom_fields'] = [ + 'label' => __('Custom Fields', 'fluent-crm'), + 'value' => 'custom_fields', + 'children' => $children + ]; + } + + $groups = apply_filters('fluentcrm_automation_condition_groups', $groups, $funnel); + + return array_values($groups); + } +} diff --git a/wp-content/plugins/fluent-crm/app/Modules/AbandonCart/Drivers/FluentCart/FluentCartDriver.php b/wp-content/plugins/fluent-crm/app/Modules/AbandonCart/Drivers/FluentCart/FluentCartDriver.php new file mode 100644 index 0000000..c765086 --- /dev/null +++ b/wp-content/plugins/fluent-crm/app/Modules/AbandonCart/Drivers/FluentCart/FluentCartDriver.php @@ -0,0 +1,278 @@ +register(); + } + + public function registerAutomationTrigger() + { + // Registered inside FluentCartTrackingInit::register() + } + + protected function getViewsBasePath() + { + return __DIR__ . '/Views/'; + } + + public function isWithinCoolOffPeriod(AbandonCartModel $cart) + { + $coolOffPeriodDay = AbCartHelper::getSetting('cool_off_period_days', 0); + if (!$coolOffPeriodDay) { + return false; + } + + $coolOffDateTime = gmdate('Y-m-d H:i:s', time() - ($coolOffPeriodDay * DAY_IN_SECONDS)); + + $winStatuses = $this->getWinOrderStatuses(); + + return fluentCrmDb()->table('fct_orders') + ->where('created_at', '>=', $coolOffDateTime) + ->whereIn('status', $winStatuses) + ->where(function ($q) use ($cart) { + $q->whereIn('customer_id', function ($sub) use ($cart) { + $sub->select('id') + ->from('fct_customers') + ->where('email', $cart->email); + if ($cart->user_id) { + $sub->orWhere('user_id', $cart->user_id); + } + }); + }) + ->exists(); + } + + public function getCartItemsHtml(AbandonCartModel $cart) + { + $cartItems = Arr::get($cart->cart, 'cart_data', []); + + return $this->loadView('AbandonCartItems', [ + 'cartItems' => $cartItems, + 'currency' => $cart->currency + ]); + } + + public function formatPrice($amount, $currency = '') + { + if (!class_exists('\FluentCart\Api\CurrencySettings')) { + return '$' . number_format((float)$amount, 2); + } + + return \FluentCart\Api\CurrencySettings::getPriceHtml((int) round(((float) $amount) * 100), $currency ?: null); + } + + public function getRecoveryUrl(AbandonCartModel $cart) + { + if ($cart->status != 'processing') { + return ''; + } + + return add_query_arg([ + 'fluentcrm' => 1, + 'route' => 'general', + 'handler' => $this->getHandlerName(), + 'fc_ab_hash' => $cart->checkout_key + ], home_url()); + } + + public function extractCartConditionData(AbandonCartModel $cart) + { + $items = Arr::get($cart->cart, 'cart_data', []); + + $productIds = []; + $categoryIds = []; + + foreach ($items as $item) { + $postId = Arr::get($item, 'post_id'); + if ($postId) { + $productIds[] = $postId; + } + } + + if ($productIds) { + $cats = fluentCrmDb()->table('term_relationships') + ->join('term_taxonomy', 'term_relationships.term_taxonomy_id', '=', 'term_taxonomy.term_taxonomy_id') + ->whereIn('term_relationships.object_id', $productIds) + ->where('term_taxonomy.taxonomy', 'product-categories') + ->select('term_taxonomy.term_id') + ->get(); + + foreach ($cats as $cat) { + $categoryIds[] = $cat->term_id; + } + } + + return [ + 'product_ids' => $productIds, + 'category_ids' => $categoryIds + ]; + } + + public function enrichCartForListing(AbandonCartModel $cart) + { + if ($cart->order_id) { + $cart->order_url = admin_url('admin.php?page=fluent-cart#/orders/' . $cart->order_id . '/view'); + } + + $newCart = $cart->cart ?: []; + $formData = Arr::get($newCart, 'checkout_data.form_data', []); + + $billingFullName = trim(Arr::get($formData, 'billing_first_name', '') . ' ' . Arr::get($formData, 'billing_last_name', '')); + + if (!$billingFullName) { + $billingFullName = $cart->full_name; + } + + // Build customer_data with billingAddress/shippingAddress for the shared Vue modal + $newCart['customer_data'] = [ + 'billingAddress' => [ + 'first_name' => $billingFullName, + 'last_name' => '', + 'address_1' => Arr::get($formData, 'billing_address_1', ''), + 'address_2' => Arr::get($formData, 'billing_address_2', ''), + 'postcode' => Arr::get($formData, 'billing_postcode', ''), + 'city' => Arr::get($formData, 'billing_city', ''), + 'country' => Arr::get($formData, 'billing_country', ''), + ], + 'shippingAddress' => [ + 'first_name' => Arr::get($formData, 'shipping_full_name', ''), + 'last_name' => '', + 'address_1' => Arr::get($formData, 'shipping_address_1', ''), + 'address_2' => Arr::get($formData, 'shipping_address_2', ''), + 'postcode' => Arr::get($formData, 'shipping_postcode', ''), + 'city' => Arr::get($formData, 'shipping_city', ''), + 'country' => Arr::get($formData, 'shipping_country', ''), + ], + 'order_comments' => Arr::get($formData, 'order_comments', ''), + ]; + + if (Arr::get($formData, 'ship_to_different') !== 'yes') { + $newCart['customer_data']['shippingAddress'] = $newCart['customer_data']['billingAddress']; + } + + // Build cart_contents from cart_data for the shared Vue modal + $cartContents = []; + $cartItems = Arr::get($newCart, 'cart_data', []); + foreach ($cartItems as $cartItem) { + $imageUrl = Arr::get($cartItem, 'featured_media', ''); + if (!$imageUrl) { + $postId = Arr::get($cartItem, 'post_id'); + if ($postId) { + $imageUrl = get_the_post_thumbnail_url($postId, 'thumbnail'); + } + } + + $subtotal = (int)Arr::get($cartItem, 'subtotal', 0); + + $title = Arr::get($cartItem, 'post_title', ''); + $subTitle = Arr::get($cartItem, 'title', ''); + + if($title && $subTitle && $title != $subTitle) { + $title .= ' - ' . $subTitle; + } + + + $cartContents[] = [ + 'title' => $title, + 'quantity' => (int)Arr::get($cartItem, 'quantity', 1), + 'line_total' => number_format($subtotal / 100, 2, '.', ''), + 'product_image' => $imageUrl ?: '', + ]; + } + $newCart['cart_contents'] = $cartContents; + + $cart->cart = $newCart; + + return $cart; + } + + public function getProviderSettingsResponse() + { + if (!defined('FLUENTCART_VERSION')) { + return []; + } + + return [ + 'fct_recovered_statuses' => $this->getWinOrderStatuses(), + ]; + } + + public function getProviderSettingsDefaults() + { + return [ + 'fct_recovered_statuses' => ['completed', 'processing'], + ]; + } + + public function getSettingsFields() + { + if (!$this->isAvailable()) { + return []; + } + + $statuses = [ + ['id' => 'completed', 'label' => __('Completed', 'fluent-crm')], + ['id' => 'processing', 'label' => __('Processing', 'fluent-crm')], + ['id' => 'on-hold', 'label' => __('On Hold', 'fluent-crm')], + ]; + + return [ + 'fct_recovered_statuses' => [ + 'name' => 'fct_recovered_statuses', + 'label' => __('Mark Cart as Recovered when FluentCart Order Status Changes to:', 'fluent-crm'), + 'type' => 'checkbox-group', + 'options' => $statuses, + 'inline_help' => __('Automatically mark a cart as recovered when the corresponding FluentCart order status changes to the selected status.', 'fluent-crm'), + ] + ]; + } + + /** + * Check if a FluentCart order status counts as a successful recovery. + * + * @param string $orderStatus + * @return bool + */ + public function isWinOrderStatus($orderStatus) + { + $recoveredStatuses = $this->getWinOrderStatuses(); + $result = in_array($orderStatus, $recoveredStatuses, true); + + return apply_filters('fluent_crm/ab_cart_is_win_status', $result, $orderStatus, $this); + } + + private function getWinOrderStatuses() + { + $settings = AbCartHelper::getSettings(); + return Arr::get($settings, 'fct_recovered_statuses', ['completed', 'processing']); + } +} diff --git a/wp-content/plugins/fluent-crm/app/Modules/AbandonCart/Drivers/FluentCart/FluentCartTrackingInit.php b/wp-content/plugins/fluent-crm/app/Modules/AbandonCart/Drivers/FluentCart/FluentCartTrackingInit.php new file mode 100644 index 0000000..0c46155 --- /dev/null +++ b/wp-content/plugins/fluent-crm/app/Modules/AbandonCart/Drivers/FluentCart/FluentCartTrackingInit.php @@ -0,0 +1,752 @@ +register(); + + // Checkout Frontend - inject tracking script + add_action('fluent_cart/after_checkout_page', [$this, 'addAbandonScript']); + + // AJAX handler for GDPR opt-out + add_action('wp_ajax_fc_ab_fct_cart_skip', [$this, 'handleAjaxOptOut']); + add_action('wp_ajax_nopriv_fc_ab_fct_cart_skip', [$this, 'handleAjaxOptOut']); + + // Sync abandoned cart when FluentCart saves checkout data + add_filter('fluent_cart/checkout/after_patch_checkout_data_fragments', function ($fragments, $data) { + $this->maybeSyncCart(Arr::get($data, 'cart')); + return $fragments; + }, 99, 2); + + // Sync abandoned cart when cart amounts change (item add/remove, coupon) + add_action('fluent_cart/checkout/cart_amount_updated', function ($data) { + $this->maybeSyncCart(Arr::get($data, 'cart')); + }, 99); + + // Sync abandoned cart on form data change (alternative save path) + add_action('fluent_cart/checkout/form_data_changed', function ($data) { + $this->maybeSyncCart(Arr::get($data, 'cart')); + }, 99); + + // Cart recovery URL handler + // URL: example.com/?fluentcrm=1&route=general&handler=fc_cart_fluent_cart&fc_ab_hash=xyz + add_action('fluent_crm/handle_frontend_for_fc_cart_fluent_cart', function ($data) { + add_action('template_redirect', function () use ($data) { + $this->maybeRestoreCart($data); + }, 1); + }); + + // Order lifecycle - link cart to order when created + add_action('fluent_cart/order_created', [$this, 'handleOrderCreated'], 1); + + // Order paid - mark cart as recovered + add_action('fluent_cart/order_paid', [$this, 'handleOrderPaid'], 1); + + // Order status changes + add_action('fluent_cart/order_status_changed', [$this, 'handleOrderStatusChanged'], 10); + + // Push contextual smart codes for this provider + add_filter('fluent_crm_funnel_context_smart_codes', [$this, 'pushContextCodes'], 1, 2); + + // Parse the context codes + add_filter('fluent_crm/smartcode_group_callback_ab_cart_fluent_cart', [$this, 'parseSmartCodes'], 10, 4); + } + + public function addAbandonScript() + { + if (!AbCartHelper::willCartTrack()) { + return; + } + + if (isset($_COOKIE['fc_ab_cart_skip_track']) && $_COOKIE['fc_ab_cart_skip_track'] == 'yes') { + return; + } + + wp_enqueue_script( + 'fluent_crm-abandon-cart-fct', + FLUENTCRM_PLUGIN_URL . 'app/Modules/AbandonCart/Drivers/FluentCart/assets/fc-cart-abandon-fluent-cart.js', + [], + FLUENTCRM_PLUGIN_VERSION, + true + ); + + wp_localize_script('fluent_crm-abandon-cart-fct', 'fc_ab_fct_cart', [ + 'nonce' => wp_create_nonce('fc_ab_fct_cart_nonce'), + '__gdpr_message' => AbCartHelper::getGDPRMessage(), + ]); + } + + public function handleAjaxOptOut() + { + $nonce = Arr::get($_REQUEST, '_nonce'); + if (!wp_verify_nonce($nonce, 'fc_ab_fct_cart_nonce')) { + wp_send_json([ + 'message' => __('Security check failed. Invalid nonce.', 'fluent-crm') + ], 403); + } + + $record = $this->getCurrentRecord(); + + if ($record) { + $record->optOut(); + } + + $cookieDays = (int)apply_filters('fluent_crm/ab_cart_opt_out_cookie_validity', 7); + setcookie('fc_ab_cart_skip_track', 'yes', time() + (86400 * $cookieDays), COOKIEPATH, COOKIE_DOMAIN); + + wp_send_json([ + 'message' => __('You have opted out from cart tracking', 'fluent-crm') + ]); + } + + public function maybeSyncCart($fctCart) + { + if (!$fctCart || !AbCartHelper::willCartTrack()) { + return; + } + + if (!$fctCart->email || empty($fctCart->cart_data)) { + return; + } + + $billingEmail = $fctCart->email; + + if (isset($_COOKIE['fc_ab_cart_skip_track']) && $_COOKIE['fc_ab_cart_skip_track'] == 'yes') { + $record = $this->getCurrentRecord($billingEmail, $fctCart->cart_hash); + if ($record && $record->status !== 'opt_out') { + $record->status = 'opt_out'; + $record->save(); + } + return; + } + + $checkoutData = $fctCart->checkout_data ?: []; + + // Calculate totals for the table columns (FluentCart stores amounts in cents). + // $subtotal is the items' price BEFORE any discount. + $subtotal = $fctCart->getItemsSubtotal(); + + // Coupon and per-item manual discounts are stored on each cart item as + // discount_total (manual_discount + coupon_discount). The old code only read + // custom_checkout_data.discount_total, which is empty for normal frontend + // coupons, so the coupon was never reflected in the cart total. Sum the per-item + // discounts plus any checkout-level (manual/upgrade/prorate) discounts. + $itemsDiscountTotal = array_sum(array_map(function ($item) { + return (int)Arr::get($item, 'discount_total', 0); + }, $fctCart->cart_data ?: [])); + + $discountTotal = $itemsDiscountTotal + + (int)Arr::get($checkoutData, 'manual_discount.amount', 0) + + (int)Arr::get($checkoutData, 'upgrade_discount.amount', 0) + + (int)Arr::get($checkoutData, 'prorate_credit.amount', 0); + + $shippingTotal = (int)$fctCart->getShippingTotal(); + $taxTotal = (int)Arr::get($checkoutData, 'tax_data.tax_total', 0); + + // Use FluentCart's authoritative total so the displayed Cart Total matches the + // checkout exactly (coupons, fees, shipping and tax all included). + $total = (int)$fctCart->getEstimatedTotal(); + + if ($total <= 0) { + $record = $this->getCurrentRecord($billingEmail); + if ($record) { + $record->delete(); + setcookie('fc_ab_fct_cart_token', '', time() - 3600, COOKIEPATH, COOKIE_DOMAIN); + } + return; + } + + $currency = ''; + if (class_exists('\FluentCart\Api\CurrencySettings')) { + $currency = \FluentCart\Api\CurrencySettings::get('currency') ?: 'USD'; + } + + $contact = FluentCrmApi('contacts')->getContact($billingEmail); + $fullName = $fctCart->full_name ?? trim($fctCart->first_name . ' ' . $fctCart->last_name); + if (!$fullName && $contact) { + $fullName = trim($contact->first_name . ' ' . $contact->last_name); + } + + // Build a per-coupon breakdown (code + discounted value) for the cart details view. + // FluentCart stores the applied codes on $fctCart->coupons and the amount each code + // saved (in cents) on checkout_data.__per_coupon_discounts, keyed by code. + $perCouponDiscounts = Arr::get($checkoutData, '__per_coupon_discounts', []); + $couponDetails = []; + foreach (($fctCart->coupons ?: []) as $couponCode) { + $couponAmount = (int)Arr::get($perCouponDiscounts, $couponCode, 0); + $couponDetails[] = [ + 'code' => $couponCode, + 'discount' => number_format($couponAmount / 100, 2, '.', ''), + ]; + } + + // Snapshot the FluentCart data as-is for easy restore + $data = [ + 'cart_hash' => $fctCart->cart_hash, + 'full_name' => $fullName, + 'email' => $billingEmail, + 'provider' => 'fluent_cart', + 'user_id' => $fctCart->user_id, + 'contact_id' => $contact ? $contact->id : null, + 'order_id' => $fctCart->order_id, + 'subtotal' => number_format($subtotal / 100, 2, '.', ''), + 'shipping' => number_format($shippingTotal / 100, 2, '.', ''), + 'discounts' => number_format($discountTotal / 100, 2, '.', ''), + 'tax' => number_format($taxTotal / 100, 2, '.', ''), + 'fees' => 0, + 'total' => number_format($total / 100, 2, '.', ''), + 'currency' => $currency, + 'cart' => [ + 'cart_data' => $fctCart->cart_data ?: [], + 'checkout_data' => $checkoutData, + 'coupons' => $fctCart->coupons ?: [], + 'coupons_detail' => $couponDetails, + 'utm_data' => $fctCart->utm_data ?: [], + 'cart_group' => $fctCart->cart_group, + 'customer_data' => $this->buildCustomerData($checkoutData), + ], + ]; + + $record = $this->getCurrentRecord($billingEmail, $fctCart->cart_hash); + + if (!$record) { + $data['status'] = 'draft'; + $record = AbandonCartModel::create($data); + } else { + $record->fill($data); + $record->save(); + } + + $cookieDays = (int)apply_filters('fluent_crm/ab_cart_cookie_validity', 30); + setcookie('fc_ab_fct_cart_token', $record->checkout_key, time() + (86400 * $cookieDays), COOKIEPATH, COOKIE_DOMAIN); + } + + private function buildCustomerData($checkoutData) + { + $formData = Arr::get($checkoutData, 'form_data', []); + + return [ + 'billingAddress' => [ + 'first_name' => Arr::get($formData, 'billing_first_name', ''), + 'last_name' => Arr::get($formData, 'billing_last_name', ''), + 'address_1' => Arr::get($formData, 'billing_address_1', ''), + 'address_2' => Arr::get($formData, 'billing_address_2', ''), + 'postcode' => Arr::get($formData, 'billing_postcode', ''), + 'city' => Arr::get($formData, 'billing_city', ''), + 'state' => Arr::get($formData, 'billing_state', ''), + 'country' => Arr::get($formData, 'billing_country', ''), + 'phone' => Arr::get($formData, 'billing_phone', ''), + ], + ]; + } + + private function getCurrentRecord($billingEmail = null, $cartHash = null) + { + if ($cartHash) { + // Try to find by cart hash first if available + $record = AbandonCartModel::where('cart_hash', $cartHash) + ->where('provider', 'fluent_cart') + ->whereIn('status', ['pending', 'opt_out', 'draft', 'processing']) + ->first(); + + if ($record) { + return $record; + } + } + + // First try from the cookie + $existingToken = Arr::get($_COOKIE, 'fc_ab_fct_cart_token'); + + if ($existingToken) { + $record = AbandonCartModel::where('checkout_key', $existingToken) + ->where('provider', 'fluent_cart') + ->whereIn('status', ['pending', 'opt_out', 'draft', 'processing']) + ->first(); + + if ($record) { + return $record; + } + } + + // Try with billing email + if ($billingEmail) { + $record = AbandonCartModel::where('email', $billingEmail) + ->where('provider', 'fluent_cart') + ->whereIn('status', ['pending', 'opt_out', 'draft', 'processing']) + ->first(); + + if ($record) { + return $record; + } + } + + // If user logged in, try with user id + $userId = get_current_user_id(); + if ($userId) { + $record = AbandonCartModel::where('user_id', $userId) + ->where('provider', 'fluent_cart') + ->whereIn('status', ['pending', 'opt_out', 'draft', 'processing']) + ->first(); + + if ($record) { + return $record; + } + } + + return null; + } + + public function handleOrderCreated($eventData) + { + $order = Arr::get($eventData, 'order'); + if (!$order) { + return; + } + + $token = sanitize_text_field(Arr::get($_COOKIE, 'fc_ab_fct_cart_token', '')); + if (!$token) { + return; + } + + $abCart = AbCartHelper::getAbCartByDataProps([ + 'checkout_key' => $token + ], ['processing', 'draft']); + + if (!$abCart || $abCart->provider !== 'fluent_cart') { + return; + } + + $abCart->order_id = $order->id; + $abCart->save(); + } + + public function handleOrderPaid($eventData) + { + $order = Arr::get($eventData, 'order'); + $customer = Arr::get($eventData, 'customer'); + if (!$order || !$customer) { + return; + } + + $abCart = AbandonCartModel::query()->where('order_id', $order->id)->where('provider', 'fluent_cart')->first(); + + if (!$abCart) { + $this->cancelAutomationsByCustomer($customer); + return; + } + + $this->markCartAsRecovered($abCart, $order); + } + + public function handleOrderStatusChanged($eventData) + { + $order = Arr::get($eventData, 'order'); + $newStatus = Arr::get($eventData, 'new_status'); + + if (!$order || !$newStatus) { + return; + } + + $abCartId = $order->getMeta('_fc_ab_cart_id'); + if (!$abCartId) { + return; + } + + $abCart = AbandonCartModel::find($abCartId); + if (!$abCart || $abCart->provider !== 'fluent_cart') { + return; + } + + $driver = new FluentCartDriver(); + + if ($driver->isWinOrderStatus($newStatus)) { + if ($abCart->status !== 'recovered') { + $this->markCartAsRecovered($abCart, $order); + } + return; + } + + $lostStatuses = ['failed', 'canceled']; + if (in_array($newStatus, $lostStatuses, true)) { + $this->handleCartLost($abCart, $order); + } + } + + private function markCartAsRecovered($abCart, $order) + { + $deletableStatuses = ['draft', 'opt_out', 'pending']; + if (in_array($abCart->status, $deletableStatuses, true)) { + $abCart->deleteCart(); + $this->deleteOtherCarts($abCart, $order); + return; + } + + $recoverableStatuses = ['processing', 'lost', 'cancelled']; + if (!in_array($abCart->status, $recoverableStatuses, true)) { + return; + } + + $settings = AbCartHelper::getSettings(); + $subscriber = $abCart->subscriber; + if ($subscriber) { + if ($attachLists = Arr::get($settings, 'lists_on_cart_abandoned', [])) { + $subscriber->detachLists($attachLists); + } + + if ($attachTags = Arr::get($settings, 'tags_on_cart_abandoned', [])) { + $subscriber->detachTags($attachTags); + } + } + + $orderTotal = $order->total_amount ?? 0; + + $oldStatus = $abCart->status; + $abCart->status = 'recovered'; + $abCart->order_id = $order->id; + $abCart->total = $orderTotal / 100; + $abCart->recovered_at = current_time('mysql'); + $abCart->save(); + + do_action('fluent_crm/ab_cart_fluent_cart_recovered', $abCart, $order, $oldStatus); + + $this->deleteOtherCarts($abCart, $order); + + $this->handleCartRecoveredAutomations($abCart); + } + + private function handleCartLost($abCart, $order) + { + if ($abCart->status == 'lost') { + return; + } + + $oldStatus = $abCart->status; + $abCart->status = 'lost'; + $abCart->save(); + + do_action('fluent_crm/ab_cart_fluent_cart_lost', $abCart, $order, $oldStatus); + + if ($abCart->automation_id) { + $subscriber = $abCart->subscriber; + if ($subscriber) { + $settings = AbCartHelper::getSettings(); + if ($attachLists = Arr::get($settings, 'lists_on_cart_lost', [])) { + $subscriber->attachLists($attachLists); + } + + if ($attachTags = Arr::get($settings, 'tags_on_cart_lost', [])) { + $subscriber->attachTags($attachTags); + } + + FunnelSubscriber::where('subscriber_id', $subscriber->id) + ->where('source_ref_id', $abCart->id) + ->whereHas('funnel', function ($q) { + $q->where('trigger_name', 'fc_ab_cart_simulation_fluent_cart'); + }) + ->where('funnel_id', $abCart->automation_id) + ->update([ + 'status' => 'cancelled', + 'notes' => __('Automatically cancelled because the cart has been lost', 'fluent-crm') + ]); + } + } + } + + private function handleCartRecoveredAutomations($abCart) + { + if (!$abCart->automation_id) { + return; + } + + $contact = $abCart->subscriber; + if (!$contact) { + return; + } + + $this->cancelAutomations($contact); + } + + private function cancelAutomations($subscriber) + { + FunnelSubscriber::where('subscriber_id', $subscriber->id) + ->whereHas('funnel', function ($q) { + $q->where('trigger_name', 'fc_ab_cart_simulation_fluent_cart'); + }) + ->whereIn('status', ['active', 'pending', 'paused']) + ->update([ + 'status' => 'cancelled', + 'notes' => __('Automatically cancelled because a cart has been recovered', 'fluent-crm') + ]); + } + + private function cancelAutomationsByCustomer($customer) + { + $subscriberIds = Subscriber::select(['id']) + ->where('email', $customer->email) + ->when($customer->user_id, function ($q) use ($customer) { + return $q->orWhere('user_id', $customer->user_id); + }) + ->pluck('id') + ->toArray(); + + if (!$subscriberIds) { + return; + } + + FunnelSubscriber::whereIn('subscriber_id', $subscriberIds) + ->whereHas('funnel', function ($q) { + $q->where('trigger_name', 'fc_ab_cart_simulation_fluent_cart'); + }) + ->whereIn('status', ['active', 'pending', 'paused']) + ->update([ + 'status' => 'cancelled', + 'notes' => __('Automatically cancelled because a cart has been recovered', 'fluent-crm') + ]); + } + + private function deleteOtherCarts($abCart, $order) + { + $customerEmail = $abCart->email; + $customerId = 0; + + if (method_exists($order, 'getAttribute')) { + $customerId = $order->customer_id ?? 0; + } + + $query = AbandonCartModel::where('provider', 'fluent_cart') + ->where('id', '!=', $abCart->id) + ->whereIn('status', ['processing', 'draft']); + + $query->where(function ($q) use ($customerEmail, $customerId) { + $q->where('email', $customerEmail); + if ($customerId) { + // Look up user_id from the FluentCart customer + $userId = fluentCrmDb()->table('fct_customers') + ->where('id', $customerId) + ->value('user_id'); + if ($userId) { + $q->orWhere('user_id', $userId); + } + } + }); + + $otherCarts = $query->get(); + + foreach ($otherCarts as $cart) { + $cart->deleteCart(); + } + } + + public function maybeRestoreCart($data) + { + $cartHash = sanitize_text_field(Arr::get($data, 'fc_ab_hash', '')); + + $abandonCart = null; + if ($cartHash) { + $abandonCart = AbandonCartModel::where('checkout_key', $cartHash)->first(); + } + + if (!$abandonCart || $abandonCart->status != 'processing' || $abandonCart->provider != 'fluent_cart') { + do_action('fluent_crm/ab_cart_restore_failed', $abandonCart); + + $checkoutUrl = home_url(); + if (class_exists('\FluentCart\Api\StoreSettings')) { + $checkoutUrl = (new \FluentCart\Api\StoreSettings())->getCheckoutPage() ?: $checkoutUrl; + } + + wp_redirect($checkoutUrl); + exit(); + } + + // Set tracking cookie + $cookieDays = (int)apply_filters('fluent_crm/ab_cart_cookie_validity', 30); + setcookie('fc_ab_fct_cart_token', $abandonCart->checkout_key, time() + (86400 * $cookieDays), COOKIEPATH, COOKIE_DOMAIN); + + $abandonCart->click_counts = $abandonCart->click_counts + 1; + $abandonCart->save(); + + // Restore the FluentCart cart from our snapshot + $snapshot = $abandonCart->cart; + $fctCartHash = $abandonCart->cart_hash; + + if ($fctCartHash) { + $fctCart = \FluentCart\App\Models\Cart::where('cart_hash', $fctCartHash)->first(); + + if ($fctCart) { + // just redirect to checkout if the cart still exists in FluentCart + $checkoutUrl = add_query_arg([ + 'fct_cart_hash' => $fctCart->cart_hash, + ], (new StoreSettings())->getCheckoutPage()); + + wp_redirect($checkoutUrl); + exit(); + } + } + + + $newCart = new Cart(); + $newCart->cart_hash = $abandonCart->cart_hash ?: Cart::generateCartHash(); + // Write the snapshot back directly + $newCart->cart_data = Arr::get($snapshot, 'cart_data', []); + $newCart->checkout_data = Arr::get($snapshot, 'checkout_data', []); + $newCart->coupons = Arr::get($snapshot, 'coupons', []); + $newCart->email = $abandonCart->email; + $newCart->utm_data = Arr::get($snapshot, 'utm_data', []); + $newCart->cart_group = 'instant'; + $newCart->ip_address = AddressHelper::getIpAddress(); + $newCart->user_agent = AddressHelper::getUserAgent(); + + $fullName = $abandonCart->full_name; + if ($fullName) { + // Try to split full name into first and last name for better compatibility + $nameParts = explode(' ', $fullName, 2); + $newCart->first_name = $nameParts[0]; + $newCart->last_name = isset($nameParts[1]) ? $nameParts[1] : ''; + } + + $newCart->save(); + + \FluentCart\Api\Cookie\Cookie::setCartHash($newCart->cart_hash); + + $abandonCart->cart_hash = $newCart->cart_hash; + $abandonCart->save(); + + wp_redirect(add_query_arg(['fct_cart_hash' => $newCart->cart_hash], (new \FluentCart\Api\StoreSettings())->getCheckoutPage())); + exit(); + } + + public function pushContextCodes($codes, $context) + { + if ($context != 'fc_ab_cart_simulation_fluent_cart') { + return $codes; + } + + $smartCodes = [ + 'key' => 'ab_cart_fluent_cart', + 'title' => 'Abandoned Cart - FluentCart', + 'shortcodes' => [ + '{{ab_cart_fluent_cart.billing_email}}' => __('Cart Billing Email', 'fluent-crm'), + '{{ab_cart_fluent_cart.cart_items_table}}' => __('Cart Items', 'fluent-crm'), + '##ab_cart_fluent_cart.recovery_url##' => __('Cart Recovery URL', 'fluent-crm'), + '{{ab_cart_fluent_cart.cart_total}}' => __('Cart Total', 'fluent-crm'), + '{{ab_cart_fluent_cart.subtotal}}' => __('Cart Subtotal (only products)', 'fluent-crm'), + '{{ab_cart_fluent_cart.shipping_total}}' => __('Cart Shipping Total', 'fluent-crm'), + '{{ab_cart_fluent_cart.discount_total}}' => __('Cart Discount Total', 'fluent-crm'), + '{{ab_cart_fluent_cart.coupon_codes}}' => __('Applied Coupon Codes', 'fluent-crm'), + '{{ab_cart_fluent_cart.tax_total}}' => __('Cart Tax Total', 'fluent-crm'), + '{{ab_cart_fluent_cart.billing_full_name}}' => __('Billing Full Name', 'fluent-crm'), + '{{ab_cart_fluent_cart.billing_address}}' => __('Billing Address', 'fluent-crm'), + '{{ab_cart_fluent_cart.shipping_address}}' => __('Shipping Address', 'fluent-crm'), + '{{ab_cart_fluent_cart.billing_city}}' => __('Billing City', 'fluent-crm'), + '{{ab_cart_fluent_cart.billing_state}}' => __('Billing State', 'fluent-crm'), + '{{ab_cart_fluent_cart.billing_postcode}}' => __('Billing Postcode', 'fluent-crm'), + '{{ab_cart_fluent_cart.billing_country}}' => __('Billing Country', 'fluent-crm'), + '{{ab_cart_fluent_cart.billing_phone}}' => __('Billing Phone', 'fluent-crm'), + ] + ]; + + $codes[] = $smartCodes; + + return $codes; + } + + public function parseSmartCodes($code, $valueKey, $defaultValue, $subscriber) + { + $abCart = null; + + if ($subscriber->funnel_subscriber_id) { + $funnelSub = FunnelSubscriber::find($subscriber->funnel_subscriber_id); + if ($funnelSub) { + $abCart = AbandonCartModel::find($funnelSub->source_ref_id); + } + } + + if (!$abCart) { + $abCart = AbandonCartModel::where('email', $subscriber->email) + ->where('provider', 'fluent_cart') + ->whereIn('status', ['processing', 'opt_out', 'lost']) + ->orderBy('id', 'DESC') + ->first(); + } + + if (!$abCart && defined('FLUENTCRM_PREVIEWING_EMAIL')) { + $abCart = AbandonCartModel::where('provider', 'fluent_cart') + ->orderBy('id', 'DESC') + ->first(); + } + + if (!$abCart) { + if (defined('FLUENTCRM_PREVIEWING_EMAIL')) { + return __('Dynamic Text will be available on real email', 'fluent-crm'); + } + + return $defaultValue; + } + + $formatPrice = function ($amount) use ($abCart) { + $driver = new FluentCartDriver(); + return $driver->formatPrice($amount, $abCart->currency); + }; + + switch ($valueKey) { + case 'billing_email': + return $abCart->email; + case 'cart_total': + return $formatPrice($abCart->total); + case 'subtotal': + return $formatPrice($abCart->subtotal); + case 'shipping_total': + return $abCart->shipping ? $formatPrice($abCart->shipping) : $defaultValue; + case 'discount_total': + return ($abCart->discounts > 0) ? $formatPrice($abCart->discounts) : $defaultValue; + case 'coupon_codes': + $couponCodes = Arr::get($abCart->cart, 'coupons', []); + return $couponCodes ? implode(', ', $couponCodes) : $defaultValue; + case 'tax_total': + return $abCart->tax ? $formatPrice($abCart->tax) : $defaultValue; + case 'billing_full_name': + return $abCart->full_name ?: $defaultValue; + case 'billing_address': + case 'shipping_address': + $prefix = ($valueKey === 'shipping_address') ? 'shipping_' : 'billing_'; + $formData = Arr::get($abCart->cart, 'checkout_data.form_data', []); + return implode(', ', array_filter([ + Arr::get($formData, $prefix . 'address_1'), + Arr::get($formData, $prefix . 'address_2'), + Arr::get($formData, $prefix . 'city'), + Arr::get($formData, $prefix . 'state'), + Arr::get($formData, $prefix . 'postcode'), + ])) ?: $defaultValue; + case 'billing_city': + case 'billing_state': + case 'billing_postcode': + case 'billing_country': + case 'billing_phone': + return Arr::get($abCart->cart, 'checkout_data.form_data.' . $valueKey, $defaultValue); + case 'recovery_url': + return add_query_arg([ + 'fluentcrm' => 1, + 'route' => 'general', + 'handler' => 'fc_cart_fluent_cart', + 'fc_ab_hash' => $abCart->checkout_key + ], home_url()); + case 'cart_items_table': + return $abCart->getCartItemsHtml(); + default: + return apply_filters('fluent_crm/ab_cart_smart_code_default_value', $defaultValue, $valueKey, $abCart); + } + } +} diff --git a/wp-content/plugins/fluent-crm/app/Modules/AbandonCart/Drivers/FluentCart/Views/AbandonCartItems.php b/wp-content/plugins/fluent-crm/app/Modules/AbandonCart/Drivers/FluentCart/Views/AbandonCartItems.php new file mode 100644 index 0000000..3b17d56 --- /dev/null +++ b/wp-content/plugins/fluent-crm/app/Modules/AbandonCart/Drivers/FluentCart/Views/AbandonCartItems.php @@ -0,0 +1,136 @@ + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + <?php echo esc_attr($title); ?> + + + + + + + +
+
diff --git a/wp-content/plugins/fluent-crm/app/Modules/AbandonCart/Drivers/FluentCart/assets/fc-cart-abandon-fluent-cart.js b/wp-content/plugins/fluent-crm/app/Modules/AbandonCart/Drivers/FluentCart/assets/fc-cart-abandon-fluent-cart.js new file mode 100644 index 0000000..9f502a2 --- /dev/null +++ b/wp-content/plugins/fluent-crm/app/Modules/AbandonCart/Drivers/FluentCart/assets/fc-cart-abandon-fluent-cart.js @@ -0,0 +1,76 @@ +(function () { + 'use strict'; + + if (typeof fc_ab_fct_cart === 'undefined') { + return; + } + + var gdprShown = false; + + function isValidEmail(email) { + return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email); + } + + function showGDPR() { + if (gdprShown || !fc_ab_fct_cart.__gdpr_message) { + return; + } + + gdprShown = true; + + var gdprDiv = document.createElement('div'); + gdprDiv.id = 'fc_ab_cart_gdpr'; + gdprDiv.style.cssText = 'padding: 10px; margin: 10px 0; font-size: 13px; color: #666; background: #f9f9f9; border-radius: 4px;'; + gdprDiv.innerHTML = fc_ab_fct_cart.__gdpr_message; + + var section = document.getElementById('billing_personal_information_section'); + if (section) { + section.appendChild(gdprDiv); + } + + document.addEventListener('click', function (e) { + if (!e.target.closest('#fc_ab_opt_out, .fc-ab-cart-opt-out')) { + return; + } + + e.preventDefault(); + + var ajaxUrl = window.fluentcart_checkout_vars && window.fluentcart_checkout_vars.ajaxurl; + if (!ajaxUrl) { + return; + } + + var params = new URLSearchParams(); + params.append('action', 'fc_ab_fct_cart_skip'); + params.append('_nonce', fc_ab_fct_cart.nonce); + + var xhr = new XMLHttpRequest(); + xhr.open('POST', ajaxUrl, true); + xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); + xhr.onreadystatechange = function () { + if (xhr.readyState === 4 && xhr.status === 200) { + var el = document.getElementById('fc_ab_cart_gdpr'); + if (el) { + el.style.display = 'none'; + } + } + }; + xhr.send(params.toString()); + }); + } + + // Use capture phase since blur doesn't bubble + document.addEventListener('blur', function (e) { + if (e.target.id === 'billing_email' && e.target.value && isValidEmail(e.target.value.trim())) { + showGDPR(); + } + }, true); + + // Check if email is already filled (e.g., logged-in user) + setTimeout(function () { + var emailField = document.getElementById('billing_email'); + if (emailField && emailField.value && isValidEmail(emailField.value.trim())) { + showGDPR(); + } + }, 2000); +})(); diff --git a/wp-content/plugins/fluent-crm/app/Modules/AbandonCart/SettingsController.php b/wp-content/plugins/fluent-crm/app/Modules/AbandonCart/SettingsController.php new file mode 100644 index 0000000..d7e493b --- /dev/null +++ b/wp-content/plugins/fluent-crm/app/Modules/AbandonCart/SettingsController.php @@ -0,0 +1,80 @@ + $settings + ]; + + $availables = DriverManager::getAvailable(); + + // Collect provider-specific options from each available driver + foreach ($availables as $driver) { + $providerOptions = $driver->getProviderSettingsResponse(); + if ($providerOptions) { + $returnData[$driver->getProviderSlug() . 'Options'] = $providerOptions; + } + } + + $returnData['available_providers'] = array_map(function ($driver) { + return [ + 'slug' => $driver->getProviderSlug(), + 'label' => $driver->getProviderLabel(), + 'settings_fields' => $driver->getSettingsFields() + ]; + }, array_values($availables)); + + return $returnData; + } + + public function saveSettings(Request $request) + { + $prevSettings = AbCartHelper::getSettings(); + + $settings = (array) $request->get('settings', []); + + $settings = Arr::only($settings, array_keys($prevSettings)); + + do_action_ref_array('fluent_crm/abandon_cart_before_settings_save', [&$settings, $prevSettings]); + + if (is_wp_error($settings)) { + return $this->sendError([ + 'message' => $settings->get_error_message() + ], 422); + } + + $isEnabled = Arr::get($settings, 'enabled') === 'yes'; + + if ($isEnabled) { + AbandonCartMigrator::migrate(); + } + + /* + * Adding this to experimental settings so we don't have to do extra query + */ + $experiments = Helper::getExperimentalSettings(); + $experiments['abandoned_cart'] = $isEnabled ? 'yes' : 'no'; + update_option('_fluentcrm_experimental_settings', $experiments, 'yes'); + + update_option('_fc_ab_cart_settings', $settings); + + return [ + 'message' => __('Settings has been saved successfully', 'fluent-crm'), + 'reload' => $prevSettings['enabled'] !== $settings['enabled'], + 'settings' => $settings + ]; + } + +} diff --git a/wp-content/plugins/fluent-crm/app/Modules/MCP/AbilitiesRegistrar.php b/wp-content/plugins/fluent-crm/app/Modules/MCP/AbilitiesRegistrar.php new file mode 100644 index 0000000..f3d5a5e --- /dev/null +++ b/wp-content/plugins/fluent-crm/app/Modules/MCP/AbilitiesRegistrar.php @@ -0,0 +1,633 @@ + [ + 'label' => __('Get CRM Context', 'fluent-crm'), + 'description' => __('Discovery. Returns identity, permissions, stats, top tags/lists, available triggers/actions, all enums, custom fields schema, default sender. Call once per session.', 'fluent-crm'), + 'input_schema' => [ + 'type' => 'object', + 'properties' => new \stdClass(), + ], + 'execute_callback' => [ContextTools::class, 'getContext'], + 'permission_callback' => function () { + return PermissionManager::currentUserCan('fcrm_view_dashboard') + || PermissionManager::currentUserCan('fcrm_read_contacts'); + }, + 'annotations' => ['readonly' => true], + ], + + 'fluent-crm/list-contacts' => [ + 'label' => __('List Contacts', 'fluent-crm'), + 'description' => __('List/filter contacts with tags + lists inline. `search` matches name/email/custom field values. Filter fields are strictly validated — see get-crm-context.enums for valid status values.', 'fluent-crm'), + 'input_schema' => [ + 'type' => 'object', + 'properties' => [ + 'search' => ['type' => 'string', 'description' => 'Full-text across first_name, last_name, email, and custom field values.'], + 'tags' => ['type' => 'array', 'items' => ['type' => ['string', 'integer']], 'description' => 'Tag ids or slugs/titles. Mixed allowed.'], + 'lists' => ['type' => 'array', 'items' => ['type' => ['string', 'integer']], 'description' => 'List ids or slugs/titles. Mixed allowed.'], + 'statuses' => ['type' => 'array', 'items' => ['type' => 'string'], 'description' => 'See get-crm-context.enums.contact_statuses.'], + 'sms_statuses' => ['type' => 'array', 'items' => ['type' => 'string'], 'description' => 'See get-crm-context.enums.sms_statuses.'], + 'contact_type' => ['type' => 'string', 'enum' => ['lead', 'customer']], + 'created_after' => ['type' => 'string', 'description' => 'YYYY-MM-DD or full ISO 8601. Site timezone.'], + 'created_before' => ['type' => 'string', 'description' => 'YYYY-MM-DD or full ISO 8601. Site timezone.'], + 'sort_by' => ['type' => 'string', 'enum' => ['id', 'email', 'first_name', 'last_name', 'created_at', 'last_activity'], 'default' => 'id'], + 'sort_type' => ['type' => 'string', 'enum' => ['ASC', 'DESC'], 'default' => 'DESC'], + 'page' => ['type' => 'integer', 'default' => 1], + 'per_page' => ['type' => 'integer', 'default' => 15, 'description' => 'Max 100.'], + 'include_custom_fields' => ['type' => 'boolean', 'default' => false, 'description' => 'Inline each contact\'s custom field values (heavier).'], + ], + ], + 'execute_callback' => [ContactTools::class, 'listContacts'], + 'permission_callback' => function () { + return PermissionManager::currentUserCan('fcrm_read_contacts'); + }, + 'annotations' => ['readonly' => true], + ], + + 'fluent-crm/get-contact' => [ + 'label' => __('Get Contact', 'fluent-crm'), + 'description' => __('Full contact profile. Provide contact_id OR email. Default include: notes, email_history, automations. Optional: activity, purchase_history, support_tickets, ai_summary, info_widgets.', 'fluent-crm'), + 'input_schema' => [ + 'type' => 'object', + 'properties' => [ + 'contact_id' => ['type' => 'integer', 'description' => 'Provide this OR email.'], + 'email' => ['type' => 'string', 'description' => 'Provide this OR contact_id.'], + 'include' => [ + 'type' => 'array', + 'description' => 'Adds optional sections to the response. The default 3 are always included.', + 'items' => ['type' => 'string', 'enum' => ['notes', 'email_history', 'automations', 'activity', 'purchase_history', 'support_tickets', 'ai_summary', 'info_widgets']], + ], + 'generate_ai_summary' => ['type' => 'boolean', 'default' => false, 'description' => 'When true and ai_summary is in include, force a fresh AI call (costs provider tokens).'], + ], + ], + 'execute_callback' => [ContactTools::class, 'getContact'], + 'permission_callback' => function () { + return PermissionManager::currentUserCan('fcrm_read_contacts'); + }, + 'annotations' => ['readonly' => true], + ], + + 'fluent-crm/list-campaigns' => [ + 'label' => __('List Campaigns', 'fluent-crm'), + 'description' => __('List campaigns with stats inline. Excludes one-off email-to-contact records by default — flip include_one_offs for a unified "what was sent recently" view.', 'fluent-crm'), + 'input_schema' => [ + 'type' => 'object', + 'properties' => [ + 'search' => ['type' => 'string', 'description' => 'Matches campaign title.'], + 'statuses' => ['type' => 'array', 'items' => ['type' => 'string'], 'description' => 'See get-crm-context.enums.campaign_statuses.'], + 'sort_by' => ['type' => 'string', 'enum' => ['id', 'created_at', 'updated_at', 'scheduled_at'], 'default' => 'created_at'], + 'sort_type' => ['type' => 'string', 'enum' => ['ASC', 'DESC'], 'default' => 'DESC'], + 'include_stats' => ['type' => 'boolean', 'default' => true, 'description' => 'When true, computes per-campaign stats inline (one extra query per row — turn off for cheap title scans).'], + 'include_one_offs' => ['type' => 'boolean', 'default' => false, 'description' => 'Also include the per-recipient custom-email rows created by send-email-to-contact.'], + 'page' => ['type' => 'integer', 'default' => 1], + 'per_page' => ['type' => 'integer', 'default' => 15, 'description' => 'Max 100.'], + ], + ], + 'execute_callback' => [CampaignTools::class, 'listCampaigns'], + 'permission_callback' => function () { + return PermissionManager::currentUserCan('fcrm_read_emails'); + }, + 'annotations' => ['readonly' => true], + ], + + 'fluent-crm/get-campaign' => [ + 'label' => __('Get Campaign', 'fluent-crm'), + 'description' => __('Campaign details. Default include: stats. Optional: subjects (A/B), link_report, recipients_estimate.', 'fluent-crm'), + 'input_schema' => [ + 'type' => 'object', + 'properties' => [ + 'campaign_id' => ['type' => 'integer'], + 'include' => [ + 'type' => 'array', + 'items' => ['type' => 'string', 'enum' => ['stats', 'subjects', 'link_report', 'recipients_estimate']], + ], + ], + 'required' => ['campaign_id'], + ], + 'execute_callback' => [CampaignTools::class, 'getCampaign'], + 'permission_callback' => function () { + return PermissionManager::currentUserCan('fcrm_read_emails'); + }, + 'annotations' => ['readonly' => true], + ], + + 'fluent-crm/list-automations' => [ + 'label' => __('List Automations', 'fluent-crm'), + 'description' => __('List/filter automations (funnels) with subscriber counts inline.', 'fluent-crm'), + 'input_schema' => [ + 'type' => 'object', + 'properties' => [ + 'search' => ['type' => 'string'], + 'statuses' => ['type' => 'array', 'items' => ['type' => 'string', 'enum' => ['draft', 'published']]], + 'sort_by' => ['type' => 'string', 'enum' => ['id', 'title', 'status', 'updated_at'], 'default' => 'id'], + 'sort_type' => ['type' => 'string', 'enum' => ['ASC', 'DESC'], 'default' => 'DESC'], + 'page' => ['type' => 'integer', 'default' => 1], + 'per_page' => ['type' => 'integer', 'default' => 15], + ], + ], + 'execute_callback' => [FunnelTools::class, 'listAutomations'], + 'permission_callback' => function () { + return PermissionManager::currentUserCan('fcrm_read_funnels'); + }, + 'annotations' => ['readonly' => true], + ], + + 'fluent-crm/list-funnel-subscribers' => [ + 'label' => __('List Funnel Subscribers', 'fluent-crm'), + 'description' => __('List contacts enrolled in a funnel by status. Use to find candidates for update-contact-automation-status when you only know the funnel.', 'fluent-crm'), + 'input_schema' => [ + 'type' => 'object', + 'properties' => [ + 'funnel_id' => ['type' => 'integer'], + 'statuses' => [ + 'type' => 'array', + 'items' => ['type' => 'string', 'enum' => ['active', 'waiting', 'completed', 'cancelled', 'skipped']], + 'description' => 'Defaults to ["active"].', + ], + 'page' => ['type' => 'integer', 'default' => 1], + 'per_page' => ['type' => 'integer', 'default' => 15], + ], + 'required' => ['funnel_id'], + ], + 'execute_callback' => [FunnelTools::class, 'listFunnelSubscribers'], + 'permission_callback' => function () { + return PermissionManager::currentUserCan('fcrm_read_funnels'); + }, + 'annotations' => ['readonly' => true], + ], + + 'fluent-crm/get-automation' => [ + 'label' => __('Get Automation', 'fluent-crm'), + 'description' => __('Funnel details with sequences and per-step report by default. Embedded email bodies in send_custom_email steps are stripped unless include_bodies=true (saves tokens).', 'fluent-crm'), + 'input_schema' => [ + 'type' => 'object', + 'properties' => [ + 'funnel_id' => ['type' => 'integer'], + 'include' => [ + 'type' => 'array', + 'description' => 'Defaults to ["sequences","report"]. Pass [] for metadata only.', + 'items' => ['type' => 'string', 'enum' => ['sequences', 'report']], + ], + 'include_bodies' => ['type' => 'boolean', 'default' => false, 'description' => 'Return full email bodies inside send_custom_email step settings. Off by default — large funnels can blow agent context.'], + ], + 'required' => ['funnel_id'], + ], + 'execute_callback' => [FunnelTools::class, 'getAutomation'], + 'permission_callback' => function () { + return PermissionManager::currentUserCan('fcrm_read_funnels'); + }, + 'annotations' => ['readonly' => true], + ], + + // ----------------------------------------------------------------- + // Phase 3 — write tools + // ----------------------------------------------------------------- + + 'fluent-crm/upsert-contact' => [ + 'label' => __('Create or Update Contact', 'fluent-crm'), + 'description' => __('Create or update a contact by id or email. status changes fire native hooks. Source stamps "mcp" only on create — preserved on update. new_email renames in place.', 'fluent-crm'), + 'input_schema' => [ + 'type' => 'object', + 'properties' => [ + 'contact_id' => ['type' => 'integer', 'description' => 'Provide this OR email for lookup.'], + 'email' => ['type' => 'string', 'description' => 'Provide this OR contact_id for lookup. Required for create.'], + 'new_email' => ['type' => 'string', 'description' => 'Renames an existing contact in place. Errors if another contact already uses this email.'], + 'first_name' => ['type' => 'string'], + 'last_name' => ['type' => 'string'], + 'prefix' => ['type' => 'string'], + 'phone' => ['type' => 'string'], + 'status' => ['type' => 'string', 'description' => 'See get-crm-context.enums.contact_statuses.'], + 'contact_type' => ['type' => 'string', 'enum' => ['lead', 'customer']], + 'address' => ['type' => 'object', 'description' => 'Object: {line_1, line_2, city, state, postal_code, country (ISO-2)}. Empty fields are ignored.'], + 'date_of_birth' => ['type' => 'string', 'description' => 'YYYY-MM-DD.'], + 'timezone' => ['type' => 'string'], + 'source' => ['type' => 'string', 'description' => 'Defaults to "mcp" on create. Omit on updates to preserve existing source.'], + 'custom_fields' => ['type' => 'object', 'description' => 'Map of custom field slug → value. See get-crm-context.custom_fields_schema.'], + 'add_tags' => ['type' => 'array', 'items' => ['type' => ['string', 'integer']]], + 'remove_tags' => ['type' => 'array', 'items' => ['type' => ['string', 'integer']]], + 'add_lists' => ['type' => 'array', 'items' => ['type' => ['string', 'integer']]], + 'remove_lists' => ['type' => 'array', 'items' => ['type' => ['string', 'integer']]], + 'auto_create_tags' => ['type' => 'boolean', 'default' => false, 'description' => 'Re-checks fcrm_manage_contact_cats. Off by default for safety.'], + 'auto_create_lists' => ['type' => 'boolean', 'default' => false], + 'double_optin' => ['type' => 'boolean', 'default' => false, 'description' => 'When status=pending, send opt-in email. No-op for other statuses.'], + 'if_exists' => ['type' => 'string', 'enum' => ['merge', 'skip', 'error'], 'default' => 'merge', 'description' => 'merge: update existing fields. skip: leave row untouched. error: return contact_exists.'], + 'status_change_reason' => ['type' => 'string', 'description' => 'When provided AND status changes, auto-creates an audit note ("Status changed via MCP").'], + ], + ], + 'execute_callback' => [ContactTools::class, 'upsertContact'], + 'permission_callback' => function () { + return PermissionManager::currentUserCan('fcrm_manage_contacts'); + }, + ], + + 'fluent-crm/bulk-upsert-contacts' => [ + 'label' => __('Bulk Create or Update Contacts', 'fluent-crm'), + 'description' => __('Batch create/update up to 500 contacts. Returns per-row {created, updated, skipped, invalid}. auto_create defaults to true here (matches CSV-import expectations) — opposite of upsert-contact.', 'fluent-crm'), + 'input_schema' => [ + 'type' => 'object', + 'properties' => [ + 'contacts' => [ + 'type' => 'array', + 'description' => 'Array of contact objects. Each object accepts the same fields as upsert-contact but no add_tags/remove_tags — pass `tags` and `lists` directly.', + 'items' => ['type' => 'object'], + ], + 'if_exists' => ['type' => 'string', 'enum' => ['merge', 'skip', 'error'], 'default' => 'merge'], + 'double_optin' => ['type' => 'boolean', 'default' => false], + 'auto_create_tags' => ['type' => 'boolean', 'default' => true, 'description' => 'Default true here (bulk-import context). Re-checks fcrm_manage_contact_cats.'], + 'auto_create_lists' => ['type' => 'boolean', 'default' => true], + ], + 'required' => ['contacts'], + ], + 'execute_callback' => [ContactTools::class, 'bulkUpsertContacts'], + 'permission_callback' => function () { + return PermissionManager::currentUserCan('fcrm_manage_contacts'); + }, + 'annotations' => ['bulk' => true], + ], + + 'fluent-crm/delete-contact' => [ + 'label' => __('Delete Contact', 'fluent-crm'), + 'description' => __('Hard-delete a contact. Optional delete_emails wipes the email log too. Cannot be undone.', 'fluent-crm'), + 'input_schema' => [ + 'type' => 'object', + 'properties' => [ + 'contact_id' => ['type' => 'integer'], + 'email' => ['type' => 'string'], + 'delete_emails' => ['type' => 'boolean', 'default' => true], + ], + ], + 'execute_callback' => [ContactTools::class, 'deleteContact'], + 'permission_callback' => function () { + return PermissionManager::currentUserCan('fcrm_manage_contacts_delete'); + }, + 'annotations' => ['destructive' => true], + ], + + 'fluent-crm/apply-segments-to-contacts' => [ + 'label' => __('Apply Tags/Lists Across Contacts', 'fluent-crm'), + 'description' => __('Add/remove tags and lists across many contacts. Provide contact_ids OR filter, not both. Always dry_run first for filter-based applies. Response includes applied_contact_ids for precise reversal. Cap 5000.', 'fluent-crm'), + 'input_schema' => [ + 'type' => 'object', + 'properties' => [ + 'contact_ids' => ['type' => 'array', 'items' => ['type' => 'integer'], 'description' => 'Explicit ids. Use OR filter, not both.'], + 'filter' => ['type' => 'object', 'description' => 'Universal filter — {tags, lists, statuses, contact_type, search, created_after, created_before}. See get-crm-context.guidelines.'], + 'add_tags' => ['type' => 'array', 'items' => ['type' => ['string', 'integer']]], + 'remove_tags' => ['type' => 'array', 'items' => ['type' => ['string', 'integer']]], + 'add_lists' => ['type' => 'array', 'items' => ['type' => ['string', 'integer']]], + 'remove_lists' => ['type' => 'array', 'items' => ['type' => ['string', 'integer']]], + 'auto_create_tags' => ['type' => 'boolean', 'default' => false, 'description' => 'Re-checks fcrm_manage_contact_cats. Suppressed during dry_run so previews never leave orphans behind.'], + 'auto_create_lists' => ['type' => 'boolean', 'default' => false], + 'dry_run' => ['type' => 'boolean', 'default' => false, 'description' => 'Preview matched count, batches_required, and tags/lists_would_create without applying. Bypasses the cap (you see real matched_contacts even if > 5000).'], + ], + ], + 'execute_callback' => [ContactTools::class, 'applySegmentsToContacts'], + 'permission_callback' => function () { + return PermissionManager::currentUserCan('fcrm_manage_contacts'); + }, + 'annotations' => ['bulk' => true], + ], + + 'fluent-crm/manage-tag' => [ + 'label' => __('Manage Tag', 'fluent-crm'), + 'description' => __('Create, update, delete, or merge tags. delete + merge are destructive (re-pivot or detach subscribers).', 'fluent-crm'), + 'input_schema' => [ + 'type' => 'object', + 'properties' => [ + 'action' => ['type' => 'string', 'enum' => ['create', 'update', 'delete', 'merge']], + 'tag_id' => ['type' => 'integer', 'description' => 'Required for update/delete.'], + 'title' => ['type' => 'string'], + 'slug' => ['type' => 'string'], + 'description' => ['type' => 'string'], + 'force' => ['type' => 'boolean', 'default' => false, 'description' => 'delete only — allow deletion when subscribers are still attached.'], + 'from_tag_ids' => ['type' => 'array', 'items' => ['type' => 'integer'], 'description' => 'merge only — source tags whose subscribers move to to_tag_id and which then get deleted.'], + 'to_tag_id' => ['type' => 'integer', 'description' => 'merge only — destination tag.'], + ], + 'required' => ['action'], + ], + 'execute_callback' => [SegmentTools::class, 'manageTag'], + 'permission_callback' => function () { + return PermissionManager::currentUserCan('fcrm_manage_contact_cats') + || PermissionManager::currentUserCan('fcrm_manage_contact_cats_delete'); + }, + 'annotations' => ['destructive' => true], + ], + + 'fluent-crm/manage-list' => [ + 'label' => __('Manage List', 'fluent-crm'), + 'description' => __('Create, update, delete, or merge lists. delete + merge are destructive (re-pivot or detach subscribers).', 'fluent-crm'), + 'input_schema' => [ + 'type' => 'object', + 'properties' => [ + 'action' => ['type' => 'string', 'enum' => ['create', 'update', 'delete', 'merge']], + 'list_id' => ['type' => 'integer'], + 'title' => ['type' => 'string'], + 'slug' => ['type' => 'string'], + 'description' => ['type' => 'string'], + 'force' => ['type' => 'boolean', 'default' => false], + 'from_list_ids' => ['type' => 'array', 'items' => ['type' => 'integer']], + 'to_list_id' => ['type' => 'integer'], + ], + 'required' => ['action'], + ], + 'execute_callback' => [SegmentTools::class, 'manageList'], + 'permission_callback' => function () { + return PermissionManager::currentUserCan('fcrm_manage_contact_cats') + || PermissionManager::currentUserCan('fcrm_manage_contact_cats_delete'); + }, + 'annotations' => ['destructive' => true], + ], + + 'fluent-crm/delete-contact-note' => [ + 'label' => __('Delete Contact Note', 'fluent-crm'), + 'description' => __('Delete a single subscriber note by id. Find the note id via get-contact include=["notes"]. Other notes and email history are untouched.', 'fluent-crm'), + 'input_schema' => [ + 'type' => 'object', + 'properties' => [ + 'note_id' => ['type' => 'integer'], + ], + 'required' => ['note_id'], + ], + 'execute_callback' => [ContactTools::class, 'deleteContactNote'], + 'permission_callback' => function () { + return PermissionManager::currentUserCan('fcrm_manage_contacts'); + }, + 'annotations' => ['destructive' => true], + ], + + 'fluent-crm/add-contact-note' => [ + 'label' => __('Add Contact Note', 'fluent-crm'), + 'description' => __('Add a note to a contact. Provide contact_id OR email plus title + description. Types: note, call, email, meeting, quote. Description supports HTML.', 'fluent-crm'), + 'input_schema' => [ + 'type' => 'object', + 'properties' => [ + 'contact_id' => ['type' => 'integer', 'description' => 'Provide this OR email.'], + 'email' => ['type' => 'string', 'description' => 'Provide this OR contact_id.'], + 'type' => ['type' => 'string', 'enum' => ['note', 'call', 'email', 'meeting', 'quote'], 'default' => 'note'], + 'title' => ['type' => 'string', 'description' => 'Max 192 chars.'], + 'description' => ['type' => 'string', 'description' => 'HTML or plain. SmartCodes resolve.'], + 'created_at' => ['type' => 'string', 'description' => 'ISO 8601, defaults to now (site timezone).'], + ], + 'required' => ['title', 'description'], + ], + 'execute_callback' => [ContactTools::class, 'addContactNote'], + 'permission_callback' => function () { + return PermissionManager::currentUserCan('fcrm_manage_contacts'); + }, + ], + + 'fluent-crm/send-test-email' => [ + 'label' => __('Send Test Email', 'fluent-crm'), + 'description' => __('Render and send a test copy of an email — does not enroll the recipient, does not create a campaign record, does not log to email_history. Subject is prefixed with "TEST:".', 'fluent-crm'), + 'input_schema' => [ + 'type' => 'object', + 'properties' => [ + 'to_email' => ['type' => 'string', 'description' => 'Where to send the test. Defaults to the current WP user\'s email.'], + 'campaign_id' => ['type' => 'integer', 'description' => 'Send a test copy of this saved campaign\'s body / subject / settings.'], + 'subject' => ['type' => 'string', 'description' => 'Override or supply a subject when not using campaign_id.'], + 'body' => ['type' => 'string', 'description' => 'Override or supply a body when not using campaign_id.'], + 'pre_header' => ['type' => 'string'], + 'design_template' => [ + 'type' => 'string', + 'enum' => array_keys(ContextTools::allowedDesignTemplates()), + ], + 'against_contact_id' => ['type' => 'integer', 'description' => 'Resolve smartcodes against this contact. Defaults to a contact matching to_email, then any subscribed contact.'], + 'against_contact_email' => ['type' => 'string'], + ], + ], + 'execute_callback' => [EmailTools::class, 'sendTestEmail'], + 'permission_callback' => function () { + return PermissionManager::currentUserCan('fcrm_manage_emails'); + }, + ], + + 'fluent-crm/send-email-to-contact' => [ + 'label' => __('Send Email to Contact', 'fluent-crm'), + 'description' => __('Send a one-off email to a subscribed/transactional contact. Routes through normal queue + bounce + FluentSMTP. SmartCodes resolve. Persists a custom_email_campaign record (hidden from list-campaigns by default).', 'fluent-crm'), + 'input_schema' => [ + 'type' => 'object', + 'properties' => [ + 'contact_id' => ['type' => 'integer', 'description' => 'Provide this OR email.'], + 'email' => ['type' => 'string', 'description' => 'Provide this OR contact_id.'], + 'subject' => ['type' => 'string'], + 'body' => ['type' => 'string', 'description' => 'HTML or plain. SmartCodes resolve.'], + 'pre_header' => ['type' => 'string'], + 'title' => ['type' => 'string', 'description' => 'Internal log title; defaults to "MCP one-off to {email}".'], + 'design_template' => [ + 'type' => 'string', + 'enum' => array_keys(ContextTools::allowedDesignTemplates()), + 'default' => 'classic', + ], + 'from_name' => ['type' => 'string', 'description' => 'Defaults to site sender (get-crm-context.default_sender.from_name).'], + 'from_email' => ['type' => 'string', 'description' => 'Defaults to site sender. Must be a configured/verified address.'], + 'reply_to_name' => ['type' => 'string'], + 'reply_to_email' => ['type' => 'string'], + 'is_transactional' => ['type' => 'string', 'enum' => ['yes', 'no'], 'default' => 'no', 'description' => 'When "yes", also auto-disables the global marketing footer for transactional-mail compliance.'], + 'disable_footer' => ['type' => 'string', 'enum' => ['yes', 'no'], 'description' => 'Explicit override of the auto-derived footer behavior.'], + 'click_tracker' => ['type' => 'string', 'enum' => ['yes', 'no', 'anonymous']], + 'open_tracker' => ['type' => 'string', 'enum' => ['yes', 'no', 'anonymous']], + 'utm' => ['type' => 'object', 'description' => 'Optional {status:0|1, source, medium, campaign, term, content}. status defaults to 0.'], + 'settings' => ['type' => 'object', 'description' => 'Free-form passthrough merged into campaign.settings (template_config, footer_settings). Caller keys override our defaults.'], + ], + 'required' => ['subject', 'body'], + ], + 'execute_callback' => [EmailTools::class, 'sendEmailToContact'], + 'permission_callback' => function () { + return PermissionManager::currentUserCan('fcrm_manage_emails'); + }, + ], + + 'fluent-crm/upsert-campaign' => [ + 'label' => __('Create or Update Campaign', 'fluent-crm'), + 'description' => __('Create or update a draft campaign. Never sends — use change-campaign-status to schedule. recipients persists tags + lists ONLY (no statuses/contact_type — apply a temp tag first). Returns estimated_recipients + warnings inline.', 'fluent-crm'), + 'input_schema' => [ + 'type' => 'object', + 'properties' => [ + 'campaign_id' => ['type' => 'integer'], + 'title' => ['type' => 'string'], + 'email_subject' => ['type' => 'string'], + 'email_pre_header' => ['type' => 'string'], + 'email_body' => ['type' => 'string'], + 'design_template' => [ + 'type' => 'string', + 'enum' => array_keys(ContextTools::allowedDesignTemplates()), + 'default' => 'classic', + ], + 'settings' => [ + 'type' => 'object', + 'description' => 'Merged into campaign.settings. Shape: {mailer_settings:{from_name,from_email,reply_to_name,reply_to_email,is_custom:yes|no}, is_transactional:yes|no, click_tracker:yes|no|anonymous, open_tracker:yes|no|anonymous, footer_settings:{disable_footer:yes|no}, template_config}.', + ], + 'recipients' => [ + 'type' => 'object', + 'description' => 'Recipient segment. Persists {tags:[id|slug|title], lists:[id|slug|title]} only. Pass other keys (statuses, contact_type, advanced_filters) and the call hard-errors with the temp-tag workaround.', + ], + 'exclude_recipients' => [ + 'type' => 'object', + 'description' => 'Same shape + restriction as recipients.', + ], + 'subjects' => [ + 'type' => 'array', + 'description' => 'A/B subjects. Each: {value: string [, key: string]}. Pass an array with 2+ items to enable A/B; the regular email_subject still acts as the primary line. Optional `key` is a stable identifier used internally — auto-generated if omitted.', + 'items' => [ + 'type' => 'object', + 'properties' => [ + 'value' => ['type' => 'string'], + 'key' => ['type' => 'string'], + ], + ], + ], + 'label_ids' => ['type' => 'array', 'items' => ['type' => 'integer']], + 'utm' => [ + 'type' => 'object', + 'description' => 'Optional. {status: 0|1 to toggle, source, medium, campaign, term, content}. status defaults to 0 (off).', + ], + 'if_exists' => [ + 'type' => 'string', + 'enum' => ['auto_suffix', 'error'], + 'default' => 'auto_suffix', + 'description' => 'On title conflict during create: auto_suffix (Title (2), Title (3)) or hard error.', + ], + ], + ], + 'execute_callback' => [CampaignTools::class, 'upsertCampaign'], + 'permission_callback' => function () { + return PermissionManager::currentUserCan('fcrm_manage_emails'); + }, + ], + + 'fluent-crm/change-campaign-status' => [ + 'label' => __('Change Campaign Status', 'fluent-crm'), + 'description' => __('State transition. schedule + delete are destructive. pause/resume only valid mid-send (working↔paused). unschedule reverts to draft and clears scheduled_at.', 'fluent-crm'), + 'input_schema' => [ + 'type' => 'object', + 'properties' => [ + 'campaign_id' => ['type' => 'integer'], + 'action' => ['type' => 'string', 'enum' => ['schedule', 'unschedule', 'pause', 'resume', 'duplicate', 'delete']], + 'scheduled_at' => ['type' => 'string', 'description' => 'Required when action=schedule and sending_type≠instant. Site timezone (see get-crm-context.site.timezone). Must be in the future.'], + 'schedule_range' => ['type' => 'array', 'items' => ['type' => 'string'], 'description' => 'Required when sending_type=range_schedule. [startISO, endISO].'], + 'sending_type' => ['type' => 'string', 'enum' => ['instant', 'schedule', 'range_schedule'], 'description' => 'Defaults to "schedule" if scheduled_at is set, else "instant".'], + 'new_title' => ['type' => 'string', 'description' => 'duplicate only — overrides the auto "[Duplicate] X" title.'], + ], + 'required' => ['campaign_id', 'action'], + ], + 'execute_callback' => [CampaignTools::class, 'changeCampaignStatus'], + 'permission_callback' => function () { + return PermissionManager::currentUserCan('fcrm_manage_emails'); + }, + 'annotations' => ['destructive' => true], + ], + + 'fluent-crm/update-contact-automation-status' => [ + 'label' => __('Update Contact Automation Status', 'fluent-crm'), + 'description' => __('Resume, cancel, or advance_now a contact in a funnel. cancel is destructive (reversible in UI but halts processing). advance_now requires advance_to_sequence_id and skips intermediate benchmarks.', 'fluent-crm'), + 'input_schema' => [ + 'type' => 'object', + 'properties' => [ + 'funnel_id' => ['type' => 'integer', 'description' => 'Use list-funnel-subscribers to find candidates.'], + 'contact_id' => ['type' => 'integer', 'description' => 'Provide this OR email.'], + 'email' => ['type' => 'string', 'description' => 'Provide this OR contact_id.'], + 'action' => ['type' => 'string', 'enum' => ['resume', 'cancel', 'advance_now']], + 'advance_to_sequence_id' => ['type' => 'integer', 'description' => 'Required when action=advance_now. The sequence id to jump to (find via get-automation include=["sequences"]).'], + ], + 'required' => ['funnel_id', 'action'], + ], + 'execute_callback' => [FunnelTools::class, 'updateContactAutomationStatus'], + 'permission_callback' => function () { + return PermissionManager::currentUserCan('fcrm_write_funnels'); + }, + ], + ]; + } + + public static function register() + { + foreach (self::getDefinitions() as $name => $definition) { + $args = [ + 'label' => $definition['label'], + 'description' => $definition['description'], + 'category' => 'fluent-crm', + 'execute_callback' => self::wrapExecuteCallback($name, $definition['execute_callback']), + 'permission_callback' => $definition['permission_callback'], + 'meta' => [ + 'show_in_rest' => true, + 'mcp' => [ + 'public' => true, + ], + ], + ]; + + if (!empty($definition['input_schema'])) { + $args['input_schema'] = $definition['input_schema']; + } + + if (!empty($definition['annotations'])) { + $args['meta']['annotations'] = $definition['annotations']; + } + + wp_register_ability($name, $args); + } + } + + /** + * Wraps every tool's execute callback in a try/catch that converts + * unhandled exceptions (SQL errors, type errors, anything that escapes + * a tool's own validation) into a structured WP_Error with the actual + * exception message instead of the adapter's generic "Tool execution + * failed" surface. Without this, the agent has no signal about what + * went wrong, which leads to retries against tools that silently + * succeeded — see fluentcrm-mcp-review.md bug #1. + */ + private static function wrapExecuteCallback($toolName, $callback) + { + return function ($params) use ($toolName, $callback) { + try { + return call_user_func($callback, $params); + } catch (\Throwable $e) { + /** + * Allows logging or alerting on unhandled tool exceptions + * before the structured error is returned to the agent. + * + * @since 2.10.0 + * + * @param \Throwable $e The exception. + * @param string $toolName Fully-qualified ability name. + * @param mixed $params The tool's input parameters. + */ + do_action('fluent_crm/mcp_tool_exception', $e, $toolName, $params); + + $details = [ + 'tool' => $toolName, + 'exception' => get_class($e), + ]; + if (defined('WP_DEBUG') && WP_DEBUG) { + $details['file'] = $e->getFile() . ':' . $e->getLine(); + $details['trace'] = array_slice(explode("\n", $e->getTraceAsString()), 0, 5); + } + return new \WP_Error('failed', $e->getMessage(), $details); + } + }; + } +} diff --git a/wp-content/plugins/fluent-crm/app/Modules/MCP/Helpers/MCPHelper.php b/wp-content/plugins/fluent-crm/app/Modules/MCP/Helpers/MCPHelper.php new file mode 100644 index 0000000..1a4e8a4 --- /dev/null +++ b/wp-content/plugins/fluent-crm/app/Modules/MCP/Helpers/MCPHelper.php @@ -0,0 +1,1144 @@ + $contactId]); + } + return $subscriber; + } + + if ($email) { + $subscriber = Subscriber::where('email', $email)->first(); + if (!$subscriber) { + return self::error('not_found', __('Contact not found', 'fluent-crm'), ['email' => $email]); + } + return $subscriber; + } + + return self::error('invalid_param', __('Provide contact_id or email', 'fluent-crm')); + } + + /** + * Resolve an array of tag identifiers (ids or titles/slugs) to integer IDs. + * Optionally creates missing tags when $autoCreate is true (caller MUST + * have re-checked `fcrm_manage_contact_cats` before passing true). + * + * @param array $items + * @param bool $autoCreate + * @return array{ids: int[], created: array} + */ + public static function resolveTagIds($items, $autoCreate = false) + { + $ids = []; + $created = []; + + foreach ((array) $items as $item) { + if ($item === '' || $item === null) { + continue; + } + + if (is_numeric($item)) { + $tag = Tag::find((int) $item); + if ($tag) { + $ids[] = (int) $tag->id; + } + continue; + } + + $value = sanitize_text_field((string) $item); + $tag = Tag::where('title', $value)->orWhere('slug', sanitize_title($value))->first(); + + if ($tag) { + $ids[] = (int) $tag->id; + continue; + } + + if ($autoCreate) { + $tag = Tag::create([ + 'title' => $value, + 'slug' => sanitize_title($value), + ]); + $ids[] = (int) $tag->id; + $created[] = ['id' => (int) $tag->id, 'title' => $tag->title]; + } + } + + return ['ids' => array_values(array_unique($ids)), 'created' => $created]; + } + + /** + * Same as resolveTagIds() but for lists. + * + * @param array $items + * @param bool $autoCreate + * @return array{ids: int[], created: array} + */ + public static function resolveListIds($items, $autoCreate = false) + { + $ids = []; + $created = []; + + foreach ((array) $items as $item) { + if ($item === '' || $item === null) { + continue; + } + + if (is_numeric($item)) { + $list = Lists::find((int) $item); + if ($list) { + $ids[] = (int) $list->id; + } + continue; + } + + $value = sanitize_text_field((string) $item); + $list = Lists::where('title', $value)->orWhere('slug', sanitize_title($value))->first(); + + if ($list) { + $ids[] = (int) $list->id; + continue; + } + + if ($autoCreate) { + $list = Lists::create([ + 'title' => $value, + 'slug' => sanitize_title($value), + ]); + $ids[] = (int) $list->id; + $created[] = ['id' => (int) $list->id, 'title' => $list->title]; + } + } + + return ['ids' => array_values(array_unique($ids)), 'created' => $created]; + } + + // --------------------------------------------------------------------- + // Formatting + // --------------------------------------------------------------------- + + /** + * Build the rich contact record consumed by get-contact / upsert-contact. + * + * @param Subscriber $subscriber + * @param array $opts { + * @type array $include One or more of: notes, email_history, automations, + * activity, purchase_history, support_tickets, + * ai_summary, info_widgets. + * } + * @return array + */ + public static function formatContactForMCP($subscriber, $opts = []) + { + $include = (array) ($opts['include'] ?? []); + + $address = [ + 'line_1' => $subscriber->address_line_1, + 'line_2' => $subscriber->address_line_2, + 'city' => $subscriber->city, + 'state' => $subscriber->state, + 'postal_code' => $subscriber->postal_code, + 'country' => $subscriber->country, + ]; + + $data = [ + 'id' => (int) $subscriber->id, + 'email' => $subscriber->email, + 'first_name' => $subscriber->first_name, + 'last_name' => $subscriber->last_name, + 'full_name' => trim((string) $subscriber->full_name), + 'prefix' => $subscriber->prefix, + 'status' => $subscriber->status, + 'contact_type' => $subscriber->contact_type, + 'phone' => $subscriber->phone, + 'address' => array_filter($address, function ($v) { return $v !== null && $v !== ''; }), + 'date_of_birth' => $subscriber->date_of_birth, + 'timezone' => $subscriber->timezone, + 'source' => $subscriber->source, + 'avatar' => $subscriber->avatar, + 'life_time_value' => $subscriber->life_time_value, + 'total_points' => isset($subscriber->total_points) ? (int) $subscriber->total_points : 0, + 'last_activity' => self::toIso8601($subscriber->last_activity), + 'created_at' => self::toIso8601($subscriber->created_at), + ]; + + // Eager-loaded relations: tags, lists. + $data['tags'] = self::formatTagList($subscriber->tags ?? []); + $data['lists'] = self::formatListList($subscriber->lists ?? []); + + // Custom fields are inlined for visibility. + $data['custom_fields'] = (array) $subscriber->custom_fields(); + + if ($subscriber->user_id) { + $data['wp_user'] = [ + 'id' => (int) $subscriber->user_id, + 'edit_url' => admin_url('user-edit.php?user_id=' . (int) $subscriber->user_id), + ]; + $user = get_user_by('ID', $subscriber->user_id); + if ($user) { + $data['wp_user']['roles'] = (array) $user->roles; + } + } else { + $data['wp_user'] = null; + } + + // Optional includes. + if (in_array('notes', $include, true)) { + $data['notes'] = self::formatNotesFor($subscriber); + } + if (in_array('email_history', $include, true)) { + $data['email_history'] = self::formatEmailHistoryFor($subscriber, (int) ($opts['email_history_limit'] ?? 10)); + } + if (in_array('automations', $include, true)) { + $data['automations'] = self::formatAutomationsFor($subscriber); + } + + return $data; + } + + public static function formatContactSummary($subscriber) + { + return [ + 'id' => (int) $subscriber->id, + 'email' => $subscriber->email, + 'first_name' => $subscriber->first_name, + 'last_name' => $subscriber->last_name, + 'full_name' => trim((string) $subscriber->full_name), + 'status' => $subscriber->status, + 'contact_type' => $subscriber->contact_type, + 'tags' => self::formatTagList($subscriber->tags ?? []), + 'lists' => self::formatListList($subscriber->lists ?? []), + 'country' => $subscriber->country, + 'city' => $subscriber->city, + 'source' => $subscriber->source, + 'last_activity' => self::toIso8601($subscriber->last_activity), + 'created_at' => self::toIso8601($subscriber->created_at), + ]; + } + + public static function formatContactList($paginated, $includeCustomFields = false) + { + $items = []; + foreach ($paginated->items() as $subscriber) { + $item = self::formatContactSummary($subscriber); + if ($includeCustomFields) { + $item['custom_fields'] = (array) $subscriber->custom_fields(); + } + $items[] = $item; + } + + return [ + 'items' => $items, + 'total' => (int) $paginated->total(), + 'page' => (int) $paginated->currentPage(), + 'per_page' => (int) $paginated->perPage(), + 'pages' => (int) $paginated->lastPage(), + ]; + } + + public static function formatTagList($tags) + { + $out = []; + foreach ($tags as $tag) { + $out[] = [ + 'id' => (int) $tag->id, + 'title' => $tag->title, + 'slug' => $tag->slug, + ]; + } + return $out; + } + + public static function formatListList($lists) + { + $out = []; + foreach ($lists as $list) { + $out[] = [ + 'id' => (int) $list->id, + 'title' => $list->title, + 'slug' => $list->slug, + ]; + } + return $out; + } + + public static function formatNoteForMCP($note) + { + $addedBy = null; + $createdBy = method_exists($note, 'createdBy') ? $note->createdBy() : null; + if (is_array($createdBy)) { + $addedBy = [ + 'id' => (int) $createdBy['ID'], + 'name' => $createdBy['display_name'], + ]; + } + + return [ + 'id' => (int) $note->id, + 'subscriber_id' => (int) $note->subscriber_id, + 'type' => $note->type, + 'title' => $note->title, + 'description_text' => self::htmlToText((string) $note->description), + 'description_html' => (string) $note->description, + 'added_by' => $addedBy, + 'created_at' => self::toIso8601($note->created_at), + ]; + } + + /** + * Return up to $limit recent notes for a subscriber. + */ + public static function formatNotesFor($subscriber, $limit = 50) + { + // SubscriberNote already excludes _company_note_ / _system_log_ via a + // global scope (see Models\SubscriberNote::boot()). + $notes = SubscriberNote::where('subscriber_id', $subscriber->id) + ->orderBy('id', 'DESC') + ->limit($limit) + ->get(); + + $formatted = []; + foreach ($notes as $note) { + $formatted[] = self::formatNoteForMCP($note); + } + return $formatted; + } + + /** + * Recent campaign emails sent to / on behalf of a subscriber, paginated to + * a small set so heavy installs don't drown the response (per MCP_PLAN + * § 10.7). + */ + public static function formatEmailHistoryFor($subscriber, $limit = 10) + { + $emails = $subscriber->campaignEmails() + ->orderBy('id', 'DESC') + ->limit(max(1, (int) $limit)) + ->get(); + + $out = []; + foreach ($emails as $email) { + $out[] = [ + 'id' => (int) $email->id, + 'subject' => $email->email_subject, + 'campaign_id' => $email->campaign_id ? (int) $email->campaign_id : null, + 'campaign_title' => $email->campaign ? $email->campaign->title : null, + 'status' => $email->status, + 'is_open' => !empty($email->is_open), + 'is_clicked' => isset($email->click_counter) ? ((int) $email->click_counter > 0) : false, + 'sent_at' => self::toIso8601($email->updated_at), + ]; + } + return $out; + } + + public static function formatAutomationsFor($subscriber) + { + $automations = $subscriber->funnel_subscribers()->with('funnel')->get(); + + $out = []; + foreach ($automations as $row) { + if (!$row->funnel) { + continue; + } + $out[] = [ + 'funnel_id' => (int) $row->funnel_id, + 'title' => $row->funnel->title, + 'status' => $row->status, + 'last_executed_at' => self::toIso8601($row->last_executed_time), + 'next_scheduled_at' => self::toIso8601($row->next_execution_time), + 'next_sequence_id' => $row->next_sequence ? (int) $row->next_sequence : null, + 'enrolled_at' => self::toIso8601($row->created_at), + ]; + } + return $out; + } + + public static function formatCampaignSummary($campaign, $includeStats = true) + { + // Only fill sent_at when the campaign has actually shipped — drafts + // and pre-send states leave it null (review #30). updated_at is + // not a reliable proxy: any settings tweak bumps it. + $sentStatuses = ['archived', 'working', 'paused']; + $sentAt = in_array($campaign->status, $sentStatuses, true) + ? self::toIso8601($campaign->updated_at) + : null; + + $item = [ + 'id' => (int) $campaign->id, + 'title' => $campaign->title, + 'email_subject' => $campaign->email_subject, + 'status' => $campaign->status, + 'design_template' => $campaign->design_template, + 'scheduled_at' => self::toIso8601($campaign->scheduled_at), + 'sent_at' => $sentAt, + 'created_at' => self::toIso8601($campaign->created_at), + ]; + + if ($includeStats) { + $item['stats'] = self::campaignStatsCompact($campaign); + } + + return $item; + } + + /** + * Compact stats for a single campaign. Mirrors the per-campaign columns + * the admin list does (sent/views/clicks via fc_campaign_emails) and + * pulls unsubscribers from fc_campaign_url_metrics where type='unsubscribe' + * — there is no is_unsubscribed column on fc_campaign_emails. + * + * Anonymous-tracking aware: when the campaign is configured for + * anonymous click/open tracking, the per-contact columns will read 0 + * even when there's real engagement (the data goes to campaign meta + * instead). We surface tracking_mode + an aggregate fallback so the + * agent doesn't mis-diagnose anonymous campaigns as having zero + * engagement (round-4 review P1 #5). + */ + public static function campaignStatsCompact($campaign) + { + $campaignId = (int) $campaign->id; + $total = (int) $campaign->recipients_count; + + $clickStatus = method_exists($campaign, 'getClickTrackingStatus') ? $campaign->getClickTrackingStatus(false) : 'yes'; + $openStatus = method_exists($campaign, 'getOpenTrackingStatus') ? $campaign->getOpenTrackingStatus(false) : 'yes'; + + // Single GROUP-BY-style aggregate over the email table. + $row = fluentCrmDb()->table('fc_campaign_emails') + ->where('campaign_id', $campaignId) + ->selectRaw("SUM(CASE WHEN status = 'sent' THEN 1 ELSE 0 END) as sent") + ->selectRaw("SUM(CASE WHEN is_open = 1 THEN 1 ELSE 0 END) as views") + ->selectRaw("SUM(CASE WHEN click_counter IS NOT NULL THEN 1 ELSE 0 END) as clicks") + ->first(); + + $sent = (int) ($row->sent ?? 0); + $views = (int) ($row->views ?? 0); + $clicks = (int) ($row->clicks ?? 0); + + // For anonymous tracking, per-contact columns are zero — pull the + // aggregate counts from campaign meta. open_count is a single int; + // click count is a serialized map of url => clicks. + if ($openStatus === 'anonymous') { + $views = (int) fluentcrm_get_campaign_meta($campaignId, '_ano_open_count', true); + } + if ($clickStatus === 'anonymous') { + $rawUrlClicks = fluentcrm_get_campaign_meta($campaignId, '_ano_url_clicks', true); + if (is_array($rawUrlClicks)) { + $clicks = (int) array_sum(array_filter($rawUrlClicks, 'is_numeric')); + } + } + + $unsubs = (int) fluentCrmDb()->table('fc_campaign_url_metrics') + ->where('campaign_id', $campaignId) + ->where('type', 'unsubscribe') + ->distinct() + ->count('subscriber_id'); + + return [ + 'total' => $total, + 'sent' => $sent, + 'views' => $views, + 'clicks' => $clicks, + 'unsubscribers' => $unsubs, + 'open_rate' => $sent ? round($views / max(1, $sent) * 100, 2) : 0, + 'click_rate' => $sent ? round($clicks / max(1, $sent) * 100, 2) : 0, + // Anonymous mode aggregates engagement into campaign meta rather + // than per-contact rows — agents must know which they're seeing. + 'tracking_mode' => [ + 'opens' => $openStatus, + 'clicks' => $clickStatus, + ], + ]; + } + + // --------------------------------------------------------------------- + // Filter translation + // --------------------------------------------------------------------- + + /** + * Translate the universal MCP filter shape (MCP_PLAN.md § 3.6) into an + * array of args ContactsQuery accepts. + */ + public static function buildContactsQueryArgs($filter) + { + $filter = (array) $filter; + $args = []; + + if (!empty($filter['search'])) { + $args['search'] = sanitize_text_field((string) $filter['search']); + $args['custom_fields'] = true; + } + + if (!empty($filter['tags'])) { + $resolved = self::resolveTagIds((array) $filter['tags']); + $args['tags'] = $resolved['ids']; + } + + if (!empty($filter['lists'])) { + $resolved = self::resolveListIds((array) $filter['lists']); + $args['lists'] = $resolved['ids']; + } + + if (!empty($filter['statuses'])) { + $args['statuses'] = array_values(array_filter( + array_map('sanitize_text_field', (array) $filter['statuses']) + )); + } + + if (!empty($filter['sms_statuses'])) { + $args['sms_statuses'] = array_values(array_filter( + array_map('sanitize_text_field', (array) $filter['sms_statuses']) + )); + } + + if (!empty($filter['contact_ids'])) { + $args['contact_ids'] = array_values(array_filter(array_map('intval', (array) $filter['contact_ids']))); + } + + // contact_type / created_after / created_before all flow through the + // advanced_filters pipeline as subscriber/ filters. Direct args + // on ContactsQuery would also work but the advanced path is what + // FluentCRM uses internally for these columns and reuses the same + // hooks. Use date-aware operators ('after'/'before') instead of + // '>='/'<=' — applyGeneralFilterQuery's exact-operator list does + // NOT include those, and falls through to a LIKE that wraps the + // value in % (round-4 review B/P1 #4). + $advanced = self::normalizeAdvancedFilters($filter['advanced_filters'] ?? []); + + if (!empty($filter['contact_type'])) { + $advanced[] = [[ + 'source' => ['subscriber', 'contact_type'], + 'operator' => '=', + 'value' => sanitize_text_field((string) $filter['contact_type']), + ]]; + } + + // Date range filters are applied separately by applyDateFilters() + // post-construction. Routing them through advanced_filters hits + // FluentCRM's broken whereTimestamp() phantom method (round-4 + // review P1 #4) which produces nonsensical SQL like + // `where 'timestamp' = 'created_at'`. + + if (!empty($advanced)) { + $args['filter_type'] = 'advanced'; + $args['filters_groups_raw'] = $advanced; + } + + // All fc_subscribers columns. The framework rewrite made orderBy() throw + // LogicException on column names that don't match ^[a-zA-Z0-9_\.]+$ + // — empty strings, "id ASC", "DROP TABLE", etc. — so an unguarded + // sort_by would 500 the tool. Schema is stable (migration only adds + // indexes), so hardcoding the column list avoids a per-request + // SHOW COLUMNS without restricting agents to the input_schema enum. + $allowedSortBy = [ + 'id', 'user_id', 'hash', 'contact_owner', 'company_id', 'prefix', + 'first_name', 'last_name', 'email', 'timezone', 'address_line_1', + 'address_line_2', 'postal_code', 'city', 'state', 'country', 'ip', + 'latitude', 'longitude', 'total_points', 'life_time_value', 'phone', + 'status', 'contact_type', 'source', 'avatar', 'date_of_birth', + 'created_at', 'last_activity', 'updated_at', + ]; + $sortBy = sanitize_key((string) ($filter['sort_by'] ?? 'id')); + if (!in_array($sortBy, $allowedSortBy, true)) { + $sortBy = 'id'; + } + $args['sort_by'] = $sortBy; + $sortType = strtoupper(sanitize_text_field((string) ($filter['sort_type'] ?? 'DESC'))); + $args['sort_type'] = in_array($sortType, ['ASC', 'DESC'], true) ? $sortType : 'DESC'; + + if (isset($filter['custom_fields']) && $filter['custom_fields']) { + $args['custom_fields'] = true; + } + + return $args; + } + + /** + * Apply created_after / created_before to a query model directly. Avoids + * the whereTimestamp phantom-method bug in + * Subscriber::applyGeneralFilterQuery (round-4 review P1 #4) — using + * raw `where(... '>=', ...)` SQL instead. + * + * Pass either a ContactsQuery instance (we'll grab getModel()) or an + * Eloquent query directly. + */ + public static function applyDateFilters($queryOrCq, $filter) + { + if (!is_array($filter)) { + return $queryOrCq; + } + $query = method_exists($queryOrCq, 'getModel') ? $queryOrCq->getModel() : $queryOrCq; + if (!is_object($query)) { + return $queryOrCq; + } + + if (!empty($filter['created_after'])) { + $value = sanitize_text_field((string) $filter['created_after']); + $query->where('created_at', '>=', $value); + } + if (!empty($filter['created_before'])) { + $value = sanitize_text_field((string) $filter['created_before']); + $query->where('created_at', '<=', $value); + } + + return $queryOrCq; + } + + /** + * Build a paginated ContactsQuery directly from the universal filter shape. + * The MCP layer reads $_REQUEST['page'] and `per_page` to drive the + * underlying paginator (matches `$model->paginate()` behavior). + */ + public static function buildContactsQuery($filter) + { + $args = self::buildContactsQueryArgs($filter); + return new ContactsQuery($args); + } + + /** + * Validate the universal-filter shape before it's used. Returns + * `true` on success or a WP_Error (`invalid_param`) on failure. + * + * Checks enforced (all fail-closed — a bad value never silently widens + * the result set): + * 1. `statuses[]` — must be in fluentcrm_subscriber_statuses(). + * 2. `sms_statuses[]` — must be in fluentcrm_subscriber_sms_statuses(). + * 3. `contact_type` — must be a key in fluentcrm_contact_types(). + * 4. `advanced_filters` — items must carry source[provider, property] + + * operator, and the (provider, property) pair must be registered in + * Helper::getAdvancedFilterOptions(). Otherwise the matching engine + * silently falls back to "match everyone". + * + * Operator-test report 2026-05-07 #1 — invalid statuses were being + * silently dropped by buildContactsQueryArgs(), which made the agent + * think it was targeting a narrow segment while actually hitting all + * 12,863 contacts. Round-2 review #3 covered the advanced_filters + * shape; round-4 review P0 #2 covered the (provider, property) pair. + */ + public static function validateUniversalFilter($filter) + { + if (!is_array($filter) || empty($filter)) { + return true; + } + + // 1. statuses[] + if (!empty($filter['statuses']) && is_array($filter['statuses'])) { + $allowed = fluentcrm_subscriber_statuses(); + $bad = array_values(array_filter( + array_map('sanitize_text_field', $filter['statuses']), + function ($s) use ($allowed) { + return $s !== '' && !in_array($s, $allowed, true); + } + )); + if (!empty($bad)) { + return self::error('invalid_param', __('statuses contains values not in the contact-status enum. Refusing — silently ignoring would widen the audience instead of narrowing it.', 'fluent-crm'), [ + 'unknown_statuses' => $bad, + 'allowed_statuses' => array_values($allowed), + ]); + } + } + + // 2. sms_statuses[] + if (!empty($filter['sms_statuses']) && is_array($filter['sms_statuses'])) { + $allowed = fluentcrm_subscriber_sms_statuses(); + $bad = array_values(array_filter( + array_map('sanitize_text_field', $filter['sms_statuses']), + function ($s) use ($allowed) { + return $s !== '' && !in_array($s, $allowed, true); + } + )); + if (!empty($bad)) { + return self::error('invalid_param', __('sms_statuses contains values not in the SMS-status enum.', 'fluent-crm'), [ + 'unknown_sms_statuses' => $bad, + 'allowed_sms_statuses' => array_values($allowed), + ]); + } + } + + // 3. contact_type + if (!empty($filter['contact_type'])) { + $allowed = array_keys(fluentcrm_contact_types()); + $value = sanitize_text_field((string) $filter['contact_type']); + if (!in_array($value, $allowed, true)) { + return self::error('invalid_param', __('contact_type is not a registered type.', 'fluent-crm'), [ + 'unknown_contact_type' => $value, + 'allowed_contact_types' => $allowed, + ]); + } + } + + $original = $filter['advanced_filters'] ?? null; + if (!empty($original) && is_array($original)) { + $normalized = self::normalizeAdvancedFilters($original); + // If the input had any items at all but nothing survived + // normalization, the agent passed an unsupported shape. + $hadAnyItems = false; + foreach ($original as $group) { + if (is_array($group) && count($group) > 0) { + $hadAnyItems = true; + break; + } + } + if ($hadAnyItems && empty($normalized)) { + return self::error('invalid_param', __('advanced_filters has no valid items. Each item needs source: [provider, property], operator, and value. For most agent use cases, the simple top-level filters are enough: tags, lists, statuses, search, contact_type, created_after, created_before.', 'fluent-crm'), [ + 'received_advanced_filters' => $original, + 'expected_item_shape' => ['source' => ['provider', 'property'], 'operator' => 'string', 'value' => 'mixed'], + 'simple_alternatives' => ['tags', 'lists', 'statuses', 'search', 'contact_type', 'created_after', 'created_before'], + ]); + } + + // Validate each (provider, property) pair against the FluentCRM + // registry. Unrecognized pairs would otherwise silently fall + // back to "match everyone" (round-4 review P0 #2). We surface + // the valid pairs in the error so the agent can self-correct. + $known = self::knownAdvancedFilterPairs(); + $unknown = []; + foreach ($normalized as $group) { + foreach ($group as $item) { + $provider = (string) $item['source'][0]; + $property = (string) $item['source'][1]; + $providerProps = $known[$provider] ?? null; + if ($providerProps === null) { + $unknown[] = ['source' => [$provider, $property], 'reason' => 'unknown_provider']; + continue; + } + if (!in_array($property, $providerProps, true)) { + $unknown[] = ['source' => [$provider, $property], 'reason' => 'unknown_property']; + } + } + } + if (!empty($unknown)) { + $compactKnown = []; + foreach ($known as $providerKey => $props) { + $compactKnown[$providerKey] = $props; + } + return self::error('invalid_param', __('advanced_filters references unknown (provider, property) pairs. The matching engine would silently fall back to "match everyone" — refusing.', 'fluent-crm'), [ + 'unknown_pairs' => $unknown, + 'known_pairs' => $compactKnown, + 'tip' => 'For status / engagement / contact_type targeting, use the simple top-level filter fields instead — they are pre-validated.', + ]); + } + } + return true; + } + + /** + * Drop malformed entries from a caller-provided advanced_filters payload + * so ContactsQuery::formatAdvancedFilters doesn't fatal on a count(null). + * + * Each item must be {source: [provider, property], operator, value[, + * extra_value]}. Items without a 2-tuple `source` and a non-empty + * `operator` are silently dropped. Empty groups are removed. + * + * @param mixed $groups + * @return array + */ + public static function normalizeAdvancedFilters($groups) + { + if (!is_array($groups)) { + return []; + } + + $out = []; + foreach ($groups as $group) { + if (!is_array($group)) { + continue; + } + $cleaned = []; + foreach ($group as $item) { + if (!is_array($item)) { + continue; + } + $source = $item['source'] ?? null; + if (!is_array($source) || count($source) !== 2 || empty($source[0]) || empty($source[1])) { + continue; + } + if (empty($item['operator'])) { + continue; + } + $cleaned[] = $item; + } + if ($cleaned) { + $out[] = $cleaned; + } + } + return $out; + } + + /** + * Cached map of registered (provider => [property, ...]) pairs that + * FluentCRM actually understands. Used to validate caller-supplied + * advanced_filters before they hit ContactsQuery — without this gate, + * an unknown (provider, property) pair causes a silent fallback to + * "match everyone" because the action hook simply doesn't fire and + * the where-clause never narrows (round-4 review P0 #2). + * + * Source of truth: Helper::getAdvancedFilterOptions() — the same + * registry the admin UI uses. + */ + public static function knownAdvancedFilterPairs() + { + static $cache = null; + if ($cache !== null) { + return $cache; + } + + $cache = []; + if (method_exists(\FluentCrm\App\Services\Helper::class, 'getAdvancedFilterOptions')) { + $opts = \FluentCrm\App\Services\Helper::getAdvancedFilterOptions(); + foreach ((array) $opts as $providerKey => $providerCfg) { + $children = $providerCfg['children'] ?? []; + $cache[$providerKey] = []; + foreach ((array) $children as $child) { + if (!empty($child['value'])) { + $cache[$providerKey][] = (string) $child['value']; + } + } + } + } + + // Subscriber/contact_type isn't always exposed in the admin UI but + // is a real column we use for the contact_type universal filter. + if (isset($cache['subscriber']) && !in_array('contact_type', $cache['subscriber'], true)) { + $cache['subscriber'][] = 'contact_type'; + } + + return $cache; + } + + // --------------------------------------------------------------------- + // Content handling + // --------------------------------------------------------------------- + + /** + * Strip HTML tags, decode entities, collapse whitespace. Keeps anchor URLs + * inline as `[text](url)` so plain-text consumers don't lose them. + */ + public static function htmlToText($html) + { + if (!$html) { + return ''; + } + + $text = preg_replace_callback( + '/]*href=[\'"]([^\'"]+)[\'"][^>]*>(.*?)<\/a>/is', + function ($m) { + $url = trim($m[1]); + $label = trim(wp_strip_all_tags($m[2])); + if ($label === '' || $label === $url) { + return $url; + } + return $label . ' (' . $url . ')'; + }, + (string) $html + ); + + $text = wp_strip_all_tags($text); + $text = html_entity_decode($text, ENT_QUOTES, 'UTF-8'); + $text = preg_replace('/\s+/', ' ', $text); + + return trim($text); + } + + public static function detectContentType($body) + { + $body = (string) $body; + // Cheap sniff: an early `<` followed by an ASCII letter signals HTML. + if (preg_match('/<[a-zA-Z]/', substr($body, 0, 200))) { + return 'html'; + } + return 'text'; + } + + public static function dualBodyShape($html) + { + $html = (string) $html; + return [ + 'body_html' => $html, + 'body_text' => self::htmlToText($html), + ]; + } + + // --------------------------------------------------------------------- + // Pagination + // --------------------------------------------------------------------- + + /** + * Normalize page/per_page from input. Mutates `$_REQUEST` so the framework + * paginator picks up the values — that is FluentCRM's existing pattern. + */ + public static function paginationFromInput($input, $defaultPerPage = 15, $maxPerPage = 100) + { + $page = max(1, (int) ($input['page'] ?? 1)); + $perPage = (int) ($input['per_page'] ?? $defaultPerPage); + if ($perPage < 1) { + $perPage = $defaultPerPage; + } + $perPage = min($perPage, $maxPerPage); + + // Match how FluentCRM controllers expect WP_REQUEST to drive paging. + $_REQUEST['page'] = $page; + $_REQUEST['per_page'] = $perPage; + + return ['page' => $page, 'per_page' => $perPage]; + } + + // --------------------------------------------------------------------- + // Validation + // --------------------------------------------------------------------- + + /** + * Return the registered custom-field slugs for contacts. Cached for + * the request lifetime — fluentcrm_get_custom_contact_fields() is + * already statically cached but we don't want to repeat the array + * walk for every bulk row. + * + * @return string[] + */ + public static function knownContactCustomFieldSlugs() + { + static $cache = null; + if ($cache !== null) { + return $cache; + } + $fields = fluentcrm_get_custom_contact_fields(); + $cache = []; + foreach ((array) $fields as $f) { + if (!empty($f['slug'])) { + $cache[] = (string) $f['slug']; + } + } + return $cache; + } + + /** + * Diff caller-supplied custom-field keys against the registered + * schema. Unknown keys would otherwise be silently dropped by + * Subscriber::syncCustomFieldValues — the agent thinks the value + * persisted but nothing was saved (operator-test report 2026-05-07 + * #6). + * + * @param array $customFields + * @return array{known: array, unknown: string[]} + */ + public static function diffCustomFields($customFields) + { + $known = []; + $unknown = []; + if (!is_array($customFields) || empty($customFields)) { + return ['known' => $known, 'unknown' => $unknown]; + } + $allowed = self::knownContactCustomFieldSlugs(); + foreach ($customFields as $key => $value) { + $slug = sanitize_key((string) $key); + if ($slug === '') { + continue; + } + if (in_array($slug, $allowed, true)) { + $known[$slug] = $value; + } else { + $unknown[] = (string) $key; + } + } + return ['known' => $known, 'unknown' => array_values(array_unique($unknown))]; + } + + /** + * Parse and validate an agent-supplied scheduled_at into a DateTime in + * the site timezone. Operator-test report 2026-05-07 #3 — previously + * the validated DateTime was discarded and the raw input string was + * passed through to MySQL, which silently dropped the offset (a + * datetime column has no timezone). On read, toIso8601 then re-parsed + * the naive string in PHP's default timezone (UTC), producing wrong + * absolute times. + * + * Input convention: + * - ISO-8601 with offset → respected as written. + * - Bare datetime / date → interpreted as SITE timezone (matches + * FluentCRM's storage convention). + * + * The caller stores `$dt->format('Y-m-d H:i:s')` which is now + * unambiguous because `$dt` carries the site tz. + */ + public static function validateScheduledAt($iso, $minFutureSeconds = 60) + { + if (!$iso) { + return self::error('invalid_param', __('scheduled_at is required', 'fluent-crm')); + } + + $siteTz = self::siteTimezoneObject(); + $input = (string) $iso; + + try { + // If the string carries an explicit offset / "Z", DateTime keeps + // it. If it's bare ("2026-05-08 09:00:00"), pass site tz as + // the second arg so the moment is interpreted correctly. + if (self::stringHasTimezone($input)) { + $dt = new \DateTime($input); + } else { + $dt = new \DateTime($input, $siteTz); + } + } catch (\Exception $e) { + return self::error('invalid_param', __('scheduled_at must be ISO 8601 (e.g. 2026-05-08T09:00:00+01:00) or a bare datetime in site timezone (2026-05-08 09:00:00).', 'fluent-crm'), [ + 'scheduled_at_input' => $input, + 'site_timezone' => $siteTz->getName(), + ]); + } + + // Convert to site tz so storage in `Y-m-d H:i:s` is consistent with + // the rest of FluentCRM (which uses current_time('mysql')). + $dt->setTimezone($siteTz); + + if ($dt->getTimestamp() < (time() + $minFutureSeconds)) { + return self::error('validation_failed', __('scheduled_at must be in the future', 'fluent-crm'), [ + 'scheduled_at_input' => $input, + 'parsed_utc' => gmdate('c', $dt->getTimestamp()), + 'parsed_site_local' => $dt->format('Y-m-d H:i:s'), + 'now_utc' => gmdate('c'), + 'site_timezone' => $siteTz->getName(), + 'now_site_local' => wp_date('Y-m-d H:i:s', time()), + ]); + } + + return $dt; + } + + /** + * Heuristic: does the string carry timezone info (Z or ±HH:MM / ±HHMM) + * after the time component? Date-only strings always count as bare. + */ + private static function stringHasTimezone($s) + { + return (bool) preg_match('/T?\d{2}:\d{2}(?::\d{2})?(?:\.\d+)?(Z|[+-]\d{2}:?\d{2})$/', trim((string) $s)); + } + + /** + * Site timezone as a DateTimeZone — the object form callers need for + * DateTime construction / setTimezone. wp_timezone() ships in WP 5.3+ + * (we target 6.9+). + */ + public static function siteTimezoneObject() + { + return wp_timezone(); + } + + /** + * Format a stored mysql datetime (assumed in site tz) into the dual + * shape callers expose to agents — get-campaign / actionSchedule + * surface this so an operator never has to guess which timezone a + * scheduled_at value is in. + * + * @return array{utc:?string, site_local:?string, site_timezone:string}|null + */ + public static function formatScheduledAtDual($value) + { + if (!$value) { + return null; + } + $siteTz = self::siteTimezoneObject(); + + try { + // Stored values come from current_time('mysql') / our own + // $dt->format('Y-m-d H:i:s') — both are site-tz strings. + // ISO inputs from outside are unlikely here but tolerated. + if ($value instanceof \DateTimeInterface) { + $dt = (new \DateTime('@' . $value->getTimestamp()))->setTimezone($siteTz); + } elseif (self::stringHasTimezone((string) $value)) { + $dt = (new \DateTime((string) $value))->setTimezone($siteTz); + } else { + $dt = new \DateTime((string) $value, $siteTz); + } + } catch (\Exception $e) { + return null; + } + + return [ + 'utc' => gmdate('c', $dt->getTimestamp()), + 'site_local' => $dt->format('Y-m-d H:i:s'), + 'site_timezone' => self::siteTimezoneName(), + ]; + } + + /** + * Friendly site timezone label. wp_timezone() returns a numeric offset + * like "+00:00" when gmt_offset is 0 and timezone_string is empty; + * fluentCrmGetTimezoneString() correctly returns "UTC" in that case. + */ + public static function siteTimezoneName() + { + return (string) fluentCrmGetTimezoneString(); + } + + public static function permissionGuard($cap) + { + if (PermissionManager::currentUserCan($cap)) { + return true; + } + return self::error('forbidden', __('You do not have permission to perform this action', 'fluent-crm'), ['required' => $cap]); + } + + // --------------------------------------------------------------------- + // Errors + // --------------------------------------------------------------------- + + public static function error($code, $message, $details = []) + { + return new \WP_Error($code, $message, $details); + } + + // --------------------------------------------------------------------- + // Misc + // --------------------------------------------------------------------- + + public static function toIso8601($value) + { + if (!$value) { + return null; + } + + try { + if ($value instanceof \DateTimeInterface) { + return $value->format('c'); + } + return (new \DateTime((string) $value))->format('c'); + } catch (\Exception $e) { + return null; + } + } +} diff --git a/wp-content/plugins/fluent-crm/app/Modules/MCP/MCPInit.php b/wp-content/plugins/fluent-crm/app/Modules/MCP/MCPInit.php new file mode 100644 index 0000000..f67615f --- /dev/null +++ b/wp-content/plugins/fluent-crm/app/Modules/MCP/MCPInit.php @@ -0,0 +1,140 @@ + __('FluentCRM', 'fluent-crm'), + 'description' => __('Contact, campaign, and automation abilities for FluentCRM.', 'fluent-crm'), + ]); + } + + public function registerAbilities() + { + AbilitiesRegistrar::register(); + + /** + * Fires after FluentCRM has registered its core MCP abilities. + * + * FluentCampaign Pro hooks this to register its 4 Pro abilities under + * the same `fluent-crm/` namespace — agents do not need to know which + * plugin owns which tool. + * + * @since 2.10.0 + */ + do_action('fluent_crm/mcp_loaded'); + } + + /** + * Register the dedicated FluentCRM MCP server when the WP MCP Adapter + * fires `mcp_adapter_init`. + * + * @param \WP\MCP\Core\McpAdapter $adapter + */ + public function registerCustomServer($adapter) + { + if (!$adapter || !is_object($adapter) || !method_exists($adapter, 'create_server')) { + return; + } + + $abilityNames = array_keys(AbilitiesRegistrar::getDefinitions()); + + /** + * Filter the list of FluentCRM ability names registered with the + * dedicated FluentCRM MCP server. + * + * FluentCampaign Pro hooks this filter (in its own MCPInit) to push + * its 4 Pro abilities into the same server. Other extensions can do + * the same to surface tools agents discover via `tools/list`. + * + * @since 2.10.0 + * + * @param array $abilityNames Array of fully-qualified ability names. + */ + $abilityNames = apply_filters('fluent_crm/mcp_ability_names', $abilityNames); + + // Allow operators to swap the route via filter. Default puts the + // server at /wp-json/fluent-crm/mcp — sibling to the existing + // FluentCRM REST namespace (fluent-crm/v2), but distinct so it does + // not get caught by the v2 policy stack. + $namespace = apply_filters('fluent_crm/mcp_server_namespace', 'fluent-crm'); + $route = apply_filters('fluent_crm/mcp_server_route', 'mcp'); + + $adapter->create_server( + 'fluent-crm', + $namespace, + $route, + __('FluentCRM MCP Server', 'fluent-crm'), + __('AI agent tools for FluentCRM contacts, campaigns, and automations.', 'fluent-crm'), + defined('FLUENTCRM_PLUGIN_VERSION') ? FLUENTCRM_PLUGIN_VERSION : '1.0.0', + ['\WP\MCP\Transport\HttpTransport'], + '\WP\MCP\Infrastructure\ErrorHandling\ErrorLogMcpErrorHandler', + '\WP\MCP\Infrastructure\Observability\NullMcpObservabilityHandler', + array_values(array_unique(array_filter((array) $abilityNames))) + ); + } + + /** + * Public helper used by the Settings UI and the snippet generator to + * report a stable endpoint URL for the FluentCRM MCP server. + */ + public static function getEndpointUrl() + { + $namespace = apply_filters('fluent_crm/mcp_server_namespace', 'fluent-crm'); + $route = apply_filters('fluent_crm/mcp_server_route', 'mcp'); + + return get_rest_url(null, trailingslashit($namespace) . $route); + } +} diff --git a/wp-content/plugins/fluent-crm/app/Modules/MCP/Tools/CampaignTools.php b/wp-content/plugins/fluent-crm/app/Modules/MCP/Tools/CampaignTools.php new file mode 100644 index 0000000..9caa144 --- /dev/null +++ b/wp-content/plugins/fluent-crm/app/Modules/MCP/Tools/CampaignTools.php @@ -0,0 +1,1113 @@ +whereIn('type', ['campaign', 'custom_email_campaign']) + ->orderBy($sortBy, $sortType); + } else { + $query = Campaign::query()->orderBy($sortBy, $sortType); + } + + if ($search !== '') { + global $wpdb; + $query->where('title', 'LIKE', '%' . $wpdb->esc_like($search) . '%'); + } + + if (!empty($statuses)) { + $query->whereIn('status', $statuses); + } + + $paginated = $query->paginate(); + + $items = []; + foreach ($paginated->items() as $campaign) { + $items[] = MCPHelper::formatCampaignSummary($campaign, $includeStats); + } + + return [ + 'items' => $items, + 'total' => (int) $paginated->total(), + 'page' => (int) $paginated->currentPage(), + 'per_page' => (int) $paginated->perPage(), + 'pages' => (int) $paginated->lastPage(), + ]; + } + + // ----------------------------------------------------------------- + // Read: get-campaign + // ----------------------------------------------------------------- + + public static function getCampaign($params) + { + $params = (array) $params; + $campaignId = (int) ($params['campaign_id'] ?? 0); + + if (!$campaignId) { + return MCPHelper::error('invalid_param', __('campaign_id is required', 'fluent-crm')); + } + + // Bypass the type global scope so one-off sends (created by + // send-email-to-contact) are reachable too. Without this, an id + // returned by list-campaigns(include_one_offs=true) couldn't be + // fetched here — operator-test report 2026-05-07 #4. + $campaign = Campaign::withoutGlobalScope('type')->find($campaignId); + if (!$campaign) { + return MCPHelper::error('not_found', __('Campaign not found', 'fluent-crm'), ['campaign_id' => $campaignId]); + } + + if ($campaign->type === 'custom_email_campaign') { + return self::formatOneOffEmail($campaign); + } + + $defaultIncludes = ['stats']; + $include = isset($params['include']) && is_array($params['include']) && $params['include'] + ? array_values(array_intersect($params['include'], ['stats', 'subjects', 'link_report', 'recipients_estimate'])) + : $defaultIncludes; + + $body = (string) $campaign->email_body; + $bodyShape = MCPHelper::dualBodyShape($body); + + $settings = is_array($campaign->settings) ? $campaign->settings : (array) $campaign->settings; + + $recipients = self::recipientsForOutput($settings, 'subscribers'); + $recipientsExcluded = self::recipientsForOutput($settings, 'excludedSubscribers'); + $recipients = [ + 'lists' => $recipients['lists'], + 'tags' => $recipients['tags'], + 'exclude_lists' => $recipientsExcluded['lists'], + 'exclude_tags' => $recipientsExcluded['tags'], + ]; + + $sentStatuses = ['archived', 'working', 'paused']; + $sentAt = in_array($campaign->status, $sentStatuses, true) + ? MCPHelper::toIso8601($campaign->updated_at) + : null; + + $data = [ + 'id' => (int) $campaign->id, + 'title' => $campaign->title, + 'email_subject' => $campaign->email_subject, + 'email_pre_header' => $campaign->email_pre_header, + 'status' => $campaign->status, + 'design_template' => $campaign->design_template, + 'body_html' => $bodyShape['body_html'], + 'body_text' => $bodyShape['body_text'], + 'settings' => self::settingsForOutput($settings), + 'recipients' => $recipients, + 'scheduled_at' => MCPHelper::toIso8601($campaign->scheduled_at), + 'scheduled_at_resolved' => MCPHelper::formatScheduledAtDual($campaign->scheduled_at), + 'sent_at' => $sentAt, + 'created_at' => MCPHelper::toIso8601($campaign->created_at), + ]; + + if (in_array('stats', $include, true)) { + $data['stats'] = MCPHelper::campaignStatsCompact($campaign); + $data['stats']['revenue'] = self::campaignRevenue($campaign); + } + + if (in_array('subjects', $include, true)) { + $data['subjects'] = self::campaignSubjects($campaign); + } + + if (in_array('link_report', $include, true)) { + $data['link_report'] = self::campaignLinkReport($campaign); + } + + if (in_array('recipients_estimate', $include, true)) { + $data['recipients_estimate'] = self::campaignRecipientsEstimate($campaign); + } + + return $data; + } + + /** + * Render a one-off send (`type=custom_email_campaign`) for get-campaign. + * One-offs don't have a marketing lifecycle — the row's `status` column + * stays 'draft' even after delivery (the real status lives on the + * single fc_campaign_emails row). Surface a `one_off_status` field + * that reflects what actually happened, plus the recipient. Operator- + * test report 2026-05-07 #4. + */ + private static function formatOneOffEmail($campaign) + { + $body = (string) $campaign->email_body; + $bodyShape = MCPHelper::dualBodyShape($body); + + // The one and only campaign-email row for this send. + $email = \FluentCrm\App\Models\CampaignEmail::withoutGlobalScope('type') + ->where('campaign_id', $campaign->id) + ->orderBy('id', 'DESC') + ->first(); + + $oneOffStatus = $email ? (string) $email->status : 'unknown'; + $sentAt = $email && in_array($email->status, ['sent', 'opened', 'clicked'], true) + ? MCPHelper::toIso8601($email->updated_at) + : null; + + $recipient = null; + if ($email && $email->subscriber_id) { + $sub = \FluentCrm\App\Models\Subscriber::find($email->subscriber_id); + if ($sub) { + $recipient = [ + 'id' => (int) $sub->id, + 'email' => $sub->email, + 'full_name' => trim((string) $sub->full_name), + ]; + } + } + + $settings = is_array($campaign->settings) ? $campaign->settings : (array) $campaign->settings; + + return [ + 'id' => (int) $campaign->id, + 'kind' => 'one_off_email', + 'title' => $campaign->title, + 'email_subject' => $campaign->email_subject, + 'email_pre_header' => $campaign->email_pre_header, + 'one_off_status' => $oneOffStatus, + 'design_template' => $campaign->design_template, + 'body_html' => $bodyShape['body_html'], + 'body_text' => $bodyShape['body_text'], + 'settings' => self::settingsForOutput($settings), + 'recipient' => $recipient, + 'sent_at' => $sentAt, + 'created_at' => MCPHelper::toIso8601($campaign->created_at), + 'note' => __('This is a one-off send (not a marketing campaign). It supports change-campaign-status action=delete only — schedule/pause/resume/duplicate are not applicable.', 'fluent-crm'), + ]; + } + + /** + * Reverse the flat [{list, tag}] storage back into the agent-friendly + * {lists:[{id,title}], tags:[{id,title}]} shape for output. Items with + * 'all' on either side are treated as wildcards (omitted from the + * matching collection). Distinct ids only. + */ + private static function recipientsForOutput($settings, $key) + { + $items = $settings[$key] ?? []; + if (!is_array($items)) { + return ['lists' => [], 'tags' => []]; + } + + $listIds = []; + $tagIds = []; + + foreach ($items as $item) { + if (!is_array($item)) { + continue; + } + $listId = $item['list'] ?? null; + $tagId = $item['tag'] ?? null; + if ($listId !== null && $listId !== '' && $listId !== 'all' && is_numeric($listId)) { + $listIds[(int) $listId] = true; + } + if ($tagId !== null && $tagId !== '' && $tagId !== 'all' && is_numeric($tagId)) { + $tagIds[(int) $tagId] = true; + } + } + + $listsOut = []; + if ($listIds) { + foreach (\FluentCrm\App\Models\Lists::whereIn('id', array_keys($listIds))->get() as $list) { + $listsOut[] = ['id' => (int) $list->id, 'title' => $list->title]; + } + } + $tagsOut = []; + if ($tagIds) { + foreach (\FluentCrm\App\Models\Tag::whereIn('id', array_keys($tagIds))->get() as $tag) { + $tagsOut[] = ['id' => (int) $tag->id, 'title' => $tag->title]; + } + } + + return ['lists' => $listsOut, 'tags' => $tagsOut]; + } + + private static function settingsForOutput($settings) + { + return [ + 'mailer_settings' => $settings['mailer_settings'] ?? new \stdClass(), + 'is_transactional' => $settings['is_transactional'] ?? 'no', + 'click_tracker' => $settings['click_tracker'] ?? 'yes', + 'open_tracker' => $settings['open_tracker'] ?? 'yes', + ]; + } + + /** + * Revenue meta is shaped like ['orderIds' => [1,2,3], 'usd' => 12450, + * 'eur' => 600] — currency keys are interleaved with the + * 'orderIds' tracking array. Iterating naïvely and grabbing the first + * key produced `currency: "orderIds"` and a wrong amount (round-4 + * review P1 #7). Skip the meta key, sum numeric values across all + * currency entries, and surface the order count. + */ + private static function campaignRevenue($campaign) + { + $revenue = fluentcrm_get_campaign_meta($campaign->id, '_campaign_revenue', true); + if (!is_array($revenue)) { + return ['total' => 0, 'currency' => '', 'orders_count' => 0, 'by_currency' => []]; + } + + $orderCount = isset($revenue['orderIds']) && is_array($revenue['orderIds']) + ? count($revenue['orderIds']) + : 0; + + $byCurrency = []; + foreach ($revenue as $key => $value) { + if ($key === 'orderIds' || !is_numeric($value)) { + continue; + } + $byCurrency[strtoupper((string) $key)] = (float) $value; + } + + // Pick a primary currency for the headline number (first one set + // wins; multi-currency installs surface the rest in by_currency). + $primaryCurrency = ''; + $primaryTotal = 0.0; + if ($byCurrency) { + $primaryCurrency = (string) array_key_first($byCurrency); + $primaryTotal = (float) $byCurrency[$primaryCurrency]; + } + + return [ + 'total' => $primaryTotal, + 'currency' => $primaryCurrency, + 'orders_count' => $orderCount, + 'by_currency' => $byCurrency, + ]; + } + + /** + * A/B subjects live in fc_meta keyed by object_id (NOT campaign_id — + * that column doesn't exist on fc_meta). The Subject model's global + * scope handles the object_type filter. + * + * Storage shape (per Campaign::syncSubjects): `key` is a stable + * identifier, `value` is the subject string itself. Operator-test + * report 2026-05-07 #7 — the read path previously assumed `value` + * was a serialized {email_subject, weight} array (which it isn't), + * so the response always came back empty even after a successful + * upsert. + */ + private static function campaignSubjects($campaign) + { + $subjects = Subject::where('object_id', $campaign->id)->get(); + $out = []; + foreach ($subjects as $subject) { + $out[] = [ + 'id' => (int) $subject->id, + 'key' => (string) $subject->key, + 'value' => (string) $subject->value, + ]; + } + return $out; + } + + /** + * Link report — delegate to CampaignUrlMetric::getLinksReport, which + * joins fc_campaign_url_metrics → fc_url_stores correctly (the metrics + * table only stores url_id; the URL string lives in fc_url_stores). + */ + private static function campaignLinkReport($campaign) + { + $metric = new CampaignUrlMetric(); + $links = method_exists($metric, 'getLinksReport') ? $metric->getLinksReport($campaign) : []; + + $formatted = []; + foreach ((array) $links as $link) { + $formatted[] = [ + 'url' => $link['url'] ?? '', + 'total_clicks' => (int) ($link['total'] ?? 0), + ]; + } + + return [ + 'links' => $formatted, + 'click_status' => method_exists($campaign, 'getClickTrackingStatus') ? $campaign->getClickTrackingStatus(false) : 'yes', + 'open_status' => method_exists($campaign, 'getOpenTrackingStatus') ? $campaign->getOpenTrackingStatus(false) : 'yes', + ]; + } + + /** + * Find the first available title shaped like "Title (2)", "Title (3)", … + * given a base title that already exists. Mirrors what CampaignController + * ::ensureUniqueDefaultTitle does for the "Untitled" placeholder, but + * for any agent-supplied title. + */ + private static function nextAvailableTitle($baseTitle) + { + $count = 2; + while ($count < 1000) { + $candidate = $baseTitle . ' (' . $count . ')'; + if (!Campaign::where('title', $candidate)->exists()) { + return $candidate; + } + $count++; + } + // Astronomical fallback — append a timestamp to guarantee uniqueness. + return $baseTitle . ' (' . time() . ')'; + } + + /** + * Estimate recipients for a campaign using its stored segment settings. + * + * Routes through the same `Campaign::getSubscriberIdsCountBySegmentSettings` + * helper as `upsert-campaign.estimated_recipients` and the underlying + * estimator behind `estimate-dynamic-segment` (via ContactsQuery's filter + * translation) — so all three callers converge on the same number for the + * same filter (review bug #4). + */ + private static function campaignRecipientsEstimate($campaign) + { + if (!in_array($campaign->status, ['draft', 'scheduled', 'pending-scheduled'], true)) { + return null; + } + + $settings = is_array($campaign->settings) ? $campaign->settings : (array) maybe_unserialize($campaign->settings); + + $start = microtime(true); + $count = self::estimateRecipientsFromSettings($settings); + $execMs = (int) round((microtime(true) - $start) * 1000); + + return [ + 'count' => $count !== null ? (int) $count : 0, + 'execution_time_ms' => $execMs, + ]; + } + + // ----------------------------------------------------------------- + // Write: upsert-campaign + // ----------------------------------------------------------------- + + public static function upsertCampaign($params) + { + $params = (array) $params; + $campaignId = isset($params['campaign_id']) ? (int) $params['campaign_id'] : 0; + + $isNew = !$campaignId; + $campaign = null; + + if (!$isNew) { + $campaign = Campaign::find($campaignId); + if (!$campaign) { + return MCPHelper::error('not_found', __('Campaign not found', 'fluent-crm'), ['campaign_id' => $campaignId]); + } + $editableStatuses = ['draft', 'scheduled', 'pending-scheduled']; + if (!in_array($campaign->status, $editableStatuses, true)) { + return MCPHelper::error('not_supported', __('Cannot edit campaign in current status', 'fluent-crm'), [ + 'status' => $campaign->status, + 'allowed_statuses' => $editableStatuses, + ]); + } + } + + // Title is required for create. + $title = isset($params['title']) ? sanitize_text_field((string) $params['title']) : ''; + if ($isNew && $title === '') { + return MCPHelper::error('invalid_param', __('title is required when creating a campaign', 'fluent-crm')); + } + + // Pre-flight validation. Must run before Campaign::create() so a + // bad recipients shape / design_template / unsupported keys never + // leaves an orphaned draft row behind (operator-test report + // 2026-05-07 #2). + $validation = self::validateUpsertInput($params); + if (is_wp_error($validation)) { + return $validation; + } + + $titleConflictWarning = null; + if ($isNew) { + // Title uniqueness: by default we auto-suffix on conflict so + // an agent retrying after a transient error doesn't get stuck. + // Round 2 #10: when we suffix, surface a warning so the agent + // can tell the user "I created a copy named X". Strict mode + // (if_exists='error') keeps the hard-error behavior. + $ifExists = isset($params['if_exists']) ? sanitize_key((string) $params['if_exists']) : 'auto_suffix'; + $originalTitle = $title; + if ($title !== '' && Campaign::where('title', $title)->exists()) { + if ($ifExists === 'error') { + return MCPHelper::error('invalid_param', __('A campaign with that title already exists', 'fluent-crm'), [ + 'title' => $title, + ]); + } + $existingId = (int) Campaign::where('title', $title)->value('id'); + $title = self::nextAvailableTitle($title); + $titleConflictWarning = sprintf( + /* translators: 1: requested title, 2: existing campaign id, 3: actual title used */ + __('A campaign with title "%1$s" already exists (id %2$d). Created this one as "%3$s" instead. Pass if_exists="error" to make conflicts a hard failure.', 'fluent-crm'), + $originalTitle, + $existingId, + $title + ); + } + $campaign = Campaign::create(['title' => $title]); + do_action('fluent_crm/campaign_created', $campaign); + + // If we auto-suffixed, sync the new title back into $params so + // the passthru loop below doesn't overwrite the suffix with the + // original (review B4 round 3). Without this, the warning was + // lying — it said "Created as X (2)" but the saved title was + // still "X" because passthru clobbered it. + if ($titleConflictWarning) { + $params['title'] = $title; + } + } + + // Build the update payload. + $updateData = []; + $passthru = ['title', 'email_subject', 'email_pre_header', 'email_body', 'design_template']; + foreach ($passthru as $field) { + if (array_key_exists($field, $params) && $params[$field] !== null) { + $updateData[$field] = $params[$field]; + } + } + + // design_template was already validated by validateUpsertInput() + // above; sanitize_key here is just to normalize for storage. + if (isset($updateData['design_template']) && $updateData['design_template'] !== '') { + $updateData['design_template'] = sanitize_key((string) $updateData['design_template']); + } + + if (!empty($params['utm']) && is_array($params['utm'])) { + foreach ($params['utm'] as $key => $value) { + $col = 'utm_' . sanitize_key($key); + $updateData[$col] = sanitize_text_field((string) $value); + } + } + + // Settings — merge into existing. + $settings = $campaign->settings ?: []; + if (is_string($settings)) { + $settings = (array) maybe_unserialize($settings); + } + + if (!empty($params['settings']) && is_array($params['settings'])) { + $settings = array_replace_recursive((array) $settings, $params['settings']); + } + + // Recipients — universal filter shape lands in `settings.subscribers`. + // Shape was already validated by validateUpsertInput(). Campaigns + // can ONLY persist tags + lists, so anything else would silently + // disappear (round-4 review P0 #1 — 3.5x audience inflation). + if (!empty($params['recipients']) && is_array($params['recipients'])) { + $settings['subscribers'] = self::filterToCampaignSegment($params['recipients']); + } + if (!empty($params['exclude_recipients']) && is_array($params['exclude_recipients'])) { + $settings['excludedSubscribers'] = self::filterToCampaignSegment($params['exclude_recipients']); + } + + // If the agent passed a top-level design_template, propagate it into + // settings.template_config so the two stay in sync. Only fall the + // other direction (template_config -> top-level) when the agent did + // NOT specify design_template — otherwise the boot's default + // template_config (set to 'simple' on this install) would clobber + // the agent's choice (round 2 #29). + if (isset($updateData['design_template']) && $updateData['design_template'] !== '') { + if (!isset($settings['template_config']) || !is_array($settings['template_config'])) { + $settings['template_config'] = []; + } + $settings['template_config']['design_template'] = $updateData['design_template']; + } elseif (!empty($settings['template_config']['design_template'])) { + $updateData['design_template'] = $settings['template_config']['design_template']; + } + + $updateData['settings'] = $settings; + $updateData = \FluentCrm\App\Services\Sanitize::campaign($updateData); + + $campaign->fill($updateData)->save(); + + // Subjects (A/B): syncSubjects requires {key, value}; the agent + // can pass {value} alone (key auto-generated). Items missing a + // value are dropped by syncSubjects, so prepare a clean payload + // here. Operator-test report 2026-05-07 #7. + if (!empty($params['subjects']) && is_array($params['subjects']) && method_exists($campaign, 'syncSubjects')) { + $prepared = []; + foreach (array_values($params['subjects']) as $i => $s) { + if (!is_array($s) || empty($s['value'])) { + continue; + } + $value = sanitize_text_field((string) $s['value']); + $key = !empty($s['key']) ? sanitize_text_field((string) $s['key']) : substr(md5($value . '|' . $i), 0, 16); + $item = ['key' => $key, 'value' => $value]; + if (!empty($s['id'])) { + $item['id'] = (int) $s['id']; + } + $prepared[] = $item; + } + if ($prepared) { + $campaign->syncSubjects($prepared); + } + } + + if (!empty($params['label_ids']) && is_array($params['label_ids']) && method_exists($campaign, 'attachLabels')) { + $campaign->attachLabels(array_map('intval', $params['label_ids'])); + } + + do_action('fluent_crm/campaign_data_updated', $campaign, $params); + + // Estimate recipients if a segment was provided so the agent sees the + // count in the same call. + $estimated = null; + $warnings = []; + if (!empty($settings['subscribers']) || !empty($settings['excludedSubscribers'])) { + $estimated = self::estimateRecipientsFromSettings($settings); + } + + if (empty($settings['excludedSubscribers'])) { + $warnings[] = __('No exclude_recipients set — campaign may include unsubscribed segments', 'fluent-crm'); + } + if (empty($params['email_pre_header']) && empty($campaign->email_pre_header)) { + $warnings[] = __('email_pre_header not set — better deliverability with a preheader', 'fluent-crm'); + } + if ($titleConflictWarning) { + $warnings[] = $titleConflictWarning; + } + + $campaign = Campaign::find($campaign->id); + + return [ + 'ok' => true, + 'action' => $isNew ? 'created' : 'updated', + 'campaign' => self::getCampaign(['campaign_id' => (int) $campaign->id, 'include' => ['stats']]), + 'estimated_recipients' => $estimated, + 'warnings' => $warnings, + ]; + } + + /** + * Pre-flight validation for upsert-campaign — must run BEFORE any + * Campaign row is created so a bad payload never leaves an orphan + * draft (operator-test report 2026-05-07 #2). Centralizes: + * + * - design_template enum (free includes only, no visual_builder) + * - recipients shape (lists/tags/sending_filter only) + * - exclude_recipients shape (same constraint) + * + * @return true|\WP_Error + */ + private static function validateUpsertInput($params) + { + if (isset($params['design_template']) && $params['design_template'] !== null && $params['design_template'] !== '') { + $designTemplate = sanitize_key((string) $params['design_template']); + $allowed = array_keys(\FluentCrm\App\Modules\MCP\Tools\ContextTools::allowedDesignTemplates()); + if (!in_array($designTemplate, $allowed, true)) { + return MCPHelper::error('invalid_param', __('design_template not allowed via MCP', 'fluent-crm'), [ + 'design_template' => $designTemplate, + 'allowed' => $allowed, + ]); + } + } + + if (!empty($params['recipients']) && is_array($params['recipients'])) { + $reject = self::rejectUnsupportedRecipientKeys($params['recipients'], 'recipients'); + if (is_wp_error($reject)) { + return $reject; + } + } + + if (!empty($params['exclude_recipients']) && is_array($params['exclude_recipients'])) { + $reject = self::rejectUnsupportedRecipientKeys($params['exclude_recipients'], 'exclude_recipients'); + if (is_wp_error($reject)) { + return $reject; + } + } + + return true; + } + + /** + * Campaigns can target lists + tags only. The `recipients` object on + * upsert-campaign accepts the universal filter shape but anything + * other than {tags, lists, sending_filter} is silently dropped on + * persistence — and previously that drop didn't even produce a + * warning. Hard-error now so the agent can take the documented + * workaround (apply a temporary tag, target by tag, remove after + * send). + * + * Round-4 review P0 #1 — single most damaging bug, 3.5x audience + * inflation in one test. + * + * @param array $recipients + * @param string $paramName Either 'recipients' or 'exclude_recipients' + * @return true|\WP_Error + */ + private static function rejectUnsupportedRecipientKeys($recipients, $paramName) + { + $supported = ['lists', 'tags', 'sending_filter']; + $unsupported = array_values(array_diff(array_keys($recipients), $supported)); + if (!empty($unsupported)) { + return MCPHelper::error('invalid_param', sprintf( + /* translators: 1: parameter name, 2: comma-separated unsupported keys */ + __('%1$s only persists lists + tags on a campaign — these keys would be silently dropped: %2$s. To target a status/engagement/contact-type segment, apply a temporary tag via apply-segments-to-contacts (use dry_run first), use that tag in recipients, then remove it after the send completes.', 'fluent-crm'), + $paramName, + implode(', ', $unsupported) + ), [ + 'unsupported_keys' => $unsupported, + 'supported_keys' => $supported, + 'workaround' => '1) apply-segments-to-contacts(filter={...}, add_tags=["temp-X"], dry_run=true); 2) re-run without dry_run; 3) upsert-campaign(recipients={tags:["temp-X"]}); 4) after change-campaign-status(action=schedule), apply-segments-to-contacts(filter={tags:["temp-X"]}, remove_tags=["temp-X"]); 5) manage-tag(action=delete, tag_id=X)', + ]); + } + return true; + } + + /** + * Translate the universal filter shape to the flat + * [{list, tag}] array that Campaign settings.subscribers expects + * (see Campaign::getSubscribeIdsByListModel — its loop reads + * `$item['list']` and `$item['tag']` directly). + * + * Semantics match ContactsQuery: + * - tags only → [{list:'all', tag:N}, ...] (tag IN [...]) + * - lists only → [{list:N, tag:'all'}, ...] (list IN [...]) + * - lists AND tags → cross product so every (list, tag) pair lands + * in queryGroups, ANDed within a pair, ORed across + * + * Storage shape: array of {list, tag} pairs at the top level + * (NOT a nested {lists:[], tags:[]} object — that was the round-1 bug). + */ + private static function filterToCampaignSegment($filter) + { + $tagIds = !empty($filter['tags']) + ? MCPHelper::resolveTagIds((array) $filter['tags'])['ids'] + : []; + $listIds = !empty($filter['lists']) + ? MCPHelper::resolveListIds((array) $filter['lists'])['ids'] + : []; + + $items = []; + + if (!$tagIds && !$listIds) { + return $items; + } + + if ($tagIds && $listIds) { + foreach ($listIds as $listId) { + foreach ($tagIds as $tagId) { + $items[] = ['list' => (string) $listId, 'tag' => (string) $tagId]; + } + } + } elseif ($tagIds) { + foreach ($tagIds as $tagId) { + $items[] = ['list' => 'all', 'tag' => (string) $tagId]; + } + } else { + foreach ($listIds as $listId) { + $items[] = ['list' => (string) $listId, 'tag' => 'all']; + } + } + + return $items; + } + + private static function estimateRecipientsFromSettings($settings) + { + try { + $count = (new Campaign())->getSubscriberIdsCountBySegmentSettings([ + 'subscribers' => $settings['subscribers'] ?? [], + 'excludedSubscribers' => $settings['excludedSubscribers'] ?? [], + 'sending_filter' => $settings['sending_filter'] ?? 'list_tag', + 'dynamic_segment' => $settings['dynamic_segment'] ?? null, + 'advanced_filters' => $settings['advanced_filters'] ?? [], + ]); + return (int) $count; + } catch (\Throwable $e) { + return null; + } + } + + // ----------------------------------------------------------------- + // Write: change-campaign-status + // ----------------------------------------------------------------- + + public static function changeCampaignStatus($params) + { + $params = (array) $params; + $campaignId = (int) ($params['campaign_id'] ?? 0); + $action = sanitize_key((string) ($params['action'] ?? '')); + + if (!$campaignId) { + return MCPHelper::error('invalid_param', __('campaign_id is required', 'fluent-crm')); + } + if (!in_array($action, ['schedule', 'unschedule', 'pause', 'resume', 'duplicate', 'delete'], true)) { + return MCPHelper::error('invalid_param', __('Invalid action', 'fluent-crm')); + } + + // Bypass the type scope so one-off sends are reachable. Allowed + // actions on one-offs are restricted below — operator-test + // report 2026-05-07 #4. + $campaign = Campaign::withoutGlobalScope('type')->find($campaignId); + if (!$campaign) { + return MCPHelper::error('not_found', __('Campaign not found', 'fluent-crm'), ['campaign_id' => $campaignId]); + } + + if ($campaign->type === 'custom_email_campaign' && $action !== 'delete') { + return MCPHelper::error('not_supported', __('One-off email sends only support action=delete. They do not have a marketing-campaign lifecycle (schedule/pause/resume/duplicate).', 'fluent-crm'), [ + 'campaign_id' => $campaignId, + 'campaign_type' => 'one_off', + 'allowed_actions' => ['delete'], + ]); + } + + $previousStatus = $campaign->status; + + switch ($action) { + case 'schedule': + return self::actionSchedule($campaign, $params, $previousStatus); + case 'unschedule': + return self::actionUnschedule($campaign, $previousStatus); + case 'pause': + return self::actionPause($campaign, $previousStatus); + case 'resume': + return self::actionResume($campaign, $previousStatus); + case 'duplicate': + return self::actionDuplicate($campaign, $params); + case 'delete': + return self::actionDelete($campaign); + } + + return MCPHelper::error('invalid_param', __('Unhandled action', 'fluent-crm')); + } + + private static function actionSchedule($campaign, $params, $previousStatus) + { + if ($campaign->status !== 'draft') { + return MCPHelper::error('not_supported', __('Only draft campaigns can be scheduled', 'fluent-crm'), [ + 'status' => $campaign->status, + ]); + } + + $sendingType = $params['sending_type'] ?? ($params['scheduled_at'] ?? null ? 'schedule' : 'instant'); + + if ($sendingType === 'instant') { + $scheduleAt = null; + } elseif ($sendingType === 'range_schedule') { + $range = $params['schedule_range'] ?? []; + if (!is_array($range) || count($range) !== 2) { + return MCPHelper::error('invalid_param', __('schedule_range must be [start, end]', 'fluent-crm')); + } + // Each end gets the same tz-aware parse as a single scheduled_at. + $rangeStart = MCPHelper::validateScheduledAt(sanitize_text_field($range[0])); + if (is_wp_error($rangeStart)) { + return $rangeStart; + } + $rangeEnd = MCPHelper::validateScheduledAt(sanitize_text_field($range[1])); + if (is_wp_error($rangeEnd)) { + return $rangeEnd; + } + if ($rangeEnd->getTimestamp() <= $rangeStart->getTimestamp()) { + return MCPHelper::error('invalid_param', __('schedule_range end must be after the start time', 'fluent-crm'), [ + 'start' => $rangeStart->format('Y-m-d H:i:s'), + 'end' => $rangeEnd->format('Y-m-d H:i:s'), + ]); + } + $scheduleAt = [ + $rangeStart->format('Y-m-d H:i:s'), + $rangeEnd->format('Y-m-d H:i:s'), + ]; + } else { + $rawInput = sanitize_text_field((string) ($params['scheduled_at'] ?? '')); + if (!$rawInput) { + return MCPHelper::error('invalid_param', __('scheduled_at is required for sending_type=schedule', 'fluent-crm')); + } + $dt = MCPHelper::validateScheduledAt($rawInput); + if (is_wp_error($dt)) { + return $dt; + } + // Store as a site-tz mysql string so MySQL has no ambiguity and + // the read path doesn't have to guess which tz the row is in + // (operator-test report 2026-05-07 #3). + $scheduleAt = $dt->format('Y-m-d H:i:s'); + } + + // Auto-commit recipients if not yet set so we can read recipients_count. + if (!$campaign->recipients_count) { + $settings = is_array($campaign->settings) ? $campaign->settings : (array) maybe_unserialize($campaign->settings); + if (!empty($settings['subscribers']) || !empty($settings['excludedSubscribers'])) { + $count = (new Campaign())->getSubscriberIdsCountBySegmentSettings($settings); + $campaign->recipients_count = (int) $count; + $campaign->save(); + } + } + + if (!$campaign->recipients_count) { + return MCPHelper::error('validation_failed', __('No recipients found for this campaign', 'fluent-crm')); + } + + // Now apply the same transition the controller does. + $settings = is_array($campaign->settings) ? $campaign->settings : (array) maybe_unserialize($campaign->settings); + + if ($scheduleAt === null) { + $settings['sending_type'] = 'instant'; + $update = [ + 'status' => 'processing', + 'updated_at' => fluentCrmTimestamp(), + 'scheduled_at' => fluentCrmTimestamp(), + 'recipients_count' => 0, + 'settings' => $settings, + ]; + } elseif (is_array($scheduleAt)) { + $settings['sending_type'] = 'range_schedule'; + $settings['schedule_range'] = [strtotime($scheduleAt[0]), strtotime($scheduleAt[1])]; + $update = [ + 'status' => 'pending-scheduled', + 'updated_at' => fluentCrmTimestamp(), + 'scheduled_at' => $scheduleAt[0], + 'recipients_count' => 0, + 'settings' => $settings, + ]; + } else { + $settings['sending_type'] = 'schedule'; + $update = [ + 'status' => 'pending-scheduled', + 'updated_at' => fluentCrmTimestamp(), + 'scheduled_at' => $scheduleAt, + 'recipients_count' => 0, + 'settings' => $settings, + ]; + } + + // Only seed trackers from site defaults when the campaign hasn't + // explicitly set them already (review #31). The previous + // unconditional override clobbered an agent's + // settings.click_tracker='yes' to the site default 'anonymous'. + if (!isset($update['settings']['click_tracker']) || $update['settings']['click_tracker'] === '') { + $update['settings']['click_tracker'] = fluentcrmTrackClicking(); + } + if (!isset($update['settings']['open_tracker']) || $update['settings']['open_tracker'] === '') { + $update['settings']['open_tracker'] = fluentcrmTrackEmailOpen(); + } + $update['settings'] = maybe_serialize($update['settings']); + + $updated = Campaign::where('id', $campaign->id)->where('status', 'draft')->update($update); + if (!$updated) { + return MCPHelper::error('failed', __('Could not transition campaign — status changed concurrently', 'fluent-crm')); + } + + // Wipe pre-processed emails only after the draft-status transition wins. + \FluentCrm\App\Models\CampaignEmail::where('campaign_id', $campaign->id)->delete(); + fluentcrm_update_campaign_meta($campaign->id, '_recipient_processed', 0); + fluentcrm_update_campaign_meta($campaign->id, '_last_recipient_id', 0); + + $campaign = Campaign::find($campaign->id); + fluentcrm_update_campaign_meta($campaign->id, '_campaign_sent_by', get_current_user_id()); + + if ($scheduleAt) { + do_action('fluent_crm/campaign_scheduled', $campaign, $campaign->scheduled_at); + } else { + do_action('fluent_crm/campaign_set_send_now', $campaign); + } + + return [ + 'ok' => true, + 'action' => 'schedule', + 'campaign' => self::getCampaign(['campaign_id' => (int) $campaign->id]), + 'previous_status' => $previousStatus, + 'current_status' => $campaign->status, + 'scheduled_at' => MCPHelper::toIso8601($campaign->scheduled_at), + 'scheduled_at_resolved' => MCPHelper::formatScheduledAtDual($campaign->scheduled_at), + ]; + } + + private static function actionUnschedule($campaign, $previousStatus) + { + if (!in_array($campaign->status, ['scheduled', 'pending-scheduled', 'processing'], true)) { + return MCPHelper::error('not_supported', __('Campaign is not in a schedulable state', 'fluent-crm'), [ + 'status' => $campaign->status, + ]); + } + + $campaign->status = 'draft'; + // Clear the stale schedule timestamp so UIs (and get-campaign) don't + // misreport "scheduled for ..." after the campaign has been + // un-scheduled (review #32). + $campaign->scheduled_at = null; + $campaign->save(); + + \FluentCrm\App\Models\CampaignEmail::where('campaign_id', $campaign->id)->delete(); + \FluentCrm\App\Models\CampaignEmail::withoutGlobalScope('type') + ->where('campaign_id', $campaign->id) + ->whereIn('status', ['scheduled', 'scheduling']) + ->delete(); + + return [ + 'ok' => true, + 'action' => 'unschedule', + 'campaign' => self::getCampaign(['campaign_id' => (int) $campaign->id]), + 'previous_status' => $previousStatus, + 'current_status' => $campaign->status, + ]; + } + + private static function actionPause($campaign, $previousStatus) + { + if ($campaign->status !== 'working') { + return MCPHelper::error('not_supported', __('Only working campaigns can be paused', 'fluent-crm'), [ + 'status' => $campaign->status, + ]); + } + $campaign->status = 'paused'; + $campaign->save(); + \FluentCrm\App\Models\CampaignEmail::where('campaign_id', $campaign->id) + ->whereIn('status', ['scheduled', 'pending', 'scheduling']) + ->update(['status' => 'paused']); + + return [ + 'ok' => true, + 'action' => 'pause', + 'campaign' => self::getCampaign(['campaign_id' => (int) $campaign->id]), + 'previous_status' => $previousStatus, + 'current_status' => 'paused', + ]; + } + + private static function actionResume($campaign, $previousStatus) + { + if ($campaign->status !== 'paused') { + return MCPHelper::error('not_supported', __('Only paused campaigns can be resumed', 'fluent-crm'), [ + 'status' => $campaign->status, + ]); + } + $campaign->status = 'working'; + $campaign->save(); + \FluentCrm\App\Models\CampaignEmail::where('campaign_id', $campaign->id) + ->where('status', 'paused') + ->update([ + 'status' => 'scheduled', + 'scheduled_at' => current_time('mysql'), + ]); + + return [ + 'ok' => true, + 'action' => 'resume', + 'campaign' => self::getCampaign(['campaign_id' => (int) $campaign->id]), + 'previous_status' => $previousStatus, + 'current_status' => 'working', + ]; + } + + private static function actionDuplicate($campaign, $params) + { + $newTitle = isset($params['new_title']) && $params['new_title'] !== '' + ? sanitize_text_field((string) $params['new_title']) + : __('[Duplicate] ', 'fluent-crm') . $campaign->title; + + $newCampaign = [ + 'title' => $newTitle, + 'slug' => $campaign->slug . '-' . time(), + 'email_body' => $campaign->email_body, + 'status' => 'draft', + 'template_id' => $campaign->template_id, + 'email_subject' => $campaign->email_subject, + 'email_pre_header' => $campaign->email_pre_header, + 'utm_status' => $campaign->utm_status, + 'utm_source' => $campaign->utm_source, + 'utm_medium' => $campaign->utm_medium, + 'utm_campaign' => $campaign->utm_campaign, + 'utm_term' => $campaign->utm_term, + 'utm_content' => $campaign->utm_content, + 'design_template' => $campaign->design_template, + 'created_by' => get_current_user_id(), + 'settings' => $campaign->settings, + ]; + + $copy = Campaign::create($newCampaign); + + if (method_exists($campaign, 'getFormattedLabels')) { + $labelIds = $campaign->getFormattedLabels()->pluck('id')->toArray(); + if ($labelIds && method_exists($copy, 'attachLabels')) { + $copy->attachLabels($labelIds); + } + } + if (method_exists($copy, 'duplicateSubjects')) { + $copy->duplicateSubjects($campaign); + } + + do_action('fluent_crm/campaign_duplicated', $copy, $campaign); + + return [ + 'ok' => true, + 'action' => 'duplicate', + 'campaign' => self::getCampaign(['campaign_id' => (int) $copy->id]), + 'duplicated_from_id' => (int) $campaign->id, + ]; + } + + private static function actionDelete($campaign) + { + if (!\FluentCrm\App\Services\PermissionManager::currentUserCan('fcrm_manage_email_delete')) { + return MCPHelper::error('forbidden', __('Deleting campaigns requires fcrm_manage_email_delete', 'fluent-crm')); + } + + $campaignId = (int) $campaign->id; + if (method_exists($campaign, 'deleteCampaignData')) { + $campaign->deleteCampaignData(); + } + $campaign->delete(); + do_action('fluent_crm/campaign_deleted', $campaignId); + + return [ + 'ok' => true, + 'action' => 'delete', + 'deleted_id' => $campaignId, + ]; + } +} diff --git a/wp-content/plugins/fluent-crm/app/Modules/MCP/Tools/ContactTools.php b/wp-content/plugins/fluent-crm/app/Modules/MCP/Tools/ContactTools.php new file mode 100644 index 0000000..774c2cc --- /dev/null +++ b/wp-content/plugins/fluent-crm/app/Modules/MCP/Tools/ContactTools.php @@ -0,0 +1,948 @@ +paginate(); + + return MCPHelper::formatContactList($paginated, !empty($params['include_custom_fields'])); + } + + // ----------------------------------------------------------------- + // Read: get-contact + // ----------------------------------------------------------------- + + public static function getContact($params) + { + $params = (array) $params; + + $defaultIncludes = ['notes', 'email_history', 'automations']; + $include = isset($params['include']) && is_array($params['include']) && $params['include'] + ? array_values(array_intersect( + $params['include'], + ['notes', 'email_history', 'automations', 'activity', 'purchase_history', 'support_tickets', 'ai_summary', 'info_widgets'] + )) + : $defaultIncludes; + + $contactId = isset($params['contact_id']) ? (int) $params['contact_id'] : 0; + $email = isset($params['email']) ? sanitize_email($params['email']) : ''; + + $with = ['tags', 'lists']; + + $subscriber = null; + if ($contactId) { + $subscriber = Subscriber::with($with)->find($contactId); + } elseif ($email) { + $subscriber = Subscriber::with($with)->where('email', $email)->first(); + } + + if (!$subscriber) { + if (!$contactId && !$email) { + return MCPHelper::error('invalid_param', __('Provide contact_id or email', 'fluent-crm')); + } + return MCPHelper::error('not_found', __('Contact not found', 'fluent-crm'), array_filter([ + 'contact_id' => $contactId ?: null, + 'email' => $email ?: null, + ])); + } + + $data = MCPHelper::formatContactForMCP($subscriber, ['include' => $include]); + + // Defaults already inlined by formatContactForMCP — fill the optional ones. + if (in_array('activity', $include, true)) { + $data['activity'] = self::buildActivityTimeline($subscriber); + } + + if (in_array('purchase_history', $include, true)) { + $data['purchase_history'] = self::buildPurchaseHistory($subscriber); + } + + if (in_array('support_tickets', $include, true)) { + $data['support_tickets'] = self::buildSupportTickets($subscriber); + } + + if (in_array('info_widgets', $include, true)) { + $data['info_widgets'] = self::buildInfoWidgets($subscriber); + } + + if (in_array('ai_summary', $include, true)) { + $data['ai_summary'] = self::buildAiSummary($subscriber, !empty($params['generate_ai_summary'])); + } + + // Status-related context — surfaced inline so the agent can see why a + // contact is unsubscribed without an extra call. + if (in_array($subscriber->status, ['unsubscribed', 'bounced', 'complained', 'spammed'], true)) { + $data['unsubscribe_reason'] = method_exists($subscriber, 'unsubscribeReason') + ? $subscriber->unsubscribeReason() + : null; + } + + return $data; + } + + /** + * Activity timeline = tracked events. The fc_event_tracking table is + * created by the free plugin's migrations but may not exist on legacy + * installs that never ran the migration. Probe with SHOW TABLES so we + * never trigger wpdb's print_error (which leaks HTML into the response + * body before the JSON envelope, even when the exception is caught). + */ + private static function buildActivityTimeline($subscriber) + { + global $wpdb; + $tableName = $wpdb->prefix . 'fc_event_tracking'; + $exists = $wpdb->get_var($wpdb->prepare('SHOW TABLES LIKE %s', $tableName)) === $tableName; + if (!$exists) { + return []; + } + + try { + $events = $subscriber->trackingEvents() + ->orderBy('id', 'DESC') + ->limit(50) + ->get(); + } catch (\Throwable $e) { + return []; + } + + $out = []; + foreach ($events as $event) { + $out[] = [ + 'id' => (int) $event->id, + 'event_key' => $event->event_key, + 'title' => $event->title, + 'value' => $event->value, + 'provider' => $event->provider ?? null, + 'counter' => isset($event->counter) ? (int) $event->counter : null, + 'created_at' => MCPHelper::toIso8601($event->created_at), + ]; + } + return $out; + } + + private static function buildPurchaseHistory($subscriber) + { + /** + * Resolved per the existing FluentCRM commerce-provider filter chain. + */ + $provider = apply_filters('fluentcrm_commerce_provider', ''); + if (!$provider) { + return []; + } + $stat = apply_filters('fluent_crm/contact_purchase_stat_' . $provider, [], $subscriber->id); + return is_array($stat) ? $stat : []; + } + + private static function buildSupportTickets($subscriber) + { + // FluentSupport hooks this filter when active. Empty otherwise. + return apply_filters('fluentcrm_get_support_tickets', [], $subscriber); + } + + private static function buildInfoWidgets($subscriber) + { + /** + * Filter that integrators (Pro, FluentSupport, FluentCart, etc.) push + * widget data into. Surface the raw filter result; ContextTools agents + * can interpret what's there. + */ + $widgets = apply_filters('fluent_crm/contact_info_widgets', [], $subscriber); + return is_array($widgets) ? $widgets : []; + } + + private static function buildAiSummary($subscriber, $generate = false) + { + $cached = fluentcrm_get_subscriber_meta($subscriber->id, '_ai_summary'); + + if ($cached && !$generate) { + return [ + 'summary' => is_array($cached) ? ($cached['summary'] ?? '') : (string) $cached, + 'generated_at' => is_array($cached) ? ($cached['generated_at'] ?? null) : null, + 'cached' => true, + ]; + } + + if (!$generate) { + return null; + } + + // Honor existing AI controller; if it's missing or disabled, return + // a structured signal rather than throwing. + if (!class_exists('FluentCrm\\App\\Http\\Controllers\\AiController')) { + return ['summary' => null, 'cached' => false, 'error' => 'ai_unavailable']; + } + + $aiSettings = fluentcrm_get_option('ai_settings', []); + if (empty($aiSettings['active_provider'])) { + return ['summary' => null, 'cached' => false, 'error' => 'ai_provider_not_configured']; + } + + // Generation requires the existing controller's prompt + provider call; + // surface a dependency_missing-style signal so the agent can prompt the + // user to enable AI rather than blocking the read. + return [ + 'summary' => null, + 'cached' => false, + 'error' => 'generation_not_supported_in_mcp_v1', + 'note' => 'Trigger AI summary from the contact profile UI; cached value will appear on subsequent get-contact calls.', + ]; + } + + // ----------------------------------------------------------------- + // Write: upsert-contact + // ----------------------------------------------------------------- + + public static function upsertContact($params) + { + $params = (array) $params; + + $contactId = isset($params['contact_id']) ? (int) $params['contact_id'] : 0; + $email = isset($params['email']) ? sanitize_email($params['email']) : ''; + $newEmail = isset($params['new_email']) ? sanitize_email($params['new_email']) : ''; + + if (!$contactId && !$email) { + return MCPHelper::error('invalid_param', __('Provide contact_id or email', 'fluent-crm')); + } + + $existing = null; + if ($contactId) { + $existing = Subscriber::find($contactId); + if (!$existing) { + return MCPHelper::error('not_found', __('Contact not found', 'fluent-crm'), ['contact_id' => $contactId]); + } + // Lookup-by-id with email mismatch is fine — id wins. + $email = $existing->email; + } else { + $existing = Subscriber::where('email', $email)->first(); + } + + $ifExists = $params['if_exists'] ?? 'merge'; + if ($existing && $ifExists === 'skip') { + return [ + 'ok' => true, + 'action' => 'skipped', + 'contact' => MCPHelper::formatContactForMCP($existing, ['include' => ['notes', 'email_history', 'automations']]), + 'changes' => null, + ]; + } + if ($existing && $ifExists === 'error') { + return MCPHelper::error('contact_exists', __('A contact with this email already exists', 'fluent-crm'), [ + 'id' => (int) $existing->id, + ]); + } + + // Re-check the escalating capability if the agent asked us to create + // missing tags/lists — defense in depth, even though the + // permission_callback already enforced the base cap. + $autoCreateTags = !empty($params['auto_create_tags']); + $autoCreateLists = !empty($params['auto_create_lists']); + if (($autoCreateTags || $autoCreateLists) + && !\FluentCrm\App\Services\PermissionManager::currentUserCan('fcrm_manage_contact_cats')) { + return MCPHelper::error('forbidden', __('Creating new tags/lists requires fcrm_manage_contact_cats', 'fluent-crm')); + } + + // Resolve add/remove segment payloads up-front so we can mention + // resolution failures in the response without partially applying. + $addTags = MCPHelper::resolveTagIds($params['add_tags'] ?? [], $autoCreateTags); + $removeTags = MCPHelper::resolveTagIds($params['remove_tags'] ?? [], false); + $addLists = MCPHelper::resolveListIds($params['add_lists'] ?? [], $autoCreateLists); + $removeLists = MCPHelper::resolveListIds($params['remove_lists'] ?? [], false); + + // Capture the pre-rename / pre-update snapshot fields BEFORE any + // mutation. The rename block below sets $existing->email to the new + // value, so reading $existing->email after that point would return + // the new email — operator-test report 2026-05-07 #9. The full + // snapshot also feeds diffFields() so fields_updated correctly + // reports 'email' on a rename. + $previousStatus = $existing ? $existing->status : null; + $previousEmail = $existing ? $existing->email : null; + $previousSnapshot = $existing ? self::snapshotCompareFields($existing) : null; + + // Email rename: when an existing contact + new_email is provided, do + // the rename in-place on the existing row BEFORE delegating to + // createOrUpdate. createOrUpdate looks up by email — passing it the + // new_email would not find a row and would create a new contact + // (review B1 round 3). The save fires fluent_crm/contact_email_changed + // through Subscriber::updateOrCreate's normal path because we then + // call it with the new email as the lookup key. + if ($existing && $newEmail && $newEmail !== $existing->email) { + $oldEmail = $existing->email; + // Make sure the new email isn't already used by another contact. + $clash = Subscriber::where('email', $newEmail)->where('id', '!=', $existing->id)->first(); + if ($clash) { + return MCPHelper::error('contact_exists', __('Another contact already uses the new_email — refusing to merge silently. Resolve manually or pick a different new_email.', 'fluent-crm'), [ + 'new_email' => $newEmail, + 'conflict_id' => (int) $clash->id, + 'subject_id' => (int) $existing->id, + ]); + } + $existing->email = $newEmail; + $existing->save(); + do_action('fluent_crm/contact_email_changed', $existing, $oldEmail); + } + + // Build the upsert payload — only fields actually provided. Lookup + // email is the post-rename value (so createOrUpdate finds the same + // row we just renamed). + $payload = [ + 'email' => $existing && $newEmail ? $newEmail : ($email ?: ($existing->email ?? null)), + ]; + + $passthru = ['first_name', 'last_name', 'prefix', 'phone', 'status', 'contact_type', 'date_of_birth', 'timezone', 'source']; + foreach ($passthru as $field) { + if (array_key_exists($field, $params) && $params[$field] !== null && $params[$field] !== '') { + $payload[$field] = $params[$field]; + } + } + + self::applyAddressShape($payload, $params['address'] ?? null); + + if (!empty($params['custom_fields']) && is_array($params['custom_fields'])) { + // Validate against the registered schema. Unknown keys would + // otherwise be silently dropped (operator-test report + // 2026-05-07 #6) — fail closed so the agent can either + // correct the slug or call get-crm-context for the schema. + $diff = MCPHelper::diffCustomFields($params['custom_fields']); + if (!empty($diff['unknown'])) { + return MCPHelper::error('invalid_param', __('custom_fields contains slugs not in the contact custom-field schema. Refusing — silent-dropping makes the agent think the value persisted.', 'fluent-crm'), [ + 'unknown_custom_field_slugs' => $diff['unknown'], + 'allowed_custom_field_slugs' => MCPHelper::knownContactCustomFieldSlugs(), + 'tip' => 'Call get-crm-context and read enums.custom_fields_schema (or call options for the live registry) before retrying.', + ]); + } + $payload['custom_values'] = $diff['known']; + } + + // Only stamp source='mcp' on creation. On update, omit the field + // entirely so the model preserves whatever signup source the contact + // already has ("web", "checkout", "import", etc.). The agent can + // still pass an explicit `source` to override this when needed. + if (!$existing && (!isset($payload['source']) || $payload['source'] === '')) { + $payload['source'] = 'mcp'; + } elseif ($existing && (!isset($payload['source']) || $payload['source'] === '')) { + unset($payload['source']); + } + + // The `Subscriber::updateOrCreate` path forwards through + // FluentCrmApi('contacts')->createOrUpdate which fires the + // contact-created/updated and status-change hooks we need. + // ($previousStatus / $previousEmail were captured above, before + // the rename block — see operator-test report 2026-05-07 #9.) + $forceUpdate = true; + $contact = FluentCrmApi('contacts')->createOrUpdate($payload, $forceUpdate, false); + + if (!$contact) { + return MCPHelper::error('failed', __('Could not create or update the contact', 'fluent-crm')); + } + + $action = !empty($contact->wasRecentlyCreated) ? 'created' : 'updated'; + + // Apply delta segment changes. + $tagsAdded = []; + $tagsRemoved = []; + $listsAdded = []; + $listsRemoved = []; + + if (!empty($addTags['ids'])) { + $contact->attachTags($addTags['ids']); + foreach ($addTags['ids'] as $id) { + $tagsAdded[] = ['id' => (int) $id]; + } + } + if (!empty($removeTags['ids'])) { + $contact->detachTags($removeTags['ids']); + foreach ($removeTags['ids'] as $id) { + $tagsRemoved[] = ['id' => (int) $id]; + } + } + if (!empty($addLists['ids'])) { + $contact->attachLists($addLists['ids']); + foreach ($addLists['ids'] as $id) { + $listsAdded[] = ['id' => (int) $id]; + } + } + if (!empty($removeLists['ids'])) { + $contact->detachLists($removeLists['ids']); + foreach ($removeLists['ids'] as $id) { + $listsRemoved[] = ['id' => (int) $id]; + } + } + + // Optional double opt-in trigger for newly-pending contacts. + if ($contact->status === 'pending' && !empty($params['double_optin'])) { + $contact->sendDoubleOptinEmail(); + } + + // Status-change reason: drop a system-style note for audit. + if (!empty($params['status_change_reason']) && $previousStatus && $previousStatus !== $contact->status) { + \FluentCrm\App\Models\SubscriberNote::create([ + 'subscriber_id' => $contact->id, + 'type' => 'note', + 'title' => __('Status changed via MCP', 'fluent-crm'), + 'description' => sanitize_text_field((string) $params['status_change_reason']), + ]); + } + + $contact = Subscriber::with(['tags', 'lists'])->find($contact->id); + + return [ + 'ok' => true, + 'action' => $action, + 'contact' => MCPHelper::formatContactForMCP($contact, ['include' => ['notes', 'email_history', 'automations']]), + 'changes' => [ + 'fields_updated' => self::diffFields($previousSnapshot, $contact), + 'tags_added' => $tagsAdded, + 'tags_removed' => $tagsRemoved, + 'lists_added' => $listsAdded, + 'lists_removed' => $listsRemoved, + 'previous_status' => $previousStatus, + 'current_status' => $contact->status, + 'previous_email' => $previousEmail, + 'current_email' => $contact->email, + 'tags_created' => $addTags['created'], + 'lists_created' => $addLists['created'], + ], + ]; + } + + // ----------------------------------------------------------------- + // Write: delete-contact-note (round 3 review #11) + // ----------------------------------------------------------------- + + public static function deleteContactNote($params) + { + $params = (array) $params; + $noteId = (int) ($params['note_id'] ?? 0); + if (!$noteId) { + return MCPHelper::error('invalid_param', __('note_id is required', 'fluent-crm')); + } + + $note = \FluentCrm\App\Models\SubscriberNote::find($noteId); + if (!$note) { + return MCPHelper::error('not_found', __('Note not found', 'fluent-crm'), ['note_id' => $noteId]); + } + + $deletedId = (int) $note->id; + $subscriberId = (int) $note->subscriber_id; + $title = (string) $note->title; + $note->delete(); + + do_action('fluent_crm/note_deleted', $deletedId, $subscriberId); + + return [ + 'ok' => true, + 'action' => 'deleted', + 'deleted_id' => $deletedId, + 'subscriber_id' => $subscriberId, + 'deleted_title' => $title, + 'note' => __('Note row removed. The contact\'s other notes and email history are unaffected.', 'fluent-crm'), + ]; + } + + /** + * Compute the would-create list for a dry-run preview. Skips numeric + * inputs (those are id lookups, not creation candidates — review B3 + * round 3) and only flags string names that have no existing match. + */ + private static function wouldCreateNames($items, $kind = 'tag') + { + $out = []; + foreach ((array) $items as $item) { + if (is_numeric($item) || $item === '' || $item === null) { + continue; + } + $name = sanitize_text_field((string) $item); + $slug = sanitize_title($name); + if ($kind === 'list') { + $hit = \FluentCrm\App\Models\Lists::where('title', $name)->orWhere('slug', $slug)->first(); + } else { + $hit = \FluentCrm\App\Models\Tag::where('title', $name)->orWhere('slug', $slug)->first(); + } + if (!$hit) { + $out[] = $name; + } + } + return array_values(array_unique($out)); + } + + /** + * Map the agent-facing {line_1, line_2, city, state, postal_code, + * country} shape onto the column-named payload that Subscriber + * createOrUpdate consumes. Mutates $payload by reference. Shared + * between upsert-contact and bulk-upsert-contacts so both stay in + * lock-step (operator-test report 2026-05-07 #5). + */ + private static function applyAddressShape(array &$payload, $address) + { + if (empty($address) || !is_array($address)) { + return; + } + $map = [ + 'line_1' => 'address_line_1', + 'line_2' => 'address_line_2', + 'city' => 'city', + 'state' => 'state', + 'postal_code' => 'postal_code', + 'country' => 'country', + ]; + foreach ($map as $key => $col) { + if (isset($address[$key]) && $address[$key] !== '') { + $payload[$col] = $address[$key]; + } + } + } + + /** + * Snapshot the diff-relevant columns of a Subscriber before any + * in-place mutation (rename, save). diffFields() compares against + * this snapshot so fields_updated stays correct even after the row + * has been written. + * + * @return array + */ + private static function snapshotCompareFields($subscriber) + { + $snapshot = []; + foreach (self::compareFieldNames() as $field) { + $snapshot[$field] = (string) ($subscriber->{$field} ?? ''); + } + return $snapshot; + } + + private static function compareFieldNames() + { + return ['email', 'first_name', 'last_name', 'prefix', 'phone', 'status', 'contact_type', 'address_line_1', 'address_line_2', 'city', 'state', 'postal_code', 'country', 'date_of_birth', 'timezone', 'source']; + } + + /** + * @param array|null $before Snapshot from snapshotCompareFields() + * @param object $after Subscriber model post-save + */ + private static function diffFields($before, $after) + { + if (!$before) { + return ['*']; + } + $changed = []; + foreach (self::compareFieldNames() as $field) { + if (($before[$field] ?? '') !== (string) ($after->{$field} ?? '')) { + $changed[] = $field; + } + } + return $changed; + } + + // ----------------------------------------------------------------- + // Write: bulk-upsert-contacts + // ----------------------------------------------------------------- + + public static function bulkUpsertContacts($params) + { + $params = (array) $params; + $contacts = (array) ($params['contacts'] ?? []); + if (!$contacts) { + return MCPHelper::error('invalid_param', __('contacts is required', 'fluent-crm')); + } + + $maxBatch = (int) apply_filters('fluent_crm/mcp_bulk_cap', 500, 'bulk-upsert-contacts'); + if (count($contacts) > $maxBatch) { + return MCPHelper::error('cap_reached', __('Too many contacts in a single call', 'fluent-crm'), [ + 'max' => $maxBatch, + 'matched' => count($contacts), + ]); + } + + $autoCreateTags = isset($params['auto_create_tags']) ? (bool) $params['auto_create_tags'] : true; + $autoCreateLists = isset($params['auto_create_lists']) ? (bool) $params['auto_create_lists'] : true; + $ifExists = $params['if_exists'] ?? 'merge'; + $doubleOptin = !empty($params['double_optin']); + + if (($autoCreateTags || $autoCreateLists) + && !\FluentCrm\App\Services\PermissionManager::currentUserCan('fcrm_manage_contact_cats')) { + return MCPHelper::error('forbidden', __('Creating new tags/lists requires fcrm_manage_contact_cats', 'fluent-crm')); + } + + $created = $updated = $skipped = $invalid = $warnings = []; + + foreach ($contacts as $row) { + if (!is_array($row) || empty($row['email']) || !is_email($row['email'])) { + $invalid[] = ['email' => $row['email'] ?? null, 'reason' => 'invalid_email']; + continue; + } + + $existing = Subscriber::where('email', sanitize_email($row['email']))->first(); + if ($existing && $ifExists === 'skip') { + $skipped[] = ['id' => (int) $existing->id, 'email' => $existing->email]; + continue; + } + if ($existing && $ifExists === 'error') { + $invalid[] = ['email' => $row['email'], 'reason' => 'contact_exists', 'id' => (int) $existing->id]; + continue; + } + + // Resolve segments per-row. + $tagIds = MCPHelper::resolveTagIds((array) ($row['tags'] ?? []), $autoCreateTags); + $listIds = MCPHelper::resolveListIds((array) ($row['lists'] ?? []), $autoCreateLists); + + $payload = $row; + $payload['tags'] = $tagIds['ids']; + $payload['lists'] = $listIds['ids']; + + // Same address-shape mapping as single upsert. Without this, + // bulk silently dropped the {line_1,...,country} object — + // operator-test report 2026-05-07 #5. + self::applyAddressShape($payload, $row['address'] ?? null); + + // Same rule as upsert-contact: stamp source='mcp_bulk' only on + // creation. On update, preserve the original source unless the + // caller passed one explicitly. + if (!$existing && (!isset($payload['source']) || $payload['source'] === '')) { + $payload['source'] = 'mcp_bulk'; + } elseif ($existing && (!isset($payload['source']) || $payload['source'] === '')) { + unset($payload['source']); + } + + if (!empty($row['custom_fields']) && is_array($row['custom_fields'])) { + // Same diff-against-schema gate as single upsert, but + // surface unknown slugs as a per-row warning so one bad + // row doesn't fail the whole batch (operator-test report + // 2026-05-07 #6). Known keys still persist. + $diff = MCPHelper::diffCustomFields($row['custom_fields']); + if (!empty($diff['unknown'])) { + $warnings[] = [ + 'email' => $row['email'], + 'reason' => 'unknown_custom_field_slugs', + 'unknown_custom_field_slugs' => $diff['unknown'], + ]; + } + $payload['custom_values'] = $diff['known']; + } + + $contact = FluentCrmApi('contacts')->createOrUpdate($payload, true, false); + if (!$contact) { + $invalid[] = ['email' => $row['email'], 'reason' => 'failed_to_save']; + continue; + } + + if ($doubleOptin && $contact->status === 'pending') { + $contact->sendDoubleOptinEmail(); + } + + $entry = [ + 'id' => (int) $contact->id, + 'email' => $contact->email, + 'status' => $contact->status, + ]; + if (!empty($contact->wasRecentlyCreated)) { + $created[] = $entry; + } else { + $updated[] = $entry; + } + } + + return [ + 'ok' => true, + 'summary' => [ + 'created' => count($created), + 'updated' => count($updated), + 'skipped' => count($skipped), + 'invalid' => count($invalid), + 'warnings' => count($warnings), + ], + 'created' => $created, + 'updated' => $updated, + 'skipped' => $skipped, + 'invalid' => $invalid, + 'warnings' => $warnings, + ]; + } + + // ----------------------------------------------------------------- + // Write: delete-contact + // ----------------------------------------------------------------- + + public static function deleteContact($params) + { + $resolved = MCPHelper::resolveContact((array) $params); + if (is_wp_error($resolved)) { + return $resolved; + } + $contact = $resolved; + $deletedId = (int) $contact->id; + $deletedEmail = (string) $contact->email; + $deleteEmails = !isset($params['delete_emails']) ? true : (bool) $params['delete_emails']; + + if ($deleteEmails) { + \FluentCrm\App\Models\CampaignEmail::where('subscriber_id', $deletedId)->delete(); + } + + $ok = \FluentCrm\App\Services\Helper::deleteContacts([$deletedId]); + if (!$ok) { + return MCPHelper::error('failed', __('Could not delete the contact', 'fluent-crm')); + } + + return [ + 'ok' => true, + 'deleted_id' => $deletedId, + 'deleted_email' => $deletedEmail, + 'emails_purged' => (bool) $deleteEmails, + ]; + } + + // ----------------------------------------------------------------- + // Write: apply-segments-to-contacts + // ----------------------------------------------------------------- + + public static function applySegmentsToContacts($params) + { + $params = (array) $params; + + $autoCreateTags = !empty($params['auto_create_tags']); + $autoCreateLists = !empty($params['auto_create_lists']); + if (($autoCreateTags || $autoCreateLists) + && !\FluentCrm\App\Services\PermissionManager::currentUserCan('fcrm_manage_contact_cats')) { + return MCPHelper::error('forbidden', __('Creating new tags/lists requires fcrm_manage_contact_cats', 'fluent-crm')); + } + + $contactIds = isset($params['contact_ids']) ? array_filter(array_map('intval', (array) $params['contact_ids'])) : []; + $filter = $params['filter'] ?? null; + $dryRun = !empty($params['dry_run']); + + if (!$contactIds && empty($filter)) { + return MCPHelper::error('invalid_param', __('Provide contact_ids or filter', 'fluent-crm')); + } + + if ($contactIds && !empty($filter)) { + return MCPHelper::error('invalid_param', __('Provide contact_ids OR filter, not both', 'fluent-crm')); + } + + $cap = (int) apply_filters('fluent_crm/mcp_bulk_cap', 5000, 'apply-segments-to-contacts'); + + if (!$contactIds) { + $validation = MCPHelper::validateUniversalFilter((array) $filter); + if (is_wp_error($validation)) { + return $validation; + } + $args = MCPHelper::buildContactsQueryArgs((array) $filter); + $args['with'] = []; // we just need ids + $cq = new ContactsQuery($args); + MCPHelper::applyDateFilters($cq, (array) $filter); + $query = $cq->getModel(); + + $matched = (int) $query->count(); + // During a dry run, expose the matched count even when it + // exceeds the cap — knowing the size is the whole point of a + // preview. The agent can then batch. + if ($matched > $cap && !$dryRun) { + return MCPHelper::error('cap_reached', __('Too many contacts match the filter', 'fluent-crm'), [ + 'max' => $cap, + 'matched' => $matched, + ]); + } + + $contactIds = array_map('intval', $query->limit($cap)->pluck('id')->toArray()); + // Stash the true matched count so dry_run can echo it (the + // pluck call above only returns up to $cap rows). + $matchedTotal = $matched; + } else { + if (count($contactIds) > $cap && !$dryRun) { + return MCPHelper::error('cap_reached', __('Too many contact_ids in a single call', 'fluent-crm'), [ + 'max' => $cap, + 'matched' => count($contactIds), + ]); + } + $matchedTotal = count($contactIds); + } + + // Resolve segment refs. Auto-create is suppressed during dry runs so + // a preview never leaves orphan tags/lists behind. + $addTags = MCPHelper::resolveTagIds((array) ($params['add_tags'] ?? []), $autoCreateTags && !$dryRun); + $removeTags = MCPHelper::resolveTagIds((array) ($params['remove_tags'] ?? []), false); + $addLists = MCPHelper::resolveListIds((array) ($params['add_lists'] ?? []), $autoCreateLists && !$dryRun); + $removeLists = MCPHelper::resolveListIds((array) ($params['remove_lists'] ?? []), false); + + // Compute the would-create set: name strings the agent supplied that + // don't resolve to an existing tag/list. Numeric inputs are id + // lookups, never creation candidates (review B3 round 3). + $tagsWouldCreate = self::wouldCreateNames((array) ($params['add_tags'] ?? []), 'tag'); + $listsWouldCreate = self::wouldCreateNames((array) ($params['add_lists'] ?? []), 'list'); + + // The "at least one" guard considers what would actually happen — if + // dry_run with names that would create, that IS work, so don't bail. + $hasAnyWork = $addTags['ids'] || $removeTags['ids'] + || $addLists['ids'] || $removeLists['ids'] + || ($dryRun && ($tagsWouldCreate || $listsWouldCreate)); + if (!$hasAnyWork) { + return MCPHelper::error('invalid_param', __('Provide at least one of add_tags, remove_tags, add_lists, remove_lists', 'fluent-crm')); + } + + if ($dryRun) { + $formatRefs = function ($ids) { + $out = []; + foreach ($ids as $id) { + $out[] = ['id' => (int) $id]; + } + return $out; + }; + $exceedsCap = $matchedTotal > $cap; + return [ + 'ok' => true, + 'dry_run' => true, + 'matched_contacts' => $matchedTotal, + 'cap' => $cap, + 'exceeds_cap' => $exceedsCap, + 'batches_required' => $exceedsCap ? (int) ceil($matchedTotal / max(1, $cap)) : 1, + 'applied_to_contacts' => 0, + 'tags_added' => $formatRefs($addTags['ids']), + 'tags_removed' => $formatRefs($removeTags['ids']), + 'lists_added' => $formatRefs($addLists['ids']), + 'lists_removed' => $formatRefs($removeLists['ids']), + 'tags_would_create' => $tagsWouldCreate, + 'lists_would_create' => $listsWouldCreate, + 'note' => $exceedsCap + ? __('Dry run — match exceeds the per-call cap. Apply by passing contact_ids in batches.', 'fluent-crm') + : __('Dry run — nothing was applied. Re-run without dry_run=true to commit.', 'fluent-crm'), + ]; + } + + // Process in chunks so attach/detach don't load thousands of rows at + // once. Each Subscriber attach/detach already de-dupes internally. + // Track the actual touched ids (review P2 #10) so an agent can + // reverse precisely without re-running the original filter — which + // may match a different set after time passes. + $chunkSize = 200; + $applied = 0; + $appliedIds = []; + foreach (array_chunk($contactIds, $chunkSize) as $batchIds) { + $subscribers = Subscriber::whereIn('id', $batchIds)->get(); + foreach ($subscribers as $sub) { + if ($addTags['ids']) { + $sub->attachTags($addTags['ids']); + } + if ($removeTags['ids']) { + $sub->detachTags($removeTags['ids']); + } + if ($addLists['ids']) { + $sub->attachLists($addLists['ids']); + } + if ($removeLists['ids']) { + $sub->detachLists($removeLists['ids']); + } + $applied++; + $appliedIds[] = (int) $sub->id; + } + } + + $formatRefs = function ($ids) { + $out = []; + foreach ($ids as $id) { + $out[] = ['id' => (int) $id]; + } + return $out; + }; + + return [ + 'ok' => true, + 'matched_contacts' => count($contactIds), + 'applied_to_contacts' => $applied, + 'applied_contact_ids' => $appliedIds, + 'tags_added' => $formatRefs($addTags['ids']), + 'tags_removed' => $formatRefs($removeTags['ids']), + 'lists_added' => $formatRefs($addLists['ids']), + 'lists_removed' => $formatRefs($removeLists['ids']), + 'tags_created' => $addTags['created'], + 'lists_created' => $addLists['created'], + 'reverse_with' => __('To reverse: re-call apply-segments-to-contacts with contact_ids=applied_contact_ids and add_*/remove_* swapped.', 'fluent-crm'), + ]; + } + + // ----------------------------------------------------------------- + // Write: add-contact-note + // ----------------------------------------------------------------- + + public static function addContactNote($params) + { + $params = (array) $params; + + $resolved = MCPHelper::resolveContact($params); + if (is_wp_error($resolved)) { + return $resolved; + } + $subscriber = $resolved; + + $title = trim((string) ($params['title'] ?? '')); + $description = (string) ($params['description'] ?? ''); + $type = sanitize_key($params['type'] ?? 'note'); + $allowedTypes = ['note', 'call', 'email', 'meeting', 'quote']; + if (!in_array($type, $allowedTypes, true)) { + $type = 'note'; + } + + if ($title === '' || $description === '') { + return MCPHelper::error('invalid_param', __('title and description are required', 'fluent-crm')); + } + + $noteData = [ + 'subscriber_id' => $subscriber->id, + 'type' => $type, + 'title' => $title, + 'description' => $description, + 'created_at' => !empty($params['created_at']) ? sanitize_text_field($params['created_at']) : current_time('mysql'), + ]; + + // Run through the same filter the controller does so smartcodes resolve. + $noteData['description'] = apply_filters('fluent_crm/parse_campaign_email_text', $noteData['description'], $subscriber); + $noteData = \FluentCrm\App\Services\Sanitize::contactNote($noteData); + + $note = \FluentCrm\App\Models\SubscriberNote::create(wp_unslash($noteData)); + + do_action('fluent_crm/note_added', $note, $subscriber, $noteData); + + return [ + 'ok' => true, + 'note' => MCPHelper::formatNoteForMCP($note), + ]; + } +} diff --git a/wp-content/plugins/fluent-crm/app/Modules/MCP/Tools/ContextTools.php b/wp-content/plugins/fluent-crm/app/Modules/MCP/Tools/ContextTools.php new file mode 100644 index 0000000..1e1cbf0 --- /dev/null +++ b/wp-content/plugins/fluent-crm/app/Modules/MCP/Tools/ContextTools.php @@ -0,0 +1,451 @@ + (int) $userId, + 'name' => $user ? $user->display_name : null, + 'email' => $user ? $user->user_email : null, + 'is_admin' => (bool) $isAdmin, + 'permissions' => array_values(PermissionManager::currentUserPermissions(false)), + ]; + + $proActive = defined('FLUENTCAMPAIGN'); + $aiState = self::detectAiProvider(); + + $site = [ + 'site_url' => site_url(), + 'fluent_crm_version' => defined('FLUENTCRM_PLUGIN_VERSION') ? FLUENTCRM_PLUGIN_VERSION : null, + 'fluent_campaign_active' => $proActive, + 'ai_provider_configured' => $aiState['configured'], + 'ai_provider' => $aiState['provider'], + 'timezone' => fluentCrmGetTimezoneString(), + 'current_time' => fluentCrmTimestamp(), + ]; + + $stats = self::buildStats(); + + $tags = self::topTagsForContext(); + $lists = self::topListsForContext(); + + $availableTriggers = self::formatRefList(apply_filters('fluentcrm_funnel_triggers', []), 'trigger_name'); + $availableActions = self::formatRefList(apply_filters('fluentcrm_funnel_blocks', [], null), 'action_name'); + + $enums = [ + 'contact_statuses' => array_values(fluentcrm_subscriber_statuses()), + 'sms_statuses' => array_values(fluentcrm_subscriber_sms_statuses()), + 'contact_types' => array_values(fluentcrm_contact_types()), + 'campaign_statuses' => ['draft', 'scheduled', 'pending-scheduled', 'processing', 'working', 'paused', 'archived'], + 'design_templates' => array_keys(self::allowedDesignTemplates()), + 'funnel_statuses' => ['draft', 'published'], + 'funnel_subscriber_statuses' => ['active', 'waiting', 'completed', 'cancelled', 'skipped'], + 'note_types' => ['note', 'call', 'email', 'meeting', 'quote'], + ]; + + $defaultSender = self::buildDefaultSender(); + + $customFieldsSchema = ['contact' => self::buildCustomFieldSchema()]; + + return [ + 'you' => $you, + 'site' => $site, + 'stats' => $stats, + 'tags' => $tags, + 'lists' => $lists, + 'available_triggers' => $availableTriggers, + 'available_actions' => $availableActions, + 'enums' => $enums, + 'default_sender' => $defaultSender, + 'custom_fields_schema' => $customFieldsSchema, + 'smart_codes' => self::buildSmartCodes(), + 'safety_levels' => self::buildSafetyLevels(), + 'rate_hints' => self::buildRateHints(), + 'mcp_capabilities' => self::buildCapabilities(), + 'guidelines' => self::buildGuidelines(), + ]; + } + + /** + * Per-tool safety classification — round-3 review R3. + * + * Lets agents branch on a stable code instead of parsing tool + * descriptions or relying on annotations alone (which only + * differentiate readonly / destructive in two coarse buckets). + * + * Levels: + * safe_render — no DB writes, no sends, no side effects + * readonly — DB reads only + * creates_or_mutates_draft — writes data the user can still review/cancel + * mutating_with_dry_run — writes data; dry_run preview available + * destructive_send — actually sends mail to a real recipient + * destructive_irrecoverable — deletion / cannot be undone + */ + private static function buildSafetyLevels() + { + return apply_filters('fluent_crm/mcp_safety_levels', [ + 'fluent-crm/get-crm-context' => 'readonly', + 'fluent-crm/list-contacts' => 'readonly', + 'fluent-crm/get-contact' => 'readonly', + 'fluent-crm/list-campaigns' => 'readonly', + 'fluent-crm/get-campaign' => 'readonly', + 'fluent-crm/list-automations' => 'readonly', + 'fluent-crm/list-funnel-subscribers' => 'readonly', + 'fluent-crm/get-automation' => 'readonly', + 'fluent-crm/list-sequences' => 'readonly', + 'fluent-crm/get-sequence' => 'readonly', + 'fluent-crm/estimate-dynamic-segment' => 'readonly', + 'fluent-crm/upsert-contact' => 'creates_or_mutates_draft', + 'fluent-crm/bulk-upsert-contacts' => 'creates_or_mutates_draft', + 'fluent-crm/add-contact-note' => 'creates_or_mutates_draft', + 'fluent-crm/upsert-campaign' => 'creates_or_mutates_draft', + 'fluent-crm/apply-segments-to-contacts' => 'mutating_with_dry_run', + 'fluent-crm/manage-sequence-subscribers' => 'mutating_with_dry_run', + 'fluent-crm/update-contact-automation-status' => 'creates_or_mutates_draft', + 'fluent-crm/manage-tag' => 'destructive_irrecoverable', + 'fluent-crm/manage-list' => 'destructive_irrecoverable', + 'fluent-crm/delete-contact' => 'destructive_irrecoverable', + 'fluent-crm/delete-contact-note' => 'destructive_irrecoverable', + 'fluent-crm/send-test-email' => 'safe_render', + 'fluent-crm/send-email-to-contact' => 'destructive_send', + // change-campaign-status: per-action — schedule + delete are + // destructive in different ways. Annotation already flags it; + // the description spells out which actions are dangerous. + 'fluent-crm/change-campaign-status' => 'destructive_send', + ]); + } + + /** + * Rate / cap hints — round-3 review R4. + * + * Surfaces the limits that are otherwise embedded only in tool + * descriptions ("Cap 5000 per call"). Lets agents pre-validate + * batch sizes deterministically. + */ + private static function buildRateHints() + { + $cap = (int) apply_filters('fluent_crm/mcp_bulk_cap', 5000, 'apply-segments-to-contacts'); + return apply_filters('fluent_crm/mcp_rate_hints', [ + 'fluent-crm/bulk-upsert-contacts' => ['max_per_call' => 500, 'recommended_batch' => 100], + 'fluent-crm/apply-segments-to-contacts' => ['max_per_call' => $cap], + 'fluent-crm/manage-sequence-subscribers' => ['max_per_call' => $cap], + 'fluent-crm/send-email-to-contact' => ['note' => 'Goes through the normal queue + bounce handling — site-level rate limits apply (see settings.email_settings.emails_per_second).'], + ]); + } + + /** + * Versioned capabilities map — round-3 review R9. + * + * Lets agents adapt their strategy across MCP versions without trial + * and error. Bump `version` whenever a capability is added/removed. + */ + private static function buildCapabilities() + { + return apply_filters('fluent_crm/mcp_capabilities', [ + 'version' => '1.4.0', + 'supports' => [ + 'dry_run_apply_segments', + 'send_test_email', + 'smart_codes_discovery', + 'safety_levels', + 'rate_hints', + 'manage_tags_lists', + 'delete_contact_note', + 'one_off_email_send', + 'campaign_warnings', + 'auto_suffix_title_conflict', + 'list_funnel_subscribers', + 'applied_contact_ids_return', + 'tracking_mode_aware_stats', + 'recipients_strict_validation', + 'advanced_filters_provider_validation', + ], + 'deprecated' => [], + 'breaking_changes_pending' => [], + ]); + } + + private static function buildStats() + { + $stats = (new Stats())->getCounts(); + + $todayStart = (new \DateTime('today', new \DateTimeZone(fluentCrmGetTimezoneString())))->format('Y-m-d H:i:s'); + $sevenDaysAgo = gmdate('Y-m-d H:i:s', time() - (7 * DAY_IN_SECONDS)); + + return [ + 'contacts_total' => Subscriber::count(), + 'contacts_subscribed' => (int) ($stats['total_subscribers']['count'] ?? Subscriber::where('status', 'subscribed')->count()), + 'contacts_new_today' => Subscriber::where('created_at', '>=', $todayStart)->count(), + 'campaigns_sent_last_7d' => \FluentCrm\App\Models\Campaign::where('status', 'archived') + ->where('updated_at', '>=', $sevenDaysAgo) + ->count(), + 'automations_active' => \FluentCrm\App\Models\Funnel::where('status', 'published')->count(), + 'automations_total' => \FluentCrm\App\Models\Funnel::count(), + ]; + } + + private static function topTagsForContext($limit = 50) + { + $tags = Tag::withCount('subscribers') + ->orderByDesc('subscribers_count') + ->limit($limit) + ->get(); + + $out = []; + foreach ($tags as $tag) { + $out[] = [ + 'id' => (int) $tag->id, + 'title' => $tag->title, + 'slug' => $tag->slug, + 'subscribers_count' => (int) $tag->subscribers_count, + ]; + } + return $out; + } + + private static function topListsForContext($limit = 50) + { + $lists = Lists::withCount('subscribers') + ->orderByDesc('subscribers_count') + ->limit($limit) + ->get(); + + $out = []; + foreach ($lists as $list) { + $out[] = [ + 'id' => (int) $list->id, + 'title' => $list->title, + 'slug' => $list->slug, + 'subscribers_count' => (int) $list->subscribers_count, + ]; + } + return $out; + } + + private static function formatRefList($items, $keyField) + { + if (!is_array($items)) { + return []; + } + $out = []; + foreach ($items as $key => $item) { + $name = is_string($key) ? $key : ($item[$keyField] ?? null); + if (!$name) { + continue; + } + $out[] = [ + 'key' => $name, + 'label' => $item['label'] ?? $item['title'] ?? $name, + 'is_pro' => !empty($item['is_pro']), + ]; + } + return $out; + } + + /** + * Design templates the MCP tools allow agents to select. The + * visual_builder template is intentionally excluded — it's an + * interactive Gutenberg editor experience, not something an agent + * should be authoring against. Use `mcp_allowed_design_templates` + * to publish it deliberately if a custom workflow needs it. + */ + public static function allowedDesignTemplates() + { + $defaults = [ + 'plain' => __('Plain', 'fluent-crm'), + 'classic' => __('Classic', 'fluent-crm'), + 'raw_html' => __('Raw HTML', 'fluent-crm'), + 'raw_classic' => __('Raw Classic', 'fluent-crm'), + ]; + + if (method_exists(Helper::class, 'getEmailDesignTemplates')) { + $all = Helper::getEmailDesignTemplates(); + if (is_array($all) && $all) { + $excluded = ['visual_builder']; + $filtered = array_diff_key($all, array_flip($excluded)); + if ($filtered) { + $defaults = $filtered; + } + } + } + + /** + * Filter the design templates surfaced to MCP agents. Useful for + * adding custom templates a site has registered, or allow-listing + * visual_builder if the operator really wants agents to use it. + * + * @since 2.10.0 + * + * @param array $templates Map of slug => label. + */ + return apply_filters('fluent_crm/mcp_allowed_design_templates', $defaults); + } + + private static function buildDefaultSender() + { + $emailSettings = Helper::getGlobalEmailSettings(); + return [ + 'from_name' => $emailSettings['from_name'] ?? '', + 'from_email' => $emailSettings['from_email'] ?? '', + 'reply_to_name' => $emailSettings['reply_to_name'] ?? '', + 'reply_to_email' => $emailSettings['reply_to_email'] ?? '', + ]; + } + + private static function buildCustomFieldSchema() + { + $model = new CustomContactField(); + $global = $model->getGlobalFields(); + $fields = is_array($global) ? ($global['fields'] ?? []) : []; + + $out = []; + foreach ((array) $fields as $field) { + $entry = [ + 'key' => $field['slug'] ?? null, + 'label' => $field['label'] ?? null, + 'type' => $field['type'] ?? null, + ]; + if (!empty($field['options'])) { + $entry['options'] = array_values((array) $field['options']); + } + if ($entry['key']) { + $out[] = $entry; + } + } + return $out; + } + + /** + * Flatten Helper::getGlobalSmartCodes() into a compact, agent-friendly + * shape — review #19. Preserves group structure so the agent can find + * codes by source (contact / custom fields / general / extensions). + */ + private static function buildSmartCodes() + { + if (!method_exists(Helper::class, 'getGlobalSmartCodes')) { + return []; + } + + $groups = Helper::getGlobalSmartCodes(); + if (!is_array($groups)) { + return []; + } + + $out = []; + foreach ($groups as $group) { + $codes = []; + $shortcodes = $group['shortcodes'] ?? []; + if (is_array($shortcodes)) { + foreach ($shortcodes as $code => $label) { + $codes[] = ['code' => (string) $code, 'label' => (string) $label]; + } + } + $out[] = [ + 'key' => $group['key'] ?? null, + 'title' => $group['title'] ?? null, + 'codes' => $codes, + ]; + } + return $out; + } + + private static function detectAiProvider() + { + $aiSettings = fluentcrm_get_option('ai_settings', []); + $provider = ''; + $configured = false; + + if (!empty($aiSettings['active_provider'])) { + $provider = sanitize_key($aiSettings['active_provider']); + $providerCfg = $aiSettings[$provider] ?? []; + $configured = !empty($providerCfg['api_key']); + } + + return ['provider' => $provider ?: null, 'configured' => $configured]; + } + + private static function buildGuidelines() + { + $default = "Be concise. When sending to a contact, confirm their status is 'subscribed'. " . + "Use add_tags/remove_tags for delta updates. Drafts are safe — only change-campaign-status " . + "with action=schedule causes sending. The site timezone is in site.timezone — use it when " . + "constructing scheduled_at. Use custom_fields_schema to construct valid custom_fields payloads " . + "on upsert-contact — never invent keys. Filter shape (universal): {search, tags[], lists[], " . + "statuses[], contact_type, created_after, created_before, sort_by, sort_type}."; + + /** + * Filter the AI guidelines text returned in get-crm-context. + * + * Useful for shop-specific nudges (e.g. "always tag MCP-touched contacts + * with `mcp-edited`"). Keep terse — the text ships in every session's + * tool-discovery payload. + * + * @since 2.10.0 + * + * @param string $default + */ + return apply_filters('fluent_crm/mcp_ai_guidelines', $default); + } + + /** + * Invalidate cached context for every user. Hooked from MCPInit on the + * relevant FluentCRM events. + */ + public static function invalidateCache() + { + global $wpdb; + $like = $wpdb->esc_like('_transient_fluent_crm_mcp_context_') . '%'; + $wpdb->query($wpdb->prepare("DELETE FROM {$wpdb->options} WHERE option_name LIKE %s", $like)); + $like = $wpdb->esc_like('_transient_timeout_fluent_crm_mcp_context_') . '%'; + $wpdb->query($wpdb->prepare("DELETE FROM {$wpdb->options} WHERE option_name LIKE %s", $like)); + } +} diff --git a/wp-content/plugins/fluent-crm/app/Modules/MCP/Tools/EmailTools.php b/wp-content/plugins/fluent-crm/app/Modules/MCP/Tools/EmailTools.php new file mode 100644 index 0000000..8968e15 --- /dev/null +++ b/wp-content/plugins/fluent-crm/app/Modules/MCP/Tools/EmailTools.php @@ -0,0 +1,436 @@ +status, $allowedStatuses, true)) { + return MCPHelper::error('invalid_param', sprintf( + /* translators: 1: current contact status, 2: comma-separated list of allowed statuses */ + __("The contact's status is '%1\$s'. To send to this contact, the contact's own status must be one of: %2\$s. The is_transactional parameter controls the message type, not the contact gate.", 'fluent-crm'), + $contact->status, + implode(', ', $allowedStatuses) + ), [ + 'current_status' => $contact->status, + 'allowed_statuses' => $allowedStatuses, + ]); + } + + $defaults = Helper::getGlobalEmailSettings(); + + $designTemplate = sanitize_key((string) ($params['design_template'] ?? 'classic')); + if ($designTemplate === '') { + $designTemplate = 'classic'; + } + // Defense in depth — even though the schema enum constrains this, + // a non-honoring agent (or a direct REST call) could still try to + // pass `visual_builder` or another disallowed value. Reject server + // side with a structured error. + $allowed = array_keys(ContextTools::allowedDesignTemplates()); + if (!in_array($designTemplate, $allowed, true)) { + return MCPHelper::error('invalid_param', __('design_template not allowed via MCP', 'fluent-crm'), [ + 'design_template' => $designTemplate, + 'allowed' => $allowed, + ]); + } + + $isTransactional = self::yesNo($params['is_transactional'] ?? null, 'no'); + + // Footer toggle — UI behavior: turning on transactional auto-disables + // the global footer because transactional mail must not include a + // marketing unsubscribe link. Honor that by default; let the caller + // override explicitly. + if (array_key_exists('disable_footer', $params)) { + $disableFooter = self::yesNo($params['disable_footer'], 'no'); + } else { + $disableFooter = $isTransactional === 'yes' ? 'yes' : 'no'; + } + + $clickTracker = self::trackerValue($params['click_tracker'] ?? null); + $openTracker = self::trackerValue($params['open_tracker'] ?? null); + + // Build the mailer override block. + $fromName = sanitize_text_field((string) ($params['from_name'] ?? $defaults['from_name'])); + $fromEmail = sanitize_email((string) ($params['from_email'] ?? $defaults['from_email'])); + $replyToName = sanitize_text_field((string) ($params['reply_to_name'] ?? ($defaults['reply_to_name'] ?? ''))); + $replyToEmail = sanitize_email((string) ($params['reply_to_email'] ?? ($defaults['reply_to_email'] ?? ''))); + + $mailerSettings = [ + 'from_name' => $fromName, + 'from_email' => $fromEmail, + 'reply_to_name' => $replyToName, + 'reply_to_email' => $replyToEmail, + 'is_custom' => 'yes', + ]; + + // Compose the settings object the way the UI does. + $settings = [ + 'mailer_settings' => $mailerSettings, + 'is_transactional' => $isTransactional, + 'footer_settings' => [ + 'disable_footer' => $disableFooter, + ], + 'template_config' => Helper::getTemplateConfig($designTemplate), + ]; + if ($clickTracker !== null) { + $settings['click_tracker'] = $clickTracker; + } + if ($openTracker !== null) { + $settings['open_tracker'] = $openTracker; + } + + // Allow callers to pass an arbitrary `settings` object for things + // we haven't surfaced as top-level params (e.g. visual-builder style + // overrides). Caller-provided keys win on conflict. + if (!empty($params['settings']) && is_array($params['settings'])) { + $settings = array_replace_recursive($settings, $params['settings']); + } + + // Custom title for audit / log; default keeps recipient email so the + // entry is searchable in the campaign list. + $title = isset($params['title']) && $params['title'] !== '' + ? sanitize_text_field((string) $params['title']) + : sprintf(__('MCP one-off to %s', 'fluent-crm'), $contact->email); + + $campaignData = [ + 'title' => $title, + 'email_subject' => $subject, + 'email_pre_header' => sanitize_text_field((string) ($params['pre_header'] ?? $params['preheader'] ?? '')), + 'email_body' => $body, + 'design_template' => $designTemplate, + 'settings' => $settings, + 'status' => 'draft', + ]; + + // UTM tagging — flatten the optional `utm` object onto the + // campaign's utm_* columns. + if (!empty($params['utm']) && is_array($params['utm'])) { + $utm = $params['utm']; + $campaignData['utm_status'] = !empty($utm['status']) ? 1 : 0; + foreach (['source', 'medium', 'campaign', 'term', 'content'] as $key) { + if (isset($utm[$key])) { + $campaignData['utm_' . $key] = sanitize_text_field((string) $utm[$key]); + } + } + } + + $campaignData = Sanitize::campaign($campaignData); + + // Mirror the WP_Error surfacing behavior of the controller. + add_action('wp_mail_failed', function ($wpError) { + if (method_exists(Helper::class, 'debugLog')) { + Helper::debugLog('MCP send-email-to-contact failure', $wpError->get_error_message(), 'error'); + } + }, 10, 1); + + $campaign = CustomEmailCampaign::create($campaignData); + + $campaign->subscribe([(int) $contact->id], [ + 'status' => 'scheduled', + 'scheduled_at' => current_time('mysql'), + ]); + + do_action('fluentcrm_process_contact_jobs', $contact); + + return [ + 'ok' => true, + 'campaign_id' => (int) $campaign->id, + 'message' => __('Email queued for delivery', 'fluent-crm'), + 'contact' => [ + 'id' => (int) $contact->id, + 'email' => $contact->email, + ], + 'applied' => [ + 'is_transactional' => $isTransactional, + 'disable_footer' => $disableFooter, + 'design_template' => $designTemplate, + 'from' => self::formatAddress($fromName, $fromEmail), + 'reply_to' => self::formatAddress($replyToName, $replyToEmail), + ], + ]; + } + + /** + * Render an RFC-5322 "Display Name " string. Previous version + * (`trim(... ' <>')`) ate the closing `>` from any "Name (with parens)" + * — review #15. + */ + private static function formatAddress($name, $email) + { + $email = trim((string) $email); + $name = trim((string) $name); + if ($email === '') return ''; + if ($name === '') return $email; + return $name . ' <' . $email . '>'; + } + + /** + * `send-test-email` — render and send a one-off test copy of either: + * - a saved campaign (pass campaign_id), or + * - a draft body/subject the agent supplies inline. + * + * Differs from send-email-to-contact: NO campaign record is created, + * NO subscriber is enrolled, NO row is logged to fc_campaign_emails, + * and the recipient does NOT need to be subscribed. The subject is + * prefixed with "TEST:" to match what the contact-profile UI does. + * Mirrors CampaignController::sendTestEmail. + */ + public static function sendTestEmail($params) + { + $params = (array) $params; + + // Resolve recipient address — defaults to the current WP user. + $toEmail = sanitize_email((string) ($params['to_email'] ?? '')); + if (!$toEmail) { + $user = wp_get_current_user(); + $toEmail = $user ? $user->user_email : ''; + } + if (!$toEmail || !is_email($toEmail)) { + return MCPHelper::error('invalid_param', __('A valid to_email is required.', 'fluent-crm')); + } + + // Source the email content from a saved campaign or inline params. + $campaignId = isset($params['campaign_id']) ? (int) $params['campaign_id'] : 0; + $subject = $body = $preHeader = ''; + $designTemplate = ''; + $settings = []; + + if ($campaignId) { + // Need to bypass the global type scope so test sends work for + // custom_email_campaign / sequence_mail / etc., not just + // type='campaign'. + $campaign = Campaign::withoutGlobalScope('type')->find($campaignId); + if (!$campaign) { + return MCPHelper::error('not_found', __('Campaign not found', 'fluent-crm'), ['campaign_id' => $campaignId]); + } + $subject = (string) $campaign->email_subject; + $body = (string) $campaign->email_body; + $preHeader = (string) $campaign->email_pre_header; + $designTemplate = (string) $campaign->design_template; + $settings = is_array($campaign->settings) ? $campaign->settings : (array) maybe_unserialize($campaign->settings); + } + + // Inline params override campaign-derived values. + if (isset($params['subject']) && $params['subject'] !== '') { + $subject = (string) $params['subject']; + } + if (isset($params['body']) && $params['body'] !== '') { + $body = (string) $params['body']; + } + if (isset($params['pre_header'])) { + $preHeader = (string) $params['pre_header']; + } + if (isset($params['design_template']) && $params['design_template'] !== '') { + $designTemplate = sanitize_key((string) $params['design_template']); + } + if ($designTemplate === '') { + $designTemplate = 'classic'; + } + // Apply the same MCP-safe enum guard as send-email-to-contact. + $allowedTemplates = array_keys(ContextTools::allowedDesignTemplates()); + if (!in_array($designTemplate, $allowedTemplates, true)) { + return MCPHelper::error('invalid_param', __('design_template not allowed via MCP', 'fluent-crm'), [ + 'design_template' => $designTemplate, + 'allowed' => $allowedTemplates, + ]); + } + + if ($subject === '' || $body === '') { + return MCPHelper::error('invalid_param', __('Provide either campaign_id, or subject + body.', 'fluent-crm')); + } + + // Resolve the subscriber whose data smartcodes get filled with. + // Priority: explicit against_contact_*, then to_email, then any + // subscribed contact (mirrors CampaignController fallback). + $subscriber = null; + if (!empty($params['against_contact_id'])) { + $subscriber = Subscriber::find((int) $params['against_contact_id']); + } + if (!$subscriber && !empty($params['against_contact_email'])) { + $subscriber = Subscriber::where('email', sanitize_email($params['against_contact_email']))->first(); + } + if (!$subscriber) { + $subscriber = Subscriber::where('email', $toEmail)->first(); + } + if (!$subscriber) { + $subscriber = Subscriber::where('status', 'subscribed')->first(); + } + if (!$subscriber) { + return MCPHelper::error('not_supported', __('No subscriber found to drive smartcode rendering. Add at least one subscribed contact.', 'fluent-crm')); + } + + // Catch wp_mail errors for the response. + $mailErrors = []; + $mailErrorListener = function ($wpError) use (&$mailErrors) { + $mailErrors[] = $wpError->get_error_message(); + }; + add_action('wp_mail_failed', $mailErrorListener, 10, 1); + + // Block-template rendering — same gate the controller uses. + $rawTemplates = ['raw_html', 'raw_classic']; + if (!in_array($designTemplate, $rawTemplates, true)) { + $body = (new BlockParser($subscriber))->parse($body); + } + + // Footer config — pulled from a stand-in object so we can pass non- + // persisted draft data through Helper::getFooterConfig the same way + // the controller does. + $stub = (object) [ + 'design_template' => $designTemplate, + 'settings' => $settings ?: ['template_config' => []], + 'email_pre_header' => $preHeader, + 'email_body' => $body, + 'email_subject' => $subject, + ]; + $footerConfig = method_exists(Helper::class, 'getFooterConfig') ? Helper::getFooterConfig($stub) : ['footer_content' => '']; + $footerText = Arr::get($footerConfig, 'footer_content', ''); + + // Run the standard parse_campaign_email_text filter chain so + // smartcodes resolve. + $body = apply_filters('fluent_crm/parse_campaign_email_text', $body, $subscriber); + $footerText = apply_filters('fluent_crm/parse_campaign_email_text', $footerText, $subscriber); + $subject = apply_filters('fluent_crm/parse_campaign_email_text', $subject, $subscriber); + $preHeader = apply_filters('fluent_crm/parse_campaign_email_text', $preHeader, $subscriber); + + $footerConfig['footer_content'] = $footerText; + + $templateData = [ + 'preHeader' => $preHeader, + 'email_body' => $body, + 'footer_text' => $footerText, + 'footer_config' => $footerConfig, + 'config' => wp_parse_args( + Arr::get($settings, 'template_config', []), + Helper::getTemplateConfig($designTemplate) + ), + ]; + + $body = apply_filters( + 'fluent_crm/email-design-template-' . $designTemplate, + $body, + $templateData, + $stub, + $subscriber + ); + + $body = str_replace('{{crm_global_email_footer}}', $footerText, $body); + $body = str_replace('{{crm_preheader_text}}', $preHeader, $body); + + $data = [ + 'to' => [ + 'email' => $toEmail, + 'name' => $subscriber->full_name ?: $toEmail, + ], + 'subject' => 'TEST: ' . $subject, + 'body' => $body, + 'headers' => Helper::getMailHeadersFromSettings(Arr::get($settings, 'mailer_settings', [])), + ]; + + if (method_exists(Helper::class, 'maybeDisableEmojiOnEmail')) { + Helper::maybeDisableEmojiOnEmail(); + } + $result = Mailer::send($data, $subscriber, null, true); + + remove_action('wp_mail_failed', $mailErrorListener, 10); + + $sent = $result !== false && empty($mailErrors); + + return [ + 'ok' => $sent, + 'sent' => $sent, + 'to' => $toEmail, + 'rendered_against' => [ + 'contact_id' => (int) $subscriber->id, + 'email' => $subscriber->email, + ], + 'subject_preview' => 'TEST: ' . $subject, + 'design_template' => $designTemplate, + 'errors' => $mailErrors, + 'note' => __('Test sends bypass the queue, do not enroll the recipient, and do not appear in email_history.', 'fluent-crm'), + ]; + } + + private static function yesNo($value, $default = 'no') + { + if ($value === null) { + return $default; + } + if (is_bool($value)) { + return $value ? 'yes' : 'no'; + } + $str = strtolower((string) $value); + if (in_array($str, ['yes', 'true', '1', 'on'], true)) { + return 'yes'; + } + if (in_array($str, ['no', 'false', '0', 'off', ''], true)) { + return 'no'; + } + return $default; + } + + private static function trackerValue($value) + { + if ($value === null || $value === '') { + return null; + } + $str = strtolower((string) $value); + if (in_array($str, ['yes', 'no', 'anonymous'], true)) { + return $str; + } + if (is_bool($value)) { + return $value ? 'yes' : 'no'; + } + return null; + } +} diff --git a/wp-content/plugins/fluent-crm/app/Modules/MCP/Tools/FunnelTools.php b/wp-content/plugins/fluent-crm/app/Modules/MCP/Tools/FunnelTools.php new file mode 100644 index 0000000..f21b26a --- /dev/null +++ b/wp-content/plugins/fluent-crm/app/Modules/MCP/Tools/FunnelTools.php @@ -0,0 +1,447 @@ +orderBy($sortBy, $sortType); + + if ($search !== '') { + global $wpdb; + $like = '%' . $wpdb->esc_like($search) . '%'; + $query->where(function ($q) use ($like) { + $q->where('title', 'LIKE', $like) + ->orWhere('trigger_name', 'LIKE', $like); + }); + } + + if (!empty($statuses)) { + $query->whereIn('status', $statuses); + } + + $paginated = $query->paginate(); + + $items = []; + $triggerLabels = self::triggerLabelMap(); + foreach ($paginated->items() as $funnel) { + $items[] = [ + 'id' => (int) $funnel->id, + 'title' => $funnel->title, + 'status' => $funnel->status, + 'trigger_name' => $funnel->trigger_name, + 'trigger_label' => $triggerLabels[$funnel->trigger_name] ?? $funnel->trigger_name, + 'in_progress_subscribers_count' => self::inProgressCount((int) $funnel->id), + 'completed_subscribers_count' => (int) ($funnel->subscribers_count ?? 0), + 'created_at' => MCPHelper::toIso8601($funnel->created_at), + 'updated_at' => MCPHelper::toIso8601($funnel->updated_at), + ]; + } + + return [ + 'items' => $items, + 'total' => (int) $paginated->total(), + 'page' => (int) $paginated->currentPage(), + 'per_page' => (int) $paginated->perPage(), + 'pages' => (int) $paginated->lastPage(), + ]; + } + + // ----------------------------------------------------------------- + // Read: get-automation + // ----------------------------------------------------------------- + + public static function getAutomation($params) + { + $params = (array) $params; + $funnelId = (int) ($params['funnel_id'] ?? 0); + + if (!$funnelId) { + return MCPHelper::error('invalid_param', __('funnel_id is required', 'fluent-crm')); + } + + $funnel = Funnel::find($funnelId); + if (!$funnel) { + return MCPHelper::error('not_found', __('Automation not found', 'fluent-crm'), ['funnel_id' => $funnelId]); + } + + $defaultIncludes = ['sequences', 'report']; + $include = isset($params['include']) && is_array($params['include']) && $params['include'] + ? array_values(array_intersect($params['include'], ['sequences', 'report'])) + : $defaultIncludes; + + $triggerLabels = self::triggerLabelMap(); + + $data = [ + 'id' => (int) $funnel->id, + 'title' => $funnel->title, + 'status' => $funnel->status, + 'trigger_name' => $funnel->trigger_name, + 'trigger_label' => $triggerLabels[$funnel->trigger_name] ?? $funnel->trigger_name, + 'trigger_settings' => is_array($funnel->settings) ? $funnel->settings : [], + 'conditions' => is_array($funnel->conditions) ? $funnel->conditions : [], + 'in_progress_subscribers_count' => self::inProgressCount((int) $funnel->id), + 'completed_subscribers_count' => (int) FunnelSubscriber::where('funnel_id', $funnel->id) + ->where('status', 'completed') + ->count(), + 'created_at' => MCPHelper::toIso8601($funnel->created_at), + 'updated_at' => MCPHelper::toIso8601($funnel->updated_at), + ]; + + if (in_array('sequences', $include, true)) { + $sequences = FunnelHelper::getFunnelSequences($funnel, true); + $includeBodies = !empty($params['include_bodies']); + $data['sequences'] = self::formatSequences($sequences, $includeBodies); + } + + if (in_array('report', $include, true)) { + $data['report'] = self::buildStepReport($funnel); + } + + return $data; + } + + /** + * Format funnel sequences for MCP. By default we strip large body + * fields out of `settings` (action_name=send_custom_email embeds an + * entire campaign payload including email_body). Pass include_bodies + * = true to get the full settings tree — review #7 (token bloat). + */ + private static function formatSequences($sequences, $includeBodies = false) + { + $out = []; + foreach ((array) $sequences as $seq) { + $row = is_object($seq) ? get_object_vars($seq) : (array) $seq; + $settings = $row['settings'] ?? []; + if (!$includeBodies) { + $settings = self::stripBodyFields($settings); + } + $out[] = [ + 'id' => isset($row['id']) ? (int) $row['id'] : null, + 'type' => $row['type'] ?? null, + 'action_name' => $row['action_name'] ?? null, + 'settings' => $settings, + 'delay' => $row['delay'] ?? 0, + 'delay_unit' => $row['c_delay_unit'] ?? ($row['delay_unit'] ?? null), + 'parent_id' => isset($row['parent_id']) ? (int) $row['parent_id'] : null, + ]; + } + return $out; + } + + /** + * Recursively redact body / html fields. Replaces them with a marker so + * the agent knows the field exists and can re-fetch with include_bodies. + * + * Round-3 review B5: dropped the previous "only if >200 chars" gate — + * any stored body field gets stripped now, regardless of size, so the + * include_bodies=false contract is honored consistently. Rare to have a + * truly tiny body field anyway, and the marker is shorter than most + * email bodies. + */ + private static function stripBodyFields($value) + { + $bodyKeys = ['email_body', 'body', 'body_html', 'body_text']; + if (!is_array($value)) { + return $value; + } + foreach ($value as $k => $v) { + if (is_string($k) && in_array($k, $bodyKeys, true) && is_string($v)) { + $len = strlen($v); + $value[$k] = $len > 0 + ? '[truncated — re-fetch with include_bodies=true; ' . $len . ' chars]' + : ''; + } elseif (is_array($v)) { + $value[$k] = self::stripBodyFields($v); + } + } + return $value; + } + + private static function buildStepReport($funnel) + { + $stepCounts = FunnelSubscriber::where('funnel_id', $funnel->id) + ->select(['last_sequence_id']) + ->selectRaw('COUNT(id) as total') + ->groupBy('last_sequence_id') + ->get(); + + $steps = []; + foreach ($stepCounts as $row) { + if (!$row->last_sequence_id) { + continue; + } + $steps[] = [ + 'step_id' => (int) $row->last_sequence_id, + 'total' => (int) $row->total, + ]; + } + return ['steps' => $steps]; + } + + private static function inProgressCount($funnelId) + { + return (int) FunnelSubscriber::where('funnel_id', $funnelId) + ->whereIn('status', ['active', 'waiting']) + ->count(); + } + + private static function triggerLabelMap() + { + $triggers = apply_filters('fluentcrm_funnel_triggers', []); + $map = []; + if (is_array($triggers)) { + foreach ($triggers as $key => $config) { + $map[$key] = $config['label'] ?? $key; + } + } + return $map; + } + + // ----------------------------------------------------------------- + // Read: list-funnel-subscribers (round-4 review P3 #11) + // ----------------------------------------------------------------- + + /** + * List the contacts currently in a funnel filtered by subscription + * status. Closes a real workflow gap: "this customer just upgraded — + * pull them out of trial-onboarding" requires knowing who's in the + * funnel first, and there was no way to find that without already + * knowing the contact id. + */ + public static function listFunnelSubscribers($params) + { + $params = (array) $params; + $funnelId = (int) ($params['funnel_id'] ?? 0); + if (!$funnelId) { + return MCPHelper::error('invalid_param', __('funnel_id is required', 'fluent-crm')); + } + + $funnel = Funnel::find($funnelId); + if (!$funnel) { + return MCPHelper::error('not_found', __('Automation not found', 'fluent-crm'), ['funnel_id' => $funnelId]); + } + + $allowedStatuses = ['active', 'waiting', 'completed', 'cancelled', 'skipped']; + $statuses = (array) ($params['statuses'] ?? ['active']); + $statuses = array_values(array_intersect(array_map('sanitize_key', $statuses), $allowedStatuses)); + if (!$statuses) { + $statuses = ['active']; + } + + MCPHelper::paginationFromInput($params); + + $rows = FunnelSubscriber::with(['subscriber' => function ($q) { + $q->select(['id', 'email', 'first_name', 'last_name', 'status', 'contact_type']); + }]) + ->where('funnel_id', $funnelId) + ->whereIn('status', $statuses) + ->orderBy('id', 'DESC') + ->paginate(); + + $items = []; + foreach ($rows->items() as $row) { + $sub = $row->subscriber; + if (!$sub) { + continue; + } + $items[] = [ + 'funnel_subscriber_id' => (int) $row->id, + 'funnel_status' => $row->status, + 'next_sequence_id' => $row->next_sequence_id ? (int) $row->next_sequence_id : null, + 'last_executed_at' => MCPHelper::toIso8601($row->last_executed_time), + 'next_execution_at' => MCPHelper::toIso8601($row->next_execution_time), + 'enrolled_at' => MCPHelper::toIso8601($row->created_at), + 'contact' => [ + 'id' => (int) $sub->id, + 'email' => $sub->email, + 'full_name' => trim((string) ($sub->first_name . ' ' . $sub->last_name)), + 'status' => $sub->status, + 'contact_type' => $sub->contact_type, + ], + ]; + } + + return [ + 'items' => $items, + 'total' => (int) $rows->total(), + 'page' => (int) $rows->currentPage(), + 'per_page' => (int) $rows->perPage(), + 'pages' => (int) $rows->lastPage(), + 'funnel' => [ + 'id' => (int) $funnel->id, + 'title' => $funnel->title, + ], + 'filtered_statuses' => $statuses, + ]; + } + + // ----------------------------------------------------------------- + // Write: update-contact-automation-status + // ----------------------------------------------------------------- + + public static function updateContactAutomationStatus($params) + { + $params = (array) $params; + $funnelId = (int) ($params['funnel_id'] ?? 0); + $action = sanitize_key((string) ($params['action'] ?? '')); + + if (!$funnelId) { + return MCPHelper::error('invalid_param', __('funnel_id is required', 'fluent-crm')); + } + if (!in_array($action, ['resume', 'cancel', 'advance_now'], true)) { + // 'pause' was intentionally dropped — FluentCRM has no native + // paused funnel-subscriber state, and the previous mapping + // silently cancelled. Tell the agent what the alternative is. + if ($action === 'pause') { + return MCPHelper::error('not_supported', __('pause is not supported — FluentCRM has no paused state for funnel subscribers. Use cancel to stop processing (reversible from the UI), or wait for a real benchmark.', 'fluent-crm'), [ + 'allowed_actions' => ['resume', 'cancel', 'advance_now'], + ]); + } + return MCPHelper::error('invalid_param', __('Invalid action', 'fluent-crm'), [ + 'allowed_actions' => ['resume', 'cancel', 'advance_now'], + ]); + } + + $contact = MCPHelper::resolveContact($params); + if (is_wp_error($contact)) { + return $contact; + } + + $funnel = Funnel::find($funnelId); + if (!$funnel) { + return MCPHelper::error('not_found', __('Automation not found', 'fluent-crm'), ['funnel_id' => $funnelId]); + } + + $row = FunnelSubscriber::where('funnel_id', $funnelId) + ->where('subscriber_id', $contact->id) + ->first(); + if (!$row) { + return MCPHelper::error('not_found', __('Contact is not enrolled in this automation', 'fluent-crm')); + } + + $previousStatus = $row->status; + + if ($row->status === 'completed') { + return MCPHelper::error('not_supported', __('Automation is already completed for this contact', 'fluent-crm'), [ + 'status' => $row->status, + ]); + } + + if ($action === 'cancel') { + $row->status = 'cancelled'; + $row->save(); + } elseif ($action === 'resume') { + $row->status = 'active'; + if (!$row->next_execution_time) { + $row->next_execution_time = gmdate('Y-m-d H:i:s', current_time('timestamp') + 60); + } + $row->save(); + } elseif ($action === 'advance_now') { + $sequenceId = (int) ($params['advance_to_sequence_id'] ?? 0); + if (!$sequenceId) { + return MCPHelper::error('invalid_param', __('advance_to_sequence_id is required for advance_now', 'fluent-crm')); + } + $sequence = \FluentCrm\App\Models\FunnelSequence::where('id', $sequenceId) + ->where('funnel_id', $funnelId) + ->first(); + if (!$sequence) { + return MCPHelper::error('not_found', __('Target sequence not found in this automation', 'fluent-crm')); + } + + // If the contact is waiting on a benchmark, mark the benchmark as + // skipped so reports stay accurate (matches the controller's path). + if ($row->status === 'waiting') { + $benchmarkSeq = \FluentCrm\App\Models\FunnelSequence::find($row->next_sequence_id); + if ($benchmarkSeq) { + \FluentCrm\App\Models\FunnelMetric::updateOrCreate( + [ + 'funnel_id' => $funnelId, + 'sequence_id' => $benchmarkSeq->id, + 'subscriber_id' => $contact->id, + ], + [ + 'benchmark_value' => 0, + 'benchmark_currency' => 'USD', + 'status' => 'skipped', + 'notes' => __('Skipped via MCP advance_now', 'fluent-crm'), + ] + ); + \FluentCrm\App\Services\Funnel\FunnelHelper::changeFunnelSubSequenceStatus($row->id, $benchmarkSeq->id, 'skipped'); + } + } + + $prev = \FluentCrm\App\Models\FunnelSequence::where('funnel_id', $funnelId) + ->where('sequence', '<', $sequence->sequence) + ->orderBy('sequence', 'DESC') + ->first(); + + $row->last_sequence_id = $prev ? $prev->id : 0; + $row->next_sequence_id = $sequence->id; + $row->next_sequence = $sequence->sequence; + $row->status = 'active'; + $row->next_execution_time = current_time('mysql'); + $row->save(); + } + + $row = FunnelSubscriber::find($row->id); + + return [ + 'ok' => true, + 'action' => $action, + 'previous_status' => $previousStatus, + 'current_status' => $row->status, + 'funnel_subscriber' => [ + 'id' => (int) $row->id, + 'funnel_id' => (int) $row->funnel_id, + 'subscriber_id' => (int) $row->subscriber_id, + 'status' => $row->status, + 'next_sequence_id' => $row->next_sequence_id ? (int) $row->next_sequence_id : null, + 'next_execution_time' => MCPHelper::toIso8601($row->next_execution_time), + ], + ]; + } +} diff --git a/wp-content/plugins/fluent-crm/app/Modules/MCP/Tools/SegmentTools.php b/wp-content/plugins/fluent-crm/app/Modules/MCP/Tools/SegmentTools.php new file mode 100644 index 0000000..79f5440 --- /dev/null +++ b/wp-content/plugins/fluent-crm/app/Modules/MCP/Tools/SegmentTools.php @@ -0,0 +1,328 @@ + $cap]); + } + + switch ($action) { + case 'create': + return self::actionCreate($params, $kind); + case 'update': + return self::actionUpdate($params, $kind); + case 'delete': + return self::actionDelete($params, $kind); + case 'merge': + return self::actionMerge($params, $kind); + } + + return MCPHelper::error('invalid_param', __('Unhandled action', 'fluent-crm')); + } + + private static function actionCreate($params, $kind) + { + $title = trim((string) ($params['title'] ?? '')); + if ($title === '') { + return MCPHelper::error('invalid_param', __('title is required for create', 'fluent-crm')); + } + $slug = isset($params['slug']) && $params['slug'] !== '' + ? sanitize_title((string) $params['slug']) + : sanitize_title($title); + $description = sanitize_textarea_field((string) ($params['description'] ?? '')); + + $existing = self::lookupByTitleOrSlug($title, $slug, $kind); + if ($existing) { + return MCPHelper::error('contact_exists', sprintf( + /* translators: 1: kind (tag/list), 2: matched id */ + __('A %1$s with that title or slug already exists (id %2$d). Use update or merge to change it.', 'fluent-crm'), + $kind, + (int) $existing->id + ), ['existing_id' => (int) $existing->id]); + } + + $modelClass = $kind === 'list' ? Lists::class : Tag::class; + $row = $modelClass::create([ + 'title' => sanitize_text_field($title), + 'slug' => $slug, + 'description' => $description, + ]); + + do_action(self::createdHook($kind), $row); + + return [ + 'ok' => true, + 'action' => 'create', + 'kind' => $kind, + $kind => self::format($row), + 'note' => sprintf( + /* translators: 1: kind, 2: title */ + __('%1$s "%2$s" created. No subscribers are attached yet.', 'fluent-crm'), + ucfirst($kind), + $row->title + ), + ]; + } + + private static function actionUpdate($params, $kind) + { + $id = (int) ($params[$kind . '_id'] ?? 0); + $row = $id ? self::find($id, $kind) : null; + if (!$row) { + return MCPHelper::error('not_found', sprintf(__('%s not found', 'fluent-crm'), ucfirst($kind)), [$kind . '_id' => $id]); + } + + $changes = []; + if (isset($params['title']) && $params['title'] !== '' && $params['title'] !== $row->title) { + $changes['title'] = ['from' => $row->title, 'to' => sanitize_text_field((string) $params['title'])]; + $row->title = $changes['title']['to']; + } + if (isset($params['slug']) && $params['slug'] !== '') { + $newSlug = sanitize_title((string) $params['slug']); + if ($newSlug !== $row->slug) { + $changes['slug'] = ['from' => $row->slug, 'to' => $newSlug]; + $row->slug = $newSlug; + } + } + if (array_key_exists('description', $params)) { + $newDesc = sanitize_textarea_field((string) $params['description']); + if ($newDesc !== $row->description) { + $changes['description'] = ['from' => $row->description, 'to' => $newDesc]; + $row->description = $newDesc; + } + } + + if (empty($changes)) { + return [ + 'ok' => true, + 'action' => 'update', + 'kind' => $kind, + $kind => self::format($row), + 'note' => __('No changes — provided fields matched the current values.', 'fluent-crm'), + ]; + } + + $row->save(); + + return [ + 'ok' => true, + 'action' => 'update', + 'kind' => $kind, + $kind => self::format($row), + 'changes' => $changes, + ]; + } + + private static function actionDelete($params, $kind) + { + $id = (int) ($params[$kind . '_id'] ?? 0); + $row = $id ? self::find($id, $kind) : null; + if (!$row) { + return MCPHelper::error('not_found', sprintf(__('%s not found', 'fluent-crm'), ucfirst($kind)), [$kind . '_id' => $id]); + } + + $force = !empty($params['force']); + $attachedCount = self::attachedSubscriberCount($row, $kind); + + if ($attachedCount > 0 && !$force) { + return MCPHelper::error('not_supported', sprintf( + /* translators: 1: kind, 2: count */ + __('%1$s has %2$d subscribers attached. Pass force=true to delete anyway, or merge into another %1$s first.', 'fluent-crm'), + ucfirst($kind), + $attachedCount + ), [ + 'attached_subscribers' => $attachedCount, + 'force_required' => true, + ]); + } + + $deletedId = (int) $row->id; + $deletedTitle = (string) $row->title; + $row->delete(); + do_action(self::deletedHook($kind), $deletedId); + + return [ + 'ok' => true, + 'action' => 'delete', + 'kind' => $kind, + 'deleted_id' => $deletedId, + 'deleted_title' => $deletedTitle, + 'detached_subscribers' => $attachedCount, + 'note' => $attachedCount > 0 + ? __('Deleted with subscribers attached — pivot rows are orphaned and cleaned up by the cleanup hook.', 'fluent-crm') + : __('Deleted. No subscribers were attached.', 'fluent-crm'), + ]; + } + + private static function actionMerge($params, $kind) + { + $fromIds = isset($params['from_' . $kind . '_ids']) ? (array) $params['from_' . $kind . '_ids'] : []; + $fromIds = array_values(array_unique(array_filter(array_map('intval', $fromIds)))); + $toId = (int) ($params['to_' . $kind . '_id'] ?? 0); + + if (!$toId) { + return MCPHelper::error('invalid_param', __('to_*_id is required for merge', 'fluent-crm')); + } + if (!$fromIds) { + return MCPHelper::error('invalid_param', __('from_*_ids must be a non-empty array of ids', 'fluent-crm')); + } + if (in_array($toId, $fromIds, true)) { + return MCPHelper::error('invalid_param', __('to_*_id cannot also be in from_*_ids', 'fluent-crm')); + } + + $to = self::find($toId, $kind); + if (!$to) { + return MCPHelper::error('not_found', sprintf(__('Target %s not found', 'fluent-crm'), $kind), ['to_id' => $toId]); + } + + $modelClass = $kind === 'list' ? Lists::class : Tag::class; + $fromRows = $modelClass::whereIn('id', $fromIds)->get(); + $foundFromIds = $fromRows->pluck('id')->map('intval')->toArray(); + $missingFromIds = array_values(array_diff($fromIds, $foundFromIds)); + + // Re-pivot subscribers attached to the `from` set onto the `to` target. + $attachedCount = 0; + foreach ($fromRows as $fromRow) { + $count = self::attachedSubscriberCount($fromRow, $kind); + $attachedCount += $count; + } + + $repivoted = 0; + foreach ($fromRows as $fromRow) { + $subscribers = self::attachedSubscribers($fromRow, $kind); + foreach ($subscribers as $sub) { + if ($kind === 'list') { + $sub->attachLists([$to->id]); + $sub->detachLists([$fromRow->id]); + } else { + $sub->attachTags([$to->id]); + $sub->detachTags([$fromRow->id]); + } + $repivoted++; + } + } + + // Delete the source rows. + foreach ($fromRows as $fromRow) { + $fromId = (int) $fromRow->id; + $fromRow->delete(); + do_action(self::deletedHook($kind), $fromId); + } + + return [ + 'ok' => true, + 'action' => 'merge', + 'kind' => $kind, + 'merged_from' => $foundFromIds, + 'merged_into' => self::format($to), + 'subscribers_repivoted' => $repivoted, + 'subscribers_seen' => $attachedCount, + 'missing_from_ids' => $missingFromIds, + 'note' => __('Each subscriber attached to a "from" target is now attached to "to" and the "from" rows are deleted. Re-running this merge with the same ids is a safe no-op.', 'fluent-crm'), + ]; + } + + // ----------------------------------------------------------------- + // helpers + // ----------------------------------------------------------------- + + private static function find($id, $kind) + { + return $kind === 'list' ? Lists::find($id) : Tag::find($id); + } + + private static function lookupByTitleOrSlug($title, $slug, $kind) + { + $modelClass = $kind === 'list' ? Lists::class : Tag::class; + return $modelClass::where('title', $title)->orWhere('slug', $slug)->first(); + } + + private static function format($row) + { + return [ + 'id' => (int) $row->id, + 'title' => $row->title, + 'slug' => $row->slug, + 'description' => $row->description ?? '', + ]; + } + + private static function attachedSubscriberCount($row, $kind) + { + return (int) ($kind === 'list' + ? $row->subscribers()->count() + : $row->subscribers()->count()); + } + + private static function attachedSubscribers($row, $kind) + { + return $kind === 'list' + ? $row->subscribers()->get() + : $row->subscribers()->get(); + } + + private static function createdHook($kind) + { + return $kind === 'list' ? 'fluent_crm/list_created' : 'fluent_crm/tag_created'; + } + + private static function deletedHook($kind) + { + return $kind === 'list' ? 'fluent_crm/list_deleted' : 'fluent_crm/tag_deleted'; + } +} diff --git a/wp-content/plugins/fluent-crm/app/Services/AutoSubscribe.php b/wp-content/plugins/fluent-crm/app/Services/AutoSubscribe.php new file mode 100644 index 0000000..6a858e8 --- /dev/null +++ b/wp-content/plugins/fluent-crm/app/Services/AutoSubscribe.php @@ -0,0 +1,515 @@ + 'no', + 'target_list' => '', + 'target_tags' => [], + 'double_optin' => 'no' + ]; + + $settings = fluentcrm_get_option('user_registration_subscribe_settings', []); + + if (!$settings) { + return $defaults; + } + + return wp_parse_args($settings, $defaults); + } + + public function getRegistrationFields() + { + return [ + 'title' => __('User Signup Optin Settings', 'fluent-crm'), + 'sub_title' => __('Automatically add your new user signups as subscriber in FluentCRM', 'fluent-crm'), + 'fields' => [ + 'status' => [ + 'type' => 'inline-checkbox', + 'label' => '', + 'checkbox_label' => __('Enable Create new contacts in FluentCRM when users register in WordPress', 'fluent-crm'), + 'checkbox_description' => __('Automatically add your new user signups as subscriber in FluentCRM', 'fluent-crm'), + 'true_label' => 'yes', + 'false_label' => 'no' + ], + 'target_list' => [ + 'type' => 'option-selector', + 'label' => __('Assign List', 'fluent-crm'), + 'option_key' => 'lists', + 'is_multiple' => false, + 'creatable' => true, + 'placeholder' => __('Select Assign List', 'fluent-crm'), + 'inline_help' => __('Select the list that will be assigned for new user registration in your site', 'fluent-crm'), + 'dependency' => [ + 'depends_on' => 'status', + 'operator' => '=', + 'value' => 'yes' + ] + ], + 'target_tags' => [ + 'type' => 'option-selector', + 'label' => __('Assign Tags', 'fluent-crm'), + 'option_key' => 'tags', + 'is_multiple' => true, + 'creatable' => true, + 'placeholder' => __('Select Assign Tag', 'fluent-crm'), + 'inline_help' => __('Select the tags that will be assigned for new user registration in your site', 'fluent-crm'), + 'dependency' => [ + 'depends_on' => 'status', + 'operator' => '=', + 'value' => 'yes' + ] + ], + 'double_optin' => [ + 'type' => 'inline-checkbox', + 'label' => __('Double Opt-In', 'fluent-crm'), + 'checkbox_label' => __('Enable Double-Optin Email Confirmation', 'fluent-crm'), + 'true_label' => 'yes', + 'false_label' => 'no', + 'dependency' => [ + 'depends_on' => 'status', + 'operator' => '=', + 'value' => 'yes' + ] + ], + ] + ]; + } + + public function getCommentSettings() + { + $defaults = [ + 'status' => 'no', + 'checkbox_label' => __('Subscribe to newsletter', 'fluent-crm'), + 'auto_checked' => 'no', + 'target_list' => '', + 'show_only_new' => 'yes', + 'target_tags' => [], + 'double_optin' => 'yes' + ]; + + $settings = fluentcrm_get_option('comment_form_subscribe_settings', []); + + if (!$settings) { + return $defaults; + } + + return wp_parse_args($settings, $defaults); + } + + public function getCommentFields() + { + return [ + 'title' => __('Comment Form Subscription Settings', 'fluent-crm'), + 'sub_title' => __('Automatically add your site commenter as a subscriber in FluentCRM', 'fluent-crm'), + 'fields' => [ + 'status' => [ + 'type' => 'inline-checkbox', + 'label' => '', + 'true_label' => 'yes', + 'false_label' => 'no', + 'checkbox_label' => __('Enable Create new contacts in FluentCRM when a visitor add a comment in your comment form', 'fluent-crm'), + 'checkbox_description' => __('Automatically add your site commenter as subscriber in FluentCRM', 'fluent-crm'), + ], + 'checkbox_label' => [ + 'label' => __('Checkbox Label for Comment Form', 'fluent-crm'), + 'type' => 'input-text', + 'placeholder' => __('Checkbox Label for Comment Form', 'fluent-crm'), + 'dependency' => [ + 'depends_on' => 'status', + 'operator' => '=', + 'value' => 'yes' + ] + ], + 'target_list' => [ + 'type' => 'option-selector', + 'label' => __('Assign List', 'fluent-crm'), + 'option_key' => 'lists', + 'is_multiple' => false, + 'placeholder' => __('Select Assign List', 'fluent-crm'), + 'inline_help' => __('Select the list that will be assigned for comment will be made in comment forms', 'fluent-crm'), + 'dependency' => [ + 'depends_on' => 'status', + 'operator' => '=', + 'value' => 'yes' + ] + ], + 'target_tags' => [ + 'type' => 'option-selector', + 'label' => __('Assign Tags', 'fluent-crm'), + 'option_key' => 'tags', + 'is_multiple' => true, + 'placeholder' => __('Select Assign Tag', 'fluent-crm'), + 'inline_help' => __('Select the tags that will be assigned for new comment will be made in comment forms', 'fluent-crm'), + 'dependency' => [ + 'depends_on' => 'status', + 'operator' => '=', + 'value' => 'yes' + ] + ], + 'auto_checked' => [ + 'type' => 'inline-checkbox', + 'label' => '', + 'checkbox_label' => __('Enable auto checked status on Comment Form subscription', 'fluent-crm'), + 'true_label' => 'yes', + 'false_label' => 'no', + 'dependency' => [ + 'depends_on' => 'status', + 'operator' => '=', + 'value' => 'yes' + ] + ], + 'show_only_new' => [ + 'type' => 'inline-checkbox', + 'label' => '', + 'checkbox_label' => __('Do not show the checkbox if current user already subscribed state', 'fluent-crm'), + 'true_label' => 'yes', + 'false_label' => 'no', + 'dependency' => [ + 'depends_on' => 'status', + 'operator' => '=', + 'value' => 'yes' + ] + ], + 'double_optin' => [ + 'type' => 'inline-checkbox', + 'label' => __('Double Opt-In', 'fluent-crm'), + 'checkbox_label' => __('Enable Double-Optin Email Confirmation', 'fluent-crm'), + 'true_label' => 'yes', + 'false_label' => 'no', + 'dependency' => [ + 'depends_on' => 'status', + 'operator' => '=', + 'value' => 'yes' + ] + ] + ] + ]; + } + + public function getUserSyncSettings() + { + $defaults = [ + 'status' => 'no', + 'delete_contact_on_user_delete' => 'no' + ]; + + $settings = fluentcrm_get_option('user_syncing_settings', []); + + if (!$settings) { + return $defaults; + } + + return wp_parse_args($settings, $defaults); + } + + public function getUserSyncFields() + { + return [ + 'title' => __('Auto Sync User Data and Contact Data', 'fluent-crm'), + 'sub_title' => __('Automatically Sync your WP User Data and FluentCRM Contact Data', 'fluent-crm'), + 'fields' => [ + 'status' => [ + 'type' => 'inline-checkbox', + 'label' => '', + 'true_label' => 'yes', + 'false_label' => 'no', + 'checkbox_label' => __('Enable Sync between WP User Data and FluentCRM Contact Data', 'fluent-crm'), + 'checkbox_description' => __('When enabled, changes to WordPress user profile fields (name, email) will be automatically synced to the corresponding FluentCRM contact', 'fluent-crm') + ], + 'delete_contact_on_user_delete' => [ + 'type' => 'inline-checkbox', + 'label' => '', + 'true_label' => 'yes', + 'false_label' => 'no', + 'checkbox_label' => __('Delete FluentCRM contact on WP User delete', 'fluent-crm'), + 'checkbox_description' => __('When enabled, deleting a WordPress user will also permanently delete the associated FluentCRM contact record', 'fluent-crm') + ] + ] + ]; + } + + public function getWooCheckoutSettings() + { + $defaults = [ + 'auto_checkout_fill' => 'no', + 'status' => 'no', + 'checkbox_label' => __('Sign me up for the newsletter!', 'fluent-crm'), + 'auto_checked' => 'no', + 'target_list' => '', + 'show_only_new' => 'yes', + 'target_tags' => [], + 'double_optin' => 'yes' + ]; + + $settings = fluentcrm_get_option('woo_checkout_form_subscribe_settings', []); + + if (!$settings) { + return $defaults; + } + + return wp_parse_args($settings, $defaults); + } + + public function getWooCheckoutFields() + { + return [ + 'title' => __('Woocommerce Checkout Subscription Field', 'fluent-crm'), + 'sub_title' => __('Add a subscription box to WooCommerce Checkout Form', 'fluent-crm'), + 'fields' => [ + 'auto_checkout_fill' => [ + 'type' => 'inline-checkbox', + 'label' => '', + 'true_label' => 'yes', + 'false_label' => 'no', + 'checkbox_label' => __('Automatically fill WooCommerce Checkout field value with current contact data', 'fluent-crm') + ], + 'status' => [ + 'type' => 'inline-checkbox', + 'label' => '', + 'true_label' => 'yes', + 'false_label' => 'no', + 'checkbox_label' => __('Enable Subscription Checkbox to WooCommerce Checkout Page', 'fluent-crm') + ], + 'checkbox_label' => [ + 'label' => __('Checkbox Label for Checkout checkbox', 'fluent-crm'), + 'type' => 'input-text', + 'placeholder' => __('Checkbox Label for Checkout checkbox', 'fluent-crm'), + 'dependency' => [ + 'depends_on' => 'status', + 'operator' => '=', + 'value' => 'yes' + ] + ], + 'target_list' => [ + 'type' => 'option-selector', + 'label' => __('Assign List', 'fluent-crm'), + 'option_key' => 'lists', + 'is_multiple' => false, + 'placeholder' => __('Select Assign List', 'fluent-crm'), + 'inline_help' => __('Select the list that will be assigned when checkbox checked', 'fluent-crm'), + 'dependency' => [ + 'depends_on' => 'status', + 'operator' => '=', + 'value' => 'yes' + ] + ], + 'target_tags' => [ + 'type' => 'option-selector', + 'label' => __('Assign Tags', 'fluent-crm'), + 'option_key' => 'tags', + 'is_multiple' => true, + 'placeholder' => __('Select Assign Tag', 'fluent-crm'), + 'inline_help' => __('Select the tags that will be assigned when checkbox checked', 'fluent-crm'), + 'dependency' => [ + 'depends_on' => 'status', + 'operator' => '=', + 'value' => 'yes' + ] + ], + 'auto_checked' => [ + 'type' => 'inline-checkbox', + 'label' => '', + 'checkbox_label' => __('Enable auto checked status on checkout page checkbox', 'fluent-crm'), + 'true_label' => 'yes', + 'false_label' => 'no', + 'dependency' => [ + 'depends_on' => 'status', + 'operator' => '=', + 'value' => 'yes' + ] + ], + 'show_only_new' => [ + 'type' => 'inline-checkbox', + 'label' => '', + 'checkbox_label' => __('Do not show the checkbox if current user already in subscribed state', 'fluent-crm'), + 'true_label' => 'yes', + 'false_label' => 'no', + 'dependency' => [ + 'depends_on' => 'status', + 'operator' => '=', + 'value' => 'yes' + ] + ], + 'double_optin' => [ + 'type' => 'inline-checkbox', + 'label' => __('Double Opt-In', 'fluent-crm'), + 'checkbox_label' => __('Enable Double-Optin Email Confirmation', 'fluent-crm'), + 'true_label' => 'yes', + 'false_label' => 'no', + 'dependency' => [ + 'depends_on' => 'status', + 'operator' => '=', + 'value' => 'yes' + ] + ] + ] + ]; + } + + /** + * Get the saved FluentCart checkout subscription settings merged with defaults. + * + * Unlike the WooCommerce equivalent, there is no 'auto_checkout_fill' + * option — only the opt-in checkbox feature is supported for FluentCart. + */ + public function getFluentCartCheckoutSettings() + { + $defaults = [ + 'status' => 'no', + 'checkbox_label' => __('Sign me up for the newsletter!', 'fluent-crm'), + 'auto_checked' => 'no', + 'target_list' => '', + 'show_only_new' => 'yes', + 'target_tags' => [], + 'double_optin' => 'yes' + ]; + + $settings = fluentcrm_get_option('fluent_cart_checkout_form_subscribe_settings', []); + + if (!$settings) { + return $defaults; + } + + $settings = wp_parse_args($settings, $defaults); + + // Ensure target_list is always a string so it matches the string option + // values emitted by the _OptionSelector.vue component (String(option.id)) + if ($settings['target_list']) { + $settings['target_list'] = (string) $settings['target_list']; + } + + return $settings; + } + + /** + * Normalize and whitelist FluentCart checkout subscription settings before + * persisting. Guards the stored option against malformed client payloads: + * yes/no flags are forced to valid values, list/tag IDs are coerced to + * strings (validated as integer IDs) and the label is plain text. + */ + public function sanitizeFluentCartCheckoutSettings($settings) + { + $settings = is_array($settings) ? $settings : []; + + $yesNo = function ($value, $default = 'no') { + if ($value === 'yes' || $value === 'no') { + return $value; + } + return $default; + }; + + $listId = Arr::get($settings, 'target_list'); + $tagIds = array_filter(array_map('intval', (array)Arr::get($settings, 'target_tags', []))); + + return [ + 'status' => $yesNo(Arr::get($settings, 'status')), + 'checkbox_label' => sanitize_text_field(Arr::get($settings, 'checkbox_label', '')), + 'auto_checked' => $yesNo(Arr::get($settings, 'auto_checked')), + 'target_list' => $listId ? (string) intval($listId) : '', + 'show_only_new' => $yesNo(Arr::get($settings, 'show_only_new'), 'yes'), + 'target_tags' => array_values($tagIds), + 'double_optin' => $yesNo(Arr::get($settings, 'double_optin'), 'yes') + ]; + } + + /** + * Field definitions for the FluentCart checkout subscription settings panel, + * rendered by the shared form-builder component in _GeneralSettings.vue. + */ + public function getFluentCartCheckoutFields() + { + return [ + 'title' => __('FluentCart Checkout Subscription Field', 'fluent-crm'), + 'sub_title' => __('Add a subscription box to FluentCart Checkout Form', 'fluent-crm'), + 'fields' => [ + 'status' => [ + 'type' => 'inline-checkbox', + 'label' => '', + 'true_label' => 'yes', + 'false_label' => 'no', + 'checkbox_label' => __('Enable Subscription Checkbox to FluentCart Checkout Page', 'fluent-crm') + ], + 'checkbox_label' => [ + 'label' => __('Checkbox Label for Checkout checkbox', 'fluent-crm'), + 'type' => 'input-text', + 'placeholder' => __('Checkbox Label for Checkout checkbox', 'fluent-crm'), + 'dependency' => [ + 'depends_on' => 'status', + 'operator' => '=', + 'value' => 'yes' + ] + ], + 'target_list' => [ + 'type' => 'option-selector', + 'label' => __('Assign List', 'fluent-crm'), + 'option_key' => 'lists', + 'is_multiple' => false, + 'placeholder' => __('Select Assign List', 'fluent-crm'), + 'inline_help' => __('Select the list that will be assigned when checkbox checked', 'fluent-crm'), + 'dependency' => [ + 'depends_on' => 'status', + 'operator' => '=', + 'value' => 'yes' + ] + ], + 'target_tags' => [ + 'type' => 'option-selector', + 'label' => __('Assign Tags', 'fluent-crm'), + 'option_key' => 'tags', + 'is_multiple' => true, + 'placeholder' => __('Select Assign Tag', 'fluent-crm'), + 'inline_help' => __('Select the tags that will be assigned when checkbox checked', 'fluent-crm'), + 'dependency' => [ + 'depends_on' => 'status', + 'operator' => '=', + 'value' => 'yes' + ] + ], + 'auto_checked' => [ + 'type' => 'inline-checkbox', + 'label' => '', + 'checkbox_label' => __('Enable auto checked status on checkout page checkbox', 'fluent-crm'), + 'true_label' => 'yes', + 'false_label' => 'no', + 'dependency' => [ + 'depends_on' => 'status', + 'operator' => '=', + 'value' => 'yes' + ] + ], + 'show_only_new' => [ + 'type' => 'inline-checkbox', + 'label' => '', + 'checkbox_label' => __('Do not show the checkbox if current user already in subscribed state', 'fluent-crm'), + 'true_label' => 'yes', + 'false_label' => 'no', + 'dependency' => [ + 'depends_on' => 'status', + 'operator' => '=', + 'value' => 'yes' + ] + ], + 'double_optin' => [ + 'type' => 'inline-checkbox', + 'label' => __('Double Opt-In', 'fluent-crm'), + 'checkbox_label' => __('Enable Double-Optin Email Confirmation', 'fluent-crm'), + 'true_label' => 'yes', + 'false_label' => 'no', + 'dependency' => [ + 'depends_on' => 'status', + 'operator' => '=', + 'value' => 'yes' + ] + ] + ] + ]; + } + +} diff --git a/wp-content/plugins/fluent-crm/app/Services/BlockParser.php b/wp-content/plugins/fluent-crm/app/Services/BlockParser.php new file mode 100644 index 0000000..49bf584 --- /dev/null +++ b/wp-content/plugins/fluent-crm/app/Services/BlockParser.php @@ -0,0 +1,511 @@ +parse($content); + } catch (\Throwable $e) { + $parsed = ''; + } + + $useFallback = (bool)apply_filters('fluent_crm/block_parser_legacy_fallback_enabled', true, $content, $parsed); + if ($useFallback && $this->shouldFallbackToLegacy($content, $parsed)) { + return $this->parseWithLegacyParser($content); + } + + return $parsed; + } + + private function shouldFallbackToLegacy($content, $parsed) + { + if (!is_string($content) || trim($content) === '') { + return false; + } + + if (is_string($parsed) && trim($parsed) !== '') { + return false; + } + + return strpos($content, '/'; + + preg_match_all($pattern, $content, $matches, PREG_OFFSET_CAPTURE); + + $lastOffset = 0; + foreach ($matches[0] as $index => $match) { + $blockName = ($matches[1][$index][0] ?? '') . $matches[2][$index][0]; + $attrs = $matches[3][$index][0] ?? '{}'; + $isSelfClosing = !empty($matches[4][$index][0]); + + $blockStart = $match[1] + strlen($match[0]); + + // Find closing tag if not self-closing + if (!$isSelfClosing) { + $closingPattern = '//'; + if (preg_match($closingPattern, $content, $closeMatch, PREG_OFFSET_CAPTURE, $blockStart)) { + $innerHTML = substr($content, $blockStart, $closeMatch[0][1] - $blockStart); + $lastOffset = $closeMatch[0][1] + strlen($closeMatch[0][0]); + } else { + $innerHTML = ''; + } + } else { + $innerHTML = ''; + } + + $blocks[] = [ + 'blockName' => $blockName, + 'attrs' => json_decode($attrs, true) ?? [], + 'innerHTML' => trim($innerHTML), + 'innerBlocks' => [] + ]; + } + + return $blocks; + } + + /** + * Render blocks to email HTML + */ + private function renderBlocks($blocks, $nested = false) + { + $html = ''; + + foreach ($blocks as $block) { + if (empty($block['blockName'])) { + // Classic content or unrecognized block + if (!empty($block['innerHTML'])) { + $html .= $this->wrapInTable($block['innerHTML']); + } + continue; + } + + $html .= $this->renderBlock($block, $nested); + } + + return $html; + } + + /** + * Render individual block + */ + private function renderBlock($block, $isNested = false) + { + $isInvisible = isset($block['attrs']['metadata']['blockVisibility']) && $block['attrs']['metadata']['blockVisibility'] === false; + if ($isInvisible) { + return ''; + } + + $blockName = $block['blockName']; + $attrs = $block['attrs'] ?? []; + $innerHTML = $block['innerHTML'] ?? ''; + $innerBlocks = $block['innerBlocks'] ?? []; + + // Per-block conditional visibility (conditional-content has its own handler) + if ($blockName !== 'fluent-crm/conditional-content') { + if (!$this->checkBlockConditionVisibility($attrs)) { + return ''; + } + } + + // create an unique element ID for blocks that don't have one, to help with styling if needed + $elementId = uniqid('block-', false); + $this->collectInlineStyles($elementId, $attrs, $blockName); + + $attrs['elem_id'] = $elementId; + $attrs['is_root'] = !$isNested; + + // For blocks with innerContent array, reconstruct innerHTML + if (empty($innerHTML) && !empty($block['innerContent'])) { + $innerHTML = implode('', array_filter($block['innerContent'], 'is_string')); + } + + // Handle different block types + switch ($blockName) { + case 'core/block': + return $this->renderSyncedPattern($attrs, $isNested); + + case 'core/paragraph': // done + return $this->renderParagraph($innerHTML, $attrs); + + case 'core/heading': // done + return $this->renderHeading($innerHTML, $attrs); + + case 'core/image': // done + return $this->renderImage($attrs, $innerHTML); + + case 'core/list': // done + return $this->renderList($innerHTML, $innerBlocks, $attrs); + + case 'core/list-item': // done + return $this->renderListItem($innerHTML, $attrs); + + case 'core/quote': // done + return $this->renderQuote($innerHTML, $innerBlocks, $attrs); + + case 'core/button': // done + return $this->renderButton($innerHTML, $attrs); + + case 'core/buttons': // done + return $this->renderButtons($innerBlocks, $attrs); + + case 'core/columns': // done + return $this->renderColumns($innerBlocks, $attrs); + + case 'core/column': // done + return $this->renderColumn($innerBlocks, $attrs, $innerHTML); + + case 'core/separator': // partially done + return $this->renderSeparator($innerHTML, $attrs); + + case 'core/spacer': // done + return $this->renderSpacer($innerHTML, $attrs); + + case 'core/group': // done + return $this->renderGroup($innerBlocks, $attrs, $innerHTML); + + case 'core/row': + return $this->renderRow($innerBlocks, $attrs, $innerHTML); + + case 'core/table': // done + return $this->renderTable($innerHTML, $attrs); + + case 'core/rss': // done + return $this->renderRss($attrs); + + case 'fluentcrm/woo-product': + case 'fluent-crm/woo-product': + return $this->renderWooProductBlock($block, $attrs, $innerHTML); + + case 'fluent-crm/cart-product': + return $this->renderCartProductBlock($block, $attrs, $innerHTML); + + case 'fluent-crm/latest-posts': // partially done + return $this->renderLatestPostsBlock($block, $attrs); + + case 'fluent-crm/woo-products': // partially done + return $this->renderProductsBlock($block, $attrs, $blockName); + + case 'fluent-crm/cart-products': // partially done + return $this->renderCartProductsBlock($block, $attrs); + + case 'fluent-crm/conditional-content': // done + case 'fluentcrm/conditional-group': // done + return $this->renderConditionalGroupBlock($innerBlocks, $attrs, $innerHTML); + + case 'core/freeform': // done + case 'core/html': // done + // Classic editor content - render as-is with email-safe wrapper + return $this->wrapInTable($innerHTML, $attrs); + + case 'core/preformatted': // done + case 'core/code': // done + case 'core/verse': // done + return $this->renderCodeBlock($innerHTML, $attrs); + case 'core/pullquote': // done + return $this->renderPullQuote($innerHTML, $attrs); + case 'core/embed': + case 'core/video': + case 'core/audio': + // For video/audio embeds in email, show a linked thumbnail or text + $url = $attrs['url'] ?? ''; + if (!empty($url)) { + $linkText = __('Click here to view media content', 'fluent-crm'); + return $this->wrapInTable("

{$linkText}

", $attrs); + } + return ''; + default: + // Fallback for unrecognized blocks + if (!empty($innerHTML)) { + $attrs['td_id'] = $attrs['elem_id'] ?? ''; + return $this->wrapInTable($innerHTML, $attrs); + } + if (!empty($innerBlocks)) { + return $this->renderBlocks($innerBlocks, true); + } + return ''; + } + } + + /** + * Render paragraph block + */ + private function renderParagraph($content, $attrs) + { + $content = trim($content); + + // Skip empty paragraphs + if (empty($content) || $content === '

') { + return ''; + } + + // Extract content if it's wrapped in

tags + if (preg_match('/]*>(.*?)<\/p>/s', $content, $matches)) { + $innerContent = $matches[1]; + } else { + $innerContent = $content; + } + + $elementId = $attrs['elem_id'] ?? ''; + + return $this->wrapInTable("

{$innerContent}

", $attrs); + } + + /** + * Render heading block + */ + private function renderHeading($content, $attrs) + { + $level = $attrs['level'] ?? 2; + + // Extract content if it's wrapped in heading tags + if (preg_match('/]*>(.*?)<\/h[1-6]>/s', $content, $matches)) { + $innerContent = $matches[1]; + } else { + $innerContent = $content; + } + // find the existing classnames from $content and preserve them in the new heading tag + $className = ''; + if (preg_match('/]*class=["\']([^"\']*)["\'][^>]*>/s', $content, $matches)) { + $className = $matches[1]; + } + + $elemId = $attrs['elem_id'] ?? ''; + + return $this->wrapInTable("{$innerContent}", $attrs); + } + + /** + * Render image block + */ + private function renderImage($attrs, $innerHTML = '') + { + + // get the content between
tags if exists, as sometimes image block can have that wrapper in innerHTML + if (preg_match('/]*>(.*?)<\/figure>/s', $innerHTML, $matches)) { + $innerHTML = $matches[1]; + } + + $id = $attrs['elem_id'] ?? ''; + + // add the id to the ]*>/s', $innerHTML, $matches)) { + $imgTag = $matches[0]; + // add id attribute to img tag + if (strpos($imgTag, 'id=') === false) { + $newImgTag = str_replace('' . $innerHTML . '
'; + + return $this->wrapInTable($html, $attrs); + } + + /** + * Render list block + */ + private function renderList($content, $innerBlocks, $attrs) + { + $ordered = $attrs['ordered'] ?? false; + $tag = $ordered ? 'ol' : 'ul'; + + $id = $attrs['elem_id'] ?? ''; + + $classes = ['fc_list_item']; + if ($attrsClass = trim((string)Arr::get($attrs, 'className', ''))) { + $classes = array_merge($classes, preg_split('/\s+/', $attrsClass)); + } + if (preg_match('/<' . $tag . '[^>]*class=["\']([^"\']+)["\']/i', $content, $matches)) { + $classes = array_merge($classes, preg_split('/\s+/', trim((string)$matches[1]))); + } + $classes = array_filter(array_unique($classes)); + $classAttr = implode(' ', $classes); + + $fontSizeValue = ''; + if ($fontSize = Arr::get($attrs, 'fontSize')) { + $fontSizeValue = $this->resolveFontSizeValue($fontSize); + } elseif (preg_match('/has-([a-z0-9-]+)-font-size/i', $classAttr, $matches)) { + $fontSizeValue = $this->resolveFontSizeValue($matches[1]); + } + + $lineHeightValue = Arr::get($attrs, 'style.typography.lineHeight', ''); + if (is_string($lineHeightValue) && strpos($lineHeightValue, 'var:') === 0) { + $lineHeightValue = $this->transformToCssVar($lineHeightValue); + } + if (!is_string($lineHeightValue)) { + $lineHeightValue = ''; + } + + $listItems = ''; + // If we have innerBlocks, render them + if (!empty($innerBlocks)) { + $listItems = ''; + foreach ($innerBlocks as $block) { + if ($block['blockName'] === 'core/list-item') { + $listItems .= $this->renderListItem($block['innerHTML'], $block['attrs'] ?? []); + } + } + } + + if (!$listItems) { + return ''; + } + + $inlineStyle = ''; + if ($fontSizeValue) { + $inlineStyle .= 'font-size:' . $fontSizeValue . ';'; + if ($id) { + $this->childCss .= '#' . $id . ' li, #' . $id . ' li p { font-size: ' . $fontSizeValue . '; } '; + } + } + if ($lineHeightValue) { + $inlineStyle .= 'line-height:' . $lineHeightValue . ';'; + if ($id) { + $this->childCss .= '#' . $id . ' li, #' . $id . ' li p { line-height: ' . $lineHeightValue . '; } '; + } + } + + $styleAttr = $inlineStyle ? " style=\"{$inlineStyle}\"" : ''; + + return $this->wrapInTable("<{$tag} class='{$classAttr}' id='$id'{$styleAttr}>{$listItems}", $attrs); + } + + /** + * Render list item + */ + private function renderListItem($content, $attrs) + { + if (!$this->checkBlockConditionVisibility($attrs)) { + return ''; + } + + $fontSizeValue = ''; + $fontFamilyValue = ''; + + // Explicit typography style on list item takes highest priority. + $styleFontSize = Arr::get($attrs, 'style.typography.fontSize', ''); + if (is_string($styleFontSize) && $styleFontSize !== '') { + if (strpos($styleFontSize, 'var:') === 0) { + $styleFontSize = $this->transformToCssVar($styleFontSize); + } + $fontSizeValue = $this->resolveFontSizeValue($styleFontSize); + } + + // Preset slug from attrs (e.g. fc-small). + if (!$fontSizeValue && ($fontSize = Arr::get($attrs, 'fontSize'))) { + $fontSizeValue = $this->resolveFontSizeValue($fontSize); + } + + // Fallback: detect preset class directly from li markup. + if (!$fontSizeValue && is_string($content) && preg_match('/has-([a-z0-9-]+)-font-size/i', $content, $matches)) { + $fontSizeValue = $this->resolveFontSizeValue($matches[1]); + } + + // Explicit typography style on list item takes highest priority. + $styleFontFamily = Arr::get($attrs, 'style.typography.fontFamily', ''); + if (is_string($styleFontFamily) && $styleFontFamily !== '') { + $fontFamilyValue = $this->resolveFontFamilyValue($styleFontFamily); + } + + // Preset slug or raw stack from attrs. + if (!$fontFamilyValue && ($fontFamily = Arr::get($attrs, 'fontFamily'))) { + $fontFamilyValue = $this->resolveFontFamilyValue($fontFamily); + } + + // Fallback: detect font-family preset class from li markup. + if (!$fontFamilyValue && is_string($content) && preg_match('/has-([a-z0-9-]+)-font-family/i', $content, $matches)) { + $fontFamilyValue = $this->resolveFontFamilyValue($matches[1]); + } + + if (!$fontSizeValue && !$fontFamilyValue) { + return $content; + } + + // Ensure list item typography is inline for email clients. + $content = preg_replace_callback('/]*)>/i', function ($matches) use ($fontSizeValue, $fontFamilyValue) { + $attrs = $matches[1]; + $styleParts = []; + if ($fontSizeValue) { + $styleParts[] = 'font-size:' . $fontSizeValue; + } + if ($fontFamilyValue) { + $styleParts[] = 'font-family:' . $fontFamilyValue; + } + $appendedStyles = implode(';', $styleParts); + + if (preg_match('/\sstyle=(["\'])(.*?)\1/i', $attrs, $styleMatch)) { + $existingStyle = rtrim(trim($styleMatch[2]), ';'); + $updatedStyle = $existingStyle . ';' . $appendedStyles; + return str_replace($styleMatch[0], ' style="' . esc_attr($updatedStyle) . '"', $matches[0]); + } + + return ''; + }, $content, 1); + + return $content; + } + + /** + * Render the core RSS block with email-safe markup. + * + * @param array $attrs Block attributes. + * @return string + */ + private function renderRss($attrs) + { + $feedUrl = esc_url_raw((string)Arr::get($attrs, 'feedURL', '')); + if (!$feedUrl || !$this->isSafeRssFeedUrl($feedUrl)) { + return ''; + } + + $rssCacheKey = md5(wp_json_encode([ + 'feed_url' => $feedUrl, + 'items_to_show' => (int)Arr::get($attrs, 'itemsToShow', 5), + 'display_date' => !empty($attrs['displayDate']), + 'display_author' => !empty($attrs['displayAuthor']), + 'display_excerpt' => !empty($attrs['displayExcerpt']), + 'excerpt_length' => (int)Arr::get($attrs, 'excerptLength', 55), + 'open_new_tab' => !empty($attrs['openInNewTab']), + 'rel' => (string)Arr::get($attrs, 'rel', '') + ])); + + if (isset(self::$rssRenderCache[$rssCacheKey])) { + return $this->wrapRssHtmlWithCurrentBlock(self::$rssRenderCache[$rssCacheKey], $attrs); + } + + if (!function_exists('fetch_feed') && defined('ABSPATH') && defined('WPINC')) { + require_once ABSPATH . WPINC . '/feed.php'; + } + + if (!function_exists('fetch_feed')) { + return ''; + } + + $rssRequestArgsFilter = function ($requestArgs, $url) use ($feedUrl) { + if ($url === $feedUrl) { + $requestArgs['timeout'] = 5; + $requestArgs['redirection'] = 3; + $requestArgs['reject_unsafe_urls'] = true; + } + + return $requestArgs; + }; + + add_filter('http_request_args', $rssRequestArgsFilter, 10, 2); + $rss = fetch_feed($feedUrl); + remove_filter('http_request_args', $rssRequestArgsFilter, 10); + + if (is_wp_error($rss) || !$rss || !method_exists($rss, 'get_item_quantity')) { + return ''; + } + + $itemsToShow = max(1, min(20, (int)Arr::get($attrs, 'itemsToShow', 5))); + $quantity = $rss->get_item_quantity($itemsToShow); + if (!$quantity) { + return ''; + } + + $items = $rss->get_items(0, $quantity); + if (!$items) { + return ''; + } + + $listItems = ''; + $displayDate = !empty($attrs['displayDate']); + $displayAuthor = !empty($attrs['displayAuthor']); + $displayExcerpt = !empty($attrs['displayExcerpt']); + $excerptLength = max(1, (int)Arr::get($attrs, 'excerptLength', 55)); + $openInNewTab = !empty($attrs['openInNewTab']); + $rel = trim((string)Arr::get($attrs, 'rel', '')); + + $linkAttrs = ''; + if ($openInNewTab) { + $linkAttrs .= ' target="_blank"'; + } + if ($rel !== '') { + $linkAttrs .= ' rel="' . esc_attr($rel) . '"'; + } + + foreach ($items as $item) { + $title = trim(wp_strip_all_tags(html_entity_decode((string)$item->get_title(), ENT_QUOTES, get_option('blog_charset')))); + if ($title === '') { + $title = __('(no title)', 'fluent-crm'); + } + + $link = esc_url((string)$item->get_link()); + $titleHtml = $link + ? '' . esc_html($title) . '' + : esc_html($title); + + $metaHtml = ''; + + if ($displayDate) { + $timestamp = $item->get_date('U'); + if ($timestamp) { + $gmtOffset = get_option('gmt_offset'); + $timestamp += (int)((float)$gmtOffset * HOUR_IN_SECONDS); + $metaHtml .= '' . + esc_html(date_i18n(get_option('date_format'), $timestamp)) . + ''; + } + } + + if ($displayAuthor) { + $author = $item->get_author(); + if (is_object($author) && method_exists($author, 'get_name')) { + $authorName = trim(wp_strip_all_tags((string)$author->get_name())); + if ($authorName !== '') { + $metaHtml .= '' . + sprintf( + /* translators: %s: author name. */ + esc_html__('by %s', 'fluent-crm'), + esc_html($authorName) + ) . + ''; + } + } + } + + $excerptHtml = ''; + if ($displayExcerpt) { + $description = html_entity_decode((string)$item->get_description(), ENT_QUOTES, get_option('blog_charset')); + $description = trim(wp_strip_all_tags($description)); + if ($description !== '') { + $excerptHtml = '
' . + esc_html(wp_trim_words($description, $excerptLength, ' [...]')) . + '
'; + } + } + + $listItems .= '
' . + '
' . $titleHtml . '
' . + $metaHtml . + $excerptHtml . + '
'; + } + + if (!$listItems) { + return ''; + } + + self::$rssRenderCache[$rssCacheKey] = $listItems; + + return $this->wrapRssHtmlWithCurrentBlock($listItems, $attrs); + } + + /** + * Validate RSS feed URLs before the server fetches remote content. + * + * @param string $url Feed URL. + * @return bool + */ + private function isSafeRssFeedUrl($url) + { + $parsed = wp_parse_url($url); + if (!$parsed || empty($parsed['host'])) { + return false; + } + + $scheme = strtolower(isset($parsed['scheme']) ? $parsed['scheme'] : ''); + if (!in_array($scheme, ['http', 'https'], true)) { + return false; + } + + $host = trim($parsed['host'], '[]'); + if (filter_var($host, FILTER_VALIDATE_IP)) { + $ip = $host; + } else { + $ip = gethostbyname($host); + if ($ip === $host && !filter_var($ip, FILTER_VALIDATE_IP)) { + return false; + } + } + + $isSafe = (bool)filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE); + + /** + * Allow site owners to enforce custom RSS feed source policies. + * + * @param bool $isSafe Whether the feed URL resolves to a public IP. + * @param string $url Feed URL. + * @param string $host Parsed URL host. + * @param string $ip Resolved host IP. + */ + return (bool)apply_filters('fluent_crm/rss_block_is_safe_feed_url', $isSafe, $url, $host, $ip); + } + + /** + * Wrap cached RSS list items with the current block id and table attributes. + * + * @param string $listItems Rendered RSS item markup. + * @param array $attrs Block attributes. + * @return string + */ + private function wrapRssHtmlWithCurrentBlock($listItems, $attrs) + { + $elementId = esc_attr(Arr::get($attrs, 'elem_id', '')); + $html = '
' . $listItems . '
'; + + return $this->wrapInTable($html, $attrs); + } + + /** + * Render quote block + */ + private function renderQuote($content, $innerBlocks, $attrs) + { + + $borderColor = BlockEditorHelper::getBorderColor($attrs, '#4f46e5'); + + $quoteSide = fluentcrm_is_rtl() ? 'right' : 'left'; + $styles = "border-{$quoteSide}: 4px solid " . $borderColor . '; padding-' . $quoteSide . ': 20px;'; + + if (!isset($attrs['style']['spacing']['margin']['bottom'])) { + $styles .= ' margin-bottom: 20px;'; + } + + $html = ''; + if ($innerBlocks) { + $html = $this->renderBlocks($innerBlocks, true); + } + + if (!$html) { + return ''; + } + + // get content if exists in $content + if (preg_match('/]*>(.*?)<\/cite>/s', $content, $matches)) { + $cite = '
' . $matches[1] . '
'; + $html .= $cite; + } + + $id = $attrs['elem_id'] ?? ''; + + return $this->wrapInTable("
{$html}
", $attrs); + } + + /** + * Render buttons container + */ + private function renderButtons($innerBlocks, $attrs) + { + $layout = $attrs['layout'] ?? []; + $justifyContent = $layout['justifyContent'] ?? 'left'; + $orientation = strtolower((string)($layout['orientation'] ?? 'horizontal')); + $isVertical = ($orientation === 'vertical'); + + $alignMap = [ + 'left' => 'left', + 'center' => 'center', + 'right' => 'right', + 'space-between' => 'start', // For email, align to start in the active direction + 'space-around' => 'center', // For email, we'll center and let spacing + ]; + + $textAlign = $this->getDirectionalTextAlign($alignMap[$justifyContent] ?? 'left'); + + $tableStyles = "width: 100%; border-collapse: collapse; text-align: {$textAlign};"; + + $elementId = $attrs['elem_id'] ?? ''; + + unset($attrs['elem_id']); + + $innerHtml = << + HTML; + + + $buttonsHtml = ''; + + foreach ($innerBlocks as $button) { + if ($button['blockName'] === 'core/button') { + $buttonElementId = uniqid('block-', false); + $button['attrs']['elem_id'] = $buttonElementId; + $bttonHtml = $this->renderButton($button['innerHTML'], $button['attrs'] ?? []); + if (!$bttonHtml) { + continue; + } + + if ($isVertical) { + $buttonsHtml .= '
' . $bttonHtml . '
'; + } else { + $buttonsHtml .= $bttonHtml; + } + + $this->collectInlineStyles($buttonElementId, $button['attrs'], 'core/button'); + + } + } + + if (!$buttonsHtml) { + return ''; + } + + $innerHtml .= $buttonsHtml; + + $innerHtml .= ""; + + return $this->wrapInTable($innerHtml, $attrs); + } + + /** + * Render button block + */ + private function renderButton($content, $attrs) + { + if (!$this->checkBlockConditionVisibility($attrs)) { + return ''; + } + + // Extract URL and text from content + $url = '#'; + $text = ''; + + if (!empty($content)) { + // Try to extract from anchor tag + if (preg_match('/]*href=["\']([^"\']*)["\'][^>]*>(.*?)<\/a>/s', $content, $matches)) { + $url = $matches[1]; + $rawText = $matches[2]; + // Remove all HTML tags but keep the text + $text = trim(preg_replace('/<[^>]*>/', '', $rawText)); + } + } + + // Fallback to attrs if extraction failed + if ($url === '#' && !empty($attrs['url'])) { + $url = $attrs['url']; + } + if (!empty($attrs['text'])) { + $text = $attrs['text']; + } + + if (!$text) { + return ''; + } + + $elementId = $attrs['elem_id'] ?? ''; + + $styles = ''; + + if ($width = Arr::get($attrs, 'width')) { + $styles .= "min-width: {$width}%;"; + } + + $class = 'fc_button'; + $className = (string)Arr::get($attrs, 'className', ''); + if ($className) { + $class .= ' ' . $className; + } + + return "escapeButtonUrl($url) . "\">" . esc_html($text) . ""; + } + + /** + * Escape button URLs without stripping smartcodes before the parser phase. + * + * Campaign smartcodes are parsed after Gutenberg blocks are rendered, so running + * esc_url() on a smartcode URL here removes the curly braces and makes the later + * parser miss it. Keep only smartcode-bearing safe-protocol URLs intact with + * esc_attr(); static URLs and unsafe protocols still go through esc_url(). + */ + private function escapeButtonUrl($url) + { + $url = (string)$url; + + if (preg_match('/^http:\/\/(\{\{[^{}\r\n]+}}|##[^#\r\n]+##)$/i', trim($url), $matches)) { + $url = $matches[1]; + } + + if (!preg_match('/(\{\{[^{}\r\n]+}}|##[^#\r\n]+##)/', $url)) { + return esc_url($url); + } + + if (!preg_match('/^([a-z][a-z0-9+.-]*):/i', ltrim($url), $matches)) { + return esc_attr($url); + } + + $protocol = strtolower($matches[1]); + + if (in_array($protocol, ['http', 'https', 'mailto', 'tel'], true)) { + return esc_attr($url); + } + + return esc_url($url); + } + + /** + * Render legacy Woo single product block. + */ + private function renderWooProductBlock($block, $attrs, $innerHTML) + { + if (!defined('WC_PLUGIN_FILE')) { + return ''; + } + + $buttonText = !empty($attrs['buttonText']) ? $attrs['buttonText'] : __('Buy Now', 'fluent-crm'); + $buttonUrl = '#'; + + if (!empty($attrs['productId']) && function_exists('wc_get_product')) { + $product = wc_get_product((int)$attrs['productId']); + if ($product && method_exists($product, 'get_permalink')) { + $buttonUrl = $product->get_permalink(); + } + } + + if (!empty($innerHTML)) { + if (preg_match('/]*href=["\']([^"\']+)["\'][^>]*>/i', $innerHTML, $urlMatch)) { + $buttonUrl = $urlMatch[1]; + } + if (preg_match('/]*>(.*?)<\/a>/is', $innerHTML, $textMatch)) { + $parsedText = trim(wp_strip_all_tags($textMatch[1])); + if ($parsedText) { + $buttonText = $parsedText; + } + } + } + + $buttonBlock = $this->getFirstButtonBlock($block); + $buttonAttrs = Arr::get($buttonBlock, 'attrs', []); + $buttonInnerHTML = Arr::get($buttonBlock, 'innerHTML', ''); + + if (!empty($buttonInnerHTML)) { + if (preg_match('/]*href=["\']([^"\']+)["\'][^>]*>/i', $buttonInnerHTML, $urlMatch)) { + $buttonUrl = $urlMatch[1]; + } + + if (preg_match('/]*>(.*?)<\/a>/is', $buttonInnerHTML, $textMatch)) { + $parsedText = trim(wp_strip_all_tags($textMatch[1])); + if ($parsedText) { + $buttonText = $parsedText; + } + } + } + + if (!empty($buttonAttrs['url'])) { + $buttonUrl = $buttonAttrs['url']; + } + + if (!empty($buttonAttrs['text'])) { + $buttonText = $buttonAttrs['text']; + } + + $buttonElementId = uniqid('block-', false); + $buttonAttrs['elem_id'] = $buttonElementId; + + $buttonHtml = $this->renderButton( + '' . esc_html($buttonText) . '', + $buttonAttrs + ); + + if (!$buttonHtml) { + $buttonHtml = '' . esc_html($buttonText) . ''; + } else { + $this->collectInlineStyles($buttonElementId, $buttonAttrs, 'core/button'); + } + + $html = WooProduct::renderProduct($buttonHtml, [ + 'blockName' => 'fluentcrm/woo-product', + 'attrs' => $attrs + ]); + + if (!$html) { + return ''; + } + + $attrs['td_id'] = $attrs['elem_id'] ?? ''; + return $this->wrapInTable($html, $attrs); + } + + /** + * Find the first nested Gutenberg button block inside product blocks. + */ + private function getFirstButtonBlock($block) + { + $innerBlocks = Arr::get($block, 'innerBlocks', []); + + foreach ($innerBlocks as $innerBlock) { + if (Arr::get($innerBlock, 'blockName') === 'core/button') { + return $innerBlock; + } + + $buttonBlock = $this->getFirstButtonBlock($innerBlock); + if ($buttonBlock) { + return $buttonBlock; + } + } + + return []; + } + + /** + * Render FluentCart single product block. + */ + private function renderCartProductBlock($block, $attrs, $innerHTML) + { + if (!defined('FLUENTCART_VERSION')) { + return ''; + } + + $buttonText = !empty($attrs['buttonText']) ? $attrs['buttonText'] : __('Buy Now', 'fluent-crm'); + $buttonUrl = '#'; + + if (!empty($attrs['productId'])) { + $productPermalink = get_permalink((int)$attrs['productId']); + if ($productPermalink) { + $buttonUrl = $productPermalink; + } + } + + if (!empty($innerHTML)) { + if (preg_match('/]*href=["\']([^"\']+)["\'][^>]*>/i', $innerHTML, $urlMatch)) { + $buttonUrl = $urlMatch[1]; + } + if (preg_match('/]*>(.*?)<\/a>/is', $innerHTML, $textMatch)) { + $parsedText = trim(wp_strip_all_tags($textMatch[1])); + if ($parsedText) { + $buttonText = $parsedText; + } + } + } + + $buttonAttrs = $this->getFirstButtonBlockAttrs($block); + $buttonElementId = uniqid('block-', false); + $buttonAttrs['elem_id'] = $buttonElementId; + + $buttonHtml = $this->renderButton( + '' . esc_html($buttonText) . '', + $buttonAttrs + ); + + if (!$buttonHtml) { + $buttonHtml = '' . esc_html($buttonText) . ''; + } else { + $this->collectInlineStyles($buttonElementId, $buttonAttrs, 'core/button'); + } + + $html = CartProduct::renderProduct($buttonHtml, [ + 'blockName' => 'fluent-crm/cart-product', + 'attrs' => $attrs + ]); + + if (!$html) { + return ''; + } + + $attrs['td_id'] = $attrs['elem_id'] ?? ''; + return $this->wrapInTable($html, $attrs); + } + + /** + * Render legacy latest posts block from FluentCampaign (if available). + */ + private function renderLatestPostsBlock($block, $attrs) + { + if (!class_exists('\FluentCampaign\App\Services\PostParser\LatestPost')) { + return ''; + } + + try { + $html = \FluentCampaign\App\Services\PostParser\LatestPost::renderPosts('', [ + 'blockName' => 'fluent-crm/latest-posts', + 'attrs' => $attrs, + 'innerHTML' => isset($block['innerHTML']) ? $block['innerHTML'] : '' + ]); + + $attrs['elem_id'] = $attrs['elem_id'] ?? ''; + return $this->wrapInTable($html, $attrs); + + } catch (\Throwable $e) { + return ''; + } + } + + /** + * Render product listing blocks via dedicated renderer. + */ + private function renderProductsBlock($block, $attrs, $blockName) + { + $html = WooProducts::renderProducts('', [ + 'blockName' => $blockName, + 'attrs' => $attrs, + 'innerHTML' => isset($block['innerHTML']) ? $block['innerHTML'] : '' + ]); + + if (!$html) { + return ''; + } + + $attrs['td_id'] = $attrs['elem_id'] ?? ''; + return $this->wrapInTable($html, $attrs); + + } + + private function renderCartProductsBlock($block, $attrs) + { + $html = CartProducts::renderProducts('', [ + 'blockName' => 'fluent-crm/cart-products', + 'attrs' => $attrs, + 'innerHTML' => isset($block['innerHTML']) ? $block['innerHTML'] : '' + ]); + + if (!$html) { + return ''; + } + + $attrs['td_id'] = $attrs['elem_id'] ?? ''; + return $this->wrapInTable($html, $attrs); + } + + /** + * Render conditional group block based on subscriber tags. + */ + private function renderConditionalGroupBlock($innerBlocks, $attrs, $innerHTML) + { + $content = ''; + if (!empty($innerBlocks)) { + $content = $this->renderBlocks($innerBlocks, true); + } elseif (!empty($innerHTML)) { + $content = $innerHTML; + } + + if (!$content) { + return ''; + } + + $content = $this->wrapInTable($content, $attrs); + + $subscriber = BlockParserHelper::getSubscriber(); + if (!$subscriber) { + $subscriber = apply_filters('fluent_crm/get_current_block_condition_subscriber', $subscriber); + } + + // Keep content visible in preview/test contexts when subscriber is unknown. + if (!$subscriber) { + return $content; + } + + $tagIds = isset($attrs['tag_ids']) && is_array($attrs['tag_ids']) ? $attrs['tag_ids'] : []; + if (!$tagIds) { + return ''; + } + + $checkType = $this->normalizeConditionalCheckType(isset($attrs['condition_type']) ? $attrs['condition_type'] : 'show_if_tag_exist'); + $tagMatched = method_exists($subscriber, 'hasAnyTagId') ? $subscriber->hasAnyTagId($tagIds) : false; + + if ($checkType === 'show_if_tag_exist') { + return $tagMatched ? $content : ''; + } + + if ($checkType === 'show_if_tag_not_exist') { + return $tagMatched ? '' : $content; + } + + return ''; + } + + + /** + * Render a synced pattern (core/block) by looking up its content from fc_meta + * and recursively rendering the contained blocks. + */ + private static $syncedPatternCache = []; + + private function renderSyncedPattern($attrs, $nested = false) + { + $ref = isset($attrs['ref']) ? (int) $attrs['ref'] : 0; + if (!$ref) { + return ''; + } + + if (!isset(self::$syncedPatternCache[$ref])) { + $pattern = \FluentCrm\App\Models\Meta::where('object_type', 'email_pattern') + ->where('id', $ref) + ->first(); + + self::$syncedPatternCache[$ref] = ($pattern && !empty($pattern->value['content'])) + ? $pattern->value['content'] + : ''; + } + + $content = self::$syncedPatternCache[$ref]; + if (!$content) { + return ''; + } + + $blocks = parse_blocks($content); + if (empty($blocks)) { + return ''; + } + + return $this->renderBlocks($blocks, $nested); + } + + private function renderCodeBlock($innerHTML, $attrs) + { + $code = ''; + // get the content between
 tags if exists, as sometimes code block can have that wrapper in innerHTML
+        if (preg_match('/]*>(.*?)<\/pre>/s', $innerHTML, $matches)) {
+            $code = $matches[1];
+        }
+
+
+        if (!$code) {
+            return '';
+        }
+
+        $elementId = $attrs['elem_id'] ?? '';
+
+        if (!isset($this->inlineStyles['#' . $elementId]['background-color'])) {
+            $this->inlineStyles['#' . $elementId]['background-color'] = '#e5e7eb';
+        }
+
+        return $this->wrapInTable("
{$code}
", $attrs); + + } + + private function renderPullQuote($innerHTML, $attrs) + { + $html = ''; + // get the content between
 tags if exists, as sometimes code block can have that wrapper in innerHTML
+        if (preg_match('/]*>(.*?)<\/blockquote>/s', $innerHTML, $matches)) {
+            $html = $matches[1];
+        }
+
+
+        if (!$html) {
+            return '';
+        }
+
+        $elementId = $attrs['elem_id'] ?? '';
+        $elementSelector = $elementId ? ('#' . $elementId) : '';
+        $existingStyles = $elementSelector ? ($this->inlineStyles[$elementSelector] ?? []) : [];
+
+        $hasBlockFontSize = !empty(Arr::get($existingStyles, 'font-size')) ||
+            !empty(Arr::get($attrs, 'fontSize')) ||
+            !empty(Arr::get($attrs, 'style.typography.fontSize'));
+
+        // Pullquote defaults for title paragraph:
+        // - Always ensure margin + line-height when missing.
+        // - Add 25px font-size only when no font-size is configured.
+        $hasParagraphFontSize = preg_match('/]*(?:style=["\'][^"\']*font-size\s*:|class=["\'][^"\']*has-[a-z0-9-]+-font-size)/i', $html);
+        $needsDefaultTitleFontSize = (!$hasBlockFontSize && !$hasParagraphFontSize);
+
+        $firstPApplied = false;
+        $html = preg_replace_callback('/]*)>/i', function ($matches) use (&$firstPApplied, $needsDefaultTitleFontSize) {
+            if ($firstPApplied) {
+                return $matches[0];
+            }
+            $firstPApplied = true;
+
+            $attrs = $matches[1];
+            if (preg_match('/style=(["\'])(.*?)\1/i', $attrs, $styleMatch)) {
+                $quote = $styleMatch[1];
+                $styleValue = rtrim($styleMatch[2], ';');
+
+                if ($needsDefaultTitleFontSize && !preg_match('/font-size\s*:/i', $styleValue)) {
+                    $styleValue .= '; font-size: 25px';
+                }
+                if (!preg_match('/(?:^|;)\s*margin\s*:/i', $styleValue)) {
+                    $styleValue .= '; margin: 0 0 1em 0';
+                }
+                if (!preg_match('/line-height\s*:/i', $styleValue)) {
+                    $styleValue .= '; line-height: 1.6';
+                }
+
+                $styleValue = trim($styleValue, " ;") . ';';
+                $attrs = preg_replace('/style=(["\'])(.*?)\1/i', 'style=' . $quote . $styleValue . $quote, $attrs, 1);
+                return '';
+            }
+
+            $styleParts = [];
+            if ($needsDefaultTitleFontSize) {
+                $styleParts[] = 'font-size: 25px';
+            }
+            $styleParts[] = 'margin: 0 0 1em 0';
+            $styleParts[] = 'line-height: 1.6';
+
+            return '';
+        }, $html, 1);
+
+        if (!$hasBlockFontSize && !preg_match('/]*(?:style=["\'][^"\']*font-size\s*:|class=["\'][^"\']*has-[a-z0-9-]+-font-size)/i', $html)) {
+            $html = preg_replace_callback('/]*)>/i', function ($matches) {
+                $attrs = $matches[1];
+                if (preg_match('/style=(["\'])(.*?)\1/i', $attrs, $styleMatch)) {
+                    $quote = $styleMatch[1];
+                    $styleValue = rtrim($styleMatch[2], ';') . '; font-size: 16px;';
+                    $attrs = preg_replace('/style=(["\'])(.*?)\1/i', 'style=' . $quote . $styleValue . $quote, $attrs, 1);
+                    return '';
+                }
+
+                return '';
+            }, $html, 1);
+        }
+
+        $styles = 'width: 100%;';
+
+        $defaultStyles = [
+            'padding-left'   => '20px',
+            'padding-right'  => '20px',
+            'padding-bottom' => '20px',
+            'padding-top'    => '20px',
+            'margin-bottom'  => '20px',
+            'text-align'     => $this->getDirectionalTextAlign(Arr::get($attrs, 'textAlign', 'left'))
+        ];
+
+        if ($elementId) {
+            $hasCustomBorder = isset($existingStyles['border'])
+                || isset($existingStyles['border-left'])
+                || isset($existingStyles['border-right'])
+                || isset($existingStyles['border-top'])
+                || isset($existingStyles['border-bottom']);
+
+            if (!$hasCustomBorder) {
+                $defaultStyles['border-left'] = '4px solid #e5e7eb';
+                $defaultStyles['border-right'] = '4px solid #e5e7eb';
+                $defaultStyles['border-bottom'] = '4px solid #e5e7eb';
+                $defaultStyles['border-top'] = '4px solid #e5e7eb';
+            }
+
+            $this->setDefaultBlockStyles($elementId, $defaultStyles);
+        }
+
+        return $this->wrapInTable("
{$html}
", $attrs); + + } + + /** + * Check per-block conditional visibility attributes. + * Returns true if the block should be shown, false if hidden. + */ + private function checkBlockConditionVisibility($attrs) + { + $conditionType = isset($attrs['fcrmConditionType']) ? $attrs['fcrmConditionType'] : ''; + $conditionType = $this->normalizeConditionalCheckType($conditionType); + + if (empty($conditionType)) { + return true; + } + + $subscriber = BlockParserHelper::getSubscriber(); + + if (!$subscriber) { + $subscriber = apply_filters('fluent_crm/get_current_block_condition_subscriber', $subscriber); + } + + if (!$subscriber) { + return true; + } + + $tagIds = isset($attrs['fcrmTagIds']) && is_array($attrs['fcrmTagIds']) ? $attrs['fcrmTagIds'] : []; + if (empty($tagIds)) { + return true; + } + + $tagMatched = $subscriber->hasAnyTagId($tagIds); + + if ($conditionType === 'show_if_tag_exist') { + return $tagMatched; + } + + if ($conditionType === 'show_if_tag_not_exist') { + return !$tagMatched; + } + + return true; + } + + /** + * Keep backward compatibility with legacy conditional values. + */ + private function normalizeConditionalCheckType($checkType) + { + $checkType = trim((string)$checkType); + if ($checkType === '') { + return ''; + } + + $map = [ + // v2 legacy values + 'show_if_tag_exists' => 'show_if_tag_exist', + 'show_if_tag_not_exists' => 'show_if_tag_not_exist', + ]; + + return Arr::get($map, $checkType, $checkType); + } + + /** + * Render columns block + */ + private function renderColumns($innerBlocks, $attrs) + { + if (empty($innerBlocks)) { + return ''; + } + + $hasBlockGap = isset($attrs['style']['spacing']['blockGap']['left']); + $blockGap = 20; + + if ($hasBlockGap) { + $blockGap = (int)str_replace('px', '', $attrs['style']['spacing']['blockGap']['left']); + } + + $id = $attrs['elem_id'] ?? ''; + + $verticalAlignment = Arr::get($attrs, 'verticalAlignment', 'top'); + + $columnCount = count($innerBlocks); + $columnWidth = floor(100 / $columnCount); + $hasExplicitColumnWidths = false; + + foreach ($innerBlocks as $innerBlock) { + if (!empty(Arr::get($innerBlock, 'attrs.width'))) { + $hasExplicitColumnWidths = true; + break; + } + } + + $isMobileStackable = Arr::get($attrs, 'isStackedOnMobile', true); + + $tableClass = 'fc_columns'; + + if ($isMobileStackable) { + $tableClass .= ' fc_columns_stack_mobile'; + } + + $columnTableStyles = [ + 'width' => '100%', + 'border-collapse' => 'collapse', + 'border-spacing' => '0' + ]; + + if ($margins = Arr::get($attrs, 'style.spacing.margin', [])) { + foreach ($margins as $marginType => $marginValue) { + if (!$marginValue) { + continue; + } + + $columnTableStyles['margin-' . $marginType] = $this->normalizeCssSize($marginValue); + } + } + + $columnsHtml = ''; + + foreach ($innerBlocks as $index => $column) { + + $blockName = $column['blockName'] ?? ''; + + if ($blockName === 'core/column') { + $elementId = uniqid('block-', false); + $this->collectInlineStyles($elementId, $column['attrs'] ?? [], 'core/column'); + + $columnVerticalAlignment = Arr::get($column, 'attrs.verticalAlignment', $verticalAlignment); + $styles = [ + 'vertical-align' => $columnVerticalAlignment, + ]; + + $columnWidthValue = $this->normalizeCssSize(Arr::get($column, 'attrs.width', '')); + $widthAttr = ''; + + if ($columnWidthValue) { + $styles['width'] = $columnWidthValue; + + if (substr($columnWidthValue, -2) === 'px') { + $styles['max-width'] = $columnWidthValue; + } + + $widthAttr = $this->normalizeHtmlWidthAttribute($columnWidthValue); + } elseif (!$hasExplicitColumnWidths) { + $styles['width'] = $columnWidth . '%'; + $widthAttr = $columnWidth . '%'; + } + + $padding = $blockGap / 2; + + $styles['padding-left'] = $padding . 'px'; + $styles['padding-right'] = $padding . 'px'; + + $widthMarkup = $widthAttr ? ' width="' . esc_attr($widthAttr) . '"' : ''; + $columnsHtml .= ''; + } else { + $columnsHtml .= ''; + } + + + } + + $columnsHtml .= ''; + + $attrs['td_id'] = $id; + + return $this->wrapInTable($columnsHtml, $attrs); + } + + /** + * Render column block + */ + private function renderColumn($innerBlocks, $attrs, $innerHTML) + { + $html = ''; + + if (!empty($innerBlocks)) { + $html = $this->renderBlocks($innerBlocks, true); + } elseif (!empty($innerHTML)) { + $html = $innerHTML; + } + + $attrs['td_id'] = $attrs['elem_id'] ?? ''; + + return $this->wrapInTable($html, $attrs); + } + + /** + * Render cover block + */ + private function renderCover($innerBlocks, $attrs, $innerHTML) + { + $url = $attrs['url'] ?? ''; + $dimRatio = $attrs['dimRatio'] ?? 50; + $overlayColor = $attrs['overlayColor'] ?? ''; + $style = $attrs['style'] ?? []; + $contentPosition = $attrs['contentPosition'] ?? 'center center'; + $minHeight = $attrs['minHeight'] ?? ''; + $minHeightUnit = $attrs['minHeightUnit'] ?? 'px'; + + // Extract image URL from innerHTML if not in attrs + if (empty($url) && !empty($innerHTML)) { + if (preg_match('/src=["\']([^"\']+)["\']/', $innerHTML, $matches)) { + $url = $matches[1]; + } + } + + $opacity = $dimRatio / 100; + + // Parse content position (e.g., "top center", "center center", "bottom left") + $verticalAlign = 'center'; + $textAlign = 'center'; + + if (!empty($contentPosition)) { + $positions = explode(' ', $contentPosition); + if (count($positions) >= 2) { + $verticalAlign = $positions[0]; // top, center, bottom + $textAlign = $positions[1]; // left, center, right + } elseif (count($positions) === 1) { + $verticalAlign = $positions[0]; + } + } + + $textAlign = $this->getDirectionalTextAlign($textAlign, 'center'); + + // Map vertical alignment to table cell vertical-align + $vAlignStyle = $verticalAlign === 'top' ? 'top' : ($verticalAlign === 'bottom' ? 'bottom' : 'middle'); + + // Determine minimum height + $minHeightValue = $minHeight ? $minHeight . $minHeightUnit : '300px'; + + // Render inner content + $innerContent = ''; + if (!empty($innerBlocks)) { + foreach ($innerBlocks as $block) { + $innerContent .= $this->renderBlock($block); + } + } + + // For email, create a table-based layout with background image + if ($url) { + $html = ''; + $html .= ''; + $html .= ''; + $html .= ''; + $html .= '
'; + $html .= $innerContent; + $html .= '
'; + return $html; + } + + return $this->wrapInTable($innerContent); + } + + /** + * Render separator block + */ + private function renderSeparator($innerHTML, $attrs) + { + $class = 'fc_separator'; + if ($className = Arr::get($attrs, 'className')) { + $class .= ' ' . $className; + } + + $id = $attrs['elem_id'] ?? ''; + $separatorColor = Arr::get($attrs, 'style.color.background', ''); + + if (!$separatorColor) { + $separatorColor = Arr::get($attrs, 'backgroundColor', ''); + } + + if ($separatorColor && strpos($separatorColor, '#') !== 0 && strpos($separatorColor, 'rgb') !== 0 && strpos($separatorColor, 'hsl') !== 0 && strpos($separatorColor, 'var(') !== 0 && strpos($separatorColor, 'var:') !== 0) { + $separatorColor = 'var(--fcom--color--' . $separatorColor . ')'; + } + + if ($separatorColor && strpos($separatorColor, 'var:') === 0) { + $separatorColor = $this->transformToCssVar($separatorColor); + } + + if ($separatorColor && strpos($separatorColor, 'var(') === 0) { + $separatorColor = BlockEditorHelper::replaceStyleSlugsWithValues($separatorColor); + } + + if (!$separatorColor) { + $separatorColor = '#d1d5db'; + } + + $escapedId = esc_attr($id); + $escapedClass = esc_attr($class); + $escapedSeparatorColor = esc_attr($separatorColor); + + $isDots = strpos($class, 'is-style-dots') !== false; + $hasWideAlignmentClass = strpos($class, 'alignwide') !== false || strpos($class, 'alignfull') !== false; + $isWide = strpos($class, 'is-style-wide') !== false || $hasWideAlignmentClass || in_array(Arr::get($attrs, 'align', ''), ['wide', 'full'], true); + + if ($isDots) { + $separator = "
···
"; + return $this->wrapInTable($separator, $attrs); + } + + $lineWidth = $isWide ? '100%' : '100px'; + $separator = "
"; + + return $this->wrapInTable($separator, $attrs); + } + + /** + * Render spacer block + */ + private function renderSpacer($innerHtml, $attrs) + { + $id = $attrs['elem_id'] ?? ''; + + return $this->wrapInTable("
 
", $attrs); + } + + /** + * Render group block + */ + private function renderGroup($innerBlocks, $attrs, $innerHTML) + { + $layoutType = strtolower((string)Arr::get($attrs, 'layout.type', '')); + if ($layoutType === 'flex') { + return $this->renderRow($innerBlocks, $attrs, $innerHTML); + } + + if (!empty($innerBlocks)) { + $content = $this->renderBlocks($innerBlocks, true); + } elseif (!empty($innerHTML)) { + $content = $innerHTML; + } else { + return ''; + } + + $id = $attrs['elem_id'] ?? ''; + $disableBottomSpacing = !empty($attrs['fcrmDisableBottomSpacing']); + $groupClasses = ['fc_group']; + + if (!empty($attrs['is_root'])) { + $groupClasses[] = 'fc_group_root'; + } + + if ($disableBottomSpacing) { + $groupClasses[] = 'fcrm-no-bottom-spacing'; + } + + $contentSize = $this->normalizeCssSize(Arr::get($attrs, 'layout.contentSize', '')); + if ($contentSize) { + $innerStyles = [ + 'max-width' => $contentSize, + 'margin-left' => 'auto', + 'margin-right' => 'auto' + ]; + + $content = "
{$content}
"; + } + + return $this->wrapInTable("
{$content}
", $attrs); + } + + /** + * Render row block. + */ + private function renderRow($innerBlocks, $attrs, $innerHTML) + { + if (empty($innerBlocks)) { + if (!empty($innerHTML)) { + return $this->wrapInTable($innerHTML, $attrs); + } + + return ''; + } + + $layout = Arr::get($attrs, 'layout', []); + $orientation = strtolower((string)Arr::get($layout, 'orientation', 'horizontal')); + $className = strtolower((string)Arr::get($attrs, 'className', '')); + if (strpos($className, 'is-vertical') !== false) { + $orientation = 'vertical'; + } + + // A vertical row behaves like stacked content in email clients. + if ($orientation === 'vertical') { + $content = !empty($innerBlocks) ? $this->renderBlocks($innerBlocks, true) : $innerHTML; + if (!$content) { + return ''; + } + + $id = $attrs['elem_id'] ?? ''; + $disableBottomSpacing = !empty($attrs['fcrmDisableBottomSpacing']); + $groupClasses = ['fc_group']; + if (!empty($attrs['is_root'])) { + $groupClasses[] = 'fc_group_root'; + } + if ($disableBottomSpacing) { + $groupClasses[] = 'fcrm-no-bottom-spacing'; + } + + $contentSize = $this->normalizeCssSize(Arr::get($attrs, 'layout.contentSize', '')); + if ($contentSize) { + $innerStyles = [ + 'max-width' => $contentSize, + 'margin-left' => 'auto', + 'margin-right' => 'auto' + ]; + $content = "
{$content}
"; + } + + return $this->wrapInTable("
{$content}
", $attrs); + } + + $justifyContent = strtolower((string)Arr::get($layout, 'justifyContent', 'left')); + $justifyMode = 'left'; + if (in_array($justifyContent, ['right', 'end'], true)) { + $justifyMode = 'right'; + } elseif ($justifyContent === 'center') { + $justifyMode = 'center'; + } elseif ($justifyContent === 'space-between') { + $justifyMode = 'space-between'; + } + + $rowGap = Arr::get($attrs, 'style.spacing.blockGap', ''); + if (is_array($rowGap)) { + $rowGap = Arr::get($rowGap, 'left', Arr::get($rowGap, 'horizontal', '')); + } + $rowGap = $this->normalizeCssSize($rowGap); + if (!$rowGap) { + $rowGap = '20px'; + } + + $id = $attrs['elem_id'] ?? ''; + $blockCount = count($innerBlocks); + + $renderRowChild = function ($innerBlock) { + $childBlock = $innerBlock; + $childAttrs = (array)Arr::get($childBlock, 'attrs', []); + // Row children must remain content-width for predictable alignment. + $childAttrs['fcrmTableWidth'] = 'auto'; + $childBlock['attrs'] = $childAttrs; + + return $this->renderBlock($childBlock, true); + }; + + if ($justifyMode === 'space-between') { + $tableStyles = [ + 'width' => '100%', + 'border-collapse' => 'separate', + 'border-spacing' => '0' + ]; + $rowHtml = ''; + + $startAlign = $this->getDirectionalTextAlign('left'); + $endAlign = $this->getDirectionalTextAlign('right'); + + foreach ($innerBlocks as $index => $innerBlock) { + $cellStyles = [ + 'vertical-align' => 'top' + ]; + + $cellAlign = 'center'; + if ($index === 0) { + $cellAlign = $startAlign; + } elseif ($index === ($blockCount - 1)) { + $cellAlign = $endAlign; + } + + $rowHtml .= ''; + } + + $rowHtml .= ''; + } else { + $outerAlign = $justifyMode === 'center' + ? 'center' + : ($justifyMode === 'right' ? $this->getDirectionalTextAlign('right') : $this->getDirectionalTextAlign('left')); + + $innerTableStyles = [ + 'width' => 'auto', + 'border-collapse' => 'separate', + 'border-spacing' => '0' + ]; + + $innerHtml = ''; + foreach ($innerBlocks as $index => $innerBlock) { + $cellStyles = [ + 'vertical-align' => 'top' + ]; + if ($index < ($blockCount - 1)) { + if (fluentcrm_is_rtl()) { + $cellStyles['padding-left'] = $rowGap; + } else { + $cellStyles['padding-right'] = $rowGap; + } + } + + $innerHtml .= ''; + } + $innerHtml .= ''; + + $rowHtml = '
' . $innerHtml . '
'; + } + + unset($attrs['elem_id']); + + return $this->wrapInTable($rowHtml, $attrs); + } + + /** + * Append inline CSS to an opening HTML tag without dropping existing styles. + */ + private function appendInlineStyleToTag($tag, $style) + { + if (preg_match('/\sstyle=(["\'])(.*?)\1/i', $tag, $matches)) { + $quote = $matches[1]; + $existingStyle = rtrim(trim($matches[2]), ';'); + $newStyle = $existingStyle ? $existingStyle . '; ' . $style : $style; + + // Use str_replace (not preg_replace) so existing style content cannot be + // misread as a regex backreference in the replacement string. + return str_replace($matches[0], ' style=' . $quote . $newStyle . $quote, $tag); + } + + return preg_replace('/>$/', ' style="' . $style . '">', $tag, 1); + } + + /** + * Apply email-safe stripe backgrounds to table body rows. + */ + private function applyTableStripeStyles($tableContent, $stripeColor = '#f0f0f0') + { + return preg_replace_callback('/]*)>(.*?)<\/tbody>/is', function ($tbodyMatches) use ($stripeColor) { + $rowIndex = 0; + $tbodyContent = preg_replace_callback('/]*)>(.*?)<\/tr>/is', function ($rowMatches) use (&$rowIndex, $stripeColor) { + $rowIndex++; + + if ($rowIndex % 2 === 0) { + return $rowMatches[0]; + } + + $rowContent = preg_replace_callback('/<(td|th)\b([^>]*)>/i', function ($cellMatches) use ($stripeColor) { + $tag = $this->appendInlineStyleToTag($cellMatches[0], 'background-color: ' . $stripeColor . ';'); + + if (stripos($tag, ' bgcolor=') === false) { + $tag = preg_replace('/>$/', ' bgcolor="' . $stripeColor . '">', $tag, 1); + } + + return $tag; + }, $rowMatches[2]); + + return '' . $rowContent . ''; + }, $tbodyMatches[2]); + + return '' . $tbodyContent . ''; + }, $tableContent); + } + + /** + * Render table block + */ + private function renderTable($content, $attrs) + { + $borderConfig = Arr::get($attrs, 'style.border', []); + $hasCustomBorder = !empty(Arr::get($borderConfig, 'width')) || + !empty(Arr::get($borderConfig, 'style')) || + !empty(Arr::get($borderConfig, 'color')) || + !empty(Arr::get($attrs, 'borderColor')); + $className = (string)Arr::get($attrs, 'className', ''); + $isStriped = strpos($className, 'is-style-stripes') !== false; + + if ($hasCustomBorder) { + $borderWidth = Arr::get($borderConfig, 'width', '1px'); + $borderStyle = Arr::get($borderConfig, 'style', 'solid'); + $borderColor = BlockEditorHelper::getBorderColor($attrs); + } else { + $borderWidth = '1px'; + $borderStyle = 'solid'; + $borderColor = '#6b7280'; + } + + $borderCss = "border: {$borderWidth} {$borderStyle} {$borderColor};"; + + $styles = "width: 100%; border-collapse: collapse;"; + $cellStyles = "padding: 6px 10px;" . $borderCss; + + if (!isset($attrs['style']['spacing']['margin']['bottom'])) { + $styles .= " margin-bottom: 20px;"; + } + + // Extract table content + if (preg_match('/]*>(.*?)<\/table>/s', $content, $matches)) { + $tableContent = $matches[1]; + // Add styles to table cells + $tableContent = preg_replace_callback('/]*)>/i', function ($matches) use ($cellStyles) { + return $this->appendInlineStyleToTag($matches[0], $cellStyles); + }, $tableContent); + $tableContent = preg_replace_callback('/]*)>/i', function ($matches) use ($cellStyles) { + return $this->appendInlineStyleToTag($matches[0], $cellStyles . ' font-weight: bold;'); + }, $tableContent); + + if ($isStriped) { + $tableContent = $this->applyTableStripeStyles($tableContent); + } + } else { + return ''; + } + + $id = $attrs['elem_id'] ?? ''; + $tableClass = 'fc_table'; + if ($isStriped) { + $tableClass .= ' fc_table_striped'; + } + + return $this->wrapInTable("{$tableContent}
", $attrs); + } + + /** + * Render social links block + */ + private function renderSocialLinks($innerBlocks, $attrs, $innerHTML) + { + $socialHtml = '
'; + + $socialLinks = []; + + // First, try to get from innerBlocks (preferred method) + if (!empty($innerBlocks)) { + foreach ($innerBlocks as $block) { + if ($block['blockName'] === 'core/social-link') { + $blockAttrs = $block['attrs'] ?? []; + $blockInner = $block['innerHTML'] ?? ''; + + // Reconstruct innerHTML from innerContent if available + if (empty($blockInner) && !empty($block['innerContent'])) { + $blockInner = implode('', array_filter($block['innerContent'], 'is_string')); + } + + $url = $blockAttrs['url'] ?? ''; + $service = $blockAttrs['service'] ?? 'link'; + $label = $blockAttrs['label'] ?? ''; + + // If URL is empty, try to extract from innerHTML + if (empty($url) && !empty($blockInner)) { + if (preg_match('/]*href=["\']([^"\']*)["\']/', $blockInner, $urlMatch)) { + $url = $urlMatch[1]; + } + } + + // Extract label from aria-label or innerHTML + if (empty($label) && !empty($blockInner)) { + if (preg_match('/aria-label=["\']([^"\']*)["\']/', $blockInner, $labelMatch)) { + $label = $labelMatch[1]; + } + } + + // Determine service from URL if not set + if ($service === 'link' && !empty($url)) { + if (strpos($url, 'wordpress.org') !== false || strpos($url, 'wordpress.com') !== false) { + $service = 'wordpress'; + } elseif (strpos($url, 'facebook.com') !== false) { + $service = 'facebook'; + } elseif (strpos($url, 'github.com') !== false) { + $service = 'github'; + } elseif (strpos($url, 'twitter.com') !== false || strpos($url, 'x.com') !== false) { + $service = 'twitter'; + } elseif (strpos($url, 'linkedin.com') !== false) { + $service = 'linkedin'; + } elseif (strpos($url, 'instagram.com') !== false) { + $service = 'instagram'; + } elseif (strpos($url, 'amazon.com') !== false) { + $service = 'amazon'; + } + } + + if (!empty($url)) { + $socialLinks[] = [ + 'url' => $url, + 'service' => $service, + 'label' => $label + ]; + } + } + } + } + + // Fallback: Parse social links from innerHTML + if (empty($socialLinks) && !empty($innerHTML)) { + preg_match_all('/]*class="[^"]*wp-social-link[^"]*"[^>]*>.*?]*href=["\']([^"\']*)["\'][^>]*(?:aria-label=["\']([^"\']*)["\'])?[^>]*>.*?<\/a>.*?<\/li>/s', $innerHTML, $matches); + + if (!empty($matches[1])) { + foreach ($matches[0] as $index => $match) { + $url = $matches[1][$index]; + $label = $matches[2][$index] ?? ''; + + // Determine service from class or URL + $service = 'link'; + if (strpos($match, 'wp-social-link-wordpress') !== false || strpos($url, 'wordpress.org') !== false || strpos($url, 'wordpress.com') !== false) { + $service = 'wordpress'; + } elseif (strpos($match, 'wp-social-link-facebook') !== false || strpos($url, 'facebook.com') !== false) { + $service = 'facebook'; + } elseif (strpos($match, 'wp-social-link-github') !== false || strpos($url, 'github.com') !== false) { + $service = 'github'; + } elseif (strpos($match, 'wp-social-link-twitter') !== false || strpos($url, 'twitter.com') !== false || strpos($url, 'x.com') !== false) { + $service = 'twitter'; + } elseif (strpos($match, 'wp-social-link-linkedin') !== false || strpos($url, 'linkedin.com') !== false) { + $service = 'linkedin'; + } elseif (strpos($match, 'wp-social-link-instagram') !== false || strpos($url, 'instagram.com') !== false) { + $service = 'instagram'; + } elseif (strpos($match, 'wp-social-link-amazon') !== false || strpos($url, 'amazon.com') !== false) { + $service = 'amazon'; + } + + $socialLinks[] = [ + 'url' => $url, + 'service' => $service, + 'label' => $label + ]; + } + } + } + + // Render social links using image icons or better text fallbacks + if (!empty($socialLinks)) { + foreach ($socialLinks as $link) { + $iconHtml = $this->getSocialIconHtml($link['service'], $link['label']); + + $socialHtml .= ''; + $socialHtml .= $iconHtml; + $socialHtml .= ''; + } + } + + $socialHtml .= '
'; + + return $this->wrapInTable($socialHtml); + } + + /** + * Get social media icon HTML (with better styling) + */ + private function getSocialIconHtml($service, $label = '') + { + // Get background color for each service + $colors = [ + 'facebook' => '#1877f2', + 'twitter' => '#1da1f2', + 'linkedin' => '#0077b5', + 'instagram' => '#e4405f', + 'github' => '#181717', + 'wordpress' => '#21759b', + 'amazon' => '#ff9900', + 'link' => '#0073aa' + ]; + + $bgColor = $colors[$service] ?? '#0073aa'; + $alt = $label ?: ucfirst($service); + + // Use service-specific icon rendering + $iconContent = $this->getSocialIconSVG($service); + + return '' . $iconContent . ''; + } + + /** + * Get social media icon SVG or text representation + */ + private function getSocialIconSVG($service) + { + // Simple SVG icons as inline data + $icons = [ + 'facebook' => '', + 'twitter' => '', + 'linkedin' => '', + 'instagram' => '', + 'github' => '', + 'wordpress' => '', + 'amazon' => '', + 'link' => '' + ]; + + return $icons[$service] ?? $icons['link']; + } + + /** + * Get color from WordPress color slug + */ + private function getColorFromSlug($slug) + { + // Debug: Uncomment to see what colors your theme provides + // $this->debugThemeColors(); + + // First, try to get from FluentCRM Helper (which reads theme.json and editor-color-palette) + static $colorMap = null; + + if ($colorMap === null) { + $colorMap = []; + + // Get theme colors using the same method as AdminMenu.php + if (class_exists('\FluentCrm\App\Services\Helper')) { + $themeColors = \FluentCrm\App\Services\Helper::getThemeColorPalette(); + if (!empty($themeColors)) { + foreach ($themeColors as $colorData) { + if (isset($colorData['slug']) && isset($colorData['color'])) { + $colorMap[$colorData['slug']] = $colorData['color']; + } + } + } + + // Also get theme preferences + $themePref = \FluentCrm\App\Services\Helper::getThemePrefScheme(); + if (!empty($themePref['colors'])) { + foreach ($themePref['colors'] as $colorData) { + if (isset($colorData['slug']) && isset($colorData['color'])) { + $colorMap[$colorData['slug']] = $colorData['color']; + } + } + } + } + } + + // Check our color map first + if (isset($colorMap[$slug])) { + return $colorMap[$slug]; + } + + // Try to get theme color from WordPress theme.json or global settings + if (function_exists('wp_get_global_settings')) { + $settings = wp_get_global_settings(); + if (!empty($settings['color']['palette']['theme'])) { + foreach ($settings['color']['palette']['theme'] as $color) { + if (isset($color['slug']) && $color['slug'] === $slug && !empty($color['color'])) { + return $color['color']; + } + } + } + } + + // Try WP_Theme_JSON for block themes + if (class_exists('WP_Theme_JSON_Resolver')) { + $theme_json = \WP_Theme_JSON_Resolver::get_merged_data(); + if ($theme_json) { + $settings = $theme_json->get_settings(); + if (!empty($settings['color']['palette'])) { + foreach ($settings['color']['palette'] as $palette) { + if (isset($palette['slug']) && $palette['slug'] === $slug && !empty($palette['color'])) { + return $palette['color']; + } + } + } + } + } + + // Fallback: Common WordPress and popular theme colors + $colors = [ + // Theme palette colors (adjust these based on your active theme) + // Twenty Twenty-Three defaults + 'theme-palette-color-1' => '#000000', // Base/Black + 'theme-palette-color-2' => '#6f42c1', // Purple + 'theme-palette-color-3' => '#007cba', // Blue + 'theme-palette-color-4' => '#16a085', // Teal + 'theme-palette-color-5' => '#e74c3c', // Red + 'theme-palette-color-6' => '#f39c12', // Orange + 'theme-palette-color-7' => '#ffffff', // White + 'theme-palette-color-8' => '#f5f5f5', // Light Gray + 'theme-palette-color-9' => '#cccccc', // Gray + + // Standard WordPress colors + 'black' => '#000000', + 'white' => '#ffffff', + 'primary' => '#0073aa', + 'secondary' => '#23282d', + 'tertiary' => '#F0F0F1', + + // Common named colors + 'red' => '#e74c3c', + 'blue' => '#3498db', + 'green' => '#2ecc71', + 'yellow' => '#f1c40f', + 'orange' => '#e67e22', + 'purple' => '#9b59b6', + 'cyan' => '#1abc9c', + 'vivid-red' => '#cf2e2e', + 'vivid-orange' => '#ff6900', + 'vivid-cyan-blue' => '#0693e3', + 'vivid-green-cyan' => '#00d084', + 'vivid-purple' => '#9b51e0', + 'luminous-vivid-amber' => '#fcb900', + 'luminous-vivid-orange' => '#ff6900', + 'light-green-cyan' => '#7bdcb5', + 'pale-pink' => '#f78da7', + 'pale-cyan-blue' => '#8ed1fc', + ]; + + return $colors[$slug] ?? '#0073aa'; + } + + /** + * Get font size from WordPress font size slug + */ + private function getFontSizeFromSlug($slug) + { + static $fontSizeMap = null; + + if ($fontSizeMap === null) { + $fontSizeMap = []; + + // Try wp_get_global_settings (WordPress 5.9+) + if (function_exists('wp_get_global_settings')) { + $settings = wp_get_global_settings(); + if (!empty($settings['typography']['fontSizes'])) { + foreach ($settings['typography']['fontSizes'] as $size) { + if (isset($size['slug']) && isset($size['size'])) { + $fontSizeMap[$size['slug']] = $size['size']; + } + } + } + } + + // Try WP_Theme_JSON for block themes + if (empty($fontSizeMap) && class_exists('WP_Theme_JSON_Resolver')) { + $theme_json = \WP_Theme_JSON_Resolver::get_merged_data(); + if ($theme_json) { + $settings = $theme_json->get_settings(); + if (!empty($settings['typography']['fontSizes'])) { + foreach ($settings['typography']['fontSizes'] as $size) { + if (isset($size['slug']) && isset($size['size'])) { + $fontSizeMap[$size['slug']] = $size['size']; + } + } + } + } + } + + // Fallback presets + if (empty($fontSizeMap)) { + $fontSizeMap = [ + 'small' => '14px', + 'medium' => '18px', + 'large' => '20px', + 'x-large' => '28px', + 'extra-small' => '12px', + 'extra-large' => '32px', + 'huge' => '42px' + ]; + } + } + + return $fontSizeMap[$slug] ?? null; + } + + /** + * Get font family from WordPress font family slug or return as-is + */ + private function getFontFamilyFromSlug($fontFamily) + { + // If it looks like a font stack (contains comma), return as-is + if (strpos($fontFamily, ',') !== false) { + return $fontFamily; + } + + static $fontFamilyMap = null; + + if ($fontFamilyMap === null) { + $fontFamilyMap = []; + + // Try wp_get_global_settings (WordPress 5.9+) + if (function_exists('wp_get_global_settings')) { + $settings = wp_get_global_settings(); + if (!empty($settings['typography']['fontFamilies'])) { + foreach ($settings['typography']['fontFamilies'] as $family) { + if (isset($family['slug']) && isset($family['fontFamily'])) { + $fontFamilyMap[$family['slug']] = $family['fontFamily']; + } + } + } + } + + // Try WP_Theme_JSON for block themes + if (empty($fontFamilyMap) && class_exists('WP_Theme_JSON_Resolver')) { + $theme_json = \WP_Theme_JSON_Resolver::get_merged_data(); + if ($theme_json) { + $settings = $theme_json->get_settings(); + if (!empty($settings['typography']['fontFamilies'])) { + foreach ($settings['typography']['fontFamilies'] as $family) { + if (isset($family['slug']) && isset($family['fontFamily'])) { + $fontFamilyMap[$family['slug']] = $family['fontFamily']; + } + } + } + } + } + + // Common font family presets + $fontFamilyMap = array_merge([ + 'system-ui' => '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif', + 'system' => '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif', + 'arial' => 'Arial, Helvetica, sans-serif', + 'helvetica' => '"Helvetica Neue", Helvetica, Arial, sans-serif', + 'times' => '"Times New Roman", Times, serif', + 'times-new-roman' => '"Times New Roman", Times, serif', + 'georgia' => 'Georgia, serif', + 'courier' => '"Courier New", Courier, monospace', + 'courier-new' => '"Courier New", Courier, monospace', + 'verdana' => 'Verdana, Geneva, sans-serif', + 'tahoma' => 'Tahoma, Geneva, sans-serif', + 'trebuchet' => '"Trebuchet MS", Helvetica, sans-serif', + 'trebuchet-ms' => '"Trebuchet MS", Helvetica, sans-serif', + 'palatino' => '"Palatino Linotype", "Book Antiqua", Palatino, serif', + 'garamond' => 'Garamond, serif', + 'impact' => 'Impact, Charcoal, sans-serif', + 'comic-sans' => '"Comic Sans MS", cursive, sans-serif', + 'comic-sans-ms' => '"Comic Sans MS", cursive, sans-serif', + 'monospace' => 'Monaco, "Lucida Console", Courier, monospace' + ], $fontFamilyMap); + } + + // Return mapped font family or original value + return $fontFamilyMap[$fontFamily] ?? $fontFamily; + } + + /** + * Resolve Gutenberg font-family values to email-safe values. + */ + private function resolveFontFamilyValue($fontFamilyValue) + { + $value = trim((string)$fontFamilyValue); + if ($value === '') { + return ''; + } + + if (strpos($value, 'var:preset|font-family|') === 0) { + return $this->getFontFamilyFromSlug(substr($value, strlen('var:preset|font-family|'))); + } + + if (preg_match('/^var\\(--wp--preset--font-family--([a-z0-9-]+)\\)$/i', $value, $matches)) { + return $this->getFontFamilyFromSlug($matches[1]); + } + + if (strpos($value, 'var(') === 0) { + return $value; + } + + return $this->getFontFamilyFromSlug($value); + } + + /** + * Debug helper: Log all theme colors (for development only) + * Uncomment the call in getColorFromSlug() to use + */ + private function debugThemeColors() + { + static $logged = false; + if ($logged) return; + + error_log('=== THEME COLORS DEBUG ==='); + + // Check wp_get_global_settings + if (function_exists('wp_get_global_settings')) { + $settings = wp_get_global_settings(); + error_log('Global Settings Colors: ' . print_r($settings['color'] ?? 'none', true)); + } + + // Check WP_Theme_JSON_Resolver + if (class_exists('WP_Theme_JSON_Resolver')) { + $theme_json = \WP_Theme_JSON_Resolver::get_merged_data(); + if ($theme_json) { + $settings = $theme_json->get_settings(); + error_log('Theme JSON Colors: ' . print_r($settings['color']['palette'] ?? 'none', true)); + } + } + + $logged = true; + } + + /** + * Wrap content in email-safe table structure + */ + private function wrapInTable($content, $atts = []) + { + if (empty(trim($content))) { + return ''; + } + + $atts = (array)$atts; + + $align = Arr::get($atts, 'align', ''); + + $tableClass = 'la-default'; + + if ($align) { + $tableClass = 'la-' . esc_attr($align); + } + + $tableAlign = Arr::get($atts, 'fcrmTableAlign', ''); + if (!$tableAlign && in_array($align, ['left', 'center', 'right'], true)) { + $tableAlign = $this->getDirectionalTextAlign($align); + } + + $tableAlignMarkup = $tableAlign ? ' align="' . esc_attr($tableAlign) . '"' : ''; + + $tableWidth = trim((string)Arr::get($atts, 'fcrmTableWidth', '100%')); + $tableWidthMarkup = ''; + if ($tableWidth !== '' && strtolower($tableWidth) !== 'auto') { + $tableWidthMarkup = ' width="' . esc_attr($tableWidth) . '"'; + } + + $tdClass = 'la-column'; + if (!empty($atts['is_root'])) { + $tdClass = 'la-root-column'; + } + + $tdId = Arr::get($atts, 'td_id', ''); + + return << + + + {$content} + + + +HTML; + } + + private function resolveFontSizeValue($slugOrSize) + { + $value = trim((string)$slugOrSize); + if ($value === '') { + return ''; + } + + // If a WP preset CSS var is passed, extract its slug for proper resolution. + if (preg_match('/^var\\(--wp--preset--font-size--([a-z0-9-]+)\\)$/i', $value, $matches)) { + $value = $matches[1]; + } elseif (strpos($value, 'var(') === 0) { + // Keep unknown CSS var expressions untouched. + return $value; + } + + // Already an explicit CSS size. + if (preg_match('/^-?\d+(\.\d+)?(px|em|rem|%|pt|vh|vw)$/i', $value)) { + return $value; + } + + // Custom defaults in block editor helper (fc-small, fc-medium, etc). + $fontPresets = BlockEditorHelper::getDefaultPreset('font-size'); + foreach ((array)$fontPresets as $preset) { + if (!is_array($preset)) { + continue; + } + if (Arr::get($preset, 'slug') === $value && Arr::get($preset, 'size')) { + return Arr::get($preset, 'size'); + } + } + + // Theme/WordPress presets (small, medium, large, x-large, etc). + if (class_exists('\FluentCrm\App\Services\Helper')) { + $themeFontSizes = Helper::getThemeFontSizes(); + foreach ((array)$themeFontSizes as $preset) { + if (!is_array($preset)) { + continue; + } + if (Arr::get($preset, 'slug') !== $value || !Arr::get($preset, 'size')) { + continue; + } + + $size = Arr::get($preset, 'size'); + if (is_numeric($size)) { + return $size . 'px'; + } + + return (string)$size; + } + } + + // Fallback to WP CSS variable naming. + return 'var(--wp--preset--font-size--' . $value . ')'; + } + + /** + * Generate complete email HTML with wrapper + */ + public function generateEmailHtml($content, $title = '') + { + $parsedContent = $this->parse($content); + + return << + + + + + + {$title} + + + + + + + +
+ + + + + +
+ + +HTML; + } +} + +// Usage Example: +/* +$parser = new GutenbergEmailParser(); + +// Option 1: Parse blocks only (returns HTML fragment) +$post_content = get_post_field('post_content', $post_id); +$emailHtml = $parser->parse($post_content); + +// Option 2: Generate complete email HTML with wrapper +$completeEmail = $parser->generateEmailHtml($post_content, 'Email Title'); + +// Send email +wp_mail($to, $subject, $completeEmail, ['Content-Type: text/html; charset=UTF-8']); +*/ diff --git a/wp-content/plugins/fluent-crm/app/Services/Helper.php b/wp-content/plugins/fluent-crm/app/Services/Helper.php new file mode 100644 index 0000000..3354a61 --- /dev/null +++ b/wp-content/plugins/fluent-crm/app/Services/Helper.php @@ -0,0 +1,2940 @@ +='); + } + + /** + * Parse mixed input into an array. + * + * Accepts either a native array or a JSON string. For string inputs, + * it attempts decoding the raw payload first, then retries with + * `wp_unslash()` only when the string changes. Returns `$default` when + * decoding fails or when the decoded JSON is not an array. + * + * @param mixed $value Input value from request/body. + * @param array $default Fallback value when parsing fails. + * @return array + */ + public static function parseArrayOrJson($value, $default = []) + { + if (!is_string($value)) { + return is_array($value) ? $value : $default; + } + + $payloads = [$value]; + $unslashed = wp_unslash($value); + if ($unslashed !== $value) { + $payloads[] = $unslashed; + } + + foreach ($payloads as $payload) { + $decoded = json_decode($payload, true); + if (json_last_error() === JSON_ERROR_NONE && is_array($decoded)) { + return $decoded; + } + } + + return $default; + } + + public static function getLinksFromString($string) + { + preg_match_all('/]+(href\=["|\'](http.*?)["|\'])/m', $string, $urls); + + if (!empty($urls[2])) { + return $urls[2]; + } + + return []; + } + + public static function urlReplaces($string) + { + preg_match_all('/]+(href=["\'](http[^"\']*)["\'])/m', $string, $urls); + $replaces = $urls[1]; + $urls = $urls[2]; + + // Replace '|' with '%7C' in the URLs + $urls = array_map(function ($url) { + return str_replace('|', '%7C', $url); + }, $urls); + + $formatted = []; + $baseUrl = self::getSiteUrl(); + + foreach ($urls as $index => $url) { + $urlSlug = UrlStores::getUrlSlug($url); + if (!$urlSlug) { + continue; + } + $formatted[$replaces[$index]] = add_query_arg([ + 'ns_url' => $urlSlug + ], $baseUrl); + } + return $formatted; + } + + public static function attachUrls($html, $campaignUrls, $insertId, $hash = false) + { + $hasSmartUrl = strpos($html, 'smart_url') !== false; + + foreach ($campaignUrls as $src => $url) { + $url .= '&mid=' . $insertId; + if ($hash) { + $url .= '&fch=' . substr($hash, 0, 8); + } + + if ($hasSmartUrl && strpos($src, 'smart_url') !== false) { + $url .= '&signed_hash=' . rawurlencode(self::signSmartUrlHash($hash)); + } + + $campaignUrls[$src] = 'href="' . $url . '"'; + } + return str_replace(array_keys($campaignUrls), array_values($campaignUrls), $html); + } + + public static function attachAnonymousUrls($html, $campaignUrls, $insertId, $hash = false) + { + $hasSmartUrl = strpos($html, 'smart_url') !== false; + foreach ($campaignUrls as $src => $url) { + $url .= '&mid=' . $insertId . '&ano=1'; + if ($hash) { + $url .= '&fch=' . substr($hash, 0, 8); + } + + if ($hasSmartUrl && strpos($src, 'smart_url') !== false) { + $url .= '&signed_hash=' . rawurlencode(self::signSmartUrlHash($hash)); + } + + $campaignUrls[$src] = 'href="' . $url . '"'; + } + + return str_replace(array_keys($campaignUrls), array_values($campaignUrls), $html); + } + + /** + * Generate an HMAC signature for smart URL verification. + * + * Uses a dedicated persistent key (not wp_salt) so that WordPress + * salt rotation does not invalidate previously sent email links. + * + * @param string $hash The email hash to sign. + * @return string + */ + public static function signSmartUrlHash($hash) + { + return hash_hmac('sha256', $hash, wp_salt('auth')); + } + + /** + * Verify a smart URL signed hash. + * + * Supports both the new HMAC signatures and legacy bcrypt hashes + * for backward compatibility with emails sent before the migration. + * + * @param string $emailHash The campaign email hash. + * @param string $signedHash The signed hash from the URL. + * @return bool + */ + public static function verifySmartUrlHash($emailHash, $signedHash) + { + // New HMAC verification (fast, constant-time) + $expected = self::signSmartUrlHash($emailHash); + if (hash_equals($expected, $signedHash)) { + return true; + } + + // Backward compatibility: verify legacy bcrypt hashes + // for emails sent before the HMAC migration + return wp_check_password($emailHash, $signedHash); + } + + public static function generateEmailHash($insertId = null) + { + return wp_generate_uuid4(); + } + + public static function injectTrackerPixel($emailBody, $hash, $emailId = null) + { + if (!$hash) { + return $emailBody; + } + + $trackingType = fluentcrmTrackEmailOpen(); + + if (!$trackingType) { + return $emailBody; + } + + $args = [ + 'fluentcrm' => 1, + 'route' => 'open', + '_e_hash' => $hash, + '_e_id' => $emailId + ]; + + if ($trackingType === 'anonymous') { + $args['ano'] = 1; + } + + $trackImageUrl = add_query_arg($args, self::getSiteUrl()); + $trackPixelHtml = ''; + + if (strpos($emailBody, '{fluent_track_pixel}') !== false) { + $emailBody = str_replace('{fluent_track_pixel}', $trackPixelHtml, $emailBody); + } elseif (stripos($emailBody, '') !== false) { + // Case-insensitive replace before the first closing body tag. + $emailBody = preg_replace('##i', $trackPixelHtml . '$0', $emailBody, 1); + } else { + // No body wrapper (e.g. raw_html templates with HTML fragments) — + // append so the pixel is never silently dropped. + $emailBody .= $trackPixelHtml; + } + + return $emailBody; + } + + public static function getProfileSections() + { + $sections = [ + 'subscriber' => [ + 'name' => 'subscriber', + 'title' => __('Overview', 'fluent-crm'), + 'handler' => 'route' + ], + 'subscriber_emails' => [ + 'name' => 'subscriber_emails', + 'title' => __('Emails', 'fluent-crm'), + 'handler' => 'route' + ], + ]; + + if (apply_filters('fluent_crm/sms_moudle_enabled', false)) { + $sections['subscriber_sms'] = [ + 'name' => 'subscriber_sms', + 'title' => __('SMS', 'fluent-crm'), + 'handler' => 'route' + ]; + } + + if (self::getPurchaseHistoryProviders()) { + $sections['subscriber_purchases'] = [ + 'name' => 'subscriber_purchases', + 'title' => __('Purchases', 'fluent-crm'), + 'handler' => 'route' + ]; + } + + if (defined('FLUENTFORM')) { + $sections['subscriber_form_submissions'] = [ + 'name' => 'subscriber_form_submissions', + 'title' => __('Forms', 'fluent-crm'), + 'handler' => 'route' + ]; + } + + /** + * Filter the list of support ticket providers. + * + * This filter allows you to modify the array of support ticket providers used in FluentCRM. + * + * @param array An array of support ticket providers. + * @since 2.5.1 + * + */ + $supportProviders = apply_filters('fluentcrm-support_tickets_providers', []); + if ($supportProviders) { + $sections['subscriber_support_tickets'] = [ + 'name' => 'subscriber_support_tickets', + 'title' => __('Tickets', 'fluent-crm'), + 'handler' => 'route' + ]; + } + + $sections['subscriber_notes'] = [ + 'name' => 'subscriber_notes', + 'title' => __('Notes', 'fluent-crm'), + 'handler' => 'route' + ]; + + /** + * Filter the contact profile sections in FluentCRM. + * + * This filter allows modification of the contact profile sections array in FluentCRM. + * + * @param array $sections An array of profile sections. + * @since 2.2.0 + * + */ + return apply_filters('fluentcrm_profile_sections', $sections); + } + + public static function getDefaultEmailTemplate() + { + /** + * Filter the default email design template. + * + * This filter allows you to modify the default email design template used by FluentCRM. + * + * @param string The default email design template. Default 'simple'. + * @since 2.7.0 + * + */ + return apply_filters('fluent_crm/default_email_design_template', 'simple'); + } + + public static function getGlobalSmartCodes() + { + $subscriberCodes = [ + 'key' => 'contact', + 'title' => __('Contact', 'fluent-crm'), + /** + * Filter the smartcodes available for FluentCRM contacts. + * + * This filter allows modification of the smartcodes that can be used for FluentCRM contacts. + * + * @param array $smartcodes An associative array of smartcodes and their descriptions. + * Default smartcodes: + * - '{{contact.full_name}}' => 'Full Name' + * - '{{contact.prefix}}' => 'Name Prefix' + * - '{{contact.first_name}}' => 'First Name' + * - '{{contact.last_name}}' => 'Last Name' + * - '{{contact.email}}' => 'Contact Email' + * - '{{contact.id}}' => 'Contact ID' + * - '{{contact.user_id}}' => 'User ID' + * - '{{contact.address_line_1}}' => 'Address Line 1' + * - '{{contact.address_line_2}}' => 'Address Line 2' + * - '{{contact.city}}' => 'City' + * - '{{contact.state}}' => 'State' + * - '{{contact.postal_code}}' => 'Postal Code' + * - '{{contact.country}}' => 'Country' + * - '{{contact.phone}}' => 'Phone Number' + * - '{{contact.status}}' => 'Status' + * - '{{contact.date_of_birth}}' => 'Date of Birth' + * @since 1.0.0 + * + */ + 'shortcodes' => apply_filters('fluentcrm_contact_smartcodes', [ + '{{contact.full_name}}' => __('Full Name', 'fluent-crm'), + '{{contact.prefix}}' => __('Name Prefix', 'fluent-crm'), + '{{contact.first_name}}' => __('First Name', 'fluent-crm'), + '{{contact.last_name}}' => __('Last Name', 'fluent-crm'), + '{{contact.email}}' => __('Contact Email', 'fluent-crm'), + '{{contact.id}}' => __('Contact ID', 'fluent-crm'), + '{{contact.user_id}}' => __('User ID', 'fluent-crm'), + '{{contact.address_line_1}}' => __('Address Line 1', 'fluent-crm'), + '{{contact.address_line_2}}' => __('Address Line 2', 'fluent-crm'), + '{{contact.city}}' => __('City', 'fluent-crm'), + '{{contact.state}}' => __('State', 'fluent-crm'), + '{{contact.postal_code}}' => __('Postal Code', 'fluent-crm'), + '{{contact.country}}' => __('Country', 'fluent-crm'), + '{{contact.phone}}' => __('Phone Number', 'fluent-crm'), + '{{contact.status}}' => __('Status', 'fluent-crm'), + '{{contact.date_of_birth}}' => __('Date of Birth', 'fluent-crm') + ]) + ]; + + if (self::isCompanyEnabled()) { + $subscriberCodes['shortcodes']['{{contact.company.name}}'] = __('Company Name', 'fluent-crm'); + $subscriberCodes['shortcodes']['{{contact.company.industry}}'] = __('Company Industry', 'fluent-crm'); + $subscriberCodes['shortcodes']['{{contact.company.address}}'] = __('Company Address', 'fluent-crm'); + } + + $smartCodes[] = $subscriberCodes; + + $customFields = fluentcrm_get_option('contact_custom_fields', []); + + if ($customFields) { + $shortcodes = []; + foreach ($customFields as $item) { + $shortcodes['{{contact.custom.' . $item['slug'] . '}}'] = $item['label']; + } + $smartCodes[] = [ + 'key' => 'contact_custom_fields', + 'title' => __('Custom Fields', 'fluent-crm'), + 'shortcodes' => $shortcodes + ]; + } + + $smartCodes[] = [ + 'key' => 'general', + 'title' => __('General', 'fluent-crm'), + /** + * Filter to modify the general smartcodes used in FluentCRM. + * + * @param array $shortcodes An associative array of smartcodes and their descriptions. + * + * Default smartcodes: + * - '{{crm.business_name}}' => 'Business Name' + * - '{{crm.business_address}}' => 'Business Address' + * - '{{wp.admin_email}}' => 'Admin Email' + * - '##wp.url##' => 'Site URL' + * - '{{other.date.+2 days}}' => 'Dynamic Date (ex: +2 days from now)' + * - '{{other.date_format.D, d M, Y}}' => 'Custom Date Format (Any PHP Date Format)' + * - '{{other.latest_post.title}}' => 'Latest Post Title (Published)' + * - '##crm.unsubscribe_url##' => 'Unsubscribe URL' + * - '##crm.manage_subscription_url##' => 'Manage Subscription URL' + * - '##web_preview_url##' => 'View On Browser URL' + * - '{{crm.unsubscribe_html|Unsubscribe}}' => 'Unsubscribe Hyperlink HTML' + * - '{{crm.manage_subscription_html|Manage Preference}}' => 'Manage Subscription Hyperlink HTML' + * @since 2.7.0 + * + */ + 'shortcodes' => apply_filters('fluent_crm/general_smartcodes', [ + '{{crm.business_name}}' => __('Business Name', 'fluent-crm'), + '{{crm.business_address}}' => __('Business Address', 'fluent-crm'), + '{{wp.admin_email}}' => __('Admin Email', 'fluent-crm'), + '##wp.url##' => __('Site URL', 'fluent-crm'), + '{{other.date.+2 days}}' => __('Dynamic Date (ex: +2 days from now)', 'fluent-crm'), + '{{other.date_format.D, d M, Y}}' => __('Custom Date Format (Any PHP Date Format)', 'fluent-crm'), + '{{other.latest_post.title}}' => __('Latest Post Title (Published)', 'fluent-crm'), + '##crm.unsubscribe_url##' => __('Unsubscribe URL', 'fluent-crm'), + '##crm.manage_subscription_url##' => __('Manage Subscription URL', 'fluent-crm'), + '##web_preview_url##' => __('View On Browser URL', 'fluent-crm'), + '{{crm.unsubscribe_html|Unsubscribe}}' => __('Unsubscribe Hyperlink HTML', 'fluent-crm'), + '{{crm.manage_subscription_html|Manage Preference}}' => __('Manage Subscription Hyperlink HTML', 'fluent-crm'), + ]) + ]; + + /** + * Filter the smart code groups. + * + * This filter allows modification of the smart code groups array. + * + * @param array $smartCodes An array of smart code groups. + * @since 2.7.0 + * + */ + return apply_filters('fluent_crm/smartcode_groups', $smartCodes); + } + + public static function getExtendedSmartCodes() + { + /** + * Filter the extended smart codes for FluentCRM. + * + * This filter allows you to modify the array of extended smart codes used in FluentCRM. + * + * @param array An array of extended smart codes. + * @since 2.7.0 + * + */ + return array_values(apply_filters('fluent_crm/extended_smart_codes', [])); + } + + public static function getDoubleOptinSettings() + { + if ($settings = fluentcrm_get_option('double_optin_settings', [])) { + if (empty($settings['after_confirmation_type'])) { + $settings['after_confirmation_type'] = 'message'; + $settings['after_conf_redirect_url'] = ''; + } + return $settings; + } + + $businessName = ''; + $businessEmail = ''; + $businessAddress = ''; + $subject = 'Please Confirm Subscription'; + $business = fluentcrmGetGlobalSettings('business_settings', []); + + if (!empty($business['business_name'])) { + $businessName = $business['business_name']; + $subject = "{$businessName} : Please Confirm Subscription"; + if (!empty($business['business_address'])) { + $businessAddress = $business['business_address']; + } + } + + $emailSettings = fluentcrmGetGlobalSettings('email_settings', []); + if (!empty($emailSettings['from_email'])) { + $businessEmail = $emailSettings['from_email']; + } + + return [ + 'email_subject' => $subject, + 'email_pre_header' => '', + 'design_template' => 'simple', + 'email_body' => '

Please Confirm Subscription

Yes, subscribe me to the mailing list

 

If you received this email by mistake, simply delete it. You won\'t be subscribed if you don\'t click the confirmation link above.

For questions about this list, please contact:
' . $businessEmail . '

', + 'after_confirmation_type' => 'message', + 'after_confirm_message' => '

Subscription Confirmed

Your subscription to our list has been confirmed.

Thank you for subscribing!

 

' . $businessName . '

' . $businessAddress . '

 

Continue to our Website

', + 'after_conf_redirect_url' => '', + ]; + } + + public static function getEmailDesignTemplates() + { + $defaultDesignConfig = BlockEditorHelper::getDefaultPrefConfig(); + + if (defined('FLUENTCAMPAIGN')) { + $defaultDesignConfig['disable_footer'] = 'no'; + } + + $plainConfig = $defaultDesignConfig; + $plainConfig['body_bg_color'] = '#FFFFFF'; + $plainConfig['design_template'] = 'plain'; + + $classicConfig = $plainConfig; + $classicConfig['design_template'] = 'classic'; + + $emptyConfig = [ + 'content_font_family' => "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'", + ]; + + /** + * Filter the email design templates available in FluentCRM. + * + * @param array { + * An array of email design templates. + * + * @type array $simple { + * @type string $id The template ID. + * @type string $label The template label. + * @type string $image The URL to the template image. + * @type array $config The configuration array for the template. + * @type bool $use_gutenberg Whether to use Gutenberg editor. + * } + * @type array $plain { + * @type string $id The template ID. + * @type string $label The template label. + * @type string $image The URL to the template image. + * @type array $config The configuration array for the template. + * @type bool $use_gutenberg Whether to use Gutenberg editor. + * } + * @type array $classic { + * @type string $id The template ID. + * @type string $label The template label. + * @type string $image The URL to the template image. + * @type array $config The configuration array for the template. + * @type bool $use_gutenberg Whether to use Gutenberg editor. + * } + * @type array $raw_classic { + * @type string $id The template ID. + * @type string $label The template label. + * @type string $image The URL to the template image. + * @type array $config The configuration array for the template. + * @type bool $use_gutenberg Whether to use Gutenberg editor. + * @type string $template_type The type of the template. + * @type string $template_info Additional information about the template. + * } + * @type array $raw_html { + * @type string $id The template ID. + * @type string $label The template label. + * @type string $image The URL to the template image. + * @type array $config The configuration array for the template. + * @type bool $use_gutenberg Whether to use Gutenberg editor. + * @type string $template_type The type of the template. + * @type string $template_info Additional information about the template. + * } + * } + * @since 2.6.51 + * + */ + $templates = apply_filters('fluent_crm/email_design_templates', [ + 'simple' => [ + 'id' => 'simple', + 'label' => __('Simple Boxed', 'fluent-crm'), + 'image' => fluentCrmMix('images/gutenberg-builder.svg'), + 'config' => $defaultDesignConfig, + 'use_gutenberg' => true + ], + 'plain' => [ + 'id' => 'plain', + 'label' => __('Plain Centered', 'fluent-crm'), + 'image' => fluentCrmMix('images/plain_centered.svg'), + 'config' => $plainConfig, + 'use_gutenberg' => true + ], + 'classic' => [ + 'id' => 'classic', + 'label' => __('Plain Left', 'fluent-crm'), + 'image' => fluentCrmMix('images/plain_left.svg'), + 'config' => $classicConfig, + 'use_gutenberg' => true + ], + 'raw_classic' => [ + 'id' => 'raw_classic', + 'label' => __('Classic Editor', 'fluent-crm'), + 'image' => fluentCrmMix('images/classic-editor.svg'), + 'config' => $emptyConfig, + 'use_gutenberg' => false, + 'template_type' => 'classic_editor', + 'template_info' => '

Classic Text Based Email

Type your simple email and FluentCRM will send that without altering any design processing. The default footer will be injected after your content if footer is not disabled.

' + ], + 'raw_html' => [ + 'id' => 'raw_html', + 'label' => __('Raw HTML', 'fluent-crm'), + 'image' => fluentCrmMix('images/html-editor.svg'), + 'config' => [], + 'use_gutenberg' => false, + 'template_type' => 'raw_text_box', + 'template_info' => '

Raw HTML Template

You can use any type of valid html and FluentCRM will send that without altering any design processing.

' + ] + ]); + + if (!defined('FLUENTCAMPAIGN')) { + $templates['visual_builder'] = [ + 'id' => 'visual_builder', + 'label' => __('Visual Builder', 'fluent-crm'), + 'image' => fluentCrmMix('images/visual-builder.svg'), + 'config' => $emptyConfig, + 'use_gutenberg' => false, + 'template_type' => 'visual_builder_demo' + ]; + } + + return $templates; + } + + public static function getTemplateConfig($templateName = '', $withGlobal = true) + { + if (!$templateName) { + $templateName = self::getDefaultEmailTemplate(); + } + $templates = self::getEmailDesignTemplates(); + if (!isset($templates[$templateName])) { + $templateName = 'simple'; + } + + $config = Arr::get($templates, $templateName . '.config', []); + + if ($withGlobal) { + $globalSettings = fluentcrm_get_option('global_email_style_config', []); + return wp_parse_args($globalSettings, $config); + } + + return $config; + + } + + public static function getActivatedFeatures() + { + return [ + 'fluentcampaign' => defined('FLUENTCAMPAIGN_FRAMEWORK_VERSION'), + 'frontend_portal' => defined('FLUENTCAMPAIGN_FRAMEWORK_VERSION') && self::isExperimentalEnabled('frontend_portal'), + 'company_module' => self::isCompanyEnabled(), + 'event_tracking' => self::isExperimentalEnabled('event_tracking'), + /** + * Filter to disable email open tracking in FluentCRM. + * + * This filter allows to disable email open tracking globally. + * + * @param bool Whether to disable email open tracking. Default is false. + * @return bool Filtered value to enable or disable email open tracking. + * @since 2.8.0 + * + */ + 'email_open_tracking' => !apply_filters('fluentcrm_disable_email_open_tracking', false), + /** + * Filter to enable or disable email click tracking. + * + * This filter allows you to control whether email click tracking is enabled or disabled. + * + * @param bool Whether to enable email click tracking. Default true. + * @since 2.8.0 + * + */ + 'email_click_tracking' => apply_filters('fluent_crm/track_click', true), + ]; + } + + public static function getContactPrefixes($withKeyed = false) + { + /** + * Base contact prefixes with translatable labels. + * These will show up in Loco Translate under the 'fluent-crm' domain. + */ + $prefixes = [ + __('Mr', 'fluent-crm'), + __('Mrs', 'fluent-crm'), + __('Ms', 'fluent-crm') + ]; + + /** + * Filter the contact name prefixes. + * + * This filter is deprecated. Please use fluent_crm/contact_name_prefixes instead. + * + * @param array An array of contact name prefixes. + * @deprecated 2.7.0 Use fluent_crm/contact_name_prefixes instead. + * + * @since 2.5.5 + * + */ + $prefixes = apply_filters('fluentcrm_contact_name_prefixes', $prefixes); + + /** + * Filter the contact name prefixes. + * + * @param array $prefixes An array of contact name prefixes. + * @since 2.7.0 + * + */ + $prefixes = apply_filters('fluent_crm/contact_name_prefixes', $prefixes); + + if ($withKeyed) { + $keyedNames = []; + foreach ($prefixes as $prefix) { + $keyedNames[$prefix] = $prefix; + } + return $keyedNames; + } + return $prefixes; + } + + public static function getGlobalEmailSettings() + { + $defaultFooter = '{{crm.business_name}}, {{crm.business_address}}
Don\'t like these emails? Unsubscribe or Manage Email Subscriptions'; + + $defaults = [ + 'from_name' => '', + 'from_email' => '', + 'emails_per_second' => 15, + 'email_footer' => $defaultFooter, + 'pref_list_type' => 'no', + 'pref_list_items' => [], + 'pref_form' => 'no', + 'pref_general' => ['first_name', 'last_name'], + 'pref_custom' => [], + 'show_on_page' => 'no', + 'pref_page_id' => '' + ]; + + if ($settings = fluentcrmGetGlobalSettings('email_settings', [])) { + if (empty($settings['email_footer'])) { + $settings['email_footer'] = $defaultFooter; + } + + if (empty($settings['pref_form'])) { + $settings['pref_form'] = 'no'; + $settings['pref_general'] = ['first_name', 'last_name']; + $settings['pref_custom'] = []; + } + + if (!isset($settings['pref_general'])) { + $settings['pref_general'] = []; + } + + if (!isset($settings['pref_custom'])) { + $settings['pref_custom'] = []; + } + + return wp_parse_args($settings, $defaults); + } + + return $defaults; + } + + public static function getPurchaseHistoryProviders() + { + $validProviders = []; + + if (defined('FLUENTCART_VERSION')) { + $validProviders['fluent_cart'] = [ + 'title' => __('FluentCart Purchase History', 'fluent-crm'), + 'name' => __('FluentCart', 'fluent-crm') + ]; + } + + if (defined('WC_PLUGIN_FILE')) { + $validProviders['woocommerce'] = [ + 'title' => __('Woocommerce Purchase History', 'fluent-crm'), + 'name' => __('WooCommerce', 'fluent-crm') + ]; + } + + if (self::isEdd3()) { + $validProviders['edd'] = [ + 'title' => __('EDD Purchase History', 'fluent-crm'), + 'name' => __('Easy Digital Downloads', 'fluent-crm') + ]; + } + + if (defined('WPPAYFORM_VERSION')) { + $validProviders['payform'] = [ + 'title' => __('Paymattic Purchase History', 'fluent-crm'), + 'name' => __('Paymattic', 'fluent-crm') + ]; + } + + if (defined('PMPRO_VERSION') && defined('FLUENTCAMPAIGN')) { + $validProviders['pmpro'] = [ + 'title' => __('Paid Membership Pro Purchase History', 'fluent-crm'), + 'name' => __('Paid Membership Pro', 'fluent-crm') + ]; + } + + /** + * Filter the list of valid purchase history providers. + * + * This filter allows modification of the valid purchase history providers used in FluentCRM. + * + * @param array $validProviders An array of valid purchase history providers. + * @since 2.7.0 + * + */ + return apply_filters('fluent_crm/purchase_history_providers', $validProviders); + } + + public static function getThemePrefScheme() + { + static $pref; + if (!$pref) { + + $color_palette = [ + [ + "name" => __("Black", "fluent-crm"), + "slug" => "black", + "color" => "#000000" + ], + [ + "name" => __("Cyan bluish gray", "fluent-crm"), + "slug" => "cyan-bluish-gray", + "color" => "#abb8c3" + ], + [ + "name" => __("White", "fluent-crm"), + "slug" => "white", + "color" => "#ffffff" + ], + [ + "name" => __("Pale pink", "fluent-crm"), + "slug" => "pale-pink", + "color" => "#f78da7" + ], + [ + "name" => __("Luminous vivid orange", "fluent-crm"), + "slug" => "luminous-vivid-orange", + "color" => "#ff6900" + ], + [ + "name" => __("Luminous vivid amber", "fluent-crm"), + "slug" => "luminous-vivid-amber", + "color" => "#fcb900" + ], + [ + "name" => __("Light green cyan", "fluent-crm"), + "slug" => "light-green-cyan", + "color" => "#7bdcb5" + ], + [ + "name" => __("Vivid green cyan", "fluent-crm"), + "slug" => "vivid-green-cyan", + "color" => "#00d084" + ], + [ + "name" => __("Pale cyan blue", "fluent-crm"), + "slug" => "pale-cyan-blue", + "color" => "#8ed1fc" + ], + [ + "name" => __("Vivid cyan blue", "fluent-crm"), + "slug" => "vivid-cyan-blue", + "color" => "#0693e3" + ], + [ + "name" => __("Vivid purple", "fluent-crm"), + "slug" => "vivid-purple", + "color" => "#9b51e0" + ] + ]; + + $font_sizes = [ + [ + 'name' => __('Small', 'fluent-crm'), + 'shortName' => 'S', + 'size' => 14, + 'slug' => 'small' + ], + [ + 'name' => __('Medium', 'fluent-crm'), + 'shortName' => 'M', + 'size' => 18, + 'slug' => 'medium' + ], + [ + 'name' => __('Large', 'fluent-crm'), + 'shortName' => 'L', + 'size' => 24, + 'slug' => 'large' + ], + [ + 'name' => __('Larger', 'fluent-crm'), + 'shortName' => 'XL', + 'size' => 32, + 'slug' => 'larger' + ] + ]; + + /** + * Filter the theme preferences for FluentCRM. + * + * This filter allows modification of the theme preferences, including colors and font sizes. + * + * @param array { + * The theme preferences. + * + * @type array $colors The color palette. + * @type array $font_sizes The font sizes. + * } + * @since 2.6.51 + * + */ + $pref = apply_filters('fluent_crm/theme_pref', [ + 'colors' => (array)$color_palette, + 'font_sizes' => (array)$font_sizes + ]); + } + + return $pref; + + } + + public static function funnelLabelColors() + { + $colors = [ + '#D6D8FF', + '#D4ECD6', + '#FEE8B5', + '#D7E8EF', + '#FFCACA', + '#F8D7C4', + '#D4D7DC', + '#FFD9E3' + ]; + + /** + * Filter the funnel label colors. + * + * This filter allows modification of the funnel label colors. + * + * @param array $colors An array of colors for the funnel labels. + * @since 2.9.30 + * + */ + return apply_filters('fluent_crm/funnel_label_color', $colors); + } + + public static function getColorSchemeValue($colorName) + { + static $colorMap = []; + if (isset($colorMap[$colorName])) { + return $colorMap[$colorName]; + } + $pref = self::getThemePrefScheme(); + $colors = $pref['colors']; + foreach ($colors as $color) { + $colorMap[$color['slug']] = $color['color']; + if ($color['slug'] == $colorName) { + return $color['color']; + } + } + + $color_palette = self::getThemeColorPalette(); + return self::getColorBySlug($color_palette, $colorName); + } + + public static function getColorBySlug($color_palette, $slug) + { + if (!$color_palette || !is_array($color_palette)) { + return null; + } + + foreach ($color_palette as $color) { + if (isset($color['slug']) && isset($color['color']) && $color['slug'] === $slug) { + return $color['color']; + } + } + + return null; + } + + public static function getThemeColorPalette() + { + $color_palette = current((array)get_theme_support('editor-color-palette')); + $theme_json_path = get_theme_file_path('theme.json'); + + if (file_exists($theme_json_path)) { + $theme_json = json_decode(file_get_contents($theme_json_path), true); + + if (isset($theme_json['settings']['color']['palette'])) { + $color_palette = $theme_json['settings']['color']['palette']; + } + } + if (!$color_palette) { + $color_palette = []; + } + + return (array)$color_palette; + } + + public static function getThemeFontSizes() + { + $font_sizes = current((array)get_theme_support('editor-font-sizes')); + $theme_json_path = get_theme_file_path('theme.json'); + + if (file_exists($theme_json_path)) { + $theme_json = json_decode(file_get_contents($theme_json_path), true); + + if (isset($theme_json['settings']['typography']['fontSizes'])) { + $font_sizes = $theme_json['settings']['typography']['fontSizes']; + } + } + + return $font_sizes; + } + + + public static function generateThemePrefCss() + { + static $color_css; + if ($color_css) { + return $color_css; + } + $pref = self::getThemePrefScheme(); + + $css = ''; + if (isset($pref['colors'])) { + foreach ($pref['colors'] as $color) { + if (isset($color['slug']) && isset($color['color'])) { + $slug = self::kebabCase($color['slug']); + $css .= '.has-' . $slug . '-color { color: ' . $color['color'] . ';} '; + $css .= '.has-' . $slug . '-background-color { background-color: ' . $color['color'] . '; background: ' . $color['color'] . '; } '; + $css .= 'a.has-' . $slug . '-background-color { border: 1px solid ' . $color['color'] . '; } '; + } + } + } + + if (isset($pref['font_sizes'])) { + foreach ($pref['font_sizes'] as $size) { + if (isset($size['slug']) && isset($size['size'])) { + $slug = self::kebabCase($size['slug']); + $css .= '.fc_email_body .has-' . $slug . '-font-size { font-size: ' . $size['size'] . 'px !important;} '; + } + } + } + + // Generate CSS for theme color palette + $themeColors = self::getThemeColorPalette(); + if (!empty($themeColors)) { + foreach ($themeColors as $themeColor) { + $color = $themeColor['color']; + + // Converts 'palette1' to 'palette-1' + $slug = self::normalizeColorSlug($themeColor['slug']); + + // Stores the original slug value without modification + $originalSlug = $themeColor['slug']; + + $css .= ".fc_email_body .has-{$originalSlug}-background-color { background-color: {$color};}"; + $css .= ".fc_email_body .has-{$originalSlug}-color { color: {$color};}"; + $css .= ".fc_email_body .has-{$slug}-background-color { background-color: {$color};}"; + $css .= ".fc_email_body .has-{$slug}-color { color: {$color};}"; + } + } + + // Generate CSS for theme font sizes + $themeFontSizes = self::getThemeFontSizes(); + if (!empty($themeFontSizes)) { + foreach ($themeFontSizes as $themeFontSize) { + $size = $themeFontSize['size']; + $slug = $themeFontSize['slug']; + $css .= ".fc_email_body .has-{$slug}-font-size { font-size: {$size} !important;}"; + } + } + + $color_css = $css; + return $color_css; + } + + private static function normalizeColorSlug($slug) + { + // Normalize the slug + $slug = strtolower($slug); + + // If the slug already follows "text-number" format, return it as is + if (preg_match('/^(.*?)-(\d+)$/', $slug, $matches)) { + return $slug; + } + + // Otherwise, fix cases like "theme-palette1" -> "theme-palette-1" + $parts = preg_split('/(\d+)/', $slug, -1, PREG_SPLIT_DELIM_CAPTURE); + + if (count($parts) > 1 && ctype_digit(trim($parts[count($parts) - 2]))) { + return implode('-', array_filter($parts)); + } + + return $slug; + } + + public static function kebabCase($string) + { + return implode('-', array_filter(preg_split('/(\d)/', strtolower(strval($string)), -1, PREG_SPLIT_DELIM_CAPTURE))); + } + + public static function getMailHeadersFromSettings($emailSettings = []) + { + if (empty($emailSettings) || Arr::get($emailSettings, 'is_custom') == 'no') { + $emailSettings = fluentcrmGetGlobalSettings('email_settings', []); + } + + if (empty($emailSettings)) { + return []; + } + + $headers = []; + if (Arr::get($emailSettings, 'from_name') && Arr::get($emailSettings, 'from_email')) { + $headers['From'] = $emailSettings['from_name'] . ' <' . $emailSettings['from_email'] . '>'; + } else if ($fromEmail = Arr::get($emailSettings, 'from_email')) { + $headers['From'] = $fromEmail; + } + + if (Arr::get($emailSettings, 'reply_to_name') && Arr::get($emailSettings, 'reply_to_email')) { + $headers['Reply-To'] = $emailSettings['reply_to_name'] . ' <' . $emailSettings['reply_to_email'] . '>'; + } else if ($replyTo = Arr::get($emailSettings, 'reply_to_email')) { + $headers['Reply-To'] = $replyTo; + } + + return $headers; + } + + public static function getMailHeader($existingHeader = []) + { + if (!empty($existingHeader['From'])) { + return $existingHeader; + } + + if (!empty($existingHeader['Reply-To'])) { + return $existingHeader; + } + + $headers = []; + static $globalHeaders; + if ($globalHeaders) { + return $globalHeaders; + } + + $globalEmailSettings = fluentcrmGetGlobalSettings('email_settings', []); + + $fromName = Arr::get($globalEmailSettings, 'from_name'); + $fromEmail = Arr::get($globalEmailSettings, 'from_email'); + + if ($fromName && $fromEmail) { + $headers['From'] = $fromName . ' <' . $fromEmail . '>'; + } else if ($fromEmail) { + $headers['From'] = $fromEmail; + } + + $replyName = Arr::get($globalEmailSettings, 'reply_to_name'); + $replyEmail = Arr::get($globalEmailSettings, 'reply_to_email'); + + if ($replyName && $replyEmail) { + $headers['Reply-To'] = $replyName . ' <' . $replyEmail . '>'; + } else if ($replyEmail) { + $headers['Reply-To'] = $replyEmail; + } + + $globalHeaders = $headers; + + return $globalHeaders; + } + + public static function recordCampaignRevenue($campaignId, $amount, $orderId, $currency = 'USD', $isRefunded = false) + { + $currency = strtolower($currency); + $existing = fluentcrm_get_campaign_meta($campaignId, '_campaign_revenue'); + $data = ['orderIds' => []]; + + if ($existing && isset($existing->value['orderIds']) && $existing->value['orderIds']) { + $data['orderIds'] = $existing->value['orderIds']; + $data[$currency] = $existing->value[$currency]; + } else { + $data[$currency] = 0; + } + if (!in_array($orderId, $data['orderIds'])) { + $data['orderIds'][] = $orderId; + } + + if ($isRefunded) { + if ($data[$currency] > $amount) { + $data[$currency] -= $amount; + $key = array_search($orderId, $data['orderIds']); + if ($key !== false) { + unset($data['orderIds'][$key]); + $data['orderIds'] = array_values($data['orderIds']); + } + } + } else { + if ($existing && isset($existing->value['orderIds']) && in_array($orderId, $existing->value['orderIds'])) { + $data[$currency] = $existing->value[$currency]; + } else { + $data[$currency] += $amount; + } + } + + return fluentcrm_update_campaign_meta($campaignId, '_campaign_revenue', $data); + } + + public static function getWPMapUserInfo($user) + { + if (is_numeric($user)) { + $user = get_user_by('ID', $user); + } + + if (!$user) { + return []; + } + + $subscriber = array_filter([ + 'user_id' => $user->ID, + 'first_name' => $user->first_name, + 'last_name' => $user->last_name, + 'email' => $user->user_email + ]); + + if ($address1 = get_user_meta($user->ID, 'billing_address_1', true)) { + $subscriber['address_line_1'] = $address1; + } + + if ($address2 = get_user_meta($user->ID, 'billing_address_2', true)) { + $subscriber['address_line_2'] = $address2; + } + + if ($city = get_user_meta($user->ID, 'billing_city', true)) { + $subscriber['city'] = $city; + } + + if ($postalCode = get_user_meta($user->ID, 'billing_postcode', true)) { + $subscriber['postal_code'] = $postalCode; + } + + if ($country = get_user_meta($user->ID, 'billing_country', true)) { + $subscriber['country'] = $country; + } + + if ($state = get_user_meta($user->ID, 'billing_state', true)) { + $subscriber['state'] = $state; + } + + if ($phone = get_user_meta($user->ID, 'billing_phone', true)) { + $subscriber['phone'] = $phone; + } + + /** + * Filter the subscriber data before it is processed. + * + * This filter allows you to modify the subscriber data before it is processed. + * + * @param array $subscriber The subscriber data. + * @param object $user The WordPress user object. + * @since 2.5.3 + * + */ + $subscriber = apply_filters('fluentcrm_user_map_data', $subscriber, $user); + + $fillables = (new Subscriber)->getFillable(); + + $subscriber = Arr::only($subscriber, $fillables); + + return array_filter($subscriber); + } + + public static function isUserSyncEnabled() + { + static $result = null; + if ($result === null) { + $settings = fluentcrm_get_option('user_syncing_settings', []); + $result = $settings && isset($settings['status']) && $settings['status'] == 'yes'; + } + + return $result; + } + + public static function isContactDeleteOnUserDeleteEnabled() + { + static $result = null; + if ($result === null) { + $settings = fluentcrm_get_option('user_syncing_settings', []); + $result = $settings && isset($settings['delete_contact_on_user_delete']) && $settings['delete_contact_on_user_delete'] == 'yes'; + } + + return $result; + } + + public static function deleteContacts($contactIds) + { + if (!$contactIds) { + return false; + } + if (!is_array($contactIds)) { + $contactIds = (array)$contactIds; + } + + do_action('fluentcrm_before_subscribers_deleted', $contactIds); + Subscriber::whereIn('id', $contactIds)->delete(); + do_action('fluentcrm_after_subscribers_deleted', $contactIds); + return true; + } + + public static function sendDoubleOptin($contactIds) + { + if (!$contactIds) { + return false; + } + if (!is_array($contactIds)) { + $contactIds = (array)$contactIds; + } + + $subscribers = Subscriber::whereIn('id', $contactIds)->where('status', 'pending')->get(); + foreach ($subscribers as $subscriber) { + $subscriber->sendDoubleOptinEmail(); + } + return true; + } + + public static function hasComplianceText($text) + { + /** + * Filters the compliance check string result. + * + * This filter allows you to modify the result of the compliance check string. + * + * @param mixed $result The result of the compliance check string. + * @param string $text The text being checked for compliance. + * @since 2.8.33 + * + */ + $result = apply_filters('fluent_crm/disable_check_compliance_string', false, $text); + + if ($result) { + return true; // directly return true if the filter returns true, would be better if we could return the $result of the filter + } + + return (bool)preg_match('/##crm\.manage_subscription_url##|##crm\.unsubscribe_url##|\{\{crm\.unsubscribe_html|\{\{crm\.manage_subscription_html|\{\{crm_global_email_footer\}\}/', $text); + } + + public static function maybeDisableEmojiOnEmail() + { + static $disabled; + if ($disabled) { + return; + } + /** + * Filter to disable emoji conversion to images in FluentCRM. + * + * This filter allows you to disable the conversion of emojis to images. + * By default, this filter is set to true, meaning the conversion is enabled. + * You can use this filter to return false if you want to disable the conversion. + * + * @param bool Whether to disable emoji conversion to images. Default true. + * @since 2.7.0 + * + */ + if (apply_filters('fluent_crm/disable_emoji_to_image', true)) { + remove_filter('wp_mail', 'wp_staticize_emoji_for_email'); + } + $disabled = true; + } + + public static function getPublicLists() + { + $emailSettings = self::getGlobalEmailSettings(); + $lists = []; + $preListType = Arr::get($emailSettings, 'pref_list_type', 'none'); + if ($preListType == 'filtered_only') { + $prefListItems = Arr::get($emailSettings, 'pref_list_items', []); + if ($prefListItems) { + $lists = Lists::whereIn('id', $prefListItems)->get(); + if ($lists->isEmpty()) { + return []; + } + } + } else if ($preListType == 'all') { + $lists = Lists::get(); + if ($lists->isEmpty()) { + return []; + } + } + + return $lists; + } + + public static function getAdvancedFilterOptions() + { + $groups = [ + 'subscriber' => [ + 'label' => __('Contact', 'fluent-crm'), + 'value' => 'subscriber', + 'children' => [ + [ + 'label' => __('General Properties', 'fluent-crm'), + 'value' => 'search', + ], + [ + 'label' => __('First Name', 'fluent-crm'), + 'value' => 'first_name', + 'type' => 'nullable_text', + ], + [ + 'label' => __('Last Name', 'fluent-crm'), + 'value' => 'last_name', + 'type' => 'nullable_text', + ], + [ + 'label' => __('Email', 'fluent-crm'), + 'value' => 'email', + ], + [ + 'label' => __('Address Line 1', 'fluent-crm'), + 'value' => 'address_line_1', + 'type' => 'nullable_text', + ], + [ + 'label' => __('Address Line 2', 'fluent-crm'), + 'value' => 'address_line_2', + 'type' => 'nullable_text', + ], + [ + 'label' => __('City', 'fluent-crm'), + 'value' => 'city', + 'type' => 'nullable_text', + ], + [ + 'label' => __('State', 'fluent-crm'), + 'value' => 'state', + 'type' => 'nullable_text', + ], + [ + 'label' => __('Postal Code', 'fluent-crm'), + 'value' => 'postal_code', + 'type' => 'nullable_text', + ], + [ + 'label' => __('Country', 'fluent-crm'), + 'value' => 'country', + 'type' => 'selections', + 'component' => 'options_selector', + 'option_key' => 'countries', + 'is_multiple' => true, + 'is_singular_value' => true + ], + [ + 'label' => __('Phone', 'fluent-crm'), + 'value' => 'phone', + 'type' => 'nullable_text', + ], + [ + 'label' => __('WP User ID', 'fluent-crm'), + 'value' => 'user_id', + 'type' => 'numeric', + ], + [ + 'label' => __('Name Prefix (Title)', 'fluent-crm'), + 'value' => 'prefix', + 'type' => 'selections', + 'options' => self::getContactPrefixes(true), + 'is_multiple' => true, + 'is_only_in' => true + ], + [ + 'label' => __('Source', 'fluent-crm'), + 'value' => 'source' + ], + [ + 'label' => __('Date of Birth', 'fluent-crm'), + 'value' => 'date_of_birth', + 'type' => 'dates', + ], + [ + 'label' => __('Last Activity', 'fluent-crm'), + 'value' => 'last_activity', + 'type' => 'dates', + ], + [ + 'label' => __('Created At', 'fluent-crm'), + 'value' => 'created_at', + 'type' => 'dates', + ], + + ], + ], + 'segment' => [ + 'label' => __('Contact Segment', 'fluent-crm'), + 'value' => 'segment', + 'children' => [ + [ + 'label' => __('Status', 'fluent-crm'), + 'value' => 'status', + 'type' => 'selections', + 'component' => 'options_selector', + 'option_key' => 'statuses', + 'is_multiple' => true, + 'is_singular_value' => true + ], + [ + 'label' => __('Type', 'fluent-crm'), + 'value' => 'contact_type', + 'type' => 'selections', + 'component' => 'options_selector', + 'option_key' => 'contact_types', + 'is_multiple' => false, + 'is_singular_value' => true + ], + [ + 'label' => __('Tags', 'fluent-crm'), + 'value' => 'tags', + 'type' => 'selections', + 'component' => 'options_selector', + 'option_key' => 'tags', + 'is_multiple' => true, + ], + [ + 'label' => __('Lists', 'fluent-crm'), + 'value' => 'lists', + 'type' => 'selections', + 'component' => 'options_selector', + 'option_key' => 'lists', + 'is_multiple' => true, + ], + [ + 'label' => __('WP User Role', 'fluent-crm'), + 'value' => 'user_role', + 'type' => 'selections', + 'component' => 'options_selector', + 'option_key' => 'user_roles_options', + 'is_multiple' => false, + 'is_singular_value' => true, + 'help' => __('Filter by user role, please make sure your users are synced with your FluentCRM contacts', 'fluent-crm') + ], + ], + ], + 'activities' => [ + 'label' => __('Contact Activities', 'fluent-crm'), + 'value' => 'activities', + 'children' => [ + [ + 'label' => __('Last Email Sent', 'fluent-crm'), + 'value' => 'email_sent', + 'type' => 'dates', + ], + [ + 'label' => __('Last Email Open', 'fluent-crm'), + 'value' => 'email_opened', + 'type' => 'dates', + 'help' => __('Please note that, some email clients send false-positive for email open pixel tracking so it may not 100% correct.', 'fluent-crm') + ], + [ + 'label' => __('Last Email Clicked', 'fluent-crm'), + 'value' => 'email_link_clicked', + 'type' => 'dates', + ], + [ + 'label' => __('Campaign Email -', 'fluent-crm'), + 'value' => 'campaign_email_activity', + 'type' => 'selections', + 'component' => 'ajax_selector', + 'option_key' => 'campaigns', + 'is_multiple' => false, + 'custom_operators' => [ + 'clicked' => 'link clicked', + 'not_clicked' => 'did not click', + 'open' => 'opened', + 'no_open' => 'did not open yet', + 'in' => 'in (email sent)', + 'not_in' => 'not in (regardless of status)' + ], + 'experimental_cache' => true, + 'help' => __('This will get only the contacts who got email in the selected campaign and then filter by email open/link clicked or not.
Please note that, some email clients send false-positive for email open pixel tracking so it may not 100% correct.', 'fluent-crm') + ], + [ + 'label' => __('Automation Activity -', 'fluent-crm'), + 'value' => 'automation_activity', + 'type' => 'selections', + 'component' => 'ajax_selector', + 'option_key' => 'funnels', + 'is_multiple' => false, + 'custom_operators' => [ + 'completed' => 'status completed', + 'active' => 'status active', + 'cancelled' => 'status cancelled', + 'waiting' => 'status waiting', + 'in' => 'in (regardless of status)', + 'not_in' => 'not in (regardless of status)' + ], + 'experimental_cache' => true, + 'help' => __('You can filter your contacts based on activity in a specific automation funnel.', 'fluent-crm') + ], + [ + 'label' => __('Email Sequence Activity -', 'fluent-crm'), + 'value' => 'email_sequence_activity', + 'type' => 'selections', + 'component' => 'ajax_selector', + 'option_key' => 'email_sequences', + 'is_multiple' => false, + 'custom_operators' => [ + 'completed' => 'status completed', + 'active' => 'status active', + 'cancelled' => 'status cancelled', + 'in' => 'in (regardless of status)', + 'not_in' => 'not in (regardless of status)' + ], + 'experimental_cache' => true, + 'help' => __('You can filter your contacts based on activity in a specific email sequences.', 'fluent-crm') + ] + ] + ] + ]; + + if (self::isCompanyEnabled()) { + $groups['segment']['children'][] = [ + 'label' => __('Company', 'fluent-crm'), + 'value' => 'companies', + 'type' => 'selections', + 'component' => 'ajax_selector', + 'option_key' => 'companies', + 'is_multiple' => true, + 'is_singular_value' => true, + 'experimental_cache' => true + ]; + $groups['segment']['children'][] = [ + 'label' => __('Company - Industry', 'fluent-crm'), + 'value' => 'company_industry', + 'type' => 'selections', + 'component' => 'ajax_selector', + 'option_key' => 'company_industries', + 'is_multiple' => true, + 'is_singular_value' => true, + 'experimental_cache' => true + ]; + $groups['segment']['children'][] = [ + 'label' => __('Company - Type', 'fluent-crm'), + 'value' => 'company_type', + 'type' => 'selections', + 'component' => 'ajax_selector', + 'option_key' => 'company_types', + 'is_multiple' => true, + 'is_singular_value' => true, + 'experimental_cache' => true + ]; + } + + if ($customFields = fluentcrm_get_custom_contact_fields()) { + // form data for custom fields in groups + $children = []; + foreach ($customFields as $field) { + $item = [ + 'label' => $field['label'], + 'value' => $field['slug'], + 'type' => $field['type'], + ]; + + if ($item['type'] == 'number') { + $item['type'] = 'numeric'; + } else if ($item['type'] == 'date') { + $item['type'] = 'dates'; + $item['date_type'] = 'date'; + $item['value_format'] = 'YYYY-MM-DD'; + } else if ($item['type'] == 'date_time') { + $item['type'] = 'dates'; + $item['has_time'] = 'yes'; + $item['date_type'] = 'datetime'; + $item['value_format'] = 'YYYY-MM-DD HH:mm:ss'; + } else if (isset($field['options'])) { + $item['type'] = 'selections'; + $options = $field['options']; + $formattedOptions = []; + foreach ($options as $option) { + $formattedOptions[$option] = $option; + } + $item['options'] = $formattedOptions; + $isMultiple = in_array($field['type'], ['checkbox', 'select-multi']); + $item['is_multiple'] = $isMultiple; + if ($isMultiple) { + $item['is_singular_value'] = true; + } + + } else { + $item['type'] = 'text'; + } + + $children[] = $item; + + } + + $groups['custom_fields'] = [ + 'label' => __('Custom Fields', 'fluent-crm'), + 'value' => 'custom_fields', + 'children' => $children + ]; + } + + if (!defined('FLUENTCAMPAIGN')) { + $disabled = true; + if (defined('WC_PLUGIN_FILE')) { + $groups['woo'] = [ + 'label' => __('WooCommerce', 'fluent-crm'), + 'value' => 'woo', + 'children' => [ + [ + 'value' => 'total_order_count', + 'label' => __('Total Order Count (Pro Required)', 'fluent-crm'), + 'type' => 'numeric', + 'disabled' => true + ], + [ + 'value' => 'total_order_value', + 'label' => __('Total Order value (Pro Required)', 'fluent-crm'), + 'type' => 'numeric', + 'disabled' => true + ], + [ + 'value' => 'last_order_date', + 'label' => __('Last Order Date (Pro Required)', 'fluent-crm'), + 'type' => 'dates', + 'disabled' => true + ], + [ + 'value' => 'first_order_date', + 'label' => __('First Order Date (Pro Required)', 'fluent-crm'), + 'type' => 'dates', + 'disabled' => true + ], + [ + 'value' => 'purchased_items', + 'label' => __('Purchased Products (Pro Required)', 'fluent-crm'), + 'type' => 'selections', + 'component' => 'product_selector', + 'is_multiple' => true, + 'disabled' => true + ], + [ + 'value' => 'commerce_exist', + 'label' => __('Is a customer? (Pro Required)', 'fluent-crm'), + 'type' => 'selections', + 'is_multiple' => false, + 'disable_values' => true, + 'value_description' => __('This filter will check if a contact has at least one shop order or not', 'fluent-crm'), + 'custom_operators' => [ + 'exist' => 'Yes', + 'not_exist' => 'No', + ], + 'disabled' => true + ] + ], + ]; + } + + if (self::isEdd3()) { + $groups['edd'] = [ + 'label' => __('EDD', 'fluent-crm'), + 'value' => 'edd', + 'children' => [ + [ + 'value' => 'total_order_count', + 'label' => __('Total Order Count (Pro Required)', 'fluent-crm'), + 'type' => 'numeric', + 'disabled' => true + ], + [ + 'value' => 'total_order_value', + 'label' => __('Total Order Value (Pro Required)', 'fluent-crm'), + 'type' => 'numeric', + 'disabled' => true + ], + [ + 'value' => 'last_order_date', + 'label' => __('Last Order Date (Pro Required)', 'fluent-crm'), + 'type' => 'dates', + 'disabled' => true + ], + [ + 'value' => 'first_order_date', + 'label' => __('First Order Date (Pro Required)', 'fluent-crm'), + 'type' => 'dates', + 'disabled' => true + ], + [ + 'value' => 'purchased_items', + 'label' => __('Purchased Products (Pro Required)', 'fluent-crm'), + 'type' => 'selections', + 'component' => 'product_selector', + 'is_multiple' => true, + 'disabled' => true + ], + ], + ]; + } + + if (class_exists('\Affiliate_WP')) { + $groups['aff_wp'] = [ + 'label' => 'AffiliateWP', + 'value' => 'aff_wp', + 'children' => [ + [ + 'value' => 'is_affiliate', + 'label' => __('Is Affiliate (Pro Required)', 'fluent-crm'), + 'type' => 'single_assert_option', + 'options' => [ + 'yes' => __('Yes', 'fluent-crm'), + 'no' => __('No', 'fluent-crm') + ], + 'disabled' => $disabled + ], + [ + 'value' => 'affiliate_id', + 'label' => __('Affiliate ID (Pro Required)', 'fluent-crm'), + 'type' => 'numeric', + 'disabled' => $disabled + ], + [ + 'value' => 'referrals', + 'label' => __('Total Referrals (Pro Required)', 'fluent-crm'), + 'type' => 'numeric', + 'disabled' => $disabled + ], + [ + 'value' => 'status', + 'label' => __('Status (Pro Required)', 'fluent-crm'), + 'type' => 'single_assert_option', + 'options' => [ + 'active' => __('Active', 'fluent-crm'), + 'inactive' => __('Inactive', 'fluent-crm'), + 'pending' => __('Pending', 'fluent-crm') + ], + 'disabled' => $disabled + ], + [ + 'value' => 'earnings', + 'label' => __('Earnings (Pro Required)', 'fluent-crm'), + 'type' => 'numeric', + 'disabled' => $disabled + ], + [ + 'value' => 'unpaid_earnings', + 'label' => __('Unpaid Earnings (Pro Required)', 'fluent-crm'), + 'type' => 'numeric', + 'disabled' => $disabled + ], + [ + 'value' => 'date_registered', + 'label' => __('Registration Date (Pro Required)', 'fluent-crm'), + 'type' => 'dates', + 'disabled' => $disabled + ], + [ + 'value' => 'last_payment_date', + 'label' => __('Last Payout Date (Pro Required)', 'fluent-crm'), + 'type' => 'dates', + 'disabled' => $disabled + ] + ] + ]; + } + + if (defined('LEARNDASH_VERSION')) { + $groups['learndash'] = [ + 'label' => __('LearnDash', 'fluent-crm'), + 'value' => 'learndash', + 'children' => [ + [ + 'value' => 'last_order_date', + 'label' => __('Last Enrollment Date (Pro Required)', 'fluent-crm'), + 'type' => 'dates', + 'disabled' => $disabled + ], + [ + 'value' => 'first_order_date', + 'label' => __('First Enrollment Date (Pro Required)', 'fluent-crm'), + 'type' => 'dates', + 'disabled' => $disabled + ], + [ + 'value' => 'purchased_items', + 'label' => __('Enrollment Courses (Pro Required)', 'fluent-crm'), + 'type' => 'selections', + 'component' => 'product_selector', + 'is_multiple' => true, + 'disabled' => $disabled + ], + [ + 'value' => 'purchased_groups', + 'label' => __('Enrollment Groups (Pro Required)', 'fluent-crm'), + 'type' => 'selections', + 'component' => 'product_selector', + 'is_multiple' => true, + 'extended_key' => 'groups', + 'disabled' => $disabled, + 'options' => [] + ], + [ + 'value' => 'purchased_categories', + 'label' => __('Enrollment Categories (Pro Required)', 'fluent-crm'), + 'type' => 'selections', + 'component' => 'tax_selector', + 'taxonomy' => 'ld_course_category', + 'is_multiple' => true, + 'disabled' => $disabled + ], + [ + 'value' => 'purchased_tags', + 'label' => __('Enrollment Tags (Pro Required)', 'fluent-crm'), + 'type' => 'selections', + 'component' => 'tax_selector', + 'taxonomy' => 'ld_course_tag', + 'is_multiple' => true, + 'disabled' => $disabled + ] + ] + ]; + } + + if (defined('LLMS_PLUGIN_FILE')) { + $groups['lifterlms'] = [ + 'label' => __('LifterLMS', 'fluent-crm'), + 'value' => 'lifterlms', + 'children' => [ + [ + 'value' => 'last_order_date', + 'label' => __('Last Enrollment Date (Pro Required)', 'fluent-crm'), + 'type' => 'dates', + 'disabled' => $disabled + ], + [ + 'value' => 'first_order_date', + 'label' => __('First Enrollment Date (Pro Required)', 'fluent-crm'), + 'type' => 'dates', + 'disabled' => $disabled + ], + [ + 'value' => 'purchased_items', + 'label' => __('Enrollment Courses (Pro Required)', 'fluent-crm'), + 'type' => 'selections', + 'component' => 'product_selector', + 'is_multiple' => true, + 'disabled' => $disabled + ], + [ + 'value' => 'purchased_groups', + 'label' => __('Enrollment Memberships (Pro Required)', 'fluent-crm'), + 'type' => 'selections', + 'component' => 'product_selector', + 'extended_key' => 'groups', + 'is_multiple' => true, + 'disabled' => $disabled + ], + [ + 'value' => 'purchased_categories', + 'label' => __('Enrollment Categories (Pro Required)', 'fluent-crm'), + 'type' => 'selections', + 'component' => 'tax_selector', + 'taxonomy' => 'course_cat', + 'is_multiple' => true, + 'disabled' => $disabled + ], + [ + 'value' => 'purchased_tags', + 'label' => __('Enrollment Tags (Pro Required)', 'fluent-crm'), + 'type' => 'selections', + 'component' => 'tax_selector', + 'taxonomy' => 'course_tag', + 'is_multiple' => true, + 'disabled' => $disabled + ] + ], + ]; + } + } + + /** + * Filter the advanced filter options for FluentCRM. + * + * This filter allows modification of the advanced filter options used in FluentCRM. + * + * @param array $groups The current filter options. + * @since 2.5.1 + * + */ + $groups = apply_filters('fluentcrm_advanced_filter_options', $groups); + + return array_values($groups); + } + + public static function getComplianceSettings() + { + $defaults = [ + 'anonymize_ip' => 'no', + 'delete_contact_on_user' => 'no', + 'personal_data_export' => 'yes', + 'one_click_unsubscribe' => 'no', + 'enable_gravatar' => 'yes', + 'gravatar_fallback' => 'yes', + 'email_click_tracking' => 'yes', // 'no'|'yes'|'anonymous' + 'email_open_tracking' => 'yes', // 'no'|'yes'|'anonymous' + ]; + + $settings = get_option('_fluentcrm_compliance_settings', []); + + return wp_parse_args($settings, $defaults); + } + + public static function getSiteUrl($path = '', $scheme = null) + { + return site_url($path, $scheme); + } + + public static function isExperimentalEnabled($module) + { + $settings = self::getExperimentalSettings(); + return Arr::get($settings, $module) === 'yes'; + } + + public static function getExperimentalSettings() + { + static $settings; + if ($settings) { + return $settings; + } + + $defaults = [ + 'campaign_archive' => 'no', + 'campaign_group_by_month' => 'no', + 'campaign_search' => '', + 'campaign_max_number' => 50, + 'campaign_ids' => [], + 'campaign_status' => 'archived', + 'frontend_portal' => 'no', + 'frontend_portal_slug' => 'fluentcrm', + 'frontend_portal_render_type' => 'standalone', + 'frontend_portal_page_id' => '', + 'classic_date_time' => 'no', + 'company_module' => 'no', + 'company_auto_logo' => 'no', + 'disable_visual_ai' => 'no', + 'multi_threading_emails' => 'no', + 'system_logs' => 'no', + 'event_tracking' => 'no', + 'abandoned_cart' => 'no', + 'activity_log' => 'no', + 'sms_module' => 'no', + ]; + + $settings = get_option('_fluentcrm_experimental_settings', []); + + if (!$settings || !is_array($settings)) { + $settings = $defaults; + return $settings; + } + + $settings = wp_parse_args($settings, $defaults); + + return $settings; + } + + public static function willMultiThreadEmail($minPendingLimit = 300) + { + if (!self::isExperimentalEnabled('multi_threading_emails')) { + return false; + } + + $rowcount = self::getUpcomingEmailCount(); + + return $rowcount >= $minPendingLimit; + } + + public static function getUpcomingEmailCount() + { + global $wpdb; + // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared + return $wpdb->get_var("SELECT count(*) as aggregate FROM `{$wpdb->prefix}fc_campaign_emails` WHERE `status` IN ('pending', 'scheduled') AND `scheduled_at` <= '" . current_time('mysql') . "'"); + } + + public static function sanitizeHtml($html) + { + if (!$html) { + return $html; + } + + // Return $html if it's just a plain text + if (!preg_match('/<[^>]*>/', $html)) { + return $html; + } + + $tags = wp_kses_allowed_html('post'); + $tags['style'] = [ + 'types' => [], + ]; + // iframe + $tags['iframe'] = [ + 'width' => [], + 'height' => [], + 'src' => [], + 'title' => [], + 'frameborder' => [], + 'allow' => [], + 'class' => [], + 'id' => [], + 'allowfullscreen' => [], + 'style' => [], + ]; + + //svg + if (empty($tags['svg'])) { + $svg_args = [ + 'svg' => [ + 'class' => true, + 'aria-hidden' => true, + 'aria-labelledby' => true, + 'role' => true, + 'xmlns' => true, + 'width' => true, + 'height' => true, + 'viewbox' => true, + ], + 'g' => ['fill' => true], + 'title' => ['title' => true], + 'path' => [ + 'd' => true, + 'fill' => true, + 'transform' => true, + ], + ]; + $tags = array_merge($tags, $svg_args); + } + + /** + * Filter the allowed HTML tags. + * + * This filter allows modification of the HTML tags that are allowed. + * + * @param array $tags An array of allowed HTML tags. + * @since 2.7.0 + * + */ + $tags = apply_filters('fluent_crm/allowed_html_tags', $tags); + + return wp_kses($html, $tags); + } + + public static function hasConditionOnString($string) + { + return (bool)preg_match('/conditional-group|fcrmConditionType|conditional-content|fc-cond-blocks|fc_vis_cond/', $string); + } + + public static function getEmailFooterContent($campaign = null) + { + if ($campaign && isset($campaign->settings)) { + + if (Arr::get($campaign->settings, 'is_transactional') == 'yes') { + return ''; + } + + $customFooter = Arr::get($campaign->settings, 'footer_settings.custom_footer'); + $emailFooter = Arr::get($campaign->settings, 'footer_settings.footer_content'); + + if ($customFooter === 'yes' && $emailFooter) { + return $emailFooter; + } + } + + return Arr::get(self::getGlobalEmailSettings(), 'email_footer', ''); + } + + public static function getFooterConfig($campaign = null) + { + + $defaults = [ + 'disable_footer' => 'no', + 'custom_footer' => 'no', + 'footer_content' => '', + 'font_size' => 13, + 'font_color' => '#202020', + 'background_color' => 'transparent', + 'footer_padding' => 20 + ]; + + if ($campaign && isset($campaign->settings)) { + if (Arr::get($campaign->settings, 'is_transactional') == 'yes') { + return []; + } + $footerSettings = Arr::get($campaign->settings, 'footer_settings', []); + $disableFooter = Arr::get($footerSettings, 'disable_footer'); + if ($disableFooter !== 'yes' && $disableFooter !== 'no') { + $disableFooter = Arr::get($campaign->settings, 'template_config.disable_footer'); + } + + if ($disableFooter == 'yes') { + $defaults['disable_footer'] = 'yes'; + $defaults['footer_content'] = ''; + return $defaults; + } + if (!empty($footerSettings['font_size'])) { + $defaults['font_size'] = $footerSettings['font_size']; + } + + if (!empty($footerSettings['font_color'])) { + $defaults['font_color'] = $footerSettings['font_color']; + } + + if (!empty($footerSettings['background_color'])) { + $defaults['background_color'] = $footerSettings['background_color']; + } + + $footerPadding = Arr::get($footerSettings, 'footer_padding'); + if ($footerPadding !== null && $footerPadding !== '') { + $defaults['footer_padding'] = min(80, max(0, intval($footerPadding))); + } else { + $defaults['footer_padding'] = 20; + } + + $customFooter = Arr::get($campaign->settings, 'footer_settings.custom_footer'); + $emailFooter = Arr::get($campaign->settings, 'footer_settings.footer_content'); + + if ($customFooter === 'yes' && $emailFooter) { + $defaults['footer_content'] = $emailFooter; + return $defaults; + } + } + + $globalContent = Arr::get(self::getGlobalEmailSettings(), 'email_footer', ''); + + $defaults['footer_content'] = $globalContent; + + return $defaults; + } + + public static function isCompanyEnabled() + { + return self::isExperimentalEnabled('company_module'); + } + + public static function companyCategories() + { + /** + * Filter the list of company categories. + * + * This filter allows modification of the company categories list. + * + * @param array An array of company categories. + * @since 2.8.0 + * + */ + return apply_filters('fluent_crm/company_categories', [ + 'Accounting', + 'Airlines/Aviation', + 'Alternative Dispute Resolution', + 'Alternative Medicine', + 'Animation', + 'Apparel & Fashion', + 'Architecture & Planning', + 'Arts and Crafts', + 'Automotive', + 'Aviation & Aerospace', + 'Banking', + 'Biotechnology', + 'Broadcast Media', + 'Building Materials', + 'Business Supplies and Equipment', + 'Capital Markets', + 'Chemicals', + 'Civic & Social Organization', + 'Civil Engineering', + 'Commercial Real Estate', + 'Computer & Network Security', + 'Computer Games', + 'Computer Hardware', + 'Computer Networking', + 'Computer Software', + 'Internet', + 'Construction', + 'Consumer Electronics', + 'Consumer Goods', + 'Consumer Services', + 'Cosmetics', + 'Dairy', + 'Defense & Space', + 'Design', + 'Education Management', + 'E-Learning', + 'Electrical/Electronic Manufacturing', + 'Entertainment', + 'Environmental Services', + 'Events Services', + 'Executive Office', + 'Facilities Services', + 'Farming', + 'Financial Services', + 'Fine Art', + 'Fishery', + 'Food & Beverages', + 'Food Production', + 'Fund-Raising', + 'Furniture', + 'Gambling & Casinos', + 'Glass, Ceramics & Concrete', + 'Government Administration', + 'Government Relations', + 'Graphic Design', + 'Health, Wellness and Fitness', + 'Higher Education', + 'Hospital & Health Care', + 'Hospitality', + 'Human Resources', + 'Import and Export', + 'Individual & Family Services', + 'Industrial Automation', + 'Information Services', + 'Information Technology and Services', + 'Insurance', + 'International Affairs', + 'International Trade and Development', + 'Investment Banking', + 'Investment Management', + 'Judiciary', + 'Law Enforcement', + 'Law Practice', + 'Legal Services', + 'Legislative Office', + 'Leisure, Travel & Tourism', + 'Libraries', + 'Logistics and Supply Chain', + 'Luxury Goods & Jewelry', + 'Machinery', + 'Management Consulting', + 'Maritime', + 'Market Research', + 'Marketing and Advertising', + 'Mechanical or Industrial Engineering', + 'Media Production', + 'Medical Devices', + 'Medical Practice', + 'Mental Health Care', + 'Military', + 'Mining & Metals', + 'Motion Pictures and Film', + 'Museums and Institutions', + 'Music', + 'Nanotechnology', + 'Newspapers', + 'Non-Profit Organization Management', + 'Oil & Energy', + 'Online Media', + 'Outsourcing/Offshoring', + 'Package/Freight Delivery', + 'Packaging and Containers', + 'Paper & Forest Products', + 'Performing Arts', + 'Pharmaceuticals', + 'Philanthropy', + 'Photography', + 'Plastics', + 'Political Organization', + 'Primary/Secondary Education', + 'Printing', + 'Professional Training & Coaching', + 'Program Development', + 'Public Policy', + 'Public Relations and Communications', + 'Public Safety', + 'Publishing', + 'Railroad Manufacture', + 'Ranching', + 'Real Estate', + 'Recreational Facilities and Services', + 'Religious Institutions', + 'Renewables & Environment', + 'Research', + 'Restaurants', + 'Retail', + 'Security and Investigations', + 'Semiconductors', + 'Shipbuilding', + 'Sporting Goods', + 'Sports', + 'Staffing and Recruiting', + 'Supermarkets', + 'Telecommunications', + 'Textiles', + 'Think Tanks', + 'Tobacco', + 'Translation and Localization', + 'Transportation/Trucking/Railroad', + 'Utilities', + 'Venture Capital & Private Equity', + 'Veterinary', + 'Warehousing', + 'Wholesale', + 'Wine and Spirits', + 'Wireless', + 'Writing and Editing' + ]); + } + + public static function companyTypes() + { + /** + * Filter the list of company types. + * + * This filter allows modification of the company types array. + * + * @param array An array of company types. + * @since 2.8.0 + * + */ + return apply_filters('fluent_crm/company_types', [ + 'Prospect', + 'Partner', + 'Reseller', + 'Vendor', + 'Other' + ]); + } + + public static function getCompanyProfileSections() + { + $sections = [ + 'overview' => [ + 'name' => 'view_company', + 'title' => __('Contacts', 'fluent-crm'), + 'handler' => 'route' + ], + 'activities' => [ + 'name' => 'company_activities', + 'title' => __('Notes & Activities', 'fluent-crm'), + 'handler' => 'route' + ], + ]; + + /** + * Filter the company profile sections. + * + * This filter allows modification of the company profile sections. + * + * @param array The array of company profile sections. + * @since 2.8.0 + * + */ + return apply_filters('fluent_crm/company_profile_sections', $sections); + } + + public static function maybeParseAndFilterWebhookData(Webhook $webhook, $postData, $key) + { + $data = Arr::get($webhook->value, $key, []); + if (!empty($postData[$key])) { + $postedData = Arr::get($postData, $key, []); + + if (is_string($postedData)) { + $postedData = explode(',', $postedData); + $postedData = map_deep($postedData, 'intval'); + } + + $newData = []; + foreach ($postedData as $item) { + if (is_numeric($item)) { + $newData[] = $item; + } + } + + if (!empty($newData)) { + $data = $newData; + } + + $data = array_filter($data); + } + + return $data; + } + + public static function getNoteSyncFields() + { + $fields = array( + 'type' => array( + 'type' => 'input-option', + 'label' => __('Type', 'fluent-crm'), + 'id' => 'fc_note_type', + 'name' => 'type', + 'options' => fluentcrm_activity_types() + ), + 'created_at' => array( + 'type' => 'input-date', + 'data_type' => 'datetime', + 'name' => 'created_at', + 'label' => __('Date Time', 'fluent-crm'), + 'id' => 'fc_note_title', + 'value_format' => 'YYYY-MM-DD HH:mm:ss', + 'help' => __('keep blank for current time', 'fluent-crm') + ), + 'title' => array( + 'type' => 'input-text', + 'name' => 'title', + 'label' => __('Title', 'fluent-crm'), + 'id' => 'fc_note_title', + 'placeholder' => __('Your Note Title', 'fluent-crm') + ), + 'description' => array( + 'type' => 'wp-editor', + 'name' => 'description', + 'label' => __('Description', 'fluent-crm'), + 'id' => 'fc_note_desc' + ), + ); + + /** + * Filter the contact note fields. + * + * This filter allows modification of the contact note fields. + * + * @param array $fields The contact note fields. + * @since 2.8.40 + * + */ + return apply_filters('fluent_crm/contact_note_fields', $fields); + } + + public static function debugLog($title, $description = '', $type = 'info') + { + static $isEnabled = null; + + if ($isEnabled === null) { + $isEnabled = (defined('FLUENT_CRM_DEBUG_LOG') && FLUENT_CRM_DEBUG_LOG) || self::isExperimentalEnabled('system_logs'); + } + + if (!$isEnabled) { + return null; + } + + if (!is_string($description)) { + $description = json_encode($description); + } + + return SystemLog::create([ + 'title' => sanitize_text_field($title), + 'description' => wp_kses_post($description) + ]); + } + + public static function getNextMinuteTaskTimeStamp() + { + $lastRunAt = fluentCrmGetOptionCache('_fcrm_last_scheduler'); + + if ($lastRunAt) { + $nextRun = $lastRunAt + 60; + } else { + $nextRun = as_next_scheduled_action('fluentcrm_scheduled_every_minute_tasks'); + } + + if ($nextRun === true || !$nextRun) { + $nextRun = time() + 60; + } + + return $nextRun; + } + + public static function isWooHposEnabled() + { + static $enabled = null; + if ($enabled !== null) { + return $enabled; + } + + $enabled = class_exists('\Automattic\WooCommerce\Utilities\OrderUtil') && \Automattic\WooCommerce\Utilities\OrderUtil::custom_orders_table_usage_is_enabled(); + + return $enabled; + } + + public static function searchWPUsers($searchQuery, $limit = 20) + { + $search = sanitize_text_field($searchQuery); + + // Search by user login, email, and nicename + $args = array( + 'role__not_in' => array('Administrator'), + 'search' => '*' . $search . '*', + 'number' => $limit + ); + + // Get users by login, email, and nicename + $user_query = new \WP_User_Query($args); + $users_by_login = $user_query->get_results(); + $users = array_unique($users_by_login, SORT_REGULAR); + + return $users; + } + + public static function latestListIdOfSubscriber($contactId) + { + $listId = SubscriberPivot::where('subscriber_id', $contactId) + ->where('object_type', 'FluentCrm\App\Models\Lists') + ->orderBy('created_at', 'DESC') + ->orderBy('id', 'DESC') + ->value('object_id'); + + return $listId; + } + + public static function createNewTags($tagsArray) + { + $tags = []; + foreach ($tagsArray as $tag) { + $tag = sanitize_text_field($tag); + //if that tag already exists then I need only it's id + $sameTag = Tag::where('title', $tag)->first(); + if ($sameTag) { + $tags[] = $sameTag->id; + continue; + } + + $tagModel = Helper::createTag($tag); + + if ($tagModel) { + $tags[] = $tagModel->id; + } + } + + return $tags; + } + + public static function createNewLists($listsArray) + { + $lists = []; + foreach ($listsArray as $list) { + $list = sanitize_text_field($list); + //if that list already exists then I need only it's id + $sameList = Lists::where('title', $list)->first(); + if ($sameList) { + $lists[] = $sameList->id; + continue; + } + + $listModel = Helper::createList($list); + + if ($listModel) { + $lists[] = $listModel->id; + } + } + + return $lists; + } + + public static function getNewAttachableLists($listsArray, $currentListIds, $ListsForAllContacts) + { + $listIds = []; + + foreach ($listsArray as $listTitle) { + $listTitle = sanitize_text_field($listTitle); + + $existinglist = Lists::where('title', $listTitle)->first(); + if ($existinglist) { + if (!in_array($existinglist->id, $currentListIds) && !in_array($existinglist->id, $ListsForAllContacts)) { + //if that existing list is not already in user's list and not in those lists that will be applied to all subscribers + $listIds[] = $existinglist->id; + } + } else { + $newList = Helper::createList($listTitle); + $listIds[] = $newList->id; + } + } + + return $listIds; + } + + public static function getNewAttachableTags($tagsArray, $currentTagIds, $TagsForAllContacts) + { + $tagIds = []; + + foreach ($tagsArray as $tagTitle) { + $tagTitle = sanitize_text_field($tagTitle); + + $existingTag = Tag::where('title', $tagTitle)->first(); + if ($existingTag) { + if (!in_array($existingTag->id, $currentTagIds) && !in_array($existingTag->id, $TagsForAllContacts)) { + //if that existing tag is not already in user's tag and not in those tags that will be applied to all subscribers + $tagIds[] = $existingTag->id; + } + } else { + $newList = Helper::createTag($tagTitle); + $tagIds[] = $newList->id; + } + } + + return $tagIds; + } + + private static function createList($listTitle) + { + $baseSlug = Str::slug($listTitle); + $slug = $baseSlug; + $counter = 1; + + // Ensure unique slug + while (Lists::where('slug', $slug)->exists()) { + $slug = "{$baseSlug}-{$counter}"; + $counter++; + } + + return Lists::create( + [ + 'title' => $listTitle, + 'slug' => $slug + ] + ); + } + + private static function createTag($tagTitle) + { + $baseSlug = Str::slug($tagTitle); + $slug = $baseSlug; + $counter = 1; + + // Ensure unique slug + while (Tag::where('slug', $slug)->exists()) { + $slug = "{$baseSlug}-{$counter}"; + $counter++; + } + + return Tag::create( + [ + 'title' => $tagTitle, + 'slug' => $slug + ] + ); + } + + /** + * Converts text into a URL-friendly slug, handling Latin and non-Latin scripts. + * + * @param string $text Input text to slugify + * @param string $fallback Fallback slug if input is empty or invalid + * @return string Sanitized slug + */ + public static function slugify($text, $fallback = '') + { + // Normalize input: cast to string and trim whitespace + $text = trim((string)$text); + + // Handle empty input + if (empty($text)) { + return sanitize_title($fallback ?: self::generateUniqueId(), $fallback); + } + + // Process as Latin-based text + $slug = remove_accents($text); // Convert accents (e.g., é → e) + $slug = strtolower($slug); // Convert to lowercase + $slug = preg_replace('/[^a-z0-9\-_]/', '-', $slug); // Replace non-alphanumeric with dashes + $slug = preg_replace('/[\-_]{2,}/', '-', $slug); // Collapse multiple dashes/underscores + $slug = trim($slug, '-_'); // Trim leading/trailing dashes/underscores + + // Check for empty result or non-Latin scripts + if (empty($slug) || preg_match('/[^\p{Latin}\p{N}\-_ ]/u', $text)) { + $slug = self::generateUniqueId(); + } + + // Final cleanup with WordPress sanitize_title + return sanitize_title($slug, $fallback); + } + + /** + * Generates a unique, hyphenated identifier (~11-12 characters). + * + * @return string Unique ID, e.g., '6f1a2-xyz12' + */ + public static function generateUniqueId() + { + return sprintf('%s-%s', substr(uniqid(), -5), wp_generate_password(5, false, false)); + } + + public static function getStatusText($text) + { + if (!$text) { + return ''; + } + + $mapStatus = [ + 'subscribed' => __('Subscribed', 'fluent-crm'), + 'pending' => __('Pending', 'fluent-crm'), + 'unsubscribed' => __('Unsubscribed', 'fluent-crm'), + 'transactional' => __('Transactional', 'fluent-crm'), + 'bounced' => __('Bounced', 'fluent-crm'), + 'complained' => __('Complained', 'fluent-crm'), + 'spammed' => __('Spammed', 'fluent-crm'), + 'checkout-draft' => __('Checkout Draft', 'fluent-crm'), + 'completed' => __('Completed', 'fluent-crm'), + 'complete' => __('Complete', 'fluent-crm'), + 'on-draft' => __('On Draft', 'fluent-crm'), + 'cancelled' => __('Cancelled', 'fluent-crm'), + 'processing' => __('Processing', 'fluent-crm'), + 'paid' => __('Paid', 'fluent-crm'), + 'success' => __('Success', 'fluent-crm') + ]; + + $mapStatus = apply_filters('fluent_crm/status_text', $mapStatus); + + return isset($mapStatus[$text]) ? $mapStatus[$text] : ucfirst($text); + } + + public static function wasProcessedByKeyId($emailLogId) + { + static $sentIds = []; + + if (isset($sentIds[$emailLogId])) { + return true; + } + + $sentIds[$emailLogId] = true; + + return false; + } + + public static function setInstantOption($optionKey, $value, $expire = 300) + { + if (wp_using_ext_object_cache()) { + return wp_cache_set($optionKey, $value, 'fc_instant_options', $expire); + } + + return update_option($optionKey, $value, false); + } + + public static function getInstantOption($optionKey) + { + if (wp_using_ext_object_cache()) { + return wp_cache_get($optionKey, 'fc_instant_options'); + } + + return get_option($optionKey); + } + + /** + * Acquire a cross-process mutex via a single atomic conditional UPDATE on + * wp_options, keyed off a stored timestamp. + * + * Why DB and not wp_cache_add(): wp_cache_add() is only atomic if the active + * object-cache drop-in implements it against the shared backend. Some do NOT + * — notably LiteSpeed Object Cache, whose add() only checks the per-process + * in-memory array and then unconditionally writes (no Memcached ADD / Redis + * SET NX). Under that drop-in every concurrent worker "wins" the lock, so the + * mailer ran multiple senders at once and overshot the provider rate limit. + * A single-row conditional UPDATE is atomic via the InnoDB row lock on every + * backend, mirroring the CAS used by GlobalRateLimiter. + * + * The UPDATE claims the lock only if it is free (empty value) or stale + * (stored timestamp older than $ttl), so a crashed holder self-recovers after + * the TTL. + * + * @param string $key wp_options option_name holding the lock timestamp. + * @param int $ttl Seconds before a held lock is treated as abandoned. + * @return bool True if this process acquired the lock. + */ + public static function acquireDbLock($key, $ttl) + { + global $wpdb; + $now = time(); + + // Ensure the row exists so the conditional UPDATE has a row to claim. + $wpdb->query($wpdb->prepare( + "INSERT IGNORE INTO {$wpdb->options} (option_name, option_value, autoload) VALUES (%s, %s, %s)", + $key, '', 'no' + )); + + // Atomic: claim only if free or expired. Empty string casts to 0, so the + // explicit '' check is what frees a cleanly released lock. + $affected = $wpdb->query($wpdb->prepare( + "UPDATE {$wpdb->options} SET option_value = %s WHERE option_name = %s AND (option_value = '' OR option_value < %d)", + (string)$now, $key, $now - $ttl + )); + + if ($affected > 0) { + wp_cache_delete($key, 'options'); + return true; + } + + return false; + } + + /** + * Heartbeat a held lock: push its timestamp to now so the TTL-based stale + * detection in acquireDbLock() cannot steal it mid-run. Caller must already + * hold the lock. + * + * @param string $key wp_options option_name holding the lock timestamp. + * @return void + */ + public static function refreshDbLock($key) + { + global $wpdb; + $wpdb->query($wpdb->prepare( + "UPDATE {$wpdb->options} SET option_value = %s WHERE option_name = %s", + (string)time(), $key + )); + wp_cache_delete($key, 'options'); + } + + /** + * Release a lock by clearing its timestamp so the next acquireDbLock() wins + * immediately instead of waiting out the TTL. Safe to call even if this + * process does not hold the lock (worst case frees the slot a tick early). + * + * @param string $key wp_options option_name holding the lock timestamp. + * @return void + */ + public static function releaseDbLock($key) + { + global $wpdb; + $wpdb->query($wpdb->prepare( + "UPDATE {$wpdb->options} SET option_value = '' WHERE option_name = %s", + $key + )); + wp_cache_delete($key, 'options'); + } + + /** + * Read the timestamp a lock was last (re)acquired with, straight from the + * wp_options row that acquireDbLock()/refreshDbLock() write to. + * + * Reads via raw SQL — NOT getInstantOption() — so it returns the live lock + * value regardless of external-object-cache mode. getInstantOption() reads + * the `fc_instant_options` cache group when an object cache is active, but + * the DB locks never write there, so it would always miss a held lock on + * those sites. Mirrors GlobalRateLimiter's direct-read approach. + * + * @param string $key wp_options option_name holding the lock timestamp. + * @return int Unix timestamp of the last (re)acquire, or 0 if free/absent. + */ + public static function getDbLockTimestamp($key) + { + global $wpdb; + + $value = $wpdb->get_var($wpdb->prepare( + "SELECT option_value FROM {$wpdb->options} WHERE option_name = %s", + $key + )); + + return (int) $value; + } + +} diff --git a/wp-content/plugins/fluent-crm/app/Services/Html/FormElementBuilder.php b/wp-content/plugins/fluent-crm/app/Services/Html/FormElementBuilder.php new file mode 100644 index 0000000..b834161 --- /dev/null +++ b/wp-content/plugins/fluent-crm/app/Services/Html/FormElementBuilder.php @@ -0,0 +1,512 @@ +renderField($field); + } + if ($print) { + echo $html; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped + } + + return $html; + } + + public function renderField($field) + { + $type = Arr::get($field, 'type'); + if ($type == 'container') { + return $this->renderContainer($field); + } + + if ($type == 'raw_html') { + return (string) Arr::get($field, 'html'); + } + + if ($type == 'hidden') { + $atts = $this->buildAttributes($field['atts']); + return ''; + } + + $inputHtml = ''; + + if ($type == 'input') { + $inputHtml = $this->renderInput($field); + } else if ($type == 'select') { + $inputHtml = $this->renderSelect($field); + } else if ($type == 'select-multi') { + $inputHtml = $this->renderMultiSelect($field); + } else if ($type == 'radio') { + $inputHtml = $this->renderRadio($field); + } else if ($type == 'checkboxes') { + $inputHtml = $this->renderCheckboxes($field); + } else if ($type == 'date') { + $inputHtml = $this->renderDate($field); + } else if ($type == 'textarea') { + $inputHtml = $this->renderTextarea($field); + } else if ($type == 'number') { + $inputHtml = $this->renderInput($field); + } else if ($type == 'custom_date') { + $inputHtml = $this->renderDatePicker($field); + } else if ($type == 'custom_date_time') { + $inputHtml = $this->renderDateTimePicker($field); + } else if ($type == 'date_dropdowns') { + $inputHtml = $this->renderDateDropdowns($field); + } + + return $this->renderLabel($field, $inputHtml); + } + + public function renderSelect($field) + { + $atts = $this->buildAttributes([ + 'id' => Arr::get($field, 'id'), + 'name' => Arr::get($field, 'name'), + ]); + + $html = ''; + + return $html; + } + + public function renderRadio($field) + { + $name = $field['name']; + $html = '
'; + + foreach ($field['options'] as $key => $label) { + $attributes = [ + 'type' => 'radio', + 'name' => $name, + 'value' => $key + ]; + + if ($key == $field['value']) { + $attributes['checked'] = true; + } + + $html .= ''; + } + + $html .= '
'; + + return $html; + } + + public function renderCheckboxes($field) + { + $name = $field['name']; + $options = $field['options']; + $selectedValues = (array) $field['value']; + + $html = '
'; + + $isAssoc = array_keys($options) !== range(0, count($options) - 1); + + foreach ($options as $optionKey => $list_option) { + $optionValue = $isAssoc ? $optionKey : $list_option; + + $attrbutes = [ + 'type' => 'checkbox', + 'name' => esc_attr($name) . '[]', + 'value' => esc_attr($optionValue) + ]; + + if (in_array($optionValue, $selectedValues)) { + $attrbutes['checked'] = true; + } + + $html .= ''; + } + + $html .= '
'; + return $html; + } + + public function renderContainer($field) + { + $innerFields = Arr::get($field, 'fields', []); + if (!$innerFields) { + return ''; + } + + $html = '
'; + $html .= $this->renderFields($innerFields); + $html .= '
'; + return $html; + } + + public function renderLabel($field, $innerHtml = '') + { + $containerClass = 'fc_field fc_field_' . $field['name'] . ' fc_field_' . $field['type']; + + if ($givenClass = Arr::get($field, 'container_class')) { + $containerClass .= ' ' . $givenClass; + } + + $html = '
'; + + if ($label = Arr::get($field, 'label')) { + if ($id = Arr::get($field, 'id')) { + // date_dropdowns: label must target first visible select, not the hidden input + $forId = (Arr::get($field, 'type') === 'date_dropdowns') ? $id . '_day' : $id; + $labelAtts = $this->buildAttributes([ + 'for' => $forId + ]); + } else { + $labelAtts = ''; + } + $required = ''; + if (Arr::get($field, 'required')) { + $required = ' *'; + } + + $html .= ''; + } + + return $html . $innerHtml . '
'; + } + + public function renderInput($field) + { + $atts = Arr::get($field, 'atts', []); + $atts['name'] = $field['name']; + + if (!empty($field['required'])) { + $atts['required'] = true; + } + + if (!empty($field['id'])) { + $atts['id'] = $field['id']; + } + + if (empty($atts['class'])) { + $atts['class'] = 'fc_input_control'; + } else { + $atts['class'] .= ' fc_input_control'; + } + + $atts['value'] = $field['value']; + + return 'buildAttributes($atts) . '/>'; + } + + public function renderDate($field) + { + // Legacy combodate field. combodate requires moment.js (not bundled on + // public pages), so custom_date (flatpickr) is preferred for new fields. + // Kept for backward compatibility. + wp_enqueue_script('combodate', FLUENTCRM_PLUGIN_URL . 'assets/libs/combodate/combodate.js', ['jquery'], '1.0.7', true); + $this->ensureFieldInitializer(); + + // Tag the input so the externalized initializer (form-fields.js) can find + // it, instead of emitting an inline + + + + +
+ + diff --git a/wp-content/plugins/fluent-crm/app/Views/admin/setup_wizard.php b/wp-content/plugins/fluent-crm/app/Views/admin/setup_wizard.php new file mode 100644 index 0000000..4bca1c6 --- /dev/null +++ b/wp-content/plugins/fluent-crm/app/Views/admin/setup_wizard.php @@ -0,0 +1,26 @@ + +> + + + + <?php esc_html_e('FluentCRM - Setup Wizard', 'fluent-crm'); ?> + + + + + +
+ + + + diff --git a/wp-content/plugins/fluent-crm/app/Views/emails/block_editor/Template.php b/wp-content/plugins/fluent-crm/app/Views/emails/block_editor/Template.php new file mode 100644 index 0000000..8d8d69f --- /dev/null +++ b/wp-content/plugins/fluent-crm/app/Views/emails/block_editor/Template.php @@ -0,0 +1,150 @@ + + +> + + + + + + + + + + + + +
+ class="fc_block_template" align="center" border="0" cellpadding="0" cellspacing="0" height="100%" width="100%" id="templateWrapper" + style="border-collapse: collapse;mso-table-lspace: 0pt;mso-table-rspace: 0pt;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;height: 100%;margin: 0;padding: 0;width: 100%;background-image: none;background-repeat: no-repeat;background-position: center;background-size: cover;color:inherit;"> + + + + + + + + + + + + + +
+ + + + + + +
+ + + + + + + + + +
+ + + + + + +
+ +
+
+ + +
+
+ + + + + + +
+ + + + + + + + + +
+ + +

+ +

+ +
+ + + +
+
+ + + + + +
+ + diff --git a/wp-content/plugins/fluent-crm/app/Views/emails/block_editor/block-styles.php b/wp-content/plugins/fluent-crm/app/Views/emails/block_editor/block-styles.php new file mode 100644 index 0000000..02baddb --- /dev/null +++ b/wp-content/plugins/fluent-crm/app/Views/emails/block_editor/block-styles.php @@ -0,0 +1,1272 @@ + 700, + * 'content_border_radius' => '0px', + * 'content_padding_top' => '20px', // matched + * 'content_padding_right' => '20px', // matched + * 'content_padding_bottom' => '20px', // matched + * 'content_padding_left' => '20px', // matched + * 'content_margin_top' => '20px', // new + * 'content_margin_bottom' => '20px', // new + * + * 'headings_font_family' => "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'", // matched + * 'content_font_family' => "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'", // matched + * + * 'body_bg_color' => '#FAFAFA', // matched + * 'content_bg_color' => '#FFFFFF', // matched + * + * 'headings_color' => '#202020', // matched + * 'text_color' => '#202020', // matched + * 'link_color' => '', // not used now + * 'link_color_hover' => '', // not used now + * + * 'paragraph_font_size' => '16px', // matched + * 'paragraph_line_height' => '1.5', // matched + * + * 'footer_text_color' => '#202020', // matched, + * + * 'design_template' => 'simple' + **/ + +$defaultDesignConfig = \FluentCrm\App\Services\BlockRender\BlockEditorHelper::getDefaultPrefConfig(); + +$config = wp_parse_args($config, $defaultDesignConfig); + +$width = $config['content_width'] ?? '800px'; +$contentBorderRadius = $config['content_border_radius'] ?? '0px'; + +$paddingTop = $config['content_padding_top'] ?? ''; +$paddingRight = $config['content_padding_right'] ?? ''; +$paddingBottom = $config['content_padding_bottom'] ?? ''; +$paddingLeft = $config['content_padding_left'] ?? ''; + +$marginTop = $config['content_margin_top'] ?? ''; +$marginBottom = $config['content_margin_bottom'] ?? ''; + +$hFont = $config['headings_font_family'] ?? ''; +$hColor = $config['headings_color'] ?? ''; +$mainColor = $config['text_color'] ?? ''; +$linkColor = $config['link_color'] ?? ''; +$bodBgyColor = $config['body_bg_color'] ?? ''; +$contentBg = $config['content_bg_color'] ?? ''; +$footerColor = $config['footer_text_color'] ?? ''; +$mainFont = $config['content_font_family'] ?? ''; + +$contentPadding = $config['content_padding'] ?? ''; + +$pSize = $config['paragraph_font_size'] ?? ''; +$pLHeight = $config['paragraph_line_height'] ?? ''; + +$sanitizeFontStack = function ($fontStack) { + $fontStack = wp_strip_all_tags((string)$fontStack); + return preg_replace('/[^a-zA-Z0-9,\s\-_"\'\.]/', '', $fontStack); +}; +$hFontCss = $sanitizeFontStack($hFont); +$mainFontCss = $sanitizeFontStack($mainFont); + +$alignLeft = 'left'; +$alignRight = 'right'; +if (fluentcrm_is_rtl()) { + $alignLeft = 'right'; + $alignRight = 'left'; +} + +?> + + + + + + + + + + + diff --git a/wp-content/plugins/fluent-crm/app/Views/emails/classic-style.php b/wp-content/plugins/fluent-crm/app/Views/emails/classic-style.php new file mode 100644 index 0000000..a6450c6 --- /dev/null +++ b/wp-content/plugins/fluent-crm/app/Views/emails/classic-style.php @@ -0,0 +1,382 @@ + + + + + diff --git a/wp-content/plugins/fluent-crm/app/Views/emails/classic/Template.php b/wp-content/plugins/fluent-crm/app/Views/emails/classic/Template.php new file mode 100644 index 0000000..18c3cff --- /dev/null +++ b/wp-content/plugins/fluent-crm/app/Views/emails/classic/Template.php @@ -0,0 +1,124 @@ + +> + + + + + + + + + + + + +
+ align="center" border="0" cellpadding="0" cellspacing="0" height="100%" width="100%" id="templateWrapper" style="border-collapse: collapse;mso-table-lspace: 0pt;mso-table-rspace: 0pt;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;height: 100%;margin: 0;padding: 0;width: 100%;background-image: none;background-repeat: no-repeat;background-position: center;background-size: cover;color:inherit;"> + + + + + + + + + + + + + +
+ + + + + +
+ + + + + + + + +
+ + + + + + +
+ +
+
+ + +
+ + + + + +
+ + + + + + + +
+ + +

+ +
+ + + +
+ + + + + +
+ + diff --git a/wp-content/plugins/fluent-crm/app/Views/emails/common-style.php b/wp-content/plugins/fluent-crm/app/Views/emails/common-style.php new file mode 100644 index 0000000..865b500 --- /dev/null +++ b/wp-content/plugins/fluent-crm/app/Views/emails/common-style.php @@ -0,0 +1,1017 @@ + + + + + + + + + + + + diff --git a/wp-content/plugins/fluent-crm/app/Views/emails/plain/Template.php b/wp-content/plugins/fluent-crm/app/Views/emails/plain/Template.php new file mode 100644 index 0000000..3008c0a --- /dev/null +++ b/wp-content/plugins/fluent-crm/app/Views/emails/plain/Template.php @@ -0,0 +1,126 @@ + +> + + + + + + + + + + + + +
+ align="center" border="0" cellpadding="0" cellspacing="0" height="100%" width="100%" id="templateWrapper" style="border-collapse: collapse;mso-table-lspace: 0pt;mso-table-rspace: 0pt;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;height: 100%;margin: 0;padding: 0;width: 100%;background-image: none;background-repeat: no-repeat;background-position: center;background-size: cover;color:inherit;"> + + + + + + + + + + + + + +
+ + + + + +
+ + + + + + + + +
+ + + + + + +
+
+ +
+
+
+ + +
+ + + + + +
+ + + + + + + +
+ + +

+ +
+ + + +
+ + + + + +
+ + diff --git a/wp-content/plugins/fluent-crm/app/Views/emails/raw_classic/Template.php b/wp-content/plugins/fluent-crm/app/Views/emails/raw_classic/Template.php new file mode 100644 index 0000000..fa40d37 --- /dev/null +++ b/wp-content/plugins/fluent-crm/app/Views/emails/raw_classic/Template.php @@ -0,0 +1,46 @@ + +> + + + + + + + + + + + + + +
+ align="center" border="0" cellpadding="0" cellspacing="0" height="100%" width="100%" id="bodyTable" style="border-collapse: collapse;mso-table-lspace: 0pt;mso-table-rspace: 0pt;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;height: 100%;margin: 0;padding: 0;width: 100%;background-image: none;background-repeat: no-repeat;background-position: center;background-size: cover;"> + + + + + + + + +
+ + +

+ +
+ + + + +
+ + diff --git a/wp-content/plugins/fluent-crm/app/Views/emails/simple/Template.php b/wp-content/plugins/fluent-crm/app/Views/emails/simple/Template.php new file mode 100644 index 0000000..5a95a6e --- /dev/null +++ b/wp-content/plugins/fluent-crm/app/Views/emails/simple/Template.php @@ -0,0 +1,123 @@ + +> + + + + + + + + + + + +
+ id="templateWrapper" align="center" border="0" cellpadding="0" cellspacing="0" height="100%" width="100%" id="bodyTable" style="border-collapse: collapse;mso-table-lspace: 0pt;mso-table-rspace: 0pt;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;height: 100%;margin: 0;padding: 0;width: 100%;background-image: none;background-repeat: no-repeat;background-position: center;background-size: cover;color:inherit;"> + + + + + + + + + + + + + +
+ + + + + +
+ + + + + + + + +
+ + + + + + +
+ +
+
+ + +
+ + + + + +
+ + + + + + + +
+ + +

+ +
+ + + +
+ + + + + +
+ + diff --git a/wp-content/plugins/fluent-crm/app/Views/emails/web_preview/Template.php b/wp-content/plugins/fluent-crm/app/Views/emails/web_preview/Template.php new file mode 100644 index 0000000..c6e7579 --- /dev/null +++ b/wp-content/plugins/fluent-crm/app/Views/emails/web_preview/Template.php @@ -0,0 +1,20 @@ + + align="center" border="0" cellpadding="0" cellspacing="0" height="100%" width="100%" id="bodyTable" style="border-collapse: collapse;mso-table-lspace: 0pt;mso-table-rspace: 0pt;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;height: 100%;margin: 0;padding: 0;width: 100%;background-image: none;background-repeat: no-repeat;background-position: center;background-size: cover;"> + + + + + + + + +
+ + +

+ +
+ + + + diff --git a/wp-content/plugins/fluent-crm/app/Views/external/confirmation.php b/wp-content/plugins/fluent-crm/app/Views/external/confirmation.php new file mode 100644 index 0000000..bb5e346 --- /dev/null +++ b/wp-content/plugins/fluent-crm/app/Views/external/confirmation.php @@ -0,0 +1,35 @@ + +> + + + + + <?php esc_html_e('Email Confirmation', 'fluent-crm') ?> + + + + +
+
+ +
+ <?php echo esc_attr($business['business_name']); ?> +
+ +

+ +
+
+ +
+
+ + + diff --git a/wp-content/plugins/fluent-crm/app/Views/external/manage_sub_request_email.php b/wp-content/plugins/fluent-crm/app/Views/external/manage_sub_request_email.php new file mode 100644 index 0000000..6ad1fb8 --- /dev/null +++ b/wp-content/plugins/fluent-crm/app/Views/external/manage_sub_request_email.php @@ -0,0 +1,52 @@ + + + + + + + FluentCRM + + + + + + + +
+ + + + + + + + + + + + +
+

+
+
+

+ Hello first_name); ?>,
+ We received a request for your email subscription preferences. +

+ +

+ To ensure that we fulfill your request, we're sending this confirmation email with an email management link. Please click the link below to manage your email subscription preferences: +

+ +

+ View Your Email Preferences +

+ +

+ If you did not make this request, it was probably submitted by someone else by mistake. You can ignore this email, and no changes will be made to your subscription preferences. +

+
+
+
+ + diff --git a/wp-content/plugins/fluent-crm/app/Views/external/manage_subscription.php b/wp-content/plugins/fluent-crm/app/Views/external/manage_subscription.php new file mode 100644 index 0000000..1d70912 --- /dev/null +++ b/wp-content/plugins/fluent-crm/app/Views/external/manage_subscription.php @@ -0,0 +1,36 @@ + +> + + + + + <?php esc_html_e('Update your preferences', 'fluent-crm') ?> + + + + +
+
+ +
+ <?php echo esc_attr($business['business_name']); ?> +
+ +

+ +
+
+

+ +
+
+ + + diff --git a/wp-content/plugins/fluent-crm/app/Views/external/manage_subscription_form.php b/wp-content/plugins/fluent-crm/app/Views/external/manage_subscription_form.php new file mode 100644 index 0000000..cc9aafa --- /dev/null +++ b/wp-content/plugins/fluent-crm/app/Views/external/manage_subscription_form.php @@ -0,0 +1,44 @@ +
+
+ + + + + +
+ + +
+
+ + +
+
+ + +
+ + +
+

+ + + +
+ +
+ +
+ +
+ +
+ +
+
+
diff --git a/wp-content/plugins/fluent-crm/app/Views/external/manage_subscription_request_form.php b/wp-content/plugins/fluent-crm/app/Views/external/manage_subscription_request_form.php new file mode 100644 index 0000000..7c0c216 --- /dev/null +++ b/wp-content/plugins/fluent-crm/app/Views/external/manage_subscription_request_form.php @@ -0,0 +1,56 @@ + + +> + + + + + <?php esc_html_e('Request Manage Subscription', 'fluent-crm') ?> + + + + +
+
+ +
+ <?php echo (isset($business['business_name'])) ? esc_html($business['business_name']) : ''; ?> +
+ +

+ +
+
+

+

+ +
+ +
+ + +
+
+ +
+
+ +
+
+ +
+ + + diff --git a/wp-content/plugins/fluent-crm/app/Views/external/pref_form.php b/wp-content/plugins/fluent-crm/app/Views/external/pref_form.php new file mode 100644 index 0000000..9c52df0 --- /dev/null +++ b/wp-content/plugins/fluent-crm/app/Views/external/pref_form.php @@ -0,0 +1,11 @@ +
+
+ + + renderFields($fields, true); ?> + + renderButton($submitBtn); ?> +
+ +
+
diff --git a/wp-content/plugins/fluent-crm/app/Views/external/unsubscribe.php b/wp-content/plugins/fluent-crm/app/Views/external/unsubscribe.php new file mode 100644 index 0000000..c530791 --- /dev/null +++ b/wp-content/plugins/fluent-crm/app/Views/external/unsubscribe.php @@ -0,0 +1,93 @@ + + +> + + + + + <?php esc_html_e('Unsubscribe', 'fluent-crm') ?> + + + + +
+ +
+ +
+ <?php echo (isset($business['business_name'])) ? esc_html($business['business_name']) : ''; ?> +
+ +

+ +
+
+ +
+ +
+ +
+

+

+ +
+ + + + + +
+ + +
+ +
+ +
+ $reason): ?> + + +
+
+ + + +
+ +
+
+
+
+
+ +
+ + + diff --git a/wp-content/plugins/fluent-crm/app/Views/external/unsubscribe_request_email.php b/wp-content/plugins/fluent-crm/app/Views/external/unsubscribe_request_email.php new file mode 100644 index 0000000..0009ce2 --- /dev/null +++ b/wp-content/plugins/fluent-crm/app/Views/external/unsubscribe_request_email.php @@ -0,0 +1,55 @@ + + + + + + + FluentCRM + + + + + + + + +
+ + + + + + + + + + + + +
+

+
+
+

+ Hello first_name); ?>,
+ We received a request to unsubscribe from our emails, and we're sorry to see you go. +

+ +

+ To ensure that we fulfill your request, we're sending this confirmation email with an unsubscribe link. Please click the link below to confirm your request and unsubscribe from our mailing list: +

+ +

+ Unsubscribe +

+ +

+ If you did not make this request, it was probably submitted by someone else by mistake. You can ignore this email, and no changes will be made to your subscription preferences. +

+
+
+
+ + diff --git a/wp-content/plugins/fluent-crm/app/Views/external/unsubscribe_request_form.php b/wp-content/plugins/fluent-crm/app/Views/external/unsubscribe_request_form.php new file mode 100644 index 0000000..fd6fcad --- /dev/null +++ b/wp-content/plugins/fluent-crm/app/Views/external/unsubscribe_request_form.php @@ -0,0 +1,56 @@ + + +> + + + + + <?php esc_html_e('Request Unsubscribe', 'fluent-crm') ?> + + + + +
+
+ +
+ <?php echo (isset($business['business_name'])) ? esc_html($business['business_name']) : ''; ?> +
+ +

+ +
+
+

+

+ +
+ +
+ + +
+
+ +
+
+ +
+
+ +
+ + + diff --git a/wp-content/plugins/fluent-crm/app/Views/external/view_on_browser.php b/wp-content/plugins/fluent-crm/app/Views/external/view_on_browser.php new file mode 100644 index 0000000..b2b1e37 --- /dev/null +++ b/wp-content/plugins/fluent-crm/app/Views/external/view_on_browser.php @@ -0,0 +1,112 @@ + $html] or a plain HTML string (error states). + * @var array $cssAssets + */ + +/* + * Pass the email payload and renderer through WordPress's script loader instead + * of emitting a hardcoded inline ` breakout from the inline script context. +$fcEmailData = wp_json_encode( + ['rendered' => $renderedBody], + JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT +); +wp_add_inline_script('fluentcrm_dompurify', 'window.fluentCrmEmail = ' . $fcEmailData . ';', 'before'); + +// Renderer: sanitize the email body and mount it inside a closed shadow root so +// its styles stay isolated from (and can't leak into) the surrounding page. +// Runs after DOMPurify loads (default 'after' position). +$fcViewOnBrowserScript = <<<'JS' +(function () { + var data = window.fluentCrmEmail; + if (!data || !data.rendered) { + return; + } + var host = document.getElementById('fluent_email_body'); + if (!host || typeof host.attachShadow !== 'function' || typeof window.DOMPurify === 'undefined') { + return; + } + var clean = window.DOMPurify.sanitize(data.rendered, { ADD_TAGS: ['style'], ADD_ATTR: ['target'] }); + var shadow = host.attachShadow({ mode: 'closed' }); + var wrapper = document.createElement('div'); + wrapper.innerHTML = clean; + shadow.appendChild(wrapper); +})(); +JS; + +wp_add_inline_script('fluentcrm_dompurify', $fcViewOnBrowserScript); +?> + +> + + + + + + <?php echo esc_attr($email_heading); ?> + + + +
+ + +
+ +
+

+
+
+ + + +
+
+
+ + +
+ + + + diff --git a/wp-content/plugins/fluent-crm/app/Vite.php b/wp-content/plugins/fluent-crm/app/Vite.php new file mode 100644 index 0000000..1bb8bf6 --- /dev/null +++ b/wp-content/plugins/fluent-crm/app/Vite.php @@ -0,0 +1,583 @@ +.js`, Vite reads the source path + 'admin/js/boot.js' => 'admin/boot.js', + 'admin/js/app.js' => 'admin/app.js', + 'admin/js/adminbar-search.js' => 'admin/adminbar-search.js', + 'admin/js/global_admin.js' => 'admin/global_admin.js', + 'admin/js/setup-wizard.js' => 'admin/setup-wizard.js', + 'admin/js/contact-navigations.js' => 'admin/experiments/contact-navigations.js', + 'admin/js/visual-editor.js' => 'admin/visual-editor/visual-editor.js', + 'public/public_pref.js' => 'public/public_pref.js', + + // CSS: Mix wrote `admin/css/.css`, Vite serves SCSS sources + 'admin/css/admin_rtl.css' => 'scss/admin_rtl.scss', + 'admin/css/app_global.css' => 'scss/app_global.scss', + 'admin/css/setup-wizard.css' => 'scss/setup-wizard.scss', + 'admin/css/app3.css' => 'styles/app3.scss', + 'public/public_pref.css' => 'scss/public_pref.scss', + ]; + + protected static ?Vite $instance = null; + public ?string $lastJsHandle = null; + private ?array $manifestData = null; + private array $enqueuedChunkCss = []; + + public function __construct() + { + $serverConfigPath = FLUENTCRM_PLUGIN_PATH . 'config' . DIRECTORY_SEPARATOR . 'vite.json'; + if (file_exists($serverConfigPath)) { + $serverConfig = json_decode(file_get_contents($serverConfigPath)); + $this->viteHost = $serverConfig->host ?: $this->viteHost; + $this->viteHostProtocol = $serverConfig->protocol ?: $this->viteHostProtocol; + $this->vitePort = $serverConfig->port ?: $this->vitePort; + } + + // Add global filter to convert Vite scripts to modules + add_filter('script_loader_tag', [$this, 'maybeConvertToModule'], 999, 3); + } + + /** + * Convert scripts from Vite dev server or built assets to ES modules + */ + public function maybeConvertToModule($tag, $handle, $src): string + { + // Fast rejection for scripts that obviously aren't ours. The filter + // is registered globally at priority 999 so it fires for EVERY + // script on EVERY admin page (including pages where FluentCRM + // isn't active). On a typical admin page that's ~30 unrelated + // scripts. Short-circuit here so the detailed checks below only + // run for our own assets. + $isPotentiallyOurs = + strpos($src, FLUENTCRM_PLUGIN_URL) !== false || + strpos($src, 'localhost:' . $this->vitePort) !== false || + strpos($src, '@vite/client') !== false || + in_array($handle, $this->moduleScripts, true); + + if (!$isPotentiallyOurs) { + return $tag; + } + + // Check if this script is from Vite dev server, Vite built assets, or is a module script + $isViteScript = false; + $fluentCrmAssetBase = FLUENTCRM_PLUGIN_URL . 'assets/'; + + // Check if from dev server + if (strpos($src, 'localhost:' . $this->vitePort) !== false || strpos($src, '@vite/client') !== false) { + $isViteScript = true; + } + + // Check if explicitly marked as module script + if (in_array($handle, $this->moduleScripts)) { + $isViteScript = true; + } + + // Check if from FluentCRM Vite built assets in production (or dev mode without server). + // Only rewrite this plugin's own built files, never third-party plugin assets. + if (!$this->shouldServeViaDevServer()) { + $assetPatterns = [ + $fluentCrmAssetBase . 'admin/', + $fluentCrmAssetBase . 'public/', + ]; + foreach ($assetPatterns as $pattern) { + if (strpos($src, $pattern) !== false && strpos($src, '.js') !== false) { + // Exclude third-party libs that are not ES modules + $excludePatterns = [ + '/libs/', + '/vendor/', + 'purify.min.js', + ]; + $isExcluded = false; + foreach ($excludePatterns as $exclude) { + if (strpos($src, $exclude) !== false) { + $isExcluded = true; + break; + } + } + if (!$isExcluded) { + $isViteScript = true; + break; + } + } + } + } + + if ($isViteScript) { + // Already has type="module" + if (strpos($tag, 'type="module"') !== false || strpos($tag, "type='module'") !== false) { + return $tag; + } + + // Convert to module + $tag = preg_replace('/usingDevMode() || !static::$instance->isViteServerRunning()) { + (static::$instance)->loadViteManifest(); + } + } + + return static::$instance; + } + + /** + * @throws Exception + */ + private function loadViteManifest() + { + if (!empty($this->manifestData)) { + return; + } + + $manifestPath = FLUENTCRM_PLUGIN_PATH . 'config' . DIRECTORY_SEPARATOR . 'vite_config.php'; + + if (file_exists($manifestPath)) { + $this->manifestData = require $manifestPath; + } + + if (empty($this->manifestData)) { + $this->manifestData = []; + // In production, you might want to uncomment this to enforce manifest requirement + // throw new Exception('Vite Manifest Not Found. Run: npm run dev or npm run build'); + } + } + + public static function enqueueScript($handle, $src, $dependency = [], $version = null, $inFooter = false): Vite + { + return static::getInstance()->enqueue_script( + $handle, + $src, + $dependency, + $version, + $inFooter + ); + } + + private function enqueue_script($handle, $src, $dependency = [], $version = null, $inFooter = false): Vite + { + if (in_array($handle, $this->moduleScripts)) { + if ($this->usingDevMode()) { + $callerReference = (debug_backtrace(2)[1]); + $fileName = explode('plugins', $callerReference['file']); + $line = $callerReference['line']; + // Uncomment to debug duplicate handles + // throw new \Exception("Handle already used: $handle at File: {$fileName[1]} Line: $line"); + } + } + + $this->moduleScripts[] = $handle; + $this->lastJsHandle = $handle; + + // No per-handle script_loader_tag filter needed — the constructor + // registers maybeConvertToModule globally at priority 999 and it + // already detects handles in $moduleScripts as Vite scripts. + + if ($this->shouldServeViaDevServer()) { + $srcPath = $this->getVitePath() . $src; + } else { + $assetFile = $this->getFileFromManifest($src); + $srcPath = $this->getProductionFilePath($assetFile); + } + + if (empty($srcPath)) { + return $this; + } + + $version = empty($version) ? FLUENTCRM_PLUGIN_VERSION : $version; + + wp_enqueue_script( + $handle, + $srcPath, + $dependency, + $version, + $inFooter + ); + + return $this; + } + + private function getFileFromManifest($src) + { + if (isset($this->manifestData[$this->resourceDirectory . $src])) { + return $this->manifestData[$this->resourceDirectory . $src]; + } + + return ''; + } + + private function getProductionFilePath($file): string + { + if (!isset($file['file'])) { + return ''; + } + + $assetPath = static::getAssetPath(); + $this->ensureChunkCssIsLoaded($file); + + return ($assetPath . $file['file']); + } + + // Per-chunk CSS auto-enqueue. The Vite build's mergeCssChunksPlugin + // collapses most chunk CSS into admin/css/style.css (which AdminMenu.php + // enqueues explicitly), and the moveManifestPlugin then strips the + // merged paths from the manifest. What remains in manifest `css` arrays + // is only files that survived to disk (e.g. legacy SCSS entry outputs + // like admin/css/admin_rtl.css) — those we enqueue here. + private function ensureChunkCssIsLoaded($file) + { + $assetPath = static::getAssetPath(); + $cssFiles = $this->collectChunkCssFiles($file); + + foreach ($cssFiles as $cssPath) { + if (isset($this->enqueuedChunkCss[$cssPath])) { + continue; + } + + wp_enqueue_style( + 'fluentcrm_vite_css_' . md5($cssPath), + $assetPath . $cssPath, + [], + FLUENTCRM_PLUGIN_VERSION + ); + + $this->enqueuedChunkCss[$cssPath] = true; + } + } + + private function collectChunkCssFiles($file, &$visited = []): array + { + $cssFiles = []; + + if (!is_array($file)) { + return $cssFiles; + } + + $fileId = isset($file['file']) ? $file['file'] : md5(wp_json_encode($file)); + if (isset($visited[$fileId])) { + return $cssFiles; + } + $visited[$fileId] = true; + + if (isset($file['css']) && is_array($file['css'])) { + foreach ($file['css'] as $path) { + if (is_string($path) && $path !== '') { + $cssFiles[] = $path; + } + } + } + + if (isset($file['imports']) && is_array($file['imports'])) { + foreach ($file['imports'] as $importKey) { + if (!isset($this->manifestData[$importKey]) || !is_array($this->manifestData[$importKey])) { + continue; + } + + $cssFiles = array_merge($cssFiles, $this->collectChunkCssFiles($this->manifestData[$importKey], $visited)); + } + } + + return array_values(array_unique($cssFiles)); + } + + public function with($params) + { + if (!is_array($params) || !Arr::isAssoc($params) || empty($this->lastJsHandle)) { + $this->lastJsHandle = null; + return; + } + + foreach ($params as $key => $val) { + wp_localize_script($this->lastJsHandle, $key, $val); + } + $this->lastJsHandle = null; + } + + public static function enqueueStyle($handle, $src, $dependency = [], $version = null, $media = 'all') + { + static::getInstance()->enqueue_style( + $handle, + $src, + $dependency, + $version, + $media + ); + } + + private function enqueue_style($handle, $src, $dependency = [], $version = null, $media = 'all') + { + if ($this->shouldServeViaDevServer()) { + $srcPath = $this->getVitePath() . $src; + } else { + $assetFile = $this->getFileFromManifest($src); + $srcPath = $this->getProductionFilePath($assetFile); + } + + if (empty($srcPath)) { + return; + } + + $version = empty($version) ? FLUENTCRM_PLUGIN_VERSION : $version; + + wp_enqueue_style( + $handle, + $srcPath, + $dependency, + $version, + $media + ); + } + + public static function enqueueStaticScript($handle, $src, $dependency = [], $version = null, $inFooter = false): Vite + { + $version = empty($version) ? FLUENTCRM_PLUGIN_VERSION : $version; + + return static::getInstance()->enqueue_static_script( + $handle, + $src, + $dependency, + $version, + $inFooter + ); + } + + private function enqueue_static_script($handle, $src, $dependency = [], $version = null, $inFooter = false): Vite + { + $version = empty($version) ? FLUENTCRM_PLUGIN_VERSION : $version; + + wp_enqueue_script( + $handle, + $this->getStaticEnqueuePath($src), + $dependency, + $version, + $inFooter + ); + + return $this; + } + + private function getStaticEnqueuePath($path): string + { + if ($this->shouldServeViaDevServer()) { + return $this->getVitePath() . $path; + } + + return $this->get_asset_url($path); + } + + public static function enqueueStaticStyle($handle, $src, $dependency = [], $version = null, $media = 'all') + { + $version = empty($version) ? FLUENTCRM_PLUGIN_VERSION : $version; + + static::getInstance()->enqueue_static_style( + $handle, $src, $dependency, $version, $media + ); + } + + private function enqueue_static_style($handle, $src, $dependency = [], $version = null, $media = 'all') + { + $version = empty($version) ? FLUENTCRM_PLUGIN_VERSION : $version; + + wp_enqueue_style( + $handle, + $this->getStaticEnqueuePath($src), + $dependency, + $version, + $media + ); + } + + public static function underDevelopment(): bool + { + return static::getInstance()->usingDevMode(); + } + + public function usingDevMode(): bool + { + $app = FluentCrm(); + return $app['config']->get('app.env') === 'dev'; + } + + /** + * True only when env=dev AND the Vite dev server is reachable. + * Use this — not usingDevMode() — to decide whether to proxy URLs to + * the Vite server. When the server is down we fall back to built assets. + */ + private function shouldServeViaDevServer(): bool + { + return $this->usingDevMode() && $this->isViteServerRunning(); + } + + /** + * Check if Vite dev server is actually running + */ + private function isViteServerRunning(): bool + { + static $isRunning = null; + + if ($isRunning !== null) { + return $isRunning; + } + + // Check if Vite client endpoint is accessible + $viteUrl = $this->viteHostProtocol . $this->viteHost . ':' . $this->vitePort . '/@vite/client'; + + $response = wp_remote_get($viteUrl, [ + 'timeout' => 1, + 'sslverify' => false + ]); + + $isRunning = !is_wp_error($response) && wp_remote_retrieve_response_code($response) === 200; + + return $isRunning; + } + + public function getVitePath(): string + { + $protocol = rtrim($this->viteHostProtocol, ':/'); + $host = rtrim($this->viteHost, '/'); + $port = $this->vitePort; + $resource = ltrim($this->resourceDirectory, '/'); + + return sprintf('%s://%s:%s/%s', $protocol, $host, $port, $resource); + } + + public static function getEnqueuePath($path = ''): string + { + $vite = static::getInstance(); + + // Normalize the path - remove leading slash + $path = ltrim($path, '/'); + + if (!$vite->usingDevMode()) { + // In production, map the path to source path first for manifest lookup + $sourcePath = $vite->getSourcePathForManifest($path); + $assetFile = $vite->getFileFromManifest($sourcePath); + if ($assetFile) { + $srcPath = $vite->getProductionFilePath($assetFile); + } else { + // Fallback to direct asset path + $srcPath = static::getAssetPath() . $path; + } + } else { + // Check if Vite dev server is actually running + if ($vite->isViteServerRunning()) { + // Use Vite dev server URL (source path, served via HMR) + $srcPath = $vite->mapToSourcePath($path); + } else { + // Vite server not running — resolve via manifest just like production. + // The plugin ships with env=dev, so without this the fallback would use + // the old Mix path pattern (assets/admin/js/app.js) which doesn't + // exist in the Vite output (assets/admin/app.js). 404 = blank app. + $sourcePath = $vite->getSourcePathForManifest($path); + $assetFile = $vite->getFileFromManifest($sourcePath); + if ($assetFile) { + $srcPath = $vite->getProductionFilePath($assetFile); + } else { + // Last resort: direct path (for assets not in manifest) + $srcPath = static::getAssetPath() . $path; + } + } + } + + return $srcPath; + } + + /** + * Resolve a Mix-style enqueue path to a dev-server URL. + * Used when the Vite dev server is running. + */ + private function mapToSourcePath($path): string + { + if (isset(self::MIX_TO_VITE_PATH_MAP[$path])) { + return $this->getVitePath() . self::MIX_TO_VITE_PATH_MAP[$path]; + } + + // If path already starts with resources/, use it as-is + if (strpos($path, 'resources/') === 0) { + return $this->getVitePath() . substr($path, 10); // Remove 'resources/' prefix + } + + // Fallback: use the path as-is (getVitePath includes resources/) + return $this->getVitePath() . $path; + } + + /** + * Resolve a Mix-style enqueue path to the manifest source key. + * Used in production for manifest lookups. + */ + private function getSourcePathForManifest($path): string + { + return self::MIX_TO_VITE_PATH_MAP[$path] ?? $path; + } + + public static function getAssetUrl($path = ''): string + { + return esc_url(static::getInstance()->get_asset_url($path) ?? ''); + } + + private function get_asset_url($path = ''): string + { + if ($this->shouldServeViaDevServer()) { + return $this->getVitePath() . $path; + } + + return FLUENTCRM_PLUGIN_URL . 'assets/' . ltrim($path, '/'); + } + + static function getAssetPath(): string + { + return FLUENTCRM_PLUGIN_URL . 'assets/'; + } + + /** + * Inject Vite client for HMR in development mode + */ + public static function injectViteClient() + { + $vite = static::getInstance(); + + if ($vite->shouldServeViaDevServer()) { + $protocol = rtrim($vite->viteHostProtocol, ':/'); + $host = rtrim($vite->viteHost, '/'); + $port = $vite->vitePort; + + // Vite client URL should NOT include /resources/ + $viteClientUrl = sprintf('%s://%s:%s/@vite/client', $protocol, $host, $port); + echo '' . "\n"; + } + } + +} diff --git a/wp-content/plugins/fluent-crm/app/index.php b/wp-content/plugins/fluent-crm/app/index.php new file mode 100644 index 0000000..6220032 --- /dev/null +++ b/wp-content/plugins/fluent-crm/app/index.php @@ -0,0 +1,2 @@ +e.toString().toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g,"").replace(/\s+/g,"-").replace(/\\-\\-+/g,"-").replace(/^-+/,"").replace(/-+$/,"")}},[["render",function(s,r,p,u,f,_){const g=t,$=o("error"),v=e,w=i;return l(),a(w,{"label-position":"top","label-width":"100px",class:"fcrm_adder_form"},{default:d(()=>[n("div",b,[c(v,{label:s.$t("Title"),class:"fcrm_adder_form_item is-required"},{default:d(()=>[c(g,{modelValue:p.item.title,"onUpdate:modelValue":r[0]||(r[0]=e=>p.item.title=e),class:"fcrm_adder_input"},null,8,["modelValue"]),c($,{error:p.errors.get("title")},null,8,["error"])],void 0,!0),_:1},8,["label"]),c(v,{label:s.$t("Slug"),class:"fcrm_adder_form_item"},{default:d(()=>[c(g,{modelValue:p.item.slug,"onUpdate:modelValue":r[1]||(r[1]=e=>p.item.slug=e),class:"fcrm_adder_input"},null,8,["modelValue"]),c($,{error:p.errors.get("slug")},null,8,["error"])],void 0,!0),_:1},8,["label"])]),c(v,{label:s.$t("For_Internal_S_"),class:"fcrm_adder_form_item_full"},{label:d(()=>[n("span",null,[m(h(s.$t("Internal Subtitle"))+" ",1),n("span",y,"("+h(s.$t("Optional"))+")",1)])]),default:d(()=>[c(g,{modelValue:p.item.description,"onUpdate:modelValue":r[2]||(r[2]=e=>p.item.description=e),placeholder:s.$t("Internal Subtitle"),class:"fcrm_adder_input"},null,8,["modelValue","placeholder"])],void 0,!0),_:1},8,["label"])],void 0),_:1})}]])},emits:["close","fetch","renew_options"],props:{visible:Boolean,type:{type:String,required:!0},api:{type:Object,required:!0}},data(){return{direction:"rtl",showing:this.visible,item:this.fresh(),errors:new g,title:this.$t("Add New")+" "+this.ucFirst(this.trans(this.type))}},watch:{visible(e){this.showing=e},showing(e){e||this.$emit("close")}},methods:{fresh:()=>({title:null,slug:null,description:""}),reset(){this.errors.clear(),this.item=this.fresh()},hide(){this.reset(),this.showing=!1},save(){this.errors.clear();const e={...this.item};let t=!1;t=this.item.id?this.$put(this.api.store+"/"+this.item.id,e):this.$post(this.api.store,e),t.then(e=>{this.$notify.success({title:this.$t("Great!"),message:e.message,offset:19}),this.$emit("fetch",this.item),this.$bus.emit("renew_options",this.type),this.hide()}).catch(e=>{this.errors.record(e)})},listeners(){const e="edit-"+this.type;this.$bus.on(e,e=>{this.item={id:e.id,slug:e.slug,title:e.title,description:e.description},this.title=this.$t("Edit")+" "+this.ucFirst(this.type)})},trans(e){return this.$t(e)},ucFirst:e=>e?e.charAt(0).toUpperCase()+e.slice(1):""},mounted(){window.fcAdmin&&window.fcAdmin.is_rtl&&(this.direction="ltr"),this.listeners()},beforeUnmount(){const e="edit-"+this.type;this.$bus.off(e)}},v={class:"fcrm_adder_drawer_body"},w={class:"dialog-footer justify-end"};const V={class:""},C={class:"icon"};const F=f({name:"ActionMenu",components:{Icons:_,Adder:f($,[["render",function(e,t,i,p,u,f){const _=o("forma"),g=s,b=r;return l(),a(b,{direction:u.direction,"with-header":!0,size:"600px","append-to-body":!0,modelValue:u.showing,"onUpdate:modelValue":t[2]||(t[2]=e=>u.showing=e),class:"fcrm_lists_adder_drawer",title:u.title,onClose:t[3]||(t[3]=e=>f.hide())},{footer:d(()=>[n("div",w,[c(g,{onClick:t[0]||(t[0]=e=>f.hide())},{default:d(()=>[m(h(e.$t("Cancel")),1)],void 0,!0),_:1}),c(g,{type:"primary",onClick:t[1]||(t[1]=e=>f.save())},{default:d(()=>[m(h(u.item.id?e.$t("Update"):e.$t("Create")),1)],void 0,!0),_:1})])]),default:d(()=>[n("div",v,[c(_,{errors:u.errors,item:u.item},null,8,["errors","item"])])],void 0),_:1},8,["direction","modelValue","title"])}]])},emits:["fetch"],props:{type:{type:String,required:!0},api:{type:Object,required:!0}},data:()=>({adder:!1}),methods:{toggle(e){this[e]=!this[e]},close(e){this[e]=!1},fetch(e){this.$emit("fetch",e)},listeners(){const e="edit-"+this.type;this.$bus.on(e,()=>{this.adder=!0})},capitalizeFirst:e=>e?e.charAt(0).toUpperCase()+e.slice(1):""},mounted(){this.listeners()},beforeUnmount(){const e="edit-"+this.type;this.$bus.off(e)}},[["render",function(e,t,i,r,a,u){const f=o("Icons"),_=s,g=o("adder");return l(),p("div",V,[c(_,{type:"primary",onClick:t[0]||(t[0]=e=>u.toggle("adder"))},{default:d(()=>[n("span",C,[c(f,{"icon-name":"plus"})]),m(" "+h(e.$t("Create"))+" "+h(u.capitalizeFirst(e.$t(i.type))),1)],void 0),_:1}),c(g,{api:i.api,type:i.type,visible:a.adder,onFetch:u.fetch,onClose:t[1]||(t[1]=e=>u.close("adder"))},null,8,["api","type","visible","onFetch"])])}]]);export{F as A}; diff --git a/wp-content/plugins/fluent-crm/assets/Animation.js b/wp-content/plugins/fluent-crm/assets/Animation.js new file mode 100644 index 0000000..a16f848 --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/Animation.js @@ -0,0 +1 @@ +import{W as e,Y as t,a5 as o,_ as n,a8 as i,ad as a,X as r,a7 as s}from"./vendor.js?ver=3.1.8";import{_ as l}from"./fc-bits-ui.js?ver=3.1.8";const d=l({name:"TransitionFade",props:{visible:{type:Boolean,default:!1}}},[["render",function(r,s,l,d,f,u){return e(),t(a,null,{default:o(()=>[l.visible?n(r.$slots,"default",{key:0},void 0,!0):i("",!0)],void 0),_:3})}],["__scopeId","data-v-5204b068"]]),f={name:"TransitionAccordion",props:{visible:{type:Boolean,default:!1},duration:{type:Number,default:300}},methods:{beforeEnter(e){e.style.height="0",e.style.opacity="0",e.style.overflow="hidden"},enter(e,t){const o=e.scrollHeight+"px";e.style.transition=`height ${this.duration}ms ease, opacity ${this.duration}ms ease`,requestAnimationFrame(()=>{e.style.height=o,e.style.opacity="1"}),setTimeout(()=>t(),this.duration)},afterEnter(e){e.style.height="auto",e.style.overflow=""},beforeLeave(e){e.style.height=e.scrollHeight+"px",e.style.opacity="1",e.style.overflow="hidden"},leave(e,t){e.offsetHeight,e.style.transition=`height ${this.duration}ms ease, opacity ${this.duration}ms ease`,e.style.height="0",e.style.opacity="0";const o=()=>{e.style.transition="",e.style.height="",e.style.opacity="",e.style.overflow="",e.removeEventListener("transitionend",o),t()};e.addEventListener("transitionend",o)},afterLeave(e){}}},u={key:0};const v=l(f,[["render",function(s,l,d,f,v,y){return e(),t(a,{onBeforeEnter:y.beforeEnter,onEnter:y.enter,onAfterEnter:y.afterEnter,onBeforeLeave:y.beforeLeave,onLeave:y.leave,onAfterLeave:y.afterLeave,mode:"in-out"},{default:o(()=>[d.visible?(e(),r("div",u,[n(s.$slots,"default",{},void 0,!0)])):i("",!0)],void 0),_:3},8,["onBeforeEnter","onEnter","onAfterEnter","onBeforeLeave","onLeave","onAfterLeave"])}],["__scopeId","data-v-dcfff484"]]);const y=l({name:"Animation",components:{TransitionFade:d,TransitionAccordion:v},props:{visible:{type:Boolean,default:!1},duration:{type:Number,default:300},fade:{type:Boolean,default:!1},accordion:{type:Boolean,default:!1}},computed:{TransitionComponent(){return this.fade?d:v}}},[["render",function(i,a,r,l,d,f){return e(),t(s(f.TransitionComponent),{visible:r.visible,duration:r.duration},{default:o(()=>[n(i.$slots,"default")],void 0),_:3},8,["visible","duration"])}]]);export{y as A}; diff --git a/wp-content/plugins/fluent-crm/assets/Badge.js b/wp-content/plugins/fluent-crm/assets/Badge.js new file mode 100644 index 0000000..2dc46ad --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/Badge.js @@ -0,0 +1 @@ +import{W as e,X as s,_ as t,a9 as r,aa as n,a0 as a}from"./vendor.js?ver=3.1.8";import{_ as c}from"./fc-bits-ui.js?ver=3.1.8";const i=c({name:"Badge",props:["type","plain"],methods:{badgeText(e){switch(e){case"completed":return this.$t("Completed");case"active":return this.$t("Active");case"publish":case"published":return this.$t("Published");case"shipped":return this.$t("Shipped");case"success":return this.$t("Success");case"failed":return this.$t("Failed");case"canceled":return this.$t("Canceled");case"bounced":return this.$t("Bounced");case"pending":return this.$t("Pending");case"subscribed":return this.$t("Subscribed");case"unsubscribed":return this.$t("Unsubscribed");case"draft":return this.$t("Draft");case"transactional":return this.$t("Transactional");case"archived":return this.$t("Archived");case"future":return this.$t("Future");case"inactive":return this.$t("Inactive");case"processing":return this.$t("Processing");case"working":return this.$t("Working");case"scheduled":return this.$t("Scheduled");case"on-hold":return this.$t("On Hold");case"dispute":return this.$t("Dispute");case"licensed":return this.$t("Licensed");case"sent":return this.$t("Sent");case"complained":return this.$t("Complained");case"spammed":return this.$t("Spammed");case"sms_subscribed":return this.$t("SMS Subscribed");case"sms_unsubscribed":return this.$t("SMS Unsubscribed");case"sms_pending":return this.$t("SMS Pending");case"sms_bounced":return this.$t("SMS Bounced");case"paused":return this.$t("Paused");case"pending-scheduled":return this.$t("Pending Scheduled");case"cancelled":return this.$t("Cancelled");default:return e}}}},[["render",function(c,i,u,d,h,$){return e(),s("span",{class:a(["fcrm_badge","fcrm_badge_"+u.type+(u.plain?" fcrm_badge_plain":"")])},[t(c.$slots,"icon"),r(" "+n($.badgeText(u.type)),1)],2)}]]);export{i as B}; diff --git a/wp-content/plugins/fluent-crm/assets/BaseCard.js b/wp-content/plugins/fluent-crm/assets/BaseCard.js new file mode 100644 index 0000000..c4ebd72 --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/BaseCard.js @@ -0,0 +1 @@ +import{ay as s}from"./vendor-element-plus.js?ver=3.1.8";import{W as a,X as e,_ as o,a8 as t,a6 as r,a0 as d}from"./vendor.js?ver=3.1.8";import{_ as c}from"./fc-bits-ui.js?ver=3.1.8";const l={name:"BaseCard",props:{bodyLoading:{type:Boolean,default:!1},noBodyPadding:{type:Boolean,default:!1},body_class:{type:[String,Array,Object],default:""}},computed:{hasHeader(){return Boolean(this.$slots.title||this.$slots.header_action)}}},_={class:"fcrm_base_card"},i={key:0,class:"fcrm_base_card_header"},n={key:0,class:"fcrm_base_card_title_wrap"},f={key:1,class:"fcrm_base_card_header_actions"},y={key:1,class:"fcrm_base_card_footer"};const m=c(l,[["render",function(c,l,m,b,p,v){const u=s;return a(),e("div",_,[v.hasHeader?(a(),e("div",i,[c.$slots.title?(a(),e("div",n,[o(c.$slots,"title")])):t("",!0),c.$slots.header_action?(a(),e("div",f,[o(c.$slots,"header_action")])):t("",!0)])):t("",!0),r((a(),e("div",{class:d(["fcrm_base_card_body",[m.body_class,{fcrm_p_0:m.noBodyPadding}]])},[o(c.$slots,"body",{},()=>[o(c.$slots,"default")])],2)),[[u,m.bodyLoading]]),c.$slots.footer?(a(),e("div",y,[o(c.$slots,"footer")])):t("",!0)])}]]);export{m as B}; diff --git a/wp-content/plugins/fluent-crm/assets/BlockComposer.js b/wp-content/plugins/fluent-crm/assets/BlockComposer.js new file mode 100644 index 0000000..cb21661 --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/BlockComposer.js @@ -0,0 +1 @@ +import{e,aF as t,aJ as i,aE as s,k as o,b0 as a,a$ as l,g as n,aK as r,aL as d,aS as c,c as m,Z as p,o as _,r as h,D as g,P as u,_ as f,bb as y,L as b,h as v,i as w,E,aO as S,ay as T,j as k,b9 as x,aH as $,aI as C}from"./vendor-element-plus.js?ver=3.1.8";import{aQ as D,W as F,X as L,Y as O,a8 as I,ab as P,aa as R,a0 as B,Z as j,a5 as V,J as A,az as N,a9 as U,bB as H,_ as z,a6 as M,a7 as G,b2 as W}from"./vendor.js?ver=3.1.8";import{p as q}from"./input-popover-dropdown.js?ver=3.1.8";import{_ as Y,I as J}from"./fc-bits-ui.js?ver=3.1.8";import{W as Z,F as K,a as Q}from"./_FormBuilder2.js?ver=3.1.8";import{k as X}from"./data_config.js?ver=3.1.8";import{E as ee}from"./EmailPreview.js?ver=3.1.8";import{P as te}from"./PaginationBar.js?ver=3.1.8";import{M as ie}from"./_MergeCodes.js?ver=3.1.8";import{B as se,a as oe}from"./BuiltinTemplateDrawer.js?ver=3.1.8";import{S as ae}from"./fc-bits.js?ver=3.1.8";import{P as le}from"./PromoCard.js?ver=3.1.8";const ne={class:"fc_raw_body"};const re=Y({name:"RawtextEditor",props:["modelValue","editor_design","hide_smart_codes"],emits:["update:modelValue"],components:{popover:q},data(){return{content:this.modelValue||"",editorShortcodes:window.fcAdmin.globalSmartCodes,cursorPos:this.value?this.value.length:0}},watch:{content(){this.$emit("update:modelValue",this.content)},modelValue(e){e!==this.content&&(this.content=e)}},methods:{handleCommand(e){var t=this.content.slice(0,this.cursorPos),i=this.content.slice(this.cursorPos,this.content.length);this.content=t+e+i,this.cursorPos+=e.length},updateCursorPos(e){var t=jQuery(".wp_editor_raw_html textarea").prop("selectionStart");this.cursorPos=t}},mounted(){jQuery(".wp_editor_raw_html textarea").on("click",e=>{this.updateCursorPos(e)})},created(){window.fcAdmin.extendedSmartCodes&&(this.editorShortcodes=[...this.editorShortcodes,...window.fcAdmin.extendedSmartCodes]),window.fcrm_funnel_context_codes&&(this.editorShortcodes=[...this.editorShortcodes,...window.fcrm_funnel_context_codes])}},[["render",function(t,i,s,o,a,l){const n=D("popover"),r=e;return F(),L("div",ne,[s.hide_smart_codes?I("",!0):(F(),O(n,{key:0,class:"popover-wrapper",data:a.editorShortcodes,onCommand:l.handleCommand},null,8,["data","onCommand"])),P(r,{type:"textarea",rows:30,class:"wp_editor_raw_html",placeholder:t.$t("Raw_Please_PHoyE"),onKeyup:l.updateCursorPos,modelValue:a.content,"onUpdate:modelValue":i[0]||(i[0]=e=>a.content=e)},null,8,["placeholder","onKeyup","modelValue"])])}]]),de={key:1,style:{"max-width":"800px",margin:"50px auto"},class:"fc_classic_editor_fallback"};const ce=Y({name:"FCBlockEditor",props:["modelValue","design_template"],emits:["changed","update:modelValue"],components:{WpEditor:Z},data(){return{content:this.modelValue||"\x3c!-- wp:paragraph --\x3e

"+this.$t("Start Writing Here")+"

\x3c!-- /wp:paragraph --\x3e",has_block_editor:"function"==typeof window.fluentCrmBootEmailEditor,editorShortcodes:window.fcAdmin.globalSmartCodes}},methods:{init(){this.has_block_editor&&window.fluentCrmBootEmailEditor(this.content,this.handleChange)},handleChange(e){this.$emit("update:modelValue",e),this.$emit("changed")}},mounted(){this.init(),jQuery(".block-editor-block-inspector__no-blocks").html('
'+this.$t("Tips")+":
  • - "+this.$t("Type")+" / "+this.$t("to see all the available blocks")+"
  • - "+this.$t("Type")+" @ "+this.$t("to insert dynamic tags")+"
  • - "+this.$t("Type")+" [[ "+this.$t("to insert post/page links")+"
  • - "+this.$t("BlockEditor.You_can_Use_Fallback_value")+" {{contact.first_name|There}}
"+this.$t("Please")+' '+this.$t("read the doc for advanced usage")+"
")}},[["render",function(e,t,i,s,o,a){const l=D("wp-editor");return o.has_block_editor?(F(),L("div",{key:0,id:"fluentcrm_block_editor_x",class:B(["fc_block_editor","fc_skin_"+i.design_template])},R(e.$t("Loading Editor...")),3)):(F(),L("div",de,[P(l,{editorShortcodes:o.editorShortcodes,modelValue:o.content,"onUpdate:modelValue":t[0]||(t[0]=e=>o.content=e),onChange:a.handleChange},null,8,["editorShortcodes","modelValue","onChange"]),j("p",null,R(e.$t("using_old_wordpress_version")),1)]))}]]),me={class:"icon"},pe=["src","alt"],_e={class:"fcrm_image_radio_label"};const he=Y({name:"InputRadioImage",components:{Icons:J},props:["field","modelValue","boxWidth","boxHeight","tooltip_prefix"],emits:["change","update:modelValue"],data(){return{model:this.modelValue,width:this.boxWidth||120,height:this.boxHeight||120}},watch:{model(e){this.$emit("update:modelValue",e),this.$emit("change",e)}}},[["render",function(e,o,a,l,n,r){const d=D("Icons"),c=i,m=t,p=s;return F(),O(p,{class:"fc_image_radio_tooltips fcrm_image_radio_tooltips",modelValue:n.model,"onUpdate:modelValue":o[0]||(o[0]=e=>n.model=e)},{default:V(()=>[(F(!0),L(A,null,N(a.field.options,(t,i)=>(F(),O(m,{key:i,value:t.id},{default:V(()=>[P(c,{content:a.tooltip_prefix?a.tooltip_prefix+t.label:t.label,placement:"top"},{default:V(()=>[j("div",{class:B([n.model==t.id?"fc_image_active fcrm_image_active":"","fc_image_box fcrm_image_box"])},[j("span",me,[P(d,{"icon-name":"circleFilled"})]),j("img",{src:t.image,alt:e.$t("Layout")},null,8,pe)],2)],void 0,!0),_:2},1032,["content"]),j("div",_e,R(t.label),1)],void 0,!0),_:2},1032,["value"]))),128))],void 0),_:1},8,["modelValue"])}]]),ge={name:"EmailStyleEditor",props:{template_config:{type:Object,required:!0},footer_settings:{type:Object,default:()=>({})},is_classic_editor:{type:Boolean,default:!1}},components:{Icons:J,FormBuilder:K},emits:["save"],data:()=>({showBodyConfig:!1,email_font_families:X,activeTab:"global"}),computed:{email_footer_fields(){return{custom_footer:{type:"input-radio",label:this.$t("Email Footer Type"),options:[{id:"no",label:this.$t("Use Global Email Footer")},{id:"yes",label:this.$t("Use Custom Email Footer")}]},footer_content:{type:"wp-editor",placeholder:this.$t("Custom Email Footer Text"),label:this.$t("Custom Email Footer Text"),help:this.$t("This email footer text will be used to this email only"),inline_help:this.$t("You should provide your business address")+" {{crm.business_address}} "+this.$t("and manage subscription/unsubscribe url is mandatory")+"
"+this.$t("Smartcode:")+" {{crm.business_name}}, {{crm.business_address}}, ##crm.manage_subscription_url##, ##crm.unsubscribe_url## "+this.$t("will be replaced with dynamic values."),dependency:{depends_on:"custom_footer",operator:"=",value:"yes"}}}},settingsFields(){if(this.isEmptyValue(this.template_config))return{};const e=[];Object.entries(this.email_font_families).forEach(([t,i])=>{e.push({id:i,label:t})});const t={body_bg_color:{label:this.$t("Body Background Color"),type:"input-color",colorFormat:"hex",showAlpha:!1},content_width:{label:this.$t("Content Max Width (PX)"),inline_help:this.$t("Gut_Suggesting_vB6t8"),type:"input-number",min:400,step:10},content_padding:{label:this.$t("Content Padding Left/Right"),type:"input-number",min:0,step:1},content_bg_color:{label:this.$t("Content Background Color"),type:"input-color",colorFormat:"hex",showAlpha:!1},text_color:{label:this.$t("Default Content Color"),type:"input-color",colorFormat:"hex",showAlpha:!1},heading_color:{label:this.$t("Default Headings Color"),type:"input-color",colorFormat:"hex",showAlpha:!1},footer_text_color:{label:this.$t("Footer Text Color"),type:"input-color",colorFormat:"hex",showAlpha:!1},link_color:{label:this.$t("Default Link Color"),type:"input-color",colorFormat:"hex",showAlpha:!1},content_font_family:{label:this.$t("Content Font Family"),type:"input-option",options:e},headings_font_family:{label:this.$t("Headings Font Family"),type:"input-option",options:e},disable_footer:{type:"inline-checkbox",true_label:"yes",false_label:"no",checkbox_label:this.$t("Disable Default Email Footer"),inline_help:this.$t("email_will_be_sent_without_footer_contents")}},i=Object.keys(this.template_config),s={};return Object.entries(t).forEach(([e,t])=>{-1!==i.indexOf(e)&&(s[e]=t)}),this.is_classic_editor&&delete s.content_padding,s}},watch:{template_config:{deep:!0,handler(){this.generateStyles()}}},methods:{open(){this.showBodyConfig=!0},syncLegacyDisableFooter(){this.footer_settings&&this.template_config&&Object.prototype.hasOwnProperty.call(this.template_config,"disable_footer")&&(this.footer_settings.disable_footer="yes"===this.template_config.disable_footer?"yes":"no")},triggerUpdate(){this.syncLegacyDisableFooter(),this.$post("templates/set-global-style",{config:this.template_config}).then(e=>{console.log(e)}).catch(e=>{this.$handleError(e)}),this.$emit("save"),this.showBodyConfig=!1},generateStyles(){let e="";const t=this.template_config;if(this.isEmptyValue(t)){const e=document.getElementById("fc_mail_config_style");return void(e&&(e.innerHTML=""))}const i=".fluentcrm_visual_editor .fc_visual_body .fce-block-editor ";e+=`${i} .block-editor-writing-flow { background-color: ${t.body_bg_color}; }`,e+=`${i} .fc_editor_body { background-color: ${t.content_bg_color}; color: ${t.text_color}; max-width: ${t.content_width}px; font-family: ${t.content_font_family} !important; }`,e+=`.fc_skin_plain .fc_editor_body,.fc_skin_classic .fc_editor_body,.fc_skin_simple .fc_editor_body {padding-left: ${t.content_padding}px !important;padding-right: ${t.content_padding}px !important; }`,e+=`${i} .fc_editor_body p,\n ${i} .fc_editor_body li, ol { color: inherit; font-size: inherit; }`,e+=`${i} .fc_editor_body h1,\n ${i} .fc_editor_body h2,\n ${i} .fc_editor_body h3,\n ${i} .fc_editor_body h4 { color: ${t.headings_color}; font-family: ${t.headings_font_family} !important; }\n ${i} .fc_editor_body a { color: ${t.link_color}; }`;const s=document.getElementById("fc_mail_config_style");s&&(s.innerHTML='")}},mounted(){this.generateStyles(),this.footer_settings||(this.footer_settings={custom_footer:"no",footer_content:""}),(!this.footer_settings.footer_content||this.footer_settings.footer_content.length<10)&&(this.footer_settings.footer_content=window.fcAdmin.global_email_footer)}},ue={style:{display:"inline-block"},class:"fc_style_editor"},fe={class:"icon"},ye={style:{"margin-top":"20px"},class:"fc_2col_form_wrapper"},be={style:{"margin-top":"20px"}},ve={class:"dialog-footer text-align-right"};const we=Y(ge,[["render",function(e,t,i,s,r,d){const c=D("Icons"),m=o,p=D("form-builder"),_=l,h=a,g=n;return F(),L("span",ue,[e.isEmptyValue(i.template_config)?I("",!0):(F(),O(m,{key:0,onClick:t[0]||(t[0]=e=>r.showBodyConfig=!0),size:"small",class:"only-icon-btn small"},{default:V(()=>[j("span",fe,[P(c,{"icon-name":"settings"})])],void 0),_:1})),P(g,{"close-on-click-modal":!1,title:e.$t("Email Styling Settings & Footer Settings"),modelValue:r.showBodyConfig,"onUpdate:modelValue":t[3]||(t[3]=e=>r.showBodyConfig=e),"append-to-body":!0,width:"60%"},{footer:V(()=>[j("span",ve,[P(m,{onClick:t[2]||(t[2]=e=>d.triggerUpdate()),type:"primary"},{default:V(()=>[U(R(e.$t("Update Settings")),1)],void 0,!0),_:1})])]),default:V(()=>[r.showBodyConfig?(F(),O(h,{key:0,modelValue:r.activeTab,"onUpdate:modelValue":t[1]||(t[1]=e=>r.activeTab=e),class:"fc_settings_popup"},{default:V(()=>[P(_,{label:e.$t("Global Settings"),name:"global"},{default:V(()=>[j("div",ye,[r.showBodyConfig?(F(),O(p,{key:0,formData:i.template_config,fields:d.settingsFields},null,8,["formData","fields"])):I("",!0)])],void 0,!0),_:1},8,["label"]),P(_,{label:e.$t("Footer Settings"),name:"email_footer"},{default:V(()=>[j("div",be,[j("p",null,R(e.$t("Customize_Email_Footer_Sec")),1),P(p,{formData:i.footer_settings,fields:d.email_footer_fields},null,8,["formData","fields"])])],void 0,!0),_:1},8,["label"])],void 0,!0),_:1},8,["modelValue"])):I("",!0)],void 0),_:1},8,["title","modelValue"]),t[4]||(t[4]=j("div",{id:"fc_mail_config_style"},null,-1))])}]]),Ee={name:"AiEmailBodyGenerator",props:{disabled:Boolean,context:{type:Object,default:()=>({})}},emits:["insert"],data:()=>({visible:!1,generating:!1,error:"",form:{prompt:"",tone:"friendly",length:"medium"}}),methods:{generate(){this.form.prompt.trim()?(this.generating=!0,this.error="",this.$post("ai/generate-email-body",{...this.form,context:this.context}).then(e=>{this.insertGeneratedBody(e||{})}).catch(e=>{this.error=this.getErrorMessage(e)}).finally(()=>{this.generating=!1})):this.error=this.$t("Please describe the email you want to generate.")},insertGeneratedBody(e){const t=e.subject_suggestions||[];this.$emit("insert",{email_body:e.email_body||"",subject:t[0]||"",preview_text:e.preview_text||""}),this.visible=!1,this.error=""},getErrorMessage(e){return e&&e.message?e.message:e&&e.data&&e.data.message?e.data.message:this.$t("Could not generate email body. Please try again.")}}},Se={class:"fcrm_ai_email_generator"},Te={class:"fcrm_ai_email_generator_dialog_header"},ke={class:"fcrm_ai_email_generator_form"},xe={class:"fcrm_ai_email_generator_grid"},$e={class:"fcrm_ai_email_generator_footer"};const Ce=Y(Ee,[["render",function(t,i,s,a,l,m){const p=o,_=e,h=d,g=r,u=c,f=n;return F(),L("span",Se,[P(p,{size:"small",class:"fcrm_ai_email_generator_trigger",disabled:s.disabled,onClick:i[0]||(i[0]=e=>l.visible=!0)},{default:V(()=>[i[6]||(i[6]=j("span",{class:"fcrm_ai_email_generator_icon","aria-hidden":"true"},[j("svg",{width:"14",height:"14",viewBox:"0 0 16 16",fill:"none",xmlns:"http://www.w3.org/2000/svg"},[j("path",{d:"M8.75 1.25L9.80875 4.44125L13 5.5L9.80875 6.55875L8.75 9.75L7.69125 6.55875L4.5 5.5L7.69125 4.44125L8.75 1.25Z",fill:"currentColor"}),j("path",{d:"M3.75 8.25L4.39125 10.1088L6.25 10.75L4.39125 11.3912L3.75 13.25L3.10875 11.3912L1.25 10.75L3.10875 10.1088L3.75 8.25Z",fill:"currentColor"}),j("path",{d:"M12 9.5L12.4288 10.8212L13.75 11.25L12.4288 11.6788L12 13L11.5712 11.6788L10.25 11.25L11.5712 10.8212L12 9.5Z",fill:"currentColor"})])],-1)),U(" "+R(t.$t("Generate with AI")),1)],void 0),_:1},8,["disabled"]),P(f,{modelValue:l.visible,"onUpdate:modelValue":i[5]||(i[5]=e=>l.visible=e),"append-to-body":!0,"close-on-click-modal":!1,width:"520px",class:"fcrm_ai_email_generator_dialog"},{header:V(()=>[j("div",Te,[i[7]||(i[7]=j("div",{class:"fcrm_ai_email_generator_dialog_icon"},[j("svg",{width:"18",height:"18",viewBox:"0 0 16 16",fill:"none",xmlns:"http://www.w3.org/2000/svg"},[j("path",{d:"M8.75 1.25L9.80875 4.44125L13 5.5L9.80875 6.55875L8.75 9.75L7.69125 6.55875L4.5 5.5L7.69125 4.44125L8.75 1.25Z",fill:"currentColor"}),j("path",{d:"M3.75 8.25L4.39125 10.1088L6.25 10.75L4.39125 11.3912L3.75 13.25L3.10875 11.3912L1.25 10.75L3.10875 10.1088L3.75 8.25Z",fill:"currentColor"})])],-1)),j("div",null,[j("h3",null,R(t.$t("Generate Email Body with AI")),1),j("p",null,R(t.$t("Describe the email you want and AI will write it into the editor.")),1)])])]),footer:V(()=>[j("div",$e,[P(p,{onClick:i[4]||(i[4]=e=>l.visible=!1)},{default:V(()=>[U(R(t.$t("Cancel")),1)],void 0,!0),_:1}),P(p,{type:"primary",loading:l.generating,disabled:!l.form.prompt.trim(),onClick:m.generate},{default:V(()=>[U(R(t.$t("Write with AI")),1)],void 0,!0),_:1},8,["loading","disabled","onClick"])])]),default:V(()=>[j("div",ke,[j("label",null,R(t.$t("What should this email be about?")),1),P(_,{type:"textarea",rows:5,modelValue:l.form.prompt,"onUpdate:modelValue":i[1]||(i[1]=e=>l.form.prompt=e),placeholder:t.$t("Example: Announce our new course and invite subscribers to join before Friday.")},null,8,["modelValue","placeholder"]),j("div",xe,[j("div",null,[j("label",null,R(t.$t("Tone")),1),P(g,{modelValue:l.form.tone,"onUpdate:modelValue":i[2]||(i[2]=e=>l.form.tone=e),class:"w-100"},{default:V(()=>[P(h,{label:t.$t("Friendly"),value:"friendly"},null,8,["label"]),P(h,{label:t.$t("Professional"),value:"professional"},null,8,["label"]),P(h,{label:t.$t("Casual"),value:"casual"},null,8,["label"]),P(h,{label:t.$t("Persuasive"),value:"persuasive"},null,8,["label"]),P(h,{label:t.$t("Educational"),value:"educational"},null,8,["label"])],void 0,!0),_:1},8,["modelValue"])]),j("div",null,[j("label",null,R(t.$t("Length")),1),P(g,{modelValue:l.form.length,"onUpdate:modelValue":i[3]||(i[3]=e=>l.form.length=e),class:"w-100"},{default:V(()=>[P(h,{label:t.$t("Short"),value:"short"},null,8,["label"]),P(h,{label:t.$t("Medium"),value:"medium"},null,8,["label"]),P(h,{label:t.$t("Long"),value:"long"},null,8,["label"])],void 0,!0),_:1},8,["modelValue"])])]),l.error?(F(),O(u,{key:0,type:"error",closable:!1,title:l.error,"show-icon":""},null,8,["title"])):I("",!0)])],void 0),_:1},8,["modelValue"])])}]]),De={class:"fcrm_editor_loader_blocks_preview"},Fe={key:0,class:"fcrm_editor_loader_blocks_title"};const Le={name:"NewEditorFrame",components:{EditorLoader:Y({name:"EditorLoader",props:{loaderText:{type:String,default:"Loading..."}}},[["render",function(e,t,i,s,o,a){return F(),L("div",De,[t[0]||(t[0]=H('
',1)),i.loaderText?(F(),L("div",Fe,R(i.loaderText),1)):I("",!0)])}]])},emits:["update:modelValue","titleUpdated","featuredMediaUpdated","useAlternateEditor","contentUpdated","editorNext","editorBack","editorFullscreenToggle","layoutChange","openEmailPreview","openTemplatePicker","openSaveTemplate","editorSaveDraft","styleConfigChange","footerSettingsUpdated","editorRecoveryNotice"],props:{editorPath:{type:String,default:"/editor"},modelValue:{type:String,default:""},fallback_content:{type:String,default:""},editorParams:{type:Object,default:()=>({})},frameHeight:{type:String,default:"500px"},documentTitle:{type:String,default:""},templateConfig:{type:Object,default:()=>({})},footerSettings:{type:Object,default:()=>null},extra_tags:{type:Array,default:()=>[]}},data:()=>({editorData:{content:""},editorFrame:null,editorOrigin:null,lastUpdate:"No updates yet",dataSent:!1,isFailing:!1,iframeReady:!1,pendingDataSend:!1,editorReadyReceived:!1,lastLayoutSent:"",sendRetryCount:0}),computed:{notLoading(){return!this.dataSent&&this.isFailing}},mounted(){setTimeout(()=>{this.editorReadyReceived||(this.isFailing=!0)},3e4),this.editorData.content=this.modelValue,this.modelValue||(this.fallback_content?this.editorData.content=this.fallback_content:this.editorData.content="\x3c!-- wp:paragraph --\x3e

\x3c!-- /wp:paragraph --\x3e"),this.editorData.title=this.documentTitle,this.createEditorIframe(),window.addEventListener("message",this.handleEditorMessage),document.body.classList.add("fcm_custom_editor_open")},watch:{modelValue(e){e!==this.editorData.content&&(this.editorData.content=e||"",this.editorReadyReceived&&this.sendDataToEditor())},fallback_content(e){!this.modelValue&&e&&e!==this.editorData.content&&(this.editorData.content=e,this.editorReadyReceived&&this.sendDataToEditor())},documentTitle(e){e!==this.editorData.title&&(this.editorData.title=e||"",this.editorReadyReceived&&this.sendDataToEditor())},editorParams:{deep:!0,handler(e,t){const i=e&&e.design_template?e.design_template:"",s=t&&t.design_template?t.design_template:"";i&&i!==s&&this.syncLayout(i)}},templateConfig:{deep:!0,handler(e){this.editorReadyReceived&&e&&this.syncStyleConfig(e)}},footerSettings:{deep:!0,handler(e){this.editorReadyReceived&&this.syncFooterSettings(e)}}},beforeUnmount(){window.removeEventListener("message",this.handleEditorMessage),this.editorFrame&&(this.editorFrame.onload=null,this.$refs.iframeContainer.removeChild(this.editorFrame),this.editorFrame=null),document.body.classList.remove("fcm_custom_editor_open")},methods:{makeSerializable(e){try{if("function"==typeof structuredClone)return structuredClone(e)}catch(t){}try{return JSON.parse(JSON.stringify(e))}catch(t){return null}},postMessageToEditor(e){if(!this.iframeReady||!this.editorFrame||!this.editorFrame.contentWindow)return!1;try{const t=this.makeSerializable(e);return t?(this.editorFrame.contentWindow.postMessage(t,this.editorOrigin||"*"),!0):(console.error("Error posting message to editor: payload is not serializable"),!1)}catch(t){return console.error("Error posting message to editor:",t),!1}},syncLayout(e){if(!e||e===this.lastLayoutSent)return;const t={action:"LAYOUT_SYNC",design_template:e};this.postMessageToEditor(t)&&(this.lastLayoutSent=e)},syncStyleConfig(e){e&&"object"==typeof e&&this.postMessageToEditor({action:"STYLE_CONFIG_SYNC",template_config:JSON.parse(JSON.stringify(e))})},syncFooterSettings(e){this.postMessageToEditor({action:"FOOTER_SETTINGS_SYNC",footer_settings:JSON.parse(JSON.stringify(e||null))})},createEditorIframe(){const e=new URL(this.editorPath,window.location.origin);this.editorOrigin=e.origin,this.editorFrame=document.createElement("iframe");let t=this.appVars.crm_editor_frame;if(this.editorParams){t+="&"+new URLSearchParams(this.editorParams).toString()}try{this.editorOrigin=new URL(t,window.location.origin).origin}catch(i){this.editorOrigin=window.location.origin}this.editorFrame.src=t,this.editorFrame.style.width="100%",this.editorFrame.style.height=this.frameHeight,this.editorFrame.onload=()=>{console.log("Iframe loaded successfully"),this.iframeReady=!0,this.pendingDataSend&&(this.pendingDataSend=!1,setTimeout(()=>{this.sendDataToEditor()},1e3)),setTimeout(()=>{!this.editorReadyReceived&&this.iframeReady&&(console.log("Forcing data send after iframe load timeout"),this.sendDataToEditor())},5e3)},this.$refs.iframeContainer.appendChild(this.editorFrame)},editorFailed(){this.$emit("useAlternateEditor")},sendDataToEditor(){if(!this.iframeReady||!this.editorFrame||!this.editorFrame.contentWindow)return console.log("Iframe not ready yet, marking for pending send"),void(this.pendingDataSend=!0);try{let e=this.editorData;e.templateConfig=this.templateConfig,e.footerSettings=this.footerSettings,this.extra_tags&&(e.extra_tags=this.extra_tags);const t=JSON.parse(JSON.stringify(e));if(!this.postMessageToEditor({type:"UPDATE_EDITOR",data:t}))return this.sendRetryCount++,void(this.sendRetryCount<10?(console.warn("postMessageToEditor returned false, retrying ("+this.sendRetryCount+"/10)..."),setTimeout(()=>{this.sendDataToEditor()},500)):(console.error("postMessageToEditor failed after 10 retries"),this.isFailing=!0));this.sendRetryCount=0;const i=this.editorParams&&this.editorParams.design_template?this.editorParams.design_template:"";i&&this.syncLayout(i),setTimeout(()=>{this.dataSent=!0},500)}catch(e){this.sendRetryCount++,this.sendRetryCount<10?(console.error("Error sending data to editor, retrying ("+this.sendRetryCount+"/10):",e),setTimeout(()=>{this.sendDataToEditor()},1e3)):(console.error("Error sending data to editor after 10 retries:",e),this.isFailing=!0)}setTimeout(()=>{var e;let t=null==(e=document.getElementById("wpbody"))?void 0:e.clientHeight;t&&document.documentElement.style.setProperty("--fcm-root-app-height",t+"px")},1500)},handleEditorMessage(e){var t;if(!(null==(t=this.editorFrame)?void 0:t.contentWindow)||e.source!==this.editorFrame.contentWindow)return;const{action:i,content:s}=e.data;if("EDITOR_UPDATED"===i)this.dataSent&&s?(this.editorData.content=s,this.$emit("update:modelValue",s),this.$emit("contentUpdated",s)):console.log("Received EDITOR_UPDATED but data not sent yet or content is empty, ignoring");else if("TITLE_UPDATED"===i)this.editorData.title=s,this.$emit("titleUpdated",s);else if("FEATURED_MEDIA_UPDATED"===i)this.$emit("featuredMediaUpdated",s);else if("EDITOR_NEXT"===i)this.$emit("editorNext");else if("EDITOR_BACK"===i)this.$emit("editorBack");else if("EDITOR_FULLSCREEN_TOGGLE"===i){const t=!(!e.data||!Object.prototype.hasOwnProperty.call(e.data,"enabled"));this.$emit("editorFullscreenToggle",t?!!e.data.enabled:null)}else"EDITOR_LAYOUT_CHANGE"===i&&e.data&&e.data.design_template?this.$emit("layoutChange",e.data.design_template):"EDITOR_OPEN_EMAIL_PREVIEW"===i?this.$emit("openEmailPreview"):"EDITOR_OPEN_TEMPLATES"===i?this.$emit("openTemplatePicker"):"EDITOR_OPEN_SAVE_TEMPLATE"===i?this.$emit("openSaveTemplate"):"EDITOR_SAVE_DRAFT"===i?this.$emit("editorSaveDraft"):"EDITOR_STYLE_CONFIG_CHANGE"===i&&e.data&&e.data.template_config?this.$emit("styleConfigChange",e.data.template_config):"EDITOR_READY"===i?(this.editorReadyReceived=!0,this.isFailing=!1,this.sendDataToEditor()):"EDITOR_INIT_FAILED"===i?this.isFailing=!0:"EDITOR_RECOVERY_NOTICE"===i?this.$emit("editorRecoveryNotice"):"EDITOR_FOOTER_SETTINGS_CHANGE"===i&&this.$emit("footerSettingsUpdated",e.data.footer_settings)}}},Oe={key:0,style:{display:"flex","justify-content":"center","align-items":"center","column-gap":"10px"}},Ie={class:"editor-container"},Pe={ref:"iframeContainer",class:"iframe-container"};const Re={name:"BlockComposer",props:{campaign:{type:Object,required:!0},enable_templates:Boolean,disable_fixed:Boolean,body_key:String,enable_template_save:Boolean,show_merge:Boolean,use_fullscreen_editor:{type:Boolean,default:!1},iframe_nav_mode:{type:String,default:""},hideBackBtn:{type:Boolean,default:!1},hideNextBtn:{type:Boolean,default:!1},hideSaveBtn:{type:Boolean,default:!1},disableGutenbergAutosave:{type:Boolean,default:!1},extra_tags:{type:Array,default:()=>[]},show_audit:Boolean,disabled_templates:{type:Object,default:()=>({})},features:{type:Object,default:()=>({})}},components:{PromoCard:le,BuildInTemplatesList:oe,Icons:J,ImageRadioToolTip:he,RawEditor:re,EmailStyleEditor:we,BlockEditor:ce,EmailPreview:ee,WpEditor:Q,PaginationBar:te,MergeCodes:ie,BuiltinTemplateDrawer:se,AiEmailBodyGenerator:Ce,Plus:b,Help:y,FolderOpened:f,MoreFilled:u,Search:g,Upload:h,View:_,FullScreen:p,Close:m,NewEditorFrame:Y(Le,[["render",function(e,t,i,s,a,l){const n=o,r=D("EditorLoader");return F(),L("div",null,[l.notLoading?(F(),L("div",Oe,[j("p",null,R(e.$t("Problem with loading the editor?")),1),P(n,{onClick:t[0]||(t[0]=e=>l.editorFailed())},{default:V(()=>[U(R(e.$t("Use Alternative Editor")),1)],void 0),_:1})])):I("",!0),j("div",Ie,[a.dataSent?I("",!0):(F(),O(r,{key:0,loaderText:e.$t("Loading editor. Please wait....")},null,8,["loaderText"])),j("div",Pe,null,512)])])}],["__scopeId","data-v-0b3373b2"]])},emits:["save","template_inserted","fetch","editor_next","editor_back"],data(){return{showNewEditor:!1,isFullScreen:!1,loadingTemplates:!1,templates:[],fetchingTemplate:!1,loading:!1,editor_status:!0,templates_modal:!1,importTemplateActiveTab:"my_templates",email_body_key:this.body_key||"email_body",search:"",pagination:{current_page:1,per_page:10,total:0},crmTemplates:[],crmTemplateCatalog:[],crmPagination:{current_page:1,per_page:8,total:0},crmLoading:!1,crmTemplatesLoaded:!1,query_data:{sort_by:"ID",sort_type:"DESC"},new_template_name:"",new_template_pop:!1,saving_template:!1,footerSettings:{custom_footer:"no",footer_content:"",disable_footer:"no",background_color:"transparent",footer_padding:"20"},previewTemplateId:"",showTemplatePreview:!1,email_template:{post_title:"",post_content:"",post_excerpt:"",email_subject:"",edit_type:"html",design_template:"simple",settings:{template_config:{}}},newEditorObj:{content:"",title:"",block_type:"",id:0},open_drawer:!1,isLayoutSidebarCollapsed:!1,fixedScrollHandler:null,fixedResizeHandler:null}},created(){this.initNewEditorObj(),this.ensureFooterSettingsInitialized()},watch:{"campaign.settings.footer_settings":{deep:!0,handler(e){const t=this.normalizeFooterSettings(e,this.campaign.settings?this.campaign.settings.template_config:{});this.isFooterSettingsEqual(this.footerSettings||{},t)||(this.footerSettings=t);const i=e||{};this.isFooterSettingsEqual(i,t)||(this.campaign.settings||(this.campaign.settings={}),this.campaign.settings.footer_settings=t),this.syncLegacyDisableFooterFlag(t.disable_footer)}},"campaign.id"(e){"template"===this.newEditorObj.block_type&&e&&String(this.newEditorObj.id)!==String(e)&&(this.newEditorObj.id=e)},"campaign.ID"(e){"template"===this.newEditorObj.block_type&&e&&String(this.newEditorObj.id)!==String(e)&&(this.newEditorObj.id=e)},"$route.params.template_id"(e){"template"===this.newEditorObj.block_type&&e&&String(this.newEditorObj.id)!==String(e)&&(this.newEditorObj.id=e)},isLayoutSidebarCollapsed(e){try{ae.set("layout_sidebar_collapsed",e?"yes":"no")}catch(t){}this.$nextTick(()=>{this.fixedResizeHandler&&this.fixedResizeHandler()})},"campaign.design_template"(e,t){if(this.email_template_designs[t],this.email_template_designs[e],this.editor_status&&("raw_classic"!==e&&"raw_html"!==e&&"visual_builder"!==e||this.unmountBlockEditor(),this.email_template_designs[e])){this.campaign.settings||(this.campaign.settings={});const t={...this.campaign.settings.template_config||{}},i={...this.email_template_designs[e].config||{}};if(!Object.keys(i).length)return void(this.campaign.settings.template_config={});Object.entries(i).forEach(([e])=>{"body_bg_color"!==e&&"content_bg_color"!==e&&"design_template"!==e&&t[e]&&(i[e]=t[e])}),this.campaign.settings.template_config=i}this.selectedDesign&&this.selectedDesign.use_gutenberg&&this.initNewEditorObj(),this.updateCustomEditorPageClass()},"newEditorObj.content"(e){this.selectedDesign&&this.selectedDesign.use_gutenberg&&e!==this.campaign[this.email_body_key]&&(this.campaign[this.email_body_key]=e,console.log("[BlockComposer] newEditorObj.content watcher - synced to campaign"))}},computed:{ft(){const e=this.features||{};return{email_style_settings:void 0===e.email_style_settings||e.email_style_settings,email_footer:void 0===e.email_footer||e.email_footer,email_preview:void 0!==e.email_preview?e.email_preview:!!this.show_audit,save_as_template:void 0!==e.save_as_template?e.save_as_template:!!this.enable_template_save,browse_templates:void 0!==e.browse_templates?e.browse_templates:!!this.enable_templates,smartcodes:void 0!==e.smartcodes?e.smartcodes:!!this.show_merge,design_switcher:void 0===e.design_switcher||e.design_switcher}},resolvedFooterSettings(){return this.normalizeFooterSettings(this.footerSettings,this.campaign.settings?this.campaign.settings.template_config:{})},showFooterComplianceWarning(){return!(!this.ft.email_footer||"yes"!==this.resolvedFooterSettings.disable_footer)},selectedDesign(){const e=this.email_template_designs[this.campaign.design_template];return e||this.email_template_designs.simple},classic_styles:()=>"body {line-height: 150%;padding: 0px 20px 20px;} body p, ul, li, ol { font-size: 16px; }",email_template_designs(){return this.disabled_templates?Object.fromEntries(Object.entries(window.fcAdmin.email_template_designs).filter(([e,t])=>!this.disabled_templates[e])):window.fcAdmin.email_template_designs},defaultGutenbergEditorId(){const e=Object.values(this.email_template_designs||{}).find(e=>e&&e.use_gutenberg);return e&&e.id||"simple"},editorTemplateDesigns(){const e=this.email_template_designs||{},t={[this.defaultGutenbergEditorId]:this.$t("Default (Gutenberg)"),raw_html:this.$t("Raw HTML")};return[this.defaultGutenbergEditorId,"raw_classic","raw_html","visual_builder"].reduce((i,s)=>(s&&e[s]&&(i[s]={...e[s],label:t[s]??e[s].label}),i),{})},selectedEditorTemplateId:{get(){const e=this.campaign.design_template;if(this.editorTemplateDesigns[e])return e;const t=this.email_template_designs[e];return t&&t.use_gutenberg&&this.editorTemplateDesigns[this.defaultGutenbergEditorId]?this.defaultGutenbergEditorId:e},set(e){this.campaign.design_template=e}},fullScreenEditorUrl(){const e=this.appVars&&this.appVars.crm_editor_frame?this.appVars.crm_editor_frame:"";if(!e||!this.newEditorObj.block_type)return"#";return e+"&"+new URLSearchParams({block_type:this.newEditorObj.block_type||"",campaign_title:this.newEditorObj.title||"",bid:this.newEditorObj.id||0}).toString()},editorIframeHeight(){return this.isFullScreen?"calc(100vh - 50px)":"calc(100vh - 80px)"},iframeEditorParams(){const e={block_type:this.newEditorObj.block_type,campaign_title:this.newEditorObj.title,bid:this.newEditorObj.id};return this.iframe_nav_mode&&(e.fcrm_ui=this.iframe_nav_mode),this.hideBackBtn&&(e.hideBackBtn="1"),this.hideNextBtn&&(e.hideNextBtn="1"),this.hideSaveBtn&&(e.hideSaveBtn="1"),this.disableGutenbergAutosave&&(e.disable_autosave="1"),this.campaign&&this.campaign.design_template&&(e.design_template=this.campaign.design_template),e},shouldRenderGutenbergEditor(){return this.selectedDesign&&this.selectedDesign.use_gutenberg},aiEmailContext(){var e;return{design_template:this.campaign.design_template||"",editor_type:this.selectedDesign&&this.selectedDesign.use_gutenberg?"block_editor":(null==(e=this.selectedDesign)?void 0:e.template_type)||"",output_format:this.aiEmailOutputFormat,campaign_type:this.newEditorObj.block_type||this.campaign.type||"",has_existing_body:this.hasExistingEmailBody?"yes":"no"}},aiEmailOutputFormat(){return this.selectedDesign?this.selectedDesign.use_gutenberg?"gutenberg_blocks":"classic_editor"===this.selectedDesign.template_type?"classic_html":"raw_html":"html"},showAiEmailGenerator(){return!!this.selectedDesign&&("visual_builder"!==this.campaign.design_template&&"visual_builder_demo"!==this.selectedDesign.template_type&&(this.selectedDesign.use_gutenberg||["classic_editor","raw_html","raw_classic"].includes(this.selectedDesign.template_type)||!this.selectedDesign.template_type))},hasExistingEmailBody(){const e=this.selectedDesign&&this.selectedDesign.use_gutenberg?this.newEditorObj.content:this.campaign[this.email_body_key];return!(!e||!String(e).trim())}},methods:{defaultFooterSettings:()=>({custom_footer:"no",footer_content:"",disable_footer:"no",background_color:"transparent",footer_padding:"20"}),normalizeFooterSettings(e,t={}){const i=e||{},s=Object.prototype.hasOwnProperty.call(i,"disable_footer"),o=Object.prototype.hasOwnProperty.call(i,"custom_footer"),a={...this.defaultFooterSettings(),...i},l=t&&t.disable_footer;s||"yes"===a.disable_footer||"no"===a.disable_footer||(a.disable_footer="yes"===l||"no"===l?l:"no"),a.custom_footer="yes"===a.custom_footer?"yes":"no";!s&&!o&&"yes"===a.disable_footer&&t&&"yes"===t.disable_footer&&"yes"!==a.custom_footer&&0===String(a.footer_content||"").replace(/<[^>]*>/g,"").trim().length&&(a.disable_footer="no",a.custom_footer="no");const n=parseInt(a.footer_padding,10);return!Number.isNaN(n)&&n>=0?(a.footer_padding=String(Math.min(n,80)),a):(a.footer_padding="20",a)},isFooterSettingsEqual(e,t){const i=e||{},s=t||{};return Array.from(new Set([...Object.keys(i),...Object.keys(s)])).every(e=>i[e]===s[e])},ensureFooterSettingsInitialized(){this.campaign.settings||(this.campaign.settings={});const e=this.normalizeFooterSettings(this.campaign.settings.footer_settings,this.campaign.settings.template_config||{});this.isFooterSettingsEqual(this.footerSettings||{},e)||(this.footerSettings=e);const t=this.campaign.settings.footer_settings||{};this.isFooterSettingsEqual(t,e)||(this.campaign.settings.footer_settings=e),this.syncLegacyDisableFooterFlag(e.disable_footer)},onFooterSettingsUpdated(e){this.campaign.settings||(this.campaign.settings={});const t=this.normalizeFooterSettings(e,this.campaign.settings.template_config||{});this.isFooterSettingsEqual(this.footerSettings||{},t)||(this.footerSettings=t);const i=this.campaign.settings.footer_settings||{};this.isFooterSettingsEqual(i,t)||(this.campaign.settings.footer_settings=t),this.syncLegacyDisableFooterFlag(t.disable_footer)},syncLegacyDisableFooterFlag(e){this.campaign.settings||(this.campaign.settings={}),this.campaign.settings.template_config&&"object"==typeof this.campaign.settings.template_config||(this.campaign.settings.template_config={});const t="yes"===e?"yes":"no";this.campaign.settings.template_config.disable_footer!==t&&(this.campaign.settings.template_config.disable_footer=t)},parseEditorType(e){const t=this.email_template_designs[e];return t?t.use_gutenberg?this.$t("Gutenberg Editor"):"classic_editor"===t.template_type?this.$t("Classic Editor"):"custom_component"===t.template_type?this.$t("Custom Component: ")+t.name:"visual_builder_demo"===t.template_type?this.$t("Visual Builder (Demo)"):t.name||e:""},toggleLayoutSidebar(){this.isLayoutSidebarCollapsed=!this.isLayoutSidebarCollapsed},restoreLayoutSidebarState(){try{this.isLayoutSidebarCollapsed="yes"===ae.get("layout_sidebar_collapsed")}catch(e){}},closeGutenbergFullScreen(){this.toggleFullScreen()},updateCustomEditorPageClass(){if(!this.use_fullscreen_editor)return;const e=this.selectedDesign&&this.selectedDesign.use_gutenberg;document.documentElement.classList.toggle("fcrm_custom_editor_page",!!e),document.body.classList.toggle("fcrm_custom_editor_page",!!e)},handleEditorNext(){this.$emit("editor_next")},handleEditorBack(){this.$emit("editor_back")},onEditorFullscreenToggle(e){if(this.selectedDesign&&this.selectedDesign.use_gutenberg)if("boolean"!=typeof e)null==e&&this.toggleFullScreen();else{if(this.isFullScreen===e)return;this.toggleFullScreen()}},onOpenEmailPreview(){this.$nextTick(()=>{var e,t,i,s;null==(t=null==(e=this.$refs.iframePreviewRef)?void 0:e.open)||t.call(e),null==(s=null==(i=this.$refs.fullscreenPreviewRef)?void 0:i.open)||s.call(i)})},onOpenTemplatePicker(){this.enable_templates&&this.fetchTemplates()},onOpenSaveTemplate(){this.enable_template_save&&(this.new_template_pop=!0)},onEditorSaveDraft(){this.triggerSave()},onEditorRecoveryNotice(){this.$notify.success(this.$t("Invalid blocks were auto-recovered and saved."))},onStyleConfigChange(e){if(this.campaign.settings||(this.campaign.settings={}),this.campaign.settings.template_config={...this.campaign.settings.template_config||{},...e},e&&Object.prototype.hasOwnProperty.call(e,"disable_footer")){const t="yes"===e.disable_footer?"yes":"no",i=this.normalizeFooterSettings({...this.footerSettings||{},disable_footer:t},this.campaign.settings.template_config||{});this.footerSettings=i,this.campaign.settings.footer_settings=i,this.syncLegacyDisableFooterFlag(i.disable_footer)}},onLayoutChange(e){if(this.campaign&&e&&this.email_template_designs&&this.email_template_designs[e]){this.campaign.design_template=e;const t=this.email_template_designs[e];t&&!t.use_gutenberg&&this.useAlternateEditor(e)}},initNewEditorObj(){if("post_content"===this.body_key){const e=this.campaign.id||this.campaign.ID||this.$route.params.template_id||0;this.newEditorObj.block_type="template",this.newEditorObj.content=this.campaign.post_content||"",this.newEditorObj.title=this.campaign.post_title||"",this.newEditorObj.id=e}else this.campaign.__fcrm_block_type?this.newEditorObj.block_type=this.campaign.__fcrm_block_type:"recurring_campaign"===this.campaign.type?this.newEditorObj.block_type="recurring_campaign":"recurring_mail"===this.campaign.type?this.newEditorObj.block_type="recurring_mail":"sequence_mail"===this.campaign.type?this.newEditorObj.block_type="sequence_mail":this.newEditorObj.block_type="campaign",this.newEditorObj.content=this.campaign.email_body||"",this.newEditorObj.title=this.campaign.title||"",this.newEditorObj.id=this.campaign.id||0,console.log("[BlockComposer] Initialized newEditorObj for block_type:",this.newEditorObj.block_type,"obj:",this.newEditorObj)},useAlternateEditor(e){console.log("useAlternateEditor"),this.unmountBlockEditor();const t=e||(this.selectedDesign&&!this.selectedDesign.use_gutenberg?this.campaign.design_template:"raw_classic");this.campaign.design_template=t,this.editor_status=!1,this.$nextTick(()=>{this.editor_status=!0})},contentChanged(e){this.campaign[this.email_body_key]=e,this.newEditorObj.content=e},handleAiEmailBodyInsert(e){const t=e.email_body||"",i=this.selectedDesign&&this.selectedDesign.use_gutenberg?this.normalizeGutenbergGeneratedContent(t):t;this.campaign[this.email_body_key]=i,this.newEditorObj.content=i,e.subject&&!this.campaign.email_subject&&(this.campaign.email_subject=e.subject),e.preview_text&&("post_content"!==this.body_key||this.campaign.post_excerpt?this.campaign.email_pre_header||(this.campaign.email_pre_header=e.preview_text):this.campaign.post_excerpt=e.preview_text),this.$notify.success(this.$t("AI generated email body inserted"))},normalizeGutenbergGeneratedContent(e){const t=String(e||"");return t.includes("\x3c!-- wp:")?t:this.convertHtmlToGutenbergBlocks(t)},convertHtmlToGutenbergBlocks(e){const t=document.createElement("div");t.innerHTML=this.$sanitize(e||"");const i=[],s=e=>e.innerHTML||e.textContent||"",o=(e,t,i="")=>((e,t="")=>"\x3c!-- wp:"+e+t+" --\x3e")(e,i)+t+(e=>"\x3c!-- /wp:"+e+" --\x3e")(e),a=e=>{const t=String(e||"").trim();t&&i.push(o("paragraph","

"+t+"

"))};return Array.from(t.childNodes).forEach(e=>{if(e.nodeType===Node.TEXT_NODE)return void a(e.textContent);if(e.nodeType!==Node.ELEMENT_NODE)return;const t=e.tagName.toLowerCase();if("h2"===t||"h3"===t){const a="h3"===t?3:2;i.push(o("heading","<"+t+">"+s(e)+"",' {"level":'+a+"}"))}else if("ul"===t||"ol"===t)i.push(o("list","<"+t+">"+s(e)+""));else if("p"===t)a(s(e));else{if("br"===t)return;a(s(e))}}),i.length?i.join("\n\n"):o("paragraph","

")},unmountBlockEditor(){this.showNewEditor=!1},triggerSave(){const e=this.normalizeFooterSettings(this.footerSettings,this.campaign.settings?this.campaign.settings.template_config:{});this.footerSettings=e,this.campaign.settings.footer_settings=e,this.syncLegacyDisableFooterFlag(e.disable_footer),this.$nextTick(()=>{this.$emit("save")})},resetTemplateImportModal(){this.templates=[],this.search="",this.importTemplateActiveTab="my_templates",this.query_data={sort_by:"ID",sort_type:"DESC"},this.pagination={...this.pagination,current_page:1,total:0}},fetchTemplates(){this.loading=!0,this.loadingTemplates=!0,this.templates_modal=!0,this.importTemplateActiveTab="my_templates";const e={per_page:this.pagination.per_page,page:this.pagination.current_page,search:this.search,orderBy:this.query_data.sort_by,order:this.query_data.sort_type};this.$get("templates",e).then(e=>{this.templates=e.templates.data,this.pagination.total=e.templates.total,this.loading=!1}).catch(e=>{console.log(e)}).finally(()=>{this.loadingTemplates=!1,this.loading=!1,this.editor_status=!0})},formatTemplateUpdatedAt(e){return this.nsHumanDiffTime(e.post_modified)||this.nsHumanDiffTime(e.post_date)||""},async InsertChange(e){if(e){if("object"==typeof e){return void(await this.insertTemplateWithWarning(e,"")&&(this.templates_modal=!1))}this.fetchingTemplate=!0;try{const t=await this.$get(`templates/${e}`);await this.insertTemplateWithWarning(t.template,e)&&(this.templates_modal=!1)}catch(t){this.handleError(t)}finally{this.fetchingTemplate=!1}}},getEditorFamily(e){var t;const i={visual_builder:"visual_builder",raw_html:"raw_html",raw_classic:"raw_classic"};if(i[e])return i[e];const s=null==(t=this.email_template_designs)?void 0:t[e];return s&&s.use_gutenberg?"gutenberg":e||""},getEditorFamilyLabel(e){return{gutenberg:this.$t("Gutenberg Editor"),visual_builder:this.$t("Visual Builder"),raw_html:this.$t("Raw HTML Editor"),raw_classic:this.$t("Classic Editor")}[e]||e},async confirmTemplateEditorChange(e){const t=this.getEditorFamily(this.campaign.design_template),i=e.design_template||"simple",s=this.getEditorFamily(i);if(!t||!s||t===s)return!0;const o=this.getEditorFamilyLabel(t),a=this.getEditorFamilyLabel(s),l=this.$t("This template uses %s. Importing it will switch this email from %s to %s. Continue?",a,o,a);try{return await this.$confirm(l,this.$t("Switch Email Editor?"),{confirmButtonText:this.$t("Continue"),cancelButtonText:this.$t("Cancel"),type:"warning"}),!0}catch(n){return!1}},async insertTemplateWithWarning(e,t){return!!(await this.confirmTemplateEditorChange(e))&&this.applyTemplateToCampaign(e,t)},applyTemplateToCampaign(e,t){if(this.disabled_templates&&this.disabled_templates[e.design_template])return this.$notify.error(this.$t("Email_Campaign_Insert_Error_Alert")),!1;this.campaign.settings||(this.campaign.settings={});const i=e.settings||{},s=e.design_template||"simple",o=this.getEditorFamily(s),a=i.template_config||{},l=this.normalizeFooterSettings(i.footer_settings,a),n="gutenberg"===o?this.normalizeGutenbergGeneratedContent(e.post_content||""):e.post_content||"";return this.campaign.template_id=t||"",this.campaign[this.email_body_key]=n,this.campaign.email_subject=e.email_subject||"",this.campaign.email_pre_header=e.post_excerpt||"",this.campaign.design_template=s,this.campaign.settings.template_config={...a,disable_footer:l.disable_footer},this.campaign.settings.footer_settings=l,this.footerSettings=l,e._visual_builder_design?this.campaign._visual_builder_design=e._visual_builder_design:this.campaign._visual_builder_design&&delete this.campaign._visual_builder_design,this.$emit("template_inserted",e),"gutenberg"===o&&(this.newEditorObj.content=n),"visual_builder"===o&&e._visual_builder_design&&this.$nextTick(()=>{var t,i;null==(i=null==(t=this.$refs.customEditorRef)?void 0:t.loadDesign)||i.call(t,e._visual_builder_design)}),this.$notify.success(this.$t("Template imported successfully")),!0},handleFixed(){if(this.disable_fixed)return;const e=this.$el&&this.$el.classList?this.$el:null;if(!!(!e||!e.closest(".fcrm_sticky_block_composer_page")))return;if(this.fixedScrollHandler||this.fixedResizeHandler)return;const t=this.$el&&this.$el.classList?this.$el:document.querySelector(".fluentcrm_visual_editor.fc_is_guten");if(!t||!t.classList.contains("fc_is_guten"))return;const i=t.parentElement;if(!i)return;const s=()=>document.contains(i)&&t.parentElement===i;let o=0,a=0,l=null;const n=()=>{s()?(o=(()=>{let e=0;const t=document.getElementById("wpadminbar");if(t){const i=t.getBoundingClientRect();i.bottom>0&&(e=Math.max(e,i.bottom))}return e?Math.round(e+8):window.innerWidth<=782?46:68})(),l=i.getBoundingClientRect(),a=l.top+(window.pageYOffset||document.documentElement.scrollTop||0)):l=null},r=()=>{t.classList.remove("fc_element_fixed"),t.style.removeProperty("position"),t.style.removeProperty("top"),t.style.removeProperty("left"),t.style.removeProperty("width"),t.style.removeProperty("z-index"),t.style.removeProperty("background"),t.style.removeProperty("transition"),s()&&i.style.removeProperty("min-height");const e=document.querySelector(".fcrm_topbar");e&&e.classList.remove("fcrm_has_fixed_composer")};let d=!1,c=!1;const m=()=>{if(this.isFullScreen)return void(d&&(r(),d=!1));if(!l)return void(d&&(r(),d=!1));const e=(window.pageYOffset||document.documentElement.scrollTop||0)>a-o;if(e!==d){if(e){t.classList.add("fc_element_fixed"),s()&&(i.style.minHeight=`${t.offsetHeight}px`),t.style.position="fixed",t.style.top=`${o}px`,t.style.left=`${l.left}px`,t.style.width=`${l.width}px`,t.style.zIndex="30",t.style.background="#ffffff",t.style.transition="top 220ms ease-out";const e=document.querySelector(".fcrm_topbar");e&&e.classList.add("fcrm_has_fixed_composer")}else r();d=e}else d&&(t.style.left=`${l.left}px`,t.style.width=`${l.width}px`)};this.fixedScrollHandler=()=>{c||(c=!0,this._fixedScrollRafId&&cancelAnimationFrame(this._fixedScrollRafId),this._fixedScrollRafId=requestAnimationFrame(()=>{this._fixedScrollRafId=null,c=!1,m()}))},this.fixedResizeHandler=()=>{c||(c=!0,this._fixedResizeRafId&&cancelAnimationFrame(this._fixedResizeRafId),this._fixedResizeRafId=requestAnimationFrame(()=>{this._fixedResizeRafId=null,c=!1,n(),m()}))},window.addEventListener("scroll",this.fixedScrollHandler,{passive:!0}),window.addEventListener("resize",this.fixedResizeHandler,{passive:!0}),this.$nextTick(()=>{n(),m(),this._fixedInitTimer=setTimeout(()=>{this._fixedInitTimer=null,n(),m()},300)})},showInserter(){const e=document.querySelector(".fce_inserter button.block-editor-inserter__toggle");e&&e.click()},handleSortable(e){"descending"===e.order?(this.query_data.sort_by=e.prop,this.query_data.sort_type="desc"):(this.query_data.sort_by=e.prop,this.query_data.sort_type="asc"),this.fetchTemplates()},saveAsTemplate(){if(!this.new_template_name)return this.$notify.error(this.$t("Please provide a template name")),!1;const e={post_content:this.campaign[this.email_body_key],email_subject:this.campaign.email_subject,post_excerpt:this.campaign.email_pre_header,design_template:this.campaign.design_template,settings:{template_config:this.campaign.settings.template_config,footer_settings:this.footerSettings},post_title:this.new_template_name,edit_type:"html"};this.selectedDesign&&this.selectedDesign.use_gutenberg&&(e.post_content=this.newEditorObj.content),"visual_builder"===this.campaign.design_template&&(e._visual_builder_design=this.campaign._visual_builder_design),this.saving_template=!0,this.$post("templates",{template:JSON.stringify(e)}).then(e=>{this.$notify.success(e.message),this.new_template_name="",this.new_template_pop=!1}).catch(e=>{this.handleError(e)}).finally(()=>{this.saving_template=!1})},showPreview(e){this.previewTemplateId=e.ID,this.$get(`templates/${this.previewTemplateId}`).then(e=>{this.email_template=e.template,this.showTemplatePreview=!0}).catch(e=>{console.log(e)}).finally(()=>{})},openBuiltinTemplateDrawer(){this.open_drawer=!0},openFullScreenEditor(){},toggleFullScreen(){if(!this.selectedDesign.use_gutenberg)return;this.isFullScreen=!this.isFullScreen;const e=this.$el,t=e.querySelector(".fc_visual_header");if(this.isFullScreen){this._prevScrollY=window.scrollY||window.pageYOffset||document.documentElement.scrollTop,this._fsHiddenSiblings=[];const i=e.parentElement;i&&Array.from(i.children).forEach(t=>{if(t!==e&&"none"!==t.style.display){const e=t.style.display||"";this._fsHiddenSiblings.push({el:t,display:e}),t.style.display="none"}});const s=document.getElementById("wpadminbar");s&&(this._wpAdminBarPrevDisplay=s.style.display||"",s.style.display="none"),this._fsBodyStyle={position:document.body.style.position||"",top:document.body.style.top||"",left:document.body.style.left||"",right:document.body.style.right||"",overflow:document.body.style.overflow||"",width:document.body.style.width||"",height:document.body.style.height||""},this._fsRootStyle={position:e.style.position||"",top:e.style.top||"",left:e.style.left||"",right:e.style.right||"",bottom:e.style.bottom||"",width:e.style.width||"",height:e.style.height||"",zIndex:e.style.zIndex||"",margin:e.style.margin||"",padding:e.style.padding||""},t&&(this._fsHeaderStyle=t.getAttribute("style")||"",t.style.width="100%",t.style.maxWidth="100%",t.style.left="0",t.style.right="0",t.style.position="relative"),e.style.position="fixed",e.style.top="0",e.style.left="0",e.style.right="0",e.style.bottom="0",e.style.width="100%",e.style.height="100%",e.style.zIndex="999999",e.style.margin="0",e.style.padding="0",document.body.style.position="fixed",document.body.style.top=`-${this._prevScrollY}px`,document.body.style.left="0",document.body.style.right="0",document.body.style.overflow="hidden",document.body.style.width="100%",document.body.style.height="100%",e.classList.add("fcrm_fullscreen_active"),document.body.classList.add("fcrm-editor-fullscreen"),document.documentElement.classList.add("fcrm-editor-fullscreen"),this.$nextTick(()=>{const t=e.querySelector(".iframe-container");t&&(this._fsIframeContainerStyle=t.style.height||"",t.style.height="calc(100vh - 50px)")})}else{this._fsHiddenSiblings&&(this._fsHiddenSiblings.forEach(e=>{e.display?e.el.style.display=e.display:e.el.style.removeProperty("display")}),this._fsHiddenSiblings=[]),this._fsRootStyle&&Object.keys(this._fsRootStyle).forEach(t=>{this._fsRootStyle[t]?e.style[t]=this._fsRootStyle[t]:e.style.removeProperty(t)}),e.classList.remove("fcrm_fullscreen_active"),document.body.classList.remove("fcrm-editor-fullscreen"),document.documentElement.classList.remove("fcrm-editor-fullscreen"),t&&(this._fsHeaderStyle&&"null"!==this._fsHeaderStyle&&""!==this._fsHeaderStyle?t.setAttribute("style",this._fsHeaderStyle):t.removeAttribute("style")),this._fsBodyStyle&&Object.keys(this._fsBodyStyle).forEach(e=>{this._fsBodyStyle[e]?document.body.style[e]=this._fsBodyStyle[e]:document.body.style.removeProperty(e)}),"number"==typeof this._prevScrollY&&window.scrollTo(0,this._prevScrollY);const i=document.getElementById("wpadminbar");if(i&&void 0!==this._wpAdminBarPrevDisplay&&(this._wpAdminBarPrevDisplay?i.style.display=this._wpAdminBarPrevDisplay:i.style.removeProperty("display")),void 0!==this._fsIframeContainerStyle){const t=e.querySelector(".iframe-container");t&&(this._fsIframeContainerStyle?t.style.height=this._fsIframeContainerStyle:t.style.removeProperty("height"))}this.$nextTick(()=>{setTimeout(()=>{try{this.handleFixed()}catch(e){}},10)})}}},beforeUnmount(){this._fixedScrollRafId&&(cancelAnimationFrame(this._fixedScrollRafId),this._fixedScrollRafId=null),this._fixedResizeRafId&&(cancelAnimationFrame(this._fixedResizeRafId),this._fixedResizeRafId=null),this._fixedInitTimer&&(clearTimeout(this._fixedInitTimer),this._fixedInitTimer=null),this.fixedScrollHandler&&(window.removeEventListener("scroll",this.fixedScrollHandler),this.fixedScrollHandler=null),this.fixedResizeHandler&&(window.removeEventListener("resize",this.fixedResizeHandler),this.fixedResizeHandler=null);const e=this.$el&&this.$el.classList?this.$el:null;if(e&&e.classList.contains("fc_element_fixed")){const t=e.parentElement;e.classList.remove("fc_element_fixed"),e.style.removeProperty("position"),e.style.removeProperty("top"),e.style.removeProperty("left"),e.style.removeProperty("width"),e.style.removeProperty("z-index"),e.style.removeProperty("background"),e.style.removeProperty("transition"),t&&t.style.removeProperty("min-height")}if(this.isFullScreen){this.isFullScreen=!1,this._fsHiddenSiblings&&(this._fsHiddenSiblings.forEach(e=>{e&&e.el&&(e.el.style.display=e.display??"")}),this._fsHiddenSiblings=null),this.$el&&this.$el.classList.remove("fcrm_fullscreen_active"),document.documentElement.classList.remove("fcrm-editor-fullscreen"),document.body.classList.remove("fcrm-editor-fullscreen"),this._fsBodyStyle&&Object.keys(this._fsBodyStyle).forEach(e=>{this._fsBodyStyle[e]?document.body.style[e]=this._fsBodyStyle[e]:document.body.style.removeProperty(e)});const e=document.getElementById("wpadminbar");e&&void 0!==this._wpAdminBarPrevDisplay&&(this._wpAdminBarPrevDisplay?e.style.display=this._wpAdminBarPrevDisplay:e.style.removeProperty("display"))}this.use_fullscreen_editor&&(document.documentElement.classList.remove("fcrm_custom_editor_page"),document.body.classList.remove("fcrm_custom_editor_page"))},mounted(){this.handleFixed(),this.updateCustomEditorPageClass(),this.restoreLayoutSidebarState()}},Be={class:"fc_editor_iframe_refs","aria-hidden":"true",style:{position:"absolute",top:"0","pointer-events":"none",visibility:"hidden"}},je={key:1,class:"fc_editor_fullscreen_refs","aria-hidden":"true",style:{position:"absolute",left:"-9999px",top:"0","pointer-events":"none",visibility:"hidden"}},Ve={class:"fcrm_block_composer_editor--header"},Ae={class:"fcrm_block_composer_editor--header-content"},Ne={class:"fcrm_block_composer_editor--header-title"},Ue={class:"fcrm_block_composer_editor--header-actions"},He={class:"icon"},ze={class:"el-dropdown-link fcrm_editor_more_link"},Me={key:0,class:"fcrm_input_hint",style:{"font-size":"90%"}},Ge=["disabled"],We={class:"fcrm_block_composer_editor--body"},qe={class:"fcrm_block_composer_editor--compose-body-row"},Ye={class:"fcrm_block_composer_editor--compose-body"},Je={key:0,class:"fc_composer_classic"},Ze={key:2,class:"fcrm_p_24"},Ke={key:3,class:"fc_composer_raw_hrml"},Qe={class:"fc_template_sidebar"},Xe={class:"fcrm_block_composer_editor--compose-sidebar-header"},et={class:"fcrm_block_composer_editor--compose-sidebar-header-title"},tt={class:"fcrm_block_composer_editor--compose-sidebar-header-actions"},it=["aria-label"],st={class:"fcrm_block_composer_editor--compose-sidebar-body"},ot={class:"fc_template_info"},at=["innerHTML"],lt={key:0,class:"fcrm_fixed_bottom_actions"},nt={key:0,class:"fc_complience_suggest"},rt={class:"fc_editor_warnning"},dt={class:"fcrm_import_template_tabs"},ct={key:0,class:"fcrm_import_template_tab_content"},mt={class:"fcrm_table_wrapper fcrm_import_template_table_wrapper"},pt={class:"fcrm_table_header"},_t={class:"fcrm_table_header_inner"},ht={class:"fcrm_table_header_inner_left"},gt={class:"icon"},ut={class:"fcrm_table_body"},ft=["onClick"],yt={class:"fcrm_template_type_icon"},bt={class:"fcrm-action-btns"},vt={class:"icon"},wt={key:1,class:"fcrm_import_template_tab_content"},Et={key:0,style:{"font-size":"90%","margin-top":"10px"}},St={key:3};const Tt=Y(Re,[["render",function(t,a,l,r,d,c){const m=D("NewEditorFrame"),p=D("email-preview"),_=D("email-style-editor"),h=D("ai-email-body-generator"),g=D("merge-codes"),u=D("Icons"),f=o,y=D("FolderOpened"),b=E,N=w,H=e,q=D("Plus"),Y=S,J=v,Z=k,K=D("wp-editor"),Q=D("PromoCard"),X=D("raw-editor"),ee=i,te=D("image-radio-tool-tip"),ie=x,se=s,oe=C,ae=$,le=D("pagination-bar"),ne=D("BuildInTemplatesList"),re=n,de=D("builtin-template-drawer"),ce=T;return F(),L("div",{class:B([{fc_is_guten:c.selectedDesign&&c.selectedDesign.use_gutenberg},"fluentcrm_visual_editor"])},[d.editor_status?(F(),L("div",{key:0,class:B(["fc_visual_body",{fcrm_not_guten:!c.selectedDesign.use_gutenberg,fcrm_has_fullscreen_bar:d.isFullScreen&&c.selectedDesign.use_gutenberg}])},[j("div",null,[c.selectedDesign.use_gutenberg?(F(),L(A,{key:0},[c.shouldRenderGutenbergEditor?(F(),O(m,{key:0,onUseAlternateEditor:a[0]||(a[0]=e=>c.useAlternateEditor()),editorParams:c.iframeEditorParams,frameHeight:c.editorIframeHeight,documentTitle:d.newEditorObj.title,templateConfig:l.campaign.settings?l.campaign.settings.template_config:{},footerSettings:c.resolvedFooterSettings,extra_tags:l.extra_tags,fallback_content:d.newEditorObj.content,onTitleUpdated:a[1]||(a[1]=e=>{d.newEditorObj.title=e}),onContentUpdated:c.contentChanged,onEditorNext:c.handleEditorNext,onEditorBack:c.handleEditorBack,onEditorFullscreenToggle:c.onEditorFullscreenToggle,onLayoutChange:c.onLayoutChange,onFooterSettingsUpdated:c.onFooterSettingsUpdated,onOpenEmailPreview:c.onOpenEmailPreview,onOpenTemplatePicker:c.onOpenTemplatePicker,onOpenSaveTemplate:c.onOpenSaveTemplate,onEditorSaveDraft:c.onEditorSaveDraft,onStyleConfigChange:c.onStyleConfigChange,onEditorRecoveryNotice:c.onEditorRecoveryNotice,modelValue:d.newEditorObj.content,"onUpdate:modelValue":a[2]||(a[2]=e=>d.newEditorObj.content=e)},null,8,["editorParams","frameHeight","documentTitle","templateConfig","footerSettings","extra_tags","fallback_content","onContentUpdated","onEditorNext","onEditorBack","onEditorFullscreenToggle","onLayoutChange","onFooterSettingsUpdated","onOpenEmailPreview","onOpenTemplatePicker","onOpenSaveTemplate","onEditorSaveDraft","onStyleConfigChange","onEditorRecoveryNotice","modelValue"])):I("",!0),j("div",Be,[P(p,{ref:"iframePreviewRef",show_audit:l.show_audit,campaign:l.campaign},null,8,["show_audit","campaign"])]),c.ft.email_style_settings&&l.use_fullscreen_editor&&l.campaign.settings?(F(),L("div",je,[P(_,{ref:"fullscreenStyleEditorRef",is_classic_editor:"classic_editor"===c.selectedDesign.template_type,footer_settings:d.footerSettings,template_config:l.campaign.settings.template_config,onSave:a[3]||(a[3]=e=>c.triggerSave())},null,8,["is_classic_editor","footer_settings","template_config"]),P(p,{ref:"fullscreenPreviewRef",show_audit:l.show_audit,campaign:l.campaign},null,8,["show_audit","campaign"])])):I("",!0)],64)):(F(),L(A,{key:1},[j("div",{class:B(["fc_design_template_"+c.selectedDesign.id+"_wrapper","fcrm_block_composer_editor_wrapper"])},[j("div",Ve,[j("div",Ae,[j("h3",Ne,R(t.$t("Email Body")),1)]),j("div",Ue,[!l.use_fullscreen_editor&&c.ft.email_style_settings&&l.campaign.settings?(F(),O(_,{key:0,is_classic_editor:"classic_editor"===c.selectedDesign.template_type,footer_settings:d.footerSettings,template_config:l.campaign.settings.template_config,onSave:a[4]||(a[4]=e=>c.triggerSave())},null,8,["is_classic_editor","footer_settings","template_config"])):I("",!0),P(p,{show_audit:l.show_audit,campaign:l.campaign},null,8,["show_audit","campaign"]),c.showAiEmailGenerator?(F(),O(h,{key:1,context:c.aiEmailContext,onInsert:c.handleAiEmailBodyInsert},null,8,["context","onInsert"])):I("",!0),l.show_merge?(F(),O(g,{key:2,extra_tags:l.extra_tags,button_text:"{ }"},null,8,["extra_tags"])):I("",!0),l.enable_templates?(F(),O(f,{key:3,title:t.$t("Use template"),size:"small",onClick:c.fetchTemplates},{default:V(()=>[j("span",He,[P(u,{"icon-name":"import"})]),U(" "+R(t.$t("Import Templates")),1)],void 0),_:1},8,["title","onClick"])):I("",!0),z(t.$slots,"fc_editor_actions",{},void 0,!0),l.enable_template_save||l.enable_templates||l.campaign.settings?(F(),O(Z,{key:4,trigger:"click"},{dropdown:V(()=>[P(J,null,{default:V(()=>[l.enable_templates?(F(),O(N,{key:0,onClick:c.fetchTemplates},{default:V(()=>[P(b,null,{default:V(()=>[P(y)],void 0,!0),_:1}),U(" "+R(t.$t("Import / Use templates")),1)],void 0,!0),_:1},8,["onClick"])):I("",!0),l.enable_template_save?(F(),O(N,{key:1},{default:V(()=>[P(Y,{placement:"right-start",width:"400",visible:d.new_template_pop,"onUpdate:visible":a[7]||(a[7]=e=>d.new_template_pop=e),trigger:"click"},{reference:V(()=>[M((F(),L("span",{disabled:d.saving_template},[P(b,null,{default:V(()=>[P(q)],void 0,!0),_:1}),U(" "+R(t.$t("Save as template")),1)],8,Ge)),[[ce,d.saving_template]])]),default:V(()=>[j("label",null,R(t.$t("Template Name")),1),P(H,{placeholder:t.$t("Template Name"),style:{margin:"10px 0"},type:"text",modelValue:d.new_template_name,"onUpdate:modelValue":a[5]||(a[5]=e=>d.new_template_name=e)},null,8,["placeholder","modelValue"]),M((F(),O(f,{disabled:d.saving_template,onClick:a[6]||(a[6]=e=>c.saveAsTemplate()),type:"primary",size:"small"},{default:V(()=>[U(R(t.$t("Save")),1)],void 0,!0),_:1},8,["disabled"])),[[ce,d.saving_template]]),"visual_builder"==l.campaign.design_template?(F(),L("p",Me,R(t.$t("Will be stored from your last saved email contents")),1)):I("",!0)],void 0,!0),_:1},8,["visible"])],void 0,!0),_:1})):I("",!0)],void 0,!0),_:1})]),default:V(()=>[j("span",ze,[P(u,{"icon-name":"more"})])],void 0),_:1})):I("",!0)])]),j("div",We,[j("div",qe,[j("div",Ye,["classic_editor"==c.selectedDesign.template_type?(F(),L("div",Je,[P(K,{height:350,extra_style:c.classic_styles,modelValue:l.campaign[d.email_body_key],"onUpdate:modelValue":a[8]||(a[8]=e=>l.campaign[d.email_body_key]=e),showSmartCodes:!1},null,8,["extra_style","modelValue"])])):"custom_component"==c.selectedDesign.template_type?(F(),L("div",{key:1,class:B("fc_composer_"+c.selectedDesign.id)},[(F(),O(G(c.selectedDesign.component),{ref:"customEditorRef",onSave:a[9]||(a[9]=e=>c.triggerSave()),modelValue:l.campaign[d.email_body_key],"onUpdate:modelValue":a[10]||(a[10]=e=>l.campaign[d.email_body_key]=e),extra_tags:l.extra_tags,campaign:l.campaign},null,40,["modelValue","extra_tags","campaign"]))],2)):"visual_builder_demo"==c.selectedDesign.template_type?(F(),L("div",Ze,[P(Q,{heading:t.$t("Build Email By Drag and Drop Visual Editor"),description:t.$t("Visual_Email_Builder_Alert"),"show-header-upgrade-icon":!1},null,8,["heading","description"])])):(F(),L("div",Ke,[P(X,{modelValue:l.campaign[d.email_body_key],"onUpdate:modelValue":a[11]||(a[11]=e=>l.campaign[d.email_body_key]=e),hide_smart_codes:!0},null,8,["modelValue"])]))]),j("div",{class:B(["fcrm_block_composer_editor--compose-sidebar",{fcrm_is_collapsed:d.isLayoutSidebarCollapsed}])},[j("div",Qe,[j("div",Xe,[j("div",et,R(t.$t("Select Editor")),1),j("div",tt,[P(ee,{content:d.isLayoutSidebarCollapsed?t.$t("Open Sidebar"):t.$t("Close Sidebar"),placement:"top"},{default:V(()=>[j("button",{type:"button",class:"fcrm_template_sidebar_toggle","aria-label":d.isLayoutSidebarCollapsed?t.$t("Open Sidebar"):t.$t("Close Sidebar"),onClick:a[12]||(a[12]=(...e)=>c.toggleLayoutSidebar&&c.toggleLayoutSidebar(...e))},[P(u,{"icon-name":"sidebar"})],8,it)],void 0),_:1},8,["content"])])]),j("div",st,[P(te,{boxWidth:60,boxHeight:50,field:{options:c.editorTemplateDesigns},modelValue:c.selectedEditorTemplateId,"onUpdate:modelValue":a[13]||(a[13]=e=>c.selectedEditorTemplateId=e)},null,8,["field","modelValue"]),j("div",ot,[j("div",{innerHTML:c.selectedDesign.template_info},null,8,at)])])])],2)])])],2),l.hideBackBtn&&l.hideNextBtn?I("",!0):(F(),L("div",lt,[l.hideBackBtn?I("",!0):(F(),O(f,{key:0,onClick:c.handleEditorBack},{default:V(()=>[U(R(t.$t("Back")),1)],void 0),_:1},8,["onClick"])),l.hideNextBtn?I("",!0):(F(),O(f,{key:1,type:"primary",onClick:c.handleEditorNext},{default:V(()=>[U(R(t.$t("Next")),1)],void 0),_:1},8,["onClick"]))]))],64))]),c.showFooterComplianceWarning?(F(),L("div",nt,[j("p",rt,[U(R(t.$t("Default footer has been disabled. Please include"))+" ",1),a[22]||(a[22]=j("code",null,"##crm.unsubscribe_url##",-1)),U(" "+R(t.$t("or"))+" ",1),a[23]||(a[23]=j("code",null,"##crm.manage_subscription_url##",-1)),U(" "+R(t.$t("in your email body for compliance")),1)]),j("div",null,[j("b",null,R(t.$t("Suggested text to include:")),1),a[24]||(a[24]=j("p",{innerHTML:"{{crm.unsubscribe_html|Unsubscribe}} | {{crm.manage_subscription_html|Manage Preference}}"},null,-1))])])):I("",!0)],2)):I("",!0),l.enable_templates?(F(),O(re,{key:1,"close-on-click-modal":!1,title:t.$t("Select Template"),modelValue:d.templates_modal,"onUpdate:modelValue":a[16]||(a[16]=e=>d.templates_modal=e),onClosed:c.resetTemplateImportModal,"append-to-body":!0,width:"60%",class:"fluentcrm_import_email_templates","modal-class":"fcrm_import_template_modal"},{default:V(()=>[j("div",dt,[P(se,{modelValue:d.importTemplateActiveTab,"onUpdate:modelValue":a[14]||(a[14]=e=>d.importTemplateActiveTab=e),class:"fcrm_import_template_tabs_nav"},{default:V(()=>[P(ie,{value:"my_templates"},{default:V(()=>[U(R(t.$t("My Templates")),1)],void 0,!0),_:1}),P(ie,{value:"crm_templates"},{default:V(()=>[U(R(t.$t("CRM Templates")),1)],void 0,!0),_:1})],void 0,!0),_:1},8,["modelValue"])]),"my_templates"===d.importTemplateActiveTab?(F(),L("div",ct,[j("div",mt,[j("div",pt,[j("div",_t,[j("div",ht,[P(H,{clearable:"",size:"small",modelValue:d.search,"onUpdate:modelValue":a[15]||(a[15]=e=>d.search=e),onClear:c.fetchTemplates,onKeyup:W(c.fetchTemplates,["enter"]),placeholder:t.$t("Type and Enter...")},{prefix:V(()=>[j("span",gt,[P(u,{"icon-name":"search"})])]),_:1},8,["modelValue","onClear","onKeyup","placeholder"])])])]),j("div",ut,[M((F(),O(ae,{"empty-text":t.$t("No Data Available"),data:d.templates,stripe:"",onSortChange:c.handleSortable,border:"",style:{width:"100%"}},{default:V(()=>[P(oe,{prop:"post_title",label:t.$t("Title"),sortable:"custom"},{default:V(e=>[j("h3",{class:"template-name",onClick:t=>c.InsertChange(e.row.ID)},[j("span",yt,["visual_builder"==e.row.design_template?(F(),O(u,{key:0,"icon-name":"visualBuilder"})):"raw_classic"==e.row.design_template?(F(),O(u,{key:1,"icon-name":"classicEditor"})):"raw_html"==e.row.design_template?(F(),O(u,{key:2,"icon-name":"rawHTML"})):(F(),O(u,{key:3,"icon-name":"gutenberg"}))]),U(" "+R(e.row.post_title),1)],8,ft)]),_:1},8,["label"]),I("",!0),P(oe,{width:"150",prop:"post_modified",label:t.$t("Last Modified"),sortable:"custom"},{default:V(e=>[U(R(c.formatTemplateUpdatedAt(e.row)),1)]),_:1},8,["label"]),P(oe,{width:"200",align:"right"},{default:V(e=>[j("div",bt,[P(f,{loading:d.fetchingTemplate,disabled:d.fetchingTemplate,onClick:t=>c.InsertChange(e.row.ID),size:"small"},{default:V(()=>[U(R(t.$t("Use Template")),1)],void 0,!0),_:1},8,["loading","disabled","onClick"]),P(f,{onClick:t=>c.showPreview(e.row),class:"only-icon-btn small","aria-label":t.$t("Preview Template"),title:t.$t("Preview Template")},{default:V(()=>[j("span",vt,[P(u,{"icon-name":"eye"})])],void 0,!0),_:1},8,["onClick","aria-label","title"])])]),_:1})],void 0,!0),_:1},8,["empty-text","data","onSortChange"])),[[ce,d.loading]])])]),P(le,{pagination:d.pagination,onFetch:c.fetchTemplates},null,8,["pagination","onFetch"])])):(F(),L("div",wt,[P(ne,{"insert-mode":!0,redirect_on_import:!1,onTemplateImported:c.InsertChange},null,8,["onTemplateImported"])]))],void 0),_:1},8,["title","modelValue","onClosed"])):I("",!0),l.enable_template_save&&c.selectedDesign&&c.selectedDesign.use_gutenberg?(F(),O(re,{key:2,title:t.$t("Save as template"),modelValue:d.new_template_pop,"onUpdate:modelValue":a[19]||(a[19]=e=>d.new_template_pop=e),width:"460px","append-to-body":!0,"close-on-click-modal":!1,class:"fluentcrm_save_as_template_dialog"},{default:V(()=>[j("label",null,R(t.$t("Template Name")),1),P(H,{placeholder:t.$t("Template Name"),style:{margin:"10px 0"},type:"text",modelValue:d.new_template_name,"onUpdate:modelValue":a[17]||(a[17]=e=>d.new_template_name=e)},null,8,["placeholder","modelValue"]),M((F(),O(f,{disabled:d.saving_template,onClick:a[18]||(a[18]=e=>c.saveAsTemplate()),type:"primary"},{default:V(()=>[U(R(t.$t("Save")),1)],void 0,!0),_:1},8,["disabled"])),[[ce,d.saving_template]]),"visual_builder"==l.campaign.design_template?(F(),L("p",Et,R(t.$t("Will be stored from your last saved email contents")),1)):I("",!0)],void 0),_:1},8,["title","modelValue"])):I("",!0),d.showTemplatePreview?(F(),L("div",St,[P(p,{onModalClosed:a[20]||(a[20]=()=>{d.showTemplatePreview=!1}),auto_load:!0,show_audit:!0,campaign:d.email_template},null,8,["campaign"])])):I("",!0),P(de,{open_drawer:d.open_drawer,"onUpdate:open_drawer":a[21]||(a[21]=e=>d.open_drawer=e)},null,8,["open_drawer"])],2)}],["__scopeId","data-v-2f9efcf8"]]);export{Tt as E}; diff --git a/wp-content/plugins/fluent-crm/assets/BuiltinTemplateDrawer.js b/wp-content/plugins/fluent-crm/assets/BuiltinTemplateDrawer.js new file mode 100644 index 0000000..db6c602 --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/BuiltinTemplateDrawer.js @@ -0,0 +1 @@ +import{aC as e,aB as t,ay as a,k as i,ba as s,g as l,U as n,L as r,E as o,aT as m}from"./vendor-element-plus.js?ver=3.1.8";import{W as d,X as c,ab as p,a5 as _,Z as h,aQ as u,Y as g,a8 as f,J as w,az as b,aa as y,a6 as T,a9 as I,a0 as v}from"./vendor.js?ver=3.1.8";import{_ as $,I as k}from"./fc-bits-ui.js?ver=3.1.8";import{P as B}from"./PromoCard.js?ver=3.1.8";const L={class:"build-in-template-loader"},x={class:"fcrm_build_in_templates_item skeleton-box"},S={class:"fcrm_build_in_templates_item--image",style:{padding:"15px",height:"152px"}},C={class:"fcrm_build_in_templates_item--content"},P={class:"fcrm_build_in_templates_item--title"};const D={name:"_BuildInTemplatesList",components:{PromoCard:B,BuildInTemplateLoader:$({name:"BuildInTemplateLoader"},[["render",function(a,i,s,l,n,r){const o=e,m=t;return d(),c("div",L,[p(m,{animated:"",count:8,style:{padding:"0"},class:"fcrm_build_in_templates_list"},{template:_(()=>[h("div",x,[h("div",S,[p(o,{animated:"",variant:"image",style:{width:"100%",height:"100%"}})]),h("div",C,[h("div",P,[p(o,{animated:"",variant:"text",style:{width:"80%"}})])])])]),_:1})])}]]),Icons:k},props:{insertMode:{type:Boolean,default:!1},redirect_on_import:{type:Boolean,default:!0}},emits:["template-imported"],data:()=>({builtInTemplates:[],isLoadingTemplates:!1,importing:!1,templateIdImporting:null,oneTimeFetch:!0,showProModal:!1}),methods:{fetchBuiltInTemplates(){this.oneTimeFetch&&(this.oneTimeFetch=!1,this.isLoadingTemplates=!0,this.$get("templates/built-in-templates").then(e=>{this.builtInTemplates=e.templates}).catch(e=>{this.handleError(e)}).finally(()=>{this.isLoadingTemplates=!1}))},importTemplate(e){this.templateIdImporting=e.id,this.importing=!0,!this.insertMode&&this.redirect_on_import?window.jQuery.post(window.ajaxurl,{body:{file:e.content},action:"fluentcrm_import_template"}).then(e=>{this.$notify.success(e.message),this.$router.push({name:"edit_template",params:{template_id:e.template_id}})}).catch(e=>{this.handleError(e.responseJSON.message)}).always(()=>{this.templateIdImporting=null,this.importing=!1}):this.$post("templates/built-in-template",{file:e.content}).then(e=>{this.$emit("template-imported",e.template)}).catch(e=>{this.handleError(e)}).finally(()=>{this.templateIdImporting=null,this.importing=!1})}},mounted(){this.fetchBuiltInTemplates()}},E={key:1,class:"fcrm_build_in_templates_list"},V={class:"fcrm_build_in_templates_item--image"},j=["src","alt"],F={key:1},M={class:"fcrm_build_in_templates_item--actions"},z=["href"],N={class:"icon"},U={class:"icon"},J={class:"icon"},A={class:"fcrm_build_in_templates_item--content"},O={class:"fcrm_build_in_templates_item--title"};const Q=$(D,[["render",function(e,t,n,r,o,m){const v=u("BuildInTemplateLoader"),$=u("Icons"),k=i,B=s,L=u("PromoCard"),x=l,S=a;return d(),c("div",null,[o.isLoadingTemplates?(d(),g(v,{key:0})):f("",!0),!o.isLoadingTemplates&&o.builtInTemplates.length?(d(),c("div",E,[(d(!0),c(w,null,b(o.builtInTemplates,a=>(d(),c("div",{key:a.id,class:"fcrm_build_in_templates_item"},[h("div",V,[a.cover_image?(d(),c("img",{key:0,src:a.cover_image,alt:a.title},null,8,j)):(d(),c("p",F,y(e.$t("Empty")),1)),h("div",M,[h("a",{href:a.link,class:"el-button preview-btn el-button--small",target:"_blank",rel:"noopener noreferrer"},[h("span",N,[p($,{"icon-name":"eye"})])],8,z),e.has_campaign_pro?T((d(),g(k,{key:0,size:"small",onClick:e=>m.importTemplate(a),disabled:o.importing},{default:_(()=>[h("span",U,[p($,{"icon-name":"import"})]),I(" "+y(e.$t("Import")),1)],void 0),_:1},8,["onClick","disabled"])),[[S,o.importing&&o.templateIdImporting==a.id]]):(d(),g(k,{key:1,size:"small",onClick:t[0]||(t[0]=e=>o.showProModal=!0)},{default:_(()=>[h("span",J,[p($,{"icon-name":"import"})]),I(" "+y(e.$t("Import")),1)],void 0),_:1}))])]),h("div",A,[h("div",O,y(a.title),1)])]))),128))])):f("",!0),o.isLoadingTemplates||o.builtInTemplates.length?f("",!0):(d(),g(B,{key:2,description:e.$t("No Built In Templates Found")},null,8,["description"])),p(x,{modelValue:o.showProModal,"onUpdate:modelValue":t[1]||(t[1]=e=>o.showProModal=e),title:e.$t("Importing Template"),width:"40%"},{default:_(()=>[p(L,{"show-header-upgrade-icon":!1})],void 0),_:1},8,["modelValue","title"])])}]]),Y={name:"BuiltinTemplateDrawer",components:{Icons:k,BuildInTemplatesList:Q,Plus:r,Loading:n},emits:["update:open_drawer"],props:{open_drawer:{type:Boolean,default:()=>!1},create_mode:{type:Boolean,default:()=>!1}},data:()=>({direction:"rtl",show_drawer:!1,creatingScratch:!1,builtInTemplates:[],isLoadingTemplates:!1,importing:!1,templateIdImporting:null,oneTimeFetch:!0,showTemplatePreview:!1,previewTemplateId:"",email_template:{post_title:"",post_content:"",post_excerpt:"",email_subject:"",edit_type:"html",design_template:"simple",settings:{template_config:{}}}}),watch:{open_drawer(e){this.show_drawer=!!e}},computed:{localDrawerVisible:{get(){return this.show_drawer},set(e){this.show_drawer=e,e||this.$emit("update:open_drawer",!1)}}},mounted(){window.fcAdmin&&window.fcAdmin.is_rtl&&(this.direction="ltr")},methods:{getDefaultTemplateTitle(){const e=new Date,t=e=>String(e).padStart(2,"0");return`Untitled - Created at ${`${e.getFullYear()}-${t(e.getMonth()+1)}-${t(e.getDate())} ${t(e.getHours())}:${t(e.getMinutes())}:${t(e.getSeconds())}`}`},createFromStratch(){if(this.creatingScratch)return;this.creatingScratch=!0;const e={post_title:this.getDefaultTemplateTitle(),post_content:" ",post_excerpt:"",email_subject:"",edit_type:"html",design_template:"simple",settings:{template_config:{}}};this.$post("templates",{template:JSON.stringify(e)}).then(e=>{const t=e&&e.template_id?e.template_id:0;if(!t)throw new Error(this.$t("Could not create template"));this.$emit("update:open_drawer",!1),this.show_drawer=!1,this.$router.push({name:"edit_template",params:{template_id:t},query:{is_new:"yes"}})}).catch(e=>{this.handleError?this.handleError(e):this.$notify.error(e.message||this.$t("Could not create template"))}).finally(()=>{this.creatingScratch=!1})}}},q={class:"fc_built_in_templates"},H={class:"icon"},W={class:"fcrm_create_from_scratch_box--content"},X={class:"fcrm_create_from_scratch_box--title fcrm_primary_text font-medium fcrm_mb_4"},Z={class:"fcrm_create_from_scratch_box--desc fcrm_secondary_text small"};const G=$(Y,[["render",function(e,t,a,i,s,l){const n=u("Icons"),r=u("Loading"),w=o,b=u("BuildInTemplatesList"),T=m;return d(),g(T,{direction:s.direction,title:a.create_mode?e.$t("Create New Email Template"):e.$t("Built In Templates"),"append-to-body":!0,"close-on-click-modal":!1,modelValue:l.localDrawerVisible,"onUpdate:modelValue":t[1]||(t[1]=e=>l.localDrawerVisible=e),size:"55%"},{default:_(()=>[h("div",q,[a.create_mode?(d(),c("div",{key:0,class:v(["fcrm_create_from_scratch_box",{"is-creating":s.creatingScratch}]),onClick:t[0]||(t[0]=e=>l.createFromStratch())},[h("span",H,[s.creatingScratch?(d(),g(w,{key:1,class:"is-loading"},{default:_(()=>[p(r)],void 0,!0),_:1})):(d(),g(n,{key:0,"icon-name":"plus"}))]),h("div",W,[h("div",X,y(e.$t("Create from Scratch")),1),h("div",Z,y(e.$t("Design your email template from a blank canvas.")),1)])],2)):f("",!0),p(b)])],void 0),_:1},8,["direction","title","modelValue"])}],["__scopeId","data-v-0cc3b88c"]]);export{G as B,Q as a}; diff --git a/wp-content/plugins/fluent-crm/assets/CalendarIcon.js b/wp-content/plugins/fluent-crm/assets/CalendarIcon.js new file mode 100644 index 0000000..e8acc4d --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/CalendarIcon.js @@ -0,0 +1 @@ +import{W as r,X as n,Z as o}from"./vendor.js?ver=3.1.8";import{_ as t}from"./fc-bits-ui.js?ver=3.1.8";const H={width:"20",height:"20",viewBox:"0 0 20 20",fill:"none",xmlns:"http://www.w3.org/2000/svg"};const V=t({name:"CalendarIcon"},[["render",function(t,V,e,s,C,i){return r(),n("svg",H,[...V[0]||(V[0]=[o("path",{d:"M13.75 3.25H16.75C16.9489 3.25 17.1397 3.32902 17.2803 3.46967C17.421 3.61032 17.5 3.80109 17.5 4V16C17.5 16.1989 17.421 16.3897 17.2803 16.5303C17.1397 16.671 16.9489 16.75 16.75 16.75H3.25C3.05109 16.75 2.86032 16.671 2.71967 16.5303C2.57902 16.3897 2.5 16.1989 2.5 16V4C2.5 3.80109 2.57902 3.61032 2.71967 3.46967C2.86032 3.32902 3.05109 3.25 3.25 3.25H6.25V1.75H7.75V3.25H12.25V1.75H13.75V3.25ZM12.25 4.75H7.75V6.25H6.25V4.75H4V7.75H16V4.75H13.75V6.25H12.25V4.75ZM16 9.25H4V15.25H16V9.25Z",fill:"currentColor"},null,-1)])])}]]);export{V as C}; diff --git a/wp-content/plugins/fluent-crm/assets/CampaignSubjectLines.js b/wp-content/plugins/fluent-crm/assets/CampaignSubjectLines.js new file mode 100644 index 0000000..4d5f18e --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/CampaignSubjectLines.js @@ -0,0 +1 @@ +import{W as a,X as e,J as s,az as t,Z as c,aa as i,a8 as r,a9 as n}from"./vendor.js?ver=3.1.8";import{_ as m}from"./fc-bits-ui.js?ver=3.1.8";const p={name:"CampaignSubjectLines",props:{campaign:{type:Object,required:!0},showPriority:{type:Boolean,default:!0}},computed:{activeSubjects(){return(this.campaign.subjects||[]).filter(a=>a&&a.value&&a.value.trim())}}},u={class:"fcrm_campaign_subject_lines"},o={class:"fcrm_campaign_subject_lines--value"},l={key:0,class:"fcrm_campaign_subject_lines--priority"};const _=m(p,[["render",function(m,p,_,j,b,f){return a(),e("span",u,[f.activeSubjects.length?(a(!0),e(s,{key:0},t(f.activeSubjects,(s,t)=>(a(),e("span",{key:s.id||t,class:"fcrm_campaign_subject_lines--item"},[c("span",o,i(s.value),1),_.showPriority?(a(),e("span",l,i(s.key)+"% ",1)):r("",!0)]))),128)):(a(),e(s,{key:1},[n(i(_.campaign.email_subject||"--"),1)],64))])}]]);export{_ as C}; diff --git a/wp-content/plugins/fluent-crm/assets/CompanyEditForm.js b/wp-content/plugins/fluent-crm/assets/CompanyEditForm.js new file mode 100644 index 0000000..c6f5cc9 --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/CompanyEditForm.js @@ -0,0 +1 @@ +import{_ as e}from"./fc-bits.js?ver=3.1.8";import{a3 as l,k as a,e as o,aw as t,aE as d,aF as s,aK as i,aL as c,aG as m,az as n,aY as r,ax as u,E as p,aT as _,ay as f,aD as h,aA as y}from"./vendor-element-plus.js?ver=3.1.8";import{c2 as v,bW as g,aQ as w,W as b,X as V,_ as $,ab as k,a5 as C,Z as M,a9 as x,aa as U,a8 as N,Y as G,J as A,az as F,a0 as E,b2 as L,a6 as S,$ as H}from"./vendor.js?ver=3.1.8";import{_ as O,I as j}from"./fc-bits-ui.js?ver=3.1.8";const I=g(()=>e(()=>import("./v3app/src/Modules/Settings/parts/CustomContactFields.js?ver=3.1.8"),[],import.meta.url)),P={name:"ProfileCustomFields",props:{custom_values:{type:Object,required:!0},show_header:{type:Boolean,default:!0}},components:{CustomFields:I,Edit:l,Icons:j},data(){return{direction:"rtl",app_ready:!1,showingCustomFieldsConfig:!1,custom_fields:this.appVars.company_custom_fields,updatedGroupName:"",editingGroupName:null,updatingGroupName:!1}},computed:{fieldGroups(){const e={};return this.each(this.custom_fields,l=>{v(this.custom_values,l.slug)&&(l.is_disabled=!0),l.group||(l.group=""),e[l.group]||(e[l.group]={}),e[l.group][l.slug]=l}),e}},methods:{clickGroupNameToEdit(e){this.updatedGroupName=e,this.editingGroupName=e,this.$nextTick(()=>{const e=Array.isArray(this.$refs.groupNameInput)?this.$refs.groupNameInput[0]:this.$refs.groupNameInput;e&&e.focus&&e.focus()})},handleGroupNameUpdate(){if(this.updatingGroupName)return;const e=this.editingGroupName,l=this.updatedGroupName.trim();if(!l||l===e)return this.updatedGroupName="",void(this.editingGroupName=null);this.updatingGroupName=!0,this.$put("companies/custom-fields/update_group_name",{old_name:e,new_name:l}).then(a=>{const o=Array.isArray(a.fields)?a.fields:this.custom_fields.map(a=>a.group===e?{...a,group:l}:a);this.custom_fields=o,this.appVars.company_custom_fields=o,this.$notify.success({title:this.$t("Great!"),message:a.message,offset:19})}).catch(e=>{this.handleError(e)}).finally(()=>{this.updatedGroupName="",this.editingGroupName=null,this.updatingGroupName=!1})},openConfig(){this.showingCustomFieldsConfig=!0},onFieldsUpdated(e){this.custom_fields=e,this.appVars.company_custom_fields=e}},mounted(){window.fcAdmin&&window.fcAdmin.is_rtl&&(this.direction="ltr"),this.each(this.custom_fields,e=>{let l="";-1!==["select-multi","checkbox"].indexOf(e.type)&&(l=[]),v(this.custom_values,e.slug)||(this.custom_values[e.slug]=l)}),this.app_ready=!0}},T={class:"fcrm_custom_field_wrapper fcrm_company_custom_field_wrapper"},Y={key:0,class:"fcrm_custom_data_header"},B={class:"icon"},R={key:1,class:"fluentcrm_custom_fields fcrm_custom_data_fields"},z={key:0,class:"fcrm_custom_data_group_head"},D={key:0},q=["onClick"],W={class:"fcrm_custom_data_grid"},Z={key:2,class:"fcrm_no_custom_fields_btn fcrm_custom_data_empty"};const K=O(P,[["render",function(e,l,f,h,y,v){const g=w("Icons"),S=a,H=o,O=s,j=d,I=c,P=i,K=n,J=m,Q=r,X=t,ee=u,le=w("Edit"),ae=p,oe=w("custom-fields"),te=_;return b(),V("div",T,[f.show_header?(b(),V("div",Y,[$(e.$slots,"header-title"),k(S,{link:"",onClick:l[0]||(l[0]=e=>v.openConfig())},{default:C(()=>[M("span",B,[k(g,{"icon-name":"EditPen"})]),x(" "+U(y.custom_fields.length?e.$t("Configure"):e.$t("Add Custom Field")),1)],void 0),_:1})])):N("",!0),y.custom_fields.length?(b(),V("div",R,[y.app_ready?(b(),G(ee,{key:0,model:f.custom_values,"label-position":"top",class:"fcrm_custom_data_form"},{default:C(()=>[(b(!0),V(A,null,F(v.fieldGroups,(a,o)=>(b(),V("section",{key:o||"ungrouped",class:E([{fcrm_custom_data_group_plain:!o},"fcrm_custom_data_group"])},[o?(b(),V("div",z,[y.editingGroupName!==o?(b(),V("h4",D,[x(U(o)+" ",1),M("span",{class:"icon cursor_pointer",onClick:e=>v.clickGroupNameToEdit(o)},[k(g,{"icon-name":"EditPen"})],8,q)])):(b(),G(H,{key:1,ref_for:!0,ref:"groupNameInput",modelValue:y.updatedGroupName,"onUpdate:modelValue":l[1]||(l[1]=e=>y.updatedGroupName=e),type:"text",onBlur:v.handleGroupNameUpdate,onKeyup:L(v.handleGroupNameUpdate,["enter"])},null,8,["modelValue","onBlur","onKeyup"])),M("span",null,U(e.$_n("%d field","%d fields",Object.keys(a).length)),1)])):N("",!0),M("div",W,[(b(!0),V(A,null,F(a,(l,a)=>(b(),G(X,{key:a,label:l.label,class:"fcrm_custom_data_item"},{default:C(()=>["text"==l.type||"number"==l.type||"textarea"==l.type?(b(),G(H,{key:0,placeholder:l.label,rows:"textarea"==l.type?2:void 0,type:l.type,modelValue:f.custom_values[a],"onUpdate:modelValue":e=>f.custom_values[a]=e},null,8,["placeholder","rows","type","modelValue","onUpdate:modelValue"])):"radio"==l.type?(b(),G(j,{key:1,modelValue:f.custom_values[a],"onUpdate:modelValue":e=>f.custom_values[a]=e,class:"fcrm_custom_data_options"},{default:C(()=>[(b(!0),V(A,null,F(l.options,e=>(b(),G(O,{key:e,value:e},{default:C(()=>[x(U(e),1)],void 0,!0),_:2},1032,["value"]))),128))],void 0,!0),_:2},1032,["modelValue","onUpdate:modelValue"])):"select-one"==l.type||"select-multi"==l.type?(b(),G(P,{key:2,placeholder:e.$t("Select")+" "+l.label,clearable:"",filterable:"",multiple:"select-multi"==l.type,modelValue:f.custom_values[a],"onUpdate:modelValue":e=>f.custom_values[a]=e},{default:C(()=>[(b(!0),V(A,null,F(l.options,e=>(b(),G(I,{key:e,value:e,label:e},null,8,["value","label"]))),128))],void 0,!0),_:2},1032,["placeholder","multiple","modelValue","onUpdate:modelValue"])):"checkbox"==l.type?(b(),G(J,{key:3,modelValue:f.custom_values[a],"onUpdate:modelValue":e=>f.custom_values[a]=e,class:"fcrm_custom_data_options"},{default:C(()=>[(b(!0),V(A,null,F(l.options,e=>(b(),G(K,{key:e,value:e},{default:C(()=>[x(U(e),1)],void 0,!0),_:2},1032,["value"]))),128))],void 0,!0),_:2},1032,["modelValue","onUpdate:modelValue"])):"date"==l.type?(b(),G(Q,{key:4,"value-format":"YYYY-MM-DD",modelValue:f.custom_values[a],"onUpdate:modelValue":e=>f.custom_values[a]=e,type:"date",placeholder:e.$t("Pick a date")},null,8,["modelValue","onUpdate:modelValue","placeholder"])):"date_time"==l.type?(b(),G(Q,{key:5,"value-format":"YYYY-MM-DD HH:mm:ss",modelValue:f.custom_values[a],"onUpdate:modelValue":e=>f.custom_values[a]=e,type:"datetime",placeholder:e.$t("Pick a date and time")},null,8,["modelValue","onUpdate:modelValue","placeholder"])):(b(),V(A,{key:6},[x(U(l),1)],64))],void 0,!0),_:2},1032,["label"]))),128))])],2))),128))],void 0),_:1},8,["model"])):N("",!0)])):(b(),V("div",Z,[M("p",null,U(e.$t("No custom fields configured for companies yet.")),1),f.show_header?(b(),G(S,{key:0,onClick:l[2]||(l[2]=e=>v.openConfig())},{default:C(()=>[k(ae,null,{default:C(()=>[k(le)],void 0,!0),_:1}),M("span",null,U(e.$t("Add Custom Field")),1)],void 0),_:1})):N("",!0)])),k(te,{direction:y.direction,"append-to-body":!0,title:e.$t("Configure Company Custom Field"),modelValue:y.showingCustomFieldsConfig,"onUpdate:modelValue":l[3]||(l[3]=e=>y.showingCustomFieldsConfig=e),size:"50%"},{default:C(()=>[y.showingCustomFieldsConfig?(b(),G(oe,{key:0,is_pop:!0,default_tab:"companies",onFieldsUpdated:v.onFieldsUpdated},null,8,["onFieldsUpdated"])):N("",!0)],void 0),_:1},8,["direction","title","modelValue"])])}]]),J={name:"photo_widget",props:{modelValue:{type:String,default:""},value:{type:String,default:""},btn_mode:{type:Boolean,default:()=>!1},btn_text:{type:String,default:()=>"+ Upload"},btn_type:{type:String,default:()=>"default"}},emits:["update:modelValue","input","changed","update:value"],data:()=>({app_ready:!1}),computed:{displayValue(){return this.modelValue||this.value||""}},methods:{initUploader(){var e;const l=null==(e=null==window?void 0:window.wp)?void 0:e.media,a=null==l?void 0:l.editor;if(!(a&&"function"==typeof a.open&&l&&l.model&&l.view))return console.warn("PhotoWidget: wp.media.editor is not available. Ensure wp_enqueue_media() is called on the page."),!1;const o=a.send.attachment;a.send.attachment=(e,l)=>{if(l&&l.url){const e=l.url;this.$emit("update:modelValue",e),this.$emit("update:value",e),this.$emit("input",e),this.$emit("changed",e)}a.send.attachment=o};const t=window.wpActiveEditor;window.wpActiveEditor="photo_widget",a.open();const d=a.frame;return d&&"function"==typeof d.on?d.on("close",()=>{window.wpActiveEditor=t}):window.wpActiveEditor=t,!1},getThumb:e=>e.url},mounted(){this.app_ready=!0}},Q={class:"fcrm_fluentcrm_photo_card"},X={key:0,class:"fcrm_fluentcrm_photo_holder"},ee={key:0,class:"fcrm_photo_image_wrapper"},le=["src"];const ae={class:"fcrm_company_summary_shared"},oe={class:"fcrm_company_summary_logo_section"},te={class:"fcrm_logo_placeholder"},de={key:1,class:"fcrm_logo_empty"},se={class:"fcrm_logo_info"},ie={class:"fcrm_logo_title"},ce={class:"fcrm_logo_subtitle"},me={class:"fcrm_company_summary_section"},ne={class:"fcrm_company_section_divider"},re={class:"fcrm_company_summary_section"},ue={class:"fcrm_company_section_divider"};const pe={class:"fcrm_company_edit_shared"},_e={class:"fcrm_company_edit_section"},fe={key:0,class:"fcrm_company_optional_toggle"},he={key:1,class:"fcrm_company_section_divider"},ye={class:"fcrm_secondary_text font-regular"},ve=["innerHTML"],ge=["innerHTML"],we={class:"fcrm_company_edit_section"},be={key:0,class:"fcrm_company_optional_toggle"},Ve={key:1,class:"fcrm_company_section_divider"},$e={key:2,class:"fcrm_company_edit_social_grid"},ke={key:0,class:"fcrm_company_edit_section fcrm_company_custom_section"};const Ce=O({name:"CompanyEditForm",components:{CompanySummaryForm:O({name:"CompanySummaryForm",components:{PhotoWidget:O(J,[["render",function(e,l,o,t,d,s){const i=a;return b(),V("div",Q,[d.app_ready?(b(),V("div",X,[s.displayValue&&!o.btn_mode?(b(),V("div",ee,[M("img",{src:s.displayValue,class:"fcrm_photo_image"},null,8,le)])):N("",!0),o.btn_mode?(b(),G(i,{key:1,size:"small",onClick:s.initUploader,type:o.btn_type},{default:C(()=>[x(U(o.btn_text),1)],void 0),_:1},8,["onClick","type"])):N("",!0),$(e.$slots,"after")])):N("",!0)])}]]),ContactSelector:O({name:"ContactSelector",props:["field","modelValue"],emits:["contactSelected","update:modelValue"],data(){return{model:this.modelValue,loading:!1,options:{},appReady:!1}},watch:{model(e){this.$emit("update:modelValue",e),this.$emit("contactSelected",this.options[e])}},methods:{fetchOptions(e){this.loading=!0,this.$get("subscribers/search-contacts",{search:e,values:this.model}).then(e=>{this.options=e.contacts}).catch(e=>{this.handleError(e)}).finally(()=>{this.loading=!1})}},mounted(){this.model&&"0"!=this.model||this.field.is_multiple||(this.model=""),this.field.pre_options&&this.field.pre_options.length&&this.each(this.field.pre_options,e=>{e&&e.id&&(e.id=e.id.toString(),this.options[e.id]=e)}),this.appReady=!0,this.model&&"object"!=typeof this.model&&!this.options[this.model]&&this.fetchOptions("")}},[["render",function(e,l,a,o,t,d){const s=c,m=i,n=f;return t.appReady?S((b(),G(m,{key:0,modelValue:t.model,"onUpdate:modelValue":l[0]||(l[0]=e=>t.model=e),multiple:a.field.is_multiple,filterable:"",remote:!a.field.cacheable,clearable:a.field.clearable,disabled:a.field.disabled,"reserve-keyword":"",size:a.field.size,placeholder:a.field.placeholder||e.$t("Search contact"),"remote-method":d.fetchOptions},{default:C(()=>[(b(!0),V(A,null,F(t.options,e=>(b(),G(s,{key:e.id,label:e.full_name+" ("+e.email+")",value:e.id},null,8,["label","value"]))),128))],void 0),_:1},8,["modelValue","multiple","remote","clearable","disabled","size","placeholder","remote-method"])),[[n,t.loading]]):N("",!0)}]])},emits:["avatarChanged"],props:{model:{type:Object,required:!0},company:{type:Object,required:!0},descriptionRows:{type:Number,default:2}},data(){return{localModel:this.model,localCompany:this.company}},watch:{model(e){this.localModel=e},company(e){this.localCompany=e}}},[["render",function(e,l,a,d,s,m){const n=w("photo-widget"),r=o,p=t,_=y,f=h,v=c,g=i,$=w("contact-selector"),x=u;return b(),V("div",ae,[M("div",oe,[M("div",te,[s.localCompany.logo?(b(),V("div",{key:0,class:"fcrm_logo_img",style:H({backgroundImage:"url("+s.localCompany.logo+")"})},null,4)):(b(),V("div",de,[...l[11]||(l[11]=[M("svg",{width:"24",height:"24",viewBox:"0 0 24 24",fill:"none",xmlns:"http://www.w3.org/2000/svg",role:"presentation","aria-hidden":"true"},[M("path",{d:"M12 2L2 7V22H22V7L12 2Z",fill:"white",opacity:"0.3"}),M("rect",{x:"5",y:"10",width:"2.5",height:"2.5",fill:"white"}),M("rect",{x:"9",y:"10",width:"2.5",height:"2.5",fill:"white"}),M("rect",{x:"13",y:"10",width:"2.5",height:"2.5",fill:"white"}),M("rect",{x:"17",y:"10",width:"2.5",height:"2.5",fill:"white"}),M("rect",{x:"5",y:"14",width:"2.5",height:"2.5",fill:"white"}),M("rect",{x:"9",y:"14",width:"2.5",height:"2.5",fill:"white"}),M("rect",{x:"13",y:"14",width:"2.5",height:"2.5",fill:"white"}),M("rect",{x:"17",y:"14",width:"2.5",height:"2.5",fill:"white"}),M("rect",{x:"5",y:"18",width:"2.5",height:"2.5",fill:"white"}),M("rect",{x:"9",y:"18",width:"2.5",height:"2.5",fill:"white"}),M("rect",{x:"13",y:"18",width:"2.5",height:"2.5",fill:"white"}),M("rect",{x:"17",y:"18",width:"2.5",height:"2.5",fill:"white"})],-1)])]))]),M("div",se,[M("div",ie,U(e.$t("Upload Logo")),1),M("div",ce,U(e.$t("Min 400x400px, PNG or JPEG")),1),k(n,{class:"fcrm_photo_widget",btn_type:"default",btn_text:e.$t("Upload"),btn_mode:!0,onChanged:l[0]||(l[0]=l=>e.$emit("avatarChanged",l)),modelValue:s.localCompany.logo,"onUpdate:modelValue":l[1]||(l[1]=e=>s.localCompany.logo=e)},null,8,["btn_text","modelValue"])])]),k(x,{class:"fcrm_company_summary_form","label-position":"top",model:s.localModel},{default:C(()=>[M("section",me,[M("div",ne,[M("span",null,U(e.$t("Basic Information")),1)]),k(f,{gutter:16},{default:C(()=>[k(_,{span:24},{default:C(()=>[k(p,{label:e.$t("Company Name"),class:"fcrm_company_form_item"},{default:C(()=>[k(r,{placeholder:e.$t("e.g. Acme Corp"),modelValue:s.localModel.name,"onUpdate:modelValue":l[2]||(l[2]=e=>s.localModel.name=e),class:"fcrm_company_input"},null,8,["placeholder","modelValue"])],void 0,!0),_:1},8,["label"])],void 0,!0),_:1}),k(_,{md:12,sm:24,xs:24},{default:C(()=>[k(p,{label:e.$t("Company Email"),class:"fcrm_company_form_item"},{default:C(()=>[k(r,{type:"email",placeholder:e.$t("you@example.com"),modelValue:s.localModel.email,"onUpdate:modelValue":l[3]||(l[3]=e=>s.localModel.email=e),class:"fcrm_company_input"},null,8,["placeholder","modelValue"])],void 0,!0),_:1},8,["label"])],void 0,!0),_:1}),k(_,{md:12,sm:24,xs:24},{default:C(()=>[k(p,{label:e.$t("Company Phone Number"),class:"fcrm_company_form_item"},{default:C(()=>[k(r,{placeholder:e.$t("+155555555"),modelValue:s.localModel.phone,"onUpdate:modelValue":l[4]||(l[4]=e=>s.localModel.phone=e),class:"fcrm_company_input"},null,8,["placeholder","modelValue"])],void 0,!0),_:1},8,["label"])],void 0,!0),_:1})],void 0,!0),_:1})]),M("section",re,[M("div",ue,[M("span",null,U(e.$t("About this company")),1)]),k(f,{gutter:16},{default:C(()=>[k(_,{md:12,sm:24,xs:24},{default:C(()=>[k(p,{label:e.$t("Website"),class:"fcrm_company_form_item"},{default:C(()=>[k(r,{type:"url",placeholder:e.$t("https://example.com"),modelValue:s.localModel.website,"onUpdate:modelValue":l[5]||(l[5]=e=>s.localModel.website=e),class:"fcrm_company_input"},null,8,["placeholder","modelValue"])],void 0,!0),_:1},8,["label"])],void 0,!0),_:1}),k(_,{md:12,sm:24,xs:24},{default:C(()=>[k(p,{label:e.$t("Number of Employees"),class:"fcrm_company_form_item"},{default:C(()=>[k(r,{min:0,type:"number",autocomplete:"new-password",placeholder:e.$t("e.g. 5000"),modelValue:s.localModel.employees_number,"onUpdate:modelValue":l[6]||(l[6]=e=>s.localModel.employees_number=e),class:"fcrm_company_input"},null,8,["placeholder","modelValue"])],void 0,!0),_:1},8,["label"])],void 0,!0),_:1}),k(_,{md:12,sm:24,xs:24},{default:C(()=>[k(p,{label:e.$t("Industry"),class:"fcrm_company_form_item"},{default:C(()=>[k(g,{modelValue:s.localModel.industry,"onUpdate:modelValue":l[7]||(l[7]=e=>s.localModel.industry=e),"allow-create":"",clearable:"",filterable:"",placeholder:e.$t("Company industry"),class:"fcrm_company_input"},{default:C(()=>[(b(!0),V(A,null,F(e.appVars.company_categories,e=>(b(),G(v,{key:e,label:e,value:e},null,8,["label","value"]))),128))],void 0,!0),_:1},8,["modelValue","placeholder"])],void 0,!0),_:1},8,["label"])],void 0,!0),_:1}),k(_,{md:12,sm:24,xs:24},{default:C(()=>[k(p,{label:e.$t("Type"),class:"fcrm_company_form_item"},{default:C(()=>[k(g,{modelValue:s.localModel.type,"onUpdate:modelValue":l[8]||(l[8]=e=>s.localModel.type=e),clearable:"",filterable:"",placeholder:e.$t("Relationship type"),class:"fcrm_company_input"},{default:C(()=>[(b(!0),V(A,null,F(e.appVars.company_types,e=>(b(),G(v,{key:e,label:e,value:e},null,8,["label","value"]))),128))],void 0,!0),_:1},8,["modelValue","placeholder"])],void 0,!0),_:1},8,["label"])],void 0,!0),_:1}),k(_,{span:24},{default:C(()=>[k(p,{label:e.$t("Company Owner"),class:"fcrm_company_form_item"},{default:C(()=>[k($,{modelValue:s.localModel.owner_id,"onUpdate:modelValue":l[9]||(l[9]=e=>s.localModel.owner_id=e),field:{clearable:!0,pre_options:[s.localCompany.owner]}},null,8,["modelValue","field"])],void 0,!0),_:1},8,["label"])],void 0,!0),_:1}),k(_,{span:24},{default:C(()=>[k(p,{label:e.$t("Description"),class:"fcrm_company_form_item"},{default:C(()=>[k(r,{type:"textarea",rows:a.descriptionRows,placeholder:e.$t("Briefly describe what company does.."),modelValue:s.localModel.description,"onUpdate:modelValue":l[10]||(l[10]=e=>s.localModel.description=e),class:"fcrm_company_textarea"},null,8,["rows","placeholder","modelValue"])],void 0,!0),_:1},8,["label"])],void 0,!0),_:1})],void 0,!0),_:1})])],void 0),_:1},8,["model"])])}]])},emits:["avatarChanged"],props:{model:{type:Object,required:!0},company:{type:Object,required:!0},mode:{type:String,default:"edit",validator:e=>["create","edit"].includes(e)}},data(){return{localModel:this.model,localCompany:this.company,showAddressFields:"create"!==this.mode,showSocialFields:"create"!==this.mode}},computed:{isCreateMode(){return"create"===this.mode}},watch:{model(e){this.localModel=e},company(e){this.localCompany=e}}},[["render",function(e,l,a,d,s,m){const r=w("company-summary-form"),p=n,_=o,f=t,v=y,g=c,E=i,L=h,S=u;return b(),V("div",pe,[k(r,{model:s.localModel,company:s.localCompany,onAvatarChanged:l[0]||(l[0]=l=>e.$emit("avatarChanged",l))},null,8,["model","company"]),M("section",_e,[m.isCreateMode?(b(),V("div",fe,[k(p,{modelValue:s.showAddressFields,"onUpdate:modelValue":l[1]||(l[1]=e=>s.showAddressFields=e),class:"fcrm_company_checkbox"},{default:C(()=>[x(U(e.$t("Add Address Info")),1)],void 0),_:1},8,["modelValue"])])):(b(),V("div",he,[M("span",null,U(e.$t("Address")),1)])),s.showAddressFields?(b(),G(S,{key:2,class:"fcrm_company_summary_form","label-position":"top",model:s.localModel},{default:C(()=>[k(L,{gutter:16},{default:C(()=>[k(v,{md:12,sm:24,xs:24},{default:C(()=>[k(f,{label:e.$t("Address Line 1"),class:"fcrm_company_form_item"},{default:C(()=>[k(_,{placeholder:e.$t("Enter street address"),autocomplete:"new-password",modelValue:s.localModel.address_line_1,"onUpdate:modelValue":l[2]||(l[2]=e=>s.localModel.address_line_1=e),class:"fcrm_company_input"},null,8,["placeholder","modelValue"])],void 0,!0),_:1},8,["label"])],void 0,!0),_:1}),k(v,{md:12,sm:24,xs:24},{default:C(()=>[k(f,{class:"fcrm_company_form_item"},{label:C(()=>[M("span",null,[x(U(e.$t("Address Line 2"))+" ",1),M("span",ye,"("+U(e.$t("Optional"))+")",1)])]),default:C(()=>[k(_,{placeholder:e.$t("Enter apartment, suite, unit"),autocomplete:"new-password",modelValue:s.localModel.address_line_2,"onUpdate:modelValue":l[3]||(l[3]=e=>s.localModel.address_line_2=e),class:"fcrm_company_input"},null,8,["placeholder","modelValue"])],void 0,!0),_:1})],void 0,!0),_:1}),k(v,{md:12,sm:24,xs:24},{default:C(()=>[k(f,{label:e.$t("City"),class:"fcrm_company_form_item"},{default:C(()=>[k(_,{placeholder:e.$t("Enter city"),autocomplete:"new-password",modelValue:s.localModel.city,"onUpdate:modelValue":l[4]||(l[4]=e=>s.localModel.city=e),class:"fcrm_company_input"},null,8,["placeholder","modelValue"])],void 0,!0),_:1},8,["label"])],void 0,!0),_:1}),k(v,{md:12,sm:24,xs:24},{default:C(()=>[k(f,{label:e.$t("State"),class:"fcrm_company_form_item"},{default:C(()=>[k(_,{placeholder:e.$t("Enter state / province"),autocomplete:"new-password",modelValue:s.localModel.state,"onUpdate:modelValue":l[5]||(l[5]=e=>s.localModel.state=e),class:"fcrm_company_input"},null,8,["placeholder","modelValue"])],void 0,!0),_:1},8,["label"])],void 0,!0),_:1}),k(v,{md:12,sm:24,xs:24},{default:C(()=>[k(f,{label:e.$t("Postal Code"),class:"fcrm_company_form_item"},{default:C(()=>[k(_,{placeholder:e.$t("Enter postal code"),autocomplete:"new-password",modelValue:s.localModel.postal_code,"onUpdate:modelValue":l[6]||(l[6]=e=>s.localModel.postal_code=e),class:"fcrm_company_input"},null,8,["placeholder","modelValue"])],void 0,!0),_:1},8,["label"])],void 0,!0),_:1}),k(v,{md:12,sm:24,xs:24},{default:C(()=>[k(f,{label:e.$t("Country"),class:"fcrm_company_form_item"},{default:C(()=>[k(E,{modelValue:s.localModel.country,"onUpdate:modelValue":l[7]||(l[7]=e=>s.localModel.country=e),clearable:"",filterable:"",autocomplete:"off",placeholder:e.$t("Select country"),class:"fcrm_company_input"},{label:C(({label:e})=>[M("span",{innerHTML:e},null,8,ve)]),default:C(()=>[(b(!0),V(A,null,F(e.appVars.countries,e=>(b(),G(g,{key:e.code,value:e.code,label:e.title},{default:C(()=>[M("span",{innerHTML:e.title},null,8,ge)],void 0,!0),_:2},1032,["value","label"]))),128))],void 0,!0),_:1},8,["modelValue","placeholder"])],void 0,!0),_:1},8,["label"])],void 0,!0),_:1})],void 0,!0),_:1})],void 0),_:1},8,["model"])):N("",!0)]),M("section",we,[m.isCreateMode?(b(),V("div",be,[k(p,{modelValue:s.showSocialFields,"onUpdate:modelValue":l[8]||(l[8]=e=>s.showSocialFields=e),class:"fcrm_company_checkbox"},{default:C(()=>[x(U(e.$t("Add Social Media URL")),1)],void 0),_:1},8,["modelValue"])])):(b(),V("div",Ve,[M("span",null,U(e.$t("Social Links")),1)])),s.showSocialFields?(b(),V("div",$e,[k(_,{type:"url",placeholder:e.$t("https://www.linkedin.com/company/example"),modelValue:s.localModel.linkedin_url,"onUpdate:modelValue":l[9]||(l[9]=e=>s.localModel.linkedin_url=e),class:"fcrm_company_social_input"},{prefix:C(()=>[...l[12]||(l[12]=[M("span",{class:"dashicons dashicons-linkedin fcrm_social_icon"},null,-1)])]),_:1},8,["placeholder","modelValue"]),k(_,{type:"url",placeholder:e.$t("https://x.com/example"),modelValue:s.localModel.twitter_url,"onUpdate:modelValue":l[10]||(l[10]=e=>s.localModel.twitter_url=e),class:"fcrm_company_social_input"},{prefix:C(()=>[...l[13]||(l[13]=[M("svg",{class:"fcrm_social_icon fcrm_social_icon_svg",xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 20 20",fill:"none","aria-hidden":"true"},[M("path",{d:"M2.5365 3.125L8.32782 10.709L2.5 16.875H3.81171L8.91408 11.4765L13.0365 16.875H17.5L11.3827 8.86448L16.8073 3.125H15.4956L10.7967 8.09678L7 3.125H2.5365ZM4.46543 4.07143H6.51594L15.5708 15.9288H13.5203L4.46543 4.07143Z",fill:"currentColor"})],-1)])]),_:1},8,["placeholder","modelValue"]),k(_,{type:"url",placeholder:e.$t("https://www.facebook.com/example"),modelValue:s.localModel.facebook_url,"onUpdate:modelValue":l[11]||(l[11]=e=>s.localModel.facebook_url=e),class:"fcrm_company_social_input fcrm_company_edit_social_full"},{prefix:C(()=>[...l[14]||(l[14]=[M("span",{class:"dashicons dashicons-facebook-alt fcrm_social_icon"},null,-1)])]),_:1},8,["placeholder","modelValue"])])):N("",!0)]),e.$slots["custom-fields"]?(b(),V("section",ke,[$(e.$slots,"custom-fields")])):N("",!0)])}]]);export{Ce as C,K as a}; diff --git a/wp-content/plugins/fluent-crm/assets/CompanyInfoSide.js b/wp-content/plugins/fluent-crm/assets/CompanyInfoSide.js new file mode 100644 index 0000000..3703322 --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/CompanyInfoSide.js @@ -0,0 +1 @@ +import{a3 as e,k as a,ay as t,aT as i}from"./vendor-element-plus.js?ver=3.1.8";import{c0 as o,b$ as s,aQ as n,W as c,X as l,Z as m,ab as r,a5 as d,a9 as p,aa as y,a6 as u,Y as _,a8 as h,$ as f,J as w,az as v,ay as k,a0 as g}from"./vendor.js?ver=3.1.8";import{i as b,j as C}from"./data_config.js?ver=3.1.8";import{C as $,a as D}from"./CompanyEditForm.js?ver=3.1.8";import{_ as S,I as q}from"./fc-bits-ui.js?ver=3.1.8";import{B as E}from"./BaseCard.js?ver=3.1.8";const x={name:"CompanyInfoSide",components:{BaseCard:E,Icons:q,CustomFieldsForm:D,CompanyEditForm:$,Edit:e},emits:["companyUpdated","companyCreated","cancel","drawerStateChanged"],props:{company:{type:Object,default:()=>null},photo_holder:{type:String,default:"fcrm_photo_holder_mini"},is_drawer:{type:Boolean,default:!1},hide_drawer_footer:{type:Boolean,default:!1},intended_contact_id:{type:Number,default:null}},data:()=>({direction:"rtl",model:{},isDirty:!1,appReady:!1,updating:!1,isHeaderEditing:!1,showSummaryEditDrawer:!1}),watch:{model:{handler(e,a){this.appReady&&(this.isDirty=!0)},deep:!0},isDirty(){this.emitDrawerState()},updating(){this.emitDrawerState()}},computed:{domainName(){return C(this.company.website)},ownerDisplayName(){return this.company.owner&&(this.company.owner.full_name||this.company.owner.email)||"--"},quickStats(){return[{label:this.$t("Owner"),value:"--"!==this.ownerDisplayName?this.ownerDisplayName:"",icon:"owner"},{label:this.$t("Industry"),value:this.company.industry,icon:"industry"},{label:this.$t("Type"),value:this.company.type,icon:"type"},{label:this.$t("Employees"),value:this.company.employees_number&&"0"!=this.company.employees_number?this.company.employees_number:"",icon:"employees"}].filter(e=>e.value)},profileAddressLines(){const e=[[this.company.city,this.company.state].filter(Boolean).join(", "),this.company.postal_code].filter(Boolean).join(" ");return[this.company.address_line_1,this.company.address_line_2,e,this.company.country?this.getCountryName(this.company.country):""].filter(Boolean)},socialLinks(){return[{label:this.$t("LinkedIn"),url:this.company.linkedin_url,icon:"linkedin"},{label:this.$t("X"),url:this.company.twitter_url,icon:"x"},{label:this.$t("Facebook"),url:this.company.facebook_url,icon:"facebook"}].filter(e=>e.url)}},mounted(){window.fcAdmin&&window.fcAdmin.is_rtl&&(this.direction="ltr")},methods:{updateAvatar(e){this.company.id?this.updateProperty("logo",e):this.model.logo=e},updateProperty(e,a,t){this.$put("companies/companies-property",{property:e,companies:[this.company.id],value:a}).then(i=>{this.$notify.success(i.message),this.company[e]=a,"logo"===e&&(this.model.logo=a),t&&t(i)}).catch(e=>{this.handleError(e)}).finally(()=>{this.updating=!1})},applyCompanySaveResponse(e){this.each(this.model,(e,a)=>{this.company[a]=e}),e.update_data&&this.each(e.update_data,(e,a)=>{this.company[a]=e}),e.company&&(this.each(e.company,(e,a)=>{this.company[a]=e}),this.model.logo=this.company.logo),e.updated_logo&&(this.company.logo=e.updated_logo,this.model.logo=e.updated_logo),this.syncCustomValues(e.company)},syncCustomValues(e){const a=o(this.model.custom_values)&&!s(this.model.custom_values)?this.model.custom_values:{};o(this.company.meta)&&!s(this.company.meta)||(this.company.meta={}),this.company.meta.custom_values=a,e&&(o(e.meta)&&!s(e.meta)||(e.meta={}),e.meta.custom_values=a)},updateInfo(){this.updating=!0;const e=!this.company.id;e&&(this.company.id=0,this.intended_contact_id&&(this.model.intended_contact_id=this.intended_contact_id)),this.$put(`companies/${this.company.id}`,this.model).then(a=>{this.$notify.success(a.message),this.applyCompanySaveResponse(a),e?this.$emit("companyCreated",a.company):this.$emit("companyUpdated",a.company)}).catch(e=>{this.handleError(e)}).finally(()=>{this.updating=!1})},openSummaryEditDrawer(){this.showSummaryEditDrawer=!0},closeSummaryEditDrawer(){this.showSummaryEditDrawer=!1},saveSummaryFromDrawer(){this.updating=!0;const e=!this.company.id;e&&(this.company.id=0,this.intended_contact_id&&(this.model.intended_contact_id=this.intended_contact_id)),this.model.logo=this.company.logo,this.$put(`companies/${this.company.id}`,this.model).then(a=>{this.$notify.success(a.message),this.applyCompanySaveResponse(a),e?this.$emit("companyCreated",a.company):this.$emit("companyUpdated",a.company),this.isDirty=!1,this.showSummaryEditDrawer=!1}).catch(e=>{this.handleError(e)}).finally(()=>{this.updating=!1})},getFormattedAddress:b,getCountryName(e){var a;if(!e)return"";const t=null==(a=this.appVars.countries)?void 0:a.find(a=>a.code===e);return t?t.title:e},reFetchLogo(){this.updating=!0,this.updateProperty("refetch_logo",this.company.website,e=>{e.updated_logo&&(this.company.logo=e.updated_logo,this.model.logo=e.updated_logo)})},handleCancel(){this.is_drawer&&this.$emit("cancel")},emitDrawerState(){this.is_drawer&&this.$emit("drawerStateChanged",{isDirty:this.isDirty,updating:this.updating})}},created(){var e,a;const t=this.company;this.model={owner_id:t.owner_id,name:t.name,logo:t.logo,email:t.email,phone:t.phone,website:t.website,industry:t.industry,type:t.type,address_line_1:t.address_line_1,address_line_2:t.address_line_2,city:t.city,state:t.state,employees_number:t.employees_number&&"0"!=t.employees_number?t.employees_number:"",postal_code:t.postal_code,country:t.country,description:t.description,linkedin_url:t.linkedin_url,twitter_url:t.twitter_url,facebook_url:t.facebook_url,custom_values:o(null==(e=t.meta)?void 0:e.custom_values)&&!s(null==(a=t.meta)?void 0:a.custom_values)?t.meta.custom_values:{}},this.$nextTick(()=>{this.appReady=!0,this.emitDrawerState()})}},A={key:0,class:"fcrm_company_drawer_main"},F={class:"fcrm_company_create_body"},L={key:0,class:"fcrm_company_drawer_footer"},N={class:"fcrm_company_drawer_footer_actions"},j={key:1,class:"fcrm_view_mode"},B={class:"fcrm_view_sections"},I={class:"icon"},z={class:"fcrm_view_section"},V={class:"fcrm_view_section_body"},U={class:"fcrm_company_quick_view"},R={class:"fcrm_company_quick_hero"},P={key:0,width:"28",height:"28",viewBox:"0 0 24 24",fill:"none",xmlns:"http://www.w3.org/2000/svg",role:"presentation","aria-hidden":"true"},H={class:"fcrm_company_quick_identity"},T={class:"fcrm_company_quick_contacts"},O=["href"],W=["href"],X={key:0,class:"fcrm_company_quick_stats"},Z={class:"icon"},G={class:"fcrm_company_quick_stat_text"},J={key:1,class:"fcrm_company_quick_stat"},M={class:"icon"},Q={class:"fcrm_company_quick_stat_text"},Y=["href"],K={class:"fcrm_company_quick_panel fcrm_pb_0"},ee={class:"fcrm_company_quick_panel_head"},ae={key:0,class:"fcrm_company_quick_description"},te={key:1,class:"fcrm_company_quick_empty"},ie={class:"fcrm_company_quick_panel fcrm_pb_0"},oe={class:"fcrm_company_quick_panel_head"},se={key:0,class:"fcrm_company_quick_address"},ne={key:1,class:"fcrm_company_quick_empty"},ce={class:"fcrm_company_quick_panel fcrm_pb_0"},le={class:"fcrm_company_quick_panel_head"},me={key:0,class:"fcrm_company_quick_socials"},re=["href"],de={key:0,class:"icon"},pe={key:1,class:"fcrm_company_quick_empty"},ye={class:"fcrm_company_quick_custom"},ue={class:"fcrm_company_quick_panel_head"},_e={class:"icon"},he={class:"fcrm_view_cf_wrap"},fe={key:0,class:"fcrm_drawer_content"},we={class:"fcrm_drawer_body fcrm_company_summary_body"},ve={class:"fcrm_company_summary_footer_actions"},ke={key:0,class:"fcrm_view_save_wrap"};const ge=S(x,[["render",function(e,o,s,b,C,$){const D=n("CustomFieldsForm"),S=n("company-edit-form"),q=a,E=n("Icons"),x=n("BaseCard"),ge=i,be=n("router-link"),Ce=t;return c(),l("div",{class:g([{fcrm_company_in_drawer:s.is_drawer,fcrm_company_unsaved:C.isDirty||!s.company.id},"fcrm_company_info_wrapper"])},[!s.is_drawer||s.company.id&&!C.isHeaderEditing?(c(),l("div",j,[m("div",B,[r(x,{"no-body-padding":!0},{title:d(()=>[m("h4",null,y(e.$t("Company Summary")),1)]),header_action:d(()=>[r(q,{size:"small",onClick:o[1]||(o[1]=e=>$.openSummaryEditDrawer())},{default:d(()=>[m("span",I,[r(E,{"icon-name":"EditPen"})]),p(" "+y(e.$t("Edit")),1)],void 0,!0),_:1})]),body:d(()=>[m("div",z,[m("div",V,[m("div",U,[m("section",R,[m("div",{class:"fcrm_company_quick_logo",style:f({backgroundImage:s.company.logo?"url("+s.company.logo+")":""})},[s.company.logo?h("",!0):(c(),l("svg",P,[...o[8]||(o[8]=[m("path",{d:"M12 2L2 7V22H22V7L12 2Z",fill:"white",opacity:"0.3"},null,-1),m("rect",{x:"5",y:"10",width:"2.5",height:"2.5",fill:"white"},null,-1),m("rect",{x:"9",y:"10",width:"2.5",height:"2.5",fill:"white"},null,-1),m("rect",{x:"13",y:"10",width:"2.5",height:"2.5",fill:"white"},null,-1),m("rect",{x:"17",y:"10",width:"2.5",height:"2.5",fill:"white"},null,-1),m("rect",{x:"5",y:"14",width:"2.5",height:"2.5",fill:"white"},null,-1),m("rect",{x:"9",y:"14",width:"2.5",height:"2.5",fill:"white"},null,-1),m("rect",{x:"13",y:"14",width:"2.5",height:"2.5",fill:"white"},null,-1),m("rect",{x:"17",y:"14",width:"2.5",height:"2.5",fill:"white"},null,-1),m("rect",{x:"5",y:"18",width:"2.5",height:"2.5",fill:"white"},null,-1),m("rect",{x:"9",y:"18",width:"2.5",height:"2.5",fill:"white"},null,-1),m("rect",{x:"13",y:"18",width:"2.5",height:"2.5",fill:"white"},null,-1),m("rect",{x:"17",y:"18",width:"2.5",height:"2.5",fill:"white"},null,-1)])]))],4),m("div",H,[m("h3",null,y(s.company.name),1),m("div",T,[s.company.email?(c(),l("a",{key:0,href:"mailto:"+s.company.email},y(s.company.email),9,O)):h("",!0),s.company.phone?(c(),l("a",{key:1,href:"tel:"+s.company.phone},y(s.company.phone),9,W)):h("",!0)])])]),s.company.website||$.quickStats.length?(c(),l("section",X,[$.quickStats?(c(!0),l(w,{key:0},v($.quickStats,e=>(c(),l("div",{key:e.label,class:"fcrm_company_quick_stat"},[m("span",Z,["owner"===e.icon?(c(),_(E,{key:0,"icon-name":"user"})):"industry"===e.icon?(c(),_(E,{key:1,"icon-name":"building"})):"type"===e.icon?(c(),_(E,{key:2,"icon-name":"briefcase"})):(c(),_(E,{key:3,"icon-name":"users"}))]),m("div",G,[m("span",null,y(e.label)+":",1),m("strong",null,y(e.value),1)])]))),128)):h("",!0),s.company.website?(c(),l("div",J,[m("span",M,[r(E,{"icon-name":"glob"})]),m("div",Q,[m("span",null,y(e.$t("Website:")),1),m("strong",null,[s.company.website?(c(),l("a",{key:0,href:s.company.website,target:"_blank",rel:"noopener"},y($.domainName||s.company.website),9,Y)):h("",!0)])])])):h("",!0)])):h("",!0),m("section",K,[m("div",ee,[m("h4",null,y(e.$t("About")),1)]),s.company.description?(c(),l("p",ae,y(s.company.description),1)):(c(),l("p",te,y(e.$t("No description added")),1))]),m("section",ie,[m("div",oe,[m("h4",null,y(e.$t("Address")),1)]),$.profileAddressLines.length?(c(),l("div",se,[(c(!0),l(w,null,v($.profileAddressLines,(e,a)=>(c(),l("p",{key:a},y(e),1))),128))])):(c(),l("p",ne,y(e.$t("No address added")),1))]),m("section",ce,[m("div",le,[m("h4",null,y(e.$t("Social Links")),1)]),$.socialLinks.length?(c(),l("div",me,[(c(!0),l(w,null,v($.socialLinks,e=>(c(),l("a",{key:e.label,href:e.url,target:"_blank",rel:"noopener",class:"el-button"},[m("span",null,[e.icon?(c(),l("span",de,["linkedin"===e.icon?(c(),_(E,{key:0,"icon-name":"linkedin"})):"x"===e.icon?(c(),_(E,{key:1,"icon-name":"x"})):(c(),_(E,{key:2,"icon-name":"facebook"}))])):h("",!0),p(" "+y(e.label),1)])],8,re))),128))])):(c(),l("p",pe,y(e.$t("No social links added")),1))])])])])]),_:1}),m("section",ye,[m("div",ue,[m("h4",null,y(e.$t("Custom Data")),1),r(q,{link:"",size:"small",onClick:o[2]||(o[2]=a=>e.$refs.companyCustomFields.openConfig())},{default:d(()=>[m("span",_e,[r(E,{"icon-name":"EditPen"})]),p(" "+y(e.$t("Configure")),1)],void 0),_:1})]),m("div",he,[r(D,{ref:"companyCustomFields",show_header:!1,custom_values:C.model.custom_values},null,8,["custom_values"]),C.isDirty?u((c(),_(q,{key:0,disabled:C.updating,type:"primary",onClick:o[3]||(o[3]=e=>$.updateInfo())},{default:d(()=>[p(y(s.company.id?e.$t("Update info"):e.$t("Create Company")),1)],void 0),_:1},8,["disabled"])),[[Ce,C.updating]]):h("",!0)])])]),r(ge,{direction:C.direction,class:"fcrm_company_summary_edit_drawer","with-header":!0,size:e.globalDrawerSize,"append-to-body":!0,title:e.$t("Edit Company"),onClose:o[6]||(o[6]=e=>$.closeSummaryEditDrawer()),modelValue:C.showSummaryEditDrawer,"onUpdate:modelValue":o[7]||(o[7]=e=>C.showSummaryEditDrawer=e)},k({default:d(()=>[C.showSummaryEditDrawer?(c(),l("div",fe,[m("div",we,[r(S,{model:C.model,company:s.company,onAvatarChanged:$.updateAvatar,class:"fcrm_pt_20"},{"custom-fields":d(()=>[r(D,{custom_values:C.model.custom_values},null,8,["custom_values"])]),_:1},8,["model","company","onAvatarChanged"])])])):h("",!0)],void 0),_:2},[C.showSummaryEditDrawer?{name:"footer",fn:d(()=>[m("div",ve,[r(q,{onClick:o[4]||(o[4]=e=>$.closeSummaryEditDrawer())},{default:d(()=>[p(y(e.$t("Cancel")),1)],void 0,!0),_:1}),r(q,{onClick:o[5]||(o[5]=e=>$.saveSummaryFromDrawer()),type:"primary",loading:C.updating},{default:d(()=>[p(y(e.$t("Update info")),1)],void 0,!0),_:1},8,["loading"])])]),key:"0"}:void 0]),1032,["direction","size","title","modelValue"]),C.isDirty?(c(),l("div",ke,[s.is_drawer&&s.company.id?(c(),_(be,{key:0,to:{name:"view_company",params:{company_id:s.company.id}},class:"fcrm_view_link"},{default:d(()=>[p(y(e.$t("Go to company record")),1)],void 0),_:1},8,["to"])):h("",!0)])):h("",!0)])):(c(),l("div",A,[m("div",F,[r(S,{mode:"create",model:C.model,company:s.company,onAvatarChanged:$.updateAvatar,class:"fcrm_pt_20"},{"custom-fields":d(()=>[r(D,{custom_values:C.model.custom_values},null,8,["custom_values"])]),_:1},8,["model","company","onAvatarChanged"])]),s.hide_drawer_footer?h("",!0):(c(),l("div",L,[m("div",N,[r(q,{size:"small",onClick:$.handleCancel},{default:d(()=>[p(y(e.$t("Cancel")),1)],void 0),_:1},8,["onClick"]),u((c(),_(q,{disabled:C.updating||!C.isDirty,type:"primary",size:"small",onClick:o[0]||(o[0]=e=>$.updateInfo())},{default:d(()=>[p(y(s.company.id?e.$t("Update info"):e.$t("Create Company")),1)],void 0),_:1},8,["disabled"])),[[Ce,C.updating]])])]))]))],2)}]]);export{ge as C}; diff --git a/wp-content/plugins/fluent-crm/assets/Confirm.js b/wp-content/plugins/fluent-crm/assets/Confirm.js new file mode 100644 index 0000000..a8e949e --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/Confirm.js @@ -0,0 +1 @@ +import{a6 as e,k as t,E as i,aO as s}from"./vendor-element-plus.js?ver=3.1.8";import{aQ as n,W as o,Y as r,a5 as a,Z as c,ab as d,a9 as l,aa as m,_ as f}from"./vendor.js?ver=3.1.8";import{_ as p}from"./fc-bits-ui.js?ver=3.1.8";const u=["innerHTML"],h={class:"action-buttons"};const v=p({name:"Confirm",components:{Delete:e},emits:["no","yes"],props:{placement:{type:String,default:"top-end"},message:{type:String,default:"Are you sure to delete this?"},width:{type:[Number,String],default:180},confirmText:{type:String,default:""},cancelText:{type:String,default:""},confirmType:{type:String,default:"danger"}},data:()=>({visible:!1,_confirmed:!1}),computed:{confirmTextComputed(){return this.confirmText||this.$t("Yes")},cancelTextComputed(){return this.cancelText||this.$t("No")},confirmButtonType(){return this.confirmType||"danger"}},methods:{show(){this._confirmed=!1,this.visible=!0},hide(){this.visible=!1},confirm(){this._confirmed=!0,this.hide(),this.$emit("yes")},cancel(){this._confirmed=!1,this.hide(),this.$emit("no")},onHide(){this._confirmed||this.$emit("no")}}},[["render",function(e,p,v,y,_,b){const T=t,g=n("Delete"),x=i,C=s;return o(),r(C,{width:v.width,onHide:b.onHide,visible:_.visible,"onUpdate:visible":p[2]||(p[2]=e=>_.visible=e),placement:v.placement,trigger:"click"},{reference:a(()=>[f(e.$slots,"reference",{},()=>[f(e.$slots,"default",{},()=>[d(x,{style:{cursor:"pointer"}},{default:a(()=>[d(g)],void 0,!0),_:1})])])]),default:a(()=>[c("p",{innerHTML:v.message,class:"fcrm_secondary_text fcrm_mb_12"},null,8,u),c("div",h,[d(T,{size:"small",class:"fcrm_secondary_btn",onClick:p[0]||(p[0]=e=>b.cancel())},{default:a(()=>[l(m(b.cancelTextComputed),1)],void 0,!0),_:1}),d(T,{type:b.confirmButtonType,size:"small",onClick:p[1]||(p[1]=e=>b.confirm())},{default:a(()=>[l(m(b.confirmTextComputed),1)],void 0,!0),_:1},8,["type"])])],void 0),_:3},8,["width","onHide","visible","placement"])}]]);export{v as C}; diff --git a/wp-content/plugins/fluent-crm/assets/Confirm2.js b/wp-content/plugins/fluent-crm/assets/Confirm2.js new file mode 100644 index 0000000..c0ca4fe --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/Confirm2.js @@ -0,0 +1 @@ +import{a6 as e,k as s,E as i,aO as t}from"./vendor-element-plus.js?ver=3.1.8";import{aQ as a,W as n,Y as l,a5 as o,Z as r,ab as d,a9 as c,aa as m,_ as f}from"./vendor.js?ver=3.1.8";import{_ as v}from"./fc-bits-ui.js?ver=3.1.8";const p=["innerHTML"],u={class:"action-buttons"};const h=v({name:"Confirm",components:{Delete:e},emits:["no","yes"],props:{placement:{default:"top-end"},message:{default:"Are you sure to delete this?"},width:{default:170}},data:()=>({visible:!1}),methods:{hide(){this.visible=!1},confirm(){this.hide(),this.$emit("yes")},cancel(){this.hide(),this.$emit("no")}}},[["render",function(e,v,h,b,_,g){const $=s,k=a("Delete"),w=i,y=t;return n(),l(y,{width:h.width,onHide:g.cancel,visible:_.visible,"onUpdate:visible":v[2]||(v[2]=e=>_.visible=e),trigger:"click",placement:h.placement},{reference:o(()=>[f(e.$slots,"reference",{},()=>[d(w,null,{default:o(()=>[d(k)],void 0,!0),_:1})])]),default:o(()=>[r("p",{innerHTML:h.message},null,8,p),r("div",u,[d($,{size:"small",onClick:v[0]||(v[0]=e=>g.cancel())},{default:o(()=>[c(m(e.$t("No")),1)],void 0,!0),_:1}),d($,{type:"danger",size:"small",onClick:v[1]||(v[1]=e=>g.confirm())},{default:o(()=>[c(m(e.$t("Yes")),1)],void 0,!0),_:1})])],void 0),_:3},8,["width","onHide","visible","placement"])}]]);export{h as C}; diff --git a/wp-content/plugins/fluent-crm/assets/ContactHeaderPopNav.js b/wp-content/plugins/fluent-crm/assets/ContactHeaderPopNav.js new file mode 100644 index 0000000..6562564 --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/ContactHeaderPopNav.js @@ -0,0 +1 @@ +import{av as s,aQ as t,W as a,X as e,Z as o,J as i,az as r,a0 as n,ab as m,a5 as c,a9 as _,aa as h,_ as l}from"./vendor.js?ver=3.1.8";import{u,a1 as p,J as d,O as f,ab as v}from"./vendor-element-plus.js?ver=3.1.8";import{_ as g}from"./fc-bits-ui.js?ver=3.1.8";const w={class:"fcrm_page_header_top_nav_wrapper"},$={class:"fcrm_page_header_top_nav"},P={class:"fcrm_page_header_top_nav_links"},b={class:"fcrm_page_header_top_actions"};const k=g({name:"ContactHeaderPopNav",data(){return{routeName:this.$route.name,menuItems:this.getMenuItems()}},watch:{"$route.name"(s){this.routeName=s}},methods:{getMenuItems(){var t,a;const e=null==(a=null==(t=this.appVars)?void 0:t.addons)?void 0:a.company_module,o=!this.hasPermission||(this.hasPermission("fcrm_read_contacts")||this.hasPermission("fcrm_manage_contacts")),i=!this.hasPermission||(this.hasPermission("fcrm_manage_contact_cats")||this.hasPermission("fcrm_manage_contact_cats_delete"));return[{show:o,title:this.$t("All Contacts"),route:"subscribers",icon:s(u)},{show:i,title:this.$t("Lists"),route:"lists",icon:s(p)},{show:i,title:this.$t("Tags"),route:"tags",icon:s(d)},{show:i&&e,title:this.$t("Companies"),route:"companies",icon:s(f)},{show:i,title:this.$t("Segments"),route:"dynamic_segments",icon:s(v)}].filter(s=>s.show).map(({show:s,...t})=>t)}}},[["render",function(s,u,p,d,f,v){const g=t("router-link");return a(),e("div",w,[o("div",$,[o("ul",P,[(a(!0),e(i,null,r(f.menuItems,s=>(a(),e("li",{key:s.route,class:n("fcrm_item_"+s.route+" fcrm_top_menu_item "+(s.item_class||""))},[m(g,{to:{name:s.route},class:"fcrm_top_nav_link"},{default:c(()=>[_(h(s.title),1)],void 0),_:2},1032,["to"])],2))),128))])]),o("div",b,[l(s.$slots,"actions")])])}]]);export{k as C}; diff --git a/wp-content/plugins/fluent-crm/assets/Csv.js b/wp-content/plugins/fluent-crm/assets/Csv.js new file mode 100644 index 0000000..4095289 --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/Csv.js @@ -0,0 +1 @@ +import{aL as e,aK as s,k as r,aR as o}from"./vendor-element-plus.js?ver=3.1.8";import{aQ as a,W as i,X as t,Z as l,aa as n,ab as c,a5 as m,J as d,az as p,Y as u,a9 as f,a0 as C,a8 as _}from"./vendor.js?ver=3.1.8";import{E as v}from"./fc-bits.js?ver=3.1.8";import{E as h}from"./Error.js?ver=3.1.8";import{_ as w,I as V}from"./fc-bits-ui.js?ver=3.1.8";const x={class:"fcrm_csv_uploader"},y={class:"fcrm_csv_upload_container"},$={class:"fcrm_csv_delimiter_container"},L={class:"fcrm_primary_text fcrm_mb_4 font-medium"},g={key:0,class:"fcrm_csv_file_upload_container"},S={class:"fcrm_primary_text fcrm_mb_8 font-medium"},b={class:"fcrm_file_uploader"},E={class:"fcrm_primary_text font-medium"},k={class:"fcrm_upload_button"},F={class:"fcrm_sample_warning_container"},U={class:"el-upload__tip"},M={class:"fcrm_primary_text small"},N={class:"icon"};const R=w({name:"Csv",components:{Icons:V,Error:h},props:["options"],emits:["success"],data(){return{errors:new v,delimiter_options:{comma:this.$t("Comma Separated (,)"),semicolon:this.$t("Semicolon Separated (;)")}}},computed:{url(){return-1!=window.FLUENTCRM.instance.appVars.rest.url.indexOf("?")?window.FLUENTCRM.instance.appVars.rest.url+"/import/csv-upload&_wpnonce="+window.FLUENTCRM.instance.appVars.rest.nonce+"&delimiter="+this.options.delimiter+"&type="+this.options.type:window.FLUENTCRM.instance.appVars.rest.url+"/import/csv-upload?_wpnonce="+window.FLUENTCRM.instance.appVars.rest.nonce+"&delimiter="+this.options.delimiter+"&type="+this.options.type}},mounted(){this.options.delimiter||(this.options.delimiter="comma")},methods:{success(e){this.errors.clear(),e.map||(e.map=e.headers.map(e=>({csv:e,table:null}))),this.$emit("success",e)},remove(){this.errors.clear()},exceed(){this.errors.record({file:{exceed:this.$t("You cannot upload more than one file.")}})},error(e){const s=JSON.parse(e.message);this.handleError(s),this.errors.record({file:{invalid:s.message||this.$t("unknown error. Please check your csv first")}})},sample(){location.href=this.options.sampleCsv},clear(){this.$refs.uploader.clearFiles()},next(){this.$notify.error(this.$t("Please Upload a CSV first"))}}},[["render",function(v,h,w,V,R,j){const T=e,z=s,I=r,O=o,Y=a("error"),Z=a("Icons");return i(),t("div",x,[l("div",y,[l("div",$,[l("label",L,n(v.$t("Select Your CSV Delimiter")),1),c(z,{modelValue:w.options.delimiter,"onUpdate:modelValue":h[0]||(h[0]=e=>w.options.delimiter=e),size:"large",class:"fcrm_csv_delimiter_select"},{default:m(()=>[(i(!0),t(d,null,p(R.delimiter_options,(e,s)=>(i(),u(T,{key:s,value:s,label:e},null,8,["value","label"]))),128))],void 0),_:1},8,["modelValue"])]),w.options.delimiter?(i(),t("div",g,[l("h3",S,n(v.$t("Upload CSV File")),1),c(O,{drag:"",limit:1,action:j.url,ref:"uploader",multiple:!1,"on-error":j.error,"on-remove":j.remove,"on-exceed":j.exceed,"on-success":j.success,class:C({"is-error":R.errors.has("file")})},{default:m(()=>[l("div",b,[h[1]||(h[1]=l("div",{class:"fcrm_upload_icon"},[l("svg",{xmlns:"http://www.w3.org/2000/svg",width:"24",height:"24",viewBox:"0 0 24 24",fill:"none"},[l("path",{d:"M11.9998 12.5274L15.8185 16.3452L14.545 17.6187L12.8998 15.9735V21H11.0998V15.9717L9.45461 17.6187L8.18111 16.3452L11.9998 12.5274ZM11.9998 3C13.5451 3.00007 15.0365 3.568 16.1904 4.59581C17.3443 5.62361 18.0803 7.03962 18.2584 8.5746C19.3782 8.87998 20.3552 9.56919 21.0184 10.5218C21.6816 11.4744 21.989 12.6297 21.8869 13.786C21.7847 14.9422 21.2794 16.0257 20.4594 16.8472C19.6394 17.6687 18.5567 18.1759 17.4007 18.2802V16.4676C17.8149 16.4085 18.2131 16.2674 18.5721 16.0527C18.9312 15.8379 19.2439 15.5539 19.4919 15.217C19.74 14.8801 19.9184 14.4972 20.0169 14.0906C20.1153 13.6839 20.1318 13.2618 20.0653 12.8488C19.9989 12.4357 19.8508 12.0401 19.6298 11.6849C19.4087 11.3297 19.1191 11.0221 18.7779 10.78C18.4367 10.538 18.0506 10.3663 17.6424 10.2751C17.2341 10.1838 16.8117 10.1748 16.3999 10.2486C16.5409 9.5924 16.5332 8.91297 16.3776 8.2601C16.222 7.60722 15.9223 6.99743 15.5004 6.47538C15.0786 5.95333 14.5454 5.53225 13.9397 5.24298C13.3341 4.9537 12.6714 4.80357 12.0003 4.80357C11.3291 4.80357 10.6664 4.9537 10.0608 5.24298C9.45515 5.53225 8.92189 5.95333 8.50007 6.47538C8.07825 6.99743 7.77854 7.60722 7.62291 8.2601C7.46728 8.91297 7.45966 9.5924 7.60061 10.2486C6.7795 10.0944 5.93076 10.2727 5.24112 10.7443C4.55147 11.2159 4.0774 11.9421 3.92321 12.7632C3.76901 13.5843 3.94731 14.433 4.41889 15.1227C4.89047 15.8123 5.6167 16.2864 6.43781 16.4406L6.59981 16.4676V18.2802C5.44371 18.1761 4.36097 17.669 3.54083 16.8476C2.72068 16.0261 2.2153 14.9426 2.11301 13.7863C2.01073 12.6301 2.31804 11.4747 2.98124 10.522C3.64444 9.56934 4.62134 8.88005 5.74121 8.5746C5.91914 7.03954 6.65507 5.62342 7.80903 4.59558C8.96298 3.56774 10.4545 2.99988 11.9998 3Z",fill:"var(--fc-secondary-text)"})])],-1)),l("div",E,n(v.$t("Choose a file or drag & drop it here.")),1),l("div",k,[c(I,null,{default:m(()=>[f(n(v.$t("Browse File")),1)],void 0,!0),_:1})])])],void 0),_:1},8,["action","on-error","on-remove","on-exceed","on-success","class"]),c(Y,{error:R.errors.get("file")},null,8,["error"]),l("div",F,[l("div",U,[l("div",M,n(v.$t("Only CSV files are allowed.")),1),c(I,{size:"small",link:"",onClick:j.sample},{default:m(()=>[l("span",N,[c(Z,{"icon-name":"import"})]),f(" "+n(v.$t("Download Sample CSV")),1)],void 0),_:1},8,["onClick"])])])])):_("",!0)])])}]]);export{R as C}; diff --git a/wp-content/plugins/fluent-crm/assets/CustomIcon.js b/wp-content/plugins/fluent-crm/assets/CustomIcon.js new file mode 100644 index 0000000..5e33cf6 --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/CustomIcon.js @@ -0,0 +1 @@ +import{E as C}from"./vendor-element-plus.js?ver=3.1.8";import{aQ as t,W as e,X as l,Z as n,Y as o,a5 as r,ab as i}from"./vendor.js?ver=3.1.8";import{_ as w}from"./fc-bits-ui.js?ver=3.1.8";const s={key:0,xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor","stroke-width":"2","stroke-linecap":"round","stroke-linejoin":"round"},h={key:1,xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor","stroke-width":"2","stroke-linecap":"round","stroke-linejoin":"round"},H={key:2,xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor","stroke-width":"2","stroke-linecap":"round","stroke-linejoin":"round"},V={key:3,viewBox:"0 0 100 100",xmlns:"http://www.w3.org/2000/svg"},g={key:4,class:"fcrm-smartcode-icon","aria-hidden":"true"},p={key:5,width:"18",height:"18",viewBox:"0 0 18 18",fill:"none",xmlns:"http://www.w3.org/2000/svg"},M={key:6,width:"18",height:"18",viewBox:"0 0 18 18",fill:"none",xmlns:"http://www.w3.org/2000/svg"},a={key:7,width:"20",height:"20",viewBox:"0 0 20 20",fill:"none",xmlns:"http://www.w3.org/2000/svg"},v={key:8,xmlns:"http://www.w3.org/2000/svg",width:"14",height:"16",viewBox:"0 0 14 16",fill:"none"},Z={key:9,xmlns:"http://www.w3.org/2000/svg",width:"14",height:"14",viewBox:"0 0 14 14",fill:"none"},d={key:10,width:"20",height:"20",viewBox:"0 0 20 20",fill:"none",xmlns:"http://www.w3.org/2000/svg"},L={key:11,xmlns:"http://www.w3.org/2000/svg",width:"20",height:"20",viewBox:"0 0 20 20",fill:"none"},u={key:12,xmlns:"http://www.w3.org/2000/svg",width:"20",height:"20",viewBox:"0 0 20 20",fill:"none"},y={key:13,xmlns:"http://www.w3.org/2000/svg",width:"20",height:"20",viewBox:"0 0 20 20",fill:"none"},x={key:14,xmlns:"http://www.w3.org/2000/svg",width:"20",height:"20",viewBox:"0 0 20 20",fill:"none"},f={key:15,xmlns:"http://www.w3.org/2000/svg",width:"20",height:"20",viewBox:"0 0 20 20",fill:"none"},c={key:16,xmlns:"http://www.w3.org/2000/svg",width:"20",height:"20",viewBox:"0 0 20 20",fill:"none"},k={key:17,xmlns:"http://www.w3.org/2000/svg",width:"20",height:"20",viewBox:"0 0 20 20",fill:"none"},m={key:18,xmlns:"http://www.w3.org/2000/svg",width:"20",height:"20",viewBox:"0 0 20 20",fill:"none"},B={key:19,xmlns:"http://www.w3.org/2000/svg",width:"14",height:"15",viewBox:"0 0 14 15",fill:"none"},_={key:20,xmlns:"http://www.w3.org/2000/svg",width:"20",height:"20",viewBox:"0 0 20 20",fill:"none"},b={key:21,xmlns:"http://www.w3.org/2000/svg",width:"20",height:"20",viewBox:"0 0 20 20",fill:"none"},j={key:22,xmlns:"http://www.w3.org/2000/svg",width:"20",height:"20",viewBox:"0 0 20 20",fill:"none"},q={key:23,xmlns:"http://www.w3.org/2000/svg",width:"20",height:"20",viewBox:"0 0 20 20",fill:"none"},Q={key:24,xmlns:"http://www.w3.org/2000/svg",width:"20",height:"20",viewBox:"0 0 20 20",fill:"none"},E={key:25,xmlns:"http://www.w3.org/2000/svg",width:"20",height:"20",viewBox:"0 0 20 20",fill:"none"},F={key:26,xmlns:"http://www.w3.org/2000/svg",width:"20",height:"20",viewBox:"0 0 20 20",fill:"none"},I={key:27,xmlns:"http://www.w3.org/2000/svg",width:"20",height:"20",viewBox:"0 0 20 20",fill:"none"},W={key:28,xmlns:"http://www.w3.org/2000/svg",width:"20",height:"20",viewBox:"0 0 20 20",fill:"none"},X={key:29,xmlns:"http://www.w3.org/2000/svg",width:"15",height:"15",viewBox:"0 0 15 15",fill:"none"},Y={key:30,xmlns:"http://www.w3.org/2000/svg",width:"17",height:"16",viewBox:"0 0 17 16",fill:"none"},z={key:31,width:"20",height:"20",viewBox:"0 0 20 20",fill:"none",xmlns:"http://www.w3.org/2000/svg"},A={key:32,width:"20",height:"20",viewBox:"0 0 20 20",fill:"none",xmlns:"http://www.w3.org/2000/svg"},D={key:33,width:"20",height:"20",viewBox:"0 0 20 20",fill:"none",xmlns:"http://www.w3.org/2000/svg"},G={key:34,width:"20",height:"20",viewBox:"0 0 20 20",fill:"none",xmlns:"http://www.w3.org/2000/svg"},J={key:35,width:"20",height:"20",viewBox:"0 0 20 20",fill:"none",xmlns:"http://www.w3.org/2000/svg"},K={key:36,width:"20",height:"20",viewBox:"0 0 20 20",fill:"none",xmlns:"http://www.w3.org/2000/svg"},N={key:37,width:"20",height:"20",viewBox:"0 0 20 20",fill:"none",xmlns:"http://www.w3.org/2000/svg"},O={key:38,width:"20",height:"20",viewBox:"0 0 20 20",fill:"none",xmlns:"http://www.w3.org/2000/svg"};const P=w({name:"CustomIcon",props:["type"]},[["render",function(w,P,R,S,T,U){const $=t("QuestionFilled"),CC=C;return"company"==R.type?(e(),l("svg",s,[...P[0]||(P[0]=[n("rect",{x:"3",y:"3",width:"18",height:"18",rx:"2",ry:"2"},null,-1),n("line",{x1:"3",y1:"9",x2:"21",y2:"9"},null,-1),n("line",{x1:"9",y1:"3",x2:"9",y2:"21"},null,-1),n("line",{x1:"15",y1:"3",x2:"15",y2:"21"},null,-1)])])):"campaign"==R.type?(e(),l("svg",h,[...P[1]||(P[1]=[n("rect",{x:"3",y:"3",width:"18",height:"18",rx:"2",ry:"2"},null,-1),n("line",{x1:"3",y1:"9",x2:"21",y2:"9"},null,-1),n("polyline",{points:"15 15 12 18 9 15"},null,-1)])])):"event-tracking"==R.type?(e(),l("svg",H,[...P[2]||(P[2]=[n("path",{d:"M3 3v18h18"},null,-1),n("path",{d:"M18 9l-5 5-2-2-3 3"},null,-1),n("path",{d:"M15 6h6v6"},null,-1)])])):"failed-emails"==R.type?(e(),l("svg",V,[...P[3]||(P[3]=[n("path",{d:"M50 15 L85 80 C87 83 85 85 82 85 L18 85 C15 85 13 83 15 80 L50 15",fill:"white",stroke:"CurrentColor","stroke-width":"6","stroke-linejoin":"round"},null,-1),n("path",{d:"M50 35 L50 60 M50 65 L50 70",stroke:"CurrentColor","stroke-width":"6","stroke-linecap":"round"},null,-1)])])):"smartcode"==R.type?(e(),l("span",g,[...P[4]||(P[4]=[n("svg",{width:"20",height:"20",viewBox:"0 0 20 20",fill:"none",xmlns:"http://www.w3.org/2000/svg"},[n("path",{d:"M8.75 5.25C7.36929 5.25 6.25 6.36929 6.25 7.75V8.75C6.25 9.30228 5.80228 9.75 5.25 9.75H4.5V11.25H5.25C5.80228 11.25 6.25 11.6977 6.25 12.25V13.25C6.25 14.6307 7.36929 15.75 8.75 15.75H9.5V14.25H8.75C8.19772 14.25 7.75 13.8023 7.75 13.25V12.25C7.75 11.6891 7.56406 11.1716 7.25051 10.7556C7.56406 10.3384 7.75 9.82091 7.75 9.25V7.75C7.75 7.19772 8.19772 6.75 8.75 6.75H9.5V5.25H8.75Z",fill:"currentColor"}),n("path",{d:"M11.25 6.75C11.8023 6.75 12.25 7.19772 12.25 7.75V9.25C12.25 9.82091 12.4359 10.3384 12.7495 10.7556C12.4359 11.1716 12.25 11.6891 12.25 12.25V13.25C12.25 13.8023 11.8023 14.25 11.25 14.25H10.5V15.75H11.25C12.6307 15.75 13.75 14.6307 13.75 13.25V12.25C13.75 11.6977 14.1977 11.25 14.75 11.25H15.5V9.75H14.75C14.1977 9.75 13.75 9.30228 13.75 8.75V7.75C13.75 6.36929 12.6307 5.25 11.25 5.25H10.5V6.75H11.25Z",fill:"currentColor"})],-1)])])):"total_subscribers"==R.type?(e(),l("svg",p,[...P[5]||(P[5]=[n("path",{d:"M2.25 15.75C2.25 14.3178 2.81893 12.9443 3.83162 11.9316C4.84432 10.9189 6.21783 10.35 7.65 10.35C9.08217 10.35 10.4557 10.9189 11.4684 11.9316C12.4811 12.9443 13.05 14.3178 13.05 15.75H11.7C11.7 14.6758 11.2733 13.6457 10.5138 12.8862C9.75426 12.1266 8.72413 11.7 7.65 11.7C6.57587 11.7 5.54574 12.1266 4.78622 12.8862C4.0267 13.6457 3.6 14.6758 3.6 15.75H2.25ZM7.65 9.67495C5.41237 9.67495 3.6 7.86258 3.6 5.62495C3.6 3.38733 5.41237 1.57495 7.65 1.57495C9.88762 1.57495 11.7 3.38733 11.7 5.62495C11.7 7.86258 9.88762 9.67495 7.65 9.67495ZM7.65 8.32495C9.14175 8.32495 10.35 7.1167 10.35 5.62495C10.35 4.1332 9.14175 2.92495 7.65 2.92495C6.15825 2.92495 4.95 4.1332 4.95 5.62495C4.95 7.1167 6.15825 8.32495 7.65 8.32495ZM13.2417 10.8245C14.1903 11.2517 14.9953 11.9438 15.56 12.8176C16.1248 13.6913 16.4251 14.7096 16.425 15.75H15.075C15.0752 14.9696 14.8499 14.2059 14.4264 13.5505C14.0028 12.8952 13.399 12.3761 12.6875 12.0557L13.241 10.8245H13.2417ZM12.7773 3.20373C13.4574 3.48405 14.0389 3.96009 14.4479 4.57143C14.857 5.18277 15.0753 5.90186 15.075 6.63745C15.0753 7.56377 14.7292 8.45673 14.1047 9.14091C13.4802 9.82509 12.6225 10.251 11.7 10.3351V8.97633C12.2001 8.90469 12.6641 8.67461 13.0239 8.31986C13.3836 7.96511 13.6202 7.50437 13.6988 7.00528C13.7774 6.50619 13.6939 5.99505 13.4607 5.54689C13.2274 5.09873 12.8566 4.73715 12.4027 4.51525L12.7773 3.20373V3.20373Z",fill:"currentColor"},null,-1)])])):"total_campaigns"==R.type?(e(),l("svg",M,[...P[6]||(P[6]=[n("path",{d:"M7.01313 11.9842C7.01313 11.9842 11.6552 12.6473 13.6447 14.6368H14.3079C14.6741 14.6368 14.971 14.3399 14.971 13.9736V9.95291C15.5431 9.80569 15.9658 9.28637 15.9658 8.66837C15.9658 8.05038 15.5431 7.53106 14.971 7.38384V3.36311C14.971 2.99686 14.6741 2.69995 14.3079 2.69995H13.6447C11.6552 4.68942 7.01313 5.35258 7.01313 5.35258H4.3605C3.62799 5.35258 3.03418 5.94639 3.03418 6.6789V10.6578C3.03418 11.3904 3.62799 11.9842 4.3605 11.9842H5.02365L5.68681 15.3H7.01313V11.9842ZM8.33944 6.45422C8.79258 6.357 9.35242 6.2226 9.95708 6.04474C11.07 5.71741 12.4842 5.20179 13.6447 4.4073V12.9294C12.4842 12.135 11.07 11.6194 9.95708 11.292C9.35242 11.1142 8.79258 10.9797 8.33944 10.8825V6.45422ZM4.3605 6.6789H7.01313V10.6578H4.3605V6.6789Z",fill:"currentColor"},null,-1)])])):"email_sent"==R.type?(e(),l("svg",a,[...P[7]||(P[7]=[n("path",{d:"M17.5 16.0053C17.4986 16.2022 17.4198 16.3907 17.2806 16.5301C17.1414 16.6694 16.953 16.7484 16.756 16.75H3.244C3.04661 16.7498 2.85737 16.6712 2.71787 16.5316C2.57836 16.392 2.5 16.2026 2.5 16.0053V15.25H16V6.475L10 11.875L2.5 5.125V4C2.5 3.80109 2.57902 3.61032 2.71967 3.46967C2.86032 3.32902 3.05109 3.25 3.25 3.25H16.75C16.9489 3.25 17.1397 3.32902 17.2803 3.46967C17.421 3.61032 17.5 3.80109 17.5 4V16.0053ZM4.3255 4.75L10 9.8575L15.6745 4.75H4.3255ZM1 12.25H7V13.75H1V12.25ZM1 8.5H4.75V10H1V8.5Z",fill:"CurrentColor"},null,-1)])])):"tags"==R.type?(e(),l("svg",v,[...P[8]||(P[8]=[n("path",{d:"M0 4.34861L6.33375 0.126112C6.457 0.043883 6.60184 0 6.75 0C6.89816 0 7.043 0.043883 7.16625 0.126112L13.5 4.34861V14.8486C13.5 15.0475 13.421 15.2383 13.2803 15.3789C13.1397 15.5196 12.9489 15.5986 12.75 15.5986H0.75C0.551088 15.5986 0.360322 15.5196 0.21967 15.3789C0.0790176 15.2383 0 15.0475 0 14.8486V4.34861ZM1.5 5.15111V14.0986H12V5.15111L6.75 1.65161L1.5 5.15111ZM3.75 11.0986H9.75V12.5986H3.75V11.0986ZM3.75 8.84861H9.75V10.3486H3.75V8.84861ZM6.75 7.34861C6.35218 7.34861 5.97065 7.19058 5.68934 6.90927C5.40804 6.62797 5.25 6.24644 5.25 5.84861C5.25 5.45079 5.40804 5.06926 5.68934 4.78795C5.97065 4.50665 6.35218 4.34861 6.75 4.34861C7.14783 4.34861 7.52936 4.50665 7.81066 4.78795C8.09197 5.06926 8.25 5.45079 8.25 5.84861C8.25 6.24644 8.09197 6.62797 7.81066 6.90927C7.52936 7.19058 7.14783 7.34861 6.75 7.34861Z",fill:"CurrentColor"},null,-1)])])):"total_templates"==R.type?(e(),l("svg",Z,[...P[9]||(P[9]=[n("path",{d:"M0.75 13.5C0.551088 13.5 0.360322 13.421 0.21967 13.2803C0.0790176 13.1397 0 12.9489 0 12.75V0.75C0 0.551088 0.0790176 0.360322 0.21967 0.21967C0.360322 0.0790176 0.551088 0 0.75 0H12.75C12.9489 0 13.1397 0.0790176 13.2803 0.21967C13.421 0.360322 13.5 0.551088 13.5 0.75V12.75C13.5 12.9489 13.421 13.1397 13.2803 13.2803C13.1397 13.421 12.9489 13.5 12.75 13.5H0.75ZM3.75 5.25H1.5V12H3.75V5.25ZM12 5.25H5.25V12H12V5.25ZM12 1.5H1.5V3.75H12V1.5Z",fill:"CurrentColor"},null,-1)])])):"total_automations"==R.type||"automation"==R.type?(e(),l("svg",d,[...P[10]||(P[10]=[n("path",{d:"M11.125 2.5C11.125 2.83319 10.9802 3.13254 10.75 3.33854V4.75H14.5C15.7427 4.75 16.75 5.75736 16.75 7V14.5C16.75 15.7427 15.7427 16.75 14.5 16.75H5.5C4.25736 16.75 3.25 15.7427 3.25 14.5V7C3.25 5.75736 4.25736 4.75 5.5 4.75H9.25V3.33854C9.01982 3.13254 8.875 2.83319 8.875 2.5C8.875 1.87868 9.3787 1.375 10 1.375C10.6213 1.375 11.125 1.87868 11.125 2.5ZM5.5 6.25C5.08579 6.25 4.75 6.58579 4.75 7V14.5C4.75 14.9142 5.08579 15.25 5.5 15.25H14.5C14.9142 15.25 15.25 14.9142 15.25 14.5V7C15.25 6.58579 14.9142 6.25 14.5 6.25H10.75H9.25H5.5ZM2.5 8.5H1V13H2.5V8.5ZM17.5 8.5H19V13H17.5V8.5ZM7.75 11.875C8.37132 11.875 8.875 11.3713 8.875 10.75C8.875 10.1287 8.37132 9.625 7.75 9.625C7.12868 9.625 6.625 10.1287 6.625 10.75C6.625 11.3713 7.12868 11.875 7.75 11.875ZM12.25 11.875C12.8713 11.875 13.375 11.3713 13.375 10.75C13.375 10.1287 12.8713 9.625 12.25 9.625C11.6287 9.625 11.125 10.1287 11.125 10.75C11.125 11.3713 11.6287 11.875 12.25 11.875Z",fill:"CurrentColor"},null,-1)])])):"preview_form"==R.type?(e(),l("svg",L,[...P[11]||(P[11]=[n("path",{d:"M9.99999 3.25C14.044 3.25 17.4085 6.16 18.1142 10C17.4092 13.84 14.044 16.75 9.99999 16.75C5.95599 16.75 2.59149 13.84 1.88574 10C2.59074 6.16 5.95599 3.25 9.99999 3.25ZM9.99999 15.25C11.5296 15.2497 13.0138 14.7301 14.2096 13.7764C15.4055 12.8226 16.2422 11.4912 16.5827 10C16.2409 8.50998 15.4037 7.18 14.208 6.22752C13.0122 5.27504 11.5287 4.7564 9.99999 4.7564C8.47126 4.7564 6.98776 5.27504 5.79202 6.22752C4.59629 7.18 3.75907 8.50998 3.41724 10C3.75781 11.4912 4.5945 12.8226 5.79035 13.7764C6.9862 14.7301 8.47039 15.2497 9.99999 15.25V15.25ZM9.99999 13.375C9.10489 13.375 8.24644 13.0194 7.61351 12.3865C6.98057 11.7536 6.62499 10.8951 6.62499 10C6.62499 9.10489 6.98057 8.24645 7.61351 7.61352C8.24644 6.98058 9.10489 6.625 9.99999 6.625C10.8951 6.625 11.7535 6.98058 12.3865 7.61352C13.0194 8.24645 13.375 9.10489 13.375 10C13.375 10.8951 13.0194 11.7536 12.3865 12.3865C11.7535 13.0194 10.8951 13.375 9.99999 13.375ZM9.99999 11.875C10.4973 11.875 10.9742 11.6775 11.3258 11.3258C11.6774 10.9742 11.875 10.4973 11.875 10C11.875 9.50272 11.6774 9.02581 11.3258 8.67418C10.9742 8.32254 10.4973 8.125 9.99999 8.125C9.50271 8.125 9.0258 8.32254 8.67417 8.67418C8.32254 9.02581 8.12499 9.50272 8.12499 10C8.12499 10.4973 8.32254 10.9742 8.67417 11.3258C9.0258 11.6775 9.50271 11.875 9.99999 11.875Z",fill:"CurrentColor"},null,-1)])])):"edit_form"==R.type?(e(),l("svg",u,[...P[12]||(P[12]=[n("path",{d:"M16 17.5H4C3.80109 17.5 3.61032 17.421 3.46967 17.2803C3.32902 17.1397 3.25 16.9489 3.25 16.75V3.25C3.25 3.05109 3.32902 2.86032 3.46967 2.71967C3.61032 2.57902 3.80109 2.5 4 2.5H16C16.1989 2.5 16.3897 2.57902 16.5303 2.71967C16.671 2.86032 16.75 3.05109 16.75 3.25V16.75C16.75 16.9489 16.671 17.1397 16.5303 17.2803C16.3897 17.421 16.1989 17.5 16 17.5ZM15.25 16V4H4.75V16H15.25ZM7 6.25H13V7.75H7V6.25ZM7 9.25H13V10.75H7V9.25ZM7 12.25H10.75V13.75H7V12.25Z",fill:"CurrentColor"},null,-1)])])):"edit_connection_form"==R.type?(e(),l("svg",y,[...P[13]||(P[13]=[n("path",{d:"M10 17.5C5.85775 17.5 2.5 14.1422 2.5 10C2.5 5.85775 5.85775 2.5 10 2.5C14.1422 2.5 17.5 5.85775 17.5 10C17.5 14.1422 14.1422 17.5 10 17.5ZM8.2825 15.7502C7.54256 14.1807 7.1139 12.4827 7.02025 10.75H4.0465C4.19244 11.9042 4.67044 12.9911 5.42243 13.8788C6.17441 14.7664 7.16801 15.4166 8.2825 15.7502V15.7502ZM8.5225 10.75C8.63575 12.5792 9.1585 14.2975 10 15.814C10.8642 14.2574 11.3691 12.5271 11.4775 10.75H8.5225V10.75ZM15.9535 10.75H12.9797C12.8861 12.4827 12.4574 14.1807 11.7175 15.7502C12.832 15.4166 13.8256 14.7664 14.5776 13.8788C15.3296 12.9911 15.8076 11.9042 15.9535 10.75V10.75ZM4.0465 9.25H7.02025C7.1139 7.51734 7.54256 5.81926 8.2825 4.24975C7.16801 4.58341 6.17441 5.23356 5.42243 6.12122C4.67044 7.00888 4.19244 8.09583 4.0465 9.25V9.25ZM8.52325 9.25H11.4767C11.3686 7.47295 10.864 5.74265 10 4.186C9.13576 5.74259 8.63092 7.47289 8.5225 9.25H8.52325ZM11.7175 4.24975C12.4574 5.81926 12.8861 7.51734 12.9797 9.25H15.9535C15.8076 8.09583 15.3296 7.00888 14.5776 6.12122C13.8256 5.23356 12.832 4.58341 11.7175 4.24975V4.24975Z",fill:"CurrentColor"},null,-1)])])):"total_revenue"==R.type?(e(),l("svg",x,[...P[14]||(P[14]=[n("path",{d:"M10 17.5C5.85775 17.5 2.5 14.1423 2.5 10C2.5 5.85775 5.85775 2.5 10 2.5C14.1423 2.5 17.5 5.85775 17.5 10C17.5 14.1423 14.1423 17.5 10 17.5ZM10 16C11.5913 16 13.1174 15.3679 14.2426 14.2426C15.3679 13.1174 16 11.5913 16 10C16 8.4087 15.3679 6.88258 14.2426 5.75736C13.1174 4.63214 11.5913 4 10 4C8.4087 4 6.88258 4.63214 5.75736 5.75736C4.63214 6.88258 4 8.4087 4 10C4 11.5913 4.63214 13.1174 5.75736 14.2426C6.88258 15.3679 8.4087 16 10 16ZM7.375 11.5H11.5C11.5995 11.5 11.6948 11.4605 11.7652 11.3902C11.8355 11.3198 11.875 11.2245 11.875 11.125C11.875 11.0255 11.8355 10.9302 11.7652 10.8598C11.6948 10.7895 11.5995 10.75 11.5 10.75H8.5C8.00272 10.75 7.52581 10.5525 7.17417 10.2008C6.82254 9.84919 6.625 9.37228 6.625 8.875C6.625 8.37772 6.82254 7.90081 7.17417 7.54917C7.52581 7.19754 8.00272 7 8.5 7H9.25V5.5H10.75V7H12.625V8.5H8.5C8.40054 8.5 8.30516 8.53951 8.23483 8.60983C8.16451 8.68016 8.125 8.77554 8.125 8.875C8.125 8.97446 8.16451 9.06984 8.23483 9.14017C8.30516 9.21049 8.40054 9.25 8.5 9.25H11.5C11.9973 9.25 12.4742 9.44754 12.8258 9.79917C13.1775 10.1508 13.375 10.6277 13.375 11.125C13.375 11.6223 13.1775 12.0992 12.8258 12.4508C12.4742 12.8025 11.9973 13 11.5 13H10.75V14.5H9.25V13H7.375V11.5Z",fill:"var(--fc-secondary-text)"},null,-1)])])):"revenue_per_customers"==R.type?(e(),l("svg",f,[...P[15]||(P[15]=[n("path",{d:"M14.125 16.75C13.4288 16.75 12.7611 16.4734 12.2688 15.9812C11.7766 15.4889 11.5 14.8212 11.5 14.125C11.5 13.4288 11.7766 12.7611 12.2688 12.2688C12.7611 11.7766 13.4288 11.5 14.125 11.5C14.8212 11.5 15.4889 11.7766 15.9812 12.2688C16.4734 12.7611 16.75 13.4288 16.75 14.125C16.75 14.8212 16.4734 15.4889 15.9812 15.9812C15.4889 16.4734 14.8212 16.75 14.125 16.75ZM14.125 15.25C14.4234 15.25 14.7095 15.1315 14.9205 14.9205C15.1315 14.7095 15.25 14.4234 15.25 14.125C15.25 13.8266 15.1315 13.5405 14.9205 13.3295C14.7095 13.1185 14.4234 13 14.125 13C13.8266 13 13.5405 13.1185 13.3295 13.3295C13.1185 13.5405 13 13.8266 13 14.125C13 14.4234 13.1185 14.7095 13.3295 14.9205C13.5405 15.1315 13.8266 15.25 14.125 15.25ZM5.875 8.5C5.53028 8.5 5.18894 8.4321 4.87046 8.30018C4.55198 8.16827 4.2626 7.97491 4.01884 7.73116C3.77509 7.4874 3.58173 7.19802 3.44982 6.87954C3.3179 6.56106 3.25 6.21972 3.25 5.875C3.25 5.53028 3.3179 5.18894 3.44982 4.87046C3.58173 4.55198 3.77509 4.2626 4.01884 4.01884C4.2626 3.77509 4.55198 3.58173 4.87046 3.44982C5.18894 3.3179 5.53028 3.25 5.875 3.25C6.57119 3.25 7.23887 3.52656 7.73116 4.01884C8.22344 4.51113 8.5 5.17881 8.5 5.875C8.5 6.57119 8.22344 7.23887 7.73116 7.73116C7.23887 8.22344 6.57119 8.5 5.875 8.5ZM5.875 7C6.17337 7 6.45952 6.88147 6.6705 6.6705C6.88147 6.45952 7 6.17337 7 5.875C7 5.57663 6.88147 5.29048 6.6705 5.0795C6.45952 4.86853 6.17337 4.75 5.875 4.75C5.57663 4.75 5.29048 4.86853 5.0795 5.0795C4.86853 5.29048 4.75 5.57663 4.75 5.875C4.75 6.17337 4.86853 6.45952 5.0795 6.6705C5.29048 6.88147 5.57663 7 5.875 7ZM15.3033 3.63625L16.3638 4.69675L4.6975 16.3638L3.637 15.3033L15.3025 3.63625H15.3033Z",fill:"var(--fc-secondary-text)"},null,-1)])])):"total_customers"==R.type?(e(),l("svg",c,[...P[16]||(P[16]=[n("path",{d:"M2.5 17.5C2.5 15.9087 3.13214 14.3826 4.25736 13.2574C5.38258 12.1321 6.9087 11.5 8.5 11.5C10.0913 11.5 11.6174 12.1321 12.7426 13.2574C13.8679 14.3826 14.5 15.9087 14.5 17.5H13C13 16.3065 12.5259 15.1619 11.682 14.318C10.8381 13.4741 9.69347 13 8.5 13C7.30653 13 6.16193 13.4741 5.31802 14.318C4.47411 15.1619 4 16.3065 4 17.5H2.5ZM8.5 10.75C6.01375 10.75 4 8.73625 4 6.25C4 3.76375 6.01375 1.75 8.5 1.75C10.9862 1.75 13 3.76375 13 6.25C13 8.73625 10.9862 10.75 8.5 10.75ZM8.5 9.25C10.1575 9.25 11.5 7.9075 11.5 6.25C11.5 4.5925 10.1575 3.25 8.5 3.25C6.8425 3.25 5.5 4.5925 5.5 6.25C5.5 7.9075 6.8425 9.25 8.5 9.25ZM14.713 12.0273C15.767 12.5019 16.6615 13.2709 17.2889 14.2418C17.9164 15.2126 18.2501 16.344 18.25 17.5H16.75C16.7502 16.633 16.4999 15.7844 16.0293 15.0562C15.5587 14.328 14.8878 13.7512 14.0972 13.3953L14.7123 12.0273H14.713ZM14.197 3.55975C14.9526 3.87122 15.5987 4.40015 16.0533 5.07942C16.5078 5.75869 16.7503 6.55768 16.75 7.375C16.7503 8.40425 16.3658 9.39642 15.6719 10.1566C14.978 10.9168 14.025 11.3901 13 11.4835V9.97375C13.5557 9.89416 14.0713 9.63851 14.471 9.24434C14.8707 8.85017 15.1335 8.33824 15.2209 7.7837C15.3082 7.22916 15.2155 6.66122 14.9563 6.16327C14.6971 5.66531 14.2851 5.26356 13.7808 5.017L14.197 3.55975Z",fill:"var(--fc-secondary-text)"},null,-1)])])):"total_orders"==R.type?(e(),l("svg",k,[...P[17]||(P[17]=[n("path",{d:"M4 13V4H2.5V2.5H4.75C4.94891 2.5 5.13968 2.57902 5.28033 2.71967C5.42098 2.86032 5.5 3.05109 5.5 3.25V12.25H14.8285L16.3285 6.25H7V4.75H17.29C17.404 4.75 17.5165 4.776 17.619 4.826C17.7214 4.87601 17.8111 4.94871 17.8813 5.03859C17.9514 5.12847 18.0001 5.23315 18.0237 5.34468C18.0473 5.45622 18.0452 5.57166 18.0175 5.68225L16.1425 13.1823C16.1019 13.3444 16.0082 13.4884 15.8764 13.5913C15.7446 13.6941 15.5822 13.75 15.415 13.75H4.75C4.55109 13.75 4.36032 13.671 4.21967 13.5303C4.07902 13.3897 4 13.1989 4 13ZM5.5 18.25C5.10218 18.25 4.72064 18.092 4.43934 17.8107C4.15804 17.5294 4 17.1478 4 16.75C4 16.3522 4.15804 15.9706 4.43934 15.6893C4.72064 15.408 5.10218 15.25 5.5 15.25C5.89783 15.25 6.27936 15.408 6.56066 15.6893C6.84197 15.9706 7 16.3522 7 16.75C7 17.1478 6.84197 17.5294 6.56066 17.8107C6.27936 18.092 5.89783 18.25 5.5 18.25ZM14.5 18.25C14.1022 18.25 13.7206 18.092 13.4393 17.8107C13.158 17.5294 13 17.1478 13 16.75C13 16.3522 13.158 15.9706 13.4393 15.6893C13.7206 15.408 14.1022 15.25 14.5 15.25C14.8978 15.25 15.2794 15.408 15.5607 15.6893C15.842 15.9706 16 16.3522 16 16.75C16 17.1478 15.842 17.5294 15.5607 17.8107C15.2794 18.092 14.8978 18.25 14.5 18.25Z",fill:"var(--fc-secondary-text)"},null,-1)])])):"total_students"==R.type?(e(),l("svg",m,[...P[18]||(P[18]=[n("path",{d:"M10 9.25C10.9946 9.25 11.9484 9.64509 12.6517 10.3483C13.3549 11.0516 13.75 12.0054 13.75 13V17.5H12.25V13C12.25 12.4261 12.0308 11.8739 11.637 11.4563C11.2433 11.0387 10.7049 10.7874 10.132 10.7537L10 10.75C9.42609 10.75 8.87386 10.9692 8.4563 11.363C8.03874 11.7567 7.78742 12.2951 7.75375 12.868L7.75 13V17.5H6.25V13C6.25 12.0054 6.64509 11.0516 7.34835 10.3483C8.05161 9.64509 9.00544 9.25 10 9.25ZM5.125 11.5C5.33425 11.5 5.5375 11.5247 5.7325 11.5705C5.60427 11.9523 5.52833 12.3496 5.50675 12.7517L5.5 13V13.0645C5.41379 13.0337 5.32412 13.0135 5.233 13.0045L5.125 13C4.84534 13 4.57571 13.1042 4.36869 13.2922C4.16166 13.4802 4.0321 13.7386 4.00525 14.017L4 14.125V17.5H2.5V14.125C2.5 13.4288 2.77656 12.7611 3.26884 12.2688C3.76113 11.7766 4.42881 11.5 5.125 11.5ZM14.875 11.5C15.5712 11.5 16.2389 11.7766 16.7312 12.2688C17.2234 12.7611 17.5 13.4288 17.5 14.125V17.5H16V14.125C16 13.8453 15.8958 13.5757 15.7078 13.3687C15.5198 13.1617 15.2614 13.0321 14.983 13.0052L14.875 13C14.7437 13 14.6178 13.0225 14.5 13.0637V13C14.5 12.5005 14.419 12.0205 14.2682 11.572C14.4625 11.5247 14.6657 11.5 14.875 11.5ZM5.125 7C5.62228 7 6.09919 7.19754 6.45083 7.54917C6.80246 7.90081 7 8.37772 7 8.875C7 9.37228 6.80246 9.84919 6.45083 10.2008C6.09919 10.5525 5.62228 10.75 5.125 10.75C4.62772 10.75 4.15081 10.5525 3.79917 10.2008C3.44754 9.84919 3.25 9.37228 3.25 8.875C3.25 8.37772 3.44754 7.90081 3.79917 7.54917C4.15081 7.19754 4.62772 7 5.125 7ZM14.875 7C15.3723 7 15.8492 7.19754 16.2008 7.54917C16.5525 7.90081 16.75 8.37772 16.75 8.875C16.75 9.37228 16.5525 9.84919 16.2008 10.2008C15.8492 10.5525 15.3723 10.75 14.875 10.75C14.3777 10.75 13.9008 10.5525 13.5492 10.2008C13.1975 9.84919 13 9.37228 13 8.875C13 8.37772 13.1975 7.90081 13.5492 7.54917C13.9008 7.19754 14.3777 7 14.875 7ZM5.125 8.5C5.02554 8.5 4.93016 8.53951 4.85984 8.60983C4.78951 8.68016 4.75 8.77554 4.75 8.875C4.75 8.97446 4.78951 9.06984 4.85984 9.14017C4.93016 9.21049 5.02554 9.25 5.125 9.25C5.22446 9.25 5.31984 9.21049 5.39016 9.14017C5.46049 9.06984 5.5 8.97446 5.5 8.875C5.5 8.77554 5.46049 8.68016 5.39016 8.60983C5.31984 8.53951 5.22446 8.5 5.125 8.5ZM14.875 8.5C14.7755 8.5 14.6802 8.53951 14.6098 8.60983C14.5395 8.68016 14.5 8.77554 14.5 8.875C14.5 8.97446 14.5395 9.06984 14.6098 9.14017C14.6802 9.21049 14.7755 9.25 14.875 9.25C14.9745 9.25 15.0698 9.21049 15.1402 9.14017C15.2105 9.06984 15.25 8.97446 15.25 8.875C15.25 8.77554 15.2105 8.68016 15.1402 8.60983C15.0698 8.53951 14.9745 8.5 14.875 8.5ZM10 2.5C10.7956 2.5 11.5587 2.81607 12.1213 3.37868C12.6839 3.94129 13 4.70435 13 5.5C13 6.29565 12.6839 7.05871 12.1213 7.62132C11.5587 8.18393 10.7956 8.5 10 8.5C9.20435 8.5 8.44129 8.18393 7.87868 7.62132C7.31607 7.05871 7 6.29565 7 5.5C7 4.70435 7.31607 3.94129 7.87868 3.37868C8.44129 2.81607 9.20435 2.5 10 2.5ZM10 4C9.60218 4 9.22064 4.15804 8.93934 4.43934C8.65804 4.72064 8.5 5.10218 8.5 5.5C8.5 5.89782 8.65804 6.27936 8.93934 6.56066C9.22064 6.84196 9.60218 7 10 7C10.3978 7 10.7794 6.84196 11.0607 6.56066C11.342 6.27936 11.5 5.89782 11.5 5.5C11.5 5.10218 11.342 4.72064 11.0607 4.43934C10.7794 4.15804 10.3978 4 10 4Z",fill:"var(--fc-secondary-text)"},null,-1)])])):"total_enrollments"==R.type?(e(),l("svg",B,[...P[19]||(P[19]=[n("path",{d:"M0 12.375V2.25C0 1.65326 0.237053 1.08097 0.65901 0.65901C1.08097 0.237053 1.65326 0 2.25 0H12.75C12.9489 0 13.1397 0.0790176 13.2803 0.21967C13.421 0.360322 13.5 0.551088 13.5 0.75V14.25C13.5 14.4489 13.421 14.6397 13.2803 14.7803C13.1397 14.921 12.9489 15 12.75 15H2.625C1.92881 15 1.26113 14.7234 0.768845 14.2312C0.276562 13.7389 0 13.0712 0 12.375V12.375ZM12 13.5V11.25H2.625C2.32663 11.25 2.04048 11.3685 1.8295 11.5795C1.61853 11.7905 1.5 12.0766 1.5 12.375C1.5 12.6734 1.61853 12.9595 1.8295 13.1705C2.04048 13.3815 2.32663 13.5 2.625 13.5H12ZM5.25 1.5H2.25C2.05109 1.5 1.86032 1.57902 1.71967 1.71967C1.57902 1.86032 1.5 2.05109 1.5 2.25V10.0027C1.85152 9.83583 2.23586 9.74948 2.625 9.75H12V1.5H10.5V7.5L7.875 6L5.25 7.5V1.5Z",fill:"var(--fc-secondary-text)"},null,-1)])])):"total_membership_enrollments"==R.type?(e(),l("svg",_,[...P[20]||(P[20]=[n("path",{d:"M5.30108 7.03475C6.28666 5.47667 8.02305 4.44455 9.99998 4.44455C11.9769 4.44455 13.7133 5.47667 14.6989 7.03475L15.8727 6.29227C14.6432 4.34871 12.4729 3.05566 9.99998 3.05566C7.52711 3.05566 5.35673 4.34871 4.12731 6.29227L5.30108 7.03475ZM9.99998 15.5557C8.02305 15.5557 6.28666 14.5236 5.30108 12.9655L4.12731 13.708C5.35673 15.6515 7.52711 16.9446 9.99998 16.9446C12.4729 16.9446 14.6432 15.6515 15.8727 13.708L14.6989 12.9655C13.7133 14.5236 11.9769 15.5557 9.99998 15.5557ZM9.99984 7.22233C10.3834 7.22233 10.6943 7.53325 10.6943 7.91677C10.6943 8.3003 10.3834 8.61122 9.99984 8.61122C9.6163 8.61122 9.30539 8.3003 9.30539 7.91677C9.30539 7.53325 9.6163 7.22233 9.99984 7.22233ZM9.99984 10.0001C11.1505 10.0001 12.0832 9.0674 12.0832 7.91677C12.0832 6.76618 11.1505 5.83344 9.99984 5.83344C8.84921 5.83344 7.9165 6.76618 7.9165 7.91677C7.9165 9.0674 8.84921 10.0001 9.99984 10.0001ZM9.99984 12.0834C9.23275 12.0834 8.61095 12.7052 8.61095 13.4723H7.22206C7.22206 11.9382 8.46571 10.6946 9.99984 10.6946C11.5339 10.6946 12.7776 11.9382 12.7776 13.4723H11.3887C11.3887 12.7052 10.7669 12.0834 9.99984 12.0834ZM3.74984 9.30566C3.36631 9.30566 3.05539 9.61657 3.05539 10.0001C3.05539 10.3836 3.36631 10.6946 3.74984 10.6946C4.13336 10.6946 4.44428 10.3836 4.44428 10.0001C4.44428 9.61657 4.13336 9.30566 3.74984 9.30566ZM1.6665 10.0001C1.6665 8.84948 2.59925 7.91677 3.74984 7.91677C4.90043 7.91677 5.83317 8.84948 5.83317 10.0001C5.83317 11.1507 4.90043 12.0834 3.74984 12.0834C2.59925 12.0834 1.6665 11.1507 1.6665 10.0001ZM15.5554 10.0001C15.5554 9.61657 15.8663 9.30566 16.2498 9.30566C16.6334 9.30566 16.9443 9.61657 16.9443 10.0001C16.9443 10.3836 16.6334 10.6946 16.2498 10.6946C15.8663 10.6946 15.5554 10.3836 15.5554 10.0001ZM16.2498 7.91677C15.0992 7.91677 14.1665 8.84948 14.1665 10.0001C14.1665 11.1507 15.0992 12.0834 16.2498 12.0834C17.4005 12.0834 18.3332 11.1507 18.3332 10.0001C18.3332 8.84948 17.4005 7.91677 16.2498 7.91677Z",fill:"var(--fc-secondary-text)"},null,-1)])])):"avarage_order_value"==R.type?(e(),l("svg",b,[...P[21]||(P[21]=[n("path",{d:"M10.0007 17.5C9.66755 17.5 9.3488 17.4372 9.0445 17.3116C8.7402 17.186 8.46864 17.0072 8.22981 16.7753C7.95316 16.4988 7.68681 16.3082 7.43075 16.2036C7.17468 16.099 6.85066 16.0468 6.45869 16.0468C5.7628 16.0468 5.17125 15.8032 4.68404 15.316C4.19684 14.8288 3.95324 14.2372 3.95324 13.5413C3.95324 13.1509 3.90079 12.8272 3.79588 12.57C3.69098 12.3128 3.50057 12.0462 3.22467 11.7702C2.98584 11.5314 2.80535 11.2597 2.68321 10.9551C2.56107 10.6505 2.5 10.332 2.5 9.99963C2.5 9.66718 2.56281 9.3488 2.68842 9.0445C2.81403 8.7402 2.99278 8.47211 3.22467 8.24023C3.50119 7.9637 3.69178 7.69735 3.79644 7.44116C3.90097 7.1851 3.95324 6.85761 3.95324 6.45869C3.95324 5.76565 4.19684 5.17658 4.68404 4.69148C5.17125 4.20639 5.7628 3.96384 6.45869 3.96384C6.83826 3.96384 7.15918 3.91151 7.42144 3.80685C7.68371 3.70232 7.95316 3.51173 8.22981 3.23508C8.46157 3.00332 8.73059 2.82284 9.03688 2.69363C9.34329 2.56454 9.66358 2.5 9.99777 2.5C10.332 2.5 10.6512 2.56454 10.9555 2.69363C11.2598 2.82284 11.5279 3.00332 11.7598 3.23508C12.0363 3.51173 12.3027 3.70232 12.5588 3.80685C12.8149 3.91151 13.1424 3.96384 13.5413 3.96384C14.2344 3.96384 14.8234 4.20639 15.3085 4.69148C15.7936 5.17658 16.0362 5.76565 16.0362 6.45869C16.0362 6.83826 16.0885 7.16092 16.1931 7.42665C16.2977 7.69251 16.4883 7.9637 16.7649 8.24023C16.9967 8.47211 17.1772 8.74033 17.3064 9.04488C17.4355 9.34955 17.5 9.66805 17.5 10.0004C17.5 10.3328 17.437 10.6519 17.311 10.9575C17.1849 11.2632 17.0029 11.5341 16.7649 11.7702C16.4889 12.0478 16.2984 12.3127 16.1935 12.5648C16.0886 12.8169 16.0362 13.1424 16.0362 13.5413C16.0362 14.2372 15.7936 14.8288 15.3085 15.316C14.8234 15.8032 14.2344 16.0468 13.5413 16.0468C13.1513 16.0468 12.8278 16.0973 12.5707 16.1984C12.3137 16.2994 12.0468 16.4883 11.7702 16.7649C11.5343 17.0029 11.2637 17.1849 10.9583 17.311C10.653 17.437 10.3338 17.5 10.0007 17.5ZM9.9987 15.6876C10.0955 15.6876 10.1827 15.6729 10.26 15.6435C10.3374 15.6141 10.4125 15.563 10.4853 15.4902C10.9419 15.0282 11.4012 14.7034 11.8632 14.5158C12.3252 14.3282 12.8881 14.2344 13.5519 14.2344C13.7452 14.2344 13.9073 14.1689 14.0381 14.0381C14.1689 13.9073 14.2344 13.7452 14.2344 13.5519C14.2344 12.8941 14.327 12.3359 14.5122 11.8773C14.6975 11.4188 15.0235 10.9548 15.4902 10.4853C15.6218 10.3537 15.6876 10.192 15.6876 10C15.6876 9.80804 15.6218 9.64628 15.4902 9.51472C15.0282 9.05269 14.7034 8.59215 14.5158 8.13309C14.3282 7.67391 14.2344 7.11578 14.2344 6.45869C14.2344 6.26525 14.1689 6.10312 14.0381 5.9723C13.9073 5.84148 13.7452 5.77606 13.5519 5.77606C12.9023 5.77606 12.3443 5.68226 11.8779 5.49464C11.4115 5.30703 10.9473 4.98221 10.4853 4.52017C10.4125 4.44739 10.3391 4.3963 10.2652 4.36691C10.1913 4.33752 10.1029 4.32283 10 4.32283C9.89708 4.32283 9.80736 4.3374 9.73085 4.36654C9.65435 4.39555 9.5823 4.44329 9.51472 4.50976C9.05108 4.97873 8.58762 5.30703 8.12435 5.49464C7.66095 5.68226 7.1022 5.77606 6.44809 5.77606C6.25477 5.77606 6.0927 5.84148 5.96188 5.9723C5.83106 6.10312 5.76565 6.26525 5.76565 6.45869C5.76565 7.11665 5.67184 7.67497 5.48423 8.13365C5.29661 8.59233 4.97179 9.05269 4.50976 9.51472C4.37819 9.64628 4.31241 9.80804 4.31241 10C4.31241 10.192 4.37819 10.3537 4.50976 10.4853C4.97179 10.9473 5.29661 11.4088 5.48423 11.8699C5.67184 12.3309 5.76565 12.8916 5.76565 13.5519C5.76565 13.7452 5.83106 13.9073 5.96188 14.0381C6.0927 14.1689 6.25477 14.2344 6.44809 14.2344C7.10617 14.2344 7.66455 14.3282 8.12323 14.5158C8.58192 14.7034 9.04227 15.0282 9.5043 15.4902C9.57709 15.563 9.65348 15.6141 9.73346 15.6435C9.81344 15.6729 9.90185 15.6876 9.9987 15.6876ZM12.0719 13.3246C12.4198 13.3246 12.7156 13.2017 12.9593 12.9558C13.2028 12.7099 13.3246 12.4113 13.3246 12.0602C13.3246 11.7089 13.2024 11.4107 12.958 11.1657C12.7136 10.9207 12.4168 10.7981 12.0676 10.7981C11.7184 10.7981 11.4213 10.9211 11.1763 11.167C10.9313 11.4129 10.8087 11.7114 10.8087 12.0626C10.8087 12.4139 10.9316 12.712 11.1772 12.9571C11.4227 13.2021 11.721 13.3246 12.0719 13.3246ZM8.16174 12.976L12.9308 8.21754L11.7825 7.06916L7.02396 11.8383L8.16174 12.976ZM8.83432 8.82279C9.07935 8.57727 9.20186 8.27904 9.20186 7.92812C9.20186 7.58017 9.07892 7.28436 8.83302 7.0407C8.58712 6.79716 8.28859 6.67539 7.93742 6.67539C7.58612 6.67539 7.28796 6.79759 7.04293 7.042C6.7979 7.28641 6.67539 7.58321 6.67539 7.93239C6.67539 8.28158 6.79834 8.57869 7.04423 8.82372C7.29013 9.06875 7.58866 9.19126 7.93983 9.19126C8.29113 9.19126 8.58929 9.06844 8.83432 8.82279Z",fill:"var(--fc-secondary-text)"},null,-1)])])):"avarage_order_or_customer"==R.type?(e(),l("svg",j,[...P[22]||(P[22]=[n("path",{d:"M10.6757 17.5L3.5 10.3275V3.5H10.3271L17.5022 10.6405L10.6757 17.5ZM10.6757 15.2073L15.2165 10.6662L9.65109 5.11949H5.11949V9.66051L10.6757 15.2073ZM6.68627 7.66011C6.95618 7.66011 7.18594 7.56564 7.37554 7.3767C7.56525 7.18776 7.66011 6.95833 7.66011 6.68841C7.66011 6.4185 7.56564 6.18868 7.3767 5.99897C7.18776 5.80926 6.95833 5.71441 6.68841 5.71441C6.4185 5.71441 6.18869 5.80888 5.99897 5.99782C5.80926 6.18676 5.71441 6.41624 5.71441 6.68627C5.71441 6.95618 5.80888 7.18594 5.99782 7.37554C6.18676 7.56525 6.41624 7.66011 6.68627 7.66011ZM10.6757 13.5777L12.8798 11.3648C12.9877 11.2565 13.0692 11.1333 13.1242 10.9951C13.1793 10.8568 13.2069 10.7094 13.2069 10.5527C13.2069 10.2395 13.1011 9.96876 12.8896 9.74049C12.6779 9.51233 12.4222 9.39825 12.1225 9.39825C11.9017 9.39825 11.6853 9.46121 11.4732 9.58714C11.261 9.71317 10.9966 9.94695 10.68 10.2885C10.3322 9.94122 10.0598 9.70601 9.86278 9.58284C9.6658 9.45978 9.45824 9.39825 9.2401 9.39825C8.93736 9.39825 8.67631 9.51112 8.45696 9.73686C8.23762 9.9627 8.12794 10.2326 8.12794 10.5464C8.12794 10.6955 8.15769 10.8422 8.21718 10.9867C8.27678 11.1312 8.35869 11.2573 8.46291 11.3648L10.6757 13.5777Z",fill:"var(--fc-secondary-text)"},null,-1)])])):"draft_revenue"==R.type?(e(),l("svg",q,[...P[23]||(P[23]=[n("path",{d:"M4.55979 17.1708C4.08091 17.1708 3.67299 17.0023 3.33604 16.6654C2.9991 16.3284 2.83063 15.9205 2.83063 15.4416V4.5612C2.83063 4.08231 2.9991 3.67439 3.33604 3.33745C3.67299 3.0005 4.08091 2.83203 4.55979 2.83203H15.4402C15.9191 2.83203 16.327 3.0005 16.664 3.33745C17.0009 3.67439 17.1694 4.08231 17.1694 4.5612V15.4416C17.1694 15.9205 17.0009 16.3284 16.664 16.6654C16.327 17.0023 15.9191 17.1708 15.4402 17.1708H12.1146V15.4416H15.4402V6.14099H4.55979V15.4416H7.88542V17.1708H4.55979ZM9.13542 17.1708V12.1356L8.02271 13.2483L6.80063 12.0312L10 8.83203L13.1994 12.0312L11.9773 13.2483L10.8646 12.1356V17.1708H9.13542Z",fill:"var(--fc-secondary-text)"},null,-1)])])):"lost_revenue"==R.type?(e(),l("svg",Q,[...P[24]||(P[24]=[n("path",{d:"M12.5996 15.9974V13.9558H14.9075L11.0798 10.1252L7.91312 13.2918L1.59958 6.99016L3.05062 5.55078L7.91312 10.4131L11.0798 7.24641L16.3587 12.5164V10.2085H18.4004V15.9974H12.5996Z",fill:"var(--fc-secondary-text)"},null,-1)])])):"processing_revenue"==R.type?(e(),l("svg",E,[...P[25]||(P[25]=[n("path",{d:"M8.02573 15.6667C6.69084 15.1111 5.62417 14.2407 4.82573 13.0556C4.02729 11.8704 3.62807 10.5617 3.62807 9.12963C3.62807 8.73457 3.65926 8.34877 3.72164 7.97222C3.78402 7.59568 3.87758 7.22222 4.00234 6.85185L2.67368 7.61111L2 6.44444L5.49942 4.44444L7.52047 7.92593L6.36023 8.59259L5.40585 6.96296C5.26862 7.30864 5.16257 7.66358 5.08772 8.02778C5.01287 8.39198 4.97544 8.76543 4.97544 9.14815C4.97544 10.3457 5.3154 11.429 5.99532 12.3981C6.67524 13.3673 7.58285 14.0679 8.71813 14.5L8.02573 15.6667ZM13.5088 6.88889V5.55556H15.4175C14.8811 4.87654 14.2136 4.33642 13.4152 3.93519C12.6168 3.53395 11.7497 3.33333 10.814 3.33333C10.1154 3.33333 9.46043 3.45062 8.84912 3.68519C8.23782 3.91975 7.67641 4.23457 7.16491 4.62963L6.47251 3.46296C7.07134 3.00617 7.73879 2.64815 8.47485 2.38889C9.21092 2.12963 9.99064 2 10.814 2C11.8994 2 12.9006 2.21914 13.8175 2.65741C14.7345 3.09568 15.5298 3.68519 16.2035 4.42593V2.88889H17.5509V6.88889H13.5088ZM12.8725 18L9.3731 16L11.3942 12.537L12.5731 13.2037L11.6187 14.8333C13.0534 14.6235 14.2511 13.9784 15.2117 12.8981C16.1723 11.8179 16.6526 10.5741 16.6526 9.16667C16.6526 9.00617 16.6464 8.84877 16.6339 8.69444C16.6214 8.54012 16.6027 8.38272 16.5778 8.22222H17.9439C17.9688 8.37037 17.9844 8.52161 17.9906 8.67593C17.9969 8.83025 18 8.98765 18 9.14815C18 10.8272 17.4542 12.321 16.3626 13.6296C15.271 14.9383 13.8893 15.7593 12.2175 16.0926L13.5462 16.8519L12.8725 18Z",fill:"var(--fc-secondary-text)"},null,-1)])])):"recovery_rate"==R.type?(e(),l("svg",F,[...P[26]||(P[26]=[n("path",{d:"M10 2.5C14.1423 2.5 17.5 5.85775 17.5 10C17.5 14.1423 14.1423 17.5 10 17.5C5.85775 17.5 2.5 14.1423 2.5 10H4C4 13.3135 6.6865 16 10 16C13.3135 16 16 13.3135 16 10C16 6.6865 13.3135 4 10 4C7.9375 4 6.118 5.04025 5.03875 6.625H7V8.125H2.5V3.625H4V5.5C5.368 3.6775 7.54675 2.5 10 2.5ZM10.75 6.25V9.68875L13.1823 12.121L12.121 13.1823L9.25 10.3098V6.25H10.75Z",fill:"var(--fc-secondary-text)"},null,-1)])])):"optout_revenue"==R.type?(e(),l("svg",I,[...P[27]||(P[27]=[n("path",{d:"M2 9.13447V7.61574H3.46364V9.13447H2ZM15.5362 9.13447V7.61574H17V9.13447H15.5362ZM2 6.32641V4.80787H3.46364V6.32641H2ZM15.5362 6.32641V4.80787H17V6.32641H15.5362ZM2 3.51854V2H3.46364V3.51854H2ZM4.70635 9.13447V7.61574H6.16998V9.13447H4.70635ZM12.8298 9.13447V7.61574H14.2935V9.13447H12.8298ZM15.5362 3.51854V2H17V3.51854H15.5362ZM4.70635 3.51854V2H6.16998V3.51854H4.70635ZM7.4127 3.51854V2H8.87633V3.51854H7.4127ZM10.1235 3.51854V2H11.5871V3.51854H10.1235ZM12.8298 3.51854V2H14.2935V3.51854H12.8298ZM8.61067 18C8.34359 18 8.0799 17.9437 7.8196 17.8312C7.5593 17.7186 7.33376 17.5626 7.14297 17.3634L3.37397 13.4622L4.12955 12.6875C4.27399 12.5376 4.43533 12.438 4.61354 12.3887C4.79164 12.3393 4.97139 12.3433 5.15282 12.4006L6.65879 12.7949V6.65306C6.65879 6.43785 6.72855 6.25749 6.86806 6.11197C7.00758 5.96645 7.18044 5.89369 7.38663 5.89369C7.59295 5.89369 7.76716 5.96645 7.90927 6.11197C8.05137 6.25749 8.12242 6.43785 8.12242 6.65306V14.7815L6.21103 14.2519L8.17585 16.2948C8.23057 16.3517 8.29534 16.3971 8.37015 16.4308C8.44496 16.4646 8.52514 16.4815 8.61067 16.4815H12.2334C12.64 16.4815 12.9881 16.3304 13.2776 16.0284C13.5672 15.7264 13.712 15.3633 13.712 14.9391V11.3481C13.712 11.1329 13.782 10.9525 13.9219 10.807C14.0618 10.6615 14.2351 10.5888 14.4419 10.5888C14.6487 10.5888 14.8228 10.6615 14.964 10.807C15.1051 10.9525 15.1757 11.1329 15.1757 11.3481V14.9391C15.1757 15.7917 14.8894 16.515 14.3169 17.1091C13.7443 17.703 13.0471 18 12.2253 18H8.61067ZM9.00981 12.4944V9.43331C9.00981 9.21823 9.07957 9.03793 9.21909 8.89241C9.3586 8.74689 9.53152 8.67413 9.73784 8.67413C9.94403 8.67413 10.1182 8.74689 10.2603 8.89241C10.4024 9.03793 10.4734 9.21823 10.4734 9.43331V12.4944H9.00981ZM11.3608 12.4944V10.5425C11.3608 10.3278 11.4306 10.1468 11.5701 9.99933C11.7098 9.85189 11.8827 9.77818 12.0889 9.77818C12.2952 9.77818 12.4694 9.85093 12.6115 9.99645C12.7536 10.142 12.8247 10.3223 12.8247 10.5375V12.4944H11.3608Z",fill:"var(--fc-secondary-text)"},null,-1)])])):"recovered_revenue"==R.type?(e(),l("svg",W,[...P[28]||(P[28]=[n("path",{d:"M10.8069 15.9725H1.39999V4H18.5035V11.6966",stroke:"var(--fc-secondary-text)","stroke-width":"1.75","stroke-linecap":"square"},null,-1),n("path",{d:"M8.53217 14L7.82634 13.3224L8.44901 12.7246H8.10807C7.23259 12.7043 6.49571 12.3926 5.89742 11.7892C5.29914 11.1859 5 10.4616 5 9.61623C5 8.75071 5.31499 8.01494 5.94496 7.40892C6.57486 6.80297 7.34098 6.5 8.24334 6.5H11.1317C12.034 6.5 12.8001 6.80213 13.43 7.40638C14.06 8.01063 14.375 8.74555 14.375 9.61115C14.375 10.4748 14.0643 11.2101 13.443 11.8171C12.8217 12.4239 12.0628 12.7274 11.1662 12.7274V11.7684C11.7831 11.7684 12.3056 11.5574 12.7338 11.1354C13.162 10.7134 13.3761 10.2053 13.3761 9.61103C13.3761 9.01317 13.1575 8.50505 12.7204 8.08665C12.2833 7.66818 11.7537 7.45895 11.1317 7.45895H8.24334C7.62131 7.45895 7.09174 7.66903 6.65465 8.08919C6.21747 8.50936 5.99888 9.01841 5.99888 9.61634C5.99888 10.1884 6.19953 10.68 6.60085 11.091C7.00217 11.5021 7.49884 11.7159 8.09086 11.7325H8.40581L7.82345 11.1734L8.53217 10.493L10.3601 12.2451L8.53217 14Z",fill:"var(--fc-secondary-text)"},null,-1)])])):"settings"==R.type?(e(),l("svg",X,[...P[29]||(P[29]=[n("path",{d:"M0 7.2285C0 6.57975 0.0825 5.95125 0.237 5.3505C0.651349 5.37228 1.06365 5.27906 1.42833 5.08115C1.79301 4.88324 2.09586 4.58834 2.3034 4.22906C2.51095 3.86977 2.6151 3.4601 2.60436 3.04532C2.59361 2.63053 2.46837 2.2268 2.2425 1.87875C3.14921 0.986677 4.26809 0.340122 5.49375 0C5.68199 0.370104 5.96898 0.6809 6.32294 0.897983C6.6769 1.11506 7.08402 1.22997 7.49925 1.22997C7.91447 1.22997 8.3216 1.11506 8.67556 0.897983C9.02952 0.6809 9.31651 0.370104 9.50475 0C10.7304 0.340122 11.8493 0.986677 12.756 1.87875C12.5299 2.22687 12.4045 2.63075 12.3936 3.04572C12.3828 3.4607 12.487 3.87057 12.6946 4.23001C12.9023 4.58944 13.2054 4.88442 13.5703 5.08231C13.9352 5.2802 14.3477 5.37328 14.7622 5.35125C14.9167 5.95125 14.9992 6.57975 14.9992 7.2285C14.9992 7.87725 14.9167 8.50575 14.7622 9.1065C14.3478 9.08457 13.9354 9.17768 13.5706 9.37553C13.2059 9.57339 12.9029 9.86827 12.6953 10.2276C12.4876 10.5869 12.3834 10.9966 12.3941 11.4115C12.4048 11.8263 12.5301 12.2301 12.756 12.5782C11.8493 13.4703 10.7304 14.1169 9.50475 14.457C9.31651 14.0869 9.02952 13.7761 8.67556 13.559C8.3216 13.3419 7.91447 13.227 7.49925 13.227C7.08402 13.227 6.6769 13.3419 6.32294 13.559C5.96898 13.7761 5.68199 14.0869 5.49375 14.457C4.26809 14.1169 3.14921 13.4703 2.2425 12.5782C2.46863 12.2301 2.59405 11.8262 2.60488 11.4113C2.61571 10.9963 2.51152 10.5864 2.30386 10.227C2.09619 9.86755 1.79314 9.57257 1.42823 9.37469C1.06332 9.1768 0.650778 9.08372 0.23625 9.10575C0.0825 8.5065 0 7.878 0 7.2285ZM3.603 9.4785C4.0755 10.2967 4.2105 11.238 4.026 12.1215C4.332 12.339 4.6575 12.5272 4.99875 12.684C5.68625 12.0681 6.57699 11.7279 7.5 11.7285C8.445 11.7285 9.3285 12.0817 10.0012 12.684C10.3425 12.5272 10.668 12.339 10.974 12.1215C10.7846 11.2185 10.9352 10.2773 11.397 9.4785C11.858 8.67928 12.5978 8.07837 13.4745 7.791C13.5092 7.4168 13.5092 7.0402 13.4745 6.666C12.5975 6.37879 11.8574 5.77786 11.3962 4.9785C10.9345 4.1797 10.7838 3.23853 10.9732 2.3355C10.6673 2.11794 10.3417 1.92961 10.0005 1.773C9.31319 2.3887 8.42276 2.72896 7.5 2.7285C6.57699 2.72914 5.68625 2.38887 4.99875 1.773C4.6576 1.92962 4.33192 2.11795 4.026 2.3355C4.21542 3.23853 4.06479 4.1797 3.603 4.9785C3.14203 5.77772 2.40224 6.37863 1.5255 6.666C1.49081 7.0402 1.49081 7.4168 1.5255 7.791C2.40252 8.0782 3.1426 8.67914 3.60375 9.4785H3.603ZM7.5 9.4785C6.90326 9.4785 6.33097 9.24145 5.90901 8.81949C5.48705 8.39753 5.25 7.82524 5.25 7.2285C5.25 6.63176 5.48705 6.05947 5.90901 5.63751C6.33097 5.21555 6.90326 4.9785 7.5 4.9785C8.09674 4.9785 8.66903 5.21555 9.09099 5.63751C9.51295 6.05947 9.75 6.63176 9.75 7.2285C9.75 7.82524 9.51295 8.39753 9.09099 8.81949C8.66903 9.24145 8.09674 9.4785 7.5 9.4785ZM7.5 7.9785C7.69891 7.9785 7.88968 7.89948 8.03033 7.75883C8.17098 7.61818 8.25 7.42741 8.25 7.2285C8.25 7.02959 8.17098 6.83882 8.03033 6.69817C7.88968 6.55752 7.69891 6.4785 7.5 6.4785C7.30109 6.4785 7.11032 6.55752 6.96967 6.69817C6.82902 6.83882 6.75 7.02959 6.75 7.2285C6.75 7.42741 6.82902 7.61818 6.96967 7.75883C7.11032 7.89948 7.30109 7.9785 7.5 7.9785V7.9785Z",fill:"var(--fc-secondary-text)"},null,-1)])])):"email_pending"==R.type?(e(),l("svg",Y,[...P[30]||(P[30]=[n("path",{d:"M13.5 3.1785L7.554 8.5035L1.5 3.162V12H6.8025C6.87706 12.52 7.02978 13.0257 7.2555 13.5H0.75C0.551088 13.5 0.360322 13.421 0.21967 13.2803C0.0790176 13.1397 0 12.9489 0 12.75V0.75C0 0.551088 0.0790176 0.360322 0.21967 0.21967C0.360322 0.0790176 0.551088 0 0.75 0H14.25C14.4489 0 14.6397 0.0790176 14.7803 0.21967C14.921 0.360322 15 0.551088 15 0.75V6.94125C14.5419 6.62141 14.0355 6.37706 13.5 6.2175V3.1785ZM13.1257 1.5H1.88325L7.54575 6.4965L13.1257 1.5Z",fill:"var(--fc-secondary-text)"},null,-1),n("path",{d:"M12 15.375C9.72176 15.375 7.875 13.5282 7.875 11.25C7.875 8.97176 9.72176 7.125 12 7.125C14.2782 7.125 16.125 8.97176 16.125 11.25C16.125 13.5282 14.2782 15.375 12 15.375ZM12 14.55C12.8752 14.55 13.7146 14.2023 14.3335 13.5835C14.9523 12.9646 15.3 12.1252 15.3 11.25C15.3 10.3748 14.9523 9.53542 14.3335 8.91655C13.7146 8.29768 12.8752 7.95 12 7.95C11.1248 7.95 10.2854 8.29768 9.66655 8.91655C9.04768 9.53542 8.7 10.3748 8.7 11.25C8.7 12.1252 9.04768 12.9646 9.66655 13.5835C10.2854 14.2023 11.1248 14.55 12 14.55ZM12.4125 11.25H14.0625V12.075H11.5875V9.1875H12.4125V11.25Z",fill:"var(--fc-secondary-text)"},null,-1)])])):"email_open"==R.type?(e(),l("svg",z,[...P[31]||(P[31]=[n("path",{d:"M2.5 7.5L10 12.5L17.5 7.5V15C17.5 15.1989 17.421 15.3897 17.2803 15.5303C17.1397 15.671 16.9489 15.75 16.75 15.75H3.25C3.05109 15.75 2.86032 15.671 2.71967 15.5303C2.57902 15.3897 2.5 15.1989 2.5 15V7.5Z",fill:"currentColor",opacity:"0.3"},null,-1),n("path",{d:"M3.25 3.25H16.75C16.9489 3.25 17.1397 3.32902 17.2803 3.46967C17.421 3.61032 17.5 3.80109 17.5 4V16C17.5 16.1989 17.421 16.3897 17.2803 16.5303C17.1397 16.671 16.9489 16.75 16.75 16.75H3.25C3.05109 16.75 2.86032 16.671 2.71967 16.5303C2.57902 16.3897 2.5 16.1989 2.5 16V4C2.5 3.80109 2.57902 3.61032 2.71967 3.46967C2.86032 3.32902 3.05109 3.25 3.25 3.25ZM16 6.4725L10.065 11.4025L4 6.46V15.25H16V6.4725ZM4.26825 4.75L10.0522 9.5975L15.7405 4.75H4.26825Z",fill:"currentColor"},null,-1)])])):"email_click"==R.type?(e(),l("svg",A,[...P[32]||(P[32]=[n("path",{d:"M8.75 2.5V4H4V16H15.25V11.25H16.75V16.75C16.75 16.9489 16.671 17.1397 16.5303 17.2803C16.3897 17.421 16.1989 17.5 16 17.5H3.25C3.05109 17.5 2.86032 17.421 2.71967 17.2803C2.57902 17.1397 2.5 16.1989 2.5 16V3.25C2.5 3.05109 2.57902 2.86032 2.71967 2.71967C2.86032 2.57902 3.05109 2.5 3.25 2.5H8.75ZM14.7197 5.2195L10 9.93925L11.0607 11L15.7803 6.28025L14.7197 5.2195ZM11.875 2.5H17.5V8.125H16V5.06075L10.5303 10.5303L9.46975 9.46975L14.9393 4H11.875V2.5Z",fill:"currentColor"},null,-1)])])):"email_bounce"==R.type?(e(),l("svg",D,[...P[33]||(P[33]=[n("path",{d:"M17.5 16.0053C17.4986 16.2022 17.4198 16.3907 17.2806 16.5301C17.1414 16.6694 16.953 16.7484 16.756 16.75H3.244C3.04661 16.7498 2.85737 16.6712 2.71787 16.5316C2.57836 16.392 2.5 16.2026 2.5 16.0053V15.25H16V6.475L10 11.875L2.5 5.125V4C2.5 3.80109 2.57902 3.61032 2.71967 3.46967C2.86032 3.32902 3.05109 3.25 3.25 3.25H16.75C16.9489 3.25 17.1397 3.32902 17.2803 3.46967C17.421 3.61032 17.5 3.80109 17.5 4V16.0053ZM4.3255 4.75L10 9.8575L15.6745 4.75H4.3255Z",fill:"currentColor"},null,-1),n("path",{d:"M15 1.5L17.5 4L15 6.5",stroke:"currentColor","stroke-width":"1.5","stroke-linecap":"round","stroke-linejoin":"round"},null,-1),n("path",{d:"M17.5 4H13.75",stroke:"currentColor","stroke-width":"1.5","stroke-linecap":"round"},null,-1)])])):"email_unsubscribe"==R.type?(e(),l("svg",G,[...P[34]||(P[34]=[n("path",{d:"M17.5 16.0053C17.4986 16.2022 17.4198 16.3907 17.2806 16.5301C17.1414 16.6694 16.953 16.7484 16.756 16.75H3.244C3.04661 16.7498 2.85737 16.6712 2.71787 16.5316C2.57836 16.392 2.5 16.2026 2.5 16.0053V15.25H16V6.475L10 11.875L2.5 5.125V4C2.5 3.80109 2.57902 3.61032 2.71967 3.46967C2.86032 3.32902 3.05109 3.25 3.25 3.25H16.75C16.9489 3.25 17.1397 3.32902 17.2803 3.46967C17.421 3.61032 17.5 3.80109 17.5 4V16.0053ZM4.3255 4.75L10 9.8575L15.6745 4.75H4.3255Z",fill:"currentColor"},null,-1),n("line",{x1:"13",y1:"2",x2:"18",y2:"7",stroke:"currentColor","stroke-width":"1.5","stroke-linecap":"round"},null,-1),n("line",{x1:"18",y1:"2",x2:"13",y2:"7",stroke:"currentColor","stroke-width":"1.5","stroke-linecap":"round"},null,-1)])])):"contact_subscribed"==R.type?(e(),l("svg",J,[...P[35]||(P[35]=[n("path",{d:"M10 17.5C5.85775 17.5 2.5 14.1423 2.5 10C2.5 5.85775 5.85775 2.5 10 2.5C14.1423 2.5 17.5 5.85775 17.5 10C17.5 14.1423 14.1423 17.5 10 17.5ZM9.2515 13L14.3032 7.94825L13.2425 6.8875L9.2515 10.8785L7.256 8.88325L6.19525 9.944L9.2515 13Z",fill:"currentColor"},null,-1)])])):"contact_pending"==R.type?(e(),l("svg",K,[...P[36]||(P[36]=[n("path",{d:"M10 17.5C5.85775 17.5 2.5 14.1423 2.5 10C2.5 5.85775 5.85775 2.5 10 2.5C14.1423 2.5 17.5 5.85775 17.5 10C17.5 14.1423 14.1423 17.5 10 17.5ZM10 16C11.5913 16 13.1174 15.3679 14.2426 14.2426C15.3679 13.1174 16 11.5913 16 10C16 8.4087 15.3679 6.88258 14.2426 5.75736C13.1174 4.63214 11.5913 4 10 4C8.4087 4 6.88258 4.63214 5.75736 5.75736C4.63214 6.88258 4 8.4087 4 10C4 11.5913 4.63214 13.1174 5.75736 14.2426C6.88258 15.3679 8.4087 16 10 16ZM10.75 10H13.75V11.5H9.25V6.25H10.75V10Z",fill:"currentColor"},null,-1)])])):"sequence"==R.type?(e(),l("svg",N,[...P[37]||(P[37]=[n("path",{d:"M3.25 2.5H16.75C16.9489 2.5 17.1397 2.57902 17.2803 2.71967C17.421 2.86032 17.5 3.05109 17.5 3.25V16.75C17.5 16.9489 17.421 17.1397 17.2803 17.2803C17.1397 17.421 16.9489 17.5 16.75 17.5H3.25C3.05109 17.5 2.86032 17.421 2.71967 17.2803C2.57902 17.1397 2.5 16.9489 2.5 16.75V3.25C2.5 3.05109 2.57902 2.86032 2.71967 2.71967C2.86032 2.57902 3.05109 2.5 3.25 2.5ZM4 4V16H16V4H4ZM6.25 6.25H8.5V8.5H6.25V6.25ZM6.25 9.25H8.5V11.5H6.25V9.25ZM6.25 12.25H8.5V14.5H6.25V12.25ZM10 7H14.5V8.5H10V7ZM10 10H14.5V11.5H10V10ZM10 13H14.5V14.5H10V13Z",fill:"currentColor"},null,-1)])])):"recurring"==R.type?(e(),l("svg",O,[...P[38]||(P[38]=[n("path",{d:"M4.5625 4.5625C5.99553 3.12946 7.95379 2.32843 9.99475 2.33118C12.0357 2.33394 13.9918 3.14026 15.4213 4.5772C16.8507 6.01413 17.6471 7.97418 17.6398 10.0151C17.6325 12.0561 16.8216 14.0103 15.3812 15.4375L14.317 14.3758C15.0591 13.635 15.6336 12.7433 16.0028 11.7591C16.3721 10.775 16.5279 9.72172 16.4601 8.67071C16.3922 7.6197 16.1023 6.59518 15.6093 5.66583C15.1163 4.73649 14.4317 3.92398 13.5994 3.28126C12.7672 2.63853 11.8064 2.18019 10.7834 1.93649C9.76032 1.6928 8.69877 1.66932 7.66606 1.8676C6.63335 2.06588 5.65324 2.48165 4.79398 3.08726C3.93471 3.69287 3.21578 4.47426 2.68375 5.381L4.375 5.62L3.25 9.25L0.625 6.25L2.125 6.46225C2.67849 5.49464 3.44759 4.6647 4.37125 4.03801C5.29491 3.41131 6.34928 3.00419 7.455 2.84875",fill:"currentColor"},null,-1),n("path",{d:"M10.75 6.25V10.3107L13.75 12.0432L13 13.3432L9.25 11.1768V6.25H10.75Z",fill:"currentColor"},null,-1)])])):(e(),o(CC,{key:39},{default:r(()=>[i($)],void 0),_:1}))}]]);export{P as C}; diff --git a/wp-content/plugins/fluent-crm/assets/DataTable.js b/wp-content/plugins/fluent-crm/assets/DataTable.js new file mode 100644 index 0000000..98ae7af --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/DataTable.js @@ -0,0 +1 @@ +import{W as e,X as a,Z as s,_ as r,a8 as t,a0 as l}from"./vendor.js?ver=3.1.8";import{_ as o}from"./fc-bits-ui.js?ver=3.1.8";const c={name:"DataTable",props:{hasSelection:{type:Boolean,default:!1},wrapper_border:{type:Boolean,default:!1}}},i={class:"fcrm_table_header"},_={key:0,class:"fcrm_table_header_inner"},n={key:0,class:"fcrm_table_header_inner_left"},d={key:1,class:"fcrm_table_header_inner_actions"},f={class:"fcrm_table_header_bulk_actions"},b={class:"fcrm_table_body"};const p=o(c,[["render",function(o,c,p,h,m,v){return e(),a("div",{class:l(["fcrm_table_wrapper",{fcrm_table_wrapper_border:p.wrapper_border}])},[s("div",i,[p.hasSelection?t("",!0):(e(),a("div",_,[o.$slots["header-left"]?(e(),a("div",n,[r(o.$slots,"header-left")])):t("",!0),o.$slots["header-actions"]?(e(),a("div",d,[r(o.$slots,"header-actions")])):t("",!0)])),s("div",f,[p.hasSelection?r(o.$slots,"bulk-actions",{key:0}):t("",!0),p.hasSelection?t("",!0):r(o.$slots,"active-filters",{key:1})])]),r(o.$slots,"header-extra"),s("div",b,[r(o.$slots,"table"),r(o.$slots,"pagination")])],2)}]]);export{p as D}; diff --git a/wp-content/plugins/fluent-crm/assets/EmailComposer.js b/wp-content/plugins/fluent-crm/assets/EmailComposer.js new file mode 100644 index 0000000..1fc5827 --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/EmailComposer.js @@ -0,0 +1 @@ +import{aD as e,aA as a,aw as t,e as s,ax as i}from"./vendor-element-plus.js?ver=3.1.8";import{aQ as l,W as o,X as d,Y as m,a5 as r,ab as _,a8 as n,_ as c}from"./vendor.js?ver=3.1.8";import{E as u}from"./BlockComposer.js?ver=3.1.8";import{I as p}from"./_FormBuilder2.js?ver=3.1.8";import{S as b}from"./TestEmail.js?ver=3.1.8";import{_ as g}from"./fc-bits-ui.js?ver=3.1.8";const v={class:"email_composer_wrapper"},h={key:1};const f=g({name:"EmailComposer",props:["campaign","label_align","disable_subject","disable_templates","disable_fixed","enable_test","show_merge","extra_tags","show_audit","disable_gutenberg_autosave","hide_gutenberg_save_button"],emits:["save"],components:{EmailBlockComposer:u,InputPopover:p,SendTestEmail:b},data:()=>({smartcodes:window.fcAdmin.globalSmartCodes,email_subject_status:!0}),methods:{triggerSave(){this.$emit("save")},resetSubject(){this.email_subject_status=!1,this.$nextTick(()=>{this.email_subject_status=!0})}},mounted(){this.extra_tags&&(this.smartcodes=[...this.smartcodes,...this.extra_tags]),window.fcAdmin.extendedSmartCodes&&(this.smartcodes=[...this.smartcodes,...window.fcAdmin.extendedSmartCodes])}},[["render",function(u,p,b,g,f,j){const x=l("input-popover"),w=t,S=a,E=s,k=e,$=i,C=l("email-block-composer"),V=l("send-test-email");return o(),d("div",v,[b.disable_subject?n("",!0):(o(),m($,{key:0,"label-position":b.label_align,model:b.campaign},{default:r(()=>[_(k,{gutter:20,class:"mb-10"},{default:r(()=>[_(S,{sm:24,md:12},{default:r(()=>[_(w,{label:u.$t("Email Subject")},{default:r(()=>[f.email_subject_status?(o(),m(x,{key:0,doc_url:"https://fluentcrm.com/docs/merge-codes-smart-codes-usage/",popper_extra:"fc_with_c_fields",placeholder:u.$t("Email Subject"),data:f.smartcodes,modelValue:b.campaign.email_subject,"onUpdate:modelValue":p[0]||(p[0]=e=>b.campaign.email_subject=e)},null,8,["placeholder","data","modelValue"])):n("",!0)],void 0,!0),_:1},8,["label"])],void 0,!0),_:1}),_(S,{sm:24,md:12},{default:r(()=>[_(w,{label:u.$t("Email Pre-Header")},{default:r(()=>[_(E,{placeholder:u.$t("Email Pre-Header"),modelValue:b.campaign.email_pre_header,"onUpdate:modelValue":p[1]||(p[1]=e=>b.campaign.email_pre_header=e)},null,8,["placeholder","modelValue"])],void 0,!0),_:1},8,["label"])],void 0,!0),_:1})],void 0,!0),_:1})],void 0),_:1},8,["label-position","model"])),_(C,{onSave:p[2]||(p[2]=e=>j.triggerSave()),iframe_nav_mode:"compose","hide-back-btn":!0,"hide-next-btn":!0,"hide-save-btn":b.hide_gutenberg_save_button,"disable-gutenberg-autosave":b.disable_gutenberg_autosave,show_audit:b.show_audit,extra_tags:b.extra_tags,show_merge:!0,enable_template_save:!0,onTemplate_inserted:p[3]||(p[3]=e=>j.resetSubject()),disable_fixed:b.disable_fixed,enable_templates:!b.disable_templates,campaign:b.campaign},{fc_editor_actions:r(()=>[c(u.$slots,"actions")]),_:3},8,["hide-save-btn","disable-gutenberg-autosave","show_audit","extra_tags","disable_fixed","enable_templates","campaign"]),c(u.$slots,"after_block_composer"),b.enable_test?(o(),d("div",h,[_(V,{campaign:b.campaign},null,8,["campaign"])])):n("",!0)])}]]);export{f as E}; diff --git a/wp-content/plugins/fluent-crm/assets/EmailPreview.js b/wp-content/plugins/fluent-crm/assets/EmailPreview.js new file mode 100644 index 0000000..747b7c5 --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/EmailPreview.js @@ -0,0 +1 @@ +import{ay as e,aL as i,aK as t,k as a,aO as s,aB as l,aT as o}from"./vendor-element-plus.js?ver=3.1.8";import{a6 as r,W as d,Y as n,a5 as c,X as m,J as p,az as _,a8 as h,aQ as v,Z as u,ab as w,a0 as f,aa as b,ax as g,a9 as C}from"./vendor.js?ver=3.1.8";import{P as y}from"./PreviewIframeBuilder.js?ver=3.1.8";import{_ as k,I as $}from"./fc-bits-ui.js?ver=3.1.8";import{S as j}from"./TestEmail.js?ver=3.1.8";import{C as P}from"./CampaignSubjectLines.js?ver=3.1.8";const M={class:"fc_email_preview"},S={class:"icon"},V={class:"el-drawer__title"},z={class:"fcrm_email_preview_shell"},I={class:"fcrm_preview_toolbar"},E={class:"fcrm_preview_device_toggle"},H={class:"fcrm_device_btn_group"},B=["aria-label","aria-pressed","title"],T=["aria-label","aria-pressed","title"],x=["aria-label","aria-pressed","title"],L={class:"fcrm_device_label"},R={class:"fcrm_preview_toolbar_actions"},D={class:"icon"},O={class:"contact_selector_title"},U={class:"contact_selector_action"},Q={class:"icon"},A={class:"fcrm_preview_meta"},F={key:0},J={key:1},K={key:2,class:"fcrm_preview_meta_subject"},W={key:0,class:"fcrm_preview_loading"},X={key:0,class:"fc_device_notch"},Y={key:1,class:"fc_device_home"},Z={key:2,class:"fcrm_preview_empty"};const q=k({name:"EmailPreview",props:["campaign","show_audit","auto_load","by_campaign_id","drawer_size"],emits:["dataLoaded","modalClosed"],components:{CampaignSubjectLines:P,SendTestEmail:j,Icons:$,PreviewIframeBuilder:y,ContactSelector:k({name:"ContactSelector",props:["field","modelValue"],emits:["contactSelected","update:modelValue"],data(){return{model:this.modelValue,loading:!1,options:{},appReady:!1}},watch:{model(e){this.$emit("update:modelValue",e),this.$emit("contactSelected",this.options[e])}},methods:{fetchOptions(e){this.loading=!0,this.$get("subscribers/search-contacts",{search:e,values:this.model,load_default:!!this.field.load_default}).then(e=>{this.options=e.contacts}).catch(e=>{this.handleError(e)}).finally(()=>{this.loading=!1})}},mounted(){this.model&&"0"!=this.model||this.field.is_multiple||(this.model=""),this.field.pre_options&&this.field.pre_options.length&&this.each(this.field.pre_options,e=>{e&&e.id&&(e.id=e.id.toString(),this.options[e.id]=e)}),this.appReady=!0,this.field.load_default&&this.fetchOptions(""),this.model&&"object"!=typeof this.model&&!this.options[this.model]&&this.fetchOptions("")}},[["render",function(a,s,l,o,v,u){const w=i,f=t,b=e;return v.appReady?r((d(),n(f,{key:0,modelValue:v.model,"onUpdate:modelValue":s[0]||(s[0]=e=>v.model=e),multiple:l.field.is_multiple,filterable:"",remote:!l.field.cacheable,clearable:l.field.clearable,disabled:l.field.disabled,"reserve-keyword":"",size:l.field.size,placeholder:l.field.placeholder||a.$t("Search contact"),"remote-method":u.fetchOptions,teleported:!1!==l.field.teleported},{default:c(()=>[(d(!0),m(p,null,_(v.options,e=>(d(),n(w,{key:e.id,label:e.full_name+" ("+e.email+")",value:e.id},null,8,["label","value"]))),128))],void 0),_:1},8,["modelValue","multiple","remote","clearable","disabled","size","placeholder","remote-method","teleported"])),[[b,v.loading]]):h("",!0)}]])},data:()=>({direction:"rtl",showing_view:!1,preview_html:"",loading_preview:!1,showChanger:!1,selectedId:!1,selectedContact:!1,previewSubjects:[],previewMode:"desktop"}),computed:{previewCampaign(){return this.campaign?{...this.campaign,subjects:this.previewSubjects.length?this.previewSubjects:this.campaign.subjects||[]}:null},deviceLabel(){return{desktop:this.$t("Desktop"),tablet:this.$t("Tablet")+" · 768px",mobile:this.$t("Mobile")+" · 375px"}[this.previewMode]||""}},methods:{open(){this.showing_view=!0,this.fetchHtml()},fetchHtml(){this.shouldRefreshVisualBuilderContent()?this.refreshVisualBuilderContentBeforePreview():this.fetchPreviewHtml()},shouldRefreshVisualBuilderContent(){return!(!this.campaign||"visual_builder"!==this.campaign.design_template||this.by_campaign_id||this.loading_preview)},refreshVisualBuilderContentBeforePreview(){let e=!1;this.loading_preview=!0,this.showing_view=!0;const i=()=>{e||(e=!0,this.fetchPreviewHtml())};this.$bus.emit("getVisualData",{callback:i,reference:"update_only"}),setTimeout(i,600)},fetchPreviewHtml(){this.loading_preview=!0,this.showing_view=!0;const e=this.campaign||{},i=e.id||e.ID||null,t=e.settings||{},a=e.email_body||e.post_content||"",s=e.title||e.post_title||"",l=e.email_pre_header||e.post_excerpt||"",o={campaign:{id:i,settings:t,email_body:a,title:s,design_template:e.design_template,email_subject:e.email_subject,subjects:e.subjects||[],email_pre_header:l,utm_status:e.utm_status,utm_source:e.utm_source,utm_medium:e.utm_medium,utm_campaign:e.utm_campaign},contact_id:this.selectedId};this.by_campaign_id&&(o.campaign={id:i},o.campaign_id=i),this.$post("campaigns/email-preview-html",o).then(e=>{this.preview_html=e.preview_html,this.previewSubjects=e.subjects||[],this.$emit("dataLoaded",e)}).catch(e=>{this.handleError(e)}).finally(()=>{this.loading_preview=!1,this.showChanger=!1})},fireClose(){this.$emit("modalClosed"),this.showing_view=!1}},mounted(){window.fcAdmin&&window.fcAdmin.is_rtl&&(this.direction="ltr"),this.auto_load&&this.fetchHtml()}},[["render",function(e,i,t,r,p,_){const y=v("Icons"),k=a,$=v("contact-selector"),j=s,P=v("send-test-email"),q=v("campaign-subject-lines"),G=l,N=v("preview-iframe-builder"),ee=o;return d(),m("span",M,[t.auto_load?h("",!0):(d(),n(k,{key:0,class:f([{fc_segmented_active:p.showing_view},"fc_segmented_btn"]),size:"small",onClick:i[0]||(i[0]=e=>_.fetchHtml()),title:e.$t("Preview Email")},{default:c(()=>[u("span",S,[w(y,{"icon-name":"eye"})])],void 0),_:1},8,["class","title"])),w(ee,{direction:p.direction,class:"fc_company_info_drawer fcrm_email_preview_drawer","with-header":!0,size:t.drawer_size||"70%","append-to-body":!0,"before-close":_.fireClose,modelValue:p.showing_view,"onUpdate:modelValue":i[12]||(i[12]=e=>p.showing_view=e)},{title:c(()=>[u("div",V,[C(b(e.$t("Email Preview"))+" ",1),u("small",null,b(e.$t("For the most accurate preview, send a test email using the Quick Test button.")),1)])]),default:c(()=>[u("div",z,[u("div",I,[u("div",E,[u("div",H,[u("button",{type:"button",class:f(["fcrm_device_btn",{active:"desktop"===p.previewMode}]),onClick:i[1]||(i[1]=e=>p.previewMode="desktop"),"aria-label":e.$t("Desktop Preview"),"aria-pressed":"desktop"===p.previewMode,title:e.$t("Desktop Preview")},[w(y,{"icon-name":"desktop"})],10,B),u("button",{type:"button",class:f(["fcrm_device_btn",{active:"tablet"===p.previewMode}]),onClick:i[2]||(i[2]=e=>p.previewMode="tablet"),"aria-label":e.$t("Tablet Preview"),"aria-pressed":"tablet"===p.previewMode,title:e.$t("Tablet Preview")},[w(y,{"icon-name":"tablet"})],10,T),u("button",{type:"button",class:f(["fcrm_device_btn",{active:"mobile"===p.previewMode}]),onClick:i[3]||(i[3]=e=>p.previewMode="mobile"),"aria-label":e.$t("Mobile Preview"),"aria-pressed":"mobile"===p.previewMode,title:e.$t("Mobile Preview")},[w(y,{"icon-name":"mobile"})],10,x)]),u("span",L,b(_.deviceLabel),1)]),u("div",R,[w(k,{size:"small",disabled:p.loading_preview,onClick:i[4]||(i[4]=e=>_.fetchHtml()),style:{width:"32px"}},{default:c(()=>[u("span",D,[w(y,{"icon-name":"reload",class:f({spining:p.loading_preview})},null,8,["class"])])],void 0,!0),_:1},8,["disabled"]),w(j,{placement:"bottom-end",width:"400",visible:p.showChanger,"onUpdate:visible":i[10]||(i[10]=e=>p.showChanger=e),trigger:"manual"},{reference:c(()=>[w(k,{size:"small",onClick:i[9]||(i[9]=e=>p.showChanger=!p.showChanger)},{default:c(()=>[C(b(e.$t("Change Contact")),1)],void 0,!0),_:1})]),default:c(()=>[u("div",{class:"contact_selector",onClick:i[8]||(i[8]=g(()=>{},["stop"]))},[u("p",O,b(e.$t("Select Contact")),1),w($,{onContactSelected:i[5]||(i[5]=e=>{p.selectedContact=e}),modelValue:p.selectedId,"onUpdate:modelValue":i[6]||(i[6]=e=>p.selectedId=e),field:{clearable:!0,size:"small",load_default:!0,teleported:!1}},null,8,["modelValue"]),u("div",U,[w(k,{disabled:!p.selectedId,onClick:i[7]||(i[7]=e=>_.fetchHtml()),type:"primary",size:"small"},{default:c(()=>[u("span",Q,[w(y,{"icon-name":"reload"})]),C(" "+b(e.$t("Refresh Email Preview")),1)],void 0,!0),_:1},8,["disabled"])])])],void 0,!0),_:1},8,["visible"]),_.previewCampaign?(d(),n(P,{key:0,btn_text:e.$t("Quick Test"),btn_class:"small",campaign:{email_subject:_.previewCampaign.email_subject,subjects:_.previewCampaign.subjects,email_pre_header:_.previewCampaign.email_pre_header,email_body:_.previewCampaign.email_body||_.previewCampaign.post_content,design_template:_.previewCampaign.design_template,settings:_.previewCampaign.settings}},null,8,["btn_text","campaign"])):h("",!0)])]),u("div",A,[p.selectedContact?(d(),m("span",F,[C(b(e.$t("Showing preview for"))+" ",1),u("b",null,b(p.selectedContact.full_name)+" ("+b(p.selectedContact.email)+") ",1)])):(d(),m("span",J,[C(b(e.$t("Showing preview for"))+" ",1),u("b",null,b(e.$t("current contact")),1)])),_.previewCampaign?(d(),m("span",K,[w(q,{campaign:_.previewCampaign,"show-priority":!1},null,8,["campaign"])])):h("",!0)]),p.loading_preview?(d(),m("div",W,[w(G,{rows:10,animated:!0})])):p.preview_html?(d(),m("div",{key:1,class:f(["fcrm_preview_stage","fcrm_preview_stage_"+p.previewMode])},[u("div",{class:f(["fc_device_frame","fc_device_frame_"+p.previewMode])},["mobile"===p.previewMode?(d(),m("div",X)):h("",!0),w(N,{preview_html:p.preview_html,frame_height:"80vh",show_audit:t.show_audit},null,8,["preview_html","show_audit"]),"mobile"===p.previewMode?(d(),m("div",Y)):h("",!0)],2)],2)):(d(),m("div",Z,[u("p",null,b(e.$t("Preview Email")),1),w(k,{type:"primary",size:"small",onClick:i[11]||(i[11]=e=>_.fetchHtml())},{default:c(()=>[C(b(e.$t("Refresh Email Preview")),1)],void 0,!0),_:1})]))])],void 0),_:1},8,["direction","size","before-close","modelValue"])])}],["__scopeId","data-v-f370520c"]]);export{q as E}; diff --git a/wp-content/plugins/fluent-crm/assets/EmailSubjects.js b/wp-content/plugins/fluent-crm/assets/EmailSubjects.js new file mode 100644 index 0000000..57b2ea7 --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/EmailSubjects.js @@ -0,0 +1 @@ +import{k as e,a6 as a,W as l,aw as t,E as s,az as d,b8 as i,e as m,aD as o,aA as u,ax as n}from"./vendor-element-plus.js?ver=3.1.8";import{W as c,X as r,Z as _,aa as p,ab as b,a5 as g,a9 as h,aQ as f,a8 as v,J as j,az as V,Y as y}from"./vendor.js?ver=3.1.8";import{I as $}from"./_FormBuilder2.js?ver=3.1.8";import{_ as k,I as S}from"./fc-bits-ui.js?ver=3.1.8";import{M as E}from"./_MailerConfig.js?ver=3.1.8";const U={class:"fc_promo_body"},C={class:"promo_block promo_block--centered"},A=["href"];const P={class:"fluentcrm_email_composer"},w={key:0,class:"fcrm_input_hint"},I={key:0,class:"fcrm_campaign_ab_testing_email_subjects"},x={class:"fcrm_campaign_ab_testing_email_subjects--header"},M={class:"fcrm_campaign_ab_testing_email_subjects--header-title"},T={style:{width:"100px"}},F={class:"icon"},z={class:"fcrm_campaign_ab_testing_email_subjects--bottom"},q={class:"icon"},R={key:1},B={key:2,class:"fcrm_highlight_gray"};const D=k({name:"EmailSubjects",props:["campaign","label_align","multi_subject","mailer_settings"],components:{Icons:S,InfoFilled:l,InputPopover:$,AbEmailSubjectPromo:k({name:"AbEmailSubjectPromo"},[["render",function(a,l,t,s,d,i){const m=e;return c(),r("div",U,[_("div",C,[_("h2",null,p(a.$t("AbEmailSubjectPromo.title")),1),_("p",null,p(a.$t("AbEmailSubjectPromo.desc")),1),_("div",null,[_("a",{href:a.appVars.crm_pro_url,target:"_blank",rel:"noopener"},[b(m,{type:"danger"},{default:g(()=>[h(p(a.$t("Get FluentCRM Pro")),1)],void 0),_:1})],8,A)])])])}],["__scopeId","data-v-822997cc"]]),MailerConfig:E,Delete:a},data(){return{loading:!1,codes_ready:!1,smartcodes:window.fcAdmin.globalSmartCodes,hide_subject:!1,multi_subject_status:!(!this.campaign.subjects||!this.campaign.subjects.length)}},computed:{isAbTestingEnabled(){return!(!this.multi_subject_status||!this.has_campaign_pro)}},methods:{addSubject(){this.campaign.subjects.push({key:50,value:""})},removeSubject(e){this.campaign.subjects.splice(e,1)},maybeResetSubject(){this.multi_subject_status&&this.has_campaign_pro?this.campaign.subjects&&this.campaign.subjects.length||(this.campaign.subjects=[],this.addSubject(),this.addSubject()):this.campaign.subjects=[]}},mounted(){}},[["render",function(a,l,$,k,S,E){const U=f("input-popover"),C=f("InfoFilled"),A=s,D=t,H=d,W=i,Y=f("Icons"),G=e,J=f("ab-email-subject-promo"),N=m,Q=f("mailer-config"),X=u,Z=o,K=n;return c(),r("div",P,[b(K,{"label-position":$.label_align,"label-width":"220px",model:$.campaign},{default:g(()=>[b(D,{label:a.$t("Email Subject")},{default:g(()=>[b(U,{doc_url:"https://fluentcrm.com/docs/merge-codes-smart-codes-usage/",popper_extra:"fc_with_c_fields",placeholder:a.$t("Email Subject"),data:S.smartcodes,modelValue:$.campaign.email_subject,"onUpdate:modelValue":l[0]||(l[0]=e=>$.campaign.email_subject=e),disabled:E.isAbTestingEnabled},null,8,["placeholder","data","modelValue","disabled"]),E.isAbTestingEnabled?(c(),r("p",w,[b(A,null,{default:g(()=>[b(C)],void 0,!0),_:1}),h(" "+p(a.$t("A_B_Testing_Alert")),1)])):v("",!0)],void 0,!0),_:1},8,["label"]),$.multi_subject?(c(),r(j,{key:0},[b(D,null,{default:g(()=>[b(H,{onChange:l[1]||(l[1]=e=>E.maybeResetSubject()),modelValue:S.multi_subject_status,"onUpdate:modelValue":l[2]||(l[2]=e=>S.multi_subject_status=e)},{default:g(()=>[h(p(a.$t("Ema_Enable_Atfes")),1)],void 0,!0),_:1},8,["modelValue"])],void 0,!0),_:1}),S.multi_subject_status?(c(),r(j,{key:0},[a.has_campaign_pro?(c(),r("div",I,[_("div",x,[_("div",M,p(a.$t("Ema_Your_ppwbctpair")),1)]),_("table",null,[_("thead",null,[_("tr",null,[_("th",null,p(a.$t("Subject")),1),_("th",T,p(a.$t("Priority (%)")),1),l[10]||(l[10]=_("th",{style:{width:"60px"}},null,-1))])]),_("tbody",null,[(c(!0),r(j,null,V($.campaign.subjects,(e,l)=>(c(),r("tr",{key:l},[_("td",null,[b(U,{doc_url:"https://fluentcrm.com/docs/merge-codes-smart-codes-usage/",placeholder:a.$t("Subject Test")+" "+(l+1),data:S.smartcodes,modelValue:e.value,"onUpdate:modelValue":a=>e.value=a},null,8,["placeholder","data","modelValue","onUpdate:modelValue"])]),_("td",null,[b(W,{min:1,max:99,modelValue:e.key,"onUpdate:modelValue":a=>e.key=a},null,8,["modelValue","onUpdate:modelValue"])]),_("td",null,[b(G,{disabled:$.campaign.subjects.length<=2,onClick:e=>E.removeSubject(l),type:"danger",size:"small",class:"fcrm_delete_subject_btn"},{default:g(()=>[_("span",F,[b(Y,{"icon-name":"delete"})])],void 0,!0),_:1},8,["disabled","onClick"])])]))),128))])]),_("div",z,[b(G,{onClick:E.addSubject,size:"small"},{default:g(()=>[_("span",q,[b(Y,{"icon-name":"plus"})]),h(" "+p(a.$t("Add More")),1)],void 0,!0),_:1},8,["onClick"])])])):(c(),r("div",R,[b(J)]))],64)):v("",!0)],64)):v("",!0),b(D,{label:a.$t("Email Pre-Header")},{default:g(()=>[b(N,{placeholder:a.$t("Email Pre-Header"),modelValue:$.campaign.email_pre_header,"onUpdate:modelValue":l[3]||(l[3]=e=>$.campaign.email_pre_header=e)},null,8,["placeholder","modelValue"])],void 0,!0),_:1},8,["label"]),$.mailer_settings&&$.campaign.settings.mailer_settings?(c(),y(Q,{key:1,class:"fc_its_gray fc_m_20",mailer_settings:$.campaign.settings.mailer_settings},null,8,["mailer_settings"])):v("",!0),b(D,null,{default:g(()=>[b(H,{"true-value":"1","false-value":"0",modelValue:$.campaign.utm_status,"onUpdate:modelValue":l[4]||(l[4]=e=>$.campaign.utm_status=e)},{default:g(()=>[h(p(a.$t("Ema_Add_UPFU")),1)],void 0,!0),_:1},8,["modelValue"])],void 0,!0),_:1}),1==$.campaign.utm_status||"1"==$.campaign.utm_status?(c(),r("div",B,[b(Z,{gutter:20},{default:g(()=>[b(X,{md:12,sm:24},{default:g(()=>[b(D,{label:a.$t("Campaign Source (required)")},{default:g(()=>[b(N,{placeholder:a.$t("The referrer: (e.g. google, newsletter)"),modelValue:$.campaign.utm_source,"onUpdate:modelValue":l[5]||(l[5]=e=>$.campaign.utm_source=e)},null,8,["placeholder","modelValue"])],void 0,!0),_:1},8,["label"])],void 0,!0),_:1}),b(X,{md:12,sm:24},{default:g(()=>[b(D,{label:a.$t("Campaign Medium (required)")},{default:g(()=>[b(N,{placeholder:a.$t("Marketing medium: (e.g. cpc, banner, email)"),modelValue:$.campaign.utm_medium,"onUpdate:modelValue":l[6]||(l[6]=e=>$.campaign.utm_medium=e)},null,8,["placeholder","modelValue"])],void 0,!0),_:1},8,["label"])],void 0,!0),_:1})],void 0,!0),_:1}),b(Z,{gutter:20},{default:g(()=>[b(X,{md:12,sm:24},{default:g(()=>[b(D,{label:a.$t("Campaign Name (required)")},{default:g(()=>[b(N,{placeholder:a.$t("Product, promo code, or slogan (e.g. spring_sale)"),modelValue:$.campaign.utm_campaign,"onUpdate:modelValue":l[7]||(l[7]=e=>$.campaign.utm_campaign=e)},null,8,["placeholder","modelValue"])],void 0,!0),_:1},8,["label"])],void 0,!0),_:1}),b(X,{md:12,sm:24},{default:g(()=>[b(D,{label:a.$t("Campaign Term")},{default:g(()=>[b(N,{placeholder:a.$t("Identify the paid keywords"),modelValue:$.campaign.utm_term,"onUpdate:modelValue":l[8]||(l[8]=e=>$.campaign.utm_term=e)},null,8,["placeholder","modelValue"])],void 0,!0),_:1},8,["label"])],void 0,!0),_:1})],void 0,!0),_:1}),b(Z,{gutter:20},{default:g(()=>[b(X,{md:12,sm:24},{default:g(()=>[b(D,{label:a.$t("Campaign Content")},{default:g(()=>[b(N,{placeholder:a.$t("Use to differentiate ads"),modelValue:$.campaign.utm_content,"onUpdate:modelValue":l[9]||(l[9]=e=>$.campaign.utm_content=e)},null,8,["placeholder","modelValue"])],void 0,!0),_:1},8,["label"])],void 0,!0),_:1}),b(X,{md:12,sm:24},{default:g(()=>[...l[11]||(l[11]=[_("span",null,null,-1)])],void 0,!0),_:1})],void 0,!0),_:1})])):v("",!0)],void 0),_:1},8,["label-position","model"])])}]]);export{D as E}; diff --git a/wp-content/plugins/fluent-crm/assets/Error.js b/wp-content/plugins/fluent-crm/assets/Error.js new file mode 100644 index 0000000..6627cc7 --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/Error.js @@ -0,0 +1 @@ +import{W as r,X as o,aa as e,a8 as s}from"./vendor.js?ver=3.1.8";import{_ as a}from"./fc-bits-ui.js?ver=3.1.8";const n={key:0,class:"el-form-item__error"};const t=a({name:"Error",props:["error"]},[["render",function(a,t,m,i,p,c){return m.error?(r(),o("span",n,e(m.error),1)):s("",!0)}]]);export{t as E}; diff --git a/wp-content/plugins/fluent-crm/assets/Exporter.js b/wp-content/plugins/fluent-crm/assets/Exporter.js new file mode 100644 index 0000000..9ebf36a --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/Exporter.js @@ -0,0 +1 @@ +import{k as e,av as t,az as s,aG as l,aD as o,aA as a,e as i,g as r}from"./vendor-element-plus.js?ver=3.1.8";import{aQ as c,W as d,Y as n,ay as m,a5 as u,Z as _,a9 as h,aa as p,ab as f,X as v,$ as g,a8 as x,J as b,az as y}from"./vendor.js?ver=3.1.8";import{s as C}from"./data_config.js?ver=3.1.8";import{G as k}from"./GenericPromo.js?ver=3.1.8";import{_ as E}from"./fc-bits-ui.js?ver=3.1.8";const V=["first_name","last_name","email"],w=["id","first_name","last_name","email","prefix","user_id","status","ip"],$=["total_order_value","total_order_count","first_order_date","last_order_date"],S={key:1,class:"fcrm_export_status"},F={key:2,class:"fcrm_export_status",style:{color:"var(--fc-error)"}},P={class:"fcrm_export_form_item"},A={class:"fcrm_check_all_wrapper"},D={class:"fcrm_export_limit_section"},U={class:"fcrm_primary_text d-block font-medium fcrm_mb_8"},O={class:"fcrm_primary_text d-block font-medium fcrm_mb_8"},j={key:0,class:"fcrm_secondary_text small fcrm_mt_4"},L={key:1,class:"fcrm_secondary_text small fcrm_mt_4",style:{color:"var(--fc-deep-bg)","font-weight":"500"}},R={key:0},z={key:1},G={class:"fcrm_export_footer_actions"};const q=E({name:"ExportSubscriber",props:["visible","search_query","selected_contacts","all_selected"],emits:["close"],components:{GenericPromo:k},data(){return{dialogVisible:this.visible,columns:[...V],available_columns:C,custom_fields:[],commerce_columns:[],limit:"",offset:"",check_all:!1,progressPercentage:0,csvFileDownloading:!1,csvChunks:[],rowsFetched:0,aborted:!1,exportStatus:"",exportError:""}},computed:{isExportingSelected(){return this.selected_contacts&&this.selected_contacts.length>0||this.all_selected},exportTitle(){return this.isExportingSelected?this.all_selected?this.$t("Export All Filtered Contacts"):this.$t("Export Selected Contacts")+" ("+this.selected_contacts.length+")":this.$t("Export Contacts")}},methods:{ucFirst:e=>e&&"string"==typeof e?e.charAt(0).toUpperCase()+e.slice(1):e||"",handleAllCheck(){this.check_all?(this.selectAllColumns(),this.appVars.contact_custom_fields.length&&this.selectAllCustomFields(),this.appVars.commerce_provider&&(this.commerce_columns=[...$])):this.resetSelections()},selectAllColumns(){this.columns=[...w,...this.available_columns.map(e=>e.value)]},selectAllCustomFields(){this.custom_fields=this.appVars.contact_custom_fields.map(e=>e.slug)},resetSelections(){this.columns=[...V],this.custom_fields=[],this.commerce_columns=[]},hide(){this.$emit("close")},async exportContacts(){this.csvFileDownloading=!0,this.progressPercentage=0,this.csvChunks=["\ufeff"],this.rowsFetched=0,this.aborted=!1,this.exportStatus="",this.exportError="";const e={...this.search_query,columns:this.columns,custom_fields:this.custom_fields,commerce_columns:this.commerce_columns,limit:this.limit,offset:this.offset};"advanced"===e.filter_type&&e.advanced_filters&&(e.advanced_filters=JSON.stringify(e.advanced_filters)),this.isExportingSelected&&!this.all_selected&&(e.contact_ids=this.selected_contacts.map(e=>e.id));try{let t=1,s=!0,l=0;for(;s&&!this.aborted;){this.exportStatus=this.$t("Fetching contacts...");const o=await this.fetchPage(e,t);1===t&&(this.csvChunks.push(this.csvRow(o.headers)),l=o.total);const a=o.rows,i=new Array(a.length);for(let e=0;e0&&(this.progressPercentage=Math.min(99,Math.round(this.rowsFetched/l*100))),t++}if(this.aborted)return void this.resetExportState();this.exportStatus=this.$t("Preparing download..."),this.progressPercentage=100,await new Promise(e=>requestAnimationFrame(e)),this.downloadCsvBlob(),this.hide()}catch(t){this.exportError=t&&t.message||this.$t("Export failed. Please try again."),this.csvFileDownloading=!1,this.exportStatus=""}},fetchPage(e,t){return this.$post("subscribers-export",{...e,page:t})},downloadCsvBlob(){const e=new Blob(this.csvChunks,{type:"text/csv;charset=utf-8;"}),t=URL.createObjectURL(e),s=document.createElement("a");s.href=t,s.setAttribute("download","contacts_export_"+Date.now()+".csv"),document.body.appendChild(s),s.click(),document.body.removeChild(s),URL.revokeObjectURL(t),this.resetExportState()},sanitizeCell:e=>(e.length&&/^[=+\-@\t\r]/.test(e)&&(e="'"+e),e),csvRow(e){return e.map(function(e){var t=null==e?"":String(e);return-1!==(t=this.sanitizeCell(t)).indexOf(",")||-1!==t.indexOf('"')||-1!==t.indexOf("\n")||-1!==t.indexOf("\r")?'"'+t.replace(/"/g,'""')+'"':t}.bind(this)).join(",")+"\r\n"},cancelExport(){this.aborted=!0},resetExportState(){this.csvFileDownloading=!1,this.progressPercentage=0,this.csvChunks=[],this.rowsFetched=0,this.exportStatus="",this.exportError=""}},watch:{visible(e){this.dialogVisible=e},dialogVisible(e){e||this.hide()}}},[["render",function(C,k,E,V,w,$){const q=c("generic-promo"),B=t,I=s,T=l,J=i,M=a,N=o,Q=e,W=r;return d(),n(W,{title:$.exportTitle,modelValue:w.dialogVisible,"onUpdate:modelValue":k[7]||(k[7]=e=>w.dialogVisible=e),width:"50%","append-to-body":!0,"close-on-click-modal":!1,onClose:k[8]||(k[8]=e=>$.hide()),class:"fcrm_subscribers-export-dialog","align-center":""},m({default:u(()=>[C.has_campaign_pro?(d(),v("div",{key:1,class:"fcrm_export_content",style:g({pointerEvents:w.csvFileDownloading?"none":"auto"})},[w.csvFileDownloading?(d(),n(B,{key:0,"text-inside":!0,"stroke-width":26,percentage:w.progressPercentage},null,8,["percentage"])):x("",!0),w.exportStatus?(d(),v("p",S,p(w.exportStatus),1)):x("",!0),w.exportError?(d(),v("p",F,p(w.exportError),1)):x("",!0),_("div",P,[_("h3",null,p(C.$t("Exp_Please_sctywte")),1),_("div",A,[f(I,{modelValue:w.check_all,"onUpdate:modelValue":k[0]||(k[0]=e=>w.check_all=e),onChange:$.handleAllCheck},{default:u(()=>[h(p(C.$t("Select All")),1)],void 0,!0),_:1},8,["modelValue","onChange"])]),f(T,{modelValue:w.columns,"onUpdate:modelValue":k[1]||(k[1]=e=>w.columns=e),class:"fcrm_2_col_items"},{default:u(()=>[f(I,{value:"id"},{default:u(()=>[h(p(C.$t("ID")),1)],void 0,!0),_:1}),f(I,{value:"prefix"},{default:u(()=>[h(p(C.$t("Name Prefix")),1)],void 0,!0),_:1}),f(I,{value:"user_id"},{default:u(()=>[h(p(C.$t("User ID")),1)],void 0,!0),_:1}),f(I,{value:"email"},{default:u(()=>[h(p(C.$t("Email")),1)],void 0,!0),_:1}),(d(!0),v(b,null,y(w.available_columns,e=>(d(),n(I,{value:e.value,key:e.value},{default:u(()=>[h(p(e.label),1)],void 0,!0),_:2},1032,["value"]))),128)),f(I,{value:"ip"},{default:u(()=>[h(p(C.$t("IP Address")),1)],void 0,!0),_:1})],void 0,!0),_:1},8,["modelValue"]),C.appVars.contact_custom_fields.length?(d(),v(b,{key:0},[_("h3",null,p(C.$t("Custom Contact Fields")),1),f(T,{modelValue:w.custom_fields,"onUpdate:modelValue":k[2]||(k[2]=e=>w.custom_fields=e),class:"fcrm_2_col_items"},{default:u(()=>[(d(!0),v(b,null,y(C.appVars.contact_custom_fields,e=>(d(),n(I,{value:e.slug,key:e.slug},{default:u(()=>[h(p(e.label),1)],void 0,!0),_:2},1032,["value"]))),128))],void 0,!0),_:1},8,["modelValue"])],64)):x("",!0),C.appVars.commerce_provider?(d(),v(b,{key:1},[_("h3",null,p(C.$t("Commerce Fields"))+" ("+p($.ucFirst(C.appVars.commerce_provider))+")",1),f(T,{modelValue:w.commerce_columns,"onUpdate:modelValue":k[3]||(k[3]=e=>w.commerce_columns=e),class:"fcrm_2_col_items"},{default:u(()=>[f(I,{value:"total_order_value"},{default:u(()=>[h(p(C.$t("Lifetime Value")),1)],void 0,!0),_:1}),f(I,{value:"total_order_count"},{default:u(()=>[h(p(C.$t("Total Order Count")),1)],void 0,!0),_:1}),f(I,{value:"first_order_date"},{default:u(()=>[h(p(C.$t("Customer Since")),1)],void 0,!0),_:1}),f(I,{value:"last_order_date"},{default:u(()=>[h(p(C.$t("Last Order Date")),1)],void 0,!0),_:1})],void 0,!0),_:1},8,["modelValue"])],64)):x("",!0)]),_("div",D,[f(N,{gutter:20},{default:u(()=>[f(M,{span:12},{default:u(()=>[_("label",U,p(C.$t("Contact Export Limit")),1),f(J,{type:"number",modelValue:w.limit,"onUpdate:modelValue":k[4]||(k[4]=e=>w.limit=e),disabled:$.isExportingSelected},null,8,["modelValue","disabled"])],void 0,!0),_:1}),f(M,{span:12},{default:u(()=>[_("label",O,p(C.$t("Contact Export Offset")),1),f(J,{type:"number",modelValue:w.offset,"onUpdate:modelValue":k[5]||(k[5]=e=>w.offset=e),disabled:$.isExportingSelected},null,8,["modelValue","disabled"])],void 0,!0),_:1})],void 0,!0),_:1}),$.isExportingSelected?(d(),v("p",L,[E.all_selected?(d(),v("span",z,p(C.$t("Exporting all contacts matching your current filters")),1)):(d(),v("span",R,p(C.$t("Exporting"))+" "+p(E.selected_contacts.length)+" "+p(C.$t("selected contact(s)")),1))])):(d(),v("p",j,p(C.$t("Exp_Leave_tbfnloo")),1))])],4)):(d(),n(q,{key:0}))],void 0),_:2},[C.has_campaign_pro?{name:"footer",fn:u(()=>[_("div",G,[w.csvFileDownloading?(d(),n(Q,{key:0,onClick:$.cancelExport},{default:u(()=>[h(p(C.$t("Cancel Export")),1)],void 0,!0),_:1},8,["onClick"])):(d(),n(Q,{key:1,onClick:$.hide},{default:u(()=>[h(p(C.$t("Cancel")),1)],void 0,!0),_:1},8,["onClick"])),f(Q,{disabled:w.csvFileDownloading,type:"primary",onClick:k[6]||(k[6]=e=>$.exportContacts())},{default:u(()=>[h(p(C.$t("Export Contacts")),1)],void 0,!0),_:1},8,["disabled"])])]),key:"0"}:void 0]),1032,["title","modelValue"])}]]);export{q as E}; diff --git a/wp-content/plugins/fluent-crm/assets/FieldEditor.js b/wp-content/plugins/fluent-crm/assets/FieldEditor.js new file mode 100644 index 0000000..ba7140f --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/FieldEditor.js @@ -0,0 +1 @@ +import{a6 as e,k as l,E as t,e as a,aL as o,aK as d,L as i,aM as n,aF as s,aE as u,aY as p,aA as r,aD as m,W as _,aw as c,ay as f,a$ as y,aJ as v,az as h,b0 as V,b8 as b,b9 as g,aG as k,bd as x,ax as $}from"./vendor-element-plus.js?ver=3.1.8";import{aQ as U,W as C,X as w,J as S,az as M,ab as A,a5 as P,a9 as D,aa as E,Z as T,Y as F,a8 as z,a6 as j,ay as I,bW as O,a0 as R,ac as L,_ as W,ax as Y}from"./vendor.js?ver=3.1.8";import{_ as N}from"./fc-bits.js?ver=3.1.8";import{_ as G,I as H}from"./fc-bits-ui.js?ver=3.1.8";import{b as q,W as J,c as B}from"./_FormBuilder2.js?ver=3.1.8";import{E as X}from"./EmailComposer.js?ver=3.1.8";import{O as K}from"./_OptionSelector.js?ver=3.1.8";import{A as Q}from"./_AjaxSelector.js?ver=3.1.8";import{T as Z}from"./_TaxonomyTermsSelector.js?ver=3.1.8";import{$ as ee}from"./data_config.js?ver=3.1.8";import{M as le}from"./_MailerConfig.js?ver=3.1.8";import{I as te}from"./ItemCopier.js?ver=3.1.8";import{M as ae}from"./_MergeCodes.js?ver=3.1.8";const oe={class:"fc_url_boxes"};const de=G({name:"MultiTextOptions",components:{Delete:e},emits:["update:modelValue"],props:{modelValue:{type:Array,default:()=>[""]},field:{type:Object}},data:()=>({options:[]}),watch:{options:{deep:!0,handler(){const e=[];this.options.forEach(l=>{l.value&&e.push(l.value)}),this.$emit("update:modelValue",e)}}},methods:{addMoreUrl(){this.options.push({value:""})},deleteUrl(e){this.options.splice(e,1)}},mounted(){const e=JSON.parse(JSON.stringify(this.modelValue));e&&e.length?(this.options=[],e.forEach(e=>{this.options.push({value:e})})):this.options=[{value:""}]}},[["render",function(e,o,d,i,n,s){const u=U("Delete"),p=t,r=l,m=a;return C(),w("div",oe,[(C(!0),w(S,null,M(n.options,(e,l)=>(C(),w("div",{class:"fc_each_text_option",key:l},[A(m,{type:d.field.input_type,placeholder:d.field.placeholder,modelValue:e.value,"onUpdate:modelValue":l=>e.value=l},{append:P(()=>[A(r,{onClick:e=>s.deleteUrl(l),disabled:1==n.options.length},{default:P(()=>[A(p,null,{default:P(()=>[A(u)],void 0,!0),_:1})],void 0,!0),_:1},8,["onClick","disabled"])]),_:2},1032,["type","placeholder","modelValue","onUpdate:modelValue"])]))),128)),A(r,{onClick:o[0]||(o[0]=e=>s.addMoreUrl()),size:"small",type:"info"},{default:P(()=>[D(E(e.$t("Add More")),1)],void 0),_:1})])}]]),ie={class:"fc_horizontal_table"};const ne=G({name:"FormFieldsGroupMapper",props:["field","model"]},[["render",function(e,l,t,a,i,n){const s=o,u=d;return C(),w("table",ie,[T("thead",null,[T("tr",null,[T("th",null,E(t.field.local_label),1),T("th",null,E(t.field.remote_label),1)])]),T("tbody",null,[(C(!0),w(S,null,M(t.field.fields,(l,a)=>(C(),w("tr",{key:a},[T("td",null,E(l.label),1),T("td",null,[A(u,{clearable:"",filterable:"",modelValue:t.model[a],"onUpdate:modelValue":e=>t.model[a]=e,placeholder:e.$t("Select Value")},{default:P(()=>[(C(!0),w(S,null,M(t.field.value_options,e=>(C(),F(s,{key:e.id,value:e.id,label:e.title},null,8,["value","label"]))),128))],void 0),_:1},8,["modelValue","onUpdate:modelValue","placeholder"])])]))),128))])])}]]);const se=G({name:"WPUrlSelector",props:["field","modelValue"],emits:["update:modelValue"],data(){return{model:this.modelValue}},watch:{model(e){this.$emit("update:modelValue",e)}}},[["render",function(e,l,t,o,d,i){const n=a;return C(),F(n,{type:"url",placeholder:t.field.placeholder,modelValue:d.model,"onUpdate:modelValue":l[0]||(l[0]=e=>d.model=e)},null,8,["placeholder","modelValue"])}]]),ue={class:"fc_condition_groups"},pe={key:0,class:"fc_cond_and"},re={class:"fc_condition_group"},me={class:"wp-list-table widefat fixed striped table-view-list posts"},_e={style:{width:"180px"}},ce={style:{width:"180px"}},fe={key:0},ye={key:1},ve={style:{"text-align":"right"}},he={key:0},Ve=["innerHTML"],be={key:0,class:"text-align-right"};const ge=G({name:"ConditionGroup",props:["field","modelValue"],emits:["update:modelValue"],components:{OptionSelector:K,AjaxSelector:Q,Plus:i,Delete:e},data(){return{model:this.modelValue}},computed:{flat_properties(){let e={};return this.each(this.field.condition_properties,l=>{e={...e,...l.options}}),e}},watch:{model(e){this.$emit("update:modelValue",e)}},methods:{addCondition(e){this.model[e].conditions.push({data_key:"",operator:"=",data_value:""})},deleteProp(e,l){this.model[e].conditions.splice(l,1)},removeGroup(e){this.model.splice(e,1)},addConditionalGroup(){this.model.push({conditions:[{data_key:"",operator:"=",data_value:""}],match_type:"match_all"})}}},[["render",function(e,i,p,r,m,_){const c=o,f=n,y=d,v=a,h=U("option-selector"),V=U("ajax-selector"),b=U("Plus"),g=t,k=l,x=U("Delete"),$=s,j=u;return C(),w("div",ue,[(C(!0),w(S,null,M(m.model,(l,t)=>(C(),w("div",{class:"fc_condition_wrapper",key:t},[0!=t?(C(),w("div",pe,E(e.$t("OR")),1)):z("",!0),T("div",re,[T("table",me,[T("thead",null,[T("tr",null,[T("th",_e,E(p.field.labels.data_key_label),1),T("th",ce,E(p.field.labels.condition_label),1),T("th",null,E(p.field.labels.data_value_label),1),i[1]||(i[1]=T("th",{style:{width:"90px"}},null,-1))])]),T("tbody",null,[(C(!0),w(S,null,M(l.conditions,(a,o)=>(C(),w("tr",{key:o},[T("td",null,[A(y,{onChange:e=>{a.operator="=",a.data_value=""},clearable:"",placeholder:e.$t("Select"),size:"small",modelValue:a.data_key,"onUpdate:modelValue":e=>a.data_key=e},{default:P(()=>[(C(!0),w(S,null,M(p.field.condition_properties,(e,l)=>(C(),F(f,{key:l,label:e.label},{default:P(()=>[(C(!0),w(S,null,M(e.options,(e,l)=>(C(),F(c,{key:l,value:l,label:e.label},null,8,["value","label"]))),128))],void 0,!0),_:2},1032,["label"]))),128))],void 0),_:2},1032,["onChange","placeholder","modelValue","onUpdate:modelValue"])]),T("td",null,[a.data_key?(C(),F(y,{key:0,clearable:"",placeholder:e.$t("Select Condition"),size:"small",modelValue:a.operator,"onUpdate:modelValue":e=>a.operator=e},{default:P(()=>[_.flat_properties[a.data_key].multiple?(C(),w(S,{key:0},[A(c,{value:"=",label:e.$t("Match any Of")},null,8,["label"]),A(c,{value:"match_all",label:e.$t("Match all of")},null,8,["label"]),A(c,{value:"match_none_of",label:e.$t("Match none of")},null,8,["label"])],64)):(C(),w(S,{key:1},[A(c,{value:"=",label:e.$t("Equal")},null,8,["label"]),A(c,{value:"!=",label:e.$t("Not Equal")},null,8,["label"]),"text"==_.flat_properties[a.data_key].type?(C(),w(S,{key:0},[A(c,{value:"contains",label:e.$t("Contains")},null,8,["label"]),A(c,{value:"doNotContains",label:e.$t("Not Contains")},null,8,["label"]),A(c,{value:"startsWith",label:e.$t("Starts With")},null,8,["label"]),A(c,{value:"endsWith",label:e.$t("Ends With")},null,8,["label"])],64)):"number"==_.flat_properties[a.data_key].type?(C(),w(S,{key:1},[A(c,{value:">",label:e.$t("Greater Than")},null,8,["label"]),A(c,{value:"<",label:e.$t("Less Than")},null,8,["label"])],64)):z("",!0)],64))],void 0),_:2},1032,["placeholder","modelValue","onUpdate:modelValue"])):z("",!0)]),T("td",null,[a.data_key&&a.operator?(C(),w("div",fe,["text"==_.flat_properties[a.data_key].type?(C(),F(v,{key:0,size:"small",link:"",modelValue:a.data_value,"onUpdate:modelValue":e=>a.data_value=e},null,8,["modelValue","onUpdate:modelValue"])):"number"==_.flat_properties[a.data_key].type?(C(),F(v,{key:1,size:"small",type:"number",modelValue:a.data_value,"onUpdate:modelValue":e=>a.data_value=e},null,8,["modelValue","onUpdate:modelValue"])):"select"==_.flat_properties[a.data_key].type?(C(),F(y,{key:2,size:"small",modelValue:a.data_value,"onUpdate:modelValue":e=>a.data_value=e,clearable:""},{default:P(()=>[(C(!0),w(S,null,M(_.flat_properties[a.data_key].options,e=>(C(),F(c,{key:e.id,label:e.title,value:e.id},null,8,["label","value"]))),128))],void 0),_:2},1032,["modelValue","onUpdate:modelValue"])):"option_selector"==_.flat_properties[a.data_key].type?(C(),F(h,{key:3,field:{placeholder:"Select",is_multiple:_.flat_properties[a.data_key].multiple,option_key:_.flat_properties[a.data_key].option_key},modelValue:a.data_value,"onUpdate:modelValue":e=>a.data_value=e},null,8,["field","modelValue","onUpdate:modelValue"])):"rest_selector"==_.flat_properties[a.data_key].type?(C(),F(V,{key:4,field:{placeholder:"Select",is_multiple:_.flat_properties[a.data_key].multiple,option_key:_.flat_properties[a.data_key].option_key},modelValue:a.data_value,"onUpdate:modelValue":e=>a.data_value=e},null,8,["field","modelValue","onUpdate:modelValue"])):z("",!0)])):(C(),w("div",ye,E(e.$t("Select data source and operator first")),1))]),T("td",ve,[A(k,{onClick:e=>_.addCondition(t),type:"success",size:"small"},{default:P(()=>[A(g,null,{default:P(()=>[A(b)],void 0,!0),_:1})],void 0),_:1},8,["onClick"]),A(k,{disabled:1==l.conditions.length,onClick:e=>_.deleteProp(t,o),size:"small",type:"danger"},{default:P(()=>[A(g,null,{default:P(()=>[A(x)],void 0,!0),_:1})],void 0),_:1},8,["disabled","onClick"])])]))),128))])]),p.field.hide_match_type?(C(),w("p",{key:1,style:{margin:"0",padding:"0"},innerHTML:e.$t("Inside group conditions are match all")},null,8,Ve)):(C(),w("div",he,[T("p",null,[T("b",null,E(e.$t("Match Type")),1)]),A(j,{modelValue:l.match_type,"onUpdate:modelValue":e=>l.match_type=e},{default:P(()=>[A($,{value:"match_all"},{default:P(()=>[D(E(p.field.labels.match_type_all_label),1)],void 0,!0),_:1}),A($,{value:"match_any"},{default:P(()=>[D(E(p.field.labels.match_type_any_label),1)],void 0,!0),_:1})],void 0),_:1},8,["modelValue","onUpdate:modelValue"])])),m.model.length>1?(C(),F(k,{key:2,onClick:e=>_.removeGroup(t),type:"danger",size:"small"},{default:P(()=>[A(g,null,{default:P(()=>[A(x)],void 0,!0),_:1}),D(" "+E(e.$t("Delete this group")),1)],void 0),_:1},8,["onClick"])):z("",!0)])]))),128)),p.field.is_multiple_grouping?(C(),w("div",be,[A(k,{onClick:i[0]||(i[0]=e=>_.addConditionalGroup()),type:"primary",size:"small"},{default:P(()=>[A(g,null,{default:P(()=>[A(b)],void 0,!0),_:1}),D(" "+E(e.$t("Add Another Conditional Group")),1)],void 0),_:1})])):z("",!0)])}]]),ke={class:"fcrm_value_property_group"},xe={style:{width:"180px"}},$e={key:0},Ue={key:4,class:"info",style:{margin:"2px 0 0 0","line-height":"1","font-size":"12px"}},Ce={class:"fcrm_value_property_table_actions"},we={class:"icon"},Se={class:"fcrm_value_property_group_footer"},Me={class:"icon"};const Ae=G({name:"ConditionGroup",props:["field","modelValue"],emits:["update:modelValue"],components:{Icons:H,OptionSelector:K},data(){return{model:this.modelValue}},watch:{model(e){this.$emit("update:modelValue",e)}},methods:{$t:ee,addProperty(){this.model.push({data_key:"",data_value:""})},deleteProp(e){this.model.splice(e,1)}}},[["render",function(e,t,i,n,s,u){const _=o,c=d,f=a,y=p,v=r,h=m,V=U("option-selector"),b=U("Icons"),g=l;return C(),w("div",ke,[T("table",null,[T("thead",null,[T("tr",null,[T("th",xe,E(i.field.data_key_label),1),T("th",null,E(i.field.data_value_label),1),t[1]||(t[1]=T("th",{style:{width:"50px"}},null,-1))])]),T("tbody",null,[(C(!0),w(S,null,M(s.model,(e,l)=>(C(),w("tr",{key:l},[T("td",null,[A(c,{clearable:"",onChange:l=>{e.data_value,delete e.data_operation},placeholder:u.$t("Select"),size:"small",modelValue:e.data_key,"onUpdate:modelValue":l=>e.data_key=l,filterable:""},{default:P(()=>[(C(!0),w(S,null,M(i.field.property_options,(e,l)=>(C(),F(_,{key:l,value:l,label:e.label},null,8,["value","label"]))),128))],void 0),_:1},8,["onChange","placeholder","modelValue","onUpdate:modelValue"])]),T("td",null,[e.data_key?(C(),w("div",$e,["text"==i.field.property_options[e.data_key].type?(C(),F(f,{key:0,size:"small",link:"",modelValue:e.data_value,"onUpdate:modelValue":l=>e.data_value=l},null,8,["modelValue","onUpdate:modelValue"])):z("",!0),"textarea"==i.field.property_options[e.data_key].type?(C(),F(f,{key:1,size:"small",type:"textarea",modelValue:e.data_value,"onUpdate:modelValue":l=>e.data_value=l},null,8,["modelValue","onUpdate:modelValue"])):z("",!0),"date"==i.field.property_options[e.data_key].type?(C(),F(y,{key:2,"value-format":"YYYY-MM-DD",size:"small",modelValue:e.data_value,"onUpdate:modelValue":l=>e.data_value=l,type:"date",placeholder:u.$t("Pick a date")},null,8,["modelValue","onUpdate:modelValue","placeholder"])):z("",!0),"date_time"==i.field.property_options[e.data_key].type?(C(),F(y,{key:3,"value-format":"YYYY-MM-DD HH:mm:ss",modelValue:e.data_value,"onUpdate:modelValue":l=>e.data_value=l,type:"datetime",size:"small",placeholder:u.$t("Pick a date and time")},null,8,["modelValue","onUpdate:modelValue","placeholder"])):z("",!0),"date"!=i.field.property_options[e.data_key].type&&"date_time"!=i.field.property_options[e.data_key].type||!i.field.property_options[e.data_key].info?"number"==i.field.property_options[e.data_key].type?(C(),w(S,{key:5},["yes"==i.field.support_operations?(C(),F(h,{key:0,gutter:10},{default:P(()=>[A(v,{span:18},{default:P(()=>[A(f,{size:"small",type:"number",class:"input-with-select",modelValue:e.data_value,"onUpdate:modelValue":l=>e.data_value=l},null,8,["modelValue","onUpdate:modelValue"])],void 0,!0),_:2},1024),A(v,{span:6},{default:P(()=>[A(c,{size:"small",modelValue:e.data_operation,"onUpdate:modelValue":l=>e.data_operation=l,placeholder:u.$t("Replace Value")},{default:P(()=>[A(_,{value:"",label:u.$t("Replace Value")},null,8,["label"]),A(_,{value:"subtract",label:u.$t("Subtract Value")},null,8,["label"]),A(_,{value:"add",label:u.$t("Add Value")},null,8,["label"])],void 0,!0),_:1},8,["modelValue","onUpdate:modelValue","placeholder"])],void 0,!0),_:2},1024)],void 0),_:2},1024)):(C(),F(f,{key:1,size:"small",type:"number",class:"input-with-select",modelValue:e.data_value,"onUpdate:modelValue":l=>e.data_value=l},null,8,["modelValue","onUpdate:modelValue"]))],64)):"select"==i.field.property_options[e.data_key].type?(C(),w(S,{key:6},["yes"==i.field.support_operations&&i.field.property_options[e.data_key].multiple?(C(),F(h,{key:0,gutter:10},{default:P(()=>[A(v,{span:18},{default:P(()=>[A(c,{size:"small",modelValue:e.data_value,"onUpdate:modelValue":l=>e.data_value=l,clearable:"",multiple:i.field.property_options[e.data_key].multiple,filterable:""},{default:P(()=>[(C(!0),w(S,null,M(i.field.property_options[e.data_key].options,e=>(C(),F(_,{key:e.id,label:e.title,value:e.id},null,8,["label","value"]))),128))],void 0,!0),_:2},1032,["modelValue","onUpdate:modelValue","multiple"])],void 0,!0),_:2},1024),A(v,{span:6},{default:P(()=>[A(c,{size:"small",modelValue:e.data_operation,"onUpdate:modelValue":l=>e.data_operation=l,placeholder:u.$t("Replace Value")},{default:P(()=>[A(_,{value:"",label:u.$t("Replace Options")},null,8,["label"]),A(_,{value:"subtract",label:u.$t("Subtract Options")},null,8,["label"]),A(_,{value:"add",label:u.$t("Add Options")},null,8,["label"])],void 0,!0),_:1},8,["modelValue","onUpdate:modelValue","placeholder"])],void 0,!0),_:2},1024)],void 0),_:2},1024)):(C(),F(c,{key:1,size:"small",modelValue:e.data_value,"onUpdate:modelValue":l=>e.data_value=l,clearable:"",multiple:i.field.property_options[e.data_key].multiple,filterable:""},{default:P(()=>[(C(!0),w(S,null,M(i.field.property_options[e.data_key].options,e=>(C(),F(_,{key:e.id,label:e.title,value:e.id},null,8,["label","value"]))),128))],void 0),_:2},1032,["modelValue","onUpdate:modelValue","multiple"]))],64)):"option_selector"==i.field.property_options[e.data_key].type?(C(),F(V,{key:7,field:{placeholder:u.$t("Select"),is_multiple:i.field.property_options[e.data_key].multiple,option_key:i.field.property_options[e.data_key].option_key},modelValue:e.data_value,"onUpdate:modelValue":l=>e.data_value=l},null,8,["field","modelValue","onUpdate:modelValue"])):z("",!0):(C(),w("p",Ue,E(i.field.property_options[e.data_key].info),1))])):z("",!0)]),T("td",null,[T("div",Ce,[A(g,{disabled:1==s.model.length,onClick:e=>u.deleteProp(l),size:"small",class:"small only-icon-btn"},{default:P(()=>[T("span",we,[A(b,{"icon-name":"delete"})])],void 0),_:1},8,["disabled","onClick"])])])]))),128))])]),T("div",Se,[A(g,{onClick:t[0]||(t[0]=e=>u.addProperty()),size:"small"},{default:P(()=>[T("span",Me,[A(b,{"icon-name":"plus"})]),D(" "+E(u.$t("Add More")),1)],void 0),_:1})])])}]]),Pe={class:"fcrm_value_property_group"},De={style:{width:"180px"}},Ee={class:"fcrm_value_property_table_actions"},Te={class:"icon"},Fe={class:"fcrm_value_property_group_footer"},ze={class:"icon"};const je=G({name:"TextValueMultiProperties",props:["field","modelValue"],emits:["update:modelValue"],components:{Icons:H,InputTextPopper:q},data(){return{model:this.modelValue}},watch:{model(e){this.$emit("update:modelValue",e)}},methods:{addProperty(){this.model.push({data_key:"",data_value:""})},deleteProp(e){this.model.splice(e,1)}}},[["render",function(e,t,o,d,i,n){const s=a,u=U("input-text-popper"),p=U("Icons"),r=l;return C(),w("div",Pe,[T("table",null,[T("thead",null,[T("tr",null,[T("th",De,E(o.field.data_key_label),1),T("th",null,E(o.field.data_value_label),1),t[1]||(t[1]=T("th",{style:{width:"50px"}},null,-1))])]),T("tbody",null,[(C(!0),w(S,null,M(i.model,(e,l)=>(C(),w("tr",{key:l},[T("td",null,[A(s,{placeholder:o.field.data_key_placeholder,link:"",modelValue:e.data_key,"onUpdate:modelValue":l=>e.data_key=l},null,8,["placeholder","modelValue","onUpdate:modelValue"])]),T("td",null,["text-popper"==o.field.value_input_type?(C(),F(u,{key:0,field:{placeholder:o.field.data_value_placeholder,popper_class:"fc_limit_height"},modelValue:e.data_value,"onUpdate:modelValue":l=>e.data_value=l},null,8,["field","modelValue","onUpdate:modelValue"])):(C(),F(s,{key:1,link:"",placeholder:o.field.data_value_placeholder,modelValue:e.data_value,"onUpdate:modelValue":l=>e.data_value=l},null,8,["placeholder","modelValue","onUpdate:modelValue"]))]),T("td",null,[T("div",Ee,[A(r,{disabled:1==i.model.length,onClick:e=>n.deleteProp(l),size:"small",class:"small only-icon-btn"},{default:P(()=>[T("span",Te,[A(p,{"icon-name":"delete"})])],void 0),_:1},8,["disabled","onClick"])])])]))),128))])]),T("div",Fe,[A(r,{onClick:t[0]||(t[0]=e=>n.addProperty()),size:"small"},{default:P(()=>[T("span",ze,[A(p,{"icon-name":"plus"})]),D(" "+E(e.$t("Add More")),1)],void 0),_:1})])])}]]),Ie={class:"fc_coupon_settings fcrm_coupon_settings"},Oe={class:"fcrm_coupon_config_settings"},Re={key:0,class:"fcrm_coupon_config_settings_title"},Le={key:1,class:"fc_info"},We={class:"row-section-title"},Ye={key:1,class:"fc_info"};const Ne=G({name:"AdvancedCouponSettings",props:["settings"],emits:["saveAndReload"],components:{AjaxSelector:Q,ItemCopier:te,InfoFilled:_},data:()=>({saving:!1}),methods:{save(){this.saving=!0,this.$emit("saveAndReload")}}},[["render",function(e,i,n,_,b,g){const k=U("item-copier"),x=s,$=u,M=c,O=U("ajax-selector"),R=l,L=U("InfoFilled"),W=t,Y=v,N=a,G=o,H=d,q=r,J=m,B=p,X=h,K=y,Q=V,Z=f;return C(),w("div",Ie,[T("div",Oe,[n.settings.smart_code?(C(),w("p",Re,[D(E(e.$t("Dynamic_Coupon_Usage"))+" ",1),A(k,{style:{"max-width":"220px"},text:n.settings.smart_code},null,8,["text"])])):z("",!0),A(M,{label:"Coupon Code Configuration Type"},{default:P(()=>[A($,{modelValue:n.settings.template_type,"onUpdate:modelValue":i[0]||(i[0]=e=>n.settings.template_type=e)},{default:P(()=>[A(x,{value:"new"},{default:P(()=>[D(E(e.$t("Configure from scratch")),1)],void 0,!0),_:1}),A(x,{value:"templated"},{default:P(()=>[D(E(e.$t("Use Existing Coupon as Template")),1)],void 0,!0),_:1})],void 0,!0),_:1},8,["modelValue"])],void 0),_:1}),"templated"==n.settings.template_type?(C(),F(M,{key:1,label:e.$t("Select your existing Coupon Code")},{default:P(()=>[A(O,{modelValue:n.settings.base_coupon_id,"onUpdate:modelValue":i[1]||(i[1]=e=>n.settings.base_coupon_id=e),field:{option_key:"woo_coupons",sub_option_key:"main_only",is_multiple:!1}},null,8,["modelValue"]),T("p",null,E(e.$t("Dynamic_Coupon_Configuration")),1)],void 0),_:1},8,["label"])):z("",!0),n.settings.smart_code?z("",!0):(C(),F(M,{key:2},{default:P(()=>[j((C(),F(R,{disabled:b.saving,onClick:g.save,type:"primary"},{default:P(()=>[D(E(e.$t("Continue")),1)],void 0,!0),_:1},8,["disabled","onClick"])),[[Z,b.saving]])],void 0),_:1}))]),n.settings.smart_code?(C(),F(Q,{key:0,type:"border-card"},{default:P(()=>[A(K,{label:"General"},{default:P(()=>[A(M,null,{label:P(()=>[D(E(e.$t("Coupon Code Prefix"))+" ",1),A(Y,{class:"item",effect:"dark",content:e.$t("Coupon_Code_Prefix_help"),placement:"top-start"},{default:P(()=>[A(W,null,{default:P(()=>[A(L)],void 0,!0),_:1})],void 0,!0),_:1},8,["content"])]),default:P(()=>[A(N,{modelValue:n.settings.code_prefix,"onUpdate:modelValue":i[2]||(i[2]=e=>n.settings.code_prefix=e),placeholder:"eg: WELCOME_{{contact.first_name}}"},{suffix:P(()=>[...i[21]||(i[21]=[T("span",null,"-RANDOM_SUFFIX",-1)])]),_:1},8,["modelValue"])],void 0,!0),_:1}),"new"==n.settings.template_type?(C(),w(S,{key:0},[A(J,{gutter:30,class:"fcrm_mb_16"},{default:P(()=>[A(q,{md:12,xs:24},{default:P(()=>[A(M,{label:e.$t("Discount Type")},{default:P(()=>[A(H,{modelValue:n.settings.discount_type,"onUpdate:modelValue":i[3]||(i[3]=e=>n.settings.discount_type=e),placeholder:e.$t("Discount Type")},{default:P(()=>[A(G,{label:e.$t("Percentage Discount"),value:"percent"},null,8,["label"]),A(G,{label:e.$t("Fixed Cart Discount"),value:"fixed_cart"},null,8,["label"]),A(G,{label:e.$t("Fixed Product Discount"),value:"fixed_product"},null,8,["label"])],void 0,!0),_:1},8,["modelValue","placeholder"])],void 0,!0),_:1},8,["label"])],void 0,!0),_:1}),A(q,{md:12,xs:24},{default:P(()=>[A(M,{label:e.$t("Amount")},{default:P(()=>[A(N,{type:"number",modelValue:n.settings.amount,"onUpdate:modelValue":i[4]||(i[4]=e=>n.settings.amount=e),placeholder:e.$t("Amount")},I({_:2},["percent"==n.settings.discount_type?{name:"suffix",fn:P(()=>[i[22]||(i[22]=T("span",null,"%",-1))]),key:"0"}:{name:"prefix",fn:P(()=>[T("span",null,E(e.appVars.woo_currency_sign),1)]),key:"1"}]),1032,["modelValue","placeholder"])],void 0,!0),_:1},8,["label"])],void 0,!0),_:1})],void 0,!0),_:1}),A(M,{class:"coupon_expiry_item"},{label:P(()=>[D(E(e.$t("Coupon Expiry"))+" ",1),A(Y,{class:"item",effect:"dark",content:e.$t("Choose when the coupon will expire"),placement:"top-start"},{default:P(()=>[A(W,null,{default:P(()=>[A(L)],void 0,!0),_:1})],void 0,!0),_:1},8,["content"])]),default:P(()=>[A($,{modelValue:n.settings.expiry_type,"onUpdate:modelValue":i[5]||(i[5]=e=>n.settings.expiry_type=e)},{default:P(()=>[A(x,{value:"never"},{default:P(()=>[D(E(e.$t("Never Expires")),1)],void 0,!0),_:1}),A(x,{value:"fixed"},{default:P(()=>[D(E(e.$t("Fixed Date")),1)],void 0,!0),_:1}),A(x,{value:"relative_days"},{default:P(()=>[D(E(e.$t("Expire after x days of creation")),1)],void 0,!0),_:1})],void 0,!0),_:1},8,["modelValue"])],void 0,!0),_:1}),"fixed"==n.settings.expiry_type?(C(),F(M,{key:0,label:e.$t("Expiry Date")},{default:P(()=>[A(B,{modelValue:n.settings.date_expires,"onUpdate:modelValue":i[6]||(i[6]=e=>n.settings.date_expires=e),type:"date","value-format":"YYYY-MM-DD",placeholder:e.$t("Pick a date")},null,8,["modelValue","placeholder"])],void 0,!0),_:1},8,["label"])):z("",!0),"relative_days"==n.settings.expiry_type?(C(),F(M,{key:1,label:e.$t("Days")},{default:P(()=>[A(N,{type:"number",min:1,modelValue:n.settings.expiry_days,"onUpdate:modelValue":i[7]||(i[7]=e=>n.settings.expiry_days=e),placeholder:e.$t("Expire after x days")},{suffix:P(()=>[D(E(e.$t("days")),1)]),_:1},8,["modelValue","placeholder"])],void 0,!0),_:1},8,["label"])):z("",!0),A(M,null,{label:P(()=>[D(E(e.$t("Allow Free Shipping"))+" ",1),A(Y,{class:"item",effect:"dark",content:e.$t("Free_Shipping_Info"),placement:"top-start"},{default:P(()=>[A(W,null,{default:P(()=>[A(L)],void 0,!0),_:1})],void 0,!0),_:1},8,["content"])]),default:P(()=>[A($,{modelValue:n.settings.free_shipping,"onUpdate:modelValue":i[8]||(i[8]=e=>n.settings.free_shipping=e)},{default:P(()=>[A(x,{value:"yes"},{default:P(()=>[D(E(e.$t("Yes")),1)],void 0,!0),_:1}),A(x,{value:"no"},{default:P(()=>[D(E(e.$t("No")),1)],void 0,!0),_:1})],void 0,!0),_:1},8,["modelValue"])],void 0,!0),_:1})],64)):(C(),w("div",Le,[T("p",null,E(e.$t("Coupon settings will be inherited from the selected base coupon")),1)])),A(M,null,{default:P(()=>[A(X,{modelValue:n.settings.contact_email_only,"onUpdate:modelValue":i[9]||(i[9]=e=>n.settings.contact_email_only=e),"true-value":"yes","false-value":"no"},{default:P(()=>[D(E(e.$t("Restrict the generated coupon to Contact Email Only")),1)],void 0,!0),_:1},8,["modelValue"])],void 0,!0),_:1})],void 0,!0),_:1}),A(K,{label:e.$t("Restrictions & Limits")},{default:P(()=>["new"==n.settings.template_type?(C(),w(S,{key:0},[A(J,{gutter:30,class:"fcrm_mb_16"},{default:P(()=>[A(q,{md:12,xs:24},{default:P(()=>[A(M,null,{label:P(()=>[D(E(e.$t("Minimum Spend"))+" ",1),A(Y,{class:"item",effect:"dark",content:e.$t("Minimum_Spend_Requirement"),placement:"top-start"},{default:P(()=>[A(W,null,{default:P(()=>[A(L)],void 0,!0),_:1})],void 0,!0),_:1},8,["content"])]),default:P(()=>[A(N,{type:"number",modelValue:n.settings.minimum_amount,"onUpdate:modelValue":i[10]||(i[10]=e=>n.settings.minimum_amount=e),placeholder:e.$t("Minimum Spend")},{prefix:P(()=>[T("span",null,E(e.appVars.woo_currency_sign),1)]),_:1},8,["modelValue","placeholder"])],void 0,!0),_:1})],void 0,!0),_:1}),A(q,{md:12,xs:24},{default:P(()=>[A(M,null,{label:P(()=>[D(E(e.$t("Maximum Spend"))+" ",1),A(Y,{class:"item",effect:"dark",content:e.$t("Maximum_Spend_Limit"),placement:"top-start"},{default:P(()=>[A(W,null,{default:P(()=>[A(L)],void 0,!0),_:1})],void 0,!0),_:1},8,["content"])]),default:P(()=>[A(N,{type:"number",modelValue:n.settings.maximum_amount,"onUpdate:modelValue":i[11]||(i[11]=e=>n.settings.maximum_amount=e),placeholder:e.$t("Maximum Spend")},{prefix:P(()=>[T("span",null,E(e.appVars.woo_currency_sign),1)]),_:1},8,["modelValue","placeholder"])],void 0,!0),_:1})],void 0,!0),_:1})],void 0,!0),_:1}),A(J,{gutter:30,class:"fcrm_mb_16"},{default:P(()=>[A(q,{md:12,xs:24},{default:P(()=>[A(M,null,{label:P(()=>[D(E(e.$t("Products"))+" ",1),A(Y,{class:"item",effect:"dark",content:e.$t("Eligible_Products_For_Discount"),placement:"top-start"},{default:P(()=>[A(W,null,{default:P(()=>[A(L)],void 0,!0),_:1})],void 0,!0),_:1},8,["content"])]),default:P(()=>[A(O,{modelValue:n.settings.product_ids,"onUpdate:modelValue":i[12]||(i[12]=e=>n.settings.product_ids=e),field:{option_key:"woo_products",is_multiple:!0}},null,8,["modelValue"])],void 0,!0),_:1})],void 0,!0),_:1}),A(q,{md:12,xs:24},{default:P(()=>[A(M,null,{label:P(()=>[D(E(e.$t("Exclude Products"))+" ",1),A(Y,{class:"item",effect:"dark",content:e.$t("Excluded_Products_For_Discount"),placement:"top-start"},{default:P(()=>[A(W,null,{default:P(()=>[A(L)],void 0,!0),_:1})],void 0,!0),_:1},8,["content"])]),default:P(()=>[A(O,{modelValue:n.settings.exclude_product_ids,"onUpdate:modelValue":i[13]||(i[13]=e=>n.settings.exclude_product_ids=e),field:{option_key:"woo_products",is_multiple:!0}},null,8,["modelValue"])],void 0,!0),_:1})],void 0,!0),_:1})],void 0,!0),_:1}),A(J,{gutter:30,class:"fcrm_mb_16"},{default:P(()=>[A(q,{md:12,xs:24},{default:P(()=>[A(M,null,{label:P(()=>[D(E(e.$t("Product categories"))+" ",1),A(Y,{class:"item",effect:"dark",content:e.$t("Eligible_Product_Categories_For_Discount"),placement:"top-start"},{default:P(()=>[A(W,null,{default:P(()=>[A(L)],void 0,!0),_:1})],void 0,!0),_:1},8,["content"])]),default:P(()=>[A(O,{modelValue:n.settings.product_categories,"onUpdate:modelValue":i[14]||(i[14]=e=>n.settings.product_categories=e),field:{option_key:"woo_categories",is_multiple:!0}},null,8,["modelValue"])],void 0,!0),_:1})],void 0,!0),_:1}),A(q,{md:12,xs:24},{default:P(()=>[A(M,null,{label:P(()=>[D(E(e.$t("Exclude Product categories"))+" ",1),A(Y,{class:"item",effect:"dark",content:e.$t("Excluded_Product_Categories_For_Discount"),placement:"top-start"},{default:P(()=>[A(W,null,{default:P(()=>[A(L)],void 0,!0),_:1})],void 0,!0),_:1},8,["content"])]),default:P(()=>[A(O,{modelValue:n.settings.exclude_product_categories,"onUpdate:modelValue":i[15]||(i[15]=e=>n.settings.exclude_product_categories=e),field:{option_key:"woo_categories",is_multiple:!0}},null,8,["modelValue"])],void 0,!0),_:1})],void 0,!0),_:1})],void 0,!0),_:1}),A(M,null,{default:P(()=>[A(X,{style:{"margin-bottom":"10px"},modelValue:n.settings.individual_use,"onUpdate:modelValue":i[16]||(i[16]=e=>n.settings.individual_use=e),"true-value":"yes","false-value":"no"},{default:P(()=>[D(E(e.$t("Individual_Coupon_Use_info")),1)],void 0,!0),_:1},8,["modelValue"]),A(X,{modelValue:n.settings.exclude_sale_items,"onUpdate:modelValue":i[17]||(i[17]=e=>n.settings.exclude_sale_items=e),"true-value":"yes","false-value":"no"},{default:P(()=>[D(E(e.$t("Exclude_Sale_Items")),1)],void 0,!0),_:1},8,["modelValue"])],void 0,!0),_:1}),T("h4",We,E(e.$t("Limits")),1),A(J,{gutter:30,class:"fcrm_mb_16"},{default:P(()=>[A(q,{md:12,xs:24},{default:P(()=>[A(M,null,{label:P(()=>[D(E(e.$t("Usage limit per coupon"))+" ",1),A(Y,{class:"item",effect:"dark",content:e.$t("How many times this coupon can be used before it is void."),placement:"top-start"},{default:P(()=>[A(W,null,{default:P(()=>[A(L)],void 0,!0),_:1})],void 0,!0),_:1},8,["content"])]),default:P(()=>[A(N,{type:"number",modelValue:n.settings.usage_limit,"onUpdate:modelValue":i[18]||(i[18]=e=>n.settings.usage_limit=e),placeholder:e.$t("Unlimited Usage")},null,8,["modelValue","placeholder"])],void 0,!0),_:1})],void 0,!0),_:1}),A(q,{md:12,xs:24},{default:P(()=>[A(M,null,{label:P(()=>[D(E(e.$t("Limit usage to X items"))+" ",1),A(Y,{class:"item",effect:"dark",content:e.$t("Coupon_Max_items_For_Discount"),placement:"top-start"},{default:P(()=>[A(W,null,{default:P(()=>[A(L)],void 0,!0),_:1})],void 0,!0),_:1},8,["content"])]),default:P(()=>[A(N,{type:"number",modelValue:n.settings.limit_usage_to_x_items,"onUpdate:modelValue":i[19]||(i[19]=e=>n.settings.limit_usage_to_x_items=e),placeholder:e.$t("Apply to all qualifying items in cart")},null,8,["modelValue","placeholder"])],void 0,!0),_:1})],void 0,!0),_:1})],void 0,!0),_:1}),A(J,{gutter:30},{default:P(()=>[A(q,{md:12,xs:24},{default:P(()=>[A(M,null,{label:P(()=>[D(E(e.$t("Usage limit per user"))+" ",1),A(Y,{class:"item",effect:"dark",content:e.$t("Coupon_Usage_Limit_Per_User"),placement:"top-start"},{default:P(()=>[A(W,null,{default:P(()=>[A(L)],void 0,!0),_:1})],void 0,!0),_:1},8,["content"])]),default:P(()=>[A(N,{type:"number",modelValue:n.settings.usage_limit_per_user,"onUpdate:modelValue":i[20]||(i[20]=e=>n.settings.usage_limit_per_user=e),placeholder:e.$t("Unlimited Usage")},null,8,["modelValue","placeholder"])],void 0,!0),_:1})],void 0,!0),_:1}),A(q,{md:12,xs:24})],void 0,!0),_:1})],64)):(C(),w("div",Ye,[T("p",null,E(e.$t("Coupon_Inherited_Restrictions_And_Limits")),1)]))],void 0,!0),_:1},8,["label"])],void 0),_:1})):z("",!0)])}]]),Ge=O(()=>N(()=>import("./v3app/src/Modules/Contacts/RichFilters/_RichFilterContainer.js?ver=3.1.8"),[],import.meta.url)),He={name:"FormField",props:{modelValue:{type:[String,Number,Array,Object,Boolean,Date],default:void 0},value:{type:[String,Number,Array,Object,Boolean,Date],default:void 0},field:{type:Object,required:!0},options:{type:Object,default:()=>({})}},emits:["update:modelValue","input","save_reload","save_inline"],components:{MultiTextOptions:de,EmailComposer:X,FormGroupMapper:ne,FormManyDropDownMapper:B,WpUrlSelector:se,OptionSelector:K,ConditionGroups:ge,InputValueProperties:Ae,WpBaseEditor:J,TextValueMultiProperties:je,InputTextPopper:q,AjaxSelector:Q,RichFilterContainer:Ge,MailerConfig:le,TaxonomyTermsSelector:Z,AdvancedCouponSettings:Ne,InfoFilled:_},data(){return{model:void 0!==this.modelValue?this.modelValue:this.value,context_codes:!1,editorCodes:[]}},computed:{numericModel:{get(){if("input-number"!==this.field.type)return this.model;if(""===this.model||void 0===this.model)return 0;if("number"==typeof this.model)return this.model;const e=Number(this.model);return isNaN(e)?null:e},set(e){this.model=null==e?"":e}}},watch:{model:{deep:!0,handler(e){this.$emit("update:modelValue",e),this.$emit("input",e)}},modelValue(e){void 0!==e&&e!==this.model&&(this.model=e)},value(e){void 0===this.modelValue&&e!==this.model&&(this.model=e)}},methods:{saveAndReload(){this.$nextTick(()=>{this.$emit("save_reload")})},saveInline(){this.$emit("save_inline")}},created(){this.field.smart_codes&&(this.editorCodes=window.fcAdmin.globalSmartCodes,this.field.context_codes&&window.fcrm_funnel_context_codes&&(this.editorCodes=[...this.editorCodes,...window.fcrm_funnel_context_codes]),window.fcAdmin.extendedSmartCodes&&this.editorCodes.push(...window.fcAdmin.extendedSmartCodes)),"email_campaign_composer"===this.field.type&&(this.context_codes=window.fcrm_funnel_context_codes)}},qe={key:0,class:"fcrm_sms_char_counter"},Je=["innerHTML"],Be={key:30},Xe=["innerHTML"];const Ke=G(He,[["render",function(e,l,i,r,m,_){const f=U("InfoFilled"),y=t,V=v,$=U("option-selector"),T=o,j=d,O=s,R=u,L=b,W=a,Y=U("input-text-popper"),N=h,G=n,H=U("multi-text-options"),q=U("email-composer"),J=U("form-group-mapper"),B=U("form-many-drop-down-mapper"),X=U("wp-url-selector"),K=p,Q=U("condition-groups"),Z=U("input-value-properties"),ee=U("text-value-multi-properties"),le=U("wp-base-editor"),te=U("ajax-selector"),ae=U("rich-filter-container"),oe=U("mailer-config"),de=g,ie=k,ne=x,se=U("taxonomy-terms-selector"),ue=U("AdvancedCouponSettings"),pe=c;return C(),F(pe,null,I({default:P(()=>{var e,t,a;return["option_selectors"==i.field.type?(C(),F($,{key:0,modelValue:m.model,"onUpdate:modelValue":l[0]||(l[0]=e=>m.model=e),field:i.field},null,8,["modelValue","field"])):"multi-select"==i.field.type||"select"==i.field.type?(C(),F(j,{key:1,modelValue:m.model,"onUpdate:modelValue":l[1]||(l[1]=e=>m.model=e),multiple:"multi-select"==i.field.type,placeholder:i.field.placeholder,clearable:"",filterable:""},{default:P(()=>[(C(!0),w(S,null,M(i.field.options,e=>(C(),F(T,{key:e.id,value:e.id,label:e.title},null,8,["value","label"]))),128))],void 0,!0),_:1},8,["modelValue","multiple","placeholder"])):"radio"==i.field.type?(C(),F(R,{key:2,modelValue:m.model,"onUpdate:modelValue":l[2]||(l[2]=e=>m.model=e)},{default:P(()=>[(C(!0),w(S,null,M(i.field.options,e=>(C(),F(O,{key:e.id,value:e.id},{default:P(()=>[D(E(e.title),1)],void 0,!0),_:2},1032,["value"]))),128))],void 0,!0),_:1},8,["modelValue"])):"input-number"==i.field.type?(C(),F(L,{key:3,modelValue:_.numericModel,"onUpdate:modelValue":l[3]||(l[3]=e=>_.numericModel=e)},null,8,["modelValue"])):"input-text"==i.field.type?(C(),F(W,{key:4,readonly:i.field.readonly,placeholder:i.field.placeholder,modelValue:m.model,"onUpdate:modelValue":l[4]||(l[4]=e=>m.model=e)},null,8,["readonly","placeholder","modelValue"])):"input-text-area"==i.field.type?(C(),w(S,{key:5},[A(W,{type:"textarea",rows:i.field.rows,placeholder:i.field.placeholder,modelValue:m.model,"onUpdate:modelValue":l[5]||(l[5]=e=>m.model=e)},null,8,["rows","placeholder","modelValue"]),i.field.show_sms_counter?(C(),w("p",qe,E((m.model||"").length)+" chars • "+E(Math.ceil((m.model||"").length/160)||0)+" msg(s) ",1)):z("",!0)],64)):"input-text-popper"==i.field.type?(C(),F(Y,{key:6,field:i.field,placeholder:i.field.placeholder,modelValue:m.model,"onUpdate:modelValue":l[6]||(l[6]=e=>m.model=e)},null,8,["field","placeholder","modelValue"])):"yes_no_check"==i.field.type?(C(),F(N,{key:7,"true-value":"yes","false-value":"no",modelValue:m.model,"onUpdate:modelValue":l[7]||(l[7]=e=>m.model=e)},{default:P(()=>[D(E(i.field.check_label),1)],void 0,!0),_:1},8,["modelValue"])):"grouped-select"==i.field.type?(C(),F(j,{key:8,modelValue:m.model,"onUpdate:modelValue":l[8]||(l[8]=e=>m.model=e),multiple:i.field.is_multiple,placeholder:i.field.placeholder,clearable:"",filterable:"","collapse-tags":i.field.is_multiple,"collapse-tags-tooltip":i.field.is_multiple,"max-collapse-tags":i.field.is_multiple?2:void 0},{default:P(()=>[(C(!0),w(S,null,M(i.field.options,e=>(C(),F(G,{key:e.slug,label:e.title},{default:P(()=>[(C(!0),w(S,null,M(e.options,e=>(C(),F(T,{key:e.id,value:e.id,label:e.title},null,8,["value","label"]))),128))],void 0,!0),_:2},1032,["label"]))),128))],void 0,!0),_:1},8,["modelValue","multiple","placeholder","collapse-tags","collapse-tags-tooltip","max-collapse-tags"])):"multi_text_options"==i.field.type?(C(),F(H,{key:9,field:i.field,modelValue:m.model,"onUpdate:modelValue":l[9]||(l[9]=e=>m.model=e)},null,8,["field","modelValue"])):"email_campaign_composer"==i.field.type?(C(),F(q,{key:10,onSave:l[10]||(l[10]=e=>_.saveInline()),extra_tags:m.context_codes,show_audit:!0,show_merge:!0,enable_test:!0,disable_fixed:!0,disable_gutenberg_autosave:!1,hide_gutenberg_save_button:!0,class:"fc_into_modal",campaign:m.model,label_align:"top"},null,8,["extra_tags","campaign"])):"reload_field_selection"==i.field.type?(C(),F(j,{key:11,onChange:l[11]||(l[11]=e=>_.saveAndReload()),modelValue:m.model,"onUpdate:modelValue":l[12]||(l[12]=e=>m.model=e),multiple:"multi-select"==i.field.type,placeholder:i.field.placeholder,clearable:"",filterable:""},{default:P(()=>[(C(!0),w(S,null,M(i.field.options,e=>(C(),F(T,{key:e.id,value:e.id,label:e.title},null,8,["value","label"]))),128))],void 0,!0),_:1},8,["modelValue","multiple","placeholder"])):"form-group-mapper"==i.field.type?(C(),F(J,{key:12,field:i.field,model:m.model},null,8,["field","model"])):"form-many-drop-down-mapper"==i.field.type?(C(),F(B,{key:13,field:i.field,modelValue:m.model,"onUpdate:modelValue":l[13]||(l[13]=e=>m.model=e)},null,8,["field","modelValue"])):"html"==i.field.type?(C(),w("div",{key:14,class:"fc_html_content",innerHTML:i.field.info},null,8,Je)):"url_selector"==i.field.type?(C(),F(X,{key:15,modelValue:m.model,"onUpdate:modelValue":l[14]||(l[14]=e=>m.model=e),field:i.field},null,8,["modelValue","field"])):"date_time"==i.field.type?(C(),F(K,{key:16,"value-format":"YYYY-MM-DD HH:mm:ss",modelValue:m.model,"onUpdate:modelValue":l[15]||(l[15]=e=>m.model=e),placeholder:i.field.placeholder,type:"datetime"},null,8,["modelValue","placeholder"])):"condition_groups"==i.field.type?(C(),F(Q,{key:17,modelValue:m.model,"onUpdate:modelValue":l[16]||(l[16]=e=>m.model=e),field:i.field},null,8,["modelValue","field"])):"input_value_pair_properties"==i.field.type?(C(),F(Z,{key:18,modelValue:m.model,"onUpdate:modelValue":l[17]||(l[17]=e=>m.model=e),field:i.field},null,8,["modelValue","field"])):"text-value-multi-properties"==i.field.type?(C(),F(ee,{key:19,modelValue:m.model,"onUpdate:modelValue":l[18]||(l[18]=e=>m.model=e),field:i.field},null,8,["modelValue","field"])):"html_editor"==i.field.type?(C(),F(le,{key:20,editorShortcodes:m.editorCodes,modelValue:m.model,"onUpdate:modelValue":l[19]||(l[19]=e=>m.model=e)},null,8,["editorShortcodes","modelValue"])):"rest_selector"==i.field.type?(C(),F(te,{key:21,modelValue:m.model,"onUpdate:modelValue":l[20]||(l[20]=e=>m.model=e),field:i.field},null,8,["modelValue","field"])):"reload_rest_selector"==i.field.type?(C(),F(te,{key:22,onChange:l[21]||(l[21]=e=>_.saveAndReload()),modelValue:m.model,"onUpdate:modelValue":l[22]||(l[22]=e=>m.model=e),field:i.field},null,8,["modelValue","field"])):"condition_block_groups"==i.field.type?(C(),F(ae,{key:23,add_label:i.field.add_label,advanced_filters:m.model,filterOptions:i.field.groups},null,8,["add_label","advanced_filters","filterOptions"])):"custom_sender_config"==i.field.type?(C(),F(oe,{key:24,mailer_settings:m.model},null,8,["mailer_settings"])):"radio_buttons"==i.field.type?(C(),F(R,{key:25,modelValue:m.model,"onUpdate:modelValue":l[23]||(l[23]=e=>m.model=e)},{default:P(()=>[(C(!0),w(S,null,M(i.field.options,e=>(C(),F(de,{key:e.id,value:e.id},{default:P(()=>[D(E(e.title),1)],void 0,!0),_:2},1032,["value"]))),128))],void 0,!0),_:1},8,["modelValue"])):"checkboxes"==i.field.type?(C(),F(ie,{key:26,modelValue:m.model,"onUpdate:modelValue":l[24]||(l[24]=e=>m.model=e)},{default:P(()=>[(C(!0),w(S,null,M(i.field.options,e=>(C(),F(N,{key:e.id,value:e.id},{default:P(()=>[D(E(e.title),1)],void 0,!0),_:2},1032,["value"]))),128))],void 0,!0),_:1},8,["modelValue"])):"time_selector"==i.field.type?(C(),F(ne,{key:27,modelValue:m.model,"onUpdate:modelValue":l[25]||(l[25]=e=>m.model=e),start:null==(e=i.field.picker_options)?void 0:e.start,end:null==(t=i.field.picker_options)?void 0:t.end,step:null==(a=i.field.picker_options)?void 0:a.step,placeholder:i.field.placeholder},null,8,["modelValue","start","end","step","placeholder"])):"tax_selector"==i.field.type?(C(),F(se,{key:28,modelValue:m.model,"onUpdate:modelValue":l[26]||(l[26]=e=>m.model=e),field:{is_multiple:i.field.is_multiple,size:"small",taxonomy:i.field.taxonomy}},null,8,["modelValue","field"])):"advanced_coupon_settings"==i.field.type?(C(),F(ue,{key:29,onSaveAndReload:_.saveAndReload,settings:m.model,field:i.field},null,8,["onSaveAndReload","settings","field"])):(C(),w("pre",Be,E(i.field),1)),i.field.inline_help?(C(),w("p",{key:31,innerHTML:i.field.inline_help},null,8,Xe)):z("",!0)]},void 0),_:2},[i.field.label?{name:"label",fn:P(()=>[D(E(i.field.label)+" ",1),i.field.help?(C(),F(V,{key:0,class:"item",effect:"dark",content:i.field.help,placement:"top-start"},{default:P(()=>[A(y,null,{default:P(()=>[A(f)],void 0,!0),_:1})],void 0,!0),_:1},8,["content"])):z("",!0)]),key:"0"}:void 0]),1024)}]]),Qe={class:"fluentcrm_funnel_header"},Ze={class:"fc_funnel_head"},el={class:"fc_funel_head_title"},ll=["innerHTML"],tl={class:"fc_funnel_head_action"},al={class:"icon"},ol={key:0,class:"fc_funnel_editor fcrm_funnel_editor_after_header_slot fcrm_funnel_editor_boxed_with_border"},dl={key:1,class:"fcrm_funnel_editor_boxed_with_border"},il={key:2},nl={key:3,class:"fcrm_funnel_editor_footer"},sl={class:"fluentcrm_pull_left"},ul={class:"fluentcrm_pull_right"},pl={class:"icon"};const rl=G({name:"FieldEditor",components:{Icons:H,FormField:Ke,MergeCodes:ae},props:["data","settings","options","show_controls","title_badge","action_name","is_editable","block_type"],emits:["closeDrawer","deleteSequence","movePosition","save","save_reload"],data(){return{is_settings_missing:!1,is_internal_loading:!1,funnel_id:this.$route.params.funnel_id,context_codes:window.fcrm_funnel_context_codes||[],sendingTestWebhook:!1}},methods:{saveFunnelSequences(){"send_custom_email"===this.action_name?this.saveEmailAction():(this.is_internal_loading=!0,this.$emit("save",1))},saveEmailAction(){if(!this.data.campaign.email_subject)return this.$notify.error("Please provide email subject"),!1;this.is_internal_loading=!0,this.$post("funnels/funnel/save-email-action-fallback",{action_data:JSON.stringify(this.data),funnel_id:this.funnel_id}).then(e=>{this.data.campaign=e.campaign,this.data.reference_campaign=e.reference_campaign,this.$emit("save",1)}).catch(e=>{this.handleError(e)}).finally(()=>{this.is_internal_loading=!1})},saveEmailActionInline(){this.$post("funnels/funnel/save-email-action-fallback",{action_data:JSON.stringify(this.data),funnel_id:this.funnel_id})},deleteFunnelSequences(){this.$emit("deleteSequence",1)},movePosition(e){this.$emit("movePosition",e)},compare(e,l,t){switch(l){case"=":return e===t;case"!=":return e!==t}},dependancyPass(e){if(e.dependency){const l=e.dependency.depends_on.split("/").reduce((e,l)=>e[l],this.data);return!!this.compare(e.dependency.value,e.dependency.operator,l)}return!0},saveAndReload(){this.$emit("save_reload")},doNothing(){},closeDrawer(){this.$emit("closeDrawer")},sendTestWebhook(){if(!this.data.remote_url)return this.$notify.error(this.$t("Please provide Remote URL")),!1;this.sendingTestWebhook=!0,this.$post("funnels/send-test-webhook",{data:this.data}).then(e=>{this.$notify.success(e.message)}).catch(e=>{this.handleError(e)}).finally(()=>{this.sendingTestWebhook=!1})}},mounted(){this.is_internal_loading=!1,this.settings?this.is_settings_missing=!1:(this.is_settings_missing=!0,this.settings={})}},[["render",function(e,t,a,o,d,i){const n=U("merge-codes"),s=U("Icons"),u=l,p=U("form-field"),r=$,m=f;return C(),F(r,{onSubmit:Y(i.doNothing,["prevent"]),data:a.data,"label-position":"top"},{default:P(()=>[j(T("div",Qe,[T("div",Ze,[T("div",el,[T("h3",null,[D(E(a.settings.title)+" ",1),a.title_badge?(C(),w("span",{key:0,class:R(["ff_funnel_badge","ff_funnel_badge-"+a.title_badge])},E(a.title_badge),3)):z("",!0)]),T("p",{innerHTML:a.settings.sub_title},null,8,ll)]),T("div",tl,["trigger"!=a.title_badge?(C(),F(n,{key:0,class:"fc_header_merge_codes",extra_tags:d.context_codes},null,8,["extra_tags"])):z("",!0),a.is_editable?z("",!0):(C(),F(u,{key:1,style:{"font-size":"22px",color:"var(--fc-primary-text)"},onClick:i.closeDrawer,link:"",class:"close-field-editor-btn"},{default:P(()=>[T("span",al,[A(s,{"icon-name":"close"})])],void 0,!0),_:1},8,["onClick"]))])])],512),[[L,a.settings.title]]),e.$slots.after_header?(C(),w("div",ol,[W(e.$slots,"after_header")])):z("",!0),a.settings.fields?(C(),w("div",dl,[(C(!0),w(S,null,M(a.settings.fields,(e,l)=>(C(),w(S,null,[i.dependancyPass(e)?(C(),w("div",{key:l,class:R(e.wrapper_class)},[A(p,{onSave_inline:t[0]||(t[0]=e=>i.saveEmailActionInline()),onSave_reload:t[1]||(t[1]=e=>i.saveAndReload()),options:a.options,modelValue:a.data[l],"onUpdate:modelValue":e=>a.data[l]=e,field:e},null,8,["options","modelValue","onUpdate:modelValue","field"])],2)):z("",!0)],64))),256))])):z("",!0),d.is_settings_missing?(C(),w("h3",il,E(e.$t("block_does_not_exist")),1)):z("",!0),a.show_controls?(C(),w("div",nl,[T("div",sl,[A(u,{loading:d.is_internal_loading,disabled:d.is_internal_loading,onClick:t[2]||(t[2]=e=>i.saveFunnelSequences(!1)),type:"primary",size:"small"},{default:P(()=>[D(E(e.$t("Save Settings")),1)],void 0,!0),_:1},8,["loading","disabled"]),"http_send_data"===a.action_name?j((C(),F(u,{key:0,disabled:d.sendingTestWebhook,onClick:i.sendTestWebhook,size:"small"},{default:P(()=>[D(E(e.$t("Send Test Webhook")),1)],void 0,!0),_:1},8,["disabled","onClick"])),[[m,d.sendingTestWebhook]]):z("",!0)]),T("div",ul,[A(u,{onClick:t[3]||(t[3]=e=>i.deleteFunnelSequences(!1)),size:"small",type:"danger",plain:"",class:"only-icon-btn small"},{default:P(()=>[T("span",pl,[A(s,{"icon-name":"delete"})])],void 0,!0),_:1})])])):z("",!0)],void 0),_:3},8,["onSubmit","data"])}]]);export{rl as F,Ke as a}; diff --git a/wp-content/plugins/fluent-crm/assets/Filterer.js b/wp-content/plugins/fluent-crm/assets/Filterer.js new file mode 100644 index 0000000..49e0123 --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/Filterer.js @@ -0,0 +1 @@ +import{at as e,h as s,k as o,E as r,j as t}from"./vendor-element-plus.js?ver=3.1.8";import{aQ as a,W as l,X as n,ab as d,a5 as f,_ as i,a9 as c,aa as m}from"./vendor.js?ver=3.1.8";import{_ as p}from"./fc-bits-ui.js?ver=3.1.8";const u={class:"fcrm_filterer"};const _=p({name:"Filterer",components:{ArrowDown:e},props:{placement:{default:"bottom-start"},filter_type:{default:"filterer"}},methods:{hide(){var e;null==(e=this.$refs.dropdownRef)||e.handleClose()}}},[["render",function(e,p,_,v,w,$){const h=a("ArrowDown"),b=r,j=o,k=s,g=t;return l(),n("div",u,[d(g,{ref:"dropdownRef",class:"fcrm_fluentcrm-filter",placement:_.placement,"hide-on-click":!1,trigger:"click"},{default:f(()=>[i(e.$slots,"header",{},()=>[d(j,{plain:"",size:"small"},{default:f(()=>[i(e.$slots,"label",{},()=>[c(m(e.$t("Columns")),1)]),i(e.$slots,"icon",{},()=>[d(b,null,{default:f(()=>[d(h)],void 0,!0),_:1})])],void 0,!0),_:3})])]),dropdown:f(()=>[d(k,{class:"fcrm_filter_dropdown"},{default:f(()=>[i(e.$slots,"items"),i(e.$slots,"footer")],void 0,!0),_:3})]),_:3},8,["placement"]),i(e.$slots,"dropdown_footer")])}]]);export{_ as F}; diff --git a/wp-content/plugins/fluent-crm/assets/Filterer2.js b/wp-content/plugins/fluent-crm/assets/Filterer2.js new file mode 100644 index 0000000..49e0123 --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/Filterer2.js @@ -0,0 +1 @@ +import{at as e,h as s,k as o,E as r,j as t}from"./vendor-element-plus.js?ver=3.1.8";import{aQ as a,W as l,X as n,ab as d,a5 as f,_ as i,a9 as c,aa as m}from"./vendor.js?ver=3.1.8";import{_ as p}from"./fc-bits-ui.js?ver=3.1.8";const u={class:"fcrm_filterer"};const _=p({name:"Filterer",components:{ArrowDown:e},props:{placement:{default:"bottom-start"},filter_type:{default:"filterer"}},methods:{hide(){var e;null==(e=this.$refs.dropdownRef)||e.handleClose()}}},[["render",function(e,p,_,v,w,$){const h=a("ArrowDown"),b=r,j=o,k=s,g=t;return l(),n("div",u,[d(g,{ref:"dropdownRef",class:"fcrm_fluentcrm-filter",placement:_.placement,"hide-on-click":!1,trigger:"click"},{default:f(()=>[i(e.$slots,"header",{},()=>[d(j,{plain:"",size:"small"},{default:f(()=>[i(e.$slots,"label",{},()=>[c(m(e.$t("Columns")),1)]),i(e.$slots,"icon",{},()=>[d(b,null,{default:f(()=>[d(h)],void 0,!0),_:1})])],void 0,!0),_:3})])]),dropdown:f(()=>[d(k,{class:"fcrm_filter_dropdown"},{default:f(()=>[i(e.$slots,"items"),i(e.$slots,"footer")],void 0,!0),_:3})]),_:3},8,["placement"]),i(e.$slots,"dropdown_footer")])}]]);export{_ as F}; diff --git a/wp-content/plugins/fluent-crm/assets/FloatingBulkActionShell.js b/wp-content/plugins/fluent-crm/assets/FloatingBulkActionShell.js new file mode 100644 index 0000000..bd3b281 --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/FloatingBulkActionShell.js @@ -0,0 +1 @@ +import{c as e,E as l,k as t}from"./vendor-element-plus.js?ver=3.1.8";import{aQ as s,W as a,X as o,_ as c,Z as n,ab as i,a5 as d,aa as r,a8 as u,Y as f,a9 as m,a0 as _}from"./vendor.js?ver=3.1.8";import{_ as y}from"./fc-bits-ui.js?ver=3.1.8";const b={name:"FloatingBulkActionShell",components:{Close:e},emits:["select-all","select-only-page","deselect"],props:{visible:{type:Boolean,default:!1},customLayout:{type:Boolean,default:!1},themeMode:{type:String,default:"light"},selectedCount:{type:Number,default:0},selectedLabel:{type:String,default:"selected"},showSelectAll:{type:Boolean,default:!1},showSelectOnlyPage:{type:Boolean,default:!1},selectAllLabel:{type:String,default:""},selectOnlyPageLabel:{type:String,default:""},deselectLabel:{type:String,default:"Deselect"}},computed:{containerClass(){return{"fcrm-dark":"light"===this.themeMode,"fcrm-force-light":"dark"===this.themeMode}}}},p={key:1,class:"fcrm_bulk_action_bar"},k={class:"fcrm_bulk_action_left"},v={class:"fcrm_selection_count"},g={class:"fcrm_selection_count_number"},h={class:"fcrm_selection_count_text"},S={key:0,class:"fcrm_bulk_divider"},L={key:3,class:"fcrm_bulk_divider"};const C=y(b,[["render",function(e,y,b,C,$,w){const A=s("Close"),B=l,O=t;return b.visible?(a(),o("div",{key:0,class:_(["fcrm_fixed_bulk_actions_to_bottom",w.containerClass])},[b.customLayout?c(e.$slots,"default",{key:0}):(a(),o("div",p,[n("div",k,[i(O,{link:"","aria-label":b.deselectLabel,onClick:y[0]||(y[0]=l=>e.$emit("deselect"))},{default:d(()=>[i(B,null,{default:d(()=>[i(A)],void 0,!0),_:1})],void 0),_:1},8,["aria-label"]),n("div",v,[c(e.$slots,"count",{},()=>[n("span",g,r(b.selectedCount),1),n("span",h,r(b.selectedLabel),1)])]),b.showSelectAll||b.showSelectOnlyPage?(a(),o("div",S)):u("",!0),b.showSelectAll?(a(),f(O,{key:1,link:"",onClick:y[1]||(y[1]=l=>e.$emit("select-all"))},{default:d(()=>[m(r(b.selectAllLabel),1)],void 0),_:1})):u("",!0),b.showSelectOnlyPage?(a(),f(O,{key:2,link:"",onClick:y[2]||(y[2]=l=>e.$emit("select-only-page"))},{default:d(()=>[m(r(b.selectOnlyPageLabel),1)],void 0),_:1})):u("",!0),e.$slots.actions?(a(),o("div",L)):u("",!0),c(e.$slots,"actions")])]))],2)):u("",!0)}]]);export{C as F}; diff --git a/wp-content/plugins/fluent-crm/assets/GenericPromo.js b/wp-content/plugins/fluent-crm/assets/GenericPromo.js new file mode 100644 index 0000000..f7d2086 --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/GenericPromo.js @@ -0,0 +1 @@ +import{P as r}from"./PromoCard.js?ver=3.1.8";import{aQ as o,W as e,X as a,ab as s}from"./vendor.js?ver=3.1.8";import{_ as i}from"./fc-bits-ui.js?ver=3.1.8";const n={class:"fc_promo_body"};const t=i({name:"GenericPromo",components:{PromoCard:r},props:{heading:{type:String,required:!1,default:""}}},[["render",function(r,i,t,d,p,c){const m=o("PromoCard");return e(),a("div",n,[s(m,{heading:t.heading||r.$t("This is a pro feature"),description:r.$t("Lin_This_iapfPdtFPta"),"show-header-upgrade-icon":!1},null,8,["heading","description"])])}],["__scopeId","data-v-832a775e"]]);export{t as G}; diff --git a/wp-content/plugins/fluent-crm/assets/GenericPromo2.js b/wp-content/plugins/fluent-crm/assets/GenericPromo2.js new file mode 100644 index 0000000..3705296 --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/GenericPromo2.js @@ -0,0 +1 @@ +import{P as r}from"./PromoCard.js?ver=3.1.8";import{aQ as o,W as e,X as a,ab as i}from"./vendor.js?ver=3.1.8";import{_ as s}from"./fc-bits-ui.js?ver=3.1.8";const n={class:"fcrm_promo_body"};const t=s({name:"GenericPromo",components:{PromoCard:r},props:{heading:{type:String,required:!1,default:""}}},[["render",function(r,s,t,d,m,p){const c=o("PromoCard");return e(),a("div",n,[i(c,{heading:t.heading||r.$t("This is a pro feature"),description:r.$t("Lin_This_iapfPdtFPta")},null,8,["heading","description"])])}]]);export{t as G}; diff --git a/wp-content/plugins/fluent-crm/assets/InlineDoc.js b/wp-content/plugins/fluent-crm/assets/InlineDoc.js new file mode 100644 index 0000000..b3443d6 --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/InlineDoc.js @@ -0,0 +1 @@ +import{k as o,aB as i,aT as d}from"./vendor-element-plus.js?ver=3.1.8";import{aQ as s,W as e,X as a,ab as t,a5 as c,Z as n,a9 as l,aa as r,a0 as h}from"./vendor.js?ver=3.1.8";import{_,I as m}from"./fc-bits-ui.js?ver=3.1.8";const w={class:"fc_inline_doc",style:{display:"inline-block"}},p={class:"icon"},u=["innerHTML"],f={key:1,class:"doc_read"};const v=_({name:"InlineDoc",components:{Icons:m},props:["doc_id","btn_class"],data:()=>({loading:!1,doc:{},show_doc:!1,direction:"rtl",docSize:"700px",isLoaded:!1}),methods:{showDoc(){this.show_doc=!0,this.isLoaded||this.getDoc()},getDoc(){this.loading=!0,this.$get("docs/"+this.doc_id).then(o=>{this.doc=o}).catch(o=>{this.handleError(o)}).finally(()=>{this.loading=!1,this.isLoaded=!0})}},mounted(){window.fcAdmin&&window.fcAdmin.is_rtl&&(this.direction="ltr"),window.outerWidth<701&&(this.docSize=window.outerWidth-50+"px")}},[["render",function(_,m,v,b,g,y){const k=s("Icons"),z=o,D=i,I=d;return e(),a("div",w,[t(z,{title:_.$t("View related documentation"),class:h(v.btn_class),onClick:y.showDoc},{default:c(()=>[n("span",p,[t(k,{"icon-name":"graduationCap"})]),l(" "+r(_.$t("Tutorial")),1)],void 0),_:1},8,["title","class","onClick"]),t(I,{modelValue:g.show_doc,"onUpdate:modelValue":m[0]||(m[0]=o=>g.show_doc=o),title:g.doc.title,"append-to-body":!0,size:g.docSize,direction:g.direction,"header-class":"fc_doc_drawer_header","body-class":"fc_doc_drawer_body"},{default:c(()=>[g.loading?(e(),a("div",f,[t(D,{animated:!0,rows:10})])):(e(),a("div",{key:0,class:"doc_read",innerHTML:g.doc.content},null,8,u))],void 0),_:1},8,["modelValue","title","size","direction"])])}]]);export{v as I}; diff --git a/wp-content/plugins/fluent-crm/assets/ItemCopier.js b/wp-content/plugins/fluent-crm/assets/ItemCopier.js new file mode 100644 index 0000000..f83de4e --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/ItemCopier.js @@ -0,0 +1 @@ +import{aj as t,aJ as e,k as o,E as a,e as i}from"./vendor-element-plus.js?ver=3.1.8";import{aQ as s,W as n,Y as c,a5 as p,ab as l,a8 as r}from"./vendor.js?ver=3.1.8";import{_ as d,I as u}from"./fc-bits-ui.js?ver=3.1.8";const m=d({name:"ItemCopier",components:{Icons:u,Check:t},props:{text:{type:String,required:!0},showViewButton:{type:Boolean,default:!1}},data:()=>({copy_success:!1}),computed:{copyTooltipText(){return this.copy_success?this.$t("Copied"):this.$t("Copy")},viewTooltipText(){return this.$t("View in new tab")}},methods:{copyItem(){this.copy_success=!1;const t=this.text;let e=!1;if(window.clipboardData&&window.clipboardData.setData)window.clipboardData.clipboardData.setData("Text",t),e=!0;else if(document.queryCommandSupported&&document.queryCommandSupported("copy")){const a=document.createElement("textarea");a.textContent=t,a.style.position="fixed",document.body.appendChild(a),a.select();try{document.execCommand("copy"),e=!0}catch(o){console.warn("Copy to clipboard failed.",o),e=!1}finally{document.body.removeChild(a)}}e?(this.copy_success=!0,this.$notify({message:this.$t("Copied to your clipboard"),position:"bottom-right",customClass:"bottom_right",type:"success"}),setTimeout(()=>{this.copy_success=!1},2e3)):this.$notify({message:this.$t("Your Browser does not support JS copy. Please copy manually"),position:"bottom-right",customClass:"bottom_right",type:"error"})},openUrl(){if(this.text){const t=window.open(this.text,"_blank");t&&(t.opener=null)}}}},[["render",function(t,d,u,m,y,h){const b=s("Check"),f=a,v=s("Icons"),w=o,C=e,x=i;return n(),c(x,{size:"small",readonly:!0,"model-value":u.text,"aria-label":t.$t("Copyable item value"),class:"fc-item-copier-input"},{append:p(()=>[l(C,{content:h.copyTooltipText,placement:"top"},{default:p(()=>[l(w,{class:"copy-btn","native-type":"button","aria-label":h.copyTooltipText,onClick:h.copyItem},{default:p(()=>[y.copy_success?(n(),c(f,{key:0,"aria-hidden":"true"},{default:p(()=>[l(b)],void 0,!0),_:1})):(n(),c(v,{key:1,"icon-name":"duplicate","aria-hidden":"true"}))],void 0,!0),_:1},8,["aria-label","onClick"])],void 0,!0),_:1},8,["content"]),u.showViewButton?(n(),c(C,{key:0,content:h.viewTooltipText,placement:"top"},{default:p(()=>[l(w,{type:"primary",class:"view-btn","native-type":"button","aria-label":h.viewTooltipText,onClick:h.openUrl},{default:p(()=>[l(v,{"icon-name":"externalLink","aria-hidden":"true"})],void 0,!0),_:1},8,["aria-label","onClick"])],void 0,!0),_:1},8,["content"])):r("",!0)]),_:1},8,["model-value","aria-label"])}]]);export{m as I}; diff --git a/wp-content/plugins/fluent-crm/assets/ItemCopier2.js b/wp-content/plugins/fluent-crm/assets/ItemCopier2.js new file mode 100644 index 0000000..80be1fc --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/ItemCopier2.js @@ -0,0 +1 @@ +import{aj as t,ac as o,E as e}from"./vendor-element-plus.js?ver=3.1.8";import{aQ as s,W as c,X as a,Z as r,aa as i,Y as n,a5 as p,ab as m}from"./vendor.js?ver=3.1.8";import{_ as d}from"./fc-bits-ui.js?ver=3.1.8";const l={class:"fcrm_item_copier_wrapper"},u={class:"fcrm_smart_url_box"},y={class:"fcrm_smart_url_text"};const _=d({name:"ItemCopier",components:{CopyDocument:o,Check:t},props:["text","showViewButton"],data:()=>({copy_success:!1}),methods:{copyItem(){this.copy_success=!1;const t=this.text;let o=!1;if(window.clipboardData&&window.clipboardData.setData)window.clipboardData.clipboardData.setData("Text",t),o=!0;else if(document.queryCommandSupported&&document.queryCommandSupported("copy")){const s=document.createElement("textarea");s.textContent=t,s.style.position="fixed",document.body.appendChild(s),s.select();try{document.execCommand("copy"),o=!0}catch(e){console.warn("Copy to clipboard failed.",e),o=!1}finally{document.body.removeChild(s)}}o?(this.copy_success=!0,this.$notify({message:this.$t("Copied to your clipboard"),position:"bottom-right",customClass:"bottom_right",type:"success"}),setTimeout(()=>{this.copy_success=!1},2e3)):this.$notify({message:this.$t("Your Browser does not support JS copy. Please copy manually"),position:"bottom-right",customClass:"bottom_right",type:"error"})},openUrl(){this.text&&window.open(this.text,"_blank")}}},[["render",function(t,o,d,_,h,f){const b=s("CopyDocument"),C=e,w=s("Check");return c(),a("div",l,[r("div",u,[r("span",y,i(d.text),1),r("button",{class:"fcrm_copy_btn",onClick:o[0]||(o[0]=(...t)=>f.copyItem&&f.copyItem(...t)),type:"button"},[h.copy_success?(c(),n(C,{key:1,class:"fcrm_check_icon"},{default:p(()=>[m(w)],void 0),_:1})):(c(),n(C,{key:0,class:"fcrm_copy_icon"},{default:p(()=>[m(b)],void 0),_:1}))])])])}]]);export{_ as I}; diff --git a/wp-content/plugins/fluent-crm/assets/PageHeader.js b/wp-content/plugins/fluent-crm/assets/PageHeader.js new file mode 100644 index 0000000..dc99560 --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/PageHeader.js @@ -0,0 +1 @@ +import{W as s,X as e,Z as a,_ as r,a8 as t}from"./vendor.js?ver=3.1.8";import{_ as c}from"./fc-bits-ui.js?ver=3.1.8";const o={class:"fcrm_page_header"},i={class:"fcrm_page_header_content"},d={key:0,class:"fcrm_page_header_title"},_={key:1,class:"fcrm_page_header_description"},l={key:2,class:"fcrm_page_header_breadcrumb"},n={key:0,class:"fcrm_page_header_actions"};const m=c({name:"PageHeader"},[["render",function(c,m,p,f,v,$){return s(),e("div",o,[a("div",i,[c.$slots.title?(s(),e("div",d,[r(c.$slots,"title")])):t("",!0),c.$slots.description?(s(),e("div",_,[r(c.$slots,"description")])):t("",!0),c.$slots.breadcrumb?(s(),e("div",l,[r(c.$slots,"breadcrumb")])):t("",!0)]),c.$slots.actions?(s(),e("div",n,[r(c.$slots,"actions")])):t("",!0)])}]]);export{m as P}; diff --git a/wp-content/plugins/fluent-crm/assets/PaginationBar.js b/wp-content/plugins/fluent-crm/assets/PaginationBar.js new file mode 100644 index 0000000..3c0b967 --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/PaginationBar.js @@ -0,0 +1 @@ +import{b5 as a,aL as e,aK as t}from"./vendor-element-plus.js?ver=3.1.8";import{W as i,Y as n,aQ as r,X as s,Z as o,aa as g,ab as p,a5 as l,J as c,az as h,a0 as u,a8 as _}from"./vendor.js?ver=3.1.8";import{_ as d}from"./fc-bits-ui.js?ver=3.1.8";const m={name:"PaginationBar",components:{Pagination:d({name:"Pagination",emits:["fetch","per_page_change"],props:{pagination:{required:!0,type:Object},extra_sizes:{required:!1,type:Array,default:()=>[]},hide_on_single:{required:!1,type:Boolean,default:()=>!1},layout:{required:!1,type:String,default:()=>"total, sizes, prev, pager, next"}},computed:{page_sizes(){const a=[];this.pagination.per_page<10&&a.push(this.pagination.per_page);return[...a,10,20,50,80,100,120,150,...this.extra_sizes]}},methods:{changePage(a){this.pagination.current_page=a,this.$emit("fetch")},changeSize(a){this.pagination.per_page=a,this.$emit("per_page_change",a),this.$emit("fetch")}}},[["render",function(e,t,r,s,o,g){const p=a;return i(),n(p,{class:"fcrm_pagination",background:!1,layout:r.layout,onCurrentChange:g.changePage,onSizeChange:g.changeSize,"hide-on-single-page":r.hide_on_single,"current-page":r.pagination.current_page,"onUpdate:currentPage":t[0]||(t[0]=a=>r.pagination.current_page=a),"page-sizes":g.page_sizes,"page-size":r.pagination.per_page,total:r.pagination.total},null,8,["layout","onCurrentChange","onSizeChange","hide-on-single-page","current-page","page-sizes","page-size","total"])}]])},emits:["fetch"],props:{pagination:{type:Object,required:!0},hide_on_single:{type:Boolean,default:!1},page_sizes:{type:Array,default:()=>[]},extra_sizes:{type:Array,default:()=>[]},wrapperClass:{type:[String,Array,Object],default:""}},computed:{currentPage(){var a;return Number(null==(a=this.pagination)?void 0:a.current_page)||1},totalPages(){var a,e;const t=Number(null==(a=this.pagination)?void 0:a.per_page)||10,i=Number(null==(e=this.pagination)?void 0:e.total)||0;return Math.max(1,Math.ceil(i/t))},pageSizes(){var a;const e=[],t=parseInt(null==(a=this.pagination)?void 0:a.per_page,10)||10;t<10&&e.push(t);const i=this.page_sizes.length?this.page_sizes:[10,20,50,80,100,120,150];return[...e,...i,...this.extra_sizes]}},methods:{onPerPageChange(a){a&&(this.pagination.per_page=a,this.pagination.current_page=1,this.$emit("fetch"))},goFirst(){this.currentPage<=1||(this.pagination.current_page=1,this.$emit("fetch"))},goLast(){this.currentPage>=this.totalPages||(this.pagination.current_page=this.totalPages,this.$emit("fetch"))}}},f={class:"fcrm-pagination-bar__left"},b={class:"fcrm-pagination-bar__page-info"},v={class:"fcrm-pagination-bar__page-info"},L={class:"fcrm-pagination-bar__right"},y=["disabled","aria-label"],z=["disabled","aria-label"];const P=d(m,[["render",function(a,d,m,P,w,x){const $=e,C=t,S=r("pagination");return!m.hide_on_single||x.totalPages>1?(i(),s("div",{key:0,class:u(["fcrm-pagination-bar",m.wrapperClass])},[o("div",f,[o("div",b,g(a.$t("Page"))+" "+g(m.pagination.current_page)+" "+g(a.$t("of"))+" "+g(x.totalPages),1),p(C,{"model-value":m.pagination.per_page,size:"small",class:"fcrm-pagination-bar__sizes fcrm_background_select","onUpdate:modelValue":x.onPerPageChange},{default:l(()=>[(i(!0),s(c,null,h(x.pageSizes,e=>(i(),n($,{key:e,label:e+" / "+a.$t("page"),value:e},null,8,["label","value"]))),128))],void 0),_:1},8,["model-value","onUpdate:modelValue"]),o("div",v,g(a.$t("Total"))+" "+g(m.pagination.total),1)]),o("div",L,[o("button",{type:"button",class:"fcrm-pagination-bar__nav fcrm-pagination-bar__nav--first",disabled:x.currentPage<=1,"aria-label":a.$t("First page"),onClick:d[0]||(d[0]=(...a)=>x.goFirst&&x.goFirst(...a))},[...d[3]||(d[3]=[o("svg",{xmlns:"http://www.w3.org/2000/svg",width:"20",height:"20",viewBox:"0 0 20 20",fill:"none"},[o("path",{d:"M14.9752 10.0004L10.6647 5.68994L9.68266 6.67203L13.0111 10.0004L9.68266 13.3288L10.6647 14.3109L14.9752 10.0004ZM11.0517 10.0004L6.74118 5.68994L5.75909 6.67203L9.08752 10.0004L5.75909 13.3288L6.74118 14.3109L11.0517 10.0004Z",fill:"var(--fc-secondary-text)"})],-1)])],8,y),p(S,{class:"fcrm-pagination-bar__right-inner",layout:"prev, pager, next",pagination:m.pagination,hide_on_single:m.hide_on_single,onFetch:d[1]||(d[1]=e=>a.$emit("fetch"))},null,8,["pagination","hide_on_single"]),o("button",{type:"button",class:"fcrm-pagination-bar__nav fcrm-pagination-bar__nav--last",disabled:x.currentPage>=x.totalPages,"aria-label":a.$t("Last page"),onClick:d[2]||(d[2]=(...a)=>x.goLast&&x.goLast(...a))},[...d[4]||(d[4]=[o("svg",{xmlns:"http://www.w3.org/2000/svg",width:"20",height:"20",viewBox:"0 0 20 20",fill:"none"},[o("path",{d:"M14.9752 10.0004L10.6647 5.68994L9.68266 6.67203L13.0111 10.0004L9.68266 13.3288L10.6647 14.3109L14.9752 10.0004ZM11.0517 10.0004L6.74118 5.68994L5.75909 6.67203L9.08752 10.0004L5.75909 13.3288L6.74118 14.3109L11.0517 10.0004Z",fill:"var(--fc-secondary-text)"})],-1)])],8,z)])],2)):_("",!0)}]]);export{P}; diff --git a/wp-content/plugins/fluent-crm/assets/PhotoWidget.js b/wp-content/plugins/fluent-crm/assets/PhotoWidget.js new file mode 100644 index 0000000..e834e7e --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/PhotoWidget.js @@ -0,0 +1 @@ +import{k as e}from"./vendor-element-plus.js?ver=3.1.8";import{aQ as t,W as a,X as o,a8 as n,ab as i,a5 as l,J as d,a9 as s,aa as p,a0 as u,_ as r}from"./vendor.js?ver=3.1.8";import{_ as c,I as m}from"./fc-bits-ui.js?ver=3.1.8";const _={name:"photo_widget",components:{Icons:m},props:{modelValue:{type:String,default:""},value:{type:String,default:""},btn_mode:{type:Boolean,default:()=>!1},btn_text:{type:String,default:()=>"Upload"},btn_type:{type:String,default:()=>"default"},btn_class:{type:String,default:()=>""},hide_upload_icon:{type:Boolean,default:()=>!1},only_icon:{type:Boolean,default:()=>!1}},emits:["update:modelValue","input","changed","update:value"],data:()=>({app_ready:!1}),computed:{displayValue(){return this.modelValue||this.value||""}},methods:{initUploader(){var e;const t=null==(e=null==window?void 0:window.wp)?void 0:e.media,a=null==t?void 0:t.editor;if(!(a&&"function"==typeof a.open&&t&&t.model&&t.view))return console.warn("PhotoWidget: wp.media.editor is not available. Ensure wp_enqueue_media() is called on the page."),!1;const o=a.send.attachment;a.send.attachment=(e,t)=>{if(t&&t.url){const e=t.url;this.$emit("update:modelValue",e),this.$emit("update:value",e),this.$emit("input",e),this.$emit("changed",e)}a.send.attachment=o};const n=window.wpActiveEditor;window.wpActiveEditor="photo_widget",a.open();const i=a.frame;return i&&"function"==typeof i.on?i.on("close",()=>{window.wpActiveEditor=n}):window.wpActiveEditor=n,!1},getThumb:e=>e.url},mounted(){this.app_ready=!0}},y={class:"fluentcrm_photo_card"},f={key:0,class:"fluentcrm_photo_holder"},h=["src"],w={key:0,class:"icon"};const v=c(_,[["render",function(c,m,_,v,g,b){const k=t("Icons"),V=e;return a(),o("div",y,[g.app_ready?(a(),o("div",f,[b.displayValue&&!_.btn_mode?(a(),o("img",{key:0,src:b.displayValue},null,8,h)):n("",!0),i(V,{size:"small",onClick:b.initUploader,type:_.btn_type,class:u(_.btn_class)},{default:l(()=>[!_.hide_upload_icon||_.only_icon?(a(),o("span",w,[i(k,{"icon-name":"simple-upload"})])):n("",!0),_.only_icon?n("",!0):(a(),o(d,{key:1},[s(p(_.btn_text),1)],64))],void 0),_:1},8,["onClick","type","class"]),r(c.$slots,"after")])):n("",!0)])}]]);export{v as P}; diff --git a/wp-content/plugins/fluent-crm/assets/PreviewIframeBuilder.js b/wp-content/plugins/fluent-crm/assets/PreviewIframeBuilder.js new file mode 100644 index 0000000..3b665a8 --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/PreviewIframeBuilder.js @@ -0,0 +1 @@ +import{W as e,X as i,aa as t,Z as a,J as s,az as l,a8 as r,a6 as n,ac as o,$ as h}from"./vendor.js?ver=3.1.8";import{_ as d}from"./fc-bits-ui.js?ver=3.1.8";const m={name:"PreviewIframeBuilder",props:{preview_html:{type:String,default:()=>""},campaign:{type:Object,default:()=>({})},campaign_id:{default:()=>0},frame_height:{type:String,default:()=>"500px"},show_audit:{type:Boolean,default:()=>!1}},data(){return{loading_preview:!0,invalidDoms:[],preview_full_html:this.preview_html}},methods:{loadFrame(){const e=this.$refs.fc_ifr;if(!e)return;const i=e.contentDocument||e.contentWindow&&e.contentWindow.document;i&&(i.open(),i.write(this.preview_full_html),i.close(),this.loading_preview=!1,this.checkDoms(this.preview_full_html))},fetchHtml(){this.loading_preview=!0,this.showing_view=!0,this.$post("campaigns/email-preview-html",{campaign_id:this.campaign_id,disable_subscriber:"yes"}).then(e=>{this.preview_full_html=e.preview_html,this.$nextTick(()=>this.loadFrame())}).catch(e=>{this.handleError(e)}).finally(()=>{this.loading_preview=!1})},checkDoms(e){if(!this.show_audit)return;this.invalidDoms=[];const i=jQuery('a[href=""], a:not([href]), a[href="#"]',e);this.each(i,e=>{console.log(e),this.invalidDoms.push({text:e.text||"Empty Text / Image"})})}},mounted(){this.campaign_id?this.fetchHtml():this.loadFrame()}},c={class:"fc_iframe_wrap"},p={key:0},_={key:1,class:"el-alert el-alert--error is-light",style:{display:"block",overflow:"hidden",padding:"0 20px","margin-bottom":"0px"}},f={style:{"font-size":"16px"}},v={class:"inline_disc_lists"};const u=d(m,[["render",function(d,m,u,w,g,y){return e(),i("div",c,[g.loading_preview?(e(),i("h3",p,t(d.$t("Loading Preview. Please wait...")),1)):u.show_audit&&g.invalidDoms&&g.invalidDoms.length?(e(),i("div",_,[a("h3",f,t(d.$t("Invalid_Link_Detected"))+" ("+t(g.invalidDoms.length)+")",1),a("ul",v,[(e(!0),i(s,null,l(g.invalidDoms,(a,s)=>(e(),i("li",{key:s},t(a.text),1))),128))])])):r("",!0),n(a("iframe",{ref:"fc_ifr",frameborder:"0",scrolling:"auto",style:h([{width:"100%",border:"none",display:"block"},{height:u.frame_height}])},null,4),[[o,!g.loading_preview]])])}]]);export{u as P}; diff --git a/wp-content/plugins/fluent-crm/assets/ProBadge.js b/wp-content/plugins/fluent-crm/assets/ProBadge.js new file mode 100644 index 0000000..52697be --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/ProBadge.js @@ -0,0 +1 @@ +import{W as a,X as C,Z as e,a8 as s,a9 as t,aa as o}from"./vendor.js?ver=3.1.8";import{_ as r}from"./fc-bits-ui.js?ver=3.1.8";const n={name:"ProBadge",props:{text:{type:String,default:"Pro"},hideIcon:{type:Boolean,default:!1}}},i={class:"fcrm_pro_badge"},c={key:0,class:"icon"};const d=r(n,[["render",function(r,n,d,p,f,l){return a(),C("div",i,[d.hideIcon?s("",!0):(a(),C("span",c,[...n[0]||(n[0]=[e("svg",{width:"16",height:"16",viewBox:"0 0 16 16",fill:"none",xmlns:"http://www.w3.org/2000/svg"},[e("path",{d:"M2.48 3.91951L5 5.59951L7.5116 2.08351C7.56711 2.00573 7.64038 1.94234 7.72533 1.89859C7.81028 1.85485 7.90445 1.83203 8 1.83203C8.09556 1.83203 8.18972 1.85485 8.27468 1.89859C8.35963 1.94234 8.4329 2.00573 8.4884 2.08351L11 5.59951L13.52 3.91951C13.6154 3.85606 13.7269 3.82124 13.8415 3.81915C13.956 3.81707 14.0687 3.84782 14.1663 3.90776C14.2639 3.96771 14.3424 4.05435 14.3923 4.15743C14.4423 4.26052 14.4617 4.37575 14.4482 4.48951L13.4624 12.8697C13.4452 13.0157 13.375 13.1502 13.2652 13.2479C13.1554 13.3455 13.0136 13.3995 12.8666 13.3995H3.1334C2.98644 13.3995 2.8446 13.3455 2.73478 13.2479C2.62496 13.1502 2.5548 13.0157 2.5376 12.8697L1.5518 4.48891C1.53848 4.3752 1.55798 4.26005 1.60798 4.15706C1.65798 4.05407 1.7364 3.96753 1.83399 3.90767C1.93158 3.8478 2.04426 3.81711 2.15873 3.81921C2.2732 3.82131 2.38468 3.85611 2.48 3.91951V3.91951ZM8 9.79951C8.31826 9.79951 8.62349 9.67308 8.84853 9.44804C9.07358 9.223 9.2 8.91777 9.2 8.59951C9.2 8.28125 9.07358 7.97603 8.84853 7.75098C8.62349 7.52594 8.31826 7.39951 8 7.39951C7.68174 7.39951 7.37652 7.52594 7.15148 7.75098C6.92643 7.97603 6.8 8.28125 6.8 8.59951C6.8 8.91777 6.92643 9.223 7.15148 9.44804C7.37652 9.67308 7.68174 9.79951 8 9.79951Z",fill:"var(--fc-warning)"})],-1)])])),t(" "+o(d.text),1)])}]]);export{d as P}; diff --git a/wp-content/plugins/fluent-crm/assets/PromoCard.js b/wp-content/plugins/fluent-crm/assets/PromoCard.js new file mode 100644 index 0000000..ccb254f --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/PromoCard.js @@ -0,0 +1 @@ +import{_ as a,I as o}from"./fc-bits-ui.js?ver=3.1.8";import{aQ as e,W as s,X as r,ab as n,a8 as t,Z as c,aa as i,_ as l,a9 as p,a0 as d}from"./vendor.js?ver=3.1.8";const u={name:"PromoCard",components:{Icons:o},props:{heading:{type:String,default:"This is a pro feature"},description:{type:String,default:"You need to upgrade to use this feature."},showUpgradeBtnIcon:{type:Boolean,default:!1},showHeaderUpgradeIcon:{type:Boolean,default:!0},align:{type:String,default:"center"}}},f={key:0,class:"fcrm_pro_icon"},m={class:"fcrm_pro_modal_actions"},g=["href"],_={key:0,class:"icon"};const h=a(u,[["render",function(a,o,u,h,y,b){const I=e("Icons");return s(),r("div",{class:d(["fcrm_pro_modal_body",`align-${u.align}`])},[u.showHeaderUpgradeIcon?(s(),r("div",f,[n(I,{"icon-name":"crown"})])):t("",!0),c("h3",null,i(u.heading),1),c("p",null,i(u.description),1),l(a.$slots,"before-cta"),c("div",m,[c("a",{href:a.appVars.crm_pro_url,target:"_blank",class:"el-button el-button--primary"},[c("span",null,[u.showUpgradeBtnIcon?(s(),r("span",_,[n(I,{"icon-name":"crown"})])):t("",!0),p(" "+i(a.$t("Upgrade to Pro")),1)])],8,g),l(a.$slots,"actions")]),l(a.$slots,"after-cta")],2)}]]);export{h as P}; diff --git a/wp-content/plugins/fluent-crm/assets/ReadableRecipientTagger.js b/wp-content/plugins/fluent-crm/assets/ReadableRecipientTagger.js new file mode 100644 index 0000000..2b6e382 --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/ReadableRecipientTagger.js @@ -0,0 +1 @@ +import{_ as e}from"./fc-bits.js?ver=3.1.8";import{ay as l,aL as t,aK as a,aM as s}from"./vendor-element-plus.js?ver=3.1.8";import{bW as i,aQ as n,a6 as d,W as r,X as c,J as o,Z as _,aa as u,a8 as g,az as p,ab as b,a5 as m,Y as f}from"./vendor.js?ver=3.1.8";import{_ as h}from"./fc-bits-ui.js?ver=3.1.8";const v=i(()=>e(()=>import("./v3app/src/Modules/Contacts/RichFilters/Filters.js?ver=3.1.8"),[],import.meta.url)),y={name:"RecipientTaggerView",props:{settings:{type:Object,required:!0},alreadySent:{type:Boolean,default:!1}},components:{RichFilter:v},data(){return{fetchingData:!1,lists:[],tags:[],segments:[],advanced_filters:[[]],FilterLabel:this.$t("Filters.instruction")}},computed:{all_tag_groups(){return{all:{title:"",options:[{title:this.$t("Rec_All_coSLs"),slug:"all",id:"all"}]},tags:{title:this.$t("Tags"),options:this.tags}}}},methods:{fetch(){this.fetchingData=!0,this.$get("reports/options",{fields:"lists,tags,segments",with_count:["lists"]}).then(e=>{this.lists=e.options.lists,this.tags=e.options.tags,this.segments=e.options.segments}).catch(e=>{this.handleError(e)}).finally(()=>{this.fetchingData=!1})}},mounted(){this.fetch()},created(){this.settings.advanced_filters?this.advanced_filters=this.settings.advanced_filters:this.settings.advanced_filters=this.advanced_filters}},$={class:"fcrm_readable_recipient_tagger"},k={class:"fcrm_readable_recipient_tagger__section"},V={key:0,class:"fcrm_readable_recipient_tagger__section_heading"},S={class:"fcrm_readable_recipient_tagger__section_table"},L={class:"list-metrics"},w={class:"list-metrics"},R={key:0,class:"fcrm_readable_recipient_tagger__section"},T={class:"fcrm_readable_recipient_tagger__section_heading"},U={class:"fcrm_readable_recipient_tagger__section_table"},A={class:"list-metrics"},C={class:"list-metrics"},F={key:1,class:"fcrm_readable_recipient_tagger__section"},j={class:"fcrm_readable_recipient_tagger__section_heading"},D={key:2,class:"fcrm_readable_recipient_tagger__section"},x={key:0,class:"fc_rich_container"},z={class:"fcrm_readable_recipient_tagger__section_heading"},E={class:"fc_rich_wrap"},O={class:"fc_rich_filter"},M={class:"fc_cond_or"};const W=h(y,[["render",function(e,i,h,v,y,W){const q=t,B=a,I=s,J=n("rich-filter"),K=l;return d((r(),c("div",$,["list_tag"==h.settings.sending_filter?(r(),c(o,{key:0},[_("div",k,[h.alreadySent?g("",!0):(r(),c("div",V,[_("h3",null,u(e.$t("Sending To Contacts")),1)])),_("table",S,[_("thead",null,[_("tr",null,[_("th",null,u(e.$t("List")),1),_("th",null,u(e.$t("Tag")),1)])]),_("tbody",null,[(r(!0),c(o,null,p(h.settings.subscribers,(l,t)=>(r(),c("tr",{key:t},[_("td",null,[b(B,{disabled:!0,size:"small",placeholder:e.$t("Choose a List"),modelValue:l.list,"onUpdate:modelValue":e=>l.list=e,filterable:"","popper-class":"fcrm_select_options_wordbreak"},{default:m(()=>[b(q,{label:e.$t("All Lists"),value:"all"},{default:m(()=>[_("span",null,u(e.$t("All available subscribers")),1),_("span",L,u(e.$t("Rec_This_wfaac")),1)],void 0,!0),_:1},8,["label"]),(r(!0),c(o,null,p(y.lists,l=>(r(),f(q,{key:l.id,label:l.title,value:String(l.id)},{default:m(()=>[_("span",null,u(l.title),1),_("span",w,u(l.subscribersCount)+" "+u(e.$t("subscribed contacts")),1)],void 0,!0),_:2},1032,["label","value"]))),128))],void 0),_:1},8,["placeholder","modelValue","onUpdate:modelValue"])]),_("td",null,[b(B,{disabled:!0,placeholder:e.$t("Select Tag"),size:"small",filterable:"",modelValue:l.tag,"onUpdate:modelValue":e=>l.tag=e,"popper-class":"fcrm_select_options_wordbreak"},{default:m(()=>[(r(!0),c(o,null,p(W.all_tag_groups,(e,l)=>(r(),f(I,{key:l,label:e.title},{default:m(()=>[(r(!0),c(o,null,p(e.options,(e,l)=>(r(),f(q,{key:l,label:e.title,value:String(e.id)},{default:m(()=>[_("span",null,u(e.title),1)],void 0,!0),_:2},1032,["label","value"]))),128))],void 0,!0),_:2},1032,["label"]))),128))],void 0),_:1},8,["placeholder","modelValue","onUpdate:modelValue"])])]))),128))])])]),h.settings.excludedSubscribers&&h.settings.excludedSubscribers.length?(r(),c("div",R,[_("div",T,[_("h3",null,u(e.$t("Excluded Contacts")),1)]),_("table",U,[_("thead",null,[_("tr",null,[_("th",null,u(e.$t("List")),1),_("th",null,u(e.$t("Tag")),1)])]),_("tbody",null,[(r(!0),c(o,null,p(h.settings.excludedSubscribers,(l,t)=>(r(),c("tr",{key:t},[_("td",null,[b(B,{disabled:!0,size:"small",clearable:"",placeholder:e.$t("Choose a List"),modelValue:l.list,"onUpdate:modelValue":e=>l.list=e,filterable:"","popper-class":"fcrm_select_options_wordbreak"},{default:m(()=>[b(q,{label:e.$t("All Lists"),value:"all"},{default:m(()=>[_("span",null,u(e.$t("All available contacts")),1),_("span",A,u(e.$t("Rec_This_wfaac")),1)],void 0,!0),_:1},8,["label"]),(r(!0),c(o,null,p(y.lists,l=>(r(),f(q,{key:l.id,label:l.title,value:String(l.id)},{default:m(()=>[_("span",null,u(l.title),1),_("span",C,u(l.subscribersCount)+" "+u(e.$t("subscribed contacts")),1)],void 0,!0),_:2},1032,["label","value"]))),128))],void 0),_:1},8,["placeholder","modelValue","onUpdate:modelValue"])]),_("td",null,[b(B,{disabled:!0,clearable:"",filterable:"",placeholder:e.$t("Select"),size:"small",modelValue:l.tag,"onUpdate:modelValue":e=>l.tag=e,"popper-class":"fcrm_select_options_wordbreak"},{default:m(()=>[(r(!0),c(o,null,p(W.all_tag_groups,(e,l)=>(r(),f(I,{key:l,label:e.title},{default:m(()=>[(r(!0),c(o,null,p(e.options,(e,l)=>(r(),f(q,{key:l,label:e.title,value:String(e.id)},{default:m(()=>[_("span",null,u(e.title),1)],void 0,!0),_:2},1032,["label","value"]))),128))],void 0,!0),_:2},1032,["label"]))),128))],void 0),_:1},8,["placeholder","modelValue","onUpdate:modelValue"])])]))),128))])])])):g("",!0)],64)):"dynamic_segment"==h.settings.sending_filter?(r(),c("div",F,[_("div",j,[_("h3",null,u(e.$t("Dynamic Segment")),1)]),b(B,{disabled:!0,filterable:"",placeholder:e.$t("Select"),"value-key":"uid",modelValue:h.settings.dynamic_segment,"onUpdate:modelValue":i[0]||(i[0]=e=>h.settings.dynamic_segment=e)},{default:m(()=>[(r(!0),c(o,null,p(y.segments,e=>(r(),f(q,{key:e.slug+"_"+e.id,value:{uid:e.slug+"_"+e.id,slug:e.slug,id:e.id},label:e.title},null,8,["value","label"]))),128))],void 0),_:1},8,["placeholder","modelValue"])])):"advanced_filters"==h.settings.sending_filter?(r(),c("div",D,[e.has_campaign_pro?(r(),c("div",x,[_("div",z,[_("h3",null,u(e.$t("Advanced Filter")),1)]),_("div",E,[(r(!0),c(o,null,p(y.advanced_filters,(l,t)=>(r(),c("div",{key:t},[_("div",O,[b(J,{add_label:y.FilterLabel,view_only:!0,items:l},null,8,["add_label","items"])]),_("div",M,[_("em",null,u(e.$t("OR")),1)])]))),128))])])):g("",!0)])):g("",!0)])),[[K,y.fetchingData]])}]]);export{W as R}; diff --git a/wp-content/plugins/fluent-crm/assets/RecipientTaggerForm.js b/wp-content/plugins/fluent-crm/assets/RecipientTaggerForm.js new file mode 100644 index 0000000..2188012 --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/RecipientTaggerForm.js @@ -0,0 +1 @@ +import{_ as e}from"./fc-bits.js?ver=3.1.8";import{aA as t,k as s,aD as l,ay as a,aE as i,b9 as n,aK as c,aL as d,aM as r,ax as o}from"./vendor-element-plus.js?ver=3.1.8";import{W as _,X as m,Z as u,ab as g,a5 as p,aa as f,a9 as h,bW as b,aQ as v,a6 as y,Y as $,J as k,az as S,a8 as C,_ as V}from"./vendor.js?ver=3.1.8";import{_ as w,I as R}from"./fc-bits-ui.js?ver=3.1.8";const L={class:"fc_promo_body"},A={class:"promo_block"},F=["href"],x={class:"promo_content"},D=["src"];const M=w({name:"DynamicSegmentCampaignPromo"},[["render",function(e,a,i,n,c,d){const r=s,o=t,b=l;return _(),m("div",L,[u("div",A,[g(b,{gutter:20},{default:p(()=>[g(o,{sm:24,md:12},{default:p(()=>[u("h2",null,f(e.$t("DynamicSegmentCampaignPromo.title")),1),u("p",null,f(e.$t("create_as_many_dynamic_segment_introduction")),1),u("div",null,[u("a",{href:e.appVars.crm_pro_url,target:"_blank",rel:"noopener"},[g(r,{type:"danger"},{default:p(()=>[h(f(e.$t("Get FluentCRM Pro")),1)],void 0,!0),_:1})],8,F)])],void 0,!0),_:1}),g(o,{sm:24,md:12},{default:p(()=>[u("div",x,[u("img",{class:"promo_image",src:e.appVars.images_url+"/promo/segment_campaign.png"},null,8,D)])],void 0,!0),_:1})],void 0),_:1})])])}]]),T={class:"fc_promo_body"},P={class:"promo_block promo_block--centered"},E=["href"];const U=w({name:"AdvancedFilterPromo"},[["render",function(e,t,l,a,i,n){const c=s;return _(),m("div",T,[u("div",P,[u("h2",null,f(e.$t("Advanced Filter is a pro feature")),1),u("p",null,f(e.$t("segment_your_contacts")),1),u("div",null,[u("a",{href:e.appVars.crm_pro_url,target:"_blank",rel:"noopener"},[g(c,{type:"primary"},{default:p(()=>[h(f(e.$t("Get FluentCRM Pro")),1)],void 0),_:1})],8,E)])])])}],["__scopeId","data-v-0bbb2bdb"]]),z=b(()=>e(()=>import("./v3app/src/Modules/Contacts/RichFilters/Filters.js?ver=3.1.8"),[],import.meta.url)),G={class:"fcrm_email_campaign_recipient_tagger"},I={class:"fcrm_email_campaign_recipient_tagger_selector"},O={class:"fcrm_email_campaign_included_contacts"},j={class:"fcrm_email_campaign_recipient_section_heading"},J={class:"list_th_col"},B={class:"tag_th_col"},N={class:"list_td_col",style:{width:"50%"}},W={class:"list-metrics"},K={class:"list-metrics"},Q={class:"tag_td_col",style:{width:"50%"}},X={class:"action_td_col"},Y={class:"icon"},Z={class:"fcrm_email_campaign_recipient_adder_action"},q={class:"icon","aria-hidden":"true"},H={class:"fcrm_email_campaign_excluded_contacts"},ee={class:"fcrm_email_campaign_recipient_section_heading"},te={class:"list_th_col"},se={class:"tag_th_col"},le={class:"list_td_col",style:{width:"50%"}},ae={class:"list-metrics"},ie={class:"list-metrics"},ne={class:"tag_td_col",style:{width:"50%"}},ce={class:"action_td_col"},de={class:"icon"},re={class:"fcrm_email_campaign_recipient_adder_action"},oe={class:"icon","aria-hidden":"true"},_e={key:1,class:"fcrm_email_campaign_dynamic_segment"},me={class:"fcrm_email_campaign_recipient_section_heading"},ue={key:2,class:"fcrm_email_campaign_advanced_filters"},ge={key:0,class:"fc_rich_container fcrm_rich_container"},pe={class:"fcrm_email_campaign_recipient_section_heading"},fe={class:"fcrm_rich_wrap"},he={class:"fcrm_rich_filter"},be={class:"fcrm_filter_group_header"},ve={key:0,class:"fcrm_and_label"},ye={key:0,class:"fcrm_cond_or"},$e={class:"icon","aria-hidden":"true"},ke={class:"fcrm_cond_or"},Se={class:"icon","aria-hidden":"true"},Ce={class:"fc_rich_container_actions"},Ve={class:"fc_counting_heading"};const we=w({name:"recipientTagger",props:["modelValue","callerModule"],emits:["update:modelValue"],components:{Icons:R,DynamicSegmentCampaignPromo:M,RichFilter:z,AdvancedFilterPromo:U},data(){return{settings:this.modelValue,fetchingData:!1,settings_mock:{subscribers:[{list:"all",tag:"all"}],excludedSubscribers:[{list:null,tag:null}],sending_filter:"list_tag",dynamic_segment:null,advanced_filters:[[]]},lists:[],tags:[],segments:[],advanced_filters:[[]],estimated_count:0,estimating:!1,FilterLabel:this.$t("Filters.instruction")}},computed:{all_tag_groups(){return{all:{title:"",options:[{title:this.$t("Rec_All_coSLs"),slug:"all",id:"all"}]},tags:{title:this.$t("Tags"),options:this.tags}}}},watch:{settings:{handler(e,t){this.$emit("update:modelValue",e),"advanced_filters"!=this.settings.sending_filter&&this.fetchEstimatedCount()},deep:!0}},methods:{fetch(){this.fetchingData=!0,this.$get("reports/options",{fields:"lists,tags,segments",with_count:["lists"]}).then(e=>{this.lists=e.options.lists,this.tags=e.options.tags,this.segments=e.options.segments}).catch(e=>{this.handleError(e)}).finally(()=>{this.fetchingData=!1})},add(e,t){this.settings[e].splice(t+1,0,{list:"subscribers"==e?"all":null,tag:"all"})},remove(e,t){this.settings[e].length>1&&this.settings[e].splice(t,1)},handleListChange(e,t){e&&(e.list?"subscribers"===t&&(e.tag="all"):e.tag=null)},normalizeSubscriberRows(){Array.isArray(this.settings.subscribers)&&(this.settings.subscribers=this.settings.subscribers.map(e=>(e&&e.list&&!e.tag&&(e.tag="all"),e)))},fetchEstimatedCount(){const e=this.settings,t={sending_filter:e.sending_filter};if("list_tag"==e.sending_filter){const s=e.subscribers.filter(e=>e.list&&e.tag),l=e.excludedSubscribers.filter(e=>e.list&&e.tag);if(!s.length)return!1;l.length&&(t.excludedSubscribers=l),t.subscribers=s}else if("dynamic_segment"==e.sending_filter){if(!e.dynamic_segment||!e.dynamic_segment.uid)return!1;t.dynamic_segment=e.dynamic_segment}else{if("advanced_filters"!=e.sending_filter)return!1;t.advanced_filters=JSON.stringify(this.advanced_filters)}this.estimating=!0;let s="campaigns/estimated-contacts";void 0!==this.callerModule&&"sms"===this.callerModule&&(s="sms/campaigns/estimated-contacts"),this.$post(s,t).then(e=>{this.estimated_count=e.count}).catch(e=>{this.handleError(e)}).finally(()=>{this.estimating=!1})},addConditionGroup(){this.advanced_filters.push([])},maybeRemoveGroup(e){this.advanced_filters.length>1&&this.advanced_filters.splice(e,1)}},mounted(){this.fetch(),this.fetchEstimatedCount()},created(){const e=JSON.parse(JSON.stringify(this.settings_mock));this.settings?(this.settings.subscribers||(this.settings.subscribers=e.subscribers),this.normalizeSubscriberRows(),this.settings.excludedSubscribers||(this.settings.excludedSubscribers=e.excludedSubscribers),this.settings.dynamic_segment||(this.settings.dynamic_segment=e.dynamic_segment),this.settings.dynamic_segment&&this.settings.dynamic_segment.uid||(this.settings.dynamic_segment=null),this.settings.advanced_filters?this.advanced_filters=this.settings.advanced_filters:this.settings.advanced_filters=this.advanced_filters,this.settings.sending_filter||(this.settings.sending_filter=e.sending_filter)):this.settings=e}},[["render",function(e,t,l,b,w,R){const L=n,A=i,F=d,x=c,D=r,M=v("Icons"),T=s,P=v("dynamic-segment-campaign-promo"),E=v("rich-filter"),U=v("advanced-filter-promo"),z=o,we=a;return _(),m("div",G,[y((_(),$(z,null,{default:p(()=>[u("div",I,[g(A,{modelValue:w.settings.sending_filter,"onUpdate:modelValue":t[0]||(t[0]=e=>w.settings.sending_filter=e)},{default:p(()=>[g(L,{class:"fcrm_list_tag_selector",value:"list_tag"},{default:p(()=>[h(f(e.$t("By List & Tag")),1)],void 0,!0),_:1}),g(L,{class:"fcrm_dynamic_segment_selector",value:"dynamic_segment"},{default:p(()=>[h(f(e.$t("By Dynamic Segment")),1)],void 0,!0),_:1}),g(L,{class:"fcrm_advanced_filters_selector",value:"advanced_filters"},{default:p(()=>[h(f(e.$t("By Advanced Filter")),1)],void 0,!0),_:1})],void 0,!0),_:1},8,["modelValue"])]),"list_tag"==w.settings.sending_filter?(_(),m(k,{key:0},[u("div",O,[u("div",j,[u("h3",null,f(e.$t("Included Contacts")),1),u("p",null,f(e.$t("sms"===l.callerModule?"Rec_Select_LaTtywtse_sms":"Rec_Select_LaTtywtse")),1)]),u("table",null,[u("thead",null,[u("tr",null,[u("th",J,f(e.$t("Select A List")),1),u("th",B,f(e.$t("Select Tag")),1),t[8]||(t[8]=u("th",{class:"action_th_col"},null,-1))])]),u("tbody",null,[(_(!0),m(k,null,S(w.settings.subscribers,(t,s)=>(_(),m("tr",{key:s},[u("td",N,[g(x,{placeholder:e.$t("Choose a List"),modelValue:t.list,"onUpdate:modelValue":e=>t.list=e,onChange:e=>R.handleListChange(t,"subscribers"),filterable:"","popper-class":"fcrm_select_options_wordbreak"},{default:p(()=>[g(F,{label:e.$t("All Lists"),value:"all"},{default:p(()=>[u("span",null,[u("span",null,f(e.$t("All available subscribers")),1),u("span",W,f(e.$t("Rec_This_wfaac")),1)])],void 0,!0),_:1},8,["label"]),(_(!0),m(k,null,S(w.lists,t=>(_(),$(F,{key:t.id,label:t.title,value:String(t.id)},{default:p(()=>[u("span",null,[u("span",null,f(t.title),1),u("span",K,f(t.subscribersCount)+" "+f(e.$t("subscribed contacts")),1)])],void 0,!0),_:2},1032,["label","value"]))),128))],void 0,!0),_:1},8,["placeholder","modelValue","onUpdate:modelValue","onChange"])]),u("td",Q,[g(x,{disabled:!t.list,placeholder:t.list?e.$t("Select Tag"):e.$t("Select List First"),filterable:"",modelValue:t.tag,"onUpdate:modelValue":e=>t.tag=e,"popper-class":"fcrm_select_options_wordbreak"},{default:p(()=>[(_(!0),m(k,null,S(R.all_tag_groups,(e,t)=>(_(),$(D,{key:t,label:e.title},{default:p(()=>[(_(!0),m(k,null,S(e.options,(e,t)=>(_(),$(F,{key:t,label:e.title,value:String(e.id)},{default:p(()=>[u("span",null,f(e.title),1)],void 0,!0),_:2},1032,["label","value"]))),128))],void 0,!0),_:2},1032,["label"]))),128))],void 0,!0),_:1},8,["disabled","placeholder","modelValue","onUpdate:modelValue"])]),u("td",X,[g(T,{class:"small only-icon-btn","aria-label":e.$t("Remove row"),size:"small",onClick:e=>R.remove("subscribers",s)},{default:p(()=>[u("span",Y,[g(M,{"icon-name":"delete"})])],void 0,!0),_:1},8,["aria-label","onClick"])])]))),128))])]),u("div",Z,[g(T,{class:"small",size:"small",onClick:t[1]||(t[1]=e=>R.add("subscribers",w.settings.subscribers.length-1))},{default:p(()=>[u("span",q,[g(M,{"icon-name":"plus"})]),h(" "+f(e.$t("Add More")),1)],void 0,!0),_:1})])]),u("div",H,[u("div",ee,[u("h3",null,f(e.$t("Excluded Contacts")),1),u("p",null,f(e.$t("Rec_Select_LaTtywtef")),1)]),u("table",null,[u("thead",null,[u("tr",null,[u("th",te,f(e.$t("Select A List")),1),u("th",se,f(e.$t("Select Tag")),1),t[9]||(t[9]=u("th",{class:"action_th_col"},null,-1))])]),u("tbody",null,[(_(!0),m(k,null,S(w.settings.excludedSubscribers,(t,s)=>(_(),m("tr",{key:s},[u("td",le,[g(x,{clearable:"",filterable:"",placeholder:e.$t("Choose a List"),modelValue:t.list,"onUpdate:modelValue":e=>t.list=e,"popper-class":"fcrm_select_options_wordbreak"},{default:p(()=>[g(F,{label:e.$t("All Lists"),value:"all"},{default:p(()=>[u("span",null,[u("span",null,f(e.$t("All available contacts")),1),u("span",ae,f(e.$t("Rec_This_wfaac")),1)])],void 0,!0),_:1},8,["label"]),(_(!0),m(k,null,S(w.lists,t=>(_(),$(F,{key:t.id,label:t.title,value:String(t.id)},{default:p(()=>[u("span",null,[u("span",null,f(t.title),1),u("span",ie,f(t.subscribersCount)+" "+f(e.$t("subscribed contacts")),1)])],void 0,!0),_:2},1032,["label","value"]))),128))],void 0,!0),_:1},8,["placeholder","modelValue","onUpdate:modelValue"])]),u("td",ne,[g(x,{disabled:!t.list,clearable:"",filterable:"",placeholder:t.list?e.$t("Select Tag"):e.$t("Select List First"),modelValue:t.tag,"onUpdate:modelValue":e=>t.tag=e,"popper-class":"fcrm_select_options_wordbreak"},{default:p(()=>[(_(!0),m(k,null,S(R.all_tag_groups,(e,t)=>(_(),$(D,{key:t,label:e.title},{default:p(()=>[(_(!0),m(k,null,S(e.options,(e,t)=>(_(),$(F,{key:t,label:e.title,value:String(e.id)},{default:p(()=>[u("span",null,f(e.title),1)],void 0,!0),_:2},1032,["label","value"]))),128))],void 0,!0),_:2},1032,["label"]))),128))],void 0,!0),_:1},8,["disabled","placeholder","modelValue","onUpdate:modelValue"])]),u("td",ce,[g(T,{class:"small only-icon-btn","aria-label":e.$t("Remove row"),size:"small",onClick:e=>R.remove("excludedSubscribers",s)},{default:p(()=>[u("span",de,[g(M,{"icon-name":"delete"})])],void 0,!0),_:1},8,["aria-label","onClick"])])]))),128))])]),u("div",re,[g(T,{class:"small",size:"small",onClick:t[2]||(t[2]=e=>R.add("excludedSubscribers",w.settings.excludedSubscribers.length-1))},{default:p(()=>[u("span",oe,[g(M,{"icon-name":"plus"})]),h(" "+f(e.$t("Add More")),1)],void 0,!0),_:1})])])],64)):"dynamic_segment"==w.settings.sending_filter?(_(),m("div",_e,[e.has_campaign_pro?(_(),m(k,{key:0},[u("div",me,[u("h3",null,f(e.$t("Select Dynamic Segment")),1),u("p",null,f(e.$t("sms"===l.callerModule?"Rec_Please_stwdsywts_sms":"Rec_Please_stwdsywts")),1)]),g(x,{placeholder:e.$t("Select Dynamic Segment"),"value-key":"uid",modelValue:w.settings.dynamic_segment,"onUpdate:modelValue":t[3]||(t[3]=e=>w.settings.dynamic_segment=e),size:"default",filterable:"",clearable:"",class:"fcrm_dynamic_select"},{default:p(()=>[(_(!0),m(k,null,S(w.segments,e=>(_(),$(F,{key:e.slug+"_"+e.id,value:{uid:e.slug+"_"+e.id,slug:e.slug,id:e.id},label:e.title},null,8,["value","label"]))),128))],void 0,!0),_:1},8,["placeholder","modelValue"])],64)):(_(),$(P,{key:1}))])):"advanced_filters"==w.settings.sending_filter?(_(),m("div",ue,[e.has_campaign_pro?(_(),m("div",ge,[u("div",pe,[u("h3",null,f(e.$t("Select custom contacts by advanced filters")),1),u("p",null,f(e.$t("Please select to with advance filter you want to send emails for this campaign")),1)]),u("div",fe,[(_(!0),m(k,null,S(w.advanced_filters,(s,l)=>(_(),m("div",{class:"fcrm_rich_wrap_inner fc_rich_wrap_inner",key:l},[u("div",he,[u("div",be,[s.length>1?(_(),m("div",ve,f(e.$t("And")),1)):C("",!0),g(E,{add_label:w.FilterLabel,canDeleteGroup:w.advanced_filters.length>1,onMaybeRemove:e=>R.maybeRemoveGroup(l),items:s},null,8,["add_label","canDeleteGroup","onMaybeRemove","items"])])]),lR.addConditionGroup())},{default:p(()=>[u("span",$e,[g(M,{"icon-name":"plus"})]),h(" "+f(e.$t("OR")),1)],void 0,!0),_:1}),t[11]||(t[11]=u("div",{class:"fcrm_or_divider_line"},null,-1))])):C("",!0)]))),128))]),u("div",ke,[t[12]||(t[12]=u("div",{class:"fcrm_or_divider_line"},null,-1)),g(T,{class:"small",onClick:t[5]||(t[5]=e=>R.addConditionGroup())},{default:p(()=>[u("span",Se,[g(M,{"icon-name":"plus"})]),h(" "+f(e.$t("OR")),1)],void 0,!0),_:1}),t[13]||(t[13]=u("div",{class:"fcrm_or_divider_line"},null,-1))]),u("div",Ce,[g(T,{class:"small",onClick:t[6]||(t[6]=e=>{w.settings.advanced_filters=[[]],R.fetch()})},{default:p(()=>[h(f(e.$t("Clear Filters")),1)],void 0,!0),_:1}),g(T,{size:"small",type:"primary",onClick:t[7]||(t[7]=e=>R.fetchEstimatedCount())},{default:p(()=>[h(f(e.$t("Filter")),1)],void 0,!0),_:1})])])):(_(),$(U,{key:1}))])):C("",!0),y((_(),m("h3",Ve,[u("span",null,f(w.estimated_count),1),h(" "+f(e.$t("Rec_contacts_fboys")),1)])),[[we,w.estimating]])],void 0),_:1})),[[we,w.fetchingData]]),V(e.$slots,"fc_tagger_bottom")])}]]);export{we as R}; diff --git a/wp-content/plugins/fluent-crm/assets/SaveButton.js b/wp-content/plugins/fluent-crm/assets/SaveButton.js new file mode 100644 index 0000000..d1fdf56 --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/SaveButton.js @@ -0,0 +1 @@ +import{k as e}from"./vendor-element-plus.js?ver=3.1.8";import{W as t,Y as a,a5 as o,X as n,a8 as s,a9 as d,aa as r}from"./vendor.js?ver=3.1.8";import{_ as i}from"./fc-bits-ui.js?ver=3.1.8";const l={name:"SaveButton",props:{loading:{type:Boolean,default:!1},text:{type:String,default:""},loadingText:{type:String,default:""}},emits:["save"],computed:{displayText(){return""===this.text?this.$t("Save"):this.text}},methods:{handleKeyboardShortcut(e){(e.metaKey||e.ctrlKey)&&"s"===e.key&&(e.preventDefault(),this.$emit("save"))}},mounted(){document.addEventListener("keydown",this.handleKeyboardShortcut)},beforeUnmount(){document.removeEventListener("keydown",this.handleKeyboardShortcut)}},m={key:0,class:"cmd"};const u=i(l,[["render",function(i,l,u,p,y,c){const v=e;return t(),a(v,{type:"primary",size:"small",onClick:l[0]||(l[0]=e=>i.$emit("save")),loading:u.loading},{default:o(()=>[u.loading?s("",!0):(t(),n("span",m,"⌘s")),d(" "+r(u.loading&&u.loadingText?u.loadingText:c.displayText),1)],void 0),_:1},8,["loading"])}]]);export{u as S}; diff --git a/wp-content/plugins/fluent-crm/assets/Searcher.js b/wp-content/plugins/fluent-crm/assets/Searcher.js new file mode 100644 index 0000000..43af196 --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/Searcher.js @@ -0,0 +1 @@ +import{e}from"./vendor-element-plus.js?ver=3.1.8";import{aQ as o,W as s,Y as a,a5 as l,Z as t,ab as d,b2 as i}from"./vendor.js?ver=3.1.8";import{_ as r,I as n}from"./fc-bits-ui.js?ver=3.1.8";const m={props:{modelValue:{type:String,default:""},disabled:Boolean,placeholder:String},name:"Searcher",emits:["update:modelValue"],components:{Icons:n},data(){return{model:this.modelValue||"",timeout:null}},methods:{fire(){this.model&&(this.doAction("loading",!0),this.doAction("search-subscribers",this.model))}},watch:{modelValue(e){this.model=e||""},model:function(e,o){this.$emit("update:modelValue",e||""),e=(e||"").trim(),(o=(o||"").trim())&&!e&&(this.doAction("loading",!0),this.doAction("search-subscribers",this.model))},disabled(e){e&&(this.model="")}}},c={class:"icon"};const u=r(m,[["render",function(r,n,m,u,h,p){const b=o("Icons"),f=e;return s(),a(f,{size:"small",modelValue:h.model,"onUpdate:modelValue":n[0]||(n[0]=e=>h.model=e),onClear:p.fire,disabled:m.disabled,onKeyup:i(p.fire,["enter"]),placeholder:m.placeholder||r.$t("Search contacts"),autofocus:""},{prefix:l(()=>[t("span",c,[d(b,{"icon-name":"search"})])]),_:1},8,["modelValue","onClear","disabled","onKeyup","placeholder"])}]]);export{u as S}; diff --git a/wp-content/plugins/fluent-crm/assets/SettingsHeader.js b/wp-content/plugins/fluent-crm/assets/SettingsHeader.js new file mode 100644 index 0000000..6e3aa1c --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/SettingsHeader.js @@ -0,0 +1 @@ +import{aJ as e}from"./vendor-element-plus.js?ver=3.1.8";import{aQ as t,W as s,X as n,ab as a,a5 as o,Z as i,a0 as r,_ as l,a9 as c,aa as p,a8 as u}from"./vendor.js?ver=3.1.8";import{_ as d,I as m}from"./fc-bits-ui.js?ver=3.1.8";const b={class:"fcrm_settings_topbar"},g={key:0,class:"fcrm_settings_topbar--breadcrumb"},_={key:1},f={key:2,class:"fcrm_settings_topbar--actions"};const M=d({name:"SettingsHeader",components:{Icons:m},inject:["toggleMenu","settingsState"],computed:{isMenuOpen(){return this.settingsState&&this.settingsState.isMenuOpen}},props:{title:{type:String,default:""}}},[["render",function(d,m,M,v,S,$){const O=t("Icons"),h=e;return s(),n("div",b,[d.$slots.breadcrumb?(s(),n("div",g,[a(h,{enterable:!1,transition:"none",content:$.isMenuOpen?d.$t("Close Sidebar"):d.$t("Open Sidebar"),placement:"right"},{default:o(()=>[i("span",{class:r(["fcrm_settings_menu_collapsable--btn",{"is-collapsed":$.isMenuOpen}]),onClick:m[0]||(m[0]=(...e)=>$.toggleMenu&&$.toggleMenu(...e))},[a(O,{"icon-name":"sidebar"})],2)],void 0),_:1},8,["content"]),l(d.$slots,"breadcrumb")])):(s(),n("h3",_,[a(h,{enterable:!1,transition:"none",content:$.isMenuOpen?d.$t("Close Sidebar"):d.$t("Open Sidebar"),placement:"right"},{default:o(()=>[i("span",{class:r(["fcrm_settings_menu_collapsable--btn",{"is-collapsed":$.isMenuOpen}]),onClick:m[1]||(m[1]=(...e)=>$.toggleMenu&&$.toggleMenu(...e))},[a(O,{"icon-name":"sidebar"})],2)],void 0),_:1},8,["content"]),c(" "+p(M.title)+" ",1),l(d.$slots,"afterTitle")])),d.$slots.actions?(s(),n("div",f,[l(d.$slots,"actions")])):u("",!0)])}]]);export{M as S}; diff --git a/wp-content/plugins/fluent-crm/assets/SettingsIcons.js b/wp-content/plugins/fluent-crm/assets/SettingsIcons.js new file mode 100644 index 0000000..12df000 --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/SettingsIcons.js @@ -0,0 +1 @@ +import{W as C,Y as t,a7 as e}from"./vendor.js?ver=3.1.8";import{_ as n}from"./fc-bits-ui.js?ver=3.1.8";const o=n({name:"SettingsIcons",props:{icon:{type:String,required:!0}},computed:{iconName(){return this.icon}},components:{business_setup:{template:'\n \n \n \n '},global_email_settings:{template:'\n \n \n \n '},email_service_setup:{template:'\n \n \n \n '},general_settings:{template:'\n \n \n \n '},custom_contact_fields:{template:'\n \n \n \n '},smart_links:{template:'\n \n \n \n '},double_optin_settings:{template:'\n \n \n \n '},sms_settings:{template:'\n \n \n \n '},integrations:{template:'\n \n \n \n '},abandoned_cart_settings:{template:'\n \n \n \n '},compliance_settings:{template:'\n \n \n \n '},incoming_webhooks:{template:'\n \n \n \n '},system_admin_tools:{template:'\n \n \n \n '},crm_managers:{template:'\n \n \n \n '},advanced_features:{template:'\n \n \n \n '},license_management:{template:'\n \n \n \n '},system_logs:{template:'\n \n \n \n \n \n \n \n '},ai_writing:{template:'\n \n \n \n '},activity_logs:{template:'\n \n \n \n \n \n \n \n '}}},[["render",function(n,o,L,s,r,i){return C(),t(e(i.iconName))}]]);export{o as S}; diff --git a/wp-content/plugins/fluent-crm/assets/SettingsRow.js b/wp-content/plugins/fluent-crm/assets/SettingsRow.js new file mode 100644 index 0000000..3d87b93 --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/SettingsRow.js @@ -0,0 +1 @@ +import{W as s,X as t,Z as e,_ as l,a8 as a,a0 as r}from"./vendor.js?ver=3.1.8";import{_ as i}from"./fc-bits-ui.js?ver=3.1.8";const o={key:0,class:"fcrm-label-title"},c={key:1,class:"fcrm-label-description"};const d=i({name:"SettingsRow",props:{rowClass:{type:[String,Array,Object],default:""},labelClass:{type:[String,Array,Object],default:""},fieldClass:{type:[String,Array,Object],default:""}}},[["render",function(i,d,f,n,p,b){return s(),t("div",{class:r(["fcrm-settings-row",f.rowClass])},[e("div",{class:r(["fcrm-settings-label",f.labelClass])},[l(i.$slots,"label",{},()=>[i.$slots.title?(s(),t("div",o,[l(i.$slots,"title")])):a("",!0),i.$slots.description?(s(),t("p",c,[l(i.$slots,"description")])):a("",!0)])],2),i.$slots.field?(s(),t("div",{key:0,class:r(["fcrm-settings-field",f.fieldClass])},[l(i.$slots,"field")],2)):l(i.$slots,"default",{key:1})],2)}]]);export{d as S}; diff --git a/wp-content/plugins/fluent-crm/assets/SmsMessageCell.js b/wp-content/plugins/fluent-crm/assets/SmsMessageCell.js new file mode 100644 index 0000000..f225a71 --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/SmsMessageCell.js @@ -0,0 +1 @@ +import{aQ as s,W as e,X as a,Z as n,ab as t,a5 as l,a9 as m,aa as r,_ as o,J as i,a0 as d,a8 as c}from"./vendor.js?ver=3.1.8";import{_ as p,I as _}from"./fc-bits-ui.js?ver=3.1.8";import{k as g}from"./vendor-element-plus.js?ver=3.1.8";const u={class:"fcrm_page_header_top_nav_wrapper"},f={class:"fcrm_page_header_top_nav"},v={class:"fcrm_page_header_top_nav_links"};const x=p({name:"SmsTabNav"},[["render",function(i,d,c,p,_,g){const x=s("router-link");return e(),a("div",u,[n("div",f,[n("ul",v,[n("li",null,[t(x,{to:"/sms/campaigns",class:"fcrm_top_nav_link"},{default:l(()=>[m(r(i.$t("SMS Campaign")),1)],void 0),_:1})]),n("li",null,[t(x,{to:"/sms/all-sms",class:"fcrm_top_nav_link"},{default:l(()=>[m(r(i.$t("SMS Activities")),1)],void 0),_:1})])])]),o(i.$slots,"actions")])}]]),h={class:"fcrm_sms_message_cell"},S={key:0,class:"fcrm_sms_message_toggle"};const k=p({name:"SmsMessageCell",components:{Icons:_},props:{message:{type:String,default:""},maxLen:{type:Number,default:160}},data:()=>({expanded:!1}),computed:{displayedText(){return this.message?this.expanded||this.message.length<=this.maxLen?this.message:String(this.message).trim().slice(0,this.maxLen)+"…":""}}},[["render",function(o,p,_,u,f,v){const x=s("Icons"),k=g;return e(),a("div",h,[_.message?(e(),a(i,{key:1},[n("span",{class:d({fcrm_sms_message_cell_full:f.expanded})},r(v.displayedText),3),_.message.length>_.maxLen?(e(),a("span",S,[t(k,{link:"",onClick:p[0]||(p[0]=s=>f.expanded=!f.expanded)},{default:l(()=>[m(r(f.expanded?o.$t("See less"):o.$t("See more"))+" ",1),t(x,{"icon-name":f.expanded?"arrow-up":"arrow-down"},null,8,["icon-name"])],void 0),_:1})])):c("",!0)],64)):(e(),a(i,{key:0},[m("—")],64))])}]]);export{k as S,x as a}; diff --git a/wp-content/plugins/fluent-crm/assets/Tagger.js b/wp-content/plugins/fluent-crm/assets/Tagger.js new file mode 100644 index 0000000..8506ba8 --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/Tagger.js @@ -0,0 +1 @@ +import{O as e,J as t,b4 as a,u as s,a3 as i,k as o,ay as c,E as n,L as r,at as l,i as d,e as m,aG as p,az as h}from"./vendor-element-plus.js?ver=3.1.8";import{c0 as _,b$ as u,aQ as y,W as f,X as g,Z as v,ab as k,a5 as b,a9 as w,aa as C,a6 as q,Y as $,$ as S,bB as F,a8 as E,J as x,az as A,a0 as N,bU as I,_ as D}from"./vendor.js?ver=3.1.8";import{i as j,j as L}from"./data_config.js?ver=3.1.8";import{C as U,a as P}from"./CompanyEditForm.js?ver=3.1.8";import{_ as V,I as O}from"./fc-bits-ui.js?ver=3.1.8";import{R as B,S as z}from"./fc-bits.js?ver=3.1.8";import{F as M}from"./Filterer.js?ver=3.1.8";const T={name:"CompanyInfoSideContact",components:{Icons:O,CustomFieldsForm:P,CompanyEditForm:U,Edit:i,User:s,Briefcase:a,PriceTag:t,OfficeBuilding:e},emits:["companyUpdated","companyCreated","cancel"],props:{company:{type:Object,default:()=>null},photo_holder:{type:String,default:"fcrm_photo_holder_mini"},is_drawer:{type:Boolean,default:!0},intended_contact_id:{type:Number,default:null}},data:()=>({model:{},isDirty:!1,appReady:!1,updating:!1,isHeaderEditing:!1,isSummaryEditing:!1}),watch:{model:{handler(e,t){this.appReady&&(this.isDirty=!0)},deep:!0}},computed:{domainName(){return L(this.company.website)},ownerDisplayName(){return this.company.owner&&(this.company.owner.full_name||this.company.owner.email)||"--"},quickStats(){return[{label:this.$t("Owner"),value:"--"!==this.ownerDisplayName?this.ownerDisplayName:"",icon:"owner"},{label:this.$t("Industry"),value:this.company.industry,icon:"industry"},{label:this.$t("Type"),value:this.company.type,icon:"type"},{label:this.$t("Employees"),value:this.company.employees_number&&"0"!=this.company.employees_number?this.company.employees_number:"",icon:"employees"}].filter(e=>e.value)},profileAddressLines(){const e=[[this.company.city,this.company.state].filter(Boolean).join(", "),this.company.postal_code].filter(Boolean).join(" ");return[this.company.address_line_1,this.company.address_line_2,e,this.company.country?this.getCountryName(this.company.country):""].filter(Boolean)},socialLinks(){return[{label:this.$t("LinkedIn"),url:this.company.linkedin_url,icon:"linkedin"},{label:this.$t("X"),url:this.company.twitter_url,icon:"x"},{label:this.$t("Facebook"),url:this.company.facebook_url,icon:"facebook"}].filter(e=>e.url)}},methods:{updateAvatar(e){this.company.id?this.updateProperty("logo",e):this.model.logo=e},updateProperty(e,t,a){this.$put("companies/companies-property",{property:e,companies:[this.company.id],value:t}).then(s=>{this.$notify.success(s.message),this.company[e]=t,"logo"===e&&(this.model.logo=t),a&&a(s)}).catch(e=>{this.handleError(e)}).finally(()=>{this.updating=!1})},_submitCompanyUpdate(){this.updating=!0;const e=!this.company.id;return e&&(this.company.id=0,this.intended_contact_id&&(this.model.intended_contact_id=this.intended_contact_id)),this.model.logo=this.company.logo,this.$put(`companies/${this.company.id}`,this.model).then(t=>(this.$notify.success(t.message),this.each(this.model,(e,t)=>{this.company[t]=e}),t.update_data&&this.each(t.update_data,(e,t)=>{this.company[t]=e}),t.company&&(this.each(t.company,(e,t)=>{this.company[t]=e}),this.model.logo=this.company.logo),t.updated_logo&&(this.company.logo=t.updated_logo,this.model.logo=t.updated_logo),this.syncCustomValues(t.company),e?this.$emit("companyCreated",t.company||this.company):this.$emit("companyUpdated",t.company||this.company),this.isDirty=!1,t)).catch(e=>(this.handleError(e),Promise.reject(e))).finally(()=>{this.updating=!1})},updateInfo(){return this._submitCompanyUpdate()},syncCustomValues(e){const t=_(this.model.custom_values)&&!u(this.model.custom_values)?this.model.custom_values:{};_(this.company.meta)&&!u(this.company.meta)||(this.company.meta={}),this.company.meta.custom_values=t,e&&(_(e.meta)&&!u(e.meta)||(e.meta={}),e.meta.custom_values=t)},startSummaryEdit(){this.isSummaryEditing=!0},cancelSummaryEdit(){this.resetModelFromCompany(),this.isDirty=!1,this.isSummaryEditing=!1},resetModelFromCompany(){var e,t;const a=this.company;this.model={owner_id:a.owner_id,name:a.name,logo:a.logo,email:a.email,phone:a.phone,website:a.website,industry:a.industry,type:a.type,address_line_1:a.address_line_1,address_line_2:a.address_line_2,city:a.city,state:a.state,employees_number:a.employees_number&&"0"!=a.employees_number?a.employees_number:"",postal_code:a.postal_code,country:a.country,description:a.description,linkedin_url:a.linkedin_url,twitter_url:a.twitter_url,facebook_url:a.facebook_url,custom_values:_(null==(e=a.meta)?void 0:e.custom_values)&&!u(null==(t=a.meta)?void 0:t.custom_values)?a.meta.custom_values:{}}},saveSummaryEdit(){return this._submitCompanyUpdate().then(()=>{this.isSummaryEditing=!1})},getFormattedAddress:j,getCountryName(e){var t;if(!e)return"";const a=null==(t=this.appVars.countries)?void 0:t.find(t=>t.code===e);return a?a.title:e},reFetchLogo(){this.updating=!0,this.updateProperty("refetch_logo",this.company.website,e=>{e.updated_logo&&(this.company.logo=e.updated_logo,this.model.logo=e.updated_logo)})},handleCancel(){this.is_drawer&&this.$emit("cancel")}},created(){this.resetModelFromCompany(),this.$nextTick(()=>{this.appReady=!0})}},H={key:0,class:"fcrm_company_drawer_main"},J={class:"fcrm_company_create_body"},R={class:"fcrm_company_drawer_footer"},K={class:"fcrm_company_drawer_footer_actions"},Q={key:1,class:"fcrm_view_mode"},W={class:"fcrm_view_sections"},X={class:"fcrm_view_section"},Z={class:"fcrm_view_section_body"},G={key:0,class:"fcrm_company_quick_view"},Y={class:"fcrm_company_quick_hero"},ee={key:0,width:"28",height:"28",viewBox:"0 0 24 24",fill:"none",xmlns:"http://www.w3.org/2000/svg",role:"presentation","aria-hidden":"true"},te={class:"fcrm_company_quick_identity"},ae={class:"fcrm_company_quick_contacts"},se=["href"],ie=["href"],oe={class:"fcrm_company_quick_actions"},ce={class:"icon"},ne={key:0,class:"fcrm_company_quick_stats"},re={class:"fcrm_company_quick_stat_text"},le={key:1,class:"fcrm_company_quick_stat"},de={class:"icon"},me={class:"fcrm_company_quick_stat_text"},pe=["href"],he={class:"fcrm_company_quick_panel fcrm_pb_0"},_e={class:"fcrm_company_quick_panel_head"},ue={key:0,class:"fcrm_company_quick_description"},ye={key:1,class:"fcrm_company_quick_empty"},fe={class:"fcrm_company_quick_panel fcrm_pb_0"},ge={class:"fcrm_company_quick_panel_head"},ve={key:0,class:"fcrm_company_quick_address"},ke={key:1,class:"fcrm_company_quick_empty"},be={class:"fcrm_company_quick_panel fcrm_pb_0"},we={class:"fcrm_company_quick_panel_head"},Ce={key:0,class:"fcrm_company_quick_socials"},qe=["href"],$e={key:0,class:"icon"},Se={key:1,class:"fcrm_company_quick_empty"},Fe={key:1,class:"fcrm_company_summary_edit_inline"},Ee={class:"fcrm_drawer_body"},xe={class:"fcrm_drawer_footer fcrm_drawer_footer_end"},Ae={class:"d-flex gap-12 items-center"},Ne={key:0,class:"fcrm_company_quick_custom"},Ie={class:"fcrm_view_cf_wrap"};const De=V(T,[["render",function(e,t,a,s,i,r){const l=y("CustomFieldsForm"),d=y("company-edit-form"),m=o,p=y("Icons"),h=y("User"),_=y("Briefcase"),u=y("PriceTag"),I=y("OfficeBuilding"),D=n,j=c;return f(),g("div",{class:N([{fcrm_company_in_drawer:a.is_drawer,fcrm_company_unsaved:i.isDirty||!a.company.id},"fcrm_company_info_wrapper"])},[!a.is_drawer||a.company.id&&!i.isHeaderEditing?(f(),g("div",Q,[v("div",W,[v("div",X,[v("div",Z,[i.isSummaryEditing?(f(),g("div",Fe,[v("div",Ee,[k(d,{model:i.model,company:a.company,onAvatarChanged:r.updateAvatar,class:"fcrm_pt_20"},{"custom-fields":b(()=>[k(l,{custom_values:i.model.custom_values},null,8,["custom_values"])]),_:1},8,["model","company","onAvatarChanged"])]),v("div",xe,[v("div",Ae,[k(m,{onClick:t[3]||(t[3]=e=>r.cancelSummaryEdit())},{default:b(()=>[w(C(e.$t("Cancel")),1)],void 0),_:1}),k(m,{onClick:t[4]||(t[4]=e=>r.saveSummaryEdit()),type:"primary",loading:i.updating},{default:b(()=>[w(C(e.$t("Update info")),1)],void 0),_:1},8,["loading"])])])])):(f(),g("div",G,[v("section",Y,[v("div",{class:"fcrm_company_quick_logo",style:S({backgroundImage:a.company.logo?"url("+a.company.logo+")":""})},[a.company.logo?E("",!0):(f(),g("svg",ee,[...t[6]||(t[6]=[F('',13)])]))],4),v("div",te,[v("h3",null,C(a.company.name),1),v("div",ae,[a.company.email?(f(),g("a",{key:0,href:"mailto:"+a.company.email},C(a.company.email),9,se)):E("",!0),a.company.phone?(f(),g("a",{key:1,href:"tel:"+a.company.phone},C(a.company.phone),9,ie)):E("",!0)])]),v("div",oe,[k(m,{size:"small",onClick:t[1]||(t[1]=e=>r.startSummaryEdit())},{default:b(()=>[v("span",ce,[k(p,{"icon-name":"EditPen"})]),w(" "+C(e.$t("Edit")),1)],void 0),_:1}),a.company.id?(f(),$(m,{key:0,size:"small",onClick:t[2]||(t[2]=t=>e.$router.push({name:"view_company",params:{company_id:a.company.id}}))},{default:b(()=>[w(C(e.$t("Open Record")),1)],void 0),_:1})):E("",!0)])]),a.company.website||r.quickStats.length?(f(),g("section",ne,[r.quickStats.length?(f(!0),g(x,{key:0},A(r.quickStats,e=>(f(),g("div",{key:e.label,class:"fcrm_company_quick_stat"},[k(D,{class:"icon"},{default:b(()=>["owner"===e.icon?(f(),$(h,{key:0})):"industry"===e.icon?(f(),$(_,{key:1})):"type"===e.icon?(f(),$(u,{key:2})):(f(),$(I,{key:3}))],void 0),_:2},1024),v("div",re,[v("span",null,C(e.label)+":",1),v("strong",null,C(e.value),1)])]))),128)):E("",!0),a.company.website?(f(),g("div",le,[v("span",de,[k(p,{"icon-name":"glob"})]),v("div",me,[v("span",null,C(e.$t("Website:")),1),v("strong",null,[a.company.website?(f(),g("a",{key:0,href:a.company.website,target:"_blank",rel:"noopener"},C(r.domainName||a.company.website),9,pe)):E("",!0)])])])):E("",!0)])):E("",!0),v("section",he,[v("div",_e,[v("h4",null,C(e.$t("About")),1)]),a.company.description?(f(),g("p",ue,C(a.company.description),1)):(f(),g("p",ye,C(e.$t("No description added")),1))]),v("section",fe,[v("div",ge,[v("h4",null,C(e.$t("Address")),1)]),r.profileAddressLines.length?(f(),g("div",ve,[(f(!0),g(x,null,A(r.profileAddressLines,(e,t)=>(f(),g("p",{key:t},C(e),1))),128))])):(f(),g("p",ke,C(e.$t("No address added")),1))]),v("section",be,[v("div",we,[v("h4",null,C(e.$t("Social Links")),1)]),r.socialLinks.length?(f(),g("div",Ce,[(f(!0),g(x,null,A(r.socialLinks,e=>(f(),g("a",{key:e.label,href:e.url,target:"_blank",rel:"noopener",class:"el-button"},[v("span",null,[e.icon?(f(),g("span",$e,["linkedin"===e.icon?(f(),$(p,{key:0,"icon-name":"linkedin"})):"x"===e.icon?(f(),$(p,{key:1,"icon-name":"x"})):(f(),$(p,{key:2,"icon-name":"facebook"}))])):E("",!0),w(" "+C(e.label),1)])],8,qe))),128))])):(f(),g("p",Se,C(e.$t("No social links added")),1))])]))])]),i.isSummaryEditing?E("",!0):(f(),g("section",Ne,[v("div",Ie,[k(l,{custom_values:i.model.custom_values},null,8,["custom_values"]),i.isDirty?q((f(),$(m,{key:0,disabled:i.updating,type:"primary",onClick:t[5]||(t[5]=e=>r.updateInfo())},{default:b(()=>[w(C(a.company.id?e.$t("Update info"):e.$t("Create Company")),1)],void 0),_:1},8,["disabled"])),[[j,i.updating]]):E("",!0)])]))])])):(f(),g("div",H,[v("div",J,[k(d,{mode:"create",model:i.model,company:a.company,onAvatarChanged:r.updateAvatar,class:"fcrm_pt_20"},{"custom-fields":b(()=>[k(l,{custom_values:i.model.custom_values},null,8,["custom_values"])]),_:1},8,["model","company","onAvatarChanged"])]),v("div",R,[v("div",K,[k(m,{size:"small",onClick:r.handleCancel},{default:b(()=>[w(C(e.$t("Cancel")),1)],void 0),_:1},8,["onClick"]),q((f(),$(m,{disabled:i.updating||!i.isDirty,type:"primary",size:"small",onClick:t[0]||(t[0]=e=>r.updateInfo())},{default:b(()=>[w(C(a.company.id?e.$t("Update info"):e.$t("Create Company")),1)],void 0),_:1},8,["disabled"])),[[j,i.updating]])])])]))],2)}]]),je="contacts_filters";function Le(e){var t,a;try{return JSON.stringify({query:e.query_data||{},advanced:e.advanced_filters||[[]],type:e.filter_type||"simple",page:(null==(t=e.pagination)?void 0:t.current_page)||1,per_page:(null==(a=e.pagination)?void 0:a.per_page)||10})}catch(s){return""}}const Ue=I("contacts",{state:()=>({subscribers:[],pagination:{current_page:1,per_page:10,total:0},query_data:{tags:[],lists:[],search:"",statuses:[],sms_statuses:[],sort_by:"id",sort_type:"DESC",custom_fields:!1,has_commerce:!1},advanced_filters:[[]],filter_type:"simple",loading:!1,first_loading:!0,cache_metadata:{last_fetch_time:null,cache_key_hash:null,is_stale:!1},options:{tags:[],lists:[],statuses:[],sms_statuses:[],contact_types:[],custom_fields:[],sampleCsv:null}}),getters:{isCacheValid(e){if(!e.cache_metadata.last_fetch_time||e.cache_metadata.is_stale)return!1;return Date.now()-e.cache_metadata.last_fetch_time<3e5},filterHash:e=>Le({query_data:e.query_data,advanced_filters:e.advanced_filters,filter_type:e.filter_type,pagination:e.pagination}),hasCachedDataForCurrentFilters(e){if(!e.cache_metadata.cache_key_hash)return!1;const t=Le({query_data:e.query_data,advanced_filters:e.advanced_filters,filter_type:e.filter_type,pagination:e.pagination});return e.cache_metadata.cache_key_hash===t},shouldShowCache(){return this.isCacheValid&&this.hasCachedDataForCurrentFilters&&this.subscribers.length>0}},actions:{async fetchContacts(e=!1,t=!1){var a,s;if(e||!this.shouldShowCache){t||(this.loading=!0);try{let e={per_page:this.pagination.per_page,page:this.pagination.current_page,filter_type:this.filter_type,custom_fields:this.query_data.custom_fields,has_commerce:this.query_data.has_commerce,sort_by:this.query_data.sort_by,sort_type:this.query_data.sort_type};const i=!!(null==(s=null==(a=window.fcAdmin)?void 0:a.addons)?void 0:s.fluentcampaign);i||(e.filter_type="simple"),"advanced"===this.filter_type&&i?e.advanced_filters=JSON.stringify(this.advanced_filters):e={...this.query_data,...e};const o=await B.get("subscribers",e);this.subscribers=o.subscribers.data,this.pagination.total=o.subscribers.total,this.cache_metadata.last_fetch_time=Date.now(),this.cache_metadata.cache_key_hash=this.filterHash,this.cache_metadata.is_stale=!1,this.persistFilters(),console.log("[ContactsStore] Fetched contacts from API"+(t?" (background)":""),{count:this.subscribers.length,total:this.pagination.total})}catch(i){throw console.error("[ContactsStore] Failed to fetch contacts",i),i}finally{t||(this.loading=!1),this.first_loading=!1}}else console.log("[ContactsStore] Using cached data")},restoreFilters(){var e,t;try{const a=z.get(je,null);return!!a&&(this.query_data=a.query_data||this.query_data,this.advanced_filters=a.advanced_filters||[[]],this.filter_type=a.filter_type||"simple",a.pagination&&(this.pagination.current_page=a.pagination.current_page||1,this.pagination.per_page=a.pagination.per_page||10),console.log("[ContactsStore] Filters restored from localStorage",{filter_type:this.filter_type,has_filters:(null==(e=this.query_data.tags)?void 0:e.length)>0||(null==(t=this.query_data.lists)?void 0:t.length)>0||this.query_data.search}),!0)}catch(a){return console.error("[ContactsStore] Failed to restore filters",a),!1}},persistFilters(){try{const e={query_data:this.query_data,advanced_filters:this.advanced_filters,filter_type:this.filter_type,pagination:{current_page:this.pagination.current_page,per_page:this.pagination.per_page}};z.set(je,e),console.log("[ContactsStore] Filters persisted to localStorage")}catch(e){console.error("[ContactsStore] Failed to persist filters",e)}},invalidateCache(){this.cache_metadata.is_stale=!0,this.cache_metadata.last_fetch_time=null,this.cache_metadata.cache_key_hash=null,console.log("[ContactsStore] In-memory cache invalidated")},updatePagination(e,t,a=!1){void 0!==e&&(this.pagination.current_page=e),void 0!==t&&(this.pagination.per_page=t),a||this.persistFilters()},updateQueryData(e){this.query_data={...this.query_data,...e},this.persistFilters()},updateAdvancedFilters(e){this.advanced_filters=e,this.persistFilters()},setFilterType(e){this.filter_type=e,this.persistFilters()},setOptions(e){this.options={...this.options,...e}},initializeFromUrlParams(e){var t,a;const s=e=>e?Array.isArray(e)?e:[e]:[],i=s(e.tags).map(e=>parseInt(e)),o=s(e.lists).map(e=>parseInt(e)),c=s(e.statuses),n=s(e.sms_statuses);this.query_data={tags:i,lists:o,search:e.search||"",statuses:c,sms_statuses:n,sort_by:e.sort_by||"id",sort_type:e.sort_type||"DESC",custom_fields:"true"===e.custom_fields||!0===e.custom_fields,has_commerce:"true"===e.has_commerce||!0===e.has_commerce},e.page&&(this.pagination.current_page=parseInt(e.page));const r=!!(null==(a=null==(t=window.fcAdmin)?void 0:t.addons)?void 0:a.fluentcampaign);if("advanced"===e.filter_type&&r){if(this.filter_type="advanced",e.advanced_filters&&"[object Object]"!==e.advanced_filters)try{"string"==typeof e.advanced_filters?this.advanced_filters=JSON.parse(e.advanced_filters):this.advanced_filters=e.advanced_filters}catch(l){console.error("[ContactsStore] Failed to parse advanced_filters",l),this.advanced_filters=[[]]}}else this.filter_type="simple"},clearFilters(){this.advanced_filters=[[]],this.query_data={tags:[],lists:[],search:"",statuses:[],sms_statuses:[],sort_by:"id",sort_type:"DESC",custom_fields:!1,has_commerce:!1},this.persistFilters()},async fetchProfile(e){try{return await B.get(`subscribers/${e}`,{with:["stats","custom_fields","subscriber.custom_values"]})}catch(t){throw console.error("[ContactsStore] Failed to fetch profile",t),t}}}}),Pe={name:"Editor",components:{Filterer:M,ArrowDown:l,Plus:r,Icons:O},emits:["search","subscribe","addedNew"],props:{type:{required:!0},options:{required:!0,type:Array},noMatch:{required:!0,type:Boolean},matched:{required:!0},selectionCount:{required:!0,type:Number},placement:{default:"bottom-start"},creatable:{default:!1}},watch:{matched(){this.init()}},data:()=>({query:null,selection:[],checkList:[],creating:!1}),methods:{init(){this.selection=[];for(const e in this.matched)this.selection.includes(e)||this.selection.push(e)},search(){this.$emit("search",this.query&&this.query.toLowerCase())},isIndeterminate(e){return this.matched[e.slug]&&this.matched[e.slug]!==this.selectionCount},save(e){const t=Object.keys(this.matched),a=e.filter(e=>!t.includes(e)),s=t.filter(t=>!e.includes(t));this.$emit("subscribe",{attach:a,detach:s})},createNewItem(){this.creating=!0,this.$post(this.type,{title:this.query}).then(e=>{this.$notify.success(e.message);const t=e.item;t.status=!0,this.$emit("addedNew",t),this.$nextTick(()=>{this.search()})}).catch(e=>{this.handleError(e)}).finally(()=>{this.creating=!1})}},mounted(){this.init()}},Ve={class:"fcrm_filter_editor"},Oe={class:"fc_no_match_search_tagger"};const Be=V(Pe,[["render",function(e,t,a,s,i,c){const n=y("icons"),r=o,l=m,_=d,u=h,q=p,S=y("filterer");return f(),g("div",Ve,[k(S,{placement:a.placement},{header:b(()=>[k(r,{plain:"",size:"small"},{default:b(()=>[k(n,{iconName:"Plus"})],void 0,!0),_:1})]),items:b(()=>[k(_,{class:"fluentcrm-filter-option no-hover fc-dropdown-search-item"},{default:b(()=>[k(l,{class:"fc_input",size:"small",modelValue:i.query,"onUpdate:modelValue":t[0]||(t[0]=e=>i.query=e),placeholder:e.$t("Search..."),onKeyup:c.search},null,8,["modelValue","placeholder","onKeyup"])],void 0,!0),_:1}),k(_,{class:"fc-dropdown-items-label fluentcrm-filter-option"},{default:b(()=>[w(C(e.$t("Choose an option:")),1)],void 0,!0),_:1}),k(q,{modelValue:i.selection,"onUpdate:modelValue":t[1]||(t[1]=e=>i.selection=e),onChange:c.save,class:"fluentcrm-filter-options fc_checkbox_group"},{default:b(()=>[(f(!0),g(x,null,A(a.options,t=>(f(),$(u,{key:t.id,value:t.slug,indeterminate:c.isIndeterminate(t),class:"el-dropdown-menu__item fc_checkbox"},{default:b(()=>[w(C(e.trans(t.title)),1)],void 0,!0),_:2},1032,["value","indeterminate"]))),128))],void 0,!0),_:1},8,["modelValue","onChange"]),a.noMatch?(f(),$(_,{key:0,class:"fluentcrm-filter-option"},{default:b(()=>[v("div",Oe,[v("p",null,C(e.$t("No items found")),1),a.creatable?(f(),$(r,{key:0,type:"primary",size:"small",onClick:c.createNewItem},{default:b(()=>[w(C(e.$t("Add new:"))+" - "+C(i.query),1)],void 0,!0),_:1},8,["onClick"])):E("",!0)])],void 0,!0),_:1})):E("",!0)]),footer:b(()=>[D(e.$slots,"footer")]),_:3},8,["placement"])])}]]),ze={data:()=>({noMatch:!1}),computed:{choices(){return this.options.filter(e=>!1!==e.status)}},methods:{search(e){let t=!0;const a=this.options.map(a=>(a.title.toLowerCase().includes(e)?(t=!1,a.status=!0):a.status=e&&!1,a));this.$emit("search",this.type,a),this.noMatch=!(!e||!t)},subscribe(e){this.$emit("subscribe",this.payload(e))},payload(e){return{type:this.type,payload:e}}}};export{De as C,Be as E,ze as T,Ue as u}; diff --git a/wp-content/plugins/fluent-crm/assets/TestEmail.js b/wp-content/plugins/fluent-crm/assets/TestEmail.js new file mode 100644 index 0000000..42327b7 --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/TestEmail.js @@ -0,0 +1 @@ +import{e,k as t,ay as s,aO as a}from"./vendor-element-plus.js?ver=3.1.8";import{aQ as i,W as n,Y as l,a5 as _,Z as m,aa as c,ab as d,a6 as o,a9 as p,a0 as r,X as f,a8 as h}from"./vendor.js?ver=3.1.8";import{_ as g,I as u}from"./fc-bits-ui.js?ver=3.1.8";const y=window.fcAdmin.auth.email,b={name:"SendTestEmail",components:{Icons:u},props:{campaign:{type:Object,default:()=>({})},btn_text:{type:String,default:()=>""},btn_type:{type:String,default:()=>"default"},placement:{type:String,default:()=>"top"},btn_class:{type:String,default:()=>""},showIcon:{type:Boolean,default:()=>!0}},data:()=>({sending_test:!1,test_email:y}),methods:{sendTestEmail(){if(!this.campaign.email_body)return this.$notify.error({title:this.$t("Oops!"),message:this.$t("Cam_Please_peb"),offset:19});window.last_fc_test_email=this.test_email,this.sending_test=!0,this.$post("campaigns/send-test-email",{campaign:{id:this.campaign.id,settings:this.campaign.settings,email_subject:this.campaign.email_subject,subjects:this.campaign.subjects||[],email_pre_header:this.campaign.email_pre_header,email_body:this.campaign.email_body,design_template:this.campaign.design_template},test_campaign:"yes",email:this.test_email}).then(e=>{this.$notify.success(e.message)}).catch(e=>{this.handleError(e)}).finally(()=>{this.sending_test=!1})}},mounted(){window.last_fc_test_email&&(this.test_email=window.last_fc_test_email)}},v={class:"fcrm_send_test_email_content"},w={class:"fcrm_send_test_email_content_header"},S={class:"fcrm_send_test_email_title"},$={class:"fcrm_send_test_email_description"},j={class:"fcrm_send_test_email_content_footer"},k={class:"fcrm_send_test_email_input_wrap"},E={class:"fcrm_input_hint"},I={class:"icon"},C={key:0,class:"icon"};const T=g(b,[["render",function(g,u,y,b,T,x){const A=e,O=t,V=i("Icons"),P=a,W=s;return n(),l(P,{placement:y.placement,width:"400","popper-class":"fcrm_send_test_email_popover",trigger:"click"},{reference:_(()=>[d(O,{type:y.btn_type,class:r(y.btn_class)},{default:_(()=>[y.showIcon?(n(),f("span",C,[d(V,{"icon-name":"paperPlane"})])):h("",!0),p(" "+c(y.btn_text||g.$t("Send a test email")),1)],void 0,!0),_:1},8,["type","class"])]),default:_(()=>[m("div",v,[m("div",w,[m("h3",S,c(g.$t("Send a test email")),1),m("p",$,c(g.$t("Cam_Type_cetstolbtsc")),1)]),m("div",j,[m("div",k,[d(A,{placeholder:g.$t("Email Address"),modelValue:T.test_email,"onUpdate:modelValue":u[0]||(u[0]=e=>T.test_email=e)},null,8,["placeholder","modelValue"]),o((n(),l(O,{disabled:T.sending_test,onClick:u[1]||(u[1]=e=>x.sendTestEmail()),type:"primary"},{default:_(()=>[p(c(g.$t("Send")),1)],void 0,!0),_:1},8,["disabled"])),[[W,T.sending_test]])]),m("p",E,[m("span",I,[d(V,{"icon-name":"el-icon-info"})]),p(" "+c(g.$t("Some_SmartCode_Not_Work_Alert")),1)])])])],void 0),_:1},8,["placement"])}]]);export{T as S}; diff --git a/wp-content/plugins/fluent-crm/assets/TopNav.js b/wp-content/plugins/fluent-crm/assets/TopNav.js new file mode 100644 index 0000000..e77b31f --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/TopNav.js @@ -0,0 +1 @@ +import{aQ as a,W as l,X as s,Z as t,ab as i,a5 as e,a9 as n,aa as o}from"./vendor.js?ver=3.1.8";import{_}from"./fc-bits-ui.js?ver=3.1.8";const r={class:"fcrm_page_header_top_nav_links"};const c=_({name:"TopNav"},[["render",function(_,c,m,u,p,v){const f=a("router-link");return l(),s("ul",r,[t("li",null,[i(f,{to:"/email/campaigns",class:"fcrm_top_nav_link"},{default:e(()=>[n(o(_.$t("Campaigns")),1)],void 0),_:1})]),t("li",null,[i(f,{to:"/email/recurring-campaigns",class:"fcrm_top_nav_link"},{default:e(()=>[n(o(_.$t("Recurring Campaigns")),1)],void 0),_:1})]),t("li",null,[i(f,{to:"/email/sequences",class:"fcrm_top_nav_link"},{default:e(()=>[n(o(_.$t("Sequences")),1)],void 0),_:1})]),t("li",null,[i(f,{to:"/email/templates",class:"fcrm_top_nav_link"},{default:e(()=>[n(o(_.$t("Templates")),1)],void 0),_:1})]),t("li",null,[i(f,{to:"/email/patterns",class:"fcrm_top_nav_link"},{default:e(()=>[n(o(_.$t("Patterns")),1)],void 0),_:1})]),t("li",null,[i(f,{to:"/email/all-emails",class:"fcrm_top_nav_link"},{default:e(()=>[n(o(_.$t("All Activities")),1)],void 0),_:1})])])}]]);export{c as T}; diff --git a/wp-content/plugins/fluent-crm/assets/_AjaxSelector.js b/wp-content/plugins/fluent-crm/assets/_AjaxSelector.js new file mode 100644 index 0000000..27d9832 --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/_AjaxSelector.js @@ -0,0 +1 @@ +import{ay as e,aL as i,aK as t}from"./vendor-element-plus.js?ver=3.1.8";import{a6 as l,W as o,Y as a,a5 as s,X as d,J as n,az as r}from"./vendor.js?ver=3.1.8";import{_ as h}from"./fc-bits-ui.js?ver=3.1.8";const m=h({name:"AjaxSelector",props:["field","modelValue"],emits:["change","update:modelValue"],data(){return{model:this.modelValue,loading:!1,options:[]}},watch:{model(e){this.$emit("update:modelValue",e),this.$emit("change",e)}},methods:{fetchOptions(e){let i=this.field.option_key;this.field.extended_key&&(i+="_"+this.field.extended_key);let t="";if(this.field.cacheable){if(t+="_fcrm_ajax_cache_"+i,window[t])return void(this.options=window[t])}else if(this.field.experimental_cache&&(t+="_fcrm_ajax_cache_"+i+"_"+e+" "+JSON.stringify(this.model),this.field.sub_option_key&&(t+="_"+JSON.stringify(this.field.sub_option_key)),window[t]))return void(this.options=window[t]);if(this.doing_ajax)return!1;this.loading=!0;const l={search:e,values:this.model,option_key:i};this.field.sub_option_key&&(l.sub_option_key=this.field.sub_option_key),this.$get("reports/ajax-options",l).then(e=>{this.options=e.options,t&&e.options&&(window[t]=e.options)}).catch(e=>{this.handleError(e)}).finally(()=>{this.loading=!1})}},mounted(){this.fetchOptions("")}},[["render",function(h,m,p,c,f,u){const _=i,b=t,y=e;return l((o(),a(b,{modelValue:f.model,"onUpdate:modelValue":m[0]||(m[0]=e=>f.model=e),multiple:p.field.is_multiple,filterable:"",remote:!p.field.cacheable,clearable:p.field.clearable,disabled:p.field.disabled,"reserve-keyword":"","allow-create":p.field.creatable,size:p.field.size,placeholder:p.field.placeholder||h.$t("Please enter a keyword"),"remote-method":u.fetchOptions},{default:s(()=>[(o(!0),d(n,null,r(f.options,e=>(o(),a(_,{key:e.id,label:e.title,value:e.id},null,8,["label","value"]))),128))],void 0),_:1},8,["modelValue","multiple","remote","clearable","disabled","allow-create","size","placeholder","remote-method"])),[[y,f.loading]])}]]);export{m as A}; diff --git a/wp-content/plugins/fluent-crm/assets/_CampaignDetails.js b/wp-content/plugins/fluent-crm/assets/_CampaignDetails.js new file mode 100644 index 0000000..fb3d9d5 --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/_CampaignDetails.js @@ -0,0 +1 @@ +import{ay as e,aI as a,aH as i,ba as s,av as t}from"./vendor-element-plus.js?ver=3.1.8";import{aQ as c,a6 as n,W as l,X as m,ab as r,a5 as _,Z as d,aa as p,a9 as o,Y as g,a8 as v,a0 as u,J as w}from"./vendor.js?ver=3.1.8";import{P as f}from"./PaginationBar.js?ver=3.1.8";import{_ as b,I as h}from"./fc-bits-ui.js?ver=3.1.8";import{R as $}from"./ReadableRecipientTagger.js?ver=3.1.8";import{P as M}from"./PreviewIframeBuilder.js?ver=3.1.8";import{C as k}from"./CampaignSubjectLines.js?ver=3.1.8";const y={class:"fc_unsubscribers_table"},C={key:0},P=["title","src"],S=["title"];const T=b({name:"Unsbscribers",props:["campaign_id"],components:{PaginationBar:f},data:()=>({loading:!0,unsubscribes:[],pagination:{total:0,per_page:20,current_page:1}}),methods:{fetch(){this.loading=!0,this.$get(`campaigns/${this.campaign_id}/unsubscribers`,{page:this.pagination.current_page,per_page:this.pagination.per_page}).then(e=>{this.unsubscribes=e.unsubscribes.data,this.pagination.total=e.unsubscribes.total}).catch(e=>{this.handleError(e)}).finally(e=>{this.loading=!1})}},mounted(){this.fetch()}},[["render",function(t,v,u,w,f,b){const h=a,$=c("router-link"),M=i,k=c("pagination-bar"),T=s,j=e;return n((l(),m("div",y,[f.pagination.total||f.loading?(l(),m("div",C,[r(M,{stripe:"",data:f.unsubscribes,border:""},{default:_(()=>[r(h,{label:t.$t("Name"),width:"250"},{default:_(e=>[d("img",{style:{display:"inline-block","margin-bottom":"-6px"},title:"Contact ID: "+e.row.subscriber.id,class:"fc_contact_photo",src:e.row.subscriber.photo},null,8,P),d("span",null,p(e.row.subscriber.full_name),1)]),_:1},8,["label"]),r(h,{label:t.$t("Email")},{default:_(e=>[r($,{to:{name:"subscriber",params:{id:e.row.subscriber_id}}},{default:_(()=>[o(p(e.row.subscriber.email),1)],void 0,!0),_:2},1032,["to"])]),_:1},8,["label"]),r(h,{label:t.$t("Reason")},{default:_(e=>[o(p(e.row.subscriber.reason),1)]),_:1},8,["label"]),r(h,{label:t.$t("Date")},{default:_(e=>[d("span",{title:e.row.created_at},p(t.nsHumanDiffTime(e.row.created_at)),9,S)]),_:1},8,["label"])],void 0),_:1},8,["data"]),r(k,{pagination:f.pagination,onFetch:b.fetch},null,8,["pagination","onFetch"])])):(l(),g(T,{key:1,"image-size":135,description:t.$t("Unsubscribes.instruction")},null,8,["description"]))])),[[j,f.loading]])}]]),j={class:"fcrm_max_w_800 fcrm_campaign_view--processing-status"},E={key:0,class:"fcrm_primary_alert"},D={key:1,class:"fcrm_sms_campaign_view--processing-status-progress"},I={class:"fcrm_sms_campaign_view--progress-card"},U={class:"fcrm_sms_campaign_view--progress-header"},x={class:"fcrm_sms_campaign_view--progress-title"},B={class:"fcrm_sms_campaign_view--progress-percent"},R={key:0,class:"fcrm_sms_campaign_view--scheduling-note"},A={class:"fcrm_sms_campaign_view--processing-status-details"},F={class:"fcrm_sms_campaign_view--details-grid"},H={class:"fcrm_sms_campaign_view--detail-row"},N={class:"fcrm_sms_campaign_view--detail-label"},Y={class:"fcrm_sms_campaign_view--detail-value"},L={class:"fcrm_sms_campaign_view--detail-row"},z={class:"fcrm_sms_campaign_view--detail-label"},J={class:"fcrm_sms_campaign_view--detail-value"},Q={class:"fcrm_sms_campaign_view--detail-row"},V={class:"fcrm_sms_campaign_view--detail-label"},W={class:"fcrm_sms_campaign_view--detail-value"},X={key:0,class:"fcrm_sms_campaign_view--details-grid fcrm_sms_campaign_view--details-grid--1"},Z={class:"fcrm_sms_campaign_view--details-grid fcrm_sms_campaign_view--details-grid--1"},q={class:"fcrm_sms_campaign_view--detail-row"},G={class:"fcrm_sms_campaign_view--detail-label"},K={class:"fcrm_sms_campaign_view--detail-value"},O={class:"fcrm_sms_campaign_view--detail-row"},ee={class:"fcrm_sms_campaign_view--detail-label"},ae={class:"fcrm_sms_campaign_view--detail-value"},ie={class:"fcrm_email_campaign_view--preview-email"},se={class:"fcrm_email_campaign_view--preview-email-title"},te={class:"fcrm_preview_toolbar"},ce={class:"fcrm_preview_device_toggle"},ne={class:"fcrm_device_btn_group"},le=["title"],me=["title"],re=["title"],_e={key:0,class:"fc_device_notch"},de={key:1,class:"fc_device_home"};const pe=b({name:"CampaignEmailProcessStat",props:["campaign"],emits:["unscheduled"],components:{CampaignSubjectLines:k,ReadableRecipients:$,PreviewIframeBuilder:M,Icons:h},data:()=>({contact_count:null,loading:!1,loading_processing_stat:!1,completed:0,processing_counter:1,show_selections:!1,scheduling_method:"",previewMode:"desktop"}),computed:{processing_percent(){return"processing"==this.campaign.status&&this.completed&&this.contact_count?parseInt(this.completed/this.contact_count*100):0},canCancelEmail(){const e=this.campaign.status;return"scheduled"==e||"pending-scheduled"==e||"processing"}},methods:{fetchCount(){this.$get(`campaigns/${this.campaign.id}/estimated-recipients-count`).then(e=>{this.contact_count=e.estimated_count})},getHumanStatusName(e){return"pending-scheduled"==e?this.$t("Scheduled"):this.ucFirst(e)},cancelSchedule(){this.loading=!0,this.$post(`campaigns/${this.campaign.id}/un-schedule`).then(e=>{this.$notify.success(e.message),this.$emit("unscheduled")}).catch(e=>{this.handleError(e)}).finally(()=>{this.loading=!1})},fetchProcessingStat(){this.loading_processing_stat=!0,this.$get(`campaigns/${this.campaign.id}/processing-stat`,{counter:this.processing_counter}).then(e=>{e.campaign||window.location.reload(!0),this.campaign.status!=e.campaign.status&&window.location.reload(!0),this.loading_processing_stat=!1,this.completed=e.campaign.recipients_count,this.campaign.status=e.campaign.status,this.scheduling_method=e.scheduling_method,this.campaign.scheduling_range=e.campaign.scheduling_range,"processing"==e.campaign.status&&this.fetchStatAgain()}).catch(e=>{this.handleError(e),this.loading_processing_stat=!1}).finally(()=>{})},fetchStatAgain(){setTimeout(()=>{this.processing_counter+=1,this.fetchProcessingStat()},3e3)}},mounted(){this.fetchCount(),this.fetchProcessingStat()}},[["render",function(a,i,s,_,g,w){const f=t,b=c("readable-recipients"),h=c("campaign-subject-lines"),$=c("Icons"),M=c("preview-iframe-builder"),k=e;return l(),m("div",j,[w.canCancelEmail&&"scheduled"==g.scheduling_method?(l(),m("div",E,[d("p",null,p(a.$t("Email_Schedule_Info")),1)])):v("",!0),"processing"==s.campaign.status?(l(),m("div",D,[d("div",I,[d("div",U,[d("span",x,p(a.$t("Emails are currently on processing")),1),n((l(),m("span",B,[o(p(w.processing_percent)+"% ("+p(g.completed)+" / "+p(g.contact_count)+") ",1)])),[[k,g.loading_processing_stat]])]),r(f,{"stroke-width":8,color:"#8F6ED6","show-text":!1,percentage:w.processing_percent,class:"fcrm_sms_campaign_view--progress-bar"},null,8,["percentage"]),s.campaign.scheduling_range?(l(),m("p",R,[i[4]||(i[4]=o("The emails will be scheduled randomly between the date-time of ",-1)),d("b",null,p(s.campaign.scheduling_range.start),1),i[5]||(i[5]=o(" and ",-1)),d("b",null,p(s.campaign.scheduling_range.end),1)])):v("",!0)])])):v("",!0),d("div",A,[d("div",F,[d("div",H,[d("div",N,p(a.$t("Campaign Status:")),1),d("div",Y,p(w.getHumanStatusName(s.campaign.status)),1)]),d("div",L,[d("div",z,p(a.$t("Scheduled on:")),1),d("div",J,p(s.campaign.scheduled_at)+" ("+p(a.nsHumanDiffTime(s.campaign.scheduled_at))+")",1)]),d("div",Q,[d("div",V,p(a.$t("Estimated Contacts:")),1),d("div",W,[o(p(g.contact_count)+" ",1),d("span",{class:"cursor_pointer is-link",onClick:i[0]||(i[0]=e=>g.show_selections=!g.show_selections)},p(a.$t("View")),1)])])]),g.show_selections?(l(),m("div",X,[r(b,{settings:s.campaign.settings},null,8,["settings"])])):v("",!0),i[6]||(i[6]=d("div",{class:"fcrm_sms_campaign_view--divider"},null,-1)),d("div",Z,[d("div",q,[d("div",G,p(a.$t("Subject:")),1),d("div",K,[r(h,{campaign:s.campaign},null,8,["campaign"])])]),d("div",O,[d("div",ee,p(a.$t("Preview Text:")),1),d("div",ae,p(s.campaign.email_pre_header||a.$t("No Preview Text")),1)])]),i[7]||(i[7]=d("div",{class:"fcrm_sms_campaign_view--divider"},null,-1)),d("div",ie,[d("div",se,p(a.$t("Email Body:")),1),d("div",te,[d("div",ce,[d("div",ne,[d("button",{type:"button",class:u(["fcrm_device_btn",{active:"desktop"===g.previewMode}]),onClick:i[1]||(i[1]=e=>g.previewMode="desktop"),title:a.$t("Desktop Preview")},[r($,{"icon-name":"desktop"})],10,le),d("button",{type:"button",class:u(["fcrm_device_btn",{active:"tablet"===g.previewMode}]),onClick:i[2]||(i[2]=e=>g.previewMode="tablet"),title:a.$t("Tablet Preview")},[r($,{"icon-name":"tablet"})],10,me),d("button",{type:"button",class:u(["fcrm_device_btn",{active:"mobile"===g.previewMode}]),onClick:i[3]||(i[3]=e=>g.previewMode="mobile"),title:a.$t("Mobile Preview")},[r($,{"icon-name":"mobile"})],10,re)])])]),d("div",{class:u(["fcrm_preview_stage","fcrm_preview_stage_"+g.previewMode])},[d("div",{class:u(["fc_device_frame","fc_device_frame_"+g.previewMode])},["mobile"===g.previewMode?(l(),m("div",_e)):v("",!0),r(M,{frame_height:"500px",campaign_id:s.campaign.id},null,8,["campaign_id"]),"mobile"===g.previewMode?(l(),m("div",de)):v("",!0)],2)],2)])])])}]]),oe={class:"fcrm_sms_campaign_view--tab-content"},ge={class:"fcrm_sms_campaign_view--section-title"},ve={class:"fcrm_sms_campaign_view--details-grid"},ue={class:"fcrm_sms_campaign_view--detail-row"},we={class:"fcrm_sms_campaign_view--detail-label"},fe={class:"fcrm_sms_campaign_view--detail-value"},be={class:"fcrm_sms_campaign_view--detail-row"},he={class:"fcrm_sms_campaign_view--detail-label"},$e={class:"fcrm_sms_campaign_view--detail-value"},Me={class:"fcrm_sms_campaign_view--detail-row"},ke={class:"fcrm_sms_campaign_view--detail-label"},ye={class:"fcrm_sms_campaign_view--detail-value"},Ce={key:0,class:"fcrm_sms_campaign_view--detail-row"},Pe={class:"fcrm_sms_campaign_view--detail-label"},Se={class:"fcrm_sms_campaign_view--detail-value"},Te={class:"fcrm_sms_campaign_view--section-title"},je={class:"fcrm_sms_campaign_view--details-grid"},Ee={class:"fcrm_sms_campaign_view--detail-row"},De={class:"fcrm_sms_campaign_view--detail-label"},Ie={class:"fcrm_sms_campaign_view--detail-value"},Ue={class:"fcrm_sms_campaign_view--detail-row"},xe={class:"fcrm_sms_campaign_view--detail-label"},Be={class:"fcrm_sms_campaign_view--detail-value"},Re={class:"fcrm_sms_campaign_view--detail-row"},Ae={class:"fcrm_sms_campaign_view--detail-label"},Fe={class:"fcrm_sms_campaign_view--detail-value"},He={class:"fcrm_sms_campaign_view--detail-row"},Ne={class:"fcrm_sms_campaign_view--detail-label"},Ye={class:"fcrm_sms_campaign_view--detail-value"},Le={class:"fcrm_sms_campaign_view--detail-row"},ze={class:"fcrm_sms_campaign_view--detail-label"},Je={class:"fcrm_sms_campaign_view--detail-value"},Qe={class:"fcrm_sms_campaign_view--section-title"},Ve={class:"fcrm_sms_campaign_view--sms-preview-card"},We={class:"fcrm_preview_toolbar"},Xe={class:"fcrm_preview_device_toggle"},Ze={class:"fcrm_device_btn_group"},qe=["aria-label","aria-pressed","title"],Ge=["aria-label","aria-pressed","title"],Ke=["aria-label","aria-pressed","title"],Oe={key:0,class:"fc_device_notch"},ea={key:1,class:"fc_device_home"};const aa=b({name:"CampaignDetails",components:{CampaignSubjectLines:k,PreviewIframeBuilder:M,Icons:h},props:["campaign"],data:()=>({previewMode:"desktop"}),methods:{scheduledAt(e){return null===e?this.$t("Not Scheduled"):this.nsDateFormat(e,"MMMM Do, YYYY [at] h:mm A")}}},[["render",function(e,a,i,s,t,n){const _=c("campaign-subject-lines"),o=c("Icons"),g=c("preview-iframe-builder");return l(),m("div",oe,[d("h3",ge,p(e.$t("Campaign Details")),1),d("div",ve,[d("div",ue,[d("span",we,p(e.$t("Subject")),1),d("span",fe,[r(_,{campaign:i.campaign},null,8,["campaign"])])]),d("div",be,[d("span",he,p(e.$t("Total Recipients")),1),d("span",$e,p(i.campaign.recipients_count),1)]),d("div",Me,[d("span",ke,p(e.$t("Scheduled on")),1),d("span",ye,p(n.scheduledAt(i.campaign.scheduled_at)),1)]),i.campaign.sent_by?(l(),m("div",Ce,[d("span",Pe,p(e.$t("Sent By")),1),d("span",Se,p(i.campaign.sent_by),1)])):v("",!0)]),a[4]||(a[4]=d("div",{class:"fcrm_sms_campaign_view--divider"},null,-1)),i.campaign.utm_status&&"1"==i.campaign.utm_status?(l(),m(w,{key:0},[d("h3",Te,p(e.$t("UTM Parameters")),1),d("div",je,[d("div",Ee,[d("span",De,p(e.$t("UTM Source")),1),d("span",Ie,p(i.campaign.utm_source),1)]),d("div",Ue,[d("span",xe,p(e.$t("UTM Medium")),1),d("span",Be,p(i.campaign.utm_medium),1)]),d("div",Re,[d("span",Ae,p(e.$t("UTM Campaign")),1),d("span",Fe,p(i.campaign.utm_campaign),1)]),d("div",He,[d("span",Ne,p(e.$t("UTM Term")),1),d("span",Ye,p(i.campaign.utm_term),1)]),d("div",Le,[d("span",ze,p(e.$t("UTM Content")),1),d("span",Je,p(i.campaign.utm_content),1)])]),a[3]||(a[3]=d("div",{class:"fcrm_sms_campaign_view--divider"},null,-1))],64)):v("",!0),d("h3",Qe,p(e.$t("Email Preview")),1),d("div",Ve,[d("div",We,[d("div",Xe,[d("div",Ze,[d("button",{type:"button",class:u(["fcrm_device_btn",{active:"desktop"===t.previewMode}]),onClick:a[0]||(a[0]=e=>t.previewMode="desktop"),"aria-label":e.$t("Desktop Preview"),"aria-pressed":"desktop"===t.previewMode,title:e.$t("Desktop Preview")},[r(o,{"icon-name":"desktop"})],10,qe),d("button",{type:"button",class:u(["fcrm_device_btn",{active:"tablet"===t.previewMode}]),onClick:a[1]||(a[1]=e=>t.previewMode="tablet"),"aria-label":e.$t("Tablet Preview"),"aria-pressed":"tablet"===t.previewMode,title:e.$t("Tablet Preview")},[r(o,{"icon-name":"tablet"})],10,Ge),d("button",{type:"button",class:u(["fcrm_device_btn",{active:"mobile"===t.previewMode}]),onClick:a[2]||(a[2]=e=>t.previewMode="mobile"),"aria-label":e.$t("Mobile Preview"),"aria-pressed":"mobile"===t.previewMode,title:e.$t("Mobile Preview")},[r(o,{"icon-name":"mobile"})],10,Ke)])])]),d("div",{class:u(["fcrm_preview_stage","fcrm_preview_stage_"+t.previewMode])},[d("div",{class:u(["fc_device_frame","fc_device_frame_"+t.previewMode])},["mobile"===t.previewMode?(l(),m("div",Oe)):v("",!0),r(g,{frame_height:"600px",campaign_id:i.campaign.id,show_audit:!1},null,8,["campaign_id"]),"mobile"===t.previewMode?(l(),m("div",ea)):v("",!0)],2)],2)])])}]]);export{aa as C,T as U,pe as a}; diff --git a/wp-content/plugins/fluent-crm/assets/_ContactsTable.js b/wp-content/plugins/fluent-crm/assets/_ContactsTable.js new file mode 100644 index 0000000..5a90d16 --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/_ContactsTable.js @@ -0,0 +1 @@ +import{ay as e,aL as t,aK as l,k as a,aT as s,aD as o,aA as i,aw as n,e as r,aY as d,az as c,ax as m,aC as u,aB as p,i as h,aG as _,E as f,aQ as b,B as y,at as g,aE as v,aF as w,ar as k,aO as C,U as x,aj as $,av as V,g as S,aI as A,aH as F,b1 as P,b2 as T,a4 as R,a6 as D,L as O,ac as B,aJ as M}from"./vendor-element-plus.js?ver=3.1.8";import{W as j,X as L,aa as I,a8 as z,aQ as U,Z as q,a6 as Y,Y as E,a5 as W,J as N,az as H,ab as J,a0 as G,bX as K,bY as Z,bZ as Q,ac as X,bW as ee,a9 as te,_ as le,b_ as ae,ay as se,b2 as oe,ax as ie}from"./vendor.js?ver=3.1.8";import{C as ne,T as re,E as de,u as ce}from"./Tagger.js?ver=3.1.8";import{F as me}from"./Filterer.js?ver=3.1.8";import{_ as ue,I as pe,a as he,T as _e}from"./fc-bits-ui.js?ver=3.1.8";import{F as fe}from"./Filterer2.js?ver=3.1.8";import{s as be}from"./data_config.js?ver=3.1.8";import{S as ye}from"./Searcher.js?ver=3.1.8";import{P as ge}from"./PaginationBar.js?ver=3.1.8";import{D as ve}from"./DataTable.js?ver=3.1.8";import{F as we}from"./FloatingBulkActionShell.js?ver=3.1.8";import{C as ke}from"./Confirm.js?ver=3.1.8";import{O as Ce}from"./_OptionSelector.js?ver=3.1.8";import xe from"./v3app/src/Modules/Contacts/Filter/FilterPopover.js?ver=3.1.8";import $e from"./v3app/src/Modules/Contacts/Filter/ActiveFiltersBar.js?ver=3.1.8";import{_ as Ve,E as Se}from"./fc-bits.js?ver=3.1.8";import{I as Ae}from"./_IntlTelInput.js?ver=3.1.8";import{B as Fe}from"./Badge.js?ver=3.1.8";import{g as Pe,a as Te}from"./relations.js?ver=3.1.8";import{c as Re}from"./clipboard.js?ver=3.1.8";const De={key:0,class:"el-form-item__error"};const Oe=ue({name:"Error",props:["error"]},[["render",function(e,t,l,a,s,o){return l.error?(j(),L("span",De,I(l.error),1)):z("",!0)}]]),Be={class:"w-full"},Me={class:"icon"},je={key:0,class:"fcrm_company_drawer_content"};const Le=ue({name:"CompanySelector",components:{Icons:pe,CompanyInfoSideContact:ne},props:["modelValue","field"],emits:["update:modelValue"],data(){return{model:this.modelValue,results:[],loading:!1,doing_ajax:!1,companyCreateDrawer:!1,new_company:{},direction:"rtl"}},computed:{canCreateCompany(){return!(!this.field.creatable||this.field.disabled||!this.hasPermission("fcrm_manage_contact_cats"))}},watch:{model(e){this.$emit("update:modelValue",e)},modelValue(e){this.model=e}},mounted(){this.fetchOptions(""),window.fcAdmin&&window.fcAdmin.is_rtl&&(this.direction="ltr")},methods:{fetchOptions(e){if(this.hasPermission("fcrm_manage_contact_cats"))return!this.doing_ajax&&void(window.fc_all_company_cache?this.results=window.fc_all_company_cache:(this.loading=!0,this.doing_ajax=!0,this.$get("companies/search",{search:e,values:this.model}).then(e=>{this.results=e.results,e.has_more||(window.fc_all_company_cache=e.results,this.field.cacheable=!0)}).catch(e=>{this.handleError(e)}).finally(()=>{this.loading=!1,this.doing_ajax=!1})))},addCreatedCompany(e){if(!e||!e.id)return;this.results.some(t=>Number(t.id)===Number(e.id))||(this.results=[{id:e.id,name:e.name,email:e.email,logo:e.logo,phone:e.phone,website:e.website},...this.results]),this.model=e.id},showCompanyCreateDrawer(){this.canCreateCompany&&(this.new_company={},this.companyCreateDrawer=!0)},closeCompanyCreateDrawer(){this.companyCreateDrawer=!1,this.new_company={}},handleCompanyCreated(e){this.companyCreateDrawer=!1,this.new_company={},e&&e.id&&(window.fc_all_company_cache&&(window.fc_all_company_cache=null),this.addCreatedCompany(e))}}},[["render",function(o,i,n,r,d,c){const m=t,u=l,p=U("Icons"),h=a,_=U("company-info-side-contact"),f=s,b=e;return j(),L("div",Be,[q("div",{class:G(["fcrm_options_selector",n.field.creatable?"fcrm_option_creatable":""])},[Y((j(),E(u,{modelValue:d.model,"onUpdate:modelValue":i[0]||(i[0]=e=>d.model=e),class:"fcrm_options",multiple:n.field.is_multiple,filterable:"",remote:!n.field.cacheable,clearable:n.field.clearable,disabled:n.field.disabled,"reserve-keyword":"",size:n.field.size,placeholder:n.field.placeholder||o.$t("Please enter a keyword"),"remote-method":c.fetchOptions},{default:W(()=>[(j(!0),L(N,null,H(d.results,e=>(j(),E(m,{key:e.id,label:e.name,value:e.id},null,8,["label","value"]))),128))],void 0),_:1},8,["modelValue","multiple","remote","clearable","disabled","size","placeholder","remote-method"])),[[b,d.loading]]),c.canCreateCompany?(j(),E(h,{key:0,class:"fcrm_with_select",type:"info","aria-label":o.$t("Create Company"),title:o.$t("Create Company"),onClick:c.showCompanyCreateDrawer},{default:W(()=>[q("span",Me,[J(p,{"icon-name":"plus"})])],void 0),_:1},8,["aria-label","title","onClick"])):z("",!0)],2),c.canCreateCompany?(j(),E(f,{key:0,class:"fcrm_company_info_drawer",direction:d.direction,"with-header":!0,size:o.globalDrawerSize,"append-to-body":!0,title:o.$t("Create Company"),modelValue:d.companyCreateDrawer,"onUpdate:modelValue":i[1]||(i[1]=e=>d.companyCreateDrawer=e)},{default:W(()=>[d.companyCreateDrawer?(j(),L("div",je,[J(_,{company:d.new_company,onCompanyCreated:c.handleCompanyCreated,onCancel:c.closeCompanyCreateDrawer},null,8,["company","onCompanyCreated","onCancel"])])):z("",!0)],void 0),_:1},8,["direction","size","title","modelValue"])):z("",!0)])}]]),Ie={key:0,class:"fcrm_date_parts_picker"},ze={class:"fcrm_date_picker_month"};const Ue=ue({name:"DateDropDownPicker",props:["modelValue"],emits:["update:modelValue"],data(){return{appReady:!1,dateParts:{day:"",month:"",year:""},months:[{label:this.$t("January"),value:"01"},{label:this.$t("February"),value:"02"},{label:this.$t("March"),value:"03"},{label:this.$t("April"),value:"04"},{label:this.$t("May"),value:"05"},{label:this.$t("June"),value:"06"},{label:this.$t("July"),value:"07"},{label:this.$t("August"),value:"08"},{label:this.$t("September"),value:"09"},{label:this.$t("October"),value:"10"},{label:this.$t("November"),value:"11"},{label:this.$t("December"),value:"12"}],days:[],years:[]}},watch:{dateParts:{handler(){this.appReady&&this.pushDate()},deep:!0}},methods:{initDateParts(){if(this.modelValue){const e=window.dayjs?window.dayjs(this.modelValue):this.$dayjs(this.modelValue);this.dateParts.day=e.format("DD"),this.dateParts.month=e.format("MM"),this.dateParts.year=e.format("YYYY")}this.$nextTick(()=>{this.appReady=!0})},range:K,pushDate(){const{day:e,month:t,year:l}=this.dateParts;if(e&&t&&l){const a=(window.dayjs||this.$dayjs)(`${l}-${t}-${e}`);if(!a.isValid())return void this.$emit("update:modelValue","");this.$emit("update:modelValue",a.format("YYYY-MM-DD"))}else this.$emit("update:modelValue","")}},mounted(){this.days=Z(K(1,32),e=>Q(e,2,"0")),this.years=K((new Date).getFullYear(),1899),this.initDateParts()}},[["render",function(e,a,s,o,i,n){const r=t,d=l;return i.appReady?(j(),L("div",Ie,[q("div",null,[J(d,{clearable:"",modelValue:i.dateParts.day,"onUpdate:modelValue":a[0]||(a[0]=e=>i.dateParts.day=e),placeholder:e.$t("Day")},{default:W(()=>[(j(!0),L(N,null,H(i.days,e=>(j(),E(r,{key:e,label:e,value:e},null,8,["label","value"]))),128))],void 0),_:1},8,["modelValue","placeholder"]),Y(q("p",null,I(e.$t("Day")),513),[[X,i.dateParts.day]])]),q("div",ze,[J(d,{clearable:"",modelValue:i.dateParts.month,"onUpdate:modelValue":a[1]||(a[1]=e=>i.dateParts.month=e),placeholder:e.$t("Month")},{default:W(()=>[(j(!0),L(N,null,H(i.months,e=>(j(),E(r,{key:e.value,label:e.label,value:e.value},null,8,["label","value"]))),128))],void 0),_:1},8,["modelValue","placeholder"]),Y(q("p",null,I(e.$t("Month")),513),[[X,i.dateParts.month]])]),q("div",null,[J(d,{clearable:"",filterable:"",modelValue:i.dateParts.year,"onUpdate:modelValue":a[2]||(a[2]=e=>i.dateParts.year=e),placeholder:e.$t("Year")},{default:W(()=>[(j(!0),L(N,null,H(i.years,e=>(j(),E(r,{key:e,label:e,value:e},null,8,["label","value"]))),128))],void 0),_:1},8,["modelValue","placeholder"]),Y(q("p",null,I(e.$t("Year")),513),[[X,i.dateParts.year]])])])):z("",!0)}]]),qe=ee(()=>Ve(()=>import("./v3app/src/Modules/Profile/Parts/_CustomFields.js?ver=3.1.8"),[],import.meta.url)),Ye={key:0,class:"fcrm_address_block"},Ee=["innerHTML"],We=["innerHTML"],Ne={class:"fcrm_identifier_section"},He={class:"fcrm_section_heading"},Je={class:"fcrm_identifier_content"},Ge={class:"fcrm_identifier_row"},Ke={key:0,class:"fcrm_identifier_field"},Ze={key:1,class:"fcrm_identifier_field"},Qe={class:"fcrm_identifier_field_full"};const Xe=ue({name:"Form",components:{Error:Oe,CustomFields:qe,CompanySelector:Le,OptionSelector:Ce,DateDropDownPicker:Ue,IntlTelInput:Ae},props:{subscriber:{required:!0,type:Object},errors:{required:!0,type:Object},listId:{default:null},tagId:{default:null},company_id:{default:null}},data(){return{show_address:!1,show_custom_data:!1,countries:window.fcAdmin.countries,name_prefixes:window.fcAdmin.contact_prefixes,phoneValue:"",phoneIsValid:!0,options:{statuses:this.appVars.available_contact_statuses,contact_types:this.appVars.available_contact_types,custom_fields:this.appVars.available_custom_fields}}},watch:{"subscriber.phone":{handler(e){this.phoneValue=e||""},immediate:!0},phoneValue(e){this.subscriber.phone=e||""}},methods:{disabledDate:e=>e.getTime()>=Date.now(),onPhoneValidate(e){this.phoneIsValid=!e||!!e.valid,e&&e.number?this.subscriber.phone=e.number:this.subscriber.phone=this.phoneValue||""},onPhoneBlur(){this.subscriber.phone||(this.subscriber.phone=this.phoneValue||"")}}},[["render",function(e,a,s,u,p,h){const _=t,f=l,b=n,y=i,g=o,v=r,w=U("error"),k=U("IntlTelInput"),C=d,x=U("company-selector"),$=c,V=U("custom-fields"),S=U("option-selector"),A=m;return j(),E(A,{class:"fcrm_add_contact_form","label-position":"top","label-width":"100px"},{default:W(()=>[J(g,{gutter:20},{default:W(()=>[J(y,{md:8,sm:24,xs:24},{default:W(()=>[J(b,{class:"fcrm_prefix_select",label:e.$t("Prefix")},{default:W(()=>[J(f,{modelValue:s.subscriber.prefix,"onUpdate:modelValue":a[0]||(a[0]=e=>s.subscriber.prefix=e),placeholder:e.$t("Select Prefix"),clearable:""},{default:W(()=>[(j(!0),L(N,null,H(p.name_prefixes,e=>(j(),E(_,{key:e,label:e,value:e},null,8,["label","value"]))),128))],void 0,!0),_:1},8,["modelValue","placeholder"])],void 0,!0),_:1},8,["label"])],void 0,!0),_:1})],void 0,!0),_:1}),J(g,{gutter:20},{default:W(()=>[J(y,{md:12,sm:24,xs:24},{default:W(()=>[J(b,{label:e.$t("First Name")},{default:W(()=>[J(v,{modelValue:s.subscriber.first_name,"onUpdate:modelValue":a[1]||(a[1]=e=>s.subscriber.first_name=e),placeholder:e.$t("e.g. John"),clearable:"",autocomplete:"off"},null,8,["modelValue","placeholder"]),J(w,{error:s.errors.get("first_name")},null,8,["error"])],void 0,!0),_:1},8,["label"])],void 0,!0),_:1}),J(y,{md:12,sm:24,xs:24},{default:W(()=>[J(b,{label:e.$t("Last Name")},{default:W(()=>[J(v,{modelValue:s.subscriber.last_name,"onUpdate:modelValue":a[2]||(a[2]=e=>s.subscriber.last_name=e),placeholder:e.$t("e.g. Doe"),clearable:"",autocomplete:"off"},null,8,["modelValue","placeholder"]),J(w,{error:s.errors.get("last_name")},null,8,["error"])],void 0,!0),_:1},8,["label"])],void 0,!0),_:1})],void 0,!0),_:1}),J(g,{gutter:20},{default:W(()=>[J(y,{md:12,sm:24,xs:24},{default:W(()=>[J(b,{label:e.$t("Email"),class:"fcrm_is-required"},{default:W(()=>[J(v,{modelValue:s.subscriber.email,"onUpdate:modelValue":a[3]||(a[3]=e=>s.subscriber.email=e),placeholder:e.$t("you@example.com"),clearable:"",autocomplete:"off",type:"email"},null,8,["modelValue","placeholder"]),J(w,{error:s.errors.get("email")},null,8,["error"])],void 0,!0),_:1},8,["label"])],void 0,!0),_:1}),J(y,{md:12,sm:24,xs:24},{default:W(()=>[J(b,{label:e.$t("Phone")},{default:W(()=>[J(k,{modelValue:p.phoneValue,"onUpdate:modelValue":a[4]||(a[4]=e=>p.phoneValue=e),placeholder:e.$t("+155555555"),onValidate:h.onPhoneValidate,onBlur:h.onPhoneBlur,class:"fcrm_phone_input"},null,8,["modelValue","placeholder","onValidate","onBlur"]),p.phoneIsValid?z("",!0):(j(),E(w,{key:0,error:e.$t("Invalid phone number")},null,8,["error"]))],void 0,!0),_:1},8,["label"])],void 0,!0),_:1})],void 0,!0),_:1}),J(g,{gutter:20},{default:W(()=>[J(y,{md:12,sm:24,xs:24},{default:W(()=>[J(b,{label:e.$t("Date of Birth")},{default:W(()=>[J(C,{type:"date",format:"DD/MM/YYYY","value-format":"YYYY-MM-DD","disabled-date":h.disabledDate,placeholder:e.$t("DD/MM/YYYY"),editable:!1,modelValue:s.subscriber.date_of_birth,"onUpdate:modelValue":a[5]||(a[5]=e=>s.subscriber.date_of_birth=e),style:{width:"100%"}},null,8,["disabled-date","placeholder","modelValue"])],void 0,!0),_:1},8,["label"])],void 0,!0),_:1}),e.has_company_module&&!s.company_id?(j(),E(y,{key:0,md:12,sm:24,xs:24},{default:W(()=>[J(b,{label:e.$t("Company / Business")},{default:W(()=>[J(x,{field:{is_multiple:!1,creatable:!0},modelValue:s.subscriber.company_id,"onUpdate:modelValue":a[6]||(a[6]=e=>s.subscriber.company_id=e)},null,8,["modelValue"])],void 0,!0),_:1},8,["label"])],void 0,!0),_:1})):z("",!0)],void 0,!0),_:1}),J(b,null,{default:W(()=>[J($,{modelValue:p.show_address,"onUpdate:modelValue":a[7]||(a[7]=e=>p.show_address=e)},{default:W(()=>[te(I(e.$t("Add Address Info")),1)],void 0,!0),_:1},8,["modelValue"])],void 0,!0),_:1}),p.show_address?(j(),L("div",Ye,[J(g,{gutter:20},{default:W(()=>[J(y,{md:12},{default:W(()=>[J(b,{label:e.$t("Address Line 1")},{default:W(()=>[J(v,{placeholder:e.$t("Enter street address"),autocomplete:"new-password",modelValue:s.subscriber.address_line_1,"onUpdate:modelValue":a[8]||(a[8]=e=>s.subscriber.address_line_1=e)},null,8,["placeholder","modelValue"])],void 0,!0),_:1},8,["label"])],void 0,!0),_:1}),J(y,{md:12},{default:W(()=>[J(b,{label:e.$t("Address Line 2 (Optional)")},{default:W(()=>[J(v,{placeholder:e.$t("Enter apartment, suite, unit"),autocomplete:"new-password",modelValue:s.subscriber.address_line_2,"onUpdate:modelValue":a[9]||(a[9]=e=>s.subscriber.address_line_2=e)},null,8,["placeholder","modelValue"])],void 0,!0),_:1},8,["label"])],void 0,!0),_:1}),J(y,{md:12},{default:W(()=>[J(b,{label:e.$t("City")},{default:W(()=>[J(v,{placeholder:e.$t("Enter city"),autocomplete:"new-password",modelValue:s.subscriber.city,"onUpdate:modelValue":a[10]||(a[10]=e=>s.subscriber.city=e)},null,8,["placeholder","modelValue"])],void 0,!0),_:1},8,["label"])],void 0,!0),_:1}),J(y,{md:12},{default:W(()=>[J(b,{label:e.$t("State")},{default:W(()=>[J(v,{placeholder:e.$t("Enter state / province"),autocomplete:"new-password",modelValue:s.subscriber.state,"onUpdate:modelValue":a[11]||(a[11]=e=>s.subscriber.state=e)},null,8,["placeholder","modelValue"])],void 0,!0),_:1},8,["label"])],void 0,!0),_:1}),J(y,{md:12},{default:W(()=>[J(b,{label:e.$t("Postal Code")},{default:W(()=>[J(v,{placeholder:e.$t("Enter postal code"),autocomplete:"new-password",modelValue:s.subscriber.postal_code,"onUpdate:modelValue":a[12]||(a[12]=e=>s.subscriber.postal_code=e)},null,8,["placeholder","modelValue"])],void 0,!0),_:1},8,["label"])],void 0,!0),_:1}),J(y,{md:12},{default:W(()=>[J(b,{label:e.$t("Country")},{default:W(()=>[J(f,{modelValue:s.subscriber.country,"onUpdate:modelValue":a[13]||(a[13]=e=>s.subscriber.country=e),clearable:"",filterable:"",autocomplete:"off",placeholder:e.$t("Select Country"),class:"fcrm_el-select-multiple"},{label:W(({label:e})=>[q("span",{innerHTML:e},null,8,Ee)]),default:W(()=>[(j(!0),L(N,null,H(p.countries,e=>(j(),E(_,{key:e.code,value:e.code,label:e.title},{default:W(()=>[q("span",{innerHTML:e.title},null,8,We)],void 0,!0),_:2},1032,["value","label"]))),128))],void 0,!0),_:1},8,["modelValue","placeholder"])],void 0,!0),_:1},8,["label"])],void 0,!0),_:1})],void 0,!0),_:1})])):z("",!0),p.options.custom_fields.length?(j(),E(b,{key:1},{default:W(()=>[J($,{modelValue:p.show_custom_data,"onUpdate:modelValue":a[14]||(a[14]=e=>p.show_custom_data=e)},{default:W(()=>[te(I(e.$t("Add Custom Data")),1)],void 0,!0),_:1},8,["modelValue"])],void 0,!0),_:1})):z("",!0),p.show_custom_data?(j(),E(V,{key:2,subscriber:s.subscriber,custom_fields:p.options.custom_fields},null,8,["subscriber","custom_fields"])):z("",!0),q("section",Ne,[q("div",He,I(e.$t("Identifiers")),1),q("div",Je,[q("div",Ge,[s.listId?z("",!0):(j(),L("div",Ke,[J(b,{label:e.$t("Lists")},{default:W(()=>[J(S,{modelValue:s.subscriber.lists,"onUpdate:modelValue":a[15]||(a[15]=e=>s.subscriber.lists=e),field:{placeholder:e.$t("Select Lists"),is_multiple:!0,creatable:!0,option_key:"lists"}},null,8,["modelValue","field"])],void 0,!0),_:1},8,["label"])])),s.tagId?z("",!0):(j(),L("div",Ze,[J(b,{label:e.$t("Tags")},{default:W(()=>[J(S,{modelValue:s.subscriber.tags,"onUpdate:modelValue":a[16]||(a[16]=e=>s.subscriber.tags=e),field:{placeholder:e.$t("Select Tags"),is_multiple:!0,creatable:!0,option_key:"tags"}},null,8,["modelValue","field"])],void 0,!0),_:1},8,["label"])]))]),q("div",Qe,[J(b,{label:e.$t("Customer Status"),class:"fcrm_is-required"},{default:W(()=>[J(f,{modelValue:s.subscriber.status,"onUpdate:modelValue":a[17]||(a[17]=e=>s.subscriber.status=e),placeholder:e.$t("Select Status")},{default:W(()=>[(j(!0),L(N,null,H(p.options.statuses,t=>(j(),E(_,{key:t.id,label:e.ucFirst(t.title),value:t.id},null,8,["label","value"]))),128))],void 0,!0),_:1},8,["modelValue","placeholder"]),J(w,{error:s.errors.get("status")},null,8,["error"])],void 0,!0),_:1},8,["label"])])])]),"pending"===s.subscriber.status?(j(),E(g,{key:3},{default:W(()=>[J(y,null,{default:W(()=>[J($,{modelValue:s.subscriber.double_optin,"onUpdate:modelValue":a[18]||(a[18]=e=>s.subscriber.double_optin=e)},{default:W(()=>[te(I(e.$t("Enable Double-Optin Email Confirmation")),1)],void 0,!0),_:1},8,["modelValue"])],void 0,!0),_:1})],void 0,!0),_:1})):z("",!0)],void 0),_:1})}]]),et={style:{margin:"0 -10px",display:"block"}};const tt={class:"fcrm_contact_form_handler"},lt={class:"fcrm_add_contact_footer"},at={class:"fcrm_add_contact_footer_link"},st={class:"fcrm_add_contact_footer_actions"};const ot=ue({name:"ContactFormHandler",props:["listId","tagId","company_id","form_class"],emits:["cancel","created"],components:{SkeletonLoading:ue({name:"SkeletonLoading"},[["render",function(e,t,l,a,s,o){const n=u,r=i,d=p;return j(),E(d,{animated:"",style:{"margin-top":"50px"}},{template:W(()=>[q("div",et,[J(r,{style:{padding:"0 10px","margin-bottom":"22px"},span:4},{default:W(()=>[J(n,{variant:"text",style:{height:"40px"}})],void 0,!0),_:1}),J(r,{style:{padding:"0 10px","margin-bottom":"22px"},span:8},{default:W(()=>[J(n,{variant:"text",style:{height:"40px"}})],void 0,!0),_:1}),J(r,{style:{padding:"0 10px","margin-bottom":"22px"},span:12},{default:W(()=>[J(n,{variant:"text",style:{height:"40px"}})],void 0,!0),_:1}),J(r,{style:{padding:"0 10px","margin-bottom":"22px"},span:12},{default:W(()=>[J(n,{variant:"text",style:{height:"40px"}})],void 0,!0),_:1}),J(r,{style:{padding:"0 10px","margin-bottom":"22px"},span:12},{default:W(()=>[J(n,{variant:"text",style:{height:"40px"}})],void 0,!0),_:1}),J(r,{style:{padding:"0 10px","margin-bottom":"22px"},span:4},{default:W(()=>[J(n,{variant:"text",style:{height:"40px"}})],void 0,!0),_:1}),J(r,{style:{padding:"0 10px","margin-bottom":"22px"},span:4},{default:W(()=>[J(n,{variant:"text",style:{height:"40px"}})],void 0,!0),_:1}),J(r,{style:{padding:"0 10px","margin-bottom":"22px"},span:4},{default:W(()=>[J(n,{variant:"text",style:{height:"40px"}})],void 0,!0),_:1}),J(r,{style:{padding:"0 10px","margin-bottom":"22px"},span:12},{default:W(()=>[J(n,{variant:"text",style:{height:"40px"}})],void 0,!0),_:1}),J(r,{style:{padding:"0 10px","margin-bottom":"22px"},span:24},{default:W(()=>[J(n,{variant:"text",style:{height:"40px"}})],void 0,!0),_:1}),J(r,{style:{padding:"0 10px","margin-bottom":"22px"},span:24},{default:W(()=>[J(n,{variant:"text",style:{height:"40px"}})],void 0,!0),_:1}),J(r,{style:{padding:"0 10px","margin-bottom":"22px"},span:8},{default:W(()=>[J(n,{variant:"text",style:{height:"40px"}})],void 0,!0),_:1}),J(r,{style:{padding:"0 10px","margin-bottom":"22px"},span:8},{default:W(()=>[J(n,{variant:"text",style:{height:"40px"}})],void 0,!0),_:1}),J(r,{style:{padding:"0 10px","margin-bottom":"22px"},span:8},{default:W(()=>[J(n,{variant:"text",style:{height:"40px"}})],void 0,!0),_:1})])]),_:1})}]]),Forma:Xe},data(){return{exist:!1,errors:new Se,subscriber:this.fresh(),creating:!1}},methods:{fresh:()=>({first_name:null,last_name:null,email:null,phone:"",date_of_birth:"",status:"subscribed",address_line_1:"",address_line_2:"",city:"",state:"",postal_code:"",country:"",tags:[],lists:[],custom_values:{},double_optin:!1}),save(e=!1){if(this.errors.clear(),!this.subscriber.email)return void this.$notify.error({message:this.$t("Email field is required"),offset:19});const t={...this.subscriber};this.listId&&(t.lists=[this.listId]),this.tagId&&(t.tags=[this.tagId]),this.company_id&&(t.company_id=this.company_id),this.creating=!0,this.$post("subscribers",t).then(t=>{this.$notify.success({title:this.$t("Great!"),message:t.message,offset:19}),this.$emit("created",t.contact,e),this.subscriber=this.fresh()}).catch(e=>{this.errors.record(e),e.subscriber&&(this.exist=e.subscriber)}).finally(()=>{this.creating=!1})},cancel(){this.$emit("cancel")}}},[["render",function(e,t,l,s,o,i){const n=U("SkeletonLoading"),r=U("forma"),d=a;return j(),L("div",tt,[o.creating?(j(),E(n,{key:0,class:"fcrm_p_20"})):(j(),E(r,{key:1,"list-id":l.listId,"tag-id":l.tagId,errors:o.errors,subscriber:o.subscriber,company_id:l.company_id,class:G(l.form_class)},null,8,["list-id","tag-id","errors","subscriber","company_id","class"])),q("div",lt,[q("div",at,[J(d,{link:"",onClick:t[0]||(t[0]=e=>i.save(!0))},{default:W(()=>[te(I(e.$t("Create & Add Another")),1)],void 0),_:1})]),q("div",st,[J(d,{onClick:t[1]||(t[1]=e=>i.cancel())},{default:W(()=>[te(I(e.$t("Cancel")),1)],void 0),_:1}),J(d,{type:"primary",onClick:t[2]||(t[2]=e=>i.save())},{default:W(()=>[te(I(e.$t("Create Contact")),1)],void 0),_:1})])])])}]]),it={name:"Adder",components:{FormHandler:ot},props:{visible:Boolean,listId:[String,Number],tagId:[String,Number],options:{type:Object,default:null}},emits:["close","fetch"],data(){return{showing:this.visible,direction:"rtl"}},watch:{visible(e){this.showing=e}},methods:{handleCreated(e,t){!0===t?(this.showing=!0,this.$emit("fetch")):(this.showing=!1,this.$router.push({name:"subscriber",params:{id:e.id}}),this.$emit("close"))},handleCancel(){this.showing=!1,this.$emit("close")}},mounted(){window.fcAdmin&&window.fcAdmin.is_rtl&&(this.direction="ltr")}},nt={key:0,class:"fcrm_company_drawer_content"};const rt=ue(it,[["render",function(e,t,l,a,o,i){const n=U("form-handler"),r=s;return j(),E(r,{class:"fcrm_company_info_drawer",size:"820","append-to-body":!0,modelValue:o.showing,"onUpdate:modelValue":t[0]||(t[0]=e=>o.showing=e),"with-header":!0,direction:o.direction,title:e.$t("Add New Contact")},{default:W(()=>[o.showing?(j(),L("div",nt,[J(n,{onCreated:i.handleCreated,onCancel:i.handleCancel,listId:l.listId,tagId:l.tagId,form_class:"fcrm_p_20"},null,8,["onCreated","onCancel","listId","tagId"])])):z("",!0)],void 0),_:1},8,["modelValue","direction","title"])}]]),dt={name:"Filters",components:{Filterer:me},emits:["filter","search"],props:{type:{required:!0},options:{required:!0,type:Array},selected:{required:!0},count:{required:!0,type:Number},noMatch:{required:!0,type:Boolean}},data:()=>({query:null,placement:"bottom-start"}),computed:{selection:{get(){return this.selected},set(e){this.$emit("filter",e)}},parsedOptions(){let e=[];return"statuses"!=this.type?this.each(this.options,t=>{e.push({id:parseInt(t.id),title:t.title,slug:t.slug})}):e=this.options,e}},methods:{search(){this.$emit("search",this.query&&this.query.toLowerCase())},deselect(e){const t=this.selection.indexOf(e.slug);this.selection.splice(t,1),this.$emit("filter",this.selection)}},mounted(){this.appVars.is_rtl&&(this.placement="bottom-end")}},ct={class:"fcrm_fluentcrm-filterer"},mt={key:0,class:"fcrm_fluentcrm-meta"};const ut=ue(dt,[["render",function(e,t,l,s,o,i){const n=U("ArrowDown"),d=f,m=a,u=r,p=h,y=c,g=_,v=U("filterer"),w=b;return j(),L("div",ct,[J(v,{placement:o.placement,filter_type:l.type,class:G({"fcrm_fluentcrm-filtered":i.selection.length})},{header:W(()=>[J(m,{plain:"",size:"small"},{default:W(()=>[te(I(e.$t("Filtered by"))+" "+I(e.ucFirst(e.trans(l.type)))+" ",1),J(d,{class:"el-icon--right"},{default:W(()=>[J(n)],void 0,!0),_:1})],void 0,!0),_:1})]),items:W(()=>[J(p,{class:"fcrm_fluentcrm-filter-option fcrm_dropdown-search-item fcrm_no-hover"},{default:W(()=>[J(u,{size:"small",modelValue:o.query,"onUpdate:modelValue":t[0]||(t[0]=e=>o.query=e),placeholder:e.$t("Search..."),onInput:i.search,class:"fcrm_input"},null,8,["modelValue","placeholder","onInput"])],void 0,!0),_:1}),J(p,{class:"fcrm_fluentcrm-filter-option fcrm_dropdown-items-label"},{default:W(()=>[te(I(e.$t("Choose an option:")),1)],void 0,!0),_:1}),J(g,{modelValue:i.selection,"onUpdate:modelValue":t[1]||(t[1]=e=>i.selection=e),class:"fcrm_fluentcrm-filter-options fcrm_checkbox_group"},{default:W(()=>[(j(!0),L(N,null,H(i.parsedOptions,e=>(j(),E(y,{key:e.id,value:e.id,class:"el-dropdown-menu__item fcrm_checkbox"},{default:W(()=>[te(I(e.title),1)],void 0,!0),_:2},1032,["value"]))),128))],void 0,!0),_:1},8,["modelValue"]),l.noMatch?(j(),E(p,{key:0,class:"fcrm_fluentcrm-filter-option"},{default:W(()=>[q("p",null,I(e.$t("No items found")),1)],void 0,!0),_:1})):z("",!0)]),_:1},8,["placement","filter_type","class"]),i.selection.length?(j(),L("div",mt,[(j(!0),L(N,null,H(i.parsedOptions,e=>(j(),L(N,{key:e.id},[-1!=l.selected.indexOf(e.id)?(j(),E(w,{key:0,closable:"",onClose:t=>i.deselect(e)},{default:W(()=>[te(I(e.title),1)],void 0),_:2},1032,["onClose"])):z("",!0)],64))),128))])):z("",!0)])}]]),pt={class:"fcrm_filter-manager"},ht={style:{padding:"10px","text-align":"center"}};const _t=ue({name:"Manager",props:["type","matched","options","selected","selection","subscribers","selectionCount","total_match"],emits:["filter"],data(){return{placement:"bottom-start",new_payload:!1,filterLabel:this.$t("Filters.instruction")}},components:{Editor:de,Filters:ut},mixins:[re],methods:{filter(e){this.$emit("filter",this.payload(e))},updatePayLoad(e){this.new_payload=e},pushPayload(){this.new_payload?(this.subscribe(this.new_payload),setTimeout(()=>{this.new_payload=!1},500)):this.$notify.error(this.$t("No changes found"))}},mounted(){this.appVars.is_rtl&&(this.placement="bottom-end")}},[["render",function(e,t,l,s,o,i){const n=a,r=U("editor"),d=U("filters");return j(),L("div",pt,[l.selection?(j(),E(r,{key:0,type:l.type,options:e.choices,noMatch:e.noMatch,matched:l.matched,selectionCount:l.selectionCount,onSearch:e.search,onSubscribe:i.updatePayLoad,placement:o.placement},{footer:W(()=>[q("div",ht,[J(n,{disabled:!o.new_payload,onClick:t[0]||(t[0]=e=>i.pushPayload()),style:{width:"100%"},type:"success",size:"small"},{default:W(()=>[te(I(e.$t("Confirm")),1)],void 0,!0),_:1},8,["disabled"])])]),_:1},8,["type","options","noMatch","matched","selectionCount","onSearch","onSubscribe","placement"])):(j(),E(d,{key:1,type:l.type,options:e.choices,noMatch:e.noMatch,selected:l.selected,count:l.total_match,onSearch:e.search,onFilter:i.filter,add_label:o.filterLabel},null,8,["type","options","noMatch","selected","count","onSearch","onFilter","add_label"]))])}]]),ft={class:"icon"},bt={class:"fcrm_checkbox_group_label"};const yt=ue({name:"ColumnToggler",components:{Icons:pe,Filterer:fe,Setting:y},props:{placement:{type:String,default:"bottom-start"}},emits:["input","update:modelValue","dataChanged"],data:()=>({selection:[],subscriberColumns:be}),computed:{columnGroups(){this.has_company_module&&(window.fc_primary_company_pushed||(be.push({label:this.$t("Primary Company"),value:"primary_company",position:4}),window.fc_primary_company_pushed=!0),window.fc_companies_pushed||(be.push({label:this.$t("Companies"),value:"companies",position:4}),window.fc_companies_pushed=!0));let e=be;"yes"!==this.appVars.sms_enabled&&(e=be.filter(e=>"sms_status"!==e.value));const t=[{slug:"subscriber",label:this.$t("Primary Fields"),fields:e}];this.appVars.commerce_provider&&t.push({slug:"commerce",label:this.$t("Commerce Fields"),fields:[{label:this.$t("Lifetime Value"),value:"commerce.total_order_value",position:1},{label:this.$t("Purchase Count"),value:"commerce.total_order_count",position:2},{label:this.$t("Customer Since"),value:"commerce.first_order_date",position:3},{label:this.$t("Last Purchase Date"),value:"commerce.last_order_date",position:3}]});const l=[];return this.each(this.appVars.contact_custom_fields,(e,t)=>{l.push({label:e.label,value:e.slug,position:t+10})}),l.length&&t.push({slug:"custom_fields",label:this.$t("Custom Fields"),fields:l}),t}},methods:{init(){const e=this.storage.get("columns");e?(this.selection=e,this.fire()):(this.selection=["tags","lists","status"],this.save())},save(){var e;this.storage.set("columns",this.selection),null==(e=this.$refs.filterer)||e.hide(),this.fire()},fire(){this.$emit("input",this.selection),this.$emit("update:modelValue",this.selection),setTimeout(()=>{this.$emit("dataChanged",this.selection)},400)}},mounted(){this.init()}},[["render",function(e,t,l,s,o,i){const n=U("Icons"),r=a,d=c,m=_,u=h,p=U("filterer");return j(),E(p,{ref:"filterer",placement:l.placement},{header:W(()=>[J(r,{size:"small",class:"small only-icon-btn"},{default:W(()=>[q("span",ft,[J(n,{"icon-name":"column"})])],void 0,!0),_:1})]),items:W(()=>[J(m,{modelValue:o.selection,"onUpdate:modelValue":t[0]||(t[0]=e=>o.selection=e),class:"fcrm_filter-options fcrm_checkbox_group fcrm_column_toggler_checks"},{default:W(()=>[(j(!0),L(N,null,H(i.columnGroups,(e,t)=>(j(),L(N,{key:t},[q("div",bt,I(e.label),1),(j(!0),L(N,null,H(e.fields,(e,l)=>(j(),L("div",{key:t+" "+l,class:"el-dropdown-menu__item"},[J(d,{value:e.value,class:"fcrm_checkbox"},{default:W(()=>[te(I(e.label),1)],void 0,!0),_:2},1032,["value"])]))),128))],64))),128))],void 0,!0),_:1},8,["modelValue"])]),footer:W(()=>[J(u,{class:"fcrm_no-hover"},{default:W(()=>[J(r,{type:"primary",size:"small",style:{width:"100%"},onClick:i.save},{default:W(()=>[le(e.$slots,"btn-label",{},()=>[te(I(e.$t("Save")),1)])],void 0,!0),_:3},8,["onClick"])],void 0,!0),_:3})]),_:3},8,["placement"])}]]);const gt=ue({name:"PropertyChanger",components:{Filterer:me,ArrowDown:g},props:["options","label","prop_key","selectedSubscribers"],emits:["fetch"],data:()=>({selected_item:""}),methods:{save(){this.changeSubscribersProperty({type:this.prop_key,value:this.selected_item})},changeSubscribersProperty(e){const{type:t,value:l}=e;l?(this.loading=!0,this.$put("subscribers/subscribers-property",{property:t,value:l,subscribers:this.selectedSubscribers.map(e=>e.id)}).then(e=>{this.$notify.success(e.message),this.$emit("fetch")}).catch(e=>{console.log(e)}).finally(()=>{this.loading=!1})):this.$notify.error(this.$t("Pro_Please_saof"))}}},[["render",function(e,t,l,s,o,i){const n=U("ArrowDown"),r=f,d=a,c=h,m=w,u=v,p=U("filterer");return j(),E(p,{placement:"bottom-start"},{header:W(()=>[J(d,{plain:"",size:"small"},{default:W(()=>[te(I(e.$t("Change"))+" "+I(l.label)+" ",1),le(e.$slots,"icon",{},()=>[J(r,null,{default:W(()=>[J(n)],void 0,!0),_:1})])],void 0,!0),_:3})]),items:W(()=>[J(c,{class:"fcrm_filter-option fcrm_no-hover"},{default:W(()=>[te(I(e.$t("Choose New"))+" "+I(l.label)+": ",1)],void 0,!0),_:1}),J(u,{class:"fcrm_checkable_block",modelValue:o.selected_item,"onUpdate:modelValue":t[0]||(t[0]=e=>o.selected_item=e)},{default:W(()=>[(j(!0),L(N,null,H(l.options,e=>(j(),E(m,{key:e.id,value:e.id},{default:W(()=>[te(I(e.title),1)],void 0,!0),_:2},1032,["value"]))),128))],void 0,!0),_:1},8,["modelValue"])]),footer:W(()=>[J(c,{class:"fcrm_no-hover"},{default:W(()=>[J(d,{type:"primary",size:"small",style:{width:"100%"},onClick:i.save},{default:W(()=>[le(e.$slots,"btn-label",{},()=>[te(I(e.$t("Change"))+" "+I(l.label),1)])],void 0,!0),_:3},8,["onClick"])],void 0,!0),_:3})]),_:3})}]]),vt={class:"fcrm_bulk_contact_custom_fields"},wt={key:0,class:"fcrm_contact_popover_header fcrm_custom_field_form"},kt={class:"fcrm_popover_header"},Ct={class:"fcrm_field_label"},xt={key:1,class:"fcrm_contact_popover_body fcrm_popover_body_flex_direction_column"},$t={class:"fcrm_popover_actions"};const Vt={class:"fcrm_bulk_action_inline"},St={class:"fcrm_bulk_wrap"},At={class:"fcrm_bulk_item"},Ft={class:"icon"},Pt={class:"icon"},Tt={key:0,style:{padding:"10px"}},Rt={class:"icon"},Dt={class:"icon"},Ot={class:"icon"},Bt={key:0,style:{padding:"10px"}},Mt={class:"icon"},jt={class:"icon"},Lt={key:0,class:"fcrm_bulk_navs"},It={key:0},zt={key:1},Ut={key:0,class:"fcrm_bulk_processing_wrap"},qt={class:"fcrm_bulk_processing--title"},Yt={class:"fcrm_secondary_text font-regular fcrm_mb_16 fcrm_bulk_processing--description"},Et={class:"fcrm_bulk_processing--bar d-flex items-center gap-4 fcrm_mb_6"},Wt={class:"fcrm_secondary_text small fcrm_bulk_processing--count"};const Nt=ue({name:"BulkContactActions",props:["selectedSubscribers","options","pagination","theme_mode"],emits:["refetch"],components:{BulkContactCustomField:ue({name:"BulkContactCustomField",components:{ArrowLeft:k},props:["options","showPopover","theme_mode"],emits:["closePopover","openPopover","updateCustomField"],data:()=>({customFieldType:"",formattedCustomFields:{},updating:!1,localShowPopover:!1}),watch:{showPopover(e){this.localShowPopover=e},localShowPopover(e){e?this.$emit("openPopover"):this.$emit("closePopover")},customFieldType(e){e&&(this.localShowPopover=!0)}},methods:{formatFields(){this.customFieldsList.forEach(e=>{"select-multi"===e.type||"checkbox"==e.type?this.formattedCustomFields[e.slug]=[]:this.formattedCustomFields[e.slug]=""})},handleCustomFieldBulk(){const e=this.selectedField,t=this.formattedCustomFields[e.slug];this.updating=!0,this.$emit("updateCustomField",{key:e.slug,type:e.type,value:t})},handleBack(){this.localShowPopover=!1,this.customFieldType=""}},computed:{contactPopoverClass(){return`fcrm_contact_popover ${"dark"===this.theme_mode?"fcrm-force-light":"fcrm-dark"}`},bulkSelectPopperClass(){return"dark"===this.theme_mode?"fcrm-force-light":"fcrm-dark"},bulkSelectWordbreakPopperClass(){return"dark"===this.theme_mode?"fcrm-force-light":""},customFieldsList(){return this.options.custom_fields||[]},selectedField(){return this.customFieldsList.find(e=>e.slug===this.customFieldType)}},mounted(){this.formatFields()}},[["render",function(s,o,i,n,m,u){const p=U("ArrowLeft"),h=f,b=a,y=t,g=l,k=r,x=w,$=v,V=c,S=_,A=d,F=C,P=e;return j(),L("div",vt,[J(F,{visible:m.localShowPopover,"onUpdate:visible":o[7]||(o[7]=e=>m.localShowPopover=e),placement:"bottom",width:300,trigger:"manual","popper-class":u.contactPopoverClass,effect:"dark"},{reference:W(()=>[J(g,{filterable:"",size:"small",class:"fcrm_bulk_select",placeholder:s.$t("Select Field"),effect:"dark","popper-class":u.bulkSelectPopperClass,modelValue:m.customFieldType,"onUpdate:modelValue":o[6]||(o[6]=e=>m.customFieldType=e),ref:"customFieldSelect"},{default:W(()=>[(j(!0),L(N,null,H(u.customFieldsList,(e,t)=>(j(),E(y,{key:t,value:e.slug,label:e.label},null,8,["value","label"]))),128))],void 0,!0),_:1},8,["placeholder","popper-class","modelValue"])]),default:W(()=>[u.selectedField?(j(),L("div",wt,[q("div",kt,[J(b,{size:"small",text:"",onClick:u.handleBack,class:"fcrm_back_button"},{default:W(()=>[J(h,null,{default:W(()=>[J(p)],void 0,!0),_:1})],void 0,!0),_:1},8,["onClick"]),q("span",Ct,I(u.selectedField.label),1)])])):z("",!0),u.selectedField?(j(),L("div",xt,["select-one"===u.selectedField.type||"select-multi"==u.selectedField.type?(j(),E(g,{key:0,placeholder:s.$t("Select")+" "+u.selectedField.label,clearable:"",filterable:"",effect:"dark","popper-class":u.bulkSelectWordbreakPopperClass,modelValue:m.formattedCustomFields[u.selectedField.slug],"onUpdate:modelValue":o[0]||(o[0]=e=>m.formattedCustomFields[u.selectedField.slug]=e),multiple:"select-multi"==u.selectedField.type,class:"fcrm_full_width",size:"small",teleported:!1},{default:W(()=>[(j(!0),L(N,null,H(u.selectedField.options,e=>(j(),E(y,{key:e,value:e,label:e},null,8,["value","label"]))),128))],void 0,!0),_:1},8,["placeholder","popper-class","modelValue","multiple"])):"text"==u.selectedField.type||"number"==u.selectedField.type||"textarea"==u.selectedField.type?(j(),E(k,{key:1,placeholder:u.selectedField.label,size:"small",type:u.selectedField.type,modelValue:m.formattedCustomFields[u.selectedField.slug],"onUpdate:modelValue":o[1]||(o[1]=e=>m.formattedCustomFields[u.selectedField.slug]=e),class:"fcrm_full_width"},null,8,["placeholder","type","modelValue"])):"radio"==u.selectedField.type?(j(),E($,{key:2,modelValue:m.formattedCustomFields[u.selectedField.slug],"onUpdate:modelValue":o[2]||(o[2]=e=>m.formattedCustomFields[u.selectedField.slug]=e),size:"small"},{default:W(()=>[(j(!0),L(N,null,H(u.selectedField.options,e=>(j(),E(x,{key:e,value:e,label:e,size:"large",class:"fcrm-radio"},null,8,["value","label"]))),128))],void 0,!0),_:1},8,["modelValue"])):"checkbox"==u.selectedField.type?(j(),E(S,{key:3,modelValue:m.formattedCustomFields[u.selectedField.slug],"onUpdate:modelValue":o[3]||(o[3]=e=>m.formattedCustomFields[u.selectedField.slug]=e),class:"fcrm_checkbox_group",size:"small"},{default:W(()=>[(j(!0),L(N,null,H(u.selectedField.options,e=>(j(),E(V,{key:e,size:"large",value:e,class:"fcrm_checkbox"},{default:W(()=>[te(I(e),1)],void 0,!0),_:2},1032,["value"]))),128))],void 0,!0),_:1},8,["modelValue"])):"date"==u.selectedField.type?(j(),E(A,{key:4,"value-format":"YYYY-MM-DD",modelValue:m.formattedCustomFields[u.selectedField.slug],"onUpdate:modelValue":o[4]||(o[4]=e=>m.formattedCustomFields[u.selectedField.slug]=e),type:"date",size:"large",class:"fcrm_full_width",teleported:!1,placeholder:s.$t("Pick a date")},null,8,["modelValue","placeholder"])):"date_time"==u.selectedField.type?(j(),E(A,{key:5,"value-format":"YYYY-MM-DD HH:mm:ss",modelValue:m.formattedCustomFields[u.selectedField.slug],"onUpdate:modelValue":o[5]||(o[5]=e=>m.formattedCustomFields[u.selectedField.slug]=e),type:"datetime",size:"large",style:{width:"100%"},teleported:!1,class:"fcrm_full_width",placeholder:s.$t("Pick a date and time")},null,8,["modelValue","placeholder"])):(j(),L(N,{key:6},[te(I(u.selectedField),1)],64)),q("div",$t,[Y((j(),E(b,{disabled:m.updating,onClick:u.handleCustomFieldBulk},{default:W(()=>[q("span",null,I(s.$t("Update")),1)],void 0,!0),_:1},8,["disabled","onClick"])),[[P,m.updating]])])])):z("",!0)],void 0),_:1},8,["visible","popper-class"])])}]]),Confirm:ke,OptionSelector:Ce,Check:$,Loading:x},data(){return{delete_confirm_message:""+this.$t("Are you sure to delete?")+"
"+this.$t("delete_all_contacts_notice"),double_optin_confirm_message:""+this.$t("Are you sure to send double optin?")+"
"+this.$t("send_double_optin_contacts_notice"),actions:{},select_job:{action_name:"",selected_options:[]},doing_action:!1,select_status:"",allSelected:!1,processCount:0,processingAllBulk:!1,currentAllActionLastContactId:0,custom_field:{},showCustomFieldPopover:!1}},watch:{"select_job.action_name":{handler(){this.select_job.selected_options=[],this.select_status=""},deep:!0}},computed:{bulkSelectPopperClass(){return"dark"===this.theme_mode?"fcrm-force-light":"fcrm-dark"},bulkSelectWordbreakPopperClass(){return"dark"===this.theme_mode?"fcrm_select_options_wordbreak fcrm-force-light":"fcrm_select_options_wordbreak fcrm-dark"},currentAction(){return!!this.select_job.action_name&&this.actions[this.select_job.action_name]},canSelectAll(){return this.pagination&&this.selectedSubscribers.length{this.resetAllBulkProgressState(),this.processingAllBulk=!0,this.processAllBulk()}).catch(()=>{this.allSelected=!1})},async doBulkAction(){const e=[];if(this.each(this.selectedSubscribers,t=>{e.push(t.id)}),!e.length)return this.$notify.error(this.$t("Please select subscribers first")),!1;this.doing_action=!0;const t=ae(e,100),l={action_name:this.select_job.action_name,action_options:this.select_job.selected_options,new_status:this.select_status,custom_field:this.custom_field};for(let a=0;a{a===t.length-1&&(this.$emit("refetch"),this.$notify.success(e.message))}).catch(e=>{this.handleError(e)});this.doing_action=!1},repeatBulkAction(e,t){return t.subscriber_ids=e,this.processCount+=e.length,this.$post("subscribers/do-bulk-action",t)},processAllBulk(e){const t={is_all:"yes",action_name:this.select_job.action_name,action_options:this.select_job.selected_options,new_status:this.select_status,last_id:this.currentAllActionLastContactId,per_page:400,contact_query:window.fcrm_sub_params};this.$post("subscribers/do-bulk-action",t).then(t=>{this.processCount+=parseInt(t.completed_contacts),t.is_completed?(this.processingAllBulk=!1,this.resetAllBulkProgressState(),this.$emit("refetch"),this.$notify.success(t.message),e&&e()):(this.currentAllActionLastContactId=t.last_contact_id,this.$nextTick(()=>{this.processAllBulk(e)}))}).catch(e=>{this.processingAllBulk=!1,this.resetAllBulkProgressState(),this.handleError(e)})},resetAllBulkProgressState(){this.processCount=0,this.currentAllActionLastContactId=0},updateCustomFieldValue(e){this.custom_field=e,this.showCustomFieldPopover=!1,this.handleBulkActionRoute()}},mounted(){this.actions={add_to_tags:{label:this.$t("Add To Tags"),options:this.options.tags},add_to_lists:{label:this.$t("Add To Lists"),options:this.options.lists},remove_from_tags:{label:this.$t("Remove From Tags"),options:this.options.tags},remove_from_lists:{label:this.$t("Remove From Lists"),options:this.options.lists},change_contact_status:{label:this.$t("Change Contact Status"),statuses:this.options.statuses},change_contact_type:{label:this.$t("Change Contact Type"),custom_options:this.appVars.contact_types,btn_text:this.$t("Change Contact Type"),is_multiple:!1},add_to_email_sequence:{label:this.$t("Add To Email Sequence")},add_to_automation:{label:this.$t("Add To Automation Funnel")}},this.appVars.custom_contact_bulk_actions&&this.appVars.custom_contact_bulk_actions.length&&this.each(this.appVars.custom_contact_bulk_actions,e=>{this.actions[e.action_name]||(this.actions[e.action_name]=e)}),this.has_company_module&&(this.actions.add_to_company={label:this.$t("Add To Company")},this.actions.remove_from_company={label:this.$t("Remove From Company")}),this.actions.send_double_optin={label:this.$t("Send Double Optin To Pending Contacts")},this.actions.update_custom_fields={label:this.$t("Update Custom Fields")},this.actions.delete_contacts={label:this.$t("Delete Contacts")}}},[["render",function(s,o,i,n,r,d){const c=t,m=l,u=U("Check"),p=f,h=a,_=U("Delete"),b=U("confirm"),y=U("option-selector"),g=U("BulkContactCustomField"),v=U("Loading"),w=V,k=S,C=e;return j(),L("div",Vt,[q("div",St,[q("div",At,[q("label",null,I(s.$t("Select Action")),1),J(m,{clearable:"",filterable:"",size:"small",class:"fcrm_bulk_select",placeholder:s.$t("Select Action"),"popper-class":d.bulkSelectPopperClass,effect:"dark",modelValue:r.select_job.action_name,"onUpdate:modelValue":o[0]||(o[0]=e=>r.select_job.action_name=e)},{default:W(()=>[(j(!0),L(N,null,H(r.actions,(e,t)=>(j(),E(c,{key:t,value:t,label:e.label},null,8,["value","label"]))),128))],void 0),_:1},8,["placeholder","popper-class","modelValue"])]),d.currentAction&&d.currentAction.options?(j(),L(N,{key:0},[J(m,{filterable:"",clearable:"",multiple:"","collapse-tags":"","max-collapse-tags":2,size:"small",class:"fcrm_bulk_select",placeholder:s.$t("Select"),"popper-class":d.bulkSelectWordbreakPopperClass,effect:"dark",modelValue:r.select_job.selected_options,"onUpdate:modelValue":o[1]||(o[1]=e=>r.select_job.selected_options=e)},{default:W(()=>[(j(!0),L(N,null,H(d.currentAction.options,e=>(j(),E(c,{key:e.id,value:e.id,label:e.title},null,8,["value","label"]))),128))],void 0),_:1},8,["placeholder","popper-class","modelValue"]),Y((j(),E(h,{disabled:r.doing_action||!r.select_job.selected_options.length,onClick:o[2]||(o[2]=e=>d.handleBulkActionRoute()),type:"primary",size:"small"},{default:W(()=>[q("span",Ft,[J(p,null,{default:W(()=>[J(u)],void 0,!0),_:1})]),te(" "+I(s.$t("Confirm")),1)],void 0),_:1},8,["disabled"])),[[C,r.doing_action]])],64)):"change_contact_status"==r.select_job.action_name?(j(),L(N,{key:1},[J(m,{filterable:"",clearable:"",size:"small",class:"fcrm_bulk_select",placeholder:s.$t("Select"),"popper-class":d.bulkSelectPopperClass,effect:"dark",modelValue:r.select_status,"onUpdate:modelValue":o[3]||(o[3]=e=>r.select_status=e)},{default:W(()=>[(j(!0),L(N,null,H(d.currentAction.statuses,e=>(j(),E(c,{key:e.id,value:e.id,label:e.title},null,8,["value","label"]))),128))],void 0),_:1},8,["placeholder","popper-class","modelValue"]),Y((j(),E(h,{disabled:r.doing_action||!r.select_status,onClick:o[4]||(o[4]=e=>d.handleBulkActionRoute()),type:"primary",size:"small"},{default:W(()=>[q("span",Pt,[J(p,null,{default:W(()=>[J(u)],void 0,!0),_:1})]),te(" "+I(s.$t("Confirm")),1)],void 0),_:1},8,["disabled"])),[[C,r.doing_action]])],64)):"delete_contacts"==r.select_job.action_name?(j(),E(b,{key:2,placement:"top-start",message:r.delete_confirm_message,onYes:o[5]||(o[5]=e=>d.handleBulkActionRoute())},{reference:W(()=>[Y((j(),E(h,{disabled:r.doing_action,size:"small",type:"danger",plain:""},{default:W(()=>[J(p,null,{default:W(()=>[J(_)],void 0,!0),_:1}),te(" "+I(s.$t("Delete Selected")),1)],void 0,!0),_:1},8,["disabled"])),[[C,r.doing_action]])]),_:1},8,["message"])):"send_double_optin"==r.select_job.action_name?(j(),E(b,{key:3,placement:"top-start",message:r.double_optin_confirm_message,onYes:o[6]||(o[6]=e=>d.handleBulkActionRoute())},{reference:W(()=>[Y((j(),E(h,{disabled:r.doing_action,size:"small"},{default:W(()=>[J(p,{class:"fcrm_bulk_confirm_icon"},{default:W(()=>[J(u)],void 0,!0),_:1}),te(" "+I(s.$t("Send Double Opt-In")),1)],void 0,!0),_:1},8,["disabled"])),[[C,r.doing_action]])]),_:1},8,["message"])):"add_to_email_sequence"==r.select_job.action_name?(j(),L(N,{key:4},[s.has_campaign_pro?(j(),L(N,{key:1},[J(y,{modelValue:r.select_status,"onUpdate:modelValue":o[7]||(o[7]=e=>r.select_status=e),field:{option_key:"email_sequences",clearable:!0,size:"small"},effect:"dark",popper_class:d.bulkSelectPopperClass,class:"fcrm_bulk_select"},null,8,["modelValue","popper_class"]),J(b,{placement:"top-start",width:230,message:s.$t("Add_Contacts_To_Email_Sequence_Confirm_Message"),onYes:o[8]||(o[8]=e=>d.handleBulkActionRoute())},{reference:W(()=>[Y((j(),E(h,{disabled:r.doing_action||!r.select_status,type:"primary",size:"small"},{default:W(()=>[q("span",Rt,[J(p,null,{default:W(()=>[J(u)],void 0,!0),_:1})]),te(" "+I(s.$t("Confirm")),1)],void 0,!0),_:1},8,["disabled"])),[[C,r.doing_action]])]),_:1},8,["message"])],64)):(j(),L("span",Tt,I(s.$t("Require FluentCRM Pro")),1))],64)):"add_to_company"==r.select_job.action_name?(j(),L(N,{key:5},[J(y,{modelValue:r.select_status,"onUpdate:modelValue":o[9]||(o[9]=e=>r.select_status=e),field:{option_key:"companies",clearable:!0,size:"small"},effect:"dark",popper_class:d.bulkSelectPopperClass,class:"fcrm_bulk_select"},null,8,["modelValue","popper_class"]),J(b,{placement:"top-start",width:230,message:s.$t("Add_Contacts_To_Company_Confirm_Message"),onYes:o[10]||(o[10]=e=>d.handleBulkActionRoute())},{reference:W(()=>[Y((j(),E(h,{disabled:r.doing_action||!r.select_status,size:"small",type:"primary"},{default:W(()=>[q("span",Dt,[J(p,null,{default:W(()=>[J(u)],void 0,!0),_:1})]),te(" "+I(s.$t("Confirm")),1)],void 0,!0),_:1},8,["disabled"])),[[C,r.doing_action]])]),_:1},8,["message"])],64)):"remove_from_company"==r.select_job.action_name?(j(),L(N,{key:6},[J(y,{modelValue:r.select_status,"onUpdate:modelValue":o[11]||(o[11]=e=>r.select_status=e),field:{option_key:"companies",clearable:!0,size:"small"},effect:"dark",popper_class:d.bulkSelectPopperClass,class:"fcrm_bulk_select"},null,8,["modelValue","popper_class"]),J(b,{placement:"top-start",width:230,message:s.$t("Remove_Contacts_From_Company_Confirm_Message"),onYes:o[12]||(o[12]=e=>d.handleBulkActionRoute())},{reference:W(()=>[Y((j(),E(h,{disabled:r.doing_action||!r.select_status,type:"primary",size:"small"},{default:W(()=>[q("span",Ot,[J(p,null,{default:W(()=>[J(u)],void 0,!0),_:1})]),te(" "+I(s.$t("Confirm")),1)],void 0,!0),_:1},8,["disabled"])),[[C,r.doing_action]])]),_:1},8,["message"])],64)):"add_to_automation"==r.select_job.action_name?(j(),L(N,{key:7},[s.has_campaign_pro?(j(),L(N,{key:1},[J(y,{modelValue:r.select_status,"onUpdate:modelValue":o[13]||(o[13]=e=>r.select_status=e),field:{option_key:"automation_funnels",clearable:!0,size:"small"},effect:"dark",popper_class:d.bulkSelectPopperClass,class:"fcrm_bulk_select"},null,8,["modelValue","popper_class"]),J(b,{placement:"top-start",width:230,message:s.$t("Add_Contacts_To_Automation_Confirm_Message"),onYes:o[14]||(o[14]=e=>d.handleBulkActionRoute())},{reference:W(()=>[Y((j(),E(h,{disabled:r.doing_action||!r.select_status,type:"primary",size:"small"},{default:W(()=>[q("span",Mt,[J(p,null,{default:W(()=>[J(u)],void 0,!0),_:1})]),te(" "+I(s.$t("Confirm")),1)],void 0,!0),_:1},8,["disabled"])),[[C,r.doing_action]])]),_:1},8,["message"])],64)):(j(),L("span",Bt,I(s.$t("Require FluentCRM Pro")),1))],64)):"update_custom_fields"==r.select_job.action_name?(j(),E(g,{key:8,options:i.options,theme_mode:i.theme_mode,showPopover:r.showCustomFieldPopover,onUpdateCustomField:d.updateCustomFieldValue,onOpenPopover:o[15]||(o[15]=e=>r.showCustomFieldPopover=!0),onClosePopover:o[16]||(o[16]=e=>r.showCustomFieldPopover=!1)},null,8,["options","theme_mode","showPopover","onUpdateCustomField"])):d.currentAction&&d.currentAction.custom_options?(j(),L(N,{key:9},[J(m,{filterable:"",clearable:"",size:"small",multiple:d.currentAction.is_multiple,"collapse-tags":d.currentAction.is_multiple,"max-collapse-tags":d.currentAction.is_multiple?2:void 0,class:"fcrm_bulk_select",placeholder:s.$t("Select"),"popper-class":d.bulkSelectPopperClass,effect:"dark",modelValue:r.select_status,"onUpdate:modelValue":o[17]||(o[17]=e=>r.select_status=e)},{default:W(()=>[(j(!0),L(N,null,H(d.currentAction.custom_options,(e,t)=>(j(),E(c,{key:t,value:t,label:e},null,8,["value","label"]))),128))],void 0),_:1},8,["multiple","collapse-tags","max-collapse-tags","placeholder","popper-class","modelValue"]),Y((j(),E(h,{disabled:r.doing_action||!r.select_status,onClick:o[18]||(o[18]=e=>d.handleBulkActionRoute()),type:"primary",size:"small"},{default:W(()=>[q("span",jt,[J(p,null,{default:W(()=>[J(u)],void 0,!0),_:1})]),te(" "+I(s.$t("Confirm")),1)],void 0),_:1},8,["disabled"])),[[C,r.doing_action]])],64)):z("",!0)]),d.canSelectAll?(j(),L("div",Lt,[r.allSelected?(j(),L("p",It,[te(I(s.$t("All %s contacts selected.",s.formatMoney(i.pagination.total)))+" ",1),J(h,{size:"small",text:"",onClick:o[19]||(o[19]=e=>r.allSelected=!1)},{default:W(()=>[te(I(s.$t("Select only this page")),1)],void 0),_:1})])):(j(),L("p",zt,[te(I(s.$t("%s contacts selected.",s.formatMoney(this.selectedSubscribers.length)))+" ",1),J(h,{size:"small",text:"",onClick:o[20]||(o[20]=e=>r.allSelected=!0)},{default:W(()=>[te(I(s.$t("Select all %s contacts",s.formatMoney(i.pagination.total))),1)],void 0),_:1})]))])):z("",!0),J(k,{modelValue:r.processingAllBulk,"onUpdate:modelValue":o[21]||(o[21]=e=>r.processingAllBulk=e),width:"450px","append-to-body":!0,"close-on-click-modal":!1,"modal-class":"fcrm_bulk_processing_dialog"},{default:W(()=>[r.processingAllBulk?(j(),L("div",Ut,[q("h3",qt,[te(I(s.$t("Action:"))+" "+I(d.currentAction.label)+" ",1),J(p,{class:"spin"},{default:W(()=>[J(v)],void 0,!0),_:1})]),q("div",Yt,I(s.$t("Please do not close this window while processing this bulk action")),1),q("div",Et,[J(w,{striped:"","striped-flow":"","text-inside":!1,"stroke-width":8,percentage:parseInt(r.processCount/i.pagination.total*100)},null,8,["percentage"])]),q("div",Wt,I(s.$t("Processing %s of %s contacts",s.formatMoney(r.processCount),s.formatMoney(i.pagination.total))),1)])):z("",!0)],void 0),_:1},8,["modelValue"])])}]]),Ht={name:"ContactsLoader",props:{columns:{type:Array,default:()=>[]},hasCompanyModule:{type:Boolean,default:!1},hasCommerceFields:{type:Boolean,default:!1},custom_fields:{type:Array,default:()=>[]},showSelection:{type:Boolean,default:!0},segmentColumnWidths:{type:Object,default:()=>({})},segmentColumnMinWidth:{type:Number,default:300},compactView:{type:Boolean,default:!1}},data:()=>({skeletonRows:[{id:1},{id:2},{id:3},{id:4},{id:5},{id:6},{id:7},{id:8},{id:9},{id:10}]})},Jt={class:"fcrm_contact_cell fcrm_contact_cell--skeleton"},Gt={style:{flex:"1",display:"flex","flex-direction":"column",gap:"0"}},Kt={class:"fcrm_pills fcrm_pills--skeleton"},Zt={class:"fcrm_pills fcrm_pills--skeleton"},Qt={class:"fcrm_company_cell fcrm_company_cell--skeleton"},Xt={class:"fcrm_datetime_cell--skeleton"},el={class:"fcrm_datetime_cell--skeleton"},tl={class:"fcrm_datetime_cell--skeleton"};function ll(e){if(null==e)return"";try{return(new Intl.NumberFormat).format(Number(e))}catch(t){return String(e)}}const al={class:"fcrm_horizontal_filter_actions"},sl={class:"fcrm_empty_state"},ol={class:"fcrm_empty_state_text"},il=["title","src","alt"],nl={class:"fcrm_contact_info"},rl={class:"fcrm_contact_name"},dl={class:"fcrm_contact_email"},cl={class:"d-flex gap-4 flex-wrap"},ml=["title"],ul={class:"fcrm_badge fcrm_segment_more_badge"},pl={key:1},hl={class:"d-flex gap-4 flex-wrap"},_l={class:"fcrm_badge fcrm_segment_more_badge"},fl={key:1},bl={key:0,class:"fcrm_company_photo"},yl=["src"],gl={style:{flex:"1"}},vl={key:1},wl={key:0},kl={key:1},Cl={key:0},xl={key:1},$l={key:0},Vl={key:1},Sl=["title"],Al={key:1},Fl={key:0},Pl=["title"],Tl=["title"],Rl=["title"],Dl={class:"icon"},Ol={key:0,class:"fcrm_selection_count_text"},Bl={class:"fcrm_selection_count_number"},Ml={class:"fcrm_selection_count_text"};const jl=ue({name:"ContactsTable",components:{Badge:Fe,ContactsLoader:ue(Ht,[["render",function(e,t,l,a,s,o){const i=u,n=p,r=A,d=F;return j(),E(d,{class:G(["fcrm_contacts_table fcrm_contacts_table--skeleton",{"fcrm_contacts_table--compact":l.compactView}]),data:s.skeletonRows,style:{width:"100%"},stripe:""},{default:W(()=>[l.showSelection?(j(),E(r,{key:0,width:"40"},{default:W(()=>[J(n,{animated:!0,style:{width:"16px"}},{template:W(()=>[J(i,{variant:"rect",style:{width:"16px",height:"16px","border-radius":"2px"}})]),_:1})]),_:1})):z("",!0),J(r,{label:e.$t("Contact"),"min-width":"300"},{default:W(()=>[q("div",Jt,[J(n,{animated:!0,style:{width:"36px",display:"flex",gap:"12px","flex-direction":"unset"}},{template:W(()=>[J(i,{variant:"circle",style:{width:"36px",height:"36px",flex:"none"}}),q("div",Gt,[J(i,{variant:"text",style:{flex:"1",height:"14px"}}),J(i,{variant:"text",style:{width:"50%",height:"14px"}})])]),_:1})])]),_:1},8,["label"]),-1!=l.columns.indexOf("prefix")?(j(),E(r,{key:1,label:e.$t("Prefix"),"min-width":"70"},{default:W(()=>[J(n,{animated:!0},{template:W(()=>[J(i,{variant:"text",style:{width:"40px",height:"14px"}})]),_:1})]),_:1},8,["label"])):z("",!0),-1!=l.columns.indexOf("first_name")?(j(),E(r,{key:2,label:e.$t("First Name"),"min-width":"140"},{default:W(()=>[J(n,{animated:!0},{template:W(()=>[J(i,{variant:"text",style:{width:"80px",height:"14px"}})]),_:1})]),_:1},8,["label"])):z("",!0),-1!=l.columns.indexOf("last_name")?(j(),E(r,{key:3,label:e.$t("Last Name"),"min-width":"140"},{default:W(()=>[J(n,{animated:!0},{template:W(()=>[J(i,{variant:"text",style:{width:"80px",height:"14px"}})]),_:1})]),_:1},8,["label"])):z("",!0),-1!=l.columns.indexOf("lists")?(j(),E(r,{key:4,label:e.$t("Lists"),width:l.segmentColumnWidths.lists,"min-width":l.segmentColumnMinWidth},{default:W(()=>[q("div",Kt,[J(n,{animated:!0},{template:W(()=>[J(i,{variant:"button",style:{width:"50px",height:"22px","border-radius":"11px"}}),J(i,{variant:"button",style:{width:"60px",height:"22px","border-radius":"11px"}})]),_:1})])]),_:1},8,["label","width","min-width"])):z("",!0),-1!=l.columns.indexOf("tags")?(j(),E(r,{key:5,label:e.$t("Tags"),width:l.segmentColumnWidths.tags,"min-width":l.segmentColumnMinWidth},{default:W(()=>[q("div",Zt,[J(n,{animated:!0},{template:W(()=>[J(i,{variant:"button",style:{width:"55px",height:"22px","border-radius":"11px"}}),J(i,{variant:"button",style:{width:"65px",height:"22px","border-radius":"11px"}})]),_:1})])]),_:1},8,["label","width","min-width"])):z("",!0),l.hasCompanyModule&&-1!=l.columns.indexOf("companies")?(j(),E(r,{key:6,"min-width":"230",label:e.$t("Companies")},{default:W(()=>[J(n,{animated:!0},{template:W(()=>[J(i,{variant:"text",style:{width:"120px",height:"14px"}})]),_:1})]),_:1},8,["label"])):z("",!0),l.hasCompanyModule&&-1!=l.columns.indexOf("primary_company")?(j(),E(r,{key:7,"min-width":"220",label:e.$t("Primary Company")},{default:W(()=>[q("div",Qt,[J(n,{animated:!0,style:{width:"28px"}},{template:W(()=>[J(i,{variant:"circle",style:{width:"28px",height:"28px"}})]),_:1}),J(n,{animated:!0,style:{"margin-left":"8px"}},{template:W(()=>[J(i,{variant:"text",style:{width:"100px",height:"14px"}})]),_:1})])]),_:1},8,["label"])):z("",!0),l.hasCommerceFields?(j(),L(N,{key:8},[-1!=l.columns.indexOf("commerce.total_order_value")?(j(),E(r,{key:0,"min-width":"120",label:e.$t("Lifetime Value")},{default:W(()=>[J(n,{animated:!0},{template:W(()=>[J(i,{variant:"text",style:{width:"60px",height:"14px"}})]),_:1})]),_:1},8,["label"])):z("",!0),-1!=l.columns.indexOf("commerce.total_order_count")?(j(),E(r,{key:1,"min-width":"120",label:e.$t("Order Count")},{default:W(()=>[J(n,{animated:!0},{template:W(()=>[J(i,{variant:"text",style:{width:"40px",height:"14px"}})]),_:1})]),_:1},8,["label"])):z("",!0),-1!=l.columns.indexOf("commerce.first_order_date")?(j(),E(r,{key:2,"min-width":"180",label:e.$t("Customer Since")},{default:W(()=>[J(n,{animated:!0},{template:W(()=>[J(i,{variant:"text",style:{width:"100px",height:"14px"}})]),_:1})]),_:1},8,["label"])):z("",!0),-1!=l.columns.indexOf("commerce.last_order_date")?(j(),E(r,{key:3,"min-width":"180",label:e.$t("Last order")},{default:W(()=>[J(n,{animated:!0},{template:W(()=>[J(i,{variant:"text",style:{width:"80px",height:"14px"}})]),_:1})]),_:1},8,["label"])):z("",!0)],64)):z("",!0),-1!=l.columns.indexOf("phone")?(j(),E(r,{key:9,"min-width":"120",label:e.$t("Phone")},{default:W(()=>[J(n,{animated:!0},{template:W(()=>[J(i,{variant:"text",style:{width:"90px",height:"14px"}})]),_:1})]),_:1},8,["label"])):z("",!0),-1!=l.columns.indexOf("date_of_birth")?(j(),E(r,{key:10,"min-width":"120",label:e.$t("Date of Birth")},{default:W(()=>[J(n,{animated:!0},{template:W(()=>[J(i,{variant:"text",style:{width:"80px",height:"14px"}})]),_:1})]),_:1},8,["label"])):z("",!0),-1!=l.columns.indexOf("address_line_1")?(j(),E(r,{key:11,"min-width":"160",label:e.$t("Address Line 1")},{default:W(()=>[J(n,{animated:!0},{template:W(()=>[J(i,{variant:"text",style:{width:"120px",height:"14px"}})]),_:1})]),_:1},8,["label"])):z("",!0),-1!=l.columns.indexOf("address_line_2")?(j(),E(r,{key:12,"min-width":"160",label:e.$t("Address Line 2")},{default:W(()=>[J(n,{animated:!0},{template:W(()=>[J(i,{variant:"text",style:{width:"120px",height:"14px"}})]),_:1})]),_:1},8,["label"])):z("",!0),-1!=l.columns.indexOf("city")?(j(),E(r,{key:13,"min-width":"120",label:e.$t("City")},{default:W(()=>[J(n,{animated:!0},{template:W(()=>[J(i,{variant:"text",style:{width:"70px",height:"14px"}})]),_:1})]),_:1},8,["label"])):z("",!0),-1!=l.columns.indexOf("state")?(j(),E(r,{key:14,"min-width":"120",label:e.$t("State")},{default:W(()=>[J(n,{animated:!0},{template:W(()=>[J(i,{variant:"text",style:{width:"60px",height:"14px"}})]),_:1})]),_:1},8,["label"])):z("",!0),-1!=l.columns.indexOf("postal_code")?(j(),E(r,{key:15,"min-width":"120",label:e.$t("Zip Code")},{default:W(()=>[J(n,{animated:!0},{template:W(()=>[J(i,{variant:"text",style:{width:"50px",height:"14px"}})]),_:1})]),_:1},8,["label"])):z("",!0),-1!=l.columns.indexOf("country")?(j(),E(r,{key:16,"min-width":"120",label:e.$t("Country")},{default:W(()=>[J(n,{animated:!0},{template:W(()=>[J(i,{variant:"text",style:{width:"80px",height:"14px"}})]),_:1})]),_:1},8,["label"])):z("",!0),-1!=l.columns.indexOf("contact_type")?(j(),E(r,{key:17,width:"150",label:e.$t("Type")},{default:W(()=>[J(n,{animated:!0},{template:W(()=>[J(i,{variant:"text",style:{width:"60px",height:"14px"}})]),_:1})]),_:1},8,["label"])):z("",!0),-1!=l.columns.indexOf("status")?(j(),E(r,{key:18,width:"150",label:e.$t("Status")},{default:W(()=>[J(n,{animated:!0},{template:W(()=>[J(i,{variant:"button",style:{width:"70px",height:"24px","border-radius":"4px"}})]),_:1})]),_:1},8,["label"])):z("",!0),(j(!0),L(N,null,H(l.custom_fields,e=>(j(),E(r,{width:"200",key:e.slug,label:e.label},{default:W(()=>[J(n,{animated:!0},{template:W(()=>[J(i,{variant:"text",style:{width:"100px",height:"14px"}})]),_:1})]),_:1},8,["label"]))),128)),-1!=l.columns.indexOf("source")?(j(),E(r,{key:19,"min-width":"150",label:e.$t("Source")},{default:W(()=>[J(n,{animated:!0},{template:W(()=>[J(i,{variant:"text",style:{width:"70px",height:"14px"}})]),_:1})]),_:1},8,["label"])):z("",!0),-1!=l.columns.indexOf("last_activity")?(j(),E(r,{key:20,"min-width":"190",label:e.$t("Last Activity")},{default:W(()=>[q("div",Xt,[J(n,{animated:!0},{template:W(()=>[J(i,{variant:"text",style:{width:"80px",height:"14px"}})]),_:1})])]),_:1},8,["label"])):z("",!0),-1!=l.columns.indexOf("created_at")?(j(),E(r,{key:21,"min-width":"190",label:e.$t("Date Added")},{default:W(()=>[q("div",el,[J(n,{animated:!0},{template:W(()=>[J(i,{variant:"text",style:{width:"80px",height:"14px"}})]),_:1})])]),_:1},8,["label"])):z("",!0),-1!=l.columns.indexOf("updated_at")?(j(),E(r,{key:22,"min-width":"190",label:e.$t("Last Changed")},{default:W(()=>[q("div",tl,[J(n,{animated:!0},{template:W(()=>[J(i,{variant:"text",style:{width:"80px",height:"14px"}})]),_:1})])]),_:1},8,["label"])):z("",!0)],void 0),_:1},8,["class","data"])}]]),Toggler:yt,Manager:_t,Searcher:ye,PropertyChanger:gt,PaginationBar:ge,DataTable:ve,FloatingBulkActionShell:we,BulkContactActions:Nt,FilterPopover:xe,ActiveFiltersBar:$e,Adder:rt,CopyDocument:B,Plus:O,ArrowDown:g,Delete:D,Download:R,Fold:T,Expand:P,Icons:pe},props:["subscribers","always_searchbar","show_skeleton","is_loading","query_data","pagination","options","ui_config","fire_ready_fetch","filter_type"],emits:["export-selected","fetch"],data:()=>({selection:!1,selectedSubscribers:[],selectionCount:0,matched_tags:[],matched_lists:[],matched_statuses:[],columns:[],hasCustomFields:!1,hasCommerceFields:!1,countries:window.fcAdmin.countries,copiedText:{id:"",title:""},pageSizes:[10,20,50,80,100,120,150],localPerPage:10,segmentColumnMinWidth:300,segmentColumnWidths:{lists:void 0,tags:void 0},compactView:!1,compactRelationLimit:2,showSearcher:!1,showAdder:!1,selectedBulkAction:null,allSelected:!1,current_mode:"system"===_e.getCurrentTheme()?_e.getSystemTheme():_e.getCurrentTheme()}),computed:{totalPages(){const e=Number(this.pagination&&this.pagination.per_page)||Number(this.localPerPage)||10,t=Number(this.pagination&&this.pagination.total)||0;return Math.max(1,Math.ceil(t/e))},custom_fields(){const e=this.appVars.contact_custom_fields;return e?e.filter(e=>-1!==this.columns.indexOf(e.slug)):[]},paginationPageSizes(){var e;return"subscribers"===(null==(e=this.$route)?void 0:e.name)?[10,20,50,100,150,300,600]:[]},canSelectAll(){if(!this.pagination||!this.pagination.per_page||!this.pagination.total)return!1;const e="subscribers"===this.$route.name,t=this.selectedSubscribers.length===this.pagination.per_page,l=this.selectedSubscribers.length0&&e.length!==this.pagination.per_page&&(this.allSelected=!1,this.$refs.bulkActionsRef&&(this.$refs.bulkActionsRef.allSelected=!1));const t={},l={};e.forEach(e=>{e.tags.forEach(e=>this.match(e,t)),e.lists.forEach(e=>this.match(e,l))}),this.selectionCount=e.length,this.matched_tags=t,this.matched_lists=l},match(e,t){t[e.slug]?t[e.slug]++:t[e.slug]=1},handleSortable(e){"descending"===e.order?(this.query_data.sort_by=e.prop,this.query_data.sort_type="DESC"):(this.query_data.sort_by=e.prop,this.query_data.sort_type="ASC"),this.fetch()},statusClass(e){switch((e||"").toString().toLowerCase()){case"subscribed":return"fcrm_badge--success";case"unsubscribed":default:return"fcrm_badge--neutral";case"pending":return"fcrm_badge--away";case"transactional":return"fcrm_badge--feature";case"bounced":return"fcrm_badge--info";case"complained":return"fcrm_badge--warning";case"spammed":return"fcrm_badge--error"}},subscribe({type:e,payload:t}){const{attach:l,detach:a}=t;this.loading=!0;const s={type:e,attach:l,detach:a,subscribers:this.selectedSubscribers.map(e=>e.id)};this.$post("subscribers/sync-segments",s).then(t=>{t.subscribers.forEach(e=>{const t=this.subscribers.findIndex(t=>t.id===e.id);-1!==t&&this.subscribers.splice(t,1,e),this.$refs.subscribersTable.toggleRowSelection(this.subscribers[t])});const l=`selected_${e}`;if(this[l]&&this[l].length&&a&&a.length){const t=this[l].filter(e=>a.includes(e));t.length&&this.filter({type:e,payload:t})}this.loading=!1,this.$notify.success({title:this.$t("Great!"),message:t.message,offset:19})})},maybeReFetch(){let e=!1;if(!this.hasCommerceFields&&this.appVars.commerce_provider){this.columns.filter(e=>0===e.indexOf("commerce.")).length&&(e=!0,this.hasCommerceFields=!0,this.query_data.has_commerce=!0)}-1!==this.columns.indexOf("primary_company")&&(this.query_data.primary_company=!0,e=!0),!this.hasCustomFields&&this.custom_fields.length&&(this.query_data.custom_fields=!0,this.hasCustomFields=!0,e=!0),e&&this.fetch()},filter({type:e,payload:t}){return this.query_data[e]=t,this.pagination.current_page=1,this.fetch()},search(e,t){this.options[e]=t},listeners(){this.addAction("search-subscribers","fluentcrm",e=>{this.query_data.search=e,this.pagination.current_page=1,this.fetch()}),this.addAction("loading","fluentcrm",e=>{this.loading=e})},countryName(e){return function(e,t=[]){const l=t.find(t=>t.code===e);return l?l.title:""}(e,this.countries)},copyText(e,t){if(this.copiedText.id=t,!e)return void(this.copiedText.title=this.$t("Nothing to copy"));const l=Re(e);this.copiedText.title=l?this.$t("Copied"):this.$t("Failed to copy")},handleFilterApply(e){this.query_data.lists=e.lists||[],this.query_data.tags=e.tags||[],this.query_data.statuses=e.statuses||[],this.query_data.sms_statuses=e.sms_statuses||[],this.pagination.current_page=1,this.fetch()},handleFilterBarChange(e){this.query_data.lists=e.lists||[],this.query_data.tags=e.tags||[],this.query_data.statuses=e.statuses||[],this.query_data.sms_statuses=e.sms_statuses||[],void 0!==e.search&&(this.query_data.search=e.search),this.pagination.current_page=1,this.fetch()},handleOpenFilter(e){this.$nextTick(()=>{var t,l;null==(l=null==(t=this.$refs.filterPopoverRef)?void 0:t.openFilterCategory)||l.call(t,e)})},handleAdderFetch(){this.showAdder=!1,this.fetch()},handleDeleteSelected(){this.selectedBulkAction="delete_contacts",this.$nextTick(()=>{this.$refs.bulkActionsRef&&(this.$refs.bulkActionsRef.select_job.action_name="delete_contacts",this.$refs.bulkActionsRef.handleBulkActionRoute())})},handleExportSelected(){this.$emit("export-selected",{subscribers:this.selectedSubscribers,allSelected:this.allSelected,query_data:this.query_data})},handleSelectAll(){this.canSelectAll&&(this.$refs.subscribersTable&&this.subscribers&&this.subscribers.length>0&&this.subscribers.forEach(e=>{this.$refs.subscribersTable.toggleRowSelection(e,!0)}),this.allSelected=!0,this.$nextTick(()=>{this.$refs.bulkActionsRef&&(this.$refs.bulkActionsRef.allSelected=!0,this.$refs.bulkActionsRef.$forceUpdate())}))},handleSelectOnlyThisPage(){this.allSelected=!1,this.$refs.bulkActionsRef&&(this.$refs.bulkActionsRef.allSelected=!1,this.$refs.bulkActionsRef.$forceUpdate())},handleDeselectAll(){this.$refs.subscribersTable&&this.$refs.subscribersTable.clearSelection(),this.selection=!1,this.selectedSubscribers=[],this.selectedBulkAction=null,this.allSelected=!1,this.$refs.bulkActionsRef&&(this.$refs.bulkActionsRef.allSelected=!1,this.$refs.bulkActionsRef.select_job.action_name="",this.$refs.bulkActionsRef.select_job.selected_options=[],this.$refs.bulkActionsRef.select_status="")},handleBulkActionRefetch(){ce().invalidateCache(),this.selectedBulkAction=null,this.fetch()},formatMoney:ll,getStoredSegmentColumnWidths(){const e=this.storage.get("contact_segment_column_widths",{}),t={};return["lists","tags"].forEach(l=>{const a=parseInt(e[l],10);a&&(t[l]=Math.max(a,this.segmentColumnMinWidth))}),t},initializeSegmentColumnWidths(){const e=this.getStoredSegmentColumnWidths();this.segmentColumnWidths={lists:e.lists,tags:e.tags}},initializeCompactView(){this.compactView="yes"===this.storage.get("contact_compact_table_view","no")},setCompactView(e){this.compactView=!!e,this.storage.set("contact_compact_table_view",this.compactView?"yes":"no"),this.$nextTick(()=>{var e;null==(e=this.$refs.subscribersTable)||e.doLayout()})},toggleCompactView(){this.setCompactView(!this.compactView)},handleColumnResize(e,t,l){if(!l||-1===["lists","tags"].indexOf(l.property))return;const a=Math.max(parseInt(e,10)||this.segmentColumnMinWidth,this.segmentColumnMinWidth);this.segmentColumnWidths={...this.segmentColumnWidths,[l.property]:a},this.storage.set("contact_segment_column_widths",this.segmentColumnWidths)},resetTablePreferences(){this.storage.remove("contact_segment_column_widths"),this.storage.remove("contact_compact_table_view"),this.segmentColumnWidths={lists:void 0,tags:void 0},this.compactView=!1,this.$nextTick(()=>{var e;null==(e=this.$refs.subscribersTable)||e.doLayout()}),this.$notify.success({title:this.$t("Done"),message:this.$t("Table preferences have been reset"),offset:19})},initializePerPage(){const e=parseInt(this.storage.get("contact_perpage",10))||10;this.pagination&&this.pagination.per_page?this.localPerPage=this.pagination.per_page:(this.localPerPage=e,this.pagination&&(this.pagination.per_page=e))}},watch:{"pagination.per_page":{handler(e){e&&e!==this.localPerPage&&(this.localPerPage=e)},immediate:!0},pagination:{handler(e){e&&e.per_page&&e.per_page!==this.localPerPage&&(this.localPerPage=e.per_page)},deep:!0,immediate:!0}},created(){this.initializePerPage(),this.initializeSegmentColumnWidths(),this.initializeCompactView()},mounted(){this.initializePerPage(),this.listeners(),this.onThemeChanged=e=>{var t;this.current_mode=(null==(t=e.detail)?void 0:t.effective)||_e.getCurrentTheme()},window.addEventListener(he,this.onThemeChanged),this.syncAllSelectedInterval=setInterval(()=>{this.$refs.bulkActionsRef&&this.$refs.bulkActionsRef.allSelected!==this.allSelected&&(this.allSelected=this.$refs.bulkActionsRef.allSelected)},100)},beforeUnmount(){this.syncAllSelectedInterval&&clearInterval(this.syncAllSelectedInterval),this.onThemeChanged&&window.removeEventListener(he,this.onThemeChanged)}},[["render",function(e,t,l,s,o,i){const n=U("searcher"),r=U("filter-popover"),d=U("toggler"),c=U("Expand"),m=U("Fold"),u=f,p=a,h=M,_=U("active-filters-bar"),b=U("ContactsLoader"),y=U("icons"),g=A,v=U("CopyDocument"),w=U("router-link"),k=U("Badge"),C=F,x=U("pagination-bar"),$=U("data-table"),V=U("bulk-contact-actions"),S=U("Icons"),P=U("floating-bulk-action-shell");return j(),L("div",{class:G([{fcrm_has_selections:o.selectedSubscribers.length},"fcrm_company_contacts_wrap"]),style:{position:"relative"}},[J($,{"has-selection":!1},se({"header-left":W(()=>[J(n,{disabled:"advanced"==l.filter_type,modelValue:l.query_data.search,"onUpdate:modelValue":t[0]||(t[0]=e=>l.query_data.search=e)},null,8,["disabled","modelValue"])]),"header-actions":W(()=>[le(e.$slots,"before_search_box"),J(r,{ref:"filterPopoverRef",disabled:"advanced"===l.filter_type,options:{lists:l.ui_config.show_list?l.options.lists:[],tags:l.ui_config.show_tag?l.options.tags:[],statuses:l.options.statuses,sms_statuses:l.options.sms_statuses},"selected-filters":l.query_data,onApply:i.handleFilterApply},null,8,["disabled","options","selected-filters","onApply"]),q("div",al,[J(d,{onDataChanged:t[1]||(t[1]=e=>i.maybeReFetch()),modelValue:o.columns,"onUpdate:modelValue":t[2]||(t[2]=e=>o.columns=e)},null,8,["modelValue"]),J(h,{placement:"bottom",content:o.compactView?e.$t("Default View"):e.$t("Compact View")},{default:W(()=>[J(p,{size:"small",class:G(["small only-icon-btn fcrm_compact_view_toggle",{"is-active":o.compactView}]),"aria-label":o.compactView?e.$t("Default View"):e.$t("Compact Compact View"),onClick:i.toggleCompactView},{default:W(()=>[J(u,null,{default:W(()=>[o.compactView?(j(),E(c,{key:0})):(j(),E(m,{key:1}))],void 0,!0),_:1})],void 0,!0),_:1},8,["class","aria-label","onClick"])],void 0,!0),_:1},8,["content"])]),le(e.$slots,"after_search_box")]),"active-filters":W(()=>["advanced"!==l.filter_type?(j(),E(_,{key:0,"selected-filters":l.query_data,options:{lists:l.ui_config.show_list?l.options.lists:[],tags:l.ui_config.show_tag?l.options.tags:[],statuses:l.options.statuses,sms_statuses:l.options.sms_statuses},onFilterChange:i.handleFilterBarChange,onOpenFilter:i.handleOpenFilter,plus_filter_icon:!0},null,8,["selected-filters","options","onFilterChange","onOpenFilter"])):z("",!0)]),table:W(()=>[le(e.$slots,"before_contacts_table"),l.show_skeleton?(j(),E(b,{key:0,columns:o.columns,"has-company-module":e.has_company_module,hasCommerceFields:o.hasCommerceFields,custom_fields:i.custom_fields,showSelection:e.hasPermission("fcrm_manage_contacts"),segmentColumnWidths:o.segmentColumnWidths,segmentColumnMinWidth:o.segmentColumnMinWidth,compactView:o.compactView},null,8,["columns","has-company-module","hasCommerceFields","custom_fields","showSelection","segmentColumnWidths","segmentColumnMinWidth","compactView"])):(j(),E(C,{key:1,class:G(["fcrm_contacts_table",{"fcrm_contacts_table--compact":o.compactView}]),"default-sort":{prop:l.query_data.sort_by,order:"DESC"==l.query_data.sort_type?"descending":"ascending"},data:l.subscribers,border:"",id:"fluentcrm-subscribers-table",onSelectionChange:i.onSelection,style:{width:"100%"},stripe:"",onSortChange:i.handleSortable,onHeaderDragend:i.handleColumnResize,ref:"subscribersTable"},{empty:W(()=>[q("div",sl,[J(y,{"icon-name":"common-empty-state"}),q("div",ol,[q("span",null,I(e.$t("Please create a contact to view")),1)])])]),default:W(()=>[e.hasPermission("fcrm_manage_contacts")?(j(),E(g,{key:0,type:"selection",width:"50"})):z("",!0),J(g,{label:e.$t("Contact"),"min-width":"300",property:"first_name",sortable:"custom"},{default:W(t=>[J(w,{class:"fcrm_contact_cell",to:{name:"subscriber",params:{id:t.row.id}}},{default:W(()=>[q("img",{title:e.$t("Contact ID:")+" "+t.row.id,class:"fcrm_contact_photo",src:t.row.photo,alt:t.row.full_name},null,8,il),q("div",nl,[q("div",rl,I(t.row.full_name),1),q("div",dl,[te(I(t.row.email)+" ",1),J(h,{placement:"top",content:o.copiedText.id==t.row.id?o.copiedText.title:e.$t("Copy Email")},{default:W(()=>[J(u,{class:"fcrm_copy-text",role:"button",tabindex:"0","aria-label":e.$t("Copy Email"),onClick:ie(e=>i.copyText(t.row.email,t.row.id),["stop","prevent"]),onKeydown:[oe(ie(e=>i.copyText(t.row.email,t.row.id),["prevent","stop"]),["enter"]),oe(ie(e=>i.copyText(t.row.email,t.row.id),["prevent","stop"]),["space"])]},{default:W(()=>[J(v)],void 0,!0),_:1},8,["aria-label","onClick","onKeydown"])],void 0,!0),_:2},1032,["content"])])])],void 0,!0),_:2},1032,["to"])]),_:1},8,["label"]),-1!=o.columns.indexOf("prefix")?(j(),E(g,{key:1,label:e.$t("Prefix"),"min-width":"70",property:"prefix"},{default:W(e=>[te(I(e.row.prefix),1)]),_:1},8,["label"])):z("",!0),-1!=o.columns.indexOf("first_name")?(j(),E(g,{key:2,label:e.$t("First Name"),"min-width":"140",property:"first_name",sortable:"custom"},{default:W(e=>[te(I(e.row.first_name),1)]),_:1},8,["label"])):z("",!0),-1!=o.columns.indexOf("last_name")?(j(),E(g,{key:3,label:e.$t("Last Name"),"min-width":"140",property:"last_name",sortable:"custom"},{default:W(e=>[te(I(e.row.last_name),1)]),_:1},8,["label"])):z("",!0),-1!=o.columns.indexOf("lists")?(j(),E(g,{key:4,label:e.$t("Lists"),property:"lists",width:o.segmentColumnWidths.lists,"min-width":o.segmentColumnMinWidth,sortable:!1},{default:W(e=>[q("div",cl,[i.getRelationsArray(e.row,"lists").length?(j(),L(N,{key:0},[(j(!0),L(N,null,H(i.visibleRelations(e.row,"lists"),e=>(j(),L("span",{key:e,class:"fcrm_badge"},[q("span",{title:e},I(e),9,ml)]))),128)),i.hiddenRelations(e.row,"lists").length?(j(),E(h,{key:0,placement:"top",content:i.hiddenRelationsTooltip(e.row,"lists")},{default:W(()=>[q("span",ul,I(i.moreRelationsLabel(e.row,"lists")),1)],void 0,!0),_:2},1032,["content"])):z("",!0)],64)):(j(),L("span",pl,"--"))])]),_:1},8,["label","width","min-width"])):z("",!0),-1!=o.columns.indexOf("tags")?(j(),E(g,{key:5,label:e.$t("Tags"),property:"tags",width:o.segmentColumnWidths.tags,"min-width":o.segmentColumnMinWidth},{default:W(e=>[q("div",hl,[i.getRelationsArray(e.row,"tags").length?(j(),L(N,{key:0},[(j(!0),L(N,null,H(i.visibleRelations(e.row,"tags"),e=>(j(),L("span",{key:e,class:"fcrm_badge"},I(e),1))),128)),i.hiddenRelations(e.row,"tags").length?(j(),E(h,{key:0,placement:"top",content:i.hiddenRelationsTooltip(e.row,"tags")},{default:W(()=>[q("span",_l,I(i.moreRelationsLabel(e.row,"tags")),1)],void 0,!0),_:2},1032,["content"])):z("",!0)],64)):(j(),L("span",fl,"--"))])]),_:1},8,["label","width","min-width"])):z("",!0),-1!=o.columns.indexOf("status")?(j(),E(g,{key:6,label:e.$t("Status"),width:"150",property:"status",sortable:"custom"},{default:W(e=>[J(k,{type:e.row.status},null,8,["type"])]),_:1},8,["label"])):z("",!0),-1!=o.columns.indexOf("sms_status")&&"yes"===e.appVars.sms_enabled?(j(),E(g,{key:7,label:e.$t("SMS Status"),width:"150",property:"sms_status",sortable:"custom"},{default:W(e=>[J(k,{type:e.row.sms_status},null,8,["type"])]),_:1},8,["label"])):z("",!0),-1!=o.columns.indexOf("phone")?(j(),E(g,{key:8,"min-width":"120",label:e.$t("Phone"),property:"phone"},{default:W(t=>[te(I(t.row.phone)+" ",1),t.row.phone?(j(),E(h,{key:0,class:"fcrm_item",effect:"dark",placement:"top",content:o.copiedText.id==t.row.id?o.copiedText.title:e.$t("Copy Phone Number")},{default:W(()=>[J(u,{class:"fcrm_copy-text",role:"button",tabindex:"0","aria-label":e.$t("Copy Phone Number"),onClick:ie(e=>i.copyText(t.row.phone,t.row.id),["stop"]),onKeydown:[oe(ie(e=>i.copyText(t.row.phone,t.row.id),["stop"]),["enter"]),oe(ie(e=>i.copyText(t.row.phone,t.row.id),["prevent","stop"]),["space"])]},{default:W(()=>[J(v)],void 0,!0),_:1},8,["aria-label","onClick","onKeydown"])],void 0,!0),_:2},1032,["content"])):z("",!0)]),_:1},8,["label"])):z("",!0),e.has_company_module&&-1!=o.columns.indexOf("companies")?(j(),E(g,{key:9,"min-width":"230",label:e.$t("Companies"),property:"companies"},{default:W(e=>[q("span",null,I(i.getRelations(e.row,"companies")),1)]),_:1},8,["label"])):z("",!0),e.has_company_module&&-1!=o.columns.indexOf("primary_company")?(j(),E(g,{key:10,"min-width":"220",label:e.$t("Primary Company")},{default:W(e=>[e.row.company?(j(),E(w,{key:0,to:{name:"view_company",params:{company_id:e.row.company_id}},class:"fcrm_company_cell fcrm_photo_text"},{default:W(()=>[e.row.company.logo?(j(),L("span",bl,[q("img",{src:e.row.company.logo},null,8,yl)])):z("",!0),q("span",gl,I(e.row.company.name),1)],void 0,!0),_:2},1032,["to"])):(j(),L("span",vl,"--"))]),_:1},8,["label"])):z("",!0),o.hasCommerceFields?(j(),L(N,{key:11},[-1!=o.columns.indexOf("commerce.total_order_value")?(j(),E(g,{key:0,"min-width":"120",label:e.$t("Lifetime Value"),property:"commerce_by_provider.total_order_value"},{default:W(t=>[t.row.commerce_by_provider?(j(),L("span",wl,I(e.appVars.commerce_currency_sign)+I(t.row.commerce_by_provider.total_order_value||"-"),1)):(j(),L("span",kl,"-"))]),_:1},8,["label"])):z("",!0),-1!=o.columns.indexOf("commerce.total_order_count")?(j(),E(g,{key:1,"min-width":"120",label:e.$t("Order Count"),property:"commerce_by_provider.total_order_count"},{default:W(e=>[e.row.commerce_by_provider?(j(),L("span",Cl,I(e.row.commerce_by_provider.total_order_count||"-"),1)):(j(),L("span",xl,"-"))]),_:1},8,["label"])):z("",!0),-1!=o.columns.indexOf("commerce.first_order_date")?(j(),E(g,{key:2,"min-width":"180",label:e.$t("Customer Since"),property:"commerce_by_provider.first_order_date"},{default:W(t=>[t.row.commerce_by_provider&&t.row.commerce_by_provider.first_order_date?(j(),L("span",$l,I(e.nsDateFormat(t.row.commerce_by_provider.first_order_date)),1)):(j(),L("span",Vl,"-"))]),_:1},8,["label"])):z("",!0),-1!=o.columns.indexOf("commerce.last_order_date")?(j(),E(g,{key:3,"min-width":"180",label:e.$t("Last order"),property:"commerce_by_provider.last_order_date"},{default:W(t=>[t.row.commerce_by_provider&&t.row.commerce_by_provider.last_order_date?(j(),L("span",{key:0,title:t.row.commerce_by_provider.last_order_date},I(e.nsHumanDiffTime(t.row.commerce_by_provider.last_order_date)),9,Sl)):(j(),L("span",Al,"-"))]),_:1},8,["label"])):z("",!0)],64)):z("",!0),-1!=o.columns.indexOf("date_of_birth")?(j(),E(g,{key:12,"min-width":"120",label:e.$t("Date of Birth"),property:"date_of_birth"},{default:W(e=>[te(I("0000-00-00"!==e.row.date_of_birth?e.row.date_of_birth:"-"),1)]),_:1},8,["label"])):z("",!0),-1!=o.columns.indexOf("address_line_1")?(j(),E(g,{key:13,"min-width":"160",label:e.$t("Address Line 1"),property:"address_line_1",sortable:"custom"},{default:W(e=>[te(I(e.row.address_line_1),1)]),_:1},8,["label"])):z("",!0),-1!=o.columns.indexOf("address_line_2")?(j(),E(g,{key:14,"min-width":"160",label:e.$t("Address Line 2"),property:"address_line_2",sortable:"custom"},{default:W(e=>[te(I(e.row.address_line_2),1)]),_:1},8,["label"])):z("",!0),-1!=o.columns.indexOf("city")?(j(),E(g,{key:15,"min-width":"120",label:e.$t("City"),property:"city",sortable:"custom"},{default:W(e=>[te(I(e.row.city),1)]),_:1},8,["label"])):z("",!0),-1!=o.columns.indexOf("state")?(j(),E(g,{key:16,"min-width":"120",label:e.$t("State"),property:"state",sortable:"custom"},{default:W(e=>[te(I(e.row.state),1)]),_:1},8,["label"])):z("",!0),-1!=o.columns.indexOf("postal_code")?(j(),E(g,{key:17,"min-width":"120",label:e.$t("Zip Code"),property:"postal_code",sortable:"custom"},{default:W(e=>[te(I(e.row.postal_code),1)]),_:1},8,["label"])):z("",!0),-1!=o.columns.indexOf("country")?(j(),E(g,{key:18,"min-width":"120",label:e.$t("Country"),property:"country",sortable:"custom"},{default:W(e=>[te(I(i.countryName(e.row.country)),1)]),_:1},8,["label"])):z("",!0),-1!=o.columns.indexOf("contact_type")?(j(),E(g,{key:19,label:e.$t("Type"),width:"150",property:"contact_type",sortable:"custom"},{default:W(t=>[te(I(e.ucWords(e.trans(t.row.contact_type))),1)]),_:1},8,["label"])):z("",!0),(j(!0),L(N,null,H(i.custom_fields,e=>(j(),E(g,{width:"200",key:e.slug,label:e.label},{default:W(t=>[te(I(i.formatCellValue(t.row.custom_fields,e.slug)),1)]),_:2},1032,["label"]))),128)),-1!=o.columns.indexOf("source")?(j(),E(g,{key:20,"min-width":"150",label:e.$t("Source"),property:"source"},{default:W(e=>[te(I(e.row.source),1)]),_:1},8,["label"])):z("",!0),-1!=o.columns.indexOf("last_activity")?(j(),E(g,{key:21,prop:"last_activity",label:e.$t("Last Activity"),"min-width":"190",property:"last_activity",sortable:"custom"},{default:W(t=>[t.row.last_activity?(j(),L("span",Fl,[q("span",{title:t.row.last_activity},I(e.nsHumanDiffTime(t.row.last_activity)),9,Pl)])):z("",!0)]),_:1},8,["label"])):z("",!0),-1!=o.columns.indexOf("created_at")?(j(),E(g,{key:22,label:e.$t("Date Added"),"min-width":"190",property:"created_at",sortable:"custom"},{default:W(t=>[t.row.created_at?(j(),L("span",{key:0,class:"fcrm_secondary_text",title:t.row.created_at},I(e.nsHumanDiffTime(t.row.created_at)),9,Tl)):z("",!0)]),_:1},8,["label"])):z("",!0),-1!=o.columns.indexOf("updated_at")?(j(),E(g,{key:23,label:e.$t("Last Changed"),"min-width":"190",property:"updated_at",sortable:"custom"},{default:W(t=>[t.row.updated_at?(j(),L("span",{key:0,class:"fcrm_secondary_text",title:t.row.updated_at},I(e.nsHumanDiffTime(t.row.updated_at)),9,Rl)):z("",!0)]),_:1},8,["label"])):z("",!0)],void 0,!0),_:1},8,["class","default-sort","data","onSelectionChange","onSortChange","onHeaderDragend"]))]),_:2},[l.show_skeleton?void 0:{name:"pagination",fn:W(()=>[J(x,{pagination:l.pagination,hide_on_single:!1,page_sizes:i.paginationPageSizes,"wrapper-class":["fcrm-contacts-pagination","fcrm-contacts-pagination-bar"],onFetch:i.fetch},null,8,["pagination","page_sizes","onFetch"])]),key:"0"}]),1024),J(P,{visible:o.selection,"theme-mode":o.current_mode,"selected-count":o.selectedSubscribers.length,"selected-label":e.$t("selected"),"show-select-all":i.canSelectAll&&!i.isAllSelected,"show-select-only-page":i.canSelectAll&&i.isAllSelected,"select-all-label":e.$t("Select All %s",i.formatMoney(l.pagination.total)),"select-only-page-label":e.$t("Select only this page"),"deselect-label":e.$t("Deselect"),onSelectAll:i.handleSelectAll,onSelectOnlyPage:i.handleSelectOnlyThisPage,onDeselect:i.handleDeselectAll},{actions:W(()=>[J(V,{ref:"bulkActionsRef",onRefetch:i.handleBulkActionRefetch,pagination:l.pagination,selectedSubscribers:o.selectedSubscribers,options:l.options,theme_mode:o.current_mode},null,8,["onRefetch","pagination","selectedSubscribers","options","theme_mode"]),J(p,{size:"small",onClick:i.handleExportSelected},{default:W(()=>[q("span",Dl,[J(S,{"icon-name":"export"})]),te(" "+I(e.$t("Export")),1)],void 0,!0),_:1},8,["onClick"])]),count:W(()=>[i.isAllSelected?(j(),L("span",Ol,I(e.$t("All %s selected",i.formatMoney(l.pagination.total))),1)):(j(),L(N,{key:1},[q("span",Bl,I(o.selectedSubscribers.length),1),q("span",Ml,I(e.$t("selected")),1)],64))]),_:1},8,["visible","theme-mode","selected-count","selected-label","show-select-all","show-select-only-page","select-all-label","select-only-page-label","deselect-label","onSelectAll","onSelectOnlyPage","onDeselect"])],2)}]]);export{rt as A,jl as C,ot as F,ll as f}; diff --git a/wp-content/plugins/fluent-crm/assets/_CustomSegementSettings.js b/wp-content/plugins/fluent-crm/assets/_CustomSegementSettings.js new file mode 100644 index 0000000..cfd8c7a --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/_CustomSegementSettings.js @@ -0,0 +1 @@ +import{ay as e,k as t}from"./vendor-element-plus.js?ver=3.1.8";import{aQ as i,a6 as s,W as a,X as n,Z as l,J as d,az as r,aa as o,ab as c,a5 as m,a9 as h,a8 as _}from"./vendor.js?ver=3.1.8";import u from"./v3app/src/Modules/Contacts/RichFilters/Filters.js?ver=3.1.8";import{_ as f,I as g}from"./fc-bits-ui.js?ver=3.1.8";const p={class:"fcrm_segment_fields"},v={class:"fcrm_rich_container"},b={class:"fcrm_rich_wrap"},y={class:"fcrm_rich_filter"},C={class:"fcrm_filter_group_header"},$={class:"fcrm_and_label"},E={key:0,class:"fcrm_cond_or"},F={class:"icon","aria-hidden":"true"},R={class:"fcrm_cond_or"},G={class:"icon","aria-hidden":"true"},k={key:0,class:"fcrm_estimated_count"},V={class:"fcrm_estimated_label"},j={class:"fcrm_estimated_badge"},x={class:"fcrm_estimated_badge_text"};const S=f({name:"customSegmentEditor",props:["modelValue","segment_id"],emits:["update:modelValue","loaded","updateSegment"],components:{Icons:g,RichFilter:u},data(){return{fields:{},loading:!1,filters:this.modelValue,estimated_count:"",estimating:!1,require_estimating:!1,FilterLabel:this.$t("Filters.instruction")}},watch:{filters:{deep:!0,handler(){this.$emit("update:modelValue",this.filters),this.require_estimating=!0,this.estimating?this.changed_in_time||(this.changed_in_time=!0):this.getEstimatedCount()}},modelValue(e){this.filters=e}},methods:{fetchFields(){this.loading=!0,this.$get("dynamic-segments/custom-fields").then(e=>{this.fields=e.fields,this.segment_id||(this.settings=e.settings_defaults),this.$emit("loaded")}).catch(e=>{this.handleError(e)}).finally(()=>{this.loading=!1})},getEstimatedCount(){this.estimating=!0,this.$post("dynamic-segments/estimated-contacts",{filters:this.filters}).then(e=>{this.estimated_count=e.count,this.changed_in_time&&(this.changed_in_time=!1,this.getEstimatedCount())}).catch(e=>{this.handleError(e)}).finally(()=>{this.estimating=!1})},addConditionGroup(){this.filters.push([])},maybeRemoveGroup(e){this.filters.length>1&&this.filters.splice(e,1)},fetch(){this.$emit("updateSegment"),this.getEstimatedCount()}},mounted(){this.fetchFields()}},[["render",function(u,f,g,S,z,I){const M=i("rich-filter"),q=i("Icons"),w=t,A=e;return s((a(),n("div",p,[l("div",v,[l("div",b,[(a(!0),n(d,null,r(z.filters,(e,t)=>(a(),n("div",{key:t},[l("div",y,[l("div",C,[l("div",$,o(u.$t("And")),1),c(M,{add_label:z.FilterLabel,button_text:u.$t("Add Property"),onMaybeRemove:e=>I.maybeRemoveGroup(t),items:e,canDeleteGroup:z.filters.length>1},null,8,["add_label","button_text","onMaybeRemove","items","canDeleteGroup"])])]),tI.addConditionGroup())},{default:m(()=>[l("span",F,[c(q,{"icon-name":"plus"})]),h(" "+o(u.$t("OR")),1)],void 0),_:1}),f[3]||(f[3]=l("div",{class:"fcrm_or_divider_line"},null,-1))])):_("",!0)]))),128))]),l("div",R,[f[4]||(f[4]=l("div",{class:"fcrm_or_divider_line"},null,-1)),c(w,{size:"small",onClick:f[1]||(f[1]=e=>I.addConditionGroup())},{default:m(()=>[l("span",G,[c(q,{"icon-name":"plus"})]),h(" "+o(u.$t("OR")),1)],void 0),_:1}),f[5]||(f[5]=l("div",{class:"fcrm_or_divider_line"},null,-1))])]),""!==z.estimated_count?s((a(),n("div",k,[l("span",V,o(u.$t("Estimated Contacts Based on your Selection")),1),l("div",j,[l("span",x,o(z.estimated_count),1)])])),[[A,z.estimating]]):_("",!0)])),[[A,z.loading]])}]]);export{S as C}; diff --git a/wp-content/plugins/fluent-crm/assets/_FormBuilder.js b/wp-content/plugins/fluent-crm/assets/_FormBuilder.js new file mode 100644 index 0000000..863d38c --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/_FormBuilder.js @@ -0,0 +1 @@ +import{W as e,aJ as l,E as t,aw as a,e as o,az as d,aO as i,aF as n,aE as s,C as r,aD as u,aA as p,ax as m,b6 as c,b7 as h,b8 as f,aG as _,k as v,g as y,M as V,ay as b,aL as g,aK as k,at as w,ao as x,aN as U,aY as C,aM as $,a6 as I,L as S}from"./vendor-element-plus.js?ver=3.1.8";import{aQ as T,W as M,Y as z,ay as E,a5 as O,Z as P,a9 as D,aa as H,ab as L,a8 as A,_ as W,X as j,a0 as B,J as F,az as N,a6 as R,ac as Y,$ as G,ax as J,b3 as q,b$ as Q,a7 as K}from"./vendor.js?ver=3.1.8";import{_ as Z,I as X}from"./fc-bits-ui.js?ver=3.1.8";import{O as ee}from"./_OptionSelector.js?ver=3.1.8";import{I as le}from"./input-popover-dropdown2.js?ver=3.1.8";import{k as te}from"./data_config.js?ver=3.1.8";const ae={class:"fcrm-with-label-text"},oe=["innerHTML"],de={key:0,style:{"margin-top":"10px"},class:"fcrm-info-alert"},ie=["innerHTML"];const ne=Z({name:"withLabelField",components:{InfoFilled:e},props:["field"],computed:{shouldHideLabel(){return this.field.hide_label_wrapper||"wp-editor"===this.field.type}}},[["render",function(e,o,d,i,n,s){const r=T("InfoFilled"),u=t,p=l,m=a;return M(),z(m,{class:B(d.field.wrapper_class)},E({default:O(()=>[W(e.$slots,"default"),d.field.inline_help&&!s.shouldHideLabel?(M(),j("div",de,[P("p",{innerHTML:d.field.inline_help},null,8,ie)])):A("",!0)],void 0),_:2},[d.field.label&&!s.shouldHideLabel?{name:"label",fn:O(()=>[P("div",null,[P("span",ae,[D(H(d.field.label)+" ",1),d.field.help?(M(),z(p,{key:0,"popper-class":"sidebar-popper",effect:"dark",placement:"top"},{content:O(()=>[P("div",{innerHTML:d.field.help},null,8,oe)]),default:O(()=>[L(u,{class:"tooltip-icon"},{default:O(()=>[L(r)],void 0,!0),_:1})],void 0,!0),_:1})):A("",!0)])])]),key:"0"}:void 0]),1032,["class"])}]]);const se=Z({name:"InputText",props:["field","modelValue"],emits:["update:modelValue"],data(){return{model:this.modelValue,isInternalUpdate:!1}},watch:{model(e){this.isInternalUpdate||this.$emit("update:modelValue",e)},modelValue(e){this.model!==e&&(this.isInternalUpdate=!0,this.model=e,this.$nextTick(()=>{this.isInternalUpdate=!1}))}}},[["render",function(e,l,t,a,d,i){const n=o;return M(),z(n,{type:t.field.data_type,min:t.field.min,max:t.field.max,placeholder:t.field.placeholder,modelValue:d.model,"onUpdate:modelValue":l[0]||(l[0]=e=>d.model=e)},null,8,["type","min","max","placeholder","modelValue"])}]]),re=["innerHTML"],ue=["innerHTML"];const pe=Z({name:"InputTagList",props:["field","modelValue"],emits:["update:modelValue"],components:{OptionSelector:ee},data(){return{model:this.modelValue,isInternalUpdate:!1}},watch:{model:{handler(e){this.isInternalUpdate||this.$emit("update:modelValue",e)},deep:!0},modelValue:{handler(e){JSON.stringify(this.model)!==JSON.stringify(e)&&(this.isInternalUpdate=!0,this.model=e,this.$nextTick(()=>{this.isInternalUpdate=!1}))},deep:!0}}},[["render",function(e,l,t,o,d,i){const n=T("option-selector"),s=a;return M(),j("div",{class:B(["fcrm_tag_list_wrapper",t.field.wrapper_class])},[L(s,{label:t.field.tag_label},{default:O(()=>[L(n,{modelValue:d.model.tags,"onUpdate:modelValue":l[0]||(l[0]=e=>d.model.tags=e),field:{is_multiple:!0,creatable:!0,option_key:"tags"}},null,8,["modelValue"]),t.field.tag_help?(M(),j("p",{key:0,class:"fcrm_inline_help",innerHTML:t.field.tag_help},null,8,re)):A("",!0)],void 0),_:1},8,["label"]),L(s,{label:t.field.list_label},{default:O(()=>[L(n,{modelValue:d.model.lists,"onUpdate:modelValue":l[1]||(l[1]=e=>d.model.lists=e),field:{is_multiple:!0,creatable:!0,option_key:"lists"}},null,8,["modelValue"]),t.field.list_help?(M(),j("p",{key:0,class:"fcrm_inline_help",innerHTML:t.field.list_help},null,8,ue)):A("",!0)],void 0),_:1},8,["label"])],2)}]]);const me=Z({name:"InlineCheckbox",props:["field","modelValue"],emits:["update:modelValue"],data(){return{model:this.modelValue,isInternalUpdate:!1}},watch:{model(e){this.isInternalUpdate||this.$emit("update:modelValue",e)},modelValue(e){this.model!==e&&(this.isInternalUpdate=!0,this.model=e,this.$nextTick(()=>{this.isInternalUpdate=!1}))}}},[["render",function(e,l,t,a,o,i){const n=d;return M(),z(n,{class:"fcrm-checkbox","true-value":void 0!==t.field.true_value?t.field.true_value:t.field.true_label,"false-value":void 0!==t.field.false_value?t.field.false_value:t.field.false_label,disabled:t.field.disabled,modelValue:o.model,"onUpdate:modelValue":l[0]||(l[0]=e=>o.model=e)},{default:O(()=>[D(H(t.field.checkbox_label),1)],void 0),_:1},8,["true-value","false-value","disabled","modelValue"])}]]),ce={class:"fcrm-input-popover"},he={key:0,class:"input-textarea-value"},fe={key:1,class:"fcrm-input-with-button"},_e={class:"el_pop_data_group"},ve={class:"el_pop_data_headings"},ye=["data-item_index","onClick"],Ve={key:0,class:"pop_doc"},be=["href"],ge={class:"el_pop_data_body"},ke={class:"el_pop_search"},we=["onClick"];const xe=Z({name:"InputTextPopper",props:["field","modelValue"],emits:["update:modelValue"],components:{InputPopover:Z({name:"inputPopover",emits:["update:modelValue"],props:{modelValue:String,placeholder:{type:String,default:""},placement:{type:String,default:"bottom"},icon:{type:String,default:"el-icon-more"},fieldType:{type:String,default:"text"},popper_class:{type:String,default:""},data:Array,attrName:{type:String,default:"attribute_name"},popper_extra:{type:String,default:""},doc_url:{type:String,default:()=>""}},data(){return{model:this.modelValue,activeIndex:"0",visible:!1,searchQuery:"",isInternalUpdate:!1}},watch:{modelValue(e){this.model!==e&&(this.isInternalUpdate=!0,this.model=e,this.$nextTick(()=>{this.isInternalUpdate=!1}))},model(e){this.isInternalUpdate||this.$emit("update:modelValue",e)}},methods:{selectEmoji(e){this.insertShortcode(e.data)},insertShortcode(e){const l=this.$el.querySelector("textarea")||this.$el.querySelector('input[type="text"]');if(!l)return this.model||(this.model=""),this.model?this.model=this.model.trim()+" "+e.replace(/param_name/,this.attrName):this.model+=e.replace(/param_name/,this.attrName),void(this.visible=!1);const t=l.selectionStart,a=l.selectionEnd,o=this.model||"";this.model=o.substring(0,t)+e+o.substring(a),this.$nextTick(()=>{l.selectionStart=l.selectionEnd=t+e.length,l.focus()}),this.visible=!1},filteredShortcodes(e){if(!this.searchQuery)return e;const l=this.searchQuery.toLowerCase(),t={};return Object.entries(e).forEach(([e,a])=>{(e.toLowerCase().includes(l)||a.toLowerCase().includes(l))&&(t[e]=a)}),t}}},[["render",function(e,l,t,a,d,n){const s=o,r=i;return M(),j("div",ce,["textarea"==t.fieldType?(M(),j("div",he,[L(s,{placeholder:t.placeholder,rows:4,type:"textarea",modelValue:d.model,"onUpdate:modelValue":l[0]||(l[0]=e=>d.model=e)},null,8,["placeholder","modelValue"])])):(M(),j("div",fe,[L(s,{class:"fcrm-input-main",placeholder:t.placeholder,modelValue:d.model,"onUpdate:modelValue":l[1]||(l[1]=e=>d.model=e),type:t.fieldType},null,8,["placeholder","modelValue","type"]),L(r,{ref:"input-popover",placement:"right-end","popper-class":"fcrm-smartcodes-popover el-dropdown-list-wrapper "+t.popper_extra,visible:d.visible,"onUpdate:visible":l[3]||(l[3]=e=>d.visible=e),width:"auto",trigger:"click"},{reference:O(()=>[...l[4]||(l[4]=[P("button",{class:"fcrm-input-button",type:"button"},[P("svg",{xmlns:"http://www.w3.org/2000/svg",width:"20",height:"20",viewBox:"0 0 20 20",fill:"none"},[P("path",{d:"M4 14.5V11.725C4 11.4266 3.88147 11.1405 3.6705 10.9295C3.45952 10.7185 3.17337 10.6 2.875 10.6H2.5V9.4H2.875C3.02274 9.4 3.16903 9.3709 3.30552 9.31436C3.44201 9.25783 3.56603 9.17496 3.6705 9.0705C3.77496 8.96603 3.85783 8.84201 3.91436 8.70552C3.9709 8.56903 4 8.42274 4 8.275V5.5C4 4.90326 4.23705 4.33097 4.65901 3.90901C5.08097 3.48705 5.65326 3.25 6.25 3.25H7V4.75H6.25C6.05109 4.75 5.86032 4.82902 5.71967 4.96967C5.57902 5.11032 5.5 5.30109 5.5 5.5V8.575C5.50008 8.89076 5.40051 9.19849 5.21548 9.45435C5.03045 9.71022 4.76939 9.90117 4.4695 10C4.76939 10.0988 5.03045 10.2898 5.21548 10.5456C5.40051 10.8015 5.50008 11.1092 5.5 11.425V14.5C5.5 14.6989 5.57902 14.8897 5.71967 15.0303C5.86032 15.171 6.05109 15.25 6.25 15.25H7V16.75H6.25C5.65326 16.75 5.08097 16.5129 4.65901 16.091C4.23705 15.669 4 15.0967 4 14.5ZM16 11.725V14.5C16 15.0967 15.7629 15.669 15.341 16.091C14.919 16.5129 14.3467 16.75 13.75 16.75H13V15.25H13.75C13.9489 15.25 14.1397 15.171 14.2803 15.0303C14.421 14.8897 14.5 14.6989 14.5 14.5V11.425C14.4999 11.1092 14.5995 10.8015 14.7845 10.5456C14.9696 10.2898 15.2306 10.0988 15.5305 10C15.2306 9.90117 14.9696 9.71022 14.7845 9.45435C14.5995 9.19849 14.4999 8.89076 14.5 8.575V5.5C14.5 5.30109 14.421 5.11032 14.2803 4.96967C14.1397 4.82902 13.9489 4.75 13.75 4.75H13V3.25H13.75C14.3467 3.25 14.919 3.48705 15.341 3.90901C15.7629 4.33097 16 4.90326 16 5.5V8.275C16 8.57337 16.1185 8.85952 16.3295 9.0705C16.5405 9.28147 16.8266 9.4 17.125 9.4H17.5V10.6H17.125C16.8266 10.6 16.5405 10.7185 16.3295 10.9295C16.1185 11.1405 16 11.4266 16 11.725Z",fill:"var(--fc-secondary-text)"})])],-1)])]),default:O(()=>[P("div",_e,[P("div",ve,[P("ul",null,[(M(!0),j(F,null,N(t.data,(e,l)=>(M(),j("li",{"data-item_index":l,key:l,class:B(d.activeIndex==l?"active_item_selected":""),onClick:e=>d.activeIndex=l},H(e.title),11,ye))),128))]),t.doc_url?(M(),j("div",Ve,[P("a",{href:t.doc_url,target:"_blank",rel:"noopener"},H(e.$t("Learn More")),9,be)])):A("",!0)]),P("div",ge,[P("div",ke,[L(s,{modelValue:d.searchQuery,"onUpdate:modelValue":l[2]||(l[2]=e=>d.searchQuery=e),placeholder:e.$t("Search shortcodes..."),clearable:""},null,8,["modelValue","placeholder"])]),(M(!0),j(F,null,N(t.data,(e,l)=>(M(),j("div",{key:l},[R(P("ul",{class:B("el_pop_body_item_"+l)},[(M(!0),j(F,null,N(n.filteredShortcodes(e.shortcodes),(e,l)=>(M(),j("li",{onClick:e=>n.insertShortcode(l),key:l},[D(H(e),1),P("span",null,H(l),1)],8,we))),128))],2),[[Y,d.activeIndex==l]])]))),128))])])],void 0),_:1},8,["popper-class","visible"])]))])}]])},data(){return{model:this.modelValue,smartcodes:window.fcAdmin.globalSmartCodes,isInternalUpdate:!1}},watch:{model(e){this.isInternalUpdate||this.$emit("update:modelValue",e)},modelValue(e){this.model!==e&&(this.isInternalUpdate=!0,this.model=e,this.$nextTick(()=>{this.isInternalUpdate=!1}))}},created(){this.field.context_codes&&window.fcrm_funnel_context_codes&&(this.smartcodes=[...this.smartcodes,...window.fcrm_funnel_context_codes]),window.fcAdmin.extendedSmartCodes&&(this.smartcodes=[...this.smartcodes,...window.fcAdmin.extendedSmartCodes])}},[["render",function(e,l,t,a,o,d){const i=T("input-popover");return M(),z(i,{doc_url:"https://fluentcrm.com/docs/merge-codes-smart-codes-usage/","field-type":t.field.field_type,placeholder:t.field.placeholder,popper_class:t.field.popper_class,data:o.smartcodes,modelValue:o.model,"onUpdate:modelValue":l[0]||(l[0]=e=>o.model=e)},null,8,["field-type","placeholder","popper_class","data","modelValue"])}]]);const Ue=Z({name:"InputRadio",props:["field","modelValue"],emits:["update:modelValue"],data(){return{model:this.modelValue,isInternalUpdate:!1}},watch:{model(e){this.isInternalUpdate||this.$emit("update:modelValue",e)},modelValue(e){this.model!==e&&(this.isInternalUpdate=!0,this.model=e,this.$nextTick(()=>{this.isInternalUpdate=!1}))}}},[["render",function(e,l,t,a,o,d){const i=n,r=s;return M(),z(r,{class:B(["fcrm-radio-group",t.field.wrapper_class]),modelValue:o.model,"onUpdate:modelValue":l[0]||(l[0]=e=>o.model=e)},{default:O(()=>[(M(!0),j(F,null,N(t.field.options,(e,l)=>(M(),z(i,{key:l,value:e.id},{default:O(()=>[D(H(e.label),1)],void 0,!0),_:2},1032,["value"]))),128))],void 0),_:1},8,["class","modelValue"])}]]),Ce={class:"fcrm-template-selector"},$e={class:"fcrm-template-grid"},Ie=["onClick"],Se={class:"fcrm-template-preview"},Te=["src","alt"],Me={class:"fcrm-template-details"},ze={class:"fcrm-template-title"},Ee={class:"fcrm-template-description"},Oe={key:0,class:"fcrm-template-checkmark"};const Pe=Z({name:"InputRadioImage",components:{Select:r},props:["field","modelValue","size"],emits:["update:modelValue"],data(){return{model:this.modelValue,boxSize:this.size||120,isInternalUpdate:!1}},methods:{selectTemplate(e){this.model=e},getTemplateDescription(e){const l=this.field&&this.field.descriptionLimit||5;let t="";if(e.description)t=String(e.description);else if(e.template_info){const l=document.createElement("div");l.innerHTML=e.template_info,t=l.textContent||""}else t="Email template design";t=t.replace(/\s+/g," ").trim();const a=t.split(" ");return a.length>l?a.slice(0,l).join(" ")+"...":t}},watch:{model(e){this.isInternalUpdate||this.$emit("update:modelValue",e)},modelValue(e){this.model!==e&&(this.isInternalUpdate=!0,this.model=e,this.$nextTick(()=>{this.isInternalUpdate=!1}))}}},[["render",function(e,l,a,o,d,i){const n=T("Select"),s=t;return M(),j("div",Ce,[P("div",$e,[(M(!0),j(F,null,N(a.field.options,(e,l)=>(M(),j("div",{key:l,class:B(["fcrm-template-card",{"fcrm-template-selected":d.model==e.id}]),onClick:l=>i.selectTemplate(e.id)},[P("div",Se,[P("img",{src:e.image,alt:e.label},null,8,Te)]),P("div",Me,[P("div",ze,H(e.label),1),P("div",Ee,H(i.getTemplateDescription(e)),1)]),d.model==e.id?(M(),j("div",Oe,[L(s,null,{default:O(()=>[L(n)],void 0),_:1})])):A("",!0)],10,Ie))),128))])])}]]),De={class:"fcrm_html"},He={key:0,class:"fcrm_html--title",style:{"margin-bottom":"0"}},Le=["innerHTML"];const Ae=Z({name:"HtmlViewer",props:["field"]},[["render",function(e,l,t,a,o,d){return M(),j("div",De,[t.field.heading?(M(),j("h3",He,H(t.field.heading),1)):A("",!0),P("div",{class:"fcrm_html--info",innerHTML:t.field.info},null,8,Le)])}]]),We={class:"fcrm_button_designer_dialog--row"},je={class:"fcrm_button_designer_dialog--controls"},Be={key:1,class:"fcrm-wp-editor-color-input"},Fe={class:"fcrm-wp-editor-color-input__hex"},Ne=["aria-label","onClick"],Re={key:2,class:"fcrm-wp-editor-slider-input"},Ye={class:"fcrm_button_designer_dialog--preview"},Ge={class:"fcrm_button_designer_dialog--preview-header"},Je={class:"fcrm_button_designer_dialog--preview-header-title"},qe={class:"fcrm_button_designer_dialog--preview-body"},Qe={class:"dialog-footer"};const Ke={name:"WPEditorField",components:{popover:le,ButtonDesigner:Z({name:"tinyButtonDesigner",components:{Icons:X},props:["visibility"],emits:["close","insert"],computed:{dialogVisible:{get(){return this.visibility},set(e){e||this.$emit("close")}}},data(){return{controls:{button_text:{type:"text",label:this.$t("Button Text"),value:this.$t("click here")},button_url:{label:this.$t("Button URL"),type:"url",value:""},backgroundColor:{label:this.$t("Background Color"),type:"color_picker",value:"#0072ff"},textColor:{label:this.$t("Text Color"),type:"color_picker",value:"#ffffff"},borderRadius:{label:this.$t("Border Radius"),type:"slider",value:5,max:50,min:0},fontSize:{label:this.$t("Font Size"),type:"slider",value:16,min:8,max:40},lineHeight:{label:this.$t("Line Height"),type:"slider",value:1,min:.8,max:3,step:.1},fontStyle:{label:this.$t("Font Style"),type:"checkboxes",value:[],options:{bold:"Bold",italic:"Italic",underline:"Underline"}}},style:""}},watch:{controls:{handler(){this.generateStyle()},deep:!0}},methods:{displayColor:e=>e&&""!==e.trim()?e:"#ffffff",displayHex(e){if(!e||""===e.trim())return"#F5F6F7";return(e.startsWith("#")?e:"#"+e).toUpperCase()},close(){this.$emit("close")},insert(){if(!this.controls.button_url.value||!this.controls.button_text.value)return void this.$notify.error("Button Text and URL is required");const e=`${this.controls.button_text.value}`;this.$emit("insert",e),this.close()},generateStyle(){const e=this.controls.fontStyle.value,l=-1===e.indexOf("underline")?"none":"underline",t=-1===e.indexOf("bold")?"normal":"bold",a=-1===e.indexOf("italic")?"normal":"italic";this.style=`color:${this.controls.textColor.value};background-color:${this.controls.backgroundColor.value};font-size:${this.controls.fontSize.value}px;line-height:${this.controls.lineHeight.value};border-radius:${this.controls.borderRadius.value}px;text-decoration:${l};font-weight:${t};font-style:${a};padding:0.8rem 1rem;border-color:#0072ff;`}},mounted(){this.generateStyle()}},[["render",function(e,l,t,i,n,s){const r=o,V=T("Icons"),b=c,g=h,k=f,w=d,x=_,U=a,C=m,$=p,I=u,S=v,E=y;return M(),z(E,{title:e.$t("Design Your Button"),modelValue:s.dialogVisible,"onUpdate:modelValue":l[3]||(l[3]=e=>s.dialogVisible=e),"append-to-body":!0,"show-close":!1,"close-on-click-modal":!1,"modal-class":"fcrm_button_designer_dialog",width:"60%"},{footer:O(()=>[P("span",Qe,[L(S,{onClick:l[1]||(l[1]=e=>s.close())},{default:O(()=>[D(H(e.$t("Cancel")),1)],void 0,!0),_:1}),L(S,{type:"primary",onClick:l[2]||(l[2]=e=>s.insert())},{default:O(()=>[D(H(e.$t("Insert")),1)],void 0,!0),_:1})])]),default:O(()=>[P("div",We,[L(I,{gutter:16},{default:O(()=>[L($,{lg:12,md:12,sm:24},{default:O(()=>[P("div",je,[L(C,{"label-position":"top"},{default:O(()=>[(M(!0),j(F,null,N(n.controls,(l,t)=>(M(),z(U,{key:t,label:l.label},{default:O(()=>["text"==l.type||"url"==l.type?(M(),z(r,{key:0,type:l.type,modelValue:l.value,"onUpdate:modelValue":e=>l.value=e,placeholder:l.placeholder},null,8,["type","modelValue","onUpdate:modelValue","placeholder"])):"color_picker"==l.type?(M(),j("div",Be,[P("div",{class:"fcrm-wp-editor-color-input__swatch",style:G({backgroundColor:s.displayColor(l.value)})},null,4),P("span",Fe,H(s.displayHex(l.value)),1),P("button",{type:"button",class:"fcrm-wp-editor-color-input__clear","aria-label":e.$t("Clear color"),onClick:J(e=>l.value="#0072ff",["stop"])},[L(V,{"icon-name":"close"})],8,Ne),L(b,{modelValue:l.value,"onUpdate:modelValue":e=>l.value=e,onActiveChange:e=>{l.value=e},class:"fcrm-wp-editor-color-picker-trigger",clearable:""},null,8,["modelValue","onUpdate:modelValue","onActiveChange"])])):"slider"==l.type?(M(),j("div",Re,[L(g,{modelValue:l.value,"onUpdate:modelValue":e=>l.value=e,min:l.min,max:l.max,step:l.step},null,8,["modelValue","onUpdate:modelValue","min","max","step"]),L(k,{modelValue:l.value,"onUpdate:modelValue":e=>l.value=e,min:l.min,max:l.max,step:l.step??1,precision:null!=l.step&&l.step<1?1:0,"controls-position":"right",class:"fcrm-wp-editor-slider-input__number"},null,8,["modelValue","onUpdate:modelValue","min","max","step","precision"])])):"checkboxes"==l.type?(M(),z(x,{key:3,modelValue:l.value,"onUpdate:modelValue":e=>l.value=e},{default:O(()=>[(M(!0),j(F,null,N(l.options,(e,l)=>(M(),z(w,{key:l,label:l,value:l},{default:O(()=>[D(H(e),1)],void 0,!0),_:2},1032,["label","value"]))),128))],void 0,!0),_:2},1032,["modelValue","onUpdate:modelValue"])):A("",!0)],void 0,!0),_:2},1032,["label"]))),128))],void 0,!0),_:1})])],void 0,!0),_:1}),L($,{lg:12,md:12,sm:24},{default:O(()=>[P("div",Ye,[P("div",Ge,[P("div",Je,H(e.$t("Button Preview"))+": ",1)]),P("div",qe,[P("a",{onClick:l[0]||(l[0]=e=>s.insert()),style:G(n.style),href:"#"},H(n.controls.button_text.value),5)])])],void 0,!0),_:1})],void 0,!0),_:1})])],void 0),_:1},8,["title","modelValue"])}]]),Picture:V,InfoFilled:e},emits:["update:modelValue"],props:{modelValue:{type:String,default:()=>""},field:{type:Object,default:()=>({})},editor_id:{type:String,default:()=>"wp_editor_"+Date.now()+parseInt(1e3*Math.random())},editorShortcodes:{type:Array,default(){var e;return(null==(e=window.fcAdmin)?void 0:e.globalSmartCodes)||[]}},height:{type:Number,default:()=>250},extra_style:{default:()=>""},showInternalLabel:{type:Boolean,default:!0}},data(){var e,l,t,a;return{showButtonDesigner:!1,hasWpEditor:!!(null==(e=window.wp)?void 0:e.editor)&&!!wp.editor.autop||!!(null==(l=window.wp)?void 0:l.oldEditor),editor:(null==(t=window.wp)?void 0:t.oldEditor)||(null==(a=window.wp)?void 0:a.editor),plain_content:this.modelValue,cursorPos:this.modelValue?this.modelValue.length:0,app_ready:!1,buttonInitiated:!1,currentEditor:!1,editorMode:"visual"}},watch:{plain_content(){this.$emit("update:modelValue",this.plain_content)}},methods:{initEditor(){if(!this.hasWpEditor)return;const e=[];Object.entries(te).forEach(([l,t])=>{e.push(l+"="+t)}),this.editor.remove(this.editor_id);const l=this;this.editor.initialize(this.editor_id,{mediaButtons:!0,tinymce:{height:l.height,fontsize_formats:"8px 10px 12px 14px 16px 18px 24px 30px 36px 45px",toolbar1:"formatselect,fontselect,fontsizeselect,customInsertButton,table,bold,italic,bullist,numlist,link,blockquote,alignleft,aligncenter,alignright,underline,strikethrough,forecolor,removeformat,codeformat,outdent,indent,undo,redo",font_formats:e.join("; "),setup(e){e.on("change",function(e,t){l.changeContentEvent()}),l.buttonInitiated||(l.buttonInitiated=!0,e.addButton("customInsertButton",{text:l.$t("Button"),classes:"fluentcrm_editor_btn",onclick(){l.showInsertButtonModal(e)}}))},formats:{alignleft:{selector:"p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li,table,img",classes:"align-left",styles:{"text-align":"left"}},aligncenter:{selector:"p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li,table,img",classes:"align-center",styles:{"text-align":"center"},attributes:{align:"center"}},alignright:{selector:"p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li,table,img",classes:"align-right",styles:{"text-align":"right"},attributes:{align:"right"}}},content_style:l.extra_style},quicktags:!0}),jQuery("#"+this.editor_id).on("change",function(e){l.changeContentEvent()})},showInsertButtonModal(e){this.currentEditor=e,this.showButtonDesigner=!0},insertHtml(e){this.currentEditor.insertContent(e)},changeContentEvent(){const e=this.editor.getContent(this.editor_id);this.$emit("update:modelValue",e)},handleCommand(e){if(this.hasWpEditor)window.tinymce.activeEditor.insertContent(e);else{var l=this.plain_content.slice(0,this.cursorPos),t=this.plain_content.slice(this.cursorPos,this.plain_content.length);this.plain_content=l+e+t,this.cursorPos+=e.length}},updateCursorPos(){var e=jQuery(".wp_vue_editor_plain").prop("selectionStart");this.cursorPos=e},triggerMediaButton(){window.wp&&window.wp.media&&window.wp.media.editor&&window.wp.media.editor.open(this.editor_id)},switchEditor(e,l,t){if(!this.hasWpEditor)return;const a=window.switchEditors;if(a&&"function"==typeof a.go)a.go(this.editor_id,e);else{const e=document.querySelector(l);e&&e.click()}this.editorMode=t},switchToVisual(){this.switchEditor("tmce",`#${this.editor_id}-tmce`,"visual")},switchToText(){this.switchEditor("html",`#${this.editor_id}-html`,"text")}},mounted(){this.initEditor(),this.app_ready=!0}},Ze={class:"wp_vue_editor_wrapper"},Xe={class:"fcrm-editor-header-row"},el={key:0,class:"fcrm-editor-label"},ll={class:"label-text"},tl=["innerHTML"],al={class:"fcrm-editor-toggle"},ol={class:"fcrm-toggle-switch"},dl=["id"],il={key:2,class:"fcrm-editor-info-alert"};const nl=Z(Ke,[["render",function(e,a,o,d,i,n){const s=T("InfoFilled"),r=t,u=l,p=T("Picture"),m=v,c=T("popover"),h=T("button-designer");return M(),j("div",Ze,[P("div",Xe,[o.field.label&&o.showInternalLabel?(M(),j("div",el,[P("span",ll,H(o.field.label),1),o.field.help?(M(),z(u,{key:0,"popper-class":"sidebar-popper",effect:"dark",placement:"top"},{content:O(()=>[P("div",{innerHTML:o.field.help},null,8,tl)]),default:O(()=>[L(r,{class:"tooltip-icon"},{default:O(()=>[L(s)],void 0,!0),_:1})],void 0),_:1})):A("",!0)])):A("",!0),P("div",{class:B(["fcrm-editor-actions",{"full-width":!o.field.label||!o.showInternalLabel}])},[L(m,{size:"small",onClick:n.triggerMediaButton},{default:O(()=>[L(r,null,{default:O(()=>[L(p)],void 0,!0),_:1}),D(" "+H(e.$t("Add Media")),1)],void 0),_:1},8,["onClick"]),o.editorShortcodes&&o.editorShortcodes.length?(M(),z(c,{key:0,buttonText:e.$t("Shortcode")+' ',btnType:"",class:"fcrm-editor-shortcode-popover",doc_url:"https://fluentcrm.com/docs/merge-codes-smart-codes-usage/",data:o.editorShortcodes,onCommand:n.handleCommand},null,8,["buttonText","data","onCommand"])):A("",!0),P("div",al,[P("div",ol,[P("button",{type:"button",class:B({active:i.hasWpEditor&&"visual"===i.editorMode}),onClick:a[0]||(a[0]=(...e)=>n.switchToVisual&&n.switchToVisual(...e))},H(e.$t("Visual")),3),P("button",{type:"button",class:B({active:!i.hasWpEditor||"text"===i.editorMode}),onClick:a[1]||(a[1]=(...e)=>n.switchToText&&n.switchToText(...e))},H(e.$t("Text")),3)])])],2)]),i.hasWpEditor?(M(),j("textarea",{key:0,class:"wp_vue_editor",id:o.editor_id},H(o.modelValue),9,dl)):R((M(),j("textarea",{key:1,class:"wp_vue_editor wp_vue_editor_plain","onUpdate:modelValue":a[2]||(a[2]=e=>i.plain_content=e),onClick:a[3]||(a[3]=(...e)=>n.updateCursorPos&&n.updateCursorPos(...e))}," ",512)),[[q,i.plain_content]]),o.field.inline_help?(M(),j("div",il,[L(r,{class:"info-icon"},{default:O(()=>[L(s)],void 0),_:1}),P("p",null,H(o.field.inline_help),1)])):A("",!0),i.showButtonDesigner?(M(),z(h,{key:3,onClose:a[4]||(a[4]=()=>{i.showButtonDesigner=!1}),onInsert:n.insertHtml,visibility:i.showButtonDesigner},null,8,["onInsert","visibility"])):A("",!0)])}]]);const sl=Z({name:"AjaxSelector",props:["field","modelValue"],emits:["change","update:modelValue"],data(){return{model:this.modelValue,loading:!1,options:[],isInternalUpdate:!1}},watch:{model(e){this.isInternalUpdate||(this.$emit("update:modelValue",e),this.$emit("change",e))},modelValue(e){JSON.stringify(this.model)!==JSON.stringify(e)&&(this.isInternalUpdate=!0,this.model=e,this.$nextTick(()=>{this.isInternalUpdate=!1}))}},methods:{fetchOptions(e){let l=this.field.option_key;this.field.extended_key&&(l+="_"+this.field.extended_key);let t="";if(this.field.cacheable){if(t+="_fcrm_ajax_cache_"+l,window[t])return void(this.options=window[t])}else if(this.field.experimental_cache&&(t+="_fcrm_ajax_cache_"+l+"_"+e+" "+JSON.stringify(this.model),this.field.sub_option_key&&(t+="_"+JSON.stringify(this.field.sub_option_key)),window[t]))return void(this.options=window[t]);if(this.doing_ajax)return!1;this.loading=!0;const a={search:e,values:this.model,option_key:l};this.field.sub_option_key&&(a.sub_option_key=this.field.sub_option_key),this.$get("reports/ajax-options",a).then(e=>{this.options=e.options,t&&e.options&&(window[t]=e.options)}).catch(e=>{this.handleError(e)}).finally(()=>{this.loading=!1})}},mounted(){this.fetchOptions("")}},[["render",function(e,l,t,a,o,d){const i=g,n=k,s=b;return R((M(),z(n,{modelValue:o.model,"onUpdate:modelValue":l[0]||(l[0]=e=>o.model=e),multiple:t.field.is_multiple,filterable:"",remote:!t.field.cacheable,clearable:t.field.clearable,disabled:t.field.disabled,"reserve-keyword":"","allow-create":t.field.creatable,size:t.field.size,"remote-show-suffix":!0,placeholder:t.field.placeholder||e.$t("Please enter a keyword"),"remote-method":d.fetchOptions},{default:O(()=>[(M(!0),j(F,null,N(o.options,e=>(M(),z(i,{key:e.id,label:e.title,value:e.id},null,8,["label","value"]))),128))],void 0),_:1},8,["modelValue","multiple","remote","clearable","disabled","allow-create","size","placeholder","remote-method"])),[[s,o.loading]])}]]),rl={class:"fcrm_checkbox_group"};const ul=Z({name:"CheckboxGroup",props:["field","modelValue"],emits:["update:modelValue"],data(){return{model:this.modelValue??[],isIndeterminate:!1,checkAll:!1,isInternalUpdate:!1}},watch:{model(e){this.isInternalUpdate||this.$emit("update:modelValue",e)},modelValue(e){JSON.stringify(this.model)!==JSON.stringify(e)&&(this.isInternalUpdate=!0,this.model=e??[],this.$nextTick(()=>{this.isInternalUpdate=!1}))}},computed:{optionKeys(){if(!this.field.has_all_selector)return[];const e=[];return this.each(this.field.options,l=>{e.push(l.id)}),e}},methods:{checked(e){if(this.field.has_all_selector){const e=this.model.length;this.checkAll=e===this.optionKeys.length,this.isIndeterminate=e>0&&eo.checkAll=e),indeterminate:o.isIndeterminate,onChange:i.all},{default:O(()=>[D(H(t.field.all_selector_label),1)],void 0),_:1},8,["modelValue","indeterminate","onChange"])):A("",!0),L(s,{class:B(t.field.input_class),modelValue:o.model,"onUpdate:modelValue":l[1]||(l[1]=e=>o.model=e),onChange:i.checked},{default:O(()=>[(M(!0),j(F,null,N(t.field.options,e=>(M(),z(n,{class:"fcrm-checkbox",value:e.id,key:e.id},{default:O(()=>[D(H(e.label),1)],void 0,!0),_:2},1032,["value"]))),128))],void 0),_:1},8,["class","modelValue","onChange"])])}]]),pl={class:"fcrm-mapper-container"},ml={key:0,class:"fcrm_horizontal_table"},cl={style:{width:"50%"}},hl={style:{width:"50%"}},fl={class:"text-align-right"},_l={class:"icon"},vl={class:"icon"};const yl=Z({name:"FormManyDropdownMapper",props:["field","modelValue"],emits:["update:modelValue"],components:{Icons:X,OptionSelector:ee,AjaxSelector:sl,InputText:se,InputTextPopper:xe,ArrowUp:x,ArrowDown:w},data(){return{render_table:!0,localValue:this.modelValue}},computed:{value(){return this.localValue}},watch:{modelValue(e){this.localValue=e}},methods:{emitUpdate(){this.$emit("update:modelValue",[...this.localValue])},addMore(){this.localValue.push({field_key:"",field_value:""}),this.emitUpdate()},deleteItem(e){this.localValue.splice(e,1),this.emitUpdate()},movePosition(e,l){let t=e-1;"down"===l&&(t=e+1);const a=this.localValue[e];this.localValue.splice(e,1),this.localValue.splice(t,0,a),this.emitUpdate(),this.render_table=!1,this.$nextTick(()=>{this.render_table=!0})}}},[["render",function(e,l,a,o,d,i){const n=T("ajax-selector"),s=T("option-selector"),r=g,u=k,p=T("input-text"),m=T("input-text-popper"),c=T("ArrowUp"),h=t,f=v,_=T("ArrowDown"),y=U,V=T("Icons");return M(),j("div",pl,[d.render_table?(M(),j("table",ml,[P("thead",null,[P("tr",null,[P("th",null,H(a.field.local_label),1),P("th",null,H(a.field.remote_label),1),l[1]||(l[1]=P("th",{width:"40px"},null,-1))])]),P("tbody",null,[(M(!0),j(F,null,N(i.value,(l,t)=>(M(),j("tr",{key:t},[P("td",cl,[a.field.field_ajax_selector?(M(),z(n,{key:0,modelValue:l.field_key,"onUpdate:modelValue":e=>l.field_key=e,field:{placeholder:a.field.local_placeholder,...a.field.field_ajax_selector}},null,8,["modelValue","onUpdate:modelValue","field"])):a.field.field_option_selector?(M(),z(s,{key:1,modelValue:l.field_key,"onUpdate:modelValue":e=>l.field_key=e,field:{placeholder:a.field.local_placeholder,...a.field.field_option_selector}},null,8,["modelValue","onUpdate:modelValue","field"])):(M(),z(u,{key:2,clearable:"",filterable:"",modelValue:l.field_key,"onUpdate:modelValue":e=>l.field_key=e,placeholder:a.field.local_placeholder},{default:O(()=>[(M(!0),j(F,null,N(a.field.fields,(e,l)=>(M(),z(r,{key:l,value:l,label:e.label},null,8,["value","label"]))),128))],void 0),_:1},8,["modelValue","onUpdate:modelValue","placeholder"]))]),P("td",hl,[a.field.value_option_selector?(M(),z(s,{key:0,modelValue:l.field_value,"onUpdate:modelValue":e=>l.field_value=e,field:{placeholder:a.field.remote_placeholder,...a.field.value_option_selector}},null,8,["modelValue","onUpdate:modelValue","field"])):a.field.value_options?(M(),z(u,{key:1,clearable:"",filterable:"",modelValue:l.field_value,"onUpdate:modelValue":e=>l.field_value=e,placeholder:a.field.remote_placeholder},{default:O(()=>[(M(!0),j(F,null,N(a.field.value_options,e=>(M(),z(r,{key:e.id,value:e.id,label:e.title},null,8,["value","label"]))),128))],void 0),_:1},8,["modelValue","onUpdate:modelValue","placeholder"])):"input-text"==a.field.remote_field_type?(M(),z(p,{key:2,field:a.field.remote_field,modelValue:l.field_value,"onUpdate:modelValue":e=>l.field_value=e},null,8,["field","modelValue","onUpdate:modelValue"])):"input-text-popper"==a.field.remote_field_type?(M(),z(m,{key:3,field:a.field.remote_field,modelValue:l.field_value,"onUpdate:modelValue":e=>l.field_value=e},null,8,["field","modelValue","onUpdate:modelValue"])):A("",!0)]),P("td",null,[P("div",fl,[a.field.manage_serial?(M(),z(y,{key:0},{default:O(()=>[L(f,{onClick:e=>i.movePosition(t,"up"),disabled:0==t,size:"small"},{default:O(()=>[L(h,null,{default:O(()=>[L(c)],void 0,!0),_:1})],void 0,!0),_:1},8,["onClick","disabled"]),L(f,{onClick:e=>i.movePosition(t,"down"),disabled:t==i.value.length-1,size:"small"},{default:O(()=>[L(h,null,{default:O(()=>[L(_)],void 0,!0),_:1})],void 0,!0),_:1},8,["onClick","disabled"])],void 0),_:2},1024)):A("",!0),L(f,{onClick:e=>i.deleteItem(t),disabled:1==i.value.length,type:"danger",size:"small",class:"only-icon-btn small","aria-label":e.$t("Delete")},{default:O(()=>[P("span",_l,[L(V,{"icon-name":"delete"})])],void 0),_:1},8,["onClick","disabled","aria-label"])])])]))),128))])])):A("",!0),L(f,{onClick:l[0]||(l[0]=e=>i.addMore()),size:"small"},{default:O(()=>[P("span",vl,[L(V,{"icon-name":"plus"})]),D(" "+H(e.$t("Add More")),1)],void 0),_:1})])}]]);const Vl=Z({name:"InputOption",props:["field","modelValue"],emits:["update:modelValue"],data(){return{model:this.modelValue}},watch:{model(e){this.$emit("update:modelValue",e)}}},[["render",function(e,l,t,a,o,d){const i=g,n=k;return M(),z(n,{clearable:"",filterable:"",placeholder:t.field.placeholder,multiple:t.field.multiple,class:B(t.field.wrapper_class),modelValue:o.model,"onUpdate:modelValue":l[0]||(l[0]=e=>o.model=e)},{default:O(()=>[(M(!0),j(F,null,N(t.field.options,e=>(M(),z(i,{key:e.id,value:e.id,label:e.label},null,8,["value","label"]))),128))],void 0),_:1},8,["placeholder","multiple","class","modelValue"])}]]);const bl=Z({name:"InputColor",props:["field","modelValue"],emits:["update:modelValue"],data(){return{model:this.modelValue}},watch:{model(e){this.$emit("update:modelValue",e)}}},[["render",function(e,l,t,a,o,d){const i=c;return M(),z(i,{onActiveChange:l[0]||(l[0]=e=>{o.model=e}),"color-format":t.field.colorFormat,"show-alpha":t.field.showAlpha,modelValue:o.model,"onUpdate:modelValue":l[1]||(l[1]=e=>o.model=e)},null,8,["color-format","show-alpha","modelValue"])}]]);const gl=Z({name:"InputDate",props:["field","modelValue"],emits:["update:modelValue"],data(){return{model:this.modelValue}},computed:{convertedFormat(){return this.field.value_format?this.field.value_format.replace(/yyyy/g,"YYYY").replace(/dd/g,"DD"):"YYYY-MM-DD HH:mm:ss"}},watch:{model(e){this.$emit("update:modelValue",e)}}},[["render",function(e,l,t,a,o,d){const i=C;return M(),z(i,{"value-format":d.convertedFormat,modelValue:o.model,"onUpdate:modelValue":l[0]||(l[0]=e=>o.model=e),placeholder:t.field.placeholder,type:t.field.data_type},null,8,["value-format","modelValue","placeholder","type"])}]]);const kl=Z({name:"InputNumber",props:["field","modelValue"],emits:["update:modelValue"],data(){return{model:this.modelValue?Number(this.modelValue):null}},watch:{model(e){this.$emit("update:modelValue",e)}}},[["render",function(e,l,t,a,o,d){const i=f;return M(),z(i,{min:t.field.min,max:t.field.max,step:t.field.step,modelValue:o.model,"onUpdate:modelValue":l[0]||(l[0]=e=>o.model=e),class:"fc-input-number-field"},null,8,["min","max","step","modelValue"])}]]),wl={style:{width:"100%"}},xl={key:2},Ul={class:"dialog-footer"};const Cl=Z({name:"VerifiedEmailInput",props:["field","modelValue"],emits:["update:modelValue"],data(){return{selectedMail:this.modelValue,model:this.modelValue,dialogWarningVisible:!1,warningMessage:this.$t("Warning default email change")}},computed:{isUsingSelect(){return this.appVars&&this.appVars.verified_senders&&this.appVars.verified_senders.length>0}},watch:{model(e){this.$emit("update:modelValue",e),this.isUsingSelect&&(null!=e&&e!=this.selectedMail&&(this.dialogWarningVisible=!0),null!=e&&e==this.selectedMail&&(this.dialogWarningVisible=!1))},modelValue(e){this.model=e,this.selectedMail=e}},methods:{handleInputBlur(){this.isUsingSelect||null==this.model||this.model==this.selectedMail||(this.dialogWarningVisible=!0)},cancelChangeDefaultEmail(){this.model=this.selectedMail,this.dialogWarningVisible=!1},confirmChangeDefaultEmail(){this.dialogWarningVisible=!1,this.selectedMail=this.model}}},[["render",function(e,l,t,a,d,i){const n=g,s=k,r=o,u=v,p=y;return M(),j("div",wl,[e.appVars.verified_senders.length?(M(),z(s,{key:0,placeholder:t.field.placeholder,filterable:"","allow-create":"",modelValue:d.model,"onUpdate:modelValue":l[0]||(l[0]=e=>d.model=e)},{default:O(()=>[(M(!0),j(F,null,N(e.appVars.verified_senders,e=>(M(),z(n,{key:e,value:e},null,8,["value"]))),128))],void 0),_:1},8,["placeholder","modelValue"])):(M(),z(r,{key:1,type:t.field.data_type,placeholder:t.field.placeholder,modelValue:d.model,"onUpdate:modelValue":l[1]||(l[1]=e=>d.model=e),onBlur:i.handleInputBlur},null,8,["type","placeholder","modelValue","onBlur"])),t.field.show_warning?(M(),j("div",xl,[L(p,{title:e.$t("Confirm"),modelValue:d.dialogWarningVisible,"onUpdate:modelValue":l[2]||(l[2]=e=>d.dialogWarningVisible=e),"close-on-click-modal":!1,"append-to-body":!0,width:"30%"},{footer:O(()=>[P("div",Ul,[L(u,{onClick:i.cancelChangeDefaultEmail},{default:O(()=>[D(H(e.$t("Cancel")),1)],void 0,!0),_:1},8,["onClick"]),L(u,{type:"primary",onClick:i.confirmChangeDefaultEmail},{default:O(()=>[D(H(e.$t("Continue")),1)],void 0,!0),_:1},8,["onClick"])])]),default:O(()=>[P("span",null,[D(H(d.warningMessage+" "),1),P("strong",null,H(d.model+"."),1)])],void 0),_:1},8,["title","modelValue"])])):A("",!0)])}]]),$l={class:"fcrm_tag_mappings"},Il={key:0,class:"fcrm_horizontal_table"};const Sl=Z({name:"TagAddRemoveElement",props:["field","modelValue"],emits:["update:modelValue"],components:{OptionSelector:ee},data(){return{model:this.modelValue,tags_ready:!1}},watch:{model(e){this.$emit("update:modelValue",e)}},mounted(){this.renewOptionCache("tags",()=>{this.tags_ready=!0})}},[["render",function(e,l,t,a,o,d){const i=T("option-selector"),n=b;return R((M(),j("div",$l,[o.tags_ready?(M(),j("table",Il,[P("thead",null,[P("tr",null,[P("th",null,H(t.field.selector_label),1),P("th",null,H(t.field.add_tag_label),1),P("th",null,H(t.field.remove_tag_label),1)])]),P("tbody",null,[(M(!0),j(F,null,N(t.field.selector_options,e=>(M(),j("tr",{key:e.id},[P("td",null,H(e.title),1),P("td",null,[L(i,{modelValue:o.model[e.id].add_tags,"onUpdate:modelValue":l=>o.model[e.id].add_tags=l,field:{option_key:"tags",creatable:!0,is_multiple:!0}},null,8,["modelValue","onUpdate:modelValue"])]),P("td",null,[L(i,{modelValue:o.model[e.id].remove_tags,"onUpdate:modelValue":l=>o.model[e.id].remove_tags=l,field:{option_key:"tags",creatable:!0,is_multiple:!0}},null,8,["modelValue","onUpdate:modelValue"])])]))),128))])])):A("",!0)])),[[n,!o.tags_ready]])}]]);const Tl=Z({name:"CascadeOptionSelector",props:["field","modelValue"],emits:["update:modelValue"],data(){return{model:this.modelValue,loading:!1,options:[],appReady:!0}},watch:{model(e){this.$emit("update:modelValue",e)}},methods:{fetchOptions(e){this.loading=!0,this.$get("reports/cascade_selections",{search:e,values:this.model,provider:this.field.provider,is_multiple:!!this.field.is_multiple}).then(e=>{this.options=e.options}).catch(e=>{this.handleError(e)}).finally(()=>{this.loading=!1})},remoteMethod(e){if(this.loading)return!1;this.fetchOptions(e)}},mounted(){this.fetchOptions(""),this.modelValue&&Q(this.modelValue)||(this.model=[])}},[["render",function(e,l,t,a,o,d){const i=g,n=$,s=k,r=b;return R((M(),z(s,{"remote-method":d.remoteMethod,filterable:"",remote:"","reserve-keyword":"","value-key":"value",modelValue:o.model,"onUpdate:modelValue":l[0]||(l[0]=e=>o.model=e),size:"small",multiple:t.field.is_multiple,placeholder:e.$t("Select")},{default:O(()=>[(M(!0),j(F,null,N(o.options,e=>(M(),z(n,{key:e.label,label:e.label},{default:O(()=>[(M(!0),j(F,null,N(e.children,e=>(M(),z(i,{key:e.value,label:e.label,value:e.value},null,8,["label","value"]))),128))],void 0,!0),_:2},1032,["label"]))),128))],void 0),_:1},8,["remote-method","modelValue","multiple","placeholder"])),[[r,o.loading]])}]]),Ml={class:"fcrm_condition_groups"},zl={key:0,class:"fcrm_cond_and"},El={class:"fcrm_condition_group"},Ol={class:"wp-list-table widefat fixed striped table-view-list posts"},Pl={style:{width:"180px"}},Dl={style:{width:"180px"}},Hl={key:0},Ll={key:1},Al={style:{"text-align":"right"}},Wl={key:0},jl=["innerHTML"],Bl={key:0,class:"text-align-right"};const Fl=Z({name:"ConditionGroup",props:["field","modelValue"],emits:["update:modelValue"],components:{OptionSelector:ee,AjaxSelector:sl,Plus:S,Delete:I},data(){return{model:this.modelValue}},computed:{flat_properties(){let e={};return this.each(this.field.condition_properties,l=>{e={...e,...l.options}}),e}},watch:{model(e){this.$emit("update:modelValue",e)}},methods:{addCondition(e){this.model[e].conditions.push({data_key:"",operator:"=",data_value:""})},deleteProp(e,l){this.model[e].conditions.splice(l,1)},removeGroup(e){this.model.splice(e,1)},addConditionalGroup(){this.model.push({conditions:[{data_key:"",operator:"=",data_value:""}],match_type:"match_all"})}}},[["render",function(e,l,a,d,i,r){const u=g,p=$,m=k,c=o,h=T("option-selector"),f=T("ajax-selector"),_=T("Plus"),y=t,V=v,b=T("Delete"),w=n,x=s;return M(),j("div",Ml,[(M(!0),j(F,null,N(i.model,(t,o)=>(M(),j("div",{class:"fcrm_condition_wrapper",key:o},[0!=o?(M(),j("div",zl,H(e.$t("OR")),1)):A("",!0),P("div",El,[P("table",Ol,[P("thead",null,[P("tr",null,[P("th",Pl,H(a.field.labels.data_key_label),1),P("th",Dl,H(a.field.labels.condition_label),1),P("th",null,H(a.field.labels.data_value_label),1),l[1]||(l[1]=P("th",{style:{width:"90px"}},null,-1))])]),P("tbody",null,[(M(!0),j(F,null,N(t.conditions,(l,d)=>(M(),j("tr",{key:d},[P("td",null,[L(m,{onChange:e=>{l.operator="=",l.data_value=""},clearable:"",placeholder:e.$t("Select"),size:"small",modelValue:l.data_key,"onUpdate:modelValue":e=>l.data_key=e},{default:O(()=>[(M(!0),j(F,null,N(a.field.condition_properties,(e,l)=>(M(),z(p,{key:l,label:e.label},{default:O(()=>[(M(!0),j(F,null,N(e.options,(e,l)=>(M(),z(u,{key:l,value:l,label:e.label},null,8,["value","label"]))),128))],void 0,!0),_:2},1032,["label"]))),128))],void 0),_:2},1032,["onChange","placeholder","modelValue","onUpdate:modelValue"])]),P("td",null,[l.data_key?(M(),z(m,{key:0,clearable:"",placeholder:e.$t("Select Condition"),size:"small",modelValue:l.operator,"onUpdate:modelValue":e=>l.operator=e},{default:O(()=>[r.flat_properties[l.data_key].multiple?(M(),j(F,{key:0},[L(u,{value:"=",label:e.$t("Match any Of")},null,8,["label"]),L(u,{value:"match_all",label:e.$t("Match all of")},null,8,["label"]),L(u,{value:"match_none_of",label:e.$t("Match none of")},null,8,["label"])],64)):(M(),j(F,{key:1},[L(u,{value:"=",label:e.$t("Equal")},null,8,["label"]),L(u,{value:"!=",label:e.$t("Not Equal")},null,8,["label"]),"text"==r.flat_properties[l.data_key].type?(M(),j(F,{key:0},[L(u,{value:"contains",label:e.$t("Contains")},null,8,["label"]),L(u,{value:"doNotContains",label:e.$t("Not Contains")},null,8,["label"]),L(u,{value:"startsWith",label:e.$t("Starts With")},null,8,["label"]),L(u,{value:"endsWith",label:e.$t("Ends With")},null,8,["label"])],64)):"number"==r.flat_properties[l.data_key].type?(M(),j(F,{key:1},[L(u,{value:">",label:e.$t("Greater Than")},null,8,["label"]),L(u,{value:"<",label:e.$t("Less Than")},null,8,["label"])],64)):A("",!0)],64))],void 0),_:2},1032,["placeholder","modelValue","onUpdate:modelValue"])):A("",!0)]),P("td",null,[l.data_key&&l.operator?(M(),j("div",Hl,["text"==r.flat_properties[l.data_key].type?(M(),z(c,{key:0,size:"small",link:"",modelValue:l.data_value,"onUpdate:modelValue":e=>l.data_value=e},null,8,["modelValue","onUpdate:modelValue"])):"number"==r.flat_properties[l.data_key].type?(M(),z(c,{key:1,size:"small",type:"number",modelValue:l.data_value,"onUpdate:modelValue":e=>l.data_value=e},null,8,["modelValue","onUpdate:modelValue"])):"select"==r.flat_properties[l.data_key].type?(M(),z(m,{key:2,size:"small",modelValue:l.data_value,"onUpdate:modelValue":e=>l.data_value=e,clearable:""},{default:O(()=>[(M(!0),j(F,null,N(r.flat_properties[l.data_key].options,e=>(M(),z(u,{key:e.id,label:e.title,value:e.id},null,8,["label","value"]))),128))],void 0),_:2},1032,["modelValue","onUpdate:modelValue"])):"option_selector"==r.flat_properties[l.data_key].type?(M(),z(h,{key:3,field:{placeholder:"Select",is_multiple:r.flat_properties[l.data_key].multiple,option_key:r.flat_properties[l.data_key].option_key},modelValue:l.data_value,"onUpdate:modelValue":e=>l.data_value=e},null,8,["field","modelValue","onUpdate:modelValue"])):"rest_selector"==r.flat_properties[l.data_key].type?(M(),z(f,{key:4,field:{placeholder:"Select",is_multiple:r.flat_properties[l.data_key].multiple,option_key:r.flat_properties[l.data_key].option_key},modelValue:l.data_value,"onUpdate:modelValue":e=>l.data_value=e},null,8,["field","modelValue","onUpdate:modelValue"])):A("",!0)])):(M(),j("div",Ll,H(e.$t("Select data source and operator first")),1))]),P("td",Al,[L(V,{onClick:e=>r.addCondition(o),type:"success",size:"small"},{default:O(()=>[L(y,null,{default:O(()=>[L(_)],void 0,!0),_:1})],void 0),_:1},8,["onClick"]),L(V,{disabled:1==t.conditions.length,onClick:e=>r.deleteProp(o,d),size:"small",type:"danger"},{default:O(()=>[L(y,null,{default:O(()=>[L(b)],void 0,!0),_:1})],void 0),_:1},8,["disabled","onClick"])])]))),128))])]),a.field.hide_match_type?(M(),j("p",{key:1,style:{margin:"0",padding:"0"},innerHTML:e.$t("Inside group conditions are match all")},null,8,jl)):(M(),j("div",Wl,[P("p",null,[P("b",null,H(e.$t("Match Type")),1)]),L(x,{modelValue:t.match_type,"onUpdate:modelValue":e=>t.match_type=e},{default:O(()=>[L(w,{value:"match_all"},{default:O(()=>[D(H(a.field.labels.match_type_all_label),1)],void 0,!0),_:1}),L(w,{value:"match_any"},{default:O(()=>[D(H(a.field.labels.match_type_any_label),1)],void 0,!0),_:1})],void 0),_:1},8,["modelValue","onUpdate:modelValue"])])),i.model.length>1?(M(),z(V,{key:2,onClick:e=>r.removeGroup(o),type:"danger",size:"small"},{default:O(()=>[L(y,null,{default:O(()=>[L(b)],void 0,!0),_:1}),D(" "+H(e.$t("Delete this group")),1)],void 0),_:1},8,["onClick"])):A("",!0)])]))),128)),a.field.is_multiple_grouping?(M(),j("div",Bl,[L(V,{onClick:l[0]||(l[0]=e=>r.addConditionalGroup()),type:"primary",size:"small"},{default:O(()=>[L(y,null,{default:O(()=>[L(_)],void 0,!0),_:1}),D(" "+H(e.$t("Add Another Conditional Group")),1)],void 0),_:1})])):A("",!0)])}]]),Nl={class:"fcrm_horizontal_table"};const Rl=Z({name:"FormFieldsGroupMapper",props:["field","model"]},[["render",function(e,l,t,a,o,d){const i=g,n=k;return M(),j("table",Nl,[P("thead",null,[P("tr",null,[P("th",null,H(t.field.local_label),1),P("th",null,H(t.field.remote_label),1)])]),P("tbody",null,[(M(!0),j(F,null,N(t.field.fields,(l,a)=>(M(),j("tr",{key:a},[P("td",null,H(l.label),1),P("td",null,[L(n,{clearable:"",filterable:"",modelValue:t.model[a],"onUpdate:modelValue":e=>t.model[a]=e,placeholder:e.$t("Select Value")},{default:O(()=>[(M(!0),j(F,null,N(t.field.value_options,e=>(M(),z(i,{key:e.id,value:e.id,label:e.title},null,8,["value","label"]))),128))],void 0),_:1},8,["modelValue","onUpdate:modelValue","placeholder"])])]))),128))])])}]]);const Yl=Z({name:"InputRadioImage",props:["field","modelValue","boxWidth","boxHeight","tooltip_prefix"],emits:["change","update:modelValue"],data(){return{model:this.modelValue,width:this.boxWidth||120,height:this.boxHeight||120}},watch:{model(e){this.$emit("update:modelValue",e),this.$emit("change",e)}}},[["render",function(e,t,a,o,d,i){const r=l,u=n,p=s;return M(),z(p,{class:"fcrm_image_radio_tooltips",modelValue:d.model,"onUpdate:modelValue":t[0]||(t[0]=e=>d.model=e)},{default:O(()=>[(M(!0),j(F,null,N(a.field.options,(e,l)=>(M(),z(u,{key:l,value:e.id},{default:O(()=>[L(r,{content:a.tooltip_prefix+e.label,placement:"top"},{default:O(()=>[P("div",{style:G({backgroundImage:"url("+e.image+")",width:d.width+"px",height:d.height+"px"}),class:B([d.model==e.id?"fcrm_image_active":"","fcrm_image_box"])},null,6)],void 0,!0),_:2},1032,["content"])],void 0,!0),_:2},1032,["value"]))),128))],void 0),_:1},8,["modelValue"])}]]),Gl={class:"fcrm_value_property_group"},Jl={class:"wp-list-table widefat fixed striped table-view-list posts"},ql={style:{width:"180px"}},Ql={key:0},Kl={key:4,class:"info",style:{margin:"2px 0 0 0","line-height":"1","font-size":"12px"}},Zl={style:{"text-align":"right"}},Xl={class:"text-align-right"};const et=Z({name:"ConditionGroup",props:["field","modelValue"],emits:["update:modelValue"],components:{OptionSelector:ee,Plus:S,Delete:I},data(){return{model:this.modelValue}},watch:{model(e){this.$emit("update:modelValue",e)}},methods:{addProperty(){this.model.push({data_key:"",data_value:""})},deleteProp(e){this.model.splice(e,1)}}},[["render",function(e,l,a,d,i,n){const s=g,r=k,m=o,c=C,h=p,f=u,_=T("option-selector"),y=T("Delete"),V=t,b=v,w=T("Plus");return M(),j("div",Gl,[P("table",Jl,[P("thead",null,[P("tr",null,[P("th",ql,H(a.field.data_key_label),1),P("th",null,H(a.field.data_value_label),1),l[1]||(l[1]=P("th",{style:{width:"50px"}},null,-1))])]),P("tbody",null,[(M(!0),j(F,null,N(i.model,(l,t)=>(M(),j("tr",{key:t},[P("td",null,[L(r,{clearable:"",onChange:e=>{l.data_value,delete l.data_operation},placeholder:e.$t("Select"),size:"small",modelValue:l.data_key,"onUpdate:modelValue":e=>l.data_key=e,filterable:""},{default:O(()=>[(M(!0),j(F,null,N(a.field.property_options,(e,l)=>(M(),z(s,{key:l,value:l,label:e.label},null,8,["value","label"]))),128))],void 0),_:1},8,["onChange","placeholder","modelValue","onUpdate:modelValue"])]),P("td",null,[l.data_key?(M(),j("div",Ql,["text"==a.field.property_options[l.data_key].type?(M(),z(m,{key:0,size:"small",link:"",modelValue:l.data_value,"onUpdate:modelValue":e=>l.data_value=e},null,8,["modelValue","onUpdate:modelValue"])):A("",!0),"textarea"==a.field.property_options[l.data_key].type?(M(),z(m,{key:1,size:"small",type:"textarea",modelValue:l.data_value,"onUpdate:modelValue":e=>l.data_value=e},null,8,["modelValue","onUpdate:modelValue"])):A("",!0),"date"==a.field.property_options[l.data_key].type?(M(),z(c,{key:2,"value-format":"YYYY-MM-DD",size:"small",modelValue:l.data_value,"onUpdate:modelValue":e=>l.data_value=e,type:"date",placeholder:e.$t("Pick a date")},null,8,["modelValue","onUpdate:modelValue","placeholder"])):A("",!0),"date_time"==a.field.property_options[l.data_key].type?(M(),z(c,{key:3,"value-format":"YYYY-MM-DD HH:mm:ss",modelValue:l.data_value,"onUpdate:modelValue":e=>l.data_value=e,type:"datetime",size:"small",placeholder:e.$t("Pick a date and time")},null,8,["modelValue","onUpdate:modelValue","placeholder"])):A("",!0),"date"==a.field.property_options[l.data_key].type||"date_time"==a.field.property_options[l.data_key].type&&a.field.property_options[l.data_key].info?(M(),j("p",Kl,H(a.field.property_options[l.data_key].info),1)):"number"==a.field.property_options[l.data_key].type?(M(),j(F,{key:5},["yes"==a.field.support_operations?(M(),z(f,{key:0,gutter:10},{default:O(()=>[L(h,{span:18},{default:O(()=>[L(m,{size:"small",type:"number",class:"input-with-select",modelValue:l.data_value,"onUpdate:modelValue":e=>l.data_value=e},null,8,["modelValue","onUpdate:modelValue"])],void 0,!0),_:2},1024),L(h,{span:6},{default:O(()=>[L(r,{size:"small",modelValue:l.data_operation,"onUpdate:modelValue":e=>l.data_operation=e,placeholder:e.$t("Replace Value")},{default:O(()=>[L(s,{value:"",label:e.$t("Replace Value")},null,8,["label"]),L(s,{value:"subtract",label:e.$t("Subtract Value")},null,8,["label"]),L(s,{value:"add",label:e.$t("Add Value")},null,8,["label"])],void 0,!0),_:1},8,["modelValue","onUpdate:modelValue","placeholder"])],void 0,!0),_:2},1024)],void 0),_:2},1024)):(M(),z(m,{key:1,size:"small",type:"number",class:"input-with-select",modelValue:l.data_value,"onUpdate:modelValue":e=>l.data_value=e},null,8,["modelValue","onUpdate:modelValue"]))],64)):"select"==a.field.property_options[l.data_key].type?(M(),j(F,{key:6},["yes"==a.field.support_operations&&a.field.property_options[l.data_key].multiple?(M(),z(f,{key:0,gutter:10},{default:O(()=>[L(h,{span:18},{default:O(()=>[L(r,{size:"small",modelValue:l.data_value,"onUpdate:modelValue":e=>l.data_value=e,clearable:"",multiple:a.field.property_options[l.data_key].multiple,filterable:""},{default:O(()=>[(M(!0),j(F,null,N(a.field.property_options[l.data_key].options,e=>(M(),z(s,{key:e.id,label:e.title,value:e.id},null,8,["label","value"]))),128))],void 0,!0),_:2},1032,["modelValue","onUpdate:modelValue","multiple"])],void 0,!0),_:2},1024),L(h,{span:6},{default:O(()=>[L(r,{size:"small",modelValue:l.data_operation,"onUpdate:modelValue":e=>l.data_operation=e,placeholder:e.$t("Replace Value")},{default:O(()=>[L(s,{value:"",label:e.$t("Replace Options")},null,8,["label"]),L(s,{value:"subtract",label:e.$t("Subtract Options")},null,8,["label"]),L(s,{value:"add",label:e.$t("Add Options")},null,8,["label"])],void 0,!0),_:1},8,["modelValue","onUpdate:modelValue","placeholder"])],void 0,!0),_:2},1024)],void 0),_:2},1024)):(M(),z(r,{key:1,size:"small",modelValue:l.data_value,"onUpdate:modelValue":e=>l.data_value=e,clearable:"",multiple:a.field.property_options[l.data_key].multiple,filterable:""},{default:O(()=>[(M(!0),j(F,null,N(a.field.property_options[l.data_key].options,e=>(M(),z(s,{key:e.id,label:e.title,value:e.id},null,8,["label","value"]))),128))],void 0),_:2},1032,["modelValue","onUpdate:modelValue","multiple"]))],64)):"option_selector"==a.field.property_options[l.data_key].type?(M(),z(_,{key:7,field:{placeholder:"Select",is_multiple:a.field.property_options[l.data_key].multiple,option_key:a.field.property_options[l.data_key].option_key},modelValue:l.data_value,"onUpdate:modelValue":e=>l.data_value=e},null,8,["field","modelValue","onUpdate:modelValue"])):A("",!0)])):A("",!0)]),P("td",Zl,[L(b,{disabled:1==i.model.length,onClick:e=>n.deleteProp(t),size:"small",type:"danger"},{default:O(()=>[L(V,null,{default:O(()=>[L(y)],void 0,!0),_:1})],void 0),_:1},8,["disabled","onClick"])])]))),128))])]),P("div",Xl,[L(b,{onClick:l[0]||(l[0]=e=>n.addProperty()),type:"success",size:"small"},{default:O(()=>[L(V,null,{default:O(()=>[L(w)],void 0,!0),_:1}),D(" "+H(e.$t("Add More")),1)],void 0),_:1})])])}]]),lt={key:0,class:"fcrm_highlight_gray"},tt={style:{margin:"0",padding:"0","font-size":"10px"}};const at=Z({name:"MailerSettings",components:{VerifiedEmailInput:Cl},props:{mailer_settings:{type:Object,default:()=>({from_name:"",from_email:"",reply_to_name:"",reply_to_email:"",is_custom:"no"})}},mounted(){}},[["render",function(e,l,t,i,n,s){const r=d,m=a,c=o,h=p,f=T("verified-email-input"),_=u;return M(),j("div",null,[L(m,null,{default:O(()=>[L(r,{"true-value":"yes","false-value":"no",modelValue:t.mailer_settings.is_custom,"onUpdate:modelValue":l[0]||(l[0]=e=>t.mailer_settings.is_custom=e)},{default:O(()=>[D(H(e.$t("Vie_Set_CFNaE")),1)],void 0,!0),_:1},8,["modelValue"])],void 0),_:1}),"yes"==t.mailer_settings.is_custom?(M(),j("div",lt,[L(_,{gutter:20},{default:O(()=>[L(h,{md:12,sm:24},{default:O(()=>[L(m,{label:e.$t("From Name")},{default:O(()=>[L(c,{placeholder:e.$t("From Name"),modelValue:t.mailer_settings.from_name,"onUpdate:modelValue":l[1]||(l[1]=e=>t.mailer_settings.from_name=e),size:"large"},null,8,["placeholder","modelValue"])],void 0,!0),_:1},8,["label"])],void 0,!0),_:1}),L(h,{md:12,sm:24},{default:O(()=>[L(m,{label:e.$t("From Email")},{default:O(()=>[L(f,{modelValue:t.mailer_settings.from_email,"onUpdate:modelValue":l[2]||(l[2]=e=>t.mailer_settings.from_email=e),field:{placeholder:e.$t("From Email"),"data-type":"email"}},null,8,["modelValue","field"]),P("p",tt,H(e.$t("Vie_Please_msteisbyS")),1)],void 0,!0),_:1},8,["label"])],void 0,!0),_:1})],void 0),_:1}),L(_,{gutter:20},{default:O(()=>[L(h,{md:12,sm:24},{default:O(()=>[L(m,{label:e.$t("Reply To Name")},{default:O(()=>[L(c,{placeholder:e.$t("Reply To Name"),modelValue:t.mailer_settings.reply_to_name,"onUpdate:modelValue":l[3]||(l[3]=e=>t.mailer_settings.reply_to_name=e),size:"large"},null,8,["placeholder","modelValue"])],void 0,!0),_:1},8,["label"])],void 0,!0),_:1}),L(h,{md:12,sm:24},{default:O(()=>[L(m,{label:e.$t("Reply To Email")},{default:O(()=>[L(c,{placeholder:e.$t("Reply To Email"),type:"email",modelValue:t.mailer_settings.reply_to_email,"onUpdate:modelValue":l[4]||(l[4]=e=>t.mailer_settings.reply_to_email=e),size:"large"},null,8,["placeholder","modelValue"])],void 0,!0),_:1},8,["label"])],void 0,!0),_:1})],void 0),_:1})])):A("",!0)])}]]),ot={class:"fcrm_url_boxes"};const dt=Z({name:"MultiTextOptions",components:{Delete:I},emits:["update:modelValue"],props:{modelValue:{type:Array,default:()=>[""]},field:{type:Object}},data:()=>({options:[]}),watch:{options:{deep:!0,handler(){const e=[];this.options.forEach(l=>{l.value&&e.push(l.value)}),this.$emit("update:modelValue",e)}}},methods:{addMoreUrl(){this.options.push({value:""})},deleteUrl(e){this.options.splice(e,1)}},mounted(){const e=JSON.parse(JSON.stringify(this.modelValue));e&&e.length?(this.options=[],e.forEach(e=>{this.options.push({value:e})})):this.options=[{value:""}]}},[["render",function(e,l,a,d,i,n){const s=T("Delete"),r=t,u=v,p=o;return M(),j("div",ot,[(M(!0),j(F,null,N(i.options,(e,l)=>(M(),j("div",{class:"fcrm_each_text_option",key:l},[L(p,{type:a.field.input_type,placeholder:a.field.placeholder,modelValue:e.value,"onUpdate:modelValue":l=>e.value=l},{append:O(()=>[L(u,{onClick:e=>n.deleteUrl(l),disabled:1==i.options.length},{default:O(()=>[L(r,null,{default:O(()=>[L(s)],void 0,!0),_:1})],void 0,!0),_:1},8,["onClick","disabled"])]),_:2},1032,["type","placeholder","modelValue","onUpdate:modelValue"])]))),128)),L(u,{onClick:l[0]||(l[0]=e=>n.addMoreUrl()),size:"small",type:"info"},{default:O(()=>[D(H(e.$t("Add More")),1)],void 0),_:1})])}]]);const it=Z({name:"TaxonomySelector",props:["field","modelValue"],emits:["update:modelValue"],data(){return{model:this.modelValue,loading:!1,options:[]}},watch:{model(e){this.$emit("update:modelValue",e)}},methods:{fetchOptions(e){this.loading=!0,this.$get("reports/taxonomy-terms",{search:e,values:this.model,taxonomy:this.field.taxonomy}).then(e=>{this.options=e.options}).catch(e=>{this.handleError(e)}).finally(()=>{this.loading=!1})}},mounted(){this.fetchOptions("")}},[["render",function(e,l,t,a,o,d){const i=g,n=k,s=b;return R((M(),z(n,{modelValue:o.model,"onUpdate:modelValue":l[0]||(l[0]=e=>o.model=e),multiple:t.field.is_multiple,filterable:"",remote:"","reserve-keyword":"",disabled:t.field.disabled,size:t.field.size,placeholder:t.field.placeholder||e.$t("Please enter a keyword"),"remote-method":d.fetchOptions},{default:O(()=>[(M(!0),j(F,null,N(o.options,e=>(M(),z(i,{key:e.id,label:e.title,value:e.id},null,8,["label","value"]))),128))],void 0),_:1},8,["modelValue","multiple","disabled","size","placeholder","remote-method"])),[[s,o.loading]])}]]),nt={class:"fcrm_value_property_group"},st={class:"wp-list-table widefat fixed striped table-view-list posts"},rt={style:{width:"180px"}},ut={style:{"text-align":"right"}},pt={class:"text-align-right"};const mt=Z({name:"TextValueMultiProperties",props:["field","modelValue"],emits:["update:modelValue"],components:{InputTextPopper:xe,Plus:S,Delete:I},data(){return{model:this.modelValue}},watch:{model(e){this.$emit("update:modelValue",e)}},methods:{addProperty(){this.model.push({data_key:"",data_value:""})},deleteProp(e){this.model.splice(e,1)}}},[["render",function(e,l,a,d,i,n){const s=o,r=T("input-text-popper"),u=T("Delete"),p=t,m=v,c=T("Plus");return M(),j("div",nt,[P("table",st,[P("thead",null,[P("tr",null,[P("th",rt,H(a.field.data_key_label),1),P("th",null,H(a.field.data_value_label),1),l[1]||(l[1]=P("th",{style:{width:"50px"}},null,-1))])]),P("tbody",null,[(M(!0),j(F,null,N(i.model,(e,l)=>(M(),j("tr",{key:l},[P("td",null,[L(s,{placeholder:a.field.data_key_placeholder,link:"",modelValue:e.data_key,"onUpdate:modelValue":l=>e.data_key=l},null,8,["placeholder","modelValue","onUpdate:modelValue"])]),P("td",null,["text-popper"==a.field.value_input_type?(M(),z(r,{key:0,field:{placeholder:a.field.data_value_placeholder,popper_class:"fcrm_limit_height"},modelValue:e.data_value,"onUpdate:modelValue":l=>e.data_value=l},null,8,["field","modelValue","onUpdate:modelValue"])):(M(),z(s,{key:1,link:"",placeholder:a.field.data_value_placeholder,modelValue:e.data_value,"onUpdate:modelValue":l=>e.data_value=l},null,8,["placeholder","modelValue","onUpdate:modelValue"]))]),P("td",ut,[L(m,{disabled:1==i.model.length,onClick:e=>n.deleteProp(l),size:"small",type:"danger"},{default:O(()=>[L(p,null,{default:O(()=>[L(u)],void 0,!0),_:1})],void 0),_:1},8,["disabled","onClick"])])]))),128))])]),P("div",pt,[L(m,{onClick:l[0]||(l[0]=e=>n.addProperty()),type:"success",size:"small"},{default:O(()=>[L(p,null,{default:O(()=>[L(c)],void 0,!0),_:1}),D(" "+H(e.$t("Add More")),1)],void 0),_:1})])])}]]);const ct=Z({name:"WPUrlSelector",props:["field","modelValue"],emits:["update:modelValue"],data(){return{model:this.modelValue}},watch:{model(e){this.$emit("update:modelValue",e)}}},[["render",function(e,l,t,a,d,i){const n=o;return M(),z(n,{type:"url",placeholder:t.field.placeholder,modelValue:d.model,"onUpdate:modelValue":l[0]||(l[0]=e=>d.model=e)},null,8,["placeholder","modelValue"])}]]);const ht=Z({name:"WPEditorField",emits:["update:modelValue"],props:{modelValue:{type:String,default:()=>""},field:{type:Object,default:()=>({})},extra_style:{default:()=>""},height:{type:Number,default:()=>250}},components:{"wp-editor":nl},data(){return{model:this.modelValue,smartcodes:window.fcAdmin.globalSmartCodes}},watch:{model(e){this.$emit("update:modelValue",e)}}},[["render",function(e,l,t,a,o,d){const i=T("wp-editor");return M(),z(i,{height:t.height,extra_style:t.extra_style,editorShortcodes:o.smartcodes,modelValue:o.model,"onUpdate:modelValue":l[0]||(l[0]=e=>o.model=e)},null,8,["height","extra_style","editorShortcodes","modelValue"])}]]),ft={class:"fcrm_global_form_builder"};const _t=Z({name:"global_form_builder",components:{WithLabel:ne,InputText:se,InputTagList:pe,InlineCheckbox:me,InputTextPopper:xe,InputRadio:Ue,ImageRadio:Pe,HtmlViewer:Ae,WpEditor:nl,AjaxSelector:sl,OptionSelector:ee,CheckboxGroup:ul,FormManyDropdownMapper:yl,InputOption:Vl,InputColor:bl,InputDate:gl,InputNumber:kl,VerifiedEmailInput:Cl,TagAddRemoveMapping:Sl,CascadeOptionSelector:Tl,ConditionGroups:Fl,FormGroupMapper:Rl,ImageRadioToolTip:Yl,InputValuePairProperties:et,MailerConfig:at,MultiTextOptions:dt,TaxonomyTermsSelector:it,TextValueMultiProperties:mt,WPUrlSelector:ct,WpEditorField:ht,"input-text-popper":xe,"input-radio":Ue,"image-radio":Pe,"html-viewer":Ae,"wp-editor":nl,"ajax-selector":sl,"option-selector":ee,"checkbox-group":ul,"form-many-drop-down-mapper":yl,"input-option":Vl,"input-color":bl,"input-date":gl,"input-number":kl,"verified-email-input":Cl,"tag-add-remove-mapping":Sl,"cascade-option-selector":Tl,"condition-groups":Fl,"form-group-mapper":Rl,"image-radio-tool-tip":Yl,"input-value-pair-properties":et,"mailer-config":at,"multi-text-options":dt,"taxonomy-terms-selector":it,"text-value-multi-properties":mt,"wp-url-selector":ct,"wp-editor-field":ht},emits:["nativeSave"],props:{formData:{type:Object,required:!1,default:()=>({})},label_position:{required:!1,type:String,default:()=>"top"},fields:{required:!0,type:Object}},methods:{nativeSave(){this.$emit("nativeSave",this.formData)},compare(e,l,t){switch(l){case"=":return e===t;case"!=":return e!==t}},dependancyPass(e){if(e.dependency){const l=e.dependency.depends_on.split("/").reduce((e,l)=>e[l],this.formData);return!!this.compare(e.dependency.value,e.dependency.operator,l)}return!0}}},[["render",function(e,l,t,a,o,d){const i=T("with-label"),n=m;return M(),j("div",ft,[L(n,{onSubmit:J(d.nativeSave,["prevent"]),model:t.formData,"label-position":t.label_position},{default:O(()=>[W(e.$slots,"before_fields"),(M(!0),j(F,null,N(t.fields,(e,l)=>(M(),j(F,{key:l},[d.dependancyPass(e)?(M(),z(i,{key:0,field:e},{default:O(()=>[(M(),z(K(e.type),{modelValue:t.formData[l],"onUpdate:modelValue":e=>t.formData[l]=e,field:e},null,8,["modelValue","onUpdate:modelValue","field"]))],void 0,!0),_:2},1032,["field"])):A("",!0)],64))),128)),W(e.$slots,"after_fields")],void 0),_:3},8,["onSubmit","model","label-position"])])}]]);export{_t as F,Cl as V,nl as W}; diff --git a/wp-content/plugins/fluent-crm/assets/_FormBuilder2.js b/wp-content/plugins/fluent-crm/assets/_FormBuilder2.js new file mode 100644 index 0000000..ee1327d --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/_FormBuilder2.js @@ -0,0 +1 @@ +import{W as e,aJ as l,E as t,aw as o,e as a,aD as i,aA as d,ax as n,b6 as s,b7 as r,b8 as u,aG as c,az as p,k as m,g as h,P as f,aO as _,aF as v,aE as b,aL as y,aK as V,aY as g,ay as w,at as x,ao as k,aN as C}from"./vendor-element-plus.js?ver=3.1.8";import{aQ as S,W as I,Y as $,ay as U,a5 as E,Z as T,a9 as M,aa as A,ab as B,a8 as D,_ as j,X as P,a0 as z,J as L,az as H,$ as W,ax as O,a6 as F,b3 as N,ac as R,a7 as Y}from"./vendor.js?ver=3.1.8";import{_ as q,I as Q}from"./fc-bits-ui.js?ver=3.1.8";import{P as K}from"./PhotoWidget.js?ver=3.1.8";import{p as G}from"./input-popover-dropdown.js?ver=3.1.8";import{k as J}from"./data_config.js?ver=3.1.8";import{O as X}from"./_OptionSelector.js?ver=3.1.8";import{A as Z}from"./_AjaxSelector.js?ver=3.1.8";import{V as ee}from"./_VerifiedEmailInput.js?ver=3.1.8";const le=["innerHTML"],te=["innerHTML"];const oe=q({name:"withLabelField",components:{Icons:Q,InfoFilled:e},props:["field"]},[["render",function(e,a,i,d,n,s){const r=S("InfoFilled"),u=t,c=l,p=o;return I(),$(p,{class:z(i.field.wrapper_class)},U({default:E(()=>[j(e.$slots,"default"),i.field.inline_help?(I(),P("p",{key:0,class:"fcrm_secondary_text small d-flex items-center gap-4 fcrm_mt_4",innerHTML:i.field.inline_help},null,8,te)):D("",!0)],void 0),_:2},[i.field.label?{name:"label",fn:E(()=>[T("div",null,[M(A(i.field.label)+" ",1),i.field.help?(I(),$(c,{key:0,"popper-class":"sidebar-popper",effect:"dark",placement:"top"},{content:E(()=>[T("div",{innerHTML:i.field.help},null,8,le)]),default:E(()=>[B(u,{class:"tooltip-icon"},{default:E(()=>[B(r)],void 0,!0),_:1})],void 0,!0),_:1})):D("",!0)])]),key:"0"}:void 0]),1032,["class"])}]]);const ae=q({name:"InputText",props:["field","modelValue"],emits:["update:modelValue"],data(){return{model:this.modelValue}},watch:{model(e){this.$emit("update:modelValue",e)},modelValue(e){this.model=e}}},[["render",function(e,l,t,o,i,d){const n=a;return I(),$(n,{type:t.field.data_type||"text",min:t.field.min,max:t.field.max,placeholder:t.field.placeholder,modelValue:i.model,"onUpdate:modelValue":l[0]||(l[0]=e=>i.model=e)},null,8,["type","min","max","placeholder","modelValue"])}]]),ie={class:"fcrm_button_designer_dialog--row"},de={class:"fcrm_button_designer_dialog--controls"},ne={key:1,class:"fcrm-wp-editor-color-input"},se={class:"fcrm-wp-editor-color-input__hex"},re=["aria-label","onClick"],ue={key:2,class:"fcrm-wp-editor-slider-input"},ce={class:"fcrm_button_designer_dialog--preview"},pe={class:"fcrm_button_designer_dialog--preview-header"},me={class:"fcrm_button_designer_dialog--preview-header-title"},he={class:"fcrm_button_designer_dialog--preview-body"},fe={class:"dialog-footer"};const _e={name:"wp_editor",components:{Icons:Q,popover:G,ButtonDesigner:q({name:"tinyButtonDesigner",components:{Icons:Q},props:["visibility"],emits:["close","insert"],data(){return{localVisible:this.visibility,controls:{button_text:{type:"text",label:this.$t("Button Text"),value:this.$t("click here"),placeholder:this.$t("Enter button text")},button_url:{label:this.$t("Button URL"),type:"url",value:"",placeholder:this.$t("https://example.com")},backgroundColor:{label:this.$t("Background Color"),type:"color_picker",value:"#0072ff"},textColor:{label:this.$t("Text Color"),type:"color_picker",value:"#ffffff"},borderRadius:{label:this.$t("Border Radius"),type:"slider",value:5,max:50,min:0},fontSize:{label:this.$t("Font Size"),type:"slider",value:16,min:8,max:40},lineHeight:{label:this.$t("Line Height"),type:"slider",value:1,min:.8,max:3,step:.1},fontStyle:{label:this.$t("Font Style"),type:"checkboxes",value:[],options:{bold:"Bold",italic:"Italic",underline:"Underline"}}},style:""}},methods:{displayColor:e=>e&&""!==e.trim()?e:"#ffffff",displayHex(e){if(!e||""===e.trim())return"#F5F6F7";return(e.startsWith("#")?e:"#"+e).toUpperCase()},close(){this.localVisible=!1,this.$emit("close")},insert(){if(!this.controls.button_url.value||!this.controls.button_text.value)return void this.$notify.error("Button Text and URL is required");const e=`${this.controls.button_text.value}`;this.$emit("insert",e),this.close()},generateStyle(){const e=this.controls.fontStyle.value,l=-1===e.indexOf("underline")?"none":"underline",t=-1===e.indexOf("bold")?"normal":"bold",o=-1===e.indexOf("italic")?"normal":"italic",a=this.controls.textColor.value||"#ffffff",i=this.controls.backgroundColor.value||"#0072ff";this.style=`color:${a};background-color:${i};font-size:${this.controls.fontSize.value}px;line-height:${this.controls.lineHeight.value};border-radius:${this.controls.borderRadius.value}px;text-decoration:${l};font-weight:${t};font-style:${o};padding:0.8rem 1rem;border-color:#0072ff;`}},mounted(){this.generateStyle()},watch:{controls:{handler(){this.generateStyle()},deep:!0},visibility(e){this.localVisible=e},localVisible(e){!e&&this.visibility&&this.$emit("close")}}},[["render",function(e,l,t,f,_,v){const b=a,y=S("Icons"),V=s,g=r,w=u,x=p,k=c,C=o,U=n,j=d,z=i,F=m,N=h;return I(),$(N,{title:e.$t("Design Your Button"),modelValue:_.localVisible,"onUpdate:modelValue":l[3]||(l[3]=e=>_.localVisible=e),"append-to-body":!0,"show-close":!1,"close-on-click-modal":!1,"modal-class":"fcrm_button_designer_dialog",width:"60%"},{footer:E(()=>[T("span",fe,[B(F,{onClick:l[1]||(l[1]=e=>v.close())},{default:E(()=>[M(A(e.$t("Cancel")),1)],void 0,!0),_:1}),B(F,{type:"primary",onClick:l[2]||(l[2]=e=>v.insert())},{default:E(()=>[M(A(e.$t("Insert")),1)],void 0,!0),_:1})])]),default:E(()=>[T("div",ie,[B(z,{gutter:16},{default:E(()=>[B(j,{lg:12,md:12,sm:24},{default:E(()=>[T("div",de,[B(U,{"label-position":"top"},{default:E(()=>[(I(!0),P(L,null,H(_.controls,(l,t)=>(I(),$(C,{key:t,label:l.label},{default:E(()=>["text"==l.type||"url"==l.type?(I(),$(b,{key:0,type:l.type,modelValue:l.value,"onUpdate:modelValue":e=>l.value=e,placeholder:l.placeholder},null,8,["type","modelValue","onUpdate:modelValue","placeholder"])):"color_picker"==l.type?(I(),P("div",ne,[T("div",{class:"fcrm-wp-editor-color-input__swatch",style:W({backgroundColor:v.displayColor(l.value)})},null,4),T("span",se,A(v.displayHex(l.value)),1),T("button",{type:"button",class:"fcrm-wp-editor-color-input__clear","aria-label":e.$t("Clear color"),onClick:O(e=>l.value="#0072ff",["stop"])},[B(y,{"icon-name":"close"})],8,re),B(V,{modelValue:l.value,"onUpdate:modelValue":e=>l.value=e,onActiveChange:e=>{l.value=e},class:"fcrm-wp-editor-color-picker-trigger",clearable:""},null,8,["modelValue","onUpdate:modelValue","onActiveChange"])])):"slider"==l.type?(I(),P("div",ue,[B(g,{modelValue:l.value,"onUpdate:modelValue":e=>l.value=e,min:l.min,max:l.max,step:l.step},null,8,["modelValue","onUpdate:modelValue","min","max","step"]),B(w,{modelValue:l.value,"onUpdate:modelValue":e=>l.value=e,min:l.min,max:l.max,step:l.step??1,precision:null!=l.step&&l.step<1?1:0,"controls-position":"right",class:"fcrm-wp-editor-slider-input__number"},null,8,["modelValue","onUpdate:modelValue","min","max","step","precision"])])):"checkboxes"==l.type?(I(),$(k,{key:3,modelValue:l.value,"onUpdate:modelValue":e=>l.value=e},{default:E(()=>[(I(!0),P(L,null,H(l.options,(e,l)=>(I(),$(x,{key:l,label:l,value:l},{default:E(()=>[M(A(e),1)],void 0,!0),_:2},1032,["label","value"]))),128))],void 0,!0),_:2},1032,["modelValue","onUpdate:modelValue"])):D("",!0)],void 0,!0),_:2},1032,["label"]))),128))],void 0,!0),_:1})])],void 0,!0),_:1}),B(j,{lg:12,md:12,sm:24},{default:E(()=>[T("div",ce,[T("div",pe,[T("div",me,A(e.$t("Button Preview"))+": ",1)]),T("div",he,[T("a",{onClick:l[0]||(l[0]=e=>v.insert()),style:W(_.style),href:"#"},A(_.controls.button_text.value),5)])])],void 0,!0),_:1})],void 0,!0),_:1})])],void 0),_:1},8,["title","modelValue"])}]])},emits:["change","update:modelValue"],props:{editor_id:{type:String,default:()=>"wp_editor_"+Date.now()+parseInt(1e3*Math.random())},modelValue:{type:String,default:()=>""},editorShortcodes:{type:Array,default:()=>[]},height:{type:Number,default:()=>250},extra_style:{default:()=>""},showSmartCodes:{type:Boolean,default:!0}},data(){return{showButtonDesigner:!1,hasWpEditor:!!window.wp.editor&&!!wp.editor.autop||!!window.wp.oldEditor,editor:window.wp.oldEditor||window.wp.editor,plain_content:this.modelValue,cursorPos:this.modelValue?this.modelValue.length:0,buttonInitiated:!1,currentEditor:!1,editorMode:"visual"}},watch:{modelValue(e){this.syncExternalContent(e||"")},plain_content(){this.$emit("update:modelValue",this.plain_content),this.$emit("change",this.plain_content)}},methods:{syncExternalContent(e){if(!this.hasWpEditor)return void(e!==this.plain_content&&(this.plain_content=e));if(this.editor&&"function"==typeof this.editor.getContent&&this.editor.getContent(this.editor_id)===e)return;this.editor&&"function"==typeof this.editor.setContent?this.editor.setContent(this.editor_id,e):window.tinymce&&window.tinymce.get(this.editor_id)&&window.tinymce.get(this.editor_id).setContent(e);const l=document.getElementById(this.editor_id);l&&l.value!==e&&(l.value=e)},initEditor(){if(!this.hasWpEditor)return;const e=[];this.each(J,(l,t)=>{e.push(t+"="+l)}),this.editor.remove(this.editor_id);const l=this;this.editor.initialize(this.editor_id,{mediaButtons:!0,tinymce:{height:l.height,fontsize_formats:"8px 10px 12px 14px 16px 18px 24px 30px 36px 45px",toolbar1:"formatselect,fontselect,fontsizeselect,customInsertButton,table,bold,italic,bullist,numlist,link,blockquote,alignleft,aligncenter,alignright,underline,strikethrough,forecolor,removeformat,codeformat,outdent,indent,undo,redo",font_formats:e.join("; "),setup(e){e.on("change",function(e,t){l.changeContentEvent()}),l.buttonInitiated||(l.buttonInitiated=!0,e.addButton("customInsertButton",{text:l.$t("Button"),classes:"fluentcrm_editor_btn",onclick(){l.showInsertButtonModal(e)}}))},formats:{alignleft:{selector:"p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li,table,img",classes:"align-left",styles:{"text-align":"left"}},aligncenter:{selector:"p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li,table,img",classes:"align-center",styles:{"text-align":"center"},attributes:{align:"center"}},alignright:{selector:"p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li,table,img",classes:"align-right",styles:{"text-align":"right"},attributes:{align:"right"}}},content_style:l.extra_style},quicktags:!0}),jQuery("#"+this.editor_id).on("change",function(e){l.changeContentEvent()})},showInsertButtonModal(e){this.currentEditor=e,this.showButtonDesigner=!0},insertHtml(e){this.currentEditor.insertContent(e)},changeContentEvent(){const e=this.editor.getContent(this.editor_id);this.$emit("update:modelValue",e),this.$emit("change",e)},handleCommand(e){if(this.hasWpEditor)window.tinymce.activeEditor.insertContent(e);else{var l=this.plain_content.slice(0,this.cursorPos),t=this.plain_content.slice(this.cursorPos,this.plain_content.length);this.plain_content=l+e+t,this.cursorPos+=e.length}},updateCursorPos(){var e=jQuery(".wp_vue_editor_plain").prop("selectionStart");this.cursorPos=e},switchEditor(e,l,t){if(!this.hasWpEditor)return;const o=window.switchEditors;let a=!1;if(o&&"function"==typeof o.go)o.go(this.editor_id,e),a=!0;else{const e=document.querySelector(l);e&&(e.click(),a=!0)}a&&(this.editorMode=t)},triggerMediaButton(){window.wp&&window.wp.media&&window.wp.media.editor&&window.wp.media.editor.open(this.editor_id)},switchToVisual(){this.switchEditor("tmce",`#${this.editor_id}-tmce`,"visual")},switchToText(){this.switchEditor("html",`#${this.editor_id}-html`,"text")}},mounted(){this.initEditor()}},ve={class:"wp_vue_editor_wrapper"},be={class:"fcrm-editor-actions"},ye={class:"icon"},Ve={class:"fcrm-editor-toggle"},ge={class:"fcrm-toggle-switch"},we=["id"];const xe=q(_e,[["render",function(e,l,t,o,a,i){const d=S("Icons"),n=m,s=S("popover"),r=S("button-designer");return I(),P("div",ve,[T("div",be,[a.hasWpEditor?(I(),$(n,{key:0,size:"small",onClick:i.triggerMediaButton},{default:E(()=>[T("span",ye,[B(d,{"icon-name":"picture"})]),M(" "+A(e.$t("Add Media")),1)],void 0),_:1},8,["onClick"])):D("",!0),t.showSmartCodes&&t.editorShortcodes&&t.editorShortcodes.length?(I(),$(s,{key:1,class:z(["popover-wrapper",{"popover-wrapper-plaintext":!a.hasWpEditor}]),doc_url:"https://fluentcrm.com/docs/merge-codes-smart-codes-usage/",data:t.editorShortcodes,onCommand:i.handleCommand},null,8,["class","data","onCommand"])):D("",!0),T("div",Ve,[T("div",ge,[T("button",{type:"button",class:z({active:a.hasWpEditor&&"visual"===a.editorMode}),onClick:l[0]||(l[0]=(...e)=>i.switchToVisual&&i.switchToVisual(...e))},A(e.$t("Visual")),3),T("button",{type:"button",class:z({active:!a.hasWpEditor||"text"===a.editorMode}),onClick:l[1]||(l[1]=(...e)=>i.switchToText&&i.switchToText(...e))},A(e.$t("Text")),3)])])]),a.hasWpEditor?(I(),P("textarea",{key:0,class:"wp_vue_editor",id:t.editor_id},A(t.modelValue),9,we)):F((I(),P("textarea",{key:1,class:"wp_vue_editor wp_vue_editor_plain","onUpdate:modelValue":l[2]||(l[2]=e=>a.plain_content=e),onClick:l[3]||(l[3]=(...e)=>i.updateCursorPos&&i.updateCursorPos(...e))}," ",512)),[[N,a.plain_content]]),a.showButtonDesigner?(I(),$(r,{key:2,onClose:l[4]||(l[4]=()=>{a.showButtonDesigner=!1}),onInsert:i.insertHtml,visibility:a.showButtonDesigner},null,8,["onInsert","visibility"])):D("",!0)])}]]);const ke=q({name:"WPEditorField",emits:["update:modelValue"],props:{modelValue:{type:String,default:()=>""},field:{type:Object,default:()=>({})},extra_style:{default:()=>""},height:{type:Number,default:()=>250},showSmartCodes:{type:Boolean,default:!0}},components:{WpBaseEditor:xe},data(){return{model:this.modelValue,smartcodes:window.fcAdmin.globalSmartCodes}},watch:{modelValue(e){e!==this.model&&(this.model=e||"")},model(e){this.$emit("update:modelValue",e)}}},[["render",function(e,l,t,o,a,i){const d=S("wp-base-editor");return I(),$(d,{height:t.height,extra_style:t.extra_style,editorShortcodes:a.smartcodes,modelValue:a.model,"onUpdate:modelValue":l[0]||(l[0]=e=>a.model=e),showSmartCodes:t.showSmartCodes},null,8,["height","extra_style","editorShortcodes","modelValue","showSmartCodes"])}]]),Ce={name:"InputPopover",components:{MoreFilled:f},emits:["update:modelValue","update"],props:{modelValue:{type:[String,Number],default:""},placeholder:{type:String,default:""},placement:{type:String,default:"bottom"},icon:{type:String,default:"el-icon-more"},fieldType:{type:String,default:"text"},popper_class:{type:String,default:""},data:{type:Array,default:()=>[]},attrName:{type:String,default:"attribute_name"},popper_extra:{type:String,default:""},doc_url:{type:String,default:""},disabled:{type:Boolean,default:!1}},data:()=>({visible:!1,activeIndex:0,searchQuery:""}),computed:{localValue:{get(){return this.modelValue},set(e){this.$emit("update:modelValue",e),this.$emit("update",e)}}},methods:{selectEmoji(e){this.insertShortcode(e.data)},insertShortcode(e){if(this.disabled)return;const l=(this.localValue||"").toString().trim(),t=(l?l+" ":"")+e.replace(/param_name/,this.attrName);this.localValue=t,this.visible=!1},filteredShortcodes(e={}){if(!this.searchQuery)return e;const l=this.searchQuery.toLowerCase(),t={};return Object.entries(e).forEach(([e,o])=>{(e.toLowerCase().includes(l)||o.toLowerCase().includes(l))&&(t[e]=o)}),t}}},Se={class:"el_pop_data_group"},Ie={class:"el_pop_data_headings"},$e=["data-item_index","onClick"],Ue={key:0,class:"pop_doc"},Ee=["href"],Te={class:"el_pop_data_body"},Me={class:"el_pop_search"},Ae=["onClick"],Be={key:1,class:"fc_textarea_with_popover"},De={class:"el_pop_data_group"},je={class:"el_pop_data_headings"},Pe=["data-item_index","onClick"],ze={key:0,class:"pop_doc"},Le=["href"],He={class:"el_pop_data_body"},We={class:"el_pop_search"},Oe=["onClick"];const Fe=q(Ce,[["render",function(e,l,o,i,d,n){const s=a,r=_,u=S("MoreFilled"),c=t;return I(),P("div",{class:z(["fc_input_popover_wrapper",{"is-textarea":"textarea"===o.fieldType}])},["textarea"!==o.fieldType?(I(),$(s,{key:0,class:"fc_pop_append",placeholder:o.placeholder,modelValue:n.localValue,"onUpdate:modelValue":l[2]||(l[2]=e=>n.localValue=e),type:o.fieldType,disabled:o.disabled},U({_:2},[o.disabled?void 0:{name:"suffix",fn:E(()=>[B(r,{ref:"input-popover",placement:"right-end","popper-class":"fcrm-smartcodes-popover el-dropdown-list-wrapper "+o.popper_extra,visible:d.visible,"onUpdate:visible":l[1]||(l[1]=e=>d.visible=e),trigger:"click"},{reference:E(()=>[...l[6]||(l[6]=[T("span",{class:"fluentcrm_url fluentcrm_clickable"}," { } ",-1)])]),default:E(()=>[T("div",Se,[T("div",Ie,[T("ul",null,[(I(!0),P(L,null,H(o.data,(e,l)=>(I(),P("li",{"data-item_index":l,key:l,class:z(d.activeIndex==l?"active_item_selected":""),onClick:e=>d.activeIndex=l},A(e.title),11,$e))),128))]),o.doc_url?(I(),P("div",Ue,[T("a",{href:o.doc_url,target:"_blank",rel:"noopener"},A(e.$t("Learn More")),9,Ee)])):D("",!0)]),T("div",Te,[T("div",Me,[B(s,{modelValue:d.searchQuery,"onUpdate:modelValue":l[0]||(l[0]=e=>d.searchQuery=e),placeholder:e.$t("Search shortcodes..."),clearable:""},null,8,["modelValue","placeholder"])]),(I(!0),P(L,null,H(o.data,(e,l)=>(I(),P("div",{key:l},[F(T("ul",{class:z("el_pop_body_item_"+l)},[(I(!0),P(L,null,H(n.filteredShortcodes(e.shortcodes),(e,l)=>(I(),P("li",{onClick:e=>n.insertShortcode(l),key:l},[M(A(e),1),T("span",null,A(l),1)],8,Ae))),128))],2),[[R,d.activeIndex==l]])]))),128))])])],void 0,!0),_:1},8,["popper-class","visible"])]),key:"0"}]),1032,["placeholder","modelValue","type","disabled"])):(I(),P("div",Be,[B(s,{placeholder:o.placeholder,rows:4,type:"textarea",modelValue:n.localValue,"onUpdate:modelValue":l[3]||(l[3]=e=>n.localValue=e),disabled:o.disabled},null,8,["placeholder","modelValue","disabled"]),o.disabled?D("",!0):(I(),$(r,{key:0,ref:"input-popover",placement:"right-end","popper-class":"fcrm-smartcodes-popover el-dropdown-list-wrapper "+o.popper_extra,visible:d.visible,"onUpdate:visible":l[5]||(l[5]=e=>d.visible=e),trigger:"click"},{reference:E(()=>[B(c,{class:"fluentcrm_url fluentcrm_clickable"},{default:E(()=>[B(u)],void 0,!0),_:1})]),default:E(()=>[T("div",De,[T("div",je,[T("ul",null,[(I(!0),P(L,null,H(o.data,(e,l)=>(I(),P("li",{"data-item_index":l,key:l,class:z(d.activeIndex==l?"active_item_selected":""),onClick:e=>d.activeIndex=l},A(e.title),11,Pe))),128))]),o.doc_url?(I(),P("div",ze,[T("a",{href:o.doc_url,target:"_blank",rel:"noopener"},A(e.$t("Learn More")),9,Le)])):D("",!0)]),T("div",He,[T("div",We,[B(s,{modelValue:d.searchQuery,"onUpdate:modelValue":l[4]||(l[4]=e=>d.searchQuery=e),placeholder:e.$t("Search shortcodes..."),clearable:""},null,8,["modelValue","placeholder"])]),(I(!0),P(L,null,H(o.data,(e,l)=>(I(),P("div",{key:l},[F(T("ul",{class:z("el_pop_body_item_"+l)},[(I(!0),P(L,null,H(n.filteredShortcodes(e.shortcodes),(e,l)=>(I(),P("li",{onClick:e=>n.insertShortcode(l),key:l},[M(A(e),1),T("span",null,A(l),1)],8,Oe))),128))],2),[[R,d.activeIndex==l]])]))),128))])])],void 0),_:1},8,["popper-class","visible"]))]))],2)}]]);const Ne=q({name:"InputTextPopper",props:["field","modelValue"],emits:["update:modelValue"],components:{InputPopover:Fe},data(){return{model:this.modelValue,smartcodes:window.fcAdmin.globalSmartCodes}},watch:{model(e){this.$emit("update:modelValue",e)}},created(){this.field.context_codes&&window.fcrm_funnel_context_codes&&(this.smartcodes=[...this.smartcodes,...window.fcrm_funnel_context_codes]),window.fcAdmin.extendedSmartCodes&&(this.smartcodes=[...this.smartcodes,...window.fcAdmin.extendedSmartCodes])}},[["render",function(e,l,t,o,a,i){const d=S("input-popover");return I(),$(d,{doc_url:"https://fluentcrm.com/docs/merge-codes-smart-codes-usage/","field-type":t.field.field_type,placeholder:t.field.placeholder,popper_class:t.field.popper_class,data:a.smartcodes,modelValue:a.model,"onUpdate:modelValue":l[0]||(l[0]=e=>a.model=e)},null,8,["field-type","placeholder","popper_class","data","modelValue"])}]]);const Re=q({name:"InputRadioImage",props:["field","modelValue","size"],emits:["update:modelValue"],data(){return{model:this.modelValue,boxSize:this.size||120}},watch:{model(e){this.$emit("update:modelValue",e)}}},[["render",function(e,l,t,o,a,i){const d=v,n=b;return I(),$(n,{class:"fc_image_radios",modelValue:a.model,"onUpdate:modelValue":l[0]||(l[0]=e=>a.model=e)},{default:E(()=>[(I(!0),P(L,null,H(t.field.options,(e,l)=>(I(),$(d,{key:l,value:e.id},{default:E(()=>[T("div",{style:W({backgroundImage:"url("+e.image+")",width:a.boxSize+"px",height:a.boxSize+"px"}),class:z([a.model==e.id?"fc_image_active":"","fc_image_box"])},[T("span",null,A(e.label),1)],6)],void 0,!0),_:2},1032,["value"]))),128))],void 0),_:1},8,["modelValue"])}]]);const Ye=q({name:"InputRadio",props:{field:{type:Object,required:!0},modelValue:{type:[String,Number,Boolean],default:null}},emits:["update:modelValue"],data(){return{model:this.modelValue}},watch:{modelValue(e){this.model=e},model(e){this.$emit("update:modelValue",e)}}},[["render",function(e,l,t,o,a,i){const d=v,n=b;return I(),$(n,{class:z(t.field.wrapper_class),modelValue:a.model,"onUpdate:modelValue":l[0]||(l[0]=e=>a.model=e)},{default:E(()=>[(I(!0),P(L,null,H(t.field.options,(e,l)=>(I(),$(d,{key:l,value:e.id},{default:E(()=>[M(A(e.label),1)],void 0,!0),_:2},1032,["value"]))),128))],void 0),_:1},8,["class","modelValue"])}]]);const qe=q({name:"InputOption",props:["field","modelValue"],emits:["update:modelValue"],data(){return{model:this.modelValue}},watch:{model(e){this.$emit("update:modelValue",e)}}},[["render",function(e,l,t,o,a,i){const d=y,n=V;return I(),$(n,{clearable:"",filterable:"",placeholder:t.field.placeholder,multiple:t.field.multiple,class:z(t.field.wrapper_class),modelValue:a.model,"onUpdate:modelValue":l[0]||(l[0]=e=>a.model=e)},{default:E(()=>[(I(!0),P(L,null,H(t.field.options,e=>(I(),$(d,{key:e.id,value:e.id,label:e.label},null,8,["value","label"]))),128))],void 0),_:1},8,["placeholder","multiple","class","modelValue"])}]]);const Qe=q({name:"InputColor",props:{field:{type:Object,required:!0},modelValue:{type:String,default:""}},emits:["update:modelValue"],data(){return{model:this.modelValue}},watch:{modelValue(e){this.model=e},model(e){this.$emit("update:modelValue",e)}},methods:{handleActiveChange(e){this.model=e}}},[["render",function(e,l,t,o,a,i){const d=s;return I(),$(d,{onActiveChange:i.handleActiveChange,"color-format":t.field.colorFormat,"show-alpha":t.field.showAlpha,size:"large",modelValue:a.model,"onUpdate:modelValue":l[0]||(l[0]=e=>a.model=e)},null,8,["onActiveChange","color-format","show-alpha","modelValue"])}]]);const Ke=q({name:"InputDate",props:["field","modelValue"],emits:["update:modelValue"],data(){return{model:this.modelValue}},computed:{convertedFormat(){return this.field.value_format?this.field.value_format.replace(/yyyy/g,"YYYY").replace(/dd/g,"DD"):"YYYY-MM-DD HH:mm:ss"}},watch:{model(e){this.$emit("update:modelValue",e)}}},[["render",function(e,l,t,o,a,i){const d=g;return I(),$(d,{"value-format":i.convertedFormat,modelValue:a.model,"onUpdate:modelValue":l[0]||(l[0]=e=>a.model=e),placeholder:t.field.placeholder,type:t.field.data_type},null,8,["value-format","modelValue","placeholder","type"])}]]);const Ge=q({name:"InputNumber",props:{field:{type:Object,required:!0},modelValue:{type:[Number,String],default:null}},emits:["update:modelValue"],data(){return{model:this.modelValue?Number(this.modelValue):null}},watch:{modelValue(e){this.model=e?Number(e):null},model(e){this.$emit("update:modelValue",e)}}},[["render",function(e,l,t,o,a,i){const d=u;return I(),$(d,{min:t.field.min,max:t.field.max,step:t.field.step,modelValue:a.model,"onUpdate:modelValue":l[0]||(l[0]=e=>a.model=e),class:"fc-input-number-field"},null,8,["min","max","step","modelValue"])}]]);const Je=q({name:"InlineCheckbox",props:["field","modelValue"],emits:["update:modelValue"],data(){return{model:this.modelValue,isInternalUpdate:!1}},watch:{model(e){this.isInternalUpdate||this.$emit("update:modelValue",e)},modelValue(e){this.model!==e&&(this.isInternalUpdate=!0,this.model=e,this.$nextTick(()=>{this.isInternalUpdate=!1}))}}},[["render",function(e,l,t,o,a,i){const d=p;return I(),$(d,{"true-value":void 0!==t.field.true_value?t.field.true_value:t.field.true_label,"false-value":void 0!==t.field.false_value?t.field.false_value:t.field.false_label,disabled:t.field.disabled,modelValue:a.model,"onUpdate:modelValue":l[0]||(l[0]=e=>a.model=e)},{default:E(()=>[M(A(t.field.checkbox_label),1)],void 0),_:1},8,["true-value","false-value","disabled","modelValue"])}]]),Xe={class:"fc_checkbox_group"};const Ze=q({name:"CheckboxGroup",props:["field","modelValue"],emits:["update:modelValue"],data(){return{model:this.modelValue??[],isIndeterminate:!1,checkAll:!1}},watch:{model(e){this.$emit("update:modelValue",e)}},computed:{optionKeys(){if(!this.field.has_all_selector)return[];const e=[];return this.each(this.field.options,l=>{e.push(l.id)}),e}},methods:{checked(e){if(this.field.has_all_selector){const e=this.optionKeys.length;this.checkAll=e===this.model.length,this.isIndeterminate=e>0&&ea.checkAll=e),indeterminate:a.isIndeterminate,onChange:i.all},{default:E(()=>[M(A(t.field.all_selector_label),1)],void 0),_:1},8,["modelValue","indeterminate","onChange"]),l[2]||(l[2]=T("div",{style:{margin:"15px 0"}},null,-1))],64)):D("",!0),B(n,{class:z(t.field.input_class),modelValue:a.model,"onUpdate:modelValue":l[1]||(l[1]=e=>a.model=e),onChange:i.checked},{default:E(()=>[(I(!0),P(L,null,H(t.field.options,e=>(I(),$(d,{value:e.id,key:e.id},{default:E(()=>[M(A(e.label),1)],void 0,!0),_:2},1032,["value"]))),128))],void 0),_:1},8,["class","modelValue","onChange"])])}]]),el=["innerHTML"],ll=["innerHTML"];const tl=q({name:"InputText",props:["field","modelValue"],emits:["update:modelValue"],components:{OptionSelector:X},data(){return{model:this.modelValue}},watch:{model(e){this.$emit("update:modelValue",e)}}},[["render",function(e,l,t,a,i,d){const n=S("option-selector"),s=o;return I(),P("div",{class:z(["fc_tag_list_wrapper",t.field.wrapper_class])},[B(s,{label:t.field.tag_label},{default:E(()=>[B(n,{modelValue:i.model.tags,"onUpdate:modelValue":l[0]||(l[0]=e=>i.model.tags=e),field:{is_multiple:!0,creatable:!0,option_key:"tags"}},null,8,["modelValue"]),t.field.tag_help?(I(),P("p",{key:0,class:"fc_inline_help",innerHTML:t.field.tag_help},null,8,el)):D("",!0)],void 0),_:1},8,["label"]),B(s,{label:t.field.list_label},{default:E(()=>[B(n,{modelValue:i.model.lists,"onUpdate:modelValue":l[1]||(l[1]=e=>i.model.lists=e),field:{is_multiple:!0,creatable:!0,option_key:"lists"}},null,8,["modelValue"]),t.field.list_help?(I(),P("p",{key:0,class:"fc_inline_help",innerHTML:t.field.list_help},null,8,ll)):D("",!0)],void 0),_:1},8,["label"])],2)}]]),ol={class:"fc_html"},al={key:0,style:{"margin-bottom":"0"}},il=["innerHTML"];const dl=q({name:"HtmlViewer",props:["field"]},[["render",function(e,l,t,o,a,i){return I(),P("div",ol,[t.field.heading?(I(),P("h3",al,A(t.field.heading),1)):D("",!0),T("div",{innerHTML:t.field.info},null,8,il)])}]]),nl={class:"fc_tag_mappings"},sl={key:0,class:"fc_horizontal_table"};const rl=q({name:"TagAddRemoveElement",props:["field","modelValue"],emits:["update:modelValue"],components:{OptionSelector:X},data(){return{model:this.modelValue,tags_ready:!1}},watch:{model(e){this.$emit("update:modelValue",e)}},mounted(){this.renewOptionCache("tags",()=>{this.tags_ready=!0})}},[["render",function(e,l,t,o,a,i){const d=S("option-selector"),n=w;return F((I(),P("div",nl,[a.tags_ready?(I(),P("table",sl,[T("thead",null,[T("tr",null,[T("th",null,A(t.field.selector_label),1),T("th",null,A(t.field.add_tag_label),1),T("th",null,A(t.field.remove_tag_label),1)])]),T("tbody",null,[(I(!0),P(L,null,H(t.field.selector_options,e=>(I(),P("tr",{key:e.id},[T("td",null,A(e.title),1),T("td",null,[B(d,{modelValue:a.model[e.id].add_tags,"onUpdate:modelValue":l=>a.model[e.id].add_tags=l,field:{option_key:"tags",creatable:!0,is_multiple:!0}},null,8,["modelValue","onUpdate:modelValue"])]),T("td",null,[B(d,{modelValue:a.model[e.id].remove_tags,"onUpdate:modelValue":l=>a.model[e.id].remove_tags=l,field:{option_key:"tags",creatable:!0,is_multiple:!0}},null,8,["modelValue","onUpdate:modelValue"])])]))),128))])])):D("",!0)])),[[n,!a.tags_ready]])}]]),ul={key:0,class:"fc_horizontal_table"},cl={class:"text-align-right fcrm_text_align_center"},pl={class:"fcrm_add_new_row_old"},ml={class:"text-align-right"},hl={class:"icon"};const fl=q({name:"FormManyDropdownMapper",components:{Icons:Q,ArrowUp:k,ArrowDown:x,OptionSelector:X,AjaxSelector:Z,InputText:ae,InputTextPopper:Ne},props:["field","modelValue"],data:()=>({render_table:!0}),methods:{addMore(){this.modelValue.push({field_key:"",field_value:""})},deleteItem(e){this.modelValue.splice(e,1)},movePosition(e,l){let t=e-1;"down"===l&&(t=e+1);const o=this.modelValue,a=o[e];o.splice(e,1),o.splice(t,0,a),this.render_table=!1,this.$nextTick(()=>{this.render_table=!0})}}},[["render",function(e,l,o,a,i,d){const n=S("ajax-selector"),s=S("option-selector"),r=y,u=V,c=S("input-text"),p=S("input-text-popper"),h=S("ArrowUp"),f=t,_=m,v=S("ArrowDown"),b=C,g=S("Icons");return i.render_table?(I(),P("table",ul,[T("thead",null,[T("tr",null,[T("th",null,A(o.field.local_label),1),T("th",null,A(o.field.remote_label),1),l[1]||(l[1]=T("th",{width:"40px"},null,-1))])]),T("tbody",null,[(I(!0),P(L,null,H(o.modelValue,(l,t)=>(I(),P("tr",{key:t},[T("td",null,[o.field.field_ajax_selector?(I(),$(n,{key:0,modelValue:l.field_key,"onUpdate:modelValue":e=>l.field_key=e,field:{placeholder:o.field.local_placeholder,...o.field.field_ajax_selector}},null,8,["modelValue","onUpdate:modelValue","field"])):o.field.field_option_selector?(I(),$(s,{key:1,modelValue:l.field_key,"onUpdate:modelValue":e=>l.field_key=e,field:{placeholder:o.field.local_placeholder,...o.field.field_option_selector}},null,8,["modelValue","onUpdate:modelValue","field"])):(I(),$(u,{key:2,clearable:"",filterable:"",modelValue:l.field_key,"onUpdate:modelValue":e=>l.field_key=e,placeholder:o.field.local_placeholder},{default:E(()=>[(I(!0),P(L,null,H(o.field.fields,(e,l)=>(I(),$(r,{key:l,value:l,label:e.label},null,8,["value","label"]))),128))],void 0),_:1},8,["modelValue","onUpdate:modelValue","placeholder"]))]),T("td",null,[o.field.value_option_selector?(I(),$(s,{key:0,modelValue:l.field_value,"onUpdate:modelValue":e=>l.field_value=e,field:{placeholder:o.field.remote_placeholder,...o.field.value_option_selector}},null,8,["modelValue","onUpdate:modelValue","field"])):o.field.value_options?(I(),$(u,{key:1,clearable:"",filterable:"",modelValue:l.field_value,"onUpdate:modelValue":e=>l.field_value=e,placeholder:o.field.remote_placeholder},{default:E(()=>[(I(!0),P(L,null,H(o.field.value_options,e=>(I(),$(r,{key:e.id,value:e.id,label:e.title},null,8,["value","label"]))),128))],void 0),_:1},8,["modelValue","onUpdate:modelValue","placeholder"])):"input-text"==o.field.remote_field_type?(I(),$(c,{key:2,field:o.field.remote_field,modelValue:l.field_value,"onUpdate:modelValue":e=>l.field_value=e},null,8,["field","modelValue","onUpdate:modelValue"])):"input-text-popper"==o.field.remote_field_type?(I(),$(p,{key:3,field:o.field.remote_field,modelValue:l.field_value,"onUpdate:modelValue":e=>l.field_value=e},null,8,["field","modelValue","onUpdate:modelValue"])):D("",!0)]),T("td",null,[T("div",cl,[o.field.manage_serial?(I(),$(b,{key:0},{default:E(()=>[B(_,{onClick:e=>d.movePosition(t,"up"),disabled:0==t,size:"small"},{default:E(()=>[B(f,null,{default:E(()=>[B(h)],void 0,!0),_:1})],void 0,!0),_:1},8,["onClick","disabled"]),B(_,{onClick:e=>d.movePosition(t,"down"),disabled:t==o.modelValue.length-1,size:"small"},{default:E(()=>[B(f,null,{default:E(()=>[B(v)],void 0,!0),_:1})],void 0,!0),_:1},8,["onClick","disabled"])],void 0),_:2},1024)):D("",!0),B(_,{onClick:e=>d.deleteItem(t),disabled:1==o.modelValue.length,type:"danger",class:"only-icon-btn small",plain:"",size:"small","aria-label":e.$t("Delete"),title:e.$t("Delete")},{default:E(()=>[B(g,{"icon-name":"delete"})],void 0),_:1},8,["onClick","disabled","aria-label","title"])])])]))),128)),T("tr",pl,[l[2]||(l[2]=T("td",null,null,-1)),l[3]||(l[3]=T("td",null,null,-1)),T("td",null,[T("div",ml,[B(_,{onClick:l[0]||(l[0]=e=>d.addMore()),size:"small"},{default:E(()=>[T("span",hl,[B(g,{"icon-name":"plus"})]),M(" "+A(e.$t("Add More")),1)],void 0),_:1})])])])])])):D("",!0)}]]),_l={class:"fc_global_form_builder"};const vl=q({name:"global_form_builder",components:{WithLabel:oe,InputText:ae,PhotoWidget:K,InputTextPopper:Ne,WpEditor:ke,ImageRadio:Re,InputRadio:Ye,InputOption:qe,AjaxSelector:Z,InputColor:Qe,InputNumber:Ge,OptionSelector:X,InlineCheckbox:Je,CheckboxGroup:Ze,VerifiedEmailInput:ee,InputTagList:tl,HtmlViewer:dl,TagAddRemoveMapping:rl,InputDate:Ke,WpBaseEditor:xe,"form-many-drop-down-mapper":fl},emits:["nativeSave"],props:{formData:{type:Object,required:!1,default:()=>({})},label_position:{required:!1,type:String,default:()=>"top"},fields:{required:!0,type:Object}},methods:{nativeSave(){this.$emit("nativeSave",this.formData)},compare(e,l,t){switch(l){case"=":return e===t;case"!=":return e!==t}},dependancyPass(e){if(!e)return!1;if(e.dependency){const l=e.dependency.depends_on.split("/").reduce((e,l)=>e[l],this.formData);return!!this.compare(e.dependency.value,e.dependency.operator,l)}return!0}}},[["render",function(e,l,t,o,a,i){const d=S("with-label"),s=n;return I(),P("div",_l,[B(s,{onSubmit:O(i.nativeSave,["prevent"]),data:t.formData,"label-position":t.label_position},{default:E(()=>[(I(!0),P(L,null,H(t.fields,(e,l)=>(I(),P(L,{key:l},[i.dependancyPass(e)?(I(),$(d,{key:0,field:e},{default:E(()=>[(I(),$(Y(e.type),{modelValue:t.formData[l],"onUpdate:modelValue":e=>t.formData[l]=e,field:e},null,8,["modelValue","onUpdate:modelValue","field"]))],void 0,!0),_:2},1032,["field"])):D("",!0)],64))),128))],void 0),_:1},8,["onSubmit","data","label-position"])])}]]);export{vl as F,Fe as I,xe as W,ke as a,Ne as b,fl as c}; diff --git a/wp-content/plugins/fluent-crm/assets/_ImportRunner.js b/wp-content/plugins/fluent-crm/assets/_ImportRunner.js new file mode 100644 index 0000000..a8711e1 --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/_ImportRunner.js @@ -0,0 +1 @@ +import{F as e}from"./_FormBuilder2.js?ver=3.1.8";import{aQ as t,W as r,X as i,Z as l,aa as a,ab as n,a9 as o,a8 as s,J as m,az as p,Y as d,a5 as c,a6 as _}from"./vendor.js?ver=3.1.8";import{_ as u}from"./fc-bits-ui.js?ver=3.1.8";import{aL as h,aK as f,aP as g,ay as v,av as y}from"./vendor-element-plus.js?ver=3.1.8";import{O as b}from"./_OptionSelector.js?ver=3.1.8";const k={class:"fc_credential"},$={class:"fc_step_header"},V={key:0,class:"fcrm_import_others_doc_link"},w=["href"];const C=u({name:"CredentialVerify",components:{FormBuilder:e},props:["driver","cred","current_driver"],emits:["verified","back"],expose:["verifyConnection","verifying"],data:()=>({verifying:!1}),methods:{verifyConnection(){this.verifying=!0,this.$emit("verifying-change",!0),this.$post("migrators/verify-cred",{driver:this.driver,credential:this.cred}).then(e=>{this.$notify.success(e.message),this.$emit("verified")}).catch(e=>{this.handleError(e)}).finally(()=>{this.verifying=!1,this.$emit("verifying-change",!1)})},back(){this.$emit("back")}}},[["render",function(e,m,p,d,c,_){const u=t("form-builder");return r(),i("div",k,[l("div",$,[l("h3",null,a(e.$t("Connect with"))+" "+a(p.driver),1),l("p",null,a(e.$t("Please configure"))+" "+a(p.driver)+" "+a(e.$t("with API key")),1)]),n(u,{class:"fcrm_mb_12",formData:p.cred,fields:p.current_driver.credential_fields},null,8,["formData","fields"]),p.current_driver.doc_url?(r(),i("p",V,[l("a",{style:{"text-decoration":"underline"},target:"_blank",rel:"noopener",href:p.current_driver.doc_url},[m[0]||(m[0]=l("svg",{xmlns:"http://www.w3.org/2000/svg",width:"11",height:"11",viewBox:"0 0 11 11",fill:"none"},[l("path",{d:"M4.2 1.8V3H1.2V9.6H7.8V6.6H9V10.2C9 10.3591 8.93679 10.5117 8.82426 10.6243C8.71174 10.7368 8.55913 10.8 8.4 10.8H0.6C0.44087 10.8 0.288258 10.7368 0.175736 10.6243C0.0632141 10.5117 0 10.3591 0 10.2V2.4C0 2.24087 0.0632141 2.08826 0.175736 1.97574C0.288258 1.86321 0.44087 1.8 0.6 1.8H4.2ZM10.8 0V4.8H9.6V2.0478L4.9242 6.7242L4.0758 5.8758L8.751 1.2H6V0H10.8Z",fill:"var(--fc-deep-bg)"})],-1)),o(" "+a(e.$t("Check the documentation")),1)],8,w),o(" "+a(e.$t("for migrating from"))+" ",1),l("b",null,a(p.current_driver.title),1)])):s("",!0)])}]]),H={class:"fcrm_import_contact_field_mapper fcrm_import_tag_mapper"},M={class:"fcrm_import_tag_mapper_table"},F={key:0};const U=u({name:"ContactFieldMapper",props:["contact_fields","contact_fillables","driver"],data:()=>({}),methods:{ucFirst:e=>e?e.charAt(0).toUpperCase()+e.slice(1):""}},[["render",function(e,t,o,s,_,u){const v=h,y=f,b=g;return r(),i("div",H,[l("table",M,[l("thead",null,[l("tr",null,[l("th",null,a(u.ucFirst(o.driver))+" "+a(e.$t("Field")),1),l("th",null,a(e.$t("FluentCRM Field")),1),l("th",null,a(e.$t("Skip")),1)])]),l("tbody",null,[(r(!0),i(m,null,p(o.contact_fields,(t,s)=>(r(),i("tr",{key:s},[l("td",null,a(t.remote_label),1),l("td",null,["yes"==t.will_skip?(r(),i("span",F,a(e.$t("this value will be skipped")),1)):(r(),d(y,{key:1,modelValue:t.fluentcrm_field,"onUpdate:modelValue":e=>t.fluentcrm_field=e,filterable:"",clearable:""},{default:c(()=>[t.options?(r(!0),i(m,{key:0},p(t.options,(e,t)=>(r(),d(v,{key:t,value:t,label:e},null,8,["value","label"]))),128)):(r(!0),i(m,{key:1},p(o.contact_fillables,(e,t)=>(r(),d(v,{key:t,value:t,label:e},null,8,["value","label"]))),128))],void 0),_:2},1032,["modelValue","onUpdate:modelValue"]))]),l("td",null,[n(b,{"active-value":"yes","inactive-value":"no",modelValue:t.will_skip,"onUpdate:modelValue":e=>t.will_skip=e},null,8,["modelValue","onUpdate:modelValue"])])]))),128))])])])}]]),x={class:"fcrm_import_tag_mapper"},I={class:"fcrm_import_tag_mapper_hidden_selector"},L={key:0,class:"fcrm_import_tag_mapper_table"},A={class:"fcrm_import_tag_mapper_auto_th"},T={class:"fcrm_import_tag_mapper_auto_header"},E={class:"fcrm_import_tag_mapper_auto_label"},S={class:"fcrm_import_tag_mapper_auto_all"},j={class:"fcrm_import_tag_mapper_select_all"},O={class:"fcrm_import_tag_mapper_remote_label"},R={key:0,class:"fcrm_import_tag_mapper_auto_created"},P={key:2},B=["innerHTML"];const Z=u({name:"TagMapper",components:{OptionSelector:b},props:["tag_options","driver","current_driver","item_label","option_key"],data:()=>({app_ready:!1,simulated_tag_id:"",element_ready:!1,autoCreateAll:"no"}),watch:{autoCreateAll(e){"yes"==e?this.tag_options.forEach(e=>{e.will_create="yes"}):this.tag_options.forEach(e=>{e.will_create="no"})}},methods:{initOptions(){this.element_ready=!0},ucFirst:e=>e?e.charAt(0).toUpperCase()+e.slice(1):""},mounted(){this.app_ready=!0}},[["render",function(e,o,c,_,u,h){const f=t("option-selector"),v=g;return r(),i("div",x,[l("span",I,[n(f,{onElement_ready:o[0]||(o[0]=e=>h.initOptions()),modelValue:u.simulated_tag_id,"onUpdate:modelValue":o[1]||(o[1]=e=>u.simulated_tag_id=e),field:{is_multiple:!1,creatable:!0,option_key:"tags"}},null,8,["modelValue"])]),u.app_ready?(r(),i("table",L,[l("thead",null,[l("tr",null,[l("th",null,a(h.ucFirst(c.driver))+" "+a(c.item_label),1),l("th",null,"FluentCRM "+a(c.item_label),1),l("th",A,[l("div",T,[l("span",E,a(e.$t("Auto Create"))+" "+a(c.item_label)+"? ",1),l("div",S,[n(v,{"active-value":"yes","inactive-value":"no",modelValue:u.autoCreateAll,"onUpdate:modelValue":o[2]||(o[2]=e=>u.autoCreateAll=e)},null,8,["modelValue"]),l("span",j,a(e.$t("Select All")),1)])])])])]),l("tbody",null,[(r(!0),i(m,null,p(c.tag_options,t=>(r(),i("tr",{key:t.remote_id},[l("td",null,[l("span",O,a(t.remote_name),1)]),l("td",null,["yes"==t.will_create?(r(),i("span",R,a(c.item_label)+" "+a(e.$t("will be created automatically in FluentCRM")),1)):u.element_ready?(r(),d(f,{key:1,modelValue:t.fluentcrm_id,"onUpdate:modelValue":e=>t.fluentcrm_id=e,field:{is_multiple:!1,creatable:!0,option_key:c.option_key}},null,8,["modelValue","onUpdate:modelValue","field"])):(r(),i("span",P,a(e.$t("Loading...")),1))]),l("td",null,[n(v,{"active-value":"yes","inactive-value":"no",modelValue:t.will_create,"onUpdate:modelValue":e=>t.will_create=e},null,8,["modelValue","onUpdate:modelValue"])])]))),128))])])):s("",!0),l("p",{innerHTML:c.current_driver[c.option_key+"_map_info"]},null,8,B)])}]]),D={class:"fc_step_header"},z={key:0,class:"text-align-center fcrm_import_runner"},J={key:1,class:"text-align-center fcrm_import_runner"},K=["innerHTML"],Q={key:1,class:"text-align-left"},W={key:2,class:"text-align-center fcrm_import_runner"},X=["innerHTML"];const Y=u({name:"ImportRunner",props:["driver","credential","map_settings","segment_options"],emits:["hide","import-done","importing-change","prev"],data:()=>({loading:!1,import_summary:{},importing:!1,import_completed:!1,import_info:{completed:0,total:0,import_tracker:{}},errors:!1}),methods:{fetchImportSummary(){this.loading=!0,this.$post("migrators/summary",{driver:this.driver,credential:this.credential,map_settings:this.map_settings,...this.segment_options}).then(e=>{this.import_summary=e.import_summary}).catch(e=>{this.handleError(e),this.$emit("prev")}).finally(()=>{this.loading=!1})},startImport(){this.importing=!0,this.$emit("importing-change",!0),this.$post("migrators/import",{driver:this.driver,credential:this.credential,map_settings:this.map_settings,completed:this.import_info.completed,import_tracker:this.import_info.import_tracker,...this.segment_options}).then(e=>{this.import_info=e.import_info,e.import_info.has_more?this.$nextTick(()=>{this.startImport()}):(this.import_completed=!0,this.$emit("importing-change",!1),this.$emit("import-done"))}).catch(e=>{this.importing=!1,this.$emit("importing-change",!1),this.handleError(e),this.errors=e}).finally(()=>{})},viewSubscriber(){this.$router.push({name:"subscribers"}),this.$emit("hide")}},mounted(){this.fetchImportSummary()}},[["render",function(e,t,n,m,p,c){const u=y,h=v;return _((r(),i("div",null,[l("div",D,[l("h3",null,a(e.$t("Review & Import")),1),t[0]||(t[0]=l("p",null,null,-1))]),p.import_completed?(r(),i("div",z,[l("h3",null,a(e.$t("All contacts from"))+" "+a(n.driver)+" "+a(e.$t("has been completed.")),1)])):p.importing?(r(),i("div",J,[l("h3",null,a(e.$t("Importing now...")),1),l("h4",null,a(e.$t("Use_Please_dnctm")),1),l("template",null,[p.import_info.total&&!p.import_info.hide_progress?(r(),d(u,{key:0,"text-inside":!0,"stroke-width":24,percentage:parseInt(p.import_info.completed/p.import_info.total*100),status:"success"},null,8,["percentage"])):s("",!0),_((r(),i("p",null,[o(a(e.$t("Migrating")),1)])),[[h,p.importing]]),l("p",{innerHTML:p.import_info.message},null,8,K),p.errors?(r(),i("div",Q,[l("h4",null,a(e.$t("Importing_Error_message")),1),l("pre",null,a(p.errors),1)])):s("",!0)])])):(r(),i("div",W,[l("h2",{innerHTML:p.import_summary.message},null,8,X)]))])),[[h,p.loading]])}]]);export{C,Y as I,Z as T,U as a}; diff --git a/wp-content/plugins/fluent-crm/assets/_IndividualProgress.js b/wp-content/plugins/fluent-crm/assets/_IndividualProgress.js new file mode 100644 index 0000000..dc61ca1 --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/_IndividualProgress.js @@ -0,0 +1 @@ +import{k as e,aO as t,P as s,bi as i,aJ as r,aQ as a,bj as c}from"./vendor-element-plus.js?ver=3.1.8";import{aQ as n,W as o,Y as l,a5 as _,Z as p,aa as d,ab as u,a9 as f,X as m,a8 as b,a0 as h,_ as y,c6 as v,J as g,az as k}from"./vendor.js?ver=3.1.8";import{B as $}from"./Badge.js?ver=3.1.8";import{_ as I,I as C}from"./fc-bits-ui.js?ver=3.1.8";const z={class:"fcrm_profile_card_popover_body"},j={class:"fcrm_profile_card_popover_photo"},w=["src","alt"],B={class:"fcrm_profile_card_popover_profile_info"},D={class:"fcrm_profile_card_popover_profile_title"},P={class:"fcrm_profile_action"},T={class:"fcrm_profile_card_popover_profile_email"},W={key:0,class:"user_profile_link"},M=["href"],q={class:"fcrm_profile_card_popover_profile_added"},A={key:0,class:"fcrm_contact_cell"},F=["title","src"],H={key:1,class:"fcrm_contact_cell"},S=["title","src"],x={class:"fcrm_contact_info"},J={class:"fcrm_contact_name"},O={class:"fcrm_contact_email"},Q={key:2,class:"fcrm_contact_cell"},E={class:"fcrm_contact_info"},L={class:"fcrm_contact_name"};const R=I({name:"ContactCardPop",components:{Icons:C,Badge:$},props:{subscriber:{type:Object,default:()=>({})},display_key:{type:String,default:()=>"email"},placement:{type:String,default:()=>"top-start"},trigger_type:{type:String,default:()=>"click"}}},[["render",function(s,i,r,a,c,v){const g=n("Badge"),k=n("Icons"),$=e,I=t;return o(),l(I,{placement:r.placement,width:"350",class:"fc_dark",trigger:r.trigger_type,"popper-class":"fcrm_profile_card_popover"},{reference:_(()=>[p("div",{class:h("fc_trigger_"+r.trigger_type)},["photo"==r.display_key?(o(),m("div",A,[p("img",{title:s.$t("Contact ID:")+" "+r.subscriber.id,class:"fc_contact_photo fcrm_contact_photo",src:r.subscriber.photo},null,8,F)])):"full"==r.display_key?(o(),m("div",H,[p("img",{title:s.$t("Contact ID:")+" "+r.subscriber.id,class:"fc_contact_photo fcrm_contact_photo",src:r.subscriber.photo},null,8,S),p("div",x,[p("div",J,[f(d(r.subscriber.full_name)+" ",1),y(s.$slots,"after_name")]),p("div",O,d(r.subscriber.email),1)])])):(o(),m("div",Q,[p("div",E,[p("div",L,[f(d(r.subscriber[r.display_key])+" ",1),y(s.$slots,"after_name")])])]))],2)]),default:_(()=>[p("div",z,[p("div",j,[p("img",{src:r.subscriber.photo,alt:r.subscriber.full_name},null,8,w)]),p("div",B,[p("div",D,[p("h3",null,d(r.subscriber.full_name),1),p("div",P,[u(g,{type:r.subscriber.status},null,8,["type"])])]),p("div",T,[p("p",null,[f(d(r.subscriber.email)+" ",1),r.subscriber.user_id&&r.subscriber.user_edit_url?(o(),m("span",W,[i[1]||(i[1]=f(" | ",-1)),p("a",{target:"_blank",rel:"noopener noreferrer",href:r.subscriber.user_edit_url},[f(d(r.subscriber.user_id)+" ",1),u(k,{"icon-name":"externalLink"})],8,M)])):b("",!0)])]),p("div",q,d(s.$t("Added"))+" "+d(s.nsHumanDiffTime(r.subscriber.created_at)),1),u($,{size:"small",onClick:i[0]||(i[0]=e=>s.$router.push({name:"subscriber",params:{id:r.subscriber.id}}))},{default:_(()=>[f(d(s.$t("View Full Profile")),1)],void 0,!0),_:1})])])],void 0),_:3},8,["placement","trigger"])}]]),V={class:"fcrm_individual_progress"};const X=I({name:"individualProgress",props:["sequences","funnel_subscriber","funnel"],data:()=>({MoreFilledIcon:s}),computed:{keyedMetrics(){return v(this.funnel_subscriber&&this.funnel_subscriber.metrics||[],"sequence_id")},timelines(){const e=this.funnel&&this.funnel.title?this.funnel.title:this.$t("Automation Removed");let t=this.$t("Entrance (%s)",e);"pending"===this.funnel_subscriber.status?t+=this.$t("_In_Wfdoc"):"waiting"===this.funnel_subscriber.status&&(t+=this.$t("_In_Wfna"));const s=[{content:t,timestamp:this.nsHumanDiffTime(this.funnel_subscriber.created_at),size:"large",type:"primary",icon:this.MoreFilledIcon}];let i=!1;return this.each(this.sequences||[],(e,t)=>{const r=this.keyedMetrics[e.id]||{};let a=e.title;"pending"===a?a+=this.$t("_In_Wfdc"):"waiting"===a&&(a+=this.$t("_In_Wfna")),"conditional"==e.type&&(i=e);let c="";if(e.condition_type){c="fc_path_"+e.condition_type,i&&(c="fc_"+i.action_name+" "+c);let t=this.$t("Condition:")+" "+e.condition_type;if(i&&"funnel_ab_testing"==i.action_name){const s="yes"==e.condition_type?this.$t("B"):this.$t("A");t=this.$t("Path: ")+s,c+="_"+s}a+=" ( "+t+" )"}r.status||(c+=" fcrm_timeline_empty"),s.push({content:a,status:r.status,notes:r.notes,timestamp:this.nsHumanDiffTime(r.created_at),color:this.getTimelineColor(r),wrapper_class:c})}),s}},methods:{getTimelineColor:e=>e.status&&"completed"===e.status?"#0bbd87":""}},[["render",function(e,t,s,n,y,v){const $=a,I=r,C=i,z=c;return o(),m("div",V,[u(z,null,{default:_(()=>[(o(!0),m(g,null,k(v.timelines,(e,t)=>(o(),l(C,{key:t,class:h(e.wrapper_class),icon:e.icon,type:e.type,color:e.color,size:e.size,timestamp:e.timestamp},{default:_(()=>[f(d(e.content)+" ",1),p("template",null,[e.notes?(o(),l(I,{key:0,class:"item",effect:"dark",content:e.notes,placement:"top-start"},{default:_(()=>[u($,{size:"small",type:"info"},{default:_(()=>[f(d(e.status),1)],void 0,!0),_:2},1024)],void 0,!0),_:2},1032,["content"])):e.status?(o(),l($,{key:1,size:"small",type:"info"},{default:_(()=>[f(d(e.status),1)],void 0,!0),_:2},1024)):b("",!0)])],void 0,!0),_:2},1032,["class","icon","type","color","size","timestamp"]))),128))],void 0),_:1})])}]]);export{R as C,X as I}; diff --git a/wp-content/plugins/fluent-crm/assets/_InlineCheckbox.js b/wp-content/plugins/fluent-crm/assets/_InlineCheckbox.js new file mode 100644 index 0000000..26b2a76 --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/_InlineCheckbox.js @@ -0,0 +1 @@ +import{az as e}from"./vendor-element-plus.js?ver=3.1.8";import{W as a,X as l,ab as s,a5 as t,Z as d,aa as o,a8 as i}from"./vendor.js?ver=3.1.8";import{_ as c}from"./fc-bits-ui.js?ver=3.1.8";const r={class:"fcrm_checkbox_row"},m={class:"fcrm_checkbox_text"},n={class:"fcrm_checkbox_text_title"},u={key:0,class:"fcrm_checkbox_text_desc"};const f=c({name:"InlineCheckbox",props:["field","modelValue"],emits:["update:modelValue"],data(){return{model:this.modelValue,isInternalUpdate:!1}},watch:{model(e){this.isInternalUpdate||this.$emit("update:modelValue",e)},modelValue(e){this.model!==e&&(this.isInternalUpdate=!0,this.model=e,this.$nextTick(()=>{this.isInternalUpdate=!1}))}}},[["render",function(c,f,b,h,p,_){const x=e;return a(),l("label",r,[s(x,{class:"fcrm_checkbox","true-value":b.field.true_label,disabled:b.field.disabled,"false-value":b.field.false_label,modelValue:p.model,"onUpdate:modelValue":f[0]||(f[0]=e=>p.model=e)},{default:t(()=>[d("div",m,[d("span",n,o(c.$t(b.field.checkbox_label)),1),b.field.checkbox_description?(a(),l("span",u,o(c.$t(b.field.checkbox_description)),1)):i("",!0)])],void 0),_:1},8,["true-value","disabled","false-value","modelValue"])])}]]);export{f as I}; diff --git a/wp-content/plugins/fluent-crm/assets/_IntlTelInput.js b/wp-content/plugins/fluent-crm/assets/_IntlTelInput.js new file mode 100644 index 0000000..e11b2d4 --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/_IntlTelInput.js @@ -0,0 +1 @@ +import{_ as t}from"./fc-bits.js?ver=3.1.8";import{c1 as e,W as i,X as r,Z as l}from"./vendor.js?ver=3.1.8";import{_ as s}from"./fc-bits-ui.js?ver=3.1.8";const n={class:"fcrm_intl_tel_input"},a=["placeholder"];const o=s({name:"IntlTelInput",props:{modelValue:{type:String,default:""},placeholder:{type:String,default:""}},emits:["update:modelValue","validate","blur"],data:()=>({}),created(){this.iti=null},watch:{modelValue:{handler(t){if(!this.iti)return;const e=(t||"").trim();if(!e)return;let i="";try{i=this.iti.getNumber()||""}catch(r){}e!==i&&this.iti.setNumber(e)},immediate:!1}},mounted(){const i=this.$refs.inputRef;if(!i)return;this.iti=e(i,{initialCountry:"",preferredCountries:[],separateDialCode:!0,countrySearch:!0,formatAsYouType:!0,strictMode:!0,loadUtils:()=>t(()=>import("./vendor.js?ver=3.1.8").then(t=>t.c9),[],import.meta.url).catch(()=>null),customPlaceholder:()=>this.placeholder||this.$t("Enter phone number")}),this.modelValue&&this.iti.setNumber(this.modelValue);const r=()=>{let t="",e=!0;try{t=this.iti.getNumber()||"",e="function"==typeof this.iti.isValidNumber?this.iti.isValidNumber():!t||t.length>=10}catch(i){e=!t||t.length>=10}this.$emit("update:modelValue",t),this.$emit("validate",{number:t,valid:e})};i.addEventListener("input",r),i.addEventListener("blur",()=>{r(),this.$emit("blur")}),i.addEventListener("countrychange",r),this._emitNumber=r},beforeUnmount(){this.iti&&(this.iti.destroy(),this.iti=null)}},[["render",function(t,e,s,o,u,d){return i(),r("div",n,[l("input",{ref:"inputRef",type:"tel",placeholder:s.placeholder,autocomplete:"off",class:"fcrm_phone_tel_input"},null,8,a)])}]]);export{o as I}; diff --git a/wp-content/plugins/fluent-crm/assets/_LinkMetrics.js b/wp-content/plugins/fluent-crm/assets/_LinkMetrics.js new file mode 100644 index 0000000..c366729 --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/_LinkMetrics.js @@ -0,0 +1 @@ +import{_ as e,T as a,ay as i,k as t,b9 as s,aE as n,e as l,aI as r,aJ as o,E as c,aH as d,H as p,ba as m}from"./vendor-element-plus.js?ver=3.1.8";import{aQ as h,W as _,X as u,Z as g,aa as f,a6 as b,Y as v,a5 as y,a9 as k,a8 as $,ab as w,a0 as C,b2 as S,ac as E,J as P}from"./vendor.js?ver=3.1.8";import{P as x}from"./PaginationBar.js?ver=3.1.8";import{C as F}from"./Confirm.js?ver=3.1.8";import R from"./admin/Modules/Email/Campaigns/_components/EmailPreview.js?ver=3.1.8";import{G as B}from"./GenericPromo.js?ver=3.1.8";import{B as L}from"./Badge.js?ver=3.1.8";import{_ as j,I as z}from"./fc-bits-ui.js?ver=3.1.8";import{S as I}from"./SettingsIcons.js?ver=3.1.8";const V={class:"fluentcrm_campaign_emails fcrm_campaign_emails_wrapper"},U={class:"fcrm_campaign_emails_wrapper--title"},A={key:0,class:"fc_highlight_gray fc_m_30 text-align-center"},T={class:"fcrm_table_wrapper"},M={class:"fcrm_table_header"},D={class:"fcrm_table_header_inner"},G={class:"fcrm_table_header_inner_left"},H={class:"fcrm_table_header_inner_actions"},O={class:"icon"},J={class:"icon"},K={class:"icon"},N={key:0,class:"fcrm_table_header_bulk_actions"},Q={class:"icon"},Y={class:"icon"},W={class:"fcrm_table_body"},X=["onClick"],Z=["title","src"],q={class:"fcrm_contact_info"},ee={class:"fcrm_contact_name"},ae={class:"fcrm_contact_email"},ie={class:"subscriber-stats"},te={class:"fcrm_badge"},se={class:"fcrm_badge",style:{"min-height":"20px"}},ne={class:"fcrm_table_body_actions"},le={class:"icon"},re={class:"icon"};const oe=j({name:"CampaignEmails",components:{Icons:z,Badge:L,PaginationBar:x,EmailPreview:R,GenericPromo:B,Confirm:F,Location:a,FolderOpened:e},props:["campaign_id","manage_mode"],emits:["fetchCampaign","updateCount"],computed:{clickStatus(){var e;return null==(e=this.campaign)?void 0:e.click_tracking_status},openStatus(){var e;return null==(e=this.campaign)?void 0:e.open_tracking_status}},data:()=>({loading:!1,emails:[],pagination:{total:0,per_page:20,current_page:1},preview:{id:null,isVisible:!1},filter_type:"all",selections:[],search:"",deleting:!1,failed_counts:0,retrying:!1,resending:!1,campaign:null,showSearchBar:!1}),methods:{fetch(){this.loading=!0;const e={viewCampaign:null,per_page:this.pagination.per_page,page:this.pagination.current_page,filter_type:this.filter_type,search:this.search};this.campaign||(e.with_campaign=1),this.$get(`campaigns/${this.campaign_id}/emails`,e).then(e=>{this.emails=e.emails.data,this.pagination.total=e.emails.total,this.failed_counts=parseInt(e.failed_counts),e.campaign&&(this.campaign=e.campaign)}).catch(e=>{this.handleError(e)}).finally(e=>{this.loading=!1})},changeFilter(){this.pagination.current_page=1,this.fetch()},previewEmail(e){this.preview.id=e,this.preview.isVisible=!0},deleteSelected(){this.deleting=!0;const e=this.selections.map(e=>e.id);this.$del(`campaigns/${this.campaign_id}/emails`,{email_ids:e}).then(e=>{this.selections=[],this.$notify.success(e.message),this.$emit("updateCount",e.recipients_count),this.fetch()}).catch(e=>{this.handleError(e)}).finally(()=>{this.deleting=!1})},handleSelectionChange(e){this.selections=e},retrySending(){this.retrying=!0,this.$post(`campaigns-pro/${this.campaign_id}/resend-failed-emails`).then(e=>{this.$notify.success(e.message),this.fetch(),this.$emit("fetchCampaign")}).catch(e=>{this.handleError(e)}).finally(()=>{this.retrying=!1})},resendEmail(e){if(!this.has_campaign_pro)return this.$notify.error(this.$t("_Ca_Please_utptutf")),!1;Array.isArray(e)||(e=[e]),this.resending=!0,this.$post(`campaigns-pro/${this.campaign_id}/resend-emails`,{email_ids:e}).then(e=>{this.$notify.success(e.message),this.fetch()}).catch(e=>{this.handleError(e)}).finally(()=>{this.resending=!1})},resendUnopenedEmails(){if(!this.has_campaign_pro)return this.$notify.error(this.$t("_Ca_Please_utptutf")),!1;this.resending=!0,this.$post(`campaigns-pro/${this.campaign_id}/resend-unopened-emails`).then(e=>{this.$notify.success(e.message),this.fetch()}).catch(e=>{this.handleError(e)}).finally(()=>{this.resending=!1})},exportCampaignEmails(){this.has_campaign_pro?location.href=window.ajaxurl+"?"+jQuery.param({action:"fluentcrm_export_archived_campaign_emails",campaign_id:this.campaign_id,filter_type:this.filter_type}):this.$notify.error(this.$t("Exporting_archived_campaign_Emails_alert"))},canResend(e){const a=e&&("sent"===e.status||"failed"===e.status),i=this.hasPermission&&this.hasPermission("fcrm_manage_emails"),t=e&&e.subscriber&&"subscribed"===e.subscriber.status;return a&&i&&t},onSearchBarAppendClick(){this.showSearchBar?this.searchFromFirstPage():(this.showSearchBar=!0,this.$nextTick(()=>{var e;return null==(e=this.$refs.notesSearchInput)?void 0:e.focus()}))},searchFromFirstPage(){this.pagination&&(this.pagination.current_page=1),this.fetch()}},mounted(){this.fetch()}},[["render",function(e,a,p,m,P,x){const F=h("generic-promo"),R=t,B=s,L=n,j=h("Icons"),z=l,I=h("confirm"),oe=r,ce=h("Location"),de=c,pe=o,me=h("FolderOpened"),he=h("Badge"),_e=d,ue=h("pagination-bar"),ge=h("email-preview"),fe=i;return _(),u("div",V,[g("div",U,f(e.$t("Recipients")),1),P.failed_counts?b((_(),u("div",A,[g("h3",null,f(P.failed_counts)+" "+f(e.$t("_Ca_failed_tstrTR")),1),e.has_campaign_pro?(_(),v(R,{key:1,onClick:a[0]||(a[0]=e=>x.retrySending()),type:"primary",size:"small"},{default:y(()=>[k(f(e.$t("Retry Sending")),1)],void 0),_:1})):(_(),v(F,{key:0}))])),[[fe,P.retrying]]):$("",!0),g("div",T,[g("div",M,[g("div",D,[g("div",G,[w(L,{onChange:a[1]||(a[1]=e=>x.changeFilter()),modelValue:P.filter_type,"onUpdate:modelValue":a[2]||(a[2]=e=>P.filter_type=e),size:"small"},{default:y(()=>[w(B,{value:"all"},{default:y(()=>[k(f(e.$t("All")),1)],void 0,!0),_:1}),!0===x.clickStatus?(_(),v(B,{key:0,value:"click"},{default:y(()=>[k(f(e.$t("Click")),1)],void 0,!0),_:1})):$("",!0),w(B,{value:"view",disabled:!0!==x.openStatus},{default:y(()=>[k(f(e.$t("View")),1)],void 0,!0),_:1},8,["disabled"]),w(B,{disabled:!0!==x.openStatus,value:"unopened",class:"non-open"},{default:y(()=>[k(f(e.$t("Unopened")),1)],void 0,!0),_:1},8,["disabled"]),P.failed_counts?(_(),v(B,{key:1,value:"failed"},{default:y(()=>[k(f(e.$t("Failed")),1)],void 0,!0),_:1})):$("",!0)],void 0),_:1},8,["modelValue"])]),g("div",H,[g("div",{class:C(["fcrm_notes_search_bar",{"fcrm_notes_search_bar-is_expanded":P.showSearchBar}])},[w(z,{ref:"notesSearchInput",onKeyup:S(x.searchFromFirstPage,["enter"]),clearable:"",size:"small",onClear:a[3]||(a[3]=e=>x.searchFromFirstPage()),placeholder:e.$t("Search"),modelValue:P.search,"onUpdate:modelValue":a[4]||(a[4]=e=>P.search=e),class:C(["fcrm_notes_search_input",{"fcrm_notes_search_input-is_expanded":P.showSearchBar}])},{append:y(()=>[w(R,{class:"small only-icon-btn",onClick:x.onSearchBarAppendClick},{default:y(()=>[g("span",O,[w(j,{"icon-name":"search"})])],void 0,!0),_:1},8,["onClick"])]),_:1},8,["onKeyup","placeholder","modelValue","class"])],2),w(R,{onClick:x.fetch,size:"small",class:"only-icon-btn small"},{default:y(()=>[g("span",J,[w(j,{"icon-name":"reload"})])],void 0),_:1},8,["onClick"]),e.hasPermission("fcrm_manage_contacts_export")?(_(),v(R,{key:0,size:"small",onClick:a[5]||(a[5]=e=>x.exportCampaignEmails())},{default:y(()=>[g("span",K,[w(j,{"icon-name":"export"})]),k(" "+f(e.$t("Export")),1)],void 0),_:1})):$("",!0)])]),P.selections.length||"unopened"===P.filter_type?(_(),u("div",N,[P.selections.length?b((_(),v(R,{key:0,onClick:a[6]||(a[6]=e=>x.deleteSelected()),type:"danger",size:"small"},{default:y(()=>[g("span",Q,[w(j,{"icon-name":"delete"})]),k(" "+f(e.$t("Delete Selected"))+" ("+f(P.selections.length)+") ",1)],void 0),_:1})),[[fe,P.deleting]]):$("",!0),"unopened"===P.filter_type?(_(),v(I,{key:1,onYes:a[7]||(a[7]=e=>x.resendUnopenedEmails()),placement:"top-start",message:e.$t("Are you sure to Resend Unopened Emails?")},{reference:y(()=>[w(R,{size:"small",type:"primary"},{default:y(()=>[g("span",Y,[w(j,{"icon-name":"paperPlane"})]),k(" "+f(e.$t("Resend Unopened Emails")),1)],void 0,!0),_:1})]),_:1},8,["message"])):$("",!0)])):$("",!0)]),g("div",W,[b((_(),v(_e,{stripe:"",border:"",data:P.emails,onSelectionChange:x.handleSelectionChange},{default:y(()=>[p.manage_mode?(_(),v(oe,{key:0,type:"selection",width:"55"})):$("",!0),w(oe,{label:e.$t("Contact"),width:"250px"},{default:y(a=>[g("div",{class:"fcrm_contact_cell",onClick:i=>e.$router.push({name:"subscriber",params:{id:a.row.subscriber.id}})},[g("img",{title:e.$t("Contact ID:")+" "+a.row.subscriber.id,class:"fcrm_contact_photo",src:a.row.subscriber.photo},null,8,Z),g("div",q,[g("div",ee,f(a.row.subscriber.full_name),1),g("div",ae,f(a.row.subscriber.email),1)])],8,X)]),_:1},8,["label"]),w(oe,{width:"190",label:e.$t("Actions")},{default:y(a=>[g("div",ie,[w(pe,{content:e.$t("Total Clicks"),placement:"top"},{default:y(()=>[g("span",te,[w(de,null,{default:y(()=>[w(ce)],void 0,!0),_:1}),k(" "+f(a.row.click_counter||0),1)])],void 0,!0),_:2},1032,["content"]),w(pe,{content:e.$t("Email opened"),placement:"top"},{default:y(()=>[b(g("span",se,[w(de,null,{default:y(()=>[w(me)],void 0,!0),_:1})],512),[[E,a.row.click_counter||1==a.row.is_open]])],void 0,!0),_:2},1032,["content"]),"subscribed"!=a.row.subscriber.status?(_(),v(he,{key:0,type:a.row.subscriber.status},null,8,["type"])):$("",!0)])]),_:1},8,["label"]),w(oe,{prop:"scheduled_at",label:e.$t("Date")},null,8,["label"]),w(oe,{label:e.$t("Status"),align:"center"},{default:y(e=>[w(he,{type:e.row.status},null,8,["type"])]),_:1},8,["label"]),w(oe,{label:e.$t("Preview"),width:"230",align:"right"},{default:y(a=>[g("div",ne,[w(R,{size:"small",onClick:e=>x.previewEmail(a.row.id)},{default:y(()=>[g("span",le,[w(j,{"icon-name":"eye"})]),k(" "+f(e.$t("Preview")),1)],void 0,!0),_:1},8,["onClick"]),"sent"!=a.row.status&&"failed"!=a.row.status||!e.hasPermission("fcrm_manage_emails")||"subscribed"!=a.row.subscriber.status?$("",!0):(_(),v(R,{key:0,size:"small",onClick:e=>x.resendEmail(a.row.id)},{default:y(()=>[g("span",re,[w(j,{"icon-name":"reload"})]),k(" "+f(e.$t("Resend")),1)],void 0,!0),_:1},8,["onClick"]))])]),_:1},8,["label"])],void 0),_:1},8,["data","onSelectionChange"])),[[fe,P.loading||P.resending]]),w(ue,{pagination:P.pagination,onFetch:x.fetch},null,8,["pagination","onFetch"])])]),w(ge,{preview:P.preview},null,8,["preview"])])}]]),ce={class:"fluentcrm_link_metrics"},de={key:0,class:"fluentcrm_inner_header"},pe={class:"fluentcrm_inner_title"},me={class:"fluentcrm_inner_actions"},he={key:0},_e=["title"],ue=["title","href"],ge=["href"],fe={key:1},be={key:0},ve={key:1},ye={key:2,style:{padding:"20px 20px 40px"},class:"text-align-center"},ke=["innerHTML"],$e=["href"];const we=j({name:"CampaignLinkMetrics",components:{Refresh:p,SettingsIcons:I},props:["campaign_id","hide_title"],data:()=>({loading:!1,links:[],click_status:null}),methods:{fetchReport(){this.loading=!0,this.$get(`campaigns/${this.campaign_id}/link-report`).then(e=>{this.links=e.links,this.click_status=e.click_status}).catch(e=>{this.handleError(e)}).finally(e=>{this.loading=!1})}},mounted(){this.has_campaign_pro&&this.fetchReport()}},[["render",function(e,a,s,n,l,o){const p=h("Refresh"),C=c,S=t,E=h("SettingsIcons"),x=r,F=d,R=m,B=i;return b((_(),u("div",ce,[s.hide_title?$("",!0):(_(),u("div",de,[g("h3",pe,f(e.$t("Campaign Link Clicks")),1),g("div",me,[w(S,{onClick:o.fetchReport,size:"small"},{default:y(()=>[w(C,null,{default:y(()=>[w(p)],void 0,!0),_:1})],void 0),_:1},8,["onClick"])])])),e.has_campaign_pro?(_(),u(P,{key:1},[l.links.length?b((_(),v(F,{key:0,height:"270","empty-text":e.$t("No Data Found"),data:l.links,style:{width:"100%"}},{default:y(()=>[w(x,{label:e.$t("URL")},{default:y(i=>[i.row.destination?(_(),u("div",he,[g("span",{title:e.$t("Smart Link")},[w(C,{"aria-label":e.$t("Smart Link"),role:"img"},{default:y(()=>[w(E,{icon:"smart_links"})],void 0,!0),_:1},8,["aria-label"]),k(" "+f(i.row.title),1)],8,_e),a[0]||(a[0]=k(": ",-1)),g("a",{title:i.row.destination,href:i.row.url,target:"_blank",rel:"noopener"},f(i.row.url),9,ue)])):(_(),u("a",{key:1,href:i.row.url,target:"_blank",rel:"noopener"},f(i.row.url),9,ge))]),_:1},8,["label"]),w(x,{width:"70",label:e.$t("Clicks"),prop:"total"},null,8,["label"])],void 0),_:1},8,["empty-text","data"])),[[B,l.loading]]):(_(),u("div",fe,[w(R,{"image-size":135},{description:y(()=>[l.click_status?(_(),u("span",ve,f(e.$t("No link activity recorded yet.")),1)):(_(),u("span",be,f(e.$t("Link tracking is disabled in your global settings.")),1))]),_:1})]))],64)):(_(),u("div",ye,[g("p",{innerHTML:e.$t("This feature is not available on your plan. Please upgrade to the PRO plan to unlock all these awesome features including %s",""+e.$t("Link clicks analytics")+"")},null,8,ke),g("a",{href:e.appVars.crm_pro_url,target:"_blank",rel:"noopener",class:"el-button el-button--danger"},f(e.$t("Get FluentCRM Pro")),9,$e)]))])),[[B,l.loading]])}]]);export{oe as C,we as L}; diff --git a/wp-content/plugins/fluent-crm/assets/_MailerConfig.js b/wp-content/plugins/fluent-crm/assets/_MailerConfig.js new file mode 100644 index 0000000..b8f9cb0 --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/_MailerConfig.js @@ -0,0 +1 @@ +import{az as e,aw as l,aA as a,e as t,aD as m}from"./vendor-element-plus.js?ver=3.1.8";import{aQ as o,W as i,X as s,ab as d,a5 as r,a9 as _,aa as n,Z as u,a8 as p}from"./vendor.js?ver=3.1.8";import{V as f}from"./_VerifiedEmailInput.js?ver=3.1.8";import{_ as v}from"./fc-bits-ui.js?ver=3.1.8";const c={key:0,class:"fcrm_highlight_gray fc_t_10"},g={style:{margin:"0",padding:"0","font-size":"10px"}};const V=v({name:"MailerSettings",components:{VerifiedEmailInput:f},props:{mailer_settings:{type:Object,default:()=>({from_name:"",from_email:"",reply_to_name:"",reply_to_email:"",is_custom:"no"})}},mounted(){}},[["render",function(f,v,V,y,b,h){const $=e,E=l,j=t,F=a,N=o("verified-email-input"),U=m;return i(),s("div",null,[d(E,null,{default:r(()=>[d($,{"true-value":"yes","false-value":"no",modelValue:V.mailer_settings.is_custom,"onUpdate:modelValue":v[0]||(v[0]=e=>V.mailer_settings.is_custom=e)},{default:r(()=>[_(n(f.$t("Vie_Set_CFNaE")),1)],void 0,!0),_:1},8,["modelValue"])],void 0),_:1}),"yes"==V.mailer_settings.is_custom?(i(),s("div",c,[d(U,{gutter:20},{default:r(()=>[d(F,{md:12,sm:24},{default:r(()=>[d(E,{label:f.$t("From Name")},{default:r(()=>[d(j,{placeholder:f.$t("From Name"),modelValue:V.mailer_settings.from_name,"onUpdate:modelValue":v[1]||(v[1]=e=>V.mailer_settings.from_name=e)},null,8,["placeholder","modelValue"])],void 0,!0),_:1},8,["label"])],void 0,!0),_:1}),d(F,{md:12,sm:24},{default:r(()=>[d(E,{label:f.$t("From Email")},{default:r(()=>[d(N,{modelValue:V.mailer_settings.from_email,"onUpdate:modelValue":v[2]||(v[2]=e=>V.mailer_settings.from_email=e),field:{placeholder:f.$t("From Email"),"data-type":"email"}},null,8,["modelValue","field"]),u("p",g,n(f.$t("Vie_Please_msteisbyS")),1)],void 0,!0),_:1},8,["label"])],void 0,!0),_:1})],void 0),_:1}),d(U,{gutter:20},{default:r(()=>[d(F,{md:12,sm:24},{default:r(()=>[d(E,{label:f.$t("Reply To Name")},{default:r(()=>[d(j,{placeholder:f.$t("Reply To Name"),modelValue:V.mailer_settings.reply_to_name,"onUpdate:modelValue":v[3]||(v[3]=e=>V.mailer_settings.reply_to_name=e)},null,8,["placeholder","modelValue"])],void 0,!0),_:1},8,["label"])],void 0,!0),_:1}),d(F,{md:12,sm:24},{default:r(()=>[d(E,{label:f.$t("Reply To Email")},{default:r(()=>[d(j,{placeholder:f.$t("Reply To Email"),modelValue:V.mailer_settings.reply_to_email,"onUpdate:modelValue":v[4]||(v[4]=e=>V.mailer_settings.reply_to_email=e)},null,8,["placeholder","modelValue"])],void 0,!0),_:1},8,["label"])],void 0,!0),_:1})],void 0),_:1})])):p("",!0)])}]]);export{V as M}; diff --git a/wp-content/plugins/fluent-crm/assets/_MergeCodes.js b/wp-content/plugins/fluent-crm/assets/_MergeCodes.js new file mode 100644 index 0000000..f79e9f6 --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/_MergeCodes.js @@ -0,0 +1 @@ +import{p as t}from"./input-popover-dropdown.js?ver=3.1.8";import{aQ as o,W as e,Y as s}from"./vendor.js?ver=3.1.8";import{_ as a}from"./fc-bits-ui.js?ver=3.1.8";const d=a({name:"MergeCodes",components:{popover:t},props:["extra_tags","button_text"],data:()=>({editorShortcodes:[]}),methods:{handleCommand(t){this.copyItem(t)},copyItem(t){this.copy_success=!1;let o=!1;if(window.clipboardData&&window.clipboardData.setData)window.clipboardData.clipboardData.setData("Text",t),o=!0;else if(document.queryCommandSupported&&document.queryCommandSupported("copy")){const s=document.createElement("textarea");s.textContent=t,s.style.position="fixed",document.body.appendChild(s),s.select();try{document.execCommand("copy"),o=!0}catch(e){console.warn("Copy to clipboard failed.",e),o=!1}finally{document.body.removeChild(s)}}o?(this.copy_success=!0,this.$notify({message:this.$t("Smartcode has been copied to your clipboard"),position:"bottom-right",customClass:"bottom_right fc_notify_z",type:"success"})):this.$notify({message:this.$t("Your Browser does not support JS copy. Please copy manually"),position:"bottom-right",customClass:"bottom_right",type:"error"})}},mounted(){this.extra_tags&&this.extra_tags.length&&this.editorShortcodes.push(...this.extra_tags),window.fcAdmin.globalSmartCodes&&this.editorShortcodes.push(...window.fcAdmin.globalSmartCodes),window.fcAdmin.extendedSmartCodes&&this.editorShortcodes.push(...window.fcAdmin.extendedSmartCodes)}},[["render",function(t,a,d,r,n,i){const c=o("popover");return e(),s(c,{doc_url:"https://fluentcrm.com/docs/merge-codes-smart-codes-usage/",style:{display:"inline-block"},btnType:"text",buttonText:d.button_text?d.button_text:"{ }",class:"popover-wrapper",data:n.editorShortcodes,onCommand:i.handleCommand},null,8,["buttonText","data","onCommand"])}]]);export{d as M}; diff --git a/wp-content/plugins/fluent-crm/assets/_OptionSelector.js b/wp-content/plugins/fluent-crm/assets/_OptionSelector.js new file mode 100644 index 0000000..aae1967 --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/_OptionSelector.js @@ -0,0 +1 @@ +import{ay as e,aL as t,aK as i,e as s,k as l,aO as a}from"./vendor-element-plus.js?ver=3.1.8";import{W as o,X as d,Z as n,a6 as r,Y as p,a5 as m,J as h,az as c,aa as f,a8 as _,ab as u,a9 as y,a0 as w}from"./vendor.js?ver=3.1.8";import{_ as g}from"./fc-bits-ui.js?ver=3.1.8";const v={class:"w-full"},V=["textContent"],b={class:"fcrm_option_selector d-flex items-center gap-8"},k={key:0,class:"fcrm_field_inline_help"};const $=g({name:"OptionSelector",props:["modelValue","field","placement","effect","popper_class"],emits:["element_ready","renew_options","update:modelValue"],data(){return{options:{},model:this.modelValue&&Array.isArray(this.modelValue)?this.modelValue.map(String):this.modelValue,element_ready:!1,new_item:"",creating:!1,isInternalUpdate:!1}},watch:{model(e){this.isInternalUpdate||this.$emit("update:modelValue",e)},modelValue(e){const t=e&&Array.isArray(e)?e.map(String):e;JSON.stringify(this.model)!==JSON.stringify(t)&&(this.isInternalUpdate=!0,this.model=t,this.$nextTick(()=>{this.isInternalUpdate=!1}))}},methods:{getOptions(){this.app_ready=!1;const e={fields:"editable_statuses,"+this.field.option_key};this.$get("reports/options",e).then(e=>{window.fc_options_cache=e.options,this.options=e.options,this.element_ready=!0,this.$emit("element_ready")}).catch(e=>{this.handleError(e)}).finally(()=>{})},createNewItem(){if(this.creating=!0,!this.new_item.length)return this.$notify.error("Provide name Field is required"),!1;this.$post(this.field.option_key+"/bulk",{items:[{title:this.new_item}]}).then(e=>{this.$notify.success(e.message),this.getOptions(),e.ids&&e.ids[0]&&(this.field.is_multiple?this.model=[...this.model,String(e.ids[0])]:this.model=String(e.ids[0])),this.new_item="","tags"==this.field.option_key?this.$bus.emit("renew_options","tag"):"lists"==this.field.option_key&&this.$bus.emit("renew_options","list")}).catch(e=>{this.handleError(e)}).finally(()=>{this.creating=!1})}},mounted(){this.field.is_multiple&&"object"!=typeof this.modelValue&&(this.model=[]),"tags"==this.field.option_key?(this.options[this.field.option_key]=this.appVars.available_tags,this.element_ready=!0,this.$emit("element_ready")):"lists"==this.field.option_key?(this.options[this.field.option_key]=this.appVars.available_lists,this.element_ready=!0,this.$emit("element_ready")):window.fc_options_cache&&window.fc_options_cache[this.field.option_key]?(this.options=window.fc_options_cache,this.element_ready=!0,this.$emit("element_ready")):this.getOptions()}},[["render",function(g,$,x,O,S,I){const H=t,N=i,U=s,A=l,j=a,z=e;return o(),d("div",v,[n("div",{class:w(["fcrm_options_selector",x.field.creatable?"fcrm_option_creatable":""])},[r((o(),p(N,{disabled:x.field.disabled,size:x.field.size,modelValue:S.model,"onUpdate:modelValue":$[0]||($[0]=e=>S.model=e),class:"fcrm_options","value-key":"id",multiple:x.field.is_multiple,placeholder:x.field.placeholder,clearable:"",filterable:"",effect:x.effect||x.field.effect,"popper-class":x.popper_class||x.field.popper_class||x.field.popperClass,teleported:!1!==x.field.teleported},{default:m(()=>[S.element_ready?(o(!0),d(h,{key:0},c(S.options[x.field.option_key],e=>(o(),p(H,{key:e.id,value:Number.isInteger(e.id)?String(e.id):e.id,label:e.title},{default:m(()=>[n("span",{textContent:f(e.title)},null,8,V)],void 0,!0),_:2},1032,["value","label"]))),128)):_("",!0)],void 0),_:1},8,["disabled","size","modelValue","multiple","placeholder","effect","popper-class","teleported"])),[[z,!S.element_ready]]),x.field.creatable&&!x.field.disabled?(o(),p(j,{key:0,placement:x.placement?x.placement:"left",width:250,trigger:"click"},{reference:m(()=>[u(A,{class:"fcrm_with_select",type:"info"},{default:m(()=>[...$[3]||($[3]=[n("svg",{xmlns:"http://www.w3.org/2000/svg",width:"20",height:"20",viewBox:"0 0 20 20",fill:"none"},[n("path",{d:"M9.25 9.25V4.75H10.75V9.25H15.25V10.75H10.75V15.25H9.25V10.75H4.75V9.25H9.25Z",fill:"var(--fc-secondary-text)"})],-1)])],void 0,!0),_:1})]),default:m(()=>[n("div",b,[u(U,{placeholder:g.$t("Provide Name"),modelValue:S.new_item,"onUpdate:modelValue":$[1]||($[1]=e=>S.new_item=e)},null,8,["placeholder","modelValue"]),u(A,{onClick:$[2]||($[2]=e=>I.createNewItem()),type:"primary"},{default:m(()=>[y(f(g.$t("Add")),1)],void 0,!0),_:1})])],void 0),_:1},8,["placement"])):_("",!0)],2),x.field.inline_help?(o(),d("span",k,f(x.field.inline_help),1)):_("",!0)])}]]);export{$ as O}; diff --git a/wp-content/plugins/fluent-crm/assets/_StepPicker.js b/wp-content/plugins/fluent-crm/assets/_StepPicker.js new file mode 100644 index 0000000..efe80cd --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/_StepPicker.js @@ -0,0 +1 @@ +import{aE as e,aF as s,aQ as t,ay as i,k as n,g as a}from"./vendor-element-plus.js?ver=3.1.8";import{W as d,Y as l,a5 as c,a6 as u,X as r,Z as o,aa as p,ab as h,J as f,az as S,a0 as b,a9 as m,a8 as v}from"./vendor.js?ver=3.1.8";import{_}from"./fc-bits-ui.js?ver=3.1.8";const q={class:"fc_step_picker"},g={class:"fc_step_picker_hint"},y={class:"dialog-footer"};const $=_({name:"StepPicker",props:{funnelSubscriber:{type:Object,required:!0},sequences:{type:Array,default:null},funnelId:{type:[Number,String],required:!0}},emits:["close","advanced"],data:()=>({visible:!0,selectedStepId:null,advancing:!1,loadingSequences:!1,fetchedSequences:[]}),computed:{resolvedSequences(){return this.sequences||this.fetchedSequences},advancableSequences(){return this.resolvedSequences.filter(e=>"conditional"!==e.type)},currentSequence(){return this.resolvedSequences.find(e=>e.id==this.funnelSubscriber.next_sequence_id)}},methods:{isCurrent(e){return e.id==this.funnelSubscriber.next_sequence_id},getDefaultStepId(){return this.funnelSubscriber.next_sequence_id||null},isCompletedStep(e){const s=this.currentSequence;return!!s&&e.sequence{this.fetchedSequences=e.sequences||[],this.selectedStepId=this.getDefaultStepId()}).catch(e=>{this.handleError(e)}).finally(()=>{this.loadingSequences=!1})},executeAdvance(){this.selectedStepId&&(this.advancing=!0,this.$post(`funnels/${this.funnelId}/subscribers/${this.funnelSubscriber.subscriber_id}/advance`,{sequence_id:this.selectedStepId}).then(e=>{this.$notify.success(e.message),this.$emit("advanced")}).catch(e=>{this.handleError(e)}).finally(()=>{this.advancing=!1}))}},mounted(){this.sequences?this.selectedStepId=this.getDefaultStepId():this.fetchSequences()}},[["render",function(_,$,I,k,C,x){const V=t,A=s,j=e,w=n,z=a,D=i;return d(),l(z,{modelValue:C.visible,"onUpdate:modelValue":$[3]||($[3]=e=>C.visible=e),title:_.$t("Advance to Step"),width:"520px","append-to-body":!0,class:"fc_step_picker_dialog",onClose:$[4]||($[4]=e=>_.$emit("close"))},{footer:c(()=>[o("span",y,[h(w,{onClick:$[1]||($[1]=e=>_.$emit("close"))},{default:c(()=>[m(p(_.$t("Cancel")),1)],void 0,!0),_:1}),h(w,{type:"primary",loading:C.advancing,disabled:!C.selectedStepId,onClick:$[2]||($[2]=e=>x.executeAdvance())},{default:c(()=>[m(p(_.$t("Advance")),1)],void 0,!0),_:1},8,["loading","disabled"])])]),default:c(()=>[u((d(),r("div",q,[o("p",g,p(_.$t("Select the step to advance this subscriber to. The selected step will be executed immediately.")),1),h(j,{modelValue:C.selectedStepId,"onUpdate:modelValue":$[0]||($[0]=e=>C.selectedStepId=e),class:"fluentcrm_line_items"},{default:c(()=>[(d(!0),r(f,null,S(x.advancableSequences,e=>(d(),l(A,{key:e.id,value:e.id,disabled:x.isCompletedStep(e),class:b({"is-current":x.isCurrent(e),"is-completed":x.isCompletedStep(e),fc_step_conditional:!!e.condition_type})},{default:c(()=>[m(p(e.title)+" ",1),x.isCurrent(e)?(d(),l(V,{key:0,size:"small",type:"info"},{default:c(()=>[m(p(_.$t("Next")),1)],void 0,!0),_:1})):v("",!0),"benchmark"===e.type?(d(),l(V,{key:1,size:"small",type:"warning"},{default:c(()=>[m(p(_.$t("Goal")),1)],void 0,!0),_:1})):v("",!0)],void 0,!0),_:2},1032,["value","disabled","class"]))),128))],void 0,!0),_:1},8,["modelValue"])])),[[D,C.loadingSequences]])],void 0),_:1},8,["modelValue","title"])}]]);export{$ as S}; diff --git a/wp-content/plugins/fluent-crm/assets/_TaxonomyTermsSelector.js b/wp-content/plugins/fluent-crm/assets/_TaxonomyTermsSelector.js new file mode 100644 index 0000000..e33b5f6 --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/_TaxonomyTermsSelector.js @@ -0,0 +1 @@ +import{ay as e,aL as l,aK as o}from"./vendor-element-plus.js?ver=3.1.8";import{a6 as t,W as a,Y as s,a5 as i,X as d,J as r,az as m}from"./vendor.js?ver=3.1.8";import{_ as n}from"./fc-bits-ui.js?ver=3.1.8";const h=n({name:"TaxonomySelector",props:["field","modelValue"],emits:["update:modelValue"],data(){return{model:this.modelValue,loading:!1,options:[]}},watch:{model(e){this.$emit("update:modelValue",e)}},methods:{fetchOptions(e){this.loading=!0,this.$get("reports/taxonomy-terms",{search:e,values:this.model,taxonomy:this.field.taxonomy}).then(e=>{this.options=e.options}).catch(e=>{this.handleError(e)}).finally(()=>{this.loading=!1})}},mounted(){this.fetchOptions("")}},[["render",function(n,h,p,u,f,c){const v=l,y=o,b=e;return t((a(),s(y,{modelValue:f.model,"onUpdate:modelValue":h[0]||(h[0]=e=>f.model=e),multiple:p.field.is_multiple,filterable:"",remote:"","reserve-keyword":"",disabled:p.field.disabled,size:p.field.size,placeholder:p.field.placeholder||n.$t("Please enter a keyword"),"remote-method":c.fetchOptions},{default:i(()=>[(a(!0),d(r,null,m(f.options,e=>(a(),s(v,{key:e.id,label:e.title,value:e.id},null,8,["label","value"]))),128))],void 0),_:1},8,["modelValue","multiple","disabled","size","placeholder","remote-method"])),[[b,f.loading]])}]]);export{h as T}; diff --git a/wp-content/plugins/fluent-crm/assets/_VerifiedEmailInput.js b/wp-content/plugins/fluent-crm/assets/_VerifiedEmailInput.js new file mode 100644 index 0000000..927eebf --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/_VerifiedEmailInput.js @@ -0,0 +1 @@ +import{aL as e,aK as l,k as a,g as i}from"./vendor-element-plus.js?ver=3.1.8";import{W as d,X as t,Y as o,a5 as s,J as n,az as r,a6 as m,c3 as u,ab as c,Z as p,a9 as f,aa as h,a8 as g}from"./vendor.js?ver=3.1.8";import{_ as V}from"./fc-bits-ui.js?ver=3.1.8";const v={style:{width:"100%"}},b=["type","placeholder"],C={key:2},k={class:"fc-verified-email-input-dialog-footer"};const y=V({name:"VerifiedEmailInput",props:["field","modelValue"],emits:["update:modelValue"],data(){return{selectedMail:this.modelValue,model:this.modelValue,dialogWarningVisible:!1,warningMessage:this.$t("Warning default email change")}},watch:{model(e){this.$emit("update:modelValue",e),null!=e&&e!=this.selectedMail&&(this.dialogWarningVisible=!0),null!=e&&e==this.selectedMail&&(this.dialogWarningVisible=!1)}},methods:{cancelChangeDefaultEmail(){this.model=this.selectedMail,this.dialogWarningVisible=!1},confirmChangeDefaultEmail(){this.dialogWarningVisible=!1,this.selectedMail=this.model}}},[["render",function(V,y,w,W,_,M){const E=e,$=l,D=a,j=i;return d(),t("div",v,[V.appVars.verified_senders.length?(d(),o($,{key:0,placeholder:w.field.placeholder,filterable:"","allow-create":"",modelValue:_.model,"onUpdate:modelValue":y[0]||(y[0]=e=>_.model=e)},{default:s(()=>[(d(!0),t(n,null,r(V.appVars.verified_senders,e=>(d(),o(E,{key:e,value:e},null,8,["value"]))),128))],void 0),_:1},8,["placeholder","modelValue"])):m((d(),t("input",{key:1,type:w.field.data_type,placeholder:w.field.placeholder,"onUpdate:modelValue":y[1]||(y[1]=e=>_.model=e),class:"fc-input-email"},null,8,b)),[[u,_.model]]),w.field.show_warning?(d(),t("div",C,[c(j,{title:V.$t("Confirm"),modelValue:_.dialogWarningVisible,"onUpdate:modelValue":y[2]||(y[2]=e=>_.dialogWarningVisible=e),"close-on-click-modal":!1,"append-to-body":!0,width:"30%"},{footer:s(()=>[p("span",k,[c(D,{onClick:M.cancelChangeDefaultEmail},{default:s(()=>[f(h(V.$t("Cancel")),1)],void 0,!0),_:1},8,["onClick"]),c(D,{type:"warning",onClick:M.confirmChangeDefaultEmail},{default:s(()=>[f(h(V.$t("Continue")),1)],void 0,!0),_:1},8,["onClick"])])]),default:s(()=>[p("span",null,[f(h(_.warningMessage+" "),1),p("strong",null,h(_.model+"."),1)])],void 0),_:1},8,["title","modelValue"])])):g("",!0)])}]]);export{y as V}; diff --git a/wp-content/plugins/fluent-crm/assets/_conditions.js b/wp-content/plugins/fluent-crm/assets/_conditions.js new file mode 100644 index 0000000..5ce7954 --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/_conditions.js @@ -0,0 +1 @@ +import{e,aw as a,aE as l,aF as s,aA as t,aK as i,aL as n,bd as c,aD as o,az as d,k as _}from"./vendor-element-plus.js?ver=3.1.8";import{W as m,X as r,ab as u,a5 as f,a9 as p,aa as g,Y as y,a8 as b,J as h,az as v,Z as $,aQ as k,ax as w}from"./vendor.js?ver=3.1.8";import{_ as V,I as C}from"./fc-bits-ui.js?ver=3.1.8";const S={class:"fcrm_flow_basic fcrm_recurring_campaign_basic_settings"},U={class:"fcrm_basic_server_time fcrm_secondary_text",style:{"font-size":"90%"}};const E=V({name:"CampaignBasicSettings",props:["campaign"],methods:{maybeResetDay(){"weekly"==this.campaign.settings.scheduling_settings.type?this.campaign.settings.scheduling_settings.day="mon":"monthly"==this.campaign.settings.scheduling_settings.type&&(this.campaign.settings.scheduling_settings.day=1)}}},[["render",function(_,k,w,V,C,E){const x=e,D=a,j=s,R=l,T=n,W=i,z=t,M=c,I=o,A=d;return m(),r("div",S,[u(D,{class:"fcrm_basic_form_item fcrm_basic_form_item_title",label:_.$t("Title of the Recurring Campaign")},{default:f(()=>[u(x,{class:"fcrm_basic_input fcrm_basic_input_title",type:"text",placeholder:_.$t("eg: Weekly Post Updated"),modelValue:w.campaign.title,"onUpdate:modelValue":k[0]||(k[0]=e=>w.campaign.title=e)},null,8,["placeholder","modelValue"])],void 0),_:1},8,["label"]),u(D,{class:"fcrm_basic_form_item fcrm_basic_form_item_frequency",label:_.$t("How often you want to send this email?")},{default:f(()=>[u(R,{class:"fcrm_basic_radio_group",onChange:k[1]||(k[1]=e=>E.maybeResetDay()),modelValue:w.campaign.settings.scheduling_settings.type,"onUpdate:modelValue":k[2]||(k[2]=e=>w.campaign.settings.scheduling_settings.type=e)},{default:f(()=>[u(j,{class:"fcrm_basic_radio",value:"daily"},{default:f(()=>[p(g(_.$t("Daily")),1)],void 0,!0),_:1}),u(j,{class:"fcrm_basic_radio",value:"weekly"},{default:f(()=>[p(g(_.$t("Weekly")),1)],void 0,!0),_:1}),u(j,{class:"fcrm_basic_radio",value:"monthly"},{default:f(()=>[p(g(_.$t("Monthly")),1)],void 0,!0),_:1})],void 0,!0),_:1},8,["modelValue"])],void 0),_:1},8,["label"]),u(I,{class:"fcrm_basic_row",gutter:30},{default:f(()=>["weekly"==w.campaign.settings.scheduling_settings.type?(m(),y(z,{key:0,class:"fcrm_basic_col fcrm_basic_col_day_weekly",md:12,xs:24},{default:f(()=>[u(D,{class:"fcrm_basic_form_item fcrm_basic_form_item_day_week",label:_.$t("Select which day you want to send email?")},{default:f(()=>[u(W,{class:"fcrm_basic_select fcrm_basic_select_day_week",placeholder:_.$t("Select Day of the week"),modelValue:w.campaign.settings.scheduling_settings.day,"onUpdate:modelValue":k[3]||(k[3]=e=>w.campaign.settings.scheduling_settings.day=e)},{default:f(()=>[u(T,{value:"mon",label:_.$t("Every Monday")},null,8,["label"]),u(T,{value:"tue",label:_.$t("Every Tuesday")},null,8,["label"]),u(T,{value:"wed",label:_.$t("Every Wednesday")},null,8,["label"]),u(T,{value:"thu",label:_.$t("Every Thursday")},null,8,["label"]),u(T,{value:"fri",label:_.$t("Every Friday")},null,8,["label"]),u(T,{value:"sat",label:_.$t("Every Saturday")},null,8,["label"]),u(T,{value:"sun",label:_.$t("Every Sunday")},null,8,["label"])],void 0,!0),_:1},8,["placeholder","modelValue"])],void 0,!0),_:1},8,["label"])],void 0,!0),_:1})):b("",!0),"monthly"==w.campaign.settings.scheduling_settings.type?(m(),y(z,{key:1,class:"fcrm_basic_col fcrm_basic_col_day_monthly",md:12,xs:24},{default:f(()=>[u(D,{class:"fcrm_basic_form_item fcrm_basic_form_item_day_month",label:_.$t("Select which day of the month to send email")},{default:f(()=>[u(W,{class:"fcrm_basic_select fcrm_basic_select_day_month",placeholder:_.$t("Select Day of the month"),modelValue:w.campaign.settings.scheduling_settings.day,"onUpdate:modelValue":k[4]||(k[4]=e=>w.campaign.settings.scheduling_settings.day=e)},{default:f(()=>[(m(),r(h,null,v(31,e=>u(T,{key:e,value:e,label:_.$t("Day")+" - "+e},null,8,["value","label"])),64))],void 0,!0),_:1},8,["placeholder","modelValue"])],void 0,!0),_:1},8,["label"])],void 0,!0),_:1})):b("",!0),u(z,{class:"fcrm_basic_col fcrm_basic_col_time",md:12,xs:24},{default:f(()=>[u(D,{class:"fcrm_basic_form_item fcrm_basic_form_item_time",label:_.$t("Which time you would like to schedule")},{default:f(()=>[u(M,{class:"fcrm_basic_time_select",modelValue:w.campaign.settings.scheduling_settings.time,"onUpdate:modelValue":k[5]||(k[5]=e=>w.campaign.settings.scheduling_settings.time=e),start:"00:00",end:"23:59",step:"00:15",placeholder:_.$t("Select Schedule time")},null,8,["modelValue","placeholder"]),$("p",U,g(_.$t("Current Date & Time (server):"))+" "+g(_.currentDateTime()),1)],void 0,!0),_:1},8,["label"])],void 0,!0),_:1})],void 0),_:1}),u(D,{class:"fcrm_basic_form_item fcrm_basic_form_item_auto_send"},{default:f(()=>[u(A,{class:"fcrm_inline_check fcrm_basic_checkbox_auto_send","true-value":"yes","false-value":"no",modelValue:w.campaign.settings.scheduling_settings.send_automatically,"onUpdate:modelValue":k[6]||(k[6]=e=>w.campaign.settings.scheduling_settings.send_automatically=e)},{default:f(()=>[p(g(_.$t("Send_Email_Auto_Info")),1)],void 0,!0),_:1},8,["modelValue"])],void 0),_:1})])}]]),x={key:0,class:"fcrm_flow_conditions"},D={class:"fcrm_conditions_row"},j={class:"fcrm_conditions_label"},R={class:"fcrm_conditions_select_wrap"},T={class:"fcrm_conditions_label"},W={class:"fcrm_conditions_input_wrap"},z={class:"fcrm_conditions_label"},M={class:"fcrm_conditions_remove_wrap"},I={class:"icon"},A={key:0,class:"fcrm_or"},B={class:"fcrm_flow_more"},F={class:"icon"},O={key:1,class:"fcrm_conditions_empty"},P={class:"fcrm_conditions_empty_text"};const q=V({name:"CampaignConditions",components:{Icons:C},props:["sending_conditions"],methods:{addMoreCondition(){this.sending_conditions.push([{object_type:"cpt",object_name:"post",object_key:"post_date",comparison_type:"within_days",compare_value:7}])},removeCondition(e,a){this.sending_conditions[e].splice(a,1),this.sending_conditions[e].length||this.sending_conditions.splice(e,1)}}},[["render",function(a,l,s,t,c,o){const d=n,V=i,C=e,S=k("Icons"),U=_;return s.sending_conditions.length?(m(),r("div",x,[(m(!0),r(h,null,v(s.sending_conditions,(e,l)=>(m(),r("div",{key:l,class:"fcrm_flow_condition_block"},[(m(!0),r(h,null,v(e,(e,s)=>(m(),r("div",{key:s,class:"fcrm_flow_condition"},[$("div",D,[$("span",j,g(a.$t("Send emails if")),1),$("span",R,[u(V,{class:"fcrm_conditions_select",modelValue:e.object_name,"onUpdate:modelValue":a=>e.object_name=a},{default:f(()=>[(m(!0),r(h,null,v(a.appVars.publicPostTypes,e=>(m(),y(d,{key:e.id,value:e.id,label:e.title},null,8,["value","label"]))),128))],void 0),_:1},8,["modelValue","onUpdate:modelValue"])]),$("span",T,g(a.$t("published within")),1),$("span",W,[u(C,{class:"fcrm_conditions_input_days",type:"number",placeholder:a.$t("type days"),modelValue:e.compare_value,"onUpdate:modelValue":a=>e.compare_value=a},null,8,["placeholder","modelValue","onUpdate:modelValue"])]),$("span",z,g(a.$t("days")),1)]),$("span",M,[u(U,{class:"small only-icon-btn","aria-label":a.$t("Remove condition"),onClick:e=>o.removeCondition(l,s),size:"small"},{default:f(()=>[$("span",I,[u(S,{"icon-name":"delete"})])],void 0),_:1},8,["aria-label","onClick"])])]))),128)),l+1!=s.sending_conditions.length?(m(),r("p",A,[$("span",null,g(a.$t("OR")),1)])):b("",!0)]))),128)),$("div",B,[l[2]||(l[2]=$("span",{class:"fcrm_flow_more_line","aria-hidden":"true"},null,-1)),u(U,{onClick:l[0]||(l[0]=e=>o.addMoreCondition()),size:"small"},{default:f(()=>[$("span",F,[u(S,{"icon-name":"plus"})]),p(" "+g(a.$t("OR")),1)],void 0),_:1}),l[3]||(l[3]=$("span",{class:"fcrm_flow_more_line","aria-hidden":"true"},null,-1))])])):(m(),r("div",O,[$("p",P,[p(g(a.$t("Emails_Will_Sent_Auto"))+", ",1),$("a",{class:"fcrm_conditions_empty_link",onClick:l[1]||(l[1]=w(e=>o.addMoreCondition(),["prevent"])),href:"#"},g(a.$t("click here")),1)])]))}]]);export{E as B,q as C}; diff --git a/wp-content/plugins/fluent-crm/assets/_eChart.js b/wp-content/plugins/fluent-crm/assets/_eChart.js new file mode 100644 index 0000000..3abf6b9 --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/_eChart.js @@ -0,0 +1 @@ +import{q as e,k as s,c4 as a,v as t,W as r,X as o,Z as n,$ as i,a8 as d,r as l,d as p}from"./vendor.js?ver=3.1.8";import{_ as c}from"./fc-bits-ui.js?ver=3.1.8";const h={class:"echart-container-wrapper"},u={key:0,class:"fcrm-chart-placeholder"};const v=c({name:"FreshChart",props:{options:{type:Object,default:null},height:{type:[Number,String],default:400}},setup(r){const o=l(null);let n=null;const i=p(()=>!!r.options&&(!!Array.isArray(r.options.series)&&r.options.series.some(e=>Array.isArray(e.data)&&e.data.length>0))),d=()=>{n&&n.resize()};return e(()=>{o.value&&(n=a(o.value),n.setOption(r.options||{},!0),window.addEventListener("resize",d))}),s(()=>r.options,e=>{o.value&&(n||(n=a(o.value),window.addEventListener("resize",d)),e&&n.setOption(e,!0))},{deep:!0}),t(()=>{n&&(window.removeEventListener("resize",d),n.dispose())}),{chartRef:o,hasData:i}}},[["render",function(e,s,a,t,l,p){return r(),o("div",h,[n("div",{ref:"chartRef",class:"chart-container",style:i("height:"+a.height+"px;")},null,4),t.hasData?d("",!0):(r(),o("div",u," Chart placeholder (ECharts not loaded or data is empty) "))])}],["__scopeId","data-v-5d01edb6"]]);export{v as E}; diff --git a/wp-content/plugins/fluent-crm/assets/_report_widget.js b/wp-content/plugins/fluent-crm/assets/_report_widget.js new file mode 100644 index 0000000..d0237d8 --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/_report_widget.js @@ -0,0 +1 @@ +import{K as e,_ as s,R as a,am as t,u as n,aQ as i}from"./vendor-element-plus.js?ver=3.1.8";import{aQ as l,W as o,X as c,ab as r,a5 as p,Z as m,a9 as d,aa as f,Y as u,a8 as _}from"./vendor.js?ver=3.1.8";import{_ as v,I as k}from"./fc-bits-ui.js?ver=3.1.8";const y={key:0,class:"fcrm_block_stats"},z={class:"icon"},j={class:"icon"},b={class:"icon"},g=["innerHTML"],w={class:"icon"},I={class:"icon"};const M=v({name:"reportWidget",components:{Icons:k,User:n,Bottom:t,Money:a,FolderOpened:s,Position:e},props:["stat"]},[["render",function(e,s,a,t,n,v){const k=l("Icons"),M=i;return a.stat?(o(),c("div",y,[r(M,{size:"small",effect:"plain"},{default:p(()=>[m("span",z,[r(k,{"icon-name":"user"})]),d(" "+f(a.stat.count),1)],void 0),_:1}),r(M,{size:"small",effect:"plain"},{default:p(()=>[d(f(a.stat.percent)+"% ",1)],void 0),_:1}),a.stat.drop_percent?(o(),u(M,{key:0,size:"small",type:"danger",effect:"plain"},{default:p(()=>[m("span",j,[r(k,{"icon-name":"arrow-long-down"})]),d(" "+f(a.stat.drop_percent)+"% ",1)],void 0),_:1})):_("",!0),a.stat.revenues?(o(),u(M,{key:1,size:"small",effect:"plain"},{default:p(()=>[m("span",b,[r(k,{"icon-name":"wallet"})]),m("span",{innerHTML:a.stat.revenues.join(" | ")},null,8,g)],void 0),_:1})):_("",!0),a.stat.email_opens?(o(),u(M,{key:2,title:e.$t("Email Open (estimated)"),size:"small",effect:"plain"},{default:p(()=>[m("span",w,[r(k,{"icon-name":"envelopeOpen"})]),d(" "+f(a.stat.email_opens),1)],void 0),_:1},8,["title"])):_("",!0),a.stat.link_clicks?(o(),u(M,{key:3,title:e.$t("Clicks Count"),size:"small",effect:"plain"},{default:p(()=>[m("span",I,[r(k,{"icon-name":"click"})]),d(" "+f(a.stat.link_clicks),1)],void 0),_:1},8,["title"])):_("",!0)])):_("",!0)}]]);export{M as R}; diff --git a/wp-content/plugins/fluent-crm/assets/admin/Modules/Dashboard/Dashboard.js b/wp-content/plugins/fluent-crm/assets/admin/Modules/Dashboard/Dashboard.js new file mode 100644 index 0000000..1b8d0a8 --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/admin/Modules/Dashboard/Dashboard.js @@ -0,0 +1 @@ +import{ay as t,at as e,j as a,k as s,E as i,h as n,i as o,aC as r,aB as l,aA as c,aD as d,aj as m,a4 as h,ag as _,y as u,aY as f,aJ as p,g}from"../../../vendor-element-plus.js?ver=3.1.8";import{aQ as b,a6 as y,W as v,X as C,ab as w,Y as x,a5 as k,J as L,az as $,Z as T,aa as S,$ as E,a9 as A,a0 as I,a8 as V,ad as D,a7 as H,b2 as M,ax as B,av as R}from"../../../vendor.js?ver=3.1.8";import{E as O}from"../../../_eChart.js?ver=3.1.8";import{f as P,b as q,$ as F,d as j,g as W}from"../../../data_config.js?ver=3.1.8";import{_ as N,a as z,T as Z,I as G}from"../../../fc-bits-ui.js?ver=3.1.8";import{C as U}from"../../../CustomIcon.js?ver=3.1.8";import{C as K}from"../../../CalendarIcon.js?ver=3.1.8";import{B as Y}from"../../../Badge.js?ver=3.1.8";import{B as Q}from"../../../BaseCard.js?ver=3.1.8";import{f as J}from"../../../fluentcrm-logo.js?ver=3.1.8";const X=N({name:"SubscribersGrowth",components:{EChart:O},props:{date_range:{type:[String,Object,Array],required:!0}},data:()=>({fetching:!1,initialLoad:!0,stats:{},maxCumulativeValue:0,freshOptions:null,current_mode:"system"===Z.getCurrentTheme()?Z.getSystemTheme():Z.getCurrentTheme()}),mounted(){this.fetchReport(),this.onThemeChanged=t=>{var e;this.current_mode=(null==(e=t.detail)?void 0:e.effective)||Z.getCurrentTheme(),this.stats&&Object.keys(this.stats).length&&this.setupChartItems()},window.addEventListener(z,this.onThemeChanged)},beforeUnmount(){this.onThemeChanged&&window.removeEventListener(z,this.onThemeChanged)},watch:{date_range(){this.fetchReport()}},methods:{fetchReport(){this.fetching=!0,this.$get("reports/subscribers",{date_range:this.date_range}).then(t=>{var e,a;this.stats=(null==(a=null==(e=t.data_sets)?void 0:e[0])?void 0:a.data)||{},this.setupChartItems()}).finally(()=>{this.fetching=!1,this.initialLoad=!1})},setupChartItems(){const t=[],e={label:this.$t("By Date"),backgroundColor:"#ff7f0e",borderColor:"#ff7f0e",data:[],fill:!1,type:"line"},a={label:this.$t("Cumulative"),backgroundColor:"rgba(111, 66, 193, 0.1)",borderColor:"#6f42c1",data:[],type:"line"};let s=0;this.each(this.stats,(i,n)=>{e.data.push(i),t.push(n),s+=parseInt(i,10),a.data.push(s)});const i=/^[A-Za-z]{3} \d{4}$/;t.every(t=>i.test(t))||t.sort(),this.maxCumulativeValue=s+10;const n=q(t,this.date_range);this.renderChart(n,e,a)},renderChart(t,e,a){const s=this.date_range;this.freshOptions={tooltip:{show:!0,showContent:!0,appendToBody:!0,confine:!1,triggerOn:"mousemove|click",transitionDuration:.1,trigger:"axis",backgroundColor:"light"===this.current_mode?"#ffffff":"#283b56",borderColor:"light"===this.current_mode?"#ffffff":"#283b56",borderWidth:1,color:"#ffffff",formatter:t=>{const e=t[0]&&t[0].axisValue;return P(e,s)+"
"+t.map(t=>``+" "+t.seriesName+": "+t.value).join("
")},axisPointer:{type:"cross",label:{backgroundColor:"#283b56"},crossStyle:{color:"#999"}},extraCssText:"z-index:10000; pointer-events:auto;",textStyle:{color:"light"===this.current_mode?"#0E121B":"#ffffff"}},legend:{top:10,left:"center",data:[e.label,a.label],icon:"roundRect",itemWidth:12,itemHeight:12,itemGap:20,textStyle:{color:"light"===this.current_mode?"#0E121B":"#ffffff"}},grid:{top:40,left:40,right:40,bottom:40,containLabel:!0},xAxis:{type:"category",data:t,axisPointer:{type:"shadow"},splitLine:{show:!1},axisLabel:{color:"light"===this.current_mode?"#0E121B":"#9CA3AF"}},yAxis:[{type:"value",min:0,name:e.label,minInterval:1,axisLabel:{formatter:t=>Number.isInteger(t)?t:""},splitLine:{lineStyle:{color:"light"===this.current_mode?"#E1E4EA":"#2c3c4e",type:"dashed"}}},{type:"value",min:0,name:a.label,max:this.maxCumulativeValue||null,minInterval:1,axisLabel:{formatter:t=>Number.isInteger(t)?t:""},splitLine:{lineStyle:{color:"light"===this.current_mode?"#E1E4EA":"#2c3c4e",type:"dashed"}}}],series:[{name:e.label,type:"line",data:e.data,yAxisIndex:0,itemStyle:{color:e.backgroundColor},emphasis:{focus:"series"}},{name:a.label,type:"line",data:a.data,yAxisIndex:1,smooth:!1,symbol:"circle",symbolSize:6,itemStyle:{color:a.borderColor},lineStyle:{color:a.borderColor,width:2}}]}}}},[["render",function(e,a,s,i,n,o){const r=b("EChart"),l=t;return y((v(),C("div",null,[w(r,{options:n.freshOptions},null,8,["options"])])),[[l,n.fetching&&!n.initialLoad]])}]]);const tt=N({name:"email-sent-growth",props:["date_range"],components:{EChart:O},data:()=>({fetching:!1,initialLoad:!0,stats:{},maxCumulativeValue:0,freshOptions:null,current_mode:"system"===Z.getCurrentTheme()?Z.getSystemTheme():Z.getCurrentTheme()}),computed:{},methods:{fetchReport(){this.fetching=!0,this.$get("reports/email-sents",{date_range:this.date_range}).then(t=>{this.stats=t.stats,this.setupChartItems()}).finally(()=>{this.fetching=!1,this.initialLoad=!1})},setupChartItems(){const t=[],e={label:this.$t("By Date"),borderColor:"#ff7f0e",data:[]},a={label:this.$t("Cumulative"),borderColor:"#6f42c1",data:[]};let s=0;this.each(this.stats,(i,n)=>{e.data.push(i),t.push(n),s+=parseInt(i),a.data.push(s)}),this.maxCumulativeValue=s+10;const i=q(t,this.date_range),n=this.date_range;this.freshOptions={tooltip:{show:!0,showContent:!0,appendToBody:!0,confine:!1,triggerOn:"mousemove|click",transitionDuration:.1,trigger:"axis",backgroundColor:"light"===this.current_mode?"#ffffff":"#283b56",borderColor:"light"===this.current_mode?"#ffffff":"#283b56",borderWidth:1,color:"#ffffff",formatter:t=>{const e=t[0]&&t[0].axisValue;return P(e,n)+"
"+t.map(t=>``+" "+t.seriesName+": "+t.value).join("
")},axisPointer:{type:"cross",label:{backgroundColor:"#283b56"},crossStyle:{color:"#999"}},extraCssText:"z-index:10000; pointer-events:auto;",textStyle:{color:"light"===this.current_mode?"#0E121B":"#ffffff"}},legend:{top:10,left:"center",data:[e.label,a.label],icon:"roundRect",itemWidth:12,itemHeight:12,itemGap:20,textStyle:{color:"light"===this.current_mode?"#0E121B":"#ffffff"}},grid:{top:40,left:40,right:40,bottom:40,containLabel:!0},xAxis:{type:"category",data:i,axisPointer:{type:"shadow"},splitLine:{show:!1},axisLabel:{color:"light"===this.current_mode?"#0E121B":"#9CA3AF"}},yAxis:[{type:"value",min:0,name:e.label,minInterval:1,axisLabel:{formatter:t=>Number.isInteger(t)?t:""},splitLine:{lineStyle:{color:"light"===this.current_mode?"#E1E4EA":"#2c3c4e",type:"dashed"}}},{type:"value",min:0,name:a.label,max:this.maxCumulativeValue||null,minInterval:1,axisLabel:{formatter:t=>Number.isInteger(t)?t:""},splitLine:{lineStyle:{color:"light"===this.current_mode?"#E1E4EA":"#2c3c4e",type:"dashed"}}}],series:[{name:e.label,type:"line",data:e.data,yAxisIndex:0,itemStyle:{color:e.borderColor},emphasis:{focus:"series"}},{name:a.label,type:"line",data:a.data,yAxisIndex:1,smooth:!1,symbol:"circle",symbolSize:4,itemStyle:{color:a.borderColor},lineStyle:{color:a.borderColor,width:2}}]}}},mounted(){this.fetchReport(),this.onThemeChanged=t=>{var e;this.current_mode=(null==(e=t.detail)?void 0:e.effective)||Z.getCurrentTheme(),this.stats&&Object.keys(this.stats).length&&this.setupChartItems()},window.addEventListener(z,this.onThemeChanged)},beforeUnmount(){this.onThemeChanged&&window.removeEventListener(z,this.onThemeChanged)}},[["render",function(e,a,s,i,n,o){const r=b("EChart"),l=t;return y((v(),C("div",null,[w(r,{options:n.freshOptions},null,8,["options"])])),[[l,n.fetching&&!n.initialLoad]])}]]);const et=N({name:"email-open-chart",props:["date_range"],components:{EChart:O},data:()=>({fetching:!1,initialLoad:!0,stats:{},maxCumulativeValue:0,freshOptions:null,current_mode:"system"===Z.getCurrentTheme()?Z.getSystemTheme():Z.getCurrentTheme()}),computed:{},methods:{fetchReport(){this.fetching=!0,this.$get("reports/email-opens",{date_range:this.date_range}).then(t=>{this.stats=t.stats,this.setupChartItems()}).finally(()=>{this.fetching=!1,this.initialLoad=!1})},setupChartItems(){const t=[],e={label:this.$t("By Date"),borderColor:"#ff7f0e",data:[]},a={label:this.$t("Cumulative"),borderColor:"#6f42c1",data:[]};let s=0;this.each(this.stats,(i,n)=>{e.data.push(i),t.push(n),s+=parseInt(i),a.data.push(s)}),this.maxCumulativeValue=s+10;const i=q(t,this.date_range),n=this.date_range;this.freshOptions={tooltip:{show:!0,showContent:!0,appendToBody:!0,confine:!1,triggerOn:"mousemove|click",transitionDuration:.1,trigger:"axis",backgroundColor:"light"===this.current_mode?"#ffffff":"#283b56",borderColor:"light"===this.current_mode?"#ffffff":"#283b56",borderWidth:1,color:"#ffffff",formatter:t=>{const e=t[0]&&t[0].axisValue;return P(e,n)+"
"+t.map(t=>``+" "+t.seriesName+": "+t.value).join("
")},axisPointer:{type:"cross",label:{backgroundColor:"#283b56"},crossStyle:{color:"#999"}},extraCssText:"z-index:10000; pointer-events:auto;",textStyle:{color:"light"===this.current_mode?"#0E121B":"#ffffff"}},legend:{top:10,left:"center",data:[e.label,a.label],icon:"roundRect",itemWidth:12,itemHeight:12,itemGap:20,textStyle:{color:"light"===this.current_mode?"#0E121B":"#ffffff"}},grid:{top:40,left:40,right:40,bottom:40,containLabel:!0},xAxis:{type:"category",data:i,axisPointer:{type:"shadow"},splitLine:{show:!1},axisLabel:{color:"light"===this.current_mode?"#0E121B":"#9CA3AF"}},yAxis:[{type:"value",min:0,name:e.label,minInterval:1,axisLabel:{formatter:t=>Number.isInteger(t)?t:""},splitLine:{lineStyle:{color:"light"===this.current_mode?"#E1E4EA":"#2c3c4e",type:"dashed"}}},{type:"value",min:0,name:a.label,max:this.maxCumulativeValue||null,minInterval:1,axisLabel:{formatter:t=>Number.isInteger(t)?t:""},splitLine:{lineStyle:{color:"light"===this.current_mode?"#E1E4EA":"#2c3c4e",type:"dashed"}}}],series:[{name:e.label,type:"line",data:e.data,yAxisIndex:0,itemStyle:{color:e.borderColor},emphasis:{focus:"series"}},{name:a.label,type:"line",data:a.data,yAxisIndex:1,smooth:!1,symbol:"circle",symbolSize:4,itemStyle:{color:a.borderColor},lineStyle:{color:a.borderColor,width:2}}]}}},mounted(){this.fetchReport(),this.onThemeChanged=t=>{var e;this.current_mode=(null==(e=t.detail)?void 0:e.effective)||Z.getCurrentTheme(),this.stats&&Object.keys(this.stats).length&&this.setupChartItems()},window.addEventListener(z,this.onThemeChanged)},beforeUnmount(){this.onThemeChanged&&window.removeEventListener(z,this.onThemeChanged)}},[["render",function(e,a,s,i,n,o){const r=b("EChart"),l=t;return y((v(),C("div",null,[w(r,{options:n.freshOptions},null,8,["options"])])),[[l,n.fetching&&!n.initialLoad]])}]]);const at=N({name:"email-click-chart",props:["date_range"],components:{EChart:O},data:()=>({fetching:!1,initialLoad:!0,stats:{},maxCumulativeValue:0,freshOptions:null,current_mode:"system"===Z.getCurrentTheme()?Z.getSystemTheme():Z.getCurrentTheme()}),computed:{},methods:{fetchReport(){this.fetching=!0,this.$get("reports/email-clicks",{date_range:this.date_range}).then(t=>{this.stats=t.stats,this.setupChartItems()}).finally(()=>{this.fetching=!1,this.initialLoad=!1})},setupChartItems(){const t=[],e={label:this.$t("By Date"),borderColor:"#ff7f0e",data:[]},a={label:this.$t("Cumulative"),borderColor:"#6f42c1",data:[]};let s=0;this.each(this.stats,(i,n)=>{e.data.push(i),t.push(n),s+=parseInt(i),a.data.push(s)}),this.maxCumulativeValue=s+10;const i=q(t,this.date_range),n=this.date_range;this.freshOptions={tooltip:{show:!0,showContent:!0,appendToBody:!0,confine:!1,triggerOn:"mousemove|click",transitionDuration:.1,trigger:"axis",backgroundColor:"light"===this.current_mode?"#ffffff":"#283b56",borderColor:"light"===this.current_mode?"#ffffff":"#283b56",borderWidth:1,color:"#ffffff",formatter:t=>{const e=t[0]&&t[0].axisValue;return P(e,n)+"
"+t.map(t=>``+" "+t.seriesName+": "+t.value).join("
")},axisPointer:{type:"cross",label:{backgroundColor:"#283b56"},crossStyle:{color:"#999"}},extraCssText:"z-index:10000; pointer-events:auto;",textStyle:{color:"light"===this.current_mode?"#0E121B":"#ffffff"}},legend:{top:10,left:"center",data:[e.label,a.label],icon:"roundRect",itemWidth:12,itemHeight:12,itemGap:20,textStyle:{color:"light"===this.current_mode?"#0E121B":"#ffffff"}},grid:{top:40,left:40,right:40,bottom:40,containLabel:!0},xAxis:{type:"category",data:i,axisPointer:{type:"shadow"},splitLine:{show:!1},axisLabel:{color:"light"===this.current_mode?"#0E121B":"#9CA3AF"}},yAxis:[{type:"value",min:0,name:e.label,minInterval:1,axisLabel:{formatter:t=>Number.isInteger(t)?t:""},splitLine:{lineStyle:{color:"light"===this.current_mode?"#E1E4EA":"#2c3c4e",type:"dashed"}}},{type:"value",min:0,name:a.label,max:this.maxCumulativeValue||null,minInterval:1,axisLabel:{formatter:t=>Number.isInteger(t)?t:""},splitLine:{lineStyle:{color:"light"===this.current_mode?"#E1E4EA":"#2c3c4e",type:"dashed"}}}],series:[{name:e.label,type:"line",data:e.data,yAxisIndex:0,itemStyle:{color:e.borderColor},emphasis:{focus:"series"}},{name:a.label,type:"line",data:a.data,yAxisIndex:1,smooth:!1,symbol:"circle",symbolSize:4,itemStyle:{color:a.borderColor},lineStyle:{color:a.borderColor,width:2}}]}}},mounted(){this.fetchReport(),this.onThemeChanged=t=>{var e;this.current_mode=(null==(e=t.detail)?void 0:e.effective)||Z.getCurrentTheme(),this.stats&&Object.keys(this.stats).length&&this.setupChartItems()},window.addEventListener(z,this.onThemeChanged)},beforeUnmount(){this.onThemeChanged&&window.removeEventListener(z,this.onThemeChanged)}},[["render",function(e,a,s,i,n,o){const r=b("EChart"),l=t;return y((v(),C("div",null,[w(r,{options:n.freshOptions},null,8,["options"])])),[[l,n.fetching&&!n.initialLoad]])}]]),st={class:"icon"},it={class:"fcrm_perf_bars"},nt={class:"fcrm_perf_bar_header"},ot={class:"fcrm_perf_bar_label"},rt={class:"fcrm_perf_value"},lt={class:"fcrm_perf_count"},ct={class:"fcrm_perf_pct"},dt={class:"fcrm_perf_bar"};const mt=N({name:"_EmailPerformance",components:{ArrowDown:e,BaseCard:Q},data:()=>({fetching:!1,period:"30d",periodOptions:{"30d":"30 Days","60d":"60 Days","90d":"90 Days",all:"All Time"},statsTotals:{sent:0,delivered:0,opened:0,clicked:0,bounced:0}}),computed:{funnelData(){const t=this.statsTotals.sent;return[{name:"Sent",value:t,color:"#7B61FF"},{name:"Delivered",value:this.statsTotals.delivered,color:"#F6B51E"},{name:"Opened",value:this.statsTotals.opened,color:"#22D3BB"},{name:"Clicked",value:this.statsTotals.clicked,color:"#F6B51E"},{name:"Bounced",value:this.statsTotals.bounced,color:"#335CFF"}].map(e=>({...e,percent:t?Math.min(e.value/t*100,100):0,pctText:t?(e.value/t*100).toFixed(1):"0.0"}))},periodDays(){return{"30d":30,"60d":60,"90d":90}[this.period]||0}},methods:{fetchReport(){this.fetching=!0;const t={days:this.periodDays};this.$get("reports/email-performance",t).then(t=>{this.statsTotals.sent=t.stats.totals.sent,this.statsTotals.delivered=t.stats.totals.delivered,this.statsTotals.opened=t.stats.totals.opened,this.statsTotals.clicked=t.stats.totals.clicked,this.statsTotals.bounced=t.stats.totals.bounced}).catch(t=>{this.handleError(t)}).finally(()=>{this.fetching=!1})},handlePeriodChange(t){this.period=t,this.fetchReport()},formatNumber:t=>Number(t).toLocaleString()},mounted(){this.fetchReport()}},[["render",function(e,r,l,c,d,m){const h=b("ArrowDown"),_=i,u=s,f=o,p=n,g=a,V=b("BaseCard"),D=t;return v(),x(V,null,{title:k(()=>[T("h4",null,S(e.$t("Email Performance")),1)]),header_action:k(()=>[w(g,{trigger:"click",onCommand:m.handlePeriodChange},{dropdown:k(()=>[w(p,{class:"fc_dropdown"},{default:k(()=>[(v(!0),C(L,null,$(d.periodOptions,(t,a)=>(v(),x(f,{key:a,command:a,class:I({"is-active":d.period===a})},{default:k(()=>[A(S(e.$t(t)),1)],void 0,!0),_:2},1032,["command","class"]))),128))],void 0,!0),_:1})]),default:k(()=>[w(u,{size:"small","aria-label":e.$t("Select Period")},{default:k(()=>[A(S(e.$t(d.periodOptions[d.period]))+" ",1),T("span",st,[w(_,null,{default:k(()=>[w(h)],void 0,!0),_:1})])],void 0,!0),_:1},8,["aria-label"])],void 0,!0),_:1},8,["onCommand"])]),body:k(()=>[y((v(),C("div",it,[(v(!0),C(L,null,$(m.funnelData,t=>(v(),C("div",{key:t.name,class:"fcrm_perf_row"},[T("div",nt,[T("span",ot,S(e.$t(t.name)),1),T("div",rt,[T("span",lt,S(m.formatNumber(t.value)),1),T("span",ct,S(t.pctText)+"%",1)])]),T("div",dt,[T("div",{class:"fcrm_perf_bar_inner",style:E({width:t.percent+"%",backgroundColor:t.color})},null,4)])]))),128))])),[[D,d.fetching]])]),_:1})}]]),ht={class:"fcrm_dashboard_loader"},_t={class:"fcrm_dashboard_user"},ut={class:"fcrm_dashboard_user_image"},ft={class:"fcrm_dashboard_user_info"},pt={class:"fcrm_card_widgets"},gt={class:"fcrm_card_widgets--header"},bt={class:"fcrm_card_widget_icon fcrm_p_0"},yt={class:"fcrm_card_widget_title"},vt={class:"fcrm_card_widget_content",style:{"margin-top":"15px"}},Ct={class:"el-dropdown-link cursor_pointer"},wt={class:"icon"},xt={class:"fluentcrm-templates-action-buttons fluentcrm-actions fcrm-dashboard-filters"},kt={class:"w-full d-flex flex-column gap-4"},Lt={class:"fcrm_dashboard_entity_card__item_content"},$t={class:"fcrm_dashboard_entity_card__item_title"},Tt={class:"fcrm_dashboard_entity_card__stats"},St={class:"fcrm_dashboard_entity_card__stat"},Et={class:"fcrm_dashboard_entity_card__stat"},At={class:"fcrm_dashboard_entity_card__stat"},It={class:"fcrm_dashboard_entity_card__stat fcrm_dashboard_entity_card__stat_open_rate"},Vt={class:"fcrm_contact_automation_stats"},Dt={class:"w-full d-flex flex-column gap-4"},Ht={class:"fcrm_dashboard_entity_card__item_media"},Mt={class:"fcrm_dashboard_user_image"},Bt={class:"fcrm_dashboard_entity_card__item_content"},Rt={class:"fcrm_dashboard_entity_card__item_title"},Ot={class:"fcrm_dashboard_entity_card__item_subtitle"},Pt={class:"fcrm_dashboard_entity_card__item_meta"},qt={class:"w-full d-flex flex-column gap-4"},Ft={class:"fcrm_dashboard_entity_card__item_content"},jt={class:"fcrm_dashboard_entity_card__item_title"},Wt={class:"fcrm_dashboard_entity_card__item_subtitle"},Nt={class:"fc_lined_items"},zt={class:"fc_li_title"},Zt={class:"fc_li_value",style:{float:"none"}};const Gt=N({name:"DashboardLoader",components:{ArrowDown:e,BaseCard:Q},data:()=>({widgets:[1,2,3,4]})},[["render",function(t,e,a,s,n,o){const m=r,h=l,_=b("ArrowDown"),u=i,f=b("BaseCard"),p=c,g=d;return v(),C("div",ht,[T("div",_t,[T("div",ut,[w(h,{animated:""},{template:k(()=>[w(m,{variant:"circle",style:{width:"48px",height:"48px","border-radius":"50%"}})]),_:1})]),T("div",ft,[w(h,{animated:""},{template:k(()=>[w(m,{style:{width:"200px","margin-bottom":"4px"}})]),_:1}),w(h,{animated:""},{template:k(()=>[w(m,{style:{width:"150px"}})]),_:1})])]),w(g,{gutter:24},{default:k(()=>[w(p,{sm:24,md:16,lg:16},{default:k(()=>[T("div",pt,[(v(!0),C(L,null,$(n.widgets,t=>(v(),C("div",{key:t,class:"fcrm_card_widget"},[T("div",gt,[T("div",bt,[w(h,{animated:"",style:{height:"100%",width:"100%"}},{template:k(()=>[w(m,{variant:"circle",style:{width:"100%",height:"100%","border-radius":"8px"}})]),_:1})]),T("div",yt,[w(h,{animated:""},{template:k(()=>[w(m,{style:{width:"80px"}})]),_:1})])]),T("div",vt,[w(h,{animated:""},{template:k(()=>[w(m,{style:{width:"70%"}})]),_:1})])]))),128))]),w(f,{class:"fcrm_subscriber_growth_card"},{title:k(()=>[T("h4",Ct,[A(S(t.$t("Subscribers Growth"))+" ",1),T("span",wt,[w(u,null,{default:k(()=>[w(_)],void 0,!0),_:1})])])]),header_action:k(()=>[T("div",xt,[w(h,{animated:""},{template:k(()=>[w(m,{style:{width:"200px",height:"36px"}})]),_:1})])]),body:k(()=>[w(h,{animated:"",rows:10})]),_:1}),w(f,{body_class:"fcrm_p_12",class:"fc_m_24"},{title:k(()=>[T("h4",null,S(t.$t("Recent Campaigns")),1)]),header_action:k(()=>[w(h,{animated:""},{template:k(()=>[w(m,{style:{width:"80px",height:"28px"}})]),_:1})]),body:k(()=>[T("div",kt,[(v(),C(L,null,$(3,t=>T("div",{key:t,class:"fcrm_dashboard_entity_card__item"},[T("div",Lt,[T("p",$t,[w(h,{animated:"",style:{width:"300px"}},{template:k(()=>[w(m,{style:{width:"70%"}})]),_:1})]),T("div",Tt,[T("div",St,[w(h,{animated:""},{template:k(()=>[w(m,{style:{width:"30px"}})]),_:1})]),e[0]||(e[0]=T("span",{class:"fcrm_dashboard_entity_card__stat_dot"},null,-1)),T("div",Et,[w(h,{animated:""},{template:k(()=>[w(m,{style:{width:"30px"}})]),_:1})]),e[1]||(e[1]=T("span",{class:"fcrm_dashboard_entity_card__stat_dot"},null,-1)),T("div",At,[w(h,{animated:""},{template:k(()=>[w(m,{style:{width:"30px"}})]),_:1})]),T("div",It,[w(h,{animated:""},{template:k(()=>[w(m,{style:{width:"50px"}})]),_:1})])])])])),64))])]),_:1}),T("div",Vt,[w(f,{body_class:"fcrm_p_12"},{title:k(()=>[T("h4",null,S(t.$t("Recent Contacts")),1)]),header_action:k(()=>[w(h,{animated:""},{template:k(()=>[w(m,{style:{width:"80px",height:"28px"}})]),_:1})]),body:k(()=>[T("div",Dt,[(v(),C(L,null,$(3,t=>T("div",{key:t,class:"fcrm_dashboard_entity_card__item"},[T("div",Ht,[T("div",Mt,[w(h,{animated:""},{template:k(()=>[w(m,{variant:"circle",style:{width:"40px",height:"40px","border-radius":"50%"}})]),_:1})])]),T("div",Bt,[T("p",Rt,[w(h,{animated:"",style:{width:"200px"}},{template:k(()=>[w(m,{style:{width:"40%",height:"10px","margin-bottom":"4px"}})]),_:1})]),T("span",Ot,[w(h,{animated:"",style:{width:"200px"}},{template:k(()=>[w(m,{style:{width:"60%",height:"10px"}})]),_:1})])]),T("div",Pt,[w(h,{animated:""},{template:k(()=>[w(m,{style:{width:"80px",height:"14px","margin-bottom":"4px"}})]),_:1})])])),64))])]),_:1}),w(f,{body_class:"fcrm_p_12"},{title:k(()=>[T("h4",null,S(t.$t("Active Automations")),1)]),header_action:k(()=>[w(h,{animated:""},{template:k(()=>[w(m,{style:{width:"80px",height:"28px"}})]),_:1})]),body:k(()=>[T("div",qt,[(v(),C(L,null,$(3,t=>T("div",{key:t,class:"fcrm_dashboard_entity_card__item"},[T("div",Ft,[T("p",jt,[w(h,{animated:"",style:{width:"200px"}},{template:k(()=>[w(m,{style:{width:"40%",height:"14px","margin-bottom":"4px"}})]),_:1})]),T("span",Wt,[e[2]||(e[2]=T("span",{class:"icon"},[T("svg",{width:"16",height:"16",viewBox:"0 0 16 16",fill:"none",xmlns:"http://www.w3.org/2000/svg"},[T("path",{d:"M8.60001 6.19999H13.4L7.40001 15.2V9.79999H3.20001L8.60001 0.799988V6.19999ZM7.40001 7.39999V5.13199L5.31921 8.59999H8.60001V11.2364L11.1578 7.39999H7.40001Z",fill:"var(--fc-secondary-text)"})])],-1)),w(h,{animated:"",style:{width:"200px"}},{template:k(()=>[w(m,{style:{width:"60%",height:"12px"}})]),_:1})])])])),64))])]),_:1})])],void 0,!0),_:1}),w(p,{sm:24,md:8,lg:8},{default:k(()=>[w(f,null,{title:k(()=>[T("h4",null,S(t.$t("Sales")),1)]),body:k(()=>[T("ul",Nt,[(v(),C(L,null,$(3,t=>T("li",{key:t,style:{display:"flex","align-items":"center","justify-content":"space-between"}},[T("span",zt,[w(h,{animated:""},{template:k(()=>[w(m,{style:{width:"100px",height:"17px"}})]),_:1})]),T("span",Zt,[w(h,{animated:""},{template:k(()=>[w(m,{style:{width:"40px",height:"17px"}})]),_:1})])])),64))])]),_:1}),w(f,null,{title:k(()=>[T("h4",null,S(t.$t("Quick Links")),1)]),body:k(()=>[w(h,{animated:"",rows:4})]),_:1}),w(f,null,{title:k(()=>[T("h4",null,S(t.$t("Email Performance")),1)]),body:k(()=>[w(h,{animated:"",rows:6})]),_:1})],void 0,!0),_:1})],void 0),_:1})])}]]);class Ut{static greeting(t=new Date){const e=t.getHours();if(e>=5&&e<12)return F("Good morning");if(e>=12&&e<17)return F("Good afternoon");if(e>=17&&e<21)return F("Good evening");{const t=[F("Step into the moonlight!"),F("Evening vibes!"),F("The night's still young!")];return t[Math.floor(Math.random()*t.length)]}}}const Kt={class:"fluentcrm_admin_dashboard fcrm-layout-width",style:{position:"relative"}},Yt={key:0,style:{"margin-bottom":"20px"},class:"dashboard_notices"},Qt=["innerHTML"],Jt={class:"fcrm_dashboard_user"},Xt=["innerHTML"],te={class:"fcrm_dashboard_user_info"},ee={class:"fcrm_card_widgets"},ae=["onClick"],se={class:"fcrm_card_widgets--header"},ie=["innerHTML"],ne=["innerHTML"],oe={class:"el-dropdown-link cursor_pointer"},re={class:"icon"},le={class:"w-full d-flex flex-column gap-4"},ce=["onClick","onKeydown"],de={class:"fcrm_dashboard_entity_card__item_content"},me={class:"fcrm_dashboard_entity_card__item_title"},he={class:"fcrm_dashboard_entity_card__stats"},_e={class:"fcrm_dashboard_entity_card__stat"},ue={class:"fcrm_dashboard_entity_card__stat_icon"},fe={class:"fcrm_dashboard_entity_card__stat"},pe={class:"fcrm_dashboard_entity_card__stat_icon"},ge={class:"fcrm_dashboard_entity_card__stat"},be={class:"fcrm_dashboard_entity_card__stat_icon"},ye={class:"fcrm_dashboard_entity_card__stat fcrm_dashboard_entity_card__stat_open_rate"},ve={key:1,class:"text-center d-flex flex-column gap-10 justify-center h-full w-full fcrm_p_20"},Ce={class:"icon"},we={class:"fcrm_secondary_text"},xe={class:"fcrm_contact_automation_stats"},ke={class:"w-full d-flex flex-column gap-4"},Le=["onClick","onKeydown"],$e={class:"fcrm_dashboard_entity_card__item_media"},Te={class:"fcrm_dashboard_user_image"},Se=["title","src"],Ee={class:"fcrm_dashboard_entity_card__item_content"},Ae={class:"fcrm_dashboard_entity_card__item_title"},Ie=["title"],Ve={class:"fcrm_dashboard_entity_card__item_meta"},De={key:1,class:"text-center d-flex flex-column gap-10 justify-center h-full w-full fcrm_p_20"},He={class:"icon"},Me={class:"fcrm_secondary_text"},Be={class:"w-full d-flex flex-column gap-4"},Re=["onClick","onKeydown"],Oe={class:"fcrm_dashboard_entity_card__item_content"},Pe={class:"fcrm_dashboard_entity_card__item_title"},qe={class:"fcrm_dashboard_entity_card__item_subtitle"},Fe={key:1,class:"text-center d-flex flex-column gap-10 justify-center h-full w-full fcrm_p_20"},je={class:"icon"},We={class:"fcrm_secondary_text"},Ne={class:"fcrm_secondary_text small"},ze={class:"fcrm_onboarding_steps--lists"},Ze=["onClick"],Ge={class:"icon"},Ue={class:"fc_lined_items"},Ke=["innerHTML"],Ye=["innerHTML"],Qe=["innerHTML"],Je={key:0,class:"fcrm_secondary_text small fcrm_mb_16"},Xe=["href"],ta={class:"el-button el-button--primary",href:"https://fluentcrm.com/?utm_source=dashboard&utm_medium=plugin&utm_campaign=pro&utm_id=wp",target:"_blank",rel:"noopener"},ea={class:"fcrm_quick_links"},aa={class:"fcrm_quick_link_icon"},sa={key:1,width:"20",height:"20",viewBox:"0 0 20 20",fill:"none",xmlns:"http://www.w3.org/2000/svg"},ia=["target","rel","href"],na={class:"d-flex flex-column items-center text-center"},oa={class:"fcrm_mb_16"},ra={class:"fcrm_primary_text font-medium fcrm_mb_4"},la={class:"fcrm_secondary_text small fcrm_mb_16"},ca={class:"d-flex flex-column items-center text-center"},da={class:"fcrm_mb_16"},ma={class:"fcrm_primary_text font-medium fcrm_mb_4"},ha={class:"fcrm_secondary_text small fcrm_mb_16"},_a={class:"text-align-center"},ua={class:"el-button el-button--primary",href:"https://fluentcrm.com/?utm_source=dashboard&utm_medium=plugin&utm_campaign=pro&utm_id=wp",target:"_blank",rel:"noopener"},fa=["innerHTML"],pa={class:"fcrm_review_card"},ga={class:"icon"},ba={class:"fcrm_review_card--title"},ya={class:"fcrm_review_card--body"},va={href:"https://wordpress.org/support/plugin/fluent-crm/reviews/#new-post",target:"_blank",class:"el-button"},Ca={class:"icon"},wa={class:"fcrm_onboarding_complete_popover_content"},xa=["src"],ka={class:"fcrm_onboarding--quick-actions"},La={class:"fcrm_onboarding--quick-actions-item-icon"},$a={class:"fcrm_onboarding--quick-actions-item-content"},Ta={class:"fcrm_onboarding--quick-actions-item-title"},Sa={class:"fcrm_onboarding--quick-actions-item-description fcrm_secondary_text"},Ea={class:"fcrm_onboarding--quick-actions-item-arrow"},Aa={class:"fcrm_onboarding--quick-actions-item-icon"},Ia={class:"fcrm_onboarding--quick-actions-item-content"},Va={class:"fcrm_onboarding--quick-actions-item-title"},Da={class:"fcrm_onboarding--quick-actions-item-description fcrm_secondary_text"},Ha={class:"fcrm_onboarding--quick-actions-item-arrow"};const Ma=N({name:"Dashboard",components:{DashboardLoader:Gt,EmailPerformance:mt,Badge:Y,BaseCard:Q,SubscribersChart:X,EmailSentChart:tt,EmailOpenChart:et,EmailClickChart:at,Icons:G,ArrowDown:e,SuccessFilled:u,CircleClose:_,CustomIcon:U,Download:h,Check:m},data(){return{logoIcon:J,greeting:Ut.greeting(),CalendarIcon:R(K),loading:!0,stats:[],quick_links:[],date_range:W(),currently_showing:"SubscribersChart",chartMaps:{SubscribersChart:this.$t("Subscribers Growth"),EmailSentChart:this.$t("Email Sending Stats"),EmailOpenChart:this.$t("Email Open Stats"),EmailClickChart:this.$t("Email Link Click Stats")},showing_charts:!0,chartKey:0,ff_config:{is_installed:!0,create_form_link:""},installing_ff:!1,sales:[],onboarding:null,data_ready:!1,dashboard_notices:[],recommendation:!1,system_tips:null,showReviewWidget:!0,dateShortcuts:j,recent_contacts:[],active_automations:[],recent_campaigns:[],triggers:[],isCongratsPopoverOpen:!1}},methods:{getCongratsPopover(){try{const t=window.localStorage.getItem("fcrm_onboarding_congratulations_shown"),e=window.localStorage.getItem("fcrm_onboarding_congratulations_pending");"yes"!==t&&"yes"===e&&(this.isCongratsPopoverOpen=!0,window.localStorage.setItem("fcrm_onboarding_congratulations_shown","yes")),window.localStorage.removeItem("fcrm_onboarding_congratulations_pending")}catch(t){}},fetchDashBoardData(t=!0){t&&(this.loading=!0),this.$get("reports/dashboard-stats").then(t=>{this.stats=t.stats,this.sales=t.sales,this.quick_links=t.quick_links,this.ff_config=t.ff_config,this.onboarding=t.onboarding,this.dashboard_notices=t.dashboard_notices,this.recommendation=t.recommendation,this.system_tips=t.system_tips,this.recent_contacts=t.recent_contacts,this.active_automations=t.active_automations,this.triggers=t.triggers,this.recent_campaigns=t.recent_campaigns,this.getCongratsPopover(),this.stats.email_pending&&delete this.stats.email_pending}).finally(()=>{t&&(this.loading=!1),this.data_ready=!0})},goToRoute(t){t&&this.$router.push(t)},maybeRouteStep(t){t.completed||this.goToRoute(t.route)},handleComponentChange(t){this.currently_showing=t},filterReport(){this.chartKey+=1},refreshData(){this.fetchDashBoardData(!1)},installFF(){this.installing_ff=!0,this.$post("setting/install-fluentform").then(t=>{this.ff_config=t.ff_config,this.$notify.success(t.message)}).catch(t=>{this.handleError(t)}).finally(()=>{this.installing_ff=!1})},closeReviewWidget(){this.showReviewWidget=!1;let t="";const e=new Date;e.setTime(e.getTime()+6048e5),t="; expires="+e.toUTCString(),document.cookie="showReviewWidget=false"+t},checkIfReviewWidgetEnabled(){const t=document.cookie.split(";");for(let e=0;ee&&t?parseFloat(t/e*100).toFixed(2)+"%":"0%",maybeRepairDbIndexes(){window.fcAdmin&&!window.fcAdmin.db_index_health_ok&&(window.__fcDbIndexRepairTried||(window.__fcDbIndexRepairTried=!0,this.$post("setting/db-index-health/repair").then(()=>{window.fcAdmin.db_index_health_ok="1"}).catch(()=>{this.$notify({type:"warning",duration:0,title:this.$t("Database needs attention"),message:this.$t("Some performance indexes are missing and could not be created automatically. Click here to open Database Health."),onClick:()=>{this.$router.push({name:"database_health"})}})})))},handleCampaignClick(t){let e="campaign-view";"draft"===t.status&&(e="campaign"),this.$router.push({name:e,params:{id:t.id},query:{t:(new Date).getTime(),step:t.next_step&&parseInt(t.next_step)<=3?t.next_step:0}})}},mounted(){this.fetchDashBoardData(),this.checkIfReviewWidgetEnabled(),this.maybeRepairDbIndexes(),this.timerId=setInterval(()=>{this.refreshData()},3e5),this.changeTitle(this.$t("Dashboard"))},beforeUnmount(){this.timerId&&clearInterval(this.timerId)}},[["render",function(t,e,r,l,m,h){const _=b("DashboardLoader"),u=b("custom-icon"),y=b("Icons"),R=o,O=n,P=a,q=f,F=b("BaseCard"),j=s,W=p,N=b("Badge"),z=c,Z=b("Check"),G=i,U=b("EmailPerformance"),K=b("icons"),Y=d,Q=g;return v(),C("div",Kt,[w(D,{name:"fcrm-fade"},{default:k(()=>[m.loading?(v(),x(_,{key:0,class:"fcrm-loader-overlay"})):V("",!0)],void 0),_:1}),T("div",{style:E(m.loading?"visibility:hidden":"")},[m.dashboard_notices&&m.dashboard_notices.length?(v(),C("div",Yt,[(v(!0),C(L,null,$(m.dashboard_notices,(t,e)=>(v(),C("div",{class:"fcrm_notice",key:e,innerHTML:t},null,8,Qt))),128))])):V("",!0),T("div",Jt,[T("div",{class:"fcrm_dashboard_user_image",innerHTML:t.appVars.auth.avatar},null,8,Xt),T("div",te,[T("h3",null,S(m.greeting)+", "+S(t.appVars.auth.first_name)+" 👋🏻",1),T("p",null,S(t.$t("Welcome to"))+" FluentCRM ",1)])]),w(Y,{gutter:24},{default:k(()=>[w(z,{sm:24,md:16,lg:16},{default:k(()=>[T("div",ee,[(v(!0),C(L,null,$(m.stats,(e,a)=>(v(),C("div",{key:a,class:"fcrm_card_widget",onClick:t=>h.goToRoute(e.route)},[T("div",se,[T("div",{class:I(["fcrm_card_widget_icon","fcrm_icon_background_"+a])},[w(u,{type:a},null,8,["type"])],2),T("div",{class:"fcrm_card_widget_title",innerHTML:e.title},null,8,ie)]),T("div",{class:"fcrm_card_widget_content",innerHTML:t.formatMoney(e.count)},null,8,ne)],8,ae))),128))]),w(F,{class:"fcrm_subscriber_growth_card","no-body-padding":!0},{title:k(()=>[w(P,{onCommand:h.handleComponentChange},{dropdown:k(()=>[w(O,{class:"fc_dropdown"},{default:k(()=>[(v(!0),C(L,null,$(m.chartMaps,(t,e)=>(v(),x(R,{key:e,command:e},{default:k(()=>[A(S(t),1)],void 0,!0),_:2},1032,["command"]))),128))],void 0,!0),_:1})]),default:k(()=>[T("h4",oe,[A(S(m.chartMaps[m.currently_showing])+" ",1),T("span",re,[w(y,{"icon-name":"downIcon"})])])],void 0,!0),_:1},8,["onCommand"])]),header_action:k(()=>[w(q,{modelValue:m.date_range,"onUpdate:modelValue":e[0]||(e[0]=t=>m.date_range=t),type:"daterange","range-separator":"-","start-placeholder":t.$t("Start date"),"end-placeholder":t.$t("End date"),format:"MMM DD","value-format":"YYYY-MM-DD",size:"large",shortcuts:m.dateShortcuts,onChange:h.filterReport,"prefix-icon":m.CalendarIcon},null,8,["modelValue","start-placeholder","end-placeholder","shortcuts","onChange","prefix-icon"])]),body:k(()=>[m.showing_charts?(v(),x(H(m.currently_showing),{class:"fcrm_mt_10",date_range:m.date_range,key:m.chartKey},null,8,["date_range"])):V("",!0)]),_:1}),w(F,{body_class:"fcrm_p_12"},{title:k(()=>[T("h4",null,S(t.$t("Recent Campaigns")),1)]),header_action:k(()=>[w(j,{onClick:h.viewAllCampaigns,size:"small","aria-label":t.$t("View All")},{default:k(()=>[A(S(t.$t("View All")),1)],void 0,!0),_:1},8,["onClick","aria-label"])]),body:k(()=>[T("div",le,[m.recent_campaigns.length?(v(!0),C(L,{key:0},$(m.recent_campaigns,(a,s)=>(v(),C("div",{class:"fcrm_dashboard_entity_card__item",key:s,onClick:t=>h.handleCampaignClick(a),onKeydown:[M(B(t=>h.handleCampaignClick(a),["prevent"]),["enter"]),M(B(t=>h.handleCampaignClick(a),["prevent"]),["space"])],role:"button",tabindex:"0"},[T("div",de,[T("p",me,S(a.title),1),T("div",he,[w(W,{content:t.$t("Total emails sent"),placement:"top"},{default:k(()=>[T("div",_e,[T("span",ue,[w(y,{"icon-name":"envelope"})]),T("span",null,S(a.stats.sent||"0"),1)])],void 0,!0),_:2},1032,["content"]),e[6]||(e[6]=T("span",{class:"fcrm_dashboard_entity_card__stat_dot"},null,-1)),w(W,{content:t.$t("Total emails opened"),placement:"top"},{default:k(()=>[T("div",fe,[T("span",pe,[w(y,{"icon-name":"envelopeOpen"})]),T("span",null,S(a.stats.views||"0"),1)])],void 0,!0),_:2},1032,["content"]),e[7]||(e[7]=T("span",{class:"fcrm_dashboard_entity_card__stat_dot"},null,-1)),w(W,{content:t.$t("Total clicks"),placement:"top"},{default:k(()=>[T("div",ge,[T("span",be,[w(y,{"icon-name":"click"})]),T("span",null,S(a.stats.clicks||"0"),1)])],void 0,!0),_:2},1032,["content"]),w(W,{content:t.$t("Percentage of emails opened"),placement:"top"},{default:k(()=>[T("div",ye,S(t.$t("Open rate:"))+" "+S(h.getPercent(a.stats.views,a.stats.sent)),1)],void 0,!0),_:2},1032,["content"])])])],40,ce))),128)):(v(),C("div",ve,[T("span",Ce,[w(y,{"icon-name":"common-empty-state"})]),T("p",we,S(t.$t("Looks like you don't have any campaigns now.")),1),w(j,{onClick:h.addCampaign,link:""},{default:k(()=>[A(S(t.$t("Add a Campaign")),1)],void 0,!0),_:1},8,["onClick"])]))])]),_:1}),T("div",xe,[w(F,{body_class:"fcrm_p_12"},{title:k(()=>[T("h4",null,S(t.$t("Recent Contacts")),1)]),header_action:k(()=>[w(j,{onClick:h.viewAllContacts,size:"small","aria-label":t.$t("View All")},{default:k(()=>[A(S(t.$t("View All")),1)],void 0,!0),_:1},8,["onClick","aria-label"])]),body:k(()=>[T("div",ke,[m.recent_contacts.length?(v(!0),C(L,{key:0},$(m.recent_contacts,(e,a)=>(v(),C("div",{class:"fcrm_dashboard_entity_card__item",key:a,onClick:t=>h.viewContact(e),onKeydown:[M(B(t=>h.viewContact(e),["prevent"]),["enter"]),M(B(t=>h.viewContact(e),["prevent"]),["space"])],role:"button",tabindex:"0"},[T("div",$e,[T("div",Te,[T("img",{title:t.$t("Contact ID:")+" "+e.id,src:e.photo},null,8,Se)])]),T("div",Ee,[T("p",Ae,S(e.full_name),1),T("span",{class:"fcrm_dashboard_entity_card__item_subtitle",title:e.email},S(e.email),9,Ie)]),T("div",Ve,[w(N,{type:e.status},null,8,["type"])])],40,Le))),128)):(v(),C("div",De,[T("span",He,[w(y,{"icon-name":"common-empty-state"})]),T("p",Me,S(t.$t("Looks like you don't have any Contacts now.")),1),w(j,{onClick:h.addContact,link:"","aria-label":t.$t("Add a Contact")},{default:k(()=>[A(S(t.$t("Add a Contact")),1)],void 0,!0),_:1},8,["onClick","aria-label"])]))])]),_:1}),w(F,{body_class:"fcrm_p_12"},{title:k(()=>[T("h4",null,S(t.$t("Active Automations")),1)]),header_action:k(()=>[w(j,{onClick:h.viewAllAutomations,size:"small","aria-label":t.$t("View All")},{default:k(()=>[A(S(t.$t("View All")),1)],void 0,!0),_:1},8,["onClick","aria-label"])]),body:k(()=>[T("div",Be,[m.active_automations.length?(v(!0),C(L,{key:0},$(m.active_automations,(t,a)=>(v(),C("div",{class:"fcrm_dashboard_entity_card__item",key:a,onClick:e=>h.viewAutomation(t),onKeydown:[M(B(e=>h.viewAutomation(t),["prevent"]),["enter"]),M(B(e=>h.viewAutomation(t),["prevent"]),["space"])],role:"button",tabindex:"0"},[T("div",Oe,[T("p",Pe,S(t.title),1),T("span",qe,[e[8]||(e[8]=T("span",{class:"icon"},[T("svg",{width:"16",height:"16",viewBox:"0 0 16 16",fill:"none",xmlns:"http://www.w3.org/2000/svg"},[T("path",{d:"M8.60001 6.19999H13.4L7.40001 15.2V9.79999H3.20001L8.60001 0.799988V6.19999ZM7.40001 7.39999V5.13199L5.31921 8.59999H8.60001V11.2364L11.1578 7.39999H7.40001Z",fill:"var(--fc-secondary-text)"})])],-1)),A(" "+S(h.getTriggerTitle(t.trigger_name)),1)])])],40,Re))),128)):(v(),C("div",Fe,[T("span",je,[w(y,{"icon-name":"common-empty-state"})]),T("p",We,S(t.$t("Looks like you don't have any active automations now.")),1),w(j,{onClick:h.createAutomation,link:"","aria-label":t.$t("Create an Automation")},{default:k(()=>[A(S(t.$t("Create an Automation")),1)],void 0,!0),_:1},8,["onClick","aria-label"])]))])]),_:1})])],void 0,!0),_:1}),w(z,{sm:24,md:8,lg:8},{default:k(()=>[m.onboarding?(v(),x(F,{key:0},{title:k(()=>[T("h4",null,S(t.$t("Getting Started")),1)]),header_action:k(()=>[T("span",Ne,S(t.$t("%s out of %s Done",m.onboarding.completed,m.onboarding.total)),1)]),body:k(()=>[T("div",ze,[(v(!0),C(L,null,$(m.onboarding.steps,(t,e)=>(v(),C("div",{class:I(["fcrm_onboarding_steps--item",{completed_step:t.completed}]),key:e,onClick:e=>h.maybeRouteStep(t)},[T("span",Ge,[t.completed?(v(),x(G,{key:0},{default:k(()=>[w(Z)],void 0,!0),_:1})):V("",!0)]),A(" "+S(t.label),1)],10,Ze))),128))])]),_:1})):V("",!0),m.sales&&m.sales.length?(v(),x(F,{key:1,class:"fcrm_mb_24"},{title:k(()=>[T("h4",null,S(t.$t("Sales")),1)]),body:k(()=>[T("ul",Ue,[(v(!0),C(L,null,$(m.sales,t=>(v(),C("li",{key:t.title},[T("span",{class:"fc_li_title",innerHTML:t.title},null,8,Ke),e[9]||(e[9]=A()),T("span",{class:"fc_li_value",innerHTML:t.content},null,8,Ye)]))),128))])]),_:1})):V("",!0),m.recommendation?(v(),x(F,{key:2,class:"text-center"},{title:k(()=>[T("h4",null,S(m.recommendation.title),1)]),body:k(()=>[T("p",{innerHTML:m.recommendation.description,class:"fcrm_secondary_text small fcrm_mb_16"},null,8,Qe),m.recommendation.learn_more?(v(),C("p",Je,[T("a",{target:"_blank",rel:"noopener",class:"font-medium",href:m.recommendation.learn_more},"Learn more",8,Xe),A(" and "+S(m.recommendation.base_title),1)])):V("",!0),T("a",ta,S(m.recommendation.btn_text),1)]),_:1})):V("",!0),w(U),w(F,{class:"fcrm_quick_links_wrap",body_class:"fcrm_p_12"},{title:k(()=>[T("h4",null,S(t.$t("Quick Links")),1)]),body:k(()=>[T("ul",ea,[(v(!0),C(L,null,$(m.quick_links,(t,a)=>(v(),C("li",{key:a},[T("div",aa,[t.icon?(v(),x(K,{key:0,iconName:t.icon},null,8,["iconName"])):(v(),C("svg",sa,[...e[10]||(e[10]=[T("path",{d:"M14.773 12.652L13.7125 11.59L14.773 10.5295C15.1237 10.1818 15.4023 9.76832 15.5928 9.3127C15.7833 8.85707 15.8819 8.36831 15.883 7.87447C15.8841 7.38063 15.7876 6.89145 15.5991 6.43499C15.4106 5.97854 15.1338 5.56381 14.7846 5.21461C14.4355 4.86541 14.0207 4.58863 13.5643 4.40014C13.1078 4.21166 12.6186 4.11519 12.1248 4.11627C11.6309 4.11735 11.1422 4.21596 10.6866 4.40644C10.2309 4.59693 9.81742 4.87553 9.46976 5.22625L8.40926 6.2875L7.34801 5.227L8.41001 4.1665C9.39462 3.18188 10.7301 2.62873 12.1225 2.62873C13.515 2.62873 14.8504 3.18188 15.835 4.1665C16.8196 5.15112 17.3728 6.48654 17.3728 7.879C17.3728 9.27146 16.8196 10.6069 15.835 11.5915L14.7738 12.652H14.773ZM12.652 14.773L11.5908 15.8335C10.6061 16.8181 9.27072 17.3713 7.87826 17.3713C6.4858 17.3713 5.15037 16.8181 4.16576 15.8335C3.18114 14.8489 2.62799 13.5135 2.62799 12.121C2.62799 10.7285 3.18114 9.39312 4.16576 8.4085L5.22701 7.348L6.28751 8.41L5.22701 9.4705C4.87629 9.81816 4.59769 10.2317 4.4072 10.6873C4.21672 11.1429 4.11811 11.6317 4.11703 12.1255C4.11595 12.6194 4.21242 13.1086 4.4009 13.565C4.58939 14.0215 4.86617 14.4362 5.21537 14.7854C5.56457 15.1346 5.9793 15.4114 6.43575 15.5999C6.89221 15.7883 7.38139 15.8848 7.87523 15.8837C8.36907 15.8826 8.85783 15.784 9.31346 15.5936C9.76908 15.4031 10.1826 15.1245 10.5303 14.7737L11.5908 13.7132L12.652 14.7737V14.773ZM12.121 6.81775L13.1823 7.879L7.87901 13.1815L6.81776 12.121L12.121 6.8185V6.81775Z",fill:"var(--fc-secondary-text)"},null,-1)])]))]),T("a",{target:t.is_external?"_blank":"_self",rel:t.is_external?"noopener noreferrer":null,href:t.url},S(t.title),9,ia)]))),128))])]),_:1}),t.appVars.disable_fluentmail_suggest?V("",!0):(v(),x(F,{key:3},{title:k(()=>[T("h4",null,S(t.$t("The Ultimate SMTP")),1)]),body:k(()=>[T("div",na,[T("div",oa,[w(K,{iconName:"FluentSMTPIcon"})]),T("h3",ra,S(t.$t("Set Up FluentSMTP")),1),T("p",la,S(t.$t("fluentsmtp.new_desc")),1),w(j,{tag:"router-link",to:{name:"smtp_settings"}},{default:k(()=>[e[11]||(e[11]=T("span",{class:"icon"},[T("svg",{width:"20",height:"20",viewBox:"0 0 20 20",fill:"none",xmlns:"http://www.w3.org/2000/svg"},[T("path",{d:"M16 6.4285L10.054 11.7535L4 6.412V15.25H11.5V16.75H3.25C3.05109 16.75 2.86032 16.671 2.71967 16.5303C2.57902 16.3897 2.5 16.1989 2.5 16V4C2.5 3.80109 2.57902 3.61032 2.71967 3.46967C2.86032 3.32902 3.05109 3.25 3.25 3.25H16.75C16.9489 3.25 17.1397 3.32902 17.2803 3.46967C17.421 3.61032 17.5 3.80109 17.5 4V10.75H16V6.4285ZM15.6257 4.75H4.38325L10.0457 9.7465L15.6265 4.75H15.6257ZM13.7875 15.661C13.7373 15.3893 13.7373 15.1107 13.7875 14.839L13.027 14.3995L13.777 13.1005L14.5375 13.54C14.746 13.3615 14.9867 13.2213 15.25 13.1275V12.25H16.75V13.1275C17.0132 13.2213 17.254 13.3615 17.4625 13.54L18.223 13.1005L18.973 14.3995L18.2125 14.839C18.2627 15.1107 18.2627 15.3893 18.2125 15.661L18.973 16.1005L18.223 17.3995L17.4625 16.96C17.2524 17.1402 17.0109 17.28 16.75 17.3725V18.25H15.25V17.3725C14.9891 17.28 14.7476 17.1402 14.5375 16.96L13.777 17.3995L13.027 16.1005L13.7875 15.661ZM16 16C16.1989 16 16.3897 15.921 16.5303 15.7803C16.671 15.6397 16.75 15.4489 16.75 15.25C16.75 15.0511 16.671 14.8603 16.5303 14.7197C16.3897 14.579 16.1989 14.5 16 14.5C15.8011 14.5 15.6103 14.579 15.4697 14.7197C15.329 14.8603 15.25 15.0511 15.25 15.25C15.25 15.4489 15.329 15.6397 15.4697 15.7803C15.6103 15.921 15.8011 16 16 16Z",fill:"var(--fc-secondary-text)"})])],-1)),A(" "+S(t.$t("Das_View_ESSS")),1)],void 0,!0),_:1})])]),_:1})),m.ff_config.is_installed?t.has_campaign_pro?V("",!0):(v(),x(F,{key:5,class:"text-center"},{title:k(()=>[T("h4",null,"Hi "+S(t.appVars.auth.first_name)+" "+S(t.appVars.auth.last_name)+",",1)]),body:k(()=>[e[13]||(e[13]=T("p",{class:"fcrm_secondary_text small fcrm_mb_16"},[A("Do more with "),T("b",{class:"fcrm_primary_text small"},"FluentCRM Pro"),A(" by using more integrations, advanced automations, sequence emails and in-detailed analytics.")],-1)),T("a",ua,S(t.$t("Upgrade to Pro")),1)]),_:1})):(v(),x(F,{key:4},{title:k(()=>[T("h4",null,S(t.$t("Grow Your Audience")),1)]),body:k(()=>[T("div",ca,[T("div",da,[w(K,{iconName:"FluentFormIcon"})]),T("h3",ma,S(t.$t("Looks like you did not install FluentForms yet !!!")),1),T("p",ha,S(t.$t("quick_links.ff_desc")),1),T("div",_a,[w(j,{onClick:e[1]||(e[1]=t=>h.installFF())},{default:k(()=>[e[12]||(e[12]=T("span",{class:"icon"},[T("svg",{width:"20",height:"20",viewBox:"0 0 20 20",fill:"none",xmlns:"http://www.w3.org/2000/svg"},[T("path",{d:"M10.75 8.5H14.5L10 13L5.5 8.5H9.25V3.25H10.75V8.5ZM4 15.25H16V10H17.5V16C17.5 16.1989 17.421 16.3897 17.2803 16.5303C17.1397 16.671 16.9489 16.75 16.75 16.75H3.25C3.05109 16.75 2.86032 16.671 2.71967 16.5303C2.57902 16.3897 2.5 16.1989 2.5 16V10H4V15.25Z",fill:"var(--fc-secondary-text)"})])],-1)),A(" "+S(t.$t("Activate Fluent Forms")),1)],void 0,!0),_:1})])])]),_:1})),m.system_tips?(v(),x(F,{key:6,class:"fcrm_system_tips_wrap"},{title:k(()=>[T("h4",null,S(m.system_tips.title),1)]),body:k(()=>[T("div",{innerHTML:m.system_tips.body},null,8,fa)]),_:1})):V("",!0),m.showReviewWidget?(v(),x(F,{key:7,"no-body-padding":!0},{body:k(()=>[T("div",pa,[w(j,{onClick:h.closeReviewWidget,class:"close_card"},{default:k(()=>[T("span",ga,[w(y,{iconName:"close"})])],void 0,!0),_:1},8,["onClick"]),T("div",ba,S(t.$t("Help Us Grow"))+" 🚀 ",1),T("div",ya,[T("p",null,S(t.$t("Share your experience with a quick review on the WordPress directory.")),1),T("a",va,[T("span",null,[T("span",Ca,[w(y,{iconName:"star"})]),A(" "+S(t.$t("Write a Review")),1)])])])])]),_:1})):V("",!0)],void 0,!0),_:1})],void 0),_:1})],4),w(Q,{modelValue:m.isCongratsPopoverOpen,"onUpdate:modelValue":e[5]||(e[5]=t=>m.isCongratsPopoverOpen=t),class:"fcrm_onboarding_complete_dialog",width:"440px","show-close":!1,"close-on-click-modal":!0,"append-to-body":!0,"modal-class":"fcrm_onboarding_complete_dialog_mask"},{default:k(()=>[T("div",wa,[T("img",{src:m.logoIcon,alt:"FluentCRM"},null,8,xa),T("h3",null,S(t.$t("You're All Set!"))+" 🥳",1),T("p",null,S(t.$t("You are all set up and ready to start. Here are a few quick things you can do to get started.")),1),T("div",ka,[T("div",{class:"fcrm_onboarding--quick-actions-item d-flex items-center gap-12",onClick:e[2]||(e[2]=e=>t.$router.push({name:"subscribers"}))},[T("div",La,[w(y,{"icon-name":"contacts"})]),T("div",$a,[T("div",Ta,S(t.$t("Create a contact")),1),T("div",Sa,S(t.$t("Create your first contact.")),1)]),T("div",Ea,[w(y,{"icon-name":"chevron-right"})])]),T("div",{class:"fcrm_onboarding--quick-actions-item d-flex items-center gap-12",onClick:e[3]||(e[3]=e=>t.$router.push({name:"campaigns"}))},[T("div",Aa,[w(y,{"icon-name":"campaigns"})]),T("div",Ia,[T("div",Va,S(t.$t("Create a campaign")),1),T("div",Da,S(t.$t("Design and send your first email.")),1)]),T("div",Ha,[w(y,{"icon-name":"chevron-right"})])])]),w(j,{type:"primary",onClick:e[4]||(e[4]=t=>m.isCongratsPopoverOpen=!1),class:"w-full"},{default:k(()=>[A(S(t.$t("Go to My Dashboard")),1)],void 0,!0),_:1})])],void 0),_:1},8,["modelValue"])])}],["__scopeId","data-v-523b0015"]]);export{Ma as default}; diff --git a/wp-content/plugins/fluent-crm/assets/admin/Modules/Dashboard/NoPermission.js b/wp-content/plugins/fluent-crm/assets/admin/Modules/Dashboard/NoPermission.js new file mode 100644 index 0000000..3c743db --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/admin/Modules/Dashboard/NoPermission.js @@ -0,0 +1 @@ +import{W as s,X as e,Z as r,aa as t}from"../../../vendor.js?ver=3.1.8";import{_ as n}from"../../../fc-bits-ui.js?ver=3.1.8";import"../../../vendor-element-plus.js?ver=3.1.8";const i={class:"fcrm_no_permission text-align-center"};const o=n({},[["render",function(n,o){return s(),e("div",i,[r("h3",null,t(n.$t("permission.title")),1),r("p",null,t(n.$t("permission.desc")),1)])}]]);export{o as default}; diff --git a/wp-content/plugins/fluent-crm/assets/admin/Modules/Documentation/Docs.js b/wp-content/plugins/fluent-crm/assets/admin/Modules/Documentation/Docs.js new file mode 100644 index 0000000..fe8d82d --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/admin/Modules/Documentation/Docs.js @@ -0,0 +1 @@ +import{e,k as t,ay as s}from"../../../vendor-element-plus.js?ver=3.1.8";import{aQ as c,W as a,X as i,Z as d,ab as o,a5 as n,a6 as r,Y as l,aa as h,a9 as _,ax as u,J as m,az as g,c7 as f}from"../../../vendor.js?ver=3.1.8";import{_ as p,I as x}from"../../../fc-bits-ui.js?ver=3.1.8";import{B as k}from"../../../BaseCard.js?ver=3.1.8";const I={class:"fcrm_docs_wrapper"},b={class:"fcrm_max_w_800"},v={class:"fcrm_docs_section_header_input"},y={class:"icon"},w={class:"fcrm_docs_section_content"},D={href:"https://fluentcrm.com/docs"},$={href:"https://wpmanageninja.com/support-tickets/"},T={class:"fcrm_docs_hint"},L={target:"_blank",rel:"noopener",href:"https://www.facebook.com/groups/fluentcrm"},C={key:0},H=["href"],M=["innerHTML"],S={class:"simple_dic"},j=["href"],B=["innerHTML"],E={key:1,class:"fcrm_docs_lists"},N={key:0},P=["href"],V=["onClick","innerHTML"],A={key:1},R=["innerHTML"],W=["href"],Y=["onClick","innerHTML"];const z=p({name:"Documentations",components:{Icons:x,BaseCard:k},data:()=>({search:"",fetching:!1,docs:[],utl_param:"?utm_source=wp&utm_medium=doc&utm_campaign=doc",reading_doc_status:!1,reading_doc:{},fuseDocs:null}),computed:{doc_cats(){if(!this.docs.length)return[];const e={item_3:{label:this.$t("Getting Started With Audience"),docs:[]},item_7:{label:this.$t("Grow Your Audience"),docs:[]},item_4:{label:this.$t("Email Campaign"),docs:[]},item_5:{label:this.$t("Automation Funnels"),docs:[]}};return this.each(this.docs,t=>{const s="item_"+t.category.value;e[s]||(e[s]={label:t.category.label,cat_id:t.category.value,docs:[]}),e[s].docs.push(t)}),Object.values(e)},search_items(){if(!this.search||!this.docs.length||!this.fuseDocs)return[];return this.fuseDocs.search(this.search).map(e=>e.item)}},methods:{openSearch(){},fetchDocs(){this.fetching=!0,this.$get("docs").then(e=>{this.docs=e.docs,this.fuseDocs=new f(this.docs,{keys:["title","content"]})}).catch(e=>{this.handleError(e)}).finally(()=>{this.fetching=!1})},readDoc(e,t,s,c=!1){const a=document.getElementById("doc_search_end");a&&a.scrollIntoView(),this.reading_doc={doc:e,docIndex:t,catIndex:s,isSearch:c},this.reading_doc_status=!0},goToNext(){if(this.reading_doc.isSearch)return this.search_items[this.reading_doc.docIndex+1]?this.readDoc(this.search_items[this.reading_doc.docIndex+1],this.reading_doc.docIndex+1,0,!0):(this.reading_doc_status=!1,this.search=""),!1;this.doc_cats[this.reading_doc.catIndex].docs[this.reading_doc.docIndex+1]?this.readDoc(this.doc_cats[this.reading_doc.catIndex].docs[this.reading_doc.docIndex+1],this.reading_doc.docIndex+1,this.reading_doc.catIndex):this.doc_cats[this.reading_doc.catIndex+1]&&this.doc_cats[this.reading_doc.catIndex+1].docs[0]?this.readDoc(this.doc_cats[this.reading_doc.catIndex+1].docs[0],0,this.reading_doc.catIndex+1):this.reading_doc_status=!1},goToPrev(){if(this.reading_doc.isSearch)return this.search_items[this.reading_doc.docIndex-1]?this.readDoc(this.search_items[this.reading_doc.docIndex-1],this.reading_doc.docIndex-1,0,!0):(this.reading_doc_status=!1,this.search=""),!1;if(this.doc_cats[this.reading_doc.catIndex].docs[this.reading_doc.docIndex-1])this.readDoc(this.doc_cats[this.reading_doc.catIndex].docs[this.reading_doc.docIndex-1],this.reading_doc.docIndex-1,this.reading_doc.catIndex);else if(this.doc_cats[this.reading_doc.catIndex-1]&&this.doc_cats[this.reading_doc.catIndex-1].docs.length){const e=this.reading_doc.catIndex-1,t=this.doc_cats[e].docs.length-1;this.readDoc(this.doc_cats[e].docs[t],t,e)}else this.reading_doc_status=!1}},mounted(){this.fetchDocs()}},[["render",function(f,p,x,k,z,G){const J=c("Icons"),q=t,F=e,O=c("base-card"),Q=s;return a(),i("div",I,[d("div",b,[o(O,null,{body:n(()=>[d("div",v,[r((a(),l(F,{clearable:"",disabled:z.fetching,size:"large",modelValue:z.search,"onUpdate:modelValue":p[1]||(p[1]=e=>z.search=e),placeholder:f.$t("Search Type and Enter...")},{prefix:n(()=>[o(q,{onClick:p[0]||(p[0]=e=>z.reading_doc_status=!1)},{default:n(()=>[d("span",y,[o(J,{"icon-name":"search"})])],void 0,!0),_:1})]),_:1},8,["disabled","modelValue","placeholder"])),[[Q,z.fetching]])]),d("div",w,[d("h1",null,h(f.$t("How can we help you?")),1),d("p",null,[_(h(f.$t("Please view the"))+" ",1),d("a",D,h(f.$t("documentation")),1),_(" "+h(f.$t("still_cant_find_the_answer"))+" ",1),d("a",$,h(f.$t("open a support ticket")),1),_(" "+h(f.$t("and we will be happy to answer your questions and assist you with any problems."))+". ",1)]),d("p",T,[_(h(f.$t("Want to discuss something with users like you?"))+" ",1),d("a",L,h(f.$t("Join our facebook community")),1)])])]),_:1}),z.reading_doc_status?(a(),i("div",C,[o(O,{class:"fcrm_reading_doc_card"},{title:n(()=>[d("h4",null,[d("a",{class:"external-link d-flex items-center gap-4",rel:"noopener",target:"_blank",href:z.reading_doc.doc.link+z.utl_param},[d("span",{innerHTML:z.reading_doc.doc.title},null,8,M),o(J,{"icon-name":"externalLink"})],8,H)])]),body:n(()=>[d("div",S,[p[6]||(p[6]=_(" You are reading the simple version of this document. ",-1)),d("a",{target:"_blank",rel:"noopener",href:z.reading_doc.doc.link+z.utl_param},"To read the doc on our site click here",8,j),p[7]||(p[7]=_(". To view all doc index ",-1)),d("a",{onClick:p[2]||(p[2]=u(e=>z.reading_doc_status=!1,["prevent"])),href:"#"},[...p[5]||(p[5]=[d("b",null,"click here",-1)])])]),d("div",{innerHTML:z.reading_doc.doc.content,class:"reading_doc_body"},null,8,B)]),footer:n(()=>[o(q,{onClick:p[3]||(p[3]=e=>G.goToPrev())},{default:n(()=>[_(h(f.$t("Read Previous Documentation")),1)],void 0,!0),_:1}),o(q,{onClick:p[4]||(p[4]=e=>G.goToNext())},{default:n(()=>[_(h(f.$t("Read Next Documentation")),1)],void 0,!0),_:1})]),_:1})])):(a(),i("div",E,[p[8]||(p[8]=d("div",{id:"doc_search_end"},null,-1)),z.search?(a(),l(O,{key:0,class:"fcrm_docs_list_card"},{title:n(()=>[d("h4",null,h(f.$t("Search Results")),1)]),body:n(()=>[G.search_items.length?(a(),i("ul",N,[(a(!0),i(m,null,g(G.search_items,(e,t)=>(a(),i("li",{key:e.id},[d("a",{class:"external-link",rel:"noopener",target:"_blank",href:e.link+z.utl_param},[o(J,{"icon-name":"externalLink"})],8,P),d("span",{class:"doc-title cursor_pointer",onClick:s=>G.readDoc(e,t,0,!0),innerHTML:e.title},null,8,V)]))),128))])):(a(),i("div",A,h(f.$t("No results found")),1))]),_:1})):(a(!0),i(m,{key:1},g(G.doc_cats,(e,t)=>(a(),l(O,{class:"fcrm_docs_list_card",key:t},{title:n(()=>[d("h4",null,[d("span",{innerHTML:e.label},null,8,R)])]),body:n(()=>[d("ul",null,[(a(!0),i(m,null,g(e.docs,(e,s)=>(a(),i("li",{key:e.id},[d("a",{class:"external-link",rel:"noopener",target:"_blank",href:e.link+z.utl_param},[o(J,{"icon-name":"externalLink"})],8,W),d("span",{class:"doc-title cursor_pointer",onClick:c=>G.readDoc(e,s,t),innerHTML:e.title},null,8,Y)]))),128))])]),_:2},1024))),128))]))])])}]]);export{z as default}; diff --git a/wp-content/plugins/fluent-crm/assets/admin/Modules/Email/AllEmails.js b/wp-content/plugins/fluent-crm/assets/admin/Modules/Email/AllEmails.js new file mode 100644 index 0000000..989ada6 --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/admin/Modules/Email/AllEmails.js @@ -0,0 +1 @@ +import{aI as e,aB as t,aC as a,aH as i,c as l,aJ as s,k as n,ay as r,aK as o,aL as c,e as d,E as m,n as h}from"../../../vendor-element-plus.js?ver=3.1.8";import{W as p,Y as u,a5 as _,ab as f,Z as g,aQ as v,X as b,a9 as y,aa as w,a8 as x,ay as $,a6 as C,J as E,az as S,b2 as k}from"../../../vendor.js?ver=3.1.8";import{P as T}from"../../../PaginationBar.js?ver=3.1.8";import F from"./Campaigns/_components/EmailPreview.js?ver=3.1.8";import{T as A}from"../../../TopNav.js?ver=3.1.8";import{_ as j,I as B,a as P,T as D}from"../../../fc-bits-ui.js?ver=3.1.8";import{P as V}from"../../../PageHeader.js?ver=3.1.8";import{D as z}from"../../../DataTable.js?ver=3.1.8";import{F as I}from"../../../FloatingBulkActionShell.js?ver=3.1.8";import L from"../../../v3app/src/Modules/Contacts/Filter/FilterPopover.js?ver=3.1.8";import R from"../../../v3app/src/Modules/Contacts/Filter/ActiveFiltersBar.js?ver=3.1.8";import{B as H}from"../../../Badge.js?ver=3.1.8";import"../../../PreviewIframeBuilder.js?ver=3.1.8";const K={class:"fcrm_contact_cell"},M={class:"fcrm_contact_info"},N={class:"fcrm_contact_name fcrm_mb_4"},U={class:"fcrm_contact_email"},J={style:{display:"flex","align-items":"center",gap:"6px"}};const Y={class:"fcrm_all_email_activities_page"},Q={class:"fcrm_page_header_top_nav_wrapper"},W={class:"fcrm_page_header_top_nav"},X={class:"fcrm-layout-width"},Z={key:0},q={class:"icon"},G={class:"fcrm_empty_state"},O={class:"fcrm_empty_state_text"},ee=["title","src","alt"],te={class:"fcrm_contact_info"},ae={class:"fcrm_contact_name"},ie={class:"fcrm_contact_email"},le={key:1},se={key:0},ne={key:1},re=["title"],oe={key:0,class:"fcrm_table_body_actions justify-center"},ce={class:"icon"},de={class:"icon"},me={class:"fcrm_bulk_action_bar"},he={class:"fcrm_bulk_action_left"},pe={class:"fc_bulk_selection_count"},ue={class:"icon"};const _e=j({name:"AllEmails",components:{Badge:H,ActiveFiltersBar:R,AllEmailsLoader:j({name:"AllEmailsLoader"},[["render",function(l,s,n,r,o,c){const d=a,m=t,h=e,v=i;return p(),u(v,{style:{width:"100%"},data:[1,2,3,4,5,6,7,8]},{default:_(()=>[f(h,{type:"selection",width:"55",fixed:""},{default:_(()=>[f(m,{animated:""},{template:_(()=>[f(d,{variant:"button",style:{width:"20px",height:"20px","border-radius":"6px"}})]),_:1})],void 0,!0),_:1}),f(h,{label:l.$t("Contact"),width:"240"},{default:_(()=>[g("div",K,[f(m,{animated:"",style:{width:"36px",flex:"none"}},{template:_(()=>[f(d,{variant:"circle",style:{width:"36px",height:"36px","border-radius":"50%"}})]),_:1}),g("div",M,[g("div",N,[f(m,{animated:""},{template:_(()=>[f(d,{variant:"text",style:{width:"100px",height:"17px","border-radius":"6px"}})]),_:1})]),g("div",U,[f(m,{animated:""},{template:_(()=>[f(d,{variant:"text",style:{width:"60px",height:"17px","border-radius":"6px"}})]),_:1})])])])],void 0,!0),_:1},8,["label"]),f(h,{label:l.$t("Subject")},{default:_(()=>[f(m,{animated:""},{template:_(()=>[f(d,{variant:"text",style:{width:"100px",height:"17px","border-radius":"6px"}})]),_:1})],void 0,!0),_:1},8,["label"]),f(h,{label:l.$t("Source")},{default:_(()=>[f(m,{animated:""},{template:_(()=>[f(d,{variant:"text",style:{width:"100px",height:"17px","border-radius":"6px"}})]),_:1})],void 0,!0),_:1},8,["label"]),f(h,{width:"180",label:l.$t("Type")},{default:_(()=>[f(m,{animated:""},{template:_(()=>[f(d,{variant:"text",style:{width:"120px",height:"17px","border-radius":"6px"}})]),_:1})],void 0,!0),_:1},8,["label"]),f(h,{width:"120",label:l.$t("Status")},{default:_(()=>[f(m,{animated:""},{template:_(()=>[f(d,{variant:"text",style:{width:"80px",height:"17px","border-radius":"6px"}})]),_:1})],void 0,!0),_:1},8,["label"]),f(h,{width:"180",label:l.$t("Sending Time")},{default:_(()=>[f(m,{animated:""},{template:_(()=>[f(d,{variant:"text",style:{width:"130px",height:"17px","border-radius":"6px"}})]),_:1})],void 0,!0),_:1},8,["label"]),f(h,{label:l.$t("Preview"),fixed:"right",width:"150",align:"center"},{default:_(()=>[f(m,{animated:""},{template:_(()=>[g("div",J,[f(d,{variant:"text",style:{width:"60px",height:"24px","border-radius":"6px"}}),f(d,{variant:"text",style:{width:"60px",height:"24px","border-radius":"6px"}})])]),_:1})],void 0,!0),_:1},8,["label"])],void 0),_:1})}]]),FilterPopover:L,Icons:B,PageHeader:V,TopNav:A,PaginationBar:T,EmailPreview:F,DataTable:z,FloatingBulkActionShell:I,Close:l},data:()=>({emails:[],pagination:{total:0,per_page:10,current_page:1},loading:!0,preview:{id:null,isVisible:!1},resending:!1,selections:[],deleting:!1,statuses:null,selectStatus:"",search:"",types:[],selectedFilters:{types:[]},current_mode:"system"===D.getCurrentTheme()?D.getSystemTheme():D.getCurrentTheme()}),methods:{fetchEmails(){this.loading=!0,this.storage.set("all_emails_perpage",this.pagination.per_page);const e={per_page:this.pagination.per_page,page:this.pagination.current_page,status:this.selectStatus,search:this.search,types:this.selectedFilters.types};this.$get("reports/emails",e).then(e=>{this.emails=e.emails.data,e.statuses&&(this.statuses=e.statuses),e.types&&(this.types=e.types),this.pagination.total=e.emails.total}).catch(e=>{this.handleError(e)}).finally(()=>{this.loading=!1})},handleStatusChange(){this.pagination.current_page=1,this.fetchEmails()},handleSearch(){this.pagination.current_page=1,this.fetchEmails()},handleFilterApply(e){this.selectedFilters.types=e.types||[],this.pagination.current_page=1,this.fetchEmails()},handleFilterBarChange(e){this.selectedFilters.types=e.types||[],this.pagination.current_page=1,this.fetchEmails()},confirmResendEmail(e){h.confirm(this.$t("Are you sure you want to resend this email? The contact will receive the email again."),this.$t("Resend Email"),{confirmButtonText:this.$t("Resend"),cancelButtonText:this.$t("Cancel"),type:"warning"}).then(()=>{this.resendEmail(e)}).catch(()=>{})},resendEmail(e){if(!this.has_campaign_pro)return this.$notify.error(this.$t("_Ca_Please_utptutf")),!1;this.resending=!0,this.$post(`campaigns-pro/${e.campaign_id}/resend-emails`,{email_ids:[e.id]}).then(e=>{this.$notify.success(e.message),this.fetchEmails()}).catch(e=>{this.handleError(e)}).finally(()=>{this.resending=!1})},previewEmail(e){this.preview.id=e,this.preview.isVisible=!0},confirmAndDeleteSelected(){h.confirm(this.$t("Are you sure you want to delete the selected emails?"),this.$t("Delete Emails"),{confirmButtonText:this.$t("Delete"),cancelButtonText:this.$t("Cancel"),type:"warning"}).then(()=>{this.deleteSelected()}).catch(()=>{})},deleteSelected(){this.deleting=!0;const e=this.selections.map(e=>e.id);this.$del("reports/emails",{email_ids:e}).then(e=>{this.selections=[],this.$notify.success(e.message),this.fetchEmails()}).catch(e=>{this.handleError(e)}).finally(()=>{this.deleting=!1})},handleSelectionChange(e){this.selections=e},clearEmailSelection(){this.$refs.emailsTable&&this.$refs.emailsTable.clearSelection(),this.selections=[]},onThemeChanged(e){var t;this.current_mode=(null==(t=e.detail)?void 0:t.effective)||("system"===D.getCurrentTheme()?D.getSystemTheme():D.getCurrentTheme())}},mounted(){window.addEventListener(P,this.onThemeChanged),this.pagination.per_page=parseInt(this.storage.get("all_emails_perpage",10),10)||10,this.fetchEmails(),this.changeTitle(this.$t("All Emails"))},beforeUnmount(){window.removeEventListener(P,this.onThemeChanged)}},[["render",function(t,a,l,h,T,F){const A=v("TopNav"),j=v("page-header"),B=v("Icons"),P=d,D=c,V=o,z=v("filter-popover"),I=v("active-filters-bar"),L=v("AllEmailsLoader"),R=v("icons"),H=e,K=v("router-link"),M=v("Badge"),N=n,U=s,J=i,_e=v("pagination-bar"),fe=v("data-table"),ge=v("Close"),ve=m,be=v("floating-bulk-action-shell"),ye=v("email-preview"),we=r;return p(),b("div",Y,[g("div",Q,[g("div",W,[f(A)])]),g("div",X,[f(j,null,{title:_(()=>[y(w(t.$t("All Email Activities"))+" ",1),T.pagination.total?(p(),b("small",Z,"("+w(t.formatMoney(T.pagination.total))+")",1)):x("",!0)]),_:1}),f(fe,{"has-selection":!1},$({"header-left":_(()=>[f(P,{clearable:"",size:"small",modelValue:T.search,"onUpdate:modelValue":a[0]||(a[0]=e=>T.search=e),onClear:F.handleSearch,onKeyup:k(F.handleSearch,["enter"]),placeholder:t.$t("Search by subject, source or email...")},{prefix:_(()=>[g("span",q,[f(B,{"icon-name":"search"})])]),_:1},8,["modelValue","onClear","onKeyup","placeholder"])]),"header-actions":_(()=>[T.statuses?(p(),u(V,{key:0,placeholder:t.$t("All"),onChange:F.handleStatusChange,modelValue:T.selectStatus,"onUpdate:modelValue":a[1]||(a[1]=e=>T.selectStatus=e),style:{"min-width":"80px"},size:"small"},{default:_(()=>[f(D,{value:"",label:t.$t("All")},null,8,["label"]),(p(!0),b(E,null,S(T.statuses,(e,a)=>(p(),u(D,{key:a,label:t.ucFirst(a)+" ("+e+")",value:a},null,8,["label","value"]))),128))],void 0,!0),_:1},8,["placeholder","onChange","modelValue"])):x("",!0),f(z,{options:{types:T.types},"selected-filters":T.selectedFilters,onApply:F.handleFilterApply},null,8,["options","selected-filters","onApply"])]),"active-filters":_(()=>[f(I,{"selected-filters":T.selectedFilters,options:{types:T.types},onFilterChange:F.handleFilterBarChange,plus_filter_icon:!0},null,8,["selected-filters","options","onFilterChange"])]),table:_(()=>[T.loading?(p(),u(L,{key:0})):C((p(),u(J,{key:1,ref:"emailsTable",border:"",stripe:"",data:T.emails,style:{width:"100%"},onSelectionChange:F.handleSelectionChange},{empty:_(()=>[g("div",G,[f(R,{"icon-name":"common-empty-state"}),g("div",O,[g("span",null,w(t.$t("You haven't sent or scheduled any email logs. Start a campaign to see activities here.")),1)])])]),default:_(()=>[f(H,{type:"selection",width:"55",fixed:""}),f(H,{label:t.$t("Contact"),width:"240"},{default:_(e=>[e.row.subscriber?(p(),u(K,{key:0,class:"fcrm_contact_cell",to:{name:"subscriber",params:{id:e.row.subscriber_id}}},{default:_(()=>[g("img",{title:t.$t("Contact ID: %s",e.row.subscriber_id),class:"fcrm_contact_photo",src:e.row.subscriber.photo,alt:e.row.subscriber.full_name},null,8,ee),g("div",te,[g("div",ae,w(e.row.subscriber.full_name),1),g("div",ie,w(e.row.subscriber.email),1)])],void 0,!0),_:2},1032,["to"])):(p(),b("span",le,w(e.row.mobile_number),1))]),_:1},8,["label"]),f(H,{label:t.$t("Subject")},{default:_(e=>[g("span",null,w(e.row.email_subject),1)]),_:1},8,["label"]),f(H,{label:t.$t("Source")},{default:_(e=>[e.row.campaign?(p(),b("span",se,w(e.row.campaign.title),1)):(p(),b("span",ne,w(t.$t("n/a")),1))]),_:1},8,["label"]),f(H,{width:"180",label:t.$t("Type")},{default:_(e=>[g("span",null,w(e.row.email_type_label),1)]),_:1},8,["label"]),f(H,{width:"120",label:t.$t("Status")},{default:_(e=>[f(M,{type:e.row.status},null,8,["type"])]),_:1},8,["label"]),f(H,{width:"180",label:t.$t("Sending Time")},{default:_(e=>[g("span",{title:e.row.scheduled_at},w(t.nsHumanDiffTime(e.row.scheduled_at)),9,re)]),_:1},8,["label"]),f(H,{label:t.$t("Preview"),fixed:"right",width:"150",align:"center"},{default:_(e=>[e.row.subscriber?(p(),b("div",oe,[f(U,{class:"box-item",effect:"dark",content:t.$t("Preview Email"),placement:"top"},{default:_(()=>[f(N,{size:"small",class:"only-icon-btn small",onClick:t=>F.previewEmail(e.row.id),"aria-label":t.$t("Preview Email")},{default:_(()=>[g("span",ce,[f(B,{"icon-name":"eye"})])],void 0,!0),_:1},8,["onClick","aria-label"])],void 0,!0),_:2},1032,["content"]),f(U,{class:"box-item",effect:"dark",content:t.$t("Resend Email"),placement:"top"},{default:_(()=>["sent"!=e.row.status&&"failed"!=e.row.status||!e.row.campaign_id?x("",!0):(p(),u(N,{key:0,size:"small",onClick:t=>F.confirmResendEmail(e.row),class:"only-icon-btn small","aria-label":t.$t("Resend Email")},{default:_(()=>[g("span",de,[f(B,{"icon-name":"resend-email"})])],void 0,!0),_:1},8,["onClick","aria-label"]))],void 0,!0),_:2},1032,["content"])])):x("",!0)]),_:1},8,["label"])],void 0,!0),_:1},8,["data","onSelectionChange"])),[[we,T.resending]])]),_:2},[T.loading||T.resending?void 0:{name:"pagination",fn:_(()=>[f(_e,{pagination:T.pagination,onFetch:F.fetchEmails,extra_sizes:[200,250,300,400,600]},null,8,["pagination","onFetch"])]),key:"0"}]),1024),f(be,{visible:!!T.selections.length,"theme-mode":T.current_mode,"custom-layout":!0},{default:_(()=>[g("div",me,[g("div",he,[f(N,{link:"","aria-label":t.$t("Deselect"),onClick:F.clearEmailSelection},{default:_(()=>[f(ve,null,{default:_(()=>[f(ge)],void 0,!0),_:1})],void 0,!0),_:1},8,["aria-label","onClick"]),g("span",pe,[g("strong",null,w(T.selections.length),1),y(" "+w(t.$t("selected")),1)]),a[2]||(a[2]=g("div",{class:"fcrm_bulk_divider"},null,-1)),C((p(),u(N,{disabled:T.deleting,type:"danger",size:"small",plain:"",onClick:F.confirmAndDeleteSelected},{default:_(()=>[g("span",ue,[f(B,{"icon-name":"delete"})]),y(" "+w(t.$t("Delete")),1)],void 0,!0),_:1},8,["disabled","onClick"])),[[we,T.deleting]])])])],void 0),_:1},8,["visible","theme-mode"])]),f(ye,{preview:T.preview},null,8,["preview"])])}]]);export{_e as default}; diff --git a/wp-content/plugins/fluent-crm/assets/admin/Modules/Email/Campaigns/Campaign.js b/wp-content/plugins/fluent-crm/assets/admin/Modules/Email/Campaigns/Campaign.js new file mode 100644 index 0000000..99adf0d --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/admin/Modules/Email/Campaigns/Campaign.js @@ -0,0 +1 @@ +import{ax as e,k as t,ay as i,av as a,D as s,aB as n,aH as r,aI as c,e as l,W as o,a2 as p,E as d,aJ as m,aF as _,aE as g,aY as h,g as u,aZ as v,a_ as f,ap as b}from"../../../../vendor-element-plus.js?ver=3.1.8";import{aQ as y,W as w,X as S,ab as C,a5 as $,Y as T,Z as k,a9 as j,aa as E,a6 as x,a8 as D,ay as R,J as P,az as I,b2 as B,a0 as N,av as V}from"../../../../vendor.js?ver=3.1.8";import{E as O}from"../../../../EmailSubjects.js?ver=3.1.8";import{_ as M,I as A}from"../../../../fc-bits-ui.js?ver=3.1.8";import{E as F}from"../../../../BlockComposer.js?ver=3.1.8";import{B as Y}from"../../../../BaseCard.js?ver=3.1.8";import{R as J}from"../../../../RecipientTaggerForm.js?ver=3.1.8";import{R as z}from"../../../../ReadableRecipientTagger.js?ver=3.1.8";import{S as U}from"../../../../TestEmail.js?ver=3.1.8";import{P as H}from"../../../../PaginationBar.js?ver=3.1.8";import{D as L}from"../../../../DataTable.js?ver=3.1.8";import{g as W}from"../../../../relations.js?ver=3.1.8";import{P as q}from"../../../../PreviewIframeBuilder.js?ver=3.1.8";import{c as K,h as G}from"../../../../data_config.js?ver=3.1.8";import{C as Q}from"../../../../CampaignSubjectLines.js?ver=3.1.8";import{C as Z}from"../../../../Confirm.js?ver=3.1.8";import"../../../../_FormBuilder2.js?ver=3.1.8";import"../../../../PhotoWidget.js?ver=3.1.8";import"../../../../input-popover-dropdown.js?ver=3.1.8";import"../../../../_OptionSelector.js?ver=3.1.8";import"../../../../_AjaxSelector.js?ver=3.1.8";import"../../../../_VerifiedEmailInput.js?ver=3.1.8";import"../../../../_MailerConfig.js?ver=3.1.8";import"../../../../EmailPreview.js?ver=3.1.8";import"../../../../_MergeCodes.js?ver=3.1.8";import"../../../../BuiltinTemplateDrawer.js?ver=3.1.8";import"../../../../PromoCard.js?ver=3.1.8";import"../../../../fc-bits.js?ver=3.1.8";const X={class:"fcrm_campaign_email_subject_settings"};const ee=M({name:"CampaignTemplate",props:["campaign","label_align"],components:{EmailSubjects:O}},[["render",function(t,i,a,s,n,r){const c=y("email-subjects"),l=e;return w(),S("div",X,[C(l,{"label-position":"top",model:a.campaign},{default:$(()=>[C(c,{mailer_settings:!0,multi_subject:!0,label_align:"top",campaign:a.campaign},null,8,["campaign"])],void 0),_:1},8,["model"])])}]]),te={class:"template"};const ie=M({name:"CampaignBodyTemplate",props:["campaign","extra_tags"],emits:["getVisualData","next","prev"],components:{BaseCard:Y,EmailBlockComposer:F},data:()=>({loading:!1}),computed:{hideComposerNext(){const e=(window.fcAdmin&&window.fcAdmin.email_template_designs||{})[this.campaign.design_template];return!(!e||e.use_gutenberg)}},methods:{nextStep(){if(!this.campaign.email_body)return this.$notify.error({title:this.$t("Oops!"),message:this.$t("Cam_Please_peb"),offset:19});this.updateCampaign(e=>{this.$emit("next")})},updateCampaign(e){this.loading=!0;const t=JSON.parse(JSON.stringify(this.campaign));delete t.template;const i={next_step:1,title:t.title,email_subject:t.email_subject,email_pre_header:t.email_pre_header,settings:t.settings,design_template:t.design_template,email_body:t.email_body,template_id:t.template_id,campaign_id:this.campaign.id};t._visual_builder_design&&"visual_builder"==t.design_template&&(i._visual_builder_design_string=JSON.stringify(t._visual_builder_design)),this.$post("campaigns/update-single-campaign",i).then(t=>{e?e(t):this.$notify.success({message:this.$t("Cam_Email_bsu")})}).catch(e=>{this.handleError(e)}).finally(()=>{this.loading=!1})},updateCampaignAjax(e){this.loading=!0;const t=JSON.parse(JSON.stringify(this.campaign));delete t.template;const i={next_step:1,title:t.title,email_subject:t.email_subject,email_pre_header:t.email_pre_header,settings:t.settings,design_template:t.design_template,email_body:t.email_body,template_id:t.template_id};t._visual_builder_design&&"visual_builder"==t.design_template&&(i._visual_builder_design_string=JSON.stringify(t._visual_builder_design)),window.jQuery.post(window.ajaxurl,{action_data:JSON.stringify(i),campaign_id:t.id,action:"fluentcrm_save_campaign_email_body",_nonce:window.fcAdmin.ajax_nonce,query_timestamp:Date.now()}).then(t=>{e?e(t):this.$notify.success({message:this.$t("Cam_Email_bsu")})}).catch(e=>{this.handleError(e.responseJSON)}).always(()=>{this.loading=!1})},maybeUpdateCampaign(){"visual_builder"==this.campaign.design_template?this.$bus.emit("getVisualData",{reference:"save"}):this.updateCampaign()},goBack(){this.$emit("prev")},maybeNextStep(){if("visual_builder"==this.campaign.design_template){const e=this;this.$bus.emit("getVisualData",{callback:function(t){e.nextStep()},reference:"update_only"})}else this.nextStep()},initKeyboardSave(e){(window.navigator.platform.match("Mac")?e.metaKey:e.ctrlKey)&&"s"===e.key&&(e.preventDefault(),this.maybeUpdateCampaign())}},mounted(){document.addEventListener("keydown",this.initKeyboardSave)},beforeDestroy(){document.removeEventListener("keydown",this.initKeyboardSave)}},[["render",function(e,i,a,s,n,r){const c=t,l=y("email-block-composer"),o=y("base-card");return w(),T(o,{body_class:"fcrm_p_0"},{body:$(()=>[k("div",te,[C(l,{onSave:i[2]||(i[2]=e=>r.updateCampaign()),onEditor_next:i[3]||(i[3]=e=>r.maybeNextStep()),onEditor_back:i[4]||(i[4]=e=>r.goBack()),show_audit:!0,extra_tags:a.extra_tags,show_merge:!0,enable_template_save:!0,enable_templates:!0,campaign:a.campaign,use_fullscreen_editor:!1,iframe_nav_mode:"compose",hideBackBtn:!0,"hide-next-btn":r.hideComposerNext},{fc_editor_actions:$(()=>[C(c,{onClick:i[0]||(i[0]=e=>r.maybeUpdateCampaign()),size:"small"},{default:$(()=>[j(E(e.$t("Save")),1)],void 0,!0),_:1}),C(c,{type:"primary",onClick:i[1]||(i[1]=e=>r.maybeNextStep()),size:"small"},{default:$(()=>[j(E(e.$t("Next")),1)],void 0,!0),_:1})]),_:1},8,["extra_tags","campaign","hide-next-btn"])])]),_:1})}]]),ae={class:"recipients"},se={key:1},ne={class:"fcrm_email_camp_recipients_processing_card"},re={class:"fcrm_sms_campaign_view--progress-card"},ce={class:"fcrm_sms_campaign_view--progress-header"},le={class:"fcrm_sms_campaign_view--progress-title"},oe={key:0,class:"fcrm_sms_campaign_view--progress-percent"},pe={class:"fcrm_sms_campaign_view--scheduling-note"},de={key:0,class:"text-align-center"},me={style:{"text-align":"left"}};const _e={class:"icon"},ge=["title","src","alt"],he={class:"fcrm_contact_info"},ue={class:"fcrm_contact_name"},ve={class:"fcrm_contact_email"},fe={class:"d-flex gap-4 flex-wrap"},be=["title"],ye={key:1},we={class:"d-flex gap-4 flex-wrap"},Se={key:1},Ce=["title"];const $e={class:"fcrm_campaign_review_wrapper"},Te={class:"fcrm_campaign_review_row"},ke={class:"fcrm_campaign_review_list"},je={class:"fcrm_campaign_review_item fcrm_campaign_review_item--recipients"},Ee={class:"fcrm_campaign_review_item_header"},xe={class:"fcrm_campaign_review_item_header--title"},De={class:"fcrm_campaign_review_item_header--action"},Re={class:"fcrm_campaign_review_item_body"},Pe={class:"fcrm_campaign_review_item_body_item"},Ie={class:"fcrm_campaign_review_item_body--value"},Be={key:0,class:"fcrm_campaign_review_item_body--recipient-selections"},Ne={class:"fcrm_campaign_review_item fcrm_campaign_review_item--subject"},Ve={class:"fcrm_campaign_review_item_header"},Oe={class:"fcrm_campaign_review_item_header--title"},Me={class:"fcrm_campaign_review_item_header--action"},Ae={class:"fcrm_campaign_review_item_body"},Fe={class:"fcrm_campaign_review_item_body_item"},Ye={class:"fcrm_campaign_review_item_body--label"},Je={class:"fcrm_campaign_review_item_body--value"},ze={class:"fcrm_campaign_review_item_body_item"},Ue={class:"fcrm_campaign_review_item_body--label"},He={class:"fcrm_campaign_review_item_body--value"},Le={class:"fcrm_campaign_review_item fcrm_campaign_review_item--broadcast"},We={class:"fcrm_campaign_review_item_header"},qe={class:"fcrm_campaign_review_item_header--title"},Ke={class:"icon"},Ge={class:"fcrm_campaign_review_item_body"},Qe={class:"fcrm_campaign_review_item_body_item"},Ze={key:0,class:"fcrm_campaign_review_item_body_broadcast--schedule fcrm_pl_24 fcrm_mt_10"},Xe={class:"fcrm_input_hint"},et={key:1,class:"fcrm_campaign_review_item_body_broadcast--range-schedule fcrm_pl_24 fcrm_mt_10"},tt={key:0,class:"fcrm_input_hint"},it=["href"],at={class:"fcrm_input_hint"},st={class:"fcrm_input_hint"},nt={class:"fcrm_campaign_review_email_body"},rt={class:"fcrm_campaign_review_item fcrm_campaign_review_item--email_body"},ct={class:"fcrm_campaign_review_item_header"},lt={class:"fcrm_campaign_review_item_header--title"},ot={class:"fcrm_campaign_review_item_header--action"},pt={class:"fcrm_campaign_review_email_body_preview"},dt={class:"fcrm_preview_device_toggle"},mt={class:"fcrm_device_btn_group"},_t=["title"],gt=["title"],ht=["title"],ut={class:"dialog-footer"};const vt={key:0,class:"fcrm_edit_campaign_page fluentcrm-campaign"},ft={class:"fcrm_page_header_top_nav_wrapper"},bt={class:"fcrm_page_header_top_nav"},yt={key:0,class:"fcrm_inline_editable_input"},wt={key:1,class:"d-flex items-center gap-4"},St=["aria-label"],Ct={class:"fcrm_page_header_top_actions"},$t={class:"fcrm_edit_campaign_steps"},Tt={class:"fcrm_edit_campaign_progress_label"},kt={class:"fcrm_edit_campaign_progress_bar"},jt=["onClick"],Et={class:"fcrm_edit_campaign_steps_wrapper fcrm_sticky_block_composer_page"},xt={key:0,class:"fcrm_compose_editor_step"},Dt={key:1,class:"fcrm_max_w_800"},Rt={key:2,class:"fcrm_max_w_800"};const Pt=M({name:"Campaign",components:{BaseCard:Y,Icons:A,TestEmail:U,Confirm:Z,CampaignTemplate:ee,Recipients:M({name:"Recipients",components:{RecipientTaggerForm:J},props:["campaign","insertingNow","processingError","totalContacts","insertedTotal","progressPercent"],emits:["retry","start-over"]},[["render",function(s,n,r,c,l,o){const p=y("recipient-tagger-form"),d=e,m=a,_=t,g=i;return w(),S("div",ae,[r.insertingNow?(w(),S("div",se,[k("div",ne,[k("div",re,[k("div",ce,[k("span",le,E(s.$t("Processing now...")),1),r.totalContacts?x((w(),S("span",oe,[j(E(r.progressPercent)+"% ("+E(r.insertedTotal)+" / "+E(r.totalContacts)+") ",1)])),[[g,r.insertingNow&&!r.processingError]]):D("",!0)]),r.totalContacts?(w(),T(m,{key:0,"stroke-width":8,color:"#8F6ED6","show-text":!1,percentage:r.progressPercent,class:"fcrm_sms_campaign_view--progress-bar"},null,8,["percentage"])):D("",!0),k("p",pe,E(s.$t("Rec_Please_dnctw")),1)])]),r.processingError?(w(),S("div",de,[k("h3",null,E(s.$t("Rec_Processing_EhMit")),1),C(_,{size:"small",type:"danger",onClick:n[1]||(n[1]=e=>s.$emit("retry"))},{default:$(()=>[j(E(s.$t("Resume")),1)],void 0),_:1}),C(_,{size:"small",type:"primary",onClick:n[2]||(n[2]=e=>s.$emit("start-over"))},{default:$(()=>[j(E(s.$t("StartOver")),1)],void 0),_:1}),k("div",me,[k("h3",null,E(s.$t("Error Details")),1),k("pre",null,E(r.processingError),1)])])):D("",!0)])):(w(),T(d,{key:0},{default:$(()=>[C(p,{modelValue:r.campaign.settings,"onUpdate:modelValue":n[0]||(n[0]=e=>r.campaign.settings=e)},null,8,["modelValue"])],void 0),_:1}))])}]]),CampaignReview:M({name:"CampaignReview",props:{campaign:{type:Object,required:!0},sendingType:{type:String,default:"send_now"},scheduleDateTime:{type:[String,Array],default:""}},emits:["goToStep","send-config-change"],components:{CampaignSubjectLines:Q,ReadableRecipients:z,SendTestEmail:U,ViewSegmentRecipients:M({name:"ViewSegmentRecipient",props:["campaign_id"],components:{Icons:A,DataTable:L,PaginationBar:H,Search:s},data:()=>({subscribers:[],loading:!1,first_loading:!0,pagination:{current_page:1,per_page:10,total:0},search:"",sort_by:"id",sort_type:"desc"}),methods:{fetch(){this.loading=!0,this.storage.set("contact_perpage",this.pagination.per_page),this.$get("campaigns/"+this.campaign_id+"/contacts-by-segment",{search:this.search,per_page:this.pagination.per_page,page:this.pagination.current_page,sort_by:this.sort_by,sort_type:this.sort_type}).then(e=>{this.pagination.total=e.subscribers.total,this.subscribers=e.subscribers.data}).catch(e=>{this.handleError(e)}).finally(()=>{this.loading=!1,this.first_loading=!1})},getRelationsArray:W,handleSortable(e){"descending"===e.order?(this.sort_by=e.prop,this.sort_type="DESC"):(this.sort_by=e.prop,this.sort_type="ASC"),this.fetch()}},mounted(){this.pagination.per_page=parseInt(this.storage.get("contact_perpage",10),10)||10,this.fetch()}},[["render",function(e,t,a,s,o,p){const d=y("Icons"),m=l,_=n,g=y("router-link"),h=c,u=r,v=y("pagination-bar"),f=y("data-table"),b=i;return w(),S("div",null,[C(f,{wrapper_border:!0},R({"header-left":$(()=>[C(m,{onKeyup:B(p.fetch,["enter"]),size:"small",placeholder:e.$t("Type and Enter..."),modelValue:o.search,"onUpdate:modelValue":t[0]||(t[0]=e=>o.search=e),onClear:t[1]||(t[1]=e=>p.fetch())},{prefix:$(()=>[k("span",_e,[C(d,{"icon-name":"search"})])]),_:1},8,["onKeyup","placeholder","modelValue"])]),table:$(()=>[o.first_loading?(w(),T(_,{key:0,style:{padding:"20px"},rows:10})):x((w(),T(u,{key:1,"default-sort":{prop:o.sort_by,order:"DESC"==o.sort_type?"descending":"ascending"},"empty-text":e.$t("No Contacts Found"),data:o.subscribers,border:"",id:"fluentcrm-subscribers-table",style:{width:"100%"},stripe:"",onSortChange:p.handleSortable,ref:"subscribersTable"},{default:$(()=>[C(h,{label:e.$t("Email"),property:"email",width:"220",sortable:"custom"},{default:$(t=>[C(g,{class:"fcrm_contact_cell",to:{name:"subscriber",params:{id:t.row.id}}},{default:$(()=>[k("img",{title:e.$t("Contact ID:")+" "+t.row.id,class:"fcrm_contact_photo",src:t.row.photo,alt:t.row.full_name},null,8,ge),k("div",he,[k("div",ue,E(t.row.full_name),1),k("div",ve,E(t.row.email),1)])],void 0,!0),_:2},1032,["to"])]),_:1},8,["label"]),C(h,{"min-width":"300",label:e.$t("Lists"),property:"lists",sortable:!1},{default:$(e=>[k("div",fe,[p.getRelationsArray(e.row,"lists").length?(w(!0),S(P,{key:0},I(p.getRelationsArray(e.row,"lists"),e=>(w(),S("span",{key:e,class:"fcrm_badge"},[k("span",{title:e},E(e),9,be)]))),128)):(w(),S("span",ye,"--"))])]),_:1},8,["label"]),C(h,{"min-width":"300",label:e.$t("Tags"),property:"tags"},{default:$(e=>[k("div",we,[p.getRelationsArray(e.row,"tags").length?(w(!0),S(P,{key:0},I(p.getRelationsArray(e.row,"tags"),e=>(w(),S("span",{key:e,class:"fcrm_badge"},E(e),1))),128)):(w(),S("span",Se,"--"))])]),_:1},8,["label"]),C(h,{"min-width":"120",label:e.$t("Phone"),property:"phone"},{default:$(e=>[j(E(e.row.phone),1)]),_:1},8,["label"]),C(h,{label:e.$t("Type"),width:"150",property:"contact_type",sortable:"custom"},{default:$(t=>[j(E(e.trans(t.row.contact_type)|e.ucWords),1)]),_:1},8,["label"]),C(h,{label:e.$t("Date Added"),"min-width":"190",property:"created_at",sortable:"custom"},{default:$(t=>[t.row.created_at?(w(),S("span",{key:0,title:t.row.created_at},E(e.nsHumanDiffTime(t.row.created_at)),9,Ce)):D("",!0)]),_:1},8,["label"])],void 0,!0),_:1},8,["default-sort","empty-text","data","onSortChange"])),[[b,o.loading]])]),_:2},[o.first_loading?void 0:{name:"pagination",fn:$(()=>[C(v,{pagination:o.pagination,hide_on_single:!1,extra_sizes:[200,250,300,400,600],onFetch:p.fetch},null,8,["pagination","onFetch"])]),key:"0"}]),1024)])}]]),PreviewIframeBuilder:q,Icons:A,EditPen:p,InfoFilled:o},data(){return{sending_type:this.sendingType,schedule_date_time:this.scheduleDateTime,subscribers_modal:!1,showing_recipients:!1,pickerOptions:G,emailDateRangeConfig:K,show_selections:!1,count:null,previewMode:"desktop"}},computed:{scheduleDefaultDate:()=>new Date,scheduleRangeDefaultValue(){const e=new Date,t=new Date;return t.setDate(t.getDate()+7),[e,t]}},watch:{sendingType(e){e!==this.sending_type&&(this.sending_type=e)},scheduleDateTime(e){JSON.stringify(e)!==JSON.stringify(this.schedule_date_time)&&(this.schedule_date_time=e)},sending_type:{handler(){this.$emit("send-config-change",{sendingType:this.sending_type,scheduleDateTime:this.schedule_date_time})},deep:!0},schedule_date_time:{handler(){this.$emit("send-config-change",{sendingType:this.sending_type,scheduleDateTime:this.schedule_date_time})},deep:!0}},methods:{sendingTypeChanged(){"send_now"===this.sending_type||"schedule"===this.sending_type?this.schedule_date_time="":"range_schedule"===this.sending_type&&(this.schedule_date_time=["",""]),this.$emit("send-config-change",{sendingType:this.sending_type,scheduleDateTime:this.schedule_date_time})},goToStep(e){this.$emit("goToStep",e)},saveThisStep(){this.$post(`campaigns/${this.campaign.id}/step`,{next_step:3})},getCount(){this.$get(`campaigns/${this.campaign.id}/estimated-recipients-count`).then(e=>{this.count=e.estimated_count})}},mounted(){this.saveThisStep(),this.getCount()}},[["render",function(e,a,s,n,r,c){const l=y("EditPen"),o=d,p=t,v=y("readable-recipients"),f=y("campaign-subject-lines"),b=y("InfoFilled"),R=m,P=_,I=g,B=h,V=y("send-test-email"),O=y("Icons"),M=y("preview-iframe-builder"),A=y("view-segment-recipients"),F=u,Y=i;return w(),S("div",$e,[k("div",Te,[k("div",ke,[k("div",je,[k("div",Ee,[k("div",xe,E(e.$t("Recipients")),1),k("div",De,[C(p,{onClick:a[0]||(a[0]=e=>c.goToStep(2)),size:"small",plain:""},{default:$(()=>[C(o,null,{default:$(()=>[C(l)],void 0,!0),_:1}),j(" "+E(e.$t("Edit Recipients")),1)],void 0),_:1})])]),k("div",Re,[k("div",Pe,[x((w(),S("p",{class:"fcrm_campaign_review_item_body--label",onClick:a[1]||(a[1]=e=>r.show_selections=!r.show_selections)},[j(E(e.$t("Total")),1)])),[[Y,null===r.count]]),k("p",Ie,E(r.count||"~0+"),1),r.show_selections?(w(),S("div",Be,[k("h3",null,E(e.$t("Recipient Sections")),1),C(v,{settings:s.campaign.settings},null,8,["settings"]),C(p,{size:"small",type:"primary",onClick:a[2]||(a[2]=e=>r.showing_recipients=!r.showing_recipients)},{default:$(()=>[j(E(e.$t("Show Individual Recipients")),1)],void 0),_:1})])):D("",!0)])])]),k("div",Ne,[k("div",Ve,[k("div",Oe,E(e.$t("Subject")),1),k("div",Me,[C(p,{onClick:a[3]||(a[3]=e=>c.goToStep(1)),size:"small",plain:""},{default:$(()=>[C(o,null,{default:$(()=>[C(l)],void 0,!0),_:1}),j(" "+E(e.$t("Edit Subject")),1)],void 0),_:1})])]),k("div",Ae,[k("div",Fe,[k("p",Ye,E(e.$t("Subject")),1),k("p",Je,[C(f,{campaign:s.campaign},null,8,["campaign"])])]),k("div",ze,[k("p",Ue,E(e.$t("Preheader Text")),1),k("p",He,E(s.campaign.email_pre_header||"--"),1)])])]),k("div",Le,[k("div",We,[k("div",qe,[j(E(e.$t("Cam_Broadcast_Schedu"))+" ",1),C(R,{class:"box-item",effect:"dark",content:e.$t("Cam_If_yteiaYcbten"),placement:"top-start"},{default:$(()=>[k("span",Ke,[C(o,null,{default:$(()=>[C(b)],void 0,!0),_:1})])],void 0),_:1},8,["content"])])]),k("div",Ge,[k("div",Qe,[k("h4",null,E(e.$t("When you send the emails?")),1),C(I,{onChange:a[4]||(a[4]=e=>c.sendingTypeChanged()),modelValue:r.sending_type,"onUpdate:modelValue":a[5]||(a[5]=e=>r.sending_type=e)},{default:$(()=>[C(P,{label:"send_now"},{default:$(()=>[j(E(e.$t("Send the emails right now")),1)],void 0,!0),_:1}),C(P,{label:"schedule"},{default:$(()=>[j(E(e.$t("Schedule the emails")),1)],void 0,!0),_:1}),C(P,{label:"range_schedule"},{default:$(()=>[j(E(e.$t("Schedule_Date_Time_Info")),1)],void 0,!0),_:1})],void 0),_:1},8,["modelValue"]),"schedule"==r.sending_type?(w(),S("div",Ze,[k("h4",null,E(e.$t("Date Time")),1),(w(),T(B,{key:"schedule-dt-"+r.sending_type,"value-format":"YYYY-MM-DD HH:mm:ss",modelValue:r.schedule_date_time,"onUpdate:modelValue":a[6]||(a[6]=e=>r.schedule_date_time=e),required:"",type:"datetime","default-value":c.scheduleDefaultDate,"disabled-date":r.pickerOptions.disabledDate,shortcuts:r.pickerOptions.shortcuts,"popper-class":"fcrm_date_time_picker",placeholder:e.$t("Select date and time")},null,8,["modelValue","default-value","disabled-date","shortcuts","placeholder"])),k("p",Xe,[j(E(e.$t("Cam_Current_ST_oySS"))+": ",1),k("code",null,E(s.campaign.server_time),1)])])):"range_schedule"==r.sending_type?(w(),S("div",et,[k("h4",null,E(e.$t("Select_Date_Time_Alert")),1),(w(),T(B,{key:"schedule-range-"+r.sending_type,disabled:!this.has_campaign_pro,format:"YYYY-MM-DD HH:mm","value-format":"YYYY-MM-DD HH:mm:ss",modelValue:r.schedule_date_time,"onUpdate:modelValue":a[7]||(a[7]=e=>r.schedule_date_time=e),required:"",type:"datetimerange","default-value":c.scheduleRangeDefaultValue,"disabled-date":r.emailDateRangeConfig.disabledDate,placeholder:e.$t("Select date and time")},null,8,["disabled","modelValue","default-value","disabled-date","placeholder"])),this.has_campaign_pro?D("",!0):(w(),S("p",tt,[j(E(e.$t("To use this feature you need FluentCRM Pro."))+" ",1),k("a",{target:"_blank",href:e.appVars.upgrade_url},E(e.$t("Please upgrade.")),9,it)])),k("p",at,E(e.$t("Camp_Notice_About_Time")),1),k("p",st,[j(E(e.$t("Cam_Current_ST_oySS"))+": ",1),k("code",null,E(s.campaign.server_time),1)])])):D("",!0)])])])]),k("div",nt,[k("div",rt,[k("div",ct,[k("div",lt,E(e.$t("Email Body")),1),k("div",ot,[C(p,{onClick:a[8]||(a[8]=e=>c.goToStep(0)),plain:""},{default:$(()=>[C(o,null,{default:$(()=>[C(l)],void 0,!0),_:1}),j(" "+E(e.$t("Edit Email Body")),1)],void 0),_:1}),C(V,{placement:"right",campaign:s.campaign},null,8,["campaign"])])])]),k("div",pt,[k("div",dt,[k("div",mt,[k("button",{type:"button",class:N(["fcrm_device_btn",{active:"desktop"===r.previewMode}]),onClick:a[9]||(a[9]=e=>r.previewMode="desktop"),title:e.$t("Desktop Preview")},[C(O,{"icon-name":"desktop"})],10,_t),k("button",{type:"button",class:N(["fcrm_device_btn",{active:"tablet"===r.previewMode}]),onClick:a[10]||(a[10]=e=>r.previewMode="tablet"),title:e.$t("Tablet Preview")},[C(O,{"icon-name":"tablet"})],10,gt),k("button",{type:"button",class:N(["fcrm_device_btn",{active:"mobile"===r.previewMode}]),onClick:a[11]||(a[11]=e=>r.previewMode="mobile"),title:e.$t("Mobile Preview")},[C(O,{"icon-name":"mobile"})],10,ht)])]),k("div",{class:N(["fc_preview_container","fc_preview_"+r.previewMode])},[C(M,{show_audit:!0,frame_height:"500px",campaign:s.campaign,campaign_id:s.campaign.id},null,8,["campaign","campaign_id"])],2)])])]),C(F,{width:"70%",title:e.$t("Campaign Recipients"),"append-to-body":!0,"close-on-click-modal":!1,modelValue:r.showing_recipients,"onUpdate:modelValue":a[13]||(a[13]=e=>r.showing_recipients=e)},{footer:$(()=>[k("span",ut,[C(p,{onClick:a[12]||(a[12]=e=>r.showing_recipients=!1)},{default:$(()=>[j(E(e.$t("Close")),1)],void 0,!0),_:1})])]),default:$(()=>[r.showing_recipients?(w(),T(A,{key:0,campaign_id:s.campaign.id},null,8,["campaign_id"])):D("",!0)],void 0),_:1},8,["title","modelValue"])])}]]),CampaignBodyComposer:ie},data(){return{ArrowRightBold:V(b),activeStep:Math.min(parseInt(this.$route.query.step,10)||0,3),campaign_id:(String(this.$route.params.id||"").match(/^\d+/)||[""])[0],campaign:null,dialogVisible:!1,dialogTitle:this.$t("Edit Campaign"),loading:!1,steps:[{title:this.$t("Compose"),description:this.$t("Compose Your Email Body")},{title:this.$t("Subject & Settings"),description:this.$t("Email Subject & Details")},{title:this.$t("Recipients"),description:this.$t("Select Email Recipients")},{title:this.$t("Review & Send"),description:this.$t("Cam_Send_osce")}],updating:!1,recipientsInsertingNow:!1,recipientsProcessingError:!1,recipientsInsertingPage:1,recipientsInsertedTotal:0,recipientsTotalContacts:0,recipientsBtnSubscribing:!1,subjectStepSaving:!1,show_title_input:!1,editableTitle:"",reviewCampaignSendingType:"send_now",reviewCampaignScheduleDateTime:"",reviewCampaignBtnSending:!1}},computed:{recipientsProgressPercent(){return this.recipientsTotalContacts?parseInt(this.recipientsInsertedTotal/this.recipientsTotalContacts*100):1},reviewCampaignConfirmMessage(){return"send_now"===this.reviewCampaignSendingType?""+this.$t("Cam_Send_Now_Confirm_Header")+"
"+this.$t("Cam_Send_Now_Message"):""+this.$t("Schedule_Cam_Confirm_Header")+""},reviewCampaignSendButtonLabel(){return"send_now"===this.reviewCampaignSendingType?this.$t("Send Emails Now"):this.$t("Schedule this campaign")}},methods:{changeStep(e){this.activeStep=e,this.$router.push({name:"campaign",query:{step:this.activeStep}})},onCampaignReviewSendConfigChange(e){this.reviewCampaignSendingType=e.sendingType||this.reviewCampaignSendingType,this.reviewCampaignScheduleDateTime=e.scheduleDateTime},targetStep(e,t){t>0&&!this.campaign.title||(2!==t||this.hasConfiguredSubject())&&(3===t&&0===this.campaign.recipients_count||t!==this.activeStep&&(1===this.activeStep&&t>1?this.persistSubjectStep(()=>{this.changeStep(t)}):this.changeStep(t)))},hasConfiguredSubject(){if(this.campaign.subjects&&this.campaign.subjects.length){return this.campaign.subjects.filter(e=>e.key&&e.value&&e.value.trim()).length>=2}return!(!this.campaign.email_subject||!this.campaign.email_subject.trim())},persistSubjectStep(e){if(this.subjectStepSaving)return;this.subjectStepSaving=!0;const t=JSON.parse(JSON.stringify(this.campaign)),i={campaign_id:t.id,next_step:2,update_subjects:!0,title:t.title,email_subject:t.email_subject,email_pre_header:t.email_pre_header,utm_status:t.utm_status,utm_source:t.utm_source,utm_medium:t.utm_medium,utm_campaign:t.utm_campaign,utm_term:t.utm_term,utm_content:t.utm_content,settings:t.settings,subjects:t.subjects||[]};this.$post("campaigns/update-single-campaign",i).then(t=>{(null==t?void 0:t.campaign)&&(this.campaign={...this.campaign,...t.campaign}),e&&e(t)}).catch(e=>{this.handleError(e)}).finally(()=>{this.subjectStepSaving=!1})},next(){0===this.activeStep&&this.unmountBlockEditor(),1!==this.activeStep?this.activeStep<3&&this.changeStep(this.activeStep+1):this.persistSubjectStep(()=>{this.changeStep(2)})},prev(){this.activeStep>0&&this.changeStep(this.activeStep-1)},stepChange(e){this.changeStep(e)},startRecipientsProcess(){this.recipientsInsertingNow=!0;this.validateRecipientsForCampaign()&&this.fetchRecipientsEstimatedContacts()},validateRecipientsForCampaign(){const e=this.campaign.settings,t=e.subscribers.filter(e=>e.list&&e.tag),i=e.excludedSubscribers.filter(e=>e.list&&e.tag);if("list_tag"===e.sending_filter){if(t.length!==e.subscribers.length||i.length&&i.length!==e.excludedSubscribers.length)return this.recipientsInsertingNow=!1,this.$notify.error({title:this.$t("Oops!"),message:this.$t("Recipients.instruction"),offset:19}),!1}else if("dynamic_segment"==e.sending_filter){if(!e.dynamic_segment.uid)return this.recipientsInsertingNow=!1,this.$notify.error({title:this.$t("Oops!"),message:this.$t("Please select the segment"),offset:19}),!1}else if("advanced_filters"==e.sending_filter){let t=!1;if(this.each(e.advanced_filters,e=>{this.isEmptyValue(e)||(t=!0)}),!t)return this.recipientsInsertingNow=!1,this.$notify.error({title:this.$t("Oops!"),message:this.$t("Please select the filters"),offset:19}),!1}const a={subscribers:t,excludedSubscribers:i,sending_filter:e.sending_filter,dynamic_segment:e.dynamic_segment,page:this.recipientsInsertingPage,advanced_filters:JSON.stringify(e.advanced_filters)};return this.recipientsBtnSubscribing=!0,this.recipientsProcessingError=!1,this.$post(`campaigns/${this.campaign.id}/draft-recipients`,a).then(e=>{if("object"!=typeof e)return this.handleError(e),this.recipientsProcessingError=e,void(this.recipientsInsertingNow=!1);e.has_more?(this.recipientsInsertingPage=e.next_page,this.recipientsInsertedTotal=e.count,this.$nextTick(()=>{this.validateRecipientsForCampaign()})):(this.campaign.recipients_count=e.count,this.$notify.success({title:this.$t("Great!"),message:this.$t("Contacts has been attached with this campaign"),offset:19}),this.next(),this.recipientsInsertingNow=!1,this.recipientsInsertingPage=1)}).catch(e=>{this.handleError(e),this.recipientsProcessingError=e||this.$t("Unknown error"),this.recipientsInsertingNow=!1}).finally(()=>{this.recipientsBtnSubscribing=!1}),!0},fetchRecipientsEstimatedContacts(){const e=this.campaign.settings,t=e.subscribers.filter(e=>e.list&&e.tag),i=e.excludedSubscribers.filter(e=>e.list&&e.tag);if("list_tag"===e.sending_filter){if(t.length!==e.subscribers.length||i.length&&i.length!==e.excludedSubscribers.length)return}else if("dynamic_segment"==e.sending_filter){if(!e.dynamic_segment.uid)return}else if("advanced_filters"==e.sending_filter){let t=!1;if(this.each(e.advanced_filters,e=>{this.isEmptyValue(e)||(t=!0)}),!t)return}const a={subscribers:t,excludedSubscribers:i,sending_filter:e.sending_filter,dynamic_segment:e.dynamic_segment,advanced_filters:JSON.stringify(e.advanced_filters)};this.$post("campaigns/estimated-contacts",a).then(e=>{this.recipientsTotalContacts=e.count}).catch(e=>{this.recipientsInsertingNow=!1,this.handleError(e)})},retryRecipientsProcess(){this.recipientsProcessingError=!1,this.validateRecipientsForCampaign()},startOverRecipientsProcess(){this.recipientsProcessingError=!1,this.recipientsInsertingNow=!1,this.recipientsBtnSubscribing=!1},backToCampaigns(){this.activeStep=0,this.$router.push({name:"campaigns",query:{t:(new Date).getTime()}})},fetch(){this.$get(`campaigns/${this.campaign_id}`,{with:["subjects","template"]}).then(e=>{var t;this.campaign=e.campaign,this.editableTitle=(null==(t=e.campaign)?void 0:t.title)||"","draft"!=this.campaign.status&&this.$router.push({name:"campaign-view",params:{id:this.campaign.id}}),this.changeTitle(this.campaign.title+" - Campaign")}).catch(e=>{this.redirectToCampaignsWithWarning(e.message)})},showInputField(){var e;this.editableTitle=(null==(e=this.campaign)?void 0:e.title)||"",this.show_title_input=!0,this.$nextTick(()=>{const e=this.$refs.titleInput,t=Array.isArray(e)?e[0]:e;t&&t.focus&&t.focus()})},cancelInlineTitle(){var e;this.show_title_input=!1,this.editableTitle=(null==(e=this.campaign)?void 0:e.title)||""},saveInlineTitle(){const e=(this.editableTitle||"").trim();e&&(e!==this.campaign.title?this.updateCampaignTitle(e):this.show_title_input=!1)},redirectToCampaignsWithWarning(e){"campaign"===this.$route.name&&this.$messageBox.alert(this.$sanitize(e),this.$t("Oops!"),{center:!0,type:"warning",confirmButtonText:this.$t("View Report"),dangerouslyUseHTMLString:!0,callback:e=>{this.$router.push({name:"campaign-view",params:{id:this.campaign_id},query:{t:(new Date).getTime()}})}})},updateCampaignTitle(e){this.updating=!0,this.$put(`campaigns/${this.campaign_id}/title`,{title:e}).then(t=>{var i;this.campaign.title=(null==(i=null==t?void 0:t.campaign)?void 0:i.title)||e,this.editableTitle=this.campaign.title,this.show_title_input=!1,this.changeTitle(this.campaign.title+" - Campaign"),this.$notify.success(t.message)}).catch(e=>{this.handleError(e)}).finally(()=>{this.updating=!1})},sendReviewCampaign(){const e={};if("schedule"===this.reviewCampaignSendingType){if(!this.reviewCampaignScheduleDateTime)return void this.$notify.error(this.$t("Please select a date and time"));e.scheduled_at=this.reviewCampaignScheduleDateTime,e.sending_type="schedule"}else if("range_schedule"===this.reviewCampaignSendingType){if(!Array.isArray(this.reviewCampaignScheduleDateTime)||!this.reviewCampaignScheduleDateTime[0]||!this.reviewCampaignScheduleDateTime[1])return void this.$notify.error(this.$t("Please select a date and time"));if(!this.has_campaign_pro)return void this.$notify.error(this.$t("You need pro version to use this feature"));e.scheduled_at=this.reviewCampaignScheduleDateTime,e.sending_type="range_schedule"}this.reviewCampaignBtnSending=!0,this.$post(`campaigns/${this.campaign.id}/schedule`,e).then(e=>{this.$notify.success({title:this.$t("Great!"),message:e.message,offset:19}),this.$router.push({name:"campaign-view",params:{id:this.campaign.id}})}).catch(e=>{this.$notify.error(e.message)}).finally(()=>{this.reviewCampaignBtnSending=!1})}},mounted(){this.fetch(),this.changeTitle(this.$t("Campaign"))},beforeRouteLeave(e,t,i){this.unmountBlockEditor(),i()}},[["render",function(e,a,s,n,r,c){const o=v,p=l,d=t,_=y("Icons"),g=f,h=m,u=y("campaign-body-composer"),b=y("test-email"),R=y("campaign-template"),V=y("BaseCard"),O=y("Recipients"),M=y("campaign-review"),A=y("confirm"),F=i;return r.campaign?(w(),S("div",vt,[k("div",ft,[k("div",bt,[C(g,{"separator-icon":r.ArrowRightBold},{default:$(()=>[C(o,{to:{name:"campaigns"}},{default:$(()=>[j(E(e.$t("Email Campaigns")),1)],void 0,!0),_:1}),C(o,{class:"fcrm_funnel_title_editable_wrap"},{default:$(()=>[r.show_title_input?(w(),S("div",yt,[C(p,{ref:"titleInput",modelValue:r.editableTitle,"onUpdate:modelValue":a[0]||(a[0]=e=>r.editableTitle=e),size:"small",placeholder:e.$t("Internal Campaign Title"),onKeyup:[a[1]||(a[1]=B(e=>c.saveInlineTitle(),["enter"])),a[2]||(a[2]=B(e=>c.cancelInlineTitle(),["esc"]))]},null,8,["modelValue","placeholder"]),C(d,{size:"small",type:"primary",loading:r.updating,disabled:r.updating||!r.editableTitle||!r.editableTitle.trim(),onClick:a[3]||(a[3]=e=>c.saveInlineTitle())},{default:$(()=>[j(E(e.$t("Save")),1)],void 0,!0),_:1},8,["loading","disabled"]),C(d,{size:"small",disabled:r.updating,onClick:a[4]||(a[4]=e=>c.cancelInlineTitle())},{default:$(()=>[j(E(e.$t("Cancel")),1)],void 0,!0),_:1},8,["disabled"])])):(w(),S("div",wt,[j(E(r.campaign.title)+" ",1),k("span",{onClick:a[5]||(a[5]=(...e)=>c.showInputField&&c.showInputField(...e)),class:"icon-edit cursor_pointer d-block","aria-label":e.$t("Rename Campaign")},[C(_,{"icon-name":"EditPen",class:"d-block"})],8,St)]))],void 0,!0),_:1})],void 0),_:1},8,["separator-icon"])]),k("div",Ct,[k("div",$t,[k("div",Tt,E(r.activeStep+1)+"/"+E(r.steps.length)+" "+E(e.$t("Completed")),1),k("div",kt,[(w(!0),S(P,null,I(r.steps,(e,t)=>(w(),T(h,{key:t,content:e.title,placement:"top"},{default:$(()=>[k("div",{class:N(["fcrm_edit_campaign_progress_segment",{"is-filled":t<=r.activeStep}]),onClick:e=>c.targetStep(e,t)},null,10,jt)],void 0),_:2},1032,["content"]))),128))])])])]),x((w(),S("div",Et,[0==r.activeStep?(w(),S("div",xt,[C(u,{onNext:a[6]||(a[6]=e=>c.next()),onPrev:a[7]||(a[7]=e=>c.prev()),campaign:r.campaign},null,8,["campaign"])])):D("",!0),1==r.activeStep?(w(),S("div",Dt,[C(V,null,{title:$(()=>[k("h4",null,E(e.$t("Subject & Settings")),1)]),header_action:$(()=>[C(b,{campaign:r.campaign},null,8,["campaign"])]),body:$(()=>[C(R,{campaign:r.campaign},null,8,["campaign"])]),footer:$(()=>[C(d,{disabled:r.subjectStepSaving,onClick:a[8]||(a[8]=e=>c.prev())},{default:$(()=>[j(E(e.$t("Back")),1)],void 0,!0),_:1},8,["disabled"]),C(d,{loading:r.subjectStepSaving,disabled:r.subjectStepSaving,onClick:a[9]||(a[9]=e=>c.next()),type:"primary"},{default:$(()=>[j(E(e.$t("Continue")),1)],void 0,!0),_:1},8,["loading","disabled"])]),_:1})])):D("",!0),2==r.activeStep?(w(),S("div",Rt,[C(V,null,{title:$(()=>[k("h4",null,E(e.$t("Recipients")),1)]),body:$(()=>[C(O,{campaign:r.campaign,"inserting-now":r.recipientsInsertingNow,"processing-error":r.recipientsProcessingError,"total-contacts":r.recipientsTotalContacts,"inserted-total":r.recipientsInsertedTotal,"progress-percent":c.recipientsProgressPercent,onRetry:c.retryRecipientsProcess,onStartOver:c.startOverRecipientsProcess},null,8,["campaign","inserting-now","processing-error","total-contacts","inserted-total","progress-percent","onRetry","onStartOver"])]),footer:$(()=>[C(d,{onClick:a[10]||(a[10]=e=>c.prev())},{default:$(()=>[j(E(e.$t("Back")),1)],void 0,!0),_:1}),C(d,{type:"primary",loading:r.recipientsBtnSubscribing,onClick:a[11]||(a[11]=e=>c.startRecipientsProcess())},{default:$(()=>[j(E(e.$t("Rec_Continue_TNS_aS")),1)],void 0,!0),_:1},8,["loading"])]),_:1})])):D("",!0),3==r.activeStep?(w(),T(V,{key:3},{body:$(()=>[C(M,{onGoToStep:c.stepChange,onSendConfigChange:c.onCampaignReviewSendConfigChange,campaign:r.campaign,"sending-type":r.reviewCampaignSendingType,"schedule-date-time":r.reviewCampaignScheduleDateTime},null,8,["onGoToStep","onSendConfigChange","campaign","sending-type","schedule-date-time"])]),footer:$(()=>[C(d,{onClick:a[12]||(a[12]=e=>c.prev())},{default:$(()=>[j(E(e.$t("Back")),1)],void 0,!0),_:1}),C(A,{placement:"top-start",width:220,message:c.reviewCampaignConfirmMessage,onYes:a[13]||(a[13]=e=>c.sendReviewCampaign())},{reference:$(()=>[x((w(),T(d,{type:"primary"},{default:$(()=>[j(E(c.reviewCampaignSendButtonLabel),1)],void 0,!0),_:1})),[[F,r.reviewCampaignBtnSending]])]),_:1},8,["message"])]),_:1})):D("",!0)])),[[F,r.loading]])])):D("",!0)}]]);export{Pt as default}; diff --git a/wp-content/plugins/fluent-crm/assets/admin/Modules/Email/Campaigns/Campaigns.js b/wp-content/plugins/fluent-crm/assets/admin/Modules/Email/Campaigns/Campaigns.js new file mode 100644 index 0000000..fcd74e6 --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/admin/Modules/Email/Campaigns/Campaigns.js @@ -0,0 +1 @@ +import{S as e,R as t,_ as a}from"../../../../fc-bits.js?ver=3.1.8";import{a6 as s,aj as i,n as l,aL as n,aK as o,ay as r,E as c,k as d,i as p,aG as m,az as h,a7 as u,L as _,c as g,o as f,a5 as b,a4 as y,a3 as C,D as v,j as w,h as k,e as $,aI as S,aJ as F,aQ as D,aH as A,aB as P,aR as x,aS as q,g as L,ax as T,aw as V,aE as B,aF as E,aT as O}from"../../../../vendor-element-plus.js?ver=3.1.8";import{aQ as j,W as I,X as R,Z as M,ab as N,a5 as U,Y as z,a8 as H,J as G,az as Q,$ as Y,aa as J,a6 as Z,a9 as W,_ as K,bU as X,ac as ee,b2 as te,ax as ae,bS as se,bV as ie,bT as le,bW as ne}from"../../../../vendor.js?ver=3.1.8";import{C as oe}from"../../../../Confirm.js?ver=3.1.8";import{P as re}from"../../../../PaginationBar.js?ver=3.1.8";import{I as ce}from"../../../../InlineDoc.js?ver=3.1.8";import{_ as de,I as pe,a as me,T as he}from"../../../../fc-bits-ui.js?ver=3.1.8";import{E as ue}from"../../../../EmailPreview.js?ver=3.1.8";import{T as _e}from"../../../../TopNav.js?ver=3.1.8";import{B as ge}from"../../../../Badge.js?ver=3.1.8";import{P as fe}from"../../../../PageHeader.js?ver=3.1.8";import{F as be}from"../../../../FloatingBulkActionShell.js?ver=3.1.8";import{F as ye}from"../../../../Filterer2.js?ver=3.1.8";import{e as Ce}from"../../../../data_config.js?ver=3.1.8";import{P as ve}from"../../../../PromoCard.js?ver=3.1.8";import"../../../../PreviewIframeBuilder.js?ver=3.1.8";import"../../../../TestEmail.js?ver=3.1.8";import"../../../../CampaignSubjectLines.js?ver=3.1.8";const we={name:"BulkCampaignActions",components:{Check:i,Delete:s},props:{selectedCampaigns:{type:Array,default:()=>[]},options:{type:Object,default:()=>({})},filters:{type:Object,default:()=>({})},allSelected:{type:Boolean,default:!1},theme_mode:{type:String,default:""}},emits:["refetch"],watch:{"select_job.action_name"(e){"apply_labels"!==e&&(this.selectedLabels=[])}},computed:{bulkSelectPopperClass(){return"dark"===this.theme_mode?"fcrm-force-light":"fcrm-dark"},bulkSelectWordbreakPopperClass(){return"dark"===this.theme_mode?"fcrm_select_options_wordbreak fcrm-force-light":"fcrm_select_options_wordbreak fcrm-dark"}},data:()=>({select_job:{action_name:"",selected_options:[]},doing_action:!1,selectedLabels:[]}),methods:{doApplyLabels(){this.selectedLabels.length&&this.doBulkAction("apply_labels")},confirmAndDeleteCampaigns(){l.confirm(this.$t("Are you sure you want to delete the selected campaigns?"),this.$t("Delete Campaigns"),{confirmButtonText:this.$t("Delete"),cancelButtonText:this.$t("Cancel"),type:"warning"}).then(()=>{this.doBulkAction("delete_campaigns")}).catch(()=>{})},doBulkAction(e){const t={action_name:e,labels:"apply_labels"===e?this.selectedLabels:[]};this.allSelected?(t.select_all=!0,t.filters=this.filters):t.campaign_ids=this.selectedCampaigns.map(e=>e.id),this.doing_action=!0,this.$post("campaigns/do-bulk-action",t).then(e=>{this.$notify.success(e.message),this.$emit("refetch"),this.selectedLabels=[],this.select_job.action_name=""}).catch(e=>{this.handleError(e)}).finally(()=>{this.doing_action=!1})}}},ke={class:"fcrm_bulk_action_inline"},$e={class:"fcrm_bulk_wrap"},Se={class:"icon"};const Fe=de(we,[["render",function(e,t,a,s,i,l){const p=n,m=o,h=j("Check"),u=c,_=d,g=j("Delete"),f=r;return I(),R("div",ke,[M("div",$e,[N(m,{clearable:"",filterable:"",size:"small",class:"fcrm_bulk_select",placeholder:e.$t("Select Action"),"popper-class":l.bulkSelectPopperClass,effect:"dark",modelValue:i.select_job.action_name,"onUpdate:modelValue":t[0]||(t[0]=e=>i.select_job.action_name=e)},{default:U(()=>[N(p,{label:e.$t("Apply Labels"),value:"apply_labels"},null,8,["label"]),e.hasPermission("fcrm_manage_email_delete")?(I(),z(p,{key:0,label:e.$t("Delete Selected"),value:"delete_campaigns"},null,8,["label"])):H("",!0)],void 0),_:1},8,["placeholder","popper-class","modelValue"]),"apply_labels"===i.select_job.action_name?(I(),R(G,{key:0},[N(m,{filterable:"",size:"small",class:"fcrm_bulk_select",placeholder:e.$t("Select Labels"),modelValue:i.selectedLabels,"onUpdate:modelValue":t[1]||(t[1]=e=>i.selectedLabels=e),multiple:"","collapse-tags":"","collapse-tags-tooltip":"","popper-class":l.bulkSelectWordbreakPopperClass,effect:"dark"},{default:U(()=>[(I(!0),R(G,null,Q(a.options&&a.options.labels||[],e=>(I(),z(p,{key:e.id,label:e.title,value:Number(e.id)},{default:U(()=>[M("span",{style:Y({background:e.settings.color,padding:"2px 5px 4px 5px",borderRadius:"4px",color:"#0E121B"})},J(e.title),5)],void 0,!0),_:2},1032,["label","value"]))),128))],void 0),_:1},8,["placeholder","modelValue","popper-class"]),Z((I(),z(_,{disabled:i.doing_action||!i.selectedLabels.length,onClick:t[2]||(t[2]=e=>l.doApplyLabels()),type:"primary",size:"small"},{default:U(()=>[M("span",Se,[N(u,null,{default:U(()=>[N(h)],void 0,!0),_:1})]),W(" "+J(e.$t("Apply Label")),1)],void 0),_:1},8,["disabled"])),[[f,i.doing_action]])],64)):"delete_campaigns"===i.select_job.action_name?Z((I(),z(_,{key:1,disabled:i.doing_action,type:"danger",size:"small",plain:"",onClick:l.confirmAndDeleteCampaigns},{default:U(()=>[N(u,null,{default:U(()=>[N(g)],void 0,!0),_:1}),W(" "+J(e.$t("Delete Selected")),1)],void 0),_:1},8,["disabled","onClick"])),[[f,i.doing_action]]):H("",!0)])])}]]),De={class:"icon"},Ae={class:"fcrm_checkbox_group_label d-none"};const Pe=de({name:"ColumnToggler",components:{Icons:pe,Filterer:ye},props:{placement:{type:String,default:"bottom-start"}},emits:["input","update:modelValue","dataChanged"],data:()=>({selection:[]}),computed:{columnGroups(){return[{slug:"campaign",label:this.$t("Primary Fields"),fields:Ce}]}},methods:{init(){const e=this.storage.get("emailCampaignColumns");e?(this.selection=e,this.fire()):(this.selection=["status","scheduled_at","recipients","open_rate","click_rate"],this.save())},save(){var e;this.storage.set("emailCampaignColumns",this.selection),null==(e=this.$refs.filterer)||e.hide(),this.fire()},fire(){this.$emit("input",this.selection),this.$emit("update:modelValue",this.selection),setTimeout(()=>{this.$emit("dataChanged",this.selection)},400)}},mounted(){this.init()}},[["render",function(e,t,a,s,i,l){const n=j("Icons"),o=d,r=h,c=m,u=p,_=j("filterer");return I(),z(_,{ref:"filterer",placement:a.placement},{header:U(()=>[N(o,{size:"small",class:"small only-icon-btn","aria-label":e.$t("Toggle columns")},{default:U(()=>[M("span",De,[N(n,{"icon-name":"column"})])],void 0,!0),_:1},8,["aria-label"])]),items:U(()=>[N(c,{modelValue:i.selection,"onUpdate:modelValue":t[0]||(t[0]=e=>i.selection=e),class:"fcrm_filter-options fcrm_checkbox_group fcrm_column_toggler_checks"},{default:U(()=>[(I(!0),R(G,null,Q(l.columnGroups,(e,t)=>(I(),R(G,{key:t},[M("div",Ae,J(e.label),1),(I(!0),R(G,null,Q(e.fields,(e,a)=>(I(),R("div",{key:t+"-"+a,class:"el-dropdown-menu__item"},[N(r,{value:e.value,class:"fcrm_checkbox"},{default:U(()=>[W(J(e.label),1)],void 0,!0),_:2},1032,["value"])]))),128))],64))),128))],void 0,!0),_:1},8,["modelValue"])]),footer:U(()=>[N(u,{class:"fcrm_no-hover"},{default:U(()=>[N(o,{type:"primary",size:"small",style:{width:"100%"},onClick:l.save},{default:U(()=>[K(e.$slots,"btn-label",{},()=>[W(J(e.$t("Save")),1)])],void 0,!0),_:3},8,["onClick"])],void 0,!0),_:3})]),_:3},8,["placement"])}]]),xe="campaigns_filters";function qe(e){var t,a,s,i,l,n,o;try{return JSON.stringify({search:(null==(t=e.query_data)?void 0:t.search)||"",statuses:(null==(a=e.query_data)?void 0:a.statuses)||[],labels:(null==(s=e.query_data)?void 0:s.labels)||[],sort_by:(null==(i=e.query_data)?void 0:i.sort_by)||"id",sort_type:(null==(l=e.query_data)?void 0:l.sort_type)||"DESC",page:(null==(n=e.pagination)?void 0:n.current_page)||1,per_page:(null==(o=e.pagination)?void 0:o.per_page)||10})}catch(r){return""}}const Le=X("campaigns",{state:()=>({campaigns:[],pagination:{current_page:1,per_page:10,total:0},query_data:{search:"",statuses:[],labels:[],sort_by:"id",sort_type:"DESC"},loading:!1,first_loading:!0,cache_metadata:{last_fetch_time:null,cache_key_hash:null,is_stale:!1},options:{labels:[]}}),getters:{isCacheValid(e){if(!e.cache_metadata.last_fetch_time||e.cache_metadata.is_stale)return!1;return Date.now()-e.cache_metadata.last_fetch_time<3e5},filterHash:e=>qe({query_data:e.query_data,pagination:e.pagination}),hasCachedDataForCurrentFilters(e){if(!e.cache_metadata.cache_key_hash)return!1;const t=qe({query_data:e.query_data,pagination:e.pagination});return e.cache_metadata.cache_key_hash===t},shouldShowCache(){return this.isCacheValid&&this.hasCachedDataForCurrentFilters&&this.campaigns.length>0}},actions:{async fetchCampaigns(e=!1,a=!1){if(e||!this.shouldShowCache){a||(this.loading=!0);try{const e={sort_by:this.query_data.sort_by,sort_type:this.query_data.sort_type,searchBy:this.query_data.search,statuses:this.query_data.statuses,per_page:this.pagination.per_page,page:this.pagination.current_page,labels:this.query_data.labels,with:["stats"]},a=await t.get("campaigns",e);this.campaigns=a.campaigns.data,this.pagination.total=a.campaigns.total,this.cache_metadata.last_fetch_time=Date.now(),this.cache_metadata.cache_key_hash=this.filterHash,this.cache_metadata.is_stale=!1,this.persistFilters()}catch(s){throw console.error("[CampaignsStore] Failed to fetch campaigns",s),s}finally{a||(this.loading=!1),this.first_loading=!1}}},restoreFilters(){try{const t=e.get(xe,null);return!!t&&(this.query_data=t.query_data||this.query_data,t.pagination&&(this.pagination.current_page=t.pagination.current_page||1,this.pagination.per_page=t.pagination.per_page||10),!0)}catch(t){return console.error("[CampaignsStore] Failed to restore filters",t),!1}},persistFilters(){try{e.set(xe,{query_data:this.query_data,pagination:{current_page:this.pagination.current_page,per_page:this.pagination.per_page}})}catch(t){console.error("[CampaignsStore] Failed to persist filters",t)}},invalidateCache(){this.cache_metadata.is_stale=!0,this.cache_metadata.last_fetch_time=null,this.cache_metadata.cache_key_hash=null},updatePagination(e,t,a=!1){void 0!==e&&(this.pagination.current_page=e),void 0!==t&&(this.pagination.per_page=t),a||this.persistFilters()},updateQueryData(e){this.query_data={...this.query_data,...e},this.persistFilters()},setOptions(e){this.options={...this.options,...e}},initializeFromUrlParams(e){const t=e=>e?Array.isArray(e)?e:[e]:[];this.query_data={search:e.searchBy||"",statuses:t(e.statuses),labels:t(e.labels).map(e=>parseInt(e)),sort_by:e.sort_by||"id",sort_type:e.sort_type||"DESC"},e.page&&(this.pagination.current_page=parseInt(e.page)),e.per_page&&(this.pagination.per_page=parseInt(e.per_page))}}}),Te=ne(()=>a(()=>import("../../../../v3app/src/Modules/Labels/Labels.js?ver=3.1.8"),[],import.meta.url)),Ve=ne(()=>a(()=>import("../../../../v3app/src/Modules/Contacts/Filter/ActiveFiltersBar.js?ver=3.1.8"),[],import.meta.url)),Be={name:"Campaigns",emits:["refresh-stats"],components:{PromoCard:ve,Icons:pe,PageHeader:fe,Toggler:Pe,FloatingBulkActionShell:be,FilterPopover:ne(()=>a(()=>import("../../../../v3app/src/Modules/Contacts/Filter/FilterPopover.js?ver=3.1.8"),[],import.meta.url)),ActiveFiltersBar:Ve,Badge:ge,TopNav:_e,BulkCampaignActions:Fe,Labels:Te,Confirm:oe,PaginationBar:re,InlineDoc:ce,EmailPreview:ue,Search:v,Edit:C,Download:y,DocumentCopy:b,Delete:s,View:f,Close:g,Plus:_,DataLine:u},data(){return{direction:"rtl",createDrawerVisible:!1,creatingCampaign:!1,createCampaignForm:{title:"",design_template:""},searchBy:"",filterByStatuses:[],statuses:[{key:"draft",label:this.$t("Draft")},{key:"pending",label:this.$t("Pending")},{key:"archived",label:this.$t("Archived")},{key:"incomplete",label:this.$t("Incomplete")},{key:"purged",label:this.$t("Purged")},{key:"processing",label:this.$t("Processing")},{key:"pending-scheduled",label:this.$t("Scheduled (pending)")},{key:"scheduled",label:this.$t("Scheduled")}],sort_type:"DESC",sort_by:"id",deleting:!1,selection:!1,selectedCampaigns:[],showingLabelsConfig:!1,labelFilter:[],previewingCampaign:null,importDialogVisible:!1,inline_errors:null,openActionPopover:{},allSelected:!1,columns:[],initialFired:!1,current_mode:"system"===he.getCurrentTheme()?he.getSystemTheme():he.getCurrentTheme()}},computed:{...le(Le,["campaigns","pagination","loading","options","query_data"]),...ie(Le,["shouldShowCache"]),layoutOptions(){const e=window.fcAdmin&&window.fcAdmin.email_template_designs?window.fcAdmin.email_template_designs:{},t=this.getDefaultGutenbergId(),a=[{id:t,label:this.$t("Default (Gutenberg)"),hint:this.$t("Block-based editor with templates"),disabled:!t,image:this.appVars.images_url+"/gutenberg-builder.svg",use_gutenberg:!0}];return a.push({id:"raw_classic",label:this.$t("Classic Editor"),hint:this.$t("Simple text editor"),disabled:!e.raw_classic,image:this.appVars.images_url+"/classic-editor.svg",use_gutenberg:!1}),a.push({id:"raw_html",label:this.$t("Raw HTML"),hint:this.$t("Full HTML control"),disabled:!e.raw_html,image:this.appVars.images_url+"/html-editor.svg",use_gutenberg:!1}),e.visual_builder&&a.push({id:"visual_builder",label:this.$t("Visual Builder"),hint:this.$t("Drag-and-drop email builder"),disabled:!1,image:this.appVars.images_url+"/visual-builder.svg",use_gutenberg:!1}),a},defaultEditorId(){const e=this.layoutOptions.find(e=>e.use_gutenberg&&!e.disabled);if(e)return e.id;const t=this.layoutOptions.find(e=>!e.disabled);return t?t.id:"simple"},showNonGutenbergTip(){const e=this.layoutOptions.find(e=>e.id===this.createCampaignForm.design_template);return!(!e||e.use_gutenberg)},url(){let e=window.ajaxurl;return e+=(e.match(/\?/)?"&":"?")+jQuery.param({action:"fluentcrm_import_email_campaign"}),e},canSelectAll(){if(!this.pagination||!this.pagination.per_page||!this.pagination.total)return!1;const e=this.selectedCampaigns.length===this.pagination.per_page,t=this.selectedCampaigns.lengthe&&e.use_gutenberg);return t?t.id:"simple"},create(){this.createCampaignForm={title:"",design_template:this.defaultEditorId},this.createDrawerVisible=!0,this.$nextTick(()=>{this.$refs.createCampaignTitleInput&&this.$refs.createCampaignTitleInput.focus()})},submitCreateCampaign(){const e=(this.createCampaignForm.title||"").trim();if(!e)return void this.$notify.error(this.$t("Please provide a campaign title"));this.creatingCampaign=!0;const t=this.layoutOptions.find(e=>e.id===this.createCampaignForm.design_template&&!e.disabled),a=t?t.id:this.defaultEditorId;this.$post("campaigns",{title:e}).then(t=>{const s=t&&t.id?t:t.campaign||{};if(!s.id)throw new Error(this.$t("Could not create campaign"));return this.$put(`campaigns/${s.id}`,{title:e,design_template:a}).then(()=>s)}).then(e=>{this.createDrawerVisible=!1,this.invalidateCache(),this.fetch(),this.$router.push({name:"campaign",params:{id:e.id},query:{step:0,t:(new Date).getTime()}})}).catch(e=>{this.handleError&&this.handleError(e)}).finally(()=>{this.creatingCampaign=!1})},isNotEditable:e=>["archived","working"].indexOf(e.status)>=0,scheduledAt(e){return null===e?this.$t("Not Scheduled"):this.nsDateFormat(e,"MMMM Do, YYYY [at] h:mm A")},setup(){let e=this.$route.query;return window.fcrm_camp_sub_params&&(e=window.fcrm_camp_sub_params),e&&Object.keys(e).length>0&&this.initializeFromUrlParams(e),this.sort_by=this.query_data.sort_by||e.sort_by,this.sort_type=this.query_data.sort_type||e.sort_type,this.searchBy=this.query_data.search||e.searchBy||"",this.filterByStatuses=this.query_data.statuses||[],this.labelFilter=this.query_data.labels||[],!1},async fetch(e=!0,t=!1){this.updateQueryData({search:this.searchBy,statuses:this.filterByStatuses,labels:this.labelFilter,sort_by:this.sort_by,sort_type:this.sort_type});const a={sort_by:this.sort_by,sort_type:this.sort_type,searchBy:this.searchBy,statuses:this.filterByStatuses,per_page:this.pagination.per_page,page:this.pagination.current_page,labels:this.labelFilter,with:["stats"]},s={};Object.keys(a).forEach(e=>{void 0!==a[e]&&(s[e]=a[e])}),window.fcrm_camp_sub_params=s,s.t=Date.now();const i=this.$route.query;JSON.stringify(i)!==JSON.stringify(s)&&this.$router.replace({name:"campaigns",query:s}).catch(e=>{"NavigationDuplicated"===e.name||e.message.includes("navigation")||console.error(e)});try{await this.fetchCampaigns(e,t),this.registerHeartBeat(),this.$bus.emit("refresh-stats")}catch(l){this.handleError(l)}finally{this.initialFired=!0}},searchCampaigns(){this.fetch()},maybeReFetch(){this.initialFired&&this.fetch(!1,!0)},setActionPopoverVisible(e,t){this.openActionPopover={...this.openActionPopover,[e]:!!t}},closeActionPopover(e){this.openActionPopover={...this.openActionPopover,[e.id]:!1}},handleDeleteConfirm(e){this.closeActionPopover(e),this.remove(e)},remove(e){this.$del(`campaigns/${e.id}`).then(e=>{this.invalidateCache(),this.fetch(),this.$notify.success({title:this.$t("Great!"),message:this.$t("Campaign deleted."),offset:19})}).catch(e=>{this.$messageBox.alert(this.$sanitize(e.message),this.$t("Oops!"),{center:!0,type:"warning",confirmButtonText:this.$t("Close"),dangerouslyUseHTMLString:!0,callback:e=>{this.$router.push({name:"campaigns",query:{t:(new Date).getTime()}})}})})},deleteSelected(){const e=[];this.each(this.selectedCampaigns,t=>{e.push(t.id)}),this.doing_action=!0,this.$post("campaigns/do-bulk-action",{campaign_ids:e}).then(e=>{this.$notify.success(e.message),this.invalidateCache(),this.fetch()}).catch(e=>{this.handleError(e)}).finally(()=>{this.doing_action=!1})},onSelection(e){this.selection=!!e.length,this.selectedCampaigns=e,e.length>0&&e.length!==this.pagination.per_page&&(this.allSelected=!1),e.length||(this.allSelected=!1)},clearCampaignSelection(){this.$refs.campaignTable&&this.$refs.campaignTable.clearSelection(),this.selectedCampaigns=[],this.allSelected=!1,this.selection=!1},selectAllCampaigns(){this.canSelectAll&&(this.$refs.campaignTable&&this.campaigns&&this.campaigns.length>0&&this.campaigns.forEach(e=>{this.$refs.campaignTable.toggleRowSelection(e,!0)}),this.allSelected=!0)},selectOnlyThisPage(){this.allSelected=!1;const e=this.campaigns.map(e=>e.id);this.selectedCampaigns=this.selectedCampaigns.filter(t=>e.includes(t.id))},registerHeartBeat(){jQuery(document).off("heartbeat-send").on("heartbeat-send",(e,t)=>{t.fluentcrm_campaign_ids=this.campaigns.map(e=>e.id)}),jQuery(document).off("heartbeat-tick").on("heartbeat-tick",(e,t)=>{if(t.fluentcrm_campaigns)for(const a in t.fluentcrm_campaigns){const e=t.fluentcrm_campaigns[a];this.campaigns.forEach(t=>{t.id===a&&t.status!==e&&(t.status=e)})}})},routeCampaign(e){let t="campaign-view";"draft"===e.status&&(t="campaign");const a=parseInt(e.next_step,10),s=!Number.isNaN(a)&&a>=0&&a<=3?a:0;this.$router.push({name:t,params:{id:e.id},query:{t:(new Date).getTime(),step:s}})},generateUrl(e){let t="campaign-view";"draft"===e.status&&(t="campaign");const a=parseInt(e.next_step,10),s=!Number.isNaN(a)&&a>=0&&a<=3?a:0;return this.$router.resolve({name:t,params:{id:e.id},query:{t:(new Date).getTime(),step:s}}).href},customOpenRateSort:(e,t)=>(e.stats&&e.stats.sent&&e.stats.views?e.stats.views/e.stats.sent:0)-(t.stats&&t.stats.sent&&t.stats.views?t.stats.views/t.stats.sent:0),customClickRateSort:(e,t)=>(e.stats&&e.stats.sent&&e.stats.clicks?e.stats.clicks/e.stats.sent:0)-(t.stats&&t.stats.sent&&t.stats.clicks?t.stats.clicks/t.stats.sent:0),getPercent:(e,t)=>t&&e?parseFloat(e/t*100).toFixed(2)+"%":"--",cloneCampaign(e){this.loading=!0,this.$post(`campaigns/${e.id}/duplicate`).then(e=>{this.$notify.success(e.message),this.invalidateCache(),this.routeCampaign(e.campaign)}).catch(e=>{this.handleError(e)}).finally(()=>{this.loading=!1})},handleSortable(e){console.log("Sorting changed:",e),"descending"===e.order?(this.sort_by=e.prop,this.sort_type="DESC"):"ascending"===e.order?(this.sort_by=e.prop,this.sort_type="ASC"):(this.sort_by="id",this.sort_type="DESC"),this.fetch()},fetchLabels(){this.$get("labels").then(e=>{this.options.labels=e.labels}).catch(e=>{this.handleError(e)}).finally(()=>{})},showLabelDialog(){this.showingLabelsConfig=!0},closeDrawer(){this.showingLabelsConfig=!1},applyLabels(e,t,a="attach"){this.$put("campaigns/"+e.id+"/update-labels",{action:a,label_ids:t}).then(e=>{this.$notify.success(e.message),this.invalidateCache(),this.fetch()}).catch(e=>{this.handleError(e)}).finally(()=>{})},exportCampaign(e){this.has_campaign_pro?location.href=window.ajaxurl+"?"+jQuery.param({action:"fluentcrm_export_email_campaign",campaign_id:e.id}):this.$notify.error(this.$t("Campaign_Export_Alert"))},showPreview(e){this.previewingCampaign=e},getCampaignStatus(e){if(!e)return"";let t=e;return this.statuses.find(t=>t.key===e)&&(t=this.statuses.find(t=>t.key===e).label),t},success(e){this.$notify.success(e.message),this.$router.push({name:"campaign",params:{id:e.campaign_id}})},error(e){try{const t=JSON.parse(e.message);this.$notify.error(t.message),t.requires&&(this.inline_errors=t.requires)}catch(t){this.$notify.error(e.message||"An error occurred")}},handleFilterApply(e){this.query_data.labels=e.labels||[],this.query_data.statuses=e.statuses||[],this.pagination.current_page=1,this.labelFilter=e.labels,this.filterByStatuses=e.statuses,this.fetch()},handleFilterBarChange(e){this.query_data.labels=e.labels||[],this.query_data.statuses=e.statuses||[],this.labelFilter=e.labels||[],this.filterByStatuses=e.statuses||[],void 0!==e.search&&(this.query_data.search=e.search),this.pagination.current_page=1,this.fetch()},handleOpenFilter(e){this.$nextTick(()=>{var t,a;null==(a=null==(t=this.$refs.filterPopoverRef)?void 0:t.openFilterCategory)||a.call(t,e)})}},watch:{filterByStatuses:{handler(){this.initialFired&&this.fetch()},deep:!0}},mounted(){if(window.fcAdmin&&window.fcAdmin.is_rtl&&(this.direction="ltr"),this.restoreFilters(),this.setup(),this.fetchLabels(),this.changeTitle(this.$t("Email Campaigns")),this.shouldShowCache?(this.loading=!1,setTimeout(()=>this.fetch(!0,!0),100)):this.fetch(),"1"===this.$route.query.create){this.create();const e={...this.$route.query};delete e.create,this.$router.replace({path:this.$route.path,query:e})}this.onThemeChanged=e=>{var t;this.current_mode=(null==(t=e.detail)?void 0:t.effective)||he.getCurrentTheme()},window.addEventListener(me,this.onThemeChanged)},beforeUnmount(){this.onThemeChanged&&window.removeEventListener(me,this.onThemeChanged)}},Ee={class:"fcrm_email_campaigns_page"},Oe={class:"fcrm_page_header_top_nav_wrapper"},je={class:"fcrm_page_header_top_nav"},Ie={class:"fcrm-layout-width"},Re={class:"icon"},Me={class:"el-popover__reference"},Ne={class:"icon"},Ue={class:"el-popover__reference"},ze={class:"icon"},He={class:"icon"},Ge={key:0,class:"fcrm_table_wrapper"},Qe={class:"fcrm_table_header"},Ye={class:"fcrm_table_header_inner"},Je={class:"fcrm_table_header_inner_left"},Ze={class:"icon"},We={class:"fcrm_table_header_inner_actions"},Ke={class:"fcrm_table_header_bulk_actions"},Xe={class:"fcrm_table_body"},et={class:"campaigns-table"},tt={class:"fcrm_empty_state"},at={class:"fcrm_empty_state_text"},st={class:"fcrm_table_row_expand"},it={class:"fcrm_quick_stats_wrap"},lt={class:"fcrm_quick_stats_label"},nt={key:0,class:"fcrm_quick_stats"},ot={class:"icon"},rt={class:"fcrm_quick_stat_digit"},ct=["title"],dt={class:"icon"},pt={class:"fcrm_quick_stat_digit"},mt=["title"],ht={class:"icon"},ut={class:"fcrm_quick_stat_digit"},_t=["title"],gt={class:"icon"},ft={class:"fcrm_quick_stat_digit"},bt={key:0},yt={class:"icon"},Ct={class:"fcrm_quick_stat_digit"},vt={class:"campaign_title"},wt=["href"],kt={key:0,style:{"margin-inline-start":"10px"}},$t={key:1,style:{"margin-inline-start":"10px"},class:"fcrm_campaign_preview_btn"},St={class:"icon"},Ft=["title"],Dt={key:0},At=["title"],Pt={key:2},xt={key:0},qt={key:0},Lt={key:1},Tt={class:"fcrm_rate_header_cell"},Vt=["title"],Bt={key:1},Et={class:"fcrm_rate_header_cell"},Ot=["title"],jt={key:1},It=["title"],Rt={style:{"font-size":"70%"}},Mt={key:1},Nt={key:0,class:"fc_funnel_labels"},Ut={class:"el-popover__reference"},zt={class:"icon"},Ht={class:"el-popover__reference"},Gt={class:"icon"},Qt={class:"el-popover__reference"},Yt={class:"icon"},Jt={class:"el-popover__reference"},Zt={class:"icon"},Wt={key:0,class:"fcrm_selection_count_text"},Kt={class:"fcrm_selection_count_number"},Xt={class:"fcrm_selection_count_text"},ea={key:1,class:"fcrm_table_wrapper"},ta={class:"fcrm_table_body"},aa={key:2,class:"fcrm_empty_state"},sa={class:"fcrm_empty_state_text"},ia={key:0,class:"fcrm_import_content"},la={class:"upload-icon"},na={class:"el-upload__text"},oa={class:"fcrm_create_campaign_drawer fcrm_campaign_setup_wrapper"},ra={class:"fcrm_layout_choice_icon"},ca={class:"fcrm_layout_choice_image"},da=["src","alt"],pa={class:"fcrm_layout_choice_label"},ma={class:"fcrm_layout_choice_hint"},ha={key:0,class:"fcrm_editor_recommendation_alert"},ua={class:"icon"},_a={class:"fcrm_dialog_footer_actions"},ga={key:1};const fa=de(Be,[["render",function(e,t,a,s,i,l){const n=j("TopNav"),o=j("Icons"),m=d,h=p,u=k,_=w,g=j("inline-doc"),f=j("page-header"),b=$,y=j("filter-popover"),C=j("toggler"),v=j("active-filters-bar"),K=S,X=j("icons"),se=j("Badge"),ie=F,le=j("Close"),ne=c,oe=j("Confirm"),re=D,ce=j("DataLine"),de=j("confirm"),pe=A,me=j("pagination-bar"),he=j("bulk-campaign-actions"),ue=j("floating-bulk-action-shell"),_e=P,ge=x,fe=q,be=j("PromoCard"),ye=L,Ce=V,ve=E,we=B,ke=T,$e=O,Se=j("labels"),Fe=j("email-preview"),De=r;return I(),R("div",Ee,[M("div",Oe,[M("div",je,[N(n)]),t[14]||(t[14]=M("div",{class:"fcrm_page_header_top_actions"},null,-1))]),M("div",Ie,[N(f,null,{title:U(()=>[W(J(e.$t("Email Campaigns"))+" ",1),Z(M("small",null,"("+J(e.formatMoney(e.pagination.total))+")",513),[[ee,e.pagination.total]])]),actions:U(()=>[N(_,{trigger:"click"},{dropdown:U(()=>[N(u,null,{default:U(()=>[N(h,{class:"fc_dropdown_action",onClick:l.showLabelDialog},{default:U(()=>[M("span",Me,[M("span",Ne,[N(o,{"icon-name":"manageLabels"})]),W(" "+J(e.$t("Manage Labels")),1)])],void 0,!0),_:1},8,["onClick"]),N(h,{class:"fc_dropdown_action",onClick:t[0]||(t[0]=e=>i.importDialogVisible=!0)},{default:U(()=>[M("span",Ue,[M("span",ze,[N(o,{"icon-name":"import"})]),W(" "+J(e.$t("Import")),1)])],void 0,!0),_:1})],void 0,!0),_:1})]),default:U(()=>[N(m,{class:"el-dropdown-link"},{default:U(()=>[W(J(e.$t("More Actions"))+" ",1),M("span",Re,[N(o,{"icon-name":"downIcon"})])],void 0,!0),_:1})],void 0,!0),_:1}),N(g,{doc_id:389}),N(m,{onClick:l.create,type:"primary"},{default:U(()=>[M("span",He,[N(o,{"icon-name":"plus"})]),W(" "+J(e.$t("Add Campaign")),1)],void 0,!0),_:1},8,["onClick"])]),_:1}),e.campaigns.length||!e.loading?(I(),R("div",Ge,[M("div",Qe,[M("div",Ye,[M("div",Je,[N(b,{onKeyup:t[1]||(t[1]=te(e=>l.fetch(),["enter"])),clearable:"",onClear:t[2]||(t[2]=e=>l.fetch()),size:"small",placeholder:e.$t("Search by title..."),modelValue:i.searchBy,"onUpdate:modelValue":t[3]||(t[3]=e=>i.searchBy=e)},{prefix:U(()=>[M("span",Ze,[N(o,{"icon-name":"search"})])]),_:1},8,["placeholder","modelValue"])]),M("div",We,[N(y,{ref:"filterPopoverRef",options:{labels:e.options.labels?e.options.labels:[],statuses:i.statuses?i.statuses:[]},"selected-filters":e.query_data,onApply:l.handleFilterApply},null,8,["options","selected-filters","onApply"]),N(C,{onDataChanged:t[4]||(t[4]=e=>l.maybeReFetch()),modelValue:i.columns,"onUpdate:modelValue":t[5]||(t[5]=e=>i.columns=e)},null,8,["modelValue"])])]),M("div",Ke,[i.selection?H("",!0):(I(),z(v,{key:0,"selected-filters":e.query_data,options:{labels:e.options.labels?e.options.labels:[],statuses:i.statuses?i.statuses:[]},onFilterChange:l.handleFilterBarChange,onOpenFilter:l.handleOpenFilter,plus_filter_icon:!0},null,8,["selected-filters","options","onFilterChange","onOpenFilter"]))])]),M("div",Xe,[M("div",et,[Z((I(),z(pe,{ref:"campaignTable",border:"",onSortChange:l.handleSortable,stripe:"",data:e.campaigns,onSelectionChange:l.onSelection},{empty:U(()=>[M("div",tt,[N(o,{"icon-name":"common-empty-state"}),M("div",at,[M("span",null,J(e.$t("No campaign records found-start by creating a new one")),1)])])]),default:U(()=>[N(K,{type:"selection",fixed:"",width:45}),N(K,{type:"expand"},{default:U(t=>[M("div",st,[M("div",it,[M("div",lt,J(e.$t("Quick Stats")),1),"draft"!==t.row.status?(I(),R("ul",nt,[M("li",null,[M("span",ot,[N(o,{"icon-name":"send-mail"})]),M("p",null,[M("span",rt,J(t.row.stats.sent||"--"),1),W(" "+J(e.$t("Sent")),1)])]),M("li",{title:t.row.stats.views},[M("span",dt,[N(o,{"icon-name":"envelopeOpen"})]),M("p",null,[M("span",pt,J(l.getPercent(t.row.stats.views,t.row.stats.sent)),1),W(" "+J(e.$t("Opened")),1)])],8,ct),M("li",{title:t.row.stats.clicks},[M("span",ht,[N(X,{"icon-name":"click"})]),M("p",null,[M("span",ut,J(l.getPercent(t.row.stats.clicks,t.row.stats.sent)),1),W(" "+J(e.$t("Clicked")),1)])],8,mt),M("li",{title:t.row.stats.unsubscribers},[M("span",gt,[N(o,{"icon-name":"unsubscribe"})]),M("p",null,[M("span",ft,J(l.getPercent(t.row.stats.unsubscribers,t.row.stats.sent)),1),W(" "+J(e.$t("Unsubscribed")),1)])],8,_t),t.row.stats.revenue?(I(),R("li",bt,[M("span",yt,[N(o,{"icon-name":"wallet"})]),M("p",null,[M("span",Ct,J(t.row.stats.revenue.total),1),W(" "+J(t.row.stats.revenue.label),1)])])):H("",!0)])):(I(),R(G,{key:1},[W("---")],64))])])]),_:1}),N(K,{sortable:"custom","min-width":300,label:e.$t("Title"),prop:"title"},{default:U(t=>[M("div",vt,[M("a",{href:l.generateUrl(t.row)},J(t.row.title),9,wt),"draft"==t.row.status?(I(),R("span",kt,[N(m,{onClick:e=>l.routeCampaign(t.row),size:"small",class:"fcrm_setup_btn"},{default:U(()=>[N(o,{"icon-name":"SetupIcon"}),W(" "+J(e.$t("Setup")),1)],void 0,!0),_:1},8,["onClick"])])):(I(),R("span",$t,[N(m,{onClick:e=>l.showPreview(t.row),size:"small",link:"","aria-label":e.$t("Preview Campaign"),title:e.$t("Preview Campaign")},{default:U(()=>[M("span",St,[N(o,{"icon-name":"eye"})])],void 0,!0),_:1},8,["onClick","aria-label","title"])]))])]),_:1},8,["label"]),-1!=i.columns.indexOf("status")?(I(),z(K,{key:0,sortable:"custom",width:120,prop:"status",label:e.$t("Status")},{default:U(e=>[N(se,{type:e.row.status},null,8,["type"])]),_:1},8,["label"])):H("",!0),-1!=i.columns.indexOf("created_at")?(I(),z(K,{key:1,sortable:"custom",prop:"created_at",width:180,label:e.$t("Created at")},{default:U(t=>[M("span",{title:t.row.created_at},J(e.nsHumanDiffTime(t.row.created_at)),9,Ft)]),_:1},8,["label"])):H("",!0),-1!=i.columns.indexOf("scheduled_at")?(I(),z(K,{key:2,sortable:"custom",prop:"scheduled_at",width:180,label:e.$t("Broadcast")},{default:U(t=>["processing"==t.row.status?(I(),R("span",Dt,"soon...")):t.row.scheduled_at&&"draft"!=t.row.status?(I(),R("span",{key:1,title:t.row.scheduled_at},J(e.nsHumanDiffTime(t.row.scheduled_at)),9,At)):(I(),R("span",Pt,J(e.$t("n/a")),1))]),_:1},8,["label"])):H("",!0),-1!=i.columns.indexOf("recipients")?(I(),z(K,{key:3,sortable:"custom",prop:"recipients_count",width:130,label:e.$t("Recipients")},{default:U(t=>[t.row.recipients_count?(I(),R("span",xt,[W(J(e.formatMoney(t.row.recipients_count))+" ",1),"processing"==t.row.status?(I(),R("span",qt,"++")):H("",!0)])):(I(),R("span",Lt,J(e.$t("n/a")),1))]),_:1},8,["label"])):H("",!0),-1!=i.columns.indexOf("open_rate")?(I(),z(K,{key:4,width:130,label:e.$t("Open Rate")},{header:U(()=>[M("div",Tt,[M("span",null,J(e.$t("Open Rate")),1),N(ie,{class:"item",effect:"dark",content:e.$t("open_rate_info"),"popper-style":{maxWidth:"260px"},placement:"top-start"},{default:U(()=>[...t[15]||(t[15]=[M("svg",{xmlns:"http://www.w3.org/2000/svg",width:"20",height:"20",viewBox:"0 0 20 20",fill:"none"},[M("path",{"fill-rule":"evenodd","clip-rule":"evenodd",d:"M10 16.25C13.4518 16.25 16.25 13.4518 16.25 10C16.25 6.54822 13.4518 3.75 10 3.75C6.54822 3.75 3.75 6.54822 3.75 10C3.75 13.4518 6.54822 16.25 10 16.25ZM11.1158 13.2086L11.2156 12.8006C11.164 12.8249 11.0807 12.8526 10.9665 12.8841C10.852 12.9157 10.7489 12.9318 10.6583 12.9318C10.4654 12.9318 10.3295 12.9001 10.2507 12.8366C10.1724 12.773 10.1333 12.6534 10.1333 12.4783C10.1333 12.4089 10.1451 12.3054 10.1697 12.17C10.1936 12.0337 10.2211 11.9126 10.2516 11.8067L10.6242 10.4876C10.6607 10.3665 10.6857 10.2334 10.6992 10.0882C10.7129 9.94325 10.7193 9.84185 10.7193 9.78429C10.7193 9.50614 10.6218 9.28041 10.4268 9.10629C10.2317 8.93229 9.95393 8.84529 9.59396 8.84529C9.39365 8.84529 9.18188 8.88088 8.95776 8.952C8.73363 9.02294 8.49933 9.1084 8.25421 9.2082L8.15415 9.6165C8.22719 9.58949 8.31419 9.56043 8.41598 9.53034C8.51732 9.50038 8.61674 9.48489 8.71347 9.48489C8.91096 9.48489 9.04399 9.51856 9.1137 9.58488C9.18342 9.65139 9.21844 9.7697 9.21844 9.93883C9.21844 10.0324 9.20736 10.1363 9.18438 10.2492C9.16172 10.3628 9.13342 10.483 9.10013 10.6098L8.72595 11.9342C8.69266 12.0734 8.66834 12.1979 8.65304 12.3084C8.63786 12.4189 8.63057 12.5272 8.63057 12.6326C8.63057 12.9048 8.73114 13.1292 8.93222 13.3063C9.13329 13.4826 9.41523 13.5714 9.77769 13.5714C10.0137 13.5714 10.2209 13.5406 10.3992 13.4785C10.5773 13.4167 10.8164 13.3268 11.1158 13.2086ZM11.0495 7.8502C11.2235 7.68882 11.3101 7.49254 11.3101 7.26272C11.3101 7.03341 11.2236 6.83675 11.0495 6.67331C10.8758 6.51032 10.6666 6.42857 10.4219 6.42857C10.1765 6.42857 9.96635 6.51013 9.79107 6.67331C9.61579 6.83675 9.52796 7.03334 9.52796 7.26272C9.52796 7.49254 9.61579 7.68875 9.79107 7.8502C9.96667 8.01217 10.1764 8.09321 10.4219 8.09321C10.6666 8.09321 10.8758 8.01217 11.0495 7.8502Z",fill:"var(--fc-secondary-border)"})],-1)])],void 0,!0),_:1},8,["content"])])]),default:U(t=>[t.row.stats.views&&t.row.stats.sent?(I(),R("span",{key:0,title:e.$t("Open Rate"),class:"fluentcrm_digit"},J(l.getPercent(t.row.stats.views,t.row.stats.sent)),9,Vt)):(I(),R("span",Bt,"--"))]),_:1},8,["label"])):H("",!0),-1!=i.columns.indexOf("click_rate")?(I(),z(K,{key:5,width:120,label:e.$t("Click Rate")},{header:U(()=>[M("div",Et,[M("span",null,J(e.$t("Click Rate")),1),N(ie,{class:"item",effect:"dark",content:e.$t("click_rate_info"),"popper-style":{maxWidth:"260px"},placement:"top-start"},{default:U(()=>[...t[16]||(t[16]=[M("svg",{xmlns:"http://www.w3.org/2000/svg",width:"20",height:"20",viewBox:"0 0 20 20",fill:"none"},[M("path",{"fill-rule":"evenodd","clip-rule":"evenodd",d:"M10 16.25C13.4518 16.25 16.25 13.4518 16.25 10C16.25 6.54822 13.4518 3.75 10 3.75C6.54822 3.75 3.75 6.54822 3.75 10C3.75 13.4518 6.54822 16.25 10 16.25ZM11.1158 13.2086L11.2156 12.8006C11.164 12.8249 11.0807 12.8526 10.9665 12.8841C10.852 12.9157 10.7489 12.9318 10.6583 12.9318C10.4654 12.9318 10.3295 12.9001 10.2507 12.8366C10.1724 12.773 10.1333 12.6534 10.1333 12.4783C10.1333 12.4089 10.1451 12.3054 10.1697 12.17C10.1936 12.0337 10.2211 11.9126 10.2516 11.8067L10.6242 10.4876C10.6607 10.3665 10.6857 10.2334 10.6992 10.0882C10.7129 9.94325 10.7193 9.84185 10.7193 9.78429C10.7193 9.50614 10.6218 9.28041 10.4268 9.10629C10.2317 8.93229 9.95393 8.84529 9.59396 8.84529C9.39365 8.84529 9.18188 8.88088 8.95776 8.952C8.73363 9.02294 8.49933 9.1084 8.25421 9.2082L8.15415 9.6165C8.22719 9.58949 8.31419 9.56043 8.41598 9.53034C8.51732 9.50038 8.61674 9.48489 8.71347 9.48489C8.91096 9.48489 9.04399 9.51856 9.1137 9.58488C9.18342 9.65139 9.21844 9.7697 9.21844 9.93883C9.21844 10.0324 9.20736 10.1363 9.18438 10.2492C9.16172 10.3628 9.13342 10.483 9.10013 10.6098L8.72595 11.9342C8.69266 12.0734 8.66834 12.1979 8.65304 12.3084C8.63786 12.4189 8.63057 12.5272 8.63057 12.6326C8.63057 12.9048 8.73114 13.1292 8.93222 13.3063C9.13329 13.4826 9.41523 13.5714 9.77769 13.5714C10.0137 13.5714 10.2209 13.5406 10.3992 13.4785C10.5773 13.4167 10.8164 13.3268 11.1158 13.2086ZM11.0495 7.8502C11.2235 7.68882 11.3101 7.49254 11.3101 7.26272C11.3101 7.03341 11.2236 6.83675 11.0495 6.67331C10.8758 6.51032 10.6666 6.42857 10.4219 6.42857C10.1765 6.42857 9.96635 6.51013 9.79107 6.67331C9.61579 6.83675 9.52796 7.03334 9.52796 7.26272C9.52796 7.49254 9.61579 7.68875 9.79107 7.8502C9.96667 8.01217 10.1764 8.09321 10.4219 8.09321C10.6666 8.09321 10.8758 8.01217 11.0495 7.8502Z",fill:"var(--fc-secondary-border)"})],-1)])],void 0,!0),_:1},8,["content"])])]),default:U(t=>[t.row.stats.clicks&&t.row.stats.sent?(I(),R("span",{key:0,title:e.$t("Click Rate"),class:"fluentcrm_digit"},J(l.getPercent(t.row.stats.clicks,t.row.stats.sent)),9,Ot)):(I(),R("span",jt,"--"))]),_:1},8,["label"])):H("",!0),-1!=i.columns.indexOf("revenue")?(I(),z(K,{key:6,width:130,label:e.$t("Revenue")},{default:U(t=>[t.row.stats.revenue?(I(),R("span",{key:0,title:e.$t("Revenue")},[W(J(t.row.stats.revenue.total)+" ",1),M("span",Rt,J(t.row.stats.revenue.currency),1)],8,It)):(I(),R("span",Mt,"--"))]),_:1},8,["label"])):H("",!0),-1!=i.columns.indexOf("labels")?(I(),z(K,{key:7,width:180,label:e.$t("Labels")},{default:U(t=>[t.row.labels?(I(),R("div",Nt,[(I(!0),R(G,null,Q(t.row.labels,a=>(I(),z(re,{key:a.id,size:"small",style:Y("background:"+a.color)},{default:U(()=>[W(J(a.title)+" ",1),N(oe,{onYes:e=>l.applyLabels(t.row,a.id,"detach"),message:e.$t("Remove_Label_From_campaign_Message")},{reference:U(()=>[N(ne,{class:"el-tag__close"},{default:U(()=>[N(le)],void 0,!0),_:1})]),_:1},8,["onYes","message"])],void 0,!0),_:2},1032,["style"]))),128))])):H("",!0)]),_:1},8,["label"])):H("",!0),N(K,{fixed:"right",width:"60","class-name":"fcrm_table_actions_cell"},{default:U(a=>[N(_,{trigger:"click","hide-on-click":!1,placement:"bottom-end"},{dropdown:U(()=>[N(u,{class:"fcrm_campaign_actions_menu"},{default:U(()=>["draft"!==a.row.status?(I(),z(h,{key:0,onClick:e=>{l.closeActionPopover(a.row),l.routeCampaign(a.row)}},{default:U(()=>[M("span",Ut,[M("span",zt,[N(ne,null,{default:U(()=>[N(ce)],void 0,!0),_:1})]),W(" "+J(e.$t("Reports")),1)])],void 0,!0),_:1},8,["onClick"])):H("",!0),e.hasPermission("fcrm_manage_emails")?(I(),z(h,{key:1,onClick:e=>{l.closeActionPopover(a.row),l.exportCampaign(a.row)}},{default:U(()=>[M("span",Ht,[M("span",Gt,[N(o,{"icon-name":"export"})]),W(" "+J(e.$t("Export")),1)])],void 0,!0),_:1},8,["onClick"])):H("",!0),e.hasPermission("fcrm_manage_emails")?(I(),z(h,{key:2,onClick:e=>{l.closeActionPopover(a.row),l.cloneCampaign(a.row)}},{default:U(()=>[M("span",Qt,[M("span",Yt,[N(o,{"icon-name":"duplicate"})]),W(" "+J(e.$t("Duplicate")),1)])],void 0,!0),_:1},8,["onClick"])):H("",!0),e.hasPermission("fcrm_manage_email_delete")&&"working"!==a.row.status?(I(),z(h,{key:3,class:"fcrm_danger_action"},{default:U(()=>[N(de,{placement:"top-start",message:e.$t("Are you sure you want to delete this campaign?"),onYes:e=>l.handleDeleteConfirm(a.row)},{reference:U(()=>[M("span",Jt,[M("span",Zt,[N(o,{"icon-name":"delete"})]),W(" "+J(e.$t("Delete")),1)])]),_:1},8,["message","onYes"])],void 0,!0),_:2},1024)):H("",!0)],void 0,!0),_:2},1024)]),default:U(()=>[N(m,{link:"",class:"el-dropdown-link fcrm_campaign_actions_trigger",style:{cursor:"pointer",display:"inline-flex"},onClick:t[6]||(t[6]=ae(()=>{},["stop"])),"aria-label":e.$t("Row actions")},{default:U(()=>[N(o,{"icon-name":"more_actions"})],void 0,!0),_:1},8,["aria-label"])],void 0,!0),_:2},1024)]),_:1})],void 0),_:1},8,["onSortChange","data","onSelectionChange"])),[[De,e.loading]]),N(me,{pagination:e.pagination,onFetch:l.fetch},null,8,["pagination","onFetch"])])]),N(ue,{visible:i.selection,"theme-mode":i.current_mode,"selected-count":i.selectedCampaigns.length,"selected-label":e.$t("selected"),"show-select-all":l.canSelectAll&&!i.allSelected,"show-select-only-page":l.canSelectAll&&i.allSelected,"select-all-label":e.$t("Select All %s",e.formatMoney(e.pagination.total)),"select-only-page-label":e.$t("Select only this page"),"deselect-label":e.$t("Deselect"),onSelectAll:l.selectAllCampaigns,onSelectOnlyPage:l.selectOnlyThisPage,onDeselect:l.clearCampaignSelection},{actions:U(()=>[N(he,{ref:"bulkCampaignActions",selectedCampaigns:i.selectedCampaigns,options:e.options,filters:e.query_data,"all-selected":i.allSelected,theme_mode:i.current_mode,onRefetch:t[7]||(t[7]=t=>{e.invalidateCache(),l.fetch()})},null,8,["selectedCampaigns","options","filters","all-selected","theme_mode"])]),count:U(()=>[i.allSelected?(I(),R("span",Wt,J(e.$t("All %s selected",e.formatMoney(e.pagination.total))),1)):(I(),R(G,{key:1},[M("span",Kt,J(i.selectedCampaigns.length),1),M("span",Xt,J(e.$t("selected")),1)],64))]),_:1},8,["visible","theme-mode","selected-count","selected-label","show-select-all","show-select-only-page","select-all-label","select-only-page-label","deselect-label","onSelectAll","onSelectOnlyPage","onDeselect"])])):e.loading?(I(),R("div",ea,[M("div",ta,[N(_e,{style:{padding:"20px"},animated:!0,rows:10})])])):(I(),R("div",aa,[N(X,{"icon-name":"campaign-empty-state"}),M("div",sa,[M("span",null,J(e.$t("Please create a campaign to view")),1)])]))]),N(ye,{title:e.$t("Import Campaign"),modelValue:i.importDialogVisible,"onUpdate:modelValue":t[8]||(t[8]=e=>i.importDialogVisible=e),"append-to-body":!0,"close-on-click-modal":!1,width:"640px","modal-class":"fcrm_import_dialog"},{default:U(()=>[e.has_campaign_pro?(I(),R("div",ia,[M("h3",null,J(e.$t("Upload JSON File")),1),N(ge,{drag:"",limit:1,action:l.url,ref:"uploader",multiple:!1,"on-error":l.error,"on-success":l.success},{default:U(()=>[M("span",la,[N(o,{"icon-name":"upload"})]),M("div",na,J(e.$t("Choose a file or drag & drop it here.")),1),N(m,null,{default:U(()=>[W(J(e.$t("Browse File")),1)],void 0,!0),_:1})],void 0,!0),_:1},8,["action","on-error","on-success"]),N(fe,{title:e.$t("Not_Import_Email_Campaigns_Alert"),type:"info","show-icon":"",closable:!1},null,8,["title"]),i.inline_errors?(I(),z(fe,{key:0,title:i.inline_errors,type:"error","show-icon":"",closable:!1},null,8,["title"])):H("",!0)])):(I(),z(be,{key:1,"show-header-upgrade-icon":!1}))],void 0),_:1},8,["title","modelValue"]),N($e,{direction:i.direction,modelValue:i.createDrawerVisible,"onUpdate:modelValue":t[12]||(t[12]=e=>i.createDrawerVisible=e),"append-to-body":!0,"close-on-click-modal":!1,title:e.$t("Create Campaign"),size:"700px"},{footer:U(()=>[M("div",_a,[N(m,{onClick:t[11]||(t[11]=e=>i.createDrawerVisible=!1)},{default:U(()=>[W(J(e.$t("Cancel")),1)],void 0,!0),_:1}),N(m,{type:"primary",loading:i.creatingCampaign,onClick:l.submitCreateCampaign},{default:U(()=>[W(J(e.$t("Create Campaign")),1)],void 0,!0),_:1},8,["loading","onClick"])])]),default:U(()=>[M("div",oa,[N(ke,{"label-position":"top",class:"fcrm_campaign_setup_form"},{default:U(()=>[N(Ce,{label:e.$t("Internal Campaign Title")},{default:U(()=>[N(b,{ref:"createCampaignTitleInput",modelValue:i.createCampaignForm.title,"onUpdate:modelValue":t[9]||(t[9]=e=>i.createCampaignForm.title=e),placeholder:e.$t("Internal Campaign Title"),maxlength:"160","show-word-limit":"",onKeyup:te(l.submitCreateCampaign,["enter"])},null,8,["modelValue","placeholder","onKeyup"])],void 0,!0),_:1},8,["label"]),N(Ce,{label:e.$t("Choose an Editor")},{default:U(()=>[N(we,{modelValue:i.createCampaignForm.design_template,"onUpdate:modelValue":t[10]||(t[10]=e=>i.createCampaignForm.design_template=e),class:"fcrm_layout_choice_group"},{default:U(()=>[(I(!0),R(G,null,Q(l.layoutOptions,e=>(I(),z(ve,{key:e.id,value:e.id,disabled:e.disabled,class:"fcrm_layout_choice"},{default:U(()=>[M("span",ra,[N(o,{"icon-name":"circleFilled"})]),M("div",ca,[M("img",{src:e.image,alt:e.label},null,8,da)]),M("span",pa,J(e.label),1),M("span",ma,J(e.hint),1)],void 0,!0),_:2},1032,["value","disabled"]))),128))],void 0,!0),_:1},8,["modelValue"]),l.showNonGutenbergTip?(I(),R("div",ha,[M("span",ua,[N(o,{"icon-name":"el-icon-info"})]),W(" "+J(e.$t("Gutenberg editor is the default and recommended option for best compatibility and long-term support.")),1)])):H("",!0)],void 0,!0),_:1},8,["label"])],void 0,!0),_:1})])],void 0),_:1},8,["direction","modelValue","title"]),i.showingLabelsConfig?(I(),z(Se,{key:0,open:i.showingLabelsConfig,onClose:l.closeDrawer,onCallFetchLabels:l.fetchLabels},null,8,["open","onClose","onCallFetchLabels"])):H("",!0),i.previewingCampaign?(I(),R("div",ga,[N(Fe,{onModalClosed:t[13]||(t[13]=()=>{i.previewingCampaign=null}),auto_load:!0,by_campaign_id:!0,campaign:i.previewingCampaign},null,8,["campaign"])])):H("",!0)])}]]);export{fa as default}; diff --git a/wp-content/plugins/fluent-crm/assets/admin/Modules/Email/Campaigns/ViewCampaign.js b/wp-content/plugins/fluent-crm/assets/admin/Modules/Email/Campaigns/ViewCampaign.js new file mode 100644 index 0000000..ec36eed --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/admin/Modules/Email/Campaigns/ViewCampaign.js @@ -0,0 +1 @@ +import{aH as a,aI as e,aQ as t,aB as i,aw as s,aE as n,aF as l,aK as c,aL as o,aG as r,az as d,k as m,ax as p,ay as _,aS as u,W as g,m as h,aA as f,E as v,aJ as y,aD as k,U as b,A as w,H as $,aZ as C,e as S,a_ as T,av as E,a$ as x,b0 as P,g as j,ap as A}from"../../../../vendor-element-plus.js?ver=3.1.8";import{aQ as I,W as R,X as V,ab as F,a5 as D,J as M,az as U,Z as z,aa as B,a9 as L,a6 as N,ac as Y,Y as q,a8 as H,a0 as O,b2 as G,$ as W,av as J}from"../../../../vendor.js?ver=3.1.8";import{L as K,C as Q}from"../../../../_LinkMetrics.js?ver=3.1.8";import{D as Z}from"../../../../DataTable.js?ver=3.1.8";import{_ as X,I as aa}from"../../../../fc-bits-ui.js?ver=3.1.8";import{P as ea}from"../../../../PaginationBar.js?ver=3.1.8";import{G as ta}from"../../../../GenericPromo.js?ver=3.1.8";import{C as ia,a as sa,U as na}from"../../../../_CampaignDetails.js?ver=3.1.8";import{R as la}from"../../../../ReadableRecipientTagger.js?ver=3.1.8";import{I as ca}from"../../../../ItemCopier.js?ver=3.1.8";import{S as oa}from"../../../../TestEmail.js?ver=3.1.8";import{B as ra}from"../../../../BaseCard.js?ver=3.1.8";import"../../../../Confirm.js?ver=3.1.8";import"./_components/EmailPreview.js?ver=3.1.8";import"../../../../PreviewIframeBuilder.js?ver=3.1.8";import"../../../../Badge.js?ver=3.1.8";import"../../../../SettingsIcons.js?ver=3.1.8";import"../../../../PromoCard.js?ver=3.1.8";import"../../../../CampaignSubjectLines.js?ver=3.1.8";import"../../../../fc-bits.js?ver=3.1.8";const da={class:"fluentcrm_subject_metrics"},ma={class:"fcrm_table_header_inner_left_title"},pa={key:0,class:"fc_list"},_a=["href"],ua={key:1,class:"fcrm_secondary_text small"};const ga=X({name:"CampaignSubjectAnalytics",components:{DataTable:Z},props:["metrics","campaign"]},[["render",function(i,s,n,l,c,o){const r=t,d=e,m=a,p=I("data-table");return R(),V("div",da,[F(p,{"has-selection":!1,wrapper_border:!0},{"header-left":D(()=>[z("h3",ma,B(i.$t("Subject Analytics")),1)]),table:D(()=>[F(m,{border:"","empty-text":i.$t("No Data Found"),class:"fc_el_border_table",stripe:"",data:n.metrics.subjects,style:{width:"100%"}},{default:D(()=>[F(d,{type:"expand"},{default:D(a=>[a.row.metric.clicks.length?(R(),V("ul",pa,[(R(!0),V(M,null,U(a.row.metric.clicks,(a,e)=>(R(),V("li",{key:e},[z("a",{target:"_blank",rel:"noopener",href:a.url},B(a.url),9,_a),s[0]||(s[0]=L(" - ",-1)),F(r,{size:"small"},{default:D(()=>[L(B(a.total),1)],void 0,!0),_:2},1024)]))),128))])):(R(),V("span",ua,B(i.$t("No metrics found.")),1))]),_:1}),F(d,{label:i.$t("Subject")},{default:D(a=>[L(B(a.row.value),1)]),_:1},8,["label"]),F(d,{width:"140",label:i.$t("Email Sent %")},{default:D(a=>[L(B(i.percent(a.row.total,n.campaign.recipients_count))+" ("+B(a.row.total)+") ",1)]),_:1},8,["label"]),F(d,{width:"140",label:i.$t("Open Rate")},{default:D(a=>[L(B(i.percent(a.row.metric.total_opens,a.row.total))+" ",1),N(z("span",null,"("+B(a.row.metric.total_opens)+")",513),[[Y,a.row.metric.total_opens]])]),_:1},8,["label"]),F(d,{width:"140",label:i.$t("Click Rate")},{default:D(a=>[L(B(i.percent(a.row.metric.total_clicks,a.row.total))+" ",1),N(z("span",null,"("+B(a.row.metric.total_clicks)+")",513),[[Y,a.row.metric.total_clicks]])]),_:1},8,["label"])],void 0,!0),_:1},8,["empty-text","data"])]),_:1})])}]]),ha={class:"fc_revenue_report"},fa=["innerHTML"];const va=X({name:"RevenueReport",props:["campaign"],components:{PaginationBar:ea},data:()=>({orders:[],labels:[],pagination:{per_page:10,current_page:1,total:0},loading:!1}),methods:{fetchOrders(){this.loading=!0,this.$get(`campaigns/${this.campaign.id}/revenues`,{per_page:this.pagination.per_page,page:this.pagination.current_page}).then(a=>{this.orders=a.orders,this.labels=a.labels,this.pagination.total=a.total}).catch(a=>{this.handleError(a)}).finally(()=>{this.loading=!1})}},mounted(){this.fetchOrders()}},[["render",function(t,s,n,l,c,o){const r=e,d=a,m=I("pagination-bar"),p=i;return R(),V("div",ha,[z("h3",null,B(t.$t("Revenue from this email campaign")),1),c.loading?(R(),q(p,{key:1})):(R(),V(M,{key:0},[F(d,{data:c.orders,border:"",stripe:""},{default:D(()=>[(R(!0),V(M,null,U(c.labels,(a,e)=>(R(),q(r,{key:e,label:a},{default:D(a=>[z("span",{innerHTML:a.row[e]},null,8,fa)]),_:2},1032,["label"]))),128))],void 0),_:1},8,["data"]),F(m,{pagination:c.pagination,onFetch:o.fetchOrders},null,8,["pagination","onFetch"])],64))])}]]),ya={class:"fluentcrm_link_metrics"},ka={class:"fcrm_mb_20"},ba={class:"fcrm_primary_text fcrm_mb_6"},wa={class:"fcrm_secondary_text small"},$a={key:0,class:"fc_campaign_action_wrapper"},Ca={key:0},Sa={key:1,style:{color:"red"}},Ta={key:1},Ea={key:0,class:"text-align-center"},xa={key:0},Pa={key:1,class:"text-align-center"},ja={key:2},Aa={key:1},Ia={key:1};const Ra={class:"fc_campaign_report"},Va={key:0},Fa={class:"fc_report_title"},Da={class:"fc_report_value"},Ma={class:"fc_report_title"},Ua={key:2,class:"fc_report_value"},za={key:3,class:"fc_report_value"},Ba={key:1,style:{padding:"20px"}},La={class:"fc_campaign_report"},Na={class:""};const Ya={class:"fcrm_view_newsletter_content"};const qa={class:"fcrm_view_campaign_page"},Ha={class:"fcrm-layout-width"},Oa={key:0,class:"fcrm_page_header"},Ga={class:"fcrm_page_header_content"},Wa={class:"fcrm_page_header_breadcrumb"},Ja={key:0,class:"fcrm_inline_editable_input"},Ka={class:"fcrm_funnel_breadcrumb_title"},Qa={style:{width:"auto"},class:"status"},Za={class:"fcrm_page_header_actions"},Xa={key:2,class:"text-align-center"},ae={class:"icon"},ee={class:"icon"},te={class:"fcrm_sms_campaign_view--body fcrm_email_campaign_view--body"},ie={key:1,class:"fcrm_email_campaign_sending"},se={key:0,class:"fcrm_sms_campaign_view--progress-card fcrm_sms_campaign_view--paused-card"},ne={class:"fcrm_sms_campaign_view--progress-header"},le={class:"fcrm_sms_campaign_view--progress-title"},ce={class:"icon"},oe={key:1,class:"fcrm_sms_campaign_view--progress-card fcrm_sms_campaign_view--sending-card"},re={class:"fcrm_sms_campaign_view--progress-header"},de={class:"fcrm_sms_campaign_view--progress-title"},me={class:"fcrm_sms_campaign_view--live-status"},pe={class:"fcrm_sms_campaign_view--progress-percent"},_e={key:1,class:"fcrm_sms_campaign_view--scheduling-note"},ue={key:2,class:"fcrm_sms_campaign_view--progress-card fcrm_sms_campaign_view--scheduled-card"},ge={class:"fcrm_sms_campaign_view--progress-header"},he={class:"fcrm_sms_campaign_view--progress-title"},fe={class:"fcrm_sms_campaign_view--progress-description"},ve={class:"fcrm_sms_campaign_view--progress-actions"},ye={class:"fcrm_sms_campaign_view--scheduled-date"},ke={class:"icon"},be={class:"date"},we={key:0,class:"mb-10"},$e={class:"fcrm_email_campaign_stats_card_wrapper"},Ce={class:"fcrm_sms_campaign_view--stats-list"},Se={class:"fcrm_sms_campaign_view--stat-label"},Te={class:"fcrm_sms_campaign_view--stat-value"},Ee={class:"fcrm_sms_campaign_view--stat-label"},xe={class:"icon"},Pe={key:0,class:"fcrm_sms_campaign_view--stat-value"},je={key:1,class:"fcrm_sms_campaign_view--stat-value"},Ae={key:0,style:{padding:"20px 0 0"}},Ie={key:0},Re={key:1},Ve={key:0},Fe={key:1},De={class:"fcrm_perf_bars"},Me={class:"fcrm_perf_bar_header"},Ue={class:"fcrm_perf_bar_label"},ze={class:"fcrm_perf_value"},Be={class:"fcrm_perf_count"},Le={class:"fcrm_perf_pct"},Ne={class:"fcrm_perf_bar"},Ye={class:"fcrm_sms_campaign_view--link-list"},qe={key:1,style:{padding:"20px"}},He={key:5,style:{"min-height":"100px"},class:"fluentcrm_stat_cards"},Oe={class:"fluentcrm_cart_counter"},Ge={class:"fluentcrm_cart_counter"},We={class:"fluentcrm_cart_counter"},Je={key:0},Ke={key:1},Qe={key:0,class:"fcrm_sms_campaign_view--main fcrm_email_campaign_view--main"},Ze={class:"fcrm_primary_text fcrm_mb_16"},Xe={key:2,class:"fluentcrm_body_boxed"},at={key:3,class:"fluentcrm_body_boxed",style:{position:"relative"}},et={class:"fc_loading_bar"},tt={class:"dialog-footer"};const it=X({name:"ViewCampaign",components:{BaseCard:ra,SendTestEmail:oa,Icons:aa,CampaignEmails:Q,LinkMetrics:K,SubjectMetrics:ga,CampaignActions:X({name:"CampaignActions",props:["campaign"],components:{GenericPromo:ta},data:()=>({action_details:{action_type:"add_tags",tags:[],activity_type:"",link_ids:[]},available_tags:window.fcAdmin.available_tags,clicked_links:[],processing:!1,processing_page:1,total_count:"calculating...",errors:null,total_processed:0,is_completed:!1,click_status:null,open_status:null}),computed:{canDoAction(){return!0===this.click_status||!0===this.open_status},activity_types(){let a={email_open:this.$t("Cam_Select_Swote"),email_not_open:this.$t("Cam_Select_Swdnoe"),email_clicked:this.$t("Cam_Select_Swcsl")};return!0!==this.click_status&&delete a.email_clicked,!0!==this.open_status&&(delete a.email_open,delete a.email_not_open),a}},methods:{getClickedLinks(){this.$get(`campaigns/${this.campaign.id}/link-report`).then(a=>{this.clicked_links=a.links,this.click_status=a.click_status,this.open_status=a.open_status})},process(){return this.action_details.activity_type?this.action_details.tags.length?"email_clicked"!==this.action_details.activity_type||this.action_details.link_ids.length?(this.errors=null,this.processing=!0,void this.$post(`campaigns-pro/${this.campaign.id}/tag-actions`,{processing_page:this.processing_page,...this.action_details}).then(a=>{a.total_count&&(this.total_count=a.total_count),this.total_processed+=a.processed_contacts,a.has_more?(this.processing_page=this.processing_page+1,this.$nextTick(()=>{this.process()})):this.is_completed=!0}).catch(a=>{this.errors=a,this.handleError(a),this.processing=!1})):(this.$notify.error(this.$t("Please Select Clicked URLS")),!1):(this.$notify.error(this.$t("Please Select Tags first")),!1):(this.$notify.error(this.$t("Please Select Activity Type")),!1)},resetAction(){this.action_details={action_type:"add_tags",tags:[],activity_type:"email_open",link_ids:[]},this.is_completed=!1,this.total_processed=0,this.processing_page=1,this.processing=!1}},mounted(){this.getClickedLinks()}},[["render",function(a,e,t,i,g,h){const f=l,v=n,y=s,k=o,b=c,w=d,$=r,C=m,S=p,T=u,E=I("generic-promo"),x=_;return R(),V("div",ya,[z("div",ka,[z("h3",ba,B(a.$t("Campaign Actions")),1),z("p",wa,B(a.$t("Cam_Add_Remove_ToyCb")),1)]),a.has_campaign_pro?(R(),V("div",$a,[h.canDoAction?(R(),V("div",Ca,[g.processing?(R(),V("div",Ta,[g.is_completed?(R(),V("div",Pa,[z("h3",null,B(a.$t("All Done")),1),z("p",null,B(g.total_processed)+" "+B(a.$t("Cam_contacts_hbp")),1),F(C,{size:"small",onClick:e[5]||(e[5]=a=>h.resetAction()),type:"info"},{default:D(()=>[L(B(a.$t("Do another Action")),1)],void 0),_:1})])):(R(),V("div",Ea,[N((R(),V("h3",null,[L(B(a.$t("Processing now...")),1)])),[[x,g.processing]]),z("h4",null,B(a.$t("Rec_Please_dnctw")),1),z("h2",null,B(g.total_processed)+"/"+B(g.total_count),1),g.total_processed?(R(),V("p",xa,B(g.total_processed)+" "+B(a.$t("Cam_Contacts_psf")),1)):H("",!0)])),g.errors?(R(),V("div",ja,[z("h3",null,B(a.$t("Errors Found"))+":",1),z("pre",null,B(g.errors),1)])):H("",!0)])):(R(),q(S,{key:0,"label-position":"top",data:g.action_details},{default:D(()=>[F(y,{label:a.$t("Action Type")},{default:D(()=>[F(v,{modelValue:g.action_details.action_type,"onUpdate:modelValue":e[0]||(e[0]=a=>g.action_details.action_type=a)},{default:D(()=>[F(f,{value:"add_tags"},{default:D(()=>[L(B(a.$t("Add Tags")),1)],void 0,!0),_:1}),F(f,{value:"remove_tags"},{default:D(()=>[L(B(a.$t("Remove Tags")),1)],void 0,!0),_:1})],void 0,!0),_:1},8,["modelValue"])],void 0,!0),_:1},8,["label"]),F(y,{label:a.$t("Select Tags")},{default:D(()=>[F(b,{multiple:!0,modelValue:g.action_details.tags,"onUpdate:modelValue":e[1]||(e[1]=a=>g.action_details.tags=a)},{default:D(()=>[(R(!0),V(M,null,U(g.available_tags,a=>(R(),q(k,{key:a.id,value:a.id,label:a.title},null,8,["value","label"]))),128))],void 0,!0),_:1},8,["modelValue"])],void 0,!0),_:1},8,["label"]),F(y,{label:a.$t("Filter Subscribers")},{default:D(()=>[F(v,{modelValue:g.action_details.activity_type,"onUpdate:modelValue":e[2]||(e[2]=a=>g.action_details.activity_type=a)},{default:D(()=>[(R(!0),V(M,null,U(h.activity_types,(a,e)=>(R(),q(f,{key:e,label:e},{default:D(()=>[L(B(a),1)],void 0,!0),_:2},1032,["label"]))),128))],void 0,!0),_:1},8,["modelValue"])],void 0,!0),_:1},8,["label"]),"email_clicked"==g.action_details.activity_type?(R(),q(y,{key:0,label:a.$t("Cam_Select_Utwc_MfaS")},{default:D(()=>[g.clicked_links.length?(R(),q($,{key:0,class:"fc_new_line_items",modelValue:g.action_details.link_ids,"onUpdate:modelValue":e[3]||(e[3]=a=>g.action_details.link_ids=a)},{default:D(()=>[(R(!0),V(M,null,U(g.clicked_links,a=>(R(),q(w,{key:a.id,value:a.id},{default:D(()=>[L(B(a.url)+" - ("+B(a.total)+") ",1)],void 0,!0),_:2},1032,["value"]))),128))],void 0,!0),_:1},8,["modelValue"])):(R(),V("p",Sa,B(a.$t("Cam_Sorry_nlftcc")),1))],void 0,!0),_:1},8,["label"])):H("",!0),F(y,null,{default:D(()=>[F(C,{onClick:e[4]||(e[4]=a=>h.process()),size:"small",type:"primary"},{default:D(()=>[L(B("add_tags"==g.action_details.action_type?a.$t("Add Tags to Subscribers"):a.$t("Cam_Remove_TFS")),1)],void 0,!0),_:1})],void 0,!0),_:1})],void 0),_:1},8,["data"]))])):(R(),V("div",Aa,[F(T,{type:"info",title:a.$t("Click & Open tracking is required to perform this action."),description:a.$t("Email Tracking was disabled for this email campaign."),"show-icon":!0},null,8,["title","description"])]))])):(R(),V("div",Ia,[F(E)]))])}]]),RevenueReport:va,Unsubscribers:na,ReadableRecipients:la,CampaignEmailProcessStat:sa,CampaignSummaryDetails:ia,IntermediateCampaignStat:X({name:"IntermediateCampaignStat",components:{LinkMetrics:K,Message:h,InfoFilled:g},props:["campaign_id"],data:()=>({sent_count:0,stat:[],analytics:[],loading:!1}),methods:{getPercent(a){return this.sent_count?parseFloat(a/this.sent_count*100).toFixed(2)+"%":"--"},getCampaignAllStats(){this.loading=!0,this.$get(`campaigns/${this.campaign_id}/overview_stats`).then(a=>{this.sent_count=a.sent_count,this.stat=a.stat,this.analytics=a.analytics}).catch(a=>{this.handleError(a)}).finally(()=>{this.loading=!1})}},mounted(){this.getCampaignAllStats()}},[["render",function(a,e,t,s,n,l){const c=I("Message"),o=v,r=I("InfoFilled"),d=y,m=i,p=f,_=I("link-metrics"),u=k;return R(),q(u,{class:"fc_campaign_archived_wrapper",gutter:30},{default:D(()=>[F(p,{md:12,sm:12,xs:24},{default:D(()=>[z("div",Ra,[z("h3",null,B(a.$t("Current Status")),1),n.loading?(R(),V("div",Ba,[F(m,{rows:5,animated:!0})])):(R(),V("ul",Va,[(R(!0),V(M,null,U(n.stat,e=>(R(),V("li",{key:e.status},[F(o,null,{default:D(()=>[F(c)],void 0,!0),_:1}),z("span",Fa,B(a.ucFirst(e.status))+" "+B(a.$t("Emails")),1),z("span",Da,B(e.total),1)]))),128)),(R(!0),V(M,null,U(n.analytics,e=>(R(),V("li",{key:e.type,class:O("fc_camp_data_"+e.type)},[z("i",{class:O(e.icon_class)},null,2),z("span",Ma,B(e.label),1),"open"==e.type?(R(),q(d,{key:0,class:"item",effect:"dark",content:a.$t("open_rate_info"),placement:"top-start"},{default:D(()=>[F(o,null,{default:D(()=>[F(r)],void 0,!0),_:1})],void 0,!0),_:1},8,["content"])):"click"==e.type?(R(),q(d,{key:1,class:"item",effect:"dark",content:a.$t("click_rate_info"),placement:"top-start"},{default:D(()=>[F(o,null,{default:D(()=>[F(r)],void 0,!0),_:1})],void 0,!0),_:1},8,["content"])):H("",!0),e.is_percent?(R(),V("span",Ua,B(l.getPercent(e.total)),1)):(R(),V("span",za,B(e.total),1))],2))),128))]))])],void 0,!0),_:1}),F(p,{md:12,sm:12,xs:24},{default:D(()=>[z("div",La,[z("h3",null,B(a.$t("Link activity")),1),z("div",Na,[F(_,{hide_title:!0,campaign_id:t.campaign_id},null,8,["campaign_id"])])])],void 0,!0),_:1})],void 0),_:1})}]]),ViewNewsletter:X({name:"ViewNewsletterSettings",props:["campaign_id"],components:{ItemCopier:ca},data:()=>({newsletterUrl:"",loading:!1}),methods:{fetchNewsletterUrl(){this.loading=!0,this.$get(`campaigns/${this.campaign_id}/share-url`).then(a=>{this.newsletterUrl=a.sharable_url}).catch(a=>{this.$handleError(a)}).finally(()=>{this.loading=!1})}},mounted(){this.fetchNewsletterUrl()}},[["render",function(a,e,t,i,s,n){const l=I("item-copier");return R(),V("div",Ya,[z("h3",null,B(a.$t("Share Newsletter via URL")),1),z("p",null,B(a.$t("Share_Newsletter_Desc")),1),F(l,{text:s.newsletterUrl,"show-view-button":!0,loading:s.loading},null,8,["text","loading"])])}]]),Refresh:$,Share:w,Loading:b,InfoFilled:g},data(){return{ArrowRightBold:J(A),activeTab:"campaign_details",loading:!0,campaign:null,emails:[],dialogVisible:!1,sent_count:0,repeatingCall:!1,stat:[],analytics:{},request_counter:1,subject_analytics:{},campaign_id:this.$route.params.id,fetch_status:!0,updating:!1,shareModal:!1,statusPollTimer:null,show_title_input:!1,editableTitle:""}},computed:{emailStatsData(){var a,e,t,i,s;const n=this.sent_count,l=parseInt((null==(a=this.campaign)?void 0:a.recipients_count)||0);return[{name:"Sent",value:n,color:"#7B61FF"},{name:"Opened",value:parseInt((null==(e=this.analytics.open)?void 0:e.total)||0),color:"#22D3BB"},{name:"Clicked",value:parseInt((null==(t=this.analytics.click)?void 0:t.total)||0),color:"#F6B51E"},{name:"Unsubscribed",value:parseInt((null==(i=this.analytics.unsubscribe)?void 0:i.total)||0),color:"#E1E4EA"},{name:"Bounced",value:parseInt((null==(s=this.stat.find(a=>"bounced"===a.status))?void 0:s.total)||0),color:"#335CFF"}].map(a=>({...a,percent:l?Math.min(a.value/l*100,100):0,pctText:l?(a.value/l*100).toFixed(1):"0.0"}))}},methods:{canCancelEmail(){const a=this.campaign.status;return"scheduled"==a||"pending-scheduled"==a||"processing"==a},backToCampaigns(){this.$router.push({name:"campaigns",query:{t:(new Date).getTime()}})},getCampaignStatus(){this.loading=!0,this.$get(`campaigns/${this.campaign_id}/status`,{request_counter:this.request_counter}).then(a=>{this.campaign||"draft"!==a.campaign.status?(this.campaign=a.campaign,this.stat=a.stat,this.sent_count=a.sent_count,this.analytics=a.analytics,this.subject_analytics=a.subject_analytics,a.campaign.scheduling_range||"working"!==a.campaign.status||this.fetchStatAgain(),this.changeTitle(this.campaign.title+" - Campaign")):this.$router.push({name:"campaign",params:{id:a.campaign.id}})}).catch(a=>{this.campaign&&this.fetchStatAgain()}).finally(()=>{this.loading=!1})},fetchStatAgain(){this.statusPollTimer&&clearTimeout(this.statusPollTimer),this.statusPollTimer=setTimeout(()=>{this.request_counter+=1,this.getCampaignStatus()},4e3)},scheduledAt(a){return null===a?this.$t("Not Scheduled"):this.nsDateFormat(a,"MMMM Do, YYYY [at] h:mm A")},getCampaignPercent(){return parseInt(this.sent_count/this.campaign.recipients_count*100)},getPercent(a){return this.sent_count?parseFloat(a/this.sent_count*100).toFixed(2)+"%":"--"},showInputField(){var a;this.editableTitle=(null==(a=this.campaign)?void 0:a.title)||"",this.show_title_input=!0,this.$nextTick(()=>{const a=this.$refs.titleInput,e=Array.isArray(a)?a[0]:a;e&&e.focus&&e.focus()})},cancelInlineTitle(){var a;this.show_title_input=!1,this.editableTitle=(null==(a=this.campaign)?void 0:a.title)||""},saveInlineTitle(){const a=(this.editableTitle||"").trim();a&&(a!==this.campaign.title?this.updateCampaignTitle(a):this.show_title_input=!1)},updateCampaignTitle(a){this.updating=!0,this.$put(`campaigns/${this.campaign_id}/title`,{title:a,scheduled_at:this.campaign.scheduled_at}).then(a=>{this.campaign=a.campaign,this.editableTitle=a.campaign.title,this.show_title_input=!1,this.$notify.success(a.message)}).catch(a=>{this.handleError(a)}).finally(()=>{this.updating=!1})},pauseSending(){this.updating=!0,this.$post(`campaigns/${this.campaign_id}/pause`).then(a=>{this.campaign=a.campaign,this.$notify.success(a.message)}).catch(a=>{this.handleError(a)}).finally(()=>{this.updating=!1})},resumeSending(){this.updating=!0,this.$post(`campaigns/${this.campaign_id}/resume`).then(a=>{this.campaign=a.campaign,this.$notify.success(a.message)}).catch(a=>{this.handleError(a)}).finally(()=>{this.updating=!1,this.getCampaignStatus()})},cancelSchedule(){this.loading=!0,this.$post(`campaigns/${this.campaign_id}/un-schedule`).then(a=>{this.$notify.success(a.message),this.$router.push({name:"campaign",params:{id:this.campaign_id},query:{t:(new Date).getTime(),step:3}})}).catch(a=>{this.handleError(a)}).finally(()=>{this.loading=!1,this.getCampaignStatus()})},handleUnscheduled(){this.$router.push({name:"campaign",params:{id:this.campaign.id},query:{t:(new Date).getTime(),step:3}})},handleResyncRevenue(){this.loading=!0,this.$post(`campaigns/${this.campaign.id}/revenues/resync`).then(a=>{this.$notify.success(a.message),this.analytics.revenue.total=a.total}).catch(a=>{this.handleError(a)}).finally(()=>{this.loading=!1})}},mounted(){this.getCampaignStatus(),this.changeTitle(this.$t("Campaign"))},beforeUnmount(){this.statusPollTimer&&(clearTimeout(this.statusPollTimer),this.statusPollTimer=null)}},[["render",function(a,e,s,n,l,c){const o=C,r=S,d=m,p=I("Icons"),g=T,h=I("send-test-email"),f=y,k=I("Share"),b=v,w=I("campaign-email-process-stat"),$=I("Loading"),A=E,Y=u,J=I("Refresh"),K=I("InfoFilled"),Q=I("BaseCard"),Z=t,X=I("link-metrics"),aa=I("intermediate-campaign-stat"),ea=i,ta=I("campaign-summary-details"),ia=x,sa=I("campaign-emails"),na=I("unsubscribers"),la=I("subject-metrics"),ca=I("revenue-report"),oa=I("campaign-actions"),ra=I("readable-recipients"),da=P,ma=I("view-newsletter"),pa=j,_a=_;return R(),V("div",qa,[z("div",Ha,[l.campaign?(R(),V("div",Oa,[z("div",Ga,[z("div",Wa,[F(g,{"separator-icon":l.ArrowRightBold},{default:D(()=>[F(o,{to:{name:"campaigns"}},{default:D(()=>[L(B(a.$t("Campaigns")),1)],void 0,!0),_:1}),F(o,{class:"fcrm_funnel_title_editable_wrap"},{default:D(()=>[l.show_title_input?(R(),V("div",Ja,[F(r,{ref:"titleInput",modelValue:l.editableTitle,"onUpdate:modelValue":e[0]||(e[0]=a=>l.editableTitle=a),size:"small",placeholder:a.$t("Internal Campaign Title"),onKeyup:[e[1]||(e[1]=G(a=>c.saveInlineTitle(),["enter"])),e[2]||(e[2]=G(a=>c.cancelInlineTitle(),["esc"]))]},null,8,["modelValue","placeholder"]),F(d,{size:"small",type:"primary",loading:l.updating,disabled:l.updating||!l.editableTitle||!l.editableTitle.trim(),onClick:e[3]||(e[3]=a=>c.saveInlineTitle())},{default:D(()=>[L(B(a.$t("Save")),1)],void 0,!0),_:1},8,["loading","disabled"]),F(d,{size:"small",disabled:l.updating,onClick:e[4]||(e[4]=a=>c.cancelInlineTitle())},{default:D(()=>[L(B(a.$t("Cancel")),1)],void 0,!0),_:1},8,["disabled"])])):(R(),V(M,{key:1},[z("div",Ka,[L(B(l.campaign.title)+" ",1),z("span",Qa," - "+B(l.campaign.status),1)]),z("span",{class:"icon-edit",style:{cursor:"pointer"},onClick:e[5]||(e[5]=(...a)=>c.showInputField&&c.showInputField(...a))},[F(p,{"icon-name":"EditPen"})])],64))],void 0,!0),_:1})],void 0),_:1},8,["separator-icon"])])]),z("div",Za,[c.canCancelEmail()?N((R(),q(d,{key:0,disabled:l.loading,onClick:e[6]||(e[6]=a=>c.cancelSchedule()),type:"danger",size:"small"},{default:D(()=>[L(B(a.$t("Cancel Schedule")),1)],void 0),_:1},8,["disabled"])),[[_a,l.loading]]):H("",!0),"pending-scheduled"==l.campaign.status||"processing"==l.campaign.status?(R(),q(h,{key:1,campaign:l.campaign,btn_class:"small"},null,8,["campaign"])):H("",!0),"working"==l.campaign.status?(R(),V("div",Xa,[F(f,{class:"item",effect:"dark",content:a.$t("Vie_Emails_asatm"),placement:"top"},{default:D(()=>[F(d,{size:"small",onClick:e[7]||(e[7]=a=>c.pauseSending())},{default:D(()=>[L(B(a.$t("Pause Sending")),1)],void 0,!0),_:1})],void 0),_:1},8,["content"])])):H("",!0),F(f,{class:"item",effect:"dark",content:a.$t("Refresh Campaign"),placement:"top"},{default:D(()=>[F(d,{onClick:c.getCampaignStatus,size:"small",class:"only-icon-btn small"},{default:D(()=>[z("span",ae,[F(p,{"icon-name":"reload"})])],void 0,!0),_:1},8,["onClick"])],void 0),_:1},8,["content"]),F(f,{class:"item",effect:"dark",content:a.$t("Share Campaign"),placement:"top"},{default:D(()=>[!l.campaign||"archived"!=l.campaign.status&&"working"!=l.campaign.status?H("",!0):(R(),q(d,{key:0,onClick:e[8]||(e[8]=a=>l.shareModal=!0),size:"small",class:"only-icon-btn small"},{default:D(()=>[z("span",ee,[F(b,null,{default:D(()=>[F(k)],void 0,!0),_:1})])],void 0,!0),_:1}))],void 0),_:1},8,["content"])])])):H("",!0),l.campaign?(R(),V(M,{key:1},[z("div",te,["pending-scheduled"==l.campaign.status||"processing"==l.campaign.status?(R(),q(w,{key:0,onUnscheduled:e[9]||(e[9]=a=>c.handleUnscheduled()),campaign:l.campaign},null,8,["campaign"])):(R(),V("div",ie,["paused"==l.campaign.status?(R(),V("div",se,[z("div",ne,[z("div",le,B(a.$t("Vie_This_cino_sNEwbs")),1)]),F(d,{onClick:e[10]||(e[10]=a=>c.resumeSending())},{default:D(()=>[z("span",ce,[F(p,{"icon-name":"play"})]),L(" "+B(a.$t("Resume Sending")),1)],void 0),_:1})])):"working"==l.campaign.status?(R(),V("div",oe,[l.campaign.scheduling_range?(R(),V("p",_e,B(a.$t("Emails have been scheduled from %s to %s. Scheduled emails will be sent automatically.",l.campaign.scheduling_range.start,l.campaign.scheduling_range.end)),1)):(R(),V(M,{key:0},[z("div",re,[z("div",de,[L(B(a.$t("Your emails are being sent now..."))+" ",1),z("span",me,[F(b,{class:"fcrm_sms_campaign_view--live-spinner is-loading"},{default:D(()=>[F($)],void 0),_:1}),z("span",null,B(a.$t("Sending")),1)])]),z("span",pe,B(c.getCampaignPercent())+"%",1)]),F(A,{percentage:c.getCampaignPercent(),"stroke-width":8,"text-inside":!1,color:"#8F6ED6","show-text":!1,class:"fcrm_sms_campaign_view--progress-bar"},null,8,["percentage"])],64))])):"scheduled"==l.campaign.status?(R(),V("div",ue,[z("div",ge,[z("div",he,B(a.$t("Vie_This_chbs")),1),z("p",fe,B(a.$t("Vie_The_ewbsboysdat")),1)]),z("div",ve,[z("div",ye,[z("span",ke,[F(p,{"icon-name":"calendarWithTime"})]),L(" "+B(a.$t("Scheduled on"))+": ",1),z("span",be,B(a.nsDateFormat(l.campaign.scheduled_at,"MMMM Do, YYYY [at] h:mm A")),1)])])])):H("",!0),"archived"==l.campaign.status?(R(),V(M,{key:3},[a.appVars.addons.email_open_tracking&&a.appVars.addons.email_click_tracking?H("",!0):(R(),V("div",we,[a.appVars.addons.email_open_tracking?H("",!0):(R(),q(Y,{key:0,class:"mb-10",closable:!1,type:"warning"},{default:D(()=>[L(B(a.$t("Email Open tracking is disabled via PHP Hook")),1)],void 0),_:1})),a.appVars.addons.email_click_tracking?H("",!0):(R(),q(Y,{key:1,closable:!1,type:"warning"},{default:D(()=>[L(B(a.$t("Email Click tracking is disabled via PHP Hook")),1)],void 0),_:1}))])),z("div",$e,[F(Q,null,{title:D(()=>[z("h4",null,B(a.$t("Campaign Performance")),1)]),body:D(()=>[z("ul",Ce,[(R(!0),V(M,null,U(l.stat,e=>(R(),V("li",{class:"fcrm_sms_campaign_view--stat-item",key:e.status},[z("span",Se,B(a.ucFirst(e.status))+" "+B(a.$t("Emails")),1),z("span",Te,B(e.total),1)]))),128)),(R(!0),V(M,null,U(l.analytics,e=>(R(),V("li",{class:O(["fcrm_sms_campaign_view--stat-item","fcrm_camp_data_"+e.type]),key:e.type},[z("span",Ee,[L(B(e.label)+" ",1),"revenue"==e.type?(R(),q(d,{key:0,size:"small",title:a.$t("Re-Sync Revenue"),onClick:c.handleResyncRevenue,class:"small only-icon-btn"},{default:D(()=>[z("span",xe,[l.loading?(R(),q(b,{key:0},{default:D(()=>[F($)],void 0,!0),_:1})):(R(),q(b,{key:1},{default:D(()=>[F(J)],void 0,!0),_:1}))])],void 0,!0),_:1},8,["title","onClick"])):H("",!0),"open"==e.type?(R(),q(f,{key:1,class:"item",effect:"dark",content:a.$t("Open_Rate_Info"),placement:"top-start"},{default:D(()=>[F(b,null,{default:D(()=>[F(K)],void 0,!0),_:1})],void 0,!0),_:1},8,["content"])):"click"==e.type?(R(),q(f,{key:2,class:"item",effect:"dark",content:a.$t("click_rate_info"),placement:"top-start"},{default:D(()=>[F(b,null,{default:D(()=>[F(K)],void 0,!0),_:1})],void 0,!0),_:1},8,["content"])):H("",!0)]),e.is_percent?(R(),V("span",Pe,B(c.getPercent(e.total)),1)):(R(),V("span",je,B(e.total),1))],2))),128))]),"archived"===l.campaign.status?(R(),V("div",Ae,[!0!==l.campaign.click_tracking_status?(R(),q(Y,{key:0,style:{"margin-bottom":"10px"},type:"warning",closable:!1},{default:D(()=>["anonymous"===l.campaign.click_tracking_status?(R(),V("span",Ie,B(a.$t("Email click tracking is set to anonymous for this campaign. The stats are aggregated and may not reflect the actual number.")),1)):(R(),V("span",Re,B(a.$t("Email click tracking was disabled for this campaign.")),1))],void 0,!0),_:1})):H("",!0),!0!==l.campaign.open_tracking_status?(R(),q(Y,{key:1,style:{"margin-bottom":"10px"},type:"info",closable:!1},{default:D(()=>["anonymous"===l.campaign.open_tracking_status?(R(),V("span",Ve,B(a.$t("Email open tracking is set to anonymous for this campaign. The stats are aggregated and may not reflect the actual number.")),1)):(R(),V("span",Fe,B(a.$t("Email open tracking was disabled for this campaign.")),1))],void 0,!0),_:1})):H("",!0)])):H("",!0)]),_:1}),F(Q,null,{title:D(()=>[z("h4",null,B(a.$t("Emails Stats")),1)]),body:D(()=>[z("div",De,[(R(!0),V(M,null,U(c.emailStatsData,e=>(R(),V("div",{key:e.name,class:"fcrm_perf_row"},[z("div",Me,[z("span",Ue,B(a.$t(e.name)),1),z("div",ze,[z("span",Be,B(e.value.toLocaleString()),1),z("span",Le,B(e.pctText)+"%",1)])]),z("div",Ne,[z("div",{class:"fcrm_perf_bar_inner",style:W({width:e.percent+"%",backgroundColor:e.color})},null,4)])]))),128))])]),_:1}),F(Q,{"no-body-padding":!0},{title:D(()=>{var e,t;return[z("h4",null,[L(B(a.$t("Link activity"))+" ",1),l.campaign&&"anonymous"===(null==(e=l.campaign.settings)?void 0:e.click_tracker)?(R(),q(Z,{key:0,size:"small",type:"warning"},{default:D(()=>[L(B(a.$t("anonymous")),1)],void 0,!0),_:1})):H("",!0),l.campaign&&!1===(null==(t=l.campaign.settings)?void 0:t.click_tracker)?(R(),q(Z,{key:1,size:"small",type:"warning"},{default:D(()=>[L(B(a.$t("disabled")),1)],void 0,!0),_:1})):H("",!0)])]}),body:D(()=>[z("ul",Ye,[l.loading?H("",!0):(R(),q(X,{key:0,hide_title:!0,campaign_id:l.campaign.id},null,8,["campaign_id"]))])]),_:1})])],64)):"working"==l.campaign.status&&l.campaign.scheduling_range?(R(),V(M,{key:4},[l.loading?(R(),V("div",qe,[F(ea,{rows:5,animated:!0})])):(R(),q(aa,{key:0,campaign_id:l.campaign.id},null,8,["campaign_id"]))],64)):N((R(),V("ul",He,[(R(!0),V(M,null,U(l.stat,e=>(R(),V("li",{key:e.status},[z("h4",null,B(a.ucFirst(e.status))+" "+B(a.$t("Emails")),1),z("div",Oe,B(e.total),1)]))),128)),z("li",null,[z("h4",null,B(a.$t("Total Emails")),1),z("div",Ge,B(l.campaign.recipients_count),1)]),(R(!0),V(M,null,U(l.analytics,a=>(R(),V("li",{key:a.type,class:O("fc_camp_data_"+a.type)},[z("div",We,[a.is_percent?(R(),V("span",Je,B(c.getPercent(a.total)),1)):(R(),V("span",Ke,B(a.total),1))]),z("h4",null,B(a.label),1)],2))),128))])),[[_a,l.loading&&"working"!=l.campaign.status]])]))]),"pending-scheduled"!==l.campaign.status&&"processing"!==l.campaign.status?(R(),V("div",Qe,[F(da,{modelValue:l.activeTab,"onUpdate:modelValue":e[12]||(e[12]=a=>l.activeTab=a),type:"border-card","tab-position":"top",style:{"min-height":"200px"},class:"fcrm_sms_campaign_view--tabs fcrm_email_campaign_view--tabs"},{default:D(()=>[F(ia,{name:"campaign_details",label:a.$t("Campaign Details")},{default:D(()=>[F(ta,{campaign:l.campaign},null,8,["campaign"])],void 0,!0),_:1},8,["label"]),F(ia,{lazy:!0,name:"campaign_subscribers",label:a.$t("Emails")},{default:D(()=>[F(sa,{onFetchCampaign:e[11]||(e[11]=a=>c.getCampaignStatus()),campaign_id:l.campaign.id},null,8,["campaign_id"])],void 0,!0),_:1},8,["label"]),l.analytics.unsubscribe?(R(),q(ia,{key:0,lazy:!0,name:"campaign_unsubscribers",label:a.$t("Unsubscribers")},{default:D(()=>[F(na,{campaign_id:l.campaign.id},null,8,["campaign_id"])],void 0,!0),_:1},8,["label"])):H("",!0),l.subject_analytics.subjects?(R(),q(ia,{key:1,name:"campaign_subject_analytics",label:a.$t("A/B Testing Result")},{default:D(()=>[F(la,{campaign:l.campaign,metrics:l.subject_analytics},null,8,["campaign","metrics"])],void 0,!0),_:1},8,["label"])):H("",!0),l.analytics.revenue?(R(),q(ia,{key:2,name:"campaign_revenue",label:a.$t("Revenue Report")},{default:D(()=>["campaign_revenue"==l.activeTab?(R(),q(ca,{key:0,campaign:l.campaign},null,8,["campaign"])):H("",!0)],void 0,!0),_:1},8,["label"])):H("",!0),F(ia,{lazy:!0,name:"campaign_actions",label:a.$t("Actions")},{default:D(()=>[F(oa,{campaign:l.campaign},null,8,["campaign"])],void 0,!0),_:1},8,["label"]),l.campaign?(R(),q(ia,{key:3,lazy:!0,name:"campaign_selections",label:a.$t("Contact Selections")},{default:D(()=>[z("h2",Ze,B(a.$t("Contact Selections")),1),F(ra,{settings:l.campaign.settings,"already-sent":"archived"===l.campaign.status},null,8,["settings","already-sent"])],void 0,!0),_:1},8,["label"])):H("",!0)],void 0),_:1},8,["modelValue"])])):H("",!0)],64)):(R(),V("div",Xe,[F(ea,{rows:10,animated:!0})])),l.loading&&l.campaign&&"working"!=l.campaign.status?(R(),V("div",at,[z("div",et,[F(A,{class:"el-progress_animated","show-text":!1,percentage:30})]),l.loading?(R(),q(ea,{key:0,style:{padding:"20px"},rows:7})):H("",!0)])):H("",!0)]),l.campaign?(R(),q(pa,{key:0,"append-to-body":!0,"close-on-click-modal":!1,modelValue:l.shareModal,"onUpdate:modelValue":e[14]||(e[14]=a=>l.shareModal=a),"modal-class":"fcrm_share_newsletter_modal",width:"40%"},{footer:D(()=>[z("span",tt,[F(d,{onClick:e[13]||(e[13]=a=>l.shareModal=!1)},{default:D(()=>[L(B(a.$t("Close")),1)],void 0,!0),_:1})])]),default:D(()=>[l.shareModal?(R(),q(ma,{key:0,campaign_id:l.campaign.id},null,8,["campaign_id"])):H("",!0)],void 0),_:1},8,["modelValue"])):H("",!0)])}]]);export{it as default}; diff --git a/wp-content/plugins/fluent-crm/assets/admin/Modules/Email/Campaigns/_components/EmailPreview.js b/wp-content/plugins/fluent-crm/assets/admin/Modules/Email/Campaigns/_components/EmailPreview.js new file mode 100644 index 0000000..e0b0642 --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/admin/Modules/Email/Campaigns/_components/EmailPreview.js @@ -0,0 +1 @@ +import{T as e,_ as i,ay as a,aS as l,k as s,aT as t}from"../../../../../vendor-element-plus.js?ver=3.1.8";import{aQ as n,W as o,X as c,ab as r,a5 as p,a6 as m,a8 as d,Z as v,aa as _,J as w,az as f,Y as u,a0 as b,a9 as h}from"../../../../../vendor.js?ver=3.1.8";import{P as g}from"../../../../../PreviewIframeBuilder.js?ver=3.1.8";import{B as k}from"../../../../../Badge.js?ver=3.1.8";import{_ as L,I as y}from"../../../../../fc-bits-ui.js?ver=3.1.8";const M={key:0},$={key:1,class:"fcrm_email_preview_email_body"},C={class:"fcrm_email_preview_email_header"},V={class:"label"},x={class:"content"},B={class:"value"},P={class:"label"},j={class:"content"},Z={class:"value"},I={class:"label"},O={class:"content"},T={class:"value"},D={class:"label"},S={class:"content"},E={class:"value"},H={class:"label"},z={class:"content"},A={class:"value"},F={class:"fcrm_email_preview_email_stats"},J={class:"label"},N={class:"content"},Q={class:"fcrm_email_preview_email_stat_item"},U={class:"value"},W={class:"fcrm_email_preview_email_stat_item"},X={class:"value"},Y={key:0},q={class:"label"},G={class:"content"},K={class:"value"},R={key:1,class:"fcrm_email_preview_email_clicks"},ee={class:"label"},ie={class:"content"},ae={class:"fcrm_preview_toolbar"},le={class:"fcrm_preview_device_toggle"},se={class:"fcrm_device_btn_group"},te=["title"],ne=["title"],oe=["title"],ce={key:0,class:"fc_device_notch"},re={key:1,class:"fc_device_home"},pe={class:"dialog-footer"};const me=L({name:"EmailPreview",props:["preview"],components:{Badge:k,PreviewIframeBuilder:g,Icons:y,FolderOpened:i,Location:e},data:()=>({direction:"rtl",email:null,info:{},loading:!1,previewMode:"desktop"}),computed:{deviceLabel(){return{desktop:this.$t("Desktop"),tablet:this.$t("Tablet")+" · 768px",mobile:this.$t("Mobile")+" · 375px"}[this.previewMode]||""}},mounted(){window.fcAdmin&&window.fcAdmin.is_rtl&&(this.direction="ltr")},methods:{fetch(){this.email=null,this.loading=!0,this.$get(`campaigns/emails/${this.preview.id}/preview`).then(e=>{this.email=e.email,this.info=e.info}).finally(()=>{this.loading=!1})},onOpen(){this.fetch()},onClosed(){this.preview.isVisible=!1,this.previewMode="desktop"}}},[["render",function(e,i,g,k,L,y){const me=n("Badge"),de=l,ve=n("Icons"),_e=n("preview-iframe-builder"),we=s,fe=t,ue=a;return o(),c("div",null,[r(fe,{direction:L.direction,size:"70%",title:e.$t("Email Preview"),onOpen:y.onOpen,"append-to-body":!0,"close-on-click-modal":!1,modelValue:g.preview.isVisible,"onUpdate:modelValue":i[4]||(i[4]=e=>g.preview.isVisible=e),"modal-class":"fcrm_email_preview_dialog"},{footer:p(()=>[v("span",pe,[r(we,{onClick:i[3]||(i[3]=e=>g.preview.isVisible=!1)},{default:p(()=>[h(_(e.$t("Close")),1)],void 0,!0),_:1})])]),default:p(()=>[L.email?d("",!0):m((o(),c("div",M,null,512)),[[ue,L.loading]]),L.email?(o(),c("div",$,[v("div",C,[v("ul",null,[v("li",null,[v("span",V,_(e.$t("Status")),1),v("span",x,[v("span",B,[r(me,{type:L.info.status},null,8,["type"])])])]),v("li",null,[v("span",P,_(e.$t("Campaign")),1),v("span",j,[v("span",Z,_(L.info.campaign?L.info.campaign.title:"n/a"),1)])]),v("li",null,[v("span",I,_(e.$t("Subject")),1),v("span",O,[v("span",T,_(L.email.subject),1)])]),v("li",null,[v("span",D,_(e.$t("To")),1),v("span",S,[v("span",E,_(L.email.to.name)+" <"+_(L.email.to.email)+">",1)])]),v("li",null,[v("span",H,_(e.$t("Date")),1),v("span",z,[v("span",A,_(e.nsDateFormat(L.info.scheduled_at)),1)])]),v("li",F,[v("span",J,_(e.$t("Stats")),1),v("span",N,[v("span",Q,[i[5]||(i[5]=v("span",{class:"icon"},[v("svg",{width:"16",height:"16",viewBox:"0 0 16 16",fill:"none",xmlns:"http://www.w3.org/2000/svg"},[v("path",{d:"M2.1458 4.91239L7.694 1.58599C7.78725 1.53004 7.89395 1.50049 8.0027 1.50049C8.11145 1.50049 8.21815 1.53004 8.3114 1.58599L13.8542 4.91299C13.8987 4.93964 13.9355 4.97736 13.961 5.02247C13.9866 5.06758 14 5.11855 14 5.17039V12.8C14 12.9591 13.9368 13.1117 13.8243 13.2243C13.7117 13.3368 13.5591 13.4 13.4 13.4H2.6C2.44087 13.4 2.28826 13.3368 2.17574 13.2243C2.06321 13.1117 2 12.9591 2 12.8V5.16979C1.99999 5.11795 2.01341 5.06698 2.03897 5.02187C2.06452 4.97676 2.10133 4.93904 2.1458 4.91239ZM3.2 5.67979V12.2H12.8V5.67919L8.0024 2.79919L3.2 5.67919V5.67979ZM8.036 9.01879L11.2136 6.34099L11.9864 7.25899L8.0444 10.5812L4.0184 7.26319L4.7816 6.33679L8.036 9.01879V9.01879Z",fill:"var(--fc-secondary-text)"})])],-1)),v("span",U,_(L.info.is_open||0),1)]),i[7]||(i[7]=v("span",{class:"dotted-shape"},null,-1)),v("span",W,[i[6]||(i[6]=v("span",{class:"icon"},[v("svg",{width:"16",height:"16",viewBox:"0 0 16 16",fill:"none",xmlns:"http://www.w3.org/2000/svg"},[v("path",{d:"M10.0328 8.89878L11.564 13.1072L8.74522 14.1332L7.21342 9.92478L4.87402 11.3918L5.84602 1.77979L12.7682 8.51898L10.0334 8.89878H10.0328ZM10.0262 12.3896L8.39722 7.91358L10.1732 7.66758L6.78922 4.37358L6.31522 9.07158L7.83322 8.11938L9.46222 12.5954L10.0262 12.3896V12.3896Z",fill:"var(--fc-secondary-text)"})])],-1)),v("span",X,_(L.info.click_counter||0),1)])])]),L.info.note?(o(),c("li",Y,[v("span",q,_(e.$t("Note")),1),v("span",G,[v("span",K,_(L.info.note),1)])])):d("",!0),L.email.clicks&&L.email.clicks.length?(o(),c("li",R,[v("span",ee,_(e.$t("Email Clicks")),1),v("span",ie,[(o(!0),c(w,null,f(L.email.clicks,e=>(o(),c("p",{key:e.id},_(e.url)+" ("+_(e.counter)+")",1))),128))])])):d("",!0)])]),"sent"==L.info.status?(o(),u(de,{key:0,class:"fcrm_email_preview_email_warning",title:e.$t("preview_email_info"),type:"warning","show-icon":"",closable:!1},null,8,["title"])):d("",!0),v("div",ae,[v("div",le,[v("div",se,[v("button",{type:"button",class:b(["fcrm_device_btn",{active:"desktop"===L.previewMode}]),onClick:i[0]||(i[0]=e=>L.previewMode="desktop"),title:e.$t("Desktop Preview")},[r(ve,{"icon-name":"desktop"})],10,te),v("button",{type:"button",class:b(["fcrm_device_btn",{active:"tablet"===L.previewMode}]),onClick:i[1]||(i[1]=e=>L.previewMode="tablet"),title:e.$t("Tablet Preview")},[r(ve,{"icon-name":"tablet"})],10,ne),v("button",{type:"button",class:b(["fcrm_device_btn",{active:"mobile"===L.previewMode}]),onClick:i[2]||(i[2]=e=>L.previewMode="mobile"),title:e.$t("Mobile Preview")},[r(ve,{"icon-name":"mobile"})],10,oe)])])]),v("div",{class:b(["fcrm_preview_stage","fcrm_preview_stage_"+L.previewMode])},[v("div",{class:b(["fc_device_frame","fc_device_frame_"+L.previewMode])},["mobile"===L.previewMode?(o(),c("div",ce)):d("",!0),r(_e,{frame_height:"80vh",show_audit:!1,preview_html:L.email.body},null,8,["preview_html"]),"mobile"===L.previewMode?(o(),c("div",re)):d("",!0)],2)],2)])):d("",!0)],void 0),_:1},8,["direction","title","onOpen","modelValue"])])}]]);export{me as default}; diff --git a/wp-content/plugins/fluent-crm/assets/admin/Modules/Email/EmailSequences/AllSequences.js b/wp-content/plugins/fluent-crm/assets/admin/Modules/Email/EmailSequences/AllSequences.js new file mode 100644 index 0000000..1b04e1e --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/admin/Modules/Email/EmailSequences/AllSequences.js @@ -0,0 +1 @@ +import{ax as e,aw as t,e as a,k as s,ay as i,g as l,c as n,R as o,as as r,a6 as c,ac as d,a2 as u,j as p,h as m,i as h,aB as g,aH as _,aI as f,E as y,aR as q,aS as v,n as b}from"../../../../vendor-element-plus.js?ver=3.1.8";import{W as S,Y as $,a5 as w,Z as k,ab as C,ax as T,b2 as V,aa as D,a6 as x,a9 as E,aQ as A,X as P,a8 as j,J as B}from"../../../../vendor.js?ver=3.1.8";import{_ as I,I as F,a as N,T as U}from"../../../../fc-bits-ui.js?ver=3.1.8";import{C as O}from"../../../../Confirm.js?ver=3.1.8";import{P as M}from"../../../../PaginationBar.js?ver=3.1.8";import{I as H}from"../../../../InlineDoc.js?ver=3.1.8";import{T as J}from"../../../../TopNav.js?ver=3.1.8";import{P as K}from"../../../../PageHeader.js?ver=3.1.8";import{D as R}from"../../../../DataTable.js?ver=3.1.8";import{F as Q}from"../../../../FloatingBulkActionShell.js?ver=3.1.8";const Y={name:"CreateSequence",props:{modelValue:{type:Boolean,default:!1}},emits:["update:modelValue","toggleDialog"],data:()=>({sequence:{title:""},errors:{title:""},saving:!1}),computed:{isVisible:{get(){return this.modelValue},set(e){this.$emit("update:modelValue",e),this.$emit("toggleDialog",e)}}},methods:{save(){this.sequence.title?(this.errors={title:""},this.saving=!0,this.$post("sequences",this.sequence).then(e=>{this.$notify.success(e.message),this.$router.push({name:"edit-sequence",params:{id:e.sequence.id}})}).catch(e=>{const t=e.data?e.data:e;if(t.status&&403===t.status)this.notify.error(t.message);else if(t.title){const e=Object.keys(t.title);this.errors.title=t.title[e[0]]}}).finally(e=>{this.saving=!1})):this.errors.title=this.$t("Title field is required")}}},z={class:"error"},L={class:"dialog-footer"};const G={class:"fcrm_email_sequences_page"},W={class:"fcrm_page_header_top_nav_wrapper"},X={class:"fcrm_page_header_top_nav"},Z={class:"fcrm-layout-width"},ee={key:0},te={class:"icon"},ae={class:"el-popover__reference"},se={class:"icon"},ie={class:"icon"},le={class:"icon"},ne={class:"fcrm_empty_state"},oe={class:"fcrm_empty_state_text"},re={key:0},ce=["title"],de={style:{"vertical-align":"middle"}},ue={key:1},pe=["aria-label"],me={class:"el-popover__reference"},he={class:"icon"},ge={class:"el-popover__reference"},_e={class:"icon"},fe={class:"el-popover__reference"},ye={class:"icon"},qe={class:"el-popover__reference"},ve={class:"icon"},be={class:"fcrm_bulk_action_bar"},Se={class:"fcrm_bulk_action_left"},$e={class:"fc_bulk_selection_count"},we={key:0,class:"fc_bulk_divider","aria-hidden":"true"},ke={key:2,class:"fc_bulk_divider","aria-hidden":"true"},Ce={class:"icon"},Te={class:"fcrm_import_content"},Ve={class:"upload-icon"},De={class:"el-upload__text"};const xe=I({name:"all-sequences",components:{Icons:F,PageHeader:K,TopNav:J,DataTable:R,FloatingBulkActionShell:Q,CreateSequence:I(Y,[["render",function(n,o,r,c,d,u){const p=a,m=t,h=e,g=s,_=l,f=i;return S(),$(_,{title:n.$t("Create new email sequence"),modelValue:u.isVisible,"onUpdate:modelValue":o[2]||(o[2]=e=>u.isVisible=e),"append-to-body":!0,"close-on-click-modal":!1,width:"640px",class:"fc-create-email-sequence"},{footer:w(()=>[k("span",L,[x((S(),$(g,{disabled:d.saving,type:"primary",onClick:o[1]||(o[1]=e=>u.save())},{default:w(()=>[E(D(n.$t("Next")),1)],void 0,!0),_:1},8,["disabled"])),[[f,d.saving]])])]),default:w(()=>[k("div",null,[C(h,{onSubmit:T(u.save,["prevent"]),model:d.sequence,"label-position":"top"},{default:w(()=>[C(m,{label:n.$t("Sequence Title")},{default:w(()=>[C(p,{ref:"title",placeholder:n.$t("Sequence Title"),modelValue:d.sequence.title,"onUpdate:modelValue":o[0]||(o[0]=e=>d.sequence.title=e),onKeyup:V(u.save,["enter"])},null,8,["placeholder","modelValue","onKeyup"]),k("span",z,D(d.errors.title),1)],void 0,!0),_:1},8,["label"])],void 0,!0),_:1},8,["onSubmit","model"])])],void 0),_:1},8,["title","modelValue"])}]]),Confirm:O,PaginationBar:M,InlineDoc:H,EditPen:u,CopyDocument:d,Delete:c,ArrowDownBold:r,Money:o,Close:n},data:()=>({sequences:[],pagination:{total:0,per_page:10,current_page:1},loading:!0,dialogVisible:!1,duplicating:!1,search:"",options:{sampleCsv:null,delimiter:"comma"},order:"desc",orderBy:"id",selection:!1,selectedSequences:[],deleting:!1,importDialogVisible:!1,inline_errors:null,allSelected:!1,current_mode:"system"===U.getCurrentTheme()?U.getSystemTheme():U.getCurrentTheme()}),computed:{url(){let e=window.ajaxurl;return e+=(e.match(/\?/)?"&":"?")+jQuery.param({action:"fluentcrm_import_sequence",_nonce:window.fcAdmin.rest.nonce}),e},canSelectAll(){if(!this.pagination||!this.pagination.per_page||!this.pagination.total)return!1;const e=this.selectedSequences.length===this.pagination.per_page,t=this.selectedSequences.length{void 0!==e[a]&&(t[a]=e[a])}),window.fcrm_seq_sub_params=t,t.t=Date.now(),this.$router.replace({name:"email-sequences",query:t}),delete e.t,this.$get("sequences",e).then(e=>{this.sequences=e.sequences.data,this.pagination.total=e.sequences.total}).catch(e=>{this.handleError(e)}).finally(()=>{this.loading=!1})},remove(e){this.$del(`sequences/${e.id}`).then(e=>{this.fetch(),this.$notify.success({title:this.$t("Great!"),message:e.message,offset:19})}).catch(e=>{this.handleError(e)})},duplicateSequence(e){this.duplicating=!0,this.$post(`sequences/${e.id}/duplicate`).then(e=>{this.$notify.success(e.message),this.$router.push({name:"edit-sequence",params:{id:e.sequence.id}})}).catch(e=>{this.handleError(e)}).finally(()=>{this.duplicating=!1})},exportSequence(e){location.href=window.ajaxurl+"?"+jQuery.param({action:"fluentcrm_export_sequence",sequence_id:e.id,_nonce:window.fcAdmin.rest.nonce})},handleSortable(e){"descending"===e.order?(this.orderBy=e.prop,this.order="desc"):(this.orderBy=e.prop,this.order="asc"),this.fetch()},onSelection(e){this.selection=!!e.length,this.selectedSequences=e,e.length>0&&e.length!==this.pagination.per_page&&(this.allSelected=!1),e.length||(this.allSelected=!1)},confirmAndDeleteSelected(){b.confirm(this.$t("Are you sure you want to delete the selected sequences?"),this.$t("Delete Sequences"),{confirmButtonText:this.$t("Delete"),cancelButtonText:this.$t("Cancel"),type:"warning"}).then(()=>{this.deleteSelected()}).catch(()=>{})},selectAllSequences(){this.canSelectAll&&(this.$refs.sequencesTable&&this.sequences&&this.sequences.length>0&&this.sequences.forEach(e=>{this.$refs.sequencesTable.toggleRowSelection(e,!0)}),this.allSelected=!0)},selectOnlyThisPage(){this.allSelected=!1;const e=this.sequences.map(e=>e.id);this.selectedSequences=this.selectedSequences.filter(t=>e.includes(t.id))},clearSequenceSelection(){this.$refs.sequencesTable&&this.$refs.sequencesTable.clearSelection(),this.selectedSequences=[],this.allSelected=!1,this.selection=!1},deleteSelected(){let e={sequence_ids:[]};this.allSelected?(e.select_all=!0,e.search=this.search||""):e.sequence_ids=this.selectedSequences.map(e=>e.id),this.deleting=!0,this.$post("sequences/do-bulk-action",e).then(e=>{this.$notify.success(e.message),this.fetch(),this.clearSequenceSelection()}).catch(e=>{this.handleError(e)}).finally(()=>{this.deleting=!1})},success(e){this.$notify.success(e.message),this.$router.push({name:"edit-sequence",params:{id:e.sequence.id}})},error(e){try{const t=JSON.parse(e.message);this.$notify.error(t.message),t.requires&&"string"==typeof t.requires&&(this.inline_errors=t.requires)}catch(t){this.$notify.error(t.message||this.$t("An error occurred. Please try again."))}},onThemeChanged(e){var t;this.current_mode=(null==(t=e.detail)?void 0:t.effective)||("system"===U.getCurrentTheme()?U.getSystemTheme():U.getCurrentTheme())}},mounted(){window.addEventListener(N,this.onThemeChanged),this.setup(),this.fetch(),this.changeTitle(this.$t("Email Sequences"))},beforeUnmount(){window.removeEventListener(N,this.onThemeChanged)}},[["render",function(e,t,n,o,r,c){const d=A("TopNav"),u=A("Icons"),b=s,T=h,I=m,F=p,N=A("inline-doc"),U=A("page-header"),O=a,M=g,H=A("icons"),J=f,K=A("router-link"),R=A("Money"),Q=y,Y=A("confirm"),z=_,L=A("pagination-bar"),xe=A("data-table"),Ee=A("Close"),Ae=A("floating-bulk-action-shell"),Pe=A("create-sequence"),je=q,Be=v,Ie=l,Fe=i;return S(),P("div",G,[k("div",W,[k("div",X,[C(d)])]),k("div",Z,[C(U,null,{title:w(()=>[E(D(e.$t("Email Sequences"))+" ",1),r.pagination.total?(S(),P("small",ee,"("+D(e.formatMoney(r.pagination.total))+")",1)):j("",!0)]),actions:w(()=>[e.hasPermission("fcrm_manage_emails")?(S(),$(F,{key:0,trigger:"click"},{dropdown:w(()=>[C(I,null,{default:w(()=>[C(T,{class:"fc_dropdown_action",onClick:t[0]||(t[0]=e=>r.importDialogVisible=!0)},{default:w(()=>[k("span",ae,[k("span",se,[C(u,{"icon-name":"import"})]),E(" "+D(e.$t("Import")),1)])],void 0,!0),_:1})],void 0,!0),_:1})]),default:w(()=>[C(b,{class:"el-dropdown-link"},{default:w(()=>[E(D(e.$t("More Actions"))+" ",1),k("span",te,[C(u,{"icon-name":"downIcon"})])],void 0,!0),_:1})],void 0,!0),_:1})):j("",!0),C(N,{doc_id:1601}),e.hasPermission("fcrm_manage_emails")?(S(),$(b,{key:1,type:"primary",onClick:t[1]||(t[1]=e=>r.dialogVisible=!0)},{default:w(()=>[k("span",ie,[C(u,{"icon-name":"plus"})]),E(" "+D(e.$t("Add Sequence")),1)],void 0,!0),_:1})):j("",!0)]),_:1}),C(xe,{"has-selection":!1},{"header-left":w(()=>[C(O,{clearable:"",size:"small",modelValue:r.search,"onUpdate:modelValue":t[2]||(t[2]=e=>r.search=e),onClear:c.fetch,onKeyup:V(c.fetch,["enter"]),placeholder:e.$t("Type and Enter...")},{prefix:w(()=>[k("span",le,[C(u,{"icon-name":"search"})])]),_:1},8,["modelValue","onClear","onKeyup","placeholder"])]),table:w(()=>[!r.loading&&!r.duplicating||r.sequences.length?x((S(),$(z,{key:1,stripe:"",border:"",ref:"sequencesTable",data:r.sequences,onSortChange:c.handleSortable,onSelectionChange:c.onSelection},{empty:w(()=>[k("div",ne,[C(H,{"icon-name":"common-empty-state"}),k("div",oe,[k("span",null,D(e.$t("Create your first email sequence to automate your email campaigns.")),1)])])]),default:w(()=>[C(J,{type:"selection",width:45}),C(J,{sortable:"custom","min-width":250,label:e.$t("Title"),prop:"title"},{default:w(e=>[C(K,{to:{name:"edit-sequence",params:{id:e.row.id},query:{t:(new Date).getTime()}}},{default:w(()=>[E(D(e.row.title),1)],void 0,!0),_:2},1032,["to"])]),_:1},8,["label"]),C(J,{width:190,label:e.$t("Emails")},{default:w(t=>[t.row.stats?(S(),P("span",re,[C(K,{to:{name:"edit-sequence",params:{id:t.row.id},query:{t:(new Date).getTime()}}},{default:w(()=>[E(D(e.$_n("%d Email","%d Emails",t.row.stats.emails)),1)],void 0,!0),_:2},1032,["to"]),t.row.stats.revenue&&t.row.stats.revenue.currency?(S(),P("span",{key:0,title:e.$t("Revenue From Sequence Emails")},[k("span",de,[C(Q,null,{default:w(()=>[C(R)],void 0,!0),_:1})]),E(" "+D(t.row.stats.revenue.currency)+" "+D(t.row.stats.revenue.amount),1)],8,ce)):j("",!0)])):(S(),P("span",ue,"--"))]),_:1},8,["label"]),C(J,{width:170,sortable:"custom",label:e.$t("Subscribers"),prop:"recipients_count"},{default:w(t=>[C(K,{to:{name:"sequence-subscribers",params:{id:t.row.id},query:{t:(new Date).getTime()}}},{default:w(()=>[k("span",null,D(e.$_n("%d subscriber","%d subscribers",t.row.stats.subscribers)),1)],void 0,!0),_:2},1032,["to"])]),_:1},8,["label"]),C(J,{width:180,sortable:"custom",label:e.$t("Created at"),prop:"created_at"},{default:w(t=>[k("span",null,D(e.nsHumanDiffTime(t.row.created_at)),1)]),_:1},8,["label"]),C(J,{fixed:"right",width:"60","class-name":"fcrm_table_actions_cell"},{default:w(t=>[C(F,{trigger:"click",placement:"bottom-end"},{dropdown:w(()=>[C(I,null,{default:w(()=>[e.hasPermission("fcrm_manage_emails")?(S(),$(T,{key:0,onClick:a=>e.$router.push({name:"edit-sequence",params:{id:t.row.id},query:{t:(new Date).getTime()}})},{default:w(()=>[k("span",me,[k("span",he,[C(u,{"icon-name":"EditPen"})]),E(" "+D(e.$t("Edit")),1)])],void 0,!0),_:1},8,["onClick"])):j("",!0),e.hasPermission("fcrm_manage_emails")?(S(),$(T,{key:1,onClick:e=>c.duplicateSequence(t.row)},{default:w(()=>[k("span",ge,[k("span",_e,[C(u,{"icon-name":"duplicate"})]),E(" "+D(e.$t("Duplicate")),1)])],void 0,!0),_:1},8,["onClick"])):j("",!0),e.hasPermission("fcrm_read_emails")?(S(),$(T,{key:2,onClick:e=>c.exportSequence(t.row)},{default:w(()=>[k("span",fe,[k("span",ye,[C(u,{"icon-name":"export"})]),E(" "+D(e.$t("Export")),1)])],void 0,!0),_:1},8,["onClick"])):j("",!0),e.hasPermission("fcrm_manage_email_delete")?(S(),$(T,{key:3,class:"fcrm_danger_action"},{default:w(()=>[C(Y,{placement:"top-start",message:e.$t("Are you sure you want to delete this Sequence?"),onYes:e=>c.remove(t.row)},{reference:w(()=>[k("span",qe,[k("span",ve,[C(u,{"icon-name":"delete"})]),E(" "+D(e.$t("Delete")),1)])]),_:1},8,["message","onYes"])],void 0,!0),_:2},1024)):j("",!0)],void 0,!0),_:2},1024)]),default:w(()=>[k("span",{class:"el-dropdown-link",role:"button",tabindex:"0","aria-label":e.$t("More actions")},[C(u,{"icon-name":"more_actions"})],8,pe)],void 0,!0),_:2},1024)]),_:1})],void 0,!0),_:1},8,["data","onSortChange","onSelectionChange"])),[[Fe,r.loading||r.duplicating]]):(S(),$(M,{key:0,style:{padding:"20px"},rows:7}))]),pagination:w(()=>[C(L,{pagination:r.pagination,onFetch:c.fetch},null,8,["pagination","onFetch"])]),_:1}),C(Ae,{visible:r.selection&&!r.loading&&!r.duplicating&&r.pagination.total,"theme-mode":r.current_mode,"custom-layout":!0},{default:w(()=>[k("div",be,[k("div",Se,[C(b,{link:"","aria-label":e.$t("Deselect"),onClick:c.clearSequenceSelection},{default:w(()=>[C(Q,null,{default:w(()=>[C(Ee)],void 0,!0),_:1})],void 0,!0),_:1},8,["aria-label","onClick"]),k("span",$e,[r.allSelected?(S(),P(B,{key:0},[E(D(e.$t("All"))+" ",1),k("strong",null,D(r.pagination.total),1),E(" "+D(e.$t("selected")),1)],64)):(S(),P(B,{key:1},[k("strong",null,D(r.selectedSequences.length),1),E(" "+D(e.$t("selected")),1)],64))]),c.canSelectAll&&!r.allSelected?(S(),P("span",we)):j("",!0),c.canSelectAll&&!r.allSelected?(S(),$(b,{key:1,link:"",onClick:c.selectAllSequences},{default:w(()=>[E(D(e.$t("Select All"))+" "+D(r.pagination.total),1)],void 0,!0),_:1},8,["onClick"])):j("",!0),r.allSelected?(S(),P("span",ke)):j("",!0),r.allSelected?(S(),$(b,{key:3,link:"",onClick:c.selectOnlyThisPage},{default:w(()=>[E(D(e.$t("Select only this page")),1)],void 0,!0),_:1},8,["onClick"])):j("",!0),t[5]||(t[5]=k("span",{class:"fc_bulk_divider","aria-hidden":"true"},null,-1)),e.hasPermission("fcrm_manage_email_delete")?x((S(),$(b,{key:4,disabled:r.deleting,type:"danger",size:"small",plain:"",onClick:c.confirmAndDeleteSelected},{default:w(()=>[k("span",Ce,[C(u,{"icon-name":"delete"})]),E(" "+D(e.$t("Delete")),1)],void 0,!0),_:1},8,["disabled","onClick"])),[[Fe,r.deleting]]):j("",!0)])])],void 0),_:1},8,["visible","theme-mode"]),C(Pe,{modelValue:r.dialogVisible,"onUpdate:modelValue":t[3]||(t[3]=e=>r.dialogVisible=e)},null,8,["modelValue"]),C(Ie,{title:e.$t("Import Sequence"),modelValue:r.importDialogVisible,"onUpdate:modelValue":t[4]||(t[4]=e=>r.importDialogVisible=e),"append-to-body":!0,"close-on-click-modal":!1,width:"640px","modal-class":"fcrm_import_dialog"},{default:w(()=>[k("div",Te,[k("h3",null,D(e.$t("Upload JSON File")),1),C(je,{drag:"",limit:1,action:c.url,ref:"uploader",multiple:!1,"on-error":c.error,"on-success":c.success,"aria-label":e.$t("Upload JSON file by dragging or clicking")},{default:w(()=>[k("span",Ve,[C(u,{"icon-name":"upload"})]),k("div",De,D(e.$t("Choose a file or drag & drop it here.")),1),C(b,null,{default:w(()=>[E(D(e.$t("Browse File")),1)],void 0,!0),_:1})],void 0,!0),_:1},8,["action","on-error","on-success","aria-label"]),C(Be,{title:e.$t("Email_Sequence_Import_Note"),type:"info","show-icon":"",closable:!1},null,8,["title"]),r.inline_errors?(S(),$(Be,{key:0,title:r.inline_errors,type:"error","show-icon":"",closable:!1},null,8,["title"])):j("",!0)])],void 0),_:1},8,["title","modelValue"])])])}]]);export{xe as default}; diff --git a/wp-content/plugins/fluent-crm/assets/admin/Modules/Email/EmailSequences/EditEmail.js b/wp-content/plugins/fluent-crm/assets/admin/Modules/Email/EmailSequences/EditEmail.js new file mode 100644 index 0000000..5496454 --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/admin/Modules/Email/EmailSequences/EditEmail.js @@ -0,0 +1 @@ +import{W as e,ay as a,a_ as t,aZ as l,aD as i,aA as s,aw as d,e as m,bc as o,aJ as n,E as u,aK as r,aL as _,az as c,aG as p,ax as h,k as v,ap as f}from"../../../../vendor-element-plus.js?ver=3.1.8";import{aQ as g,a6 as b,W as y,X as $,ab as V,a5 as S,a9 as q,aa as j,Y as w,a8 as k,Z as E,a0 as C,av as T}from"../../../../vendor.js?ver=3.1.8";import{E as U}from"../../../../BlockComposer.js?ver=3.1.8";import{I as P}from"../../../../_FormBuilder2.js?ver=3.1.8";import{S as D}from"../../../../TestEmail.js?ver=3.1.8";import{P as x}from"../../../../PageHeader.js?ver=3.1.8";import{_ as M}from"../../../../fc-bits-ui.js?ver=3.1.8";import"../../../../input-popover-dropdown.js?ver=3.1.8";import"../../../../data_config.js?ver=3.1.8";import"../../../../EmailPreview.js?ver=3.1.8";import"../../../../PreviewIframeBuilder.js?ver=3.1.8";import"../../../../CampaignSubjectLines.js?ver=3.1.8";import"../../../../PaginationBar.js?ver=3.1.8";import"../../../../_MergeCodes.js?ver=3.1.8";import"../../../../BuiltinTemplateDrawer.js?ver=3.1.8";import"../../../../PromoCard.js?ver=3.1.8";import"../../../../fc-bits.js?ver=3.1.8";import"../../../../PhotoWidget.js?ver=3.1.8";import"../../../../_OptionSelector.js?ver=3.1.8";import"../../../../_AjaxSelector.js?ver=3.1.8";import"../../../../_VerifiedEmailInput.js?ver=3.1.8";const I={class:"fcrm_edit_email_sequence_page fcrm_sticky_block_composer_page"},B={class:"fcrm_edit_email_sequence_body"},H={key:0,class:"fcrm_edit_email_sequence_schedule--config"},F={class:"fcrm_edit_email_sequence_schedule--datetime"},W={class:"fcrm_edit_email_sequence_schedule--specific-days"},A={key:0},K={key:1,class:"fluentcrm_body fluentcrm_pad_30 text-align-center"};const L=M({name:"SequenceEmailEdit",props:["sequence_id","email_id"],emits:["getVisualData"],components:{EmailBlockComposer:U,InputPopover:P,SendTestEmail:D,PageHeader:x,InfoFilled:e},data:()=>({ArrowRightBold:T(f),sequence:{},email:{},loading:!1,saving:!1,app_loaded:!1,smartcodes:window.fcAdmin.globalSmartCodes,email_subject_status:!0,is_dirty:""}),watch:{email_id(){this.fetchSequenceEmail()}},methods:{backToSequence(){this.$router.push({name:"edit-sequence",params:{id:this.sequence_id},query:{t:(new Date).getTime()}})},fetchSequenceEmail(){this.loading=!0,this.$get(`sequences/${this.sequence_id}/email/${this.email_id}`,{with:["sequence"]}).then(e=>{this.sequence=e.sequence,this.email=e.email}).catch(e=>{this.handleError(e)}).finally(()=>{this.loading=!1,this.app_loaded=!0})},save(){if(!this.email.email_body)return this.$notify.error({title:this.$t("Oops!"),message:this.$t("Cam_Please_peb"),offset:19});if(!this.email.email_subject)return this.$notify.error({title:this.$t("Oops!"),message:this.$t("Cam_Please_peS"),offset:19});this.saving=!0,this.is_dirty=!1;const e={route_method:"create",email:JSON.stringify(this.email),sequence_id:this.sequence_id};parseInt(this.email_id)&&(e.route_method="update",e.mail_id=this.email_id),this.$post("sequences/sequence-email-update-create",e).then(e=>{this.$notify.success(e.message),parseInt(this.email_id)||this.$router.push({name:"edit-sequence-email",params:{sequence_id:e.email.parent_id,email_id:e.email.id}})}).catch(e=>{this.handleError(e)}).finally(()=>{this.saving=!1,this.is_dirty=!1})},maybeSave(){"visual_builder"==this.email.design_template?this.$bus.emit("getVisualData",{}):this.save()},resetSubject(){this.email_subject_status=!1,this.$nextTick(()=>{this.email_subject_status=!0})},changingSpecificDaysStatus(){"yes"!=this.email.settings.timings.selected_days_only||this.email.settings.timings.allowed_days||(this.email.settings.timings.allowed_days=["Mon","Tue","Wed","Thu","Fri","Sat","Sun"])},handleChangeContent(){""!==this.is_dirty?this.is_dirty=!0:this.is_dirty=!1},initKeyboardSave(e){(window.navigator.platform.match("Mac")?e.metaKey:e.ctrlKey)&&"s"===e.key&&(e.preventDefault(),this.maybeSave())}},mounted(){this.fetchSequenceEmail(),document.addEventListener("keydown",this.initKeyboardSave)},beforeRouteLeave(e,a,t){if(this.is_dirty){if(!window.confirm(this.$t("Unsaved_Confirm_Msg")))return!1}this.unmountBlockEditor(),document.removeEventListener("keydown",this.initKeyboardSave),t()}},[["render",function(e,f,T,U,P,D){const x=l,M=t,L=g("send-test-email"),R=g("page-header"),O=g("input-popover"),z=d,J=s,N=m,Z=i,G=g("InfoFilled"),Q=u,X=n,Y=o,ee=_,ae=r,te=c,le=p,ie=h,se=v,de=g("email-block-composer"),me=a;return b((y(),$("div",I,[V(R,null,{breadcrumb:S(()=>[V(M,{"separator-icon":P.ArrowRightBold},{default:S(()=>[V(x,{to:{name:"email-sequences"}},{default:S(()=>[q(j(e.$t("Email Sequences")),1)],void 0,!0),_:1}),V(x,{to:{name:"edit-sequence",params:{id:T.sequence_id}}},{default:S(()=>[q(j(P.sequence.title),1)],void 0,!0),_:1},8,["to"]),P.email.email_subject?(y(),w(x,{key:0},{default:S(()=>[q(j(P.email.email_subject),1)],void 0,!0),_:1})):k("",!0)],void 0,!0),_:1},8,["separator-icon"])]),actions:S(()=>[V(L,{campaign:P.email},null,8,["campaign"])]),_:1}),E("div",B,[P.app_loaded?(y(),$("div",H,[V(ie,{"label-position":"top",model:P.email},{default:S(()=>[V(Z,{gutter:16},{default:S(()=>[V(J,{lg:12,md:12,sm:24},{default:S(()=>[V(z,{label:e.$t("Email Subject")},{default:S(()=>[P.email_subject_status?(y(),w(O,{key:0,doc_url:"https://fluentcrm.com/docs/merge-codes-smart-codes-usage/",popper_extra:"fc_with_c_fields",placeholder:e.$t("Email Subject"),data:P.smartcodes,modelValue:P.email.email_subject,"onUpdate:modelValue":f[0]||(f[0]=e=>P.email.email_subject=e)},null,8,["placeholder","data","modelValue"])):k("",!0)],void 0,!0),_:1},8,["label"])],void 0,!0),_:1}),V(J,{lg:12,md:12,sm:24},{default:S(()=>[V(z,{label:e.$t("Email Pre-Header")},{default:S(()=>[V(N,{placeholder:e.$t("Email Pre-Header"),modelValue:P.email.email_pre_header,"onUpdate:modelValue":f[1]||(f[1]=e=>P.email.email_pre_header=e)},null,8,["placeholder","modelValue"])],void 0,!0),_:1},8,["label"])],void 0,!0),_:1})],void 0,!0),_:1}),V(Z,{gutter:16},{default:S(()=>[V(J,{lg:12,md:12,sm:24},{default:S(()=>[E("div",F,[V(z,{class:"fluentcrm_width_input"},{label:S(()=>[q(j(e.$t("Sending Time Range"))+" ",1),V(X,{class:"box-item",effect:"dark",content:e.$t("Edi_If_ysatrtFstettt"),placement:"top-start"},{default:S(()=>[V(Q,null,{default:S(()=>[V(G)],void 0,!0),_:1})],void 0,!0),_:1},8,["content"])]),default:S(()=>[V(Y,{"is-range":"","value-format":"HH:mm",format:"HH:mm",modelValue:P.email.settings.timings.sending_time,"onUpdate:modelValue":f[2]||(f[2]=e=>P.email.settings.timings.sending_time=e),"range-separator":e.$t("To"),"start-placeholder":e.$t("Start Range"),"end-placeholder":e.$t("End Range")},null,8,["modelValue","range-separator","start-placeholder","end-placeholder"])],void 0,!0),_:1}),V(z,{label:e.$t("Delay"),class:"fcrm_edit_email_sequence_schedule--delay"},{label:S(()=>[q(j(e.$t("Delay"))+" ",1),V(X,{class:"box-item",effect:"dark",content:e.$t("Set after how many")+" "+(P.email.settings.timings.delay_unit||e.$t("time unit"))+" "+e.$t("the email will be triggered from the starting date"),placement:"top-start"},{default:S(()=>[V(Q,null,{default:S(()=>[V(G)],void 0,!0),_:1})],void 0,!0),_:1},8,["content"])]),default:S(()=>[V(N,{modelValue:P.email.settings.timings.delay,"onUpdate:modelValue":f[4]||(f[4]=e=>P.email.settings.timings.delay=e)},{suffix:S(()=>[V(ae,{modelValue:P.email.settings.timings.delay_unit,"onUpdate:modelValue":f[3]||(f[3]=e=>P.email.settings.timings.delay_unit=e),size:"large"},{default:S(()=>[V(ee,{value:"minutes",label:e.$t("Minutes")},null,8,["label"]),V(ee,{value:"hours",label:e.$t("Hours")},null,8,["label"]),V(ee,{value:"days",label:e.$t("Days")},null,8,["label"]),V(ee,{value:"weeks",label:e.$t("Weeks")},null,8,["label"]),V(ee,{value:"Months",label:e.$t("Months")},null,8,["label"])],void 0,!0),_:1},8,["modelValue"])]),_:1},8,["modelValue"])],void 0,!0),_:1},8,["label"])])],void 0,!0),_:1}),V(J,{lg:12,md:12,sm:24},{default:S(()=>[V(z,null,{default:S(()=>[E("div",W,[V(te,{onChange:f[5]||(f[5]=e=>D.changingSpecificDaysStatus()),"true-value":"yes","false-value":"no",modelValue:P.email.settings.timings.selected_days_only,"onUpdate:modelValue":f[6]||(f[6]=e=>P.email.settings.timings.selected_days_only=e)},{default:S(()=>[q(j(e.$t("Enable Specific Days Only")),1)],void 0,!0),_:1},8,["modelValue"]),"yes"==P.email.settings.timings.selected_days_only?(y(),w(z,{key:0,style:{"margin-top":"12px"}},{default:S(()=>[V(le,{modelValue:P.email.settings.timings.allowed_days,"onUpdate:modelValue":f[7]||(f[7]=e=>P.email.settings.timings.allowed_days=e)},{default:S(()=>[V(te,{value:"Mon"},{default:S(()=>[q(j(e.$t("Mon")),1)],void 0,!0),_:1}),V(te,{value:"Tue"},{default:S(()=>[q(j(e.$t("Tue")),1)],void 0,!0),_:1}),V(te,{value:"Wed"},{default:S(()=>[q(j(e.$t("Wed")),1)],void 0,!0),_:1}),V(te,{value:"Thu"},{default:S(()=>[q(j(e.$t("Thu")),1)],void 0,!0),_:1}),V(te,{value:"Fri"},{default:S(()=>[q(j(e.$t("Fri")),1)],void 0,!0),_:1}),V(te,{value:"Sat"},{default:S(()=>[q(j(e.$t("Sat")),1)],void 0,!0),_:1}),V(te,{value:"Sun"},{default:S(()=>[q(j(e.$t("Sun")),1)],void 0,!0),_:1})],void 0,!0),_:1},8,["modelValue"])],void 0,!0),_:1})):k("",!0)])],void 0,!0),_:1})],void 0,!0),_:1})],void 0,!0),_:1}),V(Z,{gutter:16},{default:S(()=>[V(J,{sm:24,md:24},{default:S(()=>[E("div",{class:C([{fc_is_highlighted:1==P.email.utm_status||"1"==P.email.utm_status},"fcrm_edit_email_sequence_schedule--utm"])},[V(z,null,{default:S(()=>[V(te,{"true-value":"1","false-value":"0",modelValue:P.email.utm_status,"onUpdate:modelValue":f[8]||(f[8]=e=>P.email.utm_status=e)},{default:S(()=>[q(j(e.$t("Ema_Add_UPFU")),1)],void 0,!0),_:1},8,["modelValue"])],void 0,!0),_:1}),1==P.email.utm_status||"1"==P.email.utm_status?(y(),w(Z,{key:0,class:"fcrm_edit_email_sequence_schedule--utm-row",gutter:16},{default:S(()=>[V(J,{sm:24,md:8},{default:S(()=>[V(z,{label:e.$t("Campaign Source (required)")},{default:S(()=>[V(N,{placeholder:e.$t("The referrer: (e.g. google, newsletter)"),modelValue:P.email.utm_source,"onUpdate:modelValue":f[9]||(f[9]=e=>P.email.utm_source=e)},null,8,["placeholder","modelValue"])],void 0,!0),_:1},8,["label"])],void 0,!0),_:1}),V(J,{sm:24,md:8},{default:S(()=>[V(z,{label:e.$t("Campaign Medium (required)")},{default:S(()=>[V(N,{placeholder:e.$t("Marketing medium: (e.g. cpc, banner, email)"),modelValue:P.email.utm_medium,"onUpdate:modelValue":f[10]||(f[10]=e=>P.email.utm_medium=e)},null,8,["placeholder","modelValue"])],void 0,!0),_:1},8,["label"])],void 0,!0),_:1}),V(J,{sm:24,md:8},{default:S(()=>[V(z,{label:e.$t("Campaign Name (required)")},{default:S(()=>[V(N,{placeholder:e.$t("Product, promo code, or slogan (e.g. spring_sale)"),modelValue:P.email.utm_campaign,"onUpdate:modelValue":f[11]||(f[11]=e=>P.email.utm_campaign=e)},null,8,["placeholder","modelValue"])],void 0,!0),_:1},8,["label"])],void 0,!0),_:1}),V(J,{sm:24,md:8},{default:S(()=>[V(z,{label:e.$t("Campaign Term")},{default:S(()=>[V(N,{placeholder:e.$t("Identify the paid keywords"),modelValue:P.email.utm_term,"onUpdate:modelValue":f[12]||(f[12]=e=>P.email.utm_term=e)},null,8,["placeholder","modelValue"])],void 0,!0),_:1},8,["label"])],void 0,!0),_:1}),V(J,{sm:24,md:8},{default:S(()=>[V(z,{label:e.$t("Campaign Content")},{default:S(()=>[V(N,{placeholder:e.$t("Use to differentiate ads"),modelValue:P.email.utm_content,"onUpdate:modelValue":f[13]||(f[13]=e=>P.email.utm_content=e)},null,8,["placeholder","modelValue"])],void 0,!0),_:1},8,["label"])],void 0,!0),_:1})],void 0,!0),_:1})):k("",!0)],2)],void 0,!0),_:1})],void 0,!0),_:1})],void 0),_:1},8,["model"])])):k("",!0)]),P.app_loaded?(y(),$("div",A,[V(de,{show_audit:!0,onSave:f[15]||(f[15]=e=>D.save()),onChanged:f[16]||(f[16]=e=>D.handleChangeContent()),show_merge:!0,enable_template_save:!0,"disable-gutenberg-autosave":!1,iframe_nav_mode:"compose","hide-back-btn":!0,"hide-next-btn":!0,onTemplate_inserted:f[17]||(f[17]=e=>D.resetSubject()),enable_templates:!0,campaign:P.email},{fc_editor_actions:S(()=>[V(se,{loading:P.saving,disabled:P.saving,onClick:f[14]||(f[14]=e=>D.maybeSave()),type:"primary"},{default:S(()=>[q(j(e.$t("Save")),1)],void 0,!0),_:1},8,["loading","disabled"])]),_:1},8,["campaign"])])):(y(),$("div",K,[E("h3",null,j(e.$t("Loading")),1)]))])),[[me,P.loading]])}]]);export{L as default}; diff --git a/wp-content/plugins/fluent-crm/assets/admin/Modules/Email/EmailSequences/SequenceView.js b/wp-content/plugins/fluent-crm/assets/admin/Modules/Email/EmailSequences/SequenceView.js new file mode 100644 index 0000000..fbcbec6 --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/admin/Modules/Email/EmailSequences/SequenceView.js @@ -0,0 +1 @@ +import{T as e}from"../../../../TopNav.js?ver=3.1.8";import{P as a}from"../../../../PromoCard.js?ver=3.1.8";import{P as r}from"../../../../PageHeader.js?ver=3.1.8";import{aQ as o,W as s,X as t,Z as i,ab as c,a5 as n,a9 as m,aa as l}from"../../../../vendor.js?ver=3.1.8";import{_ as d}from"../../../../fc-bits-ui.js?ver=3.1.8";import"../../../../vendor-element-plus.js?ver=3.1.8";const p={class:"fcrm_email_sequences_page"},u={class:"fcrm_page_header_top_nav_wrapper"},_={class:"fcrm_page_header_top_nav"},v={class:"fcrm-layout-width"},f={class:"fcrm_body_boxed"};const h={key:0,class:"fc_sequence_root"},g={key:1};const b=d({name:"sequence-view",components:{EmailSequencePromo:d({name:"EmailSequencePromo",components:{PromoCard:a,TopNav:e,PageHeader:r}},[["render",function(e,a,r,d,h,g){const b=o("TopNav"),w=o("page-header"),y=o("PromoCard");return s(),t("div",p,[i("div",u,[i("div",_,[c(b)]),a[0]||(a[0]=i("div",{class:"fcrm_page_header_top_actions"},null,-1))]),i("div",v,[c(w,null,{title:n(()=>[m(l(e.$t("Email Sequences")),1)]),_:1}),i("div",f,[c(y,{heading:e.$t("Email Sequences"),description:e.$t("EmailSequencePromo.title")},{"before-cta":n(()=>[...a[1]||(a[1]=[i("iframe",{style:{display:"block",margin:"20px auto 0 auto"},width:"764",height:"430",src:"https://www.youtube.com/embed/6nYOK2UzoVk",title:"YouTube video player",frameborder:"0",allow:"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture",allowfullscreen:""},null,-1)])]),_:1},8,["heading","description"])])])])}]])},data:()=>({}),mounted(){this.changeTitle(this.$t("Email Sequences"))}},[["render",function(e,a,r,i,n,m){const l=o("router-view"),d=o("email-sequence-promo");return e.has_campaign_pro?(s(),t("div",h,[c(l)])):(s(),t("div",g,[c(d)]))}]]);export{b as default}; diff --git a/wp-content/plugins/fluent-crm/assets/admin/Modules/Email/EmailSequences/ViewSequence.js b/wp-content/plugins/fluent-crm/assets/admin/Modules/Email/EmailSequences/ViewSequence.js new file mode 100644 index 0000000..a0eeda6 --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/admin/Modules/Email/EmailSequences/ViewSequence.js @@ -0,0 +1 @@ +import{c as e,k as t,a_ as i,aZ as s,e as a,av as l,aB as n,E as c,aK as o,aL as d,aO as r,h as u,i as m,j as h,ax as p,aw as _,ay as f,g,ap as v}from"../../../../vendor-element-plus.js?ver=3.1.8";import{aQ as y,W as q,X as w,Z as C,ab as $,a5 as b,Y as k,a8 as L,a9 as S,aa as V,b2 as x,J as E,az as T,bB as M,a6 as D}from"../../../../vendor.js?ver=3.1.8";import{L as I,C as B}from"../../../../_LinkMetrics.js?ver=3.1.8";import{M as H}from"../../../../_MailerConfig.js?ver=3.1.8";import{E as j}from"../../../../EmailPreview.js?ver=3.1.8";import{P as Z}from"../../../../PageHeader.js?ver=3.1.8";import{_ as P,I as U}from"../../../../fc-bits-ui.js?ver=3.1.8";import{B as A}from"../../../../BaseCard.js?ver=3.1.8";import"../../../../PaginationBar.js?ver=3.1.8";import"../../../../Confirm.js?ver=3.1.8";import"../Campaigns/_components/EmailPreview.js?ver=3.1.8";import"../../../../PreviewIframeBuilder.js?ver=3.1.8";import"../../../../Badge.js?ver=3.1.8";import"../../../../GenericPromo.js?ver=3.1.8";import"../../../../PromoCard.js?ver=3.1.8";import"../../../../SettingsIcons.js?ver=3.1.8";import"../../../../_VerifiedEmailInput.js?ver=3.1.8";import"../../../../TestEmail.js?ver=3.1.8";import"../../../../CampaignSubjectLines.js?ver=3.1.8";const F={class:"fcrm_email_sequences_view"},R={class:"fcrm-layout-width"},W={key:0,class:"fcrm_inline_editable_input"},z={key:1,class:"fcrm-sequence-title-text-wrap"},N={class:"icon","aria-hidden":"true"},Y={class:"icon"},G={style:{position:"relative"}},K={class:"fc_loading_bar"},O={key:1},J={class:""},Q={class:"fcrm_sequence_hero_card"},X={class:"fcrm_sequence_hero_title"},ee={class:"icon"},te={key:2,class:"fcrm-sequence-list"},ie={key:0,class:"fcrm-sequence-wait-divider"},se=["onClick"],ae={class:"fcrm-sequence-delay-editor"},le={class:"fcrm-sequence-delay-editor__header"},ne={class:"fcrm-sequence-delay-editor__body"},ce={class:"fcrm-sequence-delay-editor__hint"},oe={class:"fcrm-sequence-delay-editor__footer"},de={class:"fcrm-sequence-card"},re={class:"fcrm-sequence-card-main"},ue={class:"fcrm-sequence-card-title-row"},me={class:"fcrm-sequence-card-title-wrap"},he={class:"fcrm-sequence-number"},pe={class:"fcrm-sequence-card-actions"},_e=["onClick","title"],fe={class:"fcrm-sequence-more"},ge={class:"el-popover__reference"},ve={class:"icon"},ye={class:"el-popover__reference"},qe={class:"icon"},we={class:"fcrm-sequence-schedule"},Ce={class:"fcrm-sequence-stats"},$e=["onClick"],be={class:"fcrm-stat-item"},ke=["onClick"],Le={class:"fcrm-stat-item"},Se={class:"dialog-footer"};const Ve=P({name:"EditSequence",components:{BaseCard:A,Icons:U,PageHeader:Z,CampaignEmails:B,LinkMetrics:I,MailerConfig:H,EmailPreview:j,Close:e},props:["id"],data:()=>({ArrowRightBold:v,sequence:{},sequence_emails:[],loading:!1,addEmailModal:!1,showSequenceSettings:!1,savingSequence:!1,show_email_report:!1,show_email_report_id:!1,link_clicks_modal:!1,link_click_id:!1,order:"desc",orderBy:"id",duplicating:!1,show_title_input:!1,editableTitle:"",editingEmailIndex:null,editDelayValue:"",editDelayUnit:"days",savingDelay:!1}),methods:{showInputField(){this.editableTitle=this.sequence.title||"",this.show_title_input=!0,this.$nextTick(()=>{const e=this.$refs.titleInput,t=Array.isArray(e)?e[0]:e;t&&t.focus&&t.focus()})},cancelInlineTitle(){this.show_title_input=!1,this.editableTitle=this.sequence.title||""},saveInlineTitle(){const e=(this.editableTitle||"").trim();e&&(e!==this.sequence.title?(this.sequence.title=e,this.saveSequence()):this.show_title_input=!1)},showEmailReport(e){this.show_email_report_id=e,this.show_email_report=!0},showLinkReport(e){this.link_click_id=e,this.link_clicks_modal=!0},handleSequenceAction(e,t){"edit"!==t?"report"!==t?"links"!==t?"duplicate"!==t?"delete"===t&&this.confirmDelete(e):this.duplicateSequence(e):this.showLinkReport(e.id):this.showEmailReport(e.id):this.$router.push({name:"edit-sequence-email",params:{sequence_id:e.parent_id,email_id:e.id},query:{t:(new Date).getTime()}})},statCount(e,t){const i=Number((e.stats||{})[t]);return Number.isFinite(i)?i:0},fetchSequence(){this.loading=!0;const e={with:["sequence_emails","email_stats"],order:this.order,orderBy:this.orderBy};this.$get(`sequences/${this.id}`,e).then(e=>{var t;this.sequence=e.sequence,this.editableTitle=(null==(t=e.sequence)?void 0:t.title)||"",this.sequence_emails=e.sequence_emails,this.changeTitle(this.sequence.title+" - Sequence")}).catch(e=>{this.handleError(e)}).finally(()=>{this.loading=!1})},gotToSubscribers(){this.$router.push({name:"sequence-subscribers",params:{id:this.id},query:{t:(new Date).getTime()}})},toSequenceEmailEdit(e){this.$router.push({name:"edit-sequence-email",params:{sequence_id:this.sequence.id,email_id:e}})},confirmDelete(e){const t=`\n
\n
\n

${this.$t("Delete Sequence")}

\n

${this.$t("Are you sure you want to delete this sequence email?")}

\n
\n
\n `;this.$confirm(t,"",{confirmButtonText:this.$t("Yes"),cancelButtonText:this.$t("No"),type:"warning",customClass:"fcrm-status-confirm-dialog",dangerouslyUseHTMLString:!0,showClose:!1}).then(()=>{this.remove(e)})},remove(e){this.$del(`sequences/${this.sequence.id}/email/${e.id}`).then(e=>{this.fetchSequence(),this.$notify.success({title:this.$t("Great!"),message:e.message,offset:19})}).catch(e=>{this.handleError(e)})},getScheduleTiming(e={}){return e.delay&&"0"!==e.delay?`After ${e.delay} ${e.delay_unit} from starting point`:this.$t("Immediately")},delayToMinutes(e,t){const i=parseFloat(e)||0;switch((t||"days").toLowerCase()){case"minutes":return i;case"hours":return 60*i;case"days":default:return 1440*i;case"weeks":return 10080*i;case"months":return 43200*i}},formatDuration(e){if(e<60){const t=Math.round(e);return t+" "+(1===t?this.$t("minute"):this.$t("minutes"))}const t=e/60;if(t<24&&Number.isInteger(t))return t+" "+(1===t?this.$t("hour"):this.$t("hours"));const i=e/1440;if(i<7&&(Number.isInteger(i)||i===Math.round(i))){const e=Math.round(i);return e+" "+(1===e?this.$t("day"):this.$t("days"))}const s=e/10080;if(Number.isInteger(s))return s+" "+(1===s?this.$t("week"):this.$t("weeks"));const a=Math.round(i);return a+" "+(1===a?this.$t("day"):this.$t("days"))},getWaitFromPrevious(e){const t=(this.sequence_emails[e].settings||{}).timings||{},i=this.delayToMinutes(t.delay,t.delay_unit);if(0===e)return t.delay&&"0"!==t.delay?this.$t("Wait")+" "+this.formatDuration(i):"";const s=(this.sequence_emails[e-1].settings||{}).timings||{},a=i-this.delayToMinutes(s.delay,s.delay_unit);return a<=0?"":this.$t("Wait")+" "+this.formatDuration(a)},openDelayEditor(e){const t=(this.sequence_emails[e].settings||{}).timings||{};this.editDelayValue=t.delay||"0",this.editDelayUnit=t.delay_unit||"days",this.editingEmailIndex=e},saveDelay(e){const t=this.sequence_emails[e];this.savingDelay=!0,this.$patch(`sequences/${t.parent_id}/email/${t.id}/delay`,{delay:this.editDelayValue,delay_unit:this.editDelayUnit}).then(()=>{t.settings||(t.settings={}),t.settings.timings||(t.settings.timings={}),t.settings.timings.delay=this.editDelayValue,t.settings.timings.delay_unit=this.editDelayUnit,this.editingEmailIndex=null,this.$notify.success(this.$t("Wait time updated successfully"))}).catch(e=>{this.handleError(e)}).finally(()=>{this.savingDelay=!1})},saveSequence(){this.savingSequence=!0,this.$put("sequences/"+this.sequence.id,{title:this.sequence.title,settings:this.sequence.settings}).then(e=>{this.$notify.success(e.message),this.show_title_input||this.fetchSequence()}).catch(e=>{this.handleError(e)}).finally(()=>{this.savingSequence=!1,this.showSequenceSettings=!1,this.show_title_input=!1})},duplicateSequence(e){const t=e.parent_id||this.sequence.id;this.duplicating=!0,this.$post(`sequences/${t}/email/duplicate`,{email_id:e.id}).then(e=>{this.$notify.success(e.message),this.fetchSequence(),this.duplicating=!1}).catch(e=>{this.handleError(e)}).finally(()=>{this.duplicating=!1})},reapplySequence(){const e=`\n
\n
\n

${this.$t("Re-apply Sequence")}

\n

${this.$t("Are you sure you want to re-apply new sequence emails to completed subscribers?")}

\n
\n
\n `;this.$confirm(e,"",{confirmButtonText:this.$t("Confirm"),cancelButtonText:this.$t("Cancel"),customClass:"fcrm-status-confirm-dialog",type:"info",dangerouslyUseHTMLString:!0,showClose:!1}).then(()=>{this.loading=!0,this.$post("sequences/"+this.sequence.id+"/reapply").then(e=>{this.$notify.success(e.message),this.fetchSequence()}).catch(e=>{this.handleError(e)}).finally(()=>{this.loading=!1})})}},mounted(){this.fetchSequence(),this.changeTitle(this.$t("View Sequence"))}},[["render",function(e,v,I,B,H,j){const Z=s,P=a,U=t,A=i,Ve=y("Icons"),xe=y("page-header"),Ee=l,Te=n,Me=y("BaseCard"),De=y("Close"),Ie=c,Be=d,He=o,je=r,Ze=y("router-link"),Pe=y("email-preview"),Ue=m,Ae=u,Fe=h,Re=_,We=y("mailer-config"),ze=p,Ne=g,Ye=y("campaign-emails"),Ge=y("link-metrics"),Ke=f;return q(),w("div",F,[C("div",R,[$(xe,null,{breadcrumb:b(()=>[$(A,{"separator-icon":H.ArrowRightBold},{default:b(()=>[$(Z,{to:{name:"email-sequences"}},{default:b(()=>[S(V(e.$t("Email Sequences")),1)],void 0,!0),_:1}),$(Z,null,{default:b(()=>[H.show_title_input?(q(),w("div",W,[$(P,{ref:"titleInput",size:"small",placeholder:e.$t("Sequence Title"),modelValue:H.editableTitle,"onUpdate:modelValue":v[0]||(v[0]=e=>H.editableTitle=e),class:"fcrm-sequence-title-input",onKeyup:[v[1]||(v[1]=x(e=>j.saveInlineTitle(),["enter"])),v[2]||(v[2]=x(e=>j.cancelInlineTitle(),["esc"]))]},null,8,["placeholder","modelValue"]),$(U,{size:"small",type:"primary",loading:H.savingSequence,disabled:H.savingSequence||!H.editableTitle||!H.editableTitle.trim(),onClick:v[3]||(v[3]=e=>j.saveInlineTitle()),"aria-label":e.$t("Save")},{default:b(()=>[S(V(e.$t("Save")),1)],void 0,!0),_:1},8,["loading","disabled","aria-label"]),$(U,{size:"small",disabled:H.savingSequence,onClick:v[4]||(v[4]=e=>j.cancelInlineTitle()),"aria-label":e.$t("Cancel")},{default:b(()=>[S(V(e.$t("Cancel")),1)],void 0,!0),_:1},8,["disabled","aria-label"])])):(q(),w("span",z,[C("span",{class:"fcrm-sequence-title-text",onClick:v[5]||(v[5]=(...e)=>j.showInputField&&j.showInputField(...e))},V(H.sequence.title),1),C("span",{class:"fcrm-sequence-title-edit-icon icon-edit",onClick:v[6]||(v[6]=(...e)=>j.showInputField&&j.showInputField(...e))},[...v[23]||(v[23]=[C("svg",{width:"16",height:"16",viewBox:"0 0 16 16",fill:"none",xmlns:"http://www.w3.org/2000/svg"},[C("path",{d:"M4.6485 10.4001L10.7337 4.31485L9.8853 3.46645L3.8001 9.55165V10.4001H4.6485ZM5.1459 11.6001H2.6001V9.05425L9.4611 2.19325C9.57361 2.08077 9.7262 2.01758 9.8853 2.01758C10.0444 2.01758 10.197 2.08077 10.3095 2.19325L12.0069 3.89065C12.1194 4.00317 12.1826 4.15575 12.1826 4.31485C12.1826 4.47395 12.1194 4.62653 12.0069 4.73905L5.1459 11.6001V11.6001ZM2.6001 12.8001H13.4001V14.0001H2.6001V12.8001Z",fill:"var(--fc-secondary-text)"})],-1)])])]))],void 0,!0),_:1})],void 0,!0),_:1},8,["separator-icon"])]),actions:b(()=>[e.hasPermission("fcrm_manage_emails")?(q(),k(U,{key:0,onClick:v[7]||(v[7]=e=>H.showSequenceSettings=!0),class:"only-icon-btn","aria-label":e.$t("Sequence Settings")},{default:b(()=>[C("span",N,[$(Ve,{"icon-name":"settings"})])],void 0,!0),_:1},8,["aria-label"])):L("",!0),$(U,{onClick:v[8]||(v[8]=e=>j.gotToSubscribers())},{default:b(()=>[S(V(e.$t("View Subscribers")),1)],void 0,!0),_:1}),e.hasPermission("fcrm_manage_emails")?(q(),k(U,{key:1,onClick:v[9]||(v[9]=e=>j.reapplySequence())},{default:b(()=>[S(V(e.$t("Re-apply Sequence")),1)],void 0,!0),_:1})):L("",!0),e.hasPermission("fcrm_manage_emails")?(q(),k(U,{key:2,onClick:v[10]||(v[10]=e=>j.toSequenceEmailEdit(0)),type:"primary"},{default:b(()=>[C("span",Y,[$(Ve,{"icon-name":"plus"})]),S(" "+V(e.$t("Add an Email")),1)],void 0,!0),_:1})):L("",!0)]),_:1}),C("div",G,[H.loading||H.duplicating?(q(),k(Me,{key:0},{body:b(()=>[C("div",K,[$(Ee,{class:"el-progress_animated","show-text":!1,percentage:30})]),$(Te,{style:{padding:"20px"},rows:7})]),_:1})):L("",!0),H.sequence_emails.length||H.loading?L("",!0):(q(),w("div",O,[C("div",J,[C("div",Q,[C("h3",X,V(e.$t("All_Looks_lydnsasey")),1),$(U,{onClick:v[11]||(v[11]=e=>j.toSequenceEmailEdit(0))},{default:b(()=>[C("span",ee,[$(Ve,{"icon-name":"plus"})]),S(" "+V(e.$t("All_Create_YFES")),1)],void 0),_:1})])])])),H.loading||H.duplicating?L("",!0):(q(),w("div",te,[(q(!0),w(E,null,T(H.sequence_emails,(t,i)=>(q(),w(E,{key:t.id},[j.getWaitFromPrevious(i)?(q(),w("div",ie,[$(je,{visible:H.editingEmailIndex===i,placement:"bottom",width:280,trigger:"click","popper-class":"fcrm-sequence-delay-popover"},{reference:b(()=>[C("span",{class:"fcrm-sequence-wait-badge fcrm-sequence-wait-badge--editable",onClick:e=>j.openDelayEditor(i)},[v[24]||(v[24]=C("svg",{xmlns:"http://www.w3.org/2000/svg",width:"14",height:"14",viewBox:"0 0 16 16",fill:"none"},[C("path",{d:"M8 14.5C4.41 14.5 1.5 11.59 1.5 8C1.5 4.41 4.41 1.5 8 1.5C11.59 1.5 14.5 4.41 14.5 8C14.5 11.59 11.59 14.5 8 14.5ZM8 3C5.24 3 3 5.24 3 8C3 10.76 5.24 13 8 13C10.76 13 13 10.76 13 8C13 5.24 10.76 3 8 3ZM8.75 8V4.5H7.25V9.5H11V8H8.75Z",fill:"var(--fc-text-muted)"})],-1)),S(" "+V(j.getWaitFromPrevious(i))+" ",1),v[25]||(v[25]=C("svg",{class:"fcrm-sequence-wait-edit-icon",xmlns:"http://www.w3.org/2000/svg",width:"12",height:"12",viewBox:"0 0 16 16",fill:"none"},[C("path",{d:"M4.6485 10.4001L10.7337 4.31485L9.8853 3.46645L3.8001 9.55165V10.4001H4.6485ZM5.1459 11.6001H2.6001V9.05425L9.4611 2.19325C9.57361 2.08077 9.7262 2.01758 9.8853 2.01758C10.0444 2.01758 10.197 2.08077 10.3095 2.19325L12.0069 3.89065C12.1194 4.00317 12.1826 4.15575 12.1826 4.31485C12.1826 4.47395 12.1194 4.62653 12.0069 4.73905L5.1459 11.6001V11.6001ZM2.6001 12.8001H13.4001V14.0001H2.6001V12.8001Z",fill:"currentColor"})],-1))],8,se)]),default:b(()=>[C("div",ae,[C("div",le,[C("span",null,V(e.$t("Edit Wait Time")),1),C("span",{class:"fcrm-sequence-delay-editor__close",onClick:v[12]||(v[12]=e=>H.editingEmailIndex=null)},[$(Ie,null,{default:b(()=>[$(De)],void 0,!0),_:1})])]),C("div",ne,[$(P,{modelValue:H.editDelayValue,"onUpdate:modelValue":v[14]||(v[14]=e=>H.editDelayValue=e),size:"small",type:"number",min:0},{suffix:b(()=>[$(He,{modelValue:H.editDelayUnit,"onUpdate:modelValue":v[13]||(v[13]=e=>H.editDelayUnit=e),size:"small",style:{width:"100px"}},{default:b(()=>[$(Be,{value:"minutes",label:e.$t("Minutes")},null,8,["label"]),$(Be,{value:"hours",label:e.$t("Hours")},null,8,["label"]),$(Be,{value:"days",label:e.$t("Days")},null,8,["label"]),$(Be,{value:"weeks",label:e.$t("Weeks")},null,8,["label"]),$(Be,{value:"Months",label:e.$t("Months")},null,8,["label"])],void 0,!0),_:1},8,["modelValue"])]),_:1},8,["modelValue"]),C("p",ce,V(e.$t("Delay from starting point")),1)]),C("div",oe,[$(U,{onClick:v[15]||(v[15]=e=>H.editingEmailIndex=null),size:"small"},{default:b(()=>[S(V(e.$t("Cancel")),1)],void 0,!0),_:1}),$(U,{type:"primary",loading:H.savingDelay,onClick:e=>j.saveDelay(i),size:"small"},{default:b(()=>[S(V(e.$t("Save")),1)],void 0,!0),_:1},8,["loading","onClick"])])])],void 0),_:2},1032,["visible"])])):L("",!0),C("div",de,[C("div",re,[C("div",null,[C("div",ue,[C("div",me,[C("span",he,V(i+1),1),$(Ze,{class:"fcrm-sequence-card-title",to:{name:"edit-sequence-email",params:{sequence_id:t.parent_id,email_id:t.id},query:{t:(new Date).getTime()}}},{default:b(()=>[S(V(t.title),1)],void 0),_:2},1032,["to"])]),C("div",pe,[$(Pe,{campaign:t,by_campaign_id:!0},null,8,["campaign"]),C("span",{class:"fcrm-sequence-action-btn",onClick:e=>j.handleSequenceAction(t,"edit"),title:e.$t("Edit")},[$(Ve,{"icon-name":"EditPen"})],8,_e),$(Fe,{trigger:"click",onCommand:e=>j.handleSequenceAction(t,e),placement:"bottom-end"},{dropdown:b(()=>[$(Ae,{class:"fcrm-sequence-dropdown-menu"},{default:b(()=>[$(Ue,{command:"duplicate"},{default:b(()=>[C("span",ge,[C("span",ve,[$(Ve,{"icon-name":"duplicate"})]),S(" "+V(e.$t("Duplicate")),1)])],void 0,!0),_:1}),$(Ue,{command:"delete",class:"fcrm_danger_action"},{default:b(()=>[C("span",ye,[C("span",qe,[$(Ve,{"icon-name":"delete"})]),S(" "+V(e.$t("Delete")),1)])],void 0,!0),_:1})],void 0,!0),_:1})]),default:b(()=>[C("span",fe,[$(Ve,{"icon-name":"more_actions"})])],void 0),_:1},8,["onCommand"])])]),C("div",we,V(j.getScheduleTiming((t.settings||{}).timings||{})),1)]),C("div",Ce,[C("button",{type:"button",class:"fcrm-stat-item is-link",onClick:e=>j.showEmailReport(t.id)},[v[26]||(v[26]=C("svg",{xmlns:"http://www.w3.org/2000/svg",width:"20",height:"20",viewBox:"0 0 20 20",fill:"none"},[C("path",{d:"M2.44226 8.02765C2.05976 7.8739 2.06426 7.64515 2.46776 7.5109L16.7823 2.7394C17.179 2.6074 17.4063 2.8294 17.2953 3.2179L13.2048 17.5324C13.0923 17.9292 12.8485 17.9472 12.667 17.5849L9.25001 10.7502L2.44226 8.02765ZM6.10976 7.87765L10.3368 9.5689L12.6168 14.1304L15.2763 4.8229L6.10901 7.87765H6.10976Z",fill:"var(--fc-text-muted)"})],-1)),C("span",null,V(j.statCount(t,"sent"))+" "+V(e.$t("Sent")),1)],8,$e),v[30]||(v[30]=C("span",{class:"fcrm-stat-dot"},"·",-1)),C("span",be,[v[27]||(v[27]=C("svg",{xmlns:"http://www.w3.org/2000/svg",width:"20",height:"20",viewBox:"0 0 20 20",fill:"none"},[C("path",{d:"M2.68225 6.14086L9.6175 1.98286C9.73406 1.91292 9.86744 1.87598 10.0034 1.87598C10.1393 1.87598 10.2727 1.91292 10.3892 1.98286L17.3177 6.14161C17.3733 6.17492 17.4194 6.22207 17.4513 6.27846C17.4832 6.33485 17.5 6.39855 17.5 6.46336V16.0004C17.5 16.1993 17.421 16.39 17.2803 16.5307C17.1397 16.6713 16.9489 16.7504 16.75 16.7504H3.25C3.05109 16.7504 2.86032 16.6713 2.71967 16.5307C2.57902 16.39 2.5 16.1993 2.5 16.0004V6.46261C2.49999 6.3978 2.51677 6.3341 2.54871 6.27771C2.58065 6.22132 2.62666 6.17417 2.68225 6.14086ZM4 7.10011V15.2504H16V7.09936L10.003 3.49936L4 7.09936V7.10011ZM10.045 11.2739L14.017 7.92661L14.983 9.07411L10.0555 13.2269L5.023 9.07936L5.977 7.92136L10.045 11.2739Z",fill:"var(--fc-text-muted)"})],-1)),C("span",null,V(j.statCount(t,"views"))+" "+V(e.$t("Opened")),1)]),v[31]||(v[31]=C("span",{class:"fcrm-stat-dot"},"·",-1)),C("button",{type:"button",class:"fcrm-stat-item is-link",onClick:e=>j.showLinkReport(t.id)},[v[28]||(v[28]=C("svg",{xmlns:"http://www.w3.org/2000/svg",width:"20",height:"20",viewBox:"0 0 20 20",fill:"none"},[C("path",{d:"M12.5413 11.1234L14.4553 16.3839L10.9318 17.6664L9.01702 12.4059L6.09277 14.2396L7.30777 2.22461L15.9605 10.6486L12.542 11.1234H12.5413ZM12.533 15.4869L10.4968 9.89186L12.7168 9.58436L8.48677 5.46686L7.89427 11.3394L9.79177 10.1491L11.828 15.7441L12.533 15.4869Z",fill:"var(--fc-text-muted)"})],-1)),C("span",null,V(j.statCount(t,"clicks"))+" "+V(e.$t("Clicked")),1)],8,ke),v[32]||(v[32]=C("span",{class:"fcrm-stat-dot"},"·",-1)),C("span",Le,[v[29]||(v[29]=M('',1)),C("span",null,V(j.statCount(t,"unsubscribers"))+" "+V(e.$t("Unsubscribed")),1)])])])])],64))),128))]))]),$(Ne,{"close-on-click-modal":!1,title:e.$t("Edit Sequence and Settings"),width:"890px","append-to-body":!0,modelValue:H.showSequenceSettings,"onUpdate:modelValue":v[18]||(v[18]=e=>H.showSequenceSettings=e)},{footer:b(()=>[C("span",Se,[$(U,{type:"primary",onClick:v[17]||(v[17]=e=>j.saveSequence())},{default:b(()=>[S(V(e.$t("Save Settings")),1)],void 0,!0),_:1})])]),default:b(()=>[D((q(),w("div",null,[H.sequence.settings?(q(),k(ze,{key:0,"label-position":"top",data:H.sequence},{default:b(()=>[$(Re,{label:e.$t("Internal Title")},{default:b(()=>[$(P,{placeholder:e.$t("Internal Title"),modelValue:H.sequence.title,"onUpdate:modelValue":v[16]||(v[16]=e=>H.sequence.title=e)},null,8,["placeholder","modelValue"])],void 0,!0),_:1},8,["label"]),H.sequence.settings.mailer_settings?(q(),k(We,{key:0,mailer_settings:H.sequence.settings.mailer_settings},null,8,["mailer_settings"])):L("",!0)],void 0,!0),_:1},8,["data"])):L("",!0)])),[[Ke,H.savingSequence]])],void 0),_:1},8,["title","modelValue"]),$(Ne,{"close-on-click-modal":!1,onClosed:v[19]||(v[19]=e=>H.show_email_report_id=""),title:e.$t("View Sequence Emails"),width:"890px","append-to-body":!0,modelValue:H.show_email_report,"onUpdate:modelValue":v[20]||(v[20]=e=>H.show_email_report=e)},{default:b(()=>[H.show_email_report_id?(q(),k(Ye,{key:0,campaign_id:H.show_email_report_id},null,8,["campaign_id"])):L("",!0)],void 0),_:1},8,["title","modelValue"]),$(Ne,{"close-on-click-modal":!1,onClosed:v[21]||(v[21]=e=>H.link_click_id=""),title:e.$t("Link Metrics"),width:"60%","append-to-body":!0,modelValue:H.link_clicks_modal,"onUpdate:modelValue":v[22]||(v[22]=e=>H.link_clicks_modal=e)},{default:b(()=>[H.link_click_id?(q(),k(Ge,{key:0,campaign_id:H.link_click_id,hide_title:!0},null,8,["campaign_id"])):L("",!0)],void 0),_:1},8,["title","modelValue"])])])}]]);export{Ve as default}; diff --git a/wp-content/plugins/fluent-crm/assets/admin/Modules/Email/EmailSequences/ViewSequenceSubscribers.js b/wp-content/plugins/fluent-crm/assets/admin/Modules/Email/EmailSequences/ViewSequenceSubscribers.js new file mode 100644 index 0000000..c421d05 --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/admin/Modules/Email/EmailSequences/ViewSequenceSubscribers.js @@ -0,0 +1 @@ +import{k as e,av as t,c as s,aH as i,aI as a,aO as n,ay as r,E as l,a_ as c,aZ as o,ap as d}from"../../../../vendor-element-plus.js?ver=3.1.8";import{aQ as u,W as _,X as b,Y as h,a5 as m,Z as g,aa as f,ab as p,a9 as v,J as S,a6 as y,ac as w,a8 as $,av as q}from"../../../../vendor.js?ver=3.1.8";import{R as k}from"../../../../RecipientTaggerForm.js?ver=3.1.8";import{_ as C,I as T,a as x,T as E}from"../../../../fc-bits-ui.js?ver=3.1.8";import{P as D}from"../../../../PaginationBar.js?ver=3.1.8";import{D as P}from"../../../../DataTable.js?ver=3.1.8";import{F as j}from"../../../../FloatingBulkActionShell.js?ver=3.1.8";import{C as B}from"../../../../Confirm.js?ver=3.1.8";import{B as A}from"../../../../Badge.js?ver=3.1.8";import{P as F}from"../../../../PageHeader.js?ver=3.1.8";import"../../../../fc-bits.js?ver=3.1.8";const I={class:"fluentcrm_sequence_sub_adder"},V={class:"text-align-center"},R={class:"text-align-center"},H={key:1,class:"text-align-center fluentcrm_hero_box"};const O={class:"fcrm_sequence_subscribers_table_wrap"},L={class:"fcrm_table_header_inner_left_title"},N=["title","src"],z={class:"fcrm_contact_info"},J={class:"fcrm_contact_name"},U={class:"fcrm_contact_email"},Y={class:"fcrm_sequence_date_with_note"},Z=["title"],G=["aria-label"],Q=["title"],W={key:1},X={class:"fcrm_bulk_action_bar"},K={class:"fcrm_bulk_action_left"},M={class:"fc_bulk_selection_count"},ee={class:"icon"};const te={class:"fcrm_email_sequences_view"},se={class:"fcrm-layout-width"},ie={class:"fcrm_sequence_subscribers_body"},ae={key:1,class:"fluentcrm_sequence_subs"};const ne=C({name:"SequenceSubscribers",props:["id"],components:{SubscribersAdder:C({name:"sequence_sub_adder",props:["sequence_id"],components:{RecipientTaggerForm:k},data:()=>({settings:{subscribers:[{list:null,tag:"all"}],excludedSubscribers:[{list:null,tag:null}],sending_filter:"list_tag",dynamic_segment:{id:"",slug:""}},ready_tagger:!0,inserting_page:1,inserting_total_page:0,inserting_now:!1,btnSubscribing:!1,batch_completed:!1,in_total:0,estimated_count:0}),watch:{},methods:{processSubscribers(){const e=this.settings,t=e.subscribers.filter(e=>e.list&&e.tag),s=e.excludedSubscribers.filter(e=>e.list&&e.tag);if("list_tag"===e.sending_filter){if(t.length!==e.subscribers.length||s.length&&s.length!==e.excludedSubscribers.length)return void this.$notify.error({title:this.$t("Oops!"),message:this.$t("_Su_Invalid_solatisi"),offset:19})}else if("dynamic_segment"==e.sending_filter){if(!e.dynamic_segment.uid)return void this.$notify.error({title:this.$t("Oops!"),message:this.$t("Please select the segment"),offset:19})}else if("advanced_filters"==e.sending_filter){let t=!1;if(this.each(e.advanced_filters,e=>{this.isEmptyValue(e)||(t=!0)}),!t)return void this.$notify.error({title:this.$t("Oops!"),message:this.$t("Please select the filters"),offset:19})}const i={subscribers:t,excludedSubscribers:s,sending_filter:e.sending_filter,dynamic_segment:e.dynamic_segment,page:this.inserting_page,advanced_filters:JSON.stringify(e.advanced_filters)};this.btnSubscribing=!0,this.inserting_now=!0,this.ready_tagger=!1,this.$post(`sequences/${this.sequence_id}/subscribers`,i).then(e=>{e.remaining?(1===this.inserting_page&&(this.inserting_total_page=e.page_total),this.inserting_page=e.next_page,this.$nextTick(()=>{this.processSubscribers()})):(this.batch_completed=!0,this.$notify.success({title:this.$t("Great!"),message:this.$t("_Su_Subscribers_hbas"),offset:19}),this.in_total=e.in_total)}).catch(e=>{this.handleError(e),this.btnSubscribing=!1,this.inserting_now=!1}).finally(()=>{})},resetSettings(){this.$emit("completed"),this.ready_tagger=!1,this.batch_completed=!1,this.inserting_now=!1,this.btnSubscribing=!1,this.inserting_page=1,this.inserting_total_page=0,this.settings={subscribers:[{list:null,tag:"all"}],excludedSubscribers:[{list:null,tag:null}],sending_filter:"list_tag",dynamic_segment:{id:"",slug:""}},this.$nextTick(()=>{this.ready_tagger=!0})}}},[["render",function(s,i,a,n,r,l){const c=e,o=u("recipient-tagger-form"),d=t;return _(),b("div",I,[r.ready_tagger?(_(),h(o,{key:0,modelValue:r.settings,"onUpdate:modelValue":i[1]||(i[1]=e=>r.settings=e)},{fc_tagger_bottom:m(()=>[g("div",V,[g("p",null,f(s.$t("_Su_Please_ntsewbstt")),1)]),g("div",R,[p(c,{onClick:i[0]||(i[0]=e=>l.processSubscribers()),type:"primary"},{default:m(()=>[v(f(s.$t("Add to this Sequence")),1)],void 0,!0),_:1})])]),_:1},8,["modelValue"])):(_(),b("div",H,[r.batch_completed?(_(),b(S,{key:0},[g("h3",null,f(s.$t("Completed")),1),g("h4",null,f(s.$t("_Su_All_SLashbaSttsE")),1),y(p(c,{onClick:i[2]||(i[2]=e=>l.resetSettings()),type:"primary",size:"small"},{default:m(()=>[v(f(s.$t("Back")),1)],void 0),_:1},512),[[w,r.batch_completed]]),g("p",null,[g("b",null,f(r.in_total),1),v(" "+f(s.$t("_Su_Subscribers_hbat")),1)])],64)):(_(),b(S,{key:1},[g("h3",null,f(s.$t("Processing now...")),1),g("h4",null,f(s.$t("Rec_Please_dnctw")),1),r.inserting_total_page?(_(),b(S,{key:0},[g("h2",null,f(r.inserting_page)+"/"+f(r.inserting_total_page),1),p(d,{"text-inside":!0,"stroke-width":24,percentage:parseInt(r.inserting_page/r.inserting_total_page*100),status:"success"},null,8,["percentage"])],64)):$("",!0)],64))]))])}]]),SequenceSubscribersView:C({name:"SequenceSubscribers",components:{Icons:T,PaginationBar:D,DataTable:P,FloatingBulkActionShell:j,Confirm:B,Badge:A,Close:s},props:["sequence_id","reload_count"],data:()=>({loading:!1,subscribers:[],pagination:{total:0,per_page:20,current_page:1},selected_subscribers:[],removing:!1,current_mode:"system"===E.getCurrentTheme()?E.getSystemTheme():E.getCurrentTheme()}),watch:{reload_count(){this.pagination.current_page=1,this.fetch()}},methods:{fetch(){this.loading=!0,this.selected_subscribers=[];const e={per_page:this.pagination.per_page,page:this.pagination.current_page};this.$get(`sequences/${this.sequence_id}/subscribers`,e).then(e=>{this.subscribers=e.data,this.pagination.total=e.total}).catch(e=>{this.handleError(e)}).finally(()=>{this.loading=!1})},removeSubscribers(){const e=this.selected_subscribers.map(e=>e.id);this.removing=!0,this.$del(`sequences/${this.sequence_id}/subscribers`,{tracker_ids:e}).then(e=>{this.$notify.success(e.message),this.fetch()}).catch(e=>{this.handleError(e)}).finally(()=>{this.removing=!1})},handleSelectionChange(e){this.selected_subscribers=e},clearSubscriberSelection(){this.$refs.subscribersTable&&this.$refs.subscribersTable.clearSelection(),this.selected_subscribers=[]},onThemeChanged(e){var t;this.current_mode=(null==(t=e.detail)?void 0:t.effective)||("system"===E.getCurrentTheme()?E.getSystemTheme():E.getCurrentTheme())}},mounted(){window.addEventListener(x,this.onThemeChanged),this.fetch()},beforeUnmount(){window.removeEventListener(x,this.onThemeChanged)}},[["render",function(t,s,c,o,d,S){const w=a,q=u("router-link"),k=u("badge"),C=i,T=u("Icons"),x=n,E=u("pagination-bar"),D=u("data-table"),P=u("Close"),j=l,B=e,A=u("confirm"),F=u("floating-bulk-action-shell"),I=r;return _(),b("div",O,[p(D,{"has-selection":!1},{"header-left":m(()=>[g("h3",L,f(t.$t("Sequence Subscribers")),1)]),table:m(()=>[y((_(),h(C,{ref:"subscribersTable",class:"fcrm_contacts_table","empty-text":t.$t("No Data Found"),onSelectionChange:S.handleSelectionChange,border:"",data:d.subscribers,style:{width:"100%"}},{default:m(()=>[p(w,{type:"selection",width:"40"}),p(w,{label:t.$t("Contact"),"min-width":"300"},{default:m(e=>[p(q,{class:"fcrm_contact_cell",to:{name:"subscriber",params:{id:e.row.subscriber_id}}},{default:m(()=>[g("img",{title:t.$t("Contact ID: %s",e.row.subscriber_id),class:"fcrm_contact_photo",src:e.row.subscriber.photo,alt:""},null,8,N),g("div",z,[g("div",J,f(e.row.subscriber.full_name),1),g("div",U,f(e.row.subscriber.email),1)])],void 0,!0),_:2},1032,["to"])]),_:1},8,["label"]),p(w,{label:t.$t("Status"),"min-width":"140"},{default:m(e=>[p(k,{type:e.row.status},null,8,["type"])]),_:1},8,["label"]),p(w,{label:t.$t("Started At"),"min-width":"220"},{default:m(e=>[g("div",Y,[g("span",{class:"fcrm_secondary_text",title:e.row.created_at},f(t.nsHumanDiffTime(e.row.created_at)),9,Z),e.row.notes&&e.row.notes.length?(_(),h(x,{key:0,width:"500",placement:"bottom",trigger:"click"},{reference:m(()=>[g("button",{type:"button",class:"fcrm_sequence_note_btn","aria-label":t.$t("View email schedule")},[p(T,{"icon-name":"info","icon-class":"fcrm_sequence_note_icon"})],8,G)]),default:m(()=>[p(C,{border:"",data:e.row.notes},{default:m(()=>[p(w,{label:t.$t("Email"),prop:"email"},null,8,["label"]),p(w,{width:160,label:t.$t("Date Time"),prop:"scheduled_at"},null,8,["label"])],void 0,!0),_:1},8,["data"])],void 0,!0),_:2},1024)):$("",!0)])]),_:1},8,["label"]),p(w,{label:t.$t("Next Email"),"min-width":"180"},{default:m(e=>["active"===e.row.status?(_(),b("span",{key:0,class:"fcrm_secondary_text",title:e.row.next_execution_time},f(t.nsHumanDiffTime(e.row.next_execution_time)),9,Q)):(_(),b("span",W,"--"))]),_:1},8,["label"])],void 0,!0),_:1},8,["empty-text","onSelectionChange","data"])),[[I,d.loading]])]),pagination:m(()=>[p(E,{pagination:d.pagination,hide_on_single:!1,"wrapper-class":["fcrm-contacts-pagination","fcrm-contacts-pagination-bar"],onFetch:S.fetch},null,8,["pagination","onFetch"])]),_:1}),p(F,{visible:!!d.selected_subscribers.length,"theme-mode":d.current_mode,"custom-layout":!0},{default:m(()=>[g("div",X,[g("div",K,[p(B,{link:"","aria-label":t.$t("Deselect"),onClick:S.clearSubscriberSelection},{default:m(()=>[p(j,null,{default:m(()=>[p(P)],void 0,!0),_:1})],void 0,!0),_:1},8,["aria-label","onClick"]),g("span",M,[g("strong",null,f(d.selected_subscribers.length),1),v(" "+f(t.$t("selected")),1)]),s[1]||(s[1]=g("div",{class:"fcrm_bulk_divider"},null,-1)),y((_(),h(A,{message:t.$t("SequenceSubscribers.DeleteInfo"),onYes:s[0]||(s[0]=e=>S.removeSubscribers())},{reference:m(()=>[p(B,{type:"danger",size:"small",plain:""},{default:m(()=>[g("span",ee,[p(T,{"icon-name":"delete"})]),v(" "+f(t.$t("Delete")),1)],void 0,!0),_:1})]),_:1},8,["message"])),[[I,d.removing]])])])],void 0),_:1},8,["visible","theme-mode"])])}]]),PageHeader:F},data:()=>({ArrowRightBold:q(d),loading:!1,sequence:{},reload_count:0,show_adder:!1}),methods:{fetchSequence(){this.loading=!0,this.$get(`sequences/${this.id}`).then(e=>{this.sequence=e.sequence}).catch(e=>{this.handleError(e)}).finally(()=>{this.loading=!1})},backToEmails(){this.$router.push({name:"edit-sequence",params:{id:this.id},query:{t:(new Date).getTime()}})},reloadSubscribers(){this.show_adder=!1,this.reload_count+=1}},mounted(){this.fetchSequence()}},[["render",function(t,s,i,a,n,l){const d=o,w=c,q=e,k=u("page-header"),C=u("subscribers-adder"),T=u("sequence-subscribers-view"),x=r;return y((_(),b("div",te,[g("div",se,[p(k,null,{breadcrumb:m(()=>[p(w,{"separator-icon":n.ArrowRightBold},{default:m(()=>[p(d,{to:{name:"email-sequences"}},{default:m(()=>[v(f(t.$t("Email Sequences")),1)],void 0,!0),_:1}),p(d,{to:{name:"edit-sequence",params:{id:i.id}}},{default:m(()=>[v(f(n.sequence.title),1)],void 0,!0),_:1},8,["to"]),p(d,null,{default:m(()=>[v(f(t.$t("Subscribers")),1)],void 0,!0),_:1})],void 0,!0),_:1},8,["separator-icon"])]),actions:m(()=>[p(q,{onClick:s[0]||(s[0]=e=>l.backToEmails())},{default:m(()=>[v(f(t.$t("View Emails")),1)],void 0,!0),_:1}),t.hasPermission("fcrm_manage_emails")?(_(),h(q,{key:0,onClick:s[1]||(s[1]=e=>n.show_adder=!n.show_adder),type:"primary","aria-pressed":n.show_adder},{default:m(()=>[n.show_adder?(_(),b(S,{key:1},[v(f(t.$t("Show Subscribers")),1)],64)):(_(),b(S,{key:0},[v(f(t.$t("Add Subscribers")),1)],64))],void 0,!0),_:1},8,["aria-pressed"])):$("",!0)]),_:1}),g("div",ie,[n.show_adder?(_(),h(C,{key:0,onCompleted:s[2]||(s[2]=e=>l.reloadSubscribers()),sequence_id:i.id},null,8,["sequence_id"])):(_(),b("div",ae,[p(T,{reload_count:n.reload_count,sequence_id:i.id},null,8,["reload_count","sequence_id"])]))])])])),[[x,n.loading]])}]]);export{ne as default}; diff --git a/wp-content/plugins/fluent-crm/assets/admin/Modules/Email/EmailView.js b/wp-content/plugins/fluent-crm/assets/admin/Modules/Email/EmailView.js new file mode 100644 index 0000000..e7bdcc7 --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/admin/Modules/Email/EmailView.js @@ -0,0 +1 @@ +import{aQ as e,W as r,Y as o}from"../../../vendor.js?ver=3.1.8";import{_ as s}from"../../../fc-bits-ui.js?ver=3.1.8";import"../../../vendor-element-plus.js?ver=3.1.8";const t=s({name:"email-view"},[["render",function(s,t,n,a,i,m){const v=e("router-view");return r(),o(v)}]]);export{t as default}; diff --git a/wp-content/plugins/fluent-crm/assets/admin/Modules/Email/RecurringCampaigns/Campaign/EmailConfiguration.js b/wp-content/plugins/fluent-crm/assets/admin/Modules/Email/RecurringCampaigns/Campaign/EmailConfiguration.js new file mode 100644 index 0000000..1d15575 --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/admin/Modules/Email/RecurringCampaigns/Campaign/EmailConfiguration.js @@ -0,0 +1 @@ +import{aD as e,aA as a,aw as t,e as l,az as i,ax as o,k as m}from"../../../../../vendor-element-plus.js?ver=3.1.8";import{aQ as s,W as d,X as r,ab as n,a5 as u,Y as p,a8 as c,a9 as _,aa as g,Z as v}from"../../../../../vendor.js?ver=3.1.8";import{E as h}from"../../../../../BlockComposer.js?ver=3.1.8";import{I as f}from"../../../../../_FormBuilder2.js?ver=3.1.8";import{_ as b}from"../../../../../fc-bits-ui.js?ver=3.1.8";import"../../../../../input-popover-dropdown.js?ver=3.1.8";import"../../../../../data_config.js?ver=3.1.8";import"../../../../../EmailPreview.js?ver=3.1.8";import"../../../../../PreviewIframeBuilder.js?ver=3.1.8";import"../../../../../TestEmail.js?ver=3.1.8";import"../../../../../CampaignSubjectLines.js?ver=3.1.8";import"../../../../../PaginationBar.js?ver=3.1.8";import"../../../../../_MergeCodes.js?ver=3.1.8";import"../../../../../BuiltinTemplateDrawer.js?ver=3.1.8";import"../../../../../PromoCard.js?ver=3.1.8";import"../../../../../fc-bits.js?ver=3.1.8";import"../../../../../PhotoWidget.js?ver=3.1.8";import"../../../../../_OptionSelector.js?ver=3.1.8";import"../../../../../_AjaxSelector.js?ver=3.1.8";import"../../../../../_VerifiedEmailInput.js?ver=3.1.8";const j=b({name:"CampaignName",components:{EmailBlockComposer:h,InputPopover:f},props:["campaign"],emits:["getVisualData"],data:()=>({loading:!1,saving:!1,app_loaded:!1,smartcodes:window.fcAdmin.globalSmartCodes,email_subject_status:!0,is_dirty:""}),methods:{validateBody(e=!0){return!!this.campaign.email_body||(e&&this.$notify.error({title:this.$t("Oops!"),message:this.$t("Cam_Please_peb"),offset:19}),!1)},maybeSave(e=!1){"visual_builder"==this.campaign.design_template?this.$bus.emit("getVisualData",{callback:()=>this.save(e)}):this.save(e)},save(e=!1){if(!this.validateBody())return;if(e&&(!this.campaign.email_subject||!this.campaign.email_subject.trim()))return this.$notify.error({title:this.$t("Oops!"),message:this.$t("Cam_Please_peS"),offset:19});this.saving=!0,this.is_dirty=!1;const a={campaign:JSON.stringify(this.campaign),campaign_id:this.campaign.id,validate_subject:e?"yes":"no"};this.$post("recurring-campaigns/update-campaign-data",a).then(e=>{this.$notify.success(e.message)}).catch(e=>{this.handleError(e)}).finally(()=>{this.saving=!1})},resetSubject(){},handleChangeContent(){},initKeyboardSave(e){(window.navigator.platform.match("Mac")?e.metaKey:e.ctrlKey)&&"s"===e.key&&(e.preventDefault(),this.maybeSave())}},mounted(){this.changeTitle("Email - "+this.campaign.title),document.addEventListener("keydown",this.initKeyboardSave)},beforeDestroy(){document.removeEventListener("keydown",this.initKeyboardSave)}},[["render",function(h,f,b,j,y,V){const $=s("input-popover"),C=t,S=a,w=l,k=e,E=i,P=o,U=m,B=s("email-block-composer");return d(),r("div",null,[n(P,{"label-position":"top",model:b.campaign,class:"fcrm_edit_email_sequence_schedule--config"},{default:u(()=>[n(k,{gutter:20},{default:u(()=>[n(S,{sm:24,md:12},{default:u(()=>[n(C,{label:h.$t("Email Subject")},{default:u(()=>[y.email_subject_status?(d(),p($,{key:0,doc_url:"https://fluentcrm.com/docs/merge-codes-smart-codes-usage/",popper_extra:"fc_with_c_fields",placeholder:h.$t("Email Subject"),data:y.smartcodes,modelValue:b.campaign.email_subject,"onUpdate:modelValue":f[0]||(f[0]=e=>b.campaign.email_subject=e)},null,8,["placeholder","data","modelValue"])):c("",!0)],void 0,!0),_:1},8,["label"])],void 0,!0),_:1}),n(S,{sm:24,md:12},{default:u(()=>[n(C,{label:h.$t("Email Pre-Header")},{default:u(()=>[n(w,{placeholder:h.$t("Email Pre-Header"),modelValue:b.campaign.email_pre_header,"onUpdate:modelValue":f[1]||(f[1]=e=>b.campaign.email_pre_header=e)},null,8,["placeholder","modelValue"])],void 0,!0),_:1},8,["label"])],void 0,!0),_:1})],void 0,!0),_:1}),n(C,null,{default:u(()=>[n(E,{"true-value":"1","false-value":"0",modelValue:b.campaign.utm_status,"onUpdate:modelValue":f[2]||(f[2]=e=>b.campaign.utm_status=e)},{default:u(()=>[_(g(h.$t("Ema_Add_UPFU")),1)],void 0,!0),_:1},8,["modelValue"])],void 0,!0),_:1}),1==b.campaign.utm_status||"1"==b.campaign.utm_status?(d(),p(k,{key:0,gutter:16,class:"fcrm_edit_email_sequence_schedule--utm-row"},{default:u(()=>[n(S,{sm:24,md:8},{default:u(()=>[n(C,{label:h.$t("Campaign Source (required)")},{default:u(()=>[n(w,{placeholder:h.$t("The referrer: (e.g. google, newsletter)"),modelValue:b.campaign.utm_source,"onUpdate:modelValue":f[3]||(f[3]=e=>b.campaign.utm_source=e)},null,8,["placeholder","modelValue"])],void 0,!0),_:1},8,["label"])],void 0,!0),_:1}),n(S,{sm:24,md:8},{default:u(()=>[n(C,{label:h.$t("Campaign Medium (required)")},{default:u(()=>[n(w,{placeholder:h.$t("Marketing medium: (e.g. cpc, banner, email)"),modelValue:b.campaign.utm_medium,"onUpdate:modelValue":f[4]||(f[4]=e=>b.campaign.utm_medium=e)},null,8,["placeholder","modelValue"])],void 0,!0),_:1},8,["label"])],void 0,!0),_:1}),n(S,{sm:24,md:8},{default:u(()=>[n(C,{label:h.$t("Campaign Name (required)")},{default:u(()=>[n(w,{placeholder:h.$t("Product, promo code, or slogan (e.g. spring_sale)"),modelValue:b.campaign.utm_campaign,"onUpdate:modelValue":f[5]||(f[5]=e=>b.campaign.utm_campaign=e)},null,8,["placeholder","modelValue"])],void 0,!0),_:1},8,["label"])],void 0,!0),_:1}),n(S,{sm:24,md:8},{default:u(()=>[n(C,{label:h.$t("Campaign Term")},{default:u(()=>[n(w,{placeholder:h.$t("Identify the paid keywords"),modelValue:b.campaign.utm_term,"onUpdate:modelValue":f[6]||(f[6]=e=>b.campaign.utm_term=e)},null,8,["placeholder","modelValue"])],void 0,!0),_:1},8,["label"])],void 0,!0),_:1}),n(S,{sm:24,md:8},{default:u(()=>[n(C,{label:h.$t("Campaign Content")},{default:u(()=>[n(w,{placeholder:h.$t("Use to differentiate ads"),modelValue:b.campaign.utm_content,"onUpdate:modelValue":f[7]||(f[7]=e=>b.campaign.utm_content=e)},null,8,["placeholder","modelValue"])],void 0,!0),_:1},8,["label"])],void 0,!0),_:1})],void 0,!0),_:1})):c("",!0)],void 0),_:1},8,["model"]),v("div",null,[n(B,{disabled_templates:{visual_builder:!0},show_audit:!0,onSave:f[9]||(f[9]=e=>V.save()),onChanged:f[10]||(f[10]=e=>V.handleChangeContent()),show_merge:!0,enable_template_save:!0,iframe_nav_mode:"compose","hide-back-btn":!0,"hide-next-btn":!0,onTemplate_inserted:f[11]||(f[11]=e=>V.resetSubject()),enable_templates:!0,campaign:b.campaign},{fc_editor_actions:u(()=>[n(U,{loading:y.saving,disabled:y.saving,onClick:f[8]||(f[8]=e=>V.maybeSave()),size:"small",type:"primary"},{default:u(()=>[_(g(h.$t("Save")),1)],void 0,!0),_:1},8,["loading","disabled"])]),_:1},8,["campaign"])])])}]]);export{j as default}; diff --git a/wp-content/plugins/fluent-crm/assets/admin/Modules/Email/RecurringCampaigns/Campaign/EmailHistory.js b/wp-content/plugins/fluent-crm/assets/admin/Modules/Email/RecurringCampaigns/Campaign/EmailHistory.js new file mode 100644 index 0000000..0ffffbf --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/admin/Modules/Email/RecurringCampaigns/Campaign/EmailHistory.js @@ -0,0 +1 @@ +import{a7 as i,P as a,aB as e,E as t,h as r,i as s,j as l}from"../../../../../vendor-element-plus.js?ver=3.1.8";import{aQ as n,W as c,X as o,ab as d,a8 as _,J as m,Z as h,aa as g,az as p,a9 as u,a5 as f,Y as C}from"../../../../../vendor.js?ver=3.1.8";import{E as v}from"../../../../../EmailPreview.js?ver=3.1.8";import{P as w}from"../../../../../PaginationBar.js?ver=3.1.8";import{_ as y}from"../../../../../fc-bits-ui.js?ver=3.1.8";import"../../../../../PreviewIframeBuilder.js?ver=3.1.8";import"../../../../../TestEmail.js?ver=3.1.8";import"../../../../../CampaignSubjectLines.js?ver=3.1.8";const L={class:"fcrm_recurring_email_history_page"},k={key:0},$={key:0,class:"fcrm_recurring_email_history_wrapper"},x={class:"fcrm_recurring_email_history_header"},H={class:"fcrm_recurring_email_history_header--left"},M={class:"fcrm_recurring_email_history_header_title"},V={class:"fcrm_recurring_email_history_header_description"},b={class:"fcrm_recurring_email_history_body"},Z={class:"fcrm_recurring_email_history_item--content"},E={class:"fcrm_recurring_email_history_item--subject"},j={class:"fcrm_recurring_email_history_item--meta"},P={class:"fcrm_recurring_email_history_item--actions"},S={class:"el-dropdown-link"},B={key:1,class:"fcrm_recurring_email_history_wrapper"},D={class:"fcrm_recurring_email_history_header"},F={class:"fcrm_recurring_email_history_header--left"},T={class:"fcrm_recurring_email_history_header_title"},A={class:"fcrm_recurring_email_history_header_description"},I={class:"fcrm_recurring_email_history_body"},R={class:"fcrm_recurring_email_history_item--content"},z={class:"fcrm_recurring_email_history_item--subject"},J={class:"fcrm_recurring_email_history_item--meta"},Q={class:"fcrm_recurring_email_history_item--actions"},W={class:"el-dropdown-link"},X={class:"fcrm_recurring_email_history_footer"},Y={key:2,class:"fcrm_body_boxed"},q={style:{"text-align":"center",margin:"0"}},G={key:3};const K=y({name:"EmailHistory",components:{EmailPreview:v,PaginationBar:w,MoreFilled:a,DataLine:i},props:["campaign"],data:()=>({drafts:[],history:[],pagination:{total:0,per_page:20,current_page:1},loading:!1,previewingCampaign:!1,updatingStatus:!1}),methods:{fetch(){this.loading=!0,this.$get("recurring-campaigns/"+this.campaign.id+"/emails",{per_page:this.pagination.per_page,page:this.pagination.current_page}).then(i=>{i.drafts&&(this.drafts=i.drafts),this.history=i.emails.data,this.pagination.total=i.emails.total}).catch(i=>{this.handleError(i)}).finally(()=>{this.loading=!1})},showPreview(i){this.previewingCampaign=i},changeCampaignStatus(i,a){this.updatingStatus=!0,this.$put("recurring-campaigns/"+this.campaign.id+"/emails/"+a.id,{status:i}).then(i=>{this.$notify.success(i.message),this.fetch()}).catch(i=>{this.handleError(i)}).finally(()=>{this.updatingStatus=!1})},statusText(i){return{draft:this.$t("Draft"),cancelled:this.$t("Cancelled"),sent:this.$t("Sent"),failed:this.$t("Failed"),archived:this.$t("Archived")}[i]||i}},mounted(){this.fetch(),this.changeTitle(this.$t("Email History")+" - "+this.campaign.title)}},[["render",function(i,a,v,w,y,K){const N=e,O=n("MoreFilled"),U=t,ii=s,ai=r,ei=l,ti=n("DataLine"),ri=n("pagination-bar"),si=n("email-preview");return c(),o("div",L,[y.loading?(c(),o("div",k,[d(N,{animated:!0,style:{padding:"20px"},rows:7})])):_("",!0),y.drafts.length||y.history.length?(c(),o(m,{key:1},[y.drafts.length?(c(),o("div",$,[h("div",x,[h("div",H,[h("div",M,g(i.$t("Drafts")),1),h("div",V,g(i.$t("Draft_Email_Info")),1)]),a[1]||(a[1]=h("div",{class:"fcrm_recurring_email_history_header--actions"},null,-1))]),h("div",b,[(c(!0),o(m,null,p(y.drafts,e=>(c(),o("div",{key:e.id,class:"fcrm_recurring_email_history_item"},[h("div",Z,[h("div",E,g(e.email_subject),1),h("div",j,[u(g(e.status)+" ",1),a[2]||(a[2]=h("span",{class:"dotted"},null,-1)),u(" "+g(e.scheduled_at),1)])]),h("div",P,[d(ei,{trigger:"click",placement:"bottom-end"},{dropdown:f(()=>[d(ai,null,{default:f(()=>[d(ii,{class:"fc_dropdown_action",onClick:a=>i.$router.push({name:"recurring_email_report",params:{campaign_id:v.campaign.id,email_id:e.id}})},{default:f(()=>[a[3]||(a[3]=h("span",{class:"icon"},[h("svg",{width:"20",height:"20",viewBox:"0 0 20 20",fill:"none",xmlns:"http://www.w3.org/2000/svg"},[h("path",{d:"M6.875 4.375V3.125H8.125V4.375H11.875V3.125H13.125V4.375H15.625C15.9702 4.375 16.25 4.65482 16.25 5V8.125H15V5.625H13.125V6.875H11.875V5.625H8.125V6.875H6.875V5.625H5V14.375H8.75V15.625H4.375C4.02982 15.625 3.75 15.3452 3.75 15V5C3.75 4.65482 4.02982 4.375 4.375 4.375H6.875ZM13.125 10C11.7443 10 10.625 11.1193 10.625 12.5C10.625 13.8807 11.7443 15 13.125 15C14.5057 15 15.625 13.8807 15.625 12.5C15.625 11.1193 14.5057 10 13.125 10ZM9.375 12.5C9.375 10.4289 11.0539 8.75 13.125 8.75C15.1961 8.75 16.875 10.4289 16.875 12.5C16.875 14.5711 15.1961 16.25 13.125 16.25C11.0539 16.25 9.375 14.5711 9.375 12.5ZM12.5 10.625V12.7589L13.9331 14.1919L14.8169 13.3081L13.75 12.2411V10.625H12.5Z",fill:"var(--fc-secondary-text)"})])],-1)),u(" "+g(i.$t("Review & Schedule")),1)],void 0,!0),_:1},8,["onClick"]),d(ii,{onClick:i=>K.showPreview(e)},{default:f(()=>[a[4]||(a[4]=h("span",{class:"icon"},[h("svg",{width:"20",height:"20",viewBox:"0 0 20 20",fill:"none",xmlns:"http://www.w3.org/2000/svg"},[h("path",{d:"M9.99999 3.25C14.044 3.25 17.4085 6.16 18.1142 10C17.4092 13.84 14.044 16.75 9.99999 16.75C5.95599 16.75 2.59149 13.84 1.88574 10C2.59074 6.16 5.95599 3.25 9.99999 3.25ZM9.99999 15.25C11.5296 15.2497 13.0138 14.7301 14.2096 13.7764C15.4055 12.8226 16.2422 11.4912 16.5827 10C16.2409 8.50998 15.4037 7.18 14.208 6.22752C13.0122 5.27504 11.5287 4.7564 9.99999 4.7564C8.47126 4.7564 6.98776 5.27504 5.79202 6.22752C4.59629 7.18 3.75907 8.50998 3.41724 10C3.75781 11.4912 4.5945 12.8226 5.79035 13.7764C6.9862 14.7301 8.47039 15.2497 9.99999 15.25V15.25ZM9.99999 13.375C9.10489 13.375 8.24644 13.0194 7.61351 12.3865C6.98057 11.7536 6.62499 10.8951 6.62499 10C6.62499 9.10489 6.98057 8.24645 7.61351 7.61352C8.24644 6.98058 9.10489 6.625 9.99999 6.625C10.8951 6.625 11.7535 6.98058 12.3865 7.61352C13.0194 8.24645 13.375 9.10489 13.375 10C13.375 10.8951 13.0194 11.7536 12.3865 12.3865C11.7535 13.0194 10.8951 13.375 9.99999 13.375ZM9.99999 11.875C10.4973 11.875 10.9742 11.6775 11.3258 11.3258C11.6774 10.9742 11.875 10.4973 11.875 10C11.875 9.50272 11.6774 9.02581 11.3258 8.67418C10.9742 8.32254 10.4973 8.125 9.99999 8.125C9.50271 8.125 9.0258 8.32254 8.67417 8.67418C8.32254 9.02581 8.12499 9.50272 8.12499 10C8.12499 10.4973 8.32254 10.9742 8.67417 11.3258C9.0258 11.6775 9.50271 11.875 9.99999 11.875Z",fill:"var(--fc-secondary-text)"})])],-1)),u(" "+g(i.$t("Preview Email")),1)],void 0,!0),_:1},8,["onClick"]),d(ii,{class:"fc_dropdown_action",onClick:i=>K.changeCampaignStatus("cancelled",e)},{default:f(()=>[a[5]||(a[5]=h("span",{class:"icon"},[h("svg",{width:"20",height:"20",viewBox:"0 0 20 20",fill:"none",xmlns:"http://www.w3.org/2000/svg"},[h("path",{d:"M10.0001 8.93906L13.7126 5.22656L14.7731 6.28706L11.0606 9.99956L14.7731 13.7121L13.7126 14.7726L10.0001 11.0601L6.28755 14.7726L5.22705 13.7121L8.93955 9.99956L5.22705 6.28706L6.28755 5.22656L10.0001 8.93906Z",fill:"var(--fc-secondary-text)"})])],-1)),u(" "+g(i.$t("Cancel This email")),1)],void 0,!0),_:1},8,["onClick"])],void 0,!0),_:2},1024)]),default:f(()=>[h("span",S,[d(U,{style:{"font-weight":"bold",cursor:"pointer",transform:"rotate(90deg)"}},{default:f(()=>[d(O)],void 0,!0),_:1})])],void 0),_:2},1024)])]))),128))])])):_("",!0),y.history.length?(c(),o("div",B,[h("div",D,[h("div",F,[h("div",T,g(i.$t("Previous Emails")),1),h("div",A,g(i.$t("Previous_Email_Hist")),1)]),a[6]||(a[6]=h("div",{class:"fcrm_recurring_email_history_header--actions"},null,-1))]),h("div",I,[(c(!0),o(m,null,p(y.history,e=>(c(),o("div",{key:e.id,class:"fcrm_recurring_email_history_item"},[h("div",R,[h("div",z,g(e.email_subject),1),h("div",J,[u(g(K.statusText(e.status))+" ",1),a[7]||(a[7]=h("span",{class:"dotted"},null,-1)),u(" "+g(e.scheduled_at),1)])]),h("div",Q,[d(ei,{trigger:"click",placement:"bottom-end"},{dropdown:f(()=>[d(ai,null,{default:f(()=>[d(ii,{class:"fc_dropdown_action",onClick:i=>K.showPreview(e)},{default:f(()=>[a[8]||(a[8]=h("span",{class:"icon"},[h("svg",{width:"20",height:"20",viewBox:"0 0 20 20",fill:"none",xmlns:"http://www.w3.org/2000/svg"},[h("path",{d:"M9.99999 3.25C14.044 3.25 17.4085 6.16 18.1142 10C17.4092 13.84 14.044 16.75 9.99999 16.75C5.95599 16.75 2.59149 13.84 1.88574 10C2.59074 6.16 5.95599 3.25 9.99999 3.25ZM9.99999 15.25C11.5296 15.2497 13.0138 14.7301 14.2096 13.7764C15.4055 12.8226 16.2422 11.4912 16.5827 10C16.2409 8.50998 15.4037 7.18 14.208 6.22752C13.0122 5.27504 11.5287 4.7564 9.99999 4.7564C8.47126 4.7564 6.98776 5.27504 5.79202 6.22752C4.59629 7.18 3.75907 8.50998 3.41724 10C3.75781 11.4912 4.5945 12.8226 5.79035 13.7764C6.9862 14.7301 8.47039 15.2497 9.99999 15.25V15.25ZM9.99999 13.375C9.10489 13.375 8.24644 13.0194 7.61351 12.3865C6.98057 11.7536 6.62499 10.8951 6.62499 10C6.62499 9.10489 6.98057 8.24645 7.61351 7.61352C8.24644 6.98058 9.10489 6.625 9.99999 6.625C10.8951 6.625 11.7535 6.98058 12.3865 7.61352C13.0194 8.24645 13.375 9.10489 13.375 10C13.375 10.8951 13.0194 11.7536 12.3865 12.3865C11.7535 13.0194 10.8951 13.375 9.99999 13.375ZM9.99999 11.875C10.4973 11.875 10.9742 11.6775 11.3258 11.3258C11.6774 10.9742 11.875 10.4973 11.875 10C11.875 9.50272 11.6774 9.02581 11.3258 8.67418C10.9742 8.32254 10.4973 8.125 9.99999 8.125C9.50271 8.125 9.0258 8.32254 8.67417 8.67418C8.32254 9.02581 8.12499 9.50272 8.12499 10C8.12499 10.4973 8.32254 10.9742 8.67417 11.3258C9.0258 11.6775 9.50271 11.875 9.99999 11.875Z",fill:"var(--fc-secondary-text)"})])],-1)),u(" "+g(i.$t("Preview Email")),1)],void 0,!0),_:1},8,["onClick"]),"cancelled"==e.status?(c(),C(ii,{key:0,onClick:i=>K.changeCampaignStatus("draft",e)},{default:f(()=>[a[9]||(a[9]=h("span",{class:"icon"},[h("svg",{width:"20",height:"20",viewBox:"0 0 20 20",fill:"none",xmlns:"http://www.w3.org/2000/svg"},[h("path",{d:"M2.44226 8.02765C2.05976 7.8739 2.06426 7.64515 2.46776 7.5109L16.7823 2.7394C17.179 2.6074 17.4063 2.8294 17.2953 3.2179L13.2048 17.5324C13.0923 17.9292 12.8485 17.9472 12.667 17.5849L9.25001 10.7502L2.44226 8.02765ZM6.10976 7.87765L10.3368 9.5689L12.6168 14.1304L15.2763 4.8229L6.10901 7.87765H6.10976Z",fill:"var(--fc-secondary-text)"})])],-1)),u(" "+g(i.$t("Send to Draft")),1)],void 0,!0),_:1},8,["onClick"])):_("",!0),d(ii,{class:"fc_dropdown_action",onClick:a=>i.$router.push({name:"recurring_email_report",params:{campaign_id:v.campaign.id,email_id:e.id}})},{default:f(()=>[d(U,null,{default:f(()=>[d(ti)],void 0,!0),_:1}),u(" "+g(i.$t("View Report")),1)],void 0,!0),_:1},8,["onClick"])],void 0,!0),_:2},1024)]),default:f(()=>[h("span",W,[d(U,null,{default:f(()=>[d(O)],void 0,!0),_:1})])],void 0),_:2},1024)])]))),128))])])):_("",!0),h("div",X,[d(ri,{pagination:y.pagination,onFetch:K.fetch},null,8,["pagination","onFetch"])])],64)):(c(),o("div",Y,[h("h3",q,g(i.$t("All_Email_Hist")),1)])),y.previewingCampaign?(c(),o("div",G,[d(si,{by_campaign_id:!0,auto_load:!0,onModalClosed:a[0]||(a[0]=()=>{y.previewingCampaign=null}),show_audit:!0,campaign:y.previewingCampaign},null,8,["campaign"])])):_("",!0)])}]]);export{K as default}; diff --git a/wp-content/plugins/fluent-crm/assets/admin/Modules/Email/RecurringCampaigns/Campaign/EmailReport.js b/wp-content/plugins/fluent-crm/assets/admin/Modules/Email/RecurringCampaigns/Campaign/EmailReport.js new file mode 100644 index 0000000..3f2013b --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/admin/Modules/Email/RecurringCampaigns/Campaign/EmailReport.js @@ -0,0 +1 @@ +import{aq as a,k as e,ay as t,E as i,ax as s,aw as n,aY as r,W as l,av as c,aJ as m,a$ as p,b0 as o,a_ as d,aZ as g,aB as _,ap as u}from"../../../../../vendor-element-plus.js?ver=3.1.8";import{aQ as v,W as h,Y as f,a5 as b,Z as y,ab as C,a6 as k,a9 as w,aa as j,X as S,a8 as $,J as x,az as E,a0 as T,$ as P,av as B}from"../../../../../vendor.js?ver=3.1.8";import{E as R}from"../../../../../BlockComposer.js?ver=3.1.8";import{B as A}from"../../../../../BaseCard.js?ver=3.1.8";import{_ as F}from"../../../../../fc-bits-ui.js?ver=3.1.8";import{E as I}from"../../../../../EmailSubjects.js?ver=3.1.8";import{S as U}from"../../../../../TestEmail.js?ver=3.1.8";import{h as D}from"../../../../../data_config.js?ver=3.1.8";import{U as L,C as z,a as M}from"../../../../../_CampaignDetails.js?ver=3.1.8";import{C as N,L as V}from"../../../../../_LinkMetrics.js?ver=3.1.8";import{R as G}from"../../../../../ReadableRecipientTagger.js?ver=3.1.8";import{P as Y}from"../../../../../PageHeader.js?ver=3.1.8";import"../../../../../input-popover-dropdown.js?ver=3.1.8";import"../../../../../_FormBuilder2.js?ver=3.1.8";import"../../../../../PhotoWidget.js?ver=3.1.8";import"../../../../../_OptionSelector.js?ver=3.1.8";import"../../../../../_AjaxSelector.js?ver=3.1.8";import"../../../../../_VerifiedEmailInput.js?ver=3.1.8";import"../../../../../EmailPreview.js?ver=3.1.8";import"../../../../../PreviewIframeBuilder.js?ver=3.1.8";import"../../../../../CampaignSubjectLines.js?ver=3.1.8";import"../../../../../PaginationBar.js?ver=3.1.8";import"../../../../../_MergeCodes.js?ver=3.1.8";import"../../../../../BuiltinTemplateDrawer.js?ver=3.1.8";import"../../../../../PromoCard.js?ver=3.1.8";import"../../../../../fc-bits.js?ver=3.1.8";import"../../../../../_MailerConfig.js?ver=3.1.8";import"../../../../../Confirm.js?ver=3.1.8";import"../../Campaigns/_components/EmailPreview.js?ver=3.1.8";import"../../../../../Badge.js?ver=3.1.8";import"../../../../../GenericPromo.js?ver=3.1.8";import"../../../../../SettingsIcons.js?ver=3.1.8";const O={class:"template"};const q={class:"fcrm_max_w_800"},H={class:"d-flex w-full items-center gap-10 flex-wrap justify-between"},J={class:"left"},W={class:"right d-flex gap-10 items-center"};const Z={class:"email_archived_report"},Q={key:0,style:{"margin-bottom":"20px","text-align":"center"}},X={class:"fcrm_email_campaign_stats_card_wrapper"},K={class:"fcrm_sms_campaign_view--stats-list"},aa={class:"fcrm_sms_campaign_view--stat-label"},ea={class:"fcrm_sms_campaign_view--stat-value"},ta={class:"fcrm_sms_campaign_view--stat-label"},ia={key:0,class:"fcrm_sms_campaign_view--stat-value"},sa={key:1,class:"fcrm_sms_campaign_view--stat-value"},na={class:"fcrm_perf_bars"},ra={class:"fcrm_perf_bar_header"},la={class:"fcrm_perf_bar_label"},ca={class:"fcrm_perf_value"},ma={class:"fcrm_perf_count"},pa={class:"fcrm_perf_pct"},oa={class:"fcrm_perf_bar"},da={class:"fcrm_sms_campaign_view--link-list"},ga={key:1,class:"fcrm_sms_campaign_view--main fcrm_email_campaign_view--main"},_a={class:"fcrm_campaign_emails_wrapper--title"};const ua={class:"fcrm_recurring_email_report_page"},va={class:"fcrm-layout-width"},ha={key:0},fa={key:2,class:"fluentcrm_body fluentcrm_body_boxed"};const ba=F({name:"ViewSingleCampaignReport",props:["campaign_id","email_id"],components:{PageHeader:Y,SendTestEmail:U,EmailEditor:F({name:"RecurringMailEditor",props:["campaign","saving"],emits:["goToNext","updateCampaign"],components:{BaseCard:A,EmailBlockComposer:R,ArrowRight:a},computed:{isGutenbergLayout(){var a;const e=((null==(a=window.fcAdmin)?void 0:a.email_template_designs)||{})[this.campaign.design_template];return!(!e||!e.use_gutenberg)}},methods:{nextStep(){this.$emit("goToNext")},goBack(){const a=this.campaign.parent_id;a?this.$router.push({name:"past_recurring_emails",params:{campaign_id:a}}):window.history.length>1&&window.history.back()},updateCampaign(){this.$emit("updateCampaign")}}},[["render",function(a,s,n,r,l,c){const m=e,p=v("ArrowRight"),o=i,d=v("email-block-composer"),g=v("base-card"),_=t;return h(),f(g,{body_class:"fcrm_p_0"},{body:b(()=>[y("div",O,[C(d,{disabled_templates:{visual_builder:!0},onSave:s[2]||(s[2]=a=>c.updateCampaign()),show_audit:!0,show_merge:!0,enable_template_save:!0,"hide-back-btn":!0,"hide-next-btn":!0,onEditor_next:s[3]||(s[3]=a=>c.nextStep()),onEditor_back:s[4]||(s[4]=a=>c.goBack()),use_fullscreen_editor:c.isGutenbergLayout,iframe_nav_mode:"compose",enable_templates:!0,campaign:n.campaign},{fc_editor_actions:b(()=>[k((h(),f(m,{disabled:n.saving,size:"small",onClick:s[0]||(s[0]=a=>c.updateCampaign())},{default:b(()=>[w(j(a.$t("Save")),1)],void 0,!0),_:1},8,["disabled"])),[[_,n.saving]]),k((h(),f(m,{disabled:n.saving,size:"small",type:"primary",onClick:s[1]||(s[1]=a=>c.nextStep())},{default:b(()=>[w(j(a.$t("Continue to Schedule"))+" ",1),C(o,null,{default:b(()=>[C(p)],void 0,!0),_:1})],void 0,!0),_:1},8,["disabled"])),[[_,n.saving]])]),_:1},8,["use_fullscreen_editor","campaign"])])]),_:1})}]]),MailConfig:F({name:"RecurringMailConfig",props:["campaign","saving"],emits:["goToNext","goToPrev","updateCampaign"],components:{BaseCard:A,EmailSubjects:I,TestEmail:U},data:()=>({fetchingTemplate:!1,editor_status:!0,loading:!1,smart_codes:[],inline_errors:"",pickerOptions:D}),methods:{nextStep(){this.$emit("goToNext")},updateCampaign(){this.$emit("updateCampaign")},goToPrev(){this.$emit("goToPrev")}}},[["render",function(a,i,l,c,m,p){const o=v("email-subjects"),d=r,g=n,_=v("test-email"),u=s,$=e,x=v("base-card"),E=t;return h(),S("div",q,[C(x,null,{body:b(()=>[C(u,{"label-position":"top",model:l.campaign},{default:b(()=>[C(o,{mailer_settings:!0,multi_subject:!1,label_align:"top",campaign:l.campaign},null,8,["campaign"]),C(g,{label:a.$t("Set_Date_Time_Label")},{default:b(()=>[C(d,{"value-format":"YYYY-MM-DD HH:mm:ss",modelValue:l.campaign.scheduled_at,"onUpdate:modelValue":i[0]||(i[0]=a=>l.campaign.scheduled_at=a),type:"datetime","disabled-date":m.pickerOptions.disabledDate,shortcuts:m.pickerOptions.shortcuts,"popper-class":"fcrm_mail_config_datetime",placeholder:a.$t("Select date and time")},null,8,["modelValue","disabled-date","shortcuts","placeholder"])],void 0,!0),_:1},8,["label"]),C(g,null,{default:b(()=>[C(_,{campaign:l.campaign},null,8,["campaign"])],void 0,!0),_:1})],void 0,!0),_:1},8,["model"])]),footer:b(()=>[y("div",H,[y("div",J,[C($,{size:"small",link:"",disabled:l.saving,onClick:i[1]||(i[1]=a=>p.goToPrev())},{default:b(()=>[w(j(a.$t("Back")),1)],void 0,!0),_:1},8,["disabled"])]),y("div",W,[k((h(),f($,{disabled:l.saving,onClick:i[2]||(i[2]=a=>p.updateCampaign()),size:"small",type:"primary"},{default:b(()=>[w(j(a.$t("Update")),1)],void 0,!0),_:1},8,["disabled"])),[[E,l.saving]]),k((h(),f($,{disabled:l.saving,onClick:i[3]||(i[3]=a=>p.nextStep()),size:"small",type:"primary"},{default:b(()=>[w(j(a.$t("Schedule Campaign")),1)],void 0,!0),_:1},8,["disabled"])),[[E,l.saving]])])])]),_:1})])}]]),RecurringEmailReport:F({name:"RecurringEmailReport",components:{CampaignEmailProcessStat:M,ArchivedReport:F({name:"RecurringEmailArchivedReport",props:["campaign_id"],components:{LinkMetrics:V,CampaignDetails:z,CampaignEmails:N,Unsubscribers:L,ReadableRecipients:G,InfoFilled:l,BaseCard:A},data:()=>({loading:!1,request_counter:1,campaign:null,stat:[],sent_count:0,analytics:[],activeTab:"campaign_details",app_loaded:!1}),computed:{emailStatsData(){var a,e,t,i,s;const n=this.sent_count,r=parseInt((null==(a=this.campaign)?void 0:a.recipients_count)||0);return[{name:"Sent",value:n,color:"#7B61FF"},{name:"Opened",value:parseInt((null==(e=this.analytics.open)?void 0:e.total)||0),color:"#22D3BB"},{name:"Clicked",value:parseInt((null==(t=this.analytics.click)?void 0:t.total)||0),color:"#F6B51E"},{name:"Unsubscribed",value:parseInt((null==(i=this.analytics.unsubscribe)?void 0:i.total)||0),color:"#E1E4EA"},{name:"Bounced",value:parseInt((null==(s=this.stat.find(a=>"bounced"===a.status))?void 0:s.total)||0),color:"#335CFF"}].map(a=>({...a,percent:r?Math.min(a.value/r*100,100):0,pctText:r?(a.value/r*100).toFixed(1):"0.0"}))}},methods:{getCampaignStatus(){this.loading=!0,this.$get(`campaigns/${this.campaign_id}/status`,{request_counter:this.request_counter}).then(a=>{this.campaign=a.campaign,this.stat=a.stat,this.sent_count=a.sent_count,this.analytics=a.analytics,this.changeTitle(this.campaign.title+" - Campaign"),"working"==a.campaign.status&&this.fetchStatAgain()}).catch(a=>{this.handleError(a)}).finally(()=>{this.loading=!1,this.app_loaded=!0})},fetchStatAgain(){setTimeout(()=>{this.request_counter+=1,this.getCampaignStatus()},4e3)},getPercent(a){return this.sent_count?parseFloat(a/this.sent_count*100).toFixed(2)+"%":"--"},getCampaignPercent(){return parseInt(this.sent_count/this.campaign.recipients_count*100)}},mounted(){this.getCampaignStatus()}},[["render",function(a,e,s,n,r,l){const d=c,g=v("InfoFilled"),_=i,u=m,B=v("BaseCard"),R=v("link-metrics"),A=v("campaign-details"),F=p,I=v("campaign-emails"),U=v("unsubscribers"),D=v("readable-recipients"),L=o,z=t;return k((h(),S("div",Z,[r.campaign&&"working"==r.campaign.status?(h(),S("div",Q,[y("h3",null,[w(j(a.$t("Vie_Your_easrn"))+" ",1),k((h(),S("span",null,[w(j(a.$t("Sending")),1)])),[[z,"working"==r.campaign.status]])]),C(d,{"text-inside":!0,"stroke-width":36,percentage:l.getCampaignPercent(),status:"success"},null,8,["percentage"])])):$("",!0),y("div",X,[C(B,null,{title:b(()=>[y("h4",null,j(a.$t("Campaign Performance")),1)]),body:b(()=>[y("ul",K,[(h(!0),S(x,null,E(r.stat,e=>(h(),S("li",{class:"fcrm_sms_campaign_view--stat-item",key:e.status},[y("span",aa,j(a.ucFirst(e.status))+" "+j(a.$t("Emails")),1),y("span",ea,j(e.total),1)]))),128)),(h(!0),S(x,null,E(r.analytics,e=>(h(),S("li",{class:T(["fcrm_sms_campaign_view--stat-item","fcrm_camp_data_"+e.type]),key:e.type},[y("span",ta,[w(j(e.label)+" ",1),"open"==e.type?(h(),f(u,{key:0,class:"item",effect:"dark",content:a.$t("open_rate_info"),placement:"top-start"},{default:b(()=>[C(_,null,{default:b(()=>[C(g)],void 0,!0),_:1})],void 0,!0),_:1},8,["content"])):"click"==e.type?(h(),f(u,{key:1,class:"item",effect:"dark",content:a.$t("click_rate_info"),placement:"top-start"},{default:b(()=>[C(_,null,{default:b(()=>[C(g)],void 0,!0),_:1})],void 0,!0),_:1},8,["content"])):$("",!0)]),e.is_percent?(h(),S("span",ia,j(l.getPercent(e.total)),1)):(h(),S("span",sa,j(e.total),1))],2))),128))])]),_:1}),C(B,null,{title:b(()=>[y("h4",null,j(a.$t("Emails Stats")),1)]),body:b(()=>[y("div",na,[(h(!0),S(x,null,E(l.emailStatsData,e=>(h(),S("div",{key:e.name,class:"fcrm_perf_row"},[y("div",ra,[y("span",la,j(a.$t(e.name)),1),y("div",ca,[y("span",ma,j(e.value.toLocaleString()),1),y("span",pa,j(e.pctText)+"%",1)])]),y("div",oa,[y("div",{class:"fcrm_perf_bar_inner",style:P({width:e.percent+"%",backgroundColor:e.color})},null,4)])]))),128))])]),_:1}),C(B,{"no-body-padding":!0},{title:b(()=>[y("h4",null,j(a.$t("Link activity")),1)]),body:b(()=>[y("ul",da,[r.campaign?(h(),f(R,{key:0,hide_title:!0,campaign_id:s.campaign_id},null,8,["campaign_id"])):$("",!0)])]),_:1})]),r.campaign?(h(),S("div",ga,[C(L,{modelValue:r.activeTab,"onUpdate:modelValue":e[1]||(e[1]=a=>r.activeTab=a),type:"border-card","tab-position":"top",class:"fcrm_sms_campaign_view--tabs fcrm_email_campaign_view--tabs",style:{"min-height":"200px"}},{default:b(()=>[C(F,{name:"campaign_details",label:a.$t("Campaign Details")},{default:b(()=>[C(A,{campaign:r.campaign},null,8,["campaign"])],void 0,!0),_:1},8,["label"]),C(F,{lazy:!0,name:"campaign_subscribers",label:a.$t("Emails")},{default:b(()=>[C(I,{onFetchCampaign:e[0]||(e[0]=a=>l.getCampaignStatus()),campaign_id:s.campaign_id},null,8,["campaign_id"])],void 0,!0),_:1},8,["label"]),r.analytics.unsubscribe&&r.campaign?(h(),f(F,{key:0,lazy:!0,name:"campaign_unsubscribers",label:a.$t("Unsubscribers")},{default:b(()=>[C(U,{campaign_id:r.campaign.id},null,8,["campaign_id"])],void 0,!0),_:1},8,["label"])):$("",!0),r.campaign?(h(),f(F,{key:1,lazy:!0,name:"campaign_selections",label:a.$t("Contact Selections")},{default:b(()=>[y("div",_a,j(a.$t("Contact Selections")),1),C(D,{settings:r.campaign.settings,"already-sent":!0},null,8,["settings"])],void 0,!0),_:1},8,["label"])):$("",!0)],void 0),_:1},8,["modelValue"])])):$("",!0)])),[[z,!r.app_loaded]])}]])},props:["campaign","parent_campaign"],methods:{handleUnscheduled(){this.$router.push({name:"past_recurring_emails",params:{campaign_id:this.parent_campaign.id}})},getCampaignPercent(){return parseInt(this.sent_count/this.campaign.recipients_count*100)}}},[["render",function(a,e,t,i,s,n){const r=v("campaign-email-process-stat"),l=v("archived-report");return h(),S("div",null,["pending-scheduled"==t.campaign.status||"processing"==t.campaign.status||"scheduled"==t.campaign.status?(h(),f(r,{key:0,onUnscheduled:e[0]||(e[0]=a=>n.handleUnscheduled()),campaign:t.campaign},null,8,["campaign"])):(h(),f(l,{key:1,campaign_id:t.campaign.id},null,8,["campaign_id"]))])}]]),ArrowRight:a},data:()=>({ArrowRightBold:B(u),ArrowRight:B(a),parent_campaign:{},campaign:{},loading:!0,saving:!1,active_step:""}),computed:{isGutenbergLayout(){var a;const e=((null==(a=window.fcAdmin)?void 0:a.email_template_designs)||{})[this.campaign.design_template];return!(!e||!e.use_gutenberg)}},methods:{fetchCampaign(){this.loading=!0,this.$get("recurring-campaigns/"+this.campaign_id+"/emails/"+this.email_id).then(a=>{this.parent_campaign=a.campaign,this.campaign=a.email,"draft"==a.email.status?this.active_step="edit":"cancelled"==a.email.status?this.$router.push({name:"past_recurring_emails",params:{campaign_id:this.campaign_id}}):this.active_step="reports"}).catch(a=>{this.handleError(a)}).finally(()=>{this.loading=!1})},maybePublishCampaign(){this.campaign.status="pending-scheduled",this.updateCampaign(()=>{this.active_step="reports"})},gotToNextStep(){this.updateCampaign(()=>{"edit"==this.active_step?this.active_step="review":"review"==this.active_step&&(this.active_step="reports")})},updateCampaign(a){this.saving=!0,this.$post("recurring-campaigns/"+this.campaign_id+"/emails/update-email",{email:JSON.stringify(this.campaign),step:this.active_step}).then(e=>{this.$notify.success(e.message),a&&a(e)}).catch(a=>{this.handleError(a),"review"==this.active_step&&(this.campaign.status="draft")}).finally(()=>{this.saving=!1})},reloadPage(){window.location.reload(!0)}},mounted(){this.fetchCampaign()}},[["render",function(a,s,n,r,l,c){const m=g,p=d,o=v("ArrowRight"),u=i,E=e,T=v("send-test-email"),P=v("page-header"),B=v("email-editor"),R=v("mail-config"),A=v("recurring-email-report"),F=_,I=t;return h(),S("div",ua,[y("div",va,[l.parent_campaign?(h(),S("div",ha,[C(P,null,{breadcrumb:b(()=>[C(p,{"separator-icon":l.ArrowRightBold},{default:b(()=>[C(m,{to:{name:"recurring_campaigns"}},{default:b(()=>[w(j(a.$t("Recurring Campaigns")),1)],void 0,!0),_:1}),C(m,{to:{name:"past_recurring_emails",params:{campaign_id:n.campaign_id}}},{default:b(()=>[w(j(l.parent_campaign.title),1)],void 0,!0),_:1},8,["to"]),C(m,null,{default:b(()=>[w(j(l.campaign.title)+" ("+j(l.campaign.status)+") / "+j(l.active_step),1)],void 0,!0),_:1})],void 0,!0),_:1},8,["separator-icon"])]),actions:b(()=>["edit"===l.active_step&&c.isGutenbergLayout&&l.campaign.id?k((h(),f(E,{key:0,size:"small",type:"primary",disabled:l.saving,onClick:s[0]||(s[0]=a=>c.gotToNextStep())},{default:b(()=>[w(j(a.$t("Continue to Schedule"))+" ",1),C(u,null,{default:b(()=>[C(o)],void 0,!0),_:1})],void 0,!0),_:1},8,["disabled"])),[[I,l.saving]]):$("",!0),C(T,{campaign:{email_subject:l.campaign.email_subject,email_pre_header:l.campaign.post_excerpt,email_body:l.campaign.post_content||l.campaign.email_body,design_template:l.campaign.design_template,settings:l.campaign.settings}},null,8,["campaign"])]),_:1})])):$("",!0),l.campaign.id?(h(),S(x,{key:1},["edit"==l.active_step?(h(),f(B,{key:0,onGoToNext:s[1]||(s[1]=a=>c.gotToNextStep()),onUpdateCampaign:s[2]||(s[2]=a=>c.updateCampaign()),saving:l.saving,campaign:l.campaign},null,8,["saving","campaign"])):"review"==l.active_step?(h(),f(R,{key:1,onGoToNext:s[3]||(s[3]=a=>c.maybePublishCampaign()),onGoToPrev:s[4]||(s[4]=a=>l.active_step="edit"),onUpdateCampaign:s[5]||(s[5]=a=>c.updateCampaign()),campaign:l.campaign,saving:l.saving},null,8,["campaign","saving"])):"reports"==l.active_step?(h(),f(A,{key:2,onReload:s[6]||(s[6]=a=>c.reloadPage()),campaign:l.campaign,parent_campaign:l.parent_campaign},null,8,["campaign","parent_campaign"])):$("",!0)],64)):l.loading?(h(),S("div",fa,[C(F,{animated:!0,style:{padding:"20px"},rows:7})])):$("",!0)])])}]]);export{ba as default}; diff --git a/wp-content/plugins/fluent-crm/assets/admin/Modules/Email/RecurringCampaigns/Campaign/Settings.js b/wp-content/plugins/fluent-crm/assets/admin/Modules/Email/RecurringCampaigns/Campaign/Settings.js new file mode 100644 index 0000000..95b5ea0 --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/admin/Modules/Email/RecurringCampaigns/Campaign/Settings.js @@ -0,0 +1 @@ +import{k as i,ax as s}from"../../../../../vendor-element-plus.js?ver=3.1.8";import{aQ as e,W as n,X as t,ab as a,a5 as r,Z as c,aa as g,a9 as m}from"../../../../../vendor.js?ver=3.1.8";import{R as o}from"../../../../../RecipientTaggerForm.js?ver=3.1.8";import{C as l,B as _}from"../../../../../_conditions.js?ver=3.1.8";import{_ as d}from"../../../../../fc-bits-ui.js?ver=3.1.8";import"../../../../../fc-bits.js?ver=3.1.8";const p={class:"fcrm_single_recurring_camp_settings"},f={class:"fcrm_single_recurring_camp_settings--form-item"},u={class:"fcrm_single_recurring_camp_settings--form-item-header"},v={class:"fcrm_single_recurring_camp_settings--form-schedule"},h={class:"fcrm_single_recurring_camp_settings--form-item"},b={class:"fcrm_single_recurring_camp_settings--form-item-header"},S={class:"fcrm_single_recurring_camp_settings--form-conditions"},$={class:"fcrm_single_recurring_camp_settings--form-item"},j={class:"fcrm_single_recurring_camp_settings--form-item-header"},C={class:"fcrm_single_recurring_camp_settings--form-recipients"},y={class:"fcrm_single_recurring_camp_settings--form-footer"};const R=d({name:"RecurringEmailSettings",props:["campaign"],components:{RecipientTaggerForm:o,BasicSettings:_,ConditionsSettings:l},data:()=>({saving:!1}),methods:{updateCampaign(){this.saving=!0;const i={campaign:JSON.stringify({settings:this.campaign.settings,title:this.campaign.title})};this.$post("recurring-campaigns/"+this.campaign.id+"/update-settings",i).then(i=>{this.$notify.success(i.message)}).catch(i=>{this.handleError(i)}).finally(()=>{this.saving=!1})}},mounted(){this.changeTitle(this.$t("Settings")+" - "+this.campaign.title)}},[["render",function(o,l,_,d,R,T){const V=e("basic-settings"),k=e("conditions-settings"),x=e("recipient-tagger-form"),B=i,E=s;return n(),t("div",p,[a(E,{"label-position":"top",class:"fcrm_single_recurring_camp_settings--form"},{default:r(()=>[c("div",f,[c("div",u,[c("h3",null,g(o.$t("Scheduling Settings")),1)]),c("div",v,[a(V,{campaign:_.campaign},null,8,["campaign"])])]),c("div",h,[c("div",b,[c("h3",null,g(o.$t("Sending Conditions")),1)]),c("div",S,[a(k,{sending_conditions:_.campaign.settings.sending_conditions},null,8,["sending_conditions"])])]),c("div",$,[c("div",j,[c("h3",null,g(o.$t("Recipients")),1)]),c("div",C,[a(x,{modelValue:_.campaign.settings.subscribers_settings,"onUpdate:modelValue":l[0]||(l[0]=i=>_.campaign.settings.subscribers_settings=i)},null,8,["modelValue"])])]),c("div",y,[a(B,{loading:R.saving,disabled:R.saving,onClick:l[1]||(l[1]=i=>T.updateCampaign()),type:"primary"},{default:r(()=>[m(g(o.$t("Save Settings")),1)],void 0,!0),_:1},8,["loading","disabled"])])],void 0),_:1})])}]]);export{R as default}; diff --git a/wp-content/plugins/fluent-crm/assets/admin/Modules/Email/RecurringCampaigns/CreateFlow.js b/wp-content/plugins/fluent-crm/assets/admin/Modules/Email/RecurringCampaigns/CreateFlow.js new file mode 100644 index 0000000..37768ce --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/admin/Modules/Email/RecurringCampaigns/CreateFlow.js @@ -0,0 +1 @@ +import{aZ as t,a_ as e,aJ as i,k as s,ay as a,ax as n,ap as r}from"../../../../vendor-element-plus.js?ver=3.1.8";import{aQ as o,W as c,X as p,Z as l,ab as d,a5 as g,a9 as m,aa as _,J as u,az as h,Y as v,a0 as f,a6 as S,a8 as y,av as $}from"../../../../vendor.js?ver=3.1.8";import{R as C}from"../../../../RecipientTaggerForm.js?ver=3.1.8";import{C as b,B as k}from"../../../../_conditions.js?ver=3.1.8";import{B as j}from"../../../../BaseCard.js?ver=3.1.8";import{_ as w}from"../../../../fc-bits-ui.js?ver=3.1.8";import"../../../../fc-bits.js?ver=3.1.8";const B={class:"fcrm_edit_campaign_page"},R={class:"fcrm_page_header_top_nav_wrapper"},x={class:"fcrm_page_header_top_nav"},P={class:"fcrm_page_header_top_actions"},T={class:"fcrm_edit_campaign_steps"},q={class:"fcrm_edit_campaign_progress_label"},z={class:"fcrm_edit_campaign_progress_bar"},N=["onClick"],E={class:"fcrm_max_w_800"},F={class:"fcrm_edit_campaign_steps_wrapper"};const J=w({name:"CreateRecurringCampaignFlow",components:{BaseCard:j,RecipientTaggerForm:C,BasicSettings:k,ConditionsSettings:b},data(){return{ArrowRightBold:$(r),loading:!1,creating:!1,activeStep:parseInt(this.$route.query.step,10)||0,steps:[{title:this.$t("Start"),description:this.$t("Provide campaign details")},{title:this.$t("Conditions"),description:this.$t("Select automation conditions")},{title:this.$t("Recipients"),description:this.$t("Select Email Recipients")}],campaign:{title:"",email_subject:"",email_pre_header:"",email_body:"",status:"draft",settings:{subscribers_settings:{subscribers:[{list:"all",tag:"all"}],excludedSubscribers:[{list:null,tag:null}],sending_filter:"list_tag",dynamic_segment:{id:"",slug:""},advanced_filters:[[]]},scheduling_settings:{type:"weekly",day:"",time:"",send_automatically:"yes"},sending_conditions:[[{object_type:"cpt",object_name:"post",object_key:"post_date",comparison_type:"within_days",compare_value:7}]]}}}},watch:{"$route.query.step":function(){this.activeStep=this.normalizeStep(this.$route.query.step)}},created(){this.activeStep=this.normalizeStep(this.activeStep)},methods:{normalizeStep(t){const e=parseInt(t,10);return isNaN(e)||e<0?0:e>=this.steps.length?this.steps.length-1:e},setStep(t){const e=this.normalizeStep(t);e===this.activeStep&&e===this.normalizeStep(this.$route.query.step)||(this.activeStep=e,this.$router.push({name:"create_recurring_campaign",query:{...this.$route.query,step:e}}))},validateStartStep(t=!0){return this.campaign.title?this.campaign.settings.scheduling_settings.time?("weekly"!=this.campaign.settings.scheduling_settings.type||this.campaign.settings.scheduling_settings.day)&&!("monthly"==this.campaign.settings.scheduling_settings.type&&!this.campaign.settings.scheduling_settings.day)||(t&&this.$notify.error(this.$t("Please provide a day")),!1):(t&&this.$notify.error(this.$t("Please provide a time")),!1):(t&&this.$notify.error(this.$t("Please provide an unique title")),!1)},validateConditionStep(t=!0){if(this.campaign.settings.sending_conditions.length){if(this.campaign.settings.sending_conditions[0][0].compare_value<1)return t&&this.$notify.error(this.$t("Please provide condition value")),!1}return!0},canGoToStep(t){return t<=0||!!this.validateStartStep(!1)&&(1===t||!!this.validateConditionStep(!1))},targetStep(t){t!==this.activeStep&&this.canGoToStep(t)&&this.setStep(t)},nextStep(t){if(1===t){if(!this.validateStartStep())return}else if(2===t&&!this.validateConditionStep())return;this.setStep(t)},goToPrev(t){this.setStep(t)},createCampaign(){this.creating=!0,this.$post("recurring-campaigns",{campaign:JSON.stringify(this.campaign)}).then(t=>{this.$notify.success(t.message),this.$router.push({name:"view_recurring_campaign",params:{campaign_id:t.campaign_id}})}).catch(t=>{this.handleError(t),(0===t.go_to_step||t.go_to_step)&&this.setStep(t.go_to_step)}).finally(()=>{this.creating=!1})}}},[["render",function(r,$,C,b,k,j){const w=t,J=e,V=i,A=o("basic-settings"),G=s,I=o("BaseCard"),Z=o("conditions-settings"),O=o("recipient-tagger-form"),Q=n,U=a;return c(),p("div",B,[l("div",R,[l("div",x,[d(J,{"separator-icon":k.ArrowRightBold},{default:g(()=>[d(w,{to:{name:"recurring_campaigns"}},{default:g(()=>[m(_(r.$t("Recurring Email Campaigns")),1)],void 0,!0),_:1}),d(w,null,{default:g(()=>[m(_(k.campaign.title||r.$t("Create a recurring email broadcast")),1)],void 0,!0),_:1})],void 0),_:1},8,["separator-icon"])]),l("div",P,[l("div",T,[l("div",q,_(k.activeStep+1)+"/"+_(k.steps.length)+" "+_(r.$t("Completed")),1),l("div",z,[(c(!0),p(u,null,h(k.steps,(t,e)=>(c(),v(V,{key:e,content:t.title,placement:"top"},{default:g(()=>[l("div",{class:f(["fcrm_edit_campaign_progress_segment",{"is-filled":e<=k.activeStep}]),onClick:t=>j.targetStep(e)},null,10,N)],void 0),_:2},1032,["content"]))),128))])])])]),l("div",E,[d(Q,{"label-position":"top",model:k.campaign},{default:g(()=>[S((c(),p("div",F,[0===k.activeStep?(c(),v(I,{key:0},{title:g(()=>[l("h4",null,_(r.$t("Scheduling Settings")),1)]),body:g(()=>[d(A,{campaign:k.campaign},null,8,["campaign"])]),footer:g(()=>[S((c(),v(G,{onClick:$[0]||($[0]=t=>j.nextStep(1)),type:"primary"},{default:g(()=>[m(_(r.$t("Continue to next step [conditions]")),1)],void 0,!0),_:1})),[[U,k.loading]])]),_:1})):y("",!0),1===k.activeStep?(c(),v(I,{key:1},{title:g(()=>[l("h4",null,_(r.$t("Conditions for sending emails %s email",k.campaign.settings.scheduling_settings.type)),1)]),body:g(()=>[d(Z,{sending_conditions:k.campaign.settings.sending_conditions},null,8,["sending_conditions"])]),footer:g(()=>[d(G,{onClick:$[1]||($[1]=t=>j.goToPrev(0))},{default:g(()=>[m(_(r.$t("Back")),1)],void 0,!0),_:1}),S((c(),v(G,{onClick:$[2]||($[2]=t=>j.nextStep(2)),type:"primary"},{default:g(()=>[m(_(r.$t("Cam_Continue_TNS_")),1)],void 0,!0),_:1})),[[U,k.loading]])]),_:1})):y("",!0),2===k.activeStep?(c(),v(I,{key:2},{title:g(()=>[l("h4",null,_(r.$t("Select Subscribers")),1)]),body:g(()=>[d(O,{modelValue:k.campaign.settings.subscribers_settings,"onUpdate:modelValue":$[3]||($[3]=t=>k.campaign.settings.subscribers_settings=t)},null,8,["modelValue"])]),footer:g(()=>[d(G,{onClick:$[4]||($[4]=t=>j.goToPrev(1))},{default:g(()=>[m(_(r.$t("Back")),1)],void 0,!0),_:1}),S((c(),v(G,{onClick:$[5]||($[5]=t=>j.createCampaign()),type:"primary"},{default:g(()=>[m(_(r.$t("Create Recurring Campaign")),1)],void 0,!0),_:1})),[[U,k.creating]])]),_:1})):y("",!0)])),[[U,k.loading]])],void 0),_:1},8,["model"])])])}]]);export{J as default}; diff --git a/wp-content/plugins/fluent-crm/assets/admin/Modules/Email/RecurringCampaigns/RecurringCampaigns.js b/wp-content/plugins/fluent-crm/assets/admin/Modules/Email/RecurringCampaigns/RecurringCampaigns.js new file mode 100644 index 0000000..4efd154 --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/admin/Modules/Email/RecurringCampaigns/RecurringCampaigns.js @@ -0,0 +1 @@ +import{_ as e}from"../../../../fc-bits.js?ver=3.1.8";import{aj as a,a6 as t,n as s,aL as l,aK as i,ay as n,E as o,k as r,ac as c,c as p,a2 as d,j as m,h,i as u,aB as g,aH as _,aI as f,aQ as b,e as y,aR as C,aS as v,g as $}from"../../../../vendor-element-plus.js?ver=3.1.8";import{aQ as w,W as k,X as S,Z as A,ab as L,a5 as B,Y as D,a8 as F,J as T,az as P,$ as j,aa as R,a6 as E,a9 as x,ac as O,b2 as V,bW as q}from"../../../../vendor.js?ver=3.1.8";import{C as I}from"../../../../Confirm.js?ver=3.1.8";import{P as M}from"../../../../PaginationBar.js?ver=3.1.8";import{D as N}from"../../../../DataTable.js?ver=3.1.8";import{_ as z,I as H,a as U,T as W}from"../../../../fc-bits-ui.js?ver=3.1.8";import{T as Y}from"../../../../TopNav.js?ver=3.1.8";import{B as Q}from"../../../../Badge.js?ver=3.1.8";import{P as G}from"../../../../PageHeader.js?ver=3.1.8";import{F as J}from"../../../../FloatingBulkActionShell.js?ver=3.1.8";import{G as K}from"../../../../GenericPromo.js?ver=3.1.8";import"../../../../PromoCard.js?ver=3.1.8";const Z={name:"BulkCampaignActions",components:{Delete:t,Check:a},emits:["refetch"],props:{selectedCampaigns:{type:Array,default:()=>[]},options:{type:Object,default:()=>({})},filters:{type:Object,default:()=>({})},allSelected:{type:Boolean,default:!1},theme_mode:{type:String,default:""}},watch:{"select_job.action_name"(e){"apply_labels"!==e&&(this.selectedLabels=[])}},computed:{bulkSelectPopperClass(){return"dark"===this.theme_mode?"fcrm-force-light":"fcrm-dark"},bulkSelectWordbreakPopperClass(){return"dark"===this.theme_mode?"fcrm_select_options_wordbreak fcrm-force-light":"fcrm_select_options_wordbreak fcrm-dark"}},data:()=>({select_job:{action_name:""},doing_action:!1,selectedLabels:[]}),methods:{doApplyLabels(){this.selectedLabels.length&&this.doBulkAction("apply_labels")},confirmAndDeleteCampaigns(){s.confirm(this.$t("Are you sure you want to delete the selected recurring campaigns?"),this.$t("Delete Recurring Campaigns"),{confirmButtonText:this.$t("Delete"),cancelButtonText:this.$t("Cancel"),type:"warning"}).then(()=>{this.doBulkAction("delete_campaigns")}).catch(()=>{})},doBulkAction(e){const a={action_name:e,labels:"apply_labels"===e?this.selectedLabels:[]};this.allSelected?(a.select_all=!0,a.filters=this.filters):a.campaign_ids=this.selectedCampaigns.map(e=>e.id),this.doing_action=!0,this.$post("recurring-campaigns/do-bulk-action",a).then(e=>{this.$notify.success(e.message),this.$emit("refetch"),this.selectedLabels=[],this.select_job.action_name=""}).catch(e=>{this.handleError(e)}).finally(()=>{this.doing_action=!1})}}},X={class:"fcrm_bulk_action_inline"},ee={class:"fcrm_bulk_wrap"},ae={class:"icon"};const te=z(Z,[["render",function(e,a,t,s,c,p){const d=l,m=i,h=w("Check"),u=o,g=r,_=w("Delete"),f=n;return k(),S("div",X,[A("div",ee,[L(m,{clearable:"",filterable:"",size:"small",class:"fcrm_bulk_select",placeholder:e.$t("Select Action"),"popper-class":p.bulkSelectPopperClass,effect:"dark",modelValue:c.select_job.action_name,"onUpdate:modelValue":a[0]||(a[0]=e=>c.select_job.action_name=e)},{default:B(()=>[L(d,{label:e.$t("Apply Labels"),value:"apply_labels"},null,8,["label"]),e.hasPermission("fcrm_manage_email_delete")?(k(),D(d,{key:0,label:e.$t("Delete Selected"),value:"delete_campaigns"},null,8,["label"])):F("",!0)],void 0),_:1},8,["placeholder","popper-class","modelValue"]),"apply_labels"===c.select_job.action_name?(k(),S(T,{key:0},[L(m,{filterable:"",size:"small",class:"fcrm_bulk_select",placeholder:e.$t("Select Labels"),modelValue:c.selectedLabels,"onUpdate:modelValue":a[1]||(a[1]=e=>c.selectedLabels=e),multiple:"","collapse-tags":"","collapse-tags-tooltip":"","popper-class":p.bulkSelectWordbreakPopperClass,effect:"dark"},{default:B(()=>{var e;return[(k(!0),S(T,null,P((null==(e=t.options)?void 0:e.labels)||[],e=>(k(),D(d,{key:e.id,value:e.id,label:e.title},{default:B(()=>[A("span",{style:j("background:"+e.settings.color+";padding: 2px 5px 4px 5px;border-radius: 4px;color: #0E121B;")},R(e.title),5)],void 0,!0),_:2},1032,["value","label"]))),128))]},void 0),_:1},8,["placeholder","modelValue","popper-class"]),E((k(),D(g,{disabled:c.doing_action||!c.selectedLabels.length,onClick:a[2]||(a[2]=e=>p.doApplyLabels()),type:"primary",size:"small"},{default:B(()=>[A("span",ae,[L(u,null,{default:B(()=>[L(h)],void 0,!0),_:1})]),x(" "+R(e.$t("Apply Label")),1)],void 0),_:1},8,["disabled"])),[[f,c.doing_action]])],64)):"delete_campaigns"===c.select_job.action_name?E((k(),D(g,{key:1,disabled:c.doing_action,type:"danger",size:"small",plain:"",onClick:p.confirmAndDeleteCampaigns},{default:B(()=>[L(u,null,{default:B(()=>[L(_)],void 0,!0),_:1}),x(" "+R(e.$t("Delete Selected")),1)],void 0),_:1},8,["disabled","onClick"])),[[f,c.doing_action]]):F("",!0)])])}]]),se=q(()=>e(()=>import("../../../../v3app/src/Modules/Labels/Labels.js?ver=3.1.8"),[],import.meta.url)),le=q(()=>e(()=>import("../../../../v3app/src/Modules/Contacts/Filter/ActiveFiltersBar.js?ver=3.1.8"),[],import.meta.url)),ie=q(()=>e(()=>import("../../../../v3app/src/Modules/Contacts/Filter/FilterPopover.js?ver=3.1.8"),[],import.meta.url)),ne={class:"fcrm_email_recurring_campaigns_page"},oe={class:"fcrm_page_header_top_nav_wrapper"},re={class:"fcrm_page_header_top_nav"},ce={class:"fcrm-layout-width"},pe={class:"icon"},de={class:"el-popover__reference"},me={class:"icon"},he={class:"el-popover__reference"},ue={class:"icon"},ge={class:"icon"},_e={class:"icon"},fe={class:"fcrm_empty_state"},be={class:"fcrm_empty_state_text"},ye=["title"],Ce={key:0,class:"fc_funnel_labels"},ve={class:"el-popover__reference"},$e={class:"icon"},we={class:"el-popover__reference"},ke={class:"icon"},Se={class:"el-popover__reference"},Ae={class:"icon"},Le={class:"el-popover__reference"},Be={class:"icon"},De={key:0,class:"fcrm_selection_count_text"},Fe={class:"fcrm_selection_count_number"},Te={class:"fcrm_selection_count_text"},Pe={key:0,class:"fcrm_import_content"},je={class:"el-upload__text"};const Re=z({name:"RecurringCampaigns",components:{Icons:H,PageHeader:G,FloatingBulkActionShell:J,Badge:Q,GenericPromo:K,FilterPopover:ie,ActiveFiltersBar:le,TopNav:Y,Confirm:I,PaginationBar:M,DataTable:N,Labels:se,BulkCampaignActions:te,EditPen:d,Delete:t,Close:p,CopyDocument:c},data(){return{loading:!1,deleting:!1,search:"",dialogVisible:!1,app_loaded:!1,pagination:{total:0,per_page:10,current_page:1},order:"desc",orderBy:"id",selection:!1,campaigns:[],selectedCampaigns:[],duplicating:!1,showingLabelsConfig:!1,options:{labels:[]},labelFilter:[],selectedLabels:[],isOpenCampaignAction:!1,showApplyLabelSetting:!1,showActionButtons:!1,importDialogVisible:!1,inline_errors:null,statuses:[{key:"active",label:this.$t("Active")},{key:"draft",label:this.$t("Draft")}],filterByStatuses:[],query_data:{labels:[],statuses:[]},allSelected:!1,current_mode:"system"===W.getCurrentTheme()?W.getSystemTheme():W.getCurrentTheme()}},computed:{url(){let e=window.ajaxurl;return e+=(e.match(/\?/)?"&":"?")+jQuery.param({action:"fluentcrm_import_recurring_campaigns"}),e},canSelectAll(){if(!this.pagination||!this.pagination.per_page||!this.pagination.total)return!1;const e=this.selectedCampaigns.length===this.pagination.per_page,a=this.selectedCampaigns.length{this.campaigns=e.campaigns.data,this.pagination.total=e.campaigns.total}).catch(e=>{this.handleError(e)}).finally(()=>{this.loading=!1,this.app_loaded=!0})},deleteSelected(){const e=[];this.each(this.selectedCampaigns,a=>{e.push(a.id)}),this.deleteCampaigns(e,()=>{this.selectedCampaigns=[],this.selection=!1})},onSelection(e){this.selection=!!e.length,this.selectedCampaigns=e,e.length>0&&e.length!==this.pagination.per_page&&(this.allSelected=!1),e.length||(this.allSelected=!1)},clearRecurringSelection(){this.$refs.recurringCampaignTable&&this.$refs.recurringCampaignTable.clearSelection(),this.selectedCampaigns=[],this.allSelected=!1,this.selection=!1},selectAllRecurringCampaigns(){this.canSelectAll&&(this.$refs.recurringCampaignTable&&this.campaigns&&this.campaigns.length>0&&this.campaigns.forEach(e=>{this.$refs.recurringCampaignTable.toggleRowSelection(e,!0)}),this.allSelected=!0)},selectOnlyThisPage(){this.allSelected=!1;const e=this.campaigns.map(e=>e.id);this.selectedCampaigns=this.selectedCampaigns.filter(a=>e.includes(a.id))},deleteCampaigns(e,a){this.$post("recurring-campaigns/delete-bulk",{campaign_ids:e}).then(e=>{a&&a(e),this.fetch(),this.$notify.success(e.message)}).catch(e=>{this.handleError(e)})},duplicateCampaign(e){this.duplicating=!0,this.$post(`recurring-campaigns/${e.id}/duplicate`).then(e=>{this.$notify.success(e.message),this.$router.push(this.getCampaignConfigRoute({campaign_id:e.campaign_id,status:"draft"}))}).catch(e=>{this.handleError(e)}).finally(()=>{this.duplicating=!1})},handleSortable(e){"descending"===e.order?(this.orderBy=e.prop,this.order="desc"):(this.orderBy=e.prop,this.order="asc"),this.fetch()},getDescription(e){const a=e.scheduling_settings,t="yes"==a.send_automatically?"(automatically)":"(manually)";return"daily"==a.type?`Broadcasts daily at ${a.time} ${t}`:"weekly"==a.type?`Broadcasts every ${this.getFullDayName(a.day)} at ${a.time} ${t}`:"monthly"==a.type?`Broadcasts every ${this.getDayName(a.day)} of a month at ${a.time} ${t}`:"--"},getFullDayName(e){switch(e){case"sun":return this.$t("Sunday");case"mon":return this.$t("Monday");case"tue":return this.$t("Tuesday");case"wed":return this.$t("Wednesday");case"thu":return this.$t("Thursday");case"fri":return this.$t("Friday");case"sat":return this.$t("Saturday");default:return"--"}},getDayName(e){if(e<1||e>31)return"--";switch(e){case 1:case 21:case 31:return`${e}st`;case 2:case 22:return`${e}nd`;case 3:case 23:return`${e}rd`;default:return`${e}th`}},exportRecurringCampaign(e){this.has_campaign_pro?location.href=window.ajaxurl+"?"+jQuery.param({action:"fluentcrm_export_recurring_campaign",campaign_id:e.id}):this.$notify.error(this.$t("Recurring_Campaign_Export_Alert"))},fetchLabels(){this.$get("labels").then(e=>{this.options.labels=e.labels}).catch(e=>{this.handleError(e)}).finally(()=>{})},showLabelDialog(){this.showingLabelsConfig=!0},closeDrawer(){this.showingLabelsConfig=!1},handleBackAction(){this.showActionButtons=!0,this.showApplyLabelSetting=!1},toggleComponent(){this.isOpenCampaignAction=!this.isOpenCampaignAction,this.isOpenCampaignAction?(this.showActionButtons=!0,setTimeout(()=>{document.addEventListener("click",this.handleClickOutside)},0)):document.removeEventListener("click",this.handleClickOutside)},handleClickOutside(e){var a;(null==(a=this.$refs.fcFunnelActions)?void 0:a.contains(e.target))||this.closePopOver()},closePopOver(){this.isOpenCampaignAction=!1,this.showApplyLabelSetting=!1,this.showActionButtons=!0},applyLabelSetting(e){this.showApplyLabelSetting=!0,this.showActionButtons=!1,this.selectedLabels=e.labels.map(e=>e.id)},applyLabels(e,a,t="attach"){this.$put("recurring-campaigns/"+e.id+"/update-labels",{action:t,label_ids:"attach"==t?this.selectedLabels:a}).then(e=>{this.$notify.success(e.message),this.fetch()}).catch(e=>{this.handleError(e)}).finally(()=>{})},success(e){this.$notify.success(e.message),this.$router.push(this.getCampaignConfigRoute({campaign_id:e.campaign_id,status:"draft"}))},error(e){try{const a=JSON.parse(e.message);(null==a?void 0:a.message)&&this.$notify.error(a.message),(null==a?void 0:a.requires)&&"string"==typeof a.requires&&(this.inline_errors=a.requires)}catch{this.$notify.error(this.$t("An error occurred. Please try again."))}},handleFilterApply(e){this.query_data.labels=e.labels||[],this.query_data.statuses=e.statuses||[],this.pagination.current_page=1,this.labelFilter=e.labels,this.filterByStatuses=e.statuses||[],this.fetch()},handleFilterBarChange(e){this.query_data.labels=e.labels||[],this.query_data.statuses=e.statuses||[],this.labelFilter=e.labels||[],this.filterByStatuses=e.statuses||[],void 0!==e.search&&(this.query_data.search=e.search),this.pagination.current_page=1,this.fetch()},handleOpenFilter(e){this.$nextTick(()=>{var a,t;null==(t=null==(a=this.$refs.filterPopoverRef)?void 0:a.openFilterCategory)||t.call(a,e)})}},mounted(){this.pagination.per_page=parseInt(this.storage.get("recurring_campaign_perpage",10),10)||10,this.fetch(),this.fetchLabels(),this.changeTitle(this.$t("Recurring Email Campaigns")),this.onThemeChanged=e=>{var a;this.current_mode=(null==(a=e.detail)?void 0:a.effective)||W.getCurrentTheme()},window.addEventListener(U,this.onThemeChanged)},beforeUnmount(){this.onThemeChanged&&window.removeEventListener(U,this.onThemeChanged)}},[["render",function(e,a,t,s,l,i){const c=w("TopNav"),p=w("Icons"),d=r,q=u,I=h,M=m,N=w("page-header"),z=y,H=w("filter-popover"),U=w("active-filters-bar"),W=g,Y=w("icons"),Q=f,G=w("Badge"),J=w("router-link"),K=w("Close"),Z=o,X=w("Confirm"),ee=b,ae=w("confirm"),te=_,se=w("pagination-bar"),le=w("data-table"),ie=w("bulk-campaign-actions"),Re=w("floating-bulk-action-shell"),Ee=w("labels"),xe=C,Oe=v,Ve=w("generic-promo"),qe=$,Ie=n;return k(),S("div",ne,[A("div",oe,[A("div",re,[L(c)]),a[4]||(a[4]=A("div",{class:"fcrm_page_header_top_actions"},null,-1))]),A("div",ce,[L(N,null,{title:B(()=>[x(R(e.$t("Recurring Email Campaigns"))+" ",1),E(A("small",null,"("+R(l.pagination.total|e.formatMoney)+")",513),[[O,l.pagination.total]])]),actions:B(()=>[L(M,{trigger:"click"},{dropdown:B(()=>[L(I,null,{default:B(()=>[L(q,{class:"fc_dropdown_action",onClick:i.showLabelDialog},{default:B(()=>[A("span",de,[A("span",me,[L(p,{"icon-name":"manageLabels"})]),x(" "+R(e.$t("Manage Labels")),1)])],void 0,!0),_:1},8,["onClick"]),e.hasPermission("fcrm_manage_emails")?(k(),D(q,{key:0,class:"fc_dropdown_action",onClick:a[0]||(a[0]=e=>l.importDialogVisible=!0)},{default:B(()=>[A("span",he,[A("span",ue,[L(p,{"icon-name":"import"})]),x(" "+R(e.$t("Import")),1)])],void 0,!0),_:1})):F("",!0)],void 0,!0),_:1})]),default:B(()=>[L(d,{class:"el-dropdown-link"},{default:B(()=>[x(R(e.$t("More Actions"))+" ",1),A("span",pe,[L(p,{"icon-name":"downIcon"})])],void 0,!0),_:1})],void 0,!0),_:1}),e.hasPermission("fcrm_manage_emails")?(k(),D(d,{key:0,type:"primary",onClick:a[1]||(a[1]=a=>e.$router.push({name:"create_recurring_campaign"}))},{default:B(()=>[A("span",ge,[L(p,{"icon-name":"plus"})]),x(" "+R(e.$t("Add Recurring Campaign")),1)],void 0,!0),_:1})):F("",!0)]),_:1}),L(le,{"has-selection":!1},{"header-left":B(()=>[L(z,{clearable:"",size:"small",modelValue:l.search,"onUpdate:modelValue":a[2]||(a[2]=e=>l.search=e),onClear:i.fetch,onKeyup:V(i.fetch,["enter"]),placeholder:e.$t("Type and Enter...")},{prefix:B(()=>[A("span",_e,[L(p,{"icon-name":"search"})])]),_:1},8,["modelValue","onClear","onKeyup","placeholder"])]),"header-actions":B(()=>[L(H,{ref:"filterPopoverRef",options:{labels:l.options.labels?l.options.labels:[],statuses:l.statuses?l.statuses:[]},"selected-filters":l.query_data,onApply:i.handleFilterApply},null,8,["options","selected-filters","onApply"])]),"active-filters":B(()=>[l.selection?F("",!0):(k(),D(U,{key:0,"selected-filters":l.query_data,options:{labels:l.options.labels?l.options.labels:[],statuses:l.statuses?l.statuses:[]},onFilterChange:i.handleFilterBarChange,onOpenFilter:i.handleOpenFilter,plus_filter_icon:!0},null,8,["selected-filters","options","onFilterChange","onOpenFilter"]))]),table:B(()=>[!l.loading&&l.app_loaded||l.campaigns.length?E((k(),D(te,{key:1,ref:"recurringCampaignTable",stripe:"",border:"",data:l.campaigns,onSortChange:i.handleSortable,onSelectionChange:i.onSelection},{empty:B(()=>[A("div",fe,[L(Y,{"icon-name":"common-empty-state"}),A("div",be,[A("span",null,R(e.$t("Create your first recurring campaign to automatically send scheduled emails to your audience.")),1)])])]),default:B(()=>[L(Q,{type:"selection",width:"45"}),L(Q,{"min-width":200,sortable:"custom",label:e.$t("Title"),prop:"title"},{default:B(a=>[L(J,{to:i.getCampaignConfigRoute(a.row)},{default:B(()=>[x(R(a.row.title)+" ",1),L(G,{type:a.row.status},null,8,["type"])],void 0,!0),_:2},1032,["to"]),a.row.has_draft?(k(),D(d,{key:0,onClick:t=>e.$router.push({name:"past_recurring_emails",params:{campaign_id:a.row.id}}),size:"small",type:"info"},{default:B(()=>[x(R(e.$t("View Draft Email")),1)],void 0,!0),_:1},8,["onClick"])):F("",!0)]),_:1},8,["label"]),L(Q,{"min-width":200,label:e.$t("Description")},{default:B(t=>[x(R(i.getDescription(t.row.settings))+" ",1),"active"!=t.row.status||t.row.has_draft?F("",!0):(k(),S("span",{key:0,class:"fc_small",title:e.nsHumanDiffTime(t.row.scheduled_at)},[a[5]||(a[5]=A("br",null,null,-1)),x(R(e.$t("Next Schedule:"))+" "+R(t.row.scheduled_at),1)],8,ye))]),_:1},8,["label"]),L(Q,{width:180,label:e.$t("Labels")},{default:B(a=>[a.row.labels?(k(),S("div",Ce,[(k(!0),S(T,null,P(a.row.labels,t=>(k(),D(ee,{key:t.id,size:"small",style:j({background:t.color})},{default:B(()=>[x(R(t.title)+" ",1),L(X,{onYes:e=>i.applyLabels(a.row,t.id,"detach"),message:e.$t("Remove_Label_From_campaign_Message")},{reference:B(()=>[L(Z,{class:"el-tag__close"},{default:B(()=>[L(K)],void 0,!0),_:1})]),_:1},8,["onYes","message"])],void 0,!0),_:2},1032,["style"]))),128))])):F("",!0)]),_:1},8,["label"]),L(Q,{width:"100",label:e.$t("Broadcasts")},{default:B(e=>[L(J,{to:{name:"past_recurring_emails",params:{campaign_id:e.row.id}}},{default:B(()=>[x(R(e.row.emails_count),1)],void 0,!0),_:2},1032,["to"])]),_:1},8,["label"]),L(Q,{width:"180",sortable:"custom",label:e.$t("Created at"),prop:"created_at"},{default:B(a=>[A("span",null,R(e.nsHumanDiffTime(a.row.created_at)),1)]),_:1},8,["label"]),L(Q,{fixed:"right",width:"60","class-name":"fcrm_table_actions_cell"},{default:B(a=>[L(M,{trigger:"click",placement:"bottom-end"},{dropdown:B(()=>[L(I,null,{default:B(()=>[e.hasPermission("fcrm_manage_emails")?(k(),D(q,{key:0,onClick:e=>i.openCampaignForEdit(a.row)},{default:B(()=>[A("span",ve,[A("span",$e,[L(p,{"icon-name":"EditPen"})]),x(" "+R(e.$t("Edit")),1)])],void 0,!0),_:1},8,["onClick"])):F("",!0),e.hasPermission("fcrm_manage_emails")?(k(),D(q,{key:1,onClick:e=>i.duplicateCampaign(a.row)},{default:B(()=>[A("span",we,[A("span",ke,[L(p,{"icon-name":"duplicate"})]),x(" "+R(e.$t("Duplicate")),1)])],void 0,!0),_:1},8,["onClick"])):F("",!0),e.hasPermission("fcrm_manage_emails")?(k(),D(q,{key:2,onClick:e=>i.exportRecurringCampaign(a.row)},{default:B(()=>[A("span",Se,[A("span",Ae,[L(p,{"icon-name":"export"})]),x(" "+R(e.$t("Export")),1)])],void 0,!0),_:1},8,["onClick"])):F("",!0),e.hasPermission("fcrm_manage_email_delete")?(k(),D(q,{key:3,class:"fcrm_danger_action"},{default:B(()=>[L(ae,{placement:"top-start",message:e.$t("Rec_Camp_Delete_Alert"),onYes:e=>i.deleteCampaigns([a.row.id])},{reference:B(()=>[A("span",Le,[A("span",Be,[L(p,{"icon-name":"delete"})]),x(" "+R(e.$t("Delete")),1)])]),_:1},8,["message","onYes"])],void 0,!0),_:2},1024)):F("",!0)],void 0,!0),_:2},1024)]),default:B(()=>[L(d,{link:"",class:"el-dropdown-link","aria-label":e.$t("Row actions")},{default:B(()=>[L(p,{"icon-name":"more_actions"})],void 0,!0),_:1},8,["aria-label"])],void 0,!0),_:2},1024)]),_:1})],void 0,!0),_:1},8,["data","onSortChange","onSelectionChange"])),[[Ie,l.loading]]):(k(),D(W,{key:0,style:{padding:"20px"},rows:7}))]),pagination:B(()=>[L(se,{pagination:l.pagination,onFetch:i.fetch},null,8,["pagination","onFetch"])]),_:1}),L(Re,{visible:l.selection,"theme-mode":l.current_mode,"selected-count":l.selectedCampaigns.length,"selected-label":e.$t("selected"),"show-select-all":i.canSelectAll&&!l.allSelected,"show-select-only-page":i.canSelectAll&&l.allSelected,"select-all-label":e.$t("Select All %s",e.formatMoney(l.pagination.total)),"select-only-page-label":e.$t("Select only this page"),"deselect-label":e.$t("Deselect"),onSelectAll:i.selectAllRecurringCampaigns,onSelectOnlyPage:i.selectOnlyThisPage,onDeselect:i.clearRecurringSelection},{actions:B(()=>[L(ie,{selectedCampaigns:l.selectedCampaigns,options:l.options,filters:l.query_data,"all-selected":l.allSelected,theme_mode:l.current_mode,onRefetch:i.fetch},null,8,["selectedCampaigns","options","filters","all-selected","theme_mode","onRefetch"])]),count:B(()=>[l.allSelected?(k(),S("span",De,R(e.$t("All %s selected",e.formatMoney(l.pagination.total))),1)):(k(),S(T,{key:1},[A("span",Fe,R(l.selectedCampaigns.length),1),A("span",Te,R(e.$t("selected")),1)],64))]),_:1},8,["visible","theme-mode","selected-count","selected-label","show-select-all","show-select-only-page","select-all-label","select-only-page-label","deselect-label","onSelectAll","onSelectOnlyPage","onDeselect"]),l.showingLabelsConfig?(k(),D(Ee,{key:0,open:l.showingLabelsConfig,onClose:i.closeDrawer,onCallFetchLabels:i.fetchLabels},null,8,["open","onClose","onCallFetchLabels"])):F("",!0),L(qe,{title:e.$t("Import Recurring Campaign"),modelValue:l.importDialogVisible,"onUpdate:modelValue":a[3]||(a[3]=e=>l.importDialogVisible=e),"append-to-body":!0,"close-on-click-modal":!1,width:"640px",class:"","modal-class":"fcrm_import_dialog"},{default:B(()=>[e.has_campaign_pro?(k(),S("div",Pe,[A("h3",null,R(e.$t("Upload JSON File")),1),L(xe,{drag:"",limit:1,action:i.url,ref:"uploader",multiple:!1,"on-error":i.error,"on-success":i.success},{default:B(()=>[a[6]||(a[6]=A("span",{class:"upload-icon"},[A("svg",{width:"24",height:"24",viewBox:"0 0 24 24",fill:"none",xmlns:"http://www.w3.org/2000/svg"},[A("path",{d:"M12.0001 12.5274L15.8188 16.3452L14.5452 17.6187L12.9001 15.9735V21H11.1V15.9717L9.45485 17.6187L8.18135 16.3452L12.0001 12.5274ZM12.0001 3C13.5453 3.00007 15.0367 3.568 16.1906 4.59581C17.3445 5.62361 18.0805 7.03962 18.2587 8.5746C19.3785 8.87998 20.3554 9.56919 21.0186 10.5218C21.6819 11.4744 21.9893 12.6297 21.8871 13.786C21.7849 14.9422 21.2797 16.0257 20.4597 16.8472C19.6396 17.6687 18.557 18.1759 17.401 18.2802V16.4676C17.8151 16.4085 18.2133 16.2674 18.5724 16.0527C18.9314 15.8379 19.2441 15.5539 19.4922 15.217C19.7402 14.8801 19.9187 14.4972 20.0171 14.0906C20.1156 13.6839 20.1321 13.2618 20.0656 12.8488C19.9991 12.4357 19.851 12.0401 19.63 11.6849C19.4089 11.3297 19.1194 11.0221 18.7781 10.78C18.4369 10.538 18.0509 10.3663 17.6426 10.2751C17.2343 10.1838 16.812 10.1748 16.4002 10.2486C16.5411 9.5924 16.5335 8.91297 16.3778 8.2601C16.2222 7.60722 15.9225 6.99743 15.5007 6.47538C15.0789 5.95333 14.5456 5.53225 13.94 5.24298C13.3343 4.9537 12.6717 4.80357 12.0005 4.80357C11.3293 4.80357 10.6667 4.9537 10.061 5.24298C9.45539 5.53225 8.92214 5.95333 8.50031 6.47538C8.07849 6.99743 7.77879 7.60722 7.62315 8.2601C7.46752 8.91297 7.4599 9.5924 7.60085 10.2486C6.77974 10.0944 5.93101 10.2727 5.24136 10.7443C4.55171 11.2159 4.07765 11.9421 3.92345 12.7632C3.76925 13.5843 3.94756 14.433 4.41914 15.1227C4.89072 15.8123 5.61694 16.2864 6.43805 16.4406L6.60005 16.4676V18.2802C5.44396 18.1761 4.36122 17.669 3.54107 16.8476C2.72093 16.0261 2.21555 14.9426 2.11326 13.7863C2.01097 12.6301 2.31828 11.4747 2.98148 10.522C3.64468 9.56934 4.62159 8.88005 5.74145 8.5746C5.91939 7.03954 6.65532 5.62342 7.80927 4.59558C8.96323 3.56774 10.4547 2.99988 12.0001 3Z",fill:"var(--fc-secondary-text)"})])],-1)),A("div",je,R(e.$t("Choose a file or drag & drop it here.")),1),L(d,null,{default:B(()=>[x(R(e.$t("Browse File")),1)],void 0,!0),_:1})],void 0,!0),_:1},8,["action","on-error","on-success"]),L(Oe,{title:e.$t("Not_Import_Recurring_Campaigns_Alert"),type:"info","show-icon":"",closable:!1},null,8,["title"]),l.inline_errors?(k(),D(Oe,{key:0,title:l.inline_errors,type:"error","show-icon":"",closable:!1},null,8,["title"])):F("",!0)])):(k(),D(Ve,{key:1}))],void 0),_:1},8,["title","modelValue"])])])}]]);export{Re as default}; diff --git a/wp-content/plugins/fluent-crm/assets/admin/Modules/Email/RecurringCampaigns/RecurringCampaignsView.js b/wp-content/plugins/fluent-crm/assets/admin/Modules/Email/RecurringCampaigns/RecurringCampaignsView.js new file mode 100644 index 0000000..deb75d0 --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/admin/Modules/Email/RecurringCampaigns/RecurringCampaignsView.js @@ -0,0 +1 @@ +import{T as r}from"../../../../TopNav.js?ver=3.1.8";import{P as a}from"../../../../PromoCard.js?ver=3.1.8";import{P as e}from"../../../../PageHeader.js?ver=3.1.8";import{aQ as o,W as s,X as i,Z as n,ab as m,a5 as c,a9 as t,aa as p}from"../../../../vendor.js?ver=3.1.8";import{_ as d}from"../../../../fc-bits-ui.js?ver=3.1.8";import"../../../../vendor-element-plus.js?ver=3.1.8";const _={class:"fcrm_email_recurring_campaigns_page"},l={class:"fcrm_page_header_top_nav_wrapper"},g={class:"fcrm_page_header_top_nav"},v={class:"fcrm-layout-width"},u={class:"fcrm_body_boxed"};const f={key:0,class:"fc_recurring_root"},h={key:1};const P=d({name:"RecurringCampaignsView",components:{EmailRecurringPromo:d({name:"EmailRecurringPromo",components:{PromoCard:a,TopNav:r,PageHeader:e}},[["render",function(r,a,e,d,f,h){const P=o("TopNav"),y=o("page-header"),j=o("PromoCard");return s(),i("div",_,[n("div",l,[n("div",g,[m(P)]),a[0]||(a[0]=n("div",{class:"fcrm_page_header_top_actions"},null,-1))]),n("div",v,[m(y,null,{title:c(()=>[t(p(r.$t("Recurring Email Campaigns")),1)]),_:1}),n("div",u,[m(j,{heading:r.$t("Recurring Campaigns"),description:r.$t("Send_Email_Daily_Weekly_Monthly")},null,8,["heading","description"])])])])}]])}},[["render",function(r,a,e,n,c,t){const p=o("router-view"),d=o("email-recurring-promo");return r.has_campaign_pro?(s(),i("div",f,[m(p)])):(s(),i("div",h,[m(d)]))}]]);export{P as default}; diff --git a/wp-content/plugins/fluent-crm/assets/admin/Modules/Email/RecurringCampaigns/ViewSingleCampaign.js b/wp-content/plugins/fluent-crm/assets/admin/Modules/Email/RecurringCampaigns/ViewSingleCampaign.js new file mode 100644 index 0000000..8e0b520 --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/admin/Modules/Email/RecurringCampaigns/ViewSingleCampaign.js @@ -0,0 +1 @@ +import{aZ as a,a_ as i,aP as e,aB as t,ap as s}from"../../../../vendor-element-plus.js?ver=3.1.8";import{aQ as n,W as c,X as r,Z as m,ab as g,a5 as d,a9 as p,aa as l,a8 as o,a0 as _,av as u}from"../../../../vendor.js?ver=3.1.8";import{S as h}from"../../../../TestEmail.js?ver=3.1.8";import{_ as f}from"../../../../fc-bits-ui.js?ver=3.1.8";const v={class:"fcrm_single_recurring_camp_page"},y={class:"fcrm-layout-width"},b={key:0,class:"fcrm_page_header"},$={class:"fcrm_page_header_content"},x={class:"fcrm_page_header_breadcrumb"},S={class:"fcrm_page_header_actions"},k={key:1,class:"fcrm_single_recurring_camp_body fcrm_body_boxed"},j={class:"fcrm_single_recurring_camp_body_header"},w={class:"fcrm_single_recurring_camp_body_header--left"},E={key:0,class:"fcrm_action_menu"},C={class:"fcrm_single_recurring_camp_body_header--right"},T={class:"fcrm_single_recurring_camp_body--email-body fcrm_sticky_block_composer_page"},V={key:2,class:"fluentcrm_body fluentcrm_body_boxed"};const A=f({name:"ViewSingleCampaign",components:{SendTestEmail:h},props:["campaign_id"],data:()=>({ArrowRightBold:u(s),campaign:{},loading:!0}),methods:{fetchCampaign(){this.loading=!0,this.$get("recurring-campaigns/"+this.campaign_id).then(a=>{this.campaign=a.campaign}).catch(a=>{this.handleError(a)}).finally(()=>{this.loading=!1})},changeStatus(){const a=this.campaign.status,i=()=>this.$post("recurring-campaigns/"+this.campaign.id+"/change-status",{status:a}).then(a=>{this.$notify.success(a.message)});"active"===a?this.$post("recurring-campaigns/update-campaign-data",{campaign:JSON.stringify(this.campaign),campaign_id:this.campaign.id,validate_subject:"yes"}).then(i).catch(a=>{this.handleError(a),this.campaign.status="draft"}):i().catch(a=>{this.handleError(a),this.campaign.status="active"})},campaignStatusText(a){return{active:this.$t("Active"),draft:this.$t("Draft")}[a]||a}},mounted(){this.fetchCampaign()}},[["render",function(s,u,h,f,A,B){const R=a,Z=i,D=n("send-test-email"),H=n("router-link"),J=e,N=n("router-view"),O=t;return c(),r("div",v,[m("div",y,[A.campaign?(c(),r("div",b,[m("div",$,[m("div",x,[g(Z,{"separator-icon":A.ArrowRightBold},{default:d(()=>[g(R,{to:{name:"recurring_campaigns"}},{default:d(()=>[p(l(s.$t("Recurring Campaigns")),1)],void 0,!0),_:1}),g(R,null,{default:d(()=>[p(l(A.campaign.title),1)],void 0,!0),_:1})],void 0),_:1},8,["separator-icon"])])]),m("div",S,[g(D,{campaign:{email_subject:A.campaign.email_subject,email_pre_header:A.campaign.post_excerpt,email_body:A.campaign.post_content||A.campaign.email_body,design_template:A.campaign.design_template,settings:A.campaign.settings}},null,8,["campaign"])])])):o("",!0),A.campaign&&A.campaign.id?(c(),r("div",k,[m("div",j,[m("div",w,[A.campaign.id?(c(),r("ul",E,[m("li",null,[g(H,{to:{name:"view_recurring_campaign",params:{campaign_id:A.campaign.id}}},{default:d(()=>[p(l(s.$t("Email Configuration")),1)],void 0),_:1},8,["to"])]),m("li",null,[g(H,{to:{name:"recurring_campaign_settings",params:{campaign_id:A.campaign.id}}},{default:d(()=>[p(l(s.$t("Settings")),1)],void 0),_:1},8,["to"])]),m("li",null,[g(H,{to:{name:"past_recurring_emails",params:{campaign_id:A.campaign.id}}},{default:d(()=>[p(l(s.$t("Email History")),1)],void 0),_:1},8,["to"])])])):o("",!0)]),m("div",C,[g(J,{onChange:u[0]||(u[0]=a=>B.changeStatus()),modelValue:A.campaign.status,"onUpdate:modelValue":u[1]||(u[1]=a=>A.campaign.status=a),"active-value":"active","inactive-value":"draft","active-text":B.campaignStatusText(A.campaign.status)},null,8,["modelValue","active-text"])])]),m("div",T,[A.campaign.id?(c(),r("div",{key:0,class:_([s.$route.name,"fcrm_single_recurring_camp_body--email-body-inner"])},[g(N,{campaign:A.campaign},null,8,["campaign"])],2)):o("",!0)])])):A.loading?(c(),r("div",V,[g(O,{animated:!0,style:{padding:"20px"},rows:7})])):o("",!0)])])}]]);export{A as default}; diff --git a/wp-content/plugins/fluent-crm/assets/admin/Modules/Email/Templates/EditPattern.js b/wp-content/plugins/fluent-crm/assets/admin/Modules/Email/Templates/EditPattern.js new file mode 100644 index 0000000..42e0501 --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/admin/Modules/Email/Templates/EditPattern.js @@ -0,0 +1 @@ +import{aZ as t,a_ as e,k as a,ay as i,av as r,aD as s,aA as o,aw as l,e as n,aK as d,aL as p,az as c,ax as m,ap as _}from"../../../../vendor-element-plus.js?ver=3.1.8";import{aQ as g,W as u,X as v,Z as h,ab as y,a5 as f,a9 as b,aa as j,a6 as $,a8 as P,J as w,az as k,Y as E,av as x}from"../../../../vendor.js?ver=3.1.8";import{E as C}from"../../../../BlockComposer.js?ver=3.1.8";import{_ as S}from"../../../../fc-bits-ui.js?ver=3.1.8";import"../../../../input-popover-dropdown.js?ver=3.1.8";import"../../../../_FormBuilder2.js?ver=3.1.8";import"../../../../PhotoWidget.js?ver=3.1.8";import"../../../../data_config.js?ver=3.1.8";import"../../../../_OptionSelector.js?ver=3.1.8";import"../../../../_AjaxSelector.js?ver=3.1.8";import"../../../../_VerifiedEmailInput.js?ver=3.1.8";import"../../../../EmailPreview.js?ver=3.1.8";import"../../../../PreviewIframeBuilder.js?ver=3.1.8";import"../../../../TestEmail.js?ver=3.1.8";import"../../../../CampaignSubjectLines.js?ver=3.1.8";import"../../../../PaginationBar.js?ver=3.1.8";import"../../../../_MergeCodes.js?ver=3.1.8";import"../../../../BuiltinTemplateDrawer.js?ver=3.1.8";import"../../../../PromoCard.js?ver=3.1.8";import"../../../../fc-bits.js?ver=3.1.8";const V={class:"fluentcrm-templates fluentcrm-view-wrapper fluentcrm_view fcrm_edit_template_page fcrm_sticky_block_composer_page"},D={class:"fcrm_page_header_top_nav_wrapper"},B={class:"fcrm_page_header_top_nav"},A={class:"fcrm_page_header_top_actions"},F={class:"fluentcrm_body fluentcrm_body_boxed",style:{position:"relative"}},T={key:0,class:"fc_loading_bar"},z={key:1,style:{padding:"40px","text-align":"center"}},I={key:2},R={class:"fcrm_mt_20"},U={style:{"font-size":"12px",color:"var(--fc-secondary-text)",margin:"4px 0 0","padding-inline-start":"24px"}},L={key:3,style:{margin:"0 -20px"}};const N=S({name:"EditPattern",props:["pattern_id"],components:{EmailBlockComposer:C},data:()=>({ArrowRightBold:x(_),pattern:{id:0,title:"",content:"",category:"",description:"",sync_status:""},campaignData:{id:0,title:"",email_body:"",design_template:"simple",__fcrm_block_type:"email_pattern",settings:{template_config:{}}},editorFeatures:{email_style_settings:!1,email_footer:!1,email_preview:!1,save_as_template:!1,browse_templates:!1,smartcodes:!1,design_switcher:!1},categories:[],loading:!0,saving:!1,app_ready:!1,loadError:null}),computed:{isSynced:{get(){return"unsynced"!==this.pattern.sync_status},set(t){this.pattern.sync_status=t?"":"unsynced"}}},methods:{fetchCategories(){this.$get("email-patterns/categories").then(t=>{this.categories=t.categories||[]}).catch(()=>{})},fetchPattern(){this.loading=!0,this.loadError=null,this.$get(`email-patterns/${this.pattern_id}`).then(t=>{const e=t.pattern||t;this.pattern={id:e.id,title:e.title||"",content:e.content||"",category:e.category||"",description:e.description||"",sync_status:e.sync_status||""},this.campaignData.id=e.id||0,this.campaignData.title=e.title||"",this.campaignData.email_body=e.content||"",this.$nextTick(()=>{this.app_ready=!0})}).catch(t=>{this.loadError=this.$t("Failed to load pattern. Please try again."),this.handleError(t)}).finally(()=>{this.loading=!1})},savePattern(){this.pattern.title?(this.saving=!0,this.$put(`email-patterns/${this.pattern.id}`,{title:this.pattern.title,content:this.campaignData.email_body,category:this.pattern.category||"",sync_status:this.pattern.sync_status}).then(t=>{this.campaignData.title=this.pattern.title,this.$notify.success(t.message)}).catch(t=>{this.handleError(t)}).finally(()=>{this.saving=!1})):this.$notify.error(this.$t("Pattern name is required"))}},mounted(){this.fetchPattern(),this.fetchCategories(),this.changeTitle(this.$t("Edit Pattern"))}},[["render",function(_,x,C,S,N,W){const Z=t,q=e,J=a,K=r,M=n,O=l,Q=o,X=p,Y=d,G=c,H=s,tt=m,et=g("email-block-composer"),at=i;return u(),v("div",V,[h("div",D,[h("div",B,[y(q,{"separator-icon":N.ArrowRightBold},{default:f(()=>[y(Z,{to:{name:"email_patterns"}},{default:f(()=>[b(j(_.$t("Email Patterns")),1)],void 0,!0),_:1}),y(Z,null,{default:f(()=>[b(j(N.pattern.title||_.$t("Edit Pattern")),1)],void 0,!0),_:1})],void 0),_:1},8,["separator-icon"])]),h("div",A,[y(J,{loading:N.saving,disabled:N.saving,onClick:W.savePattern,type:"primary"},{default:f(()=>[b(j(_.$t("Save Pattern")),1)],void 0),_:1},8,["loading","disabled","onClick"])])]),$((u(),v("div",F,[N.loading?(u(),v("div",T,[y(K,{class:"el-progress_animated","show-text":!1,percentage:30})])):P("",!0),N.loadError?(u(),v("div",z,[h("p",null,j(N.loadError),1),y(J,{type:"primary",onClick:x[0]||(x[0]=t=>W.fetchPattern())},{default:f(()=>[b(j(_.$t("Retry")),1)],void 0),_:1})])):P("",!0),N.app_ready?(u(),v("div",I,[y(tt,{"label-position":"top"},{default:f(()=>[y(H,{gutter:30},{default:f(()=>[y(Q,{sm:24,md:8},{default:f(()=>[y(O,{label:_.$t("Pattern Name")},{default:f(()=>[y(M,{placeholder:_.$t("Pattern Name"),modelValue:N.pattern.title,"onUpdate:modelValue":x[1]||(x[1]=t=>N.pattern.title=t)},null,8,["placeholder","modelValue"])],void 0,!0),_:1},8,["label"])],void 0,!0),_:1}),y(Q,{sm:24,md:8},{default:f(()=>[y(O,{label:_.$t("Category")},{default:f(()=>[y(Y,{modelValue:N.pattern.category,"onUpdate:modelValue":x[2]||(x[2]=t=>N.pattern.category=t),filterable:"","allow-create":"",clearable:"","default-first-option":"",placeholder:_.$t("Select or create"),style:{width:"100%"},onClear:x[3]||(x[3]=t=>N.pattern.category="")},{default:f(()=>[(u(!0),v(w,null,k(N.categories,t=>(u(),E(X,{key:t,label:t,value:t},null,8,["label","value"]))),128))],void 0,!0),_:1},8,["modelValue","placeholder"])],void 0,!0),_:1},8,["label"])],void 0,!0),_:1}),y(Q,{sm:24,md:8},{default:f(()=>[h("div",R,[y(G,{modelValue:W.isSynced,"onUpdate:modelValue":x[4]||(x[4]=t=>W.isSynced=t)},{default:f(()=>[b(j(_.$t("Synced Pattern")),1)],void 0,!0),_:1},8,["modelValue"]),h("p",U,j(W.isSynced?_.$t("Changes here update all emails using this pattern."):_.$t("Inserted as a copy — emails get their own independent version.")),1)])],void 0,!0),_:1})],void 0,!0),_:1})],void 0),_:1})])):P("",!0),N.app_ready?(u(),v("div",L,[y(et,{onSave:x[5]||(x[5]=t=>W.savePattern()),iframe_nav_mode:"compose","hide-back-btn":!0,"hide-next-btn":!0,"disable-gutenberg-autosave":!0,campaign:N.campaignData,features:N.editorFeatures},null,8,["campaign","features"])])):P("",!0)])),[[at,N.loading]])])}]]);export{N as default}; diff --git a/wp-content/plugins/fluent-crm/assets/admin/Modules/Email/Templates/EditTemplate.js b/wp-content/plugins/fluent-crm/assets/admin/Modules/Email/Templates/EditTemplate.js new file mode 100644 index 0000000..8294804 --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/admin/Modules/Email/Templates/EditTemplate.js @@ -0,0 +1 @@ +import{a4 as e,P as t,aZ as a,a_ as l,k as i,h as s,i as o,E as m,j as p,ay as r,av as d,aD as n,aA as _,aw as c,e as h,ax as u,ap as v}from"../../../../vendor-element-plus.js?ver=3.1.8";import{aQ as g,W as f,X as y,Z as b,aa as T,Y as w,a5 as $,ab as j,a9 as k,J as C,a6 as S,a8 as E,av as x}from"../../../../vendor.js?ver=3.1.8";import{E as I}from"../../../../BlockComposer.js?ver=3.1.8";import{I as D}from"../../../../_FormBuilder2.js?ver=3.1.8";import{S as V}from"../../../../TestEmail.js?ver=3.1.8";import{_ as B,I as P}from"../../../../fc-bits-ui.js?ver=3.1.8";import"../../../../input-popover-dropdown.js?ver=3.1.8";import"../../../../data_config.js?ver=3.1.8";import"../../../../EmailPreview.js?ver=3.1.8";import"../../../../PreviewIframeBuilder.js?ver=3.1.8";import"../../../../CampaignSubjectLines.js?ver=3.1.8";import"../../../../PaginationBar.js?ver=3.1.8";import"../../../../_MergeCodes.js?ver=3.1.8";import"../../../../BuiltinTemplateDrawer.js?ver=3.1.8";import"../../../../PromoCard.js?ver=3.1.8";import"../../../../fc-bits.js?ver=3.1.8";import"../../../../PhotoWidget.js?ver=3.1.8";import"../../../../_OptionSelector.js?ver=3.1.8";import"../../../../_AjaxSelector.js?ver=3.1.8";import"../../../../_VerifiedEmailInput.js?ver=3.1.8";const A={class:"fluentcrm-templates fluentcrm-view-wrapper fluentcrm_view fcrm_edit_template_page fcrm_sticky_block_composer_page"},M={class:"fcrm_page_header_top_nav_wrapper"},U={class:"fcrm_page_header_top_nav"},K={key:0,class:"fcrm_page_header_top_nav--title"},L={class:"fcrm_page_header_top_actions"},F={class:"icon"},J={class:"fluentcrm_body fluentcrm_body_boxed",style:{position:"relative"}},O={key:0,class:"fc_loading_bar"},H={key:1},N={key:2,class:"fc_template_init_notice"},R={key:3,style:{margin:"0 -20px"}};const Q=B({name:"edit_template",props:["template_id"],emits:["getVisualData"],components:{Icons:P,InputPopover:D,EmailBlockComposer:I,SendTestEmail:V,MoreFilled:t,Download:e},data:()=>({ArrowRightBold:x(v),email_template:{post_title:"",post_content:" ",post_excerpt:"",email_subject:"",edit_type:"html",design_template:"simple",settings:{template_config:{}}},email_template_designs:window.fcAdmin.email_template_designs,smart_codes:[],loading:!0,saving:!1,app_ready:!1,codes_ready:!1,is_dirty:!1,prevContent:""}),computed:{hasTemplateId(){return!!parseInt(this.template_id)}},methods:{getDefaultTemplateTitle(){const e=new Date,t=e=>String(e).padStart(2,"0");return`Untitled - Created at ${`${e.getFullYear()}-${t(e.getMonth()+1)}-${t(e.getDate())} ${t(e.getHours())}:${t(e.getMinutes())}:${t(e.getSeconds())}`}`},createTemplateDraft(){this.hasTemplateId?this.maybeSaveTemplate():(this.loading=!0,this.is_dirty=!1,this.email_template.post_title||(this.email_template.post_title=this.getDefaultTemplateTitle()),this.email_template.design_template||(this.email_template.design_template="simple"),this.$post("templates",{template:JSON.stringify(this.email_template)}).then(e=>{this.$notify.success(e.message),this.$router.push({name:"edit_template",params:{template_id:e.template_id}})}).catch(e=>{console.log(e),this.handleError(e)}).finally(()=>{this.loading=!1}))},fetchSmartCodes(){this.codes_ready=!1,this.$get("templates/smartcodes",{}).then(e=>{this.smart_codes=e.smartcodes}).catch(e=>{console.log(e)}).finally(()=>{this.codes_ready=!0})},fetchTemplate(){this.loading=!0,this.$get(`templates/${this.template_id}`).then(e=>{this.email_template=e.template,this.$nextTick(()=>{this.app_ready=!0,this.prevContent=this.email_template.post_content})}).catch(e=>{console.log(e)}).finally(()=>{this.loading=!1})},saveTemplate(){this.saving=!0,this.is_dirty=!1;let e={};e=parseInt(this.template_id)?this.$post("templates",{template:JSON.stringify(this.email_template),template_id:this.template_id}):this.$post("templates",{template:JSON.stringify(this.email_template)}),this.prevContent=this.email_template.post_content,e.then(e=>{this.$notify.success(e.message),parseInt(this.template_id)||this.$router.push({name:"edit_template",params:{template_id:e.template_id}})}).catch(e=>{console.log(e),this.handleError(e)}).finally(()=>{this.saving=!1})},maybeSaveTemplate(){"visual_builder"==this.email_template.design_template?this.$bus.emit("getVisualData",{}):this.saveTemplate()},handleChangeContent(){this.is_dirty=!0},exportTemplate(){this.has_campaign_pro?location.href=window.ajaxurl+"?"+jQuery.param({action:"fluentcrm_export_template",template_id:this.template_id}):this.$notify.error(this.$t("Template_Export_Alert"))},initKeyboardSave(e){(window.navigator.platform.match("Mac")?e.metaKey:e.ctrlKey)&&"s"===e.key&&(e.preventDefault(),this.maybeSaveTemplate())},handleBeforeUnload(e){if(this.is_dirty&&this.prevContent!==this.email_template.post_content)return e.preventDefault(),e.returnValue="",""}},mounted(){this.fetchSmartCodes(),this.hasTemplateId?this.fetchTemplate():(this.loading=!1,this.app_ready=!0,this.prevContent=this.email_template.post_content),this.changeTitle(this.$t("Edit Template")),document.addEventListener("keydown",this.initKeyboardSave),window.addEventListener("beforeunload",this.handleBeforeUnload)},beforeRouteLeave(e,t,a){if(this.is_dirty&&this.prevContent!=this.email_template.post_content){if(!window.confirm(this.$t("Unsaved_Confirm_Msg")))return!1}this.unmountBlockEditor(),document.removeEventListener("keydown",this.initKeyboardSave),a()},watch:{template_id(e){e&&e>0?this.fetchTemplate():(this.loading=!1,this.app_ready=!0)}}},[["render",function(e,t,v,x,I,D){const V=a,B=l,P=i,Q=g("Icons"),W=g("Download"),Y=m,Z=o,X=s,q=p,z=g("send-test-email"),G=d,ee=h,te=c,ae=_,le=n,ie=g("input-popover"),se=u,oe=g("email-block-composer"),me=r;return f(),y("div",A,[b("div",M,[b("div",U,[0==v.template_id?(f(),y("h3",K,T(e.$t("Create Email Template")),1)):(f(),w(B,{key:1,"separator-icon":I.ArrowRightBold},{default:$(()=>[j(V,{to:{name:"templates"}},{default:$(()=>[k(T(e.$t("Email Templates")),1)],void 0,!0),_:1}),j(V,null,{default:$(()=>[k(T(I.email_template.post_title),1)],void 0,!0),_:1})],void 0),_:1},8,["separator-icon"]))]),b("div",L,[0==v.template_id?(f(),w(P,{key:0,loading:I.loading,onClick:D.createTemplateDraft,type:"primary"},{default:$(()=>[k(T(e.$t("Create Template")),1)],void 0),_:1},8,["loading","onClick"])):(f(),y(C,{key:1},[j(q,{trigger:"click"},{dropdown:$(()=>[j(X,null,{default:$(()=>[j(Z,{class:"fc_dropdown_action"},{default:$(()=>[b("span",{class:"el-popover__reference",onClick:t[0]||(t[0]=e=>D.exportTemplate())},[j(Y,null,{default:$(()=>[j(W)],void 0,!0),_:1}),k(" "+T(e.$t("Export Template")),1)])],void 0,!0),_:1})],void 0,!0),_:1})]),default:$(()=>[j(P,{class:"el-dropdown-link"},{default:$(()=>[k(T(e.$t("More Actions"))+" ",1),b("span",F,[j(Q,{"icon-name":"downIcon"})])],void 0,!0),_:1})],void 0),_:1}),j(z,{campaign:{email_subject:I.email_template.email_subject,email_pre_header:I.email_template.post_excerpt,email_body:I.email_template.post_content,design_template:I.email_template.design_template,settings:I.email_template.settings}},null,8,["campaign"]),j(P,{loading:I.saving,disabled:I.saving,onClick:D.maybeSaveTemplate,type:"primary"},{default:$(()=>[k(T(e.$t("Save Template")),1)],void 0),_:1},8,["loading","disabled","onClick"])],64))])]),S((f(),y("div",J,[I.loading?(f(),y("div",O,[j(G,{class:"el-progress_animated","show-text":!1,percentage:30})])):E("",!0),I.app_ready&&D.hasTemplateId?(f(),y("div",H,[j(se,{"label-position":"top","label-width":"120px",model:I.email_template},{default:$(()=>[j(le,{gutter:30},{default:$(()=>[j(ae,{sm:24,md:12},{default:$(()=>[j(te,{label:e.$t("Template Title")},{default:$(()=>[j(ee,{placeholder:e.$t("Template Title"),modelValue:I.email_template.post_title,"onUpdate:modelValue":t[1]||(t[1]=e=>I.email_template.post_title=e)},null,8,["placeholder","modelValue"])],void 0,!0),_:1},8,["label"])],void 0,!0),_:1})],void 0,!0),_:1}),j(le,{gutter:30},{default:$(()=>[j(ae,{sm:24,md:12},{default:$(()=>[j(te,{label:e.$t("Email Subject")},{default:$(()=>[j(ie,{doc_url:"https://fluentcrm.com/docs/merge-codes-smart-codes-usage/",popper_extra:"fc_with_c_fields",placeholder:e.$t("Email Subject"),data:I.smart_codes,modelValue:I.email_template.email_subject,"onUpdate:modelValue":t[2]||(t[2]=e=>I.email_template.email_subject=e)},null,8,["placeholder","data","modelValue"])],void 0,!0),_:1},8,["label"])],void 0,!0),_:1}),j(ae,{sm:24,md:12},{default:$(()=>[j(te,{label:e.$t("Email Pre-Header")},{default:$(()=>[j(ee,{placeholder:e.$t("Email Pre-Header"),modelValue:I.email_template.post_excerpt,"onUpdate:modelValue":t[3]||(t[3]=e=>I.email_template.post_excerpt=e)},null,8,["placeholder","modelValue"])],void 0,!0),_:1},8,["label"])],void 0,!0),_:1})],void 0,!0),_:1})],void 0),_:1},8,["model"])])):E("",!0),I.app_ready&&!D.hasTemplateId?(f(),y("div",N,[b("h3",null,T(e.$t("Click Create Template to start editing")),1)])):E("",!0),I.app_ready&&I.codes_ready&&D.hasTemplateId?(f(),y("div",R,[j(oe,{onSave:t[4]||(t[4]=e=>D.saveTemplate()),onFetch:t[5]||(t[5]=e=>D.fetchTemplate()),onChanged:t[6]||(t[6]=e=>D.handleChangeContent()),show_merge:!0,enable_templates:!0,enable_template_save:!0,show_audit:!0,iframe_nav_mode:"compose","hide-back-btn":!0,"hide-next-btn":!0,body_key:"post_content",campaign:I.email_template},null,8,["campaign"])])):E("",!0)])),[[me,I.loading]])])}]]);export{Q as default}; diff --git a/wp-content/plugins/fluent-crm/assets/admin/Modules/Email/Templates/Patterns.js b/wp-content/plugins/fluent-crm/assets/admin/Modules/Email/Templates/Patterns.js new file mode 100644 index 0000000..d348ae2 --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/admin/Modules/Email/Templates/Patterns.js @@ -0,0 +1 @@ +import{c as e,k as t,aB as a,aH as n,aI as s,j as i,h as l,i as r,ay as o,e as c,E as d,n as h}from"../../../../vendor-element-plus.js?ver=3.1.8";import{aQ as p,W as m,X as g,Z as _,ab as u,ay as f,a5 as y,a9 as k,aa as v,a8 as b,Y as P,a6 as $,b2 as C,J as S}from"../../../../vendor.js?ver=3.1.8";import{C as w}from"../../../../Confirm.js?ver=3.1.8";import{P as T}from"../../../../PaginationBar.js?ver=3.1.8";import{T as D}from"../../../../TopNav.js?ver=3.1.8";import{_ as x,I as A,a as E,T as B}from"../../../../fc-bits-ui.js?ver=3.1.8";import{P as j}from"../../../../PageHeader.js?ver=3.1.8";import{D as F}from"../../../../DataTable.js?ver=3.1.8";import{F as N}from"../../../../FloatingBulkActionShell.js?ver=3.1.8";const M={class:"fcrm_email_patterns_page"},H={class:"fcrm_page_header_top_nav_wrapper"},I={class:"fcrm_page_header_top_nav"},O={class:"fcrm-layout-width"},L={key:0},U={class:"icon"},V={class:"icon"},Y={class:"fcrm_empty_state"},K={class:"fcrm_empty_state_text"},z={key:0},J={key:1},Q={key:1,class:"template-title"},R={key:2,class:"fcrm_badge fcrm_badge_info",style:{"margin-left":"8px"}},W=["title"],X=["aria-label"],Z={class:"el-popover__reference"},q={class:"icon"},G={class:"el-popover__reference"},ee={class:"icon"},te={class:"fcrm_bulk_action_bar"},ae={class:"fcrm_bulk_action_left"},ne={class:"fc_bulk_selection_count"},se={class:"icon"};const ie=x({name:"Patterns",components:{Confirm:w,PageHeader:j,PaginationBar:T,TopNav:D,Icons:A,DataTable:F,FloatingBulkActionShell:N,Close:e},computed:{allSelectedOnPage(){const e=this.selectedPatterns.length;return this.patterns.length>0&&e===this.patterns.length&&e===this.pagination.per_page}},data:()=>({patterns:[],loading:!1,creating:!1,selection:!1,selectedPatterns:[],allSelected:!1,deleting:!1,search:"",appliedSearch:"",current_mode:"system"===B.getCurrentTheme()?B.getSystemTheme():B.getCurrentTheme(),pagination:{current_page:1,per_page:20,total:0}}),methods:{fetch(){this.loading=!0,this.selection=!1,this.allSelected=!1,this.appliedSearch=this.search,this.$get("email-patterns",{per_page:this.pagination.per_page,page:this.pagination.current_page,search:this.search}).then(e=>{this.patterns=e.patterns.data||[],this.pagination.total=e.patterns.total,!this.patterns.length&&this.pagination.current_page>1&&this.pagination.total>0&&(this.pagination.current_page=Math.min(this.pagination.current_page,Math.ceil(this.pagination.total/this.pagination.per_page)||1),this.fetch())}).catch(e=>{this.handleError(e)}).finally(()=>{this.loading=!1})},resetAndFetch(){this.pagination.current_page=1,this.fetch()},fetchPatterns(){this.fetch()},addPattern(){this.creating=!0,this.$post("email-patterns",{title:this.$t("Untitled Pattern"),content:"\x3c!-- wp:paragraph --\x3e

\x3c!-- /wp:paragraph --\x3e"}).then(e=>{this.$router.push({name:"edit_pattern",params:{pattern_id:e.pattern.id}})}).catch(e=>{this.handleError(e)}).finally(()=>{this.creating=!1})},onSelection(e){this.selection=!!e.length,this.selectedPatterns=e,this.allSelected&&e.length{this.$refs.patternTable.toggleRowSelection(e,!0)}),this.allSelected=!0},selectOnlyThisPage(){this.allSelected=!1},confirmBulkDelete(){h.confirm(this.$t("Are you sure you want to delete the selected patterns?"),this.$t("Delete Patterns"),{confirmButtonText:this.$t("Delete"),cancelButtonText:this.$t("Cancel"),type:"warning"}).then(()=>{this.doBulkDelete()}).catch(()=>{})},doBulkDelete(){const e={action_name:"delete_patterns"};this.allSelected?(e.select_all=!0,e.search=this.appliedSearch||""):e.pattern_ids=this.selectedPatterns.map(e=>e.id),this.deleting=!0,this.$post("email-patterns/do-bulk-action",e).then(e=>{this.$notify.success(e.message),this.fetch()}).catch(e=>{this.handleError(e)}).finally(()=>{this.deleting=!1})},deletePattern(e){this.$del(`email-patterns/${e.id}`).then(e=>{this.$notify.success(e.message),this.fetchPatterns()}).catch(e=>{this.handleError(e)})},onThemeChanged(e){var t;this.current_mode=(null==(t=e.detail)?void 0:t.effective)||("system"===B.getCurrentTheme()?B.getSystemTheme():B.getCurrentTheme())}},mounted(){window.addEventListener(E,this.onThemeChanged),this.fetchPatterns(),this.changeTitle(this.$t("Email Patterns"))},beforeUnmount(){window.removeEventListener(E,this.onThemeChanged)}},[["render",function(e,h,w,T,D,x){const A=p("TopNav"),E=p("Icons"),B=t,j=p("page-header"),F=c,N=a,ie=p("icons"),le=s,re=p("router-link"),oe=r,ce=p("confirm"),de=l,he=i,pe=n,me=p("pagination-bar"),ge=p("data-table"),_e=p("Close"),ue=d,fe=p("floating-bulk-action-shell"),ye=o;return m(),g("div",M,[_("div",H,[_("div",I,[u(A)])]),_("div",O,[u(j,null,f({title:y(()=>[k(v(e.$t("Email Patterns"))+" ",1),D.pagination.total?(m(),g("small",L,"("+v(e.formatMoney(D.pagination.total))+")",1)):b("",!0)]),_:2},[e.hasPermission("fcrm_manage_emails")?{name:"actions",fn:y(()=>[u(B,{type:"primary",loading:D.creating,disabled:D.creating,onClick:x.addPattern},{default:y(()=>[_("span",U,[u(E,{"icon-name":"plus"})]),k(" "+v(e.$t("Add Pattern")),1)],void 0,!0),_:1},8,["loading","disabled","onClick"])]),key:"0"}:void 0]),1024),u(ge,{"has-selection":!1},{"header-left":y(()=>[u(F,{clearable:"",modelValue:D.search,"onUpdate:modelValue":h[0]||(h[0]=e=>D.search=e),onClear:x.resetAndFetch,onKeyup:C(x.resetAndFetch,["enter"]),placeholder:e.$t("Type and Enter...")},{prefix:y(()=>[_("span",V,[u(E,{"icon-name":"search"})])]),_:1},8,["modelValue","onClear","onKeyup","placeholder"])]),table:y(()=>[D.loading&&!D.patterns.length?(m(),P(N,{key:0,style:{padding:"20px"},rows:5})):$((m(),P(pe,{key:1,ref:"patternTable",border:"",stripe:"",data:D.patterns,style:{width:"100%"},onSelectionChange:x.onSelection},{empty:y(()=>[_("div",Y,[u(ie,{"icon-name":"common-empty-state"}),_("div",K,[e.hasPermission("fcrm_manage_emails")?(m(),g("span",z,v(e.$t('No patterns yet. Click "Add Pattern" above to create your first reusable email section.')),1)):(m(),g("span",J,v(e.$t("No Data Found")),1))])])]),default:y(()=>[e.hasPermission("fcrm_manage_email_delete")?(m(),P(le,{key:0,type:"selection",width:45})):b("",!0),u(le,{prop:"title","min-width":"400",label:e.$t("Name")},{default:y(t=>[e.hasPermission("fcrm_manage_emails")?(m(),P(re,{key:0,class:"template-title",to:{name:"edit_pattern",params:{pattern_id:t.row.id}}},{default:y(()=>[k(v(t.row.title),1)],void 0,!0),_:2},1032,["to"])):(m(),g("span",Q,v(t.row.title),1)),"unsynced"!==t.row.sync_status?(m(),g("span",R,v(e.$t("Synced")),1)):b("",!0)]),_:1},8,["label"]),u(le,{width:"180",label:e.$t("Category")},{default:y(e=>[k(v(e.row.category||"—"),1)]),_:1},8,["label"]),u(le,{width:"190",label:e.$t("Last Modified")},{default:y(t=>[_("span",{title:t.row.updated_at},v(e.nsHumanDiffTime(t.row.updated_at)),9,W)]),_:1},8,["label"]),e.hasPermission("fcrm_manage_emails")||e.hasPermission("fcrm_manage_email_delete")?(m(),P(le,{key:1,fixed:"right",align:"center",width:"60","class-name":"fcrm_table_actions_cell"},{default:y(t=>[u(he,{trigger:"click",placement:"bottom-end"},{dropdown:y(()=>[u(de,null,{default:y(()=>[e.hasPermission("fcrm_manage_emails")?(m(),P(oe,{key:0,onClick:a=>e.$router.push({name:"edit_pattern",params:{pattern_id:t.row.id}})},{default:y(()=>[_("span",Z,[_("span",q,[u(E,{"icon-name":"EditPen"})]),k(" "+v(e.$t("Edit")),1)])],void 0,!0),_:1},8,["onClick"])):b("",!0),e.hasPermission("fcrm_manage_email_delete")?(m(),P(oe,{key:1,class:"fcrm_danger_action"},{default:y(()=>[u(ce,{placement:"top-start",message:e.$t("Are you sure you want to delete this pattern?"),onYes:e=>x.deletePattern(t.row)},{reference:y(()=>[_("span",G,[_("span",ee,[u(E,{"icon-name":"delete"})]),k(" "+v(e.$t("Delete")),1)])]),_:1},8,["message","onYes"])],void 0,!0),_:2},1024)):b("",!0)],void 0,!0),_:2},1024)]),default:y(()=>[_("span",{class:"el-dropdown-link",role:"button",tabindex:"0","aria-label":e.$t("More actions")},[u(E,{"icon-name":"more_actions"})],8,X)],void 0,!0),_:2},1024)]),_:1})):b("",!0)],void 0,!0),_:1},8,["data","onSelectionChange"])),[[ye,D.loading]])]),pagination:y(()=>[u(me,{pagination:D.pagination,onFetch:x.fetch},null,8,["pagination","onFetch"])]),_:1}),u(fe,{visible:D.selection&&!D.loading&&D.pagination.total,"theme-mode":D.current_mode,"custom-layout":!0},{default:y(()=>[_("div",te,[_("div",ae,[u(B,{link:"","aria-label":e.$t("Deselect"),onClick:x.clearSelection},{default:y(()=>[u(ue,null,{default:y(()=>[u(_e)],void 0,!0),_:1})],void 0,!0),_:1},8,["aria-label","onClick"]),_("span",ne,[D.allSelected?(m(),g(S,{key:0},[k(v(e.$t("All"))+" ",1),_("strong",null,v(D.pagination.total),1),k(" "+v(e.$t("selected")),1)],64)):(m(),g(S,{key:1},[_("strong",null,v(D.selectedPatterns.length),1),k(" "+v(e.$t("selected")),1)],64))]),x.allSelectedOnPage&&!D.allSelected?(m(),g(S,{key:0},[h[1]||(h[1]=_("span",{class:"fc_bulk_divider","aria-hidden":"true"},null,-1)),u(B,{link:"",onClick:x.selectAllPatterns},{default:y(()=>[k(v(e.$t("Select All"))+" "+v(D.pagination.total),1)],void 0,!0),_:1},8,["onClick"])],64)):b("",!0),D.allSelected?(m(),g(S,{key:1},[h[2]||(h[2]=_("span",{class:"fc_bulk_divider","aria-hidden":"true"},null,-1)),u(B,{link:"",onClick:x.selectOnlyThisPage},{default:y(()=>[k(v(e.$t("Select only this page")),1)],void 0,!0),_:1},8,["onClick"])],64)):b("",!0),h[3]||(h[3]=_("span",{class:"fc_bulk_divider","aria-hidden":"true"},null,-1)),e.hasPermission("fcrm_manage_email_delete")?$((m(),P(B,{key:2,disabled:D.deleting,type:"danger",size:"small",plain:"",onClick:x.confirmBulkDelete},{default:y(()=>[_("span",se,[u(E,{"icon-name":"delete"})]),k(" "+v(e.$t("Delete")),1)],void 0,!0),_:1},8,["disabled","onClick"])),[[ye,D.deleting]]):b("",!0)])])],void 0),_:1},8,["visible","theme-mode"])])])}]]);export{ie as default}; diff --git a/wp-content/plugins/fluent-crm/assets/admin/Modules/Email/Templates/Templates.js b/wp-content/plugins/fluent-crm/assets/admin/Modules/Email/Templates/Templates.js new file mode 100644 index 0000000..f5b4694 --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/admin/Modules/Email/Templates/Templates.js @@ -0,0 +1 @@ +import{c as e,n as t,E as a,k as l,ay as i,o as s,a6 as o,a2 as n,ac as r,j as c,h as p,i as m,aB as d,aH as h,aI as _,e as u,aR as g,aS as f,g as v}from"../../../../vendor-element-plus.js?ver=3.1.8";import{aQ as w,W as y,X as T,Z as b,ab as $,a5 as k,J as S,a9 as C,aa as D,a8 as P,Y as I,a6 as A,ac as B,ay as E,b2 as j}from"../../../../vendor.js?ver=3.1.8";import{C as x}from"../../../../Confirm.js?ver=3.1.8";import{P as V}from"../../../../PaginationBar.js?ver=3.1.8";import{_ as O,I as N,a as F,T as L}from"../../../../fc-bits-ui.js?ver=3.1.8";import{I as U}from"../../../../InlineDoc.js?ver=3.1.8";import{E as H}from"../../../../EmailPreview.js?ver=3.1.8";import{B as M}from"../../../../BuiltinTemplateDrawer.js?ver=3.1.8";import{F as q}from"../../../../FloatingBulkActionShell.js?ver=3.1.8";import{T as G}from"../../../../TopNav.js?ver=3.1.8";import{P as R}from"../../../../PageHeader.js?ver=3.1.8";import{D as J}from"../../../../DataTable.js?ver=3.1.8";import{G as Q}from"../../../../GenericPromo.js?ver=3.1.8";import"../../../../PreviewIframeBuilder.js?ver=3.1.8";import"../../../../TestEmail.js?ver=3.1.8";import"../../../../CampaignSubjectLines.js?ver=3.1.8";import"../../../../PromoCard.js?ver=3.1.8";const Y={name:"BulkTemplateAction",components:{Icons:N,Close:e},emits:["deselect-all","refetch","select-all","select-only-this-page"],props:{selectedTemplates:{type:Array,default:()=>[]},totalCount:{type:Number,default:0},currentPageCount:{type:Number,default:0},perPage:{type:Number,default:10},allSelected:{type:Boolean,default:!1},search:{type:String,default:""}},computed:{allSelectedOnPage(){const e=this.selectedTemplates.length,t=this.currentPageCount;return t>0&&e===t&&e===this.perPage}},data:()=>({doing_action:!1}),methods:{confirmAndDeleteTemplates(){t.confirm(this.$t("Are you sure to delete?")+" "+this.$t("delete_all_templates_notice"),this.$t("Delete Templates"),{confirmButtonText:this.$t("Delete"),cancelButtonText:this.$t("Cancel"),type:"warning"}).then(()=>{this.doBulkAction()}).catch(()=>{})},doBulkAction(){let e={action_name:"delete_templates"};this.allSelected?(e.select_all=!0,e.search=this.search||""):e.template_ids=this.selectedTemplates.map(e=>e.ID),this.doing_action=!0,this.$post("templates/do-bulk-action",e).then(e=>{this.$notify.success(e.message),this.$emit("refetch")}).catch(e=>{this.handleError(e)}).finally(()=>{this.doing_action=!1})}}},K={class:"fcrm_bulk_action_bar"},W={class:"fcrm_bulk_action_left"},z={class:"fc_bulk_selection_count"},X={key:0,class:"fc_bulk_divider","aria-hidden":"true"},Z={key:2,class:"fc_bulk_divider","aria-hidden":"true"},ee={class:"icon"};const te={class:"fcrm_email_templates_page"},ae={class:"fcrm_page_header_top_nav_wrapper"},le={class:"fcrm_page_header_top_nav"},ie={class:"fcrm-layout-width"},se={class:"icon"},oe={class:"el-popover__reference"},ne={class:"icon"},re={class:"icon"},ce={class:"icon"},pe={class:"fcrm_empty_state"},me={class:"fcrm_empty_state_text"},de={class:"fcrm_template_type_icon"},he=["title"],_e=["onClick"],ue={class:"fcrm_template_preview_pill_btn"},ge={class:"el-dropdown-link"},fe={class:"el-popover__reference"},ve={class:"icon"},we={class:"el-popover__reference"},ye={class:"icon"},Te={class:"el-popover__reference"},be={class:"icon"},$e={class:"el-popover__reference"},ke={class:"icon"},Se={key:2},Ce={key:0,class:"fcrm_import_content"},De={class:"upload-icon"},Pe={class:"el-upload__text"};const Ie=O({name:"Templates",components:{CopyDocument:r,GenericPromo:Q,Icons:N,PageHeader:R,FloatingBulkActionShell:q,DataTable:J,TopNav:G,EmailPreview:H,Confirm:x,PaginationBar:V,BulkTemplateAction:O(Y,[["render",function(e,t,s,o,n,r){const c=w("Close"),p=a,m=l,d=w("Icons"),h=i;return y(),T("div",K,[b("div",W,[$(m,{link:"","aria-label":e.$t("Deselect"),onClick:t[0]||(t[0]=t=>e.$emit("deselect-all"))},{default:k(()=>[$(p,null,{default:k(()=>[$(c)],void 0,!0),_:1})],void 0),_:1},8,["aria-label"]),b("span",z,[s.allSelected?(y(),T(S,{key:0},[C(D(e.$t("All"))+" ",1),b("strong",null,D(s.totalCount),1),C(" "+D(e.$t("selected")),1)],64)):(y(),T(S,{key:1},[b("strong",null,D(s.selectedTemplates.length),1),C(" "+D(e.$t("selected")),1)],64))]),r.allSelectedOnPage&&!s.allSelected?(y(),T("span",X)):P("",!0),r.allSelectedOnPage&&!s.allSelected?(y(),I(m,{key:1,link:"",onClick:t[1]||(t[1]=t=>e.$emit("select-all"))},{default:k(()=>[C(D(e.$t("Select All"))+" "+D(s.totalCount),1)],void 0),_:1})):P("",!0),s.allSelected?(y(),T("span",Z)):P("",!0),s.allSelected?(y(),I(m,{key:3,link:"",onClick:t[2]||(t[2]=t=>e.$emit("select-only-this-page"))},{default:k(()=>[C(D(e.$t("Select only this page")),1)],void 0),_:1})):P("",!0),t[3]||(t[3]=b("span",{class:"fc_bulk_divider","aria-hidden":"true"},null,-1)),e.hasPermission("fcrm_manage_email_delete")?A((y(),I(m,{key:4,disabled:n.doing_action,type:"danger",size:"small",plain:"",onClick:r.confirmAndDeleteTemplates},{default:k(()=>[b("span",ee,[$(d,{"icon-name":"delete"})]),C(" "+D(e.$t("Delete")),1)],void 0),_:1},8,["disabled","onClick"])),[[h,n.doing_action]]):P("",!0)])])}]]),InlineDoc:U,BuiltinTemplateDrawer:M,EditPen:n,Delete:o,View:s},data:()=>({loading:!1,templates:[],pagination:{current_page:1,per_page:20,total:0},url:"",title:"",dialogVisible:!1,order:"descending",orderBy:"ID",search:"",selection:!1,selectedTemplates:[],open_drawer:!1,show_drawer:!1,builtInTemplates:[],isLoadingTemplates:!1,importing:!1,oneTimeFetch:!0,showTemplatePreview:!1,previewTemplateId:"",email_template:{post_title:"",post_content:"",post_excerpt:"",email_subject:"",edit_type:"html",design_template:"simple",settings:{template_config:{}}},importDialogVisible:!1,inline_errors:null,allSelected:!1,current_mode:"system"===L.getCurrentTheme()?L.getSystemTheme():L.getCurrentTheme()}),computed:{importUrl(){let e=window.ajaxurl;return e+=(e.match(/\?/)?"&":"?")+jQuery.param({action:"fluentcrm_import_template"}),e},canSelectAll(){if(!this.pagination||!this.pagination.per_page||!this.pagination.total)return!1;const e=this.selectedTemplates.length===this.pagination.per_page,t=this.selectedTemplates.length({fontWeight:500,color:"publish"===e?"green":"gray"}),setup(){let e=this.$route.query;if(window.fcrm_template_sub_params&&(e=window.fcrm_template_sub_params),this.search=e.search||"",this.order="ascending"===e.order?"ASC":"DESC",this.orderBy=e.orderBy,e.page&&(this.pagination.current_page=parseInt(e.page)),e.per_page)this.pagination.per_page=parseInt(e.per_page);else{const e=parseInt(this.storage.get("template_perpage",20))||20;this.pagination.per_page=e}return!1},fetch(){this.loading=!0,this.selection=!1,this.storage.set("template_perpage",this.pagination.per_page);const e={order:"ascending"==this.order?"ASC":"DESC",orderBy:this.orderBy,per_page:this.pagination.per_page,page:this.pagination.current_page,search:this.search},t={};Object.keys(e).forEach(a=>{void 0!==e[a]&&(t[a]=e[a])}),window.fcrm_template_sub_params=t,t.t=Date.now(),this.$router.replace({name:"templates",query:t}),e.types=["publish","draft"],this.$get("templates",e).then(e=>{this.templates=e.templates.data,this.pagination.total=e.templates.total}).catch(e=>{this.handleError(e)}).finally(()=>{this.loading=!1})},onSelection(e){this.selection=!!e.length,this.selectedTemplates=e,e.length>0&&e.length!==this.pagination.per_page&&(this.allSelected=!1),e.length||(this.allSelected=!1)},clearTemplateSelection(){this.$refs.templateTable&&this.$refs.templateTable.clearSelection(),this.selectedTemplates=[],this.allSelected=!1,this.selection=!1},selectAllTemplates(){this.canSelectAll&&(this.$refs.templateTable&&this.templates&&this.templates.length>0&&this.templates.forEach(e=>{this.$refs.templateTable.toggleRowSelection(e,!0)}),this.allSelected=!0)},selectOnlyThisPage(){this.allSelected=!1;const e=this.templates.map(e=>e.ID);this.selectedTemplates=this.selectedTemplates.filter(t=>e.includes(t.ID))},duplicate(e){this.duplicating=!0,this.$post(`templates/duplicate/${e.ID}`).then(e=>{this.$notify.success(e.message),this.fetch(),this.$router.push({name:"edit_template",params:{template_id:e.template_id},query:{is_new:"yes"}})}).catch(e=>{this.handleError(e)}).finally(()=>{this.duplicating=!1})},edit(e){this.$router.push({name:"edit_template",params:{template_id:e.ID}})},remove(e){this.$del(`templates/${e.ID}`).then(e=>{this.fetch(),this.$notify.success({title:this.$t("Great!"),message:e.message,offset:19})})},syncVisibility(){this.dialogVisible=!1},onDialogClose(){this.fetch(),this.url=""},sortTemplates(e){this.orderBy=e.prop,this.order=e.order,this.fetch()},exportTemplate(e){this.has_campaign_pro?location.href=window.ajaxurl+"?"+jQuery.param({action:"fluentcrm_export_template",template_id:e.ID,_nonce:window.fcAdmin.nonce}):this.$notify.error(this.$t("Template_Export_Alert"))},showPreview(e){this.previewTemplateId=e.ID,this.fetchTemplate()},openBuiltinTemplateDrawer(){this.open_drawer=!0},fetchTemplate(){this.$get(`templates/${this.previewTemplateId}`).then(e=>{this.email_template=e.template,this.showTemplatePreview=!0}).catch(e=>{this.handleError(e)})},success(e){this.$notify.success(e.message),this.$router.push({name:"edit_template",params:{template_id:e.template_id}})},error(e){try{const t=JSON.parse(e.message);this.$notify.error(t.message),t.requires&&(this.inline_errors=t.requires)}catch(t){this.$notify.error(e.message||this.$t("An error occurred"))}},onThemeChanged(e){var t;this.current_mode=(null==(t=e.detail)?void 0:t.effective)||("system"===L.getCurrentTheme()?L.getSystemTheme():L.getCurrentTheme())}},mounted(){window.addEventListener(F,this.onThemeChanged),this.setup(),this.fetch(),this.changeTitle(this.$t("Email Templates"))},beforeUnmount(){window.removeEventListener(F,this.onThemeChanged)}},[["render",function(e,t,s,o,n,r){const S=w("TopNav"),x=w("Icons"),V=l,O=m,N=p,F=c,L=w("inline-doc"),U=w("page-header"),H=u,M=d,q=w("icons"),G=_,R=w("router-link"),J=w("View"),Q=a,Y=w("confirm"),K=h,W=w("email-preview"),z=w("pagination-bar"),X=w("data-table"),Z=w("bulk-template-action"),ee=w("floating-bulk-action-shell"),Ie=g,Ae=f,Be=w("generic-promo"),Ee=v,je=w("builtin-template-drawer"),xe=i;return y(),T("div",te,[b("div",ae,[b("div",le,[$(S)]),t[6]||(t[6]=b("div",{class:"fcrm_page_header_top_actions"},null,-1))]),b("div",ie,[$(U,null,{title:k(()=>[C(D(e.$t("Email Templates"))+" ",1),A(b("small",null,"("+D(e.formatMoney(n.pagination.total))+")",513),[[B,n.pagination.total]])]),actions:k(()=>[e.hasPermission("fcrm_manage_email_templates")?(y(),I(F,{key:0,trigger:"click"},{dropdown:k(()=>[$(N,null,{default:k(()=>[$(O,{class:"fc_dropdown_action",onClick:t[0]||(t[0]=e=>n.importDialogVisible=!0)},{default:k(()=>[b("span",oe,[b("span",ne,[$(x,{"icon-name":"import"})]),C(" "+D(e.$t("Import")),1)])],void 0,!0),_:1})],void 0,!0),_:1})]),default:k(()=>[$(V,{class:"el-dropdown-link"},{default:k(()=>[C(D(e.$t("More Actions"))+" ",1),b("span",se,[$(x,{"icon-name":"downIcon"})])],void 0,!0),_:1})],void 0,!0),_:1})):P("",!0),$(L,{doc_id:1729}),e.hasPermission("fcrm_manage_email_templates")?(y(),I(V,{key:1,type:"primary",onClick:t[1]||(t[1]=e=>r.openBuiltinTemplateDrawer())},{default:k(()=>[b("span",re,[$(x,{"icon-name":"plus"})]),C(" "+D(e.$t("Add Template")),1)],void 0,!0),_:1})):P("",!0)]),_:1}),$(X,{"has-selection":!1},E({"header-left":k(()=>[$(H,{clearable:"",modelValue:n.search,"onUpdate:modelValue":t[2]||(t[2]=e=>n.search=e),onClear:r.fetch,onKeyup:j(r.fetch,["enter"]),placeholder:e.$t("Type and Enter...")},{prefix:k(()=>[b("span",ce,[$(x,{"icon-name":"search"})])]),_:1},8,["modelValue","onClear","onKeyup","placeholder"])]),table:k(()=>[n.loading&&!n.templates.length?(y(),I(M,{key:0,style:{padding:"20px"},rows:7})):A((y(),I(K,{key:1,ref:"templateTable",border:"",data:n.templates,style:{width:"100%"},stripe:"","default-sort":{prop:n.orderBy,order:n.order},onSortChange:r.sortTemplates,onSelectionChange:r.onSelection},{empty:k(()=>[b("div",pe,[$(q,{"icon-name":"common-empty-state"}),b("div",me,[b("span",null,D(e.$t("Create your first email template to save time on future emails.")),1)])])]),default:k(()=>[$(G,{type:"selection",width:45}),$(G,{label:e.$t("ID"),width:"100",prop:"ID",sortable:"custom"},null,8,["label"]),$(G,{prop:"post_title","min-width":"450",label:e.$t("Title"),sortable:"custom"},{default:k(e=>[$(R,{class:"template-title",to:{name:"edit_template",params:{template_id:e.row.ID}}},{default:k(()=>[b("span",de,["visual_builder"==e.row.design_template?(y(),I(x,{key:0,"icon-name":"visualBuilder"})):"raw_classic"==e.row.design_template?(y(),I(x,{key:1,"icon-name":"classicEditor"})):"raw_html"==e.row.design_template?(y(),I(x,{key:2,"icon-name":"rawHTML"})):(y(),I(x,{key:3,"icon-name":"gutenberg"}))]),C(" "+D(e.row.post_title),1)],void 0,!0),_:2},1032,["to"])]),_:1},8,["label"]),$(G,{width:"190",label:e.$t("Last Modified"),prop:"post_modified",sortable:"custom"},{default:k(t=>[b("span",{title:t.row.post_modified},D(e.nsHumanDiffTime(t.row.post_modified)),9,he)]),_:1},8,["label"]),$(G,{width:"160",label:e.$t("Preview")},{default:k(t=>[b("div",{class:"fcrm_preview_text_with_icon_btn",onClick:e=>r.showPreview(t.row)},[b("span",ue,[$(Q,null,{default:k(()=>[$(J)],void 0,!0),_:1}),b("span",null,D(e.$t("Show Preview")),1)])],8,_e)]),_:1},8,["label"]),$(G,{fixed:"right",align:"center",width:"60","class-name":"fcrm_table_actions_cell"},{default:k(t=>[$(F,{trigger:"click",placement:"bottom-end"},{dropdown:k(()=>[$(N,null,{default:k(()=>[e.hasPermission("fcrm_manage_email_templates")?(y(),I(O,{key:0,onClick:e=>r.edit(t.row)},{default:k(()=>[b("span",fe,[b("span",ve,[$(x,{"icon-name":"EditPen"})]),C(" "+D(e.$t("Edit")),1)])],void 0,!0),_:1},8,["onClick"])):P("",!0),e.hasPermission("fcrm_manage_email_templates")?(y(),I(O,{key:1,onClick:e=>r.duplicate(t.row)},{default:k(()=>[b("span",we,[b("span",ye,[$(x,{"icon-name":"duplicate"})]),C(" "+D(e.$t("Duplicate")),1)])],void 0,!0),_:1},8,["onClick"])):P("",!0),e.hasPermission("fcrm_manage_email_templates")?(y(),I(O,{key:2,onClick:e=>r.exportTemplate(t.row)},{default:k(()=>[b("span",Te,[b("span",be,[$(x,{"icon-name":"export"})]),C(" "+D(e.$t("Export")),1)])],void 0,!0),_:1},8,["onClick"])):P("",!0),e.hasPermission("fcrm_manage_email_templates")?(y(),I(O,{key:3,class:"fcrm_danger_action"},{default:k(()=>[$(Y,{placement:"top-start",message:e.$t("Delete_Template_Alert"),onYes:e=>r.remove(t.row)},{reference:k(()=>[b("span",$e,[b("span",ke,[$(x,{"icon-name":"delete"})]),C(" "+D(e.$t("Delete")),1)])]),_:1},8,["message","onYes"])],void 0,!0),_:2},1024)):P("",!0)],void 0,!0),_:2},1024)]),default:k(()=>[b("span",ge,[$(x,{"icon-name":"more_actions"})])],void 0,!0),_:2},1024)]),_:1})],void 0,!0),_:1},8,["data","default-sort","onSortChange","onSelectionChange"])),[[xe,n.loading]]),n.showTemplatePreview?(y(),T("div",Se,[$(W,{onModalClosed:t[3]||(t[3]=()=>{n.showTemplatePreview=!1}),auto_load:!0,show_audit:!0,campaign:n.email_template},null,8,["campaign"])])):P("",!0)]),_:2},[n.loading?void 0:{name:"pagination",fn:k(()=>[$(z,{pagination:n.pagination,onFetch:r.fetch},null,8,["pagination","onFetch"])]),key:"0"}]),1024),$(ee,{visible:n.selection,"theme-mode":n.current_mode,"custom-layout":!0},{default:k(()=>[$(Z,{selectedTemplates:n.selectedTemplates,"total-count":n.pagination.total,"per-page":n.pagination.per_page,"current-page-count":n.templates.length,"all-selected":n.allSelected,search:n.search,onRefetch:r.fetch,onDeselectAll:r.clearTemplateSelection,onSelectAll:r.selectAllTemplates,onSelectOnlyThisPage:r.selectOnlyThisPage},null,8,["selectedTemplates","total-count","per-page","current-page-count","all-selected","search","onRefetch","onDeselectAll","onSelectAll","onSelectOnlyThisPage"])],void 0),_:1},8,["visible","theme-mode"])]),$(Ee,{title:e.$t("Import Template"),modelValue:n.importDialogVisible,"onUpdate:modelValue":t[4]||(t[4]=e=>n.importDialogVisible=e),"append-to-body":!0,"close-on-click-modal":!1,width:"640px","modal-class":"fcrm_import_dialog"},{default:k(()=>[e.has_campaign_pro?(y(),T("div",Ce,[b("h3",null,D(e.$t("Upload JSON File")),1),$(Ie,{drag:"",limit:1,action:r.importUrl,ref:"uploader",multiple:!1,"on-error":r.error,"on-success":r.success},{default:k(()=>[b("span",De,[$(x,{"icon-name":"upload"})]),b("div",Pe,D(e.$t("Choose a file or drag & drop it here.")),1),$(V,null,{default:k(()=>[C(D(e.$t("Browse File")),1)],void 0,!0),_:1})],void 0,!0),_:1},8,["action","on-error","on-success"]),$(Ae,{title:e.$t("Not_Import_Templates_Alert"),type:"info","show-icon":"",closable:!1},null,8,["title"]),n.inline_errors?(y(),I(Ae,{key:0,title:n.inline_errors,type:"error","show-icon":"",closable:!1},null,8,["title"])):P("",!0)])):(y(),I(Be,{key:1}))],void 0),_:1},8,["title","modelValue"]),$(je,{create_mode:!0,open_drawer:n.open_drawer,"onUpdate:open_drawer":t[5]||(t[5]=e=>n.open_drawer=e)},null,8,["open_drawer"])])}]]);export{Ie as default}; diff --git a/wp-content/plugins/fluent-crm/assets/admin/Modules/Forms/Forms.js b/wp-content/plugins/fluent-crm/assets/admin/Modules/Forms/Forms.js new file mode 100644 index 0000000..9c54c14 --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/admin/Modules/Forms/Forms.js @@ -0,0 +1 @@ +import{ay as e,E as t,aF as a,aE as i,aw as r,e as s,az as l,ax as o,k as n,aB as c,aH as d,aI as m,D as f,ar as _,aG as p,aO as h,j as u,h as g,i as v,aT as y}from"../../../vendor-element-plus.js?ver=3.1.8";import{W as w,X as b,Z as F,a8 as C,bB as $,aQ as k,a6 as V,ab as I,a5 as x,aa as T,a9 as E,J as S,az as A,Y as O,b2 as L,a0 as P,ay as j}from"../../../vendor.js?ver=3.1.8";import{O as D}from"../../../_OptionSelector.js?ver=3.1.8";import{_ as H,I as z}from"../../../fc-bits-ui.js?ver=3.1.8";import{I as B}from"../../../ItemCopier2.js?ver=3.1.8";import{C as U}from"../../../CustomIcon.js?ver=3.1.8";import{P as N}from"../../../PaginationBar.js?ver=3.1.8";import{I as M}from"../../../InlineDoc.js?ver=3.1.8";import{D as Q}from"../../../DataTable.js?ver=3.1.8";import{P as G}from"../../../PageHeader.js?ver=3.1.8";import K from"../../../v3app/src/Modules/Contacts/Filter/ActiveFiltersBar.js?ver=3.1.8";import"../../../v3app/src/Modules/Contacts/Filter/FilterPopover.js?ver=3.1.8";const Y={class:"fcrm_form_preview"},Z={class:"fcrm_form_preview_image"},R={class:"fcrm_form_preview_image_body"},W={key:0,class:"fcrm_form_preview_image_container"},J={key:1,class:"fcrm_form_preview_image_container"},X={key:2,class:"fcrm_form_preview_image_container"},q={class:"fcrm_form_preview_details"},ee={key:0,class:"fcrm_form_preview_title"},te={key:1,class:"fcrm_form_preview_title"},ae={key:2,class:"fcrm_form_preview_title"};const ie={class:"fc_create_form_wrapper fcrm_create_form_wrapper"},re={key:0,style:{padding:"20px"},class:"fc_created_form text-align-center"},se={class:"fcrm_success_icon"},le={class:"fcrm_item_copier_wrapper"},oe={class:"d-flex flex-wrap items-center justify-center"},ne=["href"],ce={class:"icon"},de={class:"ml-5"},me=["href"],fe={class:"icon"},_e={class:"ml-5"},pe=["href"],he={class:"icon"},ue={key:1,style:{height:"100%"}},ge={key:0,class:"fc_select_template_wrapper"},ve={class:"fc_config_template"},ye={class:"fc_drawer_footer_wrap fcrm_drawer_footer_wrap"},we={class:"fcrm_drawer_footer_wrap_title"},be={class:"fcrm_drawer_footer_wrap_actions"};const Fe={class:"fc_form_entry"},Ce=["innerHTML"],$e={key:2};const ke={class:"fc_form_entries"},Ve={key:0,class:"text-align-center"},Ie={key:1},xe={class:"icon"},Te={key:1,class:"text-align-center"},Ee={class:"fcrm_empty_state"},Se={class:"fcrm_empty_state_text"},Ae={key:2,class:"fcrm_forms_table"},Oe=["href"],Le=["title"];const Pe={class:"icon"},je={key:0,class:"fcrm_filter_menu_items_container"},De={class:"fcrm_filter_menu_text"},He={key:1,class:"fcrm_filter_empty_item"},ze={class:"fcrm_filter_empty_text"},Be={class:"fcrm_filter_menu_text"},Ue={key:3,class:"fcrm_filter_empty_item"},Ne={class:"fcrm_filter_empty_text"},Me={class:"fcrm_filter_category_header"},Qe={class:"fcrm_filter_category_title"},Ge={class:"fcrm_filter_search_item"},Ke={class:"fcrm_filter_options_container"},Ye={key:1,class:"fcrm_filter_no_results"},Ze={class:"fcrm_filter_no_results_text"};const Re={class:"fcrm_forms_page"},We={class:"fcrm-layout-width"},Je={class:"icon"},Xe={class:"icon"},qe={class:"fcrm_empty_state_text"},et={key:0,class:"fcrm_installation_loader",role:"status","aria-live":"polite"},tt={class:"fcrm_loader_text"},at={class:"icon"},it={class:"d-flex gap-4 flex-wrap"},rt={class:"icon"},st={key:1,class:"fcrm_form_info_empty"},lt=["title"],ot={class:"icon","aria-hidden":"true"},nt={class:"fcrm_empty_state"},ct={class:"fcrm_empty_state_text"};const dt=H({name:"FluentForms",components:{createForm:H({name:"CreateForm",emits:["close"],components:{OptionSelector:D,FormPreview:H({name:"FormPreview",props:{type:{type:String,default:"inline_subscribe"}},components:{},data:()=>({})},[["render",function(e,t,a,i,r,s){return w(),b("div",Y,[F("div",Z,[t[3]||(t[3]=F("div",{class:"fcrm_form_preview_image_header"},[F("div",{class:"fcrm_form_preview_action fcrm_bg_red"}),F("div",{class:"fcrm_form_preview_action fcrm_bg_yellow"}),F("div",{class:"fcrm_form_preview_action fcrm_bg_green"})],-1)),F("div",R,["inline_subscribe"==a.type?(w(),b("div",W,[...t[0]||(t[0]=[F("div",{class:"fcrm_form_preview_common_form fcrm_form_inline"},[F("div",{class:"fcrm_form_preview_input"}),F("div",{class:"fcrm_form_preview_button fcrm_bg_primary"})],-1)])])):C("",!0),"simple_optin"==a.type?(w(),b("div",J,[...t[1]||(t[1]=[F("div",{class:"fcrm_form_preview_common_form fcrm_form_simple"},[F("div",{class:"fcrm_form_preview_input"})],-1),F("div",{class:"fcrm_form_preview_button fcrm_bg_primary"},null,-1)])])):C("",!0),"with_name_subscribe"==a.type?(w(),b("div",X,[...t[2]||(t[2]=[$('
',3)])])):C("",!0)])]),F("div",q,["inline_subscribe"==a.type?(w(),b("div",ee,"Inline Opt-in Form")):C("",!0),"simple_optin"==a.type?(w(),b("div",te,"Simple Opt-in Form")):C("",!0),"with_name_subscribe"==a.type?(w(),b("div",ae,"Subscriber Form")):C("",!0),t[4]||(t[4]=F("div",{class:"fcrm_form_preview_description"},"Minimal design with centered alignment",-1))])])}]]),ItemCopier:B,CustomIcon:U},data:()=>({active_step:"template_selection",templates:[],fetching:!1,form:{template_id:"",title:"",selected_tags:[],selected_list:"",double_optin:!0},created_form:!1,creating:!1}),methods:{create(){if(!this.form.template_id||!this.form.title||!this.form.selected_list)return this.$notify.error(this.$t("_Cr_Please_fuatf"));this.creating=!0,this.$post("forms",this.form).then(e=>{this.$notify.success(e.message),this.created_form=e.created_form,this.$emit("created",e.created_form)}).catch(e=>{this.handleError(e)}).finally(()=>{this.creating=!1})},cancel(){this.$emit("close")},changeToStep(e){this.active_step=e},fetchFormTemplates(){this.fetching=!0,this.$get("forms/templates").then(e=>{this.templates=e.templates}).catch(e=>{this.handleError(e)}).finally(()=>{this.fetching=!1})}},mounted(){this.fetchFormTemplates()}},[["render",function(c,d,m,f,_,p){const h=k("Select"),u=t,g=k("item-copier"),v=k("CustomIcon"),y=k("form-preview"),$=a,L=i,P=s,j=r,D=k("option-selector"),H=l,z=o,B=n,U=e;return V((w(),b("div",ie,[_.created_form?(w(),b("div",re,[F("div",se,[I(u,null,{default:x(()=>[I(h)],void 0),_:1})]),F("h3",null,T(c.$t("_Cr_Your_fhbcs")),1),F("p",null,T(c.$t("CreateForm.desc")),1),F("code",le,[I(g,{text:_.created_form.shortcode},null,8,["text"])]),d[8]||(d[8]=F("hr",{style:{margin:"12px 0"}},null,-1)),F("ul",oe,[F("li",null,[F("a",{class:"el-button",target:"_blank",href:_.created_form.preview_url},[F("span",null,[F("span",ce,[I(v,{type:"preview_form"})]),E(" "+T(c.$t("Preview The Form")),1)])],8,ne)]),F("li",de,[F("a",{class:"el-button",target:"_blank",href:_.created_form.edit_url},[F("span",null,[F("span",fe,[I(v,{type:"edit_form"})]),E(" "+T(c.$t("Edit The Form")),1)])],8,me)]),F("li",_e,[F("a",{class:"el-button",target:"_blank",href:_.created_form.feed_url},[F("span",null,[F("span",he,[I(v,{type:"edit_connection_form"})]),E(" "+T(c.$t("Edit Connection")),1)])],8,pe)])])])):(w(),b("div",ue,["template_selection"==_.active_step?(w(),b("div",ge,[F("h3",null,T(c.$t("Select a template")),1),I(L,{onChange:d[0]||(d[0]=e=>p.changeToStep("config")),modelValue:_.form.template_id,"onUpdate:modelValue":d[1]||(d[1]=e=>_.form.template_id=e)},{default:x(()=>[(w(!0),b(S,null,A(_.templates,(e,t)=>(w(),O($,{key:t,value:e.id},{default:x(()=>[I(y,{type:e.id},null,8,["type"])],void 0,!0),_:2},1032,["value"]))),128))],void 0),_:1},8,["modelValue"])])):"config"==_.active_step?(w(),b(S,{key:1},[F("div",ve,[I(z,{data:_.form,"label-position":"top"},{default:x(()=>[I(j,{label:c.$t("Form Title")},{default:x(()=>[I(P,{type:"text",modelValue:_.form.title,"onUpdate:modelValue":d[2]||(d[2]=e=>_.form.title=e),placeholder:c.$t("Please Provide a Form Title")},null,8,["modelValue","placeholder"])],void 0,!0),_:1},8,["label"]),I(j,{label:c.$t("Add to List")},{default:x(()=>[I(D,{modelValue:_.form.selected_list,"onUpdate:modelValue":d[3]||(d[3]=e=>_.form.selected_list=e),field:{option_key:"lists",placeholder:c.$t("Select a List"),creatable:!0}},null,8,["modelValue","field"])],void 0,!0),_:1},8,["label"]),I(j,{label:c.$t("Add to Tags")},{default:x(()=>[I(D,{modelValue:_.form.selected_tags,"onUpdate:modelValue":d[4]||(d[4]=e=>_.form.selected_tags=e),field:{option_key:"tags",creatable:!0,placeholder:c.$t("Select Tags"),is_multiple:!0}},null,8,["modelValue","field"])],void 0,!0),_:1},8,["label"]),I(j,{label:c.$t("Double Opt-In")},{default:x(()=>[I(H,{modelValue:_.form.double_optin,"onUpdate:modelValue":d[5]||(d[5]=e=>_.form.double_optin=e)},{default:x(()=>[E(T(c.$t("_Cr_Enable_DOC")),1)],void 0,!0),_:1},8,["modelValue"])],void 0,!0),_:1},8,["label"])],void 0),_:1},8,["data"])]),F("div",ye,[F("div",we,[F("p",null,T(c.$t("_Cr_This_fwbciFFaycc")),1)]),F("div",be,[V((w(),O(B,{onClick:d[6]||(d[6]=e=>p.cancel())},{default:x(()=>[E(T(c.$t("Cancel")),1)],void 0),_:1})),[[U,_.creating]]),V((w(),O(B,{type:"primary",onClick:d[7]||(d[7]=e=>p.create())},{default:x(()=>[E(T(c.$t("Create Form")),1)],void 0),_:1})),[[U,_.creating]])])])],64)):C("",!0)]))])),[[U,_.fetching]])}]]),PageHeader:G,DataTable:Q,ActiveFiltersBar:K,PaginationBar:N,ItemCopier:B,InlineDoc:M,FormEntries:H({name:"FluentEntries",components:{DataTable:Q,PaginationBar:N,Icons:z,EntryDetails:H({name:"EntryDetails",props:["form_id","entry_id"],data:()=>({dataView:null,loading:!1}),methods:{fetchEntryDetails(){this.loading=!0,this.$get(`forms/${this.form_id}/entries/${this.entry_id}`).then(e=>{this.dataView=e.entry}).catch(e=>{this.handleError(e)}).finally(()=>{this.loading=!1})}},mounted(){this.fetchEntryDetails()}},[["render",function(e,t,a,i,r,s){const l=c;return w(),b("div",Fe,[r.loading?(w(),O(l,{key:0,animated:!0,rows:4})):r.dataView&&r.dataView.content_html?(w(),b("div",{key:1,innerHTML:e.$sanitize(r.dataView.content_html)},null,8,Ce)):(w(),b("div",$e,[F("p",null,T(e.$t("No details available for this entry.")),1)]))])}]])},props:{formId:{type:[Number,String],default:null}},data:()=>({entries:[],form:null,loading:!1,search:"",expandedRows:[],pagination:{current_page:1,page:1,per_page:10,total:0}}),watch:{formId:{immediate:!0,handler(e){e&&this.fetchEntries()}}},methods:{fetchEntries(){if(!this.formId)return;const e={per_page:this.pagination.per_page,page:this.pagination.page,search:this.search};this.loading=!0,this.$get(`forms/${this.formId}/entries`,e).then(e=>{this.entries=e.entries.data,this.pagination.total=e.entries.total,this.pagination.page=e.entries.page,this.form=e.form}).catch(e=>{this.handleError(e)}).finally(()=>{this.loading=!1})},handleGoToEntry(e){e&&window.open(e,"_blank")},findEmail(e){let t=e.response;for(let a in t)if("email"===a.toLowerCase()||a.toLowerCase().includes("email"))return t[a];for(let a in t){let e=t[a];if("string"==typeof e&&/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(e))return e}return"n/a"}}},[["render",function(t,a,i,r,l,o){const f=k("Icons"),_=s,p=c,h=k("entry-details"),u=m,g=n,v=d,y=k("pagination-bar"),$=k("data-table"),S=e;return w(),b("div",ke,[i.formId?(w(),b("div",Ie,[I($,{wrapper_border:!0,"has-selection":!1},{"header-left":x(()=>[I(_,{clearable:"",onKeypress:L(o.fetchEntries,["enter"]),size:"small",modelValue:l.search,"onUpdate:modelValue":a[0]||(a[0]=e=>l.search=e),onClear:a[1]||(a[1]=e=>{l.search="",o.fetchEntries()}),placeholder:t.$t("Search entries...")},{prefix:x(()=>[F("span",xe,[I(f,{"icon-name":"search"})])]),_:1},8,["onKeypress","modelValue","placeholder"])]),table:x(()=>[V((w(),b("div",null,[l.loading?(w(),O(p,{key:0,rows:8})):l.entries.length||l.loading?(w(),b("div",Ae,[I(v,{data:l.entries,width:"100%",border:"",stripe:""},{default:x(()=>[I(u,{type:"expand"},{default:x(e=>[e.expanded?(w(),O(h,{key:0,form_id:i.formId,entry_id:e.row.id},null,8,["form_id","entry_id"])):C("",!0)]),_:1}),I(u,{label:"#","min-width":"80"},{default:x(e=>[F("a",{target:"_blank",rel:"noopener",href:e.row.entry_url},T(e.row.serial_number),9,Oe)]),_:1}),I(u,{label:t.$t("Email"),"min-width":"150"},{default:x(e=>[F("span",null,T(o.findEmail(e.row)),1)]),_:1},8,["label"]),I(u,{prop:"status",label:t.$t("Status"),"min-width":"100"},{default:x(e=>[E(T(e.row.status.charAt(0).toUpperCase()+e.row.status.slice(1)),1)]),_:1},8,["label"]),I(u,{label:t.$t("Submitted At"),"min-width":"150"},{default:x(e=>[F("span",{class:"fcrm_secondary_text small",title:e.row.created_at},T(t.nsHumanDiffTime(e.row.created_at)),9,Le)]),_:1},8,["label"]),I(u,{fixed:"right",label:t.$t("Actions"),width:"80",align:"right"},{default:x(e=>[e.row.entry_url?(w(),O(g,{key:0,onClick:t=>o.handleGoToEntry(e.row.entry_url),text:""},{default:x(()=>[...a[2]||(a[2]=[F("svg",{xmlns:"http://www.w3.org/2000/svg",width:"20",height:"20",viewBox:"0 0 20 20",fill:"none"},[F("path",{d:"M8.5 5.5V7H4.75V15.25H13V11.5H14.5V16C14.5 16.1989 14.421 16.3897 14.2803 16.5303C14.1397 16.671 13.9489 16.75 13.75 16.75H4C3.80109 16.75 3.61032 16.671 3.46967 16.5303C3.32902 16.3897 3.25 16.1989 3.25 16V6.25C3.25 6.05109 3.32902 5.86032 3.46967 5.71967C3.61032 5.57902 3.80109 5.5 4 5.5H8.5ZM16.75 3.25V9.25H15.25V5.80975L9.40525 11.6553L8.34475 10.5948L14.1888 4.75H10.75V3.25H16.75Z",fill:"currentColor"})],-1)])],void 0,!0),_:1},8,["onClick"])):C("",!0)]),_:1},8,["label"])],void 0,!0),_:1},8,["data"])])):(w(),b("div",Te,[F("div",Ee,[I(f,{"icon-name":"common-empty-state"}),F("div",Se,T(t.$t("No form entries found. Entries will appear here once people submit this form.")),1)])]))])),[[S,l.loading]])]),pagination:x(()=>[!l.loading&&l.pagination.total?(w(),O(y,{key:0,pagination:l.pagination,onFetch:o.fetchEntries},null,8,["pagination","onFetch"])):C("",!0)]),_:1})])):(w(),b("div",Ve,[F("p",null,T(t.$t("Please select a form to view entries")),1)]))])}]]),FormFilterPopover:H({name:"FormFilterPopover",components:{ArrowLeft:_,Search:f,Icons:z},emits:["apply"],props:{options:{type:Object,default:()=>({lists:[],tags:[]})},selectedFilters:{type:Object,default:()=>({lists:[],tags:[]})}},data:()=>({activeCategory:null,searchQuery:"",localSelection:{lists:[],tags:[]}}),computed:{filteredOptions(){if(!this.activeCategory)return[];const e=this.options[this.activeCategory]||[];if(!this.searchQuery)return e;const t=this.searchQuery.toLowerCase();return e.filter(e=>this.getItemTitle(e).toLowerCase().includes(t))},categoryTitle(){return{lists:this.$t("Lists"),tags:this.$t("Tags")}[this.activeCategory]||""},searchPlaceholder(){return{lists:this.$t("Search lists..."),tags:this.$t("Search tags...")}[this.activeCategory]||this.$t("Search...")},hasAppliedFilters(){const e=this.selectedFilters||{};return Object.values(e).some(e=>Array.isArray(e)&&e.length>0)}},watch:{selectedFilters:{immediate:!0,deep:!0,handler(e){this.localSelection={lists:[...e.lists||[]],tags:[...e.tags||[]]}}},activeCategory(){this.searchQuery=""}},methods:{selectCategory(e){this.activeCategory=e},goBack(){this.activeCategory=null,this.searchQuery=""},getItemTitle:e=>e&&(e.title||e.name||e.label)||"",handleChange(){const e={lists:[...this.localSelection.lists||[]],tags:[...this.localSelection.tags||[]]};this.$emit("apply",e)}}},[["render",function(e,a,i,r,o,c){const d=k("Icons"),m=n,f=k("ArrowLeft"),_=t,u=k("Search"),g=s,v=l,y=p,C=h;return w(),O(C,{placement:"bottom-end",width:300,trigger:"click","hide-after":0,"popper-class":"fcrm_forms_filter_popover"},{reference:x(()=>[I(m,{size:"small",class:P(["small only-icon-btn fcrm_filter_toggle_btn",{"is-active":c.hasAppliedFilters}]),"aria-label":e.$t("Filters")},{default:x(()=>[F("span",Pe,[I(d,{"icon-name":"filter"})])],void 0,!0),_:1},8,["aria-label","class"])]),default:x(()=>[o.activeCategory?(w(),b(S,{key:1},[F("div",Me,[F("button",{class:"fcrm_filter_back_button",onClick:a[2]||(a[2]=(...e)=>c.goBack&&c.goBack(...e))},[I(_,null,{default:x(()=>[I(f)],void 0,!0),_:1})]),F("span",Qe,T(c.categoryTitle),1)]),F("div",Ge,[I(g,{modelValue:o.searchQuery,"onUpdate:modelValue":a[3]||(a[3]=e=>o.searchQuery=e),placeholder:c.searchPlaceholder,size:"small",clearable:"",class:"fcrm_filter_search_input"},{prefix:x(()=>[I(_,null,{default:x(()=>[I(u)],void 0,!0),_:1})]),_:1},8,["modelValue","placeholder"])]),F("div",Ke,[c.filteredOptions.length>0?(w(),O(y,{key:0,modelValue:o.localSelection[o.activeCategory],"onUpdate:modelValue":a[4]||(a[4]=e=>o.localSelection[o.activeCategory]=e),onChange:c.handleChange,class:"fcrm_filter_options_list"},{default:x(()=>[(w(!0),b(S,null,A(c.filteredOptions,e=>(w(),b("div",{key:e.id,class:"fcrm_filter_option_item"},[I(v,{value:e.id,class:"fcrm_filter_checkbox"},{default:x(()=>[E(T(c.getItemTitle(e)),1)],void 0,!0),_:2},1032,["value"])]))),128))],void 0,!0),_:1},8,["modelValue","onChange"])):(w(),b("div",Ye,[F("p",Ze,T(e.$t("No items found")),1)]))])],64)):(w(),b("div",je,[i.options.lists&&i.options.lists.length>0?(w(),b("div",{key:0,class:"fcrm_filter_menu_item",onClick:a[0]||(a[0]=e=>c.selectCategory("lists"))},[I(d,{"icon-name":"ListIcon"}),F("span",De,T(e.$t("List")),1)])):(w(),b("div",He,[I(d,{"icon-name":"ListIcon"}),F("span",ze,T(e.$t("No lists found")),1)])),i.options.tags&&i.options.tags.length>0?(w(),b("div",{key:2,class:"fcrm_filter_menu_item",onClick:a[1]||(a[1]=e=>c.selectCategory("tags"))},[I(d,{"icon-name":"TagIcon"}),F("span",Be,T(e.$t("Tag")),1)])):(w(),b("div",Ue,[I(d,{"icon-name":"TagIcon"}),F("span",Ne,T(e.$t("No tags found")),1)]))]))],void 0),_:1})}]]),Icons:z},data:()=>({direction:"rtl",forms:[],allForms:[],pagination:{page:1,per_page:10,total:0,current_page:1},loading:!1,installing_ff:!1,need_installation:!1,create_form_modal:!1,search:"",form_details_drawer:!1,selectedFormId:null,selectedForm:null,selectedFilters:{lists:[],tags:[]},filterOptions:{lists:[],tags:[]},useFrontendFiltering:!1,searchTimeout:null}),computed:{drawerTitle(){return this.selectedForm?this.selectedForm.title:""},filteredForms(){if(!this.useFrontendFiltering)return this.forms;let e=[...this.allForms];if(this.search){const t=this.search.toLowerCase();e=e.filter(e=>{var a,i;return(null==(a=e.title)?void 0:a.toLowerCase().includes(t))||(null==(i=e.id)?void 0:i.toString().includes(t))})}this.selectedFilters.lists.length>0&&(e=e.filter(e=>{if(!e.associate_lists)return!1;const t=e.associate_lists.split(",").map(e=>e.trim());return this.selectedFilters.lists.some(e=>{const a=this.filterOptions.lists.find(t=>t.id===e);return a&&t.includes(a.title)})})),this.selectedFilters.tags.length>0&&(e=e.filter(e=>{if(!e.associate_tags)return!1;const t=e.associate_tags.split(",").map(e=>e.trim());return this.selectedFilters.tags.some(e=>{const a=this.filterOptions.tags.find(t=>t.id===e);return a&&t.includes(a.title)})})),this.pagination.total=e.length;const t=(this.pagination.current_page-1)*this.pagination.per_page,a=t+this.pagination.per_page;return e.slice(t,a)}},watch:{search(){this.pagination.current_page=1,this.useFrontendFiltering||(clearTimeout(this.searchTimeout),this.searchTimeout=setTimeout(()=>{this.fetchForms()},500))}},methods:{fetchForms(){this.storage.set("forms_perpage",this.pagination.per_page);const e=this.useFrontendFiltering?{per_page:1e3,page:1}:{per_page:this.pagination.per_page,page:this.pagination.current_page,search:this.search||"",lists:this.selectedFilters.lists.join(","),tags:this.selectedFilters.tags.join(",")};this.loading=!0,this.$get("forms",e).then(e=>{e.installed?(e.forms.total<=1e3?(this.useFrontendFiltering=!0,this.allForms=e.forms.data,this.pagination.total=e.forms.total):(this.useFrontendFiltering=!1,this.forms=e.forms.data,this.pagination.total=e.forms.total,this.pagination.current_page=e.forms.current_page),this.filterOptions={lists:this.appVars.available_lists,tags:this.appVars.available_tags},this.need_installation=!1):this.need_installation=!0}).catch(e=>{this.handleError(e)}).finally(()=>{this.loading=!1})},handleFilterApply(e){this.selectedFilters.lists=e.lists||[],this.selectedFilters.tags=e.tags||[],this.pagination.current_page=1,this.useFrontendFiltering||this.fetchForms()},handleFilterBarChange(e){this.selectedFilters.lists=e.lists||[],this.selectedFilters.tags=e.tags||[],this.pagination.current_page=1,this.useFrontendFiltering||this.fetchForms()},installFF(){this.installing_ff=!0,this.$post("setting/install-fluentform").then(e=>{this.fetchForms(),this.$notify.success(e.message)}).catch(e=>{this.handleError(e)}).finally(()=>{this.installing_ff=!1})},handleActionCommand(e){const t=e.form[e.url],a=e.target;t&&("blank"===a?window.open(t,"_blank"):window.location.href=t)},openSubmissions(e){this.selectedFormId=e.id,this.selectedForm=e,this.form_details_drawer=!0},getFormInfoItems(e){const t=[];if(e.associate_lists){e.associate_lists.split(",").map(e=>e.trim()).filter(e=>e).forEach(e=>{t.push({type:"list",title:e})})}if(e.associate_tags){e.associate_tags.split(",").map(e=>e.trim()).filter(e=>e).forEach(e=>{t.push({type:"tag",title:e})})}return t}},mounted(){window.fcAdmin&&window.fcAdmin.is_rtl&&(this.direction="ltr"),this.pagination.per_page=parseInt(this.storage.get("forms_perpage",10),10)||10,this.fetchForms(),this.changeTitle(this.$t("Forms"))}},[["render",function(e,t,a,i,r,l){const o=k("inline-doc"),f=k("Icons"),_=n,p=k("page-header"),h=s,$=k("form-filter-popover"),V=k("active-filters-bar"),D=c,H=k("icons"),z=m,B=k("item-copier"),U=v,N=g,M=u,Q=d,G=k("pagination-bar"),K=k("data-table"),Y=k("create-form"),Z=y,R=k("form-entries");return w(),b("div",Re,[F("div",We,[I(p,null,{title:x(()=>[E(T(e.$t("Forms")),1)]),actions:x(()=>[I(o,{doc_id:267}),r.need_installation?C("",!0):(w(),O(_,{key:0,onClick:t[0]||(t[0]=e=>r.create_form_modal=!0),type:"primary","aria-label":e.$t("Create a New Form")},{default:x(()=>[F("span",Je,[I(f,{"icon-name":"plus"})]),E(" "+T(e.$t("Create a New Form")),1)],void 0,!0),_:1},8,["aria-label"]))]),_:1}),I(K,{"has-selection":!1},j({table:x(()=>[r.loading?(w(),O(D,{key:0,style:{padding:"20px"},rows:8})):r.need_installation?(w(),b("div",{key:1,class:P(["fcrm_body_boxed text-align-center fcrm_empty_state",{"is-installing":r.installing_ff}]),style:{"margin-top":"20px"}},[I(H,{"icon-name":"fluentforms"}),F("div",qe,[F("p",null,T(e.$t("Grow Your Audience by Opt-in Forms")),1),F("span",null,T(e.$t("Fluent Forms that are connected with your CRM")),1)]),r.installing_ff?(w(),b("div",et,[t[8]||(t[8]=F("div",{class:"fcrm_loader_spinner"},null,-1)),F("span",tt,T(e.$t("Installing...")),1)])):(w(),O(_,{key:1,type:"primary",onClick:t[4]||(t[4]=e=>l.installFF())},{default:x(()=>[F("span",at,[I(H,{"icon-name":"download"})]),E(" "+T(e.$t("Activate Fluent Forms")),1)],void 0,!0),_:1}))],2)):(w(),O(Q,{key:2,data:l.filteredForms,width:"100%"},{empty:x(()=>[F("div",nt,[I(H,{"icon-name":"common-empty-state"}),F("div",ct,[F("span",null,T(e.$t("No forms found. Create a form to start collecting data from your contacts.")),1)])])]),default:x(()=>[I(z,{"min-width":"80",label:e.$t("ID")},{default:x(e=>[E(" #"+T(e.row.id),1)]),_:1},8,["label"]),I(z,{"min-width":"250",label:e.$t("Title")},{default:x(e=>[E(T(e.row.title),1)]),_:1},8,["label"]),I(z,{"min-width":"280",label:e.$t("Info")},{default:x(e=>[F("div",it,[l.getFormInfoItems(e.row).length?(w(!0),b(S,{key:0},A(l.getFormInfoItems(e.row),(e,t)=>(w(),b("span",{key:t,class:"fcrm_badge"},[F("span",rt,[I(H,{iconName:"list"===e.type?"ListIcon":"TagIcon"},null,8,["iconName"])]),E(" "+T(e.title),1)]))),128)):(w(),b("span",st,"--"))])]),_:1},8,["label"]),I(z,{"min-width":"220",label:e.$t("Shortcode")},{default:x(e=>[I(B,{text:e.row.shortcode},null,8,["text"])]),_:1},8,["label"]),I(z,{"min-width":"140",label:e.$t("Created at")},{default:x(t=>[F("span",{class:"fcrm_form_date",title:t.row.created_at},T(e.nsHumanDiffTime(t.row.created_at)),9,lt)]),_:1},8,["label"]),I(z,{"min-width":"140",label:e.$t("Entries")},{default:x(t=>[I(_,{size:"small","aria-label":e.$t("View entries for %s",t.row.title),onClick:e=>l.openSubmissions(t.row)},{default:x(()=>[F("span",ot,[I(f,{"icon-name":"text"})]),E(" "+T(e.$t("View Entries")),1)],void 0,!0),_:1},8,["aria-label","onClick"])]),_:1},8,["label"]),I(z,{fixed:"right",label:e.$t("Actions"),"min-width":"80",align:"right"},{default:x(t=>[I(M,{onCommand:e=>l.handleActionCommand({...e,form:t.row}),trigger:"click",placement:"bottom-end"},{dropdown:x(()=>[I(N,null,{default:x(()=>[I(U,{command:{url:"preview_url",target:"blank"}},{default:x(()=>[E(T(e.$t("Preview Form")),1)],void 0,!0),_:1}),t.row.feed_url?(w(),O(U,{key:0,command:{url:"feed_url",target:"blank"}},{default:x(()=>[E(T(e.$t("Edit Integration Settings")),1)],void 0,!0),_:1})):C("",!0),t.row.funnel_url?(w(),O(U,{key:1,command:{url:"funnel_url",target:"same"}},{default:x(()=>[E(T(e.$t("Edit Connected Automation")),1)],void 0,!0),_:1})):C("",!0),I(U,{command:{url:"edit_url",target:"blank"}},{default:x(()=>[E(T(e.$t("Edit Form")),1)],void 0,!0),_:1})],void 0,!0),_:2},1024)]),default:x(()=>[I(_,{size:"small",text:"","aria-label":e.$t("Actions for")+" "+t.row.title},{default:x(()=>[I(f,{"icon-name":"more_actions"})],void 0,!0),_:1},8,["aria-label"])],void 0,!0),_:2},1032,["onCommand"])]),_:1},8,["label"])],void 0,!0),_:1},8,["data"]))]),_:2},[r.need_installation?void 0:{name:"header-left",fn:x(()=>[I(h,{clearable:"",size:"small",modelValue:r.search,"onUpdate:modelValue":t[1]||(t[1]=e=>r.search=e),placeholder:e.$t("Type to search..."),onKeyup:t[2]||(t[2]=L(e=>l.fetchForms(),["enter"])),onClear:t[3]||(t[3]=e=>l.fetchForms())},{prefix:x(()=>[F("span",Xe,[I(f,{"icon-name":"search"})])]),_:1},8,["modelValue","placeholder"])]),key:"0"},r.need_installation?void 0:{name:"header-actions",fn:x(()=>[I($,{options:r.filterOptions,"selected-filters":r.selectedFilters,onApply:l.handleFilterApply},null,8,["options","selected-filters","onApply"])]),key:"1"},r.need_installation?void 0:{name:"active-filters",fn:x(()=>[I(V,{"selected-filters":r.selectedFilters,options:r.filterOptions,onFilterChange:l.handleFilterBarChange,plus_filter_icon:!0},null,8,["selected-filters","options","onFilterChange"])]),key:"2"},r.need_installation?void 0:{name:"pagination",fn:x(()=>[I(G,{pagination:r.pagination,onFetch:l.fetchForms},null,8,["pagination","onFetch"])]),key:"3"}]),1024)]),I(Z,{direction:r.direction,class:"fcrm_form_info_drawer","with-header":!0,size:e.globalDrawerSize,title:e.$t("Create a Form"),"append-to-body":!0,"wrapper-closable":!1,modelValue:r.create_form_modal,"onUpdate:modelValue":t[6]||(t[6]=e=>r.create_form_modal=e)},{default:x(()=>[F("div",null,[r.create_form_modal?(w(),O(Y,{key:0,onClose:t[5]||(t[5]=e=>r.create_form_modal=!1),onCreated:l.fetchForms},null,8,["onCreated"])):C("",!0)])],void 0),_:1},8,["direction","size","title","modelValue"]),I(Z,{direction:r.direction,class:"fcrm_form_info_drawer","with-header":!0,size:e.drawerWidth,title:l.drawerTitle,"append-to-body":!0,"wrapper-closable":!1,modelValue:r.form_details_drawer,"onUpdate:modelValue":t[7]||(t[7]=e=>r.form_details_drawer=e)},{default:x(()=>[F("div",null,[I(R,{"form-id":r.selectedFormId},null,8,["form-id"])])],void 0),_:1},8,["direction","size","title","modelValue"])])}]]);export{dt as default}; diff --git a/wp-content/plugins/fluent-crm/assets/admin/Modules/Funnels/FunnelActivities.js b/wp-content/plugins/fluent-crm/assets/admin/Modules/Funnels/FunnelActivities.js new file mode 100644 index 0000000..d517f02 --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/admin/Modules/Funnels/FunnelActivities.js @@ -0,0 +1 @@ +import{c as e,P as t,W as s,a6 as a,y as l,aK as i,aL as n,aB as r,aI as o,aJ as c,E as d,j as u,h as _,i as f,k as m,ay as h,aH as p}from"../../../vendor-element-plus.js?ver=3.1.8";import{aQ as g,W as v,X as b,Z as w,ab as y,a5 as $,a9 as k,aa as C,J as S,az as F,Y as A,a8 as x,a6 as I}from"../../../vendor.js?ver=3.1.8";import{P as T}from"../../../PaginationBar.js?ver=3.1.8";import{I as P,C as j}from"../../../_IndividualProgress.js?ver=3.1.8";import{C as q}from"../../../Confirm.js?ver=3.1.8";import{_ as D,I as E,a as B,T as H}from"../../../fc-bits-ui.js?ver=3.1.8";import{P as z}from"../../../PageHeader.js?ver=3.1.8";import{F as L}from"../../../FloatingBulkActionShell.js?ver=3.1.8";import"../../../Badge.js?ver=3.1.8";const R={class:"fluentcrm_settings_wrapper"},Y={class:"fcrm_funnels_activities_page"},U={class:"fcrm_page_header_top_nav_wrapper"},V={class:"fcrm_page_header_top_nav"},W={class:"fcrm_page_header_top_nav_links"},J={class:"fcrm-layout-width"},M={class:"fcrm_table_wrapper"},K={class:"fcrm_table_body fcrm_pt_24"},N={key:1},Q={key:0},X=["title"],Z={key:1,class:"fcrm_badge fcrm_badge_plain"},G=["title"],O=["title"],ee={style:{display:"flex","align-items":"center",gap:"4px"}},te={class:"el-dropdown-link"},se={class:"el-popover__reference"},ae={class:"el-popover__reference"},le={class:"fcrm_empty_state"},ie={class:"fcrm_empty_state_text"},ne={class:"fcrm_bulk_action_bar"},re={class:"fcrm_bulk_action_left"},oe={class:"fc_bulk_selection_count"};const ce=D({name:"FunnelSubscribers",props:[],components:{PaginationBar:T,PageHeader:z,ContactCard:j,IndividualProgress:P,Confirm:q,SuccessFilled:l,Delete:a,InfoFilled:s,MoreFilled:t,Close:e,Icons:E,FloatingBulkActionShell:L},data(){return{funnel:{},subscribers:[],loading:!1,pagination:{total:0,per_page:10,current_page:1},sequences:[],stats:{metrics:[],total_revenue:0,revenue_currency:"USD"},visualization_type:"chart",search:"",deleting:!1,updating:!1,selectedIds:[],selected_status:"",selected_sequence:"",funnel_statuses:{active:this.$t("Active"),completed:this.$t("Completed"),cancelled:this.$t("Cancelled"),pending:this.$t("Pending")},loading_first:!0,syncSteps:!1,current_mode:"system"===H.getCurrentTheme()?H.getSystemTheme():H.getCurrentTheme()}},methods:{fetchSubscribers(){this.loading=!0;let e=["funnel","sequences"];this.loading_first||(e=[]),this.$get("funnels/all-activities",{per_page:this.pagination.per_page,page:this.pagination.current_page,with:e,search:this.search,status:this.selected_status,sequence_id:this.selected_sequence}).then(e=>{this.subscribers=e.activities.data,this.pagination.total=e.activities.total}).catch(e=>{this.handleError(e)}).finally(()=>{this.loading=!1,this.loading_first=!1})},rowStatusClass:({row:e})=>"fc_table_row_"+e.status,removeFromFunnel(e){this.deleting=!0,this.$del(`funnels/${e.funnel_id}/subscribers`,{subscriber_ids:[e.subscriber_id]}).then(e=>{this.$notify.success(e.message),this.fetchSubscribers()}).catch(e=>{this.handleError(e)}).finally(()=>{this.deleting=!1})},bulkRemove(){if(!this.selectedIds.length)return this.$notify.error(this.$t("Please select subscribers first")),!1;this.deleting=!0,this.$post("funnels/remove-bulk-subscribers",{funnel_subscriber_ids:this.selectedIds}).then(e=>{this.$notify.success(e.message),this.selectedIds=[],this.fetchSubscribers()}).catch(e=>{this.handleError(e)}).finally(()=>{this.deleting=!1})},changeFunnelSubscriptionStatus(e,t){this.updating=!0,this.$put(`funnels/${e.funnel_id}/subscribers/${e.subscriber_id}/status`,{status:t}).then(e=>{this.$notify.success(e.message),this.fetchSubscribers()}).catch(e=>{this.handleError(e)}).finally(()=>{this.updating=!1})},onSelection(e){const t=[];this.each(e,e=>{t.push(e.id)}),this.selectedIds=t},clearSelection(){this.$refs.activitiesTable&&this.$refs.activitiesTable.clearSelection(),this.selectedIds=[]},onThemeChanged(e){var t;this.current_mode=(null==(t=e.detail)?void 0:t.effective)||("system"===H.getCurrentTheme()?H.getSystemTheme():H.getCurrentTheme())}},mounted(){window.addEventListener(B,this.onThemeChanged),this.fetchSubscribers(),this.changeTitle(this.$t("All Automation Activities"))},beforeUnmount(){window.removeEventListener(B,this.onThemeChanged)}},[["render",function(e,t,s,a,l,T){const P=g("router-link"),j=n,q=i,D=g("page-header"),E=r,B=o,H=g("individual-progress"),z=g("InfoFilled"),L=d,ce=c,de=g("contact-card"),ue=g("SuccessFilled"),_e=g("MoreFilled"),fe=f,me=g("Delete"),he=m,pe=g("confirm"),ge=_,ve=u,be=g("Icons"),we=p,ye=g("pagination-bar"),$e=g("Close"),ke=g("floating-bulk-action-shell"),Ce=h;return v(),b("div",R,[w("div",Y,[w("div",U,[w("div",V,[w("ul",W,[w("li",null,[y(P,{to:"/funnels",class:"fcrm_top_nav_link"},{default:$(()=>[k(C(e.$t("Automation Funnels")),1)],void 0),_:1})]),w("li",null,[y(P,{to:"/funnels/funnel/all-activities",class:"fcrm_top_nav_link"},{default:$(()=>[k(C(e.$t("All Activities")),1)],void 0),_:1})])])]),t[3]||(t[3]=w("div",{class:"fcrm_page_header_top_actions"},null,-1))]),w("div",J,[y(D,null,{title:$(()=>[k(C(e.$t("All Activities")),1)]),actions:$(()=>[y(q,{onChange:t[0]||(t[0]=e=>T.fetchSubscribers()),title:e.$t("Status"),placeholder:e.$t("Status"),style:{width:"120px"},size:"small",modelValue:l.selected_status,"onUpdate:modelValue":t[1]||(t[1]=e=>l.selected_status=e)},{default:$(()=>[y(j,{value:"",label:e.$t("All")},null,8,["label"]),(v(!0),b(S,null,F(l.funnel_statuses,(e,t)=>(v(),A(j,{key:t,value:t,label:e},null,8,["value","label"]))),128))],void 0,!0),_:1},8,["title","placeholder","modelValue"])]),_:1}),w("div",M,[w("div",K,[l.loading?(v(),A(E,{key:0,style:{padding:"20px"},rows:8})):x("",!0),l.loading?x("",!0):(v(),A(we,{key:1,ref:"activitiesTable",border:"",stripe:"",onSelectionChange:T.onSelection,data:l.subscribers,"row-class-name":T.rowStatusClass},{empty:$(()=>[w("div",le,[y(be,{"icon-name":"common-empty-state"}),w("div",ie,[w("span",null,C(e.$t("Waiting for contacts from your automations.")),1)])])]),default:$(()=>[y(B,{type:"selection"}),y(B,{type:"expand"},{default:$(e=>[y(H,{funnel:e.row.funnel,funnel_subscriber:e.row,sequences:e.row.funnel?e.row.funnel.actions:[]},null,8,["funnel","funnel_subscriber","sequences"])]),_:1}),y(B,{label:e.$t("Contact"),width:"260"},{default:$(t=>[y(de,{trigger_type:"click",display_key:"full",subscriber:t.row.subscriber},{after_name:$(()=>["fcrm_manual_attach"==t.row.source_trigger_name?(v(),A(ce,{key:0,class:"item",effect:"dark",content:e.$t("ProfileAutomations.Contact_Added_manually_to_Automation"),placement:"top-start"},{default:$(()=>[y(L,null,{default:$(()=>[y(z)],void 0,!0),_:1})],void 0,!0),_:1},8,["content"])):x("",!0)]),_:2},1032,["subscriber"])]),_:1},8,["label"]),y(B,{label:e.$t("Automation")},{default:$(t=>[t.row.funnel?(v(),A(P,{key:0,to:{name:"edit_funnel",params:{funnel_id:t.row.funnel.id}}},{default:$(()=>[k(C(t.row.funnel.title),1)],void 0,!0),_:2},1032,["to"])):(v(),b("span",N,C(e.$t("Automation Removed")),1)),w("span",null,"("+C(t.row.status)+")",1)]),_:1},8,["label"]),y(B,{label:e.$t("Next Step")},{default:$(t=>["completed"!=t.row.status?(v(),b(S,{key:0},[t.row.next_sequence_item?(v(),b("span",Q,C(t.row.next_sequence_item.title),1)):x("",!0),"active"==t.row.status?(v(),b("span",{key:1,title:t.row.next_execution_time}," - ("+C(e.nsHumanDiffTime(t.row.next_execution_time))+") ",9,X)):x("",!0)],64)):(v(),b("span",Z,[y(L,{title:e.$t("Completed")},{default:$(()=>[y(ue)],void 0,!0),_:1},8,["title"]),k(" "+C(e.$t("Complete")),1)]))]),_:1},8,["label"]),y(B,{width:"150",label:e.$t("Last Executed At")},{default:$(t=>[w("span",{title:t.row.last_executed_time},C(e.nsHumanDiffTime(t.row.last_executed_time)),9,G)]),_:1},8,["label"]),y(B,{width:"150",label:e.$t("Created At")},{default:$(t=>[w("span",{title:t.row.created_at},C(e.nsHumanDiffTime(t.row.created_at)),9,O)]),_:1},8,["label"]),y(B,{width:"70",align:"right"},{default:$(t=>[w("div",ee,[y(ve,{trigger:"click"},{dropdown:$(()=>[y(ge,null,{default:$(()=>["cancelled"==t.row.status?(v(),A(fe,{key:0,class:"fc_dropdown_action",onClick:e=>T.changeFunnelSubscriptionStatus(t.row,"active")},{default:$(()=>[w("span",se,[y(L,null,{default:$(()=>[y(z)],void 0,!0),_:1}),k(" "+C(e.$t("Resume")),1)])],void 0,!0),_:1},8,["onClick"])):x("",!0),"active"==t.row.status?(v(),A(fe,{key:1,class:"fc_dropdown_action",onClick:e=>T.changeFunnelSubscriptionStatus(t.row,"cancelled")},{default:$(()=>[w("span",ae,[y(L,null,{default:$(()=>[y(z)],void 0,!0),_:1}),k(" "+C(e.$t("Cancel")),1)])],void 0,!0),_:1},8,["onClick"])):x("",!0),y(fe,{class:"fc_dropdown_action"},{default:$(()=>[I((v(),A(pe,{onYes:e=>T.removeFromFunnel(t.row)},{reference:$(()=>[y(he,{size:"small",type:"danger"},{default:$(()=>[y(L,null,{default:$(()=>[y(me)],void 0,!0),_:1}),k(" "+C(e.$t("Delete")),1)],void 0,!0),_:1})]),_:1},8,["onYes"])),[[Ce,l.deleting]])],void 0,!0),_:2},1024)],void 0,!0),_:2},1024)]),default:$(()=>[w("span",te,[y(L,{style:{"font-weight":"bold",cursor:"pointer",transform:"rotate(90deg)"}},{default:$(()=>[y(_e)],void 0,!0),_:1})])],void 0,!0),_:2},1024)])]),_:1})],void 0),_:1},8,["onSelectionChange","data","row-class-name"])),y(ye,{pagination:l.pagination,onFetch:T.fetchSubscribers},null,8,["pagination","onFetch"])])]),y(ke,{visible:!!l.selectedIds.length,"theme-mode":l.current_mode,"custom-layout":!0},{default:$(()=>[w("div",ne,[w("div",re,[y(he,{link:"","aria-label":e.$t("Deselect"),onClick:T.clearSelection},{default:$(()=>[y(L,null,{default:$(()=>[y($e)],void 0,!0),_:1})],void 0,!0),_:1},8,["aria-label","onClick"]),w("span",oe,[w("strong",null,C(l.selectedIds.length),1),k(" "+C(e.$t("selected")),1)]),t[4]||(t[4]=w("div",{class:"fcrm_bulk_divider"},null,-1)),I((v(),A(pe,{onYes:t[2]||(t[2]=e=>T.bulkRemove())},{reference:$(()=>[I((v(),A(he,{type:"danger",size:"small",plain:""},{default:$(()=>[y(L,null,{default:$(()=>[y(me)],void 0,!0),_:1}),k(" "+C(e.$t("Delete")),1)],void 0,!0),_:1})),[[Ce,l.deleting]])]),_:1})),[[Ce,l.deleting]])])])],void 0),_:1},8,["visible","theme-mode"])])])])}]]);export{ce as default}; diff --git a/wp-content/plugins/fluent-crm/assets/admin/Modules/Funnels/FunnelEditor/Edit.js b/wp-content/plugins/fluent-crm/assets/admin/Modules/Funnels/FunnelEditor/Edit.js new file mode 100644 index 0000000..192c61a --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/admin/Modules/Funnels/FunnelEditor/Edit.js @@ -0,0 +1 @@ +import{D as e,c as t,S as i,ak as n,aU as s,$ as o,aW as l,aV as c,E as a,e as r,at as _,ao as d,P as u,n as h,h as p,i as f,j as m,k as g,aN as k,aT as b,aw as v,ay as y,aK as w,aL as C,ax as $,ae as S,aZ as x,aO as T,a_ as B,az as V,aP as q,aJ as A,av as F,aB as j,aD as I,aA as E,g as L,ap as P}from"../../../../vendor-element-plus.js?ver=3.1.8";import{aQ as D,W as M,X as O,Z as R,Y as U,a5 as N,ab as H,a9 as J,aa as z,J as K,a8 as W,az as Z,a0 as G,a7 as Q,_ as Y,$ as X,ax as ee,a6 as te,b2 as ie,av as ne}from"../../../../vendor.js?ver=3.1.8";import{F as se,a as oe}from"../../../../FieldEditor.js?ver=3.1.8";import{A as le}from"../../../../Animation.js?ver=3.1.8";import{P as ce}from"../../../../ProBadge.js?ver=3.1.8";import{_ as ae,I as re}from"../../../../fc-bits-ui.js?ver=3.1.8";import{R as _e}from"../../../../_report_widget.js?ver=3.1.8";import{I as de}from"../../../../InlineDoc.js?ver=3.1.8";import{P as ue}from"../../../../PromoCard.js?ver=3.1.8";import"../../../../fc-bits.js?ver=3.1.8";import"../../../../_FormBuilder2.js?ver=3.1.8";import"../../../../PhotoWidget.js?ver=3.1.8";import"../../../../input-popover-dropdown.js?ver=3.1.8";import"../../../../data_config.js?ver=3.1.8";import"../../../../_OptionSelector.js?ver=3.1.8";import"../../../../_AjaxSelector.js?ver=3.1.8";import"../../../../_VerifiedEmailInput.js?ver=3.1.8";import"../../../../EmailComposer.js?ver=3.1.8";import"../../../../BlockComposer.js?ver=3.1.8";import"../../../../EmailPreview.js?ver=3.1.8";import"../../../../PreviewIframeBuilder.js?ver=3.1.8";import"../../../../TestEmail.js?ver=3.1.8";import"../../../../CampaignSubjectLines.js?ver=3.1.8";import"../../../../PaginationBar.js?ver=3.1.8";import"../../../../_MergeCodes.js?ver=3.1.8";import"../../../../BuiltinTemplateDrawer.js?ver=3.1.8";import"../../../../_TaxonomyTermsSelector.js?ver=3.1.8";import"../../../../_MailerConfig.js?ver=3.1.8";import"../../../../ItemCopier.js?ver=3.1.8";const he={class:"fcrm_funnel_blocks_panel"},pe={class:"fcrm_funnel_blocks_panel_header"},fe={class:"fcrm_funnel_blocks_panel_header_bottom"},me={key:0,class:"fcrm_funnel_blocks_panel_header_bottom_content"},ge={key:1,class:"fcrm_funnel_blocks_panel_header_bottom_content"},ke={key:2,class:"fcrm_funnel_blocks_panel_header_bottom_content"},be={class:"fcrm_funnel_blocks_wrapper_list"},ve={key:0,class:"fcrm_funnel_blocks_wrapper_item_title"},ye=["onClick"],we=["onClick"],Ce={class:"fcrm_trigger_selection_item_icon"},$e=["innerHTML"],Se={key:1},xe={class:"fcrm_trigger_selection_item_content"},Te={class:"fcrm_trigger_selection_item_title"},Be=["innerHTML"],Ve={key:0,class:"fcrm_empty_state"},qe={class:"fcrm_empty_state_text"};const Ae=ae({name:"blockChoice",components:{Icons:re,ProBadge:ce,Animation:le,CaretBottom:n,Menu:i,Close:t,Search:e},props:["blocks","condition_type","choice_context","show_close"],emits:["close","insert"],data:()=>({selectType:"action",searchBlock:"",closedCategories:[]}),mounted(){setTimeout(()=>{this.$refs.actions_search_input&&this.$refs.actions_search_input.focus()},300)},watch:{condition_type(){this.selectType="action"},searchBlock(e){e&&(this.selectType="all")}},computed:{current_items(){if("all"==this.selectType){const e={action:{title:this.$t("Actions"),categories:{}},benchmark:{title:this.$t("Goals"),categories:{}},conditional:{title:this.$t("Conditionals"),categories:{}}};if(this.each(this.blocks,(t,i)=>{if(!this.shouldIncludeBlock(t,i))return;const n=this.resolveBlockType(t,i);if(e[n]){const s=t.category||"Other";e[n].categories[s]||(e[n].categories[s]={title:s,items:{}}),t.action_name||(t.action_name=i),e[n].categories[s].items[i]=t}}),this.searchBlock){const t=this.searchBlock.toLowerCase();Object.keys(e).forEach(i=>{e[i].categories=Object.entries(e[i].categories).reduce((e,[i,n])=>{const s=Object.entries(n.items).reduce((e,[i,n])=>(n.title.toLowerCase().includes(t)&&(e[i]=n),e),{});return Object.keys(s).length>0&&(e[i]={title:n.title,items:s}),e},{})})}return e}const e={title:"",categories:{}};return this.each(this.blocks,(t,i)=>{if(!this.shouldIncludeBlock(t,i))return;if(this.resolveBlockType(t,i)===this.selectType){const n=t.category||"Other";e.categories[n]||(e.categories[n]={title:n,items:{}}),t.action_name||(t.action_name=i),e.categories[n].items[i]=t}}),{items:e}}},methods:{shouldIncludeBlock(e,t){return"child"!==this.choice_context||"conditional"!==e.type},resolveBlockType(e,t){let i=e.type;return"conditional"==i&&"funnel_ab_testing"==t&&"child"!=this.choice_context&&(i="action"),i},getCategoryKey:(e,t)=>`${e}::${t}`,toggleCategory(e){const t=this.closedCategories.indexOf(e);t>-1?this.closedCategories.splice(t,1):this.closedCategories.push(e)},isCategoryOpen(e){return!this.closedCategories.includes(e)},insert(e,t){if(t&&t.is_pro)return this.$alert('

This block require pro version of FluentCRM

Please download and install FluentCRM Pro to activate this block

Get FluentCRM Pro

',"Require FluentCRM Pro",{dangerouslyUseHTMLString:!0,showConfirmButton:!1}),!1;this.$emit("insert",e,t)},getElIcon:e=>s[e]||o}},[["render",function(e,t,i,n,s,o){const _=l,d=c,u=D("Close"),h=a,p=D("Search"),f=r,m=D("CaretBottom"),g=D("ProBadge"),k=D("Animation"),b=D("icons");return M(),O("div",null,[R("div",he,[R("div",pe,["child"!=i.choice_context?(M(),U(d,{key:0,onSelect:t[0]||(t[0]=e=>{s.selectType=e}),"default-active":s.selectType},{default:N(()=>[H(_,{index:"action"},{default:N(()=>[t[3]||(t[3]=R("span",{class:"icon"},[R("svg",{width:"16",height:"16",viewBox:"0 0 16 16",fill:"none",xmlns:"http://www.w3.org/2000/svg"},[R("path",{d:"M8.60019 6.1998H13.4002L7.4002 15.1998V9.79981H3.2002L8.60019 0.799805V6.1998ZM7.4002 7.3998V5.1318L5.3194 8.5998H8.60019V11.2362L11.158 7.3998H7.4002Z",fill:"currentColor"})])],-1)),J(" "+z(e.$t("Actions")),1)],void 0,!0),_:1}),i.condition_type?W("",!0):(M(),O(K,{key:0},[H(_,{index:"benchmark"},{default:N(()=>[t[4]||(t[4]=R("span",{class:"icon"},[R("svg",{width:"16",height:"16",viewBox:"0 0 16 16",fill:"none",xmlns:"http://www.w3.org/2000/svg"},[R("path",{d:"M3.94954 3.0441L8.90506 7.99961L8.0001 8.90457L5.79466 6.69913C5.51135 7.17948 5.39265 7.73919 5.45659 8.29318C5.52053 8.84717 5.76361 9.36512 6.14889 9.7683C6.53417 10.1715 7.04056 10.4378 7.59107 10.5268C8.14159 10.6159 8.70611 10.5227 9.19882 10.2615C9.69152 10.0002 10.0855 9.58529 10.3207 9.07969C10.556 8.57408 10.6197 8.00548 10.5022 7.46033C10.3848 6.91518 10.0925 6.4233 9.66983 6.05949C9.24718 5.69567 8.7173 5.47981 8.16074 5.44473L7.00554 4.28954C7.87865 4.0554 8.80638 4.13679 9.62539 4.51938C10.4444 4.90197 11.1022 5.56124 11.4829 6.38111C11.8637 7.20097 11.943 8.12888 11.7069 9.00147C11.4707 9.87405 10.9344 10.6354 10.1922 11.1514C9.44994 11.6674 8.54948 11.905 7.64929 11.8224C6.74911 11.7398 5.90692 11.3423 5.271 10.6998C4.63508 10.0574 4.2462 9.21117 4.17283 8.31019C4.09945 7.40921 4.34628 6.51123 4.86986 5.77433L3.9553 4.85977C3.19209 5.84368 2.8136 7.072 2.89068 8.31482C2.96776 9.55765 3.49512 10.7298 4.37406 11.6118C5.25299 12.4939 6.42325 13.0254 7.6658 13.1069C8.90834 13.1884 10.138 12.8143 11.1246 12.0546C12.1112 11.2949 12.7871 10.2016 13.0259 8.97952C13.2646 7.75741 13.0498 6.49018 12.4216 5.41502C11.7935 4.33987 10.795 3.53049 9.61313 3.13837C8.43126 2.74626 7.14701 2.79829 6.00074 3.28474L5.03946 2.3241C5.95321 1.84667 6.96914 1.59806 8.0001 1.59962C11.5348 1.59962 14.4001 4.4649 14.4001 7.99961C14.4001 11.5343 11.5348 14.3996 8.0001 14.3996C4.46538 14.3996 1.6001 11.5343 1.6001 7.99961C1.59914 7.05098 1.80949 6.11404 2.21588 5.25686C2.62228 4.39968 3.2145 3.64379 3.94954 3.0441V3.0441Z",fill:"currentColor"})])],-1)),J(" "+z(e.$t("Goals")),1)],void 0,!0),_:1}),H(_,{index:"conditional"},{default:N(()=>[t[5]||(t[5]=R("span",{class:"icon"},[R("svg",{width:"16",height:"16",viewBox:"0 0 16 16",fill:"none",xmlns:"http://www.w3.org/2000/svg"},[R("path",{d:"M5.06277 6.07409C5.16685 6.45499 5.39325 6.79112 5.70712 7.03072C6.02098 7.27032 6.40491 7.40011 6.79977 7.40009H9.19977C9.90659 7.40014 10.5907 7.64971 11.1315 8.1048C11.6723 8.55989 12.0351 9.19128 12.156 9.88769C12.5632 10.0205 12.9097 10.2942 13.1333 10.6596C13.3569 11.0249 13.4429 11.458 13.3759 11.8811C13.3089 12.3042 13.0933 12.6895 12.7678 12.9679C12.4422 13.2463 12.0281 13.3995 11.5998 13.4001C11.1807 13.4004 10.7746 13.2545 10.4516 12.9875C10.1286 12.7205 9.90885 12.3492 9.83029 11.9375C9.75173 11.5259 9.81926 11.0997 10.0213 10.7325C10.2232 10.3653 10.547 10.0801 10.9368 9.92609C10.8327 9.54519 10.6063 9.20906 10.2924 8.96946C9.97856 8.72986 9.59464 8.60008 9.19977 8.60009H6.79977C6.15051 8.60105 5.51861 8.39042 4.99977 8.00009V9.90209C5.40027 10.0436 5.73783 10.3222 5.95279 10.6886C6.16775 11.0549 6.24626 11.4855 6.17446 11.9042C6.10265 12.3228 5.88515 12.7026 5.5604 12.9764C5.23564 13.2502 4.82454 13.4004 4.39977 13.4004C3.975 13.4004 3.5639 13.2502 3.23915 12.9764C2.91439 12.7026 2.69689 12.3228 2.62509 11.9042C2.55328 11.4855 2.6318 11.0549 2.84676 10.6886C3.06172 10.3222 3.39928 10.0436 3.79977 9.90209V6.09809C3.40196 5.95764 3.06609 5.68195 2.85078 5.31915C2.63548 4.95635 2.5544 4.52946 2.62169 4.11298C2.68899 3.6965 2.90039 3.31688 3.21901 3.04035C3.53762 2.76382 3.94322 2.60795 4.36503 2.59993C4.78683 2.59192 5.19806 2.73227 5.52695 2.99649C5.85584 3.26072 6.08151 3.63204 6.16458 4.04566C6.24766 4.45928 6.18285 4.88894 5.98148 5.25966C5.78011 5.63038 5.45496 5.91862 5.06277 6.07409ZM4.39977 5.00009C4.5589 5.00009 4.71151 4.93688 4.82404 4.82436C4.93656 4.71183 4.99977 4.55922 4.99977 4.40009C4.99977 4.24096 4.93656 4.08835 4.82404 3.97583C4.71151 3.86331 4.5589 3.80009 4.39977 3.80009C4.24064 3.80009 4.08803 3.86331 3.97551 3.97583C3.86299 4.08835 3.79977 4.24096 3.79977 4.40009C3.79977 4.55922 3.86299 4.71183 3.97551 4.82436C4.08803 4.93688 4.24064 5.00009 4.39977 5.00009ZM4.39977 12.2001C4.5589 12.2001 4.71151 12.1369 4.82404 12.0244C4.93656 11.9118 4.99977 11.7592 4.99977 11.6001C4.99977 11.441 4.93656 11.2883 4.82404 11.1758C4.71151 11.0633 4.5589 11.0001 4.39977 11.0001C4.24064 11.0001 4.08803 11.0633 3.97551 11.1758C3.86299 11.2883 3.79977 11.441 3.79977 11.6001C3.79977 11.7592 3.86299 11.9118 3.97551 12.0244C4.08803 12.1369 4.24064 12.2001 4.39977 12.2001ZM11.5998 12.2001C11.7589 12.2001 11.9115 12.1369 12.024 12.0244C12.1366 11.9118 12.1998 11.7592 12.1998 11.6001C12.1998 11.441 12.1366 11.2883 12.024 11.1758C11.9115 11.0633 11.7589 11.0001 11.5998 11.0001C11.4406 11.0001 11.288 11.0633 11.1755 11.1758C11.063 11.2883 10.9998 11.441 10.9998 11.6001C10.9998 11.7592 11.063 11.9118 11.1755 12.0244C11.288 12.1369 11.4406 12.2001 11.5998 12.2001Z",fill:"currentColor"})])],-1)),J(" "+z(e.$t("Conditionals")),1)],void 0,!0),_:1}),H(_,{index:"all"},{default:N(()=>[J(z(e.$t("View All")),1)],void 0,!0),_:1})],64))],void 0),_:1},8,["default-active"])):W("",!0),i.show_close?(M(),U(h,{key:1,onClick:t[1]||(t[1]=()=>{e.$emit("close")}),class:"el-dialog__close"},{default:N(()=>[H(u)],void 0),_:1})):W("",!0)]),R("div",fe,[H(f,{ref:"actions_search_input",type:"text",modelValue:s.searchBlock,"onUpdate:modelValue":t[2]||(t[2]=e=>s.searchBlock=e),placeholder:e.$t("Search blocks, e.g., email, apply tags, etc."),clearable:"",autofocus:""},{prefix:N(()=>[H(h,null,{default:N(()=>[H(p)],void 0,!0),_:1})]),_:1},8,["modelValue","placeholder"]),"action"!=s.selectType||s.searchBlock?"benchmark"!=s.selectType||s.searchBlock?"conditional"!=s.selectType||s.searchBlock?W("",!0):(M(),O("div",ke,[R("h4",null,z(e.$t("Condition Blocks")),1),R("p",null,z(e.$t("_Bl_Use_tbtcspfysc")),1)])):(M(),O("div",ge,[R("h4",null,z(e.$t("Goals")),1),R("p",null,z(e.$t("_Bl_These_aygityuwda")),1)])):(M(),O("div",me,[R("h4",null,z(e.$t("Action Blocks")),1),R("p",null,z(e.$t("_Bl_Actions_battywtf")),1)]))]),R("div",be,[(M(!0),O(K,null,Z(o.current_items,(t,i)=>(M(),O("div",{class:"fcrm_funnel_blocks_wrapper_item",key:i},[t&&t.categories&&Object.keys(t.categories).length?(M(),O("div",{key:0,class:G(["fcrm_funnel_blocks_wrapper_item_inner fcrm_funnel_blocks_wrapper_item_inner_"+i,"fcrm_funnel_blocks_wrapper_item_inner_selected_"+s.selectType])},[t.title?(M(),O("h2",ve,z(t.title),1)):W("",!0),(M(!0),O(K,null,Z(t.categories,(t,n)=>(M(),O("div",{class:"fcrm_funnel_blocks_item_category",key:`${i}::${n}`},[!t.title||"action"!=s.selectType&&"all"!=s.selectType?W("",!0):(M(),O("h3",{key:0,onClick:e=>o.toggleCategory(o.getCategoryKey(i,n)),class:G(["fcrm_funnel_blocks_item_category_title",{is_collapsed:!o.isCategoryOpen(o.getCategoryKey(i,n))}])},[H(h,null,{default:N(()=>[H(m)],void 0),_:1}),J(" "+z(t.title),1)],10,ye)),H(k,{visible:o.isCategoryOpen(o.getCategoryKey(i,n)),accordion:""},{default:N(()=>[R("div",{class:G(["fcrm_funnel_blocks_item_category_list fcrm_trigger_selection_list","fcrm_funnel_blocks_item_category_list_"+s.selectType])},[(M(!0),O(K,null,Z(t.items,(t,i)=>(M(),O("div",{class:"fcrm_trigger_selection_item",key:i,onClick:e=>o.insert(i,t)},[t.is_pro?(M(),U(g,{key:0,text:e.$t("Pro")},null,8,["text"])):W("",!0),R("div",Ce,[t.svg?(M(),O("span",{key:0,class:"icon",innerHTML:t.svg},null,8,$e)):t["element-icon"]?(M(),O("span",Se,[H(h,null,{default:N(()=>[(M(),U(Q(o.getElIcon(t["element-icon"]))))],void 0,!0),_:2},1024)])):(M(),O("i",{key:2,class:G(t.icon?t.icon:"fc-icon-trigger")},null,2))]),R("div",xe,[R("h3",Te,z(t.title),1),R("p",{class:"fcrm_trigger_selection_item_description",innerHTML:t.description},null,8,Be)])],8,we))),128))],2)],void 0),_:2},1032,["visible"])]))),128))],2)):W("",!0)]))),128)),"all"!=s.selectType||Object.keys(o.current_items.action.categories).length||Object.keys(o.current_items.benchmark.categories).length||Object.keys(o.current_items.conditional.categories).length?W("",!0):(M(),O("div",Ve,[H(b,{"icon-name":"common-empty-state"}),R("div",qe,[R("span",null,z(e.$t("No Blocks Found")),1)])]))])])])}],["__scopeId","data-v-90db346c"]]);const Fe={class:"fc_child_blocks"},je=["onClick"],Ie={class:"fc_action_abs_right"},Ee=["onClick"],Le={class:"fluentcrm_block_title"},Pe=["innerHTML"],De={class:"icon"},Me={class:"fluentcrm_block_editor_body fcrm_funnel_editor_drawer_body"};const Oe={class:"fcrm_funnel_changer"},Re=["innerHTML"];const Ue={class:"fcrm_funnel_edit_wrapper"},Ne={class:"fcrm_funnel_top_nav_wrapper"},He={key:0,class:"fcrm_inline_editable_input"},Je={class:"fcrm_funnel_breadcrumb_title"},ze=["title"],Ke={class:"fcrm_funnel_breadcrumb_title_text"},We={key:0,class:"fcrm_funnel_top_nav_actions"},Ze={class:"icon"},Ge={class:"icon"},Qe={key:0,class:"fcrm_funnel_edit_body fluentcrm_tile_bg"},Ye={class:"fluentcrm_blocks_container"},Xe={class:"fluentcrm_blocks_wrapper"},et={class:"fluentcrm_blocks"},tt={class:"block_item_holder"},it={class:"fluentcrm_block_title"},nt=["innerHTML"],st=["innerHTML"],ot={class:"block_item_add"},lt={class:"fcrm_action_selector"},ct={class:"icon"},at={class:"content"},rt={class:"icon"},_t={class:"content"},dt=["onClick"],ut={class:"fc_action_abs_right"},ht=["onClick"],pt={class:"fluentcrm_block_title"},ft=["innerHTML"],mt={class:"block_conditional_wrapper"},gt={class:"block_cond_holder block_cond_no"},kt={class:"block_cond_inner"},bt={class:"block_cond_holder block_cond_yes"},vt={class:"block_cond_inner"},yt={class:"block_item_add"},wt={class:"fcrm_action_selector"},Ct=["onClick"],$t={class:"icon"},St={class:"content"},xt=["onClick"],Tt={class:"icon"},Bt={class:"content"},Vt={class:"fc_show_plus"},qt={key:1,class:"fluentcrm_body fluentcrm_tile_bg",style:{position:"relative"}},At={class:"fc_loading_bar"},Ft={class:"fluentcrm_block_editor_body"},jt={key:0,class:"fcrm_funnel_editor_drawer_body"},It={class:"fcrm_funnel_editor_drawer_body"},Et={class:"el-dialog__footer"},Lt={class:"dialog-footer fcrm_pt_12 fcrm_pb_12 fcrm_pr_20 fcrm_pl_20"};const Pt=ae({name:"FunnelEditor",props:["funnel_id","options"],components:{PromoCard:ue,Icons:re,FieldEditor:se,FormField:oe,BlockChoice:Ae,ReportWidget:_e,DomPath:ae({name:"DomPath",props:["from","to","label","side"],data:()=>({css:{}}),methods:{generateCss(){const e=jQuery(this.$el).closest(".block_item_holder_conditional"),t=e.offset(),i=e.find("."+this.from).offset(),n=e.find("."+this.to).offset().top-i.top;let s=0;s="left"==this.side?i.left-t.left-72:i.left-t.left;const o={position:"absolute",border:"2px solid #2f2925",width:"81px",left:s+"px",top:"50px",height:n+"px",borderBottom:"0",padding:"8px 20px 0 0",fontWeight:"600",marginLeft:"0"};"left"==this.side?(o.borderRight="0",o.borderTopLeftRadius="20px"):(o.borderLeft="0",o.borderTopRightRadius="20px",o.padding="8px 0 0 20px",o.marginLeft="-2px"),this.css=o}},mounted(){this.generateCss(),jQuery(window).on("resize",()=>{this.generateCss()})}},[["render",function(e,t,i,n,s,o){return M(),O("div",{style:X(s.css),class:G(["fc_dom_path_"+i.side,"fc_dom_path"])},[Y(e.$slots,"default")],6)}]]),ChildBlocks:ae({name:"ChildBlocks",components:{Icons:re,ReportWidget:_e,BlockChoice:Ae,FieldEditor:se,MoreFilled:u,ArrowUp:d,ArrowDown:_},props:["blocks","block_fields","all_blocks","show_inline_report","getBlockDescription","getBlockIcon","stats","options"],emits:["save"],data:()=>({direction:"rtl",show_choice_modal:!1,editing_block:!1,editing_modal:!1,editing_index:0}),computed:{current_block_fields(){if(!this.editing_block)return{};const e=this.editing_block.action_name;return this.block_fields[e]}},mounted(){window.fcAdmin&&window.fcAdmin.is_rtl&&(this.direction="ltr")},methods:{addBlock(e){const t=JSON.parse(JSON.stringify(this.all_blocks[e]));if("conditional"===t.type)return void this.$notify.error(this.$t("Conditional blocks are not allowed inside conditional child branches"));t.action_name=e,this.blocks.push(t),this.show_choice_modal=!1;const i=this.blocks.length-1;this.editing_index=i,this.editing_block=JSON.parse(JSON.stringify(this.blocks[i])),setTimeout(()=>{this.editing_modal=!0},300)},save(){this.blocks[this.editing_index]=this.editing_block,this.editing_modal=!1,this.editing_block=!1,this.$emit("save")},deleteBlock(){this.blocks.splice(this.editing_index,1),this.editing_modal=!1,this.editing_block=!1,this.$emit("save")},confirmDeleteChild(e,t){h.confirm(this.$t("Delete_Block_Alert"),"",{confirmButtonText:this.$t("Yes"),cancelButtonText:this.$t("No"),type:"warning"}).then(()=>{this.deleteChild(e,t)}).catch(()=>{})},deleteChild(e,t){this.blocks.splice(t,1),this.$emit("save")},cloneBlock(e,t){delete(e=JSON.parse(JSON.stringify(e))).id,this.removeBlockCampaign(e),this.blocks.splice(t,0,e),this.$emit("save")},removeBlockCampaign(e){e.settings&&e.settings.campaign&&delete e.settings.campaign.id},setCurrentBlock(e,t){this.editing_block=JSON.parse(JSON.stringify(e)),this.editing_index=t,this.$nextTick(()=>{this.editing_modal=!0})},moveToPosition(e,t){let i=t-1;"down"===e&&(i=t+1);const n=this.blocks,s=n[t];n.splice(t,1),n.splice(i,0,s),this.blocks=n,this.$emit("save")}}},[["render",function(e,t,i,n,s,o){const l=D("MoreFilled"),c=a,_=f,d=p,u=m,h=D("report-widget"),y=D("ArrowUp"),w=g,C=D("ArrowDown"),$=k,S=D("Icons"),x=D("block-choice"),T=b,B=r,V=v,q=D("field-editor");return M(),O("div",Fe,[(M(!0),O(K,null,Z(i.blocks,(n,s)=>(M(),O("div",{class:G(["fluentcrm_block","fluentcrm_block_"+n.action_name]),key:s},[R("div",{style:X({backgroundImage:i.getBlockIcon(n)}),onClick:e=>o.setCurrentBlock(n,s),class:"fluentcrm_blockin"},[R("div",Ie,[H(u,{trigger:"click"},{dropdown:N(()=>[H(d,{class:"fc_clickable_pop"},{default:N(()=>[H(_,{onClick:ee(e=>o.confirmDeleteChild(n,s),["stop"])},{default:N(()=>[R("span",null,z(e.$t("Delete")),1)],void 0,!0),_:1},8,["onClick"]),H(_,null,{default:N(()=>[R("span",{onClick:e=>o.cloneBlock(n,s),class:"el-tooltip__trigger"},z(e.$t("Clone")),9,Ee)],void 0,!0),_:2},1024)],void 0,!0),_:2},1024)]),default:N(()=>[R("span",{onClick:t[0]||(t[0]=ee(()=>{},["stop"])),class:"el-dropdown-link"},[H(c,{style:{"font-weight":"bold",cursor:"pointer"},class:"icon-90degree"},{default:N(()=>[H(l)],void 0,!0),_:1})])],void 0),_:2},1024)]),R("div",Le,[R("i",{class:G(i.getBlockIcon(n))},null,2),J(" "+z(n.title),1)]),R("div",{class:"fluentcrm_block_desc",innerHTML:i.getBlockDescription(n)},null,8,Pe),i.show_inline_report?(M(),U(h,{key:0,stat:i.stats[n.id]},null,8,["stat"])):W("",!0)],12,je),H($,{class:"fc_block_controls"},{default:N(()=>[H(w,{disabled:0==s,onClick:e=>o.moveToPosition("up",s),size:"small"},{default:N(()=>[H(c,null,{default:N(()=>[H(y)],void 0,!0),_:1})],void 0,!0),_:1},8,["disabled","onClick"]),H(w,{disabled:s+1==i.blocks.length,onClick:e=>o.moveToPosition("down",s),size:"small"},{default:N(()=>[H(c,null,{default:N(()=>[H(C)],void 0,!0),_:1})],void 0,!0),_:1},8,["disabled","onClick"])],void 0),_:2},1024)],2))),128)),H(w,{onClick:t[1]||(t[1]=e=>s.show_choice_modal=!0),style:{width:"100%"},type:"default"},{default:N(()=>[R("span",De,[H(S,{"icon-name":"plus"})]),J(" "+z(e.$t("Add Action")),1)],void 0),_:1}),H(T,{direction:s.direction,class:"fc_company_info_drawer fcrm_choice_action_add_drawer fcrm_drawer","close-on-click-modal":!0,title:e.$t("Add Action"),modelValue:s.show_choice_modal,"onUpdate:modelValue":t[2]||(t[2]=e=>s.show_choice_modal=e),"append-to-body":!0,"destroy-on-close":!0,"modal-class":"fcrm_funnel_blocks_model",size:e.globalDrawerSize},{default:N(()=>[H(x,{choice_context:"child",condition_type:"action",onInsert:o.addBlock,blocks:i.all_blocks},null,8,["onInsert","blocks"])],void 0),_:1},8,["direction","title","modelValue","size"]),H(T,{direction:s.direction,class:G(["fc_company_info_drawer fcrm_choice_action_drawer fcrm_drawer",s.editing_block?"fc_drawer_for_"+s.editing_block.action_name+(s.editing_block.id?" fc_blocked_has_id":" fc_blocked_no_id"):""]),"close-on-click-modal":!1,title:"Edit "+(s.editing_block?s.editing_block.title:""),modelValue:s.editing_modal,"onUpdate:modelValue":t[8]||(t[8]=e=>s.editing_modal=e),"append-to-body":!0,"destroy-on-close":!0,size:e.globalDrawerSize,"with-header":!1,onClose:t[9]||(t[9]=()=>{s.editing_modal=!1}),"modal-class":"fcrm_funnel_blocks_model"},{default:N(()=>[R("div",Me,[s.editing_block&&s.editing_modal?(M(),U(q,{title_badge:s.editing_block.type,onSave:t[4]||(t[4]=e=>o.save()),onSave_reload:t[5]||(t[5]=e=>o.save()),onDeleteSequence:t[6]||(t[6]=e=>o.deleteBlock()),show_controls:!0,data:s.editing_block.settings,options:i.options,action_name:s.editing_block.action_name,key:s.editing_index+"_"+s.editing_block.action_name,is_first:0===s.editing_index,is_last:s.editing_index===i.blocks.length-1,onCloseDrawer:t[7]||(t[7]=e=>s.editing_modal=!1),settings:o.current_block_fields},{after_header:N(()=>[H(V,{label:e.$t("Internal Label")},{default:N(()=>[H(B,{placeholder:e.$t("Internal Label"),modelValue:s.editing_block.title,"onUpdate:modelValue":t[3]||(t[3]=e=>s.editing_block.title=e)},null,8,["placeholder","modelValue"])],void 0,!0),_:1},8,["label"])]),_:1},8,["title_badge","data","options","action_name","is_first","is_last","settings"])):W("",!0)])],void 0),_:1},8,["direction","title","modelValue","class","size"])])}]]),TriggerChanger:ae({name:"TriggerChanger",props:["funnel"],emits:["refreshTrigger"],data(){return{funnel_data:JSON.parse(JSON.stringify(this.funnel)),triggers:[],fetching:!0,saving:!1}},methods:{getTriggers(){this.fetching=!0,this.$get("funnels/triggers").then(e=>{this.triggers=e.triggers}).catch(e=>{this.handleError(e)}).finally(()=>{this.fetching=!1})},changeTrigger(){this.saving=!0,this.$put(`funnels/${this.funnel.id}/change-trigger`,{title:this.funnel_data.title,trigger_name:this.funnel_data.trigger_name}).then(e=>{this.$notify.success(e.message),this.$emit("refreshTrigger",e.funnel)}).catch(e=>{this.handleError(e)}).finally(()=>{this.saving=!1})}},mounted(){this.getTriggers()}},[["render",function(e,t,i,n,s,o){const l=C,c=w,a=v,_=r,d=g,u=$,h=y;return M(),O("div",Oe,[te((M(),U(u,{data:s.funnel_data,"label-position":"top"},{default:N(()=>[H(a,null,{label:N(()=>[J(z(e.$t("Select New Automation Trigger"))+" ",1),R("p",null,z(e.$t("TriggerChanger.instruction")),1)]),default:N(()=>[H(c,{modelValue:s.funnel_data.trigger_name,"onUpdate:modelValue":t[0]||(t[0]=e=>s.funnel_data.trigger_name=e),filterable:"",placeholder:e.$t("Select")},{default:N(()=>[(M(!0),O(K,null,Z(s.triggers,(e,t)=>(M(),U(l,{key:t,label:e.category+" - "+e.label,value:t},null,8,["label","value"]))),128))],void 0,!0),_:1},8,["modelValue","placeholder"]),s.triggers[s.funnel_data.trigger_name]?(M(),O("p",{key:0,innerHTML:s.triggers[s.funnel_data.trigger_name].description},null,8,Re)):W("",!0)],void 0,!0),_:1}),s.funnel_data.trigger_name!=i.funnel.trigger_name?(M(),O(K,{key:0},[H(a,null,{label:N(()=>[J(z(e.$t("Automation Trigger Title")),1)]),default:N(()=>[H(_,{type:"text",placeholder:e.$t("Trigger Title"),modelValue:s.funnel_data.title,"onUpdate:modelValue":t[1]||(t[1]=e=>s.funnel_data.title=e)},null,8,["placeholder","modelValue"])],void 0,!0),_:1}),te((M(),U(d,{onClick:t[2]||(t[2]=e=>o.changeTrigger()),type:"primary"},{default:N(()=>[J(z(e.$t("Change Automation Trigger")),1)],void 0,!0),_:1})),[[h,s.saving]])],64)):W("",!0)],void 0),_:1},8,["data"])),[[h,s.fetching]])])}]]),InlineDoc:de,MoreFilled:u,CirclePlus:S,ArrowUp:d,ArrowDown:_},data(){return{direction:"rtl",ArrowRightBold:ne(P),showPopTest:!1,funnel:!1,working:!1,blocks:{},actions:[],funnel_sequences:[],block_fields:{},loading:!1,current_block:!1,current_block_index:!1,is_editing_root:!0,show_choice_modal:!1,choice_modal_index:"last",show_blocK_editor:!1,is_new_funnel:"yes"===this.$route.query.is_new,open_add_popover_index:null,stats:{},show_inline_report:!1,show_trigger_changer:!1,updatingFunnelSettings:!1,show_title_input:!1,editableTitle:"",pending_open_block_editor:!1,pending_reload_on_insert:!1,isProTrigger:!1}},computed:{showAddPopover1:{get(){return-1===this.open_add_popover_index},set(e){this.open_add_popover_index=e?-1:null}},action(){if(!this.actions)return{};const e=this.funnel.key;return this.actions[e]||{}},current_block_fields(){if(!this.current_block)return{};const e=this.current_block.action_name;return this.block_fields[e]}},methods:{fetchFunnel(){this.loading=!0,this.$get(`funnels/${this.funnel_id}`,{with:["blocks","block_fields","funnel_sequences"]}).then(e=>{var t;e.funnel.trigger||(this.$notify.error(this.$t("Attached Trigger could not be found")),this.show_trigger_changer=!0),this.funnel=e.funnel,this.editableTitle=(null==(t=e.funnel)?void 0:t.title)||"",this.block_fields=e.block_fields||{},this.blocks=e.blocks,this.actions=e.actions,window.fcrm_funnel_context_codes=e.composer_context_codes,this.funnel_sequences=e.funnel_sequences,!1!==this.current_block_index?(this.current_block=!1,this.$nextTick(()=>{this.current_block=this.funnel_sequences[this.current_block_index]})):this.is_new_funnel&&this.showRootSettings(),this.is_new_funnel=!1}).catch(e=>{this.handleError(e)}).finally(()=>{this.loading=!1,this.working=!1})},getInProgressSubscribersCount:e=>parseInt((null==e?void 0:e.in_progress_subscribers_count)||0),addBlock(e,t=null){if(this.pending_open_block_editor)return;let i;if(t?(i=JSON.parse(JSON.stringify(t)),e=t.action_name||e):i=this.blocks[e],!i)return void this.$notify.error(this.$t("Block definition not found"));const n=JSON.parse(JSON.stringify(i));let s=this.choice_modal_index;if("last"===s?s=this.funnel_sequences.length:s+=1,n.settings||(n.settings={}),"conditional"==n.type&&(n.children={yes:[],no:[]}),n.action_name=e,this.funnel_sequences.splice(s,0,n),this.current_block=!1,this.current_block_index=!1,this.$nextTick(()=>{this.current_block=this.funnel_sequences[s],this.current_block_index=s}),this.show_choice_modal)return this.pending_open_block_editor=!0,this.pending_reload_on_insert=!!n.reload_on_insert,void(this.show_choice_modal=!1);this.openBlockEditor(!!n.reload_on_insert)},openBlockEditor(e=!1){this.$nextTick(()=>{this.show_blocK_editor=!0,e&&this.saveAndFetchSettings()})},onChoiceDrawerClosed(){if(!this.pending_open_block_editor)return;const e=this.pending_reload_on_insert;this.pending_open_block_editor=!1,this.pending_reload_on_insert=!1,this.openBlockEditor(e)},handleBlockAdd(e){this.choice_modal_index=e,this.show_choice_modal=!0},setCurrentBlock(e,t){this.is_editing_root=!1,this.current_block=e,this.current_block_index=t,this.show_blocK_editor=!0},deleteFunnelSequence(){this.fireBeforeClose();const e=this.current_block_index;this.is_editing_root=!1,this.current_block=!1,this.current_block_index=!1,this.show_blocK_editor=!1,this.funnel_sequences.splice(e,1),this.saveFunnelSequences()},confirmDeleteBlock(e,t){h.confirm(this.$t("Delete_Block_Alert"),"",{confirmButtonText:this.$t("Yes"),cancelButtonText:this.$t("No"),type:"warning"}).then(()=>{this.deleteBlock(e,t)}).catch(()=>{})},deleteBlock(e,t){this.funnel_sequences.splice(t,1),this.saveFunnelSequences()},cloneBlock(e,t){delete(e=JSON.parse(JSON.stringify(e))).id,this.removeBlockCampaign(e),this.prepareConditionalBlock(e),this.funnel_sequences.splice(t,0,e),this.saveFunnelSequences()},removeBlockCampaign(e){e.settings&&e.settings.campaign&&delete e.settings.campaign.id},prepareConditionalBlock(e){"funnel_condition"===e.action_name&&(e.children.no.forEach(e=>{delete e.id,this.removeBlockCampaign(e)}),e.children.yes.forEach(e=>{delete e.id,this.removeBlockCampaign(e)}))},showRootSettings(){this.isProTrigger=!1,this.current_block=!1,this.current_block_index=!1,this.is_editing_root=!0,this.show_blocK_editor=!0},saveFunnelSequences(e,t=!1){if(window.fluencrm_fallback_funnel_ajax)return this.fallbackSaveFunnelSequence(e,t);this.working=!0,this.$post("funnels/funnel/save-funnel-sequences",{funnel_settings:JSON.stringify(this.funnel.settings),conditions:JSON.stringify(this.funnel.conditions),funnel_title:this.funnel.title,funnel_description:this.funnel.description,status:this.funnel.status,sequences:this.getStripedSequences(),funnel_id:this.funnel_id}).then(e=>{t?t(e):(this.funnel_sequences=e.sequences,this.$notify.success(e.message),this.fireBeforeClose(),this.show_blocK_editor=!1,this.current_block=!1,this.current_block_index=!1)}).catch(i=>{"rest_no_route"==i.code?(this.$notify.info(this.$t("Trying fallback save. Please Wait...")),this.fallbackSaveFunnelSequence(e,t)):this.handleError(i)}).finally(()=>{t||(this.working=!1)})},fallbackSaveFunnelSequence(e,t=!1){window.fluencrm_fallback_funnel_ajax=!0,this.working=!0,window.jQuery.post(window.ajaxurl,{action:"fluentcrm_save_funnel_sequence_ajax",_nonce:window.fcAdmin.ajax_nonce,funnel_id:this.funnel.id,funnel_settings:JSON.stringify(this.funnel.settings),conditions:JSON.stringify(this.funnel.conditions),funnel_title:this.funnel.title,status:this.funnel.status,sequences:JSON.stringify(this.funnel_sequences),is_fluentcrm:"yes"}).then(e=>{t?t(e):(this.funnel_sequences=e.sequences,this.$notify.success(e.message),this.show_blocK_editor=!1,this.current_block=!1,this.current_block_index=!1)}).catch(e=>{console.log(e),this.handleError(e)}).always(()=>{t||(this.working=!1)})},getStripedSequences(){const e=JSON.parse(JSON.stringify(this.funnel_sequences)),t=this.stripSequenceSets(e);return JSON.stringify(t)},stripSequenceSets(e){return this.each(e,e=>{"send_custom_email"==e.action_name?e=this.stripEmailSequence(e):"funnel_condition"==e.action_name&&(e.children.no=this.stripSequenceSets(e.children.no),e.children.yes=this.stripSequenceSets(e.children.yes))}),e},stripEmailSequence:e=>(e.settings.reference_campaign&&e.settings.reference_campaign==e.settings.campaign.id&&(e.settings.campaign={id:e.settings.campaign.id},e.is_stripped=!0),e),saveFunnelBlockSequence(){this.funnel_sequences[this.current_block_index]=this.current_block,this.$nextTick(()=>{this.saveFunnelSequences(!1)})},moveToPosition(e,t){let i=t-1;"down"===e&&(i=t+1);const n=this.funnel_sequences,s=n[t];n.splice(t,1),n.splice(i,0,s),this.funnel_sequences=n,this.saveFunnelSequences()},stripHtml(e){if(!e||"string"!=typeof e)return e||"";return e.replace(/<[^>]*>/g,"").replace(/&/g,"&").replace(//g,">").replace(/"/g,""").replace(/'/g,"'")},getBlockDescription(e){let t="";switch(e.action_name){case"send_custom_email":return this.isEmptyValue(e.settings.campaign.email_subject)?''+this.$t("Set Email Subject & Body")+"":this.stripHtml(e.settings.campaign.email_subject);case"fluentcrm_wait_times":if("timestamp_wait"==e.settings.wait_type)t=this.$t("Wait until ")+e.settings.wait_date_time;else if("to_day"==e.settings.wait_type)t=this.$t("Wait until next ")+e.settings.to_day.join(" / ")+" - "+e.settings.to_day_time;else if("by_custom_field"==e.settings.wait_type){if(!e.settings.by_custom_field)return this.$t("Set Custom Field");t=this.$t("Wait by custom field ")+e.settings.by_custom_field}else t=this.$t("Wait ")+e.settings.wait_time_amount+" "+e.settings.wait_time_unit;break;case"add_contact_to_company":case"detach_contact_from_company":case"fluentcrm_contact_added_to_companies":case"fluentcrm_contact_removed_from_companies":case"fcrm_has_contact_company":t='Set Companies',e.settings.company&&this.each(this.options.companies,i=>{i.id==e.settings.company&&(t=i.title)});break;case"add_contact_to_list":case"detach_contact_from_list":case"fluentcrm_contact_added_to_lists":case"fluentcrm_contact_removed_from_lists":case"fcrm_has_contact_list":if(t=''+this.$t("Set Lists")+"",!this.isEmptyValue(e.settings.lists)){const i=[];this.each(this.options.lists,t=>{-1!==e.settings.lists.indexOf(t.id.toString())&&i.push(t.title)}),t=i.join(", ")}break;case"add_contact_to_tag":case"detach_contact_from_tag":case"fluentcrm_contact_added_to_tags":case"fluentcrm_contact_removed_from_tags":case"fcrm_has_contact_tag":t=''+this.$t("Set Tags")+"",this.isEmptyValue(e.settings.tags)||(t=this.getTagNames(e.settings.tags));break;case"send_campaign_email":t='Set Campaign',e.settings.campaign_id&&this.each(this.options.campaigns,i=>{i.id==e.settings.campaign_id&&(t=i.title)});break;case"add_to_email_sequence":if(this.isEmptyValue(e.settings.sequence_id))t=''+this.$t("Set Email Sequence")+"";else{t=this.$t("Add To Sequence: ");const i=[];this.each(this.options.email_sequences,t=>{e.settings.sequence_id===t.id&&i.push(t.title)}),t=i.join(", ")}break;case"fluentcrm_email_sequence_completed":t=this.isEmptyValue(e.settings.sequence_ids)?''+this.$t("Set Email Sequence")+"":e.description;break;case"funnel_condition":t=''+this.$t("Set Condition")+"",this.isEmptyValue(e.settings.conditions)||this.isEmptyValue(e.settings.conditions[0])||(t=this.$t("Matching ")+e.settings.conditions.length+" condition sets");break;case"add_contact_activity":t=''+this.$t("Set Note Title")+"",this.isEmptyValue(e.settings.title)||(t=e.settings.title);break;case"update_contact_property":t=''+this.$t("Set Property")+"",this.isEmptyValue(e.settings.contact_properties)||this.isEmptyValue(e.settings.contact_properties[0].data_key)||(t=this.$t("Updating ")+e.settings.contact_properties.length+" properties");break;case"http_send_data":t=''+this.$t("Set Webhook URL")+"",this.isEmptyValue(e.settings.remote_url)||(t=this.$t("Send HTTP ")+e.settings.sending_method+" webhook");break;case"fcrm_change_user_role":t=''+this.$t("Set User Role")+"",this.isEmptyValue(e.settings.user_role)||(t=this.$t("Change User Role to ")+e.settings.user_role);break;case"remove_user_role":t=''+this.$t("Remove User Role")+"",this.isEmptyValue(e.settings.role)||(t=this.$t("Remove User Role: ")+e.settings.role);break;default:t=e.description}return t||this.stripHtml(e.description)},getTriggerDescription(e){let t="";switch(e.trigger_name){case"fluentcrm_contact_added_to_companies":case"fluentcrm_contact_removed_from_companies":if(this.isEmptyValue(e.settings.companies))return'Set Company';{const i=[];this.each(this.options.companies,t=>{-1!=e.settings.companies.indexOf(t.id.toString())&&i.push(t.title)}),t=i.join(", ")}break;case"fluentcrm_contact_added_to_tags":case"fluentcrm_contact_removed_from_tags":if(this.isEmptyValue(e.settings.tags))return''+this.$t("Set Tag")+"";{const i=[];this.each(this.options.tags,t=>{-1!=e.settings.tags.indexOf(t.id.toString())&&i.push(t.title)}),t=i.join(", ")}break;case"fluentcrm_contact_added_to_lists":case"fluentcrm_contact_removed_from_lists":if(this.isEmptyValue(e.settings.lists))return''+this.$t("Set Lists")+"";{const i=[];this.each(this.options.lists,t=>{-1!==e.settings.lists.indexOf(t.id.toString())&&i.push(t.title)}),t=i.join(", ")}break;default:t=this.stripHtml(e.trigger.label)}return t||this.stripHtml(e.trigger.label)},getTagNames(e){const t=[];return this.each(this.options.tags,i=>{-1!=e.indexOf(i.id.toString())&&t.push(i.title)}),t.join(", ")},saveAndFetchSettings(){this.$nextTick(()=>{this.saveFunnelSequences(!1,e=>{this.fetchFunnel()})})},gotoReports(){this.$router.push({name:"funnel_subscribers",params:{funnel_id:this.funnel_id}})},getBlockIcon(e){const t=e.action_name;return this.blocks[t]&&this.blocks[t].icon?this.blocks[t].icon:""},getStats(){this.$get(`funnels/${this.funnel_id}/report`).then(e=>{const t=e.stats.metrics,i={};this.each(t,e=>{i[e.sequence_id]=e}),this.stats=i}).catch(e=>{this.handleError(e)}).finally(()=>{})},getBlockClasses(e,t){var i,n;const s=["fc_block_type_"+(null==e?void 0:e.type),"fluentcrm_block_"+(null==e?void 0:e.action_name)];return(null==(i=null==e?void 0:e.settings)?void 0:i.type)&&"required"==(null==(n=null==e?void 0:e.settings)?void 0:n.type)&&s.push("fc_funnel_benchmark_required"),this.current_block_index===t&&s.push("fluentcrm_block_active"),s},showEditTriggerModal(){this.show_trigger_changer=!0},handleTriggerChanged(){this.show_trigger_changer=!1,this.is_new_funnel=!0,this.fetchFunnel()},handleConditionAdd(e){return this.has_campaign_pro?this.blocks.funnel_condition?(this.choice_modal_index=e,void this.addBlock("funnel_condition")):(this.$notify.error(this.$t("Condition block is not available")),!1):(this.$alert('

This block require pro version of FluentCRM

Please download and install FluentCRM Pro to activate this block

Get FluentCRM Pro

',"Require FluentCRM Pro",{dangerouslyUseHTMLString:!0,showConfirmButton:!1}),!1)},fireBeforeClose(e=!1){this.current_block&&"send_custom_email"==this.current_block.action_name&&this.unmountBlockEditor(),e&&e()},funnelRootSaved(){"yes"==this.$route.query.is_new&&this.$router.replace({query:null})},getTriggerIcon:e=>({woocommerce_order_status_completed:"fc-icon-woo_order_complete",woocommerce_order_status_processing:"fc-icon-woo_new_order",woocommerce_order_status_refunded:"fc-icon-woo_refund",woocommerce_order_status_changed:"fc-icon-woo",woocommerce_subscription_status_active:"fc-icon-woo_order_complete",woocommerce_subscription_renewal_payment_complete:"fc-icon-woo_order_complete",woocommerce_subscription_renewal_payment_failed:"fc-icon-woo_refund",wishlistmember_add_user_levels:"fc-icon-wishlist",tutor_after_enrolled:"fc-icon-tutor_lms_enrollment_course",tutor_course_complete_after:"fc-icon-tutor_lms_complete_course",tutor_lesson_completed_after:"fc-icon-tutor_lms_complete_course",rcp_membership_post_activate:"fc-icon-rcp_membership_level",rcp_transition_membership_status_expired:"fc-icon-rcp_membership_cancle",rcp_membership_post_cancel:"fc-icon-rcp_membership_cancle",pmpro_after_change_membership_level:"fc-icon-paid_membership_pro_user_level",pmpro_membership_post_membership_expiry:"fc-icon-membership_level_ex_pmp","mepr-account-is-active":"fc-icon-memberpress_membership","mepr-event-transaction-expired":"fc-icon-circle-close",llms_user_enrolled_in_course:"fc-icon-lifter_lms_course_enrollment",lifterlms_course_completed:"fc-icon-lifter_lms_complete_course",llms_user_added_to_membership_level:"fc-icon-lifter_lms_membership",lifterlms_lesson_completed:"fc-icon-lifter_lms_complete_lession-t2",learndash_update_course_access:"fc-icon-learndash_enroll_course",learndash_lesson_completed:"fc-icon-learndash_complete_lesson",learndash_topic_completed:"fc-icon-learndash_complete_topic",learndash_course_completed:"fc-icon-learndash_complete_course",ld_added_group_access:"fc-icon-learndash_course_group",simulated_learndash_update_course_removed:"fc-icon-learndash_enroll_course",fc_ab_cart_simulation_woo:"fc-icon-woo",fluentcrm_contact_birthday:"fc-icon-present",user_register:"fc-icon-wp_new_user_signup",fluentform_submission_inserted:"fc-icon-fluentforms",fluentcrm_contact_added_to_lists:"fc-icon-list_applied_2",edd_update_payment_status:"fc-icon-edd_new_order_success",edd_recurring_add_subscription_payment:"fc-icon-edd_new_order_success",edd_subscription_status_change:"fc-icon-circle-close",affwp_set_affiliate_status:"fc-icon-trigger",fluent_surecart_purchase_created_wrap:"fc-icon-shopping-cart-full",fluent_surecart_purchase_refund_wrap:"fc-icon-sold-out"}[e]||"fc-icon-trigger"),initKeyboardSave(e){(window.navigator.platform.match("Mac")?e.metaKey:e.ctrlKey)&&"s"===e.key&&(e.preventDefault(),this.saveFunnelSequences(!1,e=>{this.working=!1,this.funnel_sequences=e.sequences,this.$notify.success(e.message)}),this.funnelRootSaved())},showInputField(){var e;this.editableTitle=(null==(e=this.funnel)?void 0:e.title)||"",this.show_title_input=!0,this.$nextTick(()=>{const e=this.$refs.titleInput,t=Array.isArray(e)?e[0]:e;t&&t.focus&&t.focus()})},cancelInlineTitle(){var e;this.show_title_input=!1,this.editableTitle=(null==(e=this.funnel)?void 0:e.title)||""},saveInlineTitle(){const e=(this.editableTitle||"").trim();e&&(e!==this.funnel.title?this.updateFunnelTitle(e):this.show_title_input=!1)},updateFunnelTitle(e){this.updatingFunnelSettings=!0,this.$put(`funnels/funnel/${this.funnel.id}/title`,{title:e}).then(t=>{var i;this.funnel.title=(null==(i=null==t?void 0:t.funnel)?void 0:i.title)||e,this.editableTitle=this.funnel.title,this.show_title_input=!1,this.$notify.success(t.message)}).catch(e=>{this.handleError(e)}).finally(()=>{this.updatingFunnelSettings=!1})}},mounted(){window.fcAdmin&&window.fcAdmin.is_rtl&&(this.direction="ltr"),this.fetchFunnel(),this.getStats(),this.changeTitle(this.$t("Edit Funnel")),document.addEventListener("keydown",this.initKeyboardSave)},beforeUnmount(){window.fcrm_funnel_context_codes=void 0,document.removeEventListener("keydown",this.initKeyboardSave)}},[["render",function(e,t,i,n,s,o){var l,c;const _=x,d=r,u=g,h=T,w=B,C=V,S=q,P=D("Icons"),Q=A,Y=D("inline-doc"),X=a,ne=D("report-widget"),se=D("CirclePlus"),oe=D("MoreFilled"),le=f,ce=p,ae=m,re=D("ArrowUp"),_e=D("ArrowDown"),de=k,ue=D("dom-path"),he=D("child-blocks"),pe=F,fe=j,me=v,ge=E,ke=I,be=D("field-editor"),ve=D("form-field"),ye=$,we=b,Ce=D("block-choice"),$e=D("trigger-changer"),Se=L,xe=D("PromoCard"),Te=y;return M(),O("div",Ue,[R("div",Ne,[H(w,{class:"fcrm_funnel_breadcrumb","separator-icon":s.ArrowRightBold},{default:N(()=>[H(_,{to:{name:"funnels"}},{default:N(()=>[J(z(e.$t("Automation Funnel")),1)],void 0,!0),_:1}),s.funnel&&s.funnel.trigger?(M(),U(_,{key:0,class:"fcrm_funnel_title_editable_wrap"},{default:N(()=>[s.show_title_input?(M(),O("div",He,[H(d,{ref:"titleInput",size:"small",modelValue:s.editableTitle,"onUpdate:modelValue":t[0]||(t[0]=e=>s.editableTitle=e),placeholder:e.$t("Automation Name"),onKeyup:[t[1]||(t[1]=ie(e=>o.saveInlineTitle(),["enter"])),t[2]||(t[2]=ie(e=>o.cancelInlineTitle(),["esc"]))]},null,8,["modelValue","placeholder"]),H(u,{size:"small",type:"primary",loading:s.updatingFunnelSettings,disabled:s.updatingFunnelSettings||!s.editableTitle||!s.editableTitle.trim(),onClick:t[3]||(t[3]=e=>o.saveInlineTitle())},{default:N(()=>[J(z(e.$t("Save")),1)],void 0,!0),_:1},8,["loading","disabled"]),H(u,{size:"small",disabled:s.updatingFunnelSettings,onClick:t[4]||(t[4]=e=>o.cancelInlineTitle())},{default:N(()=>[J(z(e.$t("Cancel")),1)],void 0,!0),_:1},8,["disabled"])])):(M(),O(K,{key:1},[H(h,{placement:"right",title:s.funnel.trigger.label,width:"300",trigger:"hover",content:s.funnel.description||s.funnel.trigger.description},{reference:N(()=>[R("div",Je,[o.getInProgressSubscribersCount(s.funnel)?(M(),O("span",{key:0,class:"fcrm_live_indicator",title:e.$t("Contacts currently in this automation")},null,8,ze)):W("",!0),R("span",Ke,z(s.funnel.title),1)])]),_:1},8,["title","content"]),R("span",{onClick:t[5]||(t[5]=(...e)=>o.showInputField&&o.showInputField(...e)),class:"icon-edit"},[...t[40]||(t[40]=[R("svg",{width:"16",height:"16",viewBox:"0 0 16 16",fill:"none",xmlns:"http://www.w3.org/2000/svg"},[R("path",{d:"M4.6485 10.4001L10.7337 4.31485L9.8853 3.46645L3.8001 9.55165V10.4001H4.6485ZM5.1459 11.6001H2.6001V9.05425L9.4611 2.19325C9.57361 2.08077 9.7262 2.01758 9.8853 2.01758C10.0444 2.01758 10.197 2.08077 10.3095 2.19325L12.0069 3.89065C12.1194 4.00317 12.1826 4.15575 12.1826 4.31485C12.1826 4.47395 12.1194 4.62653 12.0069 4.73905L5.1459 11.6001V11.6001ZM2.6001 12.8001H13.4001V14.0001H2.6001V12.8001Z",fill:"var(--fc-secondary-text)"})],-1)])])],64))],void 0,!0),_:1})):W("",!0)],void 0),_:1},8,["separator-icon"]),s.funnel&&s.funnel.trigger?(M(),O("div",We,[H(C,{class:"fcrm_funnel_stats_checkbox",modelValue:s.show_inline_report,"onUpdate:modelValue":t[6]||(t[6]=e=>s.show_inline_report=e)},{default:N(()=>[J(z(e.$t("Stats")),1)],void 0),_:1},8,["modelValue"]),H(S,{onChange:t[7]||(t[7]=e=>o.saveFunnelSequences(!0)),modelValue:s.funnel.status,"onUpdate:modelValue":t[8]||(t[8]=e=>s.funnel.status=e),"active-value":"published",size:"small","active-text":"published"===s.funnel.status?e.$t("Published"):e.$t("Draft"),"inactive-value":"draft"},null,8,["modelValue","active-text"]),"published"==s.funnel.status?(M(),U(u,{key:0,onClick:t[9]||(t[9]=e=>o.gotoReports())},{default:N(()=>[R("span",Ze,[H(P,{"icon-name":"bar-chart"})]),J(" "+z(e.$t("Reports")),1)],void 0),_:1})):W("",!0),H(Q,{content:e.$t("Configure Trigger"),placement:"bottom"},{default:N(()=>[H(u,{onClick:t[10]||(t[10]=e=>o.showEditTriggerModal()),class:"configure-btn"},{default:N(()=>[R("span",Ge,[H(P,{"icon-name":"configure"})])],void 0,!0),_:1})],void 0),_:1},8,["content"]),H(Y,{doc_id:13304})])):W("",!0)]),s.funnel&&s.funnel.trigger?te((M(),O("div",Qe,[R("div",Ye,[R("div",Xe,[R("div",et,[R("div",tt,[R("div",{class:G([s.is_editing_root?"fluentcrm_block_active":"","fluentcrm_block fluentcrm_block_trigger"])},[e.appVars.icons.trigger_icon?(M(),O("div",{key:0,onClick:t[11]||(t[11]=e=>o.showRootSettings()),class:"fluentcrm_blockin"},[R("div",it,[s.funnel.trigger.svg?(M(),U(X,{key:0},{default:N(()=>[R("span",{innerHTML:s.funnel.trigger.svg},null,8,nt)],void 0),_:1})):(M(),O("i",{key:1,class:G(o.getTriggerIcon(s.funnel.trigger_name))},null,2)),J(" "+z(s.funnel.title),1)]),R("div",{class:"fluentcrm_block_desc",innerHTML:o.getTriggerDescription(s.funnel)},null,8,st),s.show_inline_report?(M(),U(ne,{key:0,class:"fcrm_block_stats",stat:s.stats[0]},null,8,["stat"])):W("",!0)])):W("",!0)],2),R("div",ot,[H(h,{placement:"right",width:"400",trigger:"hover",visible:o.showAddPopover1,"onUpdate:visible":t[15]||(t[15]=e=>o.showAddPopover1=e),onShow:t[16]||(t[16]=e=>s.open_add_popover_index=-1),"popper-class":"fcrm_action_selector_popover",onHide:t[17]||(t[17]=e=>s.open_add_popover_index=null)},{reference:N(()=>[R("div",{class:"fc_show_plus",onClick:t[14]||(t[14]=e=>o.handleBlockAdd(-1))},[H(X,{class:"fc_add_block_icon","aria-label":"Add Action"},{default:N(()=>[H(se)],void 0,!0),_:1})])]),default:N(()=>[R("div",lt,[R("ul",null,[R("li",{onClick:t[12]||(t[12]=e=>{o.handleBlockAdd(-1),s.open_add_popover_index=null})},[R("span",ct,[H(P,{"icon-name":"action"})]),R("div",at,[R("h4",null,z(e.$t("Add Action / Goal")),1),R("p",null,z(e.$t("Run Action Task to do particular task on the contact")),1)])]),R("li",{onClick:t[13]||(t[13]=e=>{o.handleConditionAdd(-1),s.open_add_popover_index=null})},[R("span",rt,[H(P,{"icon-name":"conditions"})]),R("div",_t,[R("h4",null,z(e.$t("Conditional Action")),1),R("p",null,z(e.$t("Funnel_Conditional_Action_desc")),1)])])])])],void 0),_:1},8,["visible"])])]),(M(!0),O(K,null,Z(s.funnel_sequences,(n,l)=>(M(),O("div",{class:G(["block_item_holder_"+n.type,"block_item_holder"]),key:l},[R("div",{class:G(["fluentcrm_block",o.getBlockClasses(n,l)])},[R("div",{onClick:e=>o.setCurrentBlock(n,l),class:"fluentcrm_blockin"},[R("div",ut,[H(ae,{trigger:"click"},{dropdown:N(()=>[H(ce,{class:"fc_clickable_pop"},{default:N(()=>[H(le,{onClick:ee(e=>o.confirmDeleteBlock(n,l),["stop"])},{default:N(()=>[R("span",null,z(e.$t("Delete")),1)],void 0,!0),_:1},8,["onClick"]),H(le,null,{default:N(()=>[R("span",{onClick:e=>o.cloneBlock(n,l),class:"el-tooltip__trigger"},z(e.$t("Clone")),9,ht)],void 0,!0),_:2},1024)],void 0,!0),_:2},1024)]),default:N(()=>[R("span",{onClick:t[18]||(t[18]=ee(()=>{},["stop"])),class:"el-dropdown-link"},[H(X,{style:{"font-weight":"bold",cursor:"pointer"},class:"icon-90degree"},{default:N(()=>[H(oe)],void 0,!0),_:1})])],void 0),_:2},1024)]),R("div",pt,[R("i",{class:G(o.getBlockIcon(n))},null,2),J(" "+z(n.title),1)]),R("div",{class:"fluentcrm_block_desc",innerHTML:o.getBlockDescription(n)},null,8,ft),s.show_inline_report?(M(),U(ne,{key:0,class:"fcrm_block_stats",stat:s.stats[n.id]},null,8,["stat"])):W("",!0)],8,dt),H(de,{class:"fc_block_controls"},{default:N(()=>[H(u,{disabled:0==l,onClick:e=>o.moveToPosition("up",l),size:"small"},{default:N(()=>[H(X,null,{default:N(()=>[H(re)],void 0,!0),_:1})],void 0,!0),_:1},8,["disabled","onClick"]),H(u,{disabled:l+1==s.funnel_sequences.length,onClick:e=>o.moveToPosition("down",l),size:"small"},{default:N(()=>[H(X,null,{default:N(()=>[H(_e)],void 0,!0),_:1})],void 0,!0),_:1},8,["disabled","onClick"])],void 0),_:2},1024),"conditional"===n.type?(M(),O(K,{key:0},[t[41]||(t[41]=R("span",{class:"fc_b_yes_node"},null,-1)),t[42]||(t[42]=R("span",{class:"fc_b_no_node"},null,-1))],64)):W("",!0),t[43]||(t[43]=R("span",null,null,-1))],2),"conditional"==n.type?(M(),O(K,{key:0},["funnel_ab_testing"==n.action_name?(M(),O(K,{key:0},[H(ue,{class:"fc_ab_test fc_ab_test_a",side:"left",from:"fc_b_no_node",to:"fc_b_no_node_point"},{default:N(()=>[R("span",null,[t[44]||(t[44]=J("A - ",-1)),R("span",null,z(n.settings.path_a)+"%",1)])],void 0),_:2},1024),H(ue,{class:"fc_ab_test fc_ab_test_b",side:"right",from:"fc_b_yes_node",to:"fc_b_yes_node_point"},{default:N(()=>[R("span",null,"B - "+z(n.settings.path_b)+"%",1)],void 0),_:2},1024)],64)):(M(),O(K,{key:1},[H(ue,{side:"left",from:"fc_b_no_node",to:"fc_b_no_node_point",class:"fc_condition_node_point"},{default:N(()=>[R("span",null,z(e.$t("No")),1)],void 0),_:1}),H(ue,{side:"right",from:"fc_b_yes_node",to:"fc_b_yes_node_point",class:"fc_condition_node_point"},{default:N(()=>[R("span",null,z(e.$t("Yes")),1)],void 0),_:1})],64)),R("div",mt,[R("div",gt,[t[45]||(t[45]=R("span",{class:"fc_b_no_node_point"},null,-1)),R("div",kt,[H(he,{all_blocks:s.blocks,blocks:n.children.no,show_inline_report:s.show_inline_report,getBlockDescription:o.getBlockDescription,getBlockIcon:o.getBlockIcon,stats:s.stats,options:i.options,onSave:t[19]||(t[19]=e=>o.saveFunnelSequences()),block_fields:s.block_fields},null,8,["all_blocks","blocks","show_inline_report","getBlockDescription","getBlockIcon","stats","options","block_fields"])])]),R("div",bt,[t[46]||(t[46]=R("span",{class:"fc_b_yes_node_point"},null,-1)),R("div",vt,[H(he,{all_blocks:s.blocks,blocks:n.children.yes,show_inline_report:s.show_inline_report,getBlockDescription:o.getBlockDescription,getBlockIcon:o.getBlockIcon,stats:s.stats,options:i.options,onSave:t[20]||(t[20]=e=>o.saveFunnelSequences()),block_fields:s.block_fields},null,8,["all_blocks","blocks","show_inline_report","getBlockDescription","getBlockIcon","stats","options","block_fields"])])])]),t[47]||(t[47]=R("div",{class:"fc_cond_border_no_top"},null,-1))],64)):W("",!0),R("div",yt,[H(h,{placement:"right",width:"400",trigger:"click",visible:s.open_add_popover_index===l,"onUpdate:visible":e=>s.open_add_popover_index=e?l:null,onShow:e=>s.open_add_popover_index=l,"popper-class":"fcrm_action_selector_popover",onHide:t[21]||(t[21]=e=>s.open_add_popover_index=null)},{reference:N(()=>[R("div",Vt,[H(X,{class:"fc_add_block_icon","aria-label":"Add Action"},{default:N(()=>[H(se)],void 0,!0),_:1})])]),default:N(()=>[R("div",wt,[R("ul",null,[R("li",{onClick:e=>{o.handleBlockAdd(l),s.open_add_popover_index=null}},[R("span",$t,[H(P,{"icon-name":"action"})]),R("div",St,[R("h4",null,z(e.$t("Add Action / Goal")),1),R("p",null,z(e.$t("Run Action Task to do particular task on the contact")),1)])],8,Ct),R("li",{onClick:e=>{o.handleConditionAdd(l),s.open_add_popover_index=null}},[R("span",Tt,[H(P,{"icon-name":"conditions"})]),R("div",Bt,[R("h4",null,z(e.$t("Conditional Action")),1),R("p",null,z(e.$t("Funnel_Conditional_Action_desc")),1)])],8,xt)])])],void 0),_:2},1032,["visible","onUpdate:visible","onShow"])])],2))),128))])])])])),[[Te,s.working]]):W("",!0),s.loading?(M(),O("div",qt,[R("div",At,[H(pe,{class:"el-progress_animated","show-text":!1,percentage:30})]),s.loading?(M(),U(fe,{key:0,style:{padding:"20px"},rows:8})):W("",!0)])):W("",!0),H(we,{direction:s.direction,class:G(["fc_company_info_drawer fc_drawer_edit_block fc_funnel_block_modal",s.current_block?"fc_drawer_for_"+s.current_block.action_name+(s.current_block.id?" fc_blocked_has_id":" fc_blocked_no_id"):""]),"close-on-click-modal":!1,modelValue:s.show_blocK_editor,"onUpdate:modelValue":t[34]||(t[34]=e=>s.show_blocK_editor=e),"append-to-body":!0,onClose:o.fireBeforeClose,"with-header":!1,size:e.globalDrawerSize,"destroy-on-close":!0,"modal-class":"fcrm_funnel_block_modal"},{default:N(()=>[R("div",Ft,[s.current_block?(M(),O("div",jt,[(M(),U(be,{title_badge:e.trans(s.current_block.type),block_type:s.current_block.type,onSave:t[24]||(t[24]=e=>o.saveFunnelBlockSequence()),onDeleteSequence:t[25]||(t[25]=e=>o.deleteFunnelSequence()),show_controls:!0,onSave_reload:t[26]||(t[26]=e=>o.saveAndFetchSettings()),data:s.current_block.settings,options:i.options,action_name:s.current_block.action_name,funnel_id:i.funnel_id,key:s.current_block_index+"_"+s.current_block.action_name,is_first:0===s.current_block_index,is_last:s.current_block_index===s.funnel_sequences.length-1,settings:o.current_block_fields,onCloseDrawer:t[27]||(t[27]=e=>s.show_blocK_editor=!1)},{after_header:N(()=>[H(ke,{class:"fcrm_funnel_editor_internal_config",gutter:20},{default:N(()=>[H(ge,{md:12,sm:24},{default:N(()=>[H(me,{label:e.$t("Internal Label")},{default:N(()=>[H(d,{placeholder:e.$t("Internal Label"),modelValue:s.current_block.title,"onUpdate:modelValue":t[22]||(t[22]=e=>s.current_block.title=e)},null,8,["placeholder","modelValue"])],void 0,!0),_:1},8,["label"])],void 0,!0),_:1}),H(ge,{md:12,sm:24},{default:N(()=>[H(me,{label:e.$t("Internal Description")},{default:N(()=>[H(d,{rows:2,type:"textarea",placeholder:e.$t("Internal Description"),modelValue:s.current_block.description,"onUpdate:modelValue":t[23]||(t[23]=e=>s.current_block.description=e)},null,8,["placeholder","modelValue"])],void 0,!0),_:1},8,["label"])],void 0,!0),_:1})],void 0,!0),_:1})]),_:1},8,["title_badge","block_type","data","options","action_name","funnel_id","is_first","is_last","settings"]))])):s.is_editing_root?(M(),O(K,{key:1},[R("div",It,[H(be,{key:"is_editing_root",title_badge:"trigger",onSave_reload:t[30]||(t[30]=e=>o.saveAndFetchSettings()),show_controls:!1,options:i.options,data:s.funnel.settings,onCloseDrawer:t[31]||(t[31]=e=>s.show_blocK_editor=!1),settings:s.funnel.settingsFields},{after_header:N(()=>[H(ke,{gutter:20,class:"fcrm_funnel_editor_internal_config"},{default:N(()=>[H(ge,{md:12,sm:24},{default:N(()=>[H(me,{label:e.$t("Automation Name")},{default:N(()=>[H(d,{placeholder:e.$t("Automation Name"),modelValue:s.funnel.title,"onUpdate:modelValue":t[28]||(t[28]=e=>s.funnel.title=e)},null,8,["placeholder","modelValue"])],void 0,!0),_:1},8,["label"])],void 0,!0),_:1}),H(ge,{md:12,sm:24},{default:N(()=>[H(me,{label:e.$t("Internal Description")},{default:N(()=>[H(d,{rows:2,type:"textarea",placeholder:e.$t("Internal Description"),modelValue:s.funnel.description,"onUpdate:modelValue":t[29]||(t[29]=e=>s.funnel.description=e)},null,8,["placeholder","modelValue"])],void 0,!0),_:1},8,["label"])],void 0,!0),_:1})],void 0,!0),_:1})]),_:1},8,["options","data","settings"]),e.isEmptyValue(s.funnel.conditions)?W("",!0):(M(),U(ye,{key:0,class:"fcrm_funnel_conditions fcrm_funnel_editor_boxed_with_border","label-position":"top",model:s.funnel.conditions},{default:N(()=>[R("h3",null,z(e.$t("Conditions")),1),(M(!0),O(K,null,Z(s.funnel.conditionFields,(e,n)=>(M(),O("div",{key:n,class:"fcrm_funnel_condition_field"},[(M(),U(ve,{onSave_reload:t[32]||(t[32]=e=>o.saveAndFetchSettings()),key:n,modelValue:s.funnel.conditions[n],"onUpdate:modelValue":e=>s.funnel.conditions[n]=e,field:e,options:i.options},null,8,["modelValue","onUpdate:modelValue","field","options"]))]))),128))],void 0,!0),_:1},8,["model"]))]),R("div",Et,[R("div",Lt,[H(u,{onClick:t[33]||(t[33]=e=>{o.saveFunnelSequences(!1),o.funnelRootSaved()}),size:"small",type:"primary"},{default:N(()=>[J(z(e.$t("Save Settings")),1)],void 0,!0),_:1})])])],64)):W("",!0)])],void 0),_:1},8,["direction","class","modelValue","onClose","size"]),H(we,{direction:s.direction,class:"fc_company_info_drawer fcrm_drawer","close-on-click-modal":!1,title:e.$t("Add Action / Goal"),modelValue:s.show_choice_modal,"onUpdate:modelValue":t[36]||(t[36]=e=>s.show_choice_modal=e),"append-to-body":!0,"with-header":!1,onClosed:o.onChoiceDrawerClosed,ref:"choice_action_goal_drawer",size:e.globalDrawerSize,"destroy-on-close":!0,"modal-class":"fcrm_funnel_blocks_model"},{default:N(()=>[s.show_choice_modal?(M(),U(Ce,{key:0,onClose:t[35]||(t[35]=()=>{s.show_choice_modal=!1}),show_close:!0,onInsert:o.addBlock,blocks:s.blocks},null,8,["onInsert","blocks"])):W("",!0)],void 0),_:1},8,["direction","title","modelValue","onClosed","size"]),H(Se,{"close-on-click-modal":!1,title:e.$t("Edit Primary Automation Trigger"),modelValue:s.show_trigger_changer,"onUpdate:modelValue":t[38]||(t[38]=e=>s.show_trigger_changer=e),"append-to-body":!0,width:"60%"},{default:N(()=>[s.show_trigger_changer?(M(),U($e,{key:0,funnel:s.funnel,onRefreshTrigger:t[37]||(t[37]=e=>o.handleTriggerChanged())},null,8,["funnel"])):W("",!0)],void 0),_:1},8,["title","modelValue"]),H(Se,{modelValue:s.isProTrigger,"onUpdate:modelValue":t[39]||(t[39]=e=>s.isProTrigger=e),"append-to-body":!0,width:"300px",title:(null==(c=null==(l=s.funnel)?void 0:l.trigger)?void 0:c.label)||""},{default:N(()=>[H(xe,{headign:e.$t("This is a Pro Feature"),"show-header-upgrade-icon":!1},null,8,["headign"])],void 0),_:1},8,["modelValue","title"])])}]]);export{Pt as default}; diff --git a/wp-content/plugins/fluent-crm/assets/admin/Modules/Funnels/FunnelRoute.js b/wp-content/plugins/fluent-crm/assets/admin/Modules/Funnels/FunnelRoute.js new file mode 100644 index 0000000..e18bba4 --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/admin/Modules/Funnels/FunnelRoute.js @@ -0,0 +1 @@ +import{aQ as s,W as t,X as a,ab as e}from"../../../vendor.js?ver=3.1.8";import{_ as i}from"../../../fc-bits-ui.js?ver=3.1.8";import"../../../vendor-element-plus.js?ver=3.1.8";const o={class:"fcrm_funnel_root fc_funnel_root"};const n=i({name:"FunnelRoute",data:()=>({app_ready:!1,options:{}}),methods:{getOptions(){this.app_ready=!1;const s={fields:"campaigns,email_sequences"};this.has_company_module&&(s.fields=`${s.fields},companies`),this.$get("reports/options",s).then(s=>{this.options=s.options,this.options.tags=this.appVars.available_tags,this.options.lists=this.appVars.available_lists,this.options.editable_statuses=this.appVars.available_contact_editable_statuses}).catch(s=>{this.handleError(s)}).finally(()=>{this.app_ready=!0})}},mounted(){this.getOptions(),this.changeTitle(this.$t("Automations"))}},[["render",function(i,n,p,r,l,h){const c=s("router-view");return t(),a("div",o,[e(c,{options:l.options},null,8,["options"])])}]]);export{n as default}; diff --git a/wp-content/plugins/fluent-crm/assets/admin/Modules/Funnels/FunnelSubscribers.js b/wp-content/plugins/fluent-crm/assets/admin/Modules/Funnels/FunnelSubscribers.js new file mode 100644 index 0000000..6edac22 --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/admin/Modules/Funnels/FunnelSubscribers.js @@ -0,0 +1 @@ +import{ay as e,av as t,k as s,g as a,c as n,W as i,aZ as l,a_ as r,aJ as c,b9 as o,aE as u,aH as d,aI as _,E as p,aO as m,j as h,h as f,i as b,e as g,aK as v,aL as y,aT as k,ap as w}from"../../../vendor-element-plus.js?ver=3.1.8";import{aQ as S,a6 as C,W as $,X as x,Y as A,aa as q,a8 as F,Z as E,J as P,az as R,ab as I,a9 as B,a5 as T,a0 as V,b2 as j,av as L}from"../../../vendor.js?ver=3.1.8";import{P as D}from"../../../PaginationBar.js?ver=3.1.8";import{D as N}from"../../../DataTable.js?ver=3.1.8";import{F as z}from"../../../FloatingBulkActionShell.js?ver=3.1.8";import{I as O,C as U}from"../../../_IndividualProgress.js?ver=3.1.8";import{E as M}from"../../../_eChart.js?ver=3.1.8";import{_ as W,I as H,a as Y,T as G}from"../../../fc-bits-ui.js?ver=3.1.8";import{R as K}from"../../../_report_widget.js?ver=3.1.8";import{C as Z}from"../../../Confirm.js?ver=3.1.8";import{L as J,C as Q}from"../../../_LinkMetrics.js?ver=3.1.8";import{G as X}from"../../../GenericPromo.js?ver=3.1.8";import{S as ee}from"../../../_StepPicker.js?ver=3.1.8";import{B as te}from"../../../Badge.js?ver=3.1.8";import"../Email/Campaigns/_components/EmailPreview.js?ver=3.1.8";import"../../../PreviewIframeBuilder.js?ver=3.1.8";import"../../../SettingsIcons.js?ver=3.1.8";import"../../../PromoCard.js?ver=3.1.8";const se={style:{"min-height":"400px"}},ae={key:1,class:"fc_chart_empty",style:{"text-align":"center",padding:"40px 0",opacity:".6"}};const ne={class:"fcrm_funnel_step_reports"},ie={class:"fcrm_funnel_step_reports--list"},le={class:"fcrm_funnel_step_reports--card-title"},re={class:"fcrm_funnel_step_reports--card-stats-badges"},ce={key:0,class:"fcrm_funnel_step_reports--card fcrm_funnel_step_reports--card-result"},oe={class:"fcrm_funnel_step_reports--card-title"};const ue={class:"fcrm_funnel_email_reports"},de={key:0,class:"fcrm_funnel_email_lists"},_e={class:"fcrm_funnel_email_card--left"},pe=["onClick"],me={class:"fcrm_funnel_email_card--meta"},he={class:"icon"},fe={class:"fcrm_funnel_email_card--right"},be={class:"fcrm_quick_stats_wrap"},ge={class:"fcrm_quick_stats_label"},ve={class:"fcrm_quick_stats"},ye=["onClick"],ke={class:"icon"},we={class:"fcrm_quick_stat_digit"},Se=["title"],Ce={class:"icon"},$e={class:"fcrm_quick_stat_digit"},xe=["onClick","title"],Ae={class:"icon"},qe={class:"fcrm_quick_stat_digit"},Fe=["title"],Ee={class:"icon"},Pe={class:"fcrm_quick_stat_digit"},Re={key:0,class:"fcrm_quick_stat--revenue"},Ie={class:"icon"},Be={class:"fcrm_quick_stat_digit"},Te={key:1,class:"text-align-center"};const Ve={class:"fcrm_sync_new_steps_content"},je={style:{"font-size":"16px"}},Le={key:0},De={key:1},Ne={key:2};const ze={class:"fcrm_funnel_subscribers_report"},Oe={class:"fcrm_page_header_top_nav_wrapper"},Ue={class:"fcrm_page_header_breadcrumb"},Me={class:"fcrm_page_header_top_actions"},We={key:0,class:"fcrm_funnel_matrics_wrapper fc_m_24"},He={class:"fcrm_funnel_matrics_header"},Ye={class:"fcrm_funnel_matrics_body"},Ge={key:3,class:"fcrm_funnel_matrics_stat"},Ke={class:"count"},Ze={class:"icon"},Je={class:"fcrm_empty_state"},Qe={class:"fcrm_empty_state_text"},Xe={key:1},et={class:"icon"},tt={key:0},st={key:1},at={key:0},nt=["title"],it={class:"icon"},lt=["title"],rt=["title"],ct={class:"el-dropdown-link"},ot={class:"el-popover__reference"},ut={class:"icon"},dt={class:"el-popover__reference"},_t={class:"icon"},pt={class:"el-popover__reference"},mt={class:"icon"},ht={class:"el-popover__reference"},ft={class:"icon"},bt={class:"fcrm_bulk_action_bar"},gt={class:"fcrm_bulk_action_left"},vt={class:"fc_bulk_selection_count"},yt={class:"icon"},kt={style:{padding:"10px 20px"}};const wt=W({name:"FunnelSubscribers",props:["funnel_id"],components:{Icons:H,Badge:te,PaginationBar:D,DataTable:N,FloatingBulkActionShell:z,ContactCard:U,IndividualProgress:O,FunnelChart:W({name:"funnel-reporting-bar",props:["funnel_id","stats"],components:{EChart:M},data:()=>({fetching:!0,eChartOptions:null,maxCumulativeValue:0}),computed:{},methods:{setupChartItems(){if(!this.stats.metrics||0===this.stats.metrics.length)return this.eChartOptions=null,void(this.fetching=!1);const e=[],t=[],s=[],a=this.getBackgroundColors(this.stats.metrics.length);let n=0;this.each(this.stats.metrics,(i,l)=>{t.push({value:i.count,itemStyle:{color:"benchmark"===i.type?"red":a[l]}}),s.push(i.count),e.push(i.label+"\n"+i.percent+"%"),i.count>n&&(n=i.count)}),this.maxCumulativeValue=n+10;const i=e.length,l=i>8?Math.max(8/i*100,10):100,r=this;this.eChartOptions={tooltip:{trigger:"axis",axisPointer:{type:"shadow"},formatter:e=>{const t=r.stats.metrics[e[0].dataIndex];let s=`${t.label}
`;return s+=`${t.percent}%
`,e.forEach(e=>{const t=``;s+=`${t} ${e.seriesName}: ${e.value}
`}),t.revenues&&(s+=`Revenue: ${t.revenues.join(" & ")}`),s}},legend:{top:10,data:[this.$t("Contacts"),this.$t("Line")]},grid:{left:40,right:85,bottom:60,top:50,containLabel:!0},xAxis:{type:"category",data:e,axisLabel:{interval:0,rotate:0,hideOverlap:!0}},yAxis:[{type:"value",min:0,position:"left",minInterval:1,axisLabel:{formatter:e=>Number.isInteger(e)?e:""},splitLine:{lineStyle:{color:"#E1E4EA",type:"dashed"}}},{type:"value",min:0,max:this.maxCumulativeValue,position:"right",minInterval:1,axisLabel:{formatter:e=>Number.isInteger(e)?e:""},splitLine:{lineStyle:{color:"#E1E4EA",type:"dashed"}}}],dataZoom:[{type:"inside",xAxisIndex:0,start:0,end:l},{type:"slider",xAxisIndex:0,orient:"vertical",start:0,end:l,right:20,top:90,bottom:90,width:20,showDataShadow:!1,brushSelect:!1}],series:[{name:this.$t("Contacts"),type:"bar",data:t,barMaxWidth:40,yAxisIndex:0},{name:this.$t("Line"),type:"line",data:s,yAxisIndex:1,smooth:!0,symbol:"circle",symbolSize:6,itemStyle:{color:"#335CFF"},lineStyle:{color:"#335CFF",width:2},areaStyle:{color:"rgba(55, 162, 235, 0.1)"}}]},this.fetching=!1},getBackgroundColors:e=>["#255A65","#22666C","#227372","#258077","#2D8C79","#3A997A","#4BA579","#5EB177","#73BD73","#8AC870","#A4D36C","#BFDC68","#DBE566","#544b66","#4f30c6","#190b1f","#6f23a7","#2d2134","#483ba6","#0e0d2c","#7a2d88","#181837","#2d187b","#2e2d4e","#491963","#1e052c","#3d3e8a","#2d163c","#644378","#210a50","#3f2c5b","#19164b","#461748"].slice(0,e)},mounted(){this.setupChartItems()},watch:{stats:{deep:!0,handler(){this.fetching=!0,this.setupChartItems()}}}},[["render",function(t,s,a,n,i,l){const r=S("EChart"),c=e;return C(($(),x("div",se,[!i.fetching&&i.eChartOptions?($(),A(r,{key:0,options:i.eChartOptions,height:400},null,8,["options"])):i.fetching?F("",!0):($(),x("div",ae,q(t.$t("No chart data found")),1))])),[[c,i.fetching]])}]]),FunnelTextReport:W({name:"FunnelTextReport",props:["stats","funnel"],components:{ReportWidget:K},data:()=>({colors:[{color:"var(--fc-error)",percentage:20},{color:"var(--fc-warning)",percentage:40},{color:"#5cb87a",percentage:60},{color:"#1989fa",percentage:80},{color:"var(--fc-deep-bg)",percentage:100}]}),methods:{format:e=>function(){return`${e}%`}},computed:{lastItem(){return!!this.stats.metrics.length&&this.stats.metrics[this.stats.metrics.length-1]}},mounted(){}},[["render",function(e,s,a,n,i,l){const r=t,c=S("report-widget");return $(),x("div",ne,[E("div",ie,[($(!0),x(P,null,R(a.stats.metrics,(e,t)=>($(),x("div",{class:"fcrm_funnel_step_reports--card",key:t},[E("h3",le,q(e.label),1),I(r,{percentage:e.percent>100?100:e.percent,"stroke-width":8,color:i.colors},null,8,["percentage","color"]),E("div",re,[I(c,{stat:e},null,8,["stat"])])]))),128)),l.lastItem?($(),x("div",ce,[E("h3",oe,q(e.$t("Overall Conversion Rate"))+": "+q(l.lastItem.percent)+"%",1),I(r,{percentage:100,status:"success"}),s[0]||(s[0]=E("div",{class:"fcrm_funnel_step_reports--card-stats-badges"},[E("div",{class:"fcrm_block_stats"})],-1))])):F("",!0)])])}]]),Confirm:Z,FunnelEmails:W({name:"FunnelEmails",props:["funnel_id"],components:{Icons:H,CampaignEmails:Q,LinkMetrics:J},data:()=>({email_sequences:[],fetching:!1,show_email_report:!1,show_email_report_id:!1,link_clicks_modal:!1,link_click_id:!1}),methods:{fetchEmailSequences(){this.fetching=!0,this.$get(`funnels/${this.funnel_id}/email_reports`).then(e=>{this.email_sequences=e.email_sequences}).catch(e=>{this.handleError(e)}).finally(()=>{this.fetching=!1})},getPercent:(e,t)=>t&&e?parseFloat(e/t*100).toFixed(2)+"%":"--",showEmailReport(e){this.show_email_report_id=e,this.show_email_report=!0},showLinkReport(e){this.link_click_id=e,this.link_clicks_modal=!0}},mounted(){this.fetchEmailSequences()}},[["render",function(t,n,i,l,r,c){const o=S("Icons"),u=s,d=S("campaign-emails"),_=a,p=S("link-metrics"),m=e;return C(($(),x("div",ue,[r.email_sequences.length?($(),x("div",de,[($(!0),x(P,null,R(r.email_sequences,e=>($(),x("div",{key:e.id,class:"fcrm_funnel_email_card"},[e.campaign?($(),x(P,{key:0},[E("div",_e,[E("div",{class:"fcrm_funnel_email_card--title",onClick:t=>c.showEmailReport(e.campaign.id)},[B(q(e.title)+" ",1),E("span",null,"( "+q(e.campaign.subject)+" )",1)],8,pe),E("div",me,[I(u,{link:"",size:"small",class:"font-medium",onClick:t=>c.showEmailReport(e.campaign.id)},{default:T(()=>[E("span",he,[I(o,{"icon-name":"envelope"})]),B(" "+q(t.$t("Show Individual Emails")),1)],void 0),_:1},8,["onClick"])])]),E("div",fe,[E("div",be,[E("div",ge,q(t.$t("Quick Stats")),1),E("ul",ve,[E("li",{onClick:t=>c.showEmailReport(e.campaign.id),class:"fcrm_quick_stat--sent is-link"},[E("span",ke,[I(o,{"icon-name":"send-mail"})]),E("p",null,[E("span",we,q(e.campaign.stats.sent||"--"),1),B(" "+q(t.$t("Sent")),1)])],8,ye),E("li",{title:e.campaign.stats.views,class:"fcrm_quick_stat--opened"},[E("span",Ce,[I(o,{"icon-name":"envelopeOpen"})]),E("p",null,[E("span",$e,q(c.getPercent(e.campaign.stats.views,e.campaign.stats.sent)),1),B(" "+q(t.$t("Opened")),1)])],8,Se),E("li",{onClick:t=>c.showLinkReport(e.campaign.id),title:e.campaign.stats.clicks,class:"fcrm_quick_stat--click is-link"},[E("span",Ae,[I(o,{"icon-name":"click"})]),E("p",null,[E("span",qe,q(c.getPercent(e.campaign.stats.clicks,e.campaign.stats.sent)),1),B(" "+q(t.$t("Clicked")),1)])],8,xe),E("li",{title:e.campaign.stats.unsubscribers,class:"fcrm_quick_stat--unsubscribe"},[E("span",Ee,[I(o,{"icon-name":"unsubscribe"})]),E("p",null,[E("span",Pe,q(c.getPercent(e.campaign.stats.unsubscribers,e.campaign.stats.sent)),1),B(" "+q(t.$t("Unsubscribed")),1)])],8,Fe),e.campaign.stats.revenue?($(),x("li",Re,[E("span",Ie,[I(o,{"icon-name":"wallet"})]),E("p",null,[E("span",Be,q(e.campaign.stats.revenue.total),1),B(" "+q(e.campaign.stats.revenue.label),1)])])):F("",!0)])])])],64)):F("",!0)]))),128))])):($(),x("h3",Te,q(t.$t("Sorry, No emails found in this automation")),1)),I(_,{"close-on-click-modal":!1,onClosed:n[0]||(n[0]=e=>r.show_email_report_id=""),title:t.$t("View Campaign Emails"),width:"60%","append-to-body":!0,modelValue:r.show_email_report,"onUpdate:modelValue":n[1]||(n[1]=e=>r.show_email_report=e)},{default:T(()=>[r.show_email_report_id?($(),A(d,{key:0,campaign_id:r.show_email_report_id},null,8,["campaign_id"])):F("",!0)],void 0),_:1},8,["title","modelValue"]),I(_,{"close-on-click-modal":!1,onClosed:n[2]||(n[2]=e=>r.link_click_id=""),title:t.$t("Link Metrics"),width:"60%","append-to-body":!0,modelValue:r.link_clicks_modal,"onUpdate:modelValue":n[3]||(n[3]=e=>r.link_clicks_modal=e)},{default:T(()=>[r.link_click_id?($(),A(p,{key:0,campaign_id:r.link_click_id,hide_title:!0},null,8,["campaign_id"])):F("",!0)],void 0),_:1},8,["title","modelValue"])])),[[m,r.fetching]])}]]),SyncNewSteps:W({name:"SyncNewSteps",components:{GenericPromo:X},props:["funnel_id"],emits:["reload"],data:()=>({schedule_at:"",syncableCount:0,loading:!1,processing:!1}),methods:{getCount(){this.loading=!0,this.$get(`funnels/${this.funnel_id}/syncable-counts`).then(e=>{this.syncableCount=e.syncable_count}).catch(e=>{this.handleError(e)}).finally(()=>{this.loading=!1})},syncSteps(){this.has_campaign_pro?(this.processing=!0,this.$post(`funnels/${this.funnel_id}/sync-new-steps`).then(e=>{this.$notify.success(this.$t("Automation_Sync_Note_6")),this.syncableCount=0,this.$emit("reload")}).catch(e=>{this.handleError(e)}).finally(()=>{this.processing=!1})):this.$notify.error(this.$t("Automation_Sync_Note_5"))}},mounted(){this.getCount()}},[["render",function(t,a,n,i,l,r){const c=s,o=S("generic-promo"),u=e;return C(($(),x("div",Ve,[E("p",je,q(t.$t("Automation_Sync_Note_1")),1),l.syncableCount?($(),x("div",Le,[E("h3",null,q(t.$t("There are around %d contacts that can be resumed to your newly added automation steps",l.syncableCount)),1),t.has_campaign_pro?($(),x(P,{key:0},[C(($(),A(c,{disabled:l.processing,onClick:r.syncSteps,type:"primary",size:"small"},{default:T(()=>[B(q(t.$t("Automation_Sync_Note_2")),1)],void 0),_:1},8,["disabled","onClick"])),[[u,l.processing]]),E("p",null,q(t.$t("Automation_Sync_Note_2")),1)],64)):($(),A(o,{key:1}))])):l.loading?($(),x("div",De,[E("h3",null,q(t.$t("Loading.....")),1)])):($(),x("div",Ne,[E("h3",null,q(t.$t("Automation_Sync_Note_4")),1)]))])),[[u,l.loading]])}]]),StepPicker:ee,InfoFilled:i,Close:n},data(){return{direction:"rtl",ArrowRightBold:L(w),funnel:{},subscribers:[],loading:!1,pagination:{total:0,per_page:10,current_page:1},sequences:[],stats:{metrics:[],total_revenue:0,revenue_currency:"USD"},visualization_type:"chart",search:"",deleting:!1,updating:!1,selectedSubscribers:[],selected_status:"all",selected_sequence:"",funnel_statuses:{all:this.$t("All"),active:this.$t("Active"),completed:this.$t("Completed"),cancelled:this.$t("Cancelled"),pending:this.$t("Pending")},loading_first:!0,syncSteps:!1,showSearchBar:!1,stepPickerRow:null,current_mode:"system"===G.getCurrentTheme()?G.getSystemTheme():G.getCurrentTheme()}},methods:{fetchSubscribers(){this.loading=!0;let e=["funnel","sequences"];this.loading_first||(e=[]),this.$get(`funnels/${this.funnel_id}/subscribers`,{per_page:this.pagination.per_page,page:this.pagination.current_page,with:e,search:this.search,status:this.selected_status,sequence_id:this.selected_sequence}).then(e=>{this.subscribers=e.funnel_subscribers.data,this.pagination.total=e.funnel_subscribers.total,this.loading_first&&(this.funnel=e.funnel,this.sequences=e.sequences,this.loading_first=!1)}).catch(e=>{this.handleError(e)}).finally(()=>{this.loading=!1})},fetchReport(){this.$get(`funnels/${this.funnel_id}/report`).then(e=>{this.stats=e.stats}).catch(e=>{this.handleError(e)}).finally(()=>{})},rowStatusClass:({row:e})=>"fc_table_row_"+e.status,removeFromFunnel(e){this.deleting=!0,this.$del(`funnels/${this.funnel_id}/subscribers`,{subscriber_ids:[e]}).then(e=>{this.$notify.success(e.message),this.fetchSubscribers()}).catch(e=>{this.handleError(e)}).finally(()=>{this.deleting=!1})},bulkRemove(){if(!this.selectedSubscribers.length)return this.$notify.error(this.$t("Please select subscribers first")),!1;this.deleting=!0,this.$del(`funnels/${this.funnel_id}/subscribers`,{subscriber_ids:this.selectedSubscribers}).then(e=>{this.$notify.success(e.message),this.selectedSubscribers=[],this.fetchSubscribers()}).catch(e=>{this.handleError(e)}).finally(()=>{this.deleting=!1})},changeFunnelSubscriptionStatus(e,t){this.updating=!0,this.$put(`funnels/${this.funnel_id}/subscribers/${e}/status`,{status:t}).then(e=>{this.$notify.success(e.message),this.fetchSubscribers()}).catch(e=>{this.handleError(e)}).finally(()=>{this.updating=!1})},openStepPicker(e){this.stepPickerRow=e},onSelection(e){const t=[];this.each(e,e=>{t.push(e.subscriber_id)}),this.selectedSubscribers=t},onSearchBarAppendClick(){this.showSearchBar?this.searchFromFirstPage():(this.showSearchBar=!0,this.$nextTick(()=>{var e;return null==(e=this.$refs.notesSearchInput)?void 0:e.focus()}))},searchFromFirstPage(){this.pagination&&(this.pagination.current_page=1),this.fetchSubscribers()},clearSelection(){this.$refs.subscribersTable&&this.$refs.subscribersTable.clearSelection(),this.selectedSubscribers=[]},onThemeChanged(e){var t;this.current_mode=(null==(t=e.detail)?void 0:t.effective)||("system"===G.getCurrentTheme()?G.getSystemTheme():G.getCurrentTheme())}},mounted(){window.fcAdmin&&window.fcAdmin.is_rtl&&(this.direction="ltr"),window.addEventListener(Y,this.onThemeChanged),this.fetchSubscribers(),this.fetchReport(),this.changeTitle(this.$t("Funnel Report"))},beforeUnmount(){window.removeEventListener(Y,this.onThemeChanged)}},[["render",function(t,a,n,i,w,L){const D=l,N=r,z=s,O=c,U=o,M=u,W=S("funnel-chart"),H=S("funnel-text-report"),Y=S("funnel-emails"),G=S("Icons"),K=g,Z=y,J=v,Q=S("icons"),X=_,ee=S("individual-progress"),te=S("InfoFilled"),se=p,ae=S("contact-card"),ne=m,ie=S("Badge"),le=b,re=S("confirm"),ce=f,oe=h,ue=d,de=S("pagination-bar"),_e=S("data-table"),pe=S("Close"),me=S("floating-bulk-action-shell"),he=S("sync-new-steps"),fe=k,be=S("step-picker"),ge=e;return $(),x("div",ze,[E("div",Oe,[E("div",Ue,[I(N,{class:"fcrm_funnel_report_breadcrumb","separator-icon":w.ArrowRightBold},{default:T(()=>[I(D,{to:{name:"funnels"}},{default:T(()=>[B(q(t.$t("Automation Funnels")),1)],void 0,!0),_:1}),I(D,{to:{name:"edit_funnel",params:{funnel_id:n.funnel_id}}},{default:T(()=>[B(q(w.funnel.title),1)],void 0,!0),_:1},8,["to"]),I(D,null,{default:T(()=>[B(q(t.$t("Subscribers")),1)],void 0,!0),_:1})],void 0),_:1},8,["separator-icon"])]),E("div",Me,[I(O,{class:"item","open-delay":500,effect:"dark",content:t.$t("Funnel_Step_Alert"),placement:"left"},{default:T(()=>[I(z,{onClick:a[0]||(a[0]=e=>w.syncSteps=!0),type:"default"},{default:T(()=>[B(q(t.$t("Re-apply New Steps")),1)],void 0,!0),_:1})],void 0),_:1},8,["content"])])]),w.stats.metrics.length?($(),x("div",We,[E("div",He,[I(M,{modelValue:w.visualization_type,"onUpdate:modelValue":a[1]||(a[1]=e=>w.visualization_type=e),class:"fcrm_global_radio_group"},{default:T(()=>[I(U,{value:"chart"},{default:T(()=>[B(q(t.$t("Chart Report")),1)],void 0,!0),_:1}),I(U,{value:"text"},{default:T(()=>[B(q(t.$t("Step Report")),1)],void 0,!0),_:1}),I(U,{value:"emails"},{default:T(()=>[B(q(t.$t("Emails Analytics")),1)],void 0,!0),_:1})],void 0),_:1},8,["modelValue"])]),E("div",Ye,["chart"==w.visualization_type?($(),A(W,{key:0,stats:w.stats,funnel_id:n.funnel_id},null,8,["stats","funnel_id"])):"text"==w.visualization_type?($(),A(H,{key:1,stats:w.stats,funnel:w.funnel},null,8,["stats","funnel"])):"emails"==w.visualization_type?($(),A(Y,{key:2,funnel_id:n.funnel_id},null,8,["funnel_id"])):F("",!0),w.stats.total_revenue?($(),x("h3",Ge,[B(q(t.$t("Fun_Total_Rftf"))+" ",1),E("span",Ke,q(w.stats.revenue_currency)+" "+q(w.stats.total_revenue_formatted),1)])):F("",!0)])])):F("",!0),I(_e,{"has-selection":!1},{"header-left":T(()=>[I(M,{modelValue:w.selected_status,"onUpdate:modelValue":a[2]||(a[2]=e=>w.selected_status=e),onChange:a[3]||(a[3]=e=>L.fetchSubscribers()),class:"fcrm_global_radio_group"},{default:T(()=>[($(!0),x(P,null,R(w.funnel_statuses,(e,t)=>($(),A(U,{key:t,value:t},{default:T(()=>[B(q(e),1)],void 0,!0),_:2},1032,["value"]))),128))],void 0,!0),_:1},8,["modelValue"])]),"header-actions":T(()=>[E("div",{class:V(["fcrm_notes_search_bar",{"fcrm_notes_search_bar-is_expanded":w.showSearchBar}])},[I(K,{ref:"notesSearchInput",onKeyup:j(L.searchFromFirstPage,["enter"]),clearable:"",size:"small",onClear:a[4]||(a[4]=e=>L.searchFromFirstPage()),placeholder:t.$t("Search"),modelValue:w.search,"onUpdate:modelValue":a[5]||(a[5]=e=>w.search=e),class:V(["fcrm_notes_search_input",{"fcrm_notes_search_input-is_expanded":w.showSearchBar}])},{append:T(()=>[I(z,{class:"small only-icon-btn",onClick:L.onSearchBarAppendClick},{default:T(()=>[E("span",Ze,[I(G,{"icon-name":"search"})])],void 0,!0),_:1},8,["onClick"])]),_:1},8,["onKeyup","placeholder","modelValue","class"])],2),I(J,{clearable:"",onChange:a[6]||(a[6]=e=>L.fetchSubscribers()),size:"small",title:t.$t("Sequence"),placeholder:t.$t("All Sequences"),modelValue:w.selected_sequence,"onUpdate:modelValue":a[7]||(a[7]=e=>w.selected_sequence=e)},{default:T(()=>[($(!0),x(P,null,R(w.sequences,e=>($(),A(Z,{key:e.id,value:e.id,label:e.title},null,8,["value","label"]))),128))],void 0,!0),_:1},8,["title","placeholder","modelValue"])]),table:T(()=>[C(($(),A(ue,{ref:"subscribersTable",border:"",stripe:"",onSelectionChange:L.onSelection,data:w.subscribers,"row-class-name":L.rowStatusClass},{empty:T(()=>[E("div",Je,[I(Q,{"icon-name":"common-empty-state"}),E("div",Qe,[E("span",null,q(t.$t("No Data Available")),1)])])]),default:T(()=>[I(X,{type:"selection"}),I(X,{type:"expand"},{default:T(e=>[I(ee,{funnel:w.funnel,funnel_subscriber:e.row,sequences:w.sequences},null,8,["funnel","funnel_subscriber","sequences"])]),_:1}),I(X,{label:t.$t("Contact"),width:"250"},{default:T(e=>[e.row.subscriber?($(),A(ae,{key:0,trigger_type:"click",display_key:"full",subscriber:e.row.subscriber},{after_name:T(()=>["fcrm_manual_attach"==e.row.source_trigger_name?($(),A(O,{key:0,class:"item",effect:"dark",content:t.$t("ProfileAutomations.Contact_Added_manually_to_Automation"),placement:"top-start"},{default:T(()=>[I(se,null,{default:T(()=>[I(te)],void 0,!0),_:1})],void 0,!0),_:1},8,["content"])):F("",!0)]),_:2},1032,["subscriber"])):($(),x("span",Xe,q(t.$t("No Subscriber Found")),1))]),_:1},8,["label"]),I(X,{label:t.$t("Status")},{default:T(e=>[I(ie,{type:e.row.status,plain:!0},{icon:T(()=>[E("span",et,[I(ne,{placement:"top-start","min-width":"200",trigger:"hover",content:t.$t("Current status of the subscriber in the funnel")},{reference:T(()=>[I(se,null,{default:T(()=>[I(te)],void 0,!0),_:1})]),_:1},8,["content"])])]),_:1},8,["type"])]),_:1},8,["label"]),I(X,{label:t.$t("Latest Action")},{default:T(e=>[e.row.last_sequence?($(),x("span",tt,q(e.row.last_sequence.title),1)):"pending"==e.row.status?($(),x("span",st,q(t.$t("Fun_Waiting_fdoc")),1)):F("",!0)]),_:1},8,["label"]),I(X,{label:t.$t("Next Step")},{default:T(e=>["completed"!=e.row.status?($(),x(P,{key:0},[e.row.next_sequence_item?($(),x("span",at,q(e.row.next_sequence_item.title),1)):F("",!0),"active"==e.row.status?($(),x("span",{key:1,title:e.row.next_execution_time}," - ("+q(t.nsHumanDiffTime(e.row.next_execution_time))+") ",9,nt)):F("",!0)],64)):($(),A(ie,{key:1,type:"completed",plain:!0},{icon:T(()=>[E("span",it,[I(se,null,{default:T(()=>[I(te)],void 0,!0),_:1})])]),_:1}))]),_:1},8,["label"]),I(X,{width:"150",label:t.$t("Last Executed At")},{default:T(e=>[E("span",{title:e.row.last_executed_time},q(t.nsHumanDiffTime(e.row.last_executed_time)),9,lt)]),_:1},8,["label"]),I(X,{width:"150",label:t.$t("Created At")},{default:T(e=>[E("span",{title:e.row.created_at},q(t.nsHumanDiffTime(e.row.created_at)),9,rt)]),_:1},8,["label"]),I(X,{width:"100",align:"center","class-name":"fcrm_table_actions_cell"},{default:T(e=>[I(oe,{trigger:"click",placement:"bottom-end"},{dropdown:T(()=>[I(ce,null,{default:T(()=>["active"==e.row.status||"waiting"==e.row.status?($(),A(le,{key:0,class:"fc_dropdown_action",onClick:t=>L.openStepPicker(e.row)},{default:T(()=>[E("span",ot,[E("span",ut,[I(G,{"icon-name":"play"})]),B(" "+q(t.$t("Advance Step")),1)])],void 0,!0),_:1},8,["onClick"])):F("",!0),"cancelled"==e.row.status?($(),A(le,{key:1,class:"fc_dropdown_action",onClick:t=>L.changeFunnelSubscriptionStatus(e.row.subscriber_id,"active")},{default:T(()=>[E("span",dt,[E("span",_t,[I(G,{"icon-name":"play"})]),B(" "+q(t.$t("Resume")),1)])],void 0,!0),_:1},8,["onClick"])):F("",!0),"active"==e.row.status?($(),A(le,{key:2,class:"fc_dropdown_action",onClick:t=>L.changeFunnelSubscriptionStatus(e.row.subscriber_id,"cancelled")},{default:T(()=>[E("span",pt,[E("span",mt,[I(G,{"icon-name":"close"})]),B(" "+q(t.$t("Cancel")),1)])],void 0,!0),_:1},8,["onClick"])):F("",!0),I(le,{class:"fc_dropdown_action"},{default:T(()=>[C(($(),A(re,{onYes:t=>L.removeFromFunnel(e.row.subscriber_id)},{reference:T(()=>[E("span",ht,[E("span",ft,[I(G,{"icon-name":"delete"})]),B(" "+q(t.$t("Delete")),1)])]),_:1},8,["onYes"])),[[ge,w.deleting]])],void 0,!0),_:2},1024)],void 0,!0),_:2},1024)]),default:T(()=>[E("span",ct,[I(G,{"icon-name":"more_actions"})])],void 0,!0),_:2},1024)]),_:1})],void 0,!0),_:1},8,["onSelectionChange","data","row-class-name"])),[[ge,w.loading]])]),pagination:T(()=>[I(de,{pagination:w.pagination,onFetch:L.fetchSubscribers},null,8,["pagination","onFetch"])]),_:1}),I(me,{visible:!!w.selectedSubscribers.length,"theme-mode":w.current_mode,"custom-layout":!0},{default:T(()=>[E("div",bt,[E("div",gt,[I(z,{link:"","aria-label":t.$t("Deselect"),onClick:L.clearSelection},{default:T(()=>[I(se,null,{default:T(()=>[I(pe)],void 0,!0),_:1})],void 0,!0),_:1},8,["aria-label","onClick"]),E("span",vt,[E("strong",null,q(w.selectedSubscribers.length),1),B(" "+q(t.$t("selected")),1)]),a[13]||(a[13]=E("div",{class:"fcrm_bulk_divider"},null,-1)),C(($(),A(re,{onYes:a[8]||(a[8]=e=>L.bulkRemove())},{reference:T(()=>[C(($(),A(z,{type:"danger",size:"small",plain:""},{default:T(()=>[E("span",yt,[I(G,{"icon-name":"delete"})]),B(" "+q(t.$t("Delete Selected")),1)],void 0,!0),_:1})),[[ge,w.deleting]])]),_:1})),[[ge,w.deleting]])])])],void 0),_:1},8,["visible","theme-mode"]),I(fe,{direction:w.direction,class:"fc_company_info_drawer","append-to-body":!0,size:t.globalDrawerSize,title:t.$t("Sync_New_Steps"),modelValue:w.syncSteps,"onUpdate:modelValue":a[10]||(a[10]=e=>w.syncSteps=e)},{default:T(()=>[E("div",kt,[w.syncSteps?($(),A(he,{key:0,onReload:a[9]||(a[9]=e=>{L.fetchReport(),L.fetchSubscribers(),w.syncSteps=!1}),funnel_id:n.funnel_id},null,8,["funnel_id"])):F("",!0)])],void 0),_:1},8,["direction","size","title","modelValue"]),w.stepPickerRow?($(),A(be,{key:1,"funnel-subscriber":w.stepPickerRow,sequences:w.sequences,"funnel-id":n.funnel_id,onClose:a[11]||(a[11]=e=>w.stepPickerRow=null),onAdvanced:a[12]||(a[12]=e=>{w.stepPickerRow=null,L.fetchSubscribers()})},null,8,["funnel-subscriber","sequences","funnel-id"])):F("",!0)])}]]);export{wt as default}; diff --git a/wp-content/plugins/fluent-crm/assets/admin/Modules/Funnels/Funnels.js b/wp-content/plugins/fluent-crm/assets/admin/Modules/Funnels/Funnels.js new file mode 100644 index 0000000..b28282f --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/admin/Modules/Funnels/Funnels.js @@ -0,0 +1 @@ +import{S as e,R as t,_ as s}from"../../../fc-bits.js?ver=3.1.8";import{$ as a,ae as l,an as i,aU as n,ax as o,aV as r,aW as c,E as d,k as _,e as u,aB as p,g as h,a6 as m,aj as g,aL as f,aK as b,ay as y,B as v,i as C,aG as w,az as k,aX as F,a4 as $,J as L,ac as V,a7 as S,u as T,c as A,L as P,D,j as x,h as q,aI as M,aQ as I,aP as B,aH as R}from"../../../vendor-element-plus.js?ver=3.1.8";import{aQ as j,W as H,Y as E,a5 as U,X as O,ab as Z,ax as z,Z as N,aa as W,J as K,az as Q,a0 as Y,a9 as G,a8 as J,a7 as X,a6 as ee,$ as te,_ as se,bU as ae,b2 as le,bS as ie,bV as ne,bT as oe,bW as re}from"../../../vendor.js?ver=3.1.8";import{P as ce}from"../../../ProBadge.js?ver=3.1.8";import{P as de}from"../../../PromoCard.js?ver=3.1.8";import{_ as _e,I as ue,a as pe,T as he}from"../../../fc-bits-ui.js?ver=3.1.8";import{C as me}from"../../../Confirm.js?ver=3.1.8";import{P as ge}from"../../../PaginationBar.js?ver=3.1.8";import{I as fe}from"../../../InlineDoc.js?ver=3.1.8";import{F as be}from"../../../Filterer.js?ver=3.1.8";import{a as ye}from"../../../data_config.js?ver=3.1.8";import{P as ve}from"../../../PageHeader.js?ver=3.1.8";import{F as Ce}from"../../../FloatingBulkActionShell.js?ver=3.1.8";import{B as we}from"../../../Badge.js?ver=3.1.8";const ke={key:0,class:"el-dialog__title"},Fe={key:1,class:"el-dialog__title"},$e={key:0,class:"fcrm_trigger_selection_wrapper"},Le={class:"fcrm_trigger_selection_inner"},Ve={class:"fcrm_trigger_selection_sidebar"},Se={class:"fc_trigger_icon"},Te=["innerHTML"],Ae={class:"fcrm_trigger_label"},Pe={key:0,class:"fcrm_trigger_selection_sidebar--footer"},De={class:"fcrm_trigger_selection_main"},xe={class:"fcrm_trigger_selection_internal_label_wrapper"},qe={class:"icon"},Me={class:"fcrm_trigger_selection_header"},Ie={class:"fcrm_trigger_selection_header_title"},Be={key:0,class:"fcrm_trigger_selection_list"},Re=["onClick"],je={class:"fcrm_trigger_selection_item_icon"},He=["innerHTML"],Ee={key:1},Ue={class:"fcrm_trigger_selection_item_content"},Oe={class:"fcrm_trigger_selection_item_title"},Ze={class:"fcrm_trigger_selection_item_description"},ze=["href"],Ne={key:1,class:"fcrm_trigger_prebuild_template_wrapper"},We={key:0,class:"fcrm_trigger_prebuild_template_list--section"},Ke={class:"fcrm_trigger_prebuild_template_list"},Qe={class:"fcrm_trigger_prebuild_template_item_icon"},Ye={class:"fcrm_trigger_prebuild_template_item_title"},Ge={class:"fcrm_trigger_prebuild_template_item_description"},Je={class:"fcrm_trigger_prebuild_template_item_overlay"},Xe={class:"icon"},et={class:"icon"},tt=["href"],st={key:1,class:"fcrm_promo_block"},at={key:0,class:"dialog-footer"};const lt=_e({name:"CreateFunnelModal",components:{Icons:ue,PromoCard:de,ProBadge:ce,Back:i,CirclePlus:l,Finished:a},props:["visible","triggers"],emits:["close"],data(){var e,t;return{saving:!1,funnel:{title:"",trigger_name:""},templates:[],templatesLoading:!1,showCreateNewAutomations:!0,dialogVisible:this.visible,selected_category:(null==(t=null==(e=window.fcAdmin)?void 0:e.addons)?void 0:t.fluentcampaign)?this.$t("CRM"):"__all__",allCategoryKey:"__all__",allowed_categories:[],searchTrigger:"",showProDialog:!1}},watch:{visible(e){this.dialogVisible=e},dialogVisible(){this.$emit("close")}},computed:{has_campaign_pro(){var e,t;return!!(null==(t=null==(e=window.fcAdmin)?void 0:e.addons)?void 0:t.fluentcampaign)},effectiveSelectedCategory(){return(this.searchTrigger||"").trim()?this.allCategoryKey:this.selected_category},funnel_categories(){const e=[];return this.each(this.triggers,t=>{-1===e.indexOf(t.category)&&e.push(t.category)}),e.sort()},funnelTriggers(){const e=(this.searchTrigger||"").trim().toLowerCase(),t=!!e,s=[this.$t("CRM")],a={};this.each(this.triggers,(l,i)=>{const n=this.effectiveSelectedCategory===this.allCategoryKey||l.category===this.effectiveSelectedCategory;if(t||n){if(e){const t=(l.description||"").replace(/<[^>]*>/g," ");if(!`${l.label||""} ${t} ${l.category||""} ${i||""}`.toLowerCase().includes(e))return}s.includes(l.category)||s.push(l.category),a[i]=l}});const l={};if(s.forEach(e=>{this.each(a,(t,s)=>{t.category===e&&(l[s]=t)})}),!this.has_campaign_pro){const e={},t={};return this.each(l,(s,a)=>{s.disabled?t[a]=s:e[a]=s}),{...e,...t}}return l}},methods:{funnelActiveHandle(e){this.funnel.trigger_name&&this.funnel.trigger_name==e?this.funnel.trigger_name="":this.funnel.trigger_name=e},saveFunnel(){this.saving=!0,this.$post("funnels",{funnel:this.funnel}).then(e=>{this.$notify.success(e.message),this.$router.push({name:"edit_funnel",params:{funnel_id:e.funnel.id},query:{is_new:"yes"}})}).catch(e=>{this.handleError(e)}).finally(()=>{this.saving=!1})},getCatIcon(e){e=e.replaceAll(" ","").toLowerCase();const t=this.appVars.funnel_cat_icons[e];return t?"string"==typeof t?t.startsWith("<")?t:'':t.svg?t.svg:t.icon?'':"":""},getElIcon:e=>n[e]||a,createFromTemplate(e){this.checkImportable(e)&&this.$post("funnels/create-from-template",{template:e}).then(e=>{this.$notify.success(e.message),this.$router.push({name:"edit_funnel",params:{funnel_id:e.funnel.id},query:{is_template:"yes"}})}).catch(e=>{this.handleError(e)})},checkImportable(e){if(this.visibleProRibon(e))return this.$notify.error(this.$t("This template requires FluentCRM Pro version. Please upgrade to Pro.")),!1;if(e.dependencies&&e.dependencies.length>0){const t=e.dependencies.filter(e=>"fluentcrm_pro"!==e&&!this.allowed_categories.includes(e));if(t.length>0){const e=t.map(e=>this.getDependencyLabel(e));return this.$notify.error({title:this.$t("Missing Dependencies"),message:this.$t("This template requires the following dependencies to be activated: ")+e.join(", "),duration:6e3}),!1}}return!0},getTemplates(){this.templatesLoading=!0,this.$get("funnels/templates").then(e=>{this.templates=e.all,this.allowed_categories=e.cats}).catch(e=>{this.handleError(e)}).finally(()=>{this.templatesLoading=!1})},getTriggerIcon:e=>({woocommerce_order_status_completed:"fc-icon-woo_order_complete",woocommerce_order_status_processing:"fc-icon-woo_new_order",woocommerce_order_status_refunded:"fc-icon-woo_refund",woocommerce_order_status_changed:"fc-icon-woo",woocommerce_subscription_status_active:"fc-icon-woo_order_complete",woocommerce_subscription_renewal_payment_complete:"fc-icon-woo_order_complete",woocommerce_subscription_renewal_payment_failed:"fc-icon-woo_refund",wishlistmember_add_user_levels:"fc-icon-wishlist",tutor_after_enrolled:"fc-icon-tutor_lms_enrollment_course",tutor_course_complete_after:"fc-icon-tutor_lms_complete_course",tutor_lesson_completed_after:"fc-icon-tutor_lms_complete_course",rcp_membership_post_activate:"fc-icon-rcp_membership_level",rcp_transition_membership_status_expired:"fc-icon-rcp_membership_cancle",rcp_membership_post_cancel:"fc-icon-rcp_membership_cancle",pmpro_after_change_membership_level:"fc-icon-paid_membership_pro_user_level",pmpro_membership_post_membership_expiry:"fc-icon-membership_level_ex_pmp","mepr-account-is-active":"fc-icon-memberpress_membership","mepr-event-transaction-expired":"fc-icon-circle-close",llms_user_enrolled_in_course:"fc-icon-lifter_lms_course_enrollment",lifterlms_course_completed:"fc-icon-lifter_lms_complete_course",llms_user_added_to_membership_level:"fc-icon-lifter_lms_membership",lifterlms_lesson_completed:"fc-icon-lifter_lms_complete_lession-t2",learndash_update_course_access:"fc-icon-learndash_enroll_course",learndash_lesson_completed:"fc-icon-learndash_complete_lesson",learndash_topic_completed:"fc-icon-learndash_complete_topic",learndash_course_completed:"fc-icon-learndash_complete_course",ld_added_group_access:"fc-icon-learndash_course_group",simulated_learndash_update_course_removed:"fc-icon-learndash_enroll_course",fc_ab_cart_simulation_woo:"fc-icon-woo",fluentcrm_contact_birthday:"fc-icon-present",user_register:"fc-icon-wp_new_user_signup",fluentform_submission_inserted:"fc-icon-fluentforms",fluentcrm_contact_added_to_lists:"fc-icon-list_applied_2",edd_update_payment_status:"fc-icon-edd_new_order_success",edd_recurring_add_subscription_payment:"fc-icon-edd_new_order_success",edd_subscription_status_change:"fc-icon-circle-close",affwp_set_affiliate_status:"fc-icon-trigger",fluent_surecart_purchase_created_wrap:"fc-icon-shopping-cart-full",fluent_surecart_purchase_refund_wrap:"fc-icon-sold-out"}[e]||"fc-icon-trigger"),visibleProRibon(e){const t=window.fcAdmin.addons.fluentcampaign,s=e.dependencies&&e.dependencies.includes("fluentcrm_pro");return!t&&s},getDependencyLabel:e=>({fluentforms:"Fluent Forms",memberpress:"MemberPress","fluent-boards":"Fluent Boards","fluent-support":"Fluent Support","fluent-booking":"Fluent Booking",woocommerce:"WooCommerce",wcs:"WooCommerce Subscriptions",edd:"Easy Digital Downloads",lifterlms:"LifterLMS",tutor:"Tutor LMS",learndash:"LearnDash",surecart:"SureCart",woo_abandon_carts:"WooCommerce Abandoned Cart",fluentcrm_pro:"FluentCRM Pro"}[e]||e.replace(/[-_]/g," ").replace(/\b\w/g,e=>e.toUpperCase()))},mounted(){this.getTemplates()}},[["render",function(e,t,s,a,l,i){const n=j("Icons"),m=c,g=j("Finished"),f=d,b=r,y=_,v=u,C=j("ProBadge"),w=o,k=p,F=j("PromoCard"),$=h;return H(),E($,{modelValue:l.dialogVisible,"onUpdate:modelValue":t[8]||(t[8]=e=>l.dialogVisible=e),"close-on-click-modal":!1,"append-to-body":!0,"modal-class":"fcrm_trigger_modal_wrapper",width:"75%"},{header:U(()=>[l.showCreateNewAutomations?(H(),O("span",ke,W(e.$t("_Cr_Create_aAF")),1)):(H(),O("span",Fe,[N("span",{class:"icon cursor_pointer",onClick:t[0]||(t[0]=e=>l.showCreateNewAutomations=!0)},[Z(n,{"icon-name":"arrow-left"})]),G(" "+W(e.$t("Popular pre-built funnel templates")),1)]))]),footer:U(()=>{var a;return[l.showCreateNewAutomations&&l.funnel.trigger_name&&!(null==(a=s.triggers[l.funnel.trigger_name])?void 0:a.disabled)?(H(),O("span",at,[Z(y,{type:"primary",onClick:t[7]||(t[7]=e=>i.saveFunnel())},{default:U(()=>[G(W(e.$t("Continue")),1)],void 0,!0),_:1})])):J("",!0)]}),default:U(()=>[l.showCreateNewAutomations?(H(),O("div",$e,[Z(w,{onSubmit:z(i.saveFunnel,["prevent"]),data:l.funnel,"label-position":"top"},{default:U(()=>[N("div",Le,[N("div",Ve,[Z(b,{"background-color":"#f2f2f2","text-color":"#1e1f21","active-text-color":"#ffd04b",onSelect:t[1]||(t[1]=e=>{l.selected_category=e,l.funnel.trigger_name="",l.searchTrigger=""}),class:"fc_trigger_selectors","default-active":i.effectiveSelectedCategory},{default:U(()=>[Z(m,{index:l.allCategoryKey},{default:U(()=>[t[9]||(t[9]=N("span",{class:"fc_trigger_icon"},[N("svg",{width:"20",height:"20",viewBox:"0 0 20 20",fill:"none",xmlns:"http://www.w3.org/2000/svg"},[N("path",{d:"M6.0625 2.875C6.48109 2.875 6.89558 2.95745 7.28231 3.11763C7.66903 3.27782 8.02042 3.51261 8.31641 3.8086C8.61239 4.10458 8.84718 4.45597 9.00737 4.8427C9.16755 5.22942 9.25 5.64391 9.25 6.0625V9.25H6.0625C5.21712 9.25 4.40637 8.91418 3.8086 8.3164C3.21083 7.71863 2.875 6.90788 2.875 6.0625C2.875 5.21712 3.21083 4.40637 3.8086 3.8086C4.40637 3.21082 5.21712 2.875 6.0625 2.875V2.875ZM7.75 7.75V6.0625C7.75 5.72874 7.65103 5.40248 7.46561 5.12498C7.28018 4.84747 7.01663 4.63118 6.70828 4.50345C6.39993 4.37573 6.06063 4.34231 5.73329 4.40742C5.40594 4.47254 5.10526 4.63326 4.86926 4.86926C4.63326 5.10526 4.47254 5.40594 4.40743 5.73328C4.34231 6.06063 4.37573 6.39993 4.50345 6.70828C4.63118 7.01663 4.84747 7.28018 5.12498 7.46561C5.40248 7.65103 5.72875 7.75 6.0625 7.75H7.75ZM6.0625 10.75H9.25V13.9375C9.25 14.5679 9.06306 15.1842 8.71281 15.7084C8.36256 16.2326 7.86474 16.6411 7.28231 16.8824C6.69987 17.1236 6.05897 17.1867 5.44065 17.0638C4.82234 16.9408 4.25438 16.6372 3.8086 16.1914C3.36282 15.7456 3.05924 15.1777 2.93625 14.5594C2.81326 13.941 2.87638 13.3001 3.11764 12.7177C3.35889 12.1353 3.76744 11.6374 4.29162 11.2872C4.8158 10.9369 5.43207 10.75 6.0625 10.75V10.75ZM6.0625 12.25C5.72875 12.25 5.40248 12.349 5.12498 12.5344C4.84747 12.7198 4.63118 12.9834 4.50345 13.2917C4.37573 13.6001 4.34231 13.9394 4.40743 14.2667C4.47254 14.5941 4.63326 14.8947 4.86926 15.1307C5.10526 15.3667 5.40594 15.5275 5.73329 15.5926C6.06063 15.6577 6.39993 15.6243 6.70828 15.4965C7.01663 15.3688 7.28018 15.1525 7.46561 14.875C7.65103 14.5975 7.75 14.2713 7.75 13.9375V12.25H6.0625ZM13.9375 2.875C14.7829 2.875 15.5936 3.21082 16.1914 3.8086C16.7892 4.40637 17.125 5.21712 17.125 6.0625C17.125 6.90788 16.7892 7.71863 16.1914 8.3164C15.5936 8.91418 14.7829 9.25 13.9375 9.25H10.75V6.0625C10.75 5.21712 11.0858 4.40637 11.6836 3.8086C12.2814 3.21082 13.0921 2.875 13.9375 2.875V2.875ZM13.9375 7.75C14.2713 7.75 14.5975 7.65103 14.875 7.46561C15.1525 7.28018 15.3688 7.01663 15.4965 6.70828C15.6243 6.39993 15.6577 6.06063 15.5926 5.73328C15.5275 5.40594 15.3667 5.10526 15.1307 4.86926C14.8947 4.63326 14.5941 4.47254 14.2667 4.40742C13.9394 4.34231 13.6001 4.37573 13.2917 4.50345C12.9834 4.63118 12.7198 4.84747 12.5344 5.12498C12.349 5.40248 12.25 5.72874 12.25 6.0625V7.75H13.9375ZM10.75 10.75H13.9375C14.5679 10.75 15.1842 10.9369 15.7084 11.2872C16.2326 11.6374 16.6411 12.1353 16.8824 12.7177C17.1236 13.3001 17.1867 13.941 17.0638 14.5594C16.9408 15.1777 16.6372 15.7456 16.1914 16.1914C15.7456 16.6372 15.1777 16.9408 14.5594 17.0638C13.941 17.1867 13.3001 17.1236 12.7177 16.8824C12.1353 16.6411 11.6374 16.2326 11.2872 15.7084C10.9369 15.1842 10.75 14.5679 10.75 13.9375V10.75ZM12.25 12.25V13.9375C12.25 14.2713 12.349 14.5975 12.5344 14.875C12.7198 15.1525 12.9834 15.3688 13.2917 15.4965C13.6001 15.6243 13.9394 15.6577 14.2667 15.5926C14.5941 15.5275 14.8947 15.3667 15.1307 15.1307C15.3667 14.8947 15.5275 14.5941 15.5926 14.2667C15.6577 13.9394 15.6243 13.6001 15.4965 13.2917C15.3688 12.9834 15.1525 12.7198 14.875 12.5344C14.5975 12.349 14.2713 12.25 13.9375 12.25H12.25Z",fill:"currentColor"})])],-1)),N("span",null,W(e.$t("All")),1)],void 0,!0),_:1},8,["index"]),(H(!0),O(K,null,Q(i.funnel_categories,e=>(H(),E(m,{key:e,index:e},{default:U(()=>[N("span",Se,[i.getCatIcon(e)?(H(),O("span",{key:0,innerHTML:i.getCatIcon(e)},null,8,Te)):(H(),E(f,{key:1},{default:U(()=>[Z(g)],void 0,!0),_:1}))]),N("span",Ae,W(e),1)],void 0,!0),_:2},1032,["index"]))),128))],void 0,!0),_:1},8,["default-active"]),l.showCreateNewAutomations?(H(),O("div",Pe,[Z(y,{class:Y(["pre-built-triggers-btn",{cursor_pointer:l.showCreateNewAutomations}]),onClick:t[2]||(t[2]=e=>l.showCreateNewAutomations=!1)},{default:U(()=>[G(W(e.$t("Pre-built Templates")),1)],void 0,!0),_:1},8,["class"])])):J("",!0)]),N("div",De,[N("div",xe,[Z(v,{placeholder:e.$t("Search Triggers e.g: List Applied"),modelValue:l.searchTrigger,"onUpdate:modelValue":t[3]||(t[3]=e=>l.searchTrigger=e)},{prefix:U(()=>[N("span",qe,[Z(n,{"icon-name":"search"})])]),_:1},8,["placeholder","modelValue"])]),N("div",Me,[N("h2",Ie,W(e.$t("Select the trigger for this automation")),1)]),l.selected_category?(H(),O("div",Be,[(H(!0),O(K,null,Q(i.funnelTriggers,(t,s)=>(H(),O("div",{class:Y(["fcrm_trigger_selection_item",{fc_trigger_selected:l.funnel.trigger_name==s,fcrm_trigger_selection_item_pro:t.disabled}]),key:s,onClick:e=>i.funnelActiveHandle(s)},[t.disabled?(H(),E(C,{key:0,text:t.ribbon},null,8,["text"])):t.ribbon?(H(),E(C,{key:1,text:t.ribbon,hideIcon:!0},null,8,["text"])):J("",!0),N("div",je,[t.svg?(H(),O("span",{key:0,class:"icon",innerHTML:t.svg},null,8,He)):t["element-icon"]?(H(),O("span",Ee,[Z(f,null,{default:U(()=>[(H(),E(X(i.getElIcon(t["element-icon"]))))],void 0,!0),_:2},1024)])):(H(),O("i",{key:2,class:Y(t.icon?t.icon:"fc-icon-trigger")},null,2))]),N("div",Ue,[N("h3",Oe,W(t.label),1),N("p",Ze,W(t.description),1)]),t.disabled?(H(),O("a",{key:2,href:e.appVars.crm_pro_url,target:"_blank",class:"fcrm_trigger_selection__pro el-button el-button--primary el-button--small"},W(e.$t("Upgrade To Pro")),9,ze)):J("",!0)],10,Re))),128))])):J("",!0)])])],void 0,!0),_:1},8,["onSubmit","data"])])):(H(),O("div",Ne,[l.selected_category?(H(),O("div",We,[N("div",Ke,[l.templatesLoading?(H(),O(K,{key:0},Q(6,e=>N("div",{key:e,class:"fcrm_trigger_prebuild_template_item"},[Z(k,{animated:"",rows:2})])),64)):(H(!0),O(K,{key:1},Q(l.templates,(s,a)=>{var o;return H(),O("div",{class:Y(["fcrm_trigger_prebuild_template_item",{fcrm_trigger_prebuild_template_item_pro:i.visibleProRibon(s)}]),key:a},[N("div",Qe,[N("i",{class:Y(i.getTriggerIcon(null==(o=null==s?void 0:s.funnel_data)?void 0:o.trigger_name))},null,2)]),N("div",Ye,W(s.title),1),N("div",Ge,W(s.short_description),1),N("div",Je,[i.visibleProRibon(s)?(H(),E(y,{key:1,size:"small",onClick:t[4]||(t[4]=e=>l.showProDialog=!0)},{default:U(()=>[N("span",et,[Z(n,{"icon-name":"import"})]),G(" "+W(e.$t("Import")),1)],void 0,!0),_:1})):(H(),E(y,{key:0,class:"import-btn",size:"small",onClick:e=>i.createFromTemplate(s)},{default:U(()=>[N("span",Xe,[Z(n,{"icon-name":"import"})]),G(" "+W(e.$t("Import")),1)],void 0,!0),_:1},8,["onClick"])),N("a",{class:"el-button preview-btn",href:s.link,target:"_blank",rel:"noopener noreferrer"},[N("span",null,[t[10]||(t[10]=N("span",{class:"icon"},[N("svg",{width:"20",height:"20",viewBox:"0 0 20 20",fill:"none",xmlns:"http://www.w3.org/2000/svg"},[N("path",{d:"M9.99999 3.25C14.044 3.25 17.4085 6.16 18.1142 10C17.4092 13.84 14.044 16.75 9.99999 16.75C5.95599 16.75 2.59149 13.84 1.88574 10C2.59074 6.16 5.95599 3.25 9.99999 3.25ZM9.99999 15.25C11.5296 15.2497 13.0138 14.7301 14.2096 13.7764C15.4055 12.8226 16.2422 11.4912 16.5827 10C16.2409 8.50998 15.4037 7.18 14.208 6.22752C13.0122 5.27504 11.5287 4.7564 9.99999 4.7564C8.47126 4.7564 6.98776 5.27504 5.79202 6.22752C4.59629 7.18 3.75907 8.50998 3.41724 10C3.75781 11.4912 4.5945 12.8226 5.79035 13.7764C6.9862 14.7301 8.47039 15.2497 9.99999 15.25V15.25ZM9.99999 13.375C9.10489 13.375 8.24644 13.0194 7.61351 12.3865C6.98057 11.7536 6.62499 10.8951 6.62499 10C6.62499 9.10489 6.98057 8.24645 7.61351 7.61352C8.24644 6.98058 9.10489 6.625 9.99999 6.625C10.8951 6.625 11.7535 6.98058 12.3865 7.61352C13.0194 8.24645 13.375 9.10489 13.375 10C13.375 10.8951 13.0194 11.7536 12.3865 12.3865C11.7535 13.0194 10.8951 13.375 9.99999 13.375ZM9.99999 11.875C10.4973 11.875 10.9742 11.6775 11.3258 11.3258C11.6774 10.9742 11.875 10.4973 11.875 10C11.875 9.50272 11.6774 9.02581 11.3258 8.67418C10.9742 8.32254 10.4973 8.125 9.99999 8.125C9.50271 8.125 9.0258 8.32254 8.67417 8.67418C8.32254 9.02581 8.12499 9.50272 8.12499 10C8.12499 10.4973 8.32254 10.9742 8.67417 11.3258C9.0258 11.6775 9.50271 11.875 9.99999 11.875Z",fill:"var(--fc-secondary-text)"})])],-1)),G(" "+W(e.$t("Preview")),1)])],8,tt)]),i.visibleProRibon(s)?(H(),E(C,{key:0})):J("",!0)],2)}),128))])])):J("",!0),l.funnel.trigger_name&&s.triggers[l.funnel.trigger_name].disabled?(H(),O("div",st,[Z(F,{heading:e.$t("Upgrade to FluentCRM Pro"),description:e.$t("install_fluentcrm_pro")},null,8,["heading","description"])])):J("",!0)])),Z($,{modelValue:l.showProDialog,"onUpdate:modelValue":t[5]||(t[5]=e=>l.showProDialog=e),"append-to-body":!0,width:"400px","close-on-click-modal":!1,"show-close":!0,onClosed:t[6]||(t[6]=e=>l.showProDialog=!1),title:e.$t("Upgrade to FluentCRM Pro")},{default:U(()=>[Z(F,{heading:e.$t("Upgrade to FluentCRM Pro"),"show-header-upgrade-icon":!1},null,8,["heading"])],void 0,!0),_:1},8,["modelValue","title"])],void 0),_:1},8,["modelValue"])}]]),it={class:"fcrm_bulk_action_inline"},nt={class:"fcrm_bulk_wrap"},ot={class:"fcrm_bulk_item"},rt={class:"icon"},ct={class:"icon"};const dt=_e({name:"BulkFunnelActions",props:["selectedFunnels","options","theme_mode"],emits:["refetch"],components:{Confirm:me,Check:g,Delete:m},data(){return{delete_confirm_message:""+this.$t("Are you sure to delete?")+"
"+this.$t("delete_all_funnels_notice"),actions:{},select_job:{action_name:"",selected_options:[]},doing_action:!1,select_status:"",selectedLabels:[]}},watch:{"select_job.action_name":{handler(){this.select_job.selected_options=[],this.select_status="",this.selectedLabels=[]},deep:!0}},computed:{bulkSelectPopperClass(){return"dark"===this.theme_mode?"fcrm-force-light":"fcrm-dark"},bulkSelectWordbreakPopperClass(){return"dark"===this.theme_mode?"fcrm-force-light":"fcrm-dark"}},methods:{doBulkAction(){const e=[];this.each(this.selectedFunnels,t=>{e.push(t.id)}),this.doing_action=!0,this.$post("funnels/do-bulk-action",{action_name:this.select_job.action_name,status:this.select_status,labels:this.selectedLabels,funnel_ids:e}).then(e=>{this.$notify.success(e.message),this.$emit("refetch"),this.selectedLabels=[]}).catch(e=>{this.handleError(e)}).finally(()=>{this.doing_action=!1})}},mounted(){this.actions={change_funnel_status:{label:this.$t("Change Funnel Status"),options:this.options.statuses},apply_labels:{label:this.$t("Apply Labels"),options:this.options.labels},delete_funnels:{label:this.$t("Delete Funnels")}}}},[["render",function(e,t,s,a,l,i){const n=f,o=b,r=j("Check"),c=d,u=_,p=j("Delete"),h=j("confirm"),m=y;return H(),O("div",it,[N("div",nt,[N("div",ot,[N("label",null,W(e.$t("Select Action")),1),Z(o,{clearable:"",filterable:"",size:"small",class:"fcrm_bulk_select",placeholder:e.$t("Select Action"),"popper-class":i.bulkSelectPopperClass,effect:"dark",modelValue:l.select_job.action_name,"onUpdate:modelValue":t[0]||(t[0]=e=>l.select_job.action_name=e)},{default:U(()=>[(H(!0),O(K,null,Q(l.actions,(e,t)=>(H(),E(n,{key:t,value:t,label:e.label},null,8,["value","label"]))),128))],void 0),_:1},8,["placeholder","popper-class","modelValue"])]),"change_funnel_status"==l.select_job.action_name?(H(),O(K,{key:0},[Z(o,{filterable:"",clearable:"",size:"small",class:"fcrm_bulk_select",placeholder:e.$t("Select"),"popper-class":i.bulkSelectPopperClass,effect:"dark",modelValue:l.select_status,"onUpdate:modelValue":t[1]||(t[1]=e=>l.select_status=e)},{default:U(()=>[(H(!0),O(K,null,Q(s.options.statuses,e=>(H(),E(n,{key:e.id,value:e.id,label:e.title},null,8,["value","label"]))),128))],void 0),_:1},8,["placeholder","popper-class","modelValue"]),ee((H(),E(u,{disabled:l.doing_action||!l.select_status,onClick:t[2]||(t[2]=e=>i.doBulkAction()),type:"primary",size:"small"},{default:U(()=>[N("span",rt,[Z(c,null,{default:U(()=>[Z(r)],void 0,!0),_:1})]),G(" "+W(e.$t("Change Status")),1)],void 0),_:1},8,["disabled"])),[[m,l.doing_action]])],64)):J("",!0),"apply_labels"===l.select_job.action_name?(H(),O(K,{key:1},[Z(o,{filterable:"",size:"small",class:"fcrm_bulk_select",placeholder:e.$t("Select Labels"),modelValue:l.selectedLabels,"onUpdate:modelValue":t[3]||(t[3]=e=>l.selectedLabels=e),multiple:"","collapse-tags":"","max-collapse-tags":2,"popper-class":i.bulkSelectWordbreakPopperClass,effect:"dark"},{default:U(()=>[(H(!0),O(K,null,Q(s.options.labels,e=>(H(),E(n,{key:e.id,value:e.id},{default:U(()=>[N("span",{style:te("background:"+(e.settings&&e.settings.color?e.settings.color:e.color||"#e5e7eb")+";padding: 2px 5px 4px 5px;border-radius: 4px;color: #0E121B;")},W(e.title),5)],void 0,!0),_:2},1032,["value"]))),128))],void 0),_:1},8,["placeholder","modelValue","popper-class"]),ee((H(),E(u,{disabled:l.doing_action||!l.selectedLabels.length,onClick:t[4]||(t[4]=e=>i.doBulkAction()),type:"primary",size:"small"},{default:U(()=>[N("span",ct,[Z(c,null,{default:U(()=>[Z(r)],void 0,!0),_:1})]),G(" "+W(e.$t("Apply Label")),1)],void 0),_:1},8,["disabled"])),[[m,l.doing_action]])],64)):"delete_funnels"==l.select_job.action_name?(H(),E(h,{key:2,placement:"top-start",message:l.delete_confirm_message,onYes:t[5]||(t[5]=e=>i.doBulkAction())},{reference:U(()=>[ee((H(),E(u,{disabled:l.doing_action,type:"danger",plain:"",size:"small"},{default:U(()=>[Z(c,null,{default:U(()=>[Z(p)],void 0,!0),_:1}),G(" "+W(e.$t("Delete Funnels")),1)],void 0,!0),_:1},8,["disabled"])),[[m,l.doing_action]])]),_:1},8,["message"])):J("",!0)])])}]]),_t={class:"fcrm_checkbox_group_label d-none"};const ut=_e({name:"ColumnToggler",components:{Filterer:be,Setting:v},emits:["input","update:modelValue","dataChanged"],data:()=>({selection:[],automationFunnelColumns:ye}),computed:{columnGroups(){return[{slug:"automation",label:this.$t("Primary Fields"),fields:ye}]}},methods:{init(){const e=this.storage.get("automationFunnelColumns");e?(this.selection=e,this.fire()):(this.selection=["trigger","action","stats","pause/run","labels"],this.save())},save(){var e;this.storage.set("automationFunnelColumns",this.selection),null==(e=this.$refs.filterer)||e.hide(),this.fire()},fire(){this.$emit("input",this.selection),this.$emit("update:modelValue",this.selection),setTimeout(()=>{this.$emit("dataChanged",this.selection)},400)}},mounted(){this.init()}},[["render",function(e,t,s,a,l,i){const n=_,o=k,r=w,c=C,d=j("filterer");return H(),E(d,{ref:"filterer",name:"column-toggler"},{header:U(()=>[Z(n,{class:"only-icon-btn small",size:"small"},{default:U(()=>[...t[1]||(t[1]=[N("svg",{xmlns:"http://www.w3.org/2000/svg",width:"20",height:"20",viewBox:"0 0 20 20",fill:"none"},[N("path",{d:"M9.25 4.75H4.75V15.25H9.25V4.75ZM10.75 4.75V15.25H15.25V4.75H10.75ZM4 3.25H16C16.1989 3.25 16.3897 3.32902 16.5303 3.46967C16.671 3.61032 16.75 3.80109 16.75 4V16C16.75 16.1989 16.671 16.3897 16.5303 16.5303C16.3897 16.671 16.1989 16.75 16 16.75H4C3.80109 16.75 3.61032 16.671 3.46967 16.5303C3.32902 16.3897 3.25 16.1989 3.25 16V4C3.25 3.80109 3.32902 3.61032 3.46967 3.46967C3.61032 3.32902 3.80109 3.25 4 3.25Z",fill:"var(--fc-secondary-text)"})],-1)])],void 0,!0),_:1})]),items:U(()=>[Z(r,{modelValue:l.selection,"onUpdate:modelValue":t[0]||(t[0]=e=>l.selection=e),class:"fcrm_filter-options fcrm_checkbox_group fcrm_column_toggler_checks"},{default:U(()=>[(H(!0),O(K,null,Q(i.columnGroups,(e,t)=>(H(),O("div",{key:t},[N("div",_t,W(e.label),1),(H(!0),O(K,null,Q(e.fields,(e,s)=>(H(),O("div",{class:"el-dropdown-menu__item",key:t+"-"+e.value},[Z(o,{value:e.value,class:"fcrm_checkbox"},{default:U(()=>[G(W(e.label),1)],void 0,!0),_:2},1032,["value"])]))),128))]))),128))],void 0,!0),_:1},8,["modelValue"])]),footer:U(()=>[Z(c,{class:"no-hover"},{default:U(()=>[Z(n,{type:"primary",size:"small",style:{width:"100%","margin-top":"10px"},onClick:i.save},{default:U(()=>[se(e.$slots,"btn-label",{},()=>[G(W(e.$t("Save")),1)])],void 0,!0),_:3},8,["onClick"])],void 0,!0),_:3})]),_:3},512)}]]),pt="funnels_filters";function ht(e){var t,s,a,l,i,n,o,r,c;try{return JSON.stringify({search:(null==(t=e.query_data)?void 0:t.search)||"",labels:(null==(s=e.query_data)?void 0:s.labels)||[],tags:(null==(a=e.query_data)?void 0:a.tags)||[],lists:(null==(l=e.query_data)?void 0:l.lists)||[],statuses:(null==(i=e.query_data)?void 0:i.statuses)||[],sort_by:(null==(n=e.query_data)?void 0:n.sort_by)||"id",sort_type:(null==(o=e.query_data)?void 0:o.sort_type)||"DESC",page:(null==(r=e.pagination)?void 0:r.current_page)||1,per_page:(null==(c=e.pagination)?void 0:c.per_page)||10})}catch(d){return""}}const mt=ae("funnels",{state:()=>({funnels:[],triggers:{},pagination:{current_page:1,per_page:10,total:0},query_data:{search:"",labels:[],tags:[],lists:[],statuses:[],sort_by:"id",sort_type:"DESC"},loading:!1,first_loading:!0,cache_metadata:{last_fetch_time:null,cache_key_hash:null,is_stale:!1},options:{labels:[],tags:[],lists:[],statuses:[]}}),getters:{isCacheValid(e){if(!e.cache_metadata.last_fetch_time||e.cache_metadata.is_stale)return!1;return Date.now()-e.cache_metadata.last_fetch_time<3e5},filterHash:e=>ht({query_data:e.query_data,pagination:e.pagination}),hasCachedDataForCurrentFilters(e){if(!e.cache_metadata.cache_key_hash)return!1;const t=ht({query_data:e.query_data,pagination:e.pagination});return e.cache_metadata.cache_key_hash===t},shouldShowCache(){return this.isCacheValid&&this.hasCachedDataForCurrentFilters&&this.funnels.length>0}},actions:{async fetchFunnels(e=!1,s=!1){if(e||!this.shouldShowCache){s||(this.loading=!0);try{const e={per_page:this.pagination.per_page,page:this.pagination.current_page,labels:this.query_data.labels,tags:this.query_data.tags,lists:this.query_data.lists,statuses:this.query_data.statuses,with:["triggers"],search:this.query_data.search,sort_by:this.query_data.sort_by,sort_type:this.query_data.sort_type},s=await t.get("funnels",e);this.funnels=s.funnels.data,this.pagination.total=s.funnels.total,this.triggers=s.triggers,this.cache_metadata.last_fetch_time=Date.now(),this.cache_metadata.cache_key_hash=this.filterHash,this.cache_metadata.is_stale=!1,this.persistFilters()}catch(a){throw console.error("[FunnelsStore] Failed to fetch funnels",a),a}finally{s||(this.loading=!1),this.first_loading=!1}}},restoreFilters(){try{const t=e.get(pt,null);return!!t&&(this.query_data=t.query_data||this.query_data,t.pagination&&(this.pagination.current_page=t.pagination.current_page||1,this.pagination.per_page=t.pagination.per_page||10),!0)}catch(t){return console.error("[FunnelsStore] Failed to restore filters",t),!1}},persistFilters(){try{e.set(pt,{query_data:this.query_data,pagination:{current_page:this.pagination.current_page,per_page:this.pagination.per_page}})}catch(t){console.error("[FunnelsStore] Failed to persist filters",t)}},invalidateCache(){this.cache_metadata.is_stale=!0,this.cache_metadata.last_fetch_time=null,this.cache_metadata.cache_key_hash=null},updatePagination(e,t,s=!1){void 0!==e&&(this.pagination.current_page=e),void 0!==t&&(this.pagination.per_page=t),s||this.persistFilters()},updateQueryData(e){this.query_data={...this.query_data,...e},this.persistFilters()},setOptions(e){this.options={...this.options,...e}}}}),gt=re(()=>s(()=>import("../../../v3app/src/Modules/Labels/Labels.js?ver=3.1.8"),[],import.meta.url)),ft=re(()=>s(()=>import("../../../v3app/src/Modules/Contacts/Filter/FilterPopover.js?ver=3.1.8"),[],import.meta.url)),bt={name:"AutomationFunnels",components:{Badge:we,PageHeader:ve,ActiveFiltersBar:re(()=>s(()=>import("../../../v3app/src/Modules/Contacts/Filter/ActiveFiltersBar.js?ver=3.1.8"),[],import.meta.url)),FilterPopover:ft,FloatingBulkActionShell:Ce,Icons:ue,Labels:gt,CreateFunnelModal:lt,Confirm:me,PaginationBar:ge,BulkFunnelActions:dt,InlineDoc:fe,Toggler:ut,Search:D,Plus:P,Close:A,User:T,DataLine:S,Back:i,CopyDocument:V,PriceTag:L,Download:$,Delete:m,Timer:F},data:()=>({search:"",create_modal:!1,duplicating:!1,sortBy:"id",sortType:"DESC",selection:!1,selectedFunnels:[],working:!1,visibleLabelsForm:!1,selectedLabels:[],tagFilter:[],listFilter:[],labelFilter:[],statusFilter:[],showingLabelsConfig:!1,rowActionVisible:{},applyLabelDialogVisible:!1,funnelForLabels:null,applyLabelsLoading:!1,columns:[],initialFired:!1,inline_errors:null,current_mode:"system"===he.getCurrentTheme()?he.getSystemTheme():he.getCurrentTheme()}),computed:{...oe(mt,["funnels","triggers","pagination","loading","options","query_data"]),...ne(mt,["shouldShowCache"])},methods:{...ie(mt,["fetchFunnels","invalidateCache","restoreFilters","persistFilters","updatePagination","updateQueryData","setOptions"]),closeCreateModal(){if(this.create_modal=!1,this.$route.query.add){const e={...this.$route.query};delete e.add,this.$router.replace({path:this.$route.path,query:e})}},maybeReFetch(){this.initialFired&&this.getFunnels(!1,!0)},async getFunnels(e=!0,t=!1){this.updateQueryData({search:this.search,labels:this.labelFilter,tags:this.tagFilter,lists:this.listFilter,statuses:this.statusFilter,sort_by:this.sortBy,sort_type:this.sortType}),this.storage.set("funnel_per_page",this.pagination.per_page);try{await this.fetchFunnels(e,t),this.selection=!1}catch(s){this.storage.set("funnel_per_page",10),this.pagination.per_page=10,this.handleError(s)}finally{this.initialFired=!0}},onSelection(e){this.selection=!!e.length,this.selectedFunnels=e},clearFunnelSelection(){this.$refs.funnelTable&&this.$refs.funnelTable.clearSelection(),this.selection=!1,this.selectedFunnels=[]},handleSortable(e){"descending"===e.order?(this.sortBy=e.prop,this.sortType="DESC"):(this.sortBy=e.prop,this.sortType="ASC"),this.getFunnels()},stripHtml(e){if(!e||"string"!=typeof e)return e||"";return e.replace(/<[^>]*>/g,"").replace(/&/g,"&").replace(//g,">").replace(/"/g,""").replace(/'/g,"'")},getTriggerTitle(e){return this.triggers[e]?this.stripHtml(this.triggers[e].label):''+this.stripHtml(e)+" ("+this.$t("INACTIVE")+")"},getTriggerSegmentType:e=>({fluentcrm_contact_added_to_tags:"tags",fluentcrm_contact_removed_from_tags:"tags",fluentcrm_contact_added_to_lists:"lists",fluentcrm_contact_removed_from_lists:"lists"}[e]||""),getTriggerSegmentNames(e){const t=this.getTriggerSegmentType(e.trigger_name);if(!t||!e.settings||!Array.isArray(e.settings[t]))return[];const s=e.settings[t].map(e=>e&&"object"==typeof e?e.id?e.id.toString():"":e?e.toString():"").filter(Boolean);return(this.options[t]||[]).filter(e=>-1!==s.indexOf(e.id.toString())).map(e=>e.title)},getTriggerIcon(e){return this.triggers[e.trigger_name]&&this.triggers[e.trigger_name].svg?''+this.triggers[e.trigger_name].svg+"":""},edit(e){this.$router.push({name:"edit_funnel",params:{funnel_id:e.id}})},subscribers(e){this.$router.push(this.getReportsRoute(e))},getReportsRoute:e=>({name:"funnel_subscribers",params:{funnel_id:e.id}}),isActiveAutomation:e=>e&&"published"===e.status,getInProgressSubscribersCount:e=>parseInt((null==e?void 0:e.in_progress_subscribers_count)||0),remove(e){this.$del(`funnels/${e.id}`).then(e=>{this.$notify.success(e.message),this.invalidateCache(),this.getFunnels()}).catch(e=>{this.handleError(e)})},duplicate(e){this.duplicating=!0,this.$post(`funnels/${e.id}/clone`).then(e=>{this.$notify.success(e.message),this.invalidateCache(),this.$router.push({name:"edit_funnel",params:{funnel_id:e.funnel.id},query:{is_new:"yes"}})}).catch(e=>{this.handleError(e)}).finally(()=>{this.duplicating=!1})},exportFunnel(e){location.href=window.ajaxurl+"?"+jQuery.param({action:"fluentcrm_export_funnel",_nonce:window.fcAdmin.ajax_nonce,funnel_id:e.id})},updateStatus(e){this.working=!0;const t=e.status;this.$put("funnels/"+e.id,{status:t}).then(s=>{this.$notify.success(s.message),this.invalidateCache(),e.status=t}).catch(e=>{this.handleError(e)}).finally(()=>{this.working=!1})},showLabelDialog(){this.showingLabelsConfig=!0},fetchLabels(){this.$get("labels").then(e=>{this.options.labels=e.labels}).catch(e=>{this.handleError(e)}).finally(()=>{})},onRowActionShow(e){Object.keys(this.rowActionVisible).forEach(t=>{parseInt(t)!==e.id&&(this.rowActionVisible[t]=!1)}),this.selectedLabels=[]},openApplyLabelDialog(e){this.rowActionVisible[e.id]=!1,this.funnelForLabels=e,this.selectedLabels=(e.labels||[]).map(e=>e.id),this.applyLabelDialogVisible=!0},closeApplyLabelDialog(){this.applyLabelDialogVisible=!1,this.funnelForLabels=null,this.selectedLabels=[]},applyLabelsFromDialog(){this.funnelForLabels&&this.applyLabels(this.funnelForLabels,[],"sync")},applyLabels(e,t,s="attach"){const a=this.funnelForLabels&&this.funnelForLabels.id===e.id;a&&(this.applyLabelsLoading=!0),this.$put("funnels/"+e.id+"/update-labels",{action:s,label_ids:["attach","sync"].includes(s)?this.selectedLabels:t}).then(e=>{this.$notify.success(e.message),this.invalidateCache(),this.getFunnels()}).catch(e=>{this.handleError(e)}).finally(()=>{a&&(this.applyLabelsLoading=!1,this.closeApplyLabelDialog())})},closeDrawer(){this.showingLabelsConfig=!1},syncSegmentFilters(e){this.query_data.tags=e.tags||[],this.query_data.lists=e.lists||[],this.query_data.labels=e.labels||[],this.query_data.statuses=e.statuses||[],this.tagFilter=e.tags||[],this.listFilter=e.lists||[],this.labelFilter=e.labels||[],this.statusFilter=e.statuses||[]},handleFilterApply(e){this.syncSegmentFilters(e),this.pagination.current_page=1,this.getFunnels()},handleFilterBarChange(e){this.syncSegmentFilters(e),void 0!==e.search&&(this.query_data.search=e.search,this.search=e.search),this.pagination.current_page=1,this.getFunnels()},handleOpenFilter(e){this.$nextTick(()=>{this.$refs.filterPopoverRef&&this.$refs.filterPopoverRef.openFilterCategory&&this.$refs.filterPopoverRef.openFilterCategory(e)})}},mounted(){this.setOptions({tags:this.appVars.available_tags||[],lists:this.appVars.available_lists||[],statuses:[{id:"published",title:this.$t("Publish")},{id:"draft",title:this.$t("Draft")}]}),this.restoreFilters();const e=parseInt(this.storage.get("funnel_per_page",10));this.updatePagination(void 0,e,!0),this.search=this.query_data.search||"",this.tagFilter=this.query_data.tags||[],this.listFilter=this.query_data.lists||[],this.labelFilter=this.query_data.labels||[],this.statusFilter=this.query_data.statuses||[],this.sortBy=this.query_data.sort_by||"id",this.sortType=this.query_data.sort_type||"DESC",this.changeTitle(this.$t("Automation Funnels")),this.fetchLabels(),this.shouldShowCache?(this.loading=!1,setTimeout(()=>this.getFunnels(!0,!0),100)):this.getFunnels(),this.$route.query.add&&this.hasPermission("fcrm_write_funnels")&&(this.create_modal=!0),this.onThemeChanged=e=>{var t;this.current_mode=(null==(t=e.detail)?void 0:t.effective)||he.getCurrentTheme()},window.addEventListener(pe,this.onThemeChanged)},beforeUnmount(){this.onThemeChanged&&window.removeEventListener(pe,this.onThemeChanged)}},yt={class:"fcrm_funnels_page"},vt={class:"fcrm_page_header_top_nav_wrapper"},Ct={class:"fcrm_page_header_top_nav"},wt={class:"fcrm_page_header_top_nav_links"},kt={class:"fcrm-layout-width"},Ft={class:"icon"},$t={class:"el-popover__reference"},Lt={class:"icon"},Vt={class:"el-popover__reference"},St={class:"icon"},Tt={class:"icon"},At={class:"fcrm_table_wrapper"},Pt={class:"fcrm_table_header"},Dt={class:"fcrm_table_header_inner"},xt={class:"fcrm_table_header_inner_left"},qt={class:"icon"},Mt={class:"fcrm_table_header_inner_actions"},It={class:"fcrm_automation_table_header_bulk_actions"},Bt={class:"fcrm_table_body"},Rt={class:"fcrm_funnel_title_line"},jt=["title"],Ht={class:"funnel_title_text"},Et={key:0,class:"item_description funnel_description"},Ut={class:"fcrm_funnel_trigger_cell"},Ot=["innerHTML"],Zt={class:"fcrm_funnel_trigger_content"},zt=["innerHTML"],Nt={key:0,class:"fcrm_funnel_trigger_segments"},Wt=["title"],Kt={key:0,class:"fc_funnel_labels"},Qt=["title"],Yt={key:1,class:"stats_badge_inline"},Gt=["title"],Jt={class:"el-dropdown-link"},Xt={class:"el-popover__reference"},es={class:"icon"},ts={class:"el-popover__reference"},ss={class:"icon"},as={class:"el-popover__reference"},ls={class:"icon"},is={class:"el-popover__reference"},ns={class:"icon"},os={class:"el-popover__reference"},rs={class:"icon"},cs={class:"fcrm_empty_state"},ds={class:"fcrm_empty_state_text"},_s={key:0,class:"fcrm_apply_labels_body"},us={class:"dialog-footer"};const ps=_e(bt,[["render",function(e,t,s,a,l,i){const n=j("router-link"),o=j("Icons"),r=_,c=C,m=q,g=x,v=j("inline-doc"),w=j("page-header"),k=u,F=j("filter-popover"),$=j("toggler"),L=j("active-filters-bar"),V=p,S=M,T=j("Close"),A=d,P=j("Confirm"),D=I,z=j("Badge"),Y=j("User"),X=j("Timer"),se=B,ae=j("DataLine"),ie=j("confirm"),ne=R,oe=j("pagination-bar"),re=j("bulk-funnel-actions"),ce=j("floating-bulk-action-shell"),de=j("create-funnel-modal"),_e=f,ue=b,pe=h,he=j("labels"),me=y;return H(),O("div",yt,[N("div",vt,[N("div",Ct,[N("ul",wt,[N("li",null,[Z(n,{to:"/funnels",class:"fcrm_top_nav_link"},{default:U(()=>[G(W(e.$t("Automation Funnels")),1)],void 0),_:1})]),N("li",null,[Z(n,{to:"/funnels/funnel/all-activities",class:"fcrm_top_nav_link"},{default:U(()=>[G(W(e.$t("All Activities")),1)],void 0),_:1})])])]),t[9]||(t[9]=N("div",{class:"fcrm_page_header_top_actions"},null,-1))]),N("div",kt,[Z(w,null,{title:U(()=>[G(W(e.$t("Automation Funnels")),1)]),actions:U(()=>[Z(g,{trigger:"click"},{dropdown:U(()=>[Z(m,null,{default:U(()=>[e.hasPermission("fcrm_write_funnels")?(H(),E(c,{key:0,class:"fc_dropdown_action",onClick:t[0]||(t[0]=t=>e.$router.push({name:"import_funnel"}))},{default:U(()=>[N("span",$t,[N("span",Lt,[Z(o,{"icon-name":"import"})]),G(" "+W(e.$t("Import")),1)])],void 0,!0),_:1})):J("",!0),Z(c,{class:"fc_dropdown_action",onClick:i.showLabelDialog},{default:U(()=>[N("span",Vt,[N("span",St,[Z(o,{"icon-name":"manageLabels"})]),G(" "+W(e.$t("Manage Labels")),1)])],void 0,!0),_:1},8,["onClick"])],void 0,!0),_:1})]),default:U(()=>[Z(r,{class:"el-dropdown-link"},{default:U(()=>[G(W(e.$t("More Actions"))+" ",1),N("span",Ft,[Z(o,{"icon-name":"downIcon"})])],void 0,!0),_:1})],void 0,!0),_:1}),Z(v,{doc_id:2362}),e.hasPermission("fcrm_write_funnels")?(H(),E(r,{key:0,type:"primary",onClick:t[1]||(t[1]=e=>l.create_modal=!0)},{default:U(()=>[N("span",Tt,[Z(o,{"icon-name":"plus"})]),G(" "+W(e.$t("New Automation")),1)],void 0,!0),_:1})):J("",!0)]),_:1}),N("div",At,[N("div",Pt,[N("div",Dt,[N("div",xt,[Z(k,{onKeyup:le(i.getFunnels,["enter"]),size:"small",onClear:t[2]||(t[2]=e=>i.getFunnels()),clearable:"",placeholder:e.$t("Search by title or trigger key"),modelValue:l.search,"onUpdate:modelValue":t[3]||(t[3]=e=>l.search=e)},{prefix:U(()=>[N("span",qt,[Z(o,{"icon-name":"search"})])]),_:1},8,["onKeyup","placeholder","modelValue"])]),N("div",Mt,[Z(F,{ref:"filterPopoverRef",options:{lists:e.options.lists?e.options.lists:[],tags:e.options.tags?e.options.tags:[],labels:e.options.labels?e.options.labels:[],statuses:e.options.statuses?e.options.statuses:[]},"selected-filters":e.query_data,onApply:i.handleFilterApply},null,8,["options","selected-filters","onApply"]),Z($,{onDataChanged:t[4]||(t[4]=e=>i.maybeReFetch()),modelValue:l.columns,"onUpdate:modelValue":t[5]||(t[5]=e=>l.columns=e)},null,8,["modelValue"])])]),N("div",It,[Z(L,{"selected-filters":e.query_data,options:{lists:e.options.lists?e.options.lists:[],tags:e.options.tags?e.options.tags:[],labels:e.options.labels?e.options.labels:[],statuses:e.options.statuses?e.options.statuses:[]},onFilterChange:i.handleFilterBarChange,onOpenFilter:i.handleOpenFilter,plus_filter_icon:!0},null,8,["selected-filters","options","onFilterChange","onOpenFilter"])])]),N("div",Bt,[e.loading&&!e.funnels.length?(H(),E(V,{key:0,style:{padding:"20px"},rows:8})):J("",!0),e.funnels.length||!e.loading?ee((H(),E(ne,{key:1,ref:"funnelTable",border:"","default-sort":{prop:l.sortBy,order:"DESC"==l.sortType?"descending":"ascending"},onSortChange:i.handleSortable,onSelectionChange:i.onSelection,stripe:"",data:e.funnels,style:{width:"100%"}},{empty:U(()=>[N("div",cs,[Z(o,{"icon-name":"common-empty-state"}),N("div",ds,[N("span",null,W(e.$t("Create your first automation funnel to streamline your marketing workflows.")),1)])])]),default:U(()=>[Z(S,{type:"selection",width:"45",fixed:""}),Z(S,{sortable:"custom",prop:"id",width:"80",label:e.$t("ID")},{default:U(e=>[G(W(e.row.id),1)]),_:1},8,["label"]),Z(S,{"min-width":"250",sortable:"custom",prop:"title",label:e.$t("Title")},{default:U(t=>[N("div",Rt,[i.getInProgressSubscribersCount(t.row)?(H(),O("span",{key:0,title:e.$t("Contacts currently in this automation"),class:"fcrm_live_indicator"},null,8,jt)):J("",!0),Z(n,{to:{name:"edit_funnel",params:{funnel_id:t.row.id}}},{default:U(()=>[N("p",Ht,W(t.row.title),1)],void 0,!0),_:2},1032,["to"])]),t.row.description?(H(),O("span",Et,W(t.row.description),1)):J("",!0)]),_:1},8,["label"]),-1!=l.columns.indexOf("trigger")?(H(),E(S,{key:0,"min-width":"250",sortable:"custom",prop:"trigger_name",label:e.$t("Trigger")},{default:U(e=>[N("div",Ut,[N("span",{class:"fcrm_funnel_trigger_icon",innerHTML:i.getTriggerIcon(e.row)},null,8,Ot),N("div",Zt,[N("span",{innerHTML:i.getTriggerTitle(e.row.trigger_name)},null,8,zt),i.getTriggerSegmentNames(e.row).length?(H(),O("div",Nt,[(H(!0),O(K,null,Q(i.getTriggerSegmentNames(e.row),e=>(H(),O("span",{key:e,class:"fcrm_badge"},[N("span",{title:e},W(e),9,Wt)]))),128))])):J("",!0)])])]),_:1},8,["label"])):J("",!0),-1!=l.columns.indexOf("labels")?(H(),E(S,{key:1,width:"250",label:e.$t("Labels")},{default:U(t=>[t.row.labels?(H(),O("div",Kt,[(H(!0),O(K,null,Q(t.row.labels,(s,a)=>(H(),E(D,{key:a,size:"small",style:te("background:"+s.color)},{default:U(()=>[G(W(s.title)+" ",1),Z(P,{onYes:e=>i.applyLabels(t.row,s.id,"detach"),message:e.$t("Remove_Label_From_funnel_Message")},{reference:U(()=>[Z(A,{class:"el-tag__close"},{default:U(()=>[Z(T)],void 0,!0),_:1})]),_:1},8,["onYes","message"])],void 0,!0),_:2},1032,["style"]))),128))])):J("",!0)]),_:1},8,["label"])):J("",!0),Z(S,{sortable:"custom",prop:"status",width:"100",label:e.$t("Status")},{default:U(e=>[ee(Z(z,{type:e.row.status},null,8,["type"]),[[me,l.working]])]),_:1},8,["label"]),-1!=l.columns.indexOf("stats")?(H(),E(S,{key:2,width:"125",label:e.$t("Stats")},{default:U(t=>[i.isActiveAutomation(t.row)?(H(),E(n,{key:0,to:i.getReportsRoute(t.row),class:"stats_badge_inline fcrm_stats_badge_link",title:e.$t("Reports")},{default:U(()=>[Z(A,null,{default:U(()=>[Z(Y)],void 0,!0),_:1}),G(" "+W(t.row.subscribers_count)+" ",1),i.getInProgressSubscribersCount(t.row)?(H(),O("span",{key:0,title:e.$t("Contacts currently in this automation"),class:"fcrm_stats_badge_segment"},[Z(A,{class:"fcrm_stats_badge_segment_icon"},{default:U(()=>[Z(X)],void 0,!0),_:1}),G(" "+W(i.getInProgressSubscribersCount(t.row)),1)],8,Qt)):J("",!0)],void 0,!0),_:2},1032,["to","title"])):(H(),O("span",Yt,[Z(A,null,{default:U(()=>[Z(Y)],void 0,!0),_:1}),G(" "+W(t.row.subscribers_count)+" ",1),i.getInProgressSubscribersCount(t.row)?(H(),O("span",{key:0,title:e.$t("Contacts currently in this automation"),class:"fcrm_stats_badge_segment"},[Z(A,{class:"fcrm_stats_badge_segment_icon"},{default:U(()=>[Z(X)],void 0,!0),_:1}),G(" "+W(i.getInProgressSubscribersCount(t.row)),1)],8,Gt)):J("",!0)]))]),_:1},8,["label"])):J("",!0),-1!=l.columns.indexOf("pause/run")?(H(),E(S,{key:3,width:"100",label:e.$t("Pause/Run")},{default:U(e=>[Z(se,{modelValue:e.row.status,"onUpdate:modelValue":t=>e.row.status=t,"inactive-value":"draft","active-value":"published",onChange:t=>i.updateStatus(e.row)},null,8,["modelValue","onUpdate:modelValue","onChange"])]),_:1},8,["label"])):J("",!0),-1!=l.columns.indexOf("created_at")?(H(),E(S,{key:4,sortable:"custom",prop:"created_at",width:"160",label:e.$t("Created At")},{default:U(t=>[G(W(e.$nsHumanDiffTime(t.row.created_at)),1)]),_:1},8,["label"])):J("",!0),-1!=l.columns.indexOf("updated_at")?(H(),E(S,{key:5,sortable:"custom",prop:"updated_at",width:"160",label:e.$t("Updated At")},{default:U(t=>[G(W(e.$nsHumanDiffTime(t.row.updated_at)),1)]),_:1},8,["label"])):J("",!0),-1!=l.columns.indexOf("action")?(H(),E(S,{key:6,fixed:"right","min-width":"50",align:"center","class-name":"fcrm_table_actions_cell"},{default:U(t=>[Z(g,{trigger:"click",placement:"bottom-end","popper-class":"fc-funnel-actions-popover",onVisibleChange:e=>e&&i.onRowActionShow(t.row)},{dropdown:U(()=>[Z(m,{class:"fc_funnel_acton_field"},{default:U(()=>["published"===t.row.status?(H(),E(c,{key:0,onClick:e=>i.subscribers(t.row)},{default:U(()=>[N("span",Xt,[N("span",es,[Z(A,null,{default:U(()=>[Z(ae)],void 0,!0),_:1})]),G(" "+W(e.$t("Reports")),1)])],void 0,!0),_:1},8,["onClick"])):J("",!0),e.hasPermission("fcrm_write_funnels")?(H(),E(c,{key:1,onClick:e=>i.duplicate(t.row)},{default:U(()=>[N("span",ts,[N("span",ss,[Z(o,{"icon-name":"duplicate"})]),G(" "+W(e.$t("Duplicate")),1)])],void 0,!0),_:1},8,["onClick"])):J("",!0),e.hasPermission("fcrm_write_funnels")?(H(),E(c,{key:2,onClick:e=>i.openApplyLabelDialog(t.row)},{default:U(()=>[N("span",as,[N("span",ls,[Z(o,{"icon-name":"manageLabels"})]),G(" "+W(e.$t("Apply Labels")),1)])],void 0,!0),_:1},8,["onClick"])):J("",!0),e.hasPermission("fcrm_write_funnels")?(H(),E(c,{key:3,onClick:e=>i.exportFunnel(t.row)},{default:U(()=>[N("span",is,[N("span",ns,[Z(o,{"icon-name":"export"})]),G(" "+W(e.$t("Export")),1)])],void 0,!0),_:1},8,["onClick"])):J("",!0),e.hasPermission("fcrm_write_funnels")?(H(),E(c,{key:4,class:"fcrm_danger_action"},{default:U(()=>[Z(ie,{placement:"top-start",message:e.$t("Automation_Delete_Alert"),onYes:e=>i.remove(t.row)},{reference:U(()=>[N("span",os,[N("span",rs,[Z(o,{"icon-name":"delete"})]),G(" "+W(e.$t("Delete")),1)])]),_:1},8,["message","onYes"])],void 0,!0),_:2},1024)):J("",!0)],void 0,!0),_:2},1024)]),default:U(()=>[N("span",Jt,[Z(o,{"icon-name":"more_actions"})])],void 0,!0),_:2},1032,["onVisibleChange"])]),_:1})):J("",!0)],void 0),_:1},8,["default-sort","onSortChange","onSelectionChange","data"])),[[me,e.loading||l.duplicating]]):J("",!0),Z(oe,{pagination:e.pagination,onFetch:i.getFunnels},null,8,["pagination","onFetch"])])])]),Z(ce,{visible:l.selection,"theme-mode":l.current_mode,"selected-count":l.selectedFunnels.length,"selected-label":e.$t("selected"),"deselect-label":e.$t("Deselect"),onDeselect:i.clearFunnelSelection},{actions:U(()=>[Z(re,{onRefetch:t[6]||(t[6]=t=>{e.invalidateCache(),i.getFunnels()}),selectedFunnels:l.selectedFunnels,options:e.options,theme_mode:l.current_mode},null,8,["selectedFunnels","options","theme_mode"])]),_:1},8,["visible","theme-mode","selected-count","selected-label","deselect-label","onDeselect"]),l.create_modal?(H(),E(de,{key:0,triggers:e.triggers,onClose:i.closeCreateModal,visible:l.create_modal},null,8,["triggers","onClose","visible"])):J("",!0),Z(pe,{modelValue:l.applyLabelDialogVisible,"onUpdate:modelValue":t[8]||(t[8]=e=>l.applyLabelDialogVisible=e),title:e.$t("Apply Labels"),width:"400px",class:"fcrm_apply_labels_dialog","close-on-click-modal":!1,onClose:i.closeApplyLabelDialog},{footer:U(()=>[N("div",us,[Z(r,{onClick:i.closeApplyLabelDialog},{default:U(()=>[G(W(e.$t("Cancel")),1)],void 0,!0),_:1},8,["onClick"]),Z(r,{type:"primary",loading:l.applyLabelsLoading,onClick:i.applyLabelsFromDialog},{default:U(()=>[G(W(e.$t("Apply")),1)],void 0,!0),_:1},8,["loading","onClick"])])]),default:U(()=>[l.funnelForLabels?(H(),O("div",_s,[Z(ue,{modelValue:l.selectedLabels,"onUpdate:modelValue":t[7]||(t[7]=e=>l.selectedLabels=e),multiple:"",placeholder:e.$t("Select Labels"),"popper-class":"fcrm_apply_labels_select_popper"},{default:U(()=>[(H(!0),O(K,null,Q(e.options.labels,e=>(H(),E(_e,{key:e.id,label:e.title,value:e.id},null,8,["label","value"]))),128))],void 0,!0),_:1},8,["modelValue","placeholder"])])):J("",!0)],void 0),_:1},8,["modelValue","title","onClose"]),l.showingLabelsConfig?(H(),E(he,{key:1,open:l.showingLabelsConfig,onClose:i.closeDrawer,onCallFetchLabels:i.fetchLabels},null,8,["open","onClose","onCallFetchLabels"])):J("",!0)])}]]);export{ps as default}; diff --git a/wp-content/plugins/fluent-crm/assets/admin/Modules/Funnels/ImportFunnel.js b/wp-content/plugins/fluent-crm/assets/admin/Modules/Funnels/ImportFunnel.js new file mode 100644 index 0000000..90cbccd --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/admin/Modules/Funnels/ImportFunnel.js @@ -0,0 +1 @@ +import{aZ as e,a_ as o,ay as t,k as r,aR as s,aw as i,e as n,ax as l,ap as a}from"../../../vendor-element-plus.js?ver=3.1.8";import{aQ as d,W as c,X as u,Z as _,ab as p,a5 as m,a9 as f,aa as v,a6 as h,J as b,a8 as k,Y as g,az as y,av as j}from"../../../vendor.js?ver=3.1.8";import{a as q,F as $}from"../../../FieldEditor.js?ver=3.1.8";import{_ as w,I as F}from"../../../fc-bits-ui.js?ver=3.1.8";import{P as x}from"../../../PromoCard.js?ver=3.1.8";import"../../../fc-bits.js?ver=3.1.8";import"../../../_FormBuilder2.js?ver=3.1.8";import"../../../PhotoWidget.js?ver=3.1.8";import"../../../input-popover-dropdown.js?ver=3.1.8";import"../../../data_config.js?ver=3.1.8";import"../../../_OptionSelector.js?ver=3.1.8";import"../../../_AjaxSelector.js?ver=3.1.8";import"../../../_VerifiedEmailInput.js?ver=3.1.8";import"../../../EmailComposer.js?ver=3.1.8";import"../../../BlockComposer.js?ver=3.1.8";import"../../../EmailPreview.js?ver=3.1.8";import"../../../PreviewIframeBuilder.js?ver=3.1.8";import"../../../TestEmail.js?ver=3.1.8";import"../../../CampaignSubjectLines.js?ver=3.1.8";import"../../../PaginationBar.js?ver=3.1.8";import"../../../_MergeCodes.js?ver=3.1.8";import"../../../BuiltinTemplateDrawer.js?ver=3.1.8";import"../../../_TaxonomyTermsSelector.js?ver=3.1.8";import"../../../_MailerConfig.js?ver=3.1.8";import"../../../ItemCopier.js?ver=3.1.8";const C={class:"fcrm_funnel_import_page"},I={class:"fcrm_funnel_top_nav_wrapper"},S={class:"fcrm_page_header_breadcrumb"},E={class:"fcrm_funnel_import_body fcrm_max_w_800"},V={key:0,class:"fcrm_funnel_import_upload_step"},B={class:"fcrm_funnel_import_card"},N={class:"fcrm_funnel_import_card--header"},P={class:"fcrm_funnel_import_card--header-left"},A={class:"fcrm_funnel_import_card--header-title"},J={class:"fcrm_funnel_import_card--header-description"},O={class:"fcrm_funnel_import_card--body"},T={class:"fcrm_upload_file_box"},U={class:"fcrm_upload_file_box_label"},L={class:"upload-icon"},Q={class:"el-upload__text"},R={key:0},D={key:1,class:"fcrm_funnel_import_root_editor fc_funnel_editor"},M={class:"fcrm_fixed_bottom_actions"},W={key:2,class:"fcrm_funnel_import_root_editor"},Z={class:"fcrm_fixed_bottom_actions"};const z=w({name:"ImportFunnel",components:{PromoCard:x,Icons:F,FieldEditor:$,FormField:q},props:["options"],data:()=>({ArrowRightBold:j(a),sequences:[],funnel:{},blocks:{},block_fields:{},step:"upload",sequence_step:0,importing:!1,inline_errors:!1}),computed:{url(){let e=window.ajaxurl;return e+=(e.match(/\?/)?"&":"?")+jQuery.param({action:"fluentcrm_import_funnel"}),e},current_block(){return this.sequences[this.sequence_step]||!1},current_block_fields(){if(!this.current_block)return{};const e=this.current_block.action_name;return this.block_fields[e]}},methods:{success(e){this.sequences=e.funnel_sequences,this.funnel=e.funnel,this.blocks=e.blocks,this.block_fields=e.block_fields,this.step="root_editor"},error(e){const o=JSON.parse(e.message);this.$notify.error(o.message),o.requires&&(this.inline_errors=o.requires)},showSequenceEditor(e){jQuery("html, body").animate({scrollTop:0},"slow"),this.sequence_step=e,this.step="sequence_editor"},completeImport(){this.importing=!0,this.$post("funnels/import",{funnel:this.funnel,sequences:JSON.stringify(this.sequences)}).then(e=>{this.$notify.success(e.message),this.$router.push({name:"edit_funnel",params:{funnel_id:e.funnel.id}})}).catch(e=>{this.handleError(e)}).finally(()=>{this.importing=!1})}}},[["render",function(a,j,q,$,w,F){const x=e,z=o,G=d("Icons"),X=r,Y=s,H=n,K=i,ee=d("field-editor"),oe=d("form-field"),te=l,re=d("PromoCard"),se=t;return c(),u("div",C,[_("div",I,[_("div",S,[p(z,{class:"fcrm_funnel_breadcrumb","separator-icon":w.ArrowRightBold},{default:m(()=>[p(x,{to:{name:"funnels"}},{default:m(()=>[f(v(a.$t("Automation Funnels")),1)],void 0,!0),_:1}),p(x,null,{default:m(()=>[f(v(a.$t("Import Automation Funnel")),1)],void 0,!0),_:1})],void 0),_:1},8,["separator-icon"])])]),h((c(),u("div",E,[a.has_campaign_pro?(c(),u(b,{key:0},["upload"==w.step?(c(),u("div",V,[_("div",B,[_("div",N,[_("div",P,[_("div",A,v(a.$t("Import Automation")),1),_("div",J,v(a.$t("import_your_exported_automation_json_file")),1)])]),_("div",O,[_("div",T,[_("h3",U,v(a.$t("Upload JSON File")),1),p(Y,{drag:"",limit:1,action:F.url,ref:"uploader",multiple:!1,"on-error":F.error,"on-success":F.success},{default:m(()=>[_("span",L,[p(G,{"icon-name":"upload"})]),_("div",Q,[f(v(a.$t("Drop JSON file here or"))+" ",1),_("em",null,v(a.$t("click to upload")),1)]),p(X,null,{default:m(()=>[f(v(a.$t("Browse File")),1)],void 0,!0),_:1})],void 0),_:1},8,["action","on-error","on-success"])]),w.inline_errors?(c(),u("pre",R,v(w.inline_errors),1)):k("",!0)])])])):"root_editor"==w.step?(c(),u("div",D,[p(ee,{title_badge:a.$t("Trigger"),key:"is_editing_root",show_controls:!1,options:q.options,data:w.funnel.settings,settings:w.funnel.settingsFields},{after_header:m(()=>[p(K,{label:a.$t("Funnel Name")},{default:m(()=>[p(H,{placeholder:a.$t("Funnel Name"),modelValue:w.funnel.title,"onUpdate:modelValue":j[0]||(j[0]=e=>w.funnel.title=e)},null,8,["placeholder","modelValue"])],void 0,!0),_:1},8,["label"])]),_:1},8,["title_badge","options","data","settings"]),a.isEmptyValue(w.funnel.conditions)?k("",!0):(c(),g(te,{key:0,class:"fcrm_funnel_conditions fcrm_funnel_editor_boxed_with_border","label-position":"top",data:w.funnel.conditions},{default:m(()=>[_("h3",null,v(a.$t("Conditions")),1),(c(!0),u(b,null,y(w.funnel.conditionFields,(e,o)=>(c(),u("div",{key:o},[(c(),g(oe,{key:o,modelValue:w.funnel.conditions[o],"onUpdate:modelValue":e=>w.funnel.conditions[o]=e,field:e,options:q.options},null,8,["modelValue","onUpdate:modelValue","field","options"]))]))),128))],void 0),_:1},8,["data"])),_("div",M,[p(X,{onClick:j[1]||(j[1]=e=>F.showSequenceEditor(0)),type:"primary"},{default:m(()=>[f(v(a.$t("Next")),1)],void 0),_:1})])])):"sequence_editor"==w.step&&F.current_block?(c(),u("div",W,[(c(),g(ee,{title_badge:F.current_block.type,show_controls:!1,data:F.current_block.settings,options:q.options,key:w.sequence_step+"_"+F.current_block.action_name,settings:F.current_block_fields},{after_header:m(()=>[p(K,{label:a.$t("Internal Label")},{default:m(()=>[p(H,{placeholder:a.$t("Internal Label"),modelValue:F.current_block.title,"onUpdate:modelValue":j[2]||(j[2]=e=>F.current_block.title=e)},null,8,["placeholder","modelValue"])],void 0,!0),_:1},8,["label"])]),_:1},8,["title_badge","data","options","settings"])),_("div",Z,[w.sequence_step>0?(c(),g(X,{key:0,onClick:j[3]||(j[3]=e=>F.showSequenceEditor(w.sequence_step-1))},{default:m(()=>[f(v(a.$t("Go Back")),1)],void 0),_:1})):k("",!0),w.sequence_step+1F.showSequenceEditor(w.sequence_step+1)),type:"primary"},{default:m(()=>[f(v(a.$t("Next"))+" "+v(w.sequence_step+1)+" / "+v(w.sequences.length),1)],void 0),_:1})):(c(),g(X,{key:2,onClick:j[5]||(j[5]=e=>F.completeImport()),type:"primary"},{default:m(()=>[f(v(a.$t("Complete Import")),1)],void 0),_:1}))])])):k("",!0)],64)):(c(),g(re,{key:1,heading:a.$t("Import Funnel From JSON File"),description:a.$t("importing_funnel_from_json_file")},null,8,["heading","description"]))])),[[se,w.importing]])])}]]);export{z as default}; diff --git a/wp-content/plugins/fluent-crm/assets/admin/Modules/Funnels/parts/_LazyIndividualProgress.js b/wp-content/plugins/fluent-crm/assets/admin/Modules/Funnels/parts/_LazyIndividualProgress.js new file mode 100644 index 0000000..7a2a93c --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/admin/Modules/Funnels/parts/_LazyIndividualProgress.js @@ -0,0 +1 @@ +import{P as t,aB as e,bj as s,bi as i,aJ as n,aQ as a,aO as o}from"../../../../vendor-element-plus.js?ver=3.1.8";import{c6 as l,aQ as r,W as c,X as d,ab as u,a5 as f,Z as _,aa as p,J as h,az as m,Y as b,a0 as y,a9 as g,a8 as v}from"../../../../vendor.js?ver=3.1.8";import{_ as $}from"../../../../fc-bits-ui.js?ver=3.1.8";const k={key:0,class:"fc_loading"},w={key:1,style:{padding:"10px 15px"},class:"fc_individual_progress"},z={style:{"margin-bottom":"15px"}},I={class:"fluentcrm_clickable"};const L=$({name:"LazyIndividualProgress",props:["subscriber_id","funnel"],data:()=>({funnel_subscriber:{},sequences:[],loading:!0,isLoaded:!1,MoreFilledIcon:t}),computed:{keyedMetrics(){return this.isLoaded?l(this.funnel_subscriber.metrics,"sequence_id"):{}},timelines(){if(!this.isLoaded)return[];let t=this.$t("Entrance (%s)",this.funnel.title);"pending"===this.funnel_subscriber.status?t+=this.$t("_In_Wfdoc"):"waiting"===this.funnel_subscriber.status&&(t+=this.$t("_In_Wfna"));const e=[{content:t,timestamp:this.nsHumanDiffTime(this.funnel_subscriber.created_at),size:"large",type:"primary",icon:this.MoreFilledIcon}];let s=!1;return this.each(this.sequences,(t,i)=>{const n=this.keyedMetrics[t.id]||{};let a=t.title;"pending"===a?a+=this.$t("_In_Wfdc"):"waiting"===a&&(a+=this.$t("_In_Wfna")),"conditional"==t.type&&(s=t);let o="";if(t.condition_type){o="fc_path_"+t.condition_type,s&&(o="fc_"+s.action_name+" "+o);let e=this.$t("Condition:")+" "+t.condition_type;if("funnel_ab_testing"==s.action_name){const s="yes"==t.condition_type?this.$t("B"):this.$t("A");e=this.$t("Path: ")+s,o+="_"+s}a+=" ( "+e+" )"}n.status||(o+=" fc_timeline_empty"),e.push({content:a,status:n.status,notes:n.notes,timestamp:this.nsHumanDiffTime(n.created_at),color:this.getTimelineColor(n),wrapper_class:o})}),e}},methods:{getTimelineColor:t=>t.status&&"completed"===t.status?"#0bbd87":"",fetchData(){this.loading=!0,this.$get(`funnels/${this.funnel.id}/subscribers/${this.subscriber_id}`).then(t=>{this.funnel_subscriber=t.funnel_subscriber,this.sequences=t.sequences}).catch(t=>{this.handleError(t)}).finally(()=>{this.loading=!1,this.isLoaded=!0})},maybeFetchData(){this.isLoaded||this.fetchData()}}},[["render",function(t,l,$,L,D,q){const W=e,j=a,x=n,C=i,F=s,M=r("router-link"),T=o;return c(),d("div",null,[u(T,{placement:"right",onShow:l[0]||(l[0]=t=>q.maybeFetchData()),width:"400","popper-class":"fc_individual_progress_popover",trigger:"click"},{reference:f(()=>[_("span",I,p($.funnel.title),1)]),default:f(()=>[_("div",null,[D.loading?(c(),d("div",k,[u(W,{loading:!0,rows:3})])):D.isLoaded?(c(),d("div",w,[_("div",z,p(t.$t("Current Status:"))+" "+p(D.funnel_subscriber.status),1),u(F,null,{default:f(()=>[(c(!0),d(h,null,m(q.timelines,(t,e)=>(c(),b(C,{key:e,class:y(t.wrapper_class),icon:t.icon,type:t.type,color:t.color,size:t.size,timestamp:t.timestamp},{default:f(()=>[g(p(t.content)+" ",1),_("template",null,[t.notes?(c(),b(x,{key:0,class:"item",effect:"dark",content:t.notes,placement:"top-start"},{default:f(()=>[u(j,{size:"small",type:"info"},{default:f(()=>[g(p(t.status),1)],void 0,!0),_:2},1024)],void 0,!0),_:2},1032,["content"])):t.status?(c(),b(j,{key:1,size:"small",type:"info"},{default:f(()=>[g(p(t.status),1)],void 0,!0),_:2},1024)):v("",!0)])],void 0,!0),_:2},1032,["class","icon","type","color","size","timestamp"]))),128))],void 0,!0),_:1}),u(M,{to:{name:"edit_funnel",params:{funnel_id:this.funnel.id}}},{default:f(()=>[g(p(t.$t("View Automation Steps")),1)],void 0,!0),_:1},8,["to"])])):v("",!0)])],void 0),_:1})])}]]);export{L as default}; diff --git a/wp-content/plugins/fluent-crm/assets/admin/Modules/Importer/Importer.js b/wp-content/plugins/fluent-crm/assets/admin/Modules/Importer/Importer.js new file mode 100644 index 0000000..b315278 --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/admin/Modules/Importer/Importer.js @@ -0,0 +1 @@ +import{q as e,a$ as l,be as t,bf as a,aD as s,aA as i,aR as d,E as o,e as n,aK as c,aL as u,k as v,az as r,aG as p,aH as m,aI as h,ax as f,aw as b,aE as g,aF as C,b0 as k}from"../../../vendor-element-plus.js?ver=3.1.8";import{aQ as y,W as _,X as $,ab as V,a5 as U,Z as x,aa as T,a9 as S,a8 as D,J as I,az as R,Y as A}from"../../../vendor.js?ver=3.1.8";import{_ as L}from"../../../fc-bits-ui.js?ver=3.1.8";const M={key:0,style:{"text-align":"center"}},z={class:"el-upload__text"},F={key:1},j={style:{color:"var(--fc-secondary-text)"}},w={style:{color:"var(--fc-secondary-text)"}},E={style:{"margin-top":"10px"}},N={key:0,style:{"text-align":"center"}},P={key:1},W={style:{"margin-top":"10px"}};const B=L({name:"Importer",components:{UploadFilled:e},data:()=>({activeTab:"Csv",activeCsv:1,activeUser:1,uploadedFile:null,uploadUrl:window.ajaxurl+"?action=fluentcrm-post-csv-upload",tags:[],selectedTags:[],lists:[],selectedLists:[],csvColumns:[],tableColumns:[],csvMapping:[],roles:[],tableData:[],checkAll:!1,isIndeterminate:!1,selectedRoles:[],tableColumn:["date","name","address"],IsDataInDatabase:""}),methods:{tabClicked(e,l){this.activeTab=e.name},next(){const e="active"+this.activeTab;("Csv"!==this.activeTab||1!==this.active||this.uploadedFile)&&("User"!==this.activeTab||1!==this[e]||this.selectedRoles.length)&&this.active++>2&&(this[e]=0)},prev(){const e="active"+this.activeTab;0!==this[e]&&this[e]--},fileUploaded(e,l,t){this.csvColumns=e.headers,this.tableColumns=e.columns,this.uploadedFile=e.file;const a=[];for(const s in this.csvColumns)a.push({csv:this.csvColumns[s],table:null});this.csvMapping=a},fileRemoved(e,l){this.uploadedFile=null},confirmCsv(){const e=this.csvMapping.filter(e=>e.table);e.length?this.$post("import/csv-import",{mappings:e,tags:this.selectedTags,lists:this.selectedLists,file:this.uploadedFile}).then(e=>{this.$notify({title:this.$t("Success"),type:"success",offset:20,message:this.$t("Map_Subscribers_is")})}).catch(e=>console.log(e)):this.$notify({title:this.$t("Warning"),type:"warning",offset:20,message:this.$t("No mapping found.")})},handleCheckAllChange(e){this.selectedRoles=e?Object.keys(this.roles):[],this.isIndeterminate=!1},fluentcrmCheckedRolesChange(e){const l=Object.keys(this.roles),t=e.length;this.checkAll=t===l.length,this.isIndeterminate=t>0&&t{this.tags=e}).catch(e=>this.handleError(e)),this.$get("lists").then(e=>{this.lists=e}).catch(e=>this.handleError(e)),this.$get("reports/roles").then(e=>{this.roles=e.roles}).catch(e=>this.handleError(e))}},[["render",function(e,L,B,O,Y,q){const G=a,H=t,J=y("UploadFilled"),K=o,Q=d,X=i,Z=s,ee=n,le=u,te=c,ae=v,se=l,ie=r,de=p,oe=h,ne=m,ce=b,ue=f,ve=C,re=g,pe=k;return _(),$("div",null,[V(pe,{type:"border-card",onTabClick:q.tabClicked,modelValue:Y.activeTab,"onUpdate:modelValue":L[7]||(L[7]=e=>Y.activeTab=e)},{default:U(()=>[V(se,{label:e.$t("CSV"),name:"Csv"},{default:U(()=>[V(H,{active:Y.activeCsv,"finish-status":"success","align-center":""},{default:U(()=>[V(G,{title:e.$t("Step 1")},null,8,["title"]),V(G,{title:e.$t("Step 2")},null,8,["title"])],void 0,!0),_:1},8,["active"]),1===Y.activeCsv?(_(),$("div",M,[x("h3",null,T(e.$t("Upload Your CSV file"))+": ",1),V(Z,null,{default:U(()=>[V(X,null,{default:U(()=>[V(Q,{drag:"",accept:".csv",limit:1,action:Y.uploadUrl,"on-success":q.fileUploaded,"on-remove":q.fileRemoved},{default:U(()=>[V(K,null,{default:U(()=>[V(J)],void 0,!0),_:1}),x("div",z,[S(T(e.$t("Drop file here or"))+" ",1),x("em",null,T(e.$t("click to upload")),1)])],void 0,!0),_:1},8,["action","on-success","on-remove"])],void 0,!0),_:1})],void 0,!0),_:1})])):D("",!0),2===Y.activeCsv?(_(),$("div",F,[(_(!0),$(I,null,R(Y.csvMapping,(l,t)=>(_(),A(Z,{gutter:20,key:t},{default:U(()=>[V(X,{xs:12,sm:12,md:12,lg:12,xl:12},{default:U(()=>[V(ee,{readonly:"",modelValue:Y.csvMapping[t].csv,"onUpdate:modelValue":e=>Y.csvMapping[t].csv=e},null,8,["modelValue","onUpdate:modelValue"])],void 0,!0),_:2},1024),V(X,{xs:12,sm:12,md:12,lg:12,xl:12},{default:U(()=>[V(te,{modelValue:Y.csvMapping[t].table,"onUpdate:modelValue":e=>Y.csvMapping[t].table=e,placeholder:e.$t("Select")},{default:U(()=>[(_(!0),$(I,null,R(Y.tableColumns,e=>(_(),A(le,{key:e,label:e,value:e},null,8,["label","value"]))),128))],void 0,!0),_:1},8,["modelValue","onUpdate:modelValue","placeholder"])],void 0,!0),_:2},1024)],void 0,!0),_:2},1024))),128)),V(Z,{style:{"margin-top":"10px"}},{default:U(()=>[x("div",j,T(e.$t("Select Tags")),1),x("div",null,[V(te,{modelValue:Y.selectedTags,"onUpdate:modelValue":L[0]||(L[0]=e=>Y.selectedTags=e),multiple:"",placeholder:e.$t("Select")},{default:U(()=>[(_(!0),$(I,null,R(Y.tags,e=>(_(),A(le,{key:e.id,label:e.title,value:e.id},null,8,["label","value"]))),128))],void 0,!0),_:1},8,["modelValue","placeholder"])])],void 0,!0),_:1}),V(Z,null,{default:U(()=>[x("div",w,T(e.$t("Select Lists")),1),x("div",null,[V(te,{modelValue:Y.selectedLists,"onUpdate:modelValue":L[1]||(L[1]=e=>Y.selectedLists=e),multiple:"",placeholder:e.$t("Select")},{default:U(()=>[(_(!0),$(I,null,R(Y.lists,e=>(_(),A(le,{key:e.id,label:e.title,value:e.id},null,8,["label","value"]))),128))],void 0,!0),_:1},8,["modelValue","placeholder"])])],void 0,!0),_:1})])):D("",!0),x("div",E,[Y.activeCsv>1?(_(),A(ae,{key:0,onClick:q.prev,size:"small"},{default:U(()=>[S(T(e.$t("Prev step")),1)],void 0,!0),_:1},8,["onClick"])):D("",!0),Y.activeCsv<2?(_(),A(ae,{key:1,onClick:q.next,size:"small"},{default:U(()=>[S(T(e.$t("Next step")),1)],void 0,!0),_:1},8,["onClick"])):D("",!0),V(ae,{onClick:q.confirmCsv,disabled:2!==Y.activeCsv,size:"small",type:"primary",style:{float:"right"}},{default:U(()=>[S(T(e.$t("Confirm")),1)],void 0,!0),_:1},8,["onClick","disabled"])])],void 0,!0),_:1},8,["label"]),V(se,{label:e.$t("WP Users"),name:"User"},{default:U(()=>[V(H,{active:Y.activeUser,"finish-status":"success","align-center":""},{default:U(()=>[V(G,{title:e.$t("Step 1")},null,8,["title"]),V(G,{title:e.$t("Step 2")},null,8,["title"])],void 0,!0),_:1},8,["active"]),1===Y.activeUser?(_(),$("div",N,[x("h3",null,T(e.$t("Select by Roles"))+": ",1),V(Z,null,{default:U(()=>[V(ie,{modelValue:Y.checkAll,"onUpdate:modelValue":L[2]||(L[2]=e=>Y.checkAll=e),indeterminate:Y.isIndeterminate,onChange:q.handleCheckAllChange},{default:U(()=>[S(T(e.$t("All")),1)],void 0,!0),_:1},8,["modelValue","indeterminate","onChange"]),L[8]||(L[8]=x("div",{style:{margin:"15px 0"}},null,-1)),V(de,{class:"fluentcrm-subscribers-import-check",modelValue:Y.selectedRoles,"onUpdate:modelValue":L[3]||(L[3]=e=>Y.selectedRoles=e),onChange:q.fluentcrmCheckedRolesChange},{default:U(()=>[(_(!0),$(I,null,R(Y.roles,(e,l)=>(_(),A(ie,{value:l,key:l},{default:U(()=>[S(T(e.name),1)],void 0,!0),_:2},1032,["value"]))),128))],void 0,!0),_:1},8,["modelValue","onChange"])],void 0,!0),_:1})])):D("",!0),2===Y.activeUser?(_(),$("div",P,[V(Z,null,{default:U(()=>[x("h4",null,T(e.$t("User data from database")),1),V(ne,{"empty-text":e.$t("No Data Available"),data:Y.tableData,style:{width:"100%"}},{default:U(()=>[(_(!0),$(I,null,R(Y.tableColumn,(e,l)=>(_(),A(oe,{prop:e,key:l,label:e,sortable:"",width:"auto"},null,8,["prop","label"]))),128))],void 0,!0),_:1},8,["empty-text","data"]),V(ue,null,{default:U(()=>[V(ce,{label:e.$t("Tags")},{default:U(()=>[V(te,{modelValue:Y.selectedTags,"onUpdate:modelValue":L[4]||(L[4]=e=>Y.selectedTags=e),multiple:"",placeholder:e.$t("Select")},{default:U(()=>[(_(!0),$(I,null,R(Y.tags,e=>(_(),A(le,{key:e.slug,label:e.title,value:e.slug},null,8,["label","value"]))),128))],void 0,!0),_:1},8,["modelValue","placeholder"])],void 0,!0),_:1},8,["label"]),V(ce,{label:e.$t("Lists")},{default:U(()=>[V(te,{modelValue:Y.selectedLists,"onUpdate:modelValue":L[5]||(L[5]=e=>Y.selectedLists=e),multiple:"",placeholder:e.$t("Select")},{default:U(()=>[(_(!0),$(I,null,R(Y.lists,e=>(_(),A(le,{key:e.slug,label:e.title,value:e.slug},null,8,["label","value"]))),128))],void 0,!0),_:1},8,["modelValue","placeholder"])],void 0,!0),_:1},8,["label"])],void 0,!0),_:1}),V(re,{class:"fluentcrm-subscribers-import-radio",modelValue:Y.IsDataInDatabase,"onUpdate:modelValue":L[6]||(L[6]=e=>Y.IsDataInDatabase=e)},{default:U(()=>[x("ul",null,[x("li",null,[V(ve,{value:"skip"},{default:U(()=>[S(T(e.$t("Skip if already in DB")),1)],void 0,!0),_:1})]),x("li",null,[V(ve,{value:"update"},{default:U(()=>[S(T(e.$t("Update if already in DB")),1)],void 0,!0),_:1})])])],void 0,!0),_:1},8,["modelValue"])],void 0,!0),_:1})])):D("",!0),x("div",W,[Y.activeUser>1?(_(),A(ae,{key:0,onClick:q.prev,size:"small"},{default:U(()=>[S(T(e.$t("Prev step")),1)],void 0,!0),_:1},8,["onClick"])):D("",!0),Y.activeUser<2?(_(),A(ae,{key:1,onClick:q.next,size:"small"},{default:U(()=>[S(T(e.$t("Next step")),1)],void 0,!0),_:1},8,["onClick"])):D("",!0),V(ae,{onClick:q.confirmCsv,disabled:2!==Y.activeUser,size:"small",type:"primary",style:{float:"right"}},{default:U(()=>[S(T(e.$t("Confirm")),1)],void 0,!0),_:1},8,["onClick","disabled"])])],void 0,!0),_:1},8,["label"])],void 0),_:1},8,["onTabClick","modelValue"])])}]]);export{B as default}; diff --git a/wp-content/plugins/fluent-crm/assets/admin/Modules/Migrator/Home.js b/wp-content/plugins/fluent-crm/assets/admin/Modules/Migrator/Home.js new file mode 100644 index 0000000..97d4403 --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/admin/Modules/Migrator/Home.js @@ -0,0 +1 @@ +import{ay as e,bf as t,be as i,aF as s,aE as r,k as a,aw as l,aK as n,aL as o,az as d,aD as _,aA as c,ax as p}from"../../../vendor-element-plus.js?ver=3.1.8";import{aQ as m,W as u,X as v,Z as g,aa as f,a6 as h,ab as y,a5 as b,J as k,az as $,Y as V,a0 as M,a9 as C,a8 as x}from"../../../vendor.js?ver=3.1.8";import{C as R,I as j,a as w,T as D}from"../../../_ImportRunner.js?ver=3.1.8";import{O as T}from"../../../_OptionSelector.js?ver=3.1.8";import{_ as F}from"../../../fc-bits-ui.js?ver=3.1.8";import"../../../_FormBuilder2.js?ver=3.1.8";import"../../../PhotoWidget.js?ver=3.1.8";import"../../../input-popover-dropdown.js?ver=3.1.8";import"../../../data_config.js?ver=3.1.8";import"../../../_AjaxSelector.js?ver=3.1.8";import"../../../_VerifiedEmailInput.js?ver=3.1.8";const I={class:"fluentcrm-settings fluentcrm_min_bg fluentcrm_view"},L={class:"fluentcrm_header"},S={class:"fluentcrm_header_title"},O={style:{"max-width":"1190px",margin:"0 auto"},class:"fluentcrm_pad_around"},U={class:"settings-section fluentcrm_databox"},A={key:0,class:"fc_crm_selection_step"},E={class:"fc_step_header"},H={class:"fc_crm_lists"},J=["src"],N={class:"fc_text_label"},P={key:0},W=["href"],z={class:"text-align-right"},B={key:1,class:"fc_crm_selection_step"},K={key:2,class:"fc_crm_selection_step"},Q={class:"fc_step_header"},X={key:0,style:{"margin-bottom":"20px"}},Y=["innerHTML"],Z={style:{"margin-top":"20px"},class:"text-align-right"},q={key:3,class:"fc_crm_selection_step"};const G=F({name:"CRMMigrator",components:{TagMapper:D,ContactFieldMapper:w,OptionSelector:T,ImportRunner:j,CredentialVerify:R},data:()=>({loading:!1,drivers:{},selected_driver:"",step:1,cred:{api_key:""},segment_options:{lists:[],tags:[],contact_fields:[]},map_settings:{list_id:"",local_list_id:"",local_tag_id:"",import_silently:"yes",import_active_only:"yes"},import_summary:{}}),computed:{current_driver(){return this.drivers[this.selected_driver]||{}}},methods:{getDrivers(){this.loading=!0,this.$get("migrators").then(e=>{this.drivers=e.drivers}).catch(e=>{this.handleError(e)}).finally(()=>{this.loading=!1})},back(){this.step-=1},selectDriver(){this.cred=JSON.parse(JSON.stringify(this.current_driver.credentials)),this.step=2},credentialVerified(){this.listMappings(),this.step=3},listMappings(){this.loading=!0,this.$get("migrators/list-tag-mappings",{driver:this.selected_driver,credential:this.cred,map_settings:this.map_settings}).then(e=>{this.segment_options=e.options}).catch(e=>{this.handleError(e)}).finally(()=>{this.loading=!1})},maybeRefetchTags(){this.current_driver.refresh_on_list_change&&this.listMappings()}},mounted(){this.getDrivers()}},[["render",function(R,j,w,D,T,F){const G=t,ee=i,te=s,ie=r,se=a,re=m("credential-verify"),ae=o,le=n,ne=l,oe=m("tag-mapper"),de=d,_e=m("contact-field-mapper"),ce=m("option-selector"),pe=c,me=_,ue=p,ve=m("import-runner"),ge=e;return u(),v("div",I,[g("div",L,[g("div",S,[g("h3",null,f(R.$t("Transfer Data From Other CRM")),1)]),j[12]||(j[12]=g("div",{class:"fluentcrm-templates-action-buttons fluentcrm-actions"},null,-1))]),h((u(),v("div",O,[y(ee,{active:T.step,"align-center":""},{default:b(()=>[y(G,{title:R.$t("CRM"),description:R.$t("Select Current CRM")},null,8,["title","description"]),y(G,{title:R.$t("Connect"),description:R.$t("Connect with your CRM")},null,8,["title","description"]),y(G,{title:R.$t("Map Data"),description:R.$t("Map the data")},null,8,["title","description"]),y(G,{title:R.$t("Review"),description:R.$t("Review & Import")},null,8,["title","description"])],void 0),_:1},8,["active"]),g("div",U,[1==T.step?(u(),v("div",A,[g("div",E,[g("h3",null,f(R.$t("select_current_crm_software")),1),g("p",null,f(R.$t("transfer_data_from_current_ems_to_fluentcrm")),1)]),g("div",H,[y(ie,{class:"sources fc_inline_image_radio",modelValue:T.selected_driver,"onUpdate:modelValue":j[0]||(j[0]=e=>T.selected_driver=e)},{default:b(()=>[(u(!0),v(k,null,$(T.drivers,(e,t)=>(u(),V(te,{class:M(["fc_driver_"+t,"option"]),key:t,label:t},{default:b(()=>[g("img",{style:{width:"80px",height:"80px"},src:e.logo},null,8,J),g("span",N,f(e.title),1)],void 0,!0),_:2},1032,["class","label"]))),128))],void 0),_:1},8,["modelValue"])]),F.current_driver.doc_url?(u(),v("p",P,[g("a",{style:{"text-decoration":"underline"},target:"_blank",rel:"noopener",href:F.current_driver.doc_url},f(R.$t("Check the documentation")),9,W),C(" "+f(R.$t("for migrating from"))+" ",1),g("b",null,f(F.current_driver.title),1)])):x("",!0),g("div",z,[y(se,{onClick:j[1]||(j[1]=e=>F.selectDriver()),disabled:!T.selected_driver,type:"primary"},{default:b(()=>[C(f(R.$t("Next")),1)],void 0),_:1},8,["disabled"])])])):2==T.step?(u(),v("div",B,[y(re,{driver:T.selected_driver,current_driver:F.current_driver,onVerified:j[2]||(j[2]=e=>F.credentialVerified()),onBack:j[3]||(j[3]=e=>F.back()),cred:T.cred},null,8,["driver","current_driver","cred"])])):3==T.step?(u(),v("div",K,[g("div",Q,[g("h3",null,f(R.$t("Map your Data")),1),g("p",null,f(R.$t("Please configure"))+" "+f(F.current_driver.title)+" "+f(R.$t("associate data with FluentCRM")),1)]),y(ue,{"label-position":"top",data:T.map_settings},{default:b(()=>[F.current_driver.supports.lists?(u(),V(ne,{key:0,label:R.$t("Select List")},{default:b(()=>[y(le,{onChange:j[4]||(j[4]=e=>F.maybeRefetchTags()),modelValue:T.map_settings.list_id,"onUpdate:modelValue":j[5]||(j[5]=e=>T.map_settings.list_id=e)},{default:b(()=>[(u(!0),v(k,null,$(T.segment_options.lists,e=>(u(),V(ae,{key:e.id,value:e.id,label:e.name},null,8,["value","label"]))),128))],void 0,!0),_:1},8,["modelValue"])],void 0,!0),_:1},8,["label"])):F.current_driver.supports.has_list_mapper&&T.segment_options.mapped_lists&&T.segment_options.mapped_lists.length?(u(),V(ne,{key:1,label:R.$t("Map List")},{default:b(()=>[y(oe,{option_key:"lists",item_label:"List",driver:T.selected_driver,current_driver:F.current_driver,tag_options:T.segment_options.mapped_lists},null,8,["driver","current_driver","tag_options"])],void 0,!0),_:1},8,["label"])):x("",!0),T.segment_options.all_ready?(u(),v(k,{key:2},[F.current_driver.supports.auto_tag_mapper?(u(),v("div",X,[y(de,{modelValue:T.segment_options.auto_mapping,"onUpdate:modelValue":j[6]||(j[6]=e=>T.segment_options.auto_mapping=e),"true-value":"yes"},{default:b(()=>[C("Automatically map tags from "+f(F.current_driver.title)+" in FluentCRM",1)],void 0,!0),_:1},8,["modelValue"])])):x("",!0),T.segment_options.tags.length&&"yes"!=T.segment_options.auto_mapping?(u(),V(ne,{key:1,label:R.$t("Map Tags")},{default:b(()=>[y(oe,{option_key:"tags",item_label:"Tag",driver:T.selected_driver,current_driver:F.current_driver,tag_options:T.segment_options.tags},null,8,["driver","current_driver","tag_options"])],void 0,!0),_:1},8,["label"])):x("",!0),T.segment_options.contact_fields&&T.segment_options.contact_fields.length?(u(),V(ne,{key:2,label:R.$t("Map Contact Fields")},{default:b(()=>[y(_e,{driver:T.selected_driver,contact_fields:T.segment_options.contact_fields,contact_fillables:T.segment_options.contact_fillables},null,8,["driver","contact_fields","contact_fillables"])],void 0,!0),_:1},8,["label"])):x("",!0),F.current_driver.field_map_info?(u(),v("p",{key:3,style:{"margin-bottom":"50px"},innerHTML:F.current_driver.field_map_info},null,8,Y)):x("",!0),y(me,{gutter:30},{default:b(()=>[y(pe,{md:12,sm:24},{default:b(()=>[y(ne,{label:R.$t("Assigned List in FluentCRM (optional)")},{default:b(()=>[y(ce,{modelValue:T.map_settings.local_list_id,"onUpdate:modelValue":j[7]||(j[7]=e=>T.map_settings.local_list_id=e),field:{is_multiple:!1,creatable:!0,option_key:"lists"}},null,8,["modelValue"]),g("p",null,f(R.$t("Will be applied to all the imported contacts")),1)],void 0,!0),_:1},8,["label"])],void 0,!0),_:1}),F.current_driver.supports.empty_tags?(u(),V(pe,{key:0,md:12,sm:24},{default:b(()=>[y(ne,{label:R.$t("Default Tag ID (optional)")},{default:b(()=>[y(ce,{modelValue:T.map_settings.local_tag_id,"onUpdate:modelValue":j[8]||(j[8]=e=>T.map_settings.local_tag_id=e),field:{is_multiple:!1,creatable:!0,option_key:"tags"}},null,8,["modelValue"]),g("p",null,f(R.$t("Home.Default_tag_id.instruction"))+" "+f(T.selected_driver),1)],void 0,!0),_:1},8,["label"])],void 0,!0),_:1})):x("",!0)],void 0,!0),_:1}),F.current_driver.supports.active_imports_only?(u(),V(ne,{key:4},{default:b(()=>[y(de,{"true-value":"yes","false-value":"no",modelValue:T.map_settings.import_active_only,"onUpdate:modelValue":j[9]||(j[9]=e=>T.map_settings.import_active_only=e)},{default:b(()=>[C(f(R.$t("Import only active subscribers from"))+" "+f(T.selected_driver),1)],void 0,!0),_:1},8,["modelValue"])],void 0,!0),_:1})):x("",!0),g("div",Z,[y(se,{disabled:T.loading,onClick:j[10]||(j[10]=e=>T.step++),type:"primary"},{default:b(()=>[C(f(R.$t("Continue [Review and Import]")),1)],void 0,!0),_:1},8,["disabled"])])],64)):x("",!0)],void 0),_:1},8,["data"])])):4==T.step?(u(),v("div",q,[y(ve,{onPrev:j[11]||(j[11]=()=>{T.step=3}),driver:T.selected_driver,credential:T.cred,contact_fields:T.segment_options.contact_fields,segment_options:T.segment_options,map_settings:T.map_settings},null,8,["driver","credential","contact_fields","segment_options","map_settings"])])):x("",!0)])])),[[ge,T.loading]])])}]]);export{G as default}; diff --git a/wp-content/plugins/fluent-crm/assets/admin/Modules/Profile/Parts/ProfileEmails.js b/wp-content/plugins/fluent-crm/assets/admin/Modules/Profile/Parts/ProfileEmails.js new file mode 100644 index 0000000..966cb36 --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/admin/Modules/Profile/Parts/ProfileEmails.js @@ -0,0 +1 @@ +import{_ as e}from"../../../../fc-bits.js?ver=3.1.8";import{P as t,aH as a,aI as i,j as s,h as l,i as o,ay as n,aO as r,k as c,W as d,aJ as m,E as u,Z as _,bk as p,bl as h,aD as g,aA as f,g as b,o as v,c as w,_ as y,T as k,a6 as $,G as C,z as S,aP as E,aE as V,aF as T,ax as P,az as x,aT as F}from"../../../../vendor-element-plus.js?ver=3.1.8";import{aQ as q,W as A,Y as j,a5 as L,ab as M,a6 as z,X as D,aa as B,a8 as R,Z as H,b2 as I,ax as N,a9 as O,bW as U,a0 as W,J as Y,az as J,ac as Z}from"../../../../vendor.js?ver=3.1.8";import{P as K}from"../../../../PaginationBar.js?ver=3.1.8";import{D as Q}from"../../../../DataTable.js?ver=3.1.8";import{F as G}from"../../../../FloatingBulkActionShell.js?ver=3.1.8";import{C as X}from"../../../../Confirm.js?ver=3.1.8";import{O as ee}from"../../../../_OptionSelector.js?ver=3.1.8";import{B as te}from"../../../../Badge.js?ver=3.1.8";import{_ as ae,I as ie,a as se,T as le}from"../../../../fc-bits-ui.js?ver=3.1.8";import{S as oe}from"../../../../_StepPicker.js?ver=3.1.8";import{E as ne}from"../../../../EmailComposer.js?ver=3.1.8";import{M as re}from"../../../../_MailerConfig.js?ver=3.1.8";import"../../../../BlockComposer.js?ver=3.1.8";import"../../../../input-popover-dropdown.js?ver=3.1.8";import"../../../../_FormBuilder2.js?ver=3.1.8";import"../../../../PhotoWidget.js?ver=3.1.8";import"../../../../data_config.js?ver=3.1.8";import"../../../../_AjaxSelector.js?ver=3.1.8";import"../../../../_VerifiedEmailInput.js?ver=3.1.8";import"../../../../EmailPreview.js?ver=3.1.8";import"../../../../PreviewIframeBuilder.js?ver=3.1.8";import"../../../../TestEmail.js?ver=3.1.8";import"../../../../CampaignSubjectLines.js?ver=3.1.8";import"../../../../_MergeCodes.js?ver=3.1.8";import"../../../../BuiltinTemplateDrawer.js?ver=3.1.8";import"../../../../PromoCard.js?ver=3.1.8";const ce={class:"fcrm_table_header_inner_left_title"},de={class:"fcrm_contact_popover_header"},me={class:"fcrm_contact_popover_body fcrm_popover_email_section"},ue={key:0},_e=["title"],pe={key:0},he=["title"],ge={key:2},fe=["aria-label"],be={class:"el-popover__reference"},ve={class:"icon"};const we=ae({name:"ProfileEmailSequence",components:{Icons:ie,Badge:te,Confirm:X,PaginationBar:K,DataTable:Q,OptionSelector:ee,MoreFilled:t},props:["subscriber_id"],data:()=>({sequences:[],loading:!1,removing:!1,pagination:{total:0,per_page:10,current_page:1},selected_sequence:"",doing_action:!1}),methods:{fetch(){this.loading=!0,this.$get(`sequences/subscriber/${this.subscriber_id}/sequences`,{per_page:this.pagination.per_page,page:this.pagination.current_page}).then(e=>{this.sequences=e.sequence_trackers.data,this.pagination.total=e.sequence_trackers.total}).catch(e=>{this.handleError(e)}).finally(()=>{this.loading=!1})},removeFromSequence(e,t){this.removing=!0,this.$del(`sequences/${e}/subscribers`,{tracker_ids:[t]}).then(e=>{this.$notify.success(e.message),this.fetch()}).catch(e=>{this.handleError(e)}).finally(()=>{this.removing=!1})},addToSequence(){this.selected_sequence?(this.doing_action=!0,this.$post("subscribers/do-bulk-action",{action_name:"add_to_email_sequence",new_status:this.selected_sequence,subscriber_ids:[this.subscriber_id]}).then(e=>{var t;null==(t=this.$refs.popover)||t.hide(),this.selected_sequence="",this.$notify.success(e.message),this.fetch()}).catch(e=>{this.handleError(e)}).finally(()=>{this.doing_action=!1})):this.$notify.warning({title:this.$t("Warning"),message:this.$t("Please select an email sequence"),offset:19})}},mounted(){this.fetch()}},[["render",function(e,t,d,m,u,_){const p=c,h=q("option-selector"),g=r,f=i,b=q("Badge"),v=q("Icons"),w=q("confirm"),y=o,k=l,$=s,C=a,S=q("pagination-bar"),E=q("data-table"),V=n;return A(),j(E,{wrapper_border:!0,"has-selection":!1,class:"fcrm_mb_24"},{"header-left":L(()=>[H("h3",ce,B(e.$t("Email Sequences")),1)]),"header-actions":L(()=>[M(g,{ref:"popover",placement:"left",width:"350","hide-after":0,trigger:"click","popper-class":"fcrm_contact_popover"},{reference:L(()=>[M(p,{size:"small"},{default:L(()=>[O(B(e.$t("Add Sequence")),1)],void 0,!0),_:1})]),default:L(()=>[H("div",de,[H("h3",null,B(e.$t("Select Sequence")),1)]),H("div",me,[M(h,{modelValue:u.selected_sequence,"onUpdate:modelValue":t[0]||(t[0]=e=>u.selected_sequence=e),field:{option_key:"email_sequences",clearable:!0,size:"small",placeholder:e.$t("Select Email Sequence"),teleported:!1}},null,8,["modelValue","field"]),M(p,{onClick:_.addToSequence,loading:u.doing_action,type:"primary"},{default:L(()=>[O(B(e.$t("Add To Sequence")),1)],void 0,!0),_:1},8,["onClick","loading"])])],void 0,!0),_:1},512)]),table:L(()=>[z((A(),j(C,{"empty-text":e.$t("No Data Available"),stripe:"",border:"",data:u.sequences,style:{width:"100%"}},{default:L(()=>[M(f,{label:e.$t("Sequence")},{default:L(e=>[e.row.sequence?(A(),D("span",ue,B(e.row.sequence.title),1)):R("",!0)]),_:1},8,["label"]),M(f,{label:e.$t("Started At")},{default:L(t=>[H("span",{title:t.row.created_at},B(e.$nsHumanDiffTime(t.row.created_at)),9,_e)]),_:1},8,["label"]),M(f,{label:e.$t("Next Email")},{default:L(t=>[t.row.next_sequence?(A(),D("span",pe,B(t.row.next_sequence.title),1)):R("",!0),"active"==t.row.status?(A(),D("span",{key:1,title:t.row.next_execution_time}," - ("+B(e.$nsHumanDiffTime(t.row.next_execution_time))+") ",9,he)):(A(),D("span",ge," -- "))]),_:1},8,["label"]),M(f,{width:"160",label:e.$t("Status")},{default:L(e=>[M(b,{type:e.row.status},null,8,["type"])]),_:1},8,["label"]),M(f,{width:"60",align:"center"},{default:L(a=>[M($,{trigger:"click"},{dropdown:L(()=>[M(k,null,{default:L(()=>[M(y,{class:"fcrm_danger_action"},{default:L(()=>[z((A(),D("div",null,[M(w,{onYes:e=>_.removeFromSequence(a.row.campaign_id,a.row.id)},{reference:L(()=>[H("span",be,[H("span",ve,[M(v,{"icon-name":"delete"})]),O(" "+B(e.$t("Delete")),1)])]),_:1},8,["onYes"])])),[[V,u.removing]])],void 0,!0),_:2},1024)],void 0,!0),_:2},1024)]),default:L(()=>[H("span",{class:"el-dropdown-link cursor_pointer",role:"button",tabindex:"0","aria-label":e.$t("More actions"),onKeydown:[t[1]||(t[1]=I(e=>e.currentTarget.click(),["enter"])),t[2]||(t[2]=I(N(e=>e.currentTarget.click(),["prevent"]),["space"]))]},[M(v,{"icon-name":"more_actions"})],40,fe)],void 0,!0),_:2},1024)]),_:1})],void 0,!0),_:1},8,["empty-text","data"])),[[V,u.loading]])]),pagination:L(()=>[M(S,{pagination:u.pagination,onFetch:_.fetch},null,8,["pagination","onFetch"])]),_:1})}]]),ye=U(()=>e(()=>import("../../Funnels/parts/_LazyIndividualProgress.js?ver=3.1.8"),[],import.meta.url)),ke={class:"fcrm_table_header_inner_left_title"},$e={class:"fcrm_contact_popover_header"},Ce={class:"fcrm_contact_popover_body fcrm_popover_email_section"},Se=["title"],Ee={key:0},Ve={class:"icon"},Te=["title"],Pe=["aria-label"],xe={class:"el-popover__reference"},Fe={class:"icon"},qe={class:"el-popover__reference"},Ae={class:"icon"},je={class:"el-popover__reference"},Le={class:"icon"},Me={class:"el-popover__reference"},ze={class:"icon"};const De=ae({name:"ProfileAutomations",props:["subscriber_id"],components:{Icons:ie,Badge:te,Confirm:X,PaginationBar:K,DataTable:Q,OptionSelector:ee,LazyIndividualProgress:ye,StepPicker:oe,InfoFilled:d},data:()=>({automations:[],loading:!1,deleting:!1,pagination:{total:0,per_page:10,current_page:1},updating:!1,select_job:{action_name:"add_to_automation",selected_options:[]},selected_sequence:"",doing_action:!1,stepPickerRow:null}),methods:{fetch(){this.loading=!0,this.$get(`funnels/subscriber/${this.subscriber_id}/automations`,{per_page:this.pagination.per_page,page:this.pagination.current_page}).then(e=>{this.automations=e.automations.data,this.pagination.total=e.automations.total}).catch(e=>{this.handleError(e)}).finally(()=>{this.loading=!1})},removeFromFunnel(e,t){this.deleting=!0,this.$del(`funnels/${e}/subscribers`,{subscriber_ids:[t]}).then(e=>{this.$notify.success(e.message),this.fetch()}).catch(e=>{this.handleError(e)}).finally(()=>{this.deleting=!1})},changeFunnelSubscriptionStatus(e,t,a){this.updating=!0,this.$put(`funnels/${e}/subscribers/${t}/status`,{status:a}).then(e=>{this.$notify.success(e.message),this.fetch()}).catch(e=>{this.handleError(e)}).finally(()=>{this.updating=!1})},openStepPicker(e){this.stepPickerRow=e},addToAutomation(){this.selected_sequence?(this.doing_action=!0,this.$post("subscribers/do-bulk-action",{action_name:"add_to_automation",new_status:this.selected_sequence,subscriber_ids:[this.subscriber_id]}).then(e=>{this.visiblePop=!1,this.$notify.success(e.message),this.fetch()}).catch(e=>{this.handleError(e)}).finally(()=>{this.doing_action=!1})):this.$notify.warning({title:this.$t("Warning"),message:this.$t("Please select an automation funnel"),offset:19})}},mounted(){this.fetch()}},[["render",function(e,t,d,_,p,h){const g=c,f=q("option-selector"),b=r,v=q("lazy-individual-progress"),w=i,y=q("Icons"),k=q("Badge"),$=q("InfoFilled"),C=u,S=m,E=o,V=q("confirm"),T=l,P=s,x=a,F=q("step-picker"),U=q("pagination-bar"),W=q("data-table"),Y=n;return A(),j(W,{wrapper_border:!0,"has-selection":!1},{"header-left":L(()=>[H("h3",ke,B(e.$t("Automations")),1)]),"header-actions":L(()=>[M(b,{ref:"popover",placement:"left",width:"350","hide-after":0,trigger:"click","popper-class":"fcrm_contact_popover"},{reference:L(()=>[M(g,{size:"small"},{default:L(()=>[O(B(e.$t("Add Automation")),1)],void 0,!0),_:1})]),default:L(()=>[H("div",$e,[H("h3",null,B(e.$t("Select Automation")),1)]),H("div",Ce,[M(f,{modelValue:p.selected_sequence,"onUpdate:modelValue":t[0]||(t[0]=e=>p.selected_sequence=e),field:{option_key:"automation_funnels",clearable:!0,size:"small",placeholder:e.$t("Select Automation Funnel"),teleported:!1}},null,8,["modelValue","field"]),M(g,{onClick:h.addToAutomation,loading:p.doing_action,type:"primary"},{default:L(()=>[O(B(e.$t("Add To Automation")),1)],void 0,!0),_:1},8,["onClick","loading"])])],void 0,!0),_:1},512)]),table:L(()=>[z((A(),j(x,{"empty-text":e.$t("No Data Available"),stripe:"",border:"",data:p.automations,style:{width:"100%"}},{default:L(()=>[M(w,{label:e.$t("Sequence")},{default:L(e=>[e.row.funnel?(A(),j(v,{key:0,funnel:e.row.funnel,subscriber_id:d.subscriber_id},null,8,["funnel","subscriber_id"])):R("",!0)]),_:1},8,["label"]),M(w,{label:e.$t("Started At")},{default:L(t=>[H("span",{title:t.row.created_at},B(e.$nsHumanDiffTime(t.row.created_at)),9,Se)]),_:1},8,["label"]),M(w,{label:e.$t("Next Step")},{default:L(t=>[t.row.next_sequence_item&&"completed"!=t.row.status?(A(),D("span",Ee,B(t.row.next_sequence_item.title),1)):(A(),j(k,{key:1,type:"completed",plain:!0},{icon:L(()=>[H("span",Ve,[M(y,{"icon-name":"checkFill"})])]),_:1})),"active"==t.row.status?(A(),D("span",{key:2,title:t.row.next_execution_time}," - ("+B(e.$nsHumanDiffTime(t.row.next_execution_time))+") ",9,Te)):R("",!0)]),_:1},8,["label"]),M(w,{width:"160",label:e.$t("Status")},{default:L(e=>[M(k,{type:e.row.status},null,8,["type"])]),_:1},8,["label"]),M(w,{width:"60",align:"center","class-name":"fcrm_table_actions_cell"},{default:L(a=>["fcrm_manual_attach"==a.row.source_trigger_name?(A(),j(S,{key:0,class:"item",effect:"dark",content:e.$t("ProfileAutomations.Contact_Added_manually_to_Automation"),placement:"top-start"},{default:L(()=>[M(C,null,{default:L(()=>[M($)],void 0,!0),_:1})],void 0,!0),_:1},8,["content"])):R("",!0),M(P,{trigger:"click"},{dropdown:L(()=>[M(T,null,{default:L(()=>["active"==a.row.status||"waiting"==a.row.status?(A(),j(E,{key:0,onClick:e=>h.openStepPicker(a.row)},{default:L(()=>[H("span",xe,[H("span",Fe,[M(y,{"icon-name":"play"})]),O(" "+B(e.$t("Advance Step")),1)])],void 0,!0),_:1},8,["onClick"])):R("",!0),"cancelled"==a.row.status?(A(),j(E,{key:1,onClick:e=>h.changeFunnelSubscriptionStatus(a.row.funnel_id,a.row.subscriber_id,"active")},{default:L(()=>[H("span",qe,[H("span",Ae,[M(y,{"icon-name":"play"})]),O(" "+B(e.$t("Resume")),1)])],void 0,!0),_:1},8,["onClick"])):R("",!0),"active"==a.row.status?(A(),j(E,{key:2,onClick:e=>h.changeFunnelSubscriptionStatus(a.row.funnel_id,a.row.subscriber_id,"cancelled")},{default:L(()=>[H("span",je,[H("span",Le,[M(y,{"icon-name":"close"})]),O(" "+B(e.$t("Cancel")),1)])],void 0,!0),_:1},8,["onClick"])):R("",!0),M(E,{class:"fcrm_danger_action"},{default:L(()=>[M(V,{onYes:e=>h.removeFromFunnel(a.row.funnel_id,a.row.subscriber_id)},{reference:L(()=>[H("span",Me,[H("span",ze,[M(y,{"icon-name":"delete"})]),O(" "+B(e.$t("Delete")),1)])]),_:1},8,["onYes"])],void 0,!0),_:2},1024)],void 0,!0),_:2},1024)]),default:L(()=>[H("span",{class:"el-dropdown-link cursor_pointer",role:"button",tabindex:"0","aria-label":e.$t("More actions"),onKeydown:[t[1]||(t[1]=I(e=>e.currentTarget.click(),["enter"])),t[2]||(t[2]=I(N(e=>e.currentTarget.click(),["prevent"]),["space"]))]},[M(y,{"icon-name":"more_actions"})],40,Pe)],void 0,!0),_:2},1024)]),_:1})],void 0,!0),_:1},8,["empty-text","data"])),[[Y,p.loading]]),p.stepPickerRow?(A(),j(F,{key:0,"funnel-subscriber":p.stepPickerRow,"funnel-id":p.stepPickerRow.funnel_id,onClose:t[3]||(t[3]=e=>p.stepPickerRow=null),onAdvanced:t[4]||(t[4]=e=>{p.stepPickerRow=null,h.fetch()})},null,8,["funnel-subscriber","funnel-id"])):R("",!0)]),pagination:L(()=>[M(U,{pagination:p.pagination,onFetch:h.fetch},null,8,["pagination","onFetch"])]),_:1})}]]),Be={ref:"wrapper"},Re={ref:"ifr",frameborder:"0",sandbox:"allow-same-origin",style:{width:"100%",height:"400px"}};const He={class:"fc_smtp_log_viewer"},Ie={class:"fc_smtp_email_dialog_view"},Ne={class:"fc_smtp_email_log_items"},Oe={class:"item_header"},Ue={class:"item_content"},We={style:{"text-transform":"capitalize","margin-right":"10px"}},Ye={class:"item_header"},Je={class:"item_content"},Ze={class:"item_header"},Ke={class:"item_content"},Qe={class:"item_header"},Ge={class:"item_content"},Xe={key:0},et={class:"item_header"},tt={class:"item_content"},at={class:"item_header"},it={class:"item_content"},st={key:1},lt={class:"item_header"},ot={class:"item_content"},nt={style:{color:"var(--fc-secondary-text)"}},rt={style:{"white-space":"break-spaces","overflow-wrap":"anywhere"}},ct={style:{color:"var(--fc-secondary-text)"}},dt={key:0},mt={style:{color:"var(--fc-secondary-text)"}};const ut={class:"fc_smtp_email_logs_table"},_t=["onClick"],pt={key:0,style:{color:"var(--fc-error)"}};const ht=ae({name:"SMTPEmailLogs",components:{LogViewer:ae({name:"LogViewer",components:{SMTPEmailbodyContainer:ae({name:"SMTPEmailbodyContainer",components:{FullScreen:_},props:["content"],data:()=>({}),methods:{setBody(e){e||(e=" "),this.$nextTick(()=>{const t=this.$refs.ifr;(t.contentDocument||t.contentWindow.document).body.innerHTML=e})},fullScreen(){const e=document,t=this.$refs.wrapper;(e.fullscreenEnabled||e.webkitFullscreenEnabled||e.mozFullScreenEnabled||e.msFullscreenEnabled)&&(t.requestFullscreen?t.requestFullscreen():t.webkitRequestFullscreen?t.webkitRequestFullscreen():t.mozRequestFullScreen?t.mozRequestFullScreen():t.msRequestFullscreen&&t.msRequestFullscreen())}},watch:{content:{immediate:!0,handler:"setBody"}}},[["render",function(e,t,a,i,s,l){const o=q("FullScreen"),n=u,r=c;return A(),D("div",Be,[H("iframe",Re,null,512),M(r,{size:"small",type:"primary",ref:"fullscreen",onClick:l.fullScreen},{default:L(()=>[M(n,null,{default:L(()=>[M(o)],void 0,!0),_:1}),O(" "+B(e.$t("Enter Full Screen")),1)],void 0),_:1},8,["onClick"])],512)}]])},props:["logViewerProps"],emits:["closeLogViewer"],data:()=>({activeName:"email_body"}),computed:{log(){if(!this.logViewerProps.log)return;const e={...this.logViewerProps.log};return e.headers||(e.headers={}),e.response||(e.response={}),e.extra||(e.extra={}),e}},methods:{getAttachments:e=>e&&e.attachments?Array.isArray(e.attachments)?[...e.attachments]:[e.attachments]:[],getAttachmentName(e){if(e&&e[0])return(e=e[0].replace(/\\/g,"/")).split("/").pop()},sanitize(e){return this.$sanitize(e)},handleCloseLogViewer(){this.$emit("closeLogViewer")}}},[["render",function(e,t,a,i,s,l){const o=q("SMTPEmailbodyContainer"),n=h,r=f,c=g,d=p,m=b;return A(),D("div",He,[l.log?(A(),j(m,{key:0,"append-to-body":!0,title:e.$t("Email Log"),modelValue:a.logViewerProps.dialogVisible,"onUpdate:modelValue":t[1]||(t[1]=e=>a.logViewerProps.dialogVisible=e),class:"fc_smtp_email_dialog",onClose:l.handleCloseLogViewer},{default:L(()=>[H("div",Ie,[H("ul",Ne,[H("li",null,[H("div",Oe,B(e.$t("Status"))+":",1),H("div",Ue,[H("span",{class:W({success:"sent"==l.log.status,resent:"resent"==l.log.status,fail:"failed"==l.log.status})},[H("span",We,B(l.log.status),1)],2)])]),H("li",null,[H("div",Ye,B(e.$t("Date-Time"))+":",1),H("div",Je,B(l.log.created_at),1)]),H("li",null,[H("div",Ze,B(e.$t("From"))+":",1),H("div",Ke,B(l.log.from),1)]),H("li",null,[H("div",Qe,B(e.$t("To"))+":",1),H("div",Ge,B(l.log.to),1)]),l.log.resent_count>0?(A(),D("li",Xe,[H("div",et,B(e.$t("Resent Count"))+":",1),H("div",tt,B(l.log.resent_count),1)])):R("",!0),H("li",null,[H("div",at,B(e.$t("Subject"))+":",1),H("div",it,B(l.log.subject),1)]),l.log.extra&&l.log.extra.provider?(A(),D("li",st,[H("div",lt,B(e.$t("Mailer"))+":",1),H("div",ot,B(l.log.extra.provider),1)])):R("",!0)]),M(d,{modelValue:s.activeName,"onUpdate:modelValue":t[0]||(t[0]=e=>s.activeName=e),style:{"margin-top":"10px"}},{default:L(()=>[M(n,{name:"email_body"},{title:L(()=>[H("strong",nt,B(e.$t("Email Body"))+" (sanitized)",1)]),default:L(()=>[t[2]||(t[2]=H("hr",{class:"log-border"},null,-1)),M(o,{content:l.sanitize(l.log.body)},null,8,["content"])],void 0,!0),_:1}),H("p",null,[H("strong",null,B(e.$t("Server Response")),1)]),M(c,null,{default:L(()=>[M(r,null,{default:L(()=>[H("pre",rt,B(l.log.response),1)],void 0,!0),_:1})],void 0,!0),_:1}),t[4]||(t[4]=H("hr",null,null,-1)),M(n,{name:"tech_info"},{title:L(()=>[H("strong",ct,B(e.$t("Email Headers")),1)]),default:L(()=>[H("div",null,[H("pre",null,B(l.log.headers),1),l.log.extra.custom_headers?(A(),D("pre",dt,B(l.log.extra.custom_headers),1)):R("",!0)])],void 0,!0),_:1}),M(n,{name:"attachments"},{title:L(()=>[H("strong",mt,B(e.$t("Attachments"))+" ("+B(l.getAttachments(l.log).length)+") ",1)]),default:L(()=>[t[3]||(t[3]=H("hr",{class:"log-border"},null,-1)),(A(!0),D(Y,null,J(l.getAttachments(l.log),(e,t)=>(A(),D("div",{key:t,style:{margin:"5px 0 10px 0"}}," ("+B(t+1)+") "+B(l.getAttachmentName(e)),1))),128))],void 0,!0),_:1})],void 0,!0),_:1},8,["modelValue"])])],void 0),_:1},8,["title","modelValue","onClose"])):R("",!0)])}]]),View:v},props:["emails"],data:()=>({loading:!1,logViewerProps:{log:null,dialogVisible:!1}}),methods:{tableRowClassName:({row:e})=>"row_type_"+e.status,handleView(e){this.logViewerProps.log=e,this.logViewerProps.dialogVisible=!0},closeLogViewer(){this.logViewerProps.log=null,this.logViewerProps.dialogVisible=!1}}},[["render",function(e,t,s,l,o,r){const d=i,m=q("View"),_=u,p=c,h=a,g=q("log-viewer"),f=n;return A(),D("div",ut,[z((A(),j(h,{stripe:"",data:s.emails,style:{width:"100%"},"row-class-name":r.tableRowClassName},{default:L(()=>[M(d,{label:e.$t("Subject")},{default:L(t=>[H("span",{style:{cursor:"pointer"},onClick:e=>r.handleView(t.row)},B(t.row.subject),9,_t),t.row.extra&&"Simulator"==t.row.extra.provider?(A(),D("span",pt," - "+B(e.$t("Simulated")),1)):R("",!0)]),_:1},8,["label"]),M(d,{label:e.$t("Status"),width:"120",align:"center"},{default:L(e=>[O(B(e.row.status),1)]),_:1},8,["label"]),M(d,{prop:"created_at",label:e.$t("Date-Time"),width:"200px"},{default:L(t=>[O(B(e.$nsHumanDiffTime(t.row.created_at)),1)]),_:1},8,["label"]),M(d,{label:e.$t("Actions"),width:"100px",align:"right"},{default:L(e=>[M(p,{onClick:t=>r.handleView(e.row)},{default:L(()=>[M(_,null,{default:L(()=>[M(m)],void 0,!0),_:1})],void 0,!0),_:1},8,["onClick"])]),_:1},8,["label"])],void 0),_:1},8,["data","row-class-name"])),[[f,o.loading]]),M(g,{logViewerProps:o.logViewerProps,onCloseLogViewer:r.closeLogViewer},null,8,["logViewerProps","onCloseLogViewer"])])}]]),gt=U(()=>e(()=>import("../../Email/Campaigns/_components/EmailPreview.js?ver=3.1.8"),[],import.meta.url)),ft={class:"fcrm_profile_emails_wrapper"},bt={class:"fcrm_table_header_inner_left_title"},vt={class:"fcrm_sorting_action_wrap"},wt={class:"icon"},yt={class:"d-flex gap-4 flex-wrap items-start"},kt=["onClick"],$t=["title"],Ct=["title"],St=["aria-label"],Et={class:"el-popover__reference"},Vt={class:"icon"},Tt={class:"el-popover__reference"},Pt={class:"icon"},xt={class:"fcrm_bulk_action_bar"},Ft={class:"fcrm_bulk_action_left"},qt={class:"fc_bulk_selection_count"},At={class:"icon"},jt={key:0},Lt={key:1},Mt={key:0,class:"fc_block_white"},zt={class:"fc_checkbox_note"},Dt={class:"dialog-footer fcrm_profile_email_drawer_footer"};const Bt=ae({name:"ProfileEmails",props:["subscriber_id","subscriber"],components:{Badge:te,SMTPEmailLogs:ht,PaginationBar:K,DataTable:Q,FloatingBulkActionShell:G,EmailPreview:gt,EmailSequences:we,ProfileAutomations:De,EmailComposer:ne,Confirm:X,MailerConfig:re,Sort:S,View:v,RefreshRight:C,Delete:$,Location:k,FolderOpened:y,MoreFilled:t,Close:w,Icons:ie},data:()=>({direction:"rtl",emailTab:"crm",loading:!1,emails:[],smtpEmailLogs:[],pagination:{total:0,per_page:10,current_page:1},preview:{id:null,isVisible:!1},sendEmailModal:!1,custom_email:{},resending:!1,selections:[],deleting:!1,loadingCustomEmailSettings:!1,sending_custom_email:!1,emailFilter:"all",is_transactional:"no",current_mode:"system"===le.getCurrentTheme()?le.getSystemTheme():le.getCurrentTheme()}),watch:{is_transactional(){this.syncTransactionalFooterState()}},methods:{syncTransactionalFooterState(){this.custom_email&&Object.keys(this.custom_email).length&&(this.custom_email.settings||(this.custom_email.settings={}),this.custom_email.settings.footer_settings||(this.custom_email.settings.footer_settings={}),this.custom_email.settings.is_transactional=this.is_transactional,"yes"===this.is_transactional&&(this.custom_email.settings.footer_settings.disable_footer="yes"))},fetch(){this.loading=!0,this.$get(`subscribers/${this.subscriber_id}/emails`,{per_page:this.pagination.per_page,page:this.pagination.current_page,filter:this.emailFilter,tab:this.emailTab}).then(e=>{var t;this.emails=e.emails.data,"fluentsmtp"===this.emailTab&&(this.smtpEmailLogs=this.formatLogs(null==(t=null==e?void 0:e.emails)?void 0:t.data)),this.pagination.total=e.emails.total}).catch(e=>{console.log(e)}).finally(()=>{this.loading=!1})},resendEmail(e){if(!this.has_campaign_pro)return this.$notify.error(this.$t("_Ca_Please_utptutf")),!1;this.resending=!0,this.$post(`campaigns-pro/${e.campaign_id}/resend-emails`,{email_ids:[e.id]}).then(e=>{this.$notify.success(e.message),this.fetch()}).catch(e=>{this.handleError(e)}).finally(()=>{this.resending=!1})},previewEmail(e){this.preview.id=e,this.preview.isVisible=!0},sendCustomEmail(){this.sending_custom_email=!0,this.custom_email.settings||(this.custom_email.settings={}),this.custom_email.settings.is_transactional=this.is_transactional,this.syncTransactionalFooterState(),this.$post(`subscribers/${this.subscriber_id}/emails/send`,{campaign:this.custom_email}).then(e=>{this.$notify.success(e.message),this.fireCloseEmailComposer(),this.sendEmailModal=!1,this.custom_email={},this.fetch()}).catch(e=>{console.log(e)}).finally(()=>{this.sending_custom_email=!1})},handleSelectionChange(e){this.selections=e},clearProfileEmailSelection(){this.$refs.profileEmailsTable&&this.$refs.profileEmailsTable.clearSelection(),this.selections=[]},deleteSelected(){this.deleting=!0;const e=this.selections.map(e=>e.id);this.$del(`subscribers/${this.subscriber_id}/emails`,{email_ids:e}).then(e=>{this.selections=[],this.$notify.success(e.message),this.fetch()}).catch(e=>{this.handleError(e)}).finally(()=>{this.deleting=!1})},openCustomEmailModal(){if(this.loadingCustomEmailSettings=!0,this.custom_email.email_body="",this.is_transactional="no",this.sendEmailModal=!0,window.fc_subscriber_email_mock)return this.custom_email=JSON.parse(JSON.stringify(window.fc_subscriber_email_mock)),this.custom_email.settings.mailer_settings?this.custom_email.settings.mailer_settings.is_custom="no":this.custom_email.settings.mailer_settings={from_name:"",from_email:"",reply_to_name:"",reply_to_email:"",is_custom:"no"},this.is_transactional=this.custom_email.settings&&this.custom_email.settings.is_transactional?this.custom_email.settings.is_transactional:"no",this.syncTransactionalFooterState(),void(this.loadingCustomEmailSettings=!1);this.$get(`subscribers/${this.subscriber_id}/emails/template-mock`).then(e=>{const t=e.email_mock;t.settings.mailer_settings||(t.settings.mailer_settings={from_name:"",from_email:"",reply_to_name:"",reply_to_email:"",is_custom:"no"}),window.fc_subscriber_email_mock=t,this.custom_email=t,this.is_transactional=this.custom_email.settings&&this.custom_email.settings.is_transactional?this.custom_email.settings.is_transactional:"no",this.syncTransactionalFooterState()}).catch(e=>{console.log(e)}).finally(()=>{this.loadingCustomEmailSettings=!1})},fireCloseEmailComposer(e=!1){this.unmountBlockEditor(),e&&e()},formatLogs(e){return jQuery.each(e,(t,a)=>{e[t]=this.formatLog(a)}),e},formatLog(e){return e.to=this.formatAddresses(e.to),e},formatAddresses(e){if(!e)return"";if(this.isEmptyValue(e))return"";if("string"==typeof e)return e;const t=[];return jQuery.each(e,(e,a)=>{a.name?t[e]=this.escapeHtml(`${a.name} <${a.email}>`):t[e]=this.escapeHtml(a.email)}),t.join(", ")},escapeHtml(e){if(!e)return e;const t={"&":"&","<":"<",">":">",'"':""","'":"'"};return e.replace(/[&<>"']/g,e=>t[e])},onThemeChanged(e){var t;this.current_mode=(null==(t=e.detail)?void 0:t.effective)||("system"===le.getCurrentTheme()?le.getSystemTheme():le.getCurrentTheme())}},mounted(){window.addEventListener(se,this.onThemeChanged),window.fcAdmin&&window.fcAdmin.is_rtl&&(this.direction="ltr"),this.fetch(),this.doAction("fluent_crm_profile_emails_mounted",this)},beforeUnmount(){window.removeEventListener(se,this.onThemeChanged)}},[["render",function(e,t,d,m,_,p){const h=E,g=c,f=T,b=V,v=r,w=q("Icons"),y=i,k=q("Location"),$=u,C=q("FolderOpened"),S=q("Badge"),U=o,W=l,J=s,K=a,Q=q("SMTPEmailLogs"),G=q("pagination-bar"),X=q("data-table"),ee=q("Close"),te=q("confirm"),ae=q("floating-bulk-action-shell"),ie=q("email-sequences"),se=q("profile-automations"),le=q("email-preview"),oe=q("mailer-config"),ne=x,re=P,ce=q("email-composer"),de=F,me=n;return A(),D("div",ft,[M(X,{wrapper_border:!0,"has-selection":!1,class:"fcrm_mb_24"},{"header-left":L(()=>[H("h3",bt,B(e.$t("Pro_Emails_fdcaa")),1)]),"header-actions":L(()=>[e.appVars.has_fluentsmtp?(A(),j(h,{key:0,size:"small",modelValue:_.emailTab,"onUpdate:modelValue":t[0]||(t[0]=e=>_.emailTab=e),"active-text":e.$t("FluentSMTP Logs"),"active-value":"fluentsmtp","inactive-value":"crm",onChange:p.fetch},null,8,["modelValue","active-text","onChange"])):R("",!0),e.hasPermission("fcrm_manage_contacts")&&"crm"===_.emailTab?(A(),j(v,{key:1,title:e.$t("Filter"),width:240,placement:"bottom","popper-class":"fcrm_sort_popover",trigger:"click"},{reference:L(()=>[M(g,{title:e.$t("Sort"),class:"only-icon-btn small",size:"small"},{default:L(()=>[...t[10]||(t[10]=[H("span",{class:"icon"},[H("svg",{width:"20",height:"20",viewBox:"0 0 20 20",fill:"none",xmlns:"http://www.w3.org/2000/svg"},[H("path",{d:"M16.75 4V5.5H16L12.25 11.125V17.5H7.75V11.125L4 5.5H3.25V4H16.75ZM5.803 5.5L9.25 10.6705V16H10.75V10.6705L14.197 5.5H5.803Z",fill:"var(--fc-secondary-text)"})])],-1)])],void 0,!0),_:1},8,["title"])]),default:L(()=>[H("div",vt,[M(b,{modelValue:_.emailFilter,"onUpdate:modelValue":t[1]||(t[1]=e=>_.emailFilter=e),class:"fc_filter_emails",size:"small",onChange:p.fetch},{default:L(()=>[M(f,{value:"all"},{default:L(()=>[O(B(e.$t("All")),1)],void 0,!0),_:1}),M(f,{value:"open"},{default:L(()=>[O(B(e.$t("Opened")),1)],void 0,!0),_:1}),M(f,{value:"click"},{default:L(()=>[O(B(e.$t("Clicked")),1)],void 0,!0),_:1}),M(f,{value:"unopened"},{default:L(()=>[O(B(e.$t("Unopened")),1)],void 0,!0),_:1})],void 0,!0),_:1},8,["modelValue","onChange"])])],void 0,!0),_:1},8,["title"])):R("",!0),e.has_campaign_pro&&e.hasPermission("fcrm_manage_contacts")&&"crm"==_.emailTab?(A(),j(g,{key:2,disabled:!("subscribed"==d.subscriber.status||"transactional"==d.subscriber.status),onClick:t[2]||(t[2]=e=>p.openCustomEmailModal()),size:"small"},{default:L(()=>[H("span",wt,[M(w,{"icon-name":"send-mail"})]),O(" "+B(e.$t("Send Email")),1)],void 0,!0),_:1},8,["disabled"])):R("",!0)]),table:L(()=>["crm"===_.emailTab?z((A(),j(K,{key:0,ref:"profileEmailsTable","empty-text":e.$t("No Data Available"),stripe:"",border:"",data:_.emails,style:{width:"100%"},onSelectionChange:p.handleSelectionChange},{default:L(()=>[M(y,{type:"selection",width:"55"}),M(y,{label:e.$t("Subject")},{default:L(t=>[H("div",yt,[H("span",{class:"cursor_pointer",style:{"margin-right":"8px"},onClick:e=>p.previewEmail(t.row.id)},B(t.row.email_subject),9,kt),H("span",{title:e.$t("Total Clicks"),class:"fcrm_badge"},[M($,null,{default:L(()=>[M(k)],void 0,!0),_:1}),O(" "+B(t.row.click_counter||0),1)],8,$t),z(H("span",{title:e.$t("Email opened"),class:"fcrm_badge",style:{"min-height":"20px"}},[M($,{class:"icon"},{default:L(()=>[M(C)],void 0,!0),_:1})],8,Ct),[[Z,t.row.click_counter||1==t.row.is_open]])])]),_:1},8,["label"]),M(y,{width:"190",label:e.$t("Date")},{default:L(e=>[O(B(e.row.scheduled_at),1)]),_:1},8,["label"]),M(y,{width:"120",label:e.$t("Status"),align:"center"},{default:L(e=>[M(S,{type:e.row.status},null,8,["type"])]),_:1},8,["label"]),M(y,{width:"60",align:"center"},{default:L(a=>[M(J,{trigger:"click"},{dropdown:L(()=>[M(W,null,{default:L(()=>[M(U,{onClick:e=>p.previewEmail(a.row.id)},{default:L(()=>[H("span",Et,[H("span",Vt,[M(w,{"icon-name":"eye"})]),O(" "+B(e.$t("Preview")),1)])],void 0,!0),_:1},8,["onClick"]),"sent"!==a.row.status&&"failed"!==a.row.status||!a.row.campaign_id?R("",!0):(A(),j(U,{key:0,onClick:e=>p.resendEmail(a.row)},{default:L(()=>[H("span",Tt,[H("span",Pt,[M(w,{"icon-name":"resend-email"})]),O(" "+B(e.$t("Resend")),1)])],void 0,!0),_:1},8,["onClick"]))],void 0,!0),_:2},1024)]),default:L(()=>[H("span",{class:"el-dropdown-link cursor_pointer",role:"button",tabindex:"0","aria-label":e.$t("More actions"),onKeydown:[t[3]||(t[3]=I(e=>e.currentTarget.click(),["enter"])),t[4]||(t[4]=I(N(e=>e.currentTarget.click(),["prevent"]),["space"]))]},[M(w,{"icon-name":"more_actions"})],40,St)],void 0,!0),_:2},1024)]),_:1})],void 0,!0),_:1},8,["empty-text","data","onSelectionChange"])),[[me,_.loading||_.resending]]):"fluentsmtp"===_.emailTab?z((A(),j(Q,{key:1,emails:_.smtpEmailLogs},null,8,["emails"])),[[me,_.loading||_.resending]]):R("",!0)]),pagination:L(()=>[M(G,{pagination:_.pagination,onFetch:p.fetch},null,8,["pagination","onFetch"])]),_:1}),M(ae,{visible:!!_.selections.length,"theme-mode":_.current_mode,"custom-layout":!0},{default:L(()=>[H("div",xt,[H("div",Ft,[M(g,{link:"","aria-label":e.$t("Deselect"),onClick:p.clearProfileEmailSelection},{default:L(()=>[M($,null,{default:L(()=>[M(ee)],void 0,!0),_:1})],void 0,!0),_:1},8,["aria-label","onClick"]),H("span",qt,[H("strong",null,B(_.selections.length),1),O(" "+B(e.$t("selected")),1)]),t[11]||(t[11]=H("div",{class:"fcrm_bulk_divider"},null,-1)),M(te,{onYes:t[5]||(t[5]=e=>p.deleteSelected()),placement:"top-start"},{reference:L(()=>[z((A(),j(g,{type:"danger",size:"small",plain:""},{default:L(()=>[H("span",At,[M(w,{"icon-name":"delete"})]),O(" "+B(e.$t("Delete")),1)],void 0,!0),_:1})),[[me,_.deleting]])]),_:1})])])],void 0),_:1},8,["visible","theme-mode"]),e.has_campaign_pro?(A(),D(Y,{key:0},[M(ie,{subscriber_id:d.subscriber_id},null,8,["subscriber_id"]),M(se,{subscriber_id:d.subscriber_id},null,8,["subscriber_id"])],64)):R("",!0),M(le,{preview:_.preview},null,8,["preview"]),M(de,{direction:_.direction,class:"fc_funnel_block_modal fcrm_profile_email_drawer",modelValue:_.sendEmailModal,"onUpdate:modelValue":t[9]||(t[9]=e=>_.sendEmailModal=e),size:"80%","append-to-body":!0,"destroy-on-close":!0,"close-on-click-modal":!1,"before-close":p.fireCloseEmailComposer,title:e.$t("Send Custom Email")},{default:L(()=>[_.loadingCustomEmailSettings?(A(),D("div",jt,[H("h3",null,B(e.$t("Loading Settings...")),1)])):(A(),D("div",Lt,[_.sendEmailModal?(A(),D("div",Mt,[M(ce,{enable_test:!0,disable_fixed:!0,disable_gutenberg_autosave:!0,hide_gutenberg_save_button:!0,class:"fc_into_modal",campaign:_.custom_email,label_align:"top"},{after_block_composer:L(()=>[_.loadingCustomEmailSettings?R("",!0):(A(),j(re,{key:0,style:{"margin-bottom":"20px"},class:"fc_t_10","label-position":"top"},{default:L(()=>[M(oe,{mailer_settings:_.custom_email.settings.mailer_settings},null,8,["mailer_settings"]),M(ne,{class:"fc_t_10","true-value":"yes","false-value":"no",modelValue:_.is_transactional,"onUpdate:modelValue":t[6]||(t[6]=e=>_.is_transactional=e)},{default:L(()=>[O(B(e.$t("Mark_Transactional"))+" ",1),H("span",zt,"("+B(e.$t("transaction_checkbox_note"))+")",1)],void 0,!0),_:1},8,["modelValue"])],void 0,!0),_:1}))]),_:1},8,["campaign"])])):R("",!0)])),H("div",Dt,[M(g,{size:"small",onClick:t[7]||(t[7]=e=>{p.fireCloseEmailComposer(),_.sendEmailModal=!1})},{default:L(()=>[O(B(e.$t("Cancel")),1)],void 0,!0),_:1}),z((A(),j(g,{size:"small",type:"primary",onClick:t[8]||(t[8]=e=>p.sendCustomEmail())},{default:L(()=>[O(B(e.$t("Send Email")),1)],void 0,!0),_:1})),[[me,_.sending_custom_email]])])],void 0),_:1},8,["direction","modelValue","before-close","title"])])}]]);export{Bt as default}; diff --git a/wp-content/plugins/fluent-crm/assets/admin/Modules/Profile/Parts/ProfileFiles.js b/wp-content/plugins/fluent-crm/assets/admin/Modules/Profile/Parts/ProfileFiles.js new file mode 100644 index 0000000..5f645a9 --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/admin/Modules/Profile/Parts/ProfileFiles.js @@ -0,0 +1 @@ +import{ay as a,aA as t,k as e,aD as s,aR as i}from"../../../../vendor-element-plus.js?ver=3.1.8";import{a6 as n,W as d,X as r,Z as l,ab as o,a5 as c,aa as f,a9 as _}from"../../../../vendor.js?ver=3.1.8";import{_ as u}from"../../../../fc-bits-ui.js?ver=3.1.8";const p={class:"fluentcrm_databox"},m={class:"fluentcrm_contact_header"},v={class:"fc_uploader_drawer"};const g=u({name:"ProfileFiles",props:["subscriber_id"],data:()=>({adding_file:!1,files:[],loading:!1,pagination:{total:0,per_page:10,current_page:1},new_note:{title:"",description:"",type:"note"}}),methods:{fetchAttachments(){}},mounted(){this.fetchAttachments()}},[["render",function(u,g,h,b,A,y){const j=t,w=e,x=s,k=i,C=a;return n((d(),r("div",p,[l("div",m,[o(x,{gutter:30},{default:c(()=>[o(j,{span:12},{default:c(()=>[l("h3",null,f(u.$t("Pro_Contact_F_A")),1)],void 0,!0),_:1}),o(j,{class:"text-align-right",span:12},{default:c(()=>[o(w,{onClick:g[0]||(g[0]=a=>A.adding_file=!A.adding_file),type:"primary",size:"small"},{default:c(()=>[_(f(u.$t("Add New")),1)],void 0,!0),_:1})],void 0,!0),_:1})],void 0),_:1})]),l("div",v,[o(k)]),g[1]||(g[1]=l("div",null,null,-1))])),[[C,A.loading]])}]]);export{g as default}; diff --git a/wp-content/plugins/fluent-crm/assets/admin/Modules/Profile/Parts/ProfileFormSubmissions.js b/wp-content/plugins/fluent-crm/assets/admin/Modules/Profile/Parts/ProfileFormSubmissions.js new file mode 100644 index 0000000..d46fa5a --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/admin/Modules/Profile/Parts/ProfileFormSubmissions.js @@ -0,0 +1 @@ +import{aB as i,aT as e,aH as s,aI as t,k as a,ay as o}from"../../../../vendor-element-plus.js?ver=3.1.8";import{W as n,Y as r,a5 as l,X as d,a8 as m,ab as c,Z as u,aa as p,bC as _,aQ as b,a6 as h,J as f,az as g,a9 as v}from"../../../../vendor.js?ver=3.1.8";import{P as w}from"../../../../PaginationBar.js?ver=3.1.8";import{D as y}from"../../../../DataTable.js?ver=3.1.8";import{_ as k}from"../../../../fc-bits-ui.js?ver=3.1.8";const C={key:0,class:"el-drawer__title"},D={key:0},V={key:1},F={key:2},$=["innerHTML"],T=["innerHTML"];const H={class:"fcrm_table_header_inner_left_title"},L=["innerHTML"];const M={key:0,class:"fcrm_profile_form_submissions_wrapper"},z={key:1},j={class:"text-align-center"};const B=k({name:"ProfileFormSubmissions",props:["subscriber_id"],components:{FormSubmissionBlock:k({name:"FormSubmissionsBlock",props:["provider","subscriber_id"],components:{DynamicWidgetContent:k({name:"DynamicWidgetContent",emits:["closed"],props:["provider","subscriber_id","params"],data:()=>({loading:!1,dataView:null,direction:"rtl",show:!0}),methods:{fetch(){this.loading=!0,this.$get(`subscribers/${this.subscriber_id}/dynamic-item-view`,{provider:this.provider.provider_key,params:this.params}).then(i=>{this.dataView=i.data_view}).catch(i=>{this.handleError(i),console.log(i)}).finally(()=>{this.loading=!1})}},mounted(){window.fcAdmin&&window.fcAdmin.is_rtl&&(this.direction="ltr"),this.fetch()}},[["render",function(s,t,a,o,_,b){const h=i,f=e;return n(),r(f,{"close-on-click-modal":!0,"append-to-body":!0,size:s.drawerWidth,modelValue:_.show,"onUpdate:modelValue":t[0]||(t[0]=i=>_.show=i),onClosed:t[1]||(t[1]=i=>s.$emit("closed")),direction:_.direction},{header:l(({close:i})=>[_.dataView?(n(),d("div",C,p(_.dataView.title),1)):m("",!0)]),default:l(()=>[_.loading?(n(),d("div",D,[c(h,{rows:5,animated:""})])):_.dataView?(n(),d("div",F,[_.dataView.content_html?(n(),d("div",{key:0,innerHTML:s.$sanitize(_.dataView.content_html)},null,8,$)):m("",!0)])):(n(),d("div",V,[u("p",null,p(s.$t("No data found")),1)]))]),footer:l(()=>[_.dataView&&_.dataView.footer_content?(n(),d("div",{key:0,innerHTML:s.$sanitize(_.dataView.footer_content)},null,8,T)):m("",!0)]),_:1},8,["size","modelValue","direction"])}]]),PaginationBar:w,DataTable:y},data:()=>({loading:!1,submissions:[],pagination:{per_page:10,current_page:1,total:0},columnsConfig:{},viewingDetails:null}),computed:{table_columns(){if(!_(this.columnsConfig))return this.columnsConfig;let i=[];return this.submissions.length&&(i=this.submissions[0]),i}},methods:{fetch(){this.loading=!0,this.$get(`subscribers/${this.subscriber_id}/form-submissions`,{provider:this.provider.provider_key,page:this.pagination.current_page,per_page:this.pagination.per_page}).then(i=>{i.submissions.columns_config&&(this.columnsConfig=i.submissions.columns_config),this.submissions=i.submissions.data,this.pagination.total=parseInt(i.submissions.total)}).catch(i=>{console.log(i)}).finally(()=>{this.loading=!1})},showDetails(i){this.viewingDetails={__id:i}}},mounted(){this.fetch()}},[["render",function(i,e,_,w,y,k){const C=a,D=t,V=s,F=b("DynamicWidgetContent"),$=b("pagination-bar"),T=b("data-table"),M=o;return n(),r(T,{wrapper_border:!0,"has-selection":!1},{"header-left":l(()=>[u("h3",H,p(_.provider.title),1)]),table:l(()=>[h((n(),r(V,{"empty-text":i.$t("No Data Found"),border:"",stripe:"",data:y.submissions},{empty:l(()=>[u("p",null,[v(p(i.$t("Form Submissions from"))+" ",1),u("b",null,p(_.provider.name),1),v(" "+p(i.$t("no_form_submissions_found_for_this_subscriber")),1)])]),default:l(()=>[(n(!0),d(f,null,g(k.table_columns,(e,s)=>(n(),r(D,{key:s,width:y.columnsConfig[s]?y.columnsConfig[s].width:"",label:y.columnsConfig[s]&&y.columnsConfig[s].label?y.columnsConfig[s].label:i.ucFirst(s)},{default:l(i=>[y.columnsConfig[s]&&y.columnsConfig[s].quick_action?(n(),r(C,{key:0,size:"small",onClick:e=>k.showDetails(i.row.__id),innerHTML:i.row[s]},null,8,["onClick","innerHTML"])):(n(),d("div",{key:1,innerHTML:i.row[s]},null,8,L))]),_:2},1032,["width","label"]))),128))],void 0,!0),_:1},8,["empty-text","data"])),[[M,y.loading]]),y.viewingDetails?(n(),r(F,{key:0,params:y.viewingDetails,provider:_.provider,subscriber_id:_.subscriber_id,onClosed:e[0]||(e[0]=i=>y.viewingDetails=null)},null,8,["params","provider","subscriber_id"])):m("",!0)]),pagination:l(()=>[c($,{pagination:y.pagination,onFetch:k.fetch},null,8,["pagination","onFetch"])]),_:1})}]])},data:()=>({providersData:{},app_ready:!1}),computed:{is_empty_item(){return this.isEmptyValue(this.providersData)}},mounted(){this.each(window.fcAdmin.form_submission_providers,(i,e)=>{const s={title:i.title,name:i.name,provider_key:e};this.providersData[e]=s}),this.app_ready=!0}},[["render",function(i,e,s,t,a,o){const l=b("form-submission-block");return a.app_ready?(n(),d("div",M,[o.is_empty_item?(n(),d("div",z,[u("h3",j,p(i.$t("Pro_Form_SfFFwbshCFF")),1)])):(n(!0),d(f,{key:0},g(a.providersData,(i,e)=>(n(),r(l,{key:e,provider:i,subscriber_id:s.subscriber_id},null,8,["provider","subscriber_id"]))),128))])):m("",!0)}]]);export{B as default}; diff --git a/wp-content/plugins/fluent-crm/assets/admin/Modules/Profile/Parts/ProfileNotes.js b/wp-content/plugins/fluent-crm/assets/admin/Modules/Profile/Parts/ProfileNotes.js new file mode 100644 index 0000000..3c2ffc1 --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/admin/Modules/Profile/Parts/ProfileNotes.js @@ -0,0 +1 @@ +import{a3 as e,a6 as t,a4 as s,D as i,ay as o,k as n,e as a,az as l,aT as r}from"../../../../vendor-element-plus.js?ver=3.1.8";import{aQ as d,a6 as c,W as h,X as _,Z as p,aa as u,a0 as m,ab as f,a5 as g,b2 as v,Y as b,a8 as y,a9 as k,J as w,az as N,ax as x,ac as C}from"../../../../vendor.js?ver=3.1.8";import{P as $}from"../../../../PaginationBar.js?ver=3.1.8";import{C as S}from"../../../../Confirm.js?ver=3.1.8";import{F as V}from"../../../../_FormBuilder2.js?ver=3.1.8";import{_ as M,I as L}from"../../../../fc-bits-ui.js?ver=3.1.8";import{c as j}from"../../../../clipboard.js?ver=3.1.8";import{F as B}from"../../../../FloatingBulkActionShell.js?ver=3.1.8";import"../../../../PhotoWidget.js?ver=3.1.8";import"../../../../input-popover-dropdown.js?ver=3.1.8";import"../../../../data_config.js?ver=3.1.8";import"../../../../_OptionSelector.js?ver=3.1.8";import"../../../../_AjaxSelector.js?ver=3.1.8";import"../../../../_VerifiedEmailInput.js?ver=3.1.8";const H={class:"fcrm_notes_wrapper_inner fcrm_object_notes_template"},D={class:"fcrm_notes_header"},A={class:"fcrm_notes_header_title"},T={class:"fcrm_notes_header_actions"},z={class:"icon"},I={class:"icon"},Z={class:"icon"},E={class:"fcrm_notes_body"},U={class:"fcrm_notes_list"},F=["data-note-id"],P=["onClick"],Y={class:"fcrm_note_header_left"},q={class:"fcrm_note_avatar"},O=["src","alt"],K={key:1,class:"fcrm_note_avatar_initials"},Q={class:"fcrm_note_info"},W={class:"fcrm_note_title"},G={class:"fcrm_note_meta"},J={key:0},X={key:1},R=["title"],ee={class:"fcrm_note_header_right"},te=["innerHTML"],se={key:1,class:"fcrm_empty_state"},ie={class:"fcrm_empty_state_text"},oe={class:"fc_company_save_wrap"},ne={class:"fcrm_bulk_action_bar"},ae={class:"fcrm_bulk_action_left"},le={class:"icon"},re={class:"fc_bulk_selection_count"},de={key:0,class:"fcrm_bulk_divider"},ce={class:"icon"};const he={class:"fc_notes_wrapper fcrm_notes_wrapper"};const _e=M({name:"ProfileNotes",props:["subscriber_id"],components:{ObjectNotesTemplate:M({name:"ObjectNoteTemplate",props:["subscriber_id","route_prefix","section_title","target_note_id"],emits:["added","deleted","updated"],components:{PaginationBar:$,Confirm:S,FormBuilder:V,Search:i,Download:s,Delete:t,Edit:e,FloatingBulkActionShell:B,Icons:L},data:()=>({direction:"rtl",loading:!1,notes:[],types:window.fcAdmin.activity_types,pagination:{total:0,per_page:10,current_page:1},editing_note:{title:"",description:"",type:"note",created_at:""},is_editing_note:!1,search:"",updating:!1,is_changed:!1,note_syncing_fields:{},showSearchBar:!1,expandedNotes:{},targetedNoteId:null,bulkMode:!1,selectedNotes:[]}),watch:{"editing_note.type"(e,t){this.handleValueUpdate(e,t)},"editing_note.title"(e,t){this.handleValueUpdate(e,t)},"editing_note.description"(e,t){this.handleValueUpdate(e,t)},"editing_note.created_at"(e,t){this.handleValueUpdate(e,t)}},computed:{isSubscriberNotesContext(){return"subscribers"===this.route_prefix},allSelected(){return this.notes.length>0&&this.selectedNotes.length===this.notes.length},someSelected(){return this.selectedNotes.length>0&&this.selectedNotes.lengththis.expandedNotes[e.id]),t={};this.notes.forEach(s=>{t[s.id]=!e}),this.expandedNotes=t},toggleBulkMode(){this.bulkMode=!this.bulkMode,this.bulkMode||(this.selectedNotes=[])},toggleNoteSelection(e){if(!this.isSubscriberNotesContext||!this.bulkMode)return;const t=this.selectedNotes.indexOf(e);-1===t?this.selectedNotes.push(e):this.selectedNotes.splice(t,1)},toggleAllSelection(){this.isSubscriberNotesContext&&this.bulkMode&&(this.allSelected?this.selectedNotes=[]:this.selectedNotes=this.notes.map(e=>e.id))},getInitials:e=>e&&e.display_name?e.display_name.split(" ").map(e=>e[0]).join("").toUpperCase().substring(0,2):"A",handleValueUpdate(e,t){e!==t&&(this.is_changed=!0)},handleClose(e){this.is_changed?this.$confirm(this.$t("You have unsaved data, proceed?")).then(t=>{this.is_changed=!1,this.is_editing_note=!1,this.resetNoteData(),e()}).catch(e=>{console.log(e)}):e()},fetch(){this.loading=!0;const e={per_page:this.pagination.per_page,page:this.pagination.current_page,search:this.search};this.target_note_id&&1===this.pagination.current_page&&!this.search&&(e.include_id=this.target_note_id),this.$get(`${this.route_prefix}/${this.subscriber_id}/notes`,e).then(e=>{if(this.notes=e.notes.data,this.selectedNotes=[],this.pagination.total=e.notes.total,this.note_syncing_fields=e.fields,this.resetNoteData(),this.is_editing_note=!1,e.included_note&&this.notes.unshift(e.included_note),this.target_note_id){const e=parseInt(this.target_note_id);this.notes.some(t=>t.id===e)&&(this.expandedNotes={...this.expandedNotes,[e]:!0},this.targetedNoteId=e,this.$nextTick(()=>{const t=this.$el.querySelector('[data-note-id="'+e+'"]');t&&t.scrollIntoView({behavior:"smooth",block:"center"}),setTimeout(()=>{this.targetedNoteId=null},3e3)}))}}).catch(e=>{this.handleError(e)}).finally(()=>{this.loading=!1})},bulkDelete(){this.selectedNotes.length&&this.$confirm(this.$t("Are you sure you want to delete the selected notes? This action cannot be undone."),this.$t("Delete Notes"),{confirmButtonText:this.$t("Yes, Delete"),cancelButtonText:this.$t("Cancel"),type:"warning"}).then(()=>{this.loading=!0,this.$post(`${this.route_prefix}/${this.subscriber_id}/notes/bulk-delete`,{note_ids:this.selectedNotes}).then(e=>{this.$notify.success({title:this.$t("Done!"),message:e.message,offset:19}),this.selectedNotes=[],this.bulkMode=!1,this.fetch(),this.$emit("deleted",null)}).catch(e=>{this.handleError(e)}).finally(()=>{this.loading=!1})}).catch(()=>{})},onSearchBarAppendClick(){this.showSearchBar?this.fetch():(this.showSearchBar=!0,this.$nextTick(()=>{var e;return null==(e=this.$refs.notesSearchInput)?void 0:e.focus()}))},initAddNote(){this.resetNoteData(),this.is_editing_note=!0},resetNoteData(){this.editing_note={title:"",description:"",type:"note",created_at:""}},saveNote(){this.$post(`${this.route_prefix}/${this.subscriber_id}/notes`,{note:this.editing_note}).then(e=>{this.$notify.success(e.message),this.fetch(),this.$emit("added",e.note),this.resetNoteData(),this.is_editing_note=!1}).catch(e=>{e.title&&this.$notify.error({title:this.$t("Error"),message:e.title.required}),e.description&&this.$notify.error({title:this.$t("Error"),message:e.description.required})}).finally(()=>{this.loading=!1})},remove(e){this.$del(`${this.route_prefix}/${this.subscriber_id}/notes/${e}`).then(t=>{this.fetch(),this.$notify.success({title:this.$t("Great!"),message:t.message,offset:19}),this.$emit("deleted",e)}).catch(e=>{this.handleError(e)})},editNote(e){this.editing_note=e,this.is_editing_note=!0},updateNote(){this.updating=!0,this.$put(`${this.route_prefix}/${this.subscriber_id}/notes/${this.editing_note.id}`,{note:this.editing_note}).then(e=>{this.$notify.success(e.message),this.is_editing_note=!1,this.resetNoteData(),this.$emit("updated",e.note)}).catch(e=>{this.handleError(e)}).finally(()=>{this.updating=!1})},copyNoteLink(e){const t=window.location.href.split("#")[0]+"#/"+this.route_prefix+"/"+this.subscriber_id+"/notes?note_id="+e;j(t)&&this.$notify.success(this.$t("Copied to clipboard"))},exportNotes(){if(!this.has_campaign_pro)return this.$notify.error(this.$t("Notes export feature is only available on pro version")),!1;location.href=window.ajaxurl+"?"+jQuery.param({action:"fluentcrm_export_notes",route_prefix:this.route_prefix,subscriber_id:this.subscriber_id})}},mounted(){window.fcAdmin&&window.fcAdmin.is_rtl&&(this.direction="ltr"),this.fetch()}},[["render",function(e,t,s,i,$,S){const V=d("Icons"),M=n,L=a,j=l,B=d("confirm"),he=d("pagination-bar"),_e=d("icons"),pe=d("form-builder"),ue=r,me=d("floating-bulk-action-shell"),fe=o;return c((h(),_("div",H,[p("div",D,[p("div",A,u(s.section_title),1),p("div",T,[p("div",{class:m(["fcrm_notes_search_bar",{"fcrm_notes_search_bar-is_expanded":$.showSearchBar}])},[f(L,{ref:"notesSearchInput",onKeyup:v(S.fetch,["enter"]),clearable:"",size:"small",onClear:t[0]||(t[0]=e=>S.fetch()),placeholder:e.$t("Search"),modelValue:$.search,"onUpdate:modelValue":t[1]||(t[1]=e=>$.search=e),class:m(["fcrm_notes_search_input",{"fcrm_notes_search_input-is_expanded":$.showSearchBar}])},{append:g(()=>[f(M,{class:"only-icon-btn small",size:"small",onClick:S.onSearchBarAppendClick},{default:g(()=>[p("span",z,[f(V,{"icon-name":"search"})])],void 0,!0),_:1},8,["onClick"])]),_:1},8,["onKeyup","placeholder","modelValue","class"])],2),$.notes.length?(h(),b(M,{key:0,size:"small",class:"small only-icon-btn",onClick:t[2]||(t[2]=e=>S.toggleAllNotes()),"aria-label":e.$t("Toggle all notes"),title:e.$t("Toggle all notes")},{default:g(()=>[...t[13]||(t[13]=[p("span",{class:"icon"},[p("svg",{width:"20",height:"20",viewBox:"0 0 20 20",fill:"none",xmlns:"http://www.w3.org/2000/svg"},[p("path",{d:"M5 4L10 9L15 4M5 11L10 16L15 11",stroke:"var(--fc-secondary-text)","stroke-width":"1.5","stroke-linecap":"round","stroke-linejoin":"round"})])],-1)])],void 0),_:1},8,["aria-label","title"])):y("",!0),S.isSubscriberNotesContext&&$.notes.length?(h(),b(M,{key:1,size:"small",class:m(["small only-icon-btn",{"is-active":$.bulkMode}]),onClick:t[3]||(t[3]=e=>S.toggleBulkMode()),"aria-label":e.$t("Select notes"),title:e.$t("Select notes")},{default:g(()=>[...t[14]||(t[14]=[p("span",{class:"icon"},[p("svg",{width:"20",height:"20",viewBox:"0 0 20 20",fill:"none",xmlns:"http://www.w3.org/2000/svg"},[p("rect",{x:"3",y:"3",width:"6",height:"6",rx:"1",stroke:"var(--fc-secondary-text)","stroke-width":"1.5"}),p("rect",{x:"11",y:"3",width:"6",height:"6",rx:"1",stroke:"var(--fc-secondary-text)","stroke-width":"1.5"}),p("rect",{x:"3",y:"11",width:"6",height:"6",rx:"1",stroke:"var(--fc-secondary-text)","stroke-width":"1.5"}),p("path",{d:"M11 14L13 16L17 12",stroke:"var(--fc-secondary-text)","stroke-width":"1.5","stroke-linecap":"round","stroke-linejoin":"round"})])],-1)])],void 0),_:1},8,["class","aria-label","title"])):y("",!0),f(M,{size:"small",class:"small only-icon-btn",onClick:t[4]||(t[4]=e=>S.exportNotes())},{default:g(()=>[p("span",I,[f(V,{"icon-name":"export"})])],void 0),_:1}),f(M,{size:"small",onClick:t[5]||(t[5]=e=>S.initAddNote()),type:"primary"},{default:g(()=>[p("span",Z,[f(V,{"icon-name":"plus"})]),k(" "+u(e.$t("Add Note")),1)],void 0),_:1})])]),p("div",E,[$.pagination.total?(h(),_(w,{key:0},[p("div",U,[(h(!0),_(w,null,N($.notes,s=>(h(),_("div",{key:s.id,class:m(["fcrm_note_row",{"is-expanded":$.expandedNotes[s.id],"is-targeted":$.targetedNoteId===s.id}]),"data-note-id":s.id},[p("div",{class:"fcrm_note_header",onClick:e=>$.bulkMode?S.toggleNoteSelection(s.id):S.toggleNote(s.id)},[p("div",Y,[S.isSubscriberNotesContext&&$.bulkMode?(h(),b(j,{key:0,"model-value":$.selectedNotes.includes(s.id),onClick:x(e=>S.toggleNoteSelection(s.id),["stop"]),class:"fcrm_note_select_checkbox"},null,8,["model-value","onClick"])):y("",!0),t[16]||(t[16]=p("svg",{class:"fcrm_note_chevron",width:"16",height:"16",viewBox:"0 0 16 16",fill:"none",xmlns:"http://www.w3.org/2000/svg"},[p("path",{d:"M10.4001 8L5.6001 12V4L10.4001 8Z",fill:"var(--fc-secondary-text)"})],-1)),p("div",q,[s.added_by&&s.added_by.photo?(h(),_("img",{key:0,src:s.added_by.photo,alt:s.added_by.display_name},null,8,O)):(h(),_("span",K,u(S.getInitials(s.added_by)),1))]),p("div",Q,[p("div",W,u(s.title||$.types[s.type]||s.type),1),p("div",G,[s.added_by&&s.added_by.display_name?(h(),_("span",J,u(s.added_by.display_name),1)):(h(),_("span",X,"Admin")),t[15]||(t[15]=p("span",{class:"fcrm_note_meta_dot"},"·",-1)),p("span",{title:s.created_at},u(e.nsHumanDiffTime(s.created_at)),9,R)])])]),p("div",ee,[e.hasPermission("fcrm_manage_contacts")?(h(),_("div",{key:0,class:"fcrm_note_actions",onClick:t[6]||(t[6]=x(()=>{},["stop"]))},[f(M,{onClick:e=>S.copyNoteLink(s.id),title:e.$t("Copy")},{default:g(()=>[...t[17]||(t[17]=[p("span",{class:"icon"},[p("svg",{width:"20",height:"20",viewBox:"0 0 24 24",fill:"none",xmlns:"http://www.w3.org/2000/svg"},[p("path",{fill:"var(--fc-secondary-text)",d:"M5.993,12.781 C4.748,10.564 5.068,7.706 6.954,5.82 L9.82,2.954 C12.092,0.682 15.775,0.682 18.046,2.954 C20.318,5.225 20.318,8.908 18.046,11.18 L18.007,11.219 C19.252,13.436 18.932,16.294 17.046,18.18 L14.18,21.046 C11.908,23.318 8.225,23.318 5.954,21.046 C3.682,18.775 3.682,15.092 5.954,12.82 Z M10.881,4.014 L8.014,6.881 C6.329,8.566 6.329,11.3 8.014,12.986 C9.7,14.672 12.434,14.672 14.119,12.986 L14.326,12.779 L15.387,13.839 L15.18,14.046 C12.908,16.318 9.225,16.318 6.954,14.046 C6.937,14.029 6.92,14.012 6.904,13.995 C5.329,15.689 5.366,18.338 7.014,19.986 C8.7,21.671 11.434,21.671 13.119,19.986 L15.986,17.119 C17.671,15.434 17.671,12.7 15.986,11.014 C14.3,9.329 11.566,9.329 9.881,11.014 L9.674,11.221 L8.613,10.161 L8.82,9.954 C11.092,7.682 14.775,7.682 17.046,9.954 C17.063,9.971 17.08,9.987 17.096,10.005 C18.671,8.312 18.634,5.662 16.986,4.014 C15.3,2.329 12.566,2.329 10.881,4.014 Z"})])],-1)])],void 0),_:1},8,["onClick","title"]),f(M,{onClick:e=>S.editNote(s)},{default:g(()=>[...t[18]||(t[18]=[p("span",{class:"icon"},[p("svg",{width:"20",height:"20",viewBox:"0 0 20 20",fill:"none",xmlns:"http://www.w3.org/2000/svg"},[p("path",{d:"M5.8105 13.0001L13.417 5.39356L12.3565 4.33306L4.75 11.9396V13.0001H5.8105ZM6.43225 14.5001H3.25V11.3178L11.8263 2.74156C11.9669 2.60096 12.1576 2.52197 12.3565 2.52197C12.5554 2.52197 12.7461 2.60096 12.8868 2.74156L15.0085 4.86331C15.1491 5.00396 15.2281 5.19469 15.2281 5.39356C15.2281 5.59244 15.1491 5.78317 15.0085 5.92381L6.43225 14.5001ZM3.25 16.0001H16.75V17.5001H3.25V16.0001Z",fill:"var(--fc-secondary-text)"})])],-1)])],void 0),_:1},8,["onClick"]),f(B,{onYes:e=>S.remove(s.id)},{default:g(()=>[f(M,null,{default:g(()=>[...t[19]||(t[19]=[p("span",{class:"icon"},[p("svg",{width:"20",height:"20",viewBox:"0 0 20 20",fill:"none",xmlns:"http://www.w3.org/2000/svg"},[p("path",{d:"M13.75 5.5H17.5V7H16V16.75C16 16.9489 15.921 17.1397 15.7803 17.2803C15.6397 17.421 15.4489 17.5 15.25 17.5H4.75C4.55109 17.5 4.36032 17.421 4.21967 17.2803C4.07902 17.1397 4 16.9489 4 16.75V7H2.5V5.5H6.25V3.25C6.25 3.05109 6.32902 2.86032 6.46967 2.71967C6.61032 2.57902 6.80109 2.5 7 2.5H13C13.1989 2.5 13.3897 2.57902 13.5303 2.71967C13.671 2.86032 13.75 3.05109 13.75 3.25V5.5ZM14.5 7H5.5V16H14.5V7ZM7.75 9.25H9.25V13.75H7.75V9.25ZM10.75 9.25H12.25V13.75H10.75V9.25ZM7.75 4V5.5H12.25V4H7.75Z",fill:"var(--fc-secondary-text)"})])],-1)])],void 0,!0),_:1})],void 0),_:1},8,["onYes"])])):y("",!0)])],8,P),c(p("div",{class:"fcrm_note_description",innerHTML:s.description},null,8,te),[[C,$.expandedNotes[s.id]]])],10,F))),128))]),f(he,{pagination:$.pagination,onFetch:S.fetch},null,8,["pagination","onFetch"])],64)):(h(),_("div",se,[f(_e,{"icon-name":"common-empty-state"}),p("div",ie,[p("span",null,u(e.$t("Pro_No_NfPatfn")),1)])]))]),f(ue,{direction:$.direction,class:"fc_company_info_drawer","with-header":!0,size:e.globalDrawerSize,title:$.is_editing_note&&$.editing_note.id?e.$t("Edit Note"):e.$t("Create a note"),"append-to-body":!0,"before-close":S.handleClose,"modal-class":"fcrm_drawer fcrm_note_drawer",modelValue:$.is_editing_note,"onUpdate:modelValue":t[9]||(t[9]=e=>$.is_editing_note=e)},{default:g(()=>[$.is_editing_note?(h(),_("div",{key:0,class:m(["fc_note_type_"+$.editing_note.type,"fc_company_unsaved fc_company_info_wrapper"])},[f(pe,{formData:$.editing_note,fields:$.note_syncing_fields.fields},null,8,["formData","fields"]),p("div",oe,[$.editing_note.id?(h(),b(M,{key:0,onClick:t[7]||(t[7]=e=>S.updateNote()),size:"small",type:"primary"},{default:g(()=>[k(u(e.$t("Update Note")),1)],void 0,!0),_:1})):(h(),b(M,{key:1,onClick:t[8]||(t[8]=e=>S.saveNote()),size:"small",type:"primary"},{default:g(()=>[k(u(e.$t("Create")),1)],void 0,!0),_:1}))])],2)):y("",!0)],void 0),_:1},8,["direction","size","title","before-close","modelValue"]),f(me,{visible:S.isSubscriberNotesContext&&$.bulkMode,"custom-layout":!0},{default:g(()=>[p("div",ne,[p("div",ae,[f(M,{link:"","aria-label":e.$t("Deselect"),onClick:t[10]||(t[10]=e=>S.toggleBulkMode())},{default:g(()=>[p("span",le,[f(V,{"icon-name":"close"})])],void 0,!0),_:1},8,["aria-label"]),p("span",re,[p("strong",null,u($.selectedNotes.length),1),k(" "+u(e.$t("selected")),1)]),t[20]||(t[20]=p("div",{class:"fcrm_bulk_divider"},null,-1)),f(j,{"model-value":S.allSelected,indeterminate:S.someSelected,onChange:t[11]||(t[11]=e=>S.toggleAllSelection())},{default:g(()=>[k(u(e.$t("Select all")),1)],void 0,!0),_:1},8,["model-value","indeterminate"]),$.selectedNotes.length?(h(),_("div",de)):y("",!0),$.selectedNotes.length?(h(),b(M,{key:1,size:"small",type:"danger",plain:"",onClick:t[12]||(t[12]=e=>S.bulkDelete())},{default:g(()=>[p("span",ce,[f(V,{"icon-name":"delete"})]),k(" "+u(e.$t("Delete selected")),1)],void 0,!0),_:1})):y("",!0)])])],void 0),_:1},8,["visible"])])),[[fe,$.loading]])}]])},mounted(){this.changeTitle(this.$t("Notes"))}},[["render",function(e,t,s,i,o,n){const a=d("object-notes-template");return h(),_("div",he,[f(a,{subscriber_id:s.subscriber_id,route_prefix:"subscribers",section_title:e.$t("Contact Notes & Activities"),target_note_id:e.$route.query.note_id},null,8,["subscriber_id","section_title","target_note_id"])])}]]);export{_e as default}; diff --git a/wp-content/plugins/fluent-crm/assets/admin/Modules/Profile/Parts/ProfilePurchaseHistory.js b/wp-content/plugins/fluent-crm/assets/admin/Modules/Profile/Parts/ProfilePurchaseHistory.js new file mode 100644 index 0000000..71fd3d1 --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/admin/Modules/Profile/Parts/ProfilePurchaseHistory.js @@ -0,0 +1 @@ +import{aA as t,aB as r,aH as e,aI as a,ay as s,k as o,aD as i}from"../../../../vendor-element-plus.js?ver=3.1.8";import{aQ as n,W as l,X as d,ab as c,a5 as h,Z as _,Y as p,a6 as u,J as m,az as y,a9 as b,aa as f,a8 as g}from"../../../../vendor.js?ver=3.1.8";import{P as v}from"../../../../PaginationBar.js?ver=3.1.8";import{D as k}from"../../../../DataTable.js?ver=3.1.8";import{_ as S}from"../../../../fc-bits-ui.js?ver=3.1.8";const C={class:"purchase_history_block fcrm_mb_24"},P={class:"fcrm_table_header_inner_left_title"},w={class:"fcrm_purchase_history_table_header_actions"},H={class:"fcrm_purchase_history_table_body"},D=["innerHTML"],T=["innerHTML"],q=["innerHTML"];const $={key:0,class:"fluentcrm_purchase_history_wrapper"},B={key:1},L={class:"text-align-center"};const M=S({name:"ProfilePurchaseHistory",props:["subscriber_id"],components:{PurchaseHistoryBlock:S({name:"PurchaseHistoryBlock",props:["provider","subscriber_id"],components:{PaginationBar:v,DataTable:k},data:()=>({loading:!1,orders:[],pagination:{per_page:10,current_page:1,total:0},sidebar_html:"",after_html:"",has_recount:!1,columnsConfig:{},query_data:{sort_by:"",sort_type:""},sortState:{currentProp:"",direction:""}}),computed:{table_columns(){let t=[];return this.orders&&this.orders.length&&(t=this.orders[0]),t}},methods:{handleSortable(t){t.prop?(this.query_data.sort_by=t.prop,this.sortState.currentProp!==t.prop?(this.sortState.currentProp=t.prop,this.sortState.direction="ASC"):this.sortState.direction="ASC"===this.sortState.direction?"DESC":"ASC",this.query_data.sort_type=this.sortState.direction,this.fetch()):(this.query_data.sort_by="",this.query_data.sort_type="",this.sortState.currentProp="",this.sortState.direction="")},fetch(t=!1){this.loading=!0,this.$get(`subscribers/${this.subscriber_id}/purchase-history`,{provider:this.provider.provider_key,page:this.pagination.current_page,per_page:this.pagination.per_page,sort_by:this.query_data.sort_by,sort_type:this.query_data.sort_type,will_recount:t}).then(t=>{this.orders=t.orders.data,this.pagination.total=parseInt(t.orders.total),t.orders.sidebar_html&&(this.sidebar_html=t.orders.sidebar_html),t.orders.after_html&&(this.after_html=t.orders.after_html),this.has_recount=t.orders.has_recount,t.orders.columns_config&&(this.columnsConfig=t.orders.columns_config)}).catch(t=>{this.handleError(t)}).finally(()=>{this.loading=!1})}},mounted(){this.fetch()}},[["render",function(v,k,S,$,B,L){const M=o,j=r,A=a,x=e,E=n("pagination-bar"),F=n("data-table"),z=t,I=i,J=s;return l(),d("div",C,[c(I,{gutter:24},{default:h(()=>[c(z,{md:B.sidebar_html?16:24,sm:24},{default:h(()=>[c(F,{class:"fcrm_purchase_history_table_wrapper",wrapper_border:!0,"has-selection":!1},{"header-left":h(()=>[_("h3",P,f(S.provider.title),1)]),"header-actions":h(()=>[_("div",w,[B.has_recount?(l(),p(M,{key:0,class:"fcrm_purchase_history_resync_btn",onClick:k[0]||(k[0]=t=>L.fetch("yes")),type:"default",size:"small"},{default:h(()=>[b(f(v.$t("Re-Sync")),1)],void 0,!0),_:1})):g("",!0)])]),table:h(()=>[_("div",H,[B.loading?(l(),p(j,{key:0,animated:"",rows:10})):u((l(),p(x,{key:1,"empty-text":v.$t("No Data Found"),border:"",stripe:"",data:B.orders,onSortChange:L.handleSortable},{empty:h(()=>[_("p",null,[b(f(v.$t("Purchase History from"))+" ",1),_("b",null,f(S.provider.name),1),b(" "+f(v.$t("PurchaseHistoryBlock.empty_desc")),1)])]),default:h(()=>[(l(!0),d(m,null,y(L.table_columns,(t,r)=>{var e,a;return l(),p(A,{key:r,width:B.columnsConfig[r]?B.columnsConfig[r].width:"",label:B.columnsConfig[r]&&B.columnsConfig[r].label?B.columnsConfig[r].label:"",prop:(null==(e=B.columnsConfig[r])?void 0:e.sortable)?B.columnsConfig[r].key:"",sortable:!!(null==(a=B.columnsConfig[r])?void 0:a.sortable)},{default:h(t=>[_("div",{innerHTML:t.row[r]},null,8,D)]),_:2},1032,["width","label","prop","sortable"])}),128))],void 0,!0),_:1},8,["empty-text","data","onSortChange"])),[[J,B.loading]]),B.after_html?(l(),d("div",{key:2,class:"fc_history_before",innerHTML:B.after_html},null,8,T)):g("",!0)])]),pagination:h(()=>[c(E,{pagination:B.pagination,onFetch:L.fetch},null,8,["pagination","onFetch"])]),_:1})],void 0,!0),_:1},8,["md"]),B.sidebar_html?(l(),p(z,{key:0,md:8,sm:24},{default:h(()=>[_("div",{class:"fc_history_sidebar",innerHTML:B.sidebar_html},null,8,q)],void 0,!0),_:1})):g("",!0)],void 0),_:1})])}]])},data:()=>({providersData:{},app_ready:!1}),computed:{is_empty_item(){return this.isEmptyValue(this.providersData)}},mounted(){this.each(window.fcAdmin.purchase_providers,(t,r)=>{const e={title:t.title,name:t.name,provider_key:r};this.providersData[r]=e}),this.app_ready=!0}},[["render",function(t,r,e,a,s,o){const i=n("purchase-history-block");return s.app_ready?(l(),d("div",$,[o.is_empty_item?(l(),d("div",B,[_("h3",L,f(t.$t("Pro_Purchase_hfEwbsh")),1)])):(l(!0),d(m,{key:0},y(s.providersData,(t,r)=>(l(),p(i,{key:r,provider:t,subscriber_id:e.subscriber_id},null,8,["provider","subscriber_id"]))),128))])):g("",!0)}]]);export{M as default}; diff --git a/wp-content/plugins/fluent-crm/assets/admin/Modules/Profile/Parts/ProfileSupportTickets.js b/wp-content/plugins/fluent-crm/assets/admin/Modules/Profile/Parts/ProfileSupportTickets.js new file mode 100644 index 0000000..a81e5a9 --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/admin/Modules/Profile/Parts/ProfileSupportTickets.js @@ -0,0 +1 @@ +import{aH as t,aI as e,ay as a}from"../../../../vendor-element-plus.js?ver=3.1.8";import{aQ as i,W as s,X as r,ab as o,a5 as n,a6 as c,Y as p,J as l,az as d,Z as u,a9 as _,aa as m,a8 as h}from"../../../../vendor.js?ver=3.1.8";import{P as b}from"../../../../PaginationBar.js?ver=3.1.8";import{D as f}from"../../../../DataTable.js?ver=3.1.8";import{_ as g}from"../../../../fc-bits-ui.js?ver=3.1.8";const k={class:"purchase_history_block"},v={class:"fcrm_table_header_inner_left_title"},y=["innerHTML"];const T={key:0};const w=g({name:"ProfileSuportTickets",props:["subscriber_id"],components:{SupportTicketsBlock:g({name:"SupportTicketsBlock",props:["provider","subscriber_id"],components:{DataTable:f,PaginationBar:b},data:()=>({loading:!1,tickets:[],pagination:{per_page:10,current_page:1,total:0},columnsConfig:{}}),computed:{table_columns(){let t=[];return this.tickets&&this.tickets.length&&(t=this.tickets[0]),t}},methods:{fetch(){this.loading=!0,this.$get(`subscribers/${this.subscriber_id}/support-tickets`,{provider:this.provider.provider_key,page:this.pagination.current_page,per_page:this.pagination.per_page}).then(t=>{this.tickets=t.tickets.data,this.pagination.total=parseInt(t.tickets.total),t.tickets.columns_config&&(this.columnsConfig=t.tickets.columns_config)}).catch(t=>{this.handleError(t)}).finally(()=>{this.loading=!1})}},mounted(){this.fetch()}},[["render",function(h,b,f,g,T,w){const C=e,D=t,j=i("pagination-bar"),$=i("data-table"),B=a;return s(),r("div",k,[o($,{class:"fcrm_support_ticket_block",wrapper_border:!0,"has-selection":!1},{"header-left":n(()=>[u("h3",v,m(f.provider.title),1)]),table:n(()=>[c((s(),p(D,{"empty-text":h.$t("No Data Found"),border:"",stripe:"",data:T.tickets},{empty:n(()=>[u("p",null,[_(m(h.$t("Support Tickets from"))+" ",1),u("b",null,m(f.provider.name),1),_(" "+m(h.$t("no_tickets_found_for_this_subscriber")),1)])]),default:n(()=>[(s(!0),r(l,null,d(w.table_columns,(t,e)=>(s(),p(C,{key:e,width:T.columnsConfig[e]?T.columnsConfig[e].width:"",label:T.columnsConfig[e]&&T.columnsConfig[e].label?T.columnsConfig[e].label:h.ucFirst(e)},{default:n(t=>[u("div",{innerHTML:t.row[e]},null,8,y)]),_:2},1032,["width","label"]))),128))],void 0,!0),_:1},8,["empty-text","data"])),[[B,T.loading]])]),pagination:n(()=>[o(j,{pagination:T.pagination,onFetch:w.fetch},null,8,["pagination","onFetch"])]),_:1})])}]])},data:()=>({providersData:{},app_ready:!1}),computed:{},mounted(){this.each(window.fcAdmin.support_tickets_providers,(t,e)=>{const a={title:t.title,name:t.name,provider_key:e};this.providersData[e]=a}),this.app_ready=!0}},[["render",function(t,e,a,o,n,c){const u=i("support-tickets-block");return n.app_ready?(s(),r("div",T,[(s(!0),r(l,null,d(n.providersData,(t,e)=>(s(),p(u,{key:e,provider:t,subscriber_id:a.subscriber_id},null,8,["provider","subscriber_id"]))),128))])):h("",!0)}]]);export{w as default}; diff --git a/wp-content/plugins/fluent-crm/assets/admin/Modules/Profile/Parts/SubscriberExternalView.js b/wp-content/plugins/fluent-crm/assets/admin/Modules/Profile/Parts/SubscriberExternalView.js new file mode 100644 index 0000000..3119620 --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/admin/Modules/Profile/Parts/SubscriberExternalView.js @@ -0,0 +1 @@ +import{k as e,aT as t,aD as i,aA as r,aB as s}from"../../../../vendor-element-plus.js?ver=3.1.8";import{aQ as a,W as o,X as n,Z as d,aa as l,ab as c,a5 as h,a9 as _,a8 as m,a0 as f,Y as u}from"../../../../vendor.js?ver=3.1.8";import{F as p}from"../../../../_FormBuilder2.js?ver=3.1.8";import{_ as v}from"../../../../fc-bits-ui.js?ver=3.1.8";import"../../../../PhotoWidget.js?ver=3.1.8";import"../../../../input-popover-dropdown.js?ver=3.1.8";import"../../../../data_config.js?ver=3.1.8";import"../../../../_OptionSelector.js?ver=3.1.8";import"../../../../_AjaxSelector.js?ver=3.1.8";import"../../../../_VerifiedEmailInput.js?ver=3.1.8";const g={key:0,class:"fcrm_external_profile_wrapper"},b={class:"fcrm_external_profile_header"},w={class:"fcrm_external_profile_header--title"},y={key:0,class:"fcrm_external_profile_header--actions"},x=["innerHTML"],j={key:0},F={key:0,style:{padding:"10px 15px"}},k={class:"fc_drawer_footer"};const D=v({name:"SubscriberProfileExternalView",props:["subscriber","subscriber_id"],components:{FormBuilder:p},data(){return{direction:"rtl",profile_section:this.$route.query.handler,heading:"",content_html:"",loading:!0,crud:null,showForm:!1,crudConfig:{},saving:!1}},methods:{fetchData(){this.loading=!0,this.$get(`subscribers/${this.subscriber_id}/external_view`,{section_provider:this.profile_section}).then(e=>{this.heading=e.heading,this.content_html=e.content_html,e.crud&&(this.crud=e.crud)}).catch(e=>{this.handleError(e)}).finally(()=>{this.loading=!1})},initCrudForm(){if(!this.crud.fields)return;const e={};this.each(this.crud.fields,(t,i)=>{e[i]=t.default_value||""}),this.crudConfig.model=e,this.showForm=!0},saveData(){this.saving=!0,this.$post(`subscribers/${this.subscriber_id}/external_view`,{section_provider:this.profile_section,data:this.crudConfig.model}).then(e=>{this.$notify.success(e.message||"Saved successfully"),this.showForm=!1,e.content_html?(this.content_html=e.content_html,e.heading&&(this.heading=e.heading)):this.fetchData()}).catch(e=>{this.handleError(e)}).finally(()=>{this.saving=!1})}},mounted(){window.fcAdmin&&window.fcAdmin.is_rtl&&(this.direction="ltr"),this.fetchData()}},[["render",function(p,v,D,C,S,$){const V=e,z=a("form-builder"),A=t,E=s,B=r,T=i;return S.loading?(o(),u(E,{key:1,style:{"margin-top":"20px"},class:"fc_skeleton_loader",animated:""},{template:h(()=>[c(T,{gutter:30},{default:h(()=>[c(B,{span:12},{default:h(()=>[c(E,{rows:10})],void 0,!0),_:1}),c(B,{span:12},{default:h(()=>[c(E,{rows:3})],void 0,!0),_:1})],void 0,!0),_:1})]),_:1})):(o(),n("div",g,[d("div",b,[d("h3",w,l(S.heading),1),S.crud&&S.crud.btn_label?(o(),n("div",y,[c(V,{onClick:v[0]||(v[0]=e=>$.initCrudForm()),size:"small"},{default:h(()=>[_(l(S.crud.btn_label),1)],void 0),_:1})])):m("",!0)]),d("div",{class:f(["fcrm_external_profile_data","fcrm_section_"+S.profile_section]),innerHTML:S.content_html},null,10,x),S.crud&&S.crud.btn_label?(o(),n("div",j,[c(A,{direction:S.direction,class:"fc_company_info_drawer","with-header":!0,size:p.globalDrawerSize,title:S.crud.form_heading,"append-to-body":!0,modelValue:S.showForm,"onUpdate:modelValue":v[2]||(v[2]=e=>S.showForm=e)},{default:h(()=>[S.showForm?(o(),n("div",F,[c(z,{formData:S.crudConfig.model,fields:S.crud.fields},null,8,["formData","fields"]),d("div",k,[c(V,{type:"primary",onClick:v[1]||(v[1]=e=>$.saveData())},{default:h(()=>[_(l(S.crud.save_btn_text||"Save"),1)],void 0,!0),_:1})])])):m("",!0)],void 0),_:1},8,["direction","size","title","modelValue"])])):m("",!0)]))}]]);export{D as default}; diff --git a/wp-content/plugins/fluent-crm/assets/admin/Modules/Profile/Profile.js b/wp-content/plugins/fluent-crm/assets/admin/Modules/Profile/Profile.js new file mode 100644 index 0000000..406ed5d --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/admin/Modules/Profile/Profile.js @@ -0,0 +1 @@ +import{aQ as e,aS as s,k as t,aB as i,T as a,aH as r,aI as o,aO as n,as as c,aK as l,aL as d,E as u,P as m,at as h,ay as p,h as _,i as b,j as f,D as g,e as y,az as v,aG as w,aT as C,aE as $,b9 as k,b5 as S,aq as x,ar as V,aN as L,ap as P,Q as A,aZ as M,a_ as H,aD as I,aA as T,aC as Z}from"../../../vendor-element-plus.js?ver=3.1.8";import{aQ as U,W as D,X as E,Z as z,aa as F,J as N,az as R,Y as B,a5 as q,ab as O,a8 as j,a9 as W,a0 as G,$ as K,_ as Q,a6 as Y,ax as J,b2 as X,bV as ee,ad as se,a7 as te,ac as ie,av as ae}from"../../../vendor.js?ver=3.1.8";import{T as re,E as oe,C as ne,u as ce}from"../../../Tagger.js?ver=3.1.8";import{_ as le,I as de}from"../../../fc-bits-ui.js?ver=3.1.8";import{P as ue}from"../../../PhotoWidget.js?ver=3.1.8";import{P as me}from"../../../PaginationBar.js?ver=3.1.8";import{C as he}from"../../../Confirm.js?ver=3.1.8";import{j as pe}from"../../../data_config.js?ver=3.1.8";import{P as _e}from"../../../PromoCard.js?ver=3.1.8";import{B as be}from"../../../Badge.js?ver=3.1.8";import"../../../CompanyEditForm.js?ver=3.1.8";import"../../../fc-bits.js?ver=3.1.8";import"../../../Filterer.js?ver=3.1.8";const fe={class:"header"},ge={class:"items"},ye={class:"items_actions"},ve={class:"items_inner"},we={key:0,class:"icon"},Ce={key:1,class:"icon"},$e={class:"fc_tagger_footer fcrm_mt_10"};const ke={class:"fcrm_profile_tagger fc_profile_tagger"},Se={key:0,class:"fcrm_profile_tagger_row"},xe={key:1,class:"fcrm_profile_tagger_row"};const Ve=le({name:"ProfileListTags",props:["subscriber"],components:{Tagger:le({name:"Tagger",components:{Editor:oe,Icons:de},data:()=>({new_payload:!1,showItem:!0}),props:["type","taggables","options","matched","creatable"],emits:["addedNew"],mixins:[re],computed:{none(){return"tags"==this.type?this.$t("No tags found"):"lists"==this.type?this.$t("No lists found"):"No "+this.type+" found"}},methods:{handleClose(e){this.$confirm(this.$t("Are you sure you want to remove this?"),this.$t("Warning"),{confirmButtonText:this.$t("OK"),cancelButtonText:this.$t("Cancel"),type:"warning"}).then(()=>{this.remove(e)}).catch(()=>{})},remove(e){this.subscribe({attach:[],detach:[e]})},updatePayLoad(e){this.new_payload=e},pushPayload(){this.new_payload?(this.subscribe(this.new_payload),setTimeout(()=>{this.showItem=!1,this.$nextTick(()=>{this.showItem=!0})},500)):this.$notify.error(this.$t("No changes found"))},getDescription(e){return e.pivot?this.$t("Added @ ")+e.pivot.created_at:""}}},[["render",function(i,a,r,o,n,c){const l=U("icons"),d=e,u=s,m=t,h=U("editor");return r.taggables&&n.showItem?(D(),E("div",{key:0,class:G("fcrm_profile_tagger_"+r.type)},[z("div",fe,[z("h2",null,F(i.$t(r.type)),1)]),z("div",ge,[z("div",ye,[z("div",ve,[(D(!0),E(N,null,R(r.taggables,e=>(D(),B(d,{key:e.title,class:"el-tag--white",title:c.getDescription(e),closable:"",onClose:s=>c.handleClose(e.slug)},{default:q(()=>["tags"===r.type?(D(),E("span",we,[O(l,{iconName:"TagIcon"})])):j("",!0),"lists"===r.type?(D(),E("span",Ce,[O(l,{iconName:"ListIcon"})])):j("",!0),W(" "+F(e.title),1)],void 0),_:2},1032,["title","onClose"]))),128)),r.taggables.length?j("",!0):(D(),B(u,{key:0,title:c.none,type:"info",closable:!1},null,8,["title"])),O(h,{type:r.type,options:i.choices,noMatch:i.noMatch,matched:r.matched,selectionCount:1,placement:"bottom-start",onSearch:i.search,onAddedNew:a[1]||(a[1]=e=>{i.$emit("addedNew",e)}),creatable:r.creatable,onSubscribe:c.updatePayLoad},{footer:q(()=>[z("div",$e,[O(m,{onClick:a[0]||(a[0]=e=>c.pushPayload()),style:{width:"100%"},type:"primary",size:"small"},{default:q(()=>[W(F(i.$t("Confirm")),1)],void 0,!0),_:1})])]),_:1},8,["type","options","noMatch","matched","onSearch","creatable","onSubscribe"])])])])],2)):j("",!0)}],["__scopeId","data-v-81e7743d"]])},data:()=>({options:{tags:[],lists:[]},matches:{tags:{},lists:{}},subscribing:!1}),methods:{setup(e){this.matches.tags=[],this.matches.lists=[],this.subscriber.tags=e.tags,this.subscriber.lists=e.lists,e.tags.forEach(e=>this.match(e,this.matches.tags)),e.lists.forEach(e=>this.match(e,this.matches.lists))},getOptions(){this.options={tags:this.appVars.available_tags,lists:this.appVars.available_lists}},subscribe({type:e,payload:s}){const{attach:t,detach:i}=s,a={type:e,attach:t,detach:i,subscribers:[this.subscriber.id]};this.subscribing=!0,this.$post("subscribers/sync-segments",a).then(e=>{const s=e.subscribers[0];this.setup(s),this.$notify.success({title:this.$t("Great!"),message:e.message,offset:19})}).catch(e=>{this.handleError(e)}).finally(()=>{this.subscribing=!1})},search(e,s){this.options[e]=s},match(e,s){s[e.slug]=1}},mounted(){this.getOptions(),this.setup(this.subscriber)}},[["render",function(e,s,t,a,r,o){const n=i,c=U("tagger");return D(),E("div",ke,[r.subscribing?(D(),E("div",Se,[O(n,{rows:3})])):(D(),E("div",xe,[O(c,{type:"lists",onSearch:o.search,onSubscribe:o.subscribe,class:"info-item",creatable:!0,options:r.options.lists,onAddedNew:s[0]||(s[0]=e=>{r.options.lists.push(e)}),matched:r.matches.lists,taggables:t.subscriber.lists},null,8,["onSearch","onSubscribe","options","matched","taggables"]),O(c,{type:"tags",onSearch:o.search,class:"info-item",creatable:!0,onSubscribe:o.subscribe,onAddedNew:s[1]||(s[1]=e=>{r.options.tags.push(e)}),options:r.options.tags,matched:r.matches.tags,taggables:t.subscriber.tags},null,8,["onSearch","onSubscribe","options","matched","taggables"])]))])}]]),Le=["title"],Pe={class:"fcrm_profile_header_stat_count"},Ae={class:"fcrm_table_wrapper"},Me={class:"fcrm_table_body"},He={key:0,class:"fcrm_loader_wrap"},Ie=["href"],Te={class:"counter"};const Ze={class:"fluentcrm_profile_header"},Ue={class:"fluentcrm_profile-photo"},De={class:"fcrm_profile_photo_actions"},Ee={class:"icon"},ze={class:"profile-info"},Fe={class:"fcrm_profile_header_info_row"},Ne={class:"fcrm_profile_header_infos"},Re={class:"profile_title"},Be={class:"fcrm_profile_header_name"},qe={class:"fcrm_profile_header_date"},Oe={key:0},je={class:"fcrm_profile_header_meta fcrm_profile_header_meta_email show_on_parent"},We={class:"fcrm_profile_header_meta_user"},Ge=["title"],Ke=["href"],Qe={key:0,class:"fcrm_profile_header_meta_userrole"},Ye={class:"items"},Je={class:"fcrm_profile_header_stats_badges"},Xe=["title"],es={class:"fcrm_profile_header_stat_count"},ss=["title"],ts={class:"fcrm_profile_header_stat_count"},is={key:0,class:"fc_t_10"},as={key:1,class:"fc_t_10"},rs={key:0},os={class:"fcrm_profile_header_actions"},ns={class:"fcrm_profile_action"},cs={class:"fcrm_contact_popover_header"},ls={class:"fcrm_contact_popover_body"},ds={class:"fcrm_profile_contact_type_text"},us={class:"fcrm_profile_action"},ms={class:"fcrm_contact_popover_header"},hs={class:"fcrm_contact_popover_body"},ps={key:0,class:"fcrm_profile_action"},_s={class:"fcrm_contact_popover_header"},bs={class:"fcrm_contact_popover_body"};const fs={class:"fluentcrm_profile_header_warpper"};const gs=le({name:"ProfileHeader",components:{ProfileInfo:le({name:"ProfileInfo",emits:["fetch"],props:{subscriber:{type:Object,default:()=>null},photo_holder:{type:String,default:"fc_photo_holder_mini"}},components:{ProfileStatURL:le({name:"ProfileStatURL",props:{subscriber:{type:Object,required:!0,default:()=>null}},components:{PaginationBar:me,Location:a},data:()=>({urlMetrics:[],loadingMetrics:!1,pagination:{total:0,per_page:10,current_page:1},query_data:{sort_by:"",sort_type:""}}),computed:{stats(){var e;return(null==(e=this.subscriber)?void 0:e.stats)||{emails:0,opens:0,clicks:0,total_points:0,last_activity:null}}},methods:{handleSortable(e){"descending"===e.order?(this.query_data.sort_by=e.prop,this.query_data.sort_type="DESC"):(this.query_data.sort_by=e.prop,this.query_data.sort_type="ASC"),this.getUrlMetrics()},getUrlMetrics(){this.loadingMetrics=!0,this.$get(`subscribers/${this.subscriber.id}/url-metrics`,{sort_by:this.query_data.sort_by,sort_type:this.query_data.sort_type,per_page:this.pagination.per_page,page:this.pagination.current_page,subscriber_id:this.subscriber.id}).then(e=>{this.urlMetrics=e.urlMetrics.data,this.pagination.total=e.urlMetrics.total}).catch(e=>{this.handleError(e)}).finally(()=>{this.loadingMetrics=!1})}}},[["render",function(e,s,t,a,c,l){const d=i,u=o,m=r,h=U("pagination-bar"),p=n;return D(),B(p,{placement:"bottom",width:560,trigger:"click","popper-class":"fcrm_link_stats_popover",effect:"light"},{reference:q(()=>[z("span",{onClick:s[0]||(s[0]=(...e)=>l.getUrlMetrics&&l.getUrlMetrics(...e)),class:"fcrm_profile_header_stat stats_link",title:e.$t("Click Rate")},[s[1]||(s[1]=z("span",{class:"icon"},[z("svg",{viewBox:"0 0 20 20",fill:"none",xmlns:"http://www.w3.org/2000/svg"},[z("path",{d:"M12.541 11.1234L14.455 16.3839L10.9315 17.6664L9.01675 12.4059L6.0925 14.2396L7.3075 2.22461L15.9602 10.6486L12.5417 11.1234H12.541ZM12.5327 15.4869L10.4965 9.89186L12.7165 9.58436L8.4865 5.46686L7.894 11.3394L9.7915 10.1491L11.8277 15.7441L12.5327 15.4869Z",fill:"var(--fc-text-muted)"})])],-1)),z("span",Pe,F(e.percent(l.stats.clicks,l.stats.emails)),1)],8,Le)]),default:q(()=>[z("div",Ae,[z("div",Me,[c.loadingMetrics?(D(),E("div",He,[O(d,{animated:!0,rows:5})])):(D(),B(m,{key:1,sortable:"",data:c.urlMetrics,onSortChange:l.handleSortable},{default:q(()=>[O(u,{label:e.$t("URL")},{default:q(e=>[z("a",{href:e.row.url,target:"_blank",rel:"noopener"},F(e.row.url),9,Ie)]),_:1},8,["label"]),O(u,{width:"100",sortable:"",prop:"counter",label:e.$t("Clicks")},{default:q(e=>[z("span",Te,F(e.row.count),1)]),_:1},8,["label"])],void 0,!0),_:1},8,["data","onSortChange"])),O(h,{pagination:c.pagination,onFetch:l.getUrlMetrics},null,8,["pagination","onFetch"])])]),j("",!0)],void 0),_:1})}]]),PhotoWidget:ue,ArrowDownBold:c,Icons:de},data:()=>({status_visible:!1,lead_visible:!1,subscriber_statuses:window.fcAdmin.available_contact_statuses,contact_types:window.fcAdmin.available_contact_types,sms_status_visible:!1,sms_statuses:window.fcAdmin.available_sms_statuses}),computed:{name(){return this.subscriber.first_name||this.subscriber.last_name?this.subscriber.prefix?`${this.subscriber.prefix||""} ${this.subscriber.first_name||""} ${this.subscriber.last_name||""}`:`${this.subscriber.first_name||""} ${this.subscriber.last_name||""}`:this.subscriber.email},isSmsEnabled:()=>"yes"===window.fcAdmin.sms_enabled,smsStatusMap(){return this.sms_statuses.reduce((e,s)=>(e[s.id]=s.title,e),{})},stats(){var e;return(null==(e=this.subscriber)?void 0:e.stats)||{emails:0,opens:0,clicks:0,total_points:0,last_activity:null}}},methods:{saveStatus(){this.updateProperty("status",this.subscriber.status,()=>{this.status_visible=!1})},saveLead(){this.updateProperty("contact_type",this.subscriber.contact_type,()=>{this.lead_visible=!1})},saveSmsStatus(){this.updateProperty("sms_status",this.subscriber.sms_status,()=>{this.sms_status_visible=!1})},removeAvatar(){this.subscriber.avatar="",this.updateAvatar(""),this.$emit("fetch")},updateAvatar(e){this.updateProperty("avatar",e)},updateProperty(e,s,t){this.$put("subscribers/subscribers-property",{property:e,subscribers:[this.subscriber.id],value:s}).then(e=>{this.$notify.success(e.message),t&&t(e),this.$emit("fetch")}).catch(e=>{this.handleError(e)})},sendDoubleOptinEmail(){this.$post(`subscribers/${this.subscriber.id}/send-double-optin`).then(e=>{this.$notify.success(e.message)}).catch(e=>{this.handleError(e)}).finally(()=>{})}}},[["render",function(s,i,a,r,o,c){const m=U("Icons"),h=t,p=U("photo-widget"),_=U("ProfileStatURL"),b=d,f=l,g=U("ArrowDownBold"),y=u,v=e,w=n;return D(),E("div",Ze,[z("div",Ue,[z("div",{class:G(a.photo_holder),style:K({backgroundImage:"url("+a.subscriber.photo+")"})},null,6),z("div",De,[O(p,{class:"fc_photo_changed",only_icon:!0,btn_mode:!0,btn_class:"only-icon-btn small",onChanged:c.updateAvatar,modelValue:a.subscriber.photo,"onUpdate:modelValue":i[0]||(i[0]=e=>a.subscriber.photo=e)},{after:q(()=>[a.subscriber.avatar?(D(),B(h,{key:0,size:"small",plain:"",type:"danger",class:"only-icon-btn small",onClick:c.removeAvatar},{default:q(()=>[z("span",Ee,[O(m,{"icon-name":"delete"})])],void 0,!0),_:1},8,["onClick"])):j("",!0)]),_:1},8,["onChanged","modelValue"])])]),z("div",ze,[z("div",Fe,[z("div",Ne,[z("div",Re,[Q(s.$slots,"heading"),z("h3",Be,[W(F(c.name)+" ",1),i[11]||(i[11]=z("span",{class:"dot-separator"},null,-1)),z("span",qe,[W(F(s.$t("Added"))+" "+F(s.$nsHumanDiffTime(a.subscriber.created_at))+" ",1),a.subscriber.last_activity?(D(),E("span",Oe," & "+F(s.$t("Last Activity"))+" "+F(s.$nsHumanDiffTime(a.subscriber.last_activity)),1)):j("",!0)])])]),z("p",je,[z("span",We,[W(F(a.subscriber.email)+" ",1),a.subscriber.user_id&&a.subscriber.user_edit_url?(D(),E("span",{key:0,class:"fcrm_profile_header_meta_user_id",title:s.$t("WordPress User ID")},[i[13]||(i[13]=z("span",{class:"fc_middot"},"·",-1)),z("a",{target:"_blank",rel:"noopener",href:a.subscriber.user_edit_url},[W(F(a.subscriber.user_id)+" ",1),i[12]||(i[12]=z("span",{class:"dashicons dashicons-external"},null,-1))],8,Ke)],8,Ge)):j("",!0)]),a.subscriber.user_roles&&a.subscriber.user_roles.length?(D(),E("span",Qe,[W(F(s.$t("User Roles:"))+" ",1),z("span",Ye,[(D(!0),E(N,null,R(a.subscriber.user_roles,e=>(D(),E("span",{class:"fc_tag_prof_info",key:e},F(e),1))),128))])])):j("",!0)]),z("div",Je,[z("span",{class:"fcrm_profile_header_stat",title:s.$t("Total Emails")},[i[14]||(i[14]=z("span",{class:"icon"},[z("svg",{viewBox:"0 0 20 20",fill:"none",xmlns:"http://www.w3.org/2000/svg"},[z("path",{d:"M3.25 3.75H16.75C16.9489 3.75 17.1397 3.82902 17.2803 3.96967C17.421 4.11032 17.5 4.30109 17.5 4.5V15.5C17.5 15.6989 17.421 15.8897 17.2803 16.0303C17.1397 16.171 16.9489 16.25 16.75 16.25H3.25C3.05109 16.25 2.86032 16.171 2.71967 16.0303C2.57902 15.8897 2.5 15.6989 2.5 15.5V4.5C2.5 4.30109 2.57902 4.11032 2.71967 3.96967C2.86032 3.82902 3.05109 3.75 3.25 3.75ZM16 6.9285L10.054 12.2535L4 6.912V14.75H16V6.9285ZM4.38325 5.25L10.0457 10.2465L15.6265 5.25H4.38325Z",fill:"var(--fc-text-muted)"})])],-1)),z("span",es,F(c.stats.emails),1)],8,Xe),i[16]||(i[16]=z("span",{class:"fc_middot"},"·",-1)),z("span",{class:"fcrm_profile_header_stat",title:s.$t("Open Rate")},[i[15]||(i[15]=z("span",{class:"icon"},[z("svg",{viewBox:"0 0 20 20",fill:"none",xmlns:"http://www.w3.org/2000/svg"},[z("path",{d:"M2.68225 6.14037L9.6175 1.98237C9.73406 1.91243 9.86744 1.87549 10.0034 1.87549C10.1393 1.87549 10.2727 1.91243 10.3892 1.98237L17.3177 6.14112C17.3733 6.17443 17.4194 6.22158 17.4513 6.27797C17.4832 6.33436 17.5 6.39806 17.5 6.46287V15.9999C17.5 16.1988 17.421 16.3895 17.2803 16.5302C17.1397 16.6709 16.9489 16.7499 16.75 16.7499H3.25C3.05109 16.7499 2.86032 16.6709 2.71967 16.5302C2.57902 16.3895 2.5 16.1988 2.5 15.9999V6.46212C2.49999 6.39731 2.51677 6.33361 2.54871 6.27722C2.58065 6.22083 2.62666 6.17368 2.68225 6.14037ZM4 7.09962V15.2499H16V7.09887L10.003 3.49887L4 7.09887V7.09962ZM10.045 11.2734L14.017 7.92612L14.983 9.07362L10.0555 13.2264L5.023 9.07887L5.977 7.92087L10.045 11.2734Z",fill:"var(--fc-text-muted)"})])],-1)),z("span",ts,F(s.percent(c.stats.opens,c.stats.emails)),1)],8,ss),i[17]||(i[17]=z("span",{class:"fc_middot"},"·",-1)),O(_,{subscriber:a.subscriber},null,8,["subscriber"])]),"pending"==a.subscriber.status?(D(),E("div",is,[O(h,{onClick:i[1]||(i[1]=e=>c.sendDoubleOptinEmail()),class:"fcrm_setup_btn",size:"small"},{default:q(()=>[O(m,{"icon-name":"send-mail",style:{width:"16px"}}),W(" "+F(s.$t("Send Double Opt-In Email")),1)],void 0),_:1})])):j("",!0),a.subscriber.unsubscribe_reason?(D(),E("div",as,[z("p",null,[W(F(s.ucFirst(a.subscriber.status))+" "+F(s.$t("Reason:"))+" "+F(a.subscriber.unsubscribe_reason)+" ",1),a.subscriber.unsubscribe_date?(D(),E("span",rs," @ "+F(a.subscriber.unsubscribe_date),1)):j("",!0)])])):j("",!0)]),z("div",os,[z("div",ns,[O(w,{placement:"bottom",width:"224",visible:o.lead_visible,"onUpdate:visible":i[4]||(i[4]=e=>o.lead_visible=e),trigger:"click","popper-class":"fcrm_contact_popover"},{reference:q(()=>[O(v,{size:"small"},{default:q(()=>[i[19]||(i[19]=z("span",{class:"icon"},[z("svg",{viewBox:"0 0 16 16",fill:"none",xmlns:"http://www.w3.org/2000/svg"},[z("path",{d:"M12.2 4.9999H15.2V6.1999H12.2V4.9999ZM11 7.9999H15.2V9.1999H11V7.9999ZM12.8 10.9999H15.2V12.1999H12.8V10.9999ZM2 13.9999C2 12.7269 2.50571 11.506 3.40589 10.6058C4.30606 9.70562 5.52696 9.1999 6.8 9.1999C8.07304 9.1999 9.29394 9.70562 10.1941 10.6058C11.0943 11.506 11.6 12.7269 11.6 13.9999H10.4C10.4 13.0451 10.0207 12.1294 9.34558 11.4543C8.67045 10.7792 7.75478 10.3999 6.8 10.3999C5.84522 10.3999 4.92955 10.7792 4.25442 11.4543C3.57928 12.1294 3.2 13.0451 3.2 13.9999H2ZM6.8 8.5999C4.811 8.5999 3.2 6.9889 3.2 4.9999C3.2 3.0109 4.811 1.3999 6.8 1.3999C8.789 1.3999 10.4 3.0109 10.4 4.9999C10.4 6.9889 8.789 8.5999 6.8 8.5999ZM6.8 7.3999C8.126 7.3999 9.2 6.3259 9.2 4.9999C9.2 3.6739 8.126 2.5999 6.8 2.5999C5.474 2.5999 4.4 3.6739 4.4 4.9999C4.4 6.3259 5.474 7.3999 6.8 7.3999Z",fill:"var(--fc-secondary-text)"})])],-1)),z("span",ds,F(s.$t(a.subscriber.contact_type)),1),O(y,null,{default:q(()=>[O(g)],void 0,!0),_:1})],void 0,!0),_:1})]),default:q(()=>[z("div",cs,[i[18]||(i[18]=z("svg",{xmlns:"http://www.w3.org/2000/svg",width:"14",height:"14",viewBox:"0 0 14 14",fill:"none"},[z("path",{d:"M10.675 4.3751H13.3V5.4251H10.675V4.3751ZM9.625 7.0001H13.3V8.0501H9.625V7.0001ZM11.2 9.6251H13.3V10.6751H11.2V9.6251ZM1.75 12.2501C1.75 11.1362 2.1925 10.0679 2.98015 9.28025C3.7678 8.4926 4.83609 8.0501 5.95 8.0501C7.06391 8.0501 8.1322 8.4926 8.91985 9.28025C9.7075 10.0679 10.15 11.1362 10.15 12.2501H9.1C9.1 11.4147 8.76813 10.6135 8.17739 10.0227C7.58665 9.43197 6.78543 9.1001 5.95 9.1001C5.11457 9.1001 4.31335 9.43197 3.72261 10.0227C3.13187 10.6135 2.8 11.4147 2.8 12.2501H1.75ZM5.95 7.5251C4.20962 7.5251 2.8 6.11547 2.8 4.3751C2.8 2.63472 4.20962 1.2251 5.95 1.2251C7.69037 1.2251 9.1 2.63472 9.1 4.3751C9.1 6.11547 7.69037 7.5251 5.95 7.5251ZM5.95 6.4751C7.11025 6.4751 8.05 5.53535 8.05 4.3751C8.05 3.21485 7.11025 2.2751 5.95 2.2751C4.78975 2.2751 3.85 3.21485 3.85 4.3751C3.85 5.53535 4.78975 6.4751 5.95 6.4751Z",fill:"var(--fc-text-muted)"})],-1)),z("h3",null,F(s.$t("Contact Type")),1)]),z("div",ls,[O(f,{placeholder:s.$t("Select Status"),size:"small",modelValue:a.subscriber.contact_type,"onUpdate:modelValue":i[2]||(i[2]=e=>a.subscriber.contact_type=e),teleported:!1},{default:q(()=>[(D(!0),E(N,null,R(o.contact_types,e=>(D(),B(b,{key:e.id,value:e.id,label:e.title},null,8,["value","label"]))),128))],void 0,!0),_:1},8,["placeholder","modelValue"]),O(h,{onClick:i[3]||(i[3]=e=>c.saveLead()),type:"primary",size:"small"},{default:q(()=>[W(F(s.$t("Save")),1)],void 0,!0),_:1})])],void 0),_:1},8,["visible"])]),z("div",us,[O(w,{placement:"bottom",width:"224",visible:o.status_visible,"onUpdate:visible":i[7]||(i[7]=e=>o.status_visible=e),trigger:"click","popper-class":"fcrm_contact_popover"},{reference:q(()=>[O(v,{size:"small"},{default:q(()=>[i[21]||(i[21]=z("span",{class:"icon"},[z("svg",{viewBox:"0 0 16 16",fill:"none",xmlns:"http://www.w3.org/2000/svg"},[z("path",{d:"M2.6 3H13.4C13.5591 3 13.7117 3.06321 13.8243 3.17574C13.9368 3.28826 14 3.44087 14 3.6V12.4C14 12.5591 13.9368 12.7117 13.8243 12.8243C13.7117 12.9368 13.5591 13 13.4 13H2.6C2.44087 13 2.28826 12.9368 2.17574 12.8243C2.06321 12.7117 2 12.5591 2 12.4V3.6C2 3.44087 2.06321 3.28826 2.17574 3.17574C2.28826 3.06321 2.44087 3 2.6 3ZM12.8 5.5428L8.0432 9.8028L3.2 5.5296V11.8H12.8V5.5428ZM3.5066 4.2L8.0366 8.1972L12.5012 4.2H3.5066Z",fill:"var(--fc-secondary-text)"})])],-1)),W(" "+F(s.$t(a.subscriber.status))+" ",1),O(y,null,{default:q(()=>[O(g)],void 0,!0),_:1})],void 0,!0),_:1})]),default:q(()=>[z("div",ms,[i[20]||(i[20]=z("svg",{xmlns:"http://www.w3.org/2000/svg",width:"14",height:"14",viewBox:"0 0 14 14",fill:"none"},[z("path",{d:"M2.275 2.625H11.725C11.8643 2.625 11.9977 2.68031 12.0962 2.77886C12.1948 2.87741 12.25 3.01082 12.25 3.15V10.85C12.25 10.9892 12.1948 11.1226 12.0962 11.2211C11.9977 11.3197 11.8643 11.375 11.725 11.375H2.275C2.13576 11.375 2.00235 11.3197 1.9038 11.2211C1.80525 11.1226 1.75 10.9892 1.75 10.85V3.15C1.75 3.01082 1.80525 2.87741 1.9038 2.77886C2.00235 2.68031 2.13576 2.625 2.275 2.625ZM11.2 4.8499L7.0378 8.57745L2.8 4.83765V10.325H11.2V4.8499ZM3.06827 3.675L7.0319 7.17255L10.9385 3.675H3.06827Z",fill:"var(--fc-text-muted)"})],-1)),z("h3",null,F(s.$t("Contact Status")),1)]),z("div",hs,[O(f,{placeholder:s.$t("Select Status"),size:"small",modelValue:a.subscriber.status,"onUpdate:modelValue":i[5]||(i[5]=e=>a.subscriber.status=e),teleported:!1},{default:q(()=>[(D(!0),E(N,null,R(o.subscriber_statuses,e=>(D(),B(b,{key:e.id,value:e.id,label:e.title},null,8,["value","label"]))),128))],void 0,!0),_:1},8,["placeholder","modelValue"]),s.hasPermission("fcrm_manage_contacts")?(D(),B(h,{key:0,type:"primary",onClick:i[6]||(i[6]=e=>c.saveStatus()),size:"small"},{default:q(()=>[W(F(s.$t("Save")),1)],void 0,!0),_:1})):j("",!0)])],void 0),_:1},8,["visible"])]),c.isSmsEnabled?(D(),E("div",ps,[O(w,{placement:"bottom",width:"224",visible:o.sms_status_visible,"onUpdate:visible":i[10]||(i[10]=e=>o.sms_status_visible=e),trigger:"click","popper-class":"fcrm_contact_popover"},{reference:q(()=>[O(v,{size:"small"},{default:q(()=>[i[23]||(i[23]=z("span",{class:"icon"},[z("svg",{viewBox:"0 0 16 16",fill:"none",xmlns:"http://www.w3.org/2000/svg"},[z("path",{d:"M4.673 12.2001L2 14.3001V3.2001C2 3.04097 2.06321 2.88836 2.17574 2.77583C2.28826 2.66331 2.44087 2.6001 2.6 2.6001H13.4C13.5591 2.6001 13.7117 2.66331 13.8243 2.77583C13.9368 2.88836 14 3.04097 14 3.2001V11.6001C14 11.7592 13.9368 11.9118 13.8243 12.0244C13.7117 12.1369 13.5591 12.2001 13.4 12.2001H4.673ZM4.2578 11.0001H12.8V3.8001H3.2V11.8311L4.2578 11.0001ZM7.4 6.8001H8.6V8.0001H7.4V6.8001ZM5 6.8001H6.2V8.0001H5V6.8001ZM9.8 6.8001H11V8.0001H9.8V6.8001Z",fill:"var(--fc-secondary-text)"})])],-1)),W(" "+F(c.smsStatusMap[a.subscriber.sms_status]||a.subscriber.sms_status)+" ",1),O(y,null,{default:q(()=>[O(g)],void 0,!0),_:1})],void 0,!0),_:1})]),default:q(()=>[z("div",_s,[i[22]||(i[22]=z("svg",{xmlns:"http://www.w3.org/2000/svg",width:"14",height:"14",viewBox:"0 0 14 14",fill:"none"},[z("path",{d:"M4.08838 10.675L1.75 12.5126V2.8001C2.8 3.04097 2.80525 2.88836 2.9038 2.77583C3.00235 2.66331 3.13576 2.6001 3.275 2.6001H11.725C11.8643 2.6001 11.9977 2.65541 12.0962 2.75396C12.1948 2.85251 12.25 2.98592 12.25 3.1251V10.1501C12.25 10.2893 12.1948 10.4227 12.0962 10.5212C11.9977 10.6198 11.8643 10.6751 11.725 10.6751H4.08838ZM3.72558 9.6251H11.2V3.6501H2.8V10.3521L3.72558 9.6251ZM6.475 5.9501H7.525V7.0001H6.475V5.9501ZM4.375 5.9501H5.425V7.0001H4.375V5.9501ZM8.575 5.9501H9.625V7.0001H8.575V5.9501Z",fill:"var(--fc-text-muted)"})],-1)),z("h3",null,F(s.$t("SMS Status")),1)]),z("div",bs,[O(f,{placeholder:s.$t("Select SMS Status"),size:"small",modelValue:a.subscriber.sms_status,"onUpdate:modelValue":i[8]||(i[8]=e=>a.subscriber.sms_status=e),teleported:!1},{default:q(()=>[(D(!0),E(N,null,R(o.sms_statuses,e=>(D(),B(b,{key:e.id,value:e.id,label:e.title},null,8,["value","label"]))),128))],void 0,!0),_:1},8,["placeholder","modelValue"]),s.hasPermission("fcrm_manage_contacts")?(D(),B(h,{key:0,onClick:i[9]||(i[9]=e=>c.saveSmsStatus()),type:"primary",size:"small"},{default:q(()=>[W(F(s.$t("Save")),1)],void 0,!0),_:1})):j("",!0)])],void 0),_:1},8,["visible"])])):j("",!0)])])])])}]]),ProfileListTags:Ve},props:["subscriber"],emits:["fetch","updateSubscriber"],data:()=>({}),methods:{fetch(){this.$emit("fetch")},emitUpdate(e){this.$emit("updateSubscriber",e)}}},[["render",function(e,s,t,i,a,r){const o=U("profile-info"),n=U("profile-list-tags");return D(),E("div",fs,[O(o,{subscriber:t.subscriber,onFetch:r.fetch},null,8,["subscriber","onFetch"]),O(n,{onUpdateSubscriber:r.emitUpdate,subscriber:t.subscriber},null,8,["onUpdateSubscriber","subscriber"])])}]]),ys={class:"fcrm_company_card"},vs={class:"fcrm_company_card_body"},ws={key:0,class:"fcrm_company_card_actions"},Cs={class:"el-popover__reference"},$s={class:"el-popover__reference"},ks={class:"fcrm_company_card_image"},Ss=["src","alt"],xs={class:"fcrm_company_card_content"},Vs={class:"fcrm_company_card_name"},Ls={key:0,class:"fcrm_company_card_primary_badge"},Ps={key:0,class:"fcrm_company_card_domain"},As=["href"],Ms={key:1,class:"fcrm_company_card_email"};const Hs={class:"fcrm_assign_selector"},Is={class:"icon"},Ts={key:0},Zs={key:1},Us={key:2,class:"fcrm_assign_co_list"},Ds={class:"fcrm_assign_card"},Es={class:"fcrm_assign_card--image"},zs=["src"],Fs={key:1,class:"fcrm_assign_card--image-placeholder"},Ns={class:"fcrm_assign_card--content"},Rs={class:"fcrm_assign_card--name"},Bs={class:"fcrm_assign_card--website"},qs={class:"fcrm_assign_card--email-phone"};const Os={class:"fc_sidebar_card fcrm_contact_companies_widget fc_sidebar_card_customer"},js={class:"fc_card_header fcrm_card_header"},Ws={key:0},Gs={class:"fluentcrm-actions"},Ks={class:"fcrm_sidebar_card_content"},Qs={key:0,class:"fcrm_contact_companies fc_companies"},Ys={key:1,class:"fcrm_no_company"},Js={key:0,class:"fcrm_assign_drawer--body"},Xs={class:"fcrm_assign_drawer--body-header"},et={key:0,class:"fcrm_assign_co_existing"},st={key:1,class:"fcrm_assign_co_new"};const tt={class:"fc_contact_side"},it={key:0,class:"fc_card_header"},at=["innerHTML"],rt={key:1,style:{"margin-top":"10px","text-align":"right"},class:"fc_info_widget_nav"},ot={key:0,class:"fc_card_header"},nt=["innerHTML"],ct={key:1,style:{"margin-top":"10px","text-align":"right"},class:"fc_info_widget_nav"},lt={key:0},dt={key:1},ut={class:"fc_sidebar_card text-align-center"},mt={class:"fc_card_header"},ht={style:{padding:"10px 20px 20px",background:"white"},class:"fc_sidebar_card_content"};const pt=le({name:"ContactOverViewSidebar",props:["subscriber","subscriber_id"],emits:["widgetsFetched"],components:{PromoCard:_e,ContactCompanies:le({name:"ContactCompanies",props:["subscriber"],components:{CompanyCard:le({name:"CompanyCard",components:{Icons:de,ArrowDown:h,MoreFilled:m},props:["company","subscriber"],emits:["showDetails","removed"],computed:{domainName(){return pe(this.company.website)}},data:()=>({working:!1}),methods:{markAsPrimary(){this.working=!0,this.$put("subscribers/subscribers-property",{property:"company_id",value:this.company.id,subscribers:[this.subscriber.id]}).then(e=>{this.$notify.success(this.$t("Company marked as primary")),this.subscriber.company_id=this.company.id}).catch(e=>{this.handleError(e)}).finally(()=>{this.working=!1})},removeAssociation(){this.working=!0,this.$post("companies/detach-subscribers",{subscriber_ids:[this.subscriber.id],company_ids:[this.company.id]}).then(e=>{this.$notify.success(this.$t("Company association removed")),this.$emit("removed"),e.last_primary_company_id&&(this.subscriber.company_id=e.last_primary_company_id)}).catch(e=>{this.handleError(e)}).finally(()=>{this.working=!1})}}},[["render",function(e,s,t,i,a,r){const o=b,n=_,c=f,l=U("router-link"),d=p;return Y((D(),E("div",ys,[z("div",vs,[t.subscriber?(D(),E("div",ws,[O(c,{trigger:"click",placement:"bottom-end"},{dropdown:q(()=>[O(n,null,{default:q(()=>[O(o,{class:"fc_dropdown_action",onClick:s[0]||(s[0]=s=>e.$emit("showDetails",t.company))},{default:q(()=>[z("span",Cs,F(e.$t("Edit")),1)],void 0,!0),_:1}),t.subscriber.company_id!=t.company.id?(D(),B(o,{key:0,class:"fc_dropdown_action"},{default:q(()=>[z("span",{class:"el-popover__reference",onClick:s[1]||(s[1]=e=>r.markAsPrimary())},F(e.$t("Mark as Primary")),1)],void 0,!0),_:1})):j("",!0),O(o,{class:"fc_dropdown_action",onClick:s[2]||(s[2]=e=>r.removeAssociation())},{default:q(()=>[z("span",$s,F(e.$t("Remove Association")),1)],void 0,!0),_:1})],void 0,!0),_:1})]),default:q(()=>[s[4]||(s[4]=z("span",{class:"cursor_pointer"},[z("svg",{width:"20",height:"20",viewBox:"0 0 20 20",fill:"none",xmlns:"http://www.w3.org/2000/svg"},[z("path",{d:"M9.99984 5C9.50713 5 9.104 5.40313 9.104 5.89583C9.104 6.38854 9.50713 6.79167 9.99984 6.79167C10.4925 6.79167 10.8957 6.38854 10.8957 5.89583C10.8957 5.40313 10.4925 5 9.99984 5ZM9.99984 13.9583C9.50713 13.9583 9.104 14.3615 9.104 14.8542C9.104 15.3469 9.50713 15.75 9.99984 15.75C10.4925 15.75 10.8957 15.3469 10.8957 14.8542C10.8957 14.3615 10.4925 13.9583 9.99984 13.9583ZM9.99984 9.47917C9.50713 9.47917 9.104 9.88229 9.104 10.375C9.104 10.8677 9.50713 11.2708 9.99984 11.2708C10.4925 11.2708 10.8957 10.8677 10.8957 10.375C10.8957 9.88229 10.4925 9.47917 9.99984 9.47917Z",fill:"currentColor"})])],-1))],void 0),_:1})])):j("",!0),z("div",ks,[t.company.logo?(D(),B(l,{key:0,to:{name:"view_company",params:{company_id:t.company.id}}},{default:q(()=>[z("img",{src:t.company.logo,alt:t.company.name},null,8,Ss)],void 0),_:1},8,["to"])):j("",!0)]),z("div",xs,[z("div",Vs,[z("a",{href:"#",onClick:s[3]||(s[3]=J(s=>e.$emit("showDetails",t.company),["prevent"]))},F(t.company.name),1),t.subscriber&&t.subscriber.company_id==t.company.id?(D(),E("span",Ls,F(e.$t("Primary")),1)):j("",!0)]),r.domainName?(D(),E("div",Ps,[z("a",{target:"_blank",rel:"noopener",href:t.company.website},[W(F(r.domainName)+" ",1),s[5]||(s[5]=z("span",{class:"fc_dash_external"},null,-1))],8,As)])):j("",!0),t.company.email?(D(),E("div",Ms,F(t.company.email),1)):j("",!0)])])])),[[d,a.working]])}]]),CompanyInfoSideContact:ne,CompanyAssignSelector:le({name:"CompanyAssignSelector",props:["subscriber"],components:{Icons:de,Search:g},emits:["companiesAttached"],data:()=>({company_search:"",companies:[],searched:!1,loading:!1,selectedIds:[],attaching:!1}),mounted(){this.fetchCompanies()},watch:{company_search:{handler:function(e,s){clearTimeout(this.searchTimeout),this.searchTimeout=setTimeout(()=>{this.fetchCompanies()},500)},immediate:!1}},methods:{fetchCompanies(){this.loading=!0,this.selectedIds=[],this.$get("companies/search",{limit:10,search:this.company_search,subscriber_id:this.subscriber.id}).then(e=>{this.companies=e.results,this.searched=!0}).catch(e=>{this.handleError(e)}).finally(()=>{this.loading=!1})},attachCompanies(){this.attaching=!0,this.$post("companies/attach-subscribers",{subscriber_ids:[this.subscriber.id],company_ids:this.selectedIds}).then(e=>{this.$notify.success(e.message),e.companies&&(this.each(e.companies,e=>{this.subscriber.companies.push(e)}),this.$emit("companiesAttached",e.companies))}).catch(e=>{this.handleError(e)}).finally(()=>{this.attaching=!1})}},beforeUnmount(){this.searchTimeout&&clearTimeout(this.searchTimeout)}},[["render",function(e,s,a,r,o,n){const c=U("Icons"),l=t,d=y,u=i,m=v,h=w,_=p;return D(),E("div",Hs,[O(d,{modelValue:o.company_search,"onUpdate:modelValue":s[0]||(s[0]=e=>o.company_search=e),class:"fcrm_assign_selector--search",onKeyup:s[1]||(s[1]=X(e=>n.fetchCompanies(),["enter"])),clearable:"",onClear:s[2]||(s[2]=e=>n.fetchCompanies()),placeholder:e.$t("Search Companies")},{prefix:q(()=>[O(l,null,{default:q(()=>[z("span",Is,[O(c,{"icon-name":"search"})])],void 0,!0),_:1})]),_:1},8,["modelValue","placeholder"]),o.loading?(D(),E("div",Ts,[O(u,{animated:!0,rows:3})])):o.searched&&!o.companies.length?(D(),E("div",Zs,[z("p",null,F(e.$t("No companies found based on your search")),1)])):(D(),E("div",Us,[O(h,{modelValue:o.selectedIds,"onUpdate:modelValue":s[3]||(s[3]=e=>o.selectedIds=e)},{default:q(()=>[(D(!0),E(N,null,R(o.companies,e=>(D(),B(m,{key:e.id,value:e.id},{default:q(()=>[z("div",Ds,[z("div",Es,[e.logo?(D(),E("img",{key:0,src:e.logo,alt:""},null,8,zs)):(D(),E("span",Fs))]),z("div",Ns,[z("div",Rs,[W(F(e.name)+" ",1),s[5]||(s[5]=z("span",{class:"fcrm_middot"},null,-1)),z("div",Bs,F(e.website),1)]),z("div",qs,[e.email?(D(),E(N,{key:0},[W(F(e.email),1)],64)):j("",!0),e.phone?(D(),E(N,{key:1},[s[6]||(s[6]=z("span",{class:"fcrm_middot"},null,-1)),W(" "+F(e.phone),1)],64)):j("",!0)])])])],void 0,!0),_:2},1032,["value"]))),128))],void 0),_:1},8,["modelValue"]),o.selectedIds.length?Y((D(),B(l,{key:0,disabled:o.attaching,onClick:s[4]||(s[4]=e=>n.attachCompanies()),size:"small",type:"primary"},{default:q(()=>[W(F(e.$t("Assign selected companies"))+" ("+F(o.selectedIds.length)+") ",1)],void 0),_:1},8,["disabled"])),[[_,o.attaching]]):j("",!0)]))])}]])},data(){return{direction:"rtl",showingCompany:null,drawerVisible:!1,new_company:{owner_id:""+this.subscriber.id},newCompanyDrawer:!1,assignState:"existing"}},mounted(){window.fcAdmin&&window.fcAdmin.is_rtl&&(this.direction="ltr")},methods:{showCompanyDetails(e){this.showingCompany=e,this.drawerVisible=!0},showAddCompanyDrawer(){this.new_company={owner_id:""+this.subscriber.id},this.assignState="existing",this.newCompanyDrawer=!0},newCompanyAssigned(e){this.newCompanyDrawer=!1,this.new_company={owner_id:""+this.subscriber.id},this.subscriber.companies.push(e)},companyRemoved(e){this.subscriber.companies.splice(e,1)}}},[["render",function(e,s,i,a,r,o){const n=t,c=U("company-card"),l=U("company-info-side-contact"),d=C,u=k,m=$,h=U("company-assign-selector");return D(),E("div",Os,[z("div",js,[z("h3",null,[W(F(e.$t("Companies"))+" ",1),i.subscriber.companies?(D(),E("span",Ws,"("+F(i.subscriber.companies.length)+")",1)):j("",!0)]),z("div",Gs,[O(n,{onClick:s[0]||(s[0]=e=>o.showAddCompanyDrawer()),size:"small",link:""},{default:q(()=>[W(F(e.$t("+ Add")),1)],void 0),_:1})])]),z("div",Ks,[i.subscriber.companies&&i.subscriber.companies.length?(D(),E("div",Qs,[(D(!0),E(N,null,R(i.subscriber.companies,(e,s)=>(D(),B(c,{onShowDetails:o.showCompanyDetails,company:e,subscriber:i.subscriber,onRemoved:e=>o.companyRemoved(s),key:e.id},null,8,["onShowDetails","company","subscriber","onRemoved"]))),128))])):(D(),E("div",Ys,[z("p",null,F(e.$t("Contact is not associated with any companies")),1),j("",!0)]))]),O(d,{direction:r.direction,class:"fcrm_company_info_drawer","with-header":!0,size:e.globalDrawerSize,title:r.showingCompany?r.showingCompany.name:"","append-to-body":!0,"modal-class":"fcrm_assign_drawer",modelValue:r.drawerVisible,"onUpdate:modelValue":s[2]||(s[2]=e=>r.drawerVisible=e)},{default:q(()=>[r.drawerVisible?(D(),B(l,{key:0,company:r.showingCompany},null,8,["company"])):j("",!0)],void 0),_:1},8,["direction","size","title","modelValue"]),O(d,{direction:r.direction,class:"fcrm_company_info_drawer",modelValue:r.newCompanyDrawer,"onUpdate:modelValue":s[5]||(s[5]=e=>r.newCompanyDrawer=e),"with-header":!0,size:e.globalDrawerSize,title:"existing"==r.assignState?e.$t("Add existing company"):e.$t("Create company & Assign"),"append-to-body":!0,"modal-class":"fcrm_assign_drawer"},{default:q(()=>[r.newCompanyDrawer?(D(),E("div",Js,[z("div",Xs,[O(m,{modelValue:r.assignState,"onUpdate:modelValue":s[3]||(s[3]=e=>r.assignState=e)},{default:q(()=>[O(u,{value:"existing"},{default:q(()=>[W(F(e.$t("Add Existing")),1)],void 0,!0),_:1}),O(u,{value:"new"},{default:q(()=>[W(F(e.$t("Create New")),1)],void 0,!0),_:1})],void 0,!0),_:1},8,["modelValue"])]),"existing"==r.assignState?(D(),E("div",et,[O(h,{onCompaniesAttached:s[4]||(s[4]=()=>{r.newCompanyDrawer=!1}),subscriber:i.subscriber},null,8,["subscriber"])])):(D(),E("div",st,[O(l,{onCompanyCreated:o.newCompanyAssigned,intended_contact_id:i.subscriber.id,company:r.new_company},null,8,["onCompanyCreated","intended_contact_id","company"])]))])):j("",!0)],void 0),_:1},8,["direction","modelValue","size","title"])])}]])},watch:{subscriber_id(){this.fetchWidgets()}},computed:{hasWidgets(){return this.has_company_module||this.widget_count>0}},data:()=>({loading:!1,top_widgets:[],other_widgets:[],widget_count:0,loading_widget:""}),methods:{fetchWidgets(){this.loading=!0,this.$get(`subscribers/${this.subscriber_id}/info-widgets`).then(e=>{this.top_widgets=e.widgets.top_widgets,this.other_widgets=e.widgets.other_widgets,this.widget_count=e.widgets.widgets_count,this.$emit("widgetsFetched",e.widgets.widgets_count)}).catch(e=>{this.handleError(e)}).finally(()=>{this.loading=!1})},fetchWidget(e,s){this.loading_widget=s,this.$get(`subscribers/${this.subscriber_id}/info-widgets`,{by_widget:s,page:e.current_page}).then(s=>{e.content=s.widget.content}).catch(e=>{this.handleError(e)}).finally(()=>{this.loading_widget=!1})}},mounted(){this.fetchWidgets()}},[["render",function(e,s,t,i,a,r){const o=S,n=U("contact-companies"),c=U("PromoCard"),l=p;return D(),E("div",tt,[(D(!0),E(N,null,R(a.top_widgets,(e,s)=>(D(),E("div",{key:"top_widget_"+s,class:"fc_sidebar_card fc_sidebar_card_customer"},[e.title?(D(),E("div",it,[z("h3",null,F(e.title),1)])):j("",!0),z("div",{class:"fc_sidebar_card_content",innerHTML:e.content},null,8,at),e.has_pagination?(D(),E("div",rt,[Y(O(o,{small:"",onCurrentChange:t=>r.fetchWidget(e,s),layout:"total, prev, next","page-size":e.per_page,"current-page":e.current_page,"onUpdate:currentPage":s=>e.current_page=s,total:e.total},null,8,["onCurrentChange","page-size","current-page","onUpdate:currentPage","total"]),[[l,a.loading_widget===s]])])):j("",!0)]))),128)),e.has_company_module&&t.subscriber?(D(),B(n,{key:0,subscriber:t.subscriber},null,8,["subscriber"])):j("",!0),(D(!0),E(N,null,R(a.other_widgets,(e,s)=>(D(),E("div",{key:"other_widget_"+s,class:"fc_sidebar_card fc_sidebar_card_customer"},[e.title?(D(),E("div",ot,[z("h3",null,F(e.title),1)])):j("",!0),z("div",{class:"fc_sidebar_card_content",innerHTML:e.content},null,8,nt),e.has_pagination?(D(),E("div",ct,[Y(O(o,{small:"",onCurrentChange:t=>r.fetchWidget(e,s),layout:"total, prev, next","page-size":e.per_page,"current-page":e.current_page,"onUpdate:currentPage":s=>e.current_page=s,total:e.total},null,8,["onCurrentChange","page-size","current-page","onUpdate:currentPage","total"]),[[l,a.loading_widget===s]])])):j("",!0)]))),128)),a.loading?j("",!0):(D(),E(N,{key:1},[e.has_campaign_pro?r.hasWidgets?j("",!0):(D(),E("div",dt,[z("div",ut,[z("div",mt,[z("h3",null,F(e.$t("Additional Info")),1)]),z("div",ht,[z("p",null,F(e.$t("No_Additional_Info_Alert")),1)])])])):(D(),E("div",lt,[O(c,{heading:e.$t("Get more related contact info with Pro"),description:e.$t("Fluent_CRM_Pro_Alert")},null,8,["heading","description"])]))],64))])}]]),_t={name:"ProfileNavigation",components:{ArrowLeft:V,ArrowRight:x},props:{subscriberId:{type:[String,Number],required:!0}},data:()=>({loading:!1}),computed:{...ee(ce,["subscribers","pagination"]),hasContacts(){return this.subscribers&&this.subscribers.length>0},currentIndex(){return this.hasContacts?this.subscribers.findIndex(e=>e.id==this.subscriberId):-1},currentPosition(){if(-1===this.currentIndex)return null;return(this.pagination.current_page-1)*this.pagination.per_page+this.currentIndex+1},totalContacts(){return this.pagination.total||0},isFirst(){return 0===this.currentIndex&&1===this.pagination.current_page},isLast(){const e=this.currentIndex===this.subscribers.length-1,s=this.pagination.current_page*this.pagination.per_page>=this.pagination.total;return e&&s},nextContact(){return this.currentIndex0?this.subscribers[this.currentIndex-1]:null}},methods:{async goToNext(){if(this.loading)return;if(this.nextContact)return void this.navigateToContact(this.nextContact.id);this.pagination.current_page*this.pagination.per_page0&&this.navigateToContact(this.subscribers[0].id))},async goToPrevious(){this.loading||(this.previousContact?this.navigateToContact(this.previousContact.id):this.pagination.current_page>1&&(await this.fetchPreviousPage(),this.subscribers.length>0&&this.navigateToContact(this.subscribers[this.subscribers.length-1].id)))},async fetchNextPage(){this.loading=!0;const e=ce();try{e.updatePagination(this.pagination.current_page+1,this.pagination.per_page),await e.fetchContacts(!0,!0)}catch(s){this.$notify.error({title:this.$t("Error"),message:this.$t("Failed to load next page")}),console.error("[ProfileNavigation] Failed to fetch next page:",s)}finally{this.loading=!1}},async fetchPreviousPage(){this.loading=!0;const e=ce();try{e.updatePagination(this.pagination.current_page-1,this.pagination.per_page),await e.fetchContacts(!0,!0)}catch(s){this.$notify.error({title:this.$t("Error"),message:this.$t("Failed to load previous page")}),console.error("[ProfileNavigation] Failed to fetch previous page:",s)}finally{this.loading=!1}},navigateToContact(e){this.$router.push({name:"subscriber",params:{id:e}})}}},bt={key:0,class:"fcrm_profile_navigation"},ft={key:0,class:"fcrm_profile_nav_position"},gt={class:"icon"},yt={class:"icon"};const vt=le(_t,[["render",function(e,s,i,a,r,o){const n=U("ArrowLeft"),c=u,l=t,d=U("ArrowRight"),m=L;return o.hasContacts?(D(),E("div",bt,[o.currentPosition?(D(),E("span",ft,F(o.currentPosition)+" / "+F(o.totalContacts),1)):j("",!0),O(m,null,{default:q(()=>[O(l,{size:"small",disabled:o.isFirst||r.loading,onClick:o.goToPrevious,title:e.$t("Previous Contact"),class:"only-icon-btn small"},{default:q(()=>[z("span",gt,[O(c,null,{default:q(()=>[O(n)],void 0,!0),_:1})])],void 0,!0),_:1},8,["disabled","onClick","title"]),O(l,{size:"small",disabled:o.isLast||r.loading,onClick:o.goToNext,title:e.$t("Next Contact"),class:"only-icon-btn small"},{default:q(()=>[z("span",yt,[O(c,null,{default:q(()=>[O(d)],void 0,!0),_:1})])],void 0,!0),_:1},8,["disabled","onClick","title"])],void 0),_:1})])):j("",!0)}]]),wt={name:"ProfileAiSummary",components:{Badge:be,Icons:de},props:{subscriber:{type:[Object,Boolean],required:!0},subscriberId:{type:[String,Number],required:!0},contactName:{type:String,default:""},disabled:{type:Boolean,default:!1}},data:()=>({popoverVisible:!1,loading:!1,loadedFor:null,error:"",summary:{}}),watch:{subscriberId(){this.reset()}},computed:{summaryTitle(){return this.$t("AI Summary of %s",this.contactName||this.$t("this contact"))},renderedSummary(){return this.$sanitize(this.markdownToHtml(this.summary.content||""))}},methods:{fetchAiSummary(e=!1){if(!this.subscriber||this.loading)return;if(!e&&this.loadedFor==this.subscriberId&&this.summary.content)return;this.loading=!0,this.error="";const s=this.subscriberId;this.$post("ai/contact-summary",{subscriber_id:s,generate:"no",regenerate:e?"yes":"no"}).then(e=>{this.subscriberId==s&&(this.summary=e.summary||{},this.loadedFor=s)}).catch(e=>{this.subscriberId==s&&(this.error=this.getErrorMessage(e))}).finally(()=>{this.subscriberId==s&&(this.loading=!1)})},generateAiSummary(e=!1){if(!this.subscriber||this.loading)return;this.loading=!0,this.error="";const s=this.subscriberId;this.$post("ai/contact-summary",{subscriber_id:s,generate:"yes",regenerate:e?"yes":"no"}).then(e=>{this.subscriberId==s&&(this.summary=e.summary||{},this.loadedFor=s)}).catch(e=>{this.subscriberId==s&&(this.error=this.getErrorMessage(e))}).finally(()=>{this.subscriberId==s&&(this.loading=!1)})},reset(){this.popoverVisible=!1,this.loading=!1,this.loadedFor=null,this.error="",this.summary={}},getErrorMessage(e){return e&&e.message?e.message:e&&e.data&&e.data.message?e.data.message:this.$t("Could not generate AI summary. Please try again.")},markdownToHtml(e){const s=e=>(e=>String(e).replace(/&/g,"&").replace(//g,">").replace(/"/g,""").replace(/'/g,"'"))(e).replace(/\*\*(.+?)\*\*/g,"$1").replace(/\*(.+?)\*/g,"$1").replace(/`(.+?)`/g,"$1").replace(/\[([^\]]+)]\((https?:\/\/[^\s)]+)\)/g,'$1'),t=String(e||"").split("\n");let i="",a=!1;return t.forEach(e=>{const t=e.trim();if(!t)return void(a&&(i+="",a=!1));const r=t.match(/^(#{1,4})\s+(.+)$/);if(r){a&&(i+="",a=!1);const e=Math.min(r[1].length+2,6);return void(i+=`${s(r[2])}`)}const o=t.match(/^[-*]\s+(.+)$/);if(o)return a||(i+="
    ",a=!0),void(i+=`
  • ${s(o[1])}
  • `);a&&(i+="
",a=!1),i+=`

${s(t)}

`}),a&&(i+=""),i}}},Ct={class:"fcrm_ai_button_anim_wrapper"},$t={class:"fcrm_ai_summary_trigger_icon icon","aria-hidden":"true"},kt={class:"fcrm_ai_summary_box"},St={class:"fcrm_ai_summary_banner"},xt={class:"fcrm_ai_summary_banner_icon"},Vt={class:"fcrm_ai_summary_banner_title"},Lt={class:"fcrm_ai_summary_banner_subtitle"},Pt={class:"fcrm_ai_summary_header"},At={class:"icon"},Mt={class:"fcrm_ai_summary_box_body"},Ht={key:0,class:"fcrm_ai_summary_loading"},It=["innerHTML"],Tt={key:3,class:"fcrm_ai_summary_empty"},Zt={class:"fcrm_empty_state"},Ut={class:"fcrm_empty_state_text"},Dt={class:"icon"};const Et={class:"fcrm_profile_container"},zt={class:"fcrm_profile_main_content"},Ft={class:"fcrm_header_breadcrumb_wrapper"},Nt={class:"fcrm_header_breadcrumb_actions"},Rt={class:"icon"},Bt={class:"fcrm_profile_header"},qt={style:{"padding-left":"20px"}},Ot={class:"fcrm_profile_body"},jt={class:"fcrm_profile_body_nav"},Wt=["onClick","innerHTML"],Gt={class:"fcrm_profile_body_select_wrapper"},Kt={class:"fcrm_profile_body_select_label"},Qt=["innerHTML"],Yt={key:0,class:"fcrm_profile_body_inner"},Jt={key:1,class:"fcrm_profile_body_inner"},Xt={class:"fcrm_abs_sidebar"},ei={key:0,class:"fcrm_profile_sidebar_header_title"},si={class:"fcrm_profile_sidebar_inner"},ti={key:0};const ii=le({name:"Profile",components:{Icons:de,ProfileHeader:gs,Confirm:he,OverViewSidebar:pt,ProfileNavigation:vt,ProfileAiSummary:le(wt,[["render",function(e,a,r,o,c,l){const d=U("Icons"),u=t,m=U("Badge"),h=i,p=s,_=U("icons"),b=n;return D(),B(b,{visible:c.popoverVisible,"onUpdate:visible":a[2]||(a[2]=e=>c.popoverVisible=e),placement:"bottom-end",width:"460",trigger:"click","popper-class":"fcrm_ai_summary_popover",onShow:a[3]||(a[3]=e=>l.fetchAiSummary())},{reference:q(()=>[z("div",Ct,[a[4]||(a[4]=z("div",{class:"fcrm_ai_button_anim"},[z("div",{class:"fcrm_ai_button_anim_inner"})],-1)),O(u,{size:"small",class:"fcrm_ai_summary_trigger fcrm_ai_button",loading:c.loading,disabled:r.disabled||!r.subscriber},{default:q(()=>[z("span",$t,[O(d,{"icon-name":"ai"})]),W(" "+F(e.$t("AI Summary")),1)],void 0,!0),_:1},8,["loading","disabled"])])]),default:q(()=>[z("div",kt,[z("div",St,[z("div",xt,[O(d,{"icon-name":"ai"})]),z("div",null,[z("div",Vt,F(l.summaryTitle),1),z("div",Lt,[z("span",null,F(r.subscriber.email),1),r.subscriber.status?(D(),B(m,{key:0,type:r.subscriber.status},null,8,["type"])):j("",!0)])])]),z("div",Pt,[z("div",null,[z("h3",null,F(c.summary.content?e.$t("Saved Summary"):e.$t("No Saved Summary")),1),z("p",null,F(c.summary.generated_at?e.$t("Generated")+": "+e.smartDate(c.summary.generated_at):e.$t("Generate a decision-ready markdown summary for this contact.")),1)]),c.summary.content?(D(),B(u,{key:0,loading:c.loading,onClick:a[0]||(a[0]=e=>l.generateAiSummary(!0)),size:"small"},{default:q(()=>[z("span",At,[O(d,{"icon-name":"reload"})]),W(" "+F(e.$t("Regenerate")),1)],void 0,!0),_:1},8,["loading"])):j("",!0)]),z("div",Mt,[c.loading&&!c.summary.content?(D(),E("div",Ht,[O(h,{animated:"",rows:6})])):c.error?(D(),B(p,{key:1,type:"error",closable:!1,title:c.error,"show-icon":""},null,8,["title"])):c.summary.content?(D(),E("div",{key:2,class:"fcrm_ai_summary_markdown",innerHTML:l.renderedSummary},null,8,It)):(D(),E("div",Tt,[z("div",Zt,[O(_,{"icon-name":"common-empty-state"}),z("div",Ut,[z("span",null,F(e.$t("No AI summary generated yet")),1)]),O(u,{type:"primary",size:"small",loading:c.loading,onClick:a[1]||(a[1]=e=>l.generateAiSummary())},{default:q(()=>[z("span",Dt,[O(d,{"icon-name":"reload"})]),W(" "+F(e.$t("Generate AI Summary")),1)],void 0,!0),_:1},8,["loading"])])]))])])],void 0),_:1},8,["visible"])}]]),More:A,ArrowRight:x,ArrowRightBold:P,ArrowDown:h,ArrowLeft:V},props:["id"],data:()=>({ArrowRight:ae(x),ArrowRightBold:ae(P),ArrowDown:ae(h),subscriber:!1,loading:!1,custom_fields:[],subscriber_meta:{},show_profile:!0,doing_action:!1,sidebarOpen:"yes",widgetsCount:0,hasSidebar:!0,selectedIndex:null,isFullProfileLoaded:!1,profile_parts:{}}),watch:{id(){this.fetch()},$route(){this.syncSelectedWithRoute()}},computed:{lists(){return this.subscriber.lists.map(e=>e.title)},name(){return this.subscriber.first_name||this.subscriber.last_name?`${this.subscriber.first_name||""} ${this.subscriber.last_name||""}`:this.subscriber.email},showSidebar(){return this.hasSidebar&&"yes"==this.sidebarOpen},widgetTickerCount(){return this.has_company_module&&this.subscriber&&this.subscriber.companies&&this.subscriber.companies.length?this.widgetsCount+1:this.widgetsCount},profilePartsList(){return Array.isArray(this.profile_parts)?this.profile_parts:Object.values(this.profile_parts||{})}},methods:{setup(e){const s=null==e?void 0:e.custom_values;s&&(Object.keys(s).forEach(e=>{const t=s[e];if(t){const i=this.custom_fields.find(s=>s.slug===e);!i||"select-multi"!==i.type&&"checkbox"!==i.type||"string"!=typeof t||(s[e]=t.split(", "))}}),e.custom_values=s),this.subscriber=e,this.show_profile||(this.show_profile=!0);const t=this.applyFilters("fluentcrm_profile_sections",window.fcAdmin.profile_sections,e);Object.keys(t).length!==Object.keys(this.profile_parts).length&&(this.profile_parts=t),this.filterProfileParts(this.profile_parts),this.syncSelectedWithRoute(),this.changeTitle(e.full_name+" - Contact"),this.doAction("fluent_crm_subscriber_loaded",this)},filterProfileParts(){let e=this.profile_parts;const s={};Object.keys(e).forEach(t=>{const i=e[t].name;this.$router.hasRoute(i)&&(s[t]=e[t])}),this.profile_parts=s},fetch(){this.loading=!0;const e=ce(),s=this.id,t=e.subscribers.find(e=>e.id==this.id);t?(this.subscriber=t,this.show_profile=!0,this.loading=!1,this.isFullProfileLoaded=!1,e.fetchProfile(this.id).then(e=>{this.id===s&&(this.custom_fields=e.custom_fields,this.setup(e.subscriber),this.isFullProfileLoaded=!0)}).catch(e=>{this.id===s&&this.handleError(e)})):(this.loading=!0,this.show_profile=!1,this.isFullProfileLoaded=!1,e.fetchProfile(this.id).then(e=>{this.id===s&&(this.custom_fields=e.custom_fields,this.setup(e.subscriber),this.isFullProfileLoaded=!0)}).catch(e=>{this.id===s&&this.handleError(e)}).finally(()=>{this.id===s&&(this.loading=!1)}))},sidebarLoaded(e){this.widgetsCount=e},maybeCustomHandler(e){"fluentcrm_profile_extended"==e.name&&(this.show_profile=!1,setTimeout(()=>{this.show_profile=!0},100))},deleteContact(){this.loading=!0,this.$del(`subscribers/${this.id}`).then(e=>{this.$notify.success(e.message),this.$router.push({name:"subscribers"})}).catch(e=>{this.handleError(e)}).finally(()=>{this.loading=!1})},toggleSidebar(){this.sidebarOpen="yes"==this.sidebarOpen?"no":"yes",this.storage.set("fc_profile_sidebar",this.sidebarOpen)},syncSelectedWithRoute(){var e,s;const t=null==(s=null==(e=this.$route)?void 0:e.query)?void 0:s.handler,i=this.profilePartsList.findIndex(e=>e.name===this.$route.name&&(!e.query||void 0===e.query.handler||e.query.handler==t));this.selectedIndex=i>-1?i:null},onSelectChange(e){if(null==e)return;const s=this.profilePartsList[e];if(s){if("function"==typeof this.maybeCustomHandler)try{this.maybeCustomHandler(s)}catch(t){console.error(t)}this.$router.push({name:s.name,params:{id:this.id},query:s.query||{},hash:"#fluentcrm_sub_info_body"}).catch(()=>{})}}},mounted(){this.changeTitle(this.$t("Profile")),this.fetch(),this.sidebarOpen="no"==this.storage.get("fc_profile_sidebar","yes")?"no":"yes",this.syncSelectedWithRoute()},beforeUnmount(){this.doAction("fluent_crm_leaving_profile",this.subscriber)}},[["render",function(e,s,a,r,o,n){const c=M,u=H,m=U("profile-navigation"),h=U("profile-ai-summary"),p=U("Icons"),g=t,y=U("confirm"),v=b,w=_,C=f,$=Z,k=T,S=i,x=I,V=U("profile-header"),L=U("router-link"),P=d,A=l,K=U("router-view"),Q=U("over-view-sidebar");return U("ArrowRight"),U("ArrowLeft"),D(),E("div",{class:G(["fcrm_profile_wrapper",{fc_side_closed:o.hasSidebar&&!n.showSidebar,fc_side_opened:n.showSidebar}])},[z("div",Et,[z("div",zt,[z("div",Ft,[O(u,{class:"fcrm_header_breadcrumb","separator-icon":o.ArrowRightBold},{default:q(()=>[O(c,{to:{name:"subscribers"}},{default:q(()=>[W(F(e.$t("All Contacts")),1)],void 0,!0),_:1}),O(c,null,{default:q(()=>[W(F(n.name),1)],void 0,!0),_:1})],void 0),_:1},8,["separator-icon"]),z("div",Nt,[O(m,{"subscriber-id":a.id},null,8,["subscriber-id"]),O(h,{subscriber:o.subscriber,"subscriber-id":a.id,"contact-name":n.name,disabled:o.loading},null,8,["subscriber","subscriber-id","contact-name","disabled"]),O(C,{size:"small",trigger:"click"},{dropdown:q(()=>[O(w,null,{default:q(()=>[O(v,{class:"fc_dropdown_action"},{default:q(()=>[O(y,{placement:"top-start",message:e.$t("Contact_Delete_Alert"),onYes:s[0]||(s[0]=e=>n.deleteContact())},{reference:q(()=>[z("span",null,F(e.$t("Delete Contact")),1)]),_:1},8,["message"])],void 0,!0),_:1})],void 0,!0),_:1})]),default:q(()=>[O(g,{size:"small",class:"el-dropdown-link"},{default:q(()=>[W(F(e.$t("Actions"))+" ",1),z("span",Rt,[O(p,{"icon-name":"downIcon"})])],void 0,!0),_:1})],void 0),_:1})])]),z("div",Bt,[o.loading?(D(),B(S,{key:0,class:"",animated:""},{template:q(()=>[O(x,{gutter:30},{default:q(()=>[O(k,{span:3},{default:q(()=>[O($,{variant:"circle",style:{width:"145px",height:"145px"}})],void 0,!0),_:1}),O(k,{span:20},{default:q(()=>[z("div",qt,[O(S)])],void 0,!0),_:1})],void 0,!0),_:1})]),_:1})):o.subscriber?(D(),B(V,{key:1,onUpdateSubscriber:n.setup,onFetch:n.fetch,subscriber:o.subscriber},null,8,["onUpdateSubscriber","onFetch","subscriber"])):j("",!0)]),z("div",Ot,[z("ul",jt,[(D(!0),E(N,null,R(o.profile_parts,(s,t)=>(D(),B(L,{onClick:e=>n.maybeCustomHandler(s),key:s.name+t,custom:"",to:{name:s.name,hash:"#fluentcrm_sub_info_body",params:{id:a.id},query:s.query}},{default:q(({navigate:t})=>[z("li",{class:G({item_active:e.$route.name==s.name&&(!s.query||e.$route.query.handler==s.query.handler)}),onClick:t,innerHTML:s.title},null,10,Wt)]),_:2},1032,["onClick","to"]))),128))]),z("div",Gt,[z("div",Kt,F(e.$t("Select Section")),1),O(A,{modelValue:o.selectedIndex,"onUpdate:modelValue":s[1]||(s[1]=e=>o.selectedIndex=e),placeholder:e.$t("Select section"),onChange:n.onSelectChange,class:"fcrm_profile_body_select",clearable:""},{default:q(()=>[(D(!0),E(N,null,R(n.profilePartsList,(e,s)=>(D(),B(P,{key:e.name+s,label:e.title,value:s},{default:q(()=>[z("span",{innerHTML:e.title},null,8,Qt)],void 0,!0),_:2},1032,["label","value"]))),128))],void 0),_:1},8,["modelValue","placeholder","onChange"])]),o.subscriber&&o.show_profile?(D(),E("div",Yt,[O(K,null,{default:q(({Component:s})=>[O(se,{name:"fc-fade",mode:"out-in"},{default:q(()=>[(D(),B(te(s),{onUpdateSubscriber:n.setup,key:e.$route.fullPath,custom_fields:o.custom_fields,subscriber:o.subscriber,subscriber_id:a.id,is_full_profile_loaded:o.isFullProfileLoaded},null,40,["onUpdateSubscriber","custom_fields","subscriber","subscriber_id","is_full_profile_loaded"]))],void 0,!0),_:2},1024)]),_:1})])):(D(),E("div",Jt,[O(S,{class:"fc_skeleton_loader",animated:""},{template:q(()=>[O(x,{gutter:30},{default:q(()=>[O(k,{span:12},{default:q(()=>[O(S,{rows:10})],void 0,!0),_:1}),O(k,{span:12},{default:q(()=>[O(S,{rows:3})],void 0,!0),_:1})],void 0,!0),_:1})]),_:1})]))])]),z("div",{class:G(["fcrm_profile_sidebar",{is_active:n.showSidebar}])},[z("div",Xt,[z("div",{onClick:s[2]||(s[2]=e=>n.toggleSidebar()),class:"fcrm_profile_sidebar_header"},[n.showSidebar?(D(),E("span",ei,F(e.$t("Additional Information"))+" ("+F(n.widgetTickerCount)+") ",1)):j("",!0),O(g,{type:"info",size:"small",class:"fc_sidebar_open_btn"},{default:q(()=>[...s[5]||(s[5]=[z("span",{class:"icon"},[z("svg",{width:"20",height:"20",viewBox:"0 0 20 20",fill:"none",xmlns:"http://www.w3.org/2000/svg"},[z("path",{d:"M10.119 9.30539L6.88505 6.07145L7.86714 5.08936L12.7776 9.99983L7.86714 14.9103L6.88505 13.9282L10.1189 10.6943L3.74965 10.6943L3.74963 9.30546L10.119 9.30539ZM14.1664 14.8609V5.13867H15.5553V14.8609H14.1664Z",fill:"var(--fc-secondary-text)"})])],-1)])],void 0),_:1})])]),Y(z("div",si,[Y(O(Q,{onWidgetsFetched:n.sidebarLoaded,subscriber:o.subscriber,subscriber_id:a.id},null,8,["onWidgetsFetched","subscriber","subscriber_id"]),[[ie,o.show_profile]]),o.loading?(D(),E("div",ti,[O(S,{class:"fc_skeleton_loader",style:{"margin-bottom":"20px"},animated:""}),O(S,{class:"fc_skeleton_loader",animated:""})])):j("",!0)],512),[[ie,n.showSidebar]])],2)]),j("",!0)],2)}]]);export{ii as default}; diff --git a/wp-content/plugins/fluent-crm/assets/admin/Modules/Reports/Chart/world.js b/wp-content/plugins/fluent-crm/assets/admin/Modules/Reports/Chart/world.js new file mode 100644 index 0000000..014f912 --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/admin/Modules/Reports/Chart/world.js @@ -0,0 +1 @@ +const e="FeatureCollection",o=[{type:"Feature",id:"AFG",properties:{name:"Afghanistan",iso_a2:"AF"},geometry:{type:"Polygon",coordinates:[[[61.210817,35.650072],[62.230651,35.270664],[62.984662,35.404041],[63.193538,35.857166],[63.982896,36.007957],[64.546479,36.312073],[64.746105,37.111818],[65.588948,37.305217],[65.745631,37.661164],[66.217385,37.39379],[66.518607,37.362784],[67.075782,37.356144],[67.83,37.144994],[68.135562,37.023115],[68.859446,37.344336],[69.196273,37.151144],[69.518785,37.608997],[70.116578,37.588223],[70.270574,37.735165],[70.376304,38.138396],[70.806821,38.486282],[71.348131,38.258905],[71.239404,37.953265],[71.541918,37.905774],[71.448693,37.065645],[71.844638,36.738171],[72.193041,36.948288],[72.63689,37.047558],[73.260056,37.495257],[73.948696,37.421566],[74.980002,37.41999],[75.158028,37.133031],[74.575893,37.020841],[74.067552,36.836176],[72.920025,36.720007],[71.846292,36.509942],[71.262348,36.074388],[71.498768,35.650563],[71.613076,35.153203],[71.115019,34.733126],[71.156773,34.348911],[70.881803,33.988856],[69.930543,34.02012],[70.323594,33.358533],[69.687147,33.105499],[69.262522,32.501944],[69.317764,31.901412],[68.926677,31.620189],[68.556932,31.71331],[67.792689,31.58293],[67.683394,31.303154],[66.938891,31.304911],[66.381458,30.738899],[66.346473,29.887943],[65.046862,29.472181],[64.350419,29.560031],[64.148002,29.340819],[63.550261,29.468331],[62.549857,29.318572],[60.874248,29.829239],[61.781222,30.73585],[61.699314,31.379506],[60.941945,31.548075],[60.863655,32.18292],[60.536078,32.981269],[60.9637,33.528832],[60.52843,33.676446],[60.803193,34.404102],[61.210817,35.650072]]]}},{type:"Feature",id:"AGO",properties:{name:"Angola",iso_a2:"AO"},geometry:{type:"MultiPolygon",coordinates:[[[[16.326528,-5.87747],[16.57318,-6.622645],[16.860191,-7.222298],[17.089996,-7.545689],[17.47297,-8.068551],[18.134222,-7.987678],[18.464176,-7.847014],[19.016752,-7.988246],[19.166613,-7.738184],[19.417502,-7.155429],[20.037723,-7.116361],[20.091622,-6.94309],[20.601823,-6.939318],[20.514748,-7.299606],[21.728111,-7.290872],[21.746456,-7.920085],[21.949131,-8.305901],[21.801801,-8.908707],[21.875182,-9.523708],[22.208753,-9.894796],[22.155268,-11.084801],[22.402798,-10.993075],[22.837345,-11.017622],[23.456791,-10.867863],[23.912215,-10.926826],[24.017894,-11.237298],[23.904154,-11.722282],[24.079905,-12.191297],[23.930922,-12.565848],[24.016137,-12.911046],[21.933886,-12.898437],[21.887843,-16.08031],[22.562478,-16.898451],[23.215048,-17.523116],[21.377176,-17.930636],[18.956187,-17.789095],[18.263309,-17.309951],[14.209707,-17.353101],[14.058501,-17.423381],[13.462362,-16.971212],[12.814081,-16.941343],[12.215461,-17.111668],[11.734199,-17.301889],[11.640096,-16.673142],[11.778537,-15.793816],[12.123581,-14.878316],[12.175619,-14.449144],[12.500095,-13.5477],[12.738479,-13.137906],[13.312914,-12.48363],[13.633721,-12.038645],[13.738728,-11.297863],[13.686379,-10.731076],[13.387328,-10.373578],[13.120988,-9.766897],[12.87537,-9.166934],[12.929061,-8.959091],[13.236433,-8.562629],[12.93304,-7.596539],[12.728298,-6.927122],[12.227347,-6.294448],[12.322432,-6.100092],[12.735171,-5.965682],[13.024869,-5.984389],[13.375597,-5.864241],[16.326528,-5.87747]]],[[[12.436688,-5.684304],[12.182337,-5.789931],[11.914963,-5.037987],[12.318608,-4.60623],[12.62076,-4.438023],[12.995517,-4.781103],[12.631612,-4.991271],[12.468004,-5.248362],[12.436688,-5.684304]]]]}},{type:"Feature",id:"ALB",properties:{name:"Albania",iso_a2:"AL"},geometry:{type:"Polygon",coordinates:[[[20.590247,41.855404],[20.463175,41.515089],[20.605182,41.086226],[21.02004,40.842727],[20.99999,40.580004],[20.674997,40.435],[20.615,40.110007],[20.150016,39.624998],[19.98,39.694993],[19.960002,39.915006],[19.406082,40.250773],[19.319059,40.72723],[19.40355,41.409566],[19.540027,41.719986],[19.371769,41.877548],[19.304486,42.195745],[19.738051,42.688247],[19.801613,42.500093],[20.0707,42.58863],[20.283755,42.32026],[20.52295,42.21787],[20.590247,41.855404]]]}},{type:"Feature",id:"ARE",properties:{name:"United Arab Emirates",iso_a2:"AE"},geometry:{type:"Polygon",coordinates:[[[51.579519,24.245497],[51.757441,24.294073],[51.794389,24.019826],[52.577081,24.177439],[53.404007,24.151317],[54.008001,24.121758],[54.693024,24.797892],[55.439025,25.439145],[56.070821,26.055464],[56.261042,25.714606],[56.396847,24.924732],[55.886233,24.920831],[55.804119,24.269604],[55.981214,24.130543],[55.528632,23.933604],[55.525841,23.524869],[55.234489,23.110993],[55.208341,22.70833],[55.006803,22.496948],[52.000733,23.001154],[51.617708,24.014219],[51.579519,24.245497]]]}},{type:"Feature",id:"ARG",properties:{name:"Argentina",iso_a2:"AR"},geometry:{type:"MultiPolygon",coordinates:[[[[-65.5,-55.2],[-66.45,-55.25],[-66.95992,-54.89681],[-67.56244,-54.87001],[-68.63335,-54.8695],[-68.63401,-52.63637],[-68.25,-53.1],[-67.75,-53.85],[-66.45,-54.45],[-65.05,-54.7],[-65.5,-55.2]]],[[[-64.964892,-22.075862],[-64.377021,-22.798091],[-63.986838,-21.993644],[-62.846468,-22.034985],[-62.685057,-22.249029],[-60.846565,-23.880713],[-60.028966,-24.032796],[-58.807128,-24.771459],[-57.777217,-25.16234],[-57.63366,-25.603657],[-58.618174,-27.123719],[-57.60976,-27.395899],[-56.486702,-27.548499],[-55.695846,-27.387837],[-54.788795,-26.621786],[-54.625291,-25.739255],[-54.13005,-25.547639],[-53.628349,-26.124865],[-53.648735,-26.923473],[-54.490725,-27.474757],[-55.162286,-27.881915],[-56.2909,-28.852761],[-57.625133,-30.216295],[-57.874937,-31.016556],[-58.14244,-32.044504],[-58.132648,-33.040567],[-58.349611,-33.263189],[-58.427074,-33.909454],[-58.495442,-34.43149],[-57.22583,-35.288027],[-57.362359,-35.97739],[-56.737487,-36.413126],[-56.788285,-36.901572],[-57.749157,-38.183871],[-59.231857,-38.72022],[-61.237445,-38.928425],[-62.335957,-38.827707],[-62.125763,-39.424105],[-62.330531,-40.172586],[-62.145994,-40.676897],[-62.745803,-41.028761],[-63.770495,-41.166789],[-64.73209,-40.802677],[-65.118035,-41.064315],[-64.978561,-42.058001],[-64.303408,-42.359016],[-63.755948,-42.043687],[-63.458059,-42.563138],[-64.378804,-42.873558],[-65.181804,-43.495381],[-65.328823,-44.501366],[-65.565269,-45.036786],[-66.509966,-45.039628],[-67.293794,-45.551896],[-67.580546,-46.301773],[-66.597066,-47.033925],[-65.641027,-47.236135],[-65.985088,-48.133289],[-67.166179,-48.697337],[-67.816088,-49.869669],[-68.728745,-50.264218],[-69.138539,-50.73251],[-68.815561,-51.771104],[-68.149995,-52.349983],[-68.571545,-52.299444],[-69.498362,-52.142761],[-71.914804,-52.009022],[-72.329404,-51.425956],[-72.309974,-50.67701],[-72.975747,-50.74145],[-73.328051,-50.378785],[-73.415436,-49.318436],[-72.648247,-48.878618],[-72.331161,-48.244238],[-72.447355,-47.738533],[-71.917258,-46.884838],[-71.552009,-45.560733],[-71.659316,-44.973689],[-71.222779,-44.784243],[-71.329801,-44.407522],[-71.793623,-44.207172],[-71.464056,-43.787611],[-71.915424,-43.408565],[-72.148898,-42.254888],[-71.746804,-42.051386],[-71.915734,-40.832339],[-71.680761,-39.808164],[-71.413517,-38.916022],[-70.814664,-38.552995],[-71.118625,-37.576827],[-71.121881,-36.658124],[-70.364769,-36.005089],[-70.388049,-35.169688],[-69.817309,-34.193571],[-69.814777,-33.273886],[-70.074399,-33.09121],[-70.535069,-31.36501],[-69.919008,-30.336339],[-70.01355,-29.367923],[-69.65613,-28.459141],[-69.001235,-27.521214],[-68.295542,-26.89934],[-68.5948,-26.506909],[-68.386001,-26.185016],[-68.417653,-24.518555],[-67.328443,-24.025303],[-66.985234,-22.986349],[-67.106674,-22.735925],[-66.273339,-21.83231],[-64.964892,-22.075862]]]]}},{type:"Feature",id:"ARM",properties:{name:"Armenia",iso_a2:"AM"},geometry:{type:"Polygon",coordinates:[[[43.582746,41.092143],[44.97248,41.248129],[45.179496,40.985354],[45.560351,40.81229],[45.359175,40.561504],[45.891907,40.218476],[45.610012,39.899994],[46.034534,39.628021],[46.483499,39.464155],[46.50572,38.770605],[46.143623,38.741201],[45.735379,39.319719],[45.739978,39.473999],[45.298145,39.471751],[45.001987,39.740004],[44.79399,39.713003],[44.400009,40.005],[43.656436,40.253564],[43.752658,40.740201],[43.582746,41.092143]]]}},{type:"Feature",id:"ATA",properties:{name:"Antarctica",iso_a2:"AQ"},geometry:{type:"MultiPolygon",coordinates:[[[[-59.572095,-80.040179],[-59.865849,-80.549657],[-60.159656,-81.000327],[-62.255393,-80.863178],[-64.488125,-80.921934],[-65.741666,-80.588827],[-65.741666,-80.549657],[-66.290031,-80.255773],[-64.037688,-80.294944],[-61.883246,-80.39287],[-61.138976,-79.981371],[-60.610119,-79.628679],[-59.572095,-80.040179]]],[[[-159.208184,-79.497059],[-161.127601,-79.634209],[-162.439847,-79.281465],[-163.027408,-78.928774],[-163.066604,-78.869966],[-163.712896,-78.595667],[-163.105801,-78.223338],[-161.245113,-78.380176],[-160.246208,-78.693645],[-159.482405,-79.046338],[-159.208184,-79.497059]]],[[[-45.154758,-78.04707],[-43.920828,-78.478103],[-43.48995,-79.08556],[-43.372438,-79.516645],[-43.333267,-80.026123],[-44.880537,-80.339644],[-46.506174,-80.594357],[-48.386421,-80.829485],[-50.482107,-81.025442],[-52.851988,-80.966685],[-54.164259,-80.633528],[-53.987991,-80.222028],[-51.853134,-79.94773],[-50.991326,-79.614623],[-50.364595,-79.183487],[-49.914131,-78.811209],[-49.306959,-78.458569],[-48.660616,-78.047018],[-48.660616,-78.047019],[-48.151396,-78.04707],[-46.662857,-77.831476],[-45.154758,-78.04707]]],[[[-121.211511,-73.50099],[-119.918851,-73.657725],[-118.724143,-73.481353],[-119.292119,-73.834097],[-120.232217,-74.08881],[-121.62283,-74.010468],[-122.621735,-73.657778],[-122.621735,-73.657777],[-122.406245,-73.324619],[-121.211511,-73.50099]]],[[[-125.559566,-73.481353],[-124.031882,-73.873268],[-124.619469,-73.834097],[-125.912181,-73.736118],[-127.28313,-73.461769],[-127.28313,-73.461768],[-126.558472,-73.246226],[-125.559566,-73.481353]]],[[[-98.98155,-71.933334],[-97.884743,-72.070535],[-96.787937,-71.952971],[-96.20035,-72.521205],[-96.983765,-72.442864],[-98.198083,-72.482035],[-99.432013,-72.442864],[-100.783455,-72.50162],[-101.801868,-72.305663],[-102.330725,-71.894164],[-101.703967,-71.717792],[-100.430919,-71.854993],[-98.98155,-71.933334]]],[[[-68.451346,-70.955823],[-68.333834,-71.406493],[-68.510128,-71.798407],[-68.784297,-72.170736],[-69.959471,-72.307885],[-71.075889,-72.503842],[-72.388134,-72.484257],[-71.8985,-72.092343],[-73.073622,-72.229492],[-74.19004,-72.366693],[-74.953895,-72.072757],[-75.012625,-71.661258],[-73.915819,-71.269345],[-73.915819,-71.269344],[-73.230331,-71.15178],[-72.074717,-71.190951],[-71.780962,-70.681473],[-71.72218,-70.309196],[-71.741791,-69.505782],[-71.173815,-69.035475],[-70.253252,-68.87874],[-69.724447,-69.251017],[-69.489422,-69.623346],[-69.058518,-70.074016],[-68.725541,-70.505153],[-68.451346,-70.955823]]],[[[-58.614143,-64.152467],[-59.045073,-64.36801],[-59.789342,-64.211223],[-60.611928,-64.309202],[-61.297416,-64.54433],[-62.0221,-64.799094],[-62.51176,-65.09303],[-62.648858,-65.484942],[-62.590128,-65.857219],[-62.120079,-66.190326],[-62.805567,-66.425505],[-63.74569,-66.503847],[-64.294106,-66.837004],[-64.881693,-67.150474],[-65.508425,-67.58161],[-65.665082,-67.953887],[-65.312545,-68.365335],[-64.783715,-68.678908],[-63.961103,-68.913984],[-63.1973,-69.227556],[-62.785955,-69.619419],[-62.570516,-69.991747],[-62.276736,-70.383661],[-61.806661,-70.716768],[-61.512906,-71.089045],[-61.375809,-72.010074],[-61.081977,-72.382351],[-61.003661,-72.774265],[-60.690269,-73.166179],[-60.827367,-73.695242],[-61.375809,-74.106742],[-61.96337,-74.439848],[-63.295201,-74.576997],[-63.74569,-74.92974],[-64.352836,-75.262847],[-65.860987,-75.635124],[-67.192818,-75.79191],[-68.446282,-76.007452],[-69.797724,-76.222995],[-70.600724,-76.634494],[-72.206776,-76.673665],[-73.969536,-76.634494],[-75.555977,-76.712887],[-77.24037,-76.712887],[-76.926979,-77.104802],[-75.399294,-77.28107],[-74.282876,-77.55542],[-73.656119,-77.908112],[-74.772536,-78.221633],[-76.4961,-78.123654],[-77.925858,-78.378419],[-77.984666,-78.789918],[-78.023785,-79.181833],[-76.848637,-79.514939],[-76.633224,-79.887216],[-75.360097,-80.259545],[-73.244852,-80.416331],[-71.442946,-80.69063],[-70.013163,-81.004151],[-68.191646,-81.317672],[-65.704279,-81.474458],[-63.25603,-81.748757],[-61.552026,-82.042692],[-59.691416,-82.37585],[-58.712121,-82.846106],[-58.222487,-83.218434],[-57.008117,-82.865691],[-55.362894,-82.571755],[-53.619771,-82.258235],[-51.543644,-82.003521],[-49.76135,-81.729171],[-47.273931,-81.709586],[-44.825708,-81.846735],[-42.808363,-82.081915],[-42.16202,-81.65083],[-40.771433,-81.356894],[-38.244818,-81.337309],[-36.26667,-81.121715],[-34.386397,-80.906172],[-32.310296,-80.769023],[-30.097098,-80.592651],[-28.549802,-80.337938],[-29.254901,-79.985195],[-29.685805,-79.632503],[-29.685805,-79.260226],[-31.624808,-79.299397],[-33.681324,-79.456132],[-35.639912,-79.456132],[-35.914107,-79.083855],[-35.77701,-78.339248],[-35.326546,-78.123654],[-33.896763,-77.888526],[-32.212369,-77.65345],[-30.998051,-77.359515],[-29.783732,-77.065579],[-28.882779,-76.673665],[-27.511752,-76.497345],[-26.160336,-76.360144],[-25.474822,-76.281803],[-23.927552,-76.24258],[-22.458598,-76.105431],[-21.224694,-75.909474],[-20.010375,-75.674346],[-18.913543,-75.439218],[-17.522982,-75.125698],[-16.641589,-74.79254],[-15.701491,-74.498604],[-15.40771,-74.106742],[-16.46532,-73.871614],[-16.112784,-73.460114],[-15.446855,-73.146542],[-14.408805,-72.950585],[-13.311973,-72.715457],[-12.293508,-72.401936],[-11.510067,-72.010074],[-11.020433,-71.539767],[-10.295774,-71.265416],[-9.101015,-71.324224],[-8.611381,-71.65733],[-7.416622,-71.696501],[-7.377451,-71.324224],[-6.868232,-70.93231],[-5.790985,-71.030289],[-5.536375,-71.402617],[-4.341667,-71.461373],[-3.048981,-71.285053],[-1.795492,-71.167438],[-.659489,-71.226246],[-.228637,-71.637745],[.868195,-71.304639],[1.886686,-71.128267],[3.022638,-70.991118],[4.139055,-70.853917],[5.157546,-70.618789],[6.273912,-70.462055],[7.13572,-70.246512],[7.742866,-69.893769],[8.48711,-70.148534],[9.525135,-70.011333],[10.249845,-70.48164],[10.817821,-70.834332],[11.953824,-70.638375],[12.404287,-70.246512],[13.422778,-69.972162],[14.734998,-70.030918],[15.126757,-70.403247],[15.949342,-70.030918],[17.026589,-69.913354],[18.201711,-69.874183],[19.259373,-69.893769],[20.375739,-70.011333],[21.452985,-70.07014],[21.923034,-70.403247],[22.569403,-70.697182],[23.666184,-70.520811],[24.841357,-70.48164],[25.977309,-70.48164],[27.093726,-70.462055],[28.09258,-70.324854],[29.150242,-70.20729],[30.031583,-69.93294],[30.971733,-69.75662],[31.990172,-69.658641],[32.754053,-69.384291],[33.302443,-68.835642],[33.870419,-68.502588],[34.908495,-68.659271],[35.300202,-69.012014],[36.16201,-69.247142],[37.200035,-69.168748],[37.905108,-69.52144],[38.649404,-69.776205],[39.667894,-69.541077],[40.020431,-69.109941],[40.921358,-68.933621],[41.959434,-68.600514],[42.938702,-68.463313],[44.113876,-68.267408],[44.897291,-68.051866],[45.719928,-67.816738],[46.503343,-67.601196],[47.44344,-67.718759],[48.344419,-67.366068],[48.990736,-67.091718],[49.930885,-67.111303],[50.753471,-66.876175],[50.949325,-66.523484],[51.791547,-66.249133],[52.614133,-66.053176],[53.613038,-65.89639],[54.53355,-65.818049],[55.414943,-65.876805],[56.355041,-65.974783],[57.158093,-66.249133],[57.255968,-66.680218],[58.137361,-67.013324],[58.744508,-67.287675],[59.939318,-67.405239],[60.605221,-67.679589],[61.427806,-67.953887],[62.387489,-68.012695],[63.19049,-67.816738],[64.052349,-67.405239],[64.992447,-67.620729],[65.971715,-67.738345],[66.911864,-67.855909],[67.891133,-67.934302],[68.890038,-67.934302],[69.712624,-68.972791],[69.673453,-69.227556],[69.555941,-69.678226],[68.596258,-69.93294],[67.81274,-70.305268],[67.949889,-70.697182],[69.066307,-70.677545],[68.929157,-71.069459],[68.419989,-71.441788],[67.949889,-71.853287],[68.71377,-72.166808],[69.869307,-72.264787],[71.024895,-72.088415],[71.573285,-71.696501],[71.906288,-71.324224],[72.454627,-71.010703],[73.08141,-70.716768],[73.33602,-70.364024],[73.864877,-69.874183],[74.491557,-69.776205],[75.62756,-69.737034],[76.626465,-69.619419],[77.644904,-69.462684],[78.134539,-69.07077],[78.428371,-68.698441],[79.113859,-68.326216],[80.093127,-68.071503],[80.93535,-67.875546],[81.483792,-67.542388],[82.051767,-67.366068],[82.776426,-67.209282],[83.775331,-67.30726],[84.676206,-67.209282],[85.655527,-67.091718],[86.752359,-67.150474],[87.477017,-66.876175],[87.986289,-66.209911],[88.358411,-66.484261],[88.828408,-66.954568],[89.67063,-67.150474],[90.630365,-67.228867],[91.5901,-67.111303],[92.608539,-67.189696],[93.548637,-67.209282],[94.17542,-67.111303],[95.017591,-67.170111],[95.781472,-67.385653],[96.682399,-67.248504],[97.759646,-67.248504],[98.68021,-67.111303],[99.718182,-67.248504],[100.384188,-66.915346],[100.893356,-66.58224],[101.578896,-66.30789],[102.832411,-65.563284],[103.478676,-65.700485],[104.242557,-65.974783],[104.90846,-66.327527],[106.181561,-66.934931],[107.160881,-66.954568],[108.081393,-66.954568],[109.15864,-66.837004],[110.235835,-66.699804],[111.058472,-66.425505],[111.74396,-66.13157],[112.860378,-66.092347],[113.604673,-65.876805],[114.388088,-66.072762],[114.897308,-66.386283],[115.602381,-66.699804],[116.699161,-66.660633],[117.384701,-66.915346],[118.57946,-67.170111],[119.832924,-67.268089],[120.871,-67.189696],[121.654415,-66.876175],[122.320369,-66.562654],[123.221296,-66.484261],[124.122274,-66.621462],[125.160247,-66.719389],[126.100396,-66.562654],[127.001427,-66.562654],[127.882768,-66.660633],[128.80328,-66.758611],[129.704259,-66.58224],[130.781454,-66.425505],[131.799945,-66.386283],[132.935896,-66.386283],[133.85646,-66.288304],[134.757387,-66.209963],[135.031582,-65.72007],[135.070753,-65.308571],[135.697485,-65.582869],[135.873805,-66.033591],[136.206705,-66.44509],[136.618049,-66.778197],[137.460271,-66.954568],[138.596223,-66.895761],[139.908442,-66.876175],[140.809421,-66.817367],[142.121692,-66.817367],[143.061842,-66.797782],[144.374061,-66.837004],[145.490427,-66.915346],[146.195552,-67.228867],[145.999699,-67.601196],[146.646067,-67.895131],[147.723263,-68.130259],[148.839629,-68.385024],[150.132314,-68.561292],[151.483705,-68.71813],[152.502247,-68.874813],[153.638199,-68.894502],[154.284567,-68.561292],[155.165857,-68.835642],[155.92979,-69.149215],[156.811132,-69.384291],[158.025528,-69.482269],[159.181013,-69.599833],[159.670699,-69.991747],[160.80665,-70.226875],[161.570479,-70.579618],[162.686897,-70.736353],[163.842434,-70.716768],[164.919681,-70.775524],[166.11444,-70.755938],[167.309095,-70.834332],[168.425616,-70.971481],[169.463589,-71.20666],[170.501665,-71.402617],[171.20679,-71.696501],[171.089227,-72.088415],[170.560422,-72.441159],[170.109958,-72.891829],[169.75737,-73.24452],[169.287321,-73.65602],[167.975101,-73.812806],[167.387489,-74.165498],[166.094803,-74.38104],[165.644391,-74.772954],[164.958851,-75.145283],[164.234193,-75.458804],[163.822797,-75.870303],[163.568239,-76.24258],[163.47026,-76.693302],[163.489897,-77.065579],[164.057873,-77.457442],[164.273363,-77.82977],[164.743464,-78.182514],[166.604126,-78.319611],[166.995781,-78.750748],[165.193876,-78.907483],[163.666217,-79.123025],[161.766385,-79.162248],[160.924162,-79.730482],[160.747894,-80.200737],[160.316964,-80.573066],[159.788211,-80.945395],[161.120016,-81.278501],[161.629287,-81.690001],[162.490992,-82.062278],[163.705336,-82.395435],[165.095949,-82.708956],[166.604126,-83.022477],[168.895665,-83.335998],[169.404782,-83.825891],[172.283934,-84.041433],[172.477049,-84.117914],[173.224083,-84.41371],[175.985672,-84.158997],[178.277212,-84.472518],[180,-84.71338],[-179.942499,-84.721443],[-179.058677,-84.139412],[-177.256772,-84.452933],[-177.140807,-84.417941],[-176.084673,-84.099259],[-175.947235,-84.110449],[-175.829882,-84.117914],[-174.382503,-84.534323],[-173.116559,-84.117914],[-172.889106,-84.061019],[-169.951223,-83.884647],[-168.999989,-84.117914],[-168.530199,-84.23739],[-167.022099,-84.570497],[-164.182144,-84.82521],[-161.929775,-85.138731],[-158.07138,-85.37391],[-155.192253,-85.09956],[-150.942099,-85.295517],[-148.533073,-85.609038],[-145.888918,-85.315102],[-143.107718,-85.040752],[-142.892279,-84.570497],[-146.829068,-84.531274],[-150.060732,-84.296146],[-150.902928,-83.904232],[-153.586201,-83.68869],[-153.409907,-83.23802],[-153.037759,-82.82652],[-152.665637,-82.454192],[-152.861517,-82.042692],[-154.526299,-81.768394],[-155.29018,-81.41565],[-156.83745,-81.102129],[-154.408787,-81.160937],[-152.097662,-81.004151],[-150.648293,-81.337309],[-148.865998,-81.043373],[-147.22075,-80.671045],[-146.417749,-80.337938],[-146.770286,-79.926439],[-148.062947,-79.652089],[-149.531901,-79.358205],[-151.588416,-79.299397],[-153.390322,-79.162248],[-155.329376,-79.064269],[-155.975668,-78.69194],[-157.268302,-78.378419],[-158.051768,-78.025676],[-158.365134,-76.889207],[-157.875474,-76.987238],[-156.974573,-77.300759],[-155.329376,-77.202728],[-153.742832,-77.065579],[-152.920247,-77.496664],[-151.33378,-77.398737],[-150.00195,-77.183143],[-148.748486,-76.908845],[-147.612483,-76.575738],[-146.104409,-76.47776],[-146.143528,-76.105431],[-146.496091,-75.733154],[-146.20231,-75.380411],[-144.909624,-75.204039],[-144.322037,-75.537197],[-142.794353,-75.34124],[-141.638764,-75.086475],[-140.209007,-75.06689],[-138.85759,-74.968911],[-137.5062,-74.733783],[-136.428901,-74.518241],[-135.214583,-74.302699],[-134.431194,-74.361455],[-133.745654,-74.439848],[-132.257168,-74.302699],[-130.925311,-74.479019],[-129.554284,-74.459433],[-128.242038,-74.322284],[-126.890622,-74.420263],[-125.402082,-74.518241],[-124.011496,-74.479019],[-122.562152,-74.498604],[-121.073613,-74.518241],[-119.70256,-74.479019],[-118.684145,-74.185083],[-117.469801,-74.028348],[-116.216312,-74.243891],[-115.021552,-74.067519],[-113.944331,-73.714828],[-113.297988,-74.028348],[-112.945452,-74.38104],[-112.299083,-74.714198],[-111.261059,-74.420263],[-110.066325,-74.79254],[-108.714909,-74.910103],[-107.559346,-75.184454],[-106.149148,-75.125698],[-104.876074,-74.949326],[-103.367949,-74.988497],[-102.016507,-75.125698],[-100.645531,-75.302018],[-100.1167,-74.870933],[-100.763043,-74.537826],[-101.252703,-74.185083],[-102.545337,-74.106742],[-103.113313,-73.734413],[-103.328752,-73.362084],[-103.681289,-72.61753],[-102.917485,-72.754679],[-101.60524,-72.813436],[-100.312528,-72.754679],[-99.13738,-72.911414],[-98.118889,-73.20535],[-97.688037,-73.558041],[-96.336595,-73.616849],[-95.043961,-73.4797],[-93.672907,-73.283743],[-92.439003,-73.166179],[-91.420564,-73.401307],[-90.088733,-73.322914],[-89.226951,-72.558722],[-88.423951,-73.009393],[-87.268337,-73.185764],[-86.014822,-73.087786],[-85.192236,-73.4797],[-83.879991,-73.518871],[-82.665646,-73.636434],[-81.470913,-73.851977],[-80.687447,-73.4797],[-80.295791,-73.126956],[-79.296886,-73.518871],[-77.925858,-73.420892],[-76.907367,-73.636434],[-76.221879,-73.969541],[-74.890049,-73.871614],[-73.852024,-73.65602],[-72.833533,-73.401307],[-71.619215,-73.264157],[-70.209042,-73.146542],[-68.935916,-73.009393],[-67.956622,-72.79385],[-67.369061,-72.480329],[-67.134036,-72.049244],[-67.251548,-71.637745],[-67.56494,-71.245831],[-67.917477,-70.853917],[-68.230843,-70.462055],[-68.485452,-70.109311],[-68.544209,-69.717397],[-68.446282,-69.325535],[-67.976233,-68.953206],[-67.5845,-68.541707],[-67.427843,-68.149844],[-67.62367,-67.718759],[-67.741183,-67.326845],[-67.251548,-66.876175],[-66.703184,-66.58224],[-66.056815,-66.209963],[-65.371327,-65.89639],[-64.568276,-65.602506],[-64.176542,-65.171423],[-63.628152,-64.897073],[-63.001394,-64.642308],[-62.041686,-64.583552],[-61.414928,-64.270031],[-60.709855,-64.074074],[-59.887269,-63.95651],[-59.162585,-63.701745],[-58.594557,-63.388224],[-57.811143,-63.27066],[-57.223582,-63.525425],[-57.59573,-63.858532],[-58.614143,-64.152467]]]]}},{type:"Feature",id:"ATF",properties:{name:"French Southern and Antarctic Lands",iso_a2:"TF"},geometry:{type:"Polygon",coordinates:[[[68.935,-48.625],[69.58,-48.94],[70.525,-49.065],[70.56,-49.255],[70.28,-49.71],[68.745,-49.775],[68.72,-49.2425],[68.8675,-48.83],[68.935,-48.625]]]}},{type:"Feature",id:"AUS",properties:{name:"Australia",iso_a2:"AU"},geometry:{type:"MultiPolygon",coordinates:[[[[145.397978,-40.792549],[146.364121,-41.137695],[146.908584,-41.000546],[147.689259,-40.808258],[148.289068,-40.875438],[148.359865,-42.062445],[148.017301,-42.407024],[147.914052,-43.211522],[147.564564,-42.937689],[146.870343,-43.634597],[146.663327,-43.580854],[146.048378,-43.549745],[145.43193,-42.693776],[145.29509,-42.03361],[144.718071,-41.162552],[144.743755,-40.703975],[145.397978,-40.792549]]],[[[143.561811,-13.763656],[143.922099,-14.548311],[144.563714,-14.171176],[144.894908,-14.594458],[145.374724,-14.984976],[145.271991,-15.428205],[145.48526,-16.285672],[145.637033,-16.784918],[145.888904,-16.906926],[146.160309,-17.761655],[146.063674,-18.280073],[146.387478,-18.958274],[147.471082,-19.480723],[148.177602,-19.955939],[148.848414,-20.39121],[148.717465,-20.633469],[149.28942,-21.260511],[149.678337,-22.342512],[150.077382,-22.122784],[150.482939,-22.556142],[150.727265,-22.402405],[150.899554,-23.462237],[151.609175,-24.076256],[152.07354,-24.457887],[152.855197,-25.267501],[153.136162,-26.071173],[153.161949,-26.641319],[153.092909,-27.2603],[153.569469,-28.110067],[153.512108,-28.995077],[153.339095,-29.458202],[153.069241,-30.35024],[153.089602,-30.923642],[152.891578,-31.640446],[152.450002,-32.550003],[151.709117,-33.041342],[151.343972,-33.816023],[151.010555,-34.31036],[150.714139,-35.17346],[150.32822,-35.671879],[150.075212,-36.420206],[149.946124,-37.109052],[149.997284,-37.425261],[149.423882,-37.772681],[148.304622,-37.809061],[147.381733,-38.219217],[146.922123,-38.606532],[146.317922,-39.035757],[145.489652,-38.593768],[144.876976,-38.417448],[145.032212,-37.896188],[144.485682,-38.085324],[143.609974,-38.809465],[142.745427,-38.538268],[142.17833,-38.380034],[141.606582,-38.308514],[140.638579,-38.019333],[139.992158,-37.402936],[139.806588,-36.643603],[139.574148,-36.138362],[139.082808,-35.732754],[138.120748,-35.612296],[138.449462,-35.127261],[138.207564,-34.384723],[137.71917,-35.076825],[136.829406,-35.260535],[137.352371,-34.707339],[137.503886,-34.130268],[137.890116,-33.640479],[137.810328,-32.900007],[136.996837,-33.752771],[136.372069,-34.094766],[135.989043,-34.890118],[135.208213,-34.47867],[135.239218,-33.947953],[134.613417,-33.222778],[134.085904,-32.848072],[134.273903,-32.617234],[132.990777,-32.011224],[132.288081,-31.982647],[131.326331,-31.495803],[129.535794,-31.590423],[128.240938,-31.948489],[127.102867,-32.282267],[126.148714,-32.215966],[125.088623,-32.728751],[124.221648,-32.959487],[124.028947,-33.483847],[123.659667,-33.890179],[122.811036,-33.914467],[122.183064,-34.003402],[121.299191,-33.821036],[120.580268,-33.930177],[119.893695,-33.976065],[119.298899,-34.509366],[119.007341,-34.464149],[118.505718,-34.746819],[118.024972,-35.064733],[117.295507,-35.025459],[116.625109,-35.025097],[115.564347,-34.386428],[115.026809,-34.196517],[115.048616,-33.623425],[115.545123,-33.487258],[115.714674,-33.259572],[115.679379,-32.900369],[115.801645,-32.205062],[115.689611,-31.612437],[115.160909,-30.601594],[114.997043,-30.030725],[115.040038,-29.461095],[114.641974,-28.810231],[114.616498,-28.516399],[114.173579,-28.118077],[114.048884,-27.334765],[113.477498,-26.543134],[113.338953,-26.116545],[113.778358,-26.549025],[113.440962,-25.621278],[113.936901,-25.911235],[114.232852,-26.298446],[114.216161,-25.786281],[113.721255,-24.998939],[113.625344,-24.683971],[113.393523,-24.384764],[113.502044,-23.80635],[113.706993,-23.560215],[113.843418,-23.059987],[113.736552,-22.475475],[114.149756,-21.755881],[114.225307,-22.517488],[114.647762,-21.82952],[115.460167,-21.495173],[115.947373,-21.068688],[116.711615,-20.701682],[117.166316,-20.623599],[117.441545,-20.746899],[118.229559,-20.374208],[118.836085,-20.263311],[118.987807,-20.044203],[119.252494,-19.952942],[119.805225,-19.976506],[120.85622,-19.683708],[121.399856,-19.239756],[121.655138,-18.705318],[122.241665,-18.197649],[122.286624,-17.798603],[122.312772,-17.254967],[123.012574,-16.4052],[123.433789,-17.268558],[123.859345,-17.069035],[123.503242,-16.596506],[123.817073,-16.111316],[124.258287,-16.327944],[124.379726,-15.56706],[124.926153,-15.0751],[125.167275,-14.680396],[125.670087,-14.51007],[125.685796,-14.230656],[126.125149,-14.347341],[126.142823,-14.095987],[126.582589,-13.952791],[127.065867,-13.817968],[127.804633,-14.276906],[128.35969,-14.86917],[128.985543,-14.875991],[129.621473,-14.969784],[129.4096,-14.42067],[129.888641,-13.618703],[130.339466,-13.357376],[130.183506,-13.10752],[130.617795,-12.536392],[131.223495,-12.183649],[131.735091,-12.302453],[132.575298,-12.114041],[132.557212,-11.603012],[131.824698,-11.273782],[132.357224,-11.128519],[133.019561,-11.376411],[133.550846,-11.786515],[134.393068,-12.042365],[134.678632,-11.941183],[135.298491,-12.248606],[135.882693,-11.962267],[136.258381,-12.049342],[136.492475,-11.857209],[136.95162,-12.351959],[136.685125,-12.887223],[136.305407,-13.29123],[135.961758,-13.324509],[136.077617,-13.724278],[135.783836,-14.223989],[135.428664,-14.715432],[135.500184,-14.997741],[136.295175,-15.550265],[137.06536,-15.870762],[137.580471,-16.215082],[138.303217,-16.807604],[138.585164,-16.806622],[139.108543,-17.062679],[139.260575,-17.371601],[140.215245,-17.710805],[140.875463,-17.369069],[141.07111,-16.832047],[141.274095,-16.38887],[141.398222,-15.840532],[141.702183,-15.044921],[141.56338,-14.561333],[141.63552,-14.270395],[141.519869,-13.698078],[141.65092,-12.944688],[141.842691,-12.741548],[141.68699,-12.407614],[141.928629,-11.877466],[142.118488,-11.328042],[142.143706,-11.042737],[142.51526,-10.668186],[142.79731,-11.157355],[142.866763,-11.784707],[143.115947,-11.90563],[143.158632,-12.325656],[143.522124,-12.834358],[143.597158,-13.400422],[143.561811,-13.763656]]]]}},{type:"Feature",id:"AUT",properties:{name:"Austria",iso_a2:"AT"},geometry:{type:"Polygon",coordinates:[[[16.979667,48.123497],[16.903754,47.714866],[16.340584,47.712902],[16.534268,47.496171],[16.202298,46.852386],[16.011664,46.683611],[15.137092,46.658703],[14.632472,46.431817],[13.806475,46.509306],[12.376485,46.767559],[12.153088,47.115393],[11.164828,46.941579],[11.048556,46.751359],[10.442701,46.893546],[9.932448,46.920728],[9.47997,47.10281],[9.632932,47.347601],[9.594226,47.525058],[9.896068,47.580197],[10.402084,47.302488],[10.544504,47.566399],[11.426414,47.523766],[12.141357,47.703083],[12.62076,47.672388],[12.932627,47.467646],[13.025851,47.637584],[12.884103,48.289146],[13.243357,48.416115],[13.595946,48.877172],[14.338898,48.555305],[14.901447,48.964402],[15.253416,49.039074],[16.029647,48.733899],[16.499283,48.785808],[16.960288,48.596982],[16.879983,48.470013],[16.979667,48.123497]]]}},{type:"Feature",id:"AZE",properties:{name:"Azerbaijan",iso_a2:"AZ"},geometry:{type:"MultiPolygon",coordinates:[[[[45.001987,39.740004],[45.298145,39.471751],[45.739978,39.473999],[45.735379,39.319719],[46.143623,38.741201],[45.457722,38.874139],[44.952688,39.335765],[44.79399,39.713003],[45.001987,39.740004]]],[[[47.373315,41.219732],[47.815666,41.151416],[47.987283,41.405819],[48.584353,41.80887],[49.110264,41.282287],[49.618915,40.572924],[50.08483,40.526157],[50.392821,40.256561],[49.569202,40.176101],[49.395259,39.399482],[49.223228,39.049219],[48.856532,38.815486],[48.883249,38.320245],[48.634375,38.270378],[48.010744,38.794015],[48.355529,39.288765],[48.060095,39.582235],[47.685079,39.508364],[46.50572,38.770605],[46.483499,39.464155],[46.034534,39.628021],[45.610012,39.899994],[45.891907,40.218476],[45.359175,40.561504],[45.560351,40.81229],[45.179496,40.985354],[44.97248,41.248129],[45.217426,41.411452],[45.962601,41.123873],[46.501637,41.064445],[46.637908,41.181673],[46.145432,41.722802],[46.404951,41.860675],[46.686071,41.827137],[47.373315,41.219732]]]]}},{type:"Feature",id:"BDI",properties:{name:"Burundi",iso_a2:"BI"},geometry:{type:"Polygon",coordinates:[[[29.339998,-4.499983],[29.276384,-3.293907],[29.024926,-2.839258],[29.632176,-2.917858],[29.938359,-2.348487],[30.469696,-2.413858],[30.527677,-2.807632],[30.743013,-3.034285],[30.752263,-3.35933],[30.50556,-3.568567],[30.116333,-4.090138],[29.753512,-4.452389],[29.339998,-4.499983]]]}},{type:"Feature",id:"BEL",properties:{name:"Belgium",iso_a2:"BE"},geometry:{type:"Polygon",coordinates:[[[3.314971,51.345781],[4.047071,51.267259],[4.973991,51.475024],[5.606976,51.037298],[6.156658,50.803721],[6.043073,50.128052],[5.782417,50.090328],[5.674052,49.529484],[4.799222,49.985373],[4.286023,49.907497],[3.588184,50.378992],[3.123252,50.780363],[2.658422,50.796848],[2.513573,51.148506],[3.314971,51.345781]]]}},{type:"Feature",id:"BEN",properties:{name:"Benin",iso_a2:"BJ"},geometry:{type:"Polygon",coordinates:[[[2.691702,6.258817],[1.865241,6.142158],[1.618951,6.832038],[1.664478,9.12859],[1.463043,9.334624],[1.425061,9.825395],[1.077795,10.175607],[.772336,10.470808],[.899563,10.997339],[1.24347,11.110511],[1.447178,11.547719],[1.935986,11.64115],[2.154474,11.94015],[2.490164,12.233052],[2.848643,12.235636],[3.61118,11.660167],[3.572216,11.327939],[3.797112,10.734746],[3.60007,10.332186],[3.705438,10.06321],[3.220352,9.444153],[2.912308,9.137608],[2.723793,8.506845],[2.749063,7.870734],[2.691702,6.258817]]]}},{type:"Feature",id:"BFA",properties:{name:"Burkina Faso",iso_a2:"BF"},geometry:{type:"Polygon",coordinates:[[[-2.827496,9.642461],[-3.511899,9.900326],[-3.980449,9.862344],[-4.330247,9.610835],[-4.779884,9.821985],[-4.954653,10.152714],[-5.404342,10.370737],[-5.470565,10.95127],[-5.197843,11.375146],[-5.220942,11.713859],[-4.427166,12.542646],[-4.280405,13.228444],[-4.006391,13.472485],[-3.522803,13.337662],[-3.103707,13.541267],[-2.967694,13.79815],[-2.191825,14.246418],[-2.001035,14.559008],[-1.066363,14.973815],[-.515854,15.116158],[-.266257,14.924309],[.374892,14.928908],[.295646,14.444235],[.429928,13.988733],[.993046,13.33575],[1.024103,12.851826],[2.177108,12.625018],[2.154474,11.94015],[1.935986,11.64115],[1.447178,11.547719],[1.24347,11.110511],[.899563,10.997339],[.023803,11.018682],[-.438702,11.098341],[-.761576,10.93693],[-1.203358,11.009819],[-2.940409,10.96269],[-2.963896,10.395335],[-2.827496,9.642461]]]}},{type:"Feature",id:"BGD",properties:{name:"Bangladesh",iso_a2:"BD"},geometry:{type:"Polygon",coordinates:[[[92.672721,22.041239],[92.652257,21.324048],[92.303234,21.475485],[92.368554,20.670883],[92.082886,21.192195],[92.025215,21.70157],[91.834891,22.182936],[91.417087,22.765019],[90.496006,22.805017],[90.586957,22.392794],[90.272971,21.836368],[89.847467,22.039146],[89.70205,21.857116],[89.418863,21.966179],[89.031961,22.055708],[88.876312,22.879146],[88.52977,23.631142],[88.69994,24.233715],[88.084422,24.501657],[88.306373,24.866079],[88.931554,25.238692],[88.209789,25.768066],[88.563049,26.446526],[89.355094,26.014407],[89.832481,25.965082],[89.920693,25.26975],[90.872211,25.132601],[91.799596,25.147432],[92.376202,24.976693],[91.915093,24.130414],[91.46773,24.072639],[91.158963,23.503527],[91.706475,22.985264],[91.869928,23.624346],[92.146035,23.627499],[92.672721,22.041239]]]}},{type:"Feature",id:"BGR",properties:{name:"Bulgaria",iso_a2:"BG"},geometry:{type:"Polygon",coordinates:[[[22.65715,44.234923],[22.944832,43.823785],[23.332302,43.897011],[24.100679,43.741051],[25.569272,43.688445],[26.065159,43.943494],[27.2424,44.175986],[27.970107,43.812468],[28.558081,43.707462],[28.039095,43.293172],[27.673898,42.577892],[27.99672,42.007359],[27.135739,42.141485],[26.117042,41.826905],[26.106138,41.328899],[25.197201,41.234486],[24.492645,41.583896],[23.692074,41.309081],[22.952377,41.337994],[22.881374,41.999297],[22.380526,42.32026],[22.545012,42.461362],[22.436595,42.580321],[22.604801,42.898519],[22.986019,43.211161],[22.500157,43.642814],[22.410446,44.008063],[22.65715,44.234923]]]}},{type:"Feature",id:"BHS",properties:{name:"The Bahamas",iso_a2:"BS"},geometry:{type:"MultiPolygon",coordinates:[[[[-77.53466,23.75975],[-77.78,23.71],[-78.03405,24.28615],[-78.40848,24.57564],[-78.19087,25.2103],[-77.89,25.17],[-77.54,24.34],[-77.53466,23.75975]]],[[[-77.82,26.58],[-78.91,26.42],[-78.98,26.79],[-78.51,26.87],[-77.85,26.84],[-77.82,26.58]]],[[[-77,26.59],[-77.17255,25.87918],[-77.35641,26.00735],[-77.34,26.53],[-77.78802,26.92516],[-77.79,27.04],[-77,26.59]]]]}},{type:"Feature",id:"BIH",properties:{name:"Bosnia and Herzegovina",iso_a2:"BA"},geometry:{type:"Polygon",coordinates:[[[19.005486,44.860234],[19.36803,44.863],[19.11761,44.42307],[19.59976,44.03847],[19.454,43.5681],[19.21852,43.52384],[19.03165,43.43253],[18.70648,43.20011],[18.56,42.65],[17.674922,43.028563],[17.297373,43.446341],[16.916156,43.667722],[16.456443,44.04124],[16.23966,44.351143],[15.750026,44.818712],[15.959367,45.233777],[16.318157,45.004127],[16.534939,45.211608],[17.002146,45.233777],[17.861783,45.06774],[18.553214,45.08159],[19.005486,44.860234]]]}},{type:"Feature",id:"BLR",properties:{name:"Belarus",iso_a2:"BY"},geometry:{type:"Polygon",coordinates:[[[23.484128,53.912498],[24.450684,53.905702],[25.536354,54.282423],[25.768433,54.846963],[26.588279,55.167176],[26.494331,55.615107],[27.10246,55.783314],[28.176709,56.16913],[29.229513,55.918344],[29.371572,55.670091],[29.896294,55.789463],[30.873909,55.550976],[30.971836,55.081548],[30.757534,54.811771],[31.384472,54.157056],[31.791424,53.974639],[31.731273,53.794029],[32.405599,53.618045],[32.693643,53.351421],[32.304519,53.132726],[31.497644,53.167427],[31.305201,53.073996],[31.540018,52.742052],[31.785998,52.101678],[30.927549,52.042353],[30.619454,51.822806],[30.555117,51.319503],[30.157364,51.416138],[29.254938,51.368234],[28.992835,51.602044],[28.617613,51.427714],[28.241615,51.572227],[27.454066,51.592303],[26.337959,51.832289],[25.327788,51.910656],[24.553106,51.888461],[24.005078,51.617444],[23.527071,51.578454],[23.508002,52.023647],[23.199494,52.486977],[23.799199,52.691099],[23.804935,53.089731],[23.527536,53.470122],[23.484128,53.912498]]]}},{type:"Feature",id:"BLZ",properties:{name:"Belize",iso_a2:"BZ"},geometry:{type:"Polygon",coordinates:[[[-89.14308,17.808319],[-89.150909,17.955468],[-89.029857,18.001511],[-88.848344,17.883198],[-88.490123,18.486831],[-88.300031,18.499982],[-88.296336,18.353273],[-88.106813,18.348674],[-88.123479,18.076675],[-88.285355,17.644143],[-88.197867,17.489475],[-88.302641,17.131694],[-88.239518,17.036066],[-88.355428,16.530774],[-88.551825,16.265467],[-88.732434,16.233635],[-88.930613,15.887273],[-89.229122,15.886938],[-89.150806,17.015577],[-89.14308,17.808319]]]}},{type:"Feature",id:"BMU",properties:{name:"Bermuda",iso_a2:"BM"},geometry:{type:"Polygon",coordinates:[[[-64.7799734332998,32.3072000581802],[-64.7873319183061,32.3039237143428],[-64.7946942710173,32.3032682700388],[-64.8094297981283,32.3098175728414],[-64.8167896352437,32.3058845718466],[-64.8101968029642,32.3022833180511],[-64.7962291465484,32.2934409732427],[-64.7815086336978,32.2868973114514],[-64.7997025513437,32.2796896417328],[-64.8066707691087,32.2747767569465],[-64.8225587873683,32.2669111289395],[-64.8287548840306,32.2669075473817],[-64.8306732143498,32.2583944840235],[-64.8399924854972,32.254782282336],[-64.8566090462354,32.2547740387514],[-64.8682296789446,32.2616393614322],[-64.8628241459563,32.2724481933959],[-64.8748651338951,32.2757120264753],[-64.8717752856644,32.2819371582026],[-64.8671422127295,32.2930760547989],[-64.8559068764437,32.2960321186471],[-64.8597429072279,32.3015842021933],[-64.8439233486717,32.3140553852543],[-64.8350242329311,32.3242161760006],[-64.8338690593672,32.3294587561557],[-64.8520298651164,32.3110911879954],[-64.8635922932573,32.3048469433363],[-64.8686668994079,32.30910745083],[-64.8721354593415,32.3041908606301],[-64.8779667328485,32.3038632800462],[-64.8780046844321,32.2907757831692],[-64.8849776658292,32.2819261366004],[-64.8783230004629,32.2613001418681],[-64.863194968877,32.2465799485801],[-64.8519819555722,32.2485519134663],[-64.842311980074,32.2492123317296],[-64.8388242605209,32.2475773472534],[-64.8334002575532,32.2462714714698],[-64.8256389530584,32.2472637398594],[-64.8205697556026,32.2531698880328],[-64.8105087275579,32.2561208974156],[-64.7900177727338,32.2659446936992],[-64.7745415970416,32.2718413023427],[-64.7644742436426,32.2855931353214],[-64.7551803442276,32.2908326702531],[-64.7423982971436,32.2996734994024],[-64.7206991797682,32.3137542201258],[-64.7117851247134,32.3176823360806],[-64.6962778813133,32.3275029115532],[-64.6768921127452,32.3324095397555],[-64.6567136927777,32.3451776458469],[-64.6532168823499,32.3494356627941],[-64.6605720384429,32.3589423487763],[-64.65125819471,32.3615600906466],[-64.6462011670816,32.36975169749],[-64.6613227512832,32.3763135008721],[-64.6690666074397,32.388444543924],[-64.6834270548595,32.3854968316788],[-64.6954617672714,32.3763221285869],[-64.70438689565,32.3704254760469],[-64.7117569982798,32.368132600249],[-64.7061764744404,32.3600110593559],[-64.700531552697,32.3590601356818],[-64.6940348033967,32.3640708659835],[-64.6895164826082,32.3633598579866],[-64.6864150099255,32.3547797587266],[-64.6824635995504,32.3540628176846],[-64.6835876652835,32.3626447677968],[-64.6801998697415,32.3631199096979],[-64.6672170444687,32.3597751617473],[-64.6598811264978,32.3497625771755],[-64.6737331235384,32.3390281851635],[-64.6887090648183,32.3342439408053],[-64.706732854446,32.3429010723036],[-64.7149301576112,32.3552188753513],[-64.7185967666669,32.3552239212394],[-64.7214189847314,32.3518830231342],[-64.7270616067222,32.3466461715475],[-64.734962460882,32.3442819830499],[-64.7383521549094,32.3407216514918],[-64.7411729976333,32.3311790864627],[-64.7423019216485,32.323311561213],[-64.7462482354281,32.318538611581],[-64.7566773739613,32.3130509130175],[-64.768738200563,32.3088369816572],[-64.7799734332998,32.3072000581802]]]}},{type:"Feature",id:"BOL",properties:{name:"Bolivia",iso_a2:"BO"},geometry:{type:"Polygon",coordinates:[[[-62.846468,-22.034985],[-63.986838,-21.993644],[-64.377021,-22.798091],[-64.964892,-22.075862],[-66.273339,-21.83231],[-67.106674,-22.735925],[-67.82818,-22.872919],[-68.219913,-21.494347],[-68.757167,-20.372658],[-68.442225,-19.405068],[-68.966818,-18.981683],[-69.100247,-18.260125],[-69.590424,-17.580012],[-68.959635,-16.500698],[-69.389764,-15.660129],[-69.160347,-15.323974],[-69.339535,-14.953195],[-68.948887,-14.453639],[-68.929224,-13.602684],[-68.88008,-12.899729],[-68.66508,-12.5613],[-69.529678,-10.951734],[-68.786158,-11.03638],[-68.271254,-11.014521],[-68.048192,-10.712059],[-67.173801,-10.306812],[-66.646908,-9.931331],[-65.338435,-9.761988],[-65.444837,-10.511451],[-65.321899,-10.895872],[-65.402281,-11.56627],[-64.316353,-12.461978],[-63.196499,-12.627033],[-62.80306,-13.000653],[-62.127081,-13.198781],[-61.713204,-13.489202],[-61.084121,-13.479384],[-60.503304,-13.775955],[-60.459198,-14.354007],[-60.264326,-14.645979],[-60.251149,-15.077219],[-60.542966,-15.09391],[-60.15839,-16.258284],[-58.24122,-16.299573],[-58.388058,-16.877109],[-58.280804,-17.27171],[-57.734558,-17.552468],[-57.498371,-18.174188],[-57.676009,-18.96184],[-57.949997,-19.400004],[-57.853802,-19.969995],[-58.166392,-20.176701],[-58.183471,-19.868399],[-59.115042,-19.356906],[-60.043565,-19.342747],[-61.786326,-19.633737],[-62.265961,-20.513735],[-62.291179,-21.051635],[-62.685057,-22.249029],[-62.846468,-22.034985]]]}},{type:"Feature",id:"BRA",properties:{name:"Brazil",iso_a2:"BR"},geometry:{type:"Polygon",coordinates:[[[-57.625133,-30.216295],[-56.2909,-28.852761],[-55.162286,-27.881915],[-54.490725,-27.474757],[-53.648735,-26.923473],[-53.628349,-26.124865],[-54.13005,-25.547639],[-54.625291,-25.739255],[-54.428946,-25.162185],[-54.293476,-24.5708],[-54.29296,-24.021014],[-54.652834,-23.839578],[-55.027902,-24.001274],[-55.400747,-23.956935],[-55.517639,-23.571998],[-55.610683,-22.655619],[-55.797958,-22.35693],[-56.473317,-22.0863],[-56.88151,-22.282154],[-57.937156,-22.090176],[-57.870674,-20.732688],[-58.166392,-20.176701],[-57.853802,-19.969995],[-57.949997,-19.400004],[-57.676009,-18.96184],[-57.498371,-18.174188],[-57.734558,-17.552468],[-58.280804,-17.27171],[-58.388058,-16.877109],[-58.24122,-16.299573],[-60.15839,-16.258284],[-60.542966,-15.09391],[-60.251149,-15.077219],[-60.264326,-14.645979],[-60.459198,-14.354007],[-60.503304,-13.775955],[-61.084121,-13.479384],[-61.713204,-13.489202],[-62.127081,-13.198781],[-62.80306,-13.000653],[-63.196499,-12.627033],[-64.316353,-12.461978],[-65.402281,-11.56627],[-65.321899,-10.895872],[-65.444837,-10.511451],[-65.338435,-9.761988],[-66.646908,-9.931331],[-67.173801,-10.306812],[-68.048192,-10.712059],[-68.271254,-11.014521],[-68.786158,-11.03638],[-69.529678,-10.951734],[-70.093752,-11.123972],[-70.548686,-11.009147],[-70.481894,-9.490118],[-71.302412,-10.079436],[-72.184891,-10.053598],[-72.563033,-9.520194],[-73.226713,-9.462213],[-73.015383,-9.032833],[-73.571059,-8.424447],[-73.987235,-7.52383],[-73.723401,-7.340999],[-73.724487,-6.918595],[-73.120027,-6.629931],[-73.219711,-6.089189],[-72.964507,-5.741251],[-72.891928,-5.274561],[-71.748406,-4.593983],[-70.928843,-4.401591],[-70.794769,-4.251265],[-69.893635,-4.298187],[-69.444102,-1.556287],[-69.420486,-1.122619],[-69.577065,-.549992],[-70.020656,-.185156],[-70.015566,.541414],[-69.452396,.706159],[-69.252434,.602651],[-69.218638,.985677],[-69.804597,1.089081],[-69.816973,1.714805],[-67.868565,1.692455],[-67.53781,2.037163],[-67.259998,1.719999],[-67.065048,1.130112],[-66.876326,1.253361],[-66.325765,.724452],[-65.548267,.789254],[-65.354713,1.095282],[-64.611012,1.328731],[-64.199306,1.492855],[-64.083085,1.916369],[-63.368788,2.2009],[-63.422867,2.411068],[-64.269999,2.497006],[-64.408828,3.126786],[-64.368494,3.79721],[-64.816064,4.056445],[-64.628659,4.148481],[-63.888343,4.02053],[-63.093198,3.770571],[-62.804533,4.006965],[-62.08543,4.162124],[-60.966893,4.536468],[-60.601179,4.918098],[-60.733574,5.200277],[-60.213683,5.244486],[-59.980959,5.014061],[-60.111002,4.574967],[-59.767406,4.423503],[-59.53804,3.958803],[-59.815413,3.606499],[-59.974525,2.755233],[-59.718546,2.24963],[-59.646044,1.786894],[-59.030862,1.317698],[-58.540013,1.268088],[-58.429477,1.463942],[-58.11345,1.507195],[-57.660971,1.682585],[-57.335823,1.948538],[-56.782704,1.863711],[-56.539386,1.899523],[-55.995698,1.817667],[-55.9056,2.021996],[-56.073342,2.220795],[-55.973322,2.510364],[-55.569755,2.421506],[-55.097587,2.523748],[-54.524754,2.311849],[-54.088063,2.105557],[-53.778521,2.376703],[-53.554839,2.334897],[-53.418465,2.053389],[-52.939657,2.124858],[-52.556425,2.504705],[-52.249338,3.241094],[-51.657797,4.156232],[-51.317146,4.203491],[-51.069771,3.650398],[-50.508875,1.901564],[-49.974076,1.736483],[-49.947101,1.04619],[-50.699251,.222984],[-50.388211,-.078445],[-48.620567,-.235489],[-48.584497,-1.237805],[-47.824956,-.581618],[-46.566584,-.941028],[-44.905703,-1.55174],[-44.417619,-2.13775],[-44.581589,-2.691308],[-43.418791,-2.38311],[-41.472657,-2.912018],[-39.978665,-2.873054],[-38.500383,-3.700652],[-37.223252,-4.820946],[-36.452937,-5.109404],[-35.597796,-5.149504],[-35.235389,-5.464937],[-34.89603,-6.738193],[-34.729993,-7.343221],[-35.128212,-8.996401],[-35.636967,-9.649282],[-37.046519,-11.040721],[-37.683612,-12.171195],[-38.423877,-13.038119],[-38.673887,-13.057652],[-38.953276,-13.79337],[-38.882298,-15.667054],[-39.161092,-17.208407],[-39.267339,-17.867746],[-39.583521,-18.262296],[-39.760823,-19.599113],[-40.774741,-20.904512],[-40.944756,-21.937317],[-41.754164,-22.370676],[-41.988284,-22.97007],[-43.074704,-22.967693],[-44.647812,-23.351959],[-45.352136,-23.796842],[-46.472093,-24.088969],[-47.648972,-24.885199],[-48.495458,-25.877025],[-48.641005,-26.623698],[-48.474736,-27.175912],[-48.66152,-28.186135],[-48.888457,-28.674115],[-49.587329,-29.224469],[-50.696874,-30.984465],[-51.576226,-31.777698],[-52.256081,-32.24537],[-52.7121,-33.196578],[-53.373662,-33.768378],[-53.650544,-33.202004],[-53.209589,-32.727666],[-53.787952,-32.047243],[-54.572452,-31.494511],[-55.60151,-30.853879],[-55.973245,-30.883076],[-56.976026,-30.109686],[-57.625133,-30.216295]]]}},{type:"Feature",id:"BRN",properties:{name:"Brunei",iso_a2:"BN"},geometry:{type:"Polygon",coordinates:[[[114.204017,4.525874],[114.599961,4.900011],[115.45071,5.44773],[115.4057,4.955228],[115.347461,4.316636],[114.869557,4.348314],[114.659596,4.007637],[114.204017,4.525874]]]}},{type:"Feature",id:"BTN",properties:{name:"Bhutan",iso_a2:"BT"},geometry:{type:"Polygon",coordinates:[[[91.696657,27.771742],[92.103712,27.452614],[92.033484,26.83831],[91.217513,26.808648],[90.373275,26.875724],[89.744528,26.719403],[88.835643,27.098966],[88.814248,27.299316],[89.47581,28.042759],[90.015829,28.296439],[90.730514,28.064954],[91.258854,28.040614],[91.696657,27.771742]]]}},{type:"Feature",id:"BWA",properties:{name:"Botswana",iso_a2:"BW"},geometry:{type:"Polygon",coordinates:[[[25.649163,-18.536026],[25.850391,-18.714413],[26.164791,-19.293086],[27.296505,-20.39152],[27.724747,-20.499059],[27.727228,-20.851802],[28.02137,-21.485975],[28.794656,-21.639454],[29.432188,-22.091313],[28.017236,-22.827754],[27.11941,-23.574323],[26.786407,-24.240691],[26.485753,-24.616327],[25.941652,-24.696373],[25.765849,-25.174845],[25.664666,-25.486816],[25.025171,-25.71967],[24.211267,-25.670216],[23.73357,-25.390129],[23.312097,-25.26869],[22.824271,-25.500459],[22.579532,-25.979448],[22.105969,-26.280256],[21.605896,-26.726534],[20.889609,-26.828543],[20.66647,-26.477453],[20.758609,-25.868136],[20.165726,-24.917962],[19.895768,-24.76779],[19.895458,-21.849157],[20.881134,-21.814327],[20.910641,-18.252219],[21.65504,-18.219146],[23.196858,-17.869038],[23.579006,-18.281261],[24.217365,-17.889347],[24.520705,-17.887125],[25.084443,-17.661816],[25.264226,-17.73654],[25.649163,-18.536026]]]}},{type:"Feature",id:"CAF",properties:{name:"Central African Republic",iso_a2:"CF"},geometry:{type:"Polygon",coordinates:[[[15.27946,7.421925],[16.106232,7.497088],[16.290562,7.754307],[16.456185,7.734774],[16.705988,7.508328],[17.96493,7.890914],[18.389555,8.281304],[18.911022,8.630895],[18.81201,8.982915],[19.094008,9.074847],[20.059685,9.012706],[21.000868,9.475985],[21.723822,10.567056],[22.231129,10.971889],[22.864165,11.142395],[22.977544,10.714463],[23.554304,10.089255],[23.55725,9.681218],[23.394779,9.265068],[23.459013,8.954286],[23.805813,8.666319],[24.567369,8.229188],[25.114932,7.825104],[25.124131,7.500085],[25.796648,6.979316],[26.213418,6.546603],[26.465909,5.946717],[27.213409,5.550953],[27.374226,5.233944],[27.044065,5.127853],[26.402761,5.150875],[25.650455,5.256088],[25.278798,5.170408],[25.128833,4.927245],[24.805029,4.897247],[24.410531,5.108784],[23.297214,4.609693],[22.84148,4.710126],[22.704124,4.633051],[22.405124,4.02916],[21.659123,4.224342],[20.927591,4.322786],[20.290679,4.691678],[19.467784,5.031528],[18.932312,4.709506],[18.542982,4.201785],[18.453065,3.504386],[17.8099,3.560196],[17.133042,3.728197],[16.537058,3.198255],[16.012852,2.26764],[15.907381,2.557389],[15.862732,3.013537],[15.405396,3.335301],[15.03622,3.851367],[14.950953,4.210389],[14.478372,4.732605],[14.558936,5.030598],[14.459407,5.451761],[14.53656,6.226959],[14.776545,6.408498],[15.27946,7.421925]]]}},{type:"Feature",id:"CAN",properties:{name:"Canada",iso_a2:"CA"},geometry:{type:"MultiPolygon",coordinates:[[[[-63.6645,46.55001],[-62.9393,46.41587],[-62.01208,46.44314],[-62.50391,46.03339],[-62.87433,45.96818],[-64.1428,46.39265],[-64.39261,46.72747],[-64.01486,47.03601],[-63.6645,46.55001]]],[[[-61.806305,49.10506],[-62.29318,49.08717],[-63.58926,49.40069],[-64.51912,49.87304],[-64.17322,49.95718],[-62.85829,49.70641],[-61.835585,49.28855],[-61.806305,49.10506]]],[[[-123.510002,48.510011],[-124.012891,48.370846],[-125.655013,48.825005],[-125.954994,49.179996],[-126.850004,49.53],[-127.029993,49.814996],[-128.059336,49.994959],[-128.444584,50.539138],[-128.358414,50.770648],[-127.308581,50.552574],[-126.695001,50.400903],[-125.755007,50.295018],[-125.415002,49.950001],[-124.920768,49.475275],[-123.922509,49.062484],[-123.510002,48.510011]]],[[[-56.134036,50.68701],[-56.795882,49.812309],[-56.143105,50.150117],[-55.471492,49.935815],[-55.822401,49.587129],[-54.935143,49.313011],[-54.473775,49.556691],[-53.476549,49.249139],[-53.786014,48.516781],[-53.086134,48.687804],[-52.958648,48.157164],[-52.648099,47.535548],[-53.069158,46.655499],[-53.521456,46.618292],[-54.178936,46.807066],[-53.961869,47.625207],[-54.240482,47.752279],[-55.400773,46.884994],[-55.997481,46.91972],[-55.291219,47.389562],[-56.250799,47.632545],[-57.325229,47.572807],[-59.266015,47.603348],[-59.419494,47.899454],[-58.796586,48.251525],[-59.231625,48.523188],[-58.391805,49.125581],[-57.35869,50.718274],[-56.73865,51.287438],[-55.870977,51.632094],[-55.406974,51.588273],[-55.600218,51.317075],[-56.134036,50.68701]]],[[[-132.710008,54.040009],[-131.74999,54.120004],[-132.04948,52.984621],[-131.179043,52.180433],[-131.57783,52.182371],[-132.180428,52.639707],[-132.549992,53.100015],[-133.054611,53.411469],[-133.239664,53.85108],[-133.180004,54.169975],[-132.710008,54.040009]]],[[[-79.26582,62.158675],[-79.65752,61.63308],[-80.09956,61.7181],[-80.36215,62.01649],[-80.315395,62.085565],[-79.92939,62.3856],[-79.52002,62.36371],[-79.26582,62.158675]]],[[[-81.89825,62.7108],[-83.06857,62.15922],[-83.77462,62.18231],[-83.99367,62.4528],[-83.25048,62.91409],[-81.87699,62.90458],[-81.89825,62.7108]]],[[[-85.161308,65.657285],[-84.975764,65.217518],[-84.464012,65.371772],[-83.882626,65.109618],[-82.787577,64.766693],[-81.642014,64.455136],[-81.55344,63.979609],[-80.817361,64.057486],[-80.103451,63.725981],[-80.99102,63.411246],[-82.547178,63.651722],[-83.108798,64.101876],[-84.100417,63.569712],[-85.523405,63.052379],[-85.866769,63.637253],[-87.221983,63.541238],[-86.35276,64.035833],[-86.224886,64.822917],[-85.883848,65.738778],[-85.161308,65.657285]]],[[[-75.86588,67.14886],[-76.98687,67.09873],[-77.2364,67.58809],[-76.81166,68.14856],[-75.89521,68.28721],[-75.1145,68.01036],[-75.10333,67.58202],[-75.21597,67.44425],[-75.86588,67.14886]]],[[[-95.647681,69.10769],[-96.269521,68.75704],[-97.617401,69.06003],[-98.431801,68.9507],[-99.797401,69.40003],[-98.917401,69.71003],[-98.218261,70.14354],[-97.157401,69.86003],[-96.557401,69.68003],[-96.257401,69.49003],[-95.647681,69.10769]]],[[[-90.5471,69.49766],[-90.55151,68.47499],[-89.21515,69.25873],[-88.01966,68.61508],[-88.31749,67.87338],[-87.35017,67.19872],[-86.30607,67.92146],[-85.57664,68.78456],[-85.52197,69.88211],[-84.10081,69.80539],[-82.62258,69.65826],[-81.28043,69.16202],[-81.2202,68.66567],[-81.96436,68.13253],[-81.25928,67.59716],[-81.38653,67.11078],[-83.34456,66.41154],[-84.73542,66.2573],[-85.76943,66.55833],[-86.0676,66.05625],[-87.03143,65.21297],[-87.32324,64.77563],[-88.48296,64.09897],[-89.91444,64.03273],[-90.70398,63.61017],[-90.77004,62.96021],[-91.93342,62.83508],[-93.15698,62.02469],[-94.24153,60.89865],[-94.62931,60.11021],[-94.6846,58.94882],[-93.21502,58.78212],[-92.76462,57.84571],[-92.29703,57.08709],[-90.89769,57.28468],[-89.03953,56.85172],[-88.03978,56.47162],[-87.32421,55.99914],[-86.07121,55.72383],[-85.01181,55.3026],[-83.36055,55.24489],[-82.27285,55.14832],[-82.4362,54.28227],[-82.12502,53.27703],[-81.40075,52.15788],[-79.91289,51.20842],[-79.14301,51.53393],[-78.60191,52.56208],[-79.12421,54.14145],[-79.82958,54.66772],[-78.22874,55.13645],[-77.0956,55.83741],[-76.54137,56.53423],[-76.62319,57.20263],[-77.30226,58.05209],[-78.51688,58.80458],[-77.33676,59.85261],[-77.77272,60.75788],[-78.10687,62.31964],[-77.41067,62.55053],[-75.69621,62.2784],[-74.6682,62.18111],[-73.83988,62.4438],[-72.90853,62.10507],[-71.67708,61.52535],[-71.37369,61.13717],[-69.59042,61.06141],[-69.62033,60.22125],[-69.2879,58.95736],[-68.37455,58.80106],[-67.64976,58.21206],[-66.20178,58.76731],[-65.24517,59.87071],[-64.58352,60.33558],[-63.80475,59.4426],[-62.50236,58.16708],[-61.39655,56.96745],[-61.79866,56.33945],[-60.46853,55.77548],[-59.56962,55.20407],[-57.97508,54.94549],[-57.3332,54.6265],[-56.93689,53.78032],[-56.15811,53.64749],[-55.75632,53.27036],[-55.68338,52.14664],[-56.40916,51.7707],[-57.12691,51.41972],[-58.77482,51.0643],[-60.03309,50.24277],[-61.72366,50.08046],[-63.86251,50.29099],[-65.36331,50.2982],[-66.39905,50.22897],[-67.23631,49.51156],[-68.51114,49.06836],[-69.95362,47.74488],[-71.10458,46.82171],[-70.25522,46.98606],[-68.65,48.3],[-66.55243,49.1331],[-65.05626,49.23278],[-64.17099,48.74248],[-65.11545,48.07085],[-64.79854,46.99297],[-64.47219,46.23849],[-63.17329,45.73902],[-61.52072,45.88377],[-60.51815,47.00793],[-60.4486,46.28264],[-59.80287,45.9204],[-61.03988,45.26525],[-63.25471,44.67014],[-64.24656,44.26553],[-65.36406,43.54523],[-66.1234,43.61867],[-66.16173,44.46512],[-64.42549,45.29204],[-66.02605,45.25931],[-67.13741,45.13753],[-67.79134,45.70281],[-67.79046,47.06636],[-68.23444,47.35486],[-68.905,47.185],[-69.237216,47.447781],[-69.99997,46.69307],[-70.305,45.915],[-70.66,45.46],[-71.08482,45.30524],[-71.405,45.255],[-71.50506,45.0082],[-73.34783,45.00738],[-74.867,45.00048],[-75.31821,44.81645],[-76.375,44.09631],[-76.5,44.018459],[-76.820034,43.628784],[-77.737885,43.629056],[-78.72028,43.625089],[-79.171674,43.466339],[-79.01,43.27],[-78.92,42.965],[-78.939362,42.863611],[-80.247448,42.3662],[-81.277747,42.209026],[-82.439278,41.675105],[-82.690089,41.675105],[-83.02981,41.832796],[-83.142,41.975681],[-83.12,42.08],[-82.9,42.43],[-82.43,42.98],[-82.137642,43.571088],[-82.337763,44.44],[-82.550925,45.347517],[-83.592851,45.816894],[-83.469551,45.994686],[-83.616131,46.116927],[-83.890765,46.116927],[-84.091851,46.275419],[-84.14212,46.512226],[-84.3367,46.40877],[-84.6049,46.4396],[-84.543749,46.538684],[-84.779238,46.637102],[-84.87608,46.900083],[-85.652363,47.220219],[-86.461991,47.553338],[-87.439793,47.94],[-88.378114,48.302918],[-89.272917,48.019808],[-89.6,48.01],[-90.83,48.27],[-91.64,48.14],[-92.61,48.45],[-93.63087,48.60926],[-94.32914,48.67074],[-94.64,48.84],[-94.81758,49.38905],[-95.15609,49.38425],[-95.15907,49],[-97.22872,49.0007],[-100.65,49],[-104.04826,48.99986],[-107.05,49],[-110.05,49],[-113,49],[-116.04818,49],[-117.03121,49],[-120,49],[-122.84,49],[-122.97421,49.002538],[-124.91024,49.98456],[-125.62461,50.41656],[-127.43561,50.83061],[-127.99276,51.71583],[-127.85032,52.32961],[-129.12979,52.75538],[-129.30523,53.56159],[-130.51497,54.28757],[-130.53611,54.80278],[-129.98,55.285],[-130.00778,55.91583],[-131.70781,56.55212],[-132.73042,57.69289],[-133.35556,58.41028],[-134.27111,58.86111],[-134.945,59.27056],[-135.47583,59.78778],[-136.47972,59.46389],[-137.4525,58.905],[-138.34089,59.56211],[-139.039,60],[-140.013,60.27682],[-140.99778,60.30639],[-140.9925,66.00003],[-140.986,69.712],[-139.12052,69.47102],[-137.54636,68.99002],[-136.50358,68.89804],[-135.62576,69.31512],[-134.41464,69.62743],[-132.92925,69.50534],[-131.43136,69.94451],[-129.79471,70.19369],[-129.10773,69.77927],[-128.36156,70.01286],[-128.13817,70.48384],[-127.44712,70.37721],[-125.75632,69.48058],[-124.42483,70.1584],[-124.28968,69.39969],[-123.06108,69.56372],[-122.6835,69.85553],[-121.47226,69.79778],[-119.94288,69.37786],[-117.60268,69.01128],[-116.22643,68.84151],[-115.2469,68.90591],[-113.89794,68.3989],[-115.30489,67.90261],[-113.49727,67.68815],[-110.798,67.80612],[-109.94619,67.98104],[-108.8802,67.38144],[-107.79239,67.88736],[-108.81299,68.31164],[-108.16721,68.65392],[-106.95,68.7],[-106.15,68.8],[-105.34282,68.56122],[-104.33791,68.018],[-103.22115,68.09775],[-101.45433,67.64689],[-99.90195,67.80566],[-98.4432,67.78165],[-98.5586,68.40394],[-97.66948,68.57864],[-96.11991,68.23939],[-96.12588,67.29338],[-95.48943,68.0907],[-94.685,68.06383],[-94.23282,69.06903],[-95.30408,69.68571],[-96.47131,70.08976],[-96.39115,71.19482],[-95.2088,71.92053],[-93.88997,71.76015],[-92.87818,71.31869],[-91.51964,70.19129],[-92.40692,69.69997],[-90.5471,69.49766]]],[[[-114.16717,73.12145],[-114.66634,72.65277],[-112.44102,72.9554],[-111.05039,72.4504],[-109.92035,72.96113],[-109.00654,72.63335],[-108.18835,71.65089],[-107.68599,72.06548],[-108.39639,73.08953],[-107.51645,73.23598],[-106.52259,73.07601],[-105.40246,72.67259],[-104.77484,71.6984],[-104.46476,70.99297],[-102.78537,70.49776],[-100.98078,70.02432],[-101.08929,69.58447],[-102.73116,69.50402],[-102.09329,69.11962],[-102.43024,68.75282],[-104.24,68.91],[-105.96,69.18],[-107.12254,69.11922],[-109,68.78],[-111.534149,68.630059],[-113.3132,68.53554],[-113.85496,69.00744],[-115.22,69.28],[-116.10794,69.16821],[-117.34,69.96],[-116.67473,70.06655],[-115.13112,70.2373],[-113.72141,70.19237],[-112.4161,70.36638],[-114.35,70.6],[-116.48684,70.52045],[-117.9048,70.54056],[-118.43238,70.9092],[-116.11311,71.30918],[-117.65568,71.2952],[-119.40199,71.55859],[-118.56267,72.30785],[-117.86642,72.70594],[-115.18909,73.31459],[-114.16717,73.12145]]],[[[-104.5,73.42],[-105.38,72.76],[-106.94,73.46],[-106.6,73.6],[-105.26,73.64],[-104.5,73.42]]],[[[-76.34,73.102685],[-76.251404,72.826385],[-77.314438,72.855545],[-78.39167,72.876656],[-79.486252,72.742203],[-79.775833,72.802902],[-80.876099,73.333183],[-80.833885,73.693184],[-80.353058,73.75972],[-78.064438,73.651932],[-76.34,73.102685]]],[[[-86.562179,73.157447],[-85.774371,72.534126],[-84.850112,73.340278],[-82.31559,73.750951],[-80.600088,72.716544],[-80.748942,72.061907],[-78.770639,72.352173],[-77.824624,72.749617],[-75.605845,72.243678],[-74.228616,71.767144],[-74.099141,71.33084],[-72.242226,71.556925],[-71.200015,70.920013],[-68.786054,70.525024],[-67.91497,70.121948],[-66.969033,69.186087],[-68.805123,68.720198],[-66.449866,68.067163],[-64.862314,67.847539],[-63.424934,66.928473],[-61.851981,66.862121],[-62.163177,66.160251],[-63.918444,64.998669],[-65.14886,65.426033],[-66.721219,66.388041],[-68.015016,66.262726],[-68.141287,65.689789],[-67.089646,65.108455],[-65.73208,64.648406],[-65.320168,64.382737],[-64.669406,63.392927],[-65.013804,62.674185],[-66.275045,62.945099],[-68.783186,63.74567],[-67.369681,62.883966],[-66.328297,62.280075],[-66.165568,61.930897],[-68.877367,62.330149],[-71.023437,62.910708],[-72.235379,63.397836],[-71.886278,63.679989],[-73.378306,64.193963],[-74.834419,64.679076],[-74.818503,64.389093],[-77.70998,64.229542],[-78.555949,64.572906],[-77.897281,65.309192],[-76.018274,65.326969],[-73.959795,65.454765],[-74.293883,65.811771],[-73.944912,66.310578],[-72.651167,67.284576],[-72.92606,67.726926],[-73.311618,68.069437],[-74.843307,68.554627],[-76.869101,68.894736],[-76.228649,69.147769],[-77.28737,69.76954],[-78.168634,69.826488],[-78.957242,70.16688],[-79.492455,69.871808],[-81.305471,69.743185],[-84.944706,69.966634],[-87.060003,70.260001],[-88.681713,70.410741],[-89.51342,70.762038],[-88.467721,71.218186],[-89.888151,71.222552],[-90.20516,72.235074],[-89.436577,73.129464],[-88.408242,73.537889],[-85.826151,73.803816],[-86.562179,73.157447]]],[[[-100.35642,73.84389],[-99.16387,73.63339],[-97.38,73.76],[-97.12,73.47],[-98.05359,72.99052],[-96.54,72.56],[-96.72,71.66],[-98.35966,71.27285],[-99.32286,71.35639],[-100.01482,71.73827],[-102.5,72.51],[-102.48,72.83],[-100.43836,72.70588],[-101.54,73.36],[-100.35642,73.84389]]],[[[-93.196296,72.771992],[-94.269047,72.024596],[-95.409856,72.061881],[-96.033745,72.940277],[-96.018268,73.43743],[-95.495793,73.862417],[-94.503658,74.134907],[-92.420012,74.100025],[-90.509793,73.856732],[-92.003965,72.966244],[-93.196296,72.771992]]],[[[-120.46,71.383602],[-123.09219,70.90164],[-123.62,71.34],[-125.928949,71.868688],[-125.5,72.292261],[-124.80729,73.02256],[-123.94,73.68],[-124.91775,74.29275],[-121.53788,74.44893],[-120.10978,74.24135],[-117.55564,74.18577],[-116.58442,73.89607],[-115.51081,73.47519],[-116.76794,73.22292],[-119.22,72.52],[-120.46,71.82],[-120.46,71.383602]]],[[[-93.612756,74.979997],[-94.156909,74.592347],[-95.608681,74.666864],[-96.820932,74.927623],[-96.288587,75.377828],[-94.85082,75.647218],[-93.977747,75.29649],[-93.612756,74.979997]]],[[[-98.5,76.72],[-97.735585,76.25656],[-97.704415,75.74344],[-98.16,75],[-99.80874,74.89744],[-100.88366,75.05736],[-100.86292,75.64075],[-102.50209,75.5638],[-102.56552,76.3366],[-101.48973,76.30537],[-99.98349,76.64634],[-98.57699,76.58859],[-98.5,76.72]]],[[[-108.21141,76.20168],[-107.81943,75.84552],[-106.92893,76.01282],[-105.881,75.9694],[-105.70498,75.47951],[-106.31347,75.00527],[-109.7,74.85],[-112.22307,74.41696],[-113.74381,74.39427],[-113.87135,74.72029],[-111.79421,75.1625],[-116.31221,75.04343],[-117.7104,75.2222],[-116.34602,76.19903],[-115.40487,76.47887],[-112.59056,76.14134],[-110.81422,75.54919],[-109.0671,75.47321],[-110.49726,76.42982],[-109.5811,76.79417],[-108.54859,76.67832],[-108.21141,76.20168]]],[[[-94.684086,77.097878],[-93.573921,76.776296],[-91.605023,76.778518],[-90.741846,76.449597],[-90.969661,76.074013],[-89.822238,75.847774],[-89.187083,75.610166],[-87.838276,75.566189],[-86.379192,75.482421],[-84.789625,75.699204],[-82.753445,75.784315],[-81.128531,75.713983],[-80.057511,75.336849],[-79.833933,74.923127],[-80.457771,74.657304],[-81.948843,74.442459],[-83.228894,74.564028],[-86.097452,74.410032],[-88.15035,74.392307],[-89.764722,74.515555],[-92.422441,74.837758],[-92.768285,75.38682],[-92.889906,75.882655],[-93.893824,76.319244],[-95.962457,76.441381],[-97.121379,76.751078],[-96.745123,77.161389],[-94.684086,77.097878]]],[[[-116.198587,77.645287],[-116.335813,76.876962],[-117.106051,76.530032],[-118.040412,76.481172],[-119.899318,76.053213],[-121.499995,75.900019],[-122.854924,76.116543],[-122.854925,76.116543],[-121.157535,76.864508],[-119.103939,77.51222],[-117.570131,77.498319],[-116.198587,77.645287]]],[[[-93.840003,77.519997],[-94.295608,77.491343],[-96.169654,77.555111],[-96.436304,77.834629],[-94.422577,77.820005],[-93.720656,77.634331],[-93.840003,77.519997]]],[[[-110.186938,77.697015],[-112.051191,77.409229],[-113.534279,77.732207],[-112.724587,78.05105],[-111.264443,78.152956],[-109.854452,77.996325],[-110.186938,77.697015]]],[[[-109.663146,78.601973],[-110.881314,78.40692],[-112.542091,78.407902],[-112.525891,78.550555],[-111.50001,78.849994],[-110.963661,78.804441],[-109.663146,78.601973]]],[[[-95.830295,78.056941],[-97.309843,77.850597],[-98.124289,78.082857],[-98.552868,78.458105],[-98.631984,78.87193],[-97.337231,78.831984],[-96.754399,78.765813],[-95.559278,78.418315],[-95.830295,78.056941]]],[[[-100.060192,78.324754],[-99.670939,77.907545],[-101.30394,78.018985],[-102.949809,78.343229],[-105.176133,78.380332],[-104.210429,78.67742],[-105.41958,78.918336],[-105.492289,79.301594],[-103.529282,79.165349],[-100.825158,78.800462],[-100.060192,78.324754]]],[[[-87.02,79.66],[-85.81435,79.3369],[-87.18756,79.0393],[-89.03535,78.28723],[-90.80436,78.21533],[-92.87669,78.34333],[-93.95116,78.75099],[-93.93574,79.11373],[-93.14524,79.3801],[-94.974,79.37248],[-96.07614,79.70502],[-96.70972,80.15777],[-96.01644,80.60233],[-95.32345,80.90729],[-94.29843,80.97727],[-94.73542,81.20646],[-92.40984,81.25739],[-91.13289,80.72345],[-89.45,80.509322],[-87.81,80.32],[-87.02,79.66]]],[[[-68.5,83.106322],[-65.82735,83.02801],[-63.68,82.9],[-61.85,82.6286],[-61.89388,82.36165],[-64.334,81.92775],[-66.75342,81.72527],[-67.65755,81.50141],[-65.48031,81.50657],[-67.84,80.9],[-69.4697,80.61683],[-71.18,79.8],[-73.2428,79.63415],[-73.88,79.430162],[-76.90773,79.32309],[-75.52924,79.19766],[-76.22046,79.01907],[-75.39345,78.52581],[-76.34354,78.18296],[-77.88851,77.89991],[-78.36269,77.50859],[-79.75951,77.20968],[-79.61965,76.98336],[-77.91089,77.022045],[-77.88911,76.777955],[-80.56125,76.17812],[-83.17439,76.45403],[-86.11184,76.29901],[-87.6,76.42],[-89.49068,76.47239],[-89.6161,76.95213],[-87.76739,77.17833],[-88.26,77.9],[-87.65,77.970222],[-84.97634,77.53873],[-86.34,78.18],[-87.96192,78.37181],[-87.15198,78.75867],[-85.37868,78.9969],[-85.09495,79.34543],[-86.50734,79.73624],[-86.93179,80.25145],[-84.19844,80.20836],[-83.408696,80.1],[-81.84823,80.46442],[-84.1,80.58],[-87.59895,80.51627],[-89.36663,80.85569],[-90.2,81.26],[-91.36786,81.5531],[-91.58702,81.89429],[-90.1,82.085],[-88.93227,82.11751],[-86.97024,82.27961],[-85.5,82.652273],[-84.260005,82.6],[-83.18,82.32],[-82.42,82.86],[-81.1,83.02],[-79.30664,83.13056],[-76.25,83.172059],[-75.71878,83.06404],[-72.83153,83.23324],[-70.665765,83.169781],[-68.5,83.106322]]]]}},{type:"Feature",id:"CHE",properties:{name:"Switzerland",iso_a2:"CH"},geometry:{type:"Polygon",coordinates:[[[9.594226,47.525058],[9.632932,47.347601],[9.47997,47.10281],[9.932448,46.920728],[10.442701,46.893546],[10.363378,46.483571],[9.922837,46.314899],[9.182882,46.440215],[8.966306,46.036932],[8.489952,46.005151],[8.31663,46.163642],[7.755992,45.82449],[7.273851,45.776948],[6.843593,45.991147],[6.5001,46.429673],[6.022609,46.27299],[6.037389,46.725779],[6.768714,47.287708],[6.736571,47.541801],[7.192202,47.449766],[7.466759,47.620582],[8.317301,47.61358],[8.522612,47.830828],[9.594226,47.525058]]]}},{type:"Feature",id:"CHL",properties:{name:"Chile",iso_a2:"CL"},geometry:{type:"MultiPolygon",coordinates:[[[[-68.63401,-52.63637],[-68.63335,-54.8695],[-67.56244,-54.87001],[-66.95992,-54.89681],[-67.29103,-55.30124],[-68.14863,-55.61183],[-68.639991,-55.580018],[-69.2321,-55.49906],[-69.95809,-55.19843],[-71.00568,-55.05383],[-72.2639,-54.49514],[-73.2852,-53.95752],[-74.66253,-52.83749],[-73.8381,-53.04743],[-72.43418,-53.7154],[-71.10773,-54.07433],[-70.59178,-53.61583],[-70.26748,-52.93123],[-69.34565,-52.5183],[-68.63401,-52.63637]]],[[[-68.219913,-21.494347],[-67.82818,-22.872919],[-67.106674,-22.735925],[-66.985234,-22.986349],[-67.328443,-24.025303],[-68.417653,-24.518555],[-68.386001,-26.185016],[-68.5948,-26.506909],[-68.295542,-26.89934],[-69.001235,-27.521214],[-69.65613,-28.459141],[-70.01355,-29.367923],[-69.919008,-30.336339],[-70.535069,-31.36501],[-70.074399,-33.09121],[-69.814777,-33.273886],[-69.817309,-34.193571],[-70.388049,-35.169688],[-70.364769,-36.005089],[-71.121881,-36.658124],[-71.118625,-37.576827],[-70.814664,-38.552995],[-71.413517,-38.916022],[-71.680761,-39.808164],[-71.915734,-40.832339],[-71.746804,-42.051386],[-72.148898,-42.254888],[-71.915424,-43.408565],[-71.464056,-43.787611],[-71.793623,-44.207172],[-71.329801,-44.407522],[-71.222779,-44.784243],[-71.659316,-44.973689],[-71.552009,-45.560733],[-71.917258,-46.884838],[-72.447355,-47.738533],[-72.331161,-48.244238],[-72.648247,-48.878618],[-73.415436,-49.318436],[-73.328051,-50.378785],[-72.975747,-50.74145],[-72.309974,-50.67701],[-72.329404,-51.425956],[-71.914804,-52.009022],[-69.498362,-52.142761],[-68.571545,-52.299444],[-69.461284,-52.291951],[-69.94278,-52.537931],[-70.845102,-52.899201],[-71.006332,-53.833252],[-71.429795,-53.856455],[-72.557943,-53.53141],[-73.702757,-52.835069],[-73.702757,-52.83507],[-74.946763,-52.262754],[-75.260026,-51.629355],[-74.976632,-51.043396],[-75.479754,-50.378372],[-75.608015,-48.673773],[-75.18277,-47.711919],[-74.126581,-46.939253],[-75.644395,-46.647643],[-74.692154,-45.763976],[-74.351709,-44.103044],[-73.240356,-44.454961],[-72.717804,-42.383356],[-73.3889,-42.117532],[-73.701336,-43.365776],[-74.331943,-43.224958],[-74.017957,-41.794813],[-73.677099,-39.942213],[-73.217593,-39.258689],[-73.505559,-38.282883],[-73.588061,-37.156285],[-73.166717,-37.12378],[-72.553137,-35.50884],[-71.861732,-33.909093],[-71.43845,-32.418899],[-71.668721,-30.920645],[-71.370083,-30.095682],[-71.489894,-28.861442],[-70.905124,-27.64038],[-70.724954,-25.705924],[-70.403966,-23.628997],[-70.091246,-21.393319],[-70.16442,-19.756468],[-70.372572,-18.347975],[-69.858444,-18.092694],[-69.590424,-17.580012],[-69.100247,-18.260125],[-68.966818,-18.981683],[-68.442225,-19.405068],[-68.757167,-20.372658],[-68.219913,-21.494347]]]]}},{type:"Feature",id:"CHN",properties:{name:"China",iso_a2:"CN"},geometry:{type:"MultiPolygon",coordinates:[[[[110.339188,18.678395],[109.47521,18.197701],[108.655208,18.507682],[108.626217,19.367888],[109.119056,19.821039],[110.211599,20.101254],[110.786551,20.077534],[111.010051,19.69593],[110.570647,19.255879],[110.339188,18.678395]]],[[[127.657407,49.76027],[129.397818,49.4406],[130.582293,48.729687],[130.987282,47.790132],[132.506672,47.78897],[133.373596,48.183442],[135.026311,48.47823],[134.500814,47.57844],[134.112362,47.212467],[133.769644,46.116927],[133.097127,45.144066],[131.883454,45.321162],[131.025212,44.967953],[131.288555,44.11152],[131.144688,42.92999],[130.633866,42.903015],[130.640016,42.395009],[129.994267,42.985387],[129.596669,42.424982],[128.052215,41.994285],[128.208433,41.466772],[127.343783,41.503152],[126.869083,41.816569],[126.182045,41.107336],[125.079942,40.569824],[124.265625,39.928493],[122.86757,39.637788],[122.131388,39.170452],[121.054554,38.897471],[121.585995,39.360854],[121.376757,39.750261],[122.168595,40.422443],[121.640359,40.94639],[120.768629,40.593388],[119.639602,39.898056],[119.023464,39.252333],[118.042749,39.204274],[117.532702,38.737636],[118.059699,38.061476],[118.87815,37.897325],[118.911636,37.448464],[119.702802,37.156389],[120.823457,37.870428],[121.711259,37.481123],[122.357937,37.454484],[122.519995,36.930614],[121.104164,36.651329],[120.637009,36.11144],[119.664562,35.609791],[119.151208,34.909859],[120.227525,34.360332],[120.620369,33.376723],[121.229014,32.460319],[121.908146,31.692174],[121.891919,30.949352],[121.264257,30.676267],[121.503519,30.142915],[122.092114,29.83252],[121.938428,29.018022],[121.684439,28.225513],[121.125661,28.135673],[120.395473,27.053207],[119.585497,25.740781],[118.656871,24.547391],[117.281606,23.624501],[115.890735,22.782873],[114.763827,22.668074],[114.152547,22.22376],[113.80678,22.54834],[113.241078,22.051367],[111.843592,21.550494],[110.785466,21.397144],[110.444039,20.341033],[109.889861,20.282457],[109.627655,21.008227],[109.864488,21.395051],[108.522813,21.715212],[108.05018,21.55238],[107.04342,21.811899],[106.567273,22.218205],[106.725403,22.794268],[105.811247,22.976892],[105.329209,23.352063],[104.476858,22.81915],[103.504515,22.703757],[102.706992,22.708795],[102.170436,22.464753],[101.652018,22.318199],[101.80312,21.174367],[101.270026,21.201652],[101.180005,21.436573],[101.150033,21.849984],[100.416538,21.558839],[99.983489,21.742937],[99.240899,22.118314],[99.531992,22.949039],[98.898749,23.142722],[98.660262,24.063286],[97.60472,23.897405],[97.724609,25.083637],[98.671838,25.918703],[98.712094,26.743536],[98.68269,27.508812],[98.246231,27.747221],[97.911988,28.335945],[97.327114,28.261583],[96.248833,28.411031],[96.586591,28.83098],[96.117679,29.452802],[95.404802,29.031717],[94.56599,29.277438],[93.413348,28.640629],[92.503119,27.896876],[91.696657,27.771742],[91.258854,28.040614],[90.730514,28.064954],[90.015829,28.296439],[89.47581,28.042759],[88.814248,27.299316],[88.730326,28.086865],[88.120441,27.876542],[86.954517,27.974262],[85.82332,28.203576],[85.011638,28.642774],[84.23458,28.839894],[83.898993,29.320226],[83.337115,29.463732],[82.327513,30.115268],[81.525804,30.422717],[81.111256,30.183481],[79.721367,30.882715],[78.738894,31.515906],[78.458446,32.618164],[79.176129,32.48378],[79.208892,32.994395],[78.811086,33.506198],[78.912269,34.321936],[77.837451,35.49401],[76.192848,35.898403],[75.896897,36.666806],[75.158028,37.133031],[74.980002,37.41999],[74.829986,37.990007],[74.864816,38.378846],[74.257514,38.606507],[73.928852,38.505815],[73.675379,39.431237],[73.960013,39.660008],[73.822244,39.893973],[74.776862,40.366425],[75.467828,40.562072],[76.526368,40.427946],[76.904484,41.066486],[78.187197,41.185316],[78.543661,41.582243],[80.11943,42.123941],[80.25999,42.349999],[80.18015,42.920068],[80.866206,43.180362],[79.966106,44.917517],[81.947071,45.317027],[82.458926,45.53965],[83.180484,47.330031],[85.16429,47.000956],[85.720484,47.452969],[85.768233,48.455751],[86.598776,48.549182],[87.35997,49.214981],[87.751264,49.297198],[88.013832,48.599463],[88.854298,48.069082],[90.280826,47.693549],[90.970809,46.888146],[90.585768,45.719716],[90.94554,45.286073],[92.133891,45.115076],[93.480734,44.975472],[94.688929,44.352332],[95.306875,44.241331],[95.762455,43.319449],[96.349396,42.725635],[97.451757,42.74889],[99.515817,42.524691],[100.845866,42.663804],[101.83304,42.514873],[103.312278,41.907468],[104.522282,41.908347],[104.964994,41.59741],[106.129316,42.134328],[107.744773,42.481516],[109.243596,42.519446],[110.412103,42.871234],[111.129682,43.406834],[111.829588,43.743118],[111.667737,44.073176],[111.348377,44.457442],[111.873306,45.102079],[112.436062,45.011646],[113.463907,44.808893],[114.460332,45.339817],[115.985096,45.727235],[116.717868,46.388202],[117.421701,46.672733],[118.874326,46.805412],[119.66327,46.69268],[119.772824,47.048059],[118.866574,47.74706],[118.064143,48.06673],[117.295507,47.697709],[116.308953,47.85341],[115.742837,47.726545],[115.485282,48.135383],[116.191802,49.134598],[116.678801,49.888531],[117.879244,49.510983],[119.288461,50.142883],[119.279366,50.582908],[120.18205,51.643566],[120.738191,51.964115],[120.725789,52.516226],[120.177089,52.753886],[121.003085,53.251401],[122.245748,53.431726],[123.571507,53.458804],[125.068211,53.161045],[125.946349,52.792799],[126.564399,51.784255],[126.939157,51.353894],[127.287456,50.739797],[127.657407,49.76027]]]]}},{type:"Feature",id:"CIV",properties:{name:"Ivory Coast",iso_a2:"CI"},geometry:{type:"Polygon",coordinates:[[[-2.856125,4.994476],[-3.311084,4.984296],[-4.00882,5.179813],[-4.649917,5.168264],[-5.834496,4.993701],[-6.528769,4.705088],[-7.518941,4.338288],[-7.712159,4.364566],[-7.635368,5.188159],[-7.539715,5.313345],[-7.570153,5.707352],[-7.993693,6.12619],[-8.311348,6.193033],[-8.60288,6.467564],[-8.385452,6.911801],[-8.485446,7.395208],[-8.439298,7.686043],[-8.280703,7.68718],[-8.221792,8.123329],[-8.299049,8.316444],[-8.203499,8.455453],[-7.8321,8.575704],[-8.079114,9.376224],[-8.309616,9.789532],[-8.229337,10.12902],[-8.029944,10.206535],[-7.89959,10.297382],[-7.622759,10.147236],[-6.850507,10.138994],[-6.666461,10.430811],[-6.493965,10.411303],[-6.205223,10.524061],[-6.050452,10.096361],[-5.816926,10.222555],[-5.404342,10.370737],[-4.954653,10.152714],[-4.779884,9.821985],[-4.330247,9.610835],[-3.980449,9.862344],[-3.511899,9.900326],[-2.827496,9.642461],[-2.56219,8.219628],[-2.983585,7.379705],[-3.24437,6.250472],[-2.810701,5.389051],[-2.856125,4.994476]]]}},{type:"Feature",id:"CMR",properties:{name:"Cameroon",iso_a2:"CM"},geometry:{type:"Polygon",coordinates:[[[13.075822,2.267097],[12.951334,2.321616],[12.35938,2.192812],[11.751665,2.326758],[11.276449,2.261051],[9.649158,2.283866],[9.795196,3.073404],[9.404367,3.734527],[8.948116,3.904129],[8.744924,4.352215],[8.488816,4.495617],[8.500288,4.771983],[8.757533,5.479666],[9.233163,6.444491],[9.522706,6.453482],[10.118277,7.03877],[10.497375,7.055358],[11.058788,6.644427],[11.745774,6.981383],[11.839309,7.397042],[12.063946,7.799808],[12.218872,8.305824],[12.753672,8.717763],[12.955468,9.417772],[13.1676,9.640626],[13.308676,10.160362],[13.57295,10.798566],[14.415379,11.572369],[14.468192,11.904752],[14.577178,12.085361],[14.181336,12.483657],[14.213531,12.802035],[14.495787,12.859396],[14.893386,12.219048],[14.960152,11.555574],[14.923565,10.891325],[15.467873,9.982337],[14.909354,9.992129],[14.627201,9.920919],[14.171466,10.021378],[13.954218,9.549495],[14.544467,8.965861],[14.979996,8.796104],[15.120866,8.38215],[15.436092,7.692812],[15.27946,7.421925],[14.776545,6.408498],[14.53656,6.226959],[14.459407,5.451761],[14.558936,5.030598],[14.478372,4.732605],[14.950953,4.210389],[15.03622,3.851367],[15.405396,3.335301],[15.862732,3.013537],[15.907381,2.557389],[16.012852,2.26764],[15.940919,1.727673],[15.146342,1.964015],[14.337813,2.227875],[13.075822,2.267097]]]}},{type:"Feature",id:"COD",properties:{name:"Democratic Republic of the Congo",iso_a2:"CD"},geometry:{type:"Polygon",coordinates:[[[30.83386,3.509166],[30.773347,2.339883],[31.174149,2.204465],[30.85267,1.849396],[30.468508,1.583805],[30.086154,1.062313],[29.875779,.59738],[29.819503,-.20531],[29.587838,-.587406],[29.579466,-1.341313],[29.291887,-1.620056],[29.254835,-2.21511],[29.117479,-2.292211],[29.024926,-2.839258],[29.276384,-3.293907],[29.339998,-4.499983],[29.519987,-5.419979],[29.419993,-5.939999],[29.620032,-6.520015],[30.199997,-7.079981],[30.740015,-8.340007],[30.346086,-8.238257],[29.002912,-8.407032],[28.734867,-8.526559],[28.449871,-9.164918],[28.673682,-9.605925],[28.49607,-10.789884],[28.372253,-11.793647],[28.642417,-11.971569],[29.341548,-12.360744],[29.616001,-12.178895],[29.699614,-13.257227],[28.934286,-13.248958],[28.523562,-12.698604],[28.155109,-12.272481],[27.388799,-12.132747],[27.16442,-11.608748],[26.553088,-11.92444],[25.75231,-11.784965],[25.418118,-11.330936],[24.78317,-11.238694],[24.314516,-11.262826],[24.257155,-10.951993],[23.912215,-10.926826],[23.456791,-10.867863],[22.837345,-11.017622],[22.402798,-10.993075],[22.155268,-11.084801],[22.208753,-9.894796],[21.875182,-9.523708],[21.801801,-8.908707],[21.949131,-8.305901],[21.746456,-7.920085],[21.728111,-7.290872],[20.514748,-7.299606],[20.601823,-6.939318],[20.091622,-6.94309],[20.037723,-7.116361],[19.417502,-7.155429],[19.166613,-7.738184],[19.016752,-7.988246],[18.464176,-7.847014],[18.134222,-7.987678],[17.47297,-8.068551],[17.089996,-7.545689],[16.860191,-7.222298],[16.57318,-6.622645],[16.326528,-5.87747],[13.375597,-5.864241],[13.024869,-5.984389],[12.735171,-5.965682],[12.322432,-6.100092],[12.182337,-5.789931],[12.436688,-5.684304],[12.468004,-5.248362],[12.631612,-4.991271],[12.995517,-4.781103],[13.25824,-4.882957],[13.600235,-4.500138],[14.144956,-4.510009],[14.209035,-4.793092],[14.582604,-4.970239],[15.170992,-4.343507],[15.75354,-3.855165],[16.00629,-3.535133],[15.972803,-2.712392],[16.407092,-1.740927],[16.865307,-1.225816],[17.523716,-.74383],[17.638645,-.424832],[17.663553,-.058084],[17.82654,.288923],[17.774192,.855659],[17.898835,1.741832],[18.094276,2.365722],[18.393792,2.900443],[18.453065,3.504386],[18.542982,4.201785],[18.932312,4.709506],[19.467784,5.031528],[20.290679,4.691678],[20.927591,4.322786],[21.659123,4.224342],[22.405124,4.02916],[22.704124,4.633051],[22.84148,4.710126],[23.297214,4.609693],[24.410531,5.108784],[24.805029,4.897247],[25.128833,4.927245],[25.278798,5.170408],[25.650455,5.256088],[26.402761,5.150875],[27.044065,5.127853],[27.374226,5.233944],[27.979977,4.408413],[28.428994,4.287155],[28.696678,4.455077],[29.159078,4.389267],[29.715995,4.600805],[29.9535,4.173699],[30.83386,3.509166]]]}},{type:"Feature",id:"COG",properties:{name:"Republic of the Congo",iso_a2:"CG"},geometry:{type:"Polygon",coordinates:[[[12.995517,-4.781103],[12.62076,-4.438023],[12.318608,-4.60623],[11.914963,-5.037987],[11.093773,-3.978827],[11.855122,-3.426871],[11.478039,-2.765619],[11.820964,-2.514161],[12.495703,-2.391688],[12.575284,-1.948511],[13.109619,-2.42874],[13.992407,-2.470805],[14.29921,-1.998276],[14.425456,-1.333407],[14.316418,-.552627],[13.843321,.038758],[14.276266,1.19693],[14.026669,1.395677],[13.282631,1.314184],[13.003114,1.830896],[13.075822,2.267097],[14.337813,2.227875],[15.146342,1.964015],[15.940919,1.727673],[16.012852,2.26764],[16.537058,3.198255],[17.133042,3.728197],[17.8099,3.560196],[18.453065,3.504386],[18.393792,2.900443],[18.094276,2.365722],[17.898835,1.741832],[17.774192,.855659],[17.82654,.288923],[17.663553,-.058084],[17.638645,-.424832],[17.523716,-.74383],[16.865307,-1.225816],[16.407092,-1.740927],[15.972803,-2.712392],[16.00629,-3.535133],[15.75354,-3.855165],[15.170992,-4.343507],[14.582604,-4.970239],[14.209035,-4.793092],[14.144956,-4.510009],[13.600235,-4.500138],[13.25824,-4.882957],[12.995517,-4.781103]]]}},{type:"Feature",id:"COL",properties:{name:"Colombia",iso_a2:"CO"},geometry:{type:"Polygon",coordinates:[[[-75.373223,-.152032],[-75.801466,.084801],[-76.292314,.416047],[-76.57638,.256936],[-77.424984,.395687],[-77.668613,.825893],[-77.855061,.809925],[-78.855259,1.380924],[-78.990935,1.69137],[-78.617831,1.766404],[-78.662118,2.267355],[-78.42761,2.629556],[-77.931543,2.696606],[-77.510431,3.325017],[-77.12769,3.849636],[-77.496272,4.087606],[-77.307601,4.667984],[-77.533221,5.582812],[-77.318815,5.845354],[-77.476661,6.691116],[-77.881571,7.223771],[-77.753414,7.70984],[-77.431108,7.638061],[-77.242566,7.935278],[-77.474723,8.524286],[-77.353361,8.670505],[-76.836674,8.638749],[-76.086384,9.336821],[-75.6746,9.443248],[-75.664704,9.774003],[-75.480426,10.61899],[-74.906895,11.083045],[-74.276753,11.102036],[-74.197223,11.310473],[-73.414764,11.227015],[-72.627835,11.731972],[-72.238195,11.95555],[-71.75409,12.437303],[-71.399822,12.376041],[-71.137461,12.112982],[-71.331584,11.776284],[-71.973922,11.608672],[-72.227575,11.108702],[-72.614658,10.821975],[-72.905286,10.450344],[-73.027604,9.73677],[-73.304952,9.152],[-72.78873,9.085027],[-72.660495,8.625288],[-72.439862,8.405275],[-72.360901,8.002638],[-72.479679,7.632506],[-72.444487,7.423785],[-72.198352,7.340431],[-71.960176,6.991615],[-70.674234,7.087785],[-70.093313,6.960376],[-69.38948,6.099861],[-68.985319,6.206805],[-68.265052,6.153268],[-67.695087,6.267318],[-67.34144,6.095468],[-67.521532,5.55687],[-67.744697,5.221129],[-67.823012,4.503937],[-67.621836,3.839482],[-67.337564,3.542342],[-67.303173,3.318454],[-67.809938,2.820655],[-67.447092,2.600281],[-67.181294,2.250638],[-66.876326,1.253361],[-67.065048,1.130112],[-67.259998,1.719999],[-67.53781,2.037163],[-67.868565,1.692455],[-69.816973,1.714805],[-69.804597,1.089081],[-69.218638,.985677],[-69.252434,.602651],[-69.452396,.706159],[-70.015566,.541414],[-70.020656,-.185156],[-69.577065,-.549992],[-69.420486,-1.122619],[-69.444102,-1.556287],[-69.893635,-4.298187],[-70.394044,-3.766591],[-70.692682,-3.742872],[-70.047709,-2.725156],[-70.813476,-2.256865],[-71.413646,-2.342802],[-71.774761,-2.16979],[-72.325787,-2.434218],[-73.070392,-2.308954],[-73.659504,-1.260491],[-74.122395,-1.002833],[-74.441601,-.53082],[-75.106625,-.057205],[-75.373223,-.152032]]]}},{type:"Feature",id:"CRI",properties:{name:"Costa Rica",iso_a2:"CR"},geometry:{type:"Polygon",coordinates:[[[-82.965783,8.225028],[-83.508437,8.446927],[-83.711474,8.656836],[-83.596313,8.830443],[-83.632642,9.051386],[-83.909886,9.290803],[-84.303402,9.487354],[-84.647644,9.615537],[-84.713351,9.908052],[-84.97566,10.086723],[-84.911375,9.795992],[-85.110923,9.55704],[-85.339488,9.834542],[-85.660787,9.933347],[-85.797445,10.134886],[-85.791709,10.439337],[-85.659314,10.754331],[-85.941725,10.895278],[-85.71254,11.088445],[-85.561852,11.217119],[-84.903003,10.952303],[-84.673069,11.082657],[-84.355931,10.999226],[-84.190179,10.79345],[-83.895054,10.726839],[-83.655612,10.938764],[-83.40232,10.395438],[-83.015677,9.992982],[-82.546196,9.566135],[-82.932891,9.476812],[-82.927155,9.07433],[-82.719183,8.925709],[-82.868657,8.807266],[-82.829771,8.626295],[-82.913176,8.423517],[-82.965783,8.225028]]]}},{type:"Feature",id:"CUB",properties:{name:"Cuba",iso_a2:"CU"},geometry:{type:"Polygon",coordinates:[[[-82.268151,23.188611],[-81.404457,23.117271],[-80.618769,23.10598],[-79.679524,22.765303],[-79.281486,22.399202],[-78.347434,22.512166],[-77.993296,22.277194],[-77.146422,21.657851],[-76.523825,21.20682],[-76.19462,21.220565],[-75.598222,21.016624],[-75.67106,20.735091],[-74.933896,20.693905],[-74.178025,20.284628],[-74.296648,20.050379],[-74.961595,19.923435],[-75.63468,19.873774],[-76.323656,19.952891],[-77.755481,19.855481],[-77.085108,20.413354],[-77.492655,20.673105],[-78.137292,20.739949],[-78.482827,21.028613],[-78.719867,21.598114],[-79.285,21.559175],[-80.217475,21.827324],[-80.517535,22.037079],[-81.820943,22.192057],[-82.169992,22.387109],[-81.795002,22.636965],[-82.775898,22.68815],[-83.494459,22.168518],[-83.9088,22.154565],[-84.052151,21.910575],[-84.54703,21.801228],[-84.974911,21.896028],[-84.447062,22.20495],[-84.230357,22.565755],[-83.77824,22.788118],[-83.267548,22.983042],[-82.510436,23.078747],[-82.268151,23.188611]]]}},{type:"Feature",id:"-99",properties:{name:"Northern Cyprus",iso_a2:"XX"},geometry:{type:"Polygon",coordinates:[[[32.73178,35.140026],[32.802474,35.145504],[32.946961,35.386703],[33.667227,35.373216],[34.576474,35.671596],[33.900804,35.245756],[33.973617,35.058506],[33.86644,35.093595],[33.675392,35.017863],[33.525685,35.038688],[33.475817,35.000345],[33.455922,35.101424],[33.383833,35.162712],[33.190977,35.173125],[32.919572,35.087833],[32.73178,35.140026]]]}},{type:"Feature",id:"CYP",properties:{name:"Cyprus",iso_a2:"CY"},geometry:{type:"Polygon",coordinates:[[[33.973617,35.058506],[34.004881,34.978098],[32.979827,34.571869],[32.490296,34.701655],[32.256667,35.103232],[32.73178,35.140026],[32.919572,35.087833],[33.190977,35.173125],[33.383833,35.162712],[33.455922,35.101424],[33.475817,35.000345],[33.525685,35.038688],[33.675392,35.017863],[33.86644,35.093595],[33.973617,35.058506]]]}},{type:"Feature",id:"CZE",properties:{name:"Czech Republic",iso_a2:"CZ"},geometry:{type:"Polygon",coordinates:[[[16.960288,48.596982],[16.499283,48.785808],[16.029647,48.733899],[15.253416,49.039074],[14.901447,48.964402],[14.338898,48.555305],[13.595946,48.877172],[13.031329,49.307068],[12.521024,49.547415],[12.415191,49.969121],[12.240111,50.266338],[12.966837,50.484076],[13.338132,50.733234],[14.056228,50.926918],[14.307013,51.117268],[14.570718,51.002339],[15.016996,51.106674],[15.490972,50.78473],[16.238627,50.697733],[16.176253,50.422607],[16.719476,50.215747],[16.868769,50.473974],[17.554567,50.362146],[17.649445,50.049038],[18.392914,49.988629],[18.853144,49.49623],[18.554971,49.495015],[18.399994,49.315001],[18.170498,49.271515],[18.104973,49.043983],[17.913512,48.996493],[17.886485,48.903475],[17.545007,48.800019],[17.101985,48.816969],[16.960288,48.596982]]]}},{type:"Feature",id:"DEU",properties:{name:"Germany",iso_a2:"DE"},geometry:{type:"Polygon",coordinates:[[[9.921906,54.983104],[9.93958,54.596642],[10.950112,54.363607],[10.939467,54.008693],[11.956252,54.196486],[12.51844,54.470371],[13.647467,54.075511],[14.119686,53.757029],[14.353315,53.248171],[14.074521,52.981263],[14.4376,52.62485],[14.685026,52.089947],[14.607098,51.745188],[15.016996,51.106674],[14.570718,51.002339],[14.307013,51.117268],[14.056228,50.926918],[13.338132,50.733234],[12.966837,50.484076],[12.240111,50.266338],[12.415191,49.969121],[12.521024,49.547415],[13.031329,49.307068],[13.595946,48.877172],[13.243357,48.416115],[12.884103,48.289146],[13.025851,47.637584],[12.932627,47.467646],[12.62076,47.672388],[12.141357,47.703083],[11.426414,47.523766],[10.544504,47.566399],[10.402084,47.302488],[9.896068,47.580197],[9.594226,47.525058],[8.522612,47.830828],[8.317301,47.61358],[7.466759,47.620582],[7.593676,48.333019],[8.099279,49.017784],[6.65823,49.201958],[6.18632,49.463803],[6.242751,49.902226],[6.043073,50.128052],[6.156658,50.803721],[5.988658,51.851616],[6.589397,51.852029],[6.84287,52.22844],[7.092053,53.144043],[6.90514,53.482162],[7.100425,53.693932],[7.936239,53.748296],[8.121706,53.527792],[8.800734,54.020786],[8.572118,54.395646],[8.526229,54.962744],[9.282049,54.830865],[9.921906,54.983104]]]}},{type:"Feature",id:"DJI",properties:{name:"Djibouti",iso_a2:"DJ"},geometry:{type:"Polygon",coordinates:[[[43.081226,12.699639],[43.317852,12.390148],[43.286381,11.974928],[42.715874,11.735641],[43.145305,11.46204],[42.776852,10.926879],[42.55493,11.10511],[42.31414,11.0342],[41.75557,11.05091],[41.73959,11.35511],[41.66176,11.6312],[42,12.1],[42.35156,12.54223],[42.779642,12.455416],[43.081226,12.699639]]]}},{type:"Feature",id:"DNK",properties:{name:"Denmark",iso_a2:"DK"},geometry:{type:"MultiPolygon",coordinates:[[[[12.690006,55.609991],[12.089991,54.800015],[11.043543,55.364864],[10.903914,55.779955],[12.370904,56.111407],[12.690006,55.609991]]],[[[10.912182,56.458621],[10.667804,56.081383],[10.369993,56.190007],[9.649985,55.469999],[9.921906,54.983104],[9.282049,54.830865],[8.526229,54.962744],[8.120311,55.517723],[8.089977,56.540012],[8.256582,56.809969],[8.543438,57.110003],[9.424469,57.172066],[9.775559,57.447941],[10.580006,57.730017],[10.546106,57.215733],[10.25,56.890016],[10.369993,56.609982],[10.912182,56.458621]]]]}},{type:"Feature",id:"DOM",properties:{name:"Dominican Republic",iso_a2:"DO"},geometry:{type:"Polygon",coordinates:[[[-71.712361,19.714456],[-71.587304,19.884911],[-70.806706,19.880286],[-70.214365,19.622885],[-69.950815,19.648],[-69.76925,19.293267],[-69.222126,19.313214],[-69.254346,19.015196],[-68.809412,18.979074],[-68.317943,18.612198],[-68.689316,18.205142],[-69.164946,18.422648],[-69.623988,18.380713],[-69.952934,18.428307],[-70.133233,18.245915],[-70.517137,18.184291],[-70.669298,18.426886],[-70.99995,18.283329],[-71.40021,17.598564],[-71.657662,17.757573],[-71.708305,18.044997],[-71.687738,18.31666],[-71.945112,18.6169],[-71.701303,18.785417],[-71.624873,19.169838],[-71.712361,19.714456]]]}},{type:"Feature",id:"DZA",properties:{name:"Algeria",iso_a2:"DZ"},geometry:{type:"Polygon",coordinates:[[[11.999506,23.471668],[8.572893,21.565661],[5.677566,19.601207],[4.267419,19.155265],[3.158133,19.057364],[3.146661,19.693579],[2.683588,19.85623],[2.060991,20.142233],[1.823228,20.610809],[-1.550055,22.792666],[-4.923337,24.974574],[-8.6844,27.395744],[-8.665124,27.589479],[-8.66559,27.656426],[-8.674116,28.841289],[-7.059228,29.579228],[-6.060632,29.7317],[-5.242129,30.000443],[-4.859646,30.501188],[-3.690441,30.896952],[-3.647498,31.637294],[-3.06898,31.724498],[-2.616605,32.094346],[-1.307899,32.262889],[-1.124551,32.651522],[-1.388049,32.864015],[-1.733455,33.919713],[-1.792986,34.527919],[-2.169914,35.168396],[-1.208603,35.714849],[-.127454,35.888662],[.503877,36.301273],[1.466919,36.605647],[3.161699,36.783905],[4.815758,36.865037],[5.32012,36.716519],[6.26182,37.110655],[7.330385,37.118381],[7.737078,36.885708],[8.420964,36.946427],[8.217824,36.433177],[8.376368,35.479876],[8.140981,34.655146],[7.524482,34.097376],[7.612642,33.344115],[8.430473,32.748337],[8.439103,32.506285],[9.055603,32.102692],[9.48214,30.307556],[9.805634,29.424638],[9.859998,28.95999],[9.683885,28.144174],[9.756128,27.688259],[9.629056,27.140953],[9.716286,26.512206],[9.319411,26.094325],[9.910693,25.365455],[9.948261,24.936954],[10.303847,24.379313],[10.771364,24.562532],[11.560669,24.097909],[11.999506,23.471668]]]}},{type:"Feature",id:"ECU",properties:{name:"Ecuador",iso_a2:"EC"},geometry:{type:"Polygon",coordinates:[[[-80.302561,-3.404856],[-79.770293,-2.657512],[-79.986559,-2.220794],[-80.368784,-2.685159],[-80.967765,-2.246943],[-80.764806,-1.965048],[-80.933659,-1.057455],[-80.58337,-.906663],[-80.399325,-.283703],[-80.020898,.36034],[-80.09061,.768429],[-79.542762,.982938],[-78.855259,1.380924],[-77.855061,.809925],[-77.668613,.825893],[-77.424984,.395687],[-76.57638,.256936],[-76.292314,.416047],[-75.801466,.084801],[-75.373223,-.152032],[-75.233723,-.911417],[-75.544996,-1.56161],[-76.635394,-2.608678],[-77.837905,-3.003021],[-78.450684,-3.873097],[-78.639897,-4.547784],[-79.205289,-4.959129],[-79.624979,-4.454198],[-80.028908,-4.346091],[-80.442242,-4.425724],[-80.469295,-4.059287],[-80.184015,-3.821162],[-80.302561,-3.404856]]]}},{type:"Feature",id:"EGY",properties:{name:"Egypt",iso_a2:"EG"},geometry:{type:"Polygon",coordinates:[[[34.9226,29.50133],[34.64174,29.09942],[34.42655,28.34399],[34.15451,27.8233],[33.92136,27.6487],[33.58811,27.97136],[33.13676,28.41765],[32.42323,29.85108],[32.32046,29.76043],[32.73482,28.70523],[33.34876,27.69989],[34.10455,26.14227],[34.47387,25.59856],[34.79507,25.03375],[35.69241,23.92671],[35.49372,23.75237],[35.52598,23.10244],[36.69069,22.20485],[36.86623,22],[32.9,22],[29.02,22],[25,22],[25,25.6825],[25,29.238655],[24.70007,30.04419],[24.95762,30.6616],[24.80287,31.08929],[25.16482,31.56915],[26.49533,31.58568],[27.45762,31.32126],[28.45048,31.02577],[28.91353,30.87005],[29.68342,31.18686],[30.09503,31.4734],[30.97693,31.55586],[31.68796,31.4296],[31.96041,30.9336],[32.19247,31.26034],[32.99392,31.02407],[33.7734,30.96746],[34.26544,31.21936],[34.9226,29.50133]]]}},{type:"Feature",id:"ERI",properties:{name:"Eritrea",iso_a2:"ER"},geometry:{type:"Polygon",coordinates:[[[42.35156,12.54223],[42.00975,12.86582],[41.59856,13.45209],[41.155194,13.77332],[40.8966,14.11864],[40.026219,14.519579],[39.34061,14.53155],[39.0994,14.74064],[38.51295,14.50547],[37.90607,14.95943],[37.59377,14.2131],[36.42951,14.42211],[36.323189,14.822481],[36.75386,16.291874],[36.85253,16.95655],[37.16747,17.26314],[37.904,17.42754],[38.41009,17.998307],[38.990623,16.840626],[39.26611,15.922723],[39.814294,15.435647],[41.179275,14.49108],[41.734952,13.921037],[42.276831,13.343992],[42.589576,13.000421],[43.081226,12.699639],[42.779642,12.455416],[42.35156,12.54223]]]}},{type:"Feature",id:"ESP",properties:{name:"Spain",iso_a2:"ES"},geometry:{type:"Polygon",coordinates:[[[-9.034818,41.880571],[-8.984433,42.592775],[-9.392884,43.026625],[-7.97819,43.748338],[-6.754492,43.567909],[-5.411886,43.57424],[-4.347843,43.403449],[-3.517532,43.455901],[-1.901351,43.422802],[-1.502771,43.034014],[.338047,42.579546],[.701591,42.795734],[1.826793,42.343385],[2.985999,42.473015],[3.039484,41.89212],[2.091842,41.226089],[.810525,41.014732],[.721331,40.678318],[.106692,40.123934],[-.278711,39.309978],[.111291,38.738514],[-.467124,38.292366],[-.683389,37.642354],[-1.438382,37.443064],[-2.146453,36.674144],[-3.415781,36.6589],[-4.368901,36.677839],[-4.995219,36.324708],[-5.37716,35.94685],[-5.866432,36.029817],[-6.236694,36.367677],[-6.520191,36.942913],[-7.453726,37.097788],[-7.537105,37.428904],[-7.166508,37.803894],[-7.029281,38.075764],[-7.374092,38.373059],[-7.098037,39.030073],[-7.498632,39.629571],[-7.066592,39.711892],[-7.026413,40.184524],[-6.86402,40.330872],[-6.851127,41.111083],[-6.389088,41.381815],[-6.668606,41.883387],[-7.251309,41.918346],[-7.422513,41.792075],[-8.013175,41.790886],[-8.263857,42.280469],[-8.671946,42.134689],[-9.034818,41.880571]]]}},{type:"Feature",id:"EST",properties:{name:"Estonia",iso_a2:"EE"},geometry:{type:"Polygon",coordinates:[[[24.312863,57.793424],[24.428928,58.383413],[24.061198,58.257375],[23.42656,58.612753],[23.339795,59.18724],[24.604214,59.465854],[25.864189,59.61109],[26.949136,59.445803],[27.981114,59.475388],[28.131699,59.300825],[27.420166,58.724581],[27.716686,57.791899],[27.288185,57.474528],[26.463532,57.476389],[25.60281,57.847529],[25.164594,57.970157],[24.312863,57.793424]]]}},{type:"Feature",id:"ETH",properties:{name:"Ethiopia",iso_a2:"ET"},geometry:{type:"Polygon",coordinates:[[[37.90607,14.95943],[38.51295,14.50547],[39.0994,14.74064],[39.34061,14.53155],[40.02625,14.51959],[40.8966,14.11864],[41.1552,13.77333],[41.59856,13.45209],[42.00975,12.86582],[42.35156,12.54223],[42,12.1],[41.66176,11.6312],[41.73959,11.35511],[41.75557,11.05091],[42.31414,11.0342],[42.55493,11.10511],[42.776852,10.926879],[42.55876,10.57258],[42.92812,10.02194],[43.29699,9.54048],[43.67875,9.18358],[46.94834,7.99688],[47.78942,8.003],[44.9636,5.00162],[43.66087,4.95755],[42.76967,4.25259],[42.12861,4.23413],[41.855083,3.918912],[41.1718,3.91909],[40.76848,4.25702],[39.85494,3.83879],[39.559384,3.42206],[38.89251,3.50074],[38.67114,3.61607],[38.43697,3.58851],[38.120915,3.598605],[36.855093,4.447864],[36.159079,4.447864],[35.817448,4.776966],[35.817448,5.338232],[35.298007,5.506],[34.70702,6.59422],[34.25032,6.82607],[34.0751,7.22595],[33.56829,7.71334],[32.95418,7.78497],[33.2948,8.35458],[33.8255,8.37916],[33.97498,8.68456],[33.96162,9.58358],[34.25745,10.63009],[34.73115,10.91017],[34.83163,11.31896],[35.26049,12.08286],[35.86363,12.57828],[36.27022,13.56333],[36.42951,14.42211],[37.59377,14.2131],[37.90607,14.95943]]]}},{type:"Feature",id:"FIN",properties:{name:"Finland",iso_a2:"FI"},geometry:{type:"Polygon",coordinates:[[[28.59193,69.064777],[28.445944,68.364613],[29.977426,67.698297],[29.054589,66.944286],[30.21765,65.80598],[29.54443,64.948672],[30.444685,64.204453],[30.035872,63.552814],[31.516092,62.867687],[31.139991,62.357693],[30.211107,61.780028],[28.069998,60.503517],[26.255173,60.423961],[24.496624,60.057316],[22.869695,59.846373],[22.290764,60.391921],[21.322244,60.72017],[21.544866,61.705329],[21.059211,62.607393],[21.536029,63.189735],[22.442744,63.81781],[24.730512,64.902344],[25.398068,65.111427],[25.294043,65.534346],[23.903379,66.006927],[23.56588,66.396051],[23.539473,67.936009],[21.978535,68.616846],[20.645593,69.106247],[21.244936,69.370443],[22.356238,68.841741],[23.66205,68.891247],[24.735679,68.649557],[25.689213,69.092114],[26.179622,69.825299],[27.732292,70.164193],[29.015573,69.766491],[28.59193,69.064777]]]}},{type:"Feature",id:"FJI",properties:{name:"Fiji",iso_a2:"FJ"},geometry:{type:"MultiPolygon",coordinates:[[[[178.3736,-17.33992],[178.71806,-17.62846],[178.55271,-18.15059],[177.93266,-18.28799],[177.38146,-18.16432],[177.28504,-17.72465],[177.67087,-17.38114],[178.12557,-17.50481],[178.3736,-17.33992]]],[[[179.364143,-16.801354],[178.725059,-17.012042],[178.596839,-16.63915],[179.096609,-16.433984],[179.413509,-16.379054],[180,-16.067133],[180,-16.555217],[179.364143,-16.801354]]],[[[-179.917369,-16.501783],[-180,-16.555217],[-180,-16.067133],[-179.79332,-16.020882],[-179.917369,-16.501783]]]]}},{type:"Feature",id:"FLK",properties:{name:"Falkland Islands",iso_a2:"FK"},geometry:{type:"Polygon",coordinates:[[[-61.2,-51.85],[-60,-51.25],[-59.15,-51.5],[-58.55,-51.1],[-57.75,-51.55],[-58.05,-51.9],[-59.4,-52.2],[-59.85,-51.85],[-60.7,-52.3],[-61.2,-51.85]]]}},{type:"Feature",id:"FRA",properties:{name:"France",iso_a2:"FR"},geometry:{type:"MultiPolygon",coordinates:[[[[9.560016,42.152492],[9.229752,41.380007],[8.775723,41.583612],[8.544213,42.256517],[8.746009,42.628122],[9.390001,43.009985],[9.560016,42.152492]]],[[[3.588184,50.378992],[4.286023,49.907497],[4.799222,49.985373],[5.674052,49.529484],[5.897759,49.442667],[6.18632,49.463803],[6.65823,49.201958],[8.099279,49.017784],[7.593676,48.333019],[7.466759,47.620582],[7.192202,47.449766],[6.736571,47.541801],[6.768714,47.287708],[6.037389,46.725779],[6.022609,46.27299],[6.5001,46.429673],[6.843593,45.991147],[6.802355,45.70858],[7.096652,45.333099],[6.749955,45.028518],[7.007562,44.254767],[7.549596,44.127901],[7.435185,43.693845],[6.529245,43.128892],[4.556963,43.399651],[3.100411,43.075201],[2.985999,42.473015],[1.826793,42.343385],[.701591,42.795734],[.338047,42.579546],[-1.502771,43.034014],[-1.901351,43.422802],[-1.384225,44.02261],[-1.193798,46.014918],[-2.225724,47.064363],[-2.963276,47.570327],[-4.491555,47.954954],[-4.59235,48.68416],[-3.295814,48.901692],[-1.616511,48.644421],[-1.933494,49.776342],[-.989469,49.347376],[1.338761,50.127173],[1.639001,50.946606],[2.513573,51.148506],[2.658422,50.796848],[3.123252,50.780363],[3.588184,50.378992]]]]}},{type:"Feature",id:"GAB",properties:{name:"Gabon",iso_a2:"GA"},geometry:{type:"Polygon",coordinates:[[[11.093773,-3.978827],[10.066135,-2.969483],[9.405245,-2.144313],[8.797996,-1.111301],[8.830087,-.779074],[9.04842,-.459351],[9.291351,.268666],[9.492889,1.01012],[9.830284,1.067894],[11.285079,1.057662],[11.276449,2.261051],[11.751665,2.326758],[12.35938,2.192812],[12.951334,2.321616],[13.075822,2.267097],[13.003114,1.830896],[13.282631,1.314184],[14.026669,1.395677],[14.276266,1.19693],[13.843321,.038758],[14.316418,-.552627],[14.425456,-1.333407],[14.29921,-1.998276],[13.992407,-2.470805],[13.109619,-2.42874],[12.575284,-1.948511],[12.495703,-2.391688],[11.820964,-2.514161],[11.478039,-2.765619],[11.855122,-3.426871],[11.093773,-3.978827]]]}},{type:"Feature",id:"GBR",properties:{name:"United Kingdom",iso_a2:"GB"},geometry:{type:"MultiPolygon",coordinates:[[[[-5.661949,54.554603],[-6.197885,53.867565],[-6.95373,54.073702],[-7.572168,54.059956],[-7.366031,54.595841],[-7.572168,55.131622],[-6.733847,55.17286],[-5.661949,54.554603]]],[[[-3.005005,58.635],[-4.073828,57.553025],[-3.055002,57.690019],[-1.959281,57.6848],[-2.219988,56.870017],[-3.119003,55.973793],[-2.085009,55.909998],[-2.005676,55.804903],[-1.114991,54.624986],[-.430485,54.464376],[.184981,53.325014],[.469977,52.929999],[1.681531,52.73952],[1.559988,52.099998],[1.050562,51.806761],[1.449865,51.289428],[.550334,50.765739],[-.787517,50.774989],[-2.489998,50.500019],[-2.956274,50.69688],[-3.617448,50.228356],[-4.542508,50.341837],[-5.245023,49.96],[-5.776567,50.159678],[-4.30999,51.210001],[-3.414851,51.426009],[-3.422719,51.426848],[-4.984367,51.593466],[-5.267296,51.9914],[-4.222347,52.301356],[-4.770013,52.840005],[-4.579999,53.495004],[-3.093831,53.404547],[-3.09208,53.404441],[-2.945009,53.985],[-3.614701,54.600937],[-3.630005,54.615013],[-4.844169,54.790971],[-5.082527,55.061601],[-4.719112,55.508473],[-5.047981,55.783986],[-5.586398,55.311146],[-5.644999,56.275015],[-6.149981,56.78501],[-5.786825,57.818848],[-5.009999,58.630013],[-4.211495,58.550845],[-3.005005,58.635]]]]}},{type:"Feature",id:"GEO",properties:{name:"Georgia",iso_a2:"GE"},geometry:{type:"Polygon",coordinates:[[[41.554084,41.535656],[41.703171,41.962943],[41.45347,42.645123],[40.875469,43.013628],[40.321394,43.128634],[39.955009,43.434998],[40.076965,43.553104],[40.922185,43.382159],[42.394395,43.220308],[43.756017,42.740828],[43.9312,42.554974],[44.537623,42.711993],[45.470279,42.502781],[45.77641,42.092444],[46.404951,41.860675],[46.145432,41.722802],[46.637908,41.181673],[46.501637,41.064445],[45.962601,41.123873],[45.217426,41.411452],[44.97248,41.248129],[43.582746,41.092143],[42.619549,41.583173],[41.554084,41.535656]]]}},{type:"Feature",id:"GHA",properties:{name:"Ghana",iso_a2:"GH"},geometry:{type:"Polygon",coordinates:[[[1.060122,5.928837],[-.507638,5.343473],[-1.063625,5.000548],[-1.964707,4.710462],[-2.856125,4.994476],[-2.810701,5.389051],[-3.24437,6.250472],[-2.983585,7.379705],[-2.56219,8.219628],[-2.827496,9.642461],[-2.963896,10.395335],[-2.940409,10.96269],[-1.203358,11.009819],[-.761576,10.93693],[-.438702,11.098341],[.023803,11.018682],[-.049785,10.706918],[.36758,10.191213],[.365901,9.465004],[.461192,8.677223],[.712029,8.312465],[.490957,7.411744],[.570384,6.914359],[.836931,6.279979],[1.060122,5.928837]]]}},{type:"Feature",id:"GIN",properties:{name:"Guinea",iso_a2:"GN"},geometry:{type:"Polygon",coordinates:[[[-8.439298,7.686043],[-8.722124,7.711674],[-8.926065,7.309037],[-9.208786,7.313921],[-9.403348,7.526905],[-9.33728,7.928534],[-9.755342,8.541055],[-10.016567,8.428504],[-10.230094,8.406206],[-10.505477,8.348896],[-10.494315,8.715541],[-10.65477,8.977178],[-10.622395,9.26791],[-10.839152,9.688246],[-11.117481,10.045873],[-11.917277,10.046984],[-12.150338,9.858572],[-12.425929,9.835834],[-12.596719,9.620188],[-12.711958,9.342712],[-13.24655,8.903049],[-13.685154,9.494744],[-14.074045,9.886167],[-14.330076,10.01572],[-14.579699,10.214467],[-14.693232,10.656301],[-14.839554,10.876572],[-15.130311,11.040412],[-14.685687,11.527824],[-14.382192,11.509272],[-14.121406,11.677117],[-13.9008,11.678719],[-13.743161,11.811269],[-13.828272,12.142644],[-13.718744,12.247186],[-13.700476,12.586183],[-13.217818,12.575874],[-12.499051,12.33209],[-12.278599,12.35444],[-12.203565,12.465648],[-11.658301,12.386583],[-11.513943,12.442988],[-11.456169,12.076834],[-11.297574,12.077971],[-11.036556,12.211245],[-10.87083,12.177887],[-10.593224,11.923975],[-10.165214,11.844084],[-9.890993,12.060479],[-9.567912,12.194243],[-9.327616,12.334286],[-9.127474,12.30806],[-8.905265,12.088358],[-8.786099,11.812561],[-8.376305,11.393646],[-8.581305,11.136246],[-8.620321,10.810891],[-8.407311,10.909257],[-8.282357,10.792597],[-8.335377,10.494812],[-8.029944,10.206535],[-8.229337,10.12902],[-8.309616,9.789532],[-8.079114,9.376224],[-7.8321,8.575704],[-8.203499,8.455453],[-8.299049,8.316444],[-8.221792,8.123329],[-8.280703,7.68718],[-8.439298,7.686043]]]}},{type:"Feature",id:"GMB",properties:{name:"Gambia",iso_a2:"GM"},geometry:{type:"Polygon",coordinates:[[[-16.841525,13.151394],[-16.713729,13.594959],[-15.624596,13.623587],[-15.39877,13.860369],[-15.081735,13.876492],[-14.687031,13.630357],[-14.376714,13.62568],[-14.046992,13.794068],[-13.844963,13.505042],[-14.277702,13.280585],[-14.712197,13.298207],[-15.141163,13.509512],[-15.511813,13.27857],[-15.691001,13.270353],[-15.931296,13.130284],[-16.841525,13.151394]]]}},{type:"Feature",id:"GNB",properties:{name:"Guinea Bissau",iso_a2:"GW"},geometry:{type:"Polygon",coordinates:[[[-15.130311,11.040412],[-15.66418,11.458474],[-16.085214,11.524594],[-16.314787,11.806515],[-16.308947,11.958702],[-16.613838,12.170911],[-16.677452,12.384852],[-16.147717,12.547762],[-15.816574,12.515567],[-15.548477,12.62817],[-13.700476,12.586183],[-13.718744,12.247186],[-13.828272,12.142644],[-13.743161,11.811269],[-13.9008,11.678719],[-14.121406,11.677117],[-14.382192,11.509272],[-14.685687,11.527824],[-15.130311,11.040412]]]}},{type:"Feature",id:"GNQ",properties:{name:"Equatorial Guinea",iso_a2:"GQ"},geometry:{type:"Polygon",coordinates:[[[9.492889,1.01012],[9.305613,1.160911],[9.649158,2.283866],[11.276449,2.261051],[11.285079,1.057662],[9.830284,1.067894],[9.492889,1.01012]]]}},{type:"Feature",id:"GRC",properties:{name:"Greece",iso_a2:"GR"},geometry:{type:"MultiPolygon",coordinates:[[[[23.69998,35.705004],[24.246665,35.368022],[25.025015,35.424996],[25.769208,35.354018],[25.745023,35.179998],[26.290003,35.29999],[26.164998,35.004995],[24.724982,34.919988],[24.735007,35.084991],[23.514978,35.279992],[23.69998,35.705004]]],[[[26.604196,41.562115],[26.294602,40.936261],[26.056942,40.824123],[25.447677,40.852545],[24.925848,40.947062],[23.714811,40.687129],[24.407999,40.124993],[23.899968,39.962006],[23.342999,39.960998],[22.813988,40.476005],[22.626299,40.256561],[22.849748,39.659311],[23.350027,39.190011],[22.973099,38.970903],[23.530016,38.510001],[24.025025,38.219993],[24.040011,37.655015],[23.115003,37.920011],[23.409972,37.409991],[22.774972,37.30501],[23.154225,36.422506],[22.490028,36.41],[21.670026,36.844986],[21.295011,37.644989],[21.120034,38.310323],[20.730032,38.769985],[20.217712,39.340235],[20.150016,39.624998],[20.615,40.110007],[20.674997,40.435],[20.99999,40.580004],[21.02004,40.842727],[21.674161,40.931275],[22.055378,41.149866],[22.597308,41.130487],[22.76177,41.3048],[22.952377,41.337994],[23.692074,41.309081],[24.492645,41.583896],[25.197201,41.234486],[26.106138,41.328899],[26.117042,41.826905],[26.604196,41.562115]]]]}},{type:"Feature",id:"GRL",properties:{name:"Greenland",iso_a2:"GL"},geometry:{type:"Polygon",coordinates:[[[-46.76379,82.62796],[-43.40644,83.22516],[-39.89753,83.18018],[-38.62214,83.54905],[-35.08787,83.64513],[-27.10046,83.51966],[-20.84539,82.72669],[-22.69182,82.34165],[-26.51753,82.29765],[-31.9,82.2],[-31.39646,82.02154],[-27.85666,82.13178],[-24.84448,81.78697],[-22.90328,82.09317],[-22.07175,81.73449],[-23.16961,81.15271],[-20.62363,81.52462],[-15.76818,81.91245],[-12.77018,81.71885],[-12.20855,81.29154],[-16.28533,80.58004],[-16.85,80.35],[-20.04624,80.17708],[-17.73035,80.12912],[-18.9,79.4],[-19.70499,78.75128],[-19.67353,77.63859],[-18.47285,76.98565],[-20.03503,76.94434],[-21.67944,76.62795],[-19.83407,76.09808],[-19.59896,75.24838],[-20.66818,75.15585],[-19.37281,74.29561],[-21.59422,74.22382],[-20.43454,73.81713],[-20.76234,73.46436],[-22.17221,73.30955],[-23.56593,73.30663],[-22.31311,72.62928],[-22.29954,72.18409],[-24.27834,72.59788],[-24.79296,72.3302],[-23.44296,72.08016],[-22.13281,71.46898],[-21.75356,70.66369],[-23.53603,70.471],[-24.30702,70.85649],[-25.54341,71.43094],[-25.20135,70.75226],[-26.36276,70.22646],[-23.72742,70.18401],[-22.34902,70.12946],[-25.02927,69.2588],[-27.74737,68.47046],[-30.67371,68.12503],[-31.77665,68.12078],[-32.81105,67.73547],[-34.20196,66.67974],[-36.35284,65.9789],[-37.04378,65.93768],[-38.37505,65.69213],[-39.81222,65.45848],[-40.66899,64.83997],[-40.68281,64.13902],[-41.1887,63.48246],[-42.81938,62.68233],[-42.41666,61.90093],[-42.86619,61.07404],[-43.3784,60.09772],[-44.7875,60.03676],[-46.26364,60.85328],[-48.26294,60.85843],[-49.23308,61.40681],[-49.90039,62.38336],[-51.63325,63.62691],[-52.14014,64.27842],[-52.27659,65.1767],[-53.66166,66.09957],[-53.30161,66.8365],[-53.96911,67.18899],[-52.9804,68.35759],[-51.47536,68.72958],[-51.08041,69.14781],[-50.87122,69.9291],[-52.013585,69.574925],[-52.55792,69.42616],[-53.45629,69.283625],[-54.68336,69.61003],[-54.75001,70.28932],[-54.35884,70.821315],[-53.431315,70.835755],[-51.39014,70.56978],[-53.10937,71.20485],[-54.00422,71.54719],[-55,71.406537],[-55.83468,71.65444],[-54.71819,72.58625],[-55.32634,72.95861],[-56.12003,73.64977],[-57.32363,74.71026],[-58.59679,75.09861],[-58.58516,75.51727],[-61.26861,76.10238],[-63.39165,76.1752],[-66.06427,76.13486],[-68.50438,76.06141],[-69.66485,76.37975],[-71.40257,77.00857],[-68.77671,77.32312],[-66.76397,77.37595],[-71.04293,77.63595],[-73.297,78.04419],[-73.15938,78.43271],[-69.37345,78.91388],[-65.7107,79.39436],[-65.3239,79.75814],[-68.02298,80.11721],[-67.15129,80.51582],[-63.68925,81.21396],[-62.23444,81.3211],[-62.65116,81.77042],[-60.28249,82.03363],[-57.20744,82.19074],[-54.13442,82.19962],[-53.04328,81.88833],[-50.39061,82.43883],[-48.00386,82.06481],[-46.59984,81.985945],[-44.523,81.6607],[-46.9007,82.19979],[-46.76379,82.62796]]]}},{type:"Feature",id:"GTM",properties:{name:"Guatemala",iso_a2:"GT"},geometry:{type:"Polygon",coordinates:[[[-90.095555,13.735338],[-90.608624,13.909771],[-91.23241,13.927832],[-91.689747,14.126218],[-92.22775,14.538829],[-92.20323,14.830103],[-92.087216,15.064585],[-92.229249,15.251447],[-91.74796,16.066565],[-90.464473,16.069562],[-90.438867,16.41011],[-90.600847,16.470778],[-90.711822,16.687483],[-91.08167,16.918477],[-91.453921,17.252177],[-91.002269,17.254658],[-91.00152,17.817595],[-90.067934,17.819326],[-89.14308,17.808319],[-89.150806,17.015577],[-89.229122,15.886938],[-88.930613,15.887273],[-88.604586,15.70638],[-88.518364,15.855389],[-88.225023,15.727722],[-88.68068,15.346247],[-89.154811,15.066419],[-89.22522,14.874286],[-89.145535,14.678019],[-89.353326,14.424133],[-89.587343,14.362586],[-89.534219,14.244816],[-89.721934,14.134228],[-90.064678,13.88197],[-90.095555,13.735338]]]}},{type:"Feature",id:"GUF",properties:{name:"French Guiana",iso_a2:"GF"},geometry:{type:"Polygon",coordinates:[[[-52.556425,2.504705],[-52.939657,2.124858],[-53.418465,2.053389],[-53.554839,2.334897],[-53.778521,2.376703],[-54.088063,2.105557],[-54.524754,2.311849],[-54.27123,2.738748],[-54.184284,3.194172],[-54.011504,3.62257],[-54.399542,4.212611],[-54.478633,4.896756],[-53.958045,5.756548],[-53.618453,5.646529],[-52.882141,5.409851],[-51.823343,4.565768],[-51.657797,4.156232],[-52.249338,3.241094],[-52.556425,2.504705]]]}},{type:"Feature",id:"GUY",properties:{name:"Guyana",iso_a2:"GY"},geometry:{type:"Polygon",coordinates:[[[-59.758285,8.367035],[-59.101684,7.999202],[-58.482962,7.347691],[-58.454876,6.832787],[-58.078103,6.809094],[-57.542219,6.321268],[-57.147436,5.97315],[-57.307246,5.073567],[-57.914289,4.812626],[-57.86021,4.576801],[-58.044694,4.060864],[-57.601569,3.334655],[-57.281433,3.333492],[-57.150098,2.768927],[-56.539386,1.899523],[-56.782704,1.863711],[-57.335823,1.948538],[-57.660971,1.682585],[-58.11345,1.507195],[-58.429477,1.463942],[-58.540013,1.268088],[-59.030862,1.317698],[-59.646044,1.786894],[-59.718546,2.24963],[-59.974525,2.755233],[-59.815413,3.606499],[-59.53804,3.958803],[-59.767406,4.423503],[-60.111002,4.574967],[-59.980959,5.014061],[-60.213683,5.244486],[-60.733574,5.200277],[-61.410303,5.959068],[-61.139415,6.234297],[-61.159336,6.696077],[-60.543999,6.856584],[-60.295668,7.043911],[-60.637973,7.415],[-60.550588,7.779603],[-59.758285,8.367035]]]}},{type:"Feature",id:"HND",properties:{name:"Honduras",iso_a2:"HN"},geometry:{type:"Polygon",coordinates:[[[-87.316654,12.984686],[-87.489409,13.297535],[-87.793111,13.38448],[-87.723503,13.78505],[-87.859515,13.893312],[-88.065343,13.964626],[-88.503998,13.845486],[-88.541231,13.980155],[-88.843073,14.140507],[-89.058512,14.340029],[-89.353326,14.424133],[-89.145535,14.678019],[-89.22522,14.874286],[-89.154811,15.066419],[-88.68068,15.346247],[-88.225023,15.727722],[-88.121153,15.688655],[-87.901813,15.864458],[-87.61568,15.878799],[-87.522921,15.797279],[-87.367762,15.84694],[-86.903191,15.756713],[-86.440946,15.782835],[-86.119234,15.893449],[-86.001954,16.005406],[-85.683317,15.953652],[-85.444004,15.885749],[-85.182444,15.909158],[-84.983722,15.995923],[-84.52698,15.857224],[-84.368256,15.835158],[-84.063055,15.648244],[-83.773977,15.424072],[-83.410381,15.270903],[-83.147219,14.995829],[-83.489989,15.016267],[-83.628585,14.880074],[-83.975721,14.749436],[-84.228342,14.748764],[-84.449336,14.621614],[-84.649582,14.666805],[-84.820037,14.819587],[-84.924501,14.790493],[-85.052787,14.551541],[-85.148751,14.560197],[-85.165365,14.35437],[-85.514413,14.079012],[-85.698665,13.960078],[-85.801295,13.836055],[-86.096264,14.038187],[-86.312142,13.771356],[-86.520708,13.778487],[-86.755087,13.754845],[-86.733822,13.263093],[-86.880557,13.254204],[-87.005769,13.025794],[-87.316654,12.984686]]]}},{type:"Feature",id:"HRV",properties:{name:"Croatia",iso_a2:"HR"},geometry:{type:"Polygon",coordinates:[[[18.829838,45.908878],[19.072769,45.521511],[19.390476,45.236516],[19.005486,44.860234],[18.553214,45.08159],[17.861783,45.06774],[17.002146,45.233777],[16.534939,45.211608],[16.318157,45.004127],[15.959367,45.233777],[15.750026,44.818712],[16.23966,44.351143],[16.456443,44.04124],[16.916156,43.667722],[17.297373,43.446341],[17.674922,43.028563],[18.56,42.65],[18.450016,42.479991],[17.50997,42.849995],[16.930006,43.209998],[16.015385,43.507215],[15.174454,44.243191],[15.37625,44.317915],[14.920309,44.738484],[14.901602,45.07606],[14.258748,45.233777],[13.952255,44.802124],[13.656976,45.136935],[13.679403,45.484149],[13.71506,45.500324],[14.411968,45.466166],[14.595109,45.634941],[14.935244,45.471695],[15.327675,45.452316],[15.323954,45.731783],[15.67153,45.834154],[15.768733,46.238108],[16.564808,46.503751],[16.882515,46.380632],[17.630066,45.951769],[18.456062,45.759481],[18.829838,45.908878]]]}},{type:"Feature",id:"HTI",properties:{name:"Haiti",iso_a2:"HT"},geometry:{type:"Polygon",coordinates:[[[-73.189791,19.915684],[-72.579673,19.871501],[-71.712361,19.714456],[-71.624873,19.169838],[-71.701303,18.785417],[-71.945112,18.6169],[-71.687738,18.31666],[-71.708305,18.044997],[-72.372476,18.214961],[-72.844411,18.145611],[-73.454555,18.217906],[-73.922433,18.030993],[-74.458034,18.34255],[-74.369925,18.664908],[-73.449542,18.526053],[-72.694937,18.445799],[-72.334882,18.668422],[-72.79165,19.101625],[-72.784105,19.483591],[-73.415022,19.639551],[-73.189791,19.915684]]]}},{type:"Feature",id:"HUN",properties:{name:"Hungary",iso_a2:"HU"},geometry:{type:"Polygon",coordinates:[[[16.202298,46.852386],[16.534268,47.496171],[16.340584,47.712902],[16.903754,47.714866],[16.979667,48.123497],[17.488473,47.867466],[17.857133,47.758429],[18.696513,47.880954],[18.777025,48.081768],[19.174365,48.111379],[19.661364,48.266615],[19.769471,48.202691],[20.239054,48.327567],[20.473562,48.56285],[20.801294,48.623854],[21.872236,48.319971],[22.085608,48.422264],[22.64082,48.15024],[22.710531,47.882194],[22.099768,47.672439],[21.626515,46.994238],[21.021952,46.316088],[20.220192,46.127469],[19.596045,46.17173],[18.829838,45.908878],[18.456062,45.759481],[17.630066,45.951769],[16.882515,46.380632],[16.564808,46.503751],[16.370505,46.841327],[16.202298,46.852386]]]}},{type:"Feature",id:"IDN",properties:{name:"Indonesia",iso_a2:"ID"},geometry:{type:"MultiPolygon",coordinates:[[[[120.715609,-10.239581],[120.295014,-10.25865],[118.967808,-9.557969],[119.90031,-9.36134],[120.425756,-9.665921],[120.775502,-9.969675],[120.715609,-10.239581]]],[[[124.43595,-10.140001],[123.579982,-10.359987],[123.459989,-10.239995],[123.550009,-9.900016],[123.980009,-9.290027],[124.968682,-8.89279],[125.07002,-9.089987],[125.08852,-9.393173],[124.43595,-10.140001]]],[[[117.900018,-8.095681],[118.260616,-8.362383],[118.87846,-8.280683],[119.126507,-8.705825],[117.970402,-8.906639],[117.277731,-9.040895],[116.740141,-9.032937],[117.083737,-8.457158],[117.632024,-8.449303],[117.900018,-8.095681]]],[[[122.903537,-8.094234],[122.756983,-8.649808],[121.254491,-8.933666],[119.924391,-8.810418],[119.920929,-8.444859],[120.715092,-8.236965],[121.341669,-8.53674],[122.007365,-8.46062],[122.903537,-8.094234]]],[[[108.623479,-6.777674],[110.539227,-6.877358],[110.759576,-6.465186],[112.614811,-6.946036],[112.978768,-7.594213],[114.478935,-7.776528],[115.705527,-8.370807],[114.564511,-8.751817],[113.464734,-8.348947],[112.559672,-8.376181],[111.522061,-8.302129],[110.58615,-8.122605],[109.427667,-7.740664],[108.693655,-7.6416],[108.277763,-7.766657],[106.454102,-7.3549],[106.280624,-6.9249],[105.365486,-6.851416],[106.051646,-5.895919],[107.265009,-5.954985],[108.072091,-6.345762],[108.486846,-6.421985],[108.623479,-6.777674]]],[[[134.724624,-6.214401],[134.210134,-6.895238],[134.112776,-6.142467],[134.290336,-5.783058],[134.499625,-5.445042],[134.727002,-5.737582],[134.724624,-6.214401]]],[[[127.249215,-3.459065],[126.874923,-3.790983],[126.183802,-3.607376],[125.989034,-3.177273],[127.000651,-3.129318],[127.249215,-3.459065]]],[[[130.471344,-3.093764],[130.834836,-3.858472],[129.990547,-3.446301],[129.155249,-3.362637],[128.590684,-3.428679],[127.898891,-3.393436],[128.135879,-2.84365],[129.370998,-2.802154],[130.471344,-3.093764]]],[[[134.143368,-1.151867],[134.422627,-2.769185],[135.457603,-3.367753],[136.293314,-2.307042],[137.440738,-1.703513],[138.329727,-1.702686],[139.184921,-2.051296],[139.926684,-2.409052],[141.00021,-2.600151],[141.017057,-5.859022],[141.033852,-9.117893],[140.143415,-8.297168],[139.127767,-8.096043],[138.881477,-8.380935],[137.614474,-8.411683],[138.039099,-7.597882],[138.668621,-7.320225],[138.407914,-6.232849],[137.92784,-5.393366],[135.98925,-4.546544],[135.164598,-4.462931],[133.66288,-3.538853],[133.367705,-4.024819],[132.983956,-4.112979],[132.756941,-3.746283],[132.753789,-3.311787],[131.989804,-2.820551],[133.066845,-2.460418],[133.780031,-2.479848],[133.696212,-2.214542],[132.232373,-2.212526],[131.836222,-1.617162],[130.94284,-1.432522],[130.519558,-.93772],[131.867538,-.695461],[132.380116,-.369538],[133.985548,-.78021],[134.143368,-1.151867]]],[[[125.240501,1.419836],[124.437035,.427881],[123.685505,.235593],[122.723083,.431137],[121.056725,.381217],[120.183083,.237247],[120.04087,-.519658],[120.935905,-1.408906],[121.475821,-.955962],[123.340565,-.615673],[123.258399,-1.076213],[122.822715,-.930951],[122.38853,-1.516858],[121.508274,-1.904483],[122.454572,-3.186058],[122.271896,-3.5295],[123.170963,-4.683693],[123.162333,-5.340604],[122.628515,-5.634591],[122.236394,-5.282933],[122.719569,-4.464172],[121.738234,-4.851331],[121.489463,-4.574553],[121.619171,-4.188478],[120.898182,-3.602105],[120.972389,-2.627643],[120.305453,-2.931604],[120.390047,-4.097579],[120.430717,-5.528241],[119.796543,-5.6734],[119.366906,-5.379878],[119.653606,-4.459417],[119.498835,-3.494412],[119.078344,-3.487022],[118.767769,-2.801999],[119.180974,-2.147104],[119.323394,-1.353147],[119.825999,.154254],[120.035702,.566477],[120.885779,1.309223],[121.666817,1.013944],[122.927567,.875192],[124.077522,.917102],[125.065989,1.643259],[125.240501,1.419836]]],[[[128.688249,1.132386],[128.635952,.258486],[128.12017,.356413],[127.968034,-.252077],[128.379999,-.780004],[128.100016,-.899996],[127.696475,-.266598],[127.39949,1.011722],[127.600512,1.810691],[127.932378,2.174596],[128.004156,1.628531],[128.594559,1.540811],[128.688249,1.132386]]],[[[117.875627,1.827641],[118.996747,.902219],[117.811858,.784242],[117.478339,.102475],[117.521644,-.803723],[116.560048,-1.487661],[116.533797,-2.483517],[116.148084,-4.012726],[116.000858,-3.657037],[114.864803,-4.106984],[114.468652,-3.495704],[113.755672,-3.43917],[113.256994,-3.118776],[112.068126,-3.478392],[111.703291,-2.994442],[111.04824,-3.049426],[110.223846,-2.934032],[110.070936,-1.592874],[109.571948,-1.314907],[109.091874,-.459507],[108.952658,.415375],[109.069136,1.341934],[109.66326,2.006467],[109.830227,1.338136],[110.514061,.773131],[111.159138,.976478],[111.797548,.904441],[112.380252,1.410121],[112.859809,1.49779],[113.80585,1.217549],[114.621355,1.430688],[115.134037,2.821482],[115.519078,3.169238],[115.865517,4.306559],[117.015214,4.306094],[117.882035,4.137551],[117.313232,3.234428],[118.04833,2.28769],[117.875627,1.827641]]],[[[105.817655,-5.852356],[104.710384,-5.873285],[103.868213,-5.037315],[102.584261,-4.220259],[102.156173,-3.614146],[101.399113,-2.799777],[100.902503,-2.050262],[100.141981,-.650348],[99.26374,.183142],[98.970011,1.042882],[98.601351,1.823507],[97.699598,2.453184],[97.176942,3.308791],[96.424017,3.86886],[95.380876,4.970782],[95.293026,5.479821],[95.936863,5.439513],[97.484882,5.246321],[98.369169,4.26837],[99.142559,3.59035],[99.693998,3.174329],[100.641434,2.099381],[101.658012,2.083697],[102.498271,1.3987],[103.07684,.561361],[103.838396,.104542],[103.437645,-.711946],[104.010789,-1.059212],[104.369991,-1.084843],[104.53949,-1.782372],[104.887893,-2.340425],[105.622111,-2.428844],[106.108593,-3.061777],[105.857446,-4.305525],[105.817655,-5.852356]]]]}},{type:"Feature",id:"IND",properties:{name:"India",iso_a2:"IN"},geometry:{type:"Polygon",coordinates:[[[77.837451,35.49401],[78.912269,34.321936],[78.811086,33.506198],[79.208892,32.994395],[79.176129,32.48378],[78.458446,32.618164],[78.738894,31.515906],[79.721367,30.882715],[81.111256,30.183481],[80.476721,29.729865],[80.088425,28.79447],[81.057203,28.416095],[81.999987,27.925479],[83.304249,27.364506],[84.675018,27.234901],[85.251779,26.726198],[86.024393,26.630985],[87.227472,26.397898],[88.060238,26.414615],[88.174804,26.810405],[88.043133,27.445819],[88.120441,27.876542],[88.730326,28.086865],[88.814248,27.299316],[88.835643,27.098966],[89.744528,26.719403],[90.373275,26.875724],[91.217513,26.808648],[92.033484,26.83831],[92.103712,27.452614],[91.696657,27.771742],[92.503119,27.896876],[93.413348,28.640629],[94.56599,29.277438],[95.404802,29.031717],[96.117679,29.452802],[96.586591,28.83098],[96.248833,28.411031],[97.327114,28.261583],[97.402561,27.882536],[97.051989,27.699059],[97.133999,27.083774],[96.419366,27.264589],[95.124768,26.573572],[95.155153,26.001307],[94.603249,25.162495],[94.552658,24.675238],[94.106742,23.850741],[93.325188,24.078556],[93.286327,23.043658],[93.060294,22.703111],[93.166128,22.27846],[92.672721,22.041239],[92.146035,23.627499],[91.869928,23.624346],[91.706475,22.985264],[91.158963,23.503527],[91.46773,24.072639],[91.915093,24.130414],[92.376202,24.976693],[91.799596,25.147432],[90.872211,25.132601],[89.920693,25.26975],[89.832481,25.965082],[89.355094,26.014407],[88.563049,26.446526],[88.209789,25.768066],[88.931554,25.238692],[88.306373,24.866079],[88.084422,24.501657],[88.69994,24.233715],[88.52977,23.631142],[88.876312,22.879146],[89.031961,22.055708],[88.888766,21.690588],[88.208497,21.703172],[86.975704,21.495562],[87.033169,20.743308],[86.499351,20.151638],[85.060266,19.478579],[83.941006,18.30201],[83.189217,17.671221],[82.192792,17.016636],[82.191242,16.556664],[81.692719,16.310219],[80.791999,15.951972],[80.324896,15.899185],[80.025069,15.136415],[80.233274,13.835771],[80.286294,13.006261],[79.862547,12.056215],[79.857999,10.357275],[79.340512,10.308854],[78.885345,9.546136],[79.18972,9.216544],[78.277941,8.933047],[77.941165,8.252959],[77.539898,7.965535],[76.592979,8.899276],[76.130061,10.29963],[75.746467,11.308251],[75.396101,11.781245],[74.864816,12.741936],[74.616717,13.992583],[74.443859,14.617222],[73.534199,15.990652],[73.119909,17.92857],[72.820909,19.208234],[72.824475,20.419503],[72.630533,21.356009],[71.175273,20.757441],[70.470459,20.877331],[69.16413,22.089298],[69.644928,22.450775],[69.349597,22.84318],[68.176645,23.691965],[68.842599,24.359134],[71.04324,24.356524],[70.844699,25.215102],[70.282873,25.722229],[70.168927,26.491872],[69.514393,26.940966],[70.616496,27.989196],[71.777666,27.91318],[72.823752,28.961592],[73.450638,29.976413],[74.42138,30.979815],[74.405929,31.692639],[75.258642,32.271105],[74.451559,32.7649],[74.104294,33.441473],[73.749948,34.317699],[74.240203,34.748887],[75.757061,34.504923],[76.871722,34.653544],[77.837451,35.49401]]]}},{type:"Feature",id:"IRL",properties:{name:"Ireland",iso_a2:"IE"},geometry:{type:"Polygon",coordinates:[[[-6.197885,53.867565],[-6.032985,53.153164],[-6.788857,52.260118],[-8.561617,51.669301],[-9.977086,51.820455],[-9.166283,52.864629],[-9.688525,53.881363],[-8.327987,54.664519],[-7.572168,55.131622],[-7.366031,54.595841],[-7.572168,54.059956],[-6.95373,54.073702],[-6.197885,53.867565]]]}},{type:"Feature",id:"IRN",properties:{name:"Iran",iso_a2:"IR"},geometry:{type:"Polygon",coordinates:[[[53.921598,37.198918],[54.800304,37.392421],[55.511578,37.964117],[56.180375,37.935127],[56.619366,38.121394],[57.330434,38.029229],[58.436154,37.522309],[59.234762,37.412988],[60.377638,36.527383],[61.123071,36.491597],[61.210817,35.650072],[60.803193,34.404102],[60.52843,33.676446],[60.9637,33.528832],[60.536078,32.981269],[60.863655,32.18292],[60.941945,31.548075],[61.699314,31.379506],[61.781222,30.73585],[60.874248,29.829239],[61.369309,29.303276],[61.771868,28.699334],[62.72783,28.259645],[62.755426,27.378923],[63.233898,27.217047],[63.316632,26.756532],[61.874187,26.239975],[61.497363,25.078237],[59.616134,25.380157],[58.525761,25.609962],[57.397251,25.739902],[56.970766,26.966106],[56.492139,27.143305],[55.72371,26.964633],[54.71509,26.480658],[53.493097,26.812369],[52.483598,27.580849],[51.520763,27.86569],[50.852948,28.814521],[50.115009,30.147773],[49.57685,29.985715],[48.941333,30.31709],[48.567971,29.926778],[48.014568,30.452457],[48.004698,30.985137],[47.685286,30.984853],[47.849204,31.709176],[47.334661,32.469155],[46.109362,33.017287],[45.416691,33.967798],[45.64846,34.748138],[46.151788,35.093259],[46.07634,35.677383],[45.420618,35.977546],[44.77267,37.17045],[44.225756,37.971584],[44.421403,38.281281],[44.109225,39.428136],[44.79399,39.713003],[44.952688,39.335765],[45.457722,38.874139],[46.143623,38.741201],[46.50572,38.770605],[47.685079,39.508364],[48.060095,39.582235],[48.355529,39.288765],[48.010744,38.794015],[48.634375,38.270378],[48.883249,38.320245],[49.199612,37.582874],[50.147771,37.374567],[50.842354,36.872814],[52.264025,36.700422],[53.82579,36.965031],[53.921598,37.198918]]]}},{type:"Feature",id:"IRQ",properties:{name:"Iraq",iso_a2:"IQ"},geometry:{type:"Polygon",coordinates:[[[45.420618,35.977546],[46.07634,35.677383],[46.151788,35.093259],[45.64846,34.748138],[45.416691,33.967798],[46.109362,33.017287],[47.334661,32.469155],[47.849204,31.709176],[47.685286,30.984853],[48.004698,30.985137],[48.014568,30.452457],[48.567971,29.926778],[47.974519,29.975819],[47.302622,30.05907],[46.568713,29.099025],[44.709499,29.178891],[41.889981,31.190009],[40.399994,31.889992],[39.195468,32.161009],[38.792341,33.378686],[41.006159,34.419372],[41.383965,35.628317],[41.289707,36.358815],[41.837064,36.605854],[42.349591,37.229873],[42.779126,37.385264],[43.942259,37.256228],[44.293452,37.001514],[44.772699,37.170445],[45.420618,35.977546]]]}},{type:"Feature",id:"ISL",properties:{name:"Iceland",iso_a2:"IS"},geometry:{type:"Polygon",coordinates:[[[-14.508695,66.455892],[-14.739637,65.808748],[-13.609732,65.126671],[-14.909834,64.364082],[-17.794438,63.678749],[-18.656246,63.496383],[-19.972755,63.643635],[-22.762972,63.960179],[-21.778484,64.402116],[-23.955044,64.89113],[-22.184403,65.084968],[-22.227423,65.378594],[-24.326184,65.611189],[-23.650515,66.262519],[-22.134922,66.410469],[-20.576284,65.732112],[-19.056842,66.276601],[-17.798624,65.993853],[-16.167819,66.526792],[-14.508695,66.455892]]]}},{type:"Feature",id:"ISR",properties:{name:"Israel",iso_a2:"IL"},geometry:{type:"Polygon",coordinates:[[[35.719918,32.709192],[35.545665,32.393992],[35.18393,32.532511],[34.974641,31.866582],[35.225892,31.754341],[34.970507,31.616778],[34.927408,31.353435],[35.397561,31.489086],[35.420918,31.100066],[34.922603,29.501326],[34.265433,31.219361],[34.556372,31.548824],[34.488107,31.605539],[34.752587,32.072926],[34.955417,32.827376],[35.098457,33.080539],[35.126053,33.0909],[35.460709,33.08904],[35.552797,33.264275],[35.821101,33.277426],[35.836397,32.868123],[35.700798,32.716014],[35.719918,32.709192]]]}},{type:"Feature",id:"ITA",properties:{name:"Italy",iso_a2:"IT"},geometry:{type:"MultiPolygon",coordinates:[[[[15.520376,38.231155],[15.160243,37.444046],[15.309898,37.134219],[15.099988,36.619987],[14.335229,36.996631],[13.826733,37.104531],[12.431004,37.61295],[12.570944,38.126381],[13.741156,38.034966],[14.761249,38.143874],[15.520376,38.231155]]],[[[9.210012,41.209991],[9.809975,40.500009],[9.669519,39.177376],[9.214818,39.240473],[8.806936,38.906618],[8.428302,39.171847],[8.388253,40.378311],[8.159998,40.950007],[8.709991,40.899984],[9.210012,41.209991]]],[[[12.376485,46.767559],[13.806475,46.509306],[13.69811,46.016778],[13.93763,45.591016],[13.141606,45.736692],[12.328581,45.381778],[12.383875,44.885374],[12.261453,44.600482],[12.589237,44.091366],[13.526906,43.587727],[14.029821,42.761008],[15.14257,41.95514],[15.926191,41.961315],[16.169897,41.740295],[15.889346,41.541082],[16.785002,41.179606],[17.519169,40.877143],[18.376687,40.355625],[18.480247,40.168866],[18.293385,39.810774],[17.73838,40.277671],[16.869596,40.442235],[16.448743,39.795401],[17.17149,39.4247],[17.052841,38.902871],[16.635088,38.843572],[16.100961,37.985899],[15.684087,37.908849],[15.687963,38.214593],[15.891981,38.750942],[16.109332,38.964547],[15.718814,39.544072],[15.413613,40.048357],[14.998496,40.172949],[14.703268,40.60455],[14.060672,40.786348],[13.627985,41.188287],[12.888082,41.25309],[12.106683,41.704535],[11.191906,42.355425],[10.511948,42.931463],[10.200029,43.920007],[9.702488,44.036279],[8.888946,44.366336],[8.428561,44.231228],[7.850767,43.767148],[7.435185,43.693845],[7.549596,44.127901],[7.007562,44.254767],[6.749955,45.028518],[7.096652,45.333099],[6.802355,45.70858],[6.843593,45.991147],[7.273851,45.776948],[7.755992,45.82449],[8.31663,46.163642],[8.489952,46.005151],[8.966306,46.036932],[9.182882,46.440215],[9.922837,46.314899],[10.363378,46.483571],[10.442701,46.893546],[11.048556,46.751359],[11.164828,46.941579],[12.153088,47.115393],[12.376485,46.767559]]]]}},{type:"Feature",id:"JAM",properties:{name:"Jamaica",iso_a2:"JM"},geometry:{type:"Polygon",coordinates:[[[-77.569601,18.490525],[-76.896619,18.400867],[-76.365359,18.160701],[-76.199659,17.886867],[-76.902561,17.868238],[-77.206341,17.701116],[-77.766023,17.861597],[-78.337719,18.225968],[-78.217727,18.454533],[-77.797365,18.524218],[-77.569601,18.490525]]]}},{type:"Feature",id:"JOR",properties:{name:"Jordan",iso_a2:"JO"},geometry:{type:"Polygon",coordinates:[[[35.545665,32.393992],[35.719918,32.709192],[36.834062,32.312938],[38.792341,33.378686],[39.195468,32.161009],[39.004886,32.010217],[37.002166,31.508413],[37.998849,30.5085],[37.66812,30.338665],[37.503582,30.003776],[36.740528,29.865283],[36.501214,29.505254],[36.068941,29.197495],[34.956037,29.356555],[34.922603,29.501326],[35.420918,31.100066],[35.397561,31.489086],[35.545252,31.782505],[35.545665,32.393992]]]}},{type:"Feature",id:"JPN",properties:{name:"Japan",iso_a2:"JP"},geometry:{type:"MultiPolygon",coordinates:[[[[134.638428,34.149234],[134.766379,33.806335],[134.203416,33.201178],[133.79295,33.521985],[133.280268,33.28957],[133.014858,32.704567],[132.363115,32.989382],[132.371176,33.463642],[132.924373,34.060299],[133.492968,33.944621],[133.904106,34.364931],[134.638428,34.149234]]],[[[140.976388,37.142074],[140.59977,36.343983],[140.774074,35.842877],[140.253279,35.138114],[138.975528,34.6676],[137.217599,34.606286],[135.792983,33.464805],[135.120983,33.849071],[135.079435,34.596545],[133.340316,34.375938],[132.156771,33.904933],[130.986145,33.885761],[132.000036,33.149992],[131.33279,31.450355],[130.686318,31.029579],[130.20242,31.418238],[130.447676,32.319475],[129.814692,32.61031],[129.408463,33.296056],[130.353935,33.604151],[130.878451,34.232743],[131.884229,34.749714],[132.617673,35.433393],[134.608301,35.731618],[135.677538,35.527134],[136.723831,37.304984],[137.390612,36.827391],[138.857602,37.827485],[139.426405,38.215962],[140.05479,39.438807],[139.883379,40.563312],[140.305783,41.195005],[141.368973,41.37856],[141.914263,39.991616],[141.884601,39.180865],[140.959489,38.174001],[140.976388,37.142074]]],[[[143.910162,44.1741],[144.613427,43.960883],[145.320825,44.384733],[145.543137,43.262088],[144.059662,42.988358],[143.18385,41.995215],[141.611491,42.678791],[141.067286,41.584594],[139.955106,41.569556],[139.817544,42.563759],[140.312087,43.333273],[141.380549,43.388825],[141.671952,44.772125],[141.967645,45.551483],[143.14287,44.510358],[143.910162,44.1741]]]]}},{type:"Feature",id:"KAZ",properties:{name:"Kazakhstan",iso_a2:"KZ"},geometry:{type:"Polygon",coordinates:[[[70.962315,42.266154],[70.388965,42.081308],[69.070027,41.384244],[68.632483,40.668681],[68.259896,40.662325],[67.985856,41.135991],[66.714047,41.168444],[66.510649,41.987644],[66.023392,41.994646],[66.098012,42.99766],[64.900824,43.728081],[63.185787,43.650075],[62.0133,43.504477],[61.05832,44.405817],[60.239972,44.784037],[58.689989,45.500014],[58.503127,45.586804],[55.928917,44.995858],[55.968191,41.308642],[55.455251,41.259859],[54.755345,42.043971],[54.079418,42.324109],[52.944293,42.116034],[52.50246,41.783316],[52.446339,42.027151],[52.692112,42.443895],[52.501426,42.792298],[51.342427,43.132975],[50.891292,44.031034],[50.339129,44.284016],[50.305643,44.609836],[51.278503,44.514854],[51.316899,45.245998],[52.16739,45.408391],[53.040876,45.259047],[53.220866,46.234646],[53.042737,46.853006],[52.042023,46.804637],[51.191945,47.048705],[50.034083,46.60899],[49.10116,46.39933],[48.593241,46.561034],[48.694734,47.075628],[48.057253,47.743753],[47.315231,47.715847],[46.466446,48.394152],[47.043672,49.152039],[46.751596,49.356006],[47.54948,50.454698],[48.577841,49.87476],[48.702382,50.605128],[50.766648,51.692762],[52.328724,51.718652],[54.532878,51.02624],[55.716941,50.621717],[56.777961,51.043551],[58.363291,51.063653],[59.642282,50.545442],[59.932807,50.842194],[61.337424,50.79907],[61.588003,51.272659],[59.967534,51.96042],[60.927269,52.447548],[60.739993,52.719986],[61.699986,52.979996],[60.978066,53.664993],[61.436591,54.006265],[65.178534,54.354228],[65.666876,54.601267],[68.1691,54.970392],[69.068167,55.38525],[70.865267,55.169734],[71.180131,54.133285],[72.22415,54.376655],[73.508516,54.035617],[73.425679,53.48981],[74.384845,53.546861],[76.8911,54.490524],[76.525179,54.177003],[77.800916,53.404415],[80.03556,50.864751],[80.568447,51.388336],[81.945986,50.812196],[83.383004,51.069183],[83.935115,50.889246],[84.416377,50.3114],[85.11556,50.117303],[85.54127,49.692859],[86.829357,49.826675],[87.35997,49.214981],[86.598776,48.549182],[85.768233,48.455751],[85.720484,47.452969],[85.16429,47.000956],[83.180484,47.330031],[82.458926,45.53965],[81.947071,45.317027],[79.966106,44.917517],[80.866206,43.180362],[80.18015,42.920068],[80.25999,42.349999],[79.643645,42.496683],[79.142177,42.856092],[77.658392,42.960686],[76.000354,42.988022],[75.636965,42.8779],[74.212866,43.298339],[73.645304,43.091272],[73.489758,42.500894],[71.844638,42.845395],[71.186281,42.704293],[70.962315,42.266154]]]}},{type:"Feature",id:"KEN",properties:{name:"Kenya",iso_a2:"KE"},geometry:{type:"Polygon",coordinates:[[[40.993,-.85829],[41.58513,-1.68325],[40.88477,-2.08255],[40.63785,-2.49979],[40.26304,-2.57309],[40.12119,-3.27768],[39.80006,-3.68116],[39.60489,-4.34653],[39.20222,-4.67677],[37.7669,-3.67712],[37.69869,-3.09699],[34.07262,-1.05982],[33.903711,-.95],[33.893569,.109814],[34.18,.515],[34.6721,1.17694],[35.03599,1.90584],[34.59607,3.05374],[34.47913,3.5556],[34.005,4.249885],[34.620196,4.847123],[35.298007,5.506],[35.817448,5.338232],[35.817448,4.776966],[36.159079,4.447864],[36.855093,4.447864],[38.120915,3.598605],[38.43697,3.58851],[38.67114,3.61607],[38.89251,3.50074],[39.559384,3.42206],[39.85494,3.83879],[40.76848,4.25702],[41.1718,3.91909],[41.855083,3.918912],[40.98105,2.78452],[40.993,-.85829]]]}},{type:"Feature",id:"KGZ",properties:{name:"Kyrgyzstan",iso_a2:"KG"},geometry:{type:"Polygon",coordinates:[[[70.962315,42.266154],[71.186281,42.704293],[71.844638,42.845395],[73.489758,42.500894],[73.645304,43.091272],[74.212866,43.298339],[75.636965,42.8779],[76.000354,42.988022],[77.658392,42.960686],[79.142177,42.856092],[79.643645,42.496683],[80.25999,42.349999],[80.11943,42.123941],[78.543661,41.582243],[78.187197,41.185316],[76.904484,41.066486],[76.526368,40.427946],[75.467828,40.562072],[74.776862,40.366425],[73.822244,39.893973],[73.960013,39.660008],[73.675379,39.431237],[71.784694,39.279463],[70.549162,39.604198],[69.464887,39.526683],[69.55961,40.103211],[70.648019,39.935754],[71.014198,40.244366],[71.774875,40.145844],[73.055417,40.866033],[71.870115,41.3929],[71.157859,41.143587],[70.420022,41.519998],[71.259248,42.167711],[70.962315,42.266154]]]}},{type:"Feature",id:"KHM",properties:{name:"Cambodia",iso_a2:"KH"},geometry:{type:"Polygon",coordinates:[[[103.49728,10.632555],[103.09069,11.153661],[102.584932,12.186595],[102.348099,13.394247],[102.988422,14.225721],[104.281418,14.416743],[105.218777,14.273212],[106.043946,13.881091],[106.496373,14.570584],[107.382727,14.202441],[107.614548,13.535531],[107.491403,12.337206],[105.810524,11.567615],[106.24967,10.961812],[105.199915,10.88931],[104.334335,10.486544],[103.49728,10.632555]]]}},{type:"Feature",id:"KOR",properties:{name:"South Korea",iso_a2:"KR"},geometry:{type:"Polygon",coordinates:[[[128.349716,38.612243],[129.21292,37.432392],[129.46045,36.784189],[129.468304,35.632141],[129.091377,35.082484],[128.18585,34.890377],[127.386519,34.475674],[126.485748,34.390046],[126.37392,34.93456],[126.559231,35.684541],[126.117398,36.725485],[126.860143,36.893924],[126.174759,37.749686],[126.237339,37.840378],[126.68372,37.804773],[127.073309,38.256115],[127.780035,38.304536],[128.205746,38.370397],[128.349716,38.612243]]]}},{type:"Feature",id:"CS-KM",properties:{name:"Kosovo",iso_a2:"KM"},geometry:{type:"Polygon",coordinates:[[[20.76216,42.05186],[20.71731,41.84711],[20.59023,41.85541],[20.52295,42.21787],[20.28374,42.32025],[20.0707,42.58863],[20.25758,42.81275],[20.49679,42.88469],[20.63508,43.21671],[20.81448,43.27205],[20.95651,43.13094],[21.143395,43.068685],[21.27421,42.90959],[21.43866,42.86255],[21.63302,42.67717],[21.77505,42.6827],[21.66292,42.43922],[21.54332,42.32025],[21.576636,42.245224],[21.3527,42.2068],[20.76216,42.05186]]]}},{type:"Feature",id:"KWT",properties:{name:"Kuwait",iso_a2:"KW"},geometry:{type:"Polygon",coordinates:[[[47.974519,29.975819],[48.183189,29.534477],[48.093943,29.306299],[48.416094,28.552004],[47.708851,28.526063],[47.459822,29.002519],[46.568713,29.099025],[47.302622,30.05907],[47.974519,29.975819]]]}},{type:"Feature",id:"LAO",properties:{name:"Laos",iso_a2:"LA"},geometry:{type:"Polygon",coordinates:[[[105.218777,14.273212],[105.544338,14.723934],[105.589039,15.570316],[104.779321,16.441865],[104.716947,17.428859],[103.956477,18.240954],[103.200192,18.309632],[102.998706,17.961695],[102.413005,17.932782],[102.113592,18.109102],[101.059548,17.512497],[101.035931,18.408928],[101.282015,19.462585],[100.606294,19.508344],[100.548881,20.109238],[100.115988,20.41785],[100.329101,20.786122],[101.180005,21.436573],[101.270026,21.201652],[101.80312,21.174367],[101.652018,22.318199],[102.170436,22.464753],[102.754896,21.675137],[103.203861,20.766562],[104.435,20.758733],[104.822574,19.886642],[104.183388,19.624668],[103.896532,19.265181],[105.094598,18.666975],[105.925762,17.485315],[106.556008,16.604284],[107.312706,15.908538],[107.564525,15.202173],[107.382727,14.202441],[106.496373,14.570584],[106.043946,13.881091],[105.218777,14.273212]]]}},{type:"Feature",id:"LBN",properties:{name:"Lebanon",iso_a2:"LB"},geometry:{type:"Polygon",coordinates:[[[35.821101,33.277426],[35.552797,33.264275],[35.460709,33.08904],[35.126053,33.0909],[35.482207,33.90545],[35.979592,34.610058],[35.998403,34.644914],[36.448194,34.593935],[36.61175,34.201789],[36.06646,33.824912],[35.821101,33.277426]]]}},{type:"Feature",id:"LBR",properties:{name:"Liberia",iso_a2:"LR"},geometry:{type:"Polygon",coordinates:[[[-7.712159,4.364566],[-7.974107,4.355755],[-9.004794,4.832419],[-9.91342,5.593561],[-10.765384,6.140711],[-11.438779,6.785917],[-11.199802,7.105846],[-11.146704,7.396706],[-10.695595,7.939464],[-10.230094,8.406206],[-10.016567,8.428504],[-9.755342,8.541055],[-9.33728,7.928534],[-9.403348,7.526905],[-9.208786,7.313921],[-8.926065,7.309037],[-8.722124,7.711674],[-8.439298,7.686043],[-8.485446,7.395208],[-8.385452,6.911801],[-8.60288,6.467564],[-8.311348,6.193033],[-7.993693,6.12619],[-7.570153,5.707352],[-7.539715,5.313345],[-7.635368,5.188159],[-7.712159,4.364566]]]}},{type:"Feature",id:"LBY",properties:{name:"Libya",iso_a2:"LY"},geometry:{type:"Polygon",coordinates:[[[14.8513,22.86295],[14.143871,22.491289],[13.581425,23.040506],[11.999506,23.471668],[11.560669,24.097909],[10.771364,24.562532],[10.303847,24.379313],[9.948261,24.936954],[9.910693,25.365455],[9.319411,26.094325],[9.716286,26.512206],[9.629056,27.140953],[9.756128,27.688259],[9.683885,28.144174],[9.859998,28.95999],[9.805634,29.424638],[9.48214,30.307556],[9.970017,30.539325],[10.056575,30.961831],[9.950225,31.37607],[10.636901,31.761421],[10.94479,32.081815],[11.432253,32.368903],[11.488787,33.136996],[12.66331,32.79278],[13.08326,32.87882],[13.91868,32.71196],[15.24563,32.26508],[15.71394,31.37626],[16.61162,31.18218],[18.02109,30.76357],[19.08641,30.26639],[19.57404,30.52582],[20.05335,30.98576],[19.82033,31.75179],[20.13397,32.2382],[20.85452,32.7068],[21.54298,32.8432],[22.89576,32.63858],[23.2368,32.19149],[23.60913,32.18726],[23.9275,32.01667],[24.92114,31.89936],[25.16482,31.56915],[24.80287,31.08929],[24.95762,30.6616],[24.70007,30.04419],[25,29.238655],[25,25.6825],[25,22],[25,20.00304],[23.85,20],[23.83766,19.58047],[19.84926,21.49509],[15.86085,23.40972],[14.8513,22.86295]]]}},{type:"Feature",id:"LKA",properties:{name:"Sri Lanka",iso_a2:"LK"},geometry:{type:"Polygon",coordinates:[[[81.787959,7.523055],[81.637322,6.481775],[81.21802,6.197141],[80.348357,5.96837],[79.872469,6.763463],[79.695167,8.200843],[80.147801,9.824078],[80.838818,9.268427],[81.304319,8.564206],[81.787959,7.523055]]]}},{type:"Feature",id:"LSO",properties:{name:"Lesotho",iso_a2:"LS"},geometry:{type:"Polygon",coordinates:[[[28.978263,-28.955597],[29.325166,-29.257387],[29.018415,-29.743766],[28.8484,-30.070051],[28.291069,-30.226217],[28.107205,-30.545732],[27.749397,-30.645106],[26.999262,-29.875954],[27.532511,-29.242711],[28.074338,-28.851469],[28.5417,-28.647502],[28.978263,-28.955597]]]}},{type:"Feature",id:"LTU",properties:{name:"Lithuania",iso_a2:"LT"},geometry:{type:"Polygon",coordinates:[[[22.731099,54.327537],[22.651052,54.582741],[22.757764,54.856574],[22.315724,55.015299],[21.268449,55.190482],[21.0558,56.031076],[22.201157,56.337802],[23.878264,56.273671],[24.860684,56.372528],[25.000934,56.164531],[25.533047,56.100297],[26.494331,55.615107],[26.588279,55.167176],[25.768433,54.846963],[25.536354,54.282423],[24.450684,53.905702],[23.484128,53.912498],[23.243987,54.220567],[22.731099,54.327537]]]}},{type:"Feature",id:"LUX",properties:{name:"Luxembourg",iso_a2:"LU"},geometry:{type:"Polygon",coordinates:[[[6.043073,50.128052],[6.242751,49.902226],[6.18632,49.463803],[5.897759,49.442667],[5.674052,49.529484],[5.782417,50.090328],[6.043073,50.128052]]]}},{type:"Feature",id:"LVA",properties:{name:"Latvia",iso_a2:"LV"},geometry:{type:"Polygon",coordinates:[[[21.0558,56.031076],[21.090424,56.783873],[21.581866,57.411871],[22.524341,57.753374],[23.318453,57.006236],[24.12073,57.025693],[24.312863,57.793424],[25.164594,57.970157],[25.60281,57.847529],[26.463532,57.476389],[27.288185,57.474528],[27.770016,57.244258],[27.855282,56.759326],[28.176709,56.16913],[27.10246,55.783314],[26.494331,55.615107],[25.533047,56.100297],[25.000934,56.164531],[24.860684,56.372528],[23.878264,56.273671],[22.201157,56.337802],[21.0558,56.031076]]]}},{type:"Feature",id:"MAR",properties:{name:"Morocco",iso_a2:"MA"},geometry:{type:"Polygon",coordinates:[[[-5.193863,35.755182],[-4.591006,35.330712],[-3.640057,35.399855],[-2.604306,35.179093],[-2.169914,35.168396],[-1.792986,34.527919],[-1.733455,33.919713],[-1.388049,32.864015],[-1.124551,32.651522],[-1.307899,32.262889],[-2.616605,32.094346],[-3.06898,31.724498],[-3.647498,31.637294],[-3.690441,30.896952],[-4.859646,30.501188],[-5.242129,30.000443],[-6.060632,29.7317],[-7.059228,29.579228],[-8.674116,28.841289],[-8.66559,27.656426],[-8.817809,27.656426],[-8.817828,27.656426],[-8.794884,27.120696],[-9.413037,27.088476],[-9.735343,26.860945],[-10.189424,26.860945],[-10.551263,26.990808],[-11.392555,26.883424],[-11.71822,26.104092],[-12.030759,26.030866],[-12.500963,24.770116],[-13.89111,23.691009],[-14.221168,22.310163],[-14.630833,21.86094],[-14.750955,21.5006],[-17.002962,21.420734],[-17.020428,21.42231],[-16.973248,21.885745],[-16.589137,22.158234],[-16.261922,22.67934],[-16.326414,23.017768],[-15.982611,23.723358],[-15.426004,24.359134],[-15.089332,24.520261],[-14.824645,25.103533],[-14.800926,25.636265],[-14.43994,26.254418],[-13.773805,26.618892],[-13.139942,27.640148],[-13.121613,27.654148],[-12.618837,28.038186],[-11.688919,28.148644],[-10.900957,28.832142],[-10.399592,29.098586],[-9.564811,29.933574],[-9.814718,31.177736],[-9.434793,32.038096],[-9.300693,32.564679],[-8.657476,33.240245],[-7.654178,33.697065],[-6.912544,34.110476],[-6.244342,35.145865],[-5.929994,35.759988],[-5.193863,35.755182]]]}},{type:"Feature",id:"MDA",properties:{name:"Moldova",iso_a2:"MD"},geometry:{type:"Polygon",coordinates:[[[26.619337,48.220726],[26.857824,48.368211],[27.522537,48.467119],[28.259547,48.155562],[28.670891,48.118149],[29.122698,47.849095],[29.050868,47.510227],[29.415135,47.346645],[29.559674,46.928583],[29.908852,46.674361],[29.83821,46.525326],[30.024659,46.423937],[29.759972,46.349988],[29.170654,46.379262],[29.072107,46.517678],[28.862972,46.437889],[28.933717,46.25883],[28.659987,45.939987],[28.485269,45.596907],[28.233554,45.488283],[28.054443,45.944586],[28.160018,46.371563],[28.12803,46.810476],[27.551166,47.405117],[27.233873,47.826771],[26.924176,48.123264],[26.619337,48.220726]]]}},{type:"Feature",id:"MDG",properties:{name:"Madagascar",iso_a2:"MG"},geometry:{type:"Polygon",coordinates:[[[49.543519,-12.469833],[49.808981,-12.895285],[50.056511,-13.555761],[50.217431,-14.758789],[50.476537,-15.226512],[50.377111,-15.706069],[50.200275,-16.000263],[49.860606,-15.414253],[49.672607,-15.710204],[49.863344,-16.451037],[49.774564,-16.875042],[49.498612,-17.106036],[49.435619,-17.953064],[49.041792,-19.118781],[48.548541,-20.496888],[47.930749,-22.391501],[47.547723,-23.781959],[47.095761,-24.94163],[46.282478,-25.178463],[45.409508,-25.601434],[44.833574,-25.346101],[44.03972,-24.988345],[43.763768,-24.460677],[43.697778,-23.574116],[43.345654,-22.776904],[43.254187,-22.057413],[43.433298,-21.336475],[43.893683,-21.163307],[43.89637,-20.830459],[44.374325,-20.072366],[44.464397,-19.435454],[44.232422,-18.961995],[44.042976,-18.331387],[43.963084,-17.409945],[44.312469,-16.850496],[44.446517,-16.216219],[44.944937,-16.179374],[45.502732,-15.974373],[45.872994,-15.793454],[46.312243,-15.780018],[46.882183,-15.210182],[47.70513,-14.594303],[48.005215,-14.091233],[47.869047,-13.663869],[48.293828,-13.784068],[48.84506,-13.089175],[48.863509,-12.487868],[49.194651,-12.040557],[49.543519,-12.469833]]]}},{type:"Feature",id:"MEX",properties:{name:"Mexico",iso_a2:"MX"},geometry:{type:"Polygon",coordinates:[[[-97.140008,25.869997],[-97.528072,24.992144],[-97.702946,24.272343],[-97.776042,22.93258],[-97.872367,22.444212],[-97.699044,21.898689],[-97.38896,21.411019],[-97.189333,20.635433],[-96.525576,19.890931],[-96.292127,19.320371],[-95.900885,18.828024],[-94.839063,18.562717],[-94.42573,18.144371],[-93.548651,18.423837],[-92.786114,18.524839],[-92.037348,18.704569],[-91.407903,18.876083],[-90.77187,19.28412],[-90.53359,19.867418],[-90.451476,20.707522],[-90.278618,20.999855],[-89.601321,21.261726],[-88.543866,21.493675],[-87.658417,21.458846],[-87.05189,21.543543],[-86.811982,21.331515],[-86.845908,20.849865],[-87.383291,20.255405],[-87.621054,19.646553],[-87.43675,19.472403],[-87.58656,19.04013],[-87.837191,18.259816],[-88.090664,18.516648],[-88.300031,18.499982],[-88.490123,18.486831],[-88.848344,17.883198],[-89.029857,18.001511],[-89.150909,17.955468],[-89.14308,17.808319],[-90.067934,17.819326],[-91.00152,17.817595],[-91.002269,17.254658],[-91.453921,17.252177],[-91.08167,16.918477],[-90.711822,16.687483],[-90.600847,16.470778],[-90.438867,16.41011],[-90.464473,16.069562],[-91.74796,16.066565],[-92.229249,15.251447],[-92.087216,15.064585],[-92.20323,14.830103],[-92.22775,14.538829],[-93.359464,15.61543],[-93.875169,15.940164],[-94.691656,16.200975],[-95.250227,16.128318],[-96.053382,15.752088],[-96.557434,15.653515],[-97.263592,15.917065],[-98.01303,16.107312],[-98.947676,16.566043],[-99.697397,16.706164],[-100.829499,17.171071],[-101.666089,17.649026],[-101.918528,17.91609],[-102.478132,17.975751],[-103.50099,18.292295],[-103.917527,18.748572],[-104.99201,19.316134],[-105.493038,19.946767],[-105.731396,20.434102],[-105.397773,20.531719],[-105.500661,20.816895],[-105.270752,21.076285],[-105.265817,21.422104],[-105.603161,21.871146],[-105.693414,22.26908],[-106.028716,22.773752],[-106.90998,23.767774],[-107.915449,24.548915],[-108.401905,25.172314],[-109.260199,25.580609],[-109.444089,25.824884],[-109.291644,26.442934],[-109.801458,26.676176],[-110.391732,27.162115],[-110.641019,27.859876],[-111.178919,27.941241],[-111.759607,28.467953],[-112.228235,28.954409],[-112.271824,29.266844],[-112.809594,30.021114],[-113.163811,30.786881],[-113.148669,31.170966],[-113.871881,31.567608],[-114.205737,31.524045],[-114.776451,31.799532],[-114.9367,31.393485],[-114.771232,30.913617],[-114.673899,30.162681],[-114.330974,29.750432],[-113.588875,29.061611],[-113.424053,28.826174],[-113.271969,28.754783],[-113.140039,28.411289],[-112.962298,28.42519],[-112.761587,27.780217],[-112.457911,27.525814],[-112.244952,27.171727],[-111.616489,26.662817],[-111.284675,25.73259],[-110.987819,25.294606],[-110.710007,24.826004],[-110.655049,24.298595],[-110.172856,24.265548],[-109.771847,23.811183],[-109.409104,23.364672],[-109.433392,23.185588],[-109.854219,22.818272],[-110.031392,22.823078],[-110.295071,23.430973],[-110.949501,24.000964],[-111.670568,24.484423],[-112.182036,24.738413],[-112.148989,25.470125],[-112.300711,26.012004],[-112.777297,26.32196],[-113.464671,26.768186],[-113.59673,26.63946],[-113.848937,26.900064],[-114.465747,27.14209],[-115.055142,27.722727],[-114.982253,27.7982],[-114.570366,27.741485],[-114.199329,28.115003],[-114.162018,28.566112],[-114.931842,29.279479],[-115.518654,29.556362],[-115.887365,30.180794],[-116.25835,30.836464],[-116.721526,31.635744],[-117.12776,32.53534],[-115.99135,32.61239],[-114.72139,32.72083],[-114.815,32.52528],[-113.30498,32.03914],[-111.02361,31.33472],[-109.035,31.34194],[-108.24194,31.34222],[-108.24,31.754854],[-106.50759,31.75452],[-106.1429,31.39995],[-105.63159,31.08383],[-105.03737,30.64402],[-104.70575,30.12173],[-104.45697,29.57196],[-103.94,29.27],[-103.11,28.97],[-102.48,29.76],[-101.6624,29.7793],[-100.9576,29.38071],[-100.45584,28.69612],[-100.11,28.11],[-99.52,27.54],[-99.3,26.84],[-99.02,26.37],[-98.24,26.06],[-97.53,25.84],[-97.140008,25.869997]]]}},{type:"Feature",id:"MKD",properties:{name:"Macedonia",iso_a2:"MK"},geometry:{type:"Polygon",coordinates:[[[20.59023,41.85541],[20.71731,41.84711],[20.76216,42.05186],[21.3527,42.2068],[21.576636,42.245224],[21.91708,42.30364],[22.380526,42.32026],[22.881374,41.999297],[22.952377,41.337994],[22.76177,41.3048],[22.597308,41.130487],[22.055378,41.149866],[21.674161,40.931275],[21.02004,40.842727],[20.60518,41.08622],[20.46315,41.51509],[20.59023,41.85541]]]}},{type:"Feature",id:"MLI",properties:{name:"Mali",iso_a2:"ML"},geometry:{type:"Polygon",coordinates:[[[-12.17075,14.616834],[-11.834208,14.799097],[-11.666078,15.388208],[-11.349095,15.411256],[-10.650791,15.132746],[-10.086846,15.330486],[-9.700255,15.264107],[-9.550238,15.486497],[-5.537744,15.50169],[-5.315277,16.201854],[-5.488523,16.325102],[-5.971129,20.640833],[-6.453787,24.956591],[-4.923337,24.974574],[-1.550055,22.792666],[1.823228,20.610809],[2.060991,20.142233],[2.683588,19.85623],[3.146661,19.693579],[3.158133,19.057364],[4.267419,19.155265],[4.27021,16.852227],[3.723422,16.184284],[3.638259,15.56812],[2.749993,15.409525],[1.385528,15.323561],[1.015783,14.968182],[.374892,14.928908],[-.266257,14.924309],[-.515854,15.116158],[-1.066363,14.973815],[-2.001035,14.559008],[-2.191825,14.246418],[-2.967694,13.79815],[-3.103707,13.541267],[-3.522803,13.337662],[-4.006391,13.472485],[-4.280405,13.228444],[-4.427166,12.542646],[-5.220942,11.713859],[-5.197843,11.375146],[-5.470565,10.95127],[-5.404342,10.370737],[-5.816926,10.222555],[-6.050452,10.096361],[-6.205223,10.524061],[-6.493965,10.411303],[-6.666461,10.430811],[-6.850507,10.138994],[-7.622759,10.147236],[-7.89959,10.297382],[-8.029944,10.206535],[-8.335377,10.494812],[-8.282357,10.792597],[-8.407311,10.909257],[-8.620321,10.810891],[-8.581305,11.136246],[-8.376305,11.393646],[-8.786099,11.812561],[-8.905265,12.088358],[-9.127474,12.30806],[-9.327616,12.334286],[-9.567912,12.194243],[-9.890993,12.060479],[-10.165214,11.844084],[-10.593224,11.923975],[-10.87083,12.177887],[-11.036556,12.211245],[-11.297574,12.077971],[-11.456169,12.076834],[-11.513943,12.442988],[-11.467899,12.754519],[-11.553398,13.141214],[-11.927716,13.422075],[-12.124887,13.994727],[-12.17075,14.616834]]]}},{type:"Feature",id:"MLT",properties:{name:"Malta",iso_a2:"MT"},geometry:{type:"MultiPolygon",coordinates:[[[[14.566171,35.852721],[14.532684,35.820191],[14.436463,35.821664],[14.352334,35.872281],[14.3513,35.978399],[14.448348,35.957444],[14.537025,35.886285],[14.566171,35.852721]]],[[[14.313473,36.027569],[14.253632,36.012143],[14.194204,36.042245],[14.180354,36.060383],[14.263243,36.075809],[14.303758,36.062295],[14.320914,36.03625],[14.313473,36.027569]]]]}},{type:"Feature",id:"MMR",properties:{name:"Myanmar",iso_a2:"MM"},geometry:{type:"Polygon",coordinates:[[[99.543309,20.186598],[98.959676,19.752981],[98.253724,19.708203],[97.797783,18.62708],[97.375896,18.445438],[97.859123,17.567946],[98.493761,16.837836],[98.903348,16.177824],[98.537376,15.308497],[98.192074,15.123703],[98.430819,14.622028],[99.097755,13.827503],[99.212012,13.269294],[99.196354,12.804748],[99.587286,11.892763],[99.038121,10.960546],[98.553551,9.93296],[98.457174,10.675266],[98.764546,11.441292],[98.428339,12.032987],[98.509574,13.122378],[98.103604,13.64046],[97.777732,14.837286],[97.597072,16.100568],[97.16454,16.928734],[96.505769,16.427241],[95.369352,15.71439],[94.808405,15.803454],[94.188804,16.037936],[94.533486,17.27724],[94.324817,18.213514],[93.540988,19.366493],[93.663255,19.726962],[93.078278,19.855145],[92.368554,20.670883],[92.303234,21.475485],[92.652257,21.324048],[92.672721,22.041239],[93.166128,22.27846],[93.060294,22.703111],[93.286327,23.043658],[93.325188,24.078556],[94.106742,23.850741],[94.552658,24.675238],[94.603249,25.162495],[95.155153,26.001307],[95.124768,26.573572],[96.419366,27.264589],[97.133999,27.083774],[97.051989,27.699059],[97.402561,27.882536],[97.327114,28.261583],[97.911988,28.335945],[98.246231,27.747221],[98.68269,27.508812],[98.712094,26.743536],[98.671838,25.918703],[97.724609,25.083637],[97.60472,23.897405],[98.660262,24.063286],[98.898749,23.142722],[99.531992,22.949039],[99.240899,22.118314],[99.983489,21.742937],[100.416538,21.558839],[101.150033,21.849984],[101.180005,21.436573],[100.329101,20.786122],[100.115988,20.41785],[99.543309,20.186598]]]}},{type:"Feature",id:"MNE",properties:{name:"Montenegro",iso_a2:"ME"},geometry:{type:"Polygon",coordinates:[[[19.801613,42.500093],[19.738051,42.688247],[19.30449,42.19574],[19.37177,41.87755],[19.16246,41.95502],[18.88214,42.28151],[18.45,42.48],[18.56,42.65],[18.70648,43.20011],[19.03165,43.43253],[19.21852,43.52384],[19.48389,43.35229],[19.63,43.21378],[19.95857,43.10604],[20.3398,42.89852],[20.25758,42.81275],[20.0707,42.58863],[19.801613,42.500093]]]}},{type:"Feature",id:"MNG",properties:{name:"Mongolia",iso_a2:"MN"},geometry:{type:"Polygon",coordinates:[[[87.751264,49.297198],[88.805567,49.470521],[90.713667,50.331812],[92.234712,50.802171],[93.104219,50.49529],[94.147566,50.480537],[94.815949,50.013433],[95.814028,49.977467],[97.259728,49.726061],[98.231762,50.422401],[97.82574,51.010995],[98.861491,52.047366],[99.981732,51.634006],[100.88948,51.516856],[102.065223,51.259921],[102.255909,50.510561],[103.676545,50.089966],[104.621552,50.275329],[105.886591,50.406019],[106.888804,50.274296],[107.868176,49.793705],[108.475167,49.282548],[109.402449,49.292961],[110.662011,49.130128],[111.581231,49.377968],[112.89774,49.543565],[114.362456,50.248303],[114.96211,50.140247],[115.485695,49.805177],[116.678801,49.888531],[116.191802,49.134598],[115.485282,48.135383],[115.742837,47.726545],[116.308953,47.85341],[117.295507,47.697709],[118.064143,48.06673],[118.866574,47.74706],[119.772824,47.048059],[119.66327,46.69268],[118.874326,46.805412],[117.421701,46.672733],[116.717868,46.388202],[115.985096,45.727235],[114.460332,45.339817],[113.463907,44.808893],[112.436062,45.011646],[111.873306,45.102079],[111.348377,44.457442],[111.667737,44.073176],[111.829588,43.743118],[111.129682,43.406834],[110.412103,42.871234],[109.243596,42.519446],[107.744773,42.481516],[106.129316,42.134328],[104.964994,41.59741],[104.522282,41.908347],[103.312278,41.907468],[101.83304,42.514873],[100.845866,42.663804],[99.515817,42.524691],[97.451757,42.74889],[96.349396,42.725635],[95.762455,43.319449],[95.306875,44.241331],[94.688929,44.352332],[93.480734,44.975472],[92.133891,45.115076],[90.94554,45.286073],[90.585768,45.719716],[90.970809,46.888146],[90.280826,47.693549],[88.854298,48.069082],[88.013832,48.599463],[87.751264,49.297198]]]}},{type:"Feature",id:"MOZ",properties:{name:"Mozambique",iso_a2:"MZ"},geometry:{type:"Polygon",coordinates:[[[34.559989,-11.52002],[35.312398,-11.439146],[36.514082,-11.720938],[36.775151,-11.594537],[37.471284,-11.568751],[37.827645,-11.268769],[38.427557,-11.285202],[39.52103,-10.896854],[40.316589,-10.317096],[40.478387,-10.765441],[40.437253,-11.761711],[40.560811,-12.639177],[40.59962,-14.201975],[40.775475,-14.691764],[40.477251,-15.406294],[40.089264,-16.100774],[39.452559,-16.720891],[38.538351,-17.101023],[37.411133,-17.586368],[36.281279,-18.659688],[35.896497,-18.84226],[35.1984,-19.552811],[34.786383,-19.784012],[34.701893,-20.497043],[35.176127,-21.254361],[35.373428,-21.840837],[35.385848,-22.14],[35.562546,-22.09],[35.533935,-23.070788],[35.371774,-23.535359],[35.60747,-23.706563],[35.458746,-24.12261],[35.040735,-24.478351],[34.215824,-24.816314],[33.01321,-25.357573],[32.574632,-25.727318],[32.660363,-26.148584],[32.915955,-26.215867],[32.83012,-26.742192],[32.071665,-26.73382],[31.985779,-26.29178],[31.837778,-25.843332],[31.752408,-25.484284],[31.930589,-24.369417],[31.670398,-23.658969],[31.191409,-22.25151],[32.244988,-21.116489],[32.508693,-20.395292],[32.659743,-20.30429],[32.772708,-19.715592],[32.611994,-19.419383],[32.654886,-18.67209],[32.849861,-17.979057],[32.847639,-16.713398],[32.328239,-16.392074],[31.852041,-16.319417],[31.636498,-16.07199],[31.173064,-15.860944],[30.338955,-15.880839],[30.274256,-15.507787],[30.179481,-14.796099],[33.214025,-13.97186],[33.7897,-14.451831],[34.064825,-14.35995],[34.459633,-14.61301],[34.517666,-15.013709],[34.307291,-15.478641],[34.381292,-16.18356],[35.03381,-16.8013],[35.339063,-16.10744],[35.771905,-15.896859],[35.686845,-14.611046],[35.267956,-13.887834],[34.907151,-13.565425],[34.559989,-13.579998],[34.280006,-12.280025],[34.559989,-11.52002]]]}},{type:"Feature",id:"MRT",properties:{name:"Mauritania",iso_a2:"MR"},geometry:{type:"Polygon",coordinates:[[[-12.17075,14.616834],[-12.830658,15.303692],[-13.435738,16.039383],[-14.099521,16.304302],[-14.577348,16.598264],[-15.135737,16.587282],[-15.623666,16.369337],[-16.12069,16.455663],[-16.463098,16.135036],[-16.549708,16.673892],[-16.270552,17.166963],[-16.146347,18.108482],[-16.256883,19.096716],[-16.377651,19.593817],[-16.277838,20.092521],[-16.536324,20.567866],[-17.063423,20.999752],[-16.845194,21.333323],[-12.929102,21.327071],[-13.118754,22.77122],[-12.874222,23.284832],[-11.937224,23.374594],[-11.969419,25.933353],[-8.687294,25.881056],[-8.6844,27.395744],[-4.923337,24.974574],[-6.453787,24.956591],[-5.971129,20.640833],[-5.488523,16.325102],[-5.315277,16.201854],[-5.537744,15.50169],[-9.550238,15.486497],[-9.700255,15.264107],[-10.086846,15.330486],[-10.650791,15.132746],[-11.349095,15.411256],[-11.666078,15.388208],[-11.834208,14.799097],[-12.17075,14.616834]]]}},{type:"Feature",id:"MWI",properties:{name:"Malawi",iso_a2:"MW"},geometry:{type:"Polygon",coordinates:[[[34.559989,-11.52002],[34.280006,-12.280025],[34.559989,-13.579998],[34.907151,-13.565425],[35.267956,-13.887834],[35.686845,-14.611046],[35.771905,-15.896859],[35.339063,-16.10744],[35.03381,-16.8013],[34.381292,-16.18356],[34.307291,-15.478641],[34.517666,-15.013709],[34.459633,-14.61301],[34.064825,-14.35995],[33.7897,-14.451831],[33.214025,-13.97186],[32.688165,-13.712858],[32.991764,-12.783871],[33.306422,-12.435778],[33.114289,-11.607198],[33.31531,-10.79655],[33.485688,-10.525559],[33.231388,-9.676722],[32.759375,-9.230599],[33.739729,-9.417151],[33.940838,-9.693674],[34.280006,-10.16],[34.559989,-11.52002]]]}},{type:"Feature",id:"MYS",properties:{name:"Malaysia",iso_a2:"MY"},geometry:{type:"MultiPolygon",coordinates:[[[[101.075516,6.204867],[101.154219,5.691384],[101.814282,5.810808],[102.141187,6.221636],[102.371147,6.128205],[102.961705,5.524495],[103.381215,4.855001],[103.438575,4.181606],[103.332122,3.726698],[103.429429,3.382869],[103.502448,2.791019],[103.854674,2.515454],[104.247932,1.631141],[104.228811,1.293048],[103.519707,1.226334],[102.573615,1.967115],[101.390638,2.760814],[101.27354,3.270292],[100.695435,3.93914],[100.557408,4.76728],[100.196706,5.312493],[100.30626,6.040562],[100.085757,6.464489],[100.259596,6.642825],[101.075516,6.204867]]],[[[118.618321,4.478202],[117.882035,4.137551],[117.015214,4.306094],[115.865517,4.306559],[115.519078,3.169238],[115.134037,2.821482],[114.621355,1.430688],[113.80585,1.217549],[112.859809,1.49779],[112.380252,1.410121],[111.797548,.904441],[111.159138,.976478],[110.514061,.773131],[109.830227,1.338136],[109.66326,2.006467],[110.396135,1.663775],[111.168853,1.850637],[111.370081,2.697303],[111.796928,2.885897],[112.995615,3.102395],[113.712935,3.893509],[114.204017,4.525874],[114.659596,4.007637],[114.869557,4.348314],[115.347461,4.316636],[115.4057,4.955228],[115.45071,5.44773],[116.220741,6.143191],[116.725103,6.924771],[117.129626,6.928053],[117.643393,6.422166],[117.689075,5.98749],[118.347691,5.708696],[119.181904,5.407836],[119.110694,5.016128],[118.439727,4.966519],[118.618321,4.478202]]]]}},{type:"Feature",id:"NAM",properties:{name:"Namibia",iso_a2:"NA"},geometry:{type:"Polygon",coordinates:[[[16.344977,-28.576705],[15.601818,-27.821247],[15.210472,-27.090956],[14.989711,-26.117372],[14.743214,-25.39292],[14.408144,-23.853014],[14.385717,-22.656653],[14.257714,-22.111208],[13.868642,-21.699037],[13.352498,-20.872834],[12.826845,-19.673166],[12.608564,-19.045349],[11.794919,-18.069129],[11.734199,-17.301889],[12.215461,-17.111668],[12.814081,-16.941343],[13.462362,-16.971212],[14.058501,-17.423381],[14.209707,-17.353101],[18.263309,-17.309951],[18.956187,-17.789095],[21.377176,-17.930636],[23.215048,-17.523116],[24.033862,-17.295843],[24.682349,-17.353411],[25.07695,-17.578823],[25.084443,-17.661816],[24.520705,-17.887125],[24.217365,-17.889347],[23.579006,-18.281261],[23.196858,-17.869038],[21.65504,-18.219146],[20.910641,-18.252219],[20.881134,-21.814327],[19.895458,-21.849157],[19.895768,-24.76779],[19.894734,-28.461105],[19.002127,-28.972443],[18.464899,-29.045462],[17.836152,-28.856378],[17.387497,-28.783514],[17.218929,-28.355943],[16.824017,-28.082162],[16.344977,-28.576705]]]}},{type:"Feature",id:"NCL",properties:{name:"New Caledonia",iso_a2:"NC"},geometry:{type:"Polygon",coordinates:[[[165.77999,-21.080005],[166.599991,-21.700019],[167.120011,-22.159991],[166.740035,-22.399976],[166.189732,-22.129708],[165.474375,-21.679607],[164.829815,-21.14982],[164.167995,-20.444747],[164.029606,-20.105646],[164.459967,-20.120012],[165.020036,-20.459991],[165.460009,-20.800022],[165.77999,-21.080005]]]}},{type:"Feature",id:"NER",properties:{name:"Niger",iso_a2:"NE"},geometry:{type:"Polygon",coordinates:[[[2.154474,11.94015],[2.177108,12.625018],[1.024103,12.851826],[.993046,13.33575],[.429928,13.988733],[.295646,14.444235],[.374892,14.928908],[1.015783,14.968182],[1.385528,15.323561],[2.749993,15.409525],[3.638259,15.56812],[3.723422,16.184284],[4.27021,16.852227],[4.267419,19.155265],[5.677566,19.601207],[8.572893,21.565661],[11.999506,23.471668],[13.581425,23.040506],[14.143871,22.491289],[14.8513,22.86295],[15.096888,21.308519],[15.471077,21.048457],[15.487148,20.730415],[15.903247,20.387619],[15.685741,19.95718],[15.300441,17.92795],[15.247731,16.627306],[13.972202,15.684366],[13.540394,14.367134],[13.956699,13.996691],[13.954477,13.353449],[14.595781,13.330427],[14.495787,12.859396],[14.213531,12.802035],[14.181336,12.483657],[13.995353,12.461565],[13.318702,13.556356],[13.083987,13.596147],[12.302071,13.037189],[11.527803,13.32898],[10.989593,13.387323],[10.701032,13.246918],[10.114814,13.277252],[9.524928,12.851102],[9.014933,12.826659],[7.804671,13.343527],[7.330747,13.098038],[6.820442,13.115091],[6.445426,13.492768],[5.443058,13.865924],[4.368344,13.747482],[4.107946,13.531216],[3.967283,12.956109],[3.680634,12.552903],[3.61118,11.660167],[2.848643,12.235636],[2.490164,12.233052],[2.154474,11.94015]]]}},{type:"Feature",id:"NGA",properties:{name:"Nigeria",iso_a2:"NG"},geometry:{type:"Polygon",coordinates:[[[8.500288,4.771983],[7.462108,4.412108],[7.082596,4.464689],[6.698072,4.240594],[5.898173,4.262453],[5.362805,4.887971],[5.033574,5.611802],[4.325607,6.270651],[3.57418,6.2583],[2.691702,6.258817],[2.749063,7.870734],[2.723793,8.506845],[2.912308,9.137608],[3.220352,9.444153],[3.705438,10.06321],[3.60007,10.332186],[3.797112,10.734746],[3.572216,11.327939],[3.61118,11.660167],[3.680634,12.552903],[3.967283,12.956109],[4.107946,13.531216],[4.368344,13.747482],[5.443058,13.865924],[6.445426,13.492768],[6.820442,13.115091],[7.330747,13.098038],[7.804671,13.343527],[9.014933,12.826659],[9.524928,12.851102],[10.114814,13.277252],[10.701032,13.246918],[10.989593,13.387323],[11.527803,13.32898],[12.302071,13.037189],[13.083987,13.596147],[13.318702,13.556356],[13.995353,12.461565],[14.181336,12.483657],[14.577178,12.085361],[14.468192,11.904752],[14.415379,11.572369],[13.57295,10.798566],[13.308676,10.160362],[13.1676,9.640626],[12.955468,9.417772],[12.753672,8.717763],[12.218872,8.305824],[12.063946,7.799808],[11.839309,7.397042],[11.745774,6.981383],[11.058788,6.644427],[10.497375,7.055358],[10.118277,7.03877],[9.522706,6.453482],[9.233163,6.444491],[8.757533,5.479666],[8.500288,4.771983]]]}},{type:"Feature",id:"NIC",properties:{name:"Nicaragua",iso_a2:"NI"},geometry:{type:"Polygon",coordinates:[[[-85.71254,11.088445],[-86.058488,11.403439],[-86.52585,11.806877],[-86.745992,12.143962],[-87.167516,12.458258],[-87.668493,12.90991],[-87.557467,13.064552],[-87.392386,12.914018],[-87.316654,12.984686],[-87.005769,13.025794],[-86.880557,13.254204],[-86.733822,13.263093],[-86.755087,13.754845],[-86.520708,13.778487],[-86.312142,13.771356],[-86.096264,14.038187],[-85.801295,13.836055],[-85.698665,13.960078],[-85.514413,14.079012],[-85.165365,14.35437],[-85.148751,14.560197],[-85.052787,14.551541],[-84.924501,14.790493],[-84.820037,14.819587],[-84.649582,14.666805],[-84.449336,14.621614],[-84.228342,14.748764],[-83.975721,14.749436],[-83.628585,14.880074],[-83.489989,15.016267],[-83.147219,14.995829],[-83.233234,14.899866],[-83.284162,14.676624],[-83.182126,14.310703],[-83.4125,13.970078],[-83.519832,13.567699],[-83.552207,13.127054],[-83.498515,12.869292],[-83.473323,12.419087],[-83.626104,12.32085],[-83.719613,11.893124],[-83.650858,11.629032],[-83.85547,11.373311],[-83.808936,11.103044],[-83.655612,10.938764],[-83.895054,10.726839],[-84.190179,10.79345],[-84.355931,10.999226],[-84.673069,11.082657],[-84.903003,10.952303],[-85.561852,11.217119],[-85.71254,11.088445]]]}},{type:"Feature",id:"NLD",properties:{name:"Netherlands",iso_a2:"NL"},geometry:{type:"Polygon",coordinates:[[[6.074183,53.510403],[6.90514,53.482162],[7.092053,53.144043],[6.84287,52.22844],[6.589397,51.852029],[5.988658,51.851616],[6.156658,50.803721],[5.606976,51.037298],[4.973991,51.475024],[4.047071,51.267259],[3.314971,51.345755],[3.830289,51.620545],[4.705997,53.091798],[6.074183,53.510403]]]}},{type:"Feature",id:"NOR",properties:{name:"Norway",iso_a2:"NO"},geometry:{type:"MultiPolygon",coordinates:[[[[28.165547,71.185474],[31.293418,70.453788],[30.005435,70.186259],[31.101079,69.55808],[29.399581,69.156916],[28.59193,69.064777],[29.015573,69.766491],[27.732292,70.164193],[26.179622,69.825299],[25.689213,69.092114],[24.735679,68.649557],[23.66205,68.891247],[22.356238,68.841741],[21.244936,69.370443],[20.645593,69.106247],[20.025269,69.065139],[19.87856,68.407194],[17.993868,68.567391],[17.729182,68.010552],[16.768879,68.013937],[16.108712,67.302456],[15.108411,66.193867],[13.55569,64.787028],[13.919905,64.445421],[13.571916,64.049114],[12.579935,64.066219],[11.930569,63.128318],[11.992064,61.800362],[12.631147,61.293572],[12.300366,60.117933],[11.468272,59.432393],[11.027369,58.856149],[10.356557,59.469807],[8.382,58.313288],[7.048748,58.078884],[5.665835,58.588155],[5.308234,59.663232],[4.992078,61.970998],[5.9129,62.614473],[8.553411,63.454008],[10.527709,64.486038],[12.358347,65.879726],[14.761146,67.810642],[16.435927,68.563205],[19.184028,69.817444],[21.378416,70.255169],[23.023742,70.202072],[24.546543,71.030497],[26.37005,70.986262],[28.165547,71.185474]]],[[[24.72412,77.85385],[22.49032,77.44493],[20.72601,77.67704],[21.41611,77.93504],[20.8119,78.25463],[22.88426,78.45494],[23.28134,78.07954],[24.72412,77.85385]]],[[[18.25183,79.70175],[21.54383,78.95611],[19.02737,78.5626],[18.47172,77.82669],[17.59441,77.63796],[17.1182,76.80941],[15.91315,76.77045],[13.76259,77.38035],[14.66956,77.73565],[13.1706,78.02493],[11.22231,78.8693],[10.44453,79.65239],[13.17077,80.01046],[13.71852,79.66039],[15.14282,79.67431],[15.52255,80.01608],[16.99085,80.05086],[18.25183,79.70175]]],[[[25.447625,80.40734],[27.407506,80.056406],[25.924651,79.517834],[23.024466,79.400012],[20.075188,79.566823],[19.897266,79.842362],[18.462264,79.85988],[17.368015,80.318896],[20.455992,80.598156],[21.907945,80.357679],[22.919253,80.657144],[25.447625,80.40734]]]]}},{type:"Feature",id:"NPL",properties:{name:"Nepal",iso_a2:"NP"},geometry:{type:"Polygon",coordinates:[[[88.120441,27.876542],[88.043133,27.445819],[88.174804,26.810405],[88.060238,26.414615],[87.227472,26.397898],[86.024393,26.630985],[85.251779,26.726198],[84.675018,27.234901],[83.304249,27.364506],[81.999987,27.925479],[81.057203,28.416095],[80.088425,28.79447],[80.476721,29.729865],[81.111256,30.183481],[81.525804,30.422717],[82.327513,30.115268],[83.337115,29.463732],[83.898993,29.320226],[84.23458,28.839894],[85.011638,28.642774],[85.82332,28.203576],[86.954517,27.974262],[88.120441,27.876542]]]}},{type:"Feature",id:"NZL",properties:{name:"New Zealand",iso_a2:"NZ"},geometry:{type:"MultiPolygon",coordinates:[[[[173.020375,-40.919052],[173.247234,-41.331999],[173.958405,-40.926701],[174.247587,-41.349155],[174.248517,-41.770008],[173.876447,-42.233184],[173.22274,-42.970038],[172.711246,-43.372288],[173.080113,-43.853344],[172.308584,-43.865694],[171.452925,-44.242519],[171.185138,-44.897104],[170.616697,-45.908929],[169.831422,-46.355775],[169.332331,-46.641235],[168.411354,-46.619945],[167.763745,-46.290197],[166.676886,-46.219917],[166.509144,-45.852705],[167.046424,-45.110941],[168.303763,-44.123973],[168.949409,-43.935819],[169.667815,-43.555326],[170.52492,-43.031688],[171.12509,-42.512754],[171.569714,-41.767424],[171.948709,-41.514417],[172.097227,-40.956104],[172.79858,-40.493962],[173.020375,-40.919052]]],[[[174.612009,-36.156397],[175.336616,-37.209098],[175.357596,-36.526194],[175.808887,-36.798942],[175.95849,-37.555382],[176.763195,-37.881253],[177.438813,-37.961248],[178.010354,-37.579825],[178.517094,-37.695373],[178.274731,-38.582813],[177.97046,-39.166343],[177.206993,-39.145776],[176.939981,-39.449736],[177.032946,-39.879943],[176.885824,-40.065978],[176.508017,-40.604808],[176.01244,-41.289624],[175.239567,-41.688308],[175.067898,-41.425895],[174.650973,-41.281821],[175.22763,-40.459236],[174.900157,-39.908933],[173.824047,-39.508854],[173.852262,-39.146602],[174.574802,-38.797683],[174.743474,-38.027808],[174.697017,-37.381129],[174.292028,-36.711092],[174.319004,-36.534824],[173.840997,-36.121981],[173.054171,-35.237125],[172.636005,-34.529107],[173.007042,-34.450662],[173.551298,-35.006183],[174.32939,-35.265496],[174.612009,-36.156397]]]]}},{type:"Feature",id:"OMN",properties:{name:"Oman",iso_a2:"OM"},geometry:{type:"MultiPolygon",coordinates:[[[[58.861141,21.114035],[58.487986,20.428986],[58.034318,20.481437],[57.826373,20.243002],[57.665762,19.736005],[57.7887,19.06757],[57.694391,18.94471],[57.234264,18.947991],[56.609651,18.574267],[56.512189,18.087113],[56.283521,17.876067],[55.661492,17.884128],[55.269939,17.632309],[55.2749,17.228354],[54.791002,16.950697],[54.239253,17.044981],[53.570508,16.707663],[53.108573,16.651051],[52.782184,17.349742],[52.00001,19.000003],[54.999982,19.999994],[55.666659,22.000001],[55.208341,22.70833],[55.234489,23.110993],[55.525841,23.524869],[55.528632,23.933604],[55.981214,24.130543],[55.804119,24.269604],[55.886233,24.920831],[56.396847,24.924732],[56.84514,24.241673],[57.403453,23.878594],[58.136948,23.747931],[58.729211,23.565668],[59.180502,22.992395],[59.450098,22.660271],[59.80806,22.533612],[59.806148,22.310525],[59.442191,21.714541],[59.282408,21.433886],[58.861141,21.114035]]],[[[56.391421,25.895991],[56.261042,25.714606],[56.070821,26.055464],[56.362017,26.395934],[56.485679,26.309118],[56.391421,25.895991]]]]}},{type:"Feature",id:"PAK",properties:{name:"Pakistan",iso_a2:"PK"},geometry:{type:"Polygon",coordinates:[[[75.158028,37.133031],[75.896897,36.666806],[76.192848,35.898403],[77.837451,35.49401],[76.871722,34.653544],[75.757061,34.504923],[74.240203,34.748887],[73.749948,34.317699],[74.104294,33.441473],[74.451559,32.7649],[75.258642,32.271105],[74.405929,31.692639],[74.42138,30.979815],[73.450638,29.976413],[72.823752,28.961592],[71.777666,27.91318],[70.616496,27.989196],[69.514393,26.940966],[70.168927,26.491872],[70.282873,25.722229],[70.844699,25.215102],[71.04324,24.356524],[68.842599,24.359134],[68.176645,23.691965],[67.443667,23.944844],[67.145442,24.663611],[66.372828,25.425141],[64.530408,25.237039],[62.905701,25.218409],[61.497363,25.078237],[61.874187,26.239975],[63.316632,26.756532],[63.233898,27.217047],[62.755426,27.378923],[62.72783,28.259645],[61.771868,28.699334],[61.369309,29.303276],[60.874248,29.829239],[62.549857,29.318572],[63.550261,29.468331],[64.148002,29.340819],[64.350419,29.560031],[65.046862,29.472181],[66.346473,29.887943],[66.381458,30.738899],[66.938891,31.304911],[67.683394,31.303154],[67.792689,31.58293],[68.556932,31.71331],[68.926677,31.620189],[69.317764,31.901412],[69.262522,32.501944],[69.687147,33.105499],[70.323594,33.358533],[69.930543,34.02012],[70.881803,33.988856],[71.156773,34.348911],[71.115019,34.733126],[71.613076,35.153203],[71.498768,35.650563],[71.262348,36.074388],[71.846292,36.509942],[72.920025,36.720007],[74.067552,36.836176],[74.575893,37.020841],[75.158028,37.133031]]]}},{type:"Feature",id:"PAN",properties:{name:"Panama",iso_a2:"PA"},geometry:{type:"Polygon",coordinates:[[[-77.881571,7.223771],[-78.214936,7.512255],[-78.429161,8.052041],[-78.182096,8.319182],[-78.435465,8.387705],[-78.622121,8.718124],[-79.120307,8.996092],[-79.557877,8.932375],[-79.760578,8.584515],[-80.164481,8.333316],[-80.382659,8.298409],[-80.480689,8.090308],[-80.00369,7.547524],[-80.276671,7.419754],[-80.421158,7.271572],[-80.886401,7.220541],[-81.059543,7.817921],[-81.189716,7.647906],[-81.519515,7.70661],[-81.721311,8.108963],[-82.131441,8.175393],[-82.390934,8.292362],[-82.820081,8.290864],[-82.850958,8.073823],[-82.965783,8.225028],[-82.913176,8.423517],[-82.829771,8.626295],[-82.868657,8.807266],[-82.719183,8.925709],[-82.927155,9.07433],[-82.932891,9.476812],[-82.546196,9.566135],[-82.187123,9.207449],[-82.207586,8.995575],[-81.808567,8.950617],[-81.714154,9.031955],[-81.439287,8.786234],[-80.947302,8.858504],[-80.521901,9.111072],[-79.9146,9.312765],[-79.573303,9.61161],[-79.021192,9.552931],[-79.05845,9.454565],[-78.500888,9.420459],[-78.055928,9.24773],[-77.729514,8.946844],[-77.353361,8.670505],[-77.474723,8.524286],[-77.242566,7.935278],[-77.431108,7.638061],[-77.753414,7.70984],[-77.881571,7.223771]]]}},{type:"Feature",id:"PER",properties:{name:"Peru",iso_a2:"PE"},geometry:{type:"Polygon",coordinates:[[[-69.590424,-17.580012],[-69.858444,-18.092694],[-70.372572,-18.347975],[-71.37525,-17.773799],[-71.462041,-17.363488],[-73.44453,-16.359363],[-75.237883,-15.265683],[-76.009205,-14.649286],[-76.423469,-13.823187],[-76.259242,-13.535039],[-77.106192,-12.222716],[-78.092153,-10.377712],[-79.036953,-8.386568],[-79.44592,-7.930833],[-79.760578,-7.194341],[-80.537482,-6.541668],[-81.249996,-6.136834],[-80.926347,-5.690557],[-81.410943,-4.736765],[-81.09967,-4.036394],[-80.302561,-3.404856],[-80.184015,-3.821162],[-80.469295,-4.059287],[-80.442242,-4.425724],[-80.028908,-4.346091],[-79.624979,-4.454198],[-79.205289,-4.959129],[-78.639897,-4.547784],[-78.450684,-3.873097],[-77.837905,-3.003021],[-76.635394,-2.608678],[-75.544996,-1.56161],[-75.233723,-.911417],[-75.373223,-.152032],[-75.106625,-.057205],[-74.441601,-.53082],[-74.122395,-1.002833],[-73.659504,-1.260491],[-73.070392,-2.308954],[-72.325787,-2.434218],[-71.774761,-2.16979],[-71.413646,-2.342802],[-70.813476,-2.256865],[-70.047709,-2.725156],[-70.692682,-3.742872],[-70.394044,-3.766591],[-69.893635,-4.298187],[-70.794769,-4.251265],[-70.928843,-4.401591],[-71.748406,-4.593983],[-72.891928,-5.274561],[-72.964507,-5.741251],[-73.219711,-6.089189],[-73.120027,-6.629931],[-73.724487,-6.918595],[-73.723401,-7.340999],[-73.987235,-7.52383],[-73.571059,-8.424447],[-73.015383,-9.032833],[-73.226713,-9.462213],[-72.563033,-9.520194],[-72.184891,-10.053598],[-71.302412,-10.079436],[-70.481894,-9.490118],[-70.548686,-11.009147],[-70.093752,-11.123972],[-69.529678,-10.951734],[-68.66508,-12.5613],[-68.88008,-12.899729],[-68.929224,-13.602684],[-68.948887,-14.453639],[-69.339535,-14.953195],[-69.160347,-15.323974],[-69.389764,-15.660129],[-68.959635,-16.500698],[-69.590424,-17.580012]]]}},{type:"Feature",id:"PHL",properties:{name:"Philippines",iso_a2:"PH"},geometry:{type:"MultiPolygon",coordinates:[[[[126.376814,8.414706],[126.478513,7.750354],[126.537424,7.189381],[126.196773,6.274294],[125.831421,7.293715],[125.363852,6.786485],[125.683161,6.049657],[125.396512,5.581003],[124.219788,6.161355],[123.93872,6.885136],[124.243662,7.36061],[123.610212,7.833527],[123.296071,7.418876],[122.825506,7.457375],[122.085499,6.899424],[121.919928,7.192119],[122.312359,8.034962],[122.942398,8.316237],[123.487688,8.69301],[123.841154,8.240324],[124.60147,8.514158],[124.764612,8.960409],[125.471391,8.986997],[125.412118,9.760335],[126.222714,9.286074],[126.306637,8.782487],[126.376814,8.414706]]],[[[123.982438,10.278779],[123.623183,9.950091],[123.309921,9.318269],[122.995883,9.022189],[122.380055,9.713361],[122.586089,9.981045],[122.837081,10.261157],[122.947411,10.881868],[123.49885,10.940624],[123.337774,10.267384],[124.077936,11.232726],[123.982438,10.278779]]],[[[118.504581,9.316383],[117.174275,8.3675],[117.664477,9.066889],[118.386914,9.6845],[118.987342,10.376292],[119.511496,11.369668],[119.689677,10.554291],[119.029458,10.003653],[118.504581,9.316383]]],[[[121.883548,11.891755],[122.483821,11.582187],[123.120217,11.58366],[123.100838,11.165934],[122.637714,10.741308],[122.00261,10.441017],[121.967367,10.905691],[122.03837,11.415841],[121.883548,11.891755]]],[[[125.502552,12.162695],[125.783465,11.046122],[125.011884,11.311455],[125.032761,10.975816],[125.277449,10.358722],[124.801819,10.134679],[124.760168,10.837995],[124.459101,10.88993],[124.302522,11.495371],[124.891013,11.415583],[124.87799,11.79419],[124.266762,12.557761],[125.227116,12.535721],[125.502552,12.162695]]],[[[121.527394,13.06959],[121.26219,12.20556],[120.833896,12.704496],[120.323436,13.466413],[121.180128,13.429697],[121.527394,13.06959]]],[[[121.321308,18.504065],[121.937601,18.218552],[122.246006,18.47895],[122.336957,18.224883],[122.174279,17.810283],[122.515654,17.093505],[122.252311,16.262444],[121.662786,15.931018],[121.50507,15.124814],[121.728829,14.328376],[122.258925,14.218202],[122.701276,14.336541],[123.950295,13.782131],[123.855107,13.237771],[124.181289,12.997527],[124.077419,12.536677],[123.298035,13.027526],[122.928652,13.55292],[122.671355,13.185836],[122.03465,13.784482],[121.126385,13.636687],[120.628637,13.857656],[120.679384,14.271016],[120.991819,14.525393],[120.693336,14.756671],[120.564145,14.396279],[120.070429,14.970869],[119.920929,15.406347],[119.883773,16.363704],[120.286488,16.034629],[120.390047,17.599081],[120.715867,18.505227],[121.321308,18.504065]]]]}},{type:"Feature",id:"PNG",properties:{name:"Papua New Guinea",iso_a2:"PG"},geometry:{type:"MultiPolygon",coordinates:[[[[155.880026,-6.819997],[155.599991,-6.919991],[155.166994,-6.535931],[154.729192,-5.900828],[154.514114,-5.139118],[154.652504,-5.042431],[154.759991,-5.339984],[155.062918,-5.566792],[155.547746,-6.200655],[156.019965,-6.540014],[155.880026,-6.819997]]],[[[151.982796,-5.478063],[151.459107,-5.56028],[151.30139,-5.840728],[150.754447,-6.083763],[150.241197,-6.317754],[149.709963,-6.316513],[148.890065,-6.02604],[148.318937,-5.747142],[148.401826,-5.437756],[149.298412,-5.583742],[149.845562,-5.505503],[149.99625,-5.026101],[150.139756,-5.001348],[150.236908,-5.53222],[150.807467,-5.455842],[151.089672,-5.113693],[151.647881,-4.757074],[151.537862,-4.167807],[152.136792,-4.14879],[152.338743,-4.312966],[152.318693,-4.867661],[151.982796,-5.478063]]],[[[147.191874,-7.388024],[148.084636,-8.044108],[148.734105,-9.104664],[149.306835,-9.071436],[149.266631,-9.514406],[150.038728,-9.684318],[149.738798,-9.872937],[150.801628,-10.293687],[150.690575,-10.582713],[150.028393,-10.652476],[149.78231,-10.393267],[148.923138,-10.280923],[147.913018,-10.130441],[147.135443,-9.492444],[146.567881,-8.942555],[146.048481,-8.067414],[144.744168,-7.630128],[143.897088,-7.91533],[143.286376,-8.245491],[143.413913,-8.983069],[142.628431,-9.326821],[142.068259,-9.159596],[141.033852,-9.117893],[141.017057,-5.859022],[141.00021,-2.600151],[142.735247,-3.289153],[144.583971,-3.861418],[145.27318,-4.373738],[145.829786,-4.876498],[145.981922,-5.465609],[147.648073,-6.083659],[147.891108,-6.614015],[146.970905,-6.721657],[147.191874,-7.388024]]],[[[153.140038,-4.499983],[152.827292,-4.766427],[152.638673,-4.176127],[152.406026,-3.789743],[151.953237,-3.462062],[151.384279,-3.035422],[150.66205,-2.741486],[150.939965,-2.500002],[151.479984,-2.779985],[151.820015,-2.999972],[152.239989,-3.240009],[152.640017,-3.659983],[153.019994,-3.980015],[153.140038,-4.499983]]]]}},{type:"Feature",id:"POL",properties:{name:"Poland",iso_a2:"PL"},geometry:{type:"Polygon",coordinates:[[[15.016996,51.106674],[14.607098,51.745188],[14.685026,52.089947],[14.4376,52.62485],[14.074521,52.981263],[14.353315,53.248171],[14.119686,53.757029],[14.8029,54.050706],[16.363477,54.513159],[17.622832,54.851536],[18.620859,54.682606],[18.696255,54.438719],[19.66064,54.426084],[20.892245,54.312525],[22.731099,54.327537],[23.243987,54.220567],[23.484128,53.912498],[23.527536,53.470122],[23.804935,53.089731],[23.799199,52.691099],[23.199494,52.486977],[23.508002,52.023647],[23.527071,51.578454],[24.029986,50.705407],[23.922757,50.424881],[23.426508,50.308506],[22.51845,49.476774],[22.776419,49.027395],[22.558138,49.085738],[21.607808,49.470107],[20.887955,49.328772],[20.415839,49.431453],[19.825023,49.217125],[19.320713,49.571574],[18.909575,49.435846],[18.853144,49.49623],[18.392914,49.988629],[17.649445,50.049038],[17.554567,50.362146],[16.868769,50.473974],[16.719476,50.215747],[16.176253,50.422607],[16.238627,50.697733],[15.490972,50.78473],[15.016996,51.106674]]]}},{type:"Feature",id:"PRI",properties:{name:"Puerto Rico",iso_a2:"PR"},geometry:{type:"Polygon",coordinates:[[[-66.282434,18.514762],[-65.771303,18.426679],[-65.591004,18.228035],[-65.847164,17.975906],[-66.599934,17.981823],[-67.184162,17.946553],[-67.242428,18.37446],[-67.100679,18.520601],[-66.282434,18.514762]]]}},{type:"Feature",id:"PRK",properties:{name:"North Korea",iso_a2:"KP"},geometry:{type:"Polygon",coordinates:[[[130.640016,42.395009],[130.780007,42.220007],[130.400031,42.280004],[129.965949,41.941368],[129.667362,41.601104],[129.705189,40.882828],[129.188115,40.661808],[129.0104,40.485436],[128.633368,40.189847],[127.967414,40.025413],[127.533436,39.75685],[127.50212,39.323931],[127.385434,39.213472],[127.783343,39.050898],[128.349716,38.612243],[128.205746,38.370397],[127.780035,38.304536],[127.073309,38.256115],[126.68372,37.804773],[126.237339,37.840378],[126.174759,37.749686],[125.689104,37.94001],[125.568439,37.752089],[125.27533,37.669071],[125.240087,37.857224],[124.981033,37.948821],[124.712161,38.108346],[124.985994,38.548474],[125.221949,38.665857],[125.132859,38.848559],[125.38659,39.387958],[125.321116,39.551385],[124.737482,39.660344],[124.265625,39.928493],[125.079942,40.569824],[126.182045,41.107336],[126.869083,41.816569],[127.343783,41.503152],[128.208433,41.466772],[128.052215,41.994285],[129.596669,42.424982],[129.994267,42.985387],[130.640016,42.395009]]]}},{type:"Feature",id:"PRT",properties:{name:"Portugal",iso_a2:"PT"},geometry:{type:"Polygon",coordinates:[[[-9.034818,41.880571],[-8.671946,42.134689],[-8.263857,42.280469],[-8.013175,41.790886],[-7.422513,41.792075],[-7.251309,41.918346],[-6.668606,41.883387],[-6.389088,41.381815],[-6.851127,41.111083],[-6.86402,40.330872],[-7.026413,40.184524],[-7.066592,39.711892],[-7.498632,39.629571],[-7.098037,39.030073],[-7.374092,38.373059],[-7.029281,38.075764],[-7.166508,37.803894],[-7.537105,37.428904],[-7.453726,37.097788],[-7.855613,36.838269],[-8.382816,36.97888],[-8.898857,36.868809],[-8.746101,37.651346],[-8.839998,38.266243],[-9.287464,38.358486],[-9.526571,38.737429],[-9.446989,39.392066],[-9.048305,39.755093],[-8.977353,40.159306],[-8.768684,40.760639],[-8.790853,41.184334],[-8.990789,41.543459],[-9.034818,41.880571]]]}},{type:"Feature",id:"PRY",properties:{name:"Paraguay",iso_a2:"PY"},geometry:{type:"Polygon",coordinates:[[[-62.685057,-22.249029],[-62.291179,-21.051635],[-62.265961,-20.513735],[-61.786326,-19.633737],[-60.043565,-19.342747],[-59.115042,-19.356906],[-58.183471,-19.868399],[-58.166392,-20.176701],[-57.870674,-20.732688],[-57.937156,-22.090176],[-56.88151,-22.282154],[-56.473317,-22.0863],[-55.797958,-22.35693],[-55.610683,-22.655619],[-55.517639,-23.571998],[-55.400747,-23.956935],[-55.027902,-24.001274],[-54.652834,-23.839578],[-54.29296,-24.021014],[-54.293476,-24.5708],[-54.428946,-25.162185],[-54.625291,-25.739255],[-54.788795,-26.621786],[-55.695846,-27.387837],[-56.486702,-27.548499],[-57.60976,-27.395899],[-58.618174,-27.123719],[-57.63366,-25.603657],[-57.777217,-25.16234],[-58.807128,-24.771459],[-60.028966,-24.032796],[-60.846565,-23.880713],[-62.685057,-22.249029]]]}},{type:"Feature",id:"QAT",properties:{name:"Qatar",iso_a2:"QA"},geometry:{type:"Polygon",coordinates:[[[50.810108,24.754743],[50.743911,25.482424],[51.013352,26.006992],[51.286462,26.114582],[51.589079,25.801113],[51.6067,25.21567],[51.389608,24.627386],[51.112415,24.556331],[50.810108,24.754743]]]}},{type:"Feature",id:"ROU",properties:{name:"Romania",iso_a2:"RO"},geometry:{type:"Polygon",coordinates:[[[22.710531,47.882194],[23.142236,48.096341],[23.760958,47.985598],[24.402056,47.981878],[24.866317,47.737526],[25.207743,47.891056],[25.945941,47.987149],[26.19745,48.220881],[26.619337,48.220726],[26.924176,48.123264],[27.233873,47.826771],[27.551166,47.405117],[28.12803,46.810476],[28.160018,46.371563],[28.054443,45.944586],[28.233554,45.488283],[28.679779,45.304031],[29.149725,45.464925],[29.603289,45.293308],[29.626543,45.035391],[29.141612,44.82021],[28.837858,44.913874],[28.558081,43.707462],[27.970107,43.812468],[27.2424,44.175986],[26.065159,43.943494],[25.569272,43.688445],[24.100679,43.741051],[23.332302,43.897011],[22.944832,43.823785],[22.65715,44.234923],[22.474008,44.409228],[22.705726,44.578003],[22.459022,44.702517],[22.145088,44.478422],[21.562023,44.768947],[21.483526,45.18117],[20.874313,45.416375],[20.762175,45.734573],[20.220192,46.127469],[21.021952,46.316088],[21.626515,46.994238],[22.099768,47.672439],[22.710531,47.882194]]]}},{type:"Feature",id:"RUS",properties:{name:"Russia",iso_a2:"RU"},geometry:{type:"MultiPolygon",coordinates:[[[[143.648007,50.7476],[144.654148,48.976391],[143.173928,49.306551],[142.558668,47.861575],[143.533492,46.836728],[143.505277,46.137908],[142.747701,46.740765],[142.09203,45.966755],[141.906925,46.805929],[142.018443,47.780133],[141.904445,48.859189],[142.1358,49.615163],[142.179983,50.952342],[141.594076,51.935435],[141.682546,53.301966],[142.606934,53.762145],[142.209749,54.225476],[142.654786,54.365881],[142.914616,53.704578],[143.260848,52.74076],[143.235268,51.75666],[143.648007,50.7476]]],[[[22.731099,54.327537],[20.892245,54.312525],[19.66064,54.426084],[19.888481,54.86616],[21.268449,55.190482],[22.315724,55.015299],[22.757764,54.856574],[22.651052,54.582741],[22.731099,54.327537]]],[[[-175.01425,66.58435],[-174.33983,66.33556],[-174.57182,67.06219],[-171.85731,66.91308],[-169.89958,65.97724],[-170.89107,65.54139],[-172.53025,65.43791],[-172.555,64.46079],[-172.95533,64.25269],[-173.89184,64.2826],[-174.65392,64.63125],[-175.98353,64.92288],[-176.20716,65.35667],[-177.22266,65.52024],[-178.35993,65.39052],[-178.90332,65.74044],[-178.68611,66.11211],[-179.88377,65.87456],[-179.43268,65.40411],[-180,64.979709],[-180,68.963636],[-177.55,68.2],[-174.92825,67.20589],[-175.01425,66.58435]]],[[[180,70.832199],[178.903425,70.78114],[178.7253,71.0988],[180,71.515714],[180,70.832199]]],[[[-178.69378,70.89302],[-180,70.832199],[-180,71.515714],[-179.871875,71.55762],[-179.02433,71.55553],[-177.577945,71.26948],[-177.663575,71.13277],[-178.69378,70.89302]]],[[[143.60385,73.21244],[142.08763,73.20544],[140.038155,73.31692],[139.86312,73.36983],[140.81171,73.76506],[142.06207,73.85758],[143.48283,73.47525],[143.60385,73.21244]]],[[[150.73167,75.08406],[149.575925,74.68892],[147.977465,74.778355],[146.11919,75.17298],[146.358485,75.49682],[148.22223,75.345845],[150.73167,75.08406]]],[[[145.086285,75.562625],[144.3,74.82],[140.61381,74.84768],[138.95544,74.61148],[136.97439,75.26167],[137.51176,75.94917],[138.831075,76.13676],[141.471615,76.09289],[145.086285,75.562625]]],[[[57.535693,70.720464],[56.944979,70.632743],[53.677375,70.762658],[53.412017,71.206662],[51.601895,71.474759],[51.455754,72.014881],[52.478275,72.229442],[52.444169,72.774731],[54.427614,73.627548],[53.50829,73.749814],[55.902459,74.627486],[55.631933,75.081412],[57.868644,75.60939],[61.170044,76.251883],[64.498368,76.439055],[66.210977,76.809782],[68.15706,76.939697],[68.852211,76.544811],[68.180573,76.233642],[64.637326,75.737755],[61.583508,75.260885],[58.477082,74.309056],[56.986786,73.333044],[55.419336,72.371268],[55.622838,71.540595],[57.535693,70.720464]]],[[[106.97013,76.97419],[107.24,76.48],[108.1538,76.72335],[111.07726,76.71],[113.33151,76.22224],[114.13417,75.84764],[113.88539,75.32779],[112.77918,75.03186],[110.15125,74.47673],[109.4,74.18],[110.64,74.04],[112.11919,73.78774],[113.01954,73.97693],[113.52958,73.33505],[113.96881,73.59488],[115.56782,73.75285],[118.77633,73.58772],[119.02,73.12],[123.20066,72.97122],[123.25777,73.73503],[125.38,73.56],[126.97644,73.56549],[128.59126,73.03871],[129.05157,72.39872],[128.46,71.98],[129.71599,71.19304],[131.28858,70.78699],[132.2535,71.8363],[133.85766,71.38642],[135.56193,71.65525],[137.49755,71.34763],[138.23409,71.62803],[139.86983,71.48783],[139.14791,72.41619],[140.46817,72.84941],[149.5,72.2],[150.35118,71.60643],[152.9689,70.84222],[157.00688,71.03141],[158.99779,70.86672],[159.83031,70.45324],[159.70866,69.72198],[160.94053,69.43728],[162.27907,69.64204],[164.05248,69.66823],[165.94037,69.47199],[167.83567,69.58269],[169.57763,68.6938],[170.81688,69.01363],[170.0082,69.65276],[170.45345,70.09703],[173.64391,69.81743],[175.72403,69.87725],[178.6,69.4],[180,68.963636],[180,64.979709],[179.99281,64.97433],[178.7072,64.53493],[177.41128,64.60821],[178.313,64.07593],[178.90825,63.25197],[179.37034,62.98262],[179.48636,62.56894],[179.22825,62.3041],[177.3643,62.5219],[174.56929,61.76915],[173.68013,61.65261],[172.15,60.95],[170.6985,60.33618],[170.33085,59.88177],[168.90046,60.57355],[166.29498,59.78855],[165.84,60.16],[164.87674,59.7316],[163.53929,59.86871],[163.21711,59.21101],[162.01733,58.24328],[162.05297,57.83912],[163.19191,57.61503],[163.05794,56.15924],[162.12958,56.12219],[161.70146,55.28568],[162.11749,54.85514],[160.36877,54.34433],[160.02173,53.20257],[158.53094,52.95868],[158.23118,51.94269],[156.78979,51.01105],[156.42,51.7],[155.99182,53.15895],[155.43366,55.38103],[155.91442,56.76792],[156.75815,57.3647],[156.81035,57.83204],[158.36433,58.05575],[160.15064,59.31477],[161.87204,60.343],[163.66969,61.1409],[164.47355,62.55061],[163.25842,62.46627],[162.65791,61.6425],[160.12148,60.54423],[159.30232,61.77396],[156.72068,61.43442],[154.21806,59.75818],[155.04375,59.14495],[152.81185,58.88385],[151.26573,58.78089],[151.33815,59.50396],[149.78371,59.65573],[148.54481,59.16448],[145.48722,59.33637],[142.19782,59.03998],[138.95848,57.08805],[135.12619,54.72959],[136.70171,54.60355],[137.19342,53.97732],[138.1647,53.75501],[138.80463,54.25455],[139.90151,54.18968],[141.34531,53.08957],[141.37923,52.23877],[140.59742,51.23967],[140.51308,50.04553],[140.06193,48.44671],[138.55472,46.99965],[138.21971,46.30795],[136.86232,45.1435],[135.51535,43.989],[134.86939,43.39821],[133.53687,42.81147],[132.90627,42.79849],[132.27807,43.28456],[130.93587,42.55274],[130.78,42.22],[130.64,42.395],[130.633866,42.903015],[131.144688,42.92999],[131.288555,44.11152],[131.02519,44.96796],[131.883454,45.321162],[133.09712,45.14409],[133.769644,46.116927],[134.11235,47.21248],[134.50081,47.57845],[135.026311,48.47823],[133.373596,48.183442],[132.50669,47.78896],[130.98726,47.79013],[130.582293,48.729687],[129.397818,49.4406],[127.6574,49.76027],[127.287456,50.739797],[126.939157,51.353894],[126.564399,51.784255],[125.946349,52.792799],[125.068211,53.161045],[123.57147,53.4588],[122.245748,53.431726],[121.003085,53.251401],[120.177089,52.753886],[120.725789,52.516226],[120.7382,51.96411],[120.18208,51.64355],[119.27939,50.58292],[119.288461,50.142883],[117.879244,49.510983],[116.678801,49.888531],[115.485695,49.805177],[114.96211,50.140247],[114.362456,50.248303],[112.89774,49.543565],[111.581231,49.377968],[110.662011,49.130128],[109.402449,49.292961],[108.475167,49.282548],[107.868176,49.793705],[106.888804,50.274296],[105.886591,50.406019],[104.62158,50.27532],[103.676545,50.089966],[102.25589,50.51056],[102.06521,51.25991],[100.88948,51.516856],[99.981732,51.634006],[98.861491,52.047366],[97.82574,51.010995],[98.231762,50.422401],[97.25976,49.72605],[95.81402,49.97746],[94.815949,50.013433],[94.147566,50.480537],[93.10421,50.49529],[92.234712,50.802171],[90.713667,50.331812],[88.805567,49.470521],[87.751264,49.297198],[87.35997,49.214981],[86.829357,49.826675],[85.54127,49.692859],[85.11556,50.117303],[84.416377,50.3114],[83.935115,50.889246],[83.383004,51.069183],[81.945986,50.812196],[80.568447,51.388336],[80.03556,50.864751],[77.800916,53.404415],[76.525179,54.177003],[76.8911,54.490524],[74.38482,53.54685],[73.425679,53.48981],[73.508516,54.035617],[72.22415,54.376655],[71.180131,54.133285],[70.865267,55.169734],[69.068167,55.38525],[68.1691,54.970392],[65.66687,54.60125],[65.178534,54.354228],[61.4366,54.00625],[60.978066,53.664993],[61.699986,52.979996],[60.739993,52.719986],[60.927269,52.447548],[59.967534,51.96042],[61.588003,51.272659],[61.337424,50.79907],[59.932807,50.842194],[59.642282,50.545442],[58.36332,51.06364],[56.77798,51.04355],[55.71694,50.62171],[54.532878,51.02624],[52.328724,51.718652],[50.766648,51.692762],[48.702382,50.605128],[48.577841,49.87476],[47.54948,50.454698],[46.751596,49.356006],[47.043672,49.152039],[46.466446,48.394152],[47.31524,47.71585],[48.05725,47.74377],[48.694734,47.075628],[48.59325,46.56104],[49.10116,46.39933],[48.64541,45.80629],[47.67591,45.64149],[46.68201,44.6092],[47.59094,43.66016],[47.49252,42.98658],[48.58437,41.80888],[47.987283,41.405819],[47.815666,41.151416],[47.373315,41.219732],[46.686071,41.827137],[46.404951,41.860675],[45.7764,42.09244],[45.470279,42.502781],[44.537623,42.711993],[43.93121,42.55496],[43.75599,42.74083],[42.3944,43.2203],[40.92219,43.38215],[40.076965,43.553104],[39.955009,43.434998],[38.68,44.28],[37.53912,44.65721],[36.67546,45.24469],[37.40317,45.40451],[38.23295,46.24087],[37.67372,46.63657],[39.14767,47.04475],[39.1212,47.26336],[38.223538,47.10219],[38.255112,47.5464],[38.77057,47.82562],[39.738278,47.898937],[39.89562,48.23241],[39.67465,48.78382],[40.080789,49.30743],[40.06904,49.60105],[38.594988,49.926462],[38.010631,49.915662],[37.39346,50.383953],[36.626168,50.225591],[35.356116,50.577197],[35.37791,50.77394],[35.022183,51.207572],[34.224816,51.255993],[34.141978,51.566413],[34.391731,51.768882],[33.7527,52.335075],[32.715761,52.238465],[32.412058,52.288695],[32.15944,52.06125],[31.78597,52.10168],[31.540018,52.742052],[31.305201,53.073996],[31.49764,53.16743],[32.304519,53.132726],[32.693643,53.351421],[32.405599,53.618045],[31.731273,53.794029],[31.791424,53.974639],[31.384472,54.157056],[30.757534,54.811771],[30.971836,55.081548],[30.873909,55.550976],[29.896294,55.789463],[29.371572,55.670091],[29.229513,55.918344],[28.176709,56.16913],[27.855282,56.759326],[27.770016,57.244258],[27.288185,57.474528],[27.716686,57.791899],[27.42015,58.72457],[28.131699,59.300825],[27.98112,59.47537],[29.1177,60.02805],[28.07,60.50352],[30.211107,61.780028],[31.139991,62.357693],[31.516092,62.867687],[30.035872,63.552814],[30.444685,64.204453],[29.54443,64.948672],[30.21765,65.80598],[29.054589,66.944286],[29.977426,67.698297],[28.445944,68.364613],[28.59193,69.064777],[29.39955,69.15692],[31.10108,69.55811],[32.13272,69.90595],[33.77547,69.30142],[36.51396,69.06342],[40.29234,67.9324],[41.05987,67.45713],[41.12595,66.79158],[40.01583,66.26618],[38.38295,65.99953],[33.91871,66.75961],[33.18444,66.63253],[34.81477,65.90015],[34.878574,65.436213],[34.94391,64.41437],[36.23129,64.10945],[37.01273,63.84983],[37.14197,64.33471],[36.539579,64.76446],[37.17604,65.14322],[39.59345,64.52079],[40.4356,64.76446],[39.7626,65.49682],[42.09309,66.47623],[43.01604,66.41858],[43.94975,66.06908],[44.53226,66.75634],[43.69839,67.35245],[44.18795,67.95051],[43.45282,68.57079],[46.25,68.25],[46.82134,67.68997],[45.55517,67.56652],[45.56202,67.01005],[46.34915,66.66767],[47.89416,66.88455],[48.13876,67.52238],[50.22766,67.99867],[53.71743,68.85738],[54.47171,68.80815],[53.48582,68.20131],[54.72628,68.09702],[55.44268,68.43866],[57.31702,68.46628],[58.802,68.88082],[59.94142,68.27844],[61.07784,68.94069],[60.03,69.52],[60.55,69.85],[63.504,69.54739],[64.888115,69.234835],[68.51216,68.09233],[69.18068,68.61563],[68.16444,69.14436],[68.13522,69.35649],[66.93008,69.45461],[67.25976,69.92873],[66.72492,70.70889],[66.69466,71.02897],[68.54006,71.9345],[69.19636,72.84336],[69.94,73.04],[72.58754,72.77629],[72.79603,72.22006],[71.84811,71.40898],[72.47011,71.09019],[72.79188,70.39114],[72.5647,69.02085],[73.66787,68.4079],[73.2387,67.7404],[71.28,66.32],[72.42301,66.17267],[72.82077,66.53267],[73.92099,66.78946],[74.18651,67.28429],[75.052,67.76047],[74.46926,68.32899],[74.93584,68.98918],[73.84236,69.07146],[73.60187,69.62763],[74.3998,70.63175],[73.1011,71.44717],[74.89082,72.12119],[74.65926,72.83227],[75.15801,72.85497],[75.68351,72.30056],[75.28898,71.33556],[76.35911,71.15287],[75.90313,71.87401],[77.57665,72.26717],[79.65202,72.32011],[81.5,71.75],[80.61071,72.58285],[80.51109,73.6482],[82.25,73.85],[84.65526,73.80591],[86.8223,73.93688],[86.00956,74.45967],[87.16682,75.11643],[88.31571,75.14393],[90.26,75.64],[92.90058,75.77333],[93.23421,76.0472],[95.86,76.14],[96.67821,75.91548],[98.92254,76.44689],[100.75967,76.43028],[101.03532,76.86189],[101.99084,77.28754],[104.3516,77.69792],[106.06664,77.37389],[104.705,77.1274],[106.97013,76.97419]]],[[[105.07547,78.30689],[99.43814,77.921],[101.2649,79.23399],[102.08635,79.34641],[102.837815,79.28129],[105.37243,78.71334],[105.07547,78.30689]]],[[[51.136187,80.54728],[49.793685,80.415428],[48.894411,80.339567],[48.754937,80.175468],[47.586119,80.010181],[46.502826,80.247247],[47.072455,80.559424],[44.846958,80.58981],[46.799139,80.771918],[48.318477,80.78401],[48.522806,80.514569],[49.09719,80.753986],[50.039768,80.918885],[51.522933,80.699726],[51.136187,80.54728]]],[[[99.93976,78.88094],[97.75794,78.7562],[94.97259,79.044745],[93.31288,79.4265],[92.5454,80.14379],[91.18107,80.34146],[93.77766,81.0246],[95.940895,81.2504],[97.88385,80.746975],[100.186655,79.780135],[99.93976,78.88094]]]]}},{type:"Feature",id:"RWA",properties:{name:"Rwanda",iso_a2:"RW"},geometry:{type:"Polygon",coordinates:[[[30.419105,-1.134659],[30.816135,-1.698914],[30.758309,-2.28725],[30.469696,-2.413858],[29.938359,-2.348487],[29.632176,-2.917858],[29.024926,-2.839258],[29.117479,-2.292211],[29.254835,-2.21511],[29.291887,-1.620056],[29.579466,-1.341313],[29.821519,-1.443322],[30.419105,-1.134659]]]}},{type:"Feature",id:"ESH",properties:{name:"Western Sahara",iso_a2:"EH"},geometry:{type:"Polygon",coordinates:[[[-8.794884,27.120696],[-8.817828,27.656426],[-8.66559,27.656426],[-8.665124,27.589479],[-8.6844,27.395744],[-8.687294,25.881056],[-11.969419,25.933353],[-11.937224,23.374594],[-12.874222,23.284832],[-13.118754,22.77122],[-12.929102,21.327071],[-16.845194,21.333323],[-17.063423,20.999752],[-17.020428,21.42231],[-17.002962,21.420734],[-14.750955,21.5006],[-14.630833,21.86094],[-14.221168,22.310163],[-13.89111,23.691009],[-12.500963,24.770116],[-12.030759,26.030866],[-11.71822,26.104092],[-11.392555,26.883424],[-10.551263,26.990808],[-10.189424,26.860945],[-9.735343,26.860945],[-9.413037,27.088476],[-8.794884,27.120696]]]}},{type:"Feature",id:"SAU",properties:{name:"Saudi Arabia",iso_a2:"SA"},geometry:{type:"Polygon",coordinates:[[[42.779332,16.347891],[42.649573,16.774635],[42.347989,17.075806],[42.270888,17.474722],[41.754382,17.833046],[41.221391,18.6716],[40.939341,19.486485],[40.247652,20.174635],[39.801685,20.338862],[39.139399,21.291905],[39.023696,21.986875],[39.066329,22.579656],[38.492772,23.688451],[38.02386,24.078686],[37.483635,24.285495],[37.154818,24.858483],[37.209491,25.084542],[36.931627,25.602959],[36.639604,25.826228],[36.249137,26.570136],[35.640182,27.37652],[35.130187,28.063352],[34.632336,28.058546],[34.787779,28.607427],[34.83222,28.957483],[34.956037,29.356555],[36.068941,29.197495],[36.501214,29.505254],[36.740528,29.865283],[37.503582,30.003776],[37.66812,30.338665],[37.998849,30.5085],[37.002166,31.508413],[39.004886,32.010217],[39.195468,32.161009],[40.399994,31.889992],[41.889981,31.190009],[44.709499,29.178891],[46.568713,29.099025],[47.459822,29.002519],[47.708851,28.526063],[48.416094,28.552004],[48.807595,27.689628],[49.299554,27.461218],[49.470914,27.109999],[50.152422,26.689663],[50.212935,26.277027],[50.113303,25.943972],[50.239859,25.60805],[50.527387,25.327808],[50.660557,24.999896],[50.810108,24.754743],[51.112415,24.556331],[51.389608,24.627386],[51.579519,24.245497],[51.617708,24.014219],[52.000733,23.001154],[55.006803,22.496948],[55.208341,22.70833],[55.666659,22.000001],[54.999982,19.999994],[52.00001,19.000003],[49.116672,18.616668],[48.183344,18.166669],[47.466695,17.116682],[47.000005,16.949999],[46.749994,17.283338],[46.366659,17.233315],[45.399999,17.333335],[45.216651,17.433329],[44.062613,17.410359],[43.791519,17.319977],[43.380794,17.579987],[43.115798,17.08844],[43.218375,16.66689],[42.779332,16.347891]]]}},{type:"Feature",id:"SDN",properties:{name:"Sudan",iso_a2:"SD"},geometry:{type:"Polygon",coordinates:[[[33.963393,9.464285],[33.824963,9.484061],[33.842131,9.981915],[33.721959,10.325262],[33.206938,10.720112],[33.086766,11.441141],[33.206938,12.179338],[32.743419,12.248008],[32.67475,12.024832],[32.073892,11.97333],[32.314235,11.681484],[32.400072,11.080626],[31.850716,10.531271],[31.352862,9.810241],[30.837841,9.707237],[29.996639,10.290927],[29.618957,10.084919],[29.515953,9.793074],[29.000932,9.604232],[28.966597,9.398224],[27.97089,9.398224],[27.833551,9.604232],[27.112521,9.638567],[26.752006,9.466893],[26.477328,9.55273],[25.962307,10.136421],[25.790633,10.411099],[25.069604,10.27376],[24.794926,9.810241],[24.537415,8.917538],[24.194068,8.728696],[23.88698,8.61973],[23.805813,8.666319],[23.459013,8.954286],[23.394779,9.265068],[23.55725,9.681218],[23.554304,10.089255],[22.977544,10.714463],[22.864165,11.142395],[22.87622,11.38461],[22.50869,11.67936],[22.49762,12.26024],[22.28801,12.64605],[21.93681,12.58818],[22.03759,12.95546],[22.29658,13.37232],[22.18329,13.78648],[22.51202,14.09318],[22.30351,14.32682],[22.56795,14.94429],[23.02459,15.68072],[23.88689,15.61084],[23.83766,19.58047],[23.85,20],[25,20.00304],[25,22],[29.02,22],[32.9,22],[36.86623,22],[37.18872,21.01885],[36.96941,20.83744],[37.1147,19.80796],[37.48179,18.61409],[37.86276,18.36786],[38.41009,17.998307],[37.904,17.42754],[37.16747,17.26314],[36.85253,16.95655],[36.75389,16.29186],[36.32322,14.82249],[36.42951,14.42211],[36.27022,13.56333],[35.86363,12.57828],[35.26049,12.08286],[34.83163,11.31896],[34.73115,10.91017],[34.25745,10.63009],[33.96162,9.58358],[33.963393,9.464285]]]}},{type:"Feature",id:"SSD",properties:{name:"South Sudan",iso_a2:"SS"},geometry:{type:"Polygon",coordinates:[[[33.963393,9.464285],[33.97498,8.68456],[33.8255,8.37916],[33.2948,8.35458],[32.95418,7.78497],[33.56829,7.71334],[34.0751,7.22595],[34.25032,6.82607],[34.70702,6.59422],[35.298007,5.506],[34.620196,4.847123],[34.005,4.249885],[33.39,3.79],[32.68642,3.79232],[31.88145,3.55827],[31.24556,3.7819],[30.83385,3.50917],[29.95349,4.1737],[29.715995,4.600805],[29.159078,4.389267],[28.696678,4.455077],[28.428994,4.287155],[27.979977,4.408413],[27.374226,5.233944],[27.213409,5.550953],[26.465909,5.946717],[26.213418,6.546603],[25.796648,6.979316],[25.124131,7.500085],[25.114932,7.825104],[24.567369,8.229188],[23.88698,8.61973],[24.194068,8.728696],[24.537415,8.917538],[24.794926,9.810241],[25.069604,10.27376],[25.790633,10.411099],[25.962307,10.136421],[26.477328,9.55273],[26.752006,9.466893],[27.112521,9.638567],[27.833551,9.604232],[27.97089,9.398224],[28.966597,9.398224],[29.000932,9.604232],[29.515953,9.793074],[29.618957,10.084919],[29.996639,10.290927],[30.837841,9.707237],[31.352862,9.810241],[31.850716,10.531271],[32.400072,11.080626],[32.314235,11.681484],[32.073892,11.97333],[32.67475,12.024832],[32.743419,12.248008],[33.206938,12.179338],[33.086766,11.441141],[33.206938,10.720112],[33.721959,10.325262],[33.842131,9.981915],[33.824963,9.484061],[33.963393,9.464285]]]}},{type:"Feature",id:"SEN",properties:{name:"Senegal",iso_a2:"SN"},geometry:{type:"Polygon",coordinates:[[[-16.713729,13.594959],[-17.126107,14.373516],[-17.625043,14.729541],[-17.185173,14.919477],[-16.700706,15.621527],[-16.463098,16.135036],[-16.12069,16.455663],[-15.623666,16.369337],[-15.135737,16.587282],[-14.577348,16.598264],[-14.099521,16.304302],[-13.435738,16.039383],[-12.830658,15.303692],[-12.17075,14.616834],[-12.124887,13.994727],[-11.927716,13.422075],[-11.553398,13.141214],[-11.467899,12.754519],[-11.513943,12.442988],[-11.658301,12.386583],[-12.203565,12.465648],[-12.278599,12.35444],[-12.499051,12.33209],[-13.217818,12.575874],[-13.700476,12.586183],[-15.548477,12.62817],[-15.816574,12.515567],[-16.147717,12.547762],[-16.677452,12.384852],[-16.841525,13.151394],[-15.931296,13.130284],[-15.691001,13.270353],[-15.511813,13.27857],[-15.141163,13.509512],[-14.712197,13.298207],[-14.277702,13.280585],[-13.844963,13.505042],[-14.046992,13.794068],[-14.376714,13.62568],[-14.687031,13.630357],[-15.081735,13.876492],[-15.39877,13.860369],[-15.624596,13.623587],[-16.713729,13.594959]]]}},{type:"Feature",id:"SLB",properties:{name:"Solomon Islands",iso_a2:"SB"},geometry:{type:"MultiPolygon",coordinates:[[[[162.119025,-10.482719],[162.398646,-10.826367],[161.700032,-10.820011],[161.319797,-10.204751],[161.917383,-10.446701],[162.119025,-10.482719]]],[[[160.852229,-9.872937],[160.462588,-9.89521],[159.849447,-9.794027],[159.640003,-9.63998],[159.702945,-9.24295],[160.362956,-9.400304],[160.688518,-9.610162],[160.852229,-9.872937]]],[[[161.679982,-9.599982],[161.529397,-9.784312],[160.788253,-8.917543],[160.579997,-8.320009],[160.920028,-8.320009],[161.280006,-9.120011],[161.679982,-9.599982]]],[[[159.875027,-8.33732],[159.917402,-8.53829],[159.133677,-8.114181],[158.586114,-7.754824],[158.21115,-7.421872],[158.359978,-7.320018],[158.820001,-7.560003],[159.640003,-8.020027],[159.875027,-8.33732]]],[[[157.538426,-7.34782],[157.33942,-7.404767],[156.90203,-7.176874],[156.491358,-6.765943],[156.542828,-6.599338],[157.14,-7.021638],[157.538426,-7.34782]]]]}},{type:"Feature",id:"SLE",properties:{name:"Sierra Leone",iso_a2:"SL"},geometry:{type:"Polygon",coordinates:[[[-11.438779,6.785917],[-11.708195,6.860098],[-12.428099,7.262942],[-12.949049,7.798646],[-13.124025,8.163946],[-13.24655,8.903049],[-12.711958,9.342712],[-12.596719,9.620188],[-12.425929,9.835834],[-12.150338,9.858572],[-11.917277,10.046984],[-11.117481,10.045873],[-10.839152,9.688246],[-10.622395,9.26791],[-10.65477,8.977178],[-10.494315,8.715541],[-10.505477,8.348896],[-10.230094,8.406206],[-10.695595,7.939464],[-11.146704,7.396706],[-11.199802,7.105846],[-11.438779,6.785917]]]}},{type:"Feature",id:"SLV",properties:{name:"El Salvador",iso_a2:"SV"},geometry:{type:"Polygon",coordinates:[[[-87.793111,13.38448],[-87.904112,13.149017],[-88.483302,13.163951],[-88.843228,13.259734],[-89.256743,13.458533],[-89.812394,13.520622],[-90.095555,13.735338],[-90.064678,13.88197],[-89.721934,14.134228],[-89.534219,14.244816],[-89.587343,14.362586],[-89.353326,14.424133],[-89.058512,14.340029],[-88.843073,14.140507],[-88.541231,13.980155],[-88.503998,13.845486],[-88.065343,13.964626],[-87.859515,13.893312],[-87.723503,13.78505],[-87.793111,13.38448]]]}},{type:"Feature",id:"-99",properties:{name:"Somaliland",iso_a2:"XX"},geometry:{type:"Polygon",coordinates:[[[48.93813,9.451749],[48.486736,8.837626],[47.78942,8.003],[46.948328,7.996877],[43.67875,9.18358],[43.296975,9.540477],[42.92812,10.02194],[42.55876,10.57258],[42.776852,10.926879],[43.145305,11.46204],[43.47066,11.27771],[43.666668,10.864169],[44.117804,10.445538],[44.614259,10.442205],[45.556941,10.698029],[46.645401,10.816549],[47.525658,11.127228],[48.021596,11.193064],[48.378784,11.375482],[48.948206,11.410622],[48.942005,11.394266],[48.938491,10.982327],[48.938233,9.9735],[48.93813,9.451749]]]}},{type:"Feature",id:"SOM",properties:{name:"Somalia",iso_a2:"SO"},geometry:{type:"Polygon",coordinates:[[[49.72862,11.5789],[50.25878,11.67957],[50.73202,12.0219],[51.1112,12.02464],[51.13387,11.74815],[51.04153,11.16651],[51.04531,10.6409],[50.83418,10.27972],[50.55239,9.19874],[50.07092,8.08173],[49.4527,6.80466],[48.59455,5.33911],[47.74079,4.2194],[46.56476,2.85529],[45.56399,2.04576],[44.06815,1.05283],[43.13597,.2922],[42.04157,-.91916],[41.81095,-1.44647],[41.58513,-1.68325],[40.993,-.85829],[40.98105,2.78452],[41.855083,3.918912],[42.12861,4.23413],[42.76967,4.25259],[43.66087,4.95755],[44.9636,5.00162],[47.78942,8.003],[48.486736,8.837626],[48.93813,9.451749],[48.938233,9.9735],[48.938491,10.982327],[48.942005,11.394266],[48.948205,11.410617],[49.26776,11.43033],[49.72862,11.5789]]]}},{type:"Feature",id:"SRB",properties:{name:"Republic of Serbia",iso_a2:"RS"},geometry:{type:"Polygon",coordinates:[[[20.874313,45.416375],[21.483526,45.18117],[21.562023,44.768947],[22.145088,44.478422],[22.459022,44.702517],[22.705726,44.578003],[22.474008,44.409228],[22.65715,44.234923],[22.410446,44.008063],[22.500157,43.642814],[22.986019,43.211161],[22.604801,42.898519],[22.436595,42.580321],[22.545012,42.461362],[22.380526,42.32026],[21.91708,42.30364],[21.576636,42.245224],[21.54332,42.32025],[21.66292,42.43922],[21.77505,42.6827],[21.63302,42.67717],[21.43866,42.86255],[21.27421,42.90959],[21.143395,43.068685],[20.95651,43.13094],[20.81448,43.27205],[20.63508,43.21671],[20.49679,42.88469],[20.25758,42.81275],[20.3398,42.89852],[19.95857,43.10604],[19.63,43.21378],[19.48389,43.35229],[19.21852,43.52384],[19.454,43.5681],[19.59976,44.03847],[19.11761,44.42307],[19.36803,44.863],[19.00548,44.86023],[19.390476,45.236516],[19.072769,45.521511],[18.82982,45.90888],[19.596045,46.17173],[20.220192,46.127469],[20.762175,45.734573],[20.874313,45.416375]]]}},{type:"Feature",id:"SUR",properties:{name:"Suriname",iso_a2:"SR"},geometry:{type:"Polygon",coordinates:[[[-57.147436,5.97315],[-55.949318,5.772878],[-55.84178,5.953125],[-55.03325,6.025291],[-53.958045,5.756548],[-54.478633,4.896756],[-54.399542,4.212611],[-54.006931,3.620038],[-54.181726,3.18978],[-54.269705,2.732392],[-54.524754,2.311849],[-55.097587,2.523748],[-55.569755,2.421506],[-55.973322,2.510364],[-56.073342,2.220795],[-55.9056,2.021996],[-55.995698,1.817667],[-56.539386,1.899523],[-57.150098,2.768927],[-57.281433,3.333492],[-57.601569,3.334655],[-58.044694,4.060864],[-57.86021,4.576801],[-57.914289,4.812626],[-57.307246,5.073567],[-57.147436,5.97315]]]}},{type:"Feature",id:"SVK",properties:{name:"Slovakia",iso_a2:"SK"},geometry:{type:"Polygon",coordinates:[[[18.853144,49.49623],[18.909575,49.435846],[19.320713,49.571574],[19.825023,49.217125],[20.415839,49.431453],[20.887955,49.328772],[21.607808,49.470107],[22.558138,49.085738],[22.280842,48.825392],[22.085608,48.422264],[21.872236,48.319971],[20.801294,48.623854],[20.473562,48.56285],[20.239054,48.327567],[19.769471,48.202691],[19.661364,48.266615],[19.174365,48.111379],[18.777025,48.081768],[18.696513,47.880954],[17.857133,47.758429],[17.488473,47.867466],[16.979667,48.123497],[16.879983,48.470013],[16.960288,48.596982],[17.101985,48.816969],[17.545007,48.800019],[17.886485,48.903475],[17.913512,48.996493],[18.104973,49.043983],[18.170498,49.271515],[18.399994,49.315001],[18.554971,49.495015],[18.853144,49.49623]]]}},{type:"Feature",id:"SVN",properties:{name:"Slovenia",iso_a2:"SI"},geometry:{type:"Polygon",coordinates:[[[13.806475,46.509306],[14.632472,46.431817],[15.137092,46.658703],[16.011664,46.683611],[16.202298,46.852386],[16.370505,46.841327],[16.564808,46.503751],[15.768733,46.238108],[15.67153,45.834154],[15.323954,45.731783],[15.327675,45.452316],[14.935244,45.471695],[14.595109,45.634941],[14.411968,45.466166],[13.71506,45.500324],[13.93763,45.591016],[13.69811,46.016778],[13.806475,46.509306]]]}},{type:"Feature",id:"SWE",properties:{name:"Sweden",iso_a2:"SE"},geometry:{type:"MultiPolygon",coordinates:[[[[22.183173,65.723741],[21.213517,65.026005],[21.369631,64.413588],[19.778876,63.609554],[17.847779,62.7494],[17.119555,61.341166],[17.831346,60.636583],[18.787722,60.081914],[17.869225,58.953766],[16.829185,58.719827],[16.44771,57.041118],[15.879786,56.104302],[14.666681,56.200885],[14.100721,55.407781],[12.942911,55.361737],[12.625101,56.30708],[11.787942,57.441817],[11.027369,58.856149],[11.468272,59.432393],[12.300366,60.117933],[12.631147,61.293572],[11.992064,61.800362],[11.930569,63.128318],[12.579935,64.066219],[13.571916,64.049114],[13.919905,64.445421],[13.55569,64.787028],[15.108411,66.193867],[16.108712,67.302456],[16.768879,68.013937],[17.729182,68.010552],[17.993868,68.567391],[19.87856,68.407194],[20.025269,69.065139],[20.645593,69.106247],[21.978535,68.616846],[23.539473,67.936009],[23.56588,66.396051],[23.903379,66.006927],[22.183173,65.723741]]],[[[17.061767,57.385783],[17.210083,57.326521],[16.430053,56.179196],[16.364135,56.556455],[17.061767,57.385783]]],[[[19.35791,57.958588],[18.8031,57.651279],[18.825073,57.444949],[18.995361,57.441993],[18.951416,57.370976],[18.693237,57.305756],[18.709716,57.204734],[18.462524,57.127295],[18.319702,56.926992],[18.105468,56.891003],[18.187866,57.109402],[18.072509,57.267163],[18.154907,57.394664],[18.094482,57.545312],[18.660278,57.929434],[19.039306,57.941098],[19.105224,57.993543],[19.374389,57.996454],[19.35791,57.958588]]],[[[20.846557,63.82371],[21.066284,63.829768],[20.9729,63.71567],[20.824584,63.579121],[20.695495,63.59134],[20.819091,63.714454],[20.799865,63.780059],[20.846557,63.82371]]]]}},{type:"Feature",id:"SWZ",properties:{name:"Swaziland",iso_a2:"SZ"},geometry:{type:"Polygon",coordinates:[[[32.071665,-26.73382],[31.86806,-27.177927],[31.282773,-27.285879],[30.685962,-26.743845],[30.676609,-26.398078],[30.949667,-26.022649],[31.04408,-25.731452],[31.333158,-25.660191],[31.837778,-25.843332],[31.985779,-26.29178],[32.071665,-26.73382]]]}},{type:"Feature",id:"SYR",properties:{name:"Syria",iso_a2:"SY"},geometry:{type:"Polygon",coordinates:[[[38.792341,33.378686],[36.834062,32.312938],[35.719918,32.709192],[35.700798,32.716014],[35.836397,32.868123],[35.821101,33.277426],[36.06646,33.824912],[36.61175,34.201789],[36.448194,34.593935],[35.998403,34.644914],[35.905023,35.410009],[36.149763,35.821535],[36.41755,36.040617],[36.685389,36.259699],[36.739494,36.81752],[37.066761,36.623036],[38.167727,36.90121],[38.699891,36.712927],[39.52258,36.716054],[40.673259,37.091276],[41.212089,37.074352],[42.349591,37.229873],[41.837064,36.605854],[41.289707,36.358815],[41.383965,35.628317],[41.006159,34.419372],[38.792341,33.378686]]]}},{type:"Feature",id:"TCD",properties:{name:"Chad",iso_a2:"TD"},geometry:{type:"Polygon",coordinates:[[[14.495787,12.859396],[14.595781,13.330427],[13.954477,13.353449],[13.956699,13.996691],[13.540394,14.367134],[13.97217,15.68437],[15.247731,16.627306],[15.300441,17.92795],[15.685741,19.95718],[15.903247,20.387619],[15.487148,20.730415],[15.47106,21.04845],[15.096888,21.308519],[14.8513,22.86295],[15.86085,23.40972],[19.84926,21.49509],[23.83766,19.58047],[23.88689,15.61084],[23.02459,15.68072],[22.56795,14.94429],[22.30351,14.32682],[22.51202,14.09318],[22.18329,13.78648],[22.29658,13.37232],[22.03759,12.95546],[21.93681,12.58818],[22.28801,12.64605],[22.49762,12.26024],[22.50869,11.67936],[22.87622,11.38461],[22.864165,11.142395],[22.231129,10.971889],[21.723822,10.567056],[21.000868,9.475985],[20.059685,9.012706],[19.094008,9.074847],[18.81201,8.982915],[18.911022,8.630895],[18.389555,8.281304],[17.96493,7.890914],[16.705988,7.508328],[16.456185,7.734774],[16.290562,7.754307],[16.106232,7.497088],[15.27946,7.421925],[15.436092,7.692812],[15.120866,8.38215],[14.979996,8.796104],[14.544467,8.965861],[13.954218,9.549495],[14.171466,10.021378],[14.627201,9.920919],[14.909354,9.992129],[15.467873,9.982337],[14.923565,10.891325],[14.960152,11.555574],[14.89336,12.21905],[14.495787,12.859396]]]}},{type:"Feature",id:"TGO",properties:{name:"Togo",iso_a2:"TG"},geometry:{type:"Polygon",coordinates:[[[1.865241,6.142158],[1.060122,5.928837],[.836931,6.279979],[.570384,6.914359],[.490957,7.411744],[.712029,8.312465],[.461192,8.677223],[.365901,9.465004],[.36758,10.191213],[-.049785,10.706918],[.023803,11.018682],[.899563,10.997339],[.772336,10.470808],[1.077795,10.175607],[1.425061,9.825395],[1.463043,9.334624],[1.664478,9.12859],[1.618951,6.832038],[1.865241,6.142158]]]}},{type:"Feature",id:"THA",properties:{name:"Thailand",iso_a2:"TH"},geometry:{type:"Polygon",coordinates:[[[102.584932,12.186595],[101.687158,12.64574],[100.83181,12.627085],[100.978467,13.412722],[100.097797,13.406856],[100.018733,12.307001],[99.478921,10.846367],[99.153772,9.963061],[99.222399,9.239255],[99.873832,9.207862],[100.279647,8.295153],[100.459274,7.429573],[101.017328,6.856869],[101.623079,6.740622],[102.141187,6.221636],[101.814282,5.810808],[101.154219,5.691384],[101.075516,6.204867],[100.259596,6.642825],[100.085757,6.464489],[99.690691,6.848213],[99.519642,7.343454],[98.988253,7.907993],[98.503786,8.382305],[98.339662,7.794512],[98.150009,8.350007],[98.25915,8.973923],[98.553551,9.93296],[99.038121,10.960546],[99.587286,11.892763],[99.196354,12.804748],[99.212012,13.269294],[99.097755,13.827503],[98.430819,14.622028],[98.192074,15.123703],[98.537376,15.308497],[98.903348,16.177824],[98.493761,16.837836],[97.859123,17.567946],[97.375896,18.445438],[97.797783,18.62708],[98.253724,19.708203],[98.959676,19.752981],[99.543309,20.186598],[100.115988,20.41785],[100.548881,20.109238],[100.606294,19.508344],[101.282015,19.462585],[101.035931,18.408928],[101.059548,17.512497],[102.113592,18.109102],[102.413005,17.932782],[102.998706,17.961695],[103.200192,18.309632],[103.956477,18.240954],[104.716947,17.428859],[104.779321,16.441865],[105.589039,15.570316],[105.544338,14.723934],[105.218777,14.273212],[104.281418,14.416743],[102.988422,14.225721],[102.348099,13.394247],[102.584932,12.186595]]]}},{type:"Feature",id:"TJK",properties:{name:"Tajikistan",iso_a2:"TJ"},geometry:{type:"Polygon",coordinates:[[[71.014198,40.244366],[70.648019,39.935754],[69.55961,40.103211],[69.464887,39.526683],[70.549162,39.604198],[71.784694,39.279463],[73.675379,39.431237],[73.928852,38.505815],[74.257514,38.606507],[74.864816,38.378846],[74.829986,37.990007],[74.980002,37.41999],[73.948696,37.421566],[73.260056,37.495257],[72.63689,37.047558],[72.193041,36.948288],[71.844638,36.738171],[71.448693,37.065645],[71.541918,37.905774],[71.239404,37.953265],[71.348131,38.258905],[70.806821,38.486282],[70.376304,38.138396],[70.270574,37.735165],[70.116578,37.588223],[69.518785,37.608997],[69.196273,37.151144],[68.859446,37.344336],[68.135562,37.023115],[67.83,37.144994],[68.392033,38.157025],[68.176025,38.901553],[67.44222,39.140144],[67.701429,39.580478],[68.536416,39.533453],[69.011633,40.086158],[69.329495,40.727824],[70.666622,40.960213],[70.45816,40.496495],[70.601407,40.218527],[71.014198,40.244366]]]}},{type:"Feature",id:"TKM",properties:{name:"Turkmenistan",iso_a2:"TM"},geometry:{type:"Polygon",coordinates:[[[61.210817,35.650072],[61.123071,36.491597],[60.377638,36.527383],[59.234762,37.412988],[58.436154,37.522309],[57.330434,38.029229],[56.619366,38.121394],[56.180375,37.935127],[55.511578,37.964117],[54.800304,37.392421],[53.921598,37.198918],[53.735511,37.906136],[53.880929,38.952093],[53.101028,39.290574],[53.357808,39.975286],[52.693973,40.033629],[52.915251,40.876523],[53.858139,40.631034],[54.736845,40.951015],[54.008311,41.551211],[53.721713,42.123191],[52.91675,41.868117],[52.814689,41.135371],[52.50246,41.783316],[52.944293,42.116034],[54.079418,42.324109],[54.755345,42.043971],[55.455251,41.259859],[55.968191,41.308642],[57.096391,41.32231],[56.932215,41.826026],[57.78653,42.170553],[58.629011,42.751551],[59.976422,42.223082],[60.083341,41.425146],[60.465953,41.220327],[61.547179,41.26637],[61.882714,41.084857],[62.37426,40.053886],[63.518015,39.363257],[64.170223,38.892407],[65.215999,38.402695],[66.54615,37.974685],[66.518607,37.362784],[66.217385,37.39379],[65.745631,37.661164],[65.588948,37.305217],[64.746105,37.111818],[64.546479,36.312073],[63.982896,36.007957],[63.193538,35.857166],[62.984662,35.404041],[62.230651,35.270664],[61.210817,35.650072]]]}},{type:"Feature",id:"TLS",properties:{name:"East Timor",iso_a2:"TL"},geometry:{type:"Polygon",coordinates:[[[124.968682,-8.89279],[125.086246,-8.656887],[125.947072,-8.432095],[126.644704,-8.398247],[126.957243,-8.273345],[127.335928,-8.397317],[126.967992,-8.668256],[125.925885,-9.106007],[125.08852,-9.393173],[125.07002,-9.089987],[124.968682,-8.89279]]]}},{type:"Feature",id:"TTO",properties:{name:"Trinidad and Tobago",iso_a2:"TT"},geometry:{type:"Polygon",coordinates:[[[-61.68,10.76],[-61.105,10.89],[-60.895,10.855],[-60.935,10.11],[-61.77,10],[-61.95,10.09],[-61.66,10.365],[-61.68,10.76]]]}},{type:"Feature",id:"TUN",properties:{name:"Tunisia",iso_a2:"TN"},geometry:{type:"Polygon",coordinates:[[[9.48214,30.307556],[9.055603,32.102692],[8.439103,32.506285],[8.430473,32.748337],[7.612642,33.344115],[7.524482,34.097376],[8.140981,34.655146],[8.376368,35.479876],[8.217824,36.433177],[8.420964,36.946427],[9.509994,37.349994],[10.210002,37.230002],[10.18065,36.724038],[11.028867,37.092103],[11.100026,36.899996],[10.600005,36.41],[10.593287,35.947444],[10.939519,35.698984],[10.807847,34.833507],[10.149593,34.330773],[10.339659,33.785742],[10.856836,33.76874],[11.108501,33.293343],[11.488787,33.136996],[11.432253,32.368903],[10.94479,32.081815],[10.636901,31.761421],[9.950225,31.37607],[10.056575,30.961831],[9.970017,30.539325],[9.48214,30.307556]]]}},{type:"Feature",id:"TUR",properties:{name:"Turkey",iso_a2:"TR"},geometry:{type:"MultiPolygon",coordinates:[[[[36.913127,41.335358],[38.347665,40.948586],[39.512607,41.102763],[40.373433,41.013673],[41.554084,41.535656],[42.619549,41.583173],[43.582746,41.092143],[43.752658,40.740201],[43.656436,40.253564],[44.400009,40.005],[44.79399,39.713003],[44.109225,39.428136],[44.421403,38.281281],[44.225756,37.971584],[44.772699,37.170445],[44.293452,37.001514],[43.942259,37.256228],[42.779126,37.385264],[42.349591,37.229873],[41.212089,37.074352],[40.673259,37.091276],[39.52258,36.716054],[38.699891,36.712927],[38.167727,36.90121],[37.066761,36.623036],[36.739494,36.81752],[36.685389,36.259699],[36.41755,36.040617],[36.149763,35.821535],[35.782085,36.274995],[36.160822,36.650606],[35.550936,36.565443],[34.714553,36.795532],[34.026895,36.21996],[32.509158,36.107564],[31.699595,36.644275],[30.621625,36.677865],[30.391096,36.262981],[29.699976,36.144357],[28.732903,36.676831],[27.641187,36.658822],[27.048768,37.653361],[26.318218,38.208133],[26.8047,38.98576],[26.170785,39.463612],[27.28002,40.420014],[28.819978,40.460011],[29.240004,41.219991],[31.145934,41.087622],[32.347979,41.736264],[33.513283,42.01896],[35.167704,42.040225],[36.913127,41.335358]]],[[[27.192377,40.690566],[26.358009,40.151994],[26.043351,40.617754],[26.056942,40.824123],[26.294602,40.936261],[26.604196,41.562115],[26.117042,41.826905],[27.135739,42.141485],[27.99672,42.007359],[28.115525,41.622886],[28.988443,41.299934],[28.806438,41.054962],[27.619017,40.999823],[27.192377,40.690566]]]]}},{type:"Feature",id:"TWN",properties:{name:"Taiwan",iso_a2:"TW"},geometry:{type:"Polygon",coordinates:[[[121.777818,24.394274],[121.175632,22.790857],[120.74708,21.970571],[120.220083,22.814861],[120.106189,23.556263],[120.69468,24.538451],[121.495044,25.295459],[121.951244,24.997596],[121.777818,24.394274]]]}},{type:"Feature",id:"TZA",properties:{name:"United Republic of Tanzania",iso_a2:"TZ"},geometry:{type:"Polygon",coordinates:[[[33.903711,-.95],[34.07262,-1.05982],[37.69869,-3.09699],[37.7669,-3.67712],[39.20222,-4.67677],[38.74054,-5.90895],[38.79977,-6.47566],[39.44,-6.84],[39.47,-7.1],[39.19469,-7.7039],[39.25203,-8.00781],[39.18652,-8.48551],[39.53574,-9.11237],[39.9496,-10.0984],[40.31659,-10.3171],[39.521,-10.89688],[38.427557,-11.285202],[37.82764,-11.26879],[37.47129,-11.56876],[36.775151,-11.594537],[36.514082,-11.720938],[35.312398,-11.439146],[34.559989,-11.52002],[34.28,-10.16],[33.940838,-9.693674],[33.73972,-9.41715],[32.759375,-9.230599],[32.191865,-8.930359],[31.556348,-8.762049],[31.157751,-8.594579],[30.74,-8.34],[30.2,-7.08],[29.62,-6.52],[29.419993,-5.939999],[29.519987,-5.419979],[29.339998,-4.499983],[29.753512,-4.452389],[30.11632,-4.09012],[30.50554,-3.56858],[30.75224,-3.35931],[30.74301,-3.03431],[30.52766,-2.80762],[30.46967,-2.41383],[30.758309,-2.28725],[30.816135,-1.698914],[30.419105,-1.134659],[30.76986,-1.01455],[31.86617,-1.02736],[33.903711,-.95]]]}},{type:"Feature",id:"UGA",properties:{name:"Uganda",iso_a2:"UG"},geometry:{type:"Polygon",coordinates:[[[31.86617,-1.02736],[30.76986,-1.01455],[30.419105,-1.134659],[29.821519,-1.443322],[29.579466,-1.341313],[29.587838,-.587406],[29.8195,-.2053],[29.875779,.59738],[30.086154,1.062313],[30.468508,1.583805],[30.85267,1.849396],[31.174149,2.204465],[30.77332,2.33989],[30.83385,3.50917],[31.24556,3.7819],[31.88145,3.55827],[32.68642,3.79232],[33.39,3.79],[34.005,4.249885],[34.47913,3.5556],[34.59607,3.05374],[35.03599,1.90584],[34.6721,1.17694],[34.18,.515],[33.893569,.109814],[33.903711,-.95],[31.86617,-1.02736]]]}},{type:"Feature",id:"UKR",properties:{name:"Ukraine",iso_a2:"UA"},geometry:{type:"Polygon",coordinates:[[[31.785998,52.101678],[32.159412,52.061267],[32.412058,52.288695],[32.715761,52.238465],[33.7527,52.335075],[34.391731,51.768882],[34.141978,51.566413],[34.224816,51.255993],[35.022183,51.207572],[35.377924,50.773955],[35.356116,50.577197],[36.626168,50.225591],[37.39346,50.383953],[38.010631,49.915662],[38.594988,49.926462],[40.069058,49.601055],[40.080789,49.30743],[39.674664,48.783818],[39.895632,48.232405],[39.738278,47.898937],[38.770585,47.825608],[38.255112,47.5464],[38.223538,47.10219],[37.425137,47.022221],[36.759855,46.6987],[35.823685,46.645964],[34.962342,46.273197],[35.020788,45.651219],[35.510009,45.409993],[36.529998,45.46999],[36.334713,45.113216],[35.239999,44.939996],[33.882511,44.361479],[33.326421,44.564877],[33.546924,45.034771],[32.454174,45.327466],[32.630804,45.519186],[33.588162,45.851569],[33.298567,46.080598],[31.74414,46.333348],[31.675307,46.706245],[30.748749,46.5831],[30.377609,46.03241],[29.603289,45.293308],[29.149725,45.464925],[28.679779,45.304031],[28.233554,45.488283],[28.485269,45.596907],[28.659987,45.939987],[28.933717,46.25883],[28.862972,46.437889],[29.072107,46.517678],[29.170654,46.379262],[29.759972,46.349988],[30.024659,46.423937],[29.83821,46.525326],[29.908852,46.674361],[29.559674,46.928583],[29.415135,47.346645],[29.050868,47.510227],[29.122698,47.849095],[28.670891,48.118149],[28.259547,48.155562],[27.522537,48.467119],[26.857824,48.368211],[26.619337,48.220726],[26.19745,48.220881],[25.945941,47.987149],[25.207743,47.891056],[24.866317,47.737526],[24.402056,47.981878],[23.760958,47.985598],[23.142236,48.096341],[22.710531,47.882194],[22.64082,48.15024],[22.085608,48.422264],[22.280842,48.825392],[22.558138,49.085738],[22.776419,49.027395],[22.51845,49.476774],[23.426508,50.308506],[23.922757,50.424881],[24.029986,50.705407],[23.527071,51.578454],[24.005078,51.617444],[24.553106,51.888461],[25.327788,51.910656],[26.337959,51.832289],[27.454066,51.592303],[28.241615,51.572227],[28.617613,51.427714],[28.992835,51.602044],[29.254938,51.368234],[30.157364,51.416138],[30.555117,51.319503],[30.619454,51.822806],[30.927549,52.042353],[31.785998,52.101678]]]}},{type:"Feature",id:"URY",properties:{name:"Uruguay",iso_a2:"UY"},geometry:{type:"Polygon",coordinates:[[[-57.625133,-30.216295],[-56.976026,-30.109686],[-55.973245,-30.883076],[-55.60151,-30.853879],[-54.572452,-31.494511],[-53.787952,-32.047243],[-53.209589,-32.727666],[-53.650544,-33.202004],[-53.373662,-33.768378],[-53.806426,-34.396815],[-54.935866,-34.952647],[-55.67409,-34.752659],[-56.215297,-34.859836],[-57.139685,-34.430456],[-57.817861,-34.462547],[-58.427074,-33.909454],[-58.349611,-33.263189],[-58.132648,-33.040567],[-58.14244,-32.044504],[-57.874937,-31.016556],[-57.625133,-30.216295]]]}},{type:"Feature",id:"USA",properties:{name:"United States of America",iso_a2:"US"},geometry:{type:"MultiPolygon",coordinates:[[[[-155.54211,19.08348],[-155.68817,18.91619],[-155.93665,19.05939],[-155.90806,19.33888],[-156.07347,19.70294],[-156.02368,19.81422],[-155.85008,19.97729],[-155.91907,20.17395],[-155.86108,20.26721],[-155.78505,20.2487],[-155.40214,20.07975],[-155.22452,19.99302],[-155.06226,19.8591],[-154.80741,19.50871],[-154.83147,19.45328],[-155.22217,19.23972],[-155.54211,19.08348]]],[[[-156.07926,20.64397],[-156.41445,20.57241],[-156.58673,20.783],[-156.70167,20.8643],[-156.71055,20.92676],[-156.61258,21.01249],[-156.25711,20.91745],[-155.99566,20.76404],[-156.07926,20.64397]]],[[[-156.75824,21.17684],[-156.78933,21.06873],[-157.32521,21.09777],[-157.25027,21.21958],[-156.75824,21.17684]]],[[[-157.65283,21.32217],[-157.70703,21.26442],[-157.7786,21.27729],[-158.12667,21.31244],[-158.2538,21.53919],[-158.29265,21.57912],[-158.0252,21.71696],[-157.94161,21.65272],[-157.65283,21.32217]]],[[[-159.34512,21.982],[-159.46372,21.88299],[-159.80051,22.06533],[-159.74877,22.1382],[-159.5962,22.23618],[-159.36569,22.21494],[-159.34512,21.982]]],[[[-94.81758,49.38905],[-94.64,48.84],[-94.32914,48.67074],[-93.63087,48.60926],[-92.61,48.45],[-91.64,48.14],[-90.83,48.27],[-89.6,48.01],[-89.272917,48.019808],[-88.378114,48.302918],[-87.439793,47.94],[-86.461991,47.553338],[-85.652363,47.220219],[-84.87608,46.900083],[-84.779238,46.637102],[-84.543749,46.538684],[-84.6049,46.4396],[-84.3367,46.40877],[-84.14212,46.512226],[-84.091851,46.275419],[-83.890765,46.116927],[-83.616131,46.116927],[-83.469551,45.994686],[-83.592851,45.816894],[-82.550925,45.347517],[-82.337763,44.44],[-82.137642,43.571088],[-82.43,42.98],[-82.9,42.43],[-83.12,42.08],[-83.142,41.975681],[-83.02981,41.832796],[-82.690089,41.675105],[-82.439278,41.675105],[-81.277747,42.209026],[-80.247448,42.3662],[-78.939362,42.863611],[-78.92,42.965],[-79.01,43.27],[-79.171674,43.466339],[-78.72028,43.625089],[-77.737885,43.629056],[-76.820034,43.628784],[-76.5,44.018459],[-76.375,44.09631],[-75.31821,44.81645],[-74.867,45.00048],[-73.34783,45.00738],[-71.50506,45.0082],[-71.405,45.255],[-71.08482,45.30524],[-70.66,45.46],[-70.305,45.915],[-69.99997,46.69307],[-69.237216,47.447781],[-68.905,47.185],[-68.23444,47.35486],[-67.79046,47.06636],[-67.79134,45.70281],[-67.13741,45.13753],[-66.96466,44.8097],[-68.03252,44.3252],[-69.06,43.98],[-70.11617,43.68405],[-70.645476,43.090238],[-70.81489,42.8653],[-70.825,42.335],[-70.495,41.805],[-70.08,41.78],[-70.185,42.145],[-69.88497,41.92283],[-69.96503,41.63717],[-70.64,41.475],[-71.12039,41.49445],[-71.86,41.32],[-72.295,41.27],[-72.87643,41.22065],[-73.71,40.931102],[-72.24126,41.11948],[-71.945,40.93],[-73.345,40.63],[-73.982,40.628],[-73.952325,40.75075],[-74.25671,40.47351],[-73.96244,40.42763],[-74.17838,39.70926],[-74.90604,38.93954],[-74.98041,39.1964],[-75.20002,39.24845],[-75.52805,39.4985],[-75.32,38.96],[-75.071835,38.782032],[-75.05673,38.40412],[-75.37747,38.01551],[-75.94023,37.21689],[-76.03127,37.2566],[-75.72205,37.93705],[-76.23287,38.319215],[-76.35,39.15],[-76.542725,38.717615],[-76.32933,38.08326],[-76.989998,38.239992],[-76.30162,37.917945],[-76.25874,36.9664],[-75.9718,36.89726],[-75.86804,36.55125],[-75.72749,35.55074],[-76.36318,34.80854],[-77.397635,34.51201],[-78.05496,33.92547],[-78.55435,33.86133],[-79.06067,33.49395],[-79.20357,33.15839],[-80.301325,32.509355],[-80.86498,32.0333],[-81.33629,31.44049],[-81.49042,30.72999],[-81.31371,30.03552],[-80.98,29.18],[-80.535585,28.47213],[-80.53,28.04],[-80.056539,26.88],[-80.088015,26.205765],[-80.13156,25.816775],[-80.38103,25.20616],[-80.68,25.08],[-81.17213,25.20126],[-81.33,25.64],[-81.71,25.87],[-82.24,26.73],[-82.70515,27.49504],[-82.85526,27.88624],[-82.65,28.55],[-82.93,29.1],[-83.70959,29.93656],[-84.1,30.09],[-85.10882,29.63615],[-85.28784,29.68612],[-85.7731,30.15261],[-86.4,30.4],[-87.53036,30.27433],[-88.41782,30.3849],[-89.18049,30.31598],[-89.593831,30.159994],[-89.413735,29.89419],[-89.43,29.48864],[-89.21767,29.29108],[-89.40823,29.15961],[-89.77928,29.30714],[-90.15463,29.11743],[-90.880225,29.148535],[-91.626785,29.677],[-92.49906,29.5523],[-93.22637,29.78375],[-93.84842,29.71363],[-94.69,29.48],[-95.60026,28.73863],[-96.59404,28.30748],[-97.14,27.83],[-97.37,27.38],[-97.38,26.69],[-97.33,26.21],[-97.14,25.87],[-97.53,25.84],[-98.24,26.06],[-99.02,26.37],[-99.3,26.84],[-99.52,27.54],[-100.11,28.11],[-100.45584,28.69612],[-100.9576,29.38071],[-101.6624,29.7793],[-102.48,29.76],[-103.11,28.97],[-103.94,29.27],[-104.45697,29.57196],[-104.70575,30.12173],[-105.03737,30.64402],[-105.63159,31.08383],[-106.1429,31.39995],[-106.50759,31.75452],[-108.24,31.754854],[-108.24194,31.34222],[-109.035,31.34194],[-111.02361,31.33472],[-113.30498,32.03914],[-114.815,32.52528],[-114.72139,32.72083],[-115.99135,32.61239],[-117.12776,32.53534],[-117.295938,33.046225],[-117.944,33.621236],[-118.410602,33.740909],[-118.519895,34.027782],[-119.081,34.078],[-119.438841,34.348477],[-120.36778,34.44711],[-120.62286,34.60855],[-120.74433,35.15686],[-121.71457,36.16153],[-122.54747,37.55176],[-122.51201,37.78339],[-122.95319,38.11371],[-123.7272,38.95166],[-123.86517,39.76699],[-124.39807,40.3132],[-124.17886,41.14202],[-124.2137,41.99964],[-124.53284,42.76599],[-124.14214,43.70838],[-124.020535,44.615895],[-123.89893,45.52341],[-124.079635,46.86475],[-124.39567,47.72017],[-124.68721,48.184433],[-124.566101,48.379715],[-123.12,48.04],[-122.58736,47.096],[-122.34,47.36],[-122.5,48.18],[-122.84,49],[-120,49],[-117.03121,49],[-116.04818,49],[-113,49],[-110.05,49],[-107.05,49],[-104.04826,48.99986],[-100.65,49],[-97.22872,49.0007],[-95.15907,49],[-95.15609,49.38425],[-94.81758,49.38905]]],[[[-153.006314,57.115842],[-154.00509,56.734677],[-154.516403,56.992749],[-154.670993,57.461196],[-153.76278,57.816575],[-153.228729,57.968968],[-152.564791,57.901427],[-152.141147,57.591059],[-153.006314,57.115842]]],[[[-165.579164,59.909987],[-166.19277,59.754441],[-166.848337,59.941406],[-167.455277,60.213069],[-166.467792,60.38417],[-165.67443,60.293607],[-165.579164,59.909987]]],[[[-171.731657,63.782515],[-171.114434,63.592191],[-170.491112,63.694975],[-169.682505,63.431116],[-168.689439,63.297506],[-168.771941,63.188598],[-169.52944,62.976931],[-170.290556,63.194438],[-170.671386,63.375822],[-171.553063,63.317789],[-171.791111,63.405846],[-171.731657,63.782515]]],[[[-155.06779,71.147776],[-154.344165,70.696409],[-153.900006,70.889989],[-152.210006,70.829992],[-152.270002,70.600006],[-150.739992,70.430017],[-149.720003,70.53001],[-147.613362,70.214035],[-145.68999,70.12001],[-144.920011,69.989992],[-143.589446,70.152514],[-142.07251,69.851938],[-140.985988,69.711998],[-140.992499,66.000029],[-140.99777,60.306397],[-140.012998,60.276838],[-139.039,60.000007],[-138.34089,59.56211],[-137.4525,58.905],[-136.47972,59.46389],[-135.47583,59.78778],[-134.945,59.27056],[-134.27111,58.86111],[-133.355549,58.410285],[-132.73042,57.69289],[-131.70781,56.55212],[-130.00778,55.91583],[-129.979994,55.284998],[-130.53611,54.802753],[-131.085818,55.178906],[-131.967211,55.497776],[-132.250011,56.369996],[-133.539181,57.178887],[-134.078063,58.123068],[-135.038211,58.187715],[-136.628062,58.212209],[-137.800006,58.499995],[-139.867787,59.537762],[-140.825274,59.727517],[-142.574444,60.084447],[-143.958881,59.99918],[-145.925557,60.45861],[-147.114374,60.884656],[-148.224306,60.672989],[-148.018066,59.978329],[-148.570823,59.914173],[-149.727858,59.705658],[-150.608243,59.368211],[-151.716393,59.155821],[-151.859433,59.744984],[-151.409719,60.725803],[-150.346941,61.033588],[-150.621111,61.284425],[-151.895839,60.727198],[-152.57833,60.061657],[-154.019172,59.350279],[-153.287511,58.864728],[-154.232492,58.146374],[-155.307491,57.727795],[-156.308335,57.422774],[-156.556097,56.979985],[-158.117217,56.463608],[-158.433321,55.994154],[-159.603327,55.566686],[-160.28972,55.643581],[-161.223048,55.364735],[-162.237766,55.024187],[-163.069447,54.689737],[-164.785569,54.404173],[-164.942226,54.572225],[-163.84834,55.039431],[-162.870001,55.348043],[-161.804175,55.894986],[-160.563605,56.008055],[-160.07056,56.418055],[-158.684443,57.016675],[-158.461097,57.216921],[-157.72277,57.570001],[-157.550274,58.328326],[-157.041675,58.918885],[-158.194731,58.615802],[-158.517218,58.787781],[-159.058606,58.424186],[-159.711667,58.93139],[-159.981289,58.572549],[-160.355271,59.071123],[-161.355003,58.670838],[-161.968894,58.671665],[-162.054987,59.266925],[-161.874171,59.633621],[-162.518059,59.989724],[-163.818341,59.798056],[-164.662218,60.267484],[-165.346388,60.507496],[-165.350832,61.073895],[-166.121379,61.500019],[-165.734452,62.074997],[-164.919179,62.633076],[-164.562508,63.146378],[-163.753332,63.219449],[-163.067224,63.059459],[-162.260555,63.541936],[-161.53445,63.455817],[-160.772507,63.766108],[-160.958335,64.222799],[-161.518068,64.402788],[-160.777778,64.788604],[-161.391926,64.777235],[-162.45305,64.559445],[-162.757786,64.338605],[-163.546394,64.55916],[-164.96083,64.446945],[-166.425288,64.686672],[-166.845004,65.088896],[-168.11056,65.669997],[-166.705271,66.088318],[-164.47471,66.57666],[-163.652512,66.57666],[-163.788602,66.077207],[-161.677774,66.11612],[-162.489715,66.735565],[-163.719717,67.116395],[-164.430991,67.616338],[-165.390287,68.042772],[-166.764441,68.358877],[-166.204707,68.883031],[-164.430811,68.915535],[-163.168614,69.371115],[-162.930566,69.858062],[-161.908897,70.33333],[-160.934797,70.44769],[-159.039176,70.891642],[-158.119723,70.824721],[-156.580825,71.357764],[-155.06779,71.147776]]]]}},{type:"Feature",id:"UZB",properties:{name:"Uzbekistan",iso_a2:"UZ"},geometry:{type:"Polygon",coordinates:[[[66.518607,37.362784],[66.54615,37.974685],[65.215999,38.402695],[64.170223,38.892407],[63.518015,39.363257],[62.37426,40.053886],[61.882714,41.084857],[61.547179,41.26637],[60.465953,41.220327],[60.083341,41.425146],[59.976422,42.223082],[58.629011,42.751551],[57.78653,42.170553],[56.932215,41.826026],[57.096391,41.32231],[55.968191,41.308642],[55.928917,44.995858],[58.503127,45.586804],[58.689989,45.500014],[60.239972,44.784037],[61.05832,44.405817],[62.0133,43.504477],[63.185787,43.650075],[64.900824,43.728081],[66.098012,42.99766],[66.023392,41.994646],[66.510649,41.987644],[66.714047,41.168444],[67.985856,41.135991],[68.259896,40.662325],[68.632483,40.668681],[69.070027,41.384244],[70.388965,42.081308],[70.962315,42.266154],[71.259248,42.167711],[70.420022,41.519998],[71.157859,41.143587],[71.870115,41.3929],[73.055417,40.866033],[71.774875,40.145844],[71.014198,40.244366],[70.601407,40.218527],[70.45816,40.496495],[70.666622,40.960213],[69.329495,40.727824],[69.011633,40.086158],[68.536416,39.533453],[67.701429,39.580478],[67.44222,39.140144],[68.176025,38.901553],[68.392033,38.157025],[67.83,37.144994],[67.075782,37.356144],[66.518607,37.362784]]]}},{type:"Feature",id:"VEN",properties:{name:"Venezuela",iso_a2:"VE"},geometry:{type:"Polygon",coordinates:[[[-71.331584,11.776284],[-71.360006,11.539994],[-71.94705,11.423282],[-71.620868,10.96946],[-71.633064,10.446494],[-72.074174,9.865651],[-71.695644,9.072263],[-71.264559,9.137195],[-71.039999,9.859993],[-71.350084,10.211935],[-71.400623,10.968969],[-70.155299,11.375482],[-70.293843,11.846822],[-69.943245,12.162307],[-69.5843,11.459611],[-68.882999,11.443385],[-68.233271,10.885744],[-68.194127,10.554653],[-67.296249,10.545868],[-66.227864,10.648627],[-65.655238,10.200799],[-64.890452,10.077215],[-64.329479,10.389599],[-64.318007,10.641418],[-63.079322,10.701724],[-61.880946,10.715625],[-62.730119,10.420269],[-62.388512,9.948204],[-61.588767,9.873067],[-60.830597,9.38134],[-60.671252,8.580174],[-60.150096,8.602757],[-59.758285,8.367035],[-60.550588,7.779603],[-60.637973,7.415],[-60.295668,7.043911],[-60.543999,6.856584],[-61.159336,6.696077],[-61.139415,6.234297],[-61.410303,5.959068],[-60.733574,5.200277],[-60.601179,4.918098],[-60.966893,4.536468],[-62.08543,4.162124],[-62.804533,4.006965],[-63.093198,3.770571],[-63.888343,4.02053],[-64.628659,4.148481],[-64.816064,4.056445],[-64.368494,3.79721],[-64.408828,3.126786],[-64.269999,2.497006],[-63.422867,2.411068],[-63.368788,2.2009],[-64.083085,1.916369],[-64.199306,1.492855],[-64.611012,1.328731],[-65.354713,1.095282],[-65.548267,.789254],[-66.325765,.724452],[-66.876326,1.253361],[-67.181294,2.250638],[-67.447092,2.600281],[-67.809938,2.820655],[-67.303173,3.318454],[-67.337564,3.542342],[-67.621836,3.839482],[-67.823012,4.503937],[-67.744697,5.221129],[-67.521532,5.55687],[-67.34144,6.095468],[-67.695087,6.267318],[-68.265052,6.153268],[-68.985319,6.206805],[-69.38948,6.099861],[-70.093313,6.960376],[-70.674234,7.087785],[-71.960176,6.991615],[-72.198352,7.340431],[-72.444487,7.423785],[-72.479679,7.632506],[-72.360901,8.002638],[-72.439862,8.405275],[-72.660495,8.625288],[-72.78873,9.085027],[-73.304952,9.152],[-73.027604,9.73677],[-72.905286,10.450344],[-72.614658,10.821975],[-72.227575,11.108702],[-71.973922,11.608672],[-71.331584,11.776284]]]}},{type:"Feature",id:"VNM",properties:{name:"Vietnam",iso_a2:"VN"},geometry:{type:"Polygon",coordinates:[[[108.05018,21.55238],[106.715068,20.696851],[105.881682,19.75205],[105.662006,19.058165],[106.426817,18.004121],[107.361954,16.697457],[108.269495,16.079742],[108.877107,15.276691],[109.33527,13.426028],[109.200136,11.666859],[108.36613,11.008321],[107.220929,10.364484],[106.405113,9.53084],[105.158264,8.59976],[104.795185,9.241038],[105.076202,9.918491],[104.334335,10.486544],[105.199915,10.88931],[106.24967,10.961812],[105.810524,11.567615],[107.491403,12.337206],[107.614548,13.535531],[107.382727,14.202441],[107.564525,15.202173],[107.312706,15.908538],[106.556008,16.604284],[105.925762,17.485315],[105.094598,18.666975],[103.896532,19.265181],[104.183388,19.624668],[104.822574,19.886642],[104.435,20.758733],[103.203861,20.766562],[102.754896,21.675137],[102.170436,22.464753],[102.706992,22.708795],[103.504515,22.703757],[104.476858,22.81915],[105.329209,23.352063],[105.811247,22.976892],[106.725403,22.794268],[106.567273,22.218205],[107.04342,21.811899],[108.05018,21.55238]]]}},{type:"Feature",id:"VUT",properties:{name:"Vanuatu",iso_a2:"VU"},geometry:{type:"MultiPolygon",coordinates:[[[[167.844877,-16.466333],[167.515181,-16.59785],[167.180008,-16.159995],[167.216801,-15.891846],[167.844877,-16.466333]]],[[[167.107712,-14.93392],[167.270028,-15.740021],[167.001207,-15.614602],[166.793158,-15.668811],[166.649859,-15.392704],[166.629137,-14.626497],[167.107712,-14.93392]]]]}},{type:"Feature",id:"PSE",properties:{name:"West Bank",iso_a2:"PS"},geometry:{type:"Polygon",coordinates:[[[35.545665,32.393992],[35.545252,31.782505],[35.397561,31.489086],[34.927408,31.353435],[34.970507,31.616778],[35.225892,31.754341],[34.974641,31.866582],[35.18393,32.532511],[35.545665,32.393992]]]}},{type:"Feature",id:"YEM",properties:{name:"Yemen",iso_a2:"YE"},geometry:{type:"Polygon",coordinates:[[[53.108573,16.651051],[52.385206,16.382411],[52.191729,15.938433],[52.168165,15.59742],[51.172515,15.17525],[49.574576,14.708767],[48.679231,14.003202],[48.238947,13.94809],[47.938914,14.007233],[47.354454,13.59222],[46.717076,13.399699],[45.877593,13.347764],[45.62505,13.290946],[45.406459,13.026905],[45.144356,12.953938],[44.989533,12.699587],[44.494576,12.721653],[44.175113,12.58595],[43.482959,12.6368],[43.222871,13.22095],[43.251448,13.767584],[43.087944,14.06263],[42.892245,14.802249],[42.604873,15.213335],[42.805015,15.261963],[42.702438,15.718886],[42.823671,15.911742],[42.779332,16.347891],[43.218375,16.66689],[43.115798,17.08844],[43.380794,17.579987],[43.791519,17.319977],[44.062613,17.410359],[45.216651,17.433329],[45.399999,17.333335],[46.366659,17.233315],[46.749994,17.283338],[47.000005,16.949999],[47.466695,17.116682],[48.183344,18.166669],[49.116672,18.616668],[52.00001,19.000003],[52.782184,17.349742],[53.108573,16.651051]]]}},{type:"Feature",id:"ZAF",properties:{name:"South Africa",iso_a2:"ZA"},geometry:{type:"Polygon",coordinates:[[[31.521001,-29.257387],[31.325561,-29.401978],[30.901763,-29.909957],[30.622813,-30.423776],[30.055716,-31.140269],[28.925553,-32.172041],[28.219756,-32.771953],[27.464608,-33.226964],[26.419452,-33.61495],[25.909664,-33.66704],[25.780628,-33.944646],[25.172862,-33.796851],[24.677853,-33.987176],[23.594043,-33.794474],[22.988189,-33.916431],[22.574157,-33.864083],[21.542799,-34.258839],[20.689053,-34.417175],[20.071261,-34.795137],[19.616405,-34.819166],[19.193278,-34.462599],[18.855315,-34.444306],[18.424643,-33.997873],[18.377411,-34.136521],[18.244499,-33.867752],[18.25008,-33.281431],[17.92519,-32.611291],[18.24791,-32.429131],[18.221762,-31.661633],[17.566918,-30.725721],[17.064416,-29.878641],[17.062918,-29.875954],[16.344977,-28.576705],[16.824017,-28.082162],[17.218929,-28.355943],[17.387497,-28.783514],[17.836152,-28.856378],[18.464899,-29.045462],[19.002127,-28.972443],[19.894734,-28.461105],[19.895768,-24.76779],[20.165726,-24.917962],[20.758609,-25.868136],[20.66647,-26.477453],[20.889609,-26.828543],[21.605896,-26.726534],[22.105969,-26.280256],[22.579532,-25.979448],[22.824271,-25.500459],[23.312097,-25.26869],[23.73357,-25.390129],[24.211267,-25.670216],[25.025171,-25.71967],[25.664666,-25.486816],[25.765849,-25.174845],[25.941652,-24.696373],[26.485753,-24.616327],[26.786407,-24.240691],[27.11941,-23.574323],[28.017236,-22.827754],[29.432188,-22.091313],[29.839037,-22.102216],[30.322883,-22.271612],[30.659865,-22.151567],[31.191409,-22.25151],[31.670398,-23.658969],[31.930589,-24.369417],[31.752408,-25.484284],[31.837778,-25.843332],[31.333158,-25.660191],[31.04408,-25.731452],[30.949667,-26.022649],[30.676609,-26.398078],[30.685962,-26.743845],[31.282773,-27.285879],[31.86806,-27.177927],[32.071665,-26.73382],[32.83012,-26.742192],[32.580265,-27.470158],[32.462133,-28.301011],[32.203389,-28.752405],[31.521001,-29.257387]],[[28.978263,-28.955597],[28.5417,-28.647502],[28.074338,-28.851469],[27.532511,-29.242711],[26.999262,-29.875954],[27.749397,-30.645106],[28.107205,-30.545732],[28.291069,-30.226217],[28.8484,-30.070051],[29.018415,-29.743766],[29.325166,-29.257387],[28.978263,-28.955597]]]}},{type:"Feature",id:"ZMB",properties:{name:"Zambia",iso_a2:"ZM"},geometry:{type:"Polygon",coordinates:[[[32.759375,-9.230599],[33.231388,-9.676722],[33.485688,-10.525559],[33.31531,-10.79655],[33.114289,-11.607198],[33.306422,-12.435778],[32.991764,-12.783871],[32.688165,-13.712858],[33.214025,-13.97186],[30.179481,-14.796099],[30.274256,-15.507787],[29.516834,-15.644678],[28.947463,-16.043051],[28.825869,-16.389749],[28.467906,-16.4684],[27.598243,-17.290831],[27.044427,-17.938026],[26.706773,-17.961229],[26.381935,-17.846042],[25.264226,-17.73654],[25.084443,-17.661816],[25.07695,-17.578823],[24.682349,-17.353411],[24.033862,-17.295843],[23.215048,-17.523116],[22.562478,-16.898451],[21.887843,-16.08031],[21.933886,-12.898437],[24.016137,-12.911046],[23.930922,-12.565848],[24.079905,-12.191297],[23.904154,-11.722282],[24.017894,-11.237298],[23.912215,-10.926826],[24.257155,-10.951993],[24.314516,-11.262826],[24.78317,-11.238694],[25.418118,-11.330936],[25.75231,-11.784965],[26.553088,-11.92444],[27.16442,-11.608748],[27.388799,-12.132747],[28.155109,-12.272481],[28.523562,-12.698604],[28.934286,-13.248958],[29.699614,-13.257227],[29.616001,-12.178895],[29.341548,-12.360744],[28.642417,-11.971569],[28.372253,-11.793647],[28.49607,-10.789884],[28.673682,-9.605925],[28.449871,-9.164918],[28.734867,-8.526559],[29.002912,-8.407032],[30.346086,-8.238257],[30.740015,-8.340007],[31.157751,-8.594579],[31.556348,-8.762049],[32.191865,-8.930359],[32.759375,-9.230599]]]}},{type:"Feature",id:"ZWE",properties:{name:"Zimbabwe",iso_a2:"ZW"},geometry:{type:"Polygon",coordinates:[[[31.191409,-22.25151],[30.659865,-22.151567],[30.322883,-22.271612],[29.839037,-22.102216],[29.432188,-22.091313],[28.794656,-21.639454],[28.02137,-21.485975],[27.727228,-20.851802],[27.724747,-20.499059],[27.296505,-20.39152],[26.164791,-19.293086],[25.850391,-18.714413],[25.649163,-18.536026],[25.264226,-17.73654],[26.381935,-17.846042],[26.706773,-17.961229],[27.044427,-17.938026],[27.598243,-17.290831],[28.467906,-16.4684],[28.825869,-16.389749],[28.947463,-16.043051],[29.516834,-15.644678],[30.274256,-15.507787],[30.338955,-15.880839],[31.173064,-15.860944],[31.636498,-16.07199],[31.852041,-16.319417],[32.328239,-16.392074],[32.847639,-16.713398],[32.849861,-17.979057],[32.654886,-18.67209],[32.611994,-19.419383],[32.772708,-19.715592],[32.659743,-20.30429],[32.508693,-20.395292],[32.244988,-21.116489],[31.191409,-22.25151]]]}}],t={type:e,features:o};export{t as default,o as features,e as type}; diff --git a/wp-content/plugins/fluent-crm/assets/admin/Modules/Reports/ReportsHome.js b/wp-content/plugins/fluent-crm/assets/admin/Modules/Reports/ReportsHome.js new file mode 100644 index 0000000..0b38296 --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/admin/Modules/Reports/ReportsHome.js @@ -0,0 +1 @@ +import{k as t,e,aY as a,aL as r,aK as s,ay as n,aJ as o,az as i,aB as l,W as c,E as d,aO as p,ba as u,a6 as h,o as m,n as _,aI as g,j as f,h as v,i as b,aH as y,g as C}from"../../../vendor-element-plus.js?ver=3.1.8";import{q as w,k,v as $,W as x,X as S,Z as T,$ as M,a8 as D,c4 as R,r as O,d as L,aQ as A,Y as I,ab as V,aa as E,a9 as P,a0 as N,a5 as Y,J as j,az as G,c8 as F,av as z,a6 as B,b2 as U,ax as q,bB as H,_ as W}from"../../../vendor.js?ver=3.1.8";import{_ as Z,a as K,T as Q,I as X}from"../../../fc-bits-ui.js?ver=3.1.8";import{C as J,_ as tt}from"../../../fc-bits.js?ver=3.1.8";import{d as et,g as at}from"../../../data_config.js?ver=3.1.8";import{C as rt}from"../../../CalendarIcon.js?ver=3.1.8";import{P as st}from"../../../PageHeader.js?ver=3.1.8";import{B as nt}from"../../../BaseCard.js?ver=3.1.8";import{A as ot}from"../../../_AjaxSelector.js?ver=3.1.8";import{C as it}from"../../../CustomIcon.js?ver=3.1.8";import{C as lt}from"../../../Confirm.js?ver=3.1.8";import{I as ct}from"../../../ItemCopier.js?ver=3.1.8";import dt from"../Funnels/parts/_LazyIndividualProgress.js?ver=3.1.8";import{P as pt}from"../../../PaginationBar.js?ver=3.1.8";const ut={class:"echart-container-wrapper"},ht={key:0,class:"fc-chart-placeholder"};const mt=Z({name:"ReportGrowthChart",props:{options:{type:Object,default:null},height:{type:[Number,String],default:420}},setup(t){const e=O(null);let a=null;const r=L(()=>!(!t.options||!Array.isArray(t.options.series))&&t.options.series.some(t=>Array.isArray(t.data)&&t.data.length>0)),s=()=>{a&&a.resize()},n=()=>{!a&&e.value&&(a=R(e.value),a.setOption(t.options||{},!0),window.addEventListener("resize",s))};return w(()=>{n()}),k(()=>t.options,t=>{e.value&&(a||n(),t&&a&&a.setOption(t,!0))},{deep:!0}),$(()=>{a&&(window.removeEventListener("resize",s),a.dispose(),a=null)}),{chartRef:e,hasData:r}}},[["render",function(t,e,a,r,s,n){return x(),S("div",ut,[T("div",{ref:"chartRef",class:"chart-container",style:M("height:"+a.height+"px;")},null,4),r.hasData?D("",!0):(x(),S("div",ht," Chart placeholder (ECharts not loaded or data is empty) "))])}],["__scopeId","data-v-62202f49"]]),_t={class:"fc_chart"};const gt=Z({components:{ReportGrowthChart:mt},name:"ChartBuilder",props:{currency_sign:{default:""},data_sets:{default:()=>[]},height:{type:[Number,String],default:260}},data:()=>({maxCumulativeValue:0,chartOptions:null,current_mode:"system"===Q.getCurrentTheme()?Q.getSystemTheme():Q.getCurrentTheme()}),watch:{data_sets:{deep:!0,immediate:!0,handler(){this.setupChartItemsX()}},currency_sign(){this.setupChartItemsX()},current_mode(){this.setupChartItemsX()}},mounted(){this.onThemeChanged=t=>{var e;this.current_mode=(null==(e=t.detail)?void 0:e.effective)||Q.getCurrentTheme()},window.addEventListener(K,this.onThemeChanged)},beforeUnmount(){this.onThemeChanged&&window.removeEventListener(K,this.onThemeChanged)},methods:{decodeHtmlEntities(t){if(!t)return"";const e=document.createElement("textarea");return e.innerHTML=t,e.value},setupChartItemsX(){if(!Array.isArray(this.data_sets)||!this.data_sets.length)return void(this.chartOptions={series:[]});const t="light"===this.current_mode,e=t?"#0E121B":"#ffffff",a=t?"#4A5565":"#9CA3AF",r=t?"#E1E4EA":"#2c3c4e",s=t?"#ffffff":"#283b56",n=t?"#222530":"#94A3B8",o=[],i=[];let l=0;this.each(this.data_sets,(t,e)=>{const a=[];this.each(t.data,(t,r)=>{const s=Number(t)||0;a.push(s),s>l&&(l=s),0===e&&o.push(r)});const r=this.getReadableSeriesColor(t.borderColor||t.backgroundColor||n,n),s={name:t.label,type:t.type||"line",data:a,yAxisIndex:e,smooth:!1,symbol:"circle",symbolSize:4,showSymbol:!1,itemStyle:{color:r},lineStyle:{color:r,width:2}};"bar"===s.type&&(s.barMaxWidth=24),t.fill&&(s.areaStyle={opacity:.15,color:t.backgroundColor||r}),i.push(s)}),l>1e4?l=1e3*Math.ceil(l/1e3):l>500?l=100*Math.ceil(l/100):l+=10,this.maxCumulativeValue=l;const c=this.decodeHtmlEntities(this.currency_sign),d=this.data_sets.map((t,e)=>({type:"value",name:t.label,position:0===e?"left":"right",min:0,max:this.maxCumulativeValue,minInterval:1,nameTextStyle:{color:a},axisLine:{lineStyle:{color:r}},axisTick:{lineStyle:{color:r}},axisLabel:{color:a,formatter:t=>Math.floor(t)!==t?"":c?c+" "+t:t},splitLine:{show:0===e,lineStyle:{color:r,type:"dashed"}}}));this.chartOptions={tooltip:{show:!0,showContent:!0,triggerOn:"mousemove|click",trigger:"axis",appendToBody:!0,confine:!1,transitionDuration:.1,backgroundColor:s,borderColor:s,borderWidth:1,textStyle:{color:e},axisPointer:{type:"cross",label:{backgroundColor:"#283b56"},crossStyle:{color:r}},formatter:t=>Array.isArray(t)&&t.length?t[0].axisValue+"
"+t.map(t=>''+""+t.seriesName+": "+c+J.formatMoney(Number(t.value)||0,0)).join("
"):"",extraCssText:"z-index:10000; pointer-events:auto;"},legend:{top:10,left:"center",data:this.data_sets.map(t=>t.label),icon:"roundRect",itemWidth:12,itemHeight:12,itemGap:20,textStyle:{color:e}},grid:{top:45,left:30,right:30,bottom:20,containLabel:!0},xAxis:{type:"category",data:o,axisPointer:{type:"shadow"},splitLine:{show:!1},axisLine:{lineStyle:{color:r}},axisTick:{lineStyle:{color:r}},axisLabel:{color:a,formatter:t=>{if(!t||!window.dayjs)return t;const e=window.dayjs(t);if(!e.isValid())return t;const a=window.dayjs();return e.year()!==a.year()?e.format("MMM D, YYYY"):e.format("MMM D")}}},yAxis:d,series:i}},getReadableSeriesColor(t,e){if(!t)return e;if("light"===this.current_mode)return t;const a=String(t).trim().match(/^#([0-9a-fA-F]{6})$/);if(!a)return t;const r=a[1];return.2126*parseInt(r.slice(0,2),16)+.7152*parseInt(r.slice(2,4),16)+.0722*parseInt(r.slice(4,6),16)<55?"#94A3B8":t}}},[["render",function(t,e,a,r,s,n){const o=A("ReportGrowthChart");return x(),S("div",_t,[s.chartOptions?(x(),I(o,{key:0,options:s.chartOptions,height:a.height},null,8,["options","height"])):D("",!0)])}]]),ft={class:"fcrm_card_widget_icon fcrm_icon_background_gray fcrm_mb-12"},vt={class:"fcrm_card_widget_title"},bt={class:"fcrm_card_widget_content fcrm_mt-4"},yt={key:0,class:"fcrm_card_stat_sub"};const Ct=Z({name:"StatCard",components:{Icons:X},props:{icon:{type:String,required:!0},label:{type:String,required:!0},value:{type:[String,Number],default:0},sub:{type:String,default:""},variant:{type:String,default:"default",validator:t=>["default","success","info","danger","muted","warning"].includes(t)}}},[["render",function(t,e,a,r,s,n){const o=A("Icons");return x(),S("div",{class:N(["fcrm_card_widget fcrm_icon_background_gray fcrm_card_widget_stat","fcrm_card_stat_"+a.variant])},[T("div",ft,[V(o,{"icon-name":a.icon},null,8,["icon-name"])]),T("div",vt,E(a.label),1),T("div",bt,[P(E(a.value)+" ",1),a.sub?(x(),S("span",yt,E(a.sub),1)):D("",!0)])],2)}]]);let wt=!1;const kt={class:"fcrm_country_map_layout"},$t={class:"fcrm_country_map_col"},xt={class:"fcrm_country_map_controls"},St={class:"icon"},Tt={class:"icon"},Mt={class:"icon"},Dt={key:0,class:"fcrm_country_map_empty"},Rt={key:0,class:"fcrm_country_list_col"},Ot={class:"fcrm_country_list_search"},Lt={class:"fcrm_country_list_scroll"},At={class:"fcrm_country_list_table"},It={scope:"col"},Vt={scope:"col",class:"fcrm_country_list_count"},Et={class:"fcrm_country_list_name"},Pt={class:"fcrm_country_list_count"},Nt={key:0},Yt={colspan:"2"};const jt=Z({name:"CountryMap",components:{Icons:X},props:{countryData:{type:Array,default:()=>[]},height:{type:[Number,String],default:420}},setup(t){const e=O(null),a=O("");let r=null;const s=L(()=>{var t;const e=(null==(t=window.fcAdmin)?void 0:t.countries)||[],a={};return e.forEach(t=>{a[t.code.toUpperCase().trim()]=t.title}),a}),n=L(()=>t.countryData&&t.countryData.length>0),o=L(()=>t.countryData?t.countryData.map(t=>{const e=(t.country_code||"").toUpperCase().trim();return{...t,country_code:e,country_name:s.value[e]}}).filter(t=>t.country_name).sort((t,e)=>e.contact_count-t.contact_count):[]),i=L(()=>{if(!a.value)return o.value;const t=a.value.toLowerCase();return o.value.filter(e=>e.country_name.toLowerCase().includes(t)||e.country_code.toLowerCase().includes(t))}),l=L(()=>o.value.length?Math.max(...o.value.map(t=>t.contact_count||0),1):1),c=L(()=>o.value.map(t=>({name:t.country_code,value:t.contact_count,countryName:t.country_name}))),d=t=>String(t).replace(/[&<>"']/g,t=>({"&":"&","<":"<",">":">",'"':""","'":"'"}[t])),p=()=>({tooltip:{trigger:"item",formatter:t=>{if(!t.data||!t.data.value){const e=s.value[t.name]||t.name;return d(e)+": 0"}const e=t.data.countryName||t.name;return d(e)+": "+Number(t.data.value).toLocaleString()}},visualMap:{min:0,max:l.value,left:20,bottom:20,text:["High","Low"],calculable:!0,inRange:{color:["#EDF9FF","#35ADE9"]},textStyle:{color:"#666",fontSize:11}},series:[{type:"map",map:"world",nameProperty:"iso_a2",roam:"move",scaleLimit:{min:1,max:8},zoom:1.2,emphasis:{label:{show:!0,fontSize:11,color:"#ffffff",textShadowColor:"#000000",textShadowBlur:2,formatter:t=>{var e;return(null==(e=t.data)?void 0:e.countryName)||s.value[t.name]||t.name}},itemStyle:{areaColor:"#8762F0"}},itemStyle:{borderColor:"#ccc",borderWidth:.5,areaColor:"#f5f5f5"},data:c.value}]}),u=()=>{r&&r.resize()},h=async()=>{if(!r&&e.value){if(!wt){const t=await tt(()=>import("./Chart/world.js?ver=3.1.8"),[],import.meta.url);F("world",t.default||t),wt=!0}e.value&&(r=R(e.value),r.setOption(p(),!0),window.addEventListener("resize",u))}};return w(()=>{h()}),k(()=>t.countryData,()=>{e.value&&(r?r.setOption(p(),!0):h())},{deep:!0}),$(()=>{r&&(window.removeEventListener("resize",u),r.dispose(),r=null)}),{chartRef:e,searchQuery:a,hasData:n,countryList:o,filteredList:i,zoomIn:()=>{if(!r)return;const t=r.getOption().series[0].zoom||1.2;r.setOption({series:[{zoom:Math.min(1.5*t,8)}]})},zoomOut:()=>{if(!r)return;const t=r.getOption().series[0].zoom||1.2;r.setOption({series:[{zoom:Math.max(t/1.5,1)}]})},resetZoom:()=>{r&&r.setOption(p(),!0)},formatNumber:t=>Number(t).toLocaleString()}}},[["render",function(a,r,s,n,o,i){const l=A("Icons"),c=t,d=e;return x(),S("div",kt,[T("div",$t,[T("div",xt,[V(c,{size:"small",class:"small only-icon-btn",onClick:n.zoomIn,title:a.$t("Zoom in"),"aria-label":a.$t("Zoom in")},{default:Y(()=>[T("span",St,[V(l,{"icon-name":"plus"})])],void 0),_:1},8,["onClick","title","aria-label"]),V(c,{size:"small",class:"small only-icon-btn",onClick:n.zoomOut,title:a.$t("Zoom out"),"aria-label":a.$t("Zoom out")},{default:Y(()=>[T("span",Tt,[V(l,{"icon-name":"minus"})])],void 0),_:1},8,["onClick","title","aria-label"]),V(c,{size:"small",class:"small only-icon-btn",onClick:n.resetZoom,title:a.$t("Reset"),"aria-label":a.$t("Reset")},{default:Y(()=>[T("span",Mt,[V(l,{"icon-name":"reload"})])],void 0),_:1},8,["onClick","title","aria-label"])]),T("div",{ref:"chartRef",class:"fcrm_country_map",style:M("height:"+s.height+"px;")},null,4),n.hasData?D("",!0):(x(),S("div",Dt,E(a.$t("No country data to display")),1))]),n.countryList.length?(x(),S("div",Rt,[T("div",Ot,[V(d,{modelValue:n.searchQuery,"onUpdate:modelValue":r[0]||(r[0]=t=>n.searchQuery=t),placeholder:a.$t("Search country...")},{prefix:Y(()=>[V(l,{"icon-name":"search"})]),_:1},8,["modelValue","placeholder"])]),T("div",Lt,[T("table",At,[T("thead",null,[T("tr",null,[T("th",It,E(a.$t("Country")),1),T("th",Vt,E(a.$t("Contacts")),1)])]),T("tbody",null,[(x(!0),S(j,null,G(n.filteredList,t=>(x(),S("tr",{key:t.country_code},[T("td",Et,E(t.country_name),1),T("td",Pt,E(n.formatNumber(t.contact_count)),1)]))),128)),n.filteredList.length?D("",!0):(x(),S("tr",Nt,[T("td",Yt,E(a.$t("No match")),1)]))])])])])):D("",!0)])}]]),Gt={class:"fcrm_range_picker"};const Ft=Z({name:"RangePicker",props:{range_settings:{type:Object,required:!0}},emits:["changed"],data(){return{CalendarIcon:z(rt),dateShortcuts:et,compare_type_options:{previous_period:this.$t("Previous Period"),previous_month:this.$t("Previous Month"),previous_quarter:this.$t("Previous Quarter"),previous_year:this.$t("Previous Year"),custom:this.$t("Custom date"),no_comparison:this.$t("No Comparison")}}},methods:{onChange(){this.$emit("changed")}},created(){const t=window.dayjs,e=t().subtract(1,"day").format("YYYY-MM-DD"),a=t().subtract(30,"day").format("YYYY-MM-DD");this.range_settings.date_range=[a,e],this.range_settings.compare_type="previous_period"}},[["render",function(t,e,n,o,i,l){const c=a,d=r,p=s;return x(),S("div",Gt,[V(c,{modelValue:n.range_settings.date_range,"onUpdate:modelValue":e[0]||(e[0]=t=>n.range_settings.date_range=t),type:"daterange",size:"small",onChange:e[1]||(e[1]=t=>l.onChange()),"value-format":"YYYY-MM-DD",format:"MMM D, YYYY",shortcuts:i.dateShortcuts,"unlink-panels":"","range-separator":t.$t("→"),"start-placeholder":t.$t("Start"),"end-placeholder":t.$t("End"),"prefix-icon":i.CalendarIcon},null,8,["modelValue","shortcuts","range-separator","start-placeholder","end-placeholder","prefix-icon"]),V(p,{onChange:e[2]||(e[2]=t=>l.onChange()),modelValue:n.range_settings.compare_type,"onUpdate:modelValue":e[3]||(e[3]=t=>n.range_settings.compare_type=t),placeholder:t.$t("Compare to"),style:{width:"150px"},size:"small"},{default:Y(()=>[(x(!0),S(j,null,G(i.compare_type_options,(t,e)=>(x(),I(d,{key:e,label:t,value:e},null,8,["label","value"]))),128))],void 0),_:1},8,["modelValue","placeholder"]),"custom"==n.range_settings.compare_type?(x(),I(c,{key:0,onChange:e[4]||(e[4]=t=>l.onChange()),modelValue:n.range_settings.compare_range,"onUpdate:modelValue":e[5]||(e[5]=t=>n.range_settings.compare_range=t),type:"daterange",size:"small",class:"fcrm_range_compare_date","value-format":"YYYY-MM-DD",format:"MMM D, YYYY","range-separator":t.$t("→"),"start-placeholder":t.$t("Start"),"end-placeholder":t.$t("End"),"prefix-icon":i.CalendarIcon},null,8,["modelValue","range-separator","start-placeholder","end-placeholder","prefix-icon"])):D("",!0)])}]]),zt={name:"TopTagsChart",components:{Icons:X,ReportGrowthChart:mt},props:{tags:{type:Array,default:()=>[]},total:{type:Number,default:0},loading:{type:Boolean,default:!1}},computed:{chartOptions(){if(!this.tags||!this.tags.length)return null;const t=[],e=[],a=[];for(let n=this.tags.length-1;n>=0;n--){const e=this.tags[n];t.push(e.title),a.push(Number(e.contact_count)||0)}const r=a.reduce((t,e)=>t+e,0),s=Number(this.total)||r;return a.forEach(t=>{const a=s?t/s*100:0;e.push(parseFloat(a.toFixed(1)))}),{tooltip:{trigger:"axis",axisPointer:{type:"shadow"},appendToBody:!0,formatter:t=>{if(!Array.isArray(t)||!t.length)return"";const e=t[0],r=e.dataIndex;return""+(s=e.name,String(s??"").replace(/[&<>"']/g,t=>({"&":"&","<":"<",">":">",'"':""","'":"'"}[t]))+"
")+this.$t("Count")+": "+J.formatMoney(a[r],0)+"
"+this.$t("Percentage")+": "+e.value+"%";var s},extraCssText:"z-index:10000;"},grid:{top:10,left:10,right:10,bottom:10,containLabel:!0},xAxis:{type:"value",axisLabel:{formatter:"{value}%",color:"#99A0AE"},splitLine:{lineStyle:{color:"#E1E4EA",type:"dashed"}}},yAxis:{type:"category",data:t,axisTick:{show:!1},axisLine:{show:!1},axisLabel:{show:!1}},series:[{name:this.$t("Tags"),type:"bar",data:e,barMaxWidth:20,barGap:"100%",barCategoryGap:"60%",label:{show:!0,position:[0,-16],formatter:e=>t[e.dataIndex],color:"#525866",fontSize:12},itemStyle:{color:"#6895FF",borderRadius:[0,4,4,0]}}]}}}},Bt={class:"fcrm_report_card fcrm_report_card_half"},Ut={class:"fcrm_report_card_header"},qt={class:"fcrm_report_card_body"},Ht={key:1,class:"fcrm_empty_state"},Wt={class:"fcrm_empty_state_text"};const Zt=Z(zt,[["render",function(t,e,a,r,s,o){const i=A("ReportGrowthChart"),l=A("Icons"),c=n;return x(),S("div",Bt,[T("div",Ut,[T("h4",null,E(t.$t("Top Tags")),1)]),B((x(),S("div",qt,[o.chartOptions?(x(),I(i,{key:0,options:o.chartOptions,height:300},null,8,["options"])):a.loading?D("",!0):(x(),S("div",Ht,[V(l,{"icon-name":"common-empty-state"}),T("div",Wt,[T("span",null,E(t.$t("No tags found")),1)])]))])),[[c,a.loading]])])}]]),Kt={"&":"&","<":"<",">":">",'"':""","'":"'"},Qt=t=>String(t??"").replace(/[&<>"']/g,t=>Kt[t]),Xt={name:"TopListsChart",components:{Icons:X,ReportGrowthChart:mt},props:{lists:{type:Array,default:()=>[]},total:{type:Number,default:0},loading:{type:Boolean,default:!1}},computed:{chartOptions(){if(!this.lists||!this.lists.length)return null;const t=[],e=[],a=[];for(let n=this.lists.length-1;n>=0;n--){const e=this.lists[n];t.push(e.title),a.push(Number(e.contact_count)||0)}const r=a.reduce((t,e)=>t+e,0),s=Number(this.total)||r;return a.forEach(t=>{const a=s?t/s*100:0;e.push(parseFloat(a.toFixed(1)))}),{tooltip:{trigger:"axis",axisPointer:{type:"shadow"},appendToBody:!0,formatter:t=>{if(!Array.isArray(t)||!t.length)return"";const e=t[0],r=e.dataIndex;return""+Qt(e.name)+"
"+Qt(this.$t("Count"))+": "+Qt(J.formatMoney(a[r],0))+"
"+Qt(this.$t("Percentage"))+": "+e.value+"%"},extraCssText:"z-index:10000;"},grid:{top:10,left:10,right:10,bottom:10,containLabel:!0},xAxis:{type:"value",min:0,max:100,axisLabel:{formatter:"{value}%",color:"#99A0AE"},splitLine:{lineStyle:{color:"#E1E4EA",type:"dashed"}}},yAxis:{type:"category",data:t,axisTick:{show:!1},axisLine:{show:!1},axisLabel:{show:!1}},series:[{name:this.$t("Lists"),type:"bar",data:e,barMaxWidth:20,barGap:"100%",barCategoryGap:"60%",label:{show:!0,position:[0,-16],formatter:e=>t[e.dataIndex],color:"#525866",fontSize:12},itemStyle:{color:"#CAC0FF",borderRadius:[0,4,4,0]}}]}}}},Jt={class:"fcrm_report_card fcrm_report_card_half"},te={class:"fcrm_report_card_header"},ee=["aria-label"],ae={key:1,class:"fcrm_empty_state"},re={class:"fcrm_empty_state_text"};const se={class:"fcrm_report_section"},ne={class:"d-flex items-center gap-4"},oe={class:"fcrm-layout-width"},ie={class:"fcrm_card_widgets"},le={class:"fcrm_report_card"},ce={class:"fcrm_report_card_header"},de={class:"fcrm_report_card_title_group"},pe={class:"fcrm_report_card_actions"},ue={class:"fcrm_period_selector"},he={class:"fcrm_report_card_body"},me={key:1,class:"fcrm_empty_state"},_e={class:"fcrm_empty_state_text"},ge={class:"fcrm_report_card fcrm_report_card--contacts-by-country"},fe={class:"fcrm_report_card_header"},ve={class:"fcrm_report_card_body fcrm_report_card_body_flush"},be={key:1,class:"fcrm_empty_state"},ye={class:"fcrm_empty_state_text"},Ce={class:"fcrm_report_row"},we={class:"fcrm_report_card fcrm_report_card_half"},ke={class:"fcrm_report_card_header"},$e={class:"fcrm_report_card_actions"},xe={class:"fcrm_report_card_body"},Se={key:1,class:"fcrm_empty_state"},Te={class:"fcrm_empty_state_text"},Me={class:"fcrm_report_card fcrm_report_card_half"},De={class:"fcrm_report_card_header"},Re={class:"fcrm_report_card_actions"},Oe={class:"fcrm_report_card_body"},Le={key:1,class:"fcrm_empty_state"},Ae={class:"fcrm_empty_state_text"},Ie={class:"fcrm_report_row"};const Ve=Z({name:"ContactReports",inject:["toggleMenu","isReportsMenuOpen"],components:{Icons:X,PageHeader:st,ChartBuilder:gt,StatCard:Ct,CountryMap:jt,RangePicker:Ft,TopTagsChart:Zt,TopListsChart:Z(Xt,[["render",function(t,e,a,r,s,o){const i=A("ReportGrowthChart"),l=A("Icons"),c=n;return x(),S("div",Jt,[T("div",te,[T("h4",null,E(t.$t("Top Lists")),1)]),B((x(),S("div",{class:"fcrm_report_card_body",role:"img","aria-label":t.$t("Bar chart showing top lists by contact percentage")},[o.chartOptions?(x(),I(i,{key:0,options:o.chartOptions,height:300},null,8,["options"])):a.loading?D("",!0):(x(),S("div",ae,[V(l,{"icon-name":"common-empty-state"}),T("div",re,[T("span",null,E(t.$t("No lists found")),1)])]))],8,ee)),[[c,a.loading]])])}]])},data:()=>({range_settings:{date_range:[],compare_range:[],compare_type:"previous_period"},growthData:[],chartType:"line",statusStats:[],totalContacts:0,countryData:[],tagStats:{},listStats:{},allTags:[],allLists:[],tagOptionsLoading:!1,listOptionsLoading:!1,tagOptionsRequestId:0,listOptionsRequestId:0,selectedTagId:"",selectedListId:"",tagGrowthData:[],listGrowthData:[],statusIconMap:{subscribed:"user",unsubscribed:"unsubscribe",pending:"envelope",bounced:"envelope",complained:"unsubscribe",spammed:"spammed",transactional:"transactional"},statusVariantMap:{subscribed:"success",unsubscribed:"danger",pending:"warning",bounced:"danger",complained:"danger"},loading:{growth:!1,status:!1,tags:!1,lists:!1,countries:!1,tagGrowth:!1,listGrowth:!1}}),computed:{isMenuOpen(){return!!this.isReportsMenuOpen&&this.isReportsMenuOpen()},chartDataSets(){return this.growthData&&this.growthData.length?this.growthData.map(t=>({...t,type:this.chartType})):[]}},methods:{setChartType(t){this.chartType=t;try{localStorage.setItem("fcrm_contact_report_chart_type",t)}catch(e){}},formatDateLabel(t){if(!t)return"";const e=window.dayjs?window.dayjs(t):null;if(!e||!e.isValid())return t;const a=window.dayjs();return a&&e.year()!==a.year()?e.format("MMM D, YYYY"):e.format("MMM D")},fetchGrowthStats(){this.loading.growth=!0,this.$get("reports/subscribers",{...this.range_settings}).then(t=>{this.growthData=t.data_sets||[],t.current_range&&(this.range_settings.date_range=t.current_range)}).catch(t=>this.handleError(t)).finally(()=>{this.loading.growth=!1})},fetchStatusStats(){this.loading.status=!0,this.$get("reports/contacts-by-status").then(t=>{this.statusStats=t.stats,this.totalContacts=t.total}).catch(t=>this.handleError(t)).finally(()=>{this.loading.status=!1})},fetchCountryStats(){this.loading.countries=!0,this.$get("reports/contacts-by-country").then(t=>{this.countryData=t.countries||[]}).catch(t=>this.handleError(t)).finally(()=>{this.loading.countries=!1})},fetchTagStats(){this.loading.tags=!0,this.$get("reports/contacts-by-tags",{per_page:5}).then(t=>{this.tagStats=t.tags}).catch(t=>this.handleError(t)).finally(()=>{this.loading.tags=!1})},fetchListStats(){this.loading.lists=!0,this.$get("reports/contacts-by-lists",{per_page:5}).then(t=>{this.listStats=t.lists}).catch(t=>this.handleError(t)).finally(()=>{this.loading.lists=!1})},fetchTagOptions(t=""){const e=++this.tagOptionsRequestId;this.tagOptionsLoading=!0,this.$get("tags",{search:t||"",per_page:50}).then(t=>{e===this.tagOptionsRequestId&&(this.allTags=t.tags?t.tags.data||t.tags:[])}).catch(()=>{}).finally(()=>{e===this.tagOptionsRequestId&&(this.tagOptionsLoading=!1)})},fetchListOptions(t=""){const e=++this.listOptionsRequestId;this.listOptionsLoading=!0,this.$get("lists",{search:t||"",per_page:50}).then(t=>{e===this.listOptionsRequestId&&(this.allLists=t.lists?t.lists.data||t.lists:[])}).catch(()=>{}).finally(()=>{e===this.listOptionsRequestId&&(this.listOptionsLoading=!1)})},fetchTagGrowth(){this.selectedTagId?(this.loading.tagGrowth=!0,this.$get("reports/subscribers",{date_range:this.range_settings.date_range,tag_id:this.selectedTagId}).then(t=>{this.tagGrowthData=t.data_sets||[]}).catch(t=>this.handleError(t)).finally(()=>{this.loading.tagGrowth=!1})):this.tagGrowthData=[]},fetchListGrowth(){this.selectedListId?(this.loading.listGrowth=!0,this.$get("reports/subscribers",{date_range:this.range_settings.date_range,list_id:this.selectedListId}).then(t=>{this.listGrowthData=t.data_sets||[]}).catch(t=>this.handleError(t)).finally(()=>{this.loading.listGrowth=!1})):this.listGrowthData=[]},formatStatus(t){return{subscribed:this.$t("Subscribed"),unsubscribed:this.$t("Unsubscribed"),pending:this.$t("Pending"),bounced:this.$t("Bounced"),complained:this.$t("Complained"),spammed:this.$t("Spammed"),transactional:this.$t("Transactional")}[t]||t},getPercent(t){return this.totalContacts?(t/this.totalContacts*100).toFixed(1):0},formatNumber:t=>Number(t).toLocaleString()},mounted(){try{const t=localStorage.getItem("fcrm_contact_report_chart_type");t&&(this.chartType=t)}catch(t){}this.fetchGrowthStats(),this.fetchStatusStats(),this.fetchCountryStats(),this.fetchTagStats(),this.fetchListStats(),this.fetchTagOptions(),this.fetchListOptions()}},[["render",function(e,a,i,l,c,d){const p=A("Icons"),u=o,h=A("page-header"),m=A("stat-card"),_=A("range-picker"),g=t,f=A("chart-builder"),v=A("country-map"),b=r,y=s,C=A("top-tags-chart"),w=A("top-lists-chart"),k=n;return x(),S("div",se,[V(h,null,{title:Y(()=>[T("div",ne,[V(u,{enterable:!1,transition:"none",content:d.isMenuOpen?e.$t("Close Sidebar"):e.$t("Open Sidebar"),placement:"right"},{default:Y(()=>[T("span",{class:N(["fcrm-report-collapse-sidebar-btn cursor_pointer",{"is-collapsed":d.isMenuOpen}]),onClick:a[0]||(a[0]=(...t)=>d.toggleMenu&&d.toggleMenu(...t))},[V(p,{"icon-name":"sidebar",class:"d-block"})],2)],void 0,!0),_:1},8,["content"]),P(" "+E(e.$t("Contacts")),1)])]),_:1}),T("div",oe,[B((x(),S("div",ie,[(x(!0),S(j,null,G(c.statusStats,t=>(x(),I(m,{key:t.status,icon:c.statusIconMap[t.status]||"total_subscribers",label:d.formatStatus(t.status),value:d.formatNumber(t.count),sub:d.getPercent(t.count)+"%",variant:c.statusVariantMap[t.status]||"default"},null,8,["icon","label","value","sub","variant"]))),128)),c.totalContacts?(x(),I(m,{key:0,icon:"user",label:e.$t("Total Contacts"),value:d.formatNumber(c.totalContacts),variant:"default"},null,8,["label","value"])):D("",!0)])),[[k,c.loading.status]]),T("div",le,[T("div",ce,[T("div",de,[T("h4",null,E(e.$t("Contact Growth")),1)]),T("div",pe,[V(_,{onChanged:a[1]||(a[1]=t=>d.fetchGrowthStats()),range_settings:c.range_settings},null,8,["range_settings"]),T("div",ue,[V(g,{size:"small",class:N(["small only-icon-btn",{is_active:"bar"===c.chartType}]),"aria-label":e.$t("Bar chart"),"aria-pressed":"bar"===c.chartType,onClick:a[2]||(a[2]=t=>d.setChartType("bar"))},{default:Y(()=>[V(p,{"icon-name":"bar-chart"})],void 0),_:1},8,["class","aria-label","aria-pressed"]),V(g,{size:"small",class:N(["small only-icon-btn",{is_active:"line"===c.chartType}]),"aria-label":e.$t("Line chart"),"aria-pressed":"line"===c.chartType,onClick:a[3]||(a[3]=t=>d.setChartType("line"))},{default:Y(()=>[V(p,{"icon-name":"line-chart"})],void 0),_:1},8,["class","aria-label","aria-pressed"])])])]),B((x(),S("div",he,[c.growthData.length?(x(),I(f,{key:0,data_sets:d.chartDataSets,currency_sign:"",height:350},null,8,["data_sets"])):c.loading.growth?D("",!0):(x(),S("div",me,[V(p,{"icon-name":"common-empty-state"}),T("div",_e,[T("span",null,E(e.$t("No data available")),1)])]))])),[[k,c.loading.growth]])]),T("div",ge,[T("div",fe,[T("h4",null,E(e.$t("Contacts by Country")),1)]),B((x(),S("div",ve,[c.countryData.length?(x(),I(v,{key:0,"country-data":c.countryData},null,8,["country-data"])):c.loading.countries?D("",!0):(x(),S("div",be,[V(p,{"icon-name":"common-empty-state"}),T("div",ye,[T("span",null,E(e.$t("No country data available")),1)])]))])),[[k,c.loading.countries]])]),T("div",Ce,[T("div",we,[T("div",ke,[T("h4",null,E(e.$t("Contact Growth by Tag")),1),T("div",$e,[V(y,{modelValue:c.selectedTagId,"onUpdate:modelValue":a[4]||(a[4]=t=>c.selectedTagId=t),filterable:"",remote:"","reserve-keyword":"",clearable:"",size:"small",placeholder:e.$t("Select Tag"),"remote-method":d.fetchTagOptions,loading:c.tagOptionsLoading,onChange:d.fetchTagGrowth,style:{"min-width":"160px"}},{default:Y(()=>[(x(!0),S(j,null,G(c.allTags,t=>(x(),I(b,{key:t.id,label:t.title,value:t.id},null,8,["label","value"]))),128))],void 0),_:1},8,["modelValue","placeholder","remote-method","loading","onChange"])])]),B((x(),S("div",xe,[c.tagGrowthData.length?(x(),I(f,{key:0,data_sets:c.tagGrowthData,currency_sign:""},null,8,["data_sets"])):c.loading.tagGrowth&&c.selectedTagId?D("",!0):(x(),S("div",Se,[V(p,{"icon-name":"common-empty-state"}),T("div",Te,[T("span",null,E(c.selectedTagId?e.$t("No data available"):e.$t("Select a tag to view growth")),1)])]))])),[[k,c.loading.tagGrowth]])]),T("div",Me,[T("div",De,[T("h4",null,E(e.$t("Contact Growth by List")),1),T("div",Re,[V(y,{modelValue:c.selectedListId,"onUpdate:modelValue":a[5]||(a[5]=t=>c.selectedListId=t),filterable:"",remote:"","reserve-keyword":"",clearable:"",size:"small",placeholder:e.$t("Select List"),"remote-method":d.fetchListOptions,loading:c.listOptionsLoading,onChange:d.fetchListGrowth,style:{"min-width":"160px"}},{default:Y(()=>[(x(!0),S(j,null,G(c.allLists,t=>(x(),I(b,{key:t.id,label:t.title,value:t.id},null,8,["label","value"]))),128))],void 0),_:1},8,["modelValue","placeholder","remote-method","loading","onChange"])])]),B((x(),S("div",Oe,[c.listGrowthData.length?(x(),I(f,{key:0,data_sets:c.listGrowthData,currency_sign:""},null,8,["data_sets"])):c.loading.listGrowth&&c.selectedListId?D("",!0):(x(),S("div",Le,[V(p,{"icon-name":"common-empty-state"}),T("div",Ae,[T("span",null,E(c.selectedListId?e.$t("No data available"):e.$t("Select a list to view growth")),1)])]))])),[[k,c.loading.listGrowth]])])]),T("div",Ie,[V(C,{tags:c.tagStats.data||[],total:c.totalContacts,loading:c.loading.tags},null,8,["tags","total","loading"]),V(w,{lists:c.listStats.data||[],total:c.totalContacts,loading:c.loading.lists},null,8,["lists","total","loading"])])])])}]]),Ee={class:"fcrm_report_card"},Pe={class:"fcrm_report_card_header"},Ne={class:"fcrm_report_card_body"},Ye={key:0,class:"fcrm_card_widgets"},je={class:"fcrm_card_widget with-border pointer-auto"},Ge={class:"fcrm_card_widget_title"},Fe={class:"fcrm_card_widget_content"},ze={class:"fcrm_card_widget with-border pointer-auto"},Be={class:"fcrm_card_widget_title"},Ue={class:"fcrm_card_widget_content"},qe={class:"fcrm_card_widget with-border pointer-auto"},He={class:"fcrm_card_widget_title"},We={class:"fcrm_card_widget_content"},Ze={class:"fcrm_card_stat_sub"},Ke={key:2,class:"fcrm_empty_state"},Qe={class:"fcrm_empty_state_text"};const Xe={class:"fcrm_report_section"},Je={class:"d-flex items-center gap-4"},ta={class:"fcrm_range_picker"},ea={class:"fcrm-layout-width"},aa={class:"fcrm_card_widgets"},ra={class:"fcrm_report_card"},sa={class:"fcrm_report_card_header"},na={class:"fcrm_report_card_actions"},oa={class:"fcrm_report_card_body"},ia={key:1,class:"fcrm_empty_state"},la={class:"fcrm_empty_state_text"},ca={class:"fcrm_report_card"},da={class:"fcrm_report_card_header"},pa={class:"fcrm_report_card_body"},ua={key:1,class:"fcrm_empty_state"},ha={class:"fcrm_empty_state_text"},ma={class:"fcrm_report_card"},_a={class:"fcrm_report_card_header"},ga={class:"fcrm_report_card_body"},fa={key:1,class:"fcrm_empty_state"},va={class:"fcrm_empty_state_text"},ba={class:"w-full d-flex flex-column gap-4"},ya=["onClick","onKeydown"],Ca={class:"fcrm_dashboard_entity_card__item_content"},wa={class:"fcrm_dashboard_entity_card__item_title"},ka={class:"fcrm_dashboard_entity_card__stats"},$a={class:"fcrm_dashboard_entity_card__stat"},xa={class:"fcrm_dashboard_entity_card__stat_icon"},Sa={class:"fcrm_dashboard_entity_card__stat"},Ta={class:"fcrm_dashboard_entity_card__stat_icon"},Ma={class:"fcrm_dashboard_entity_card__stat"},Da={class:"fcrm_dashboard_entity_card__stat_icon"},Ra={class:"fcrm_dashboard_entity_card__stat fcrm_dashboard_entity_card__stat_open_rate"},Oa={key:1,class:"text-center d-flex flex-column gap-10 justify-center h-full w-full fcrm_p_20"},La={class:"icon"},Aa={class:"fcrm_secondary_text"};const Ia=Z({name:"EmailReports",inject:["toggleMenu","isReportsMenuOpen"],components:{BaseCard:nt,Icons:X,PageHeader:st,ChartBuilder:gt,StatCard:Ct,TopCampaigns:Z({name:"TopCampaigns",components:{Icons:X,Chart:mt},data:()=>({campaigns:[],loading:!1,current_mode:"system"===Q.getCurrentTheme()?Q.getSystemTheme():Q.getCurrentTheme()}),computed:{chartColors(){return"light"===this.current_mode?{openRate:"#97BAFF",clickRate:"#FFD268",textMuted:"#4A5565",text:"#0E121B",gridLine:"#E1E4EA",tooltipBackground:"#ffffff",tooltipBorder:"#E1E4EA"}:{openRate:"#97BAFF",clickRate:"#FFD268",textMuted:"#9CA3AF",text:"#ffffff",gridLine:"#2c3c4e",tooltipBackground:"#283b56",tooltipBorder:"#283b56"}},avgOpenRate(){if(!this.campaigns.length)return 0;const t=this.campaigns.reduce((t,e)=>t+Number(e.open_rate||0),0);return Math.round(t/this.campaigns.length)},avgClickRate(){if(!this.campaigns.length)return 0;const t=this.campaigns.reduce((t,e)=>t+Number(e.click_rate||0),0);return Math.round(t/this.campaigns.length)},formattedTotalSends(){const t=this.campaigns.reduce((t,e)=>{const a=null!=e.total_sent?e.total_sent:e.sent||0;return t+Number(a)},0);return t>=1e3?Math.round(t/1e3)+"k":t},chartOptions(){if(!this.campaigns.length)return null;const t=[...this.campaigns].sort((t,e)=>new Date(t.created_at)-new Date(e.created_at)),e=t.map(t=>t.title),a=t.map(t=>{if(!t.created_at)return"";return new Date(t.created_at).toLocaleDateString(void 0,{month:"short",day:"numeric",year:"numeric"})}),r=t.map(t=>Number(t.open_rate||0).toFixed(1)),s=t.map(t=>Number(t.click_rate||0).toFixed(1)),n=this.$t("Open Rate"),o=this.$t("Click Rate"),i=this.chartColors;return{tooltip:{trigger:"axis",backgroundColor:i.tooltipBackground,borderColor:i.tooltipBorder,borderWidth:1,textStyle:{color:i.text},axisPointer:{type:"shadow",shadowStyle:{color:"rgba(148, 163, 184, 0.28)"}},formatter:t=>{const a=t[0].dataIndex,r=document.createElement("div");r.appendChild(document.createTextNode(e[a]));let s=r.innerHTML+"
";return t.forEach(t=>{s+=t.marker+" "+this.$t("%s: %s%",t.seriesName,t.value)+"
"}),s}},legend:{data:[n,o],top:0,left:"center",icon:"circle",itemWidth:10,itemHeight:10,textStyle:{color:i.textMuted,fontSize:13}},grid:{left:"4%",right:0,bottom:0,top:"12%",containLabel:!0},xAxis:{type:"category",data:a,axisLabel:{color:i.textMuted,fontSize:12,interval:0},axisLine:{show:!0,lineStyle:{color:i.gridLine}},axisTick:{show:!0,lineStyle:{color:i.gridLine}}},yAxis:{type:"value",axisLabel:{color:i.textMuted,fontSize:12,formatter:"{value}"},splitLine:{lineStyle:{type:"dashed",color:i.gridLine}},name:this.$t("Rate"),nameLocation:"middle",nameGap:40,nameTextStyle:{color:i.textMuted,fontSize:13}},series:[{name:n,type:"bar",data:r,itemStyle:{color:i.openRate,borderRadius:[3,3,0,0]},barGap:"20%",barMaxWidth:40},{name:o,type:"bar",data:s,itemStyle:{color:i.clickRate,borderRadius:[3,3,0,0]},barMaxWidth:40}]}}},methods:{fetchTopCampaigns(){this.loading=!0,this.$get("reports/top-campaigns",{sort_by:"open_rate",per_page:10}).then(t=>{this.campaigns=t.campaigns||[]}).catch(()=>{this.campaigns=[]}).finally(()=>{this.loading=!1})}},mounted(){this.onThemeChanged=t=>{var e;this.current_mode=(null==(e=t.detail)?void 0:e.effective)||Q.getCurrentTheme()},window.addEventListener(K,this.onThemeChanged),this.fetchTopCampaigns()},beforeUnmount(){this.onThemeChanged&&window.removeEventListener(K,this.onThemeChanged)}},[["render",function(t,e,a,r,s,o){const i=A("Chart"),l=A("Icons"),c=n;return x(),S("div",Ee,[T("div",Pe,[T("h4",null,E(t.$t("Campaigns")),1)]),B((x(),S("div",Ne,[s.campaigns.length?(x(),S("div",Ye,[T("div",je,[T("div",Ge,E(t.$t("Avg. Open Rate")),1),T("div",Fe,E(o.avgOpenRate)+"%",1)]),T("div",ze,[T("div",Be,E(t.$t("Avg. Click Rate")),1),T("div",Ue,E(o.avgClickRate)+"%",1)]),T("div",qe,[T("div",He,E(t.$t("Total Sends")),1),T("div",We,[P(E(o.formattedTotalSends)+" ",1),T("div",Ze,E(s.campaigns.length)+" "+E(t.$t("Campaigns")),1)])])])):D("",!0),o.chartOptions?(x(),I(i,{key:1,options:o.chartOptions,height:400},null,8,["options"])):s.loading?D("",!0):(x(),S("div",Ke,[V(l,{"icon-name":"common-empty-state"}),T("div",Qe,[T("span",null,E(t.$t("No data available")),1)])]))])),[[c,s.loading]])])}]])},data:()=>({CalendarIcon:z(rt),dateShortcuts:et,performance:{},chartDateRange:(()=>{const t=new Date,e=new Date;return e.setMonth(e.getMonth()-1),[e.toISOString().split("T")[0],t.toISOString().split("T")[0]]})(),sentChartData:[],clickChartData:[],unsubChartData:[],campaigns:{},selectedCampaignId:"",campaignOptions:[],campaignSearchLoading:!1,loading:{performance:!1,sents:!1,clicks:!1,unsubs:!1,campaigns:!1}}),computed:{isMenuOpen(){return!!this.isReportsMenuOpen&&this.isReportsMenuOpen()},perfTotals(){return this.performance.totals||{sent:0,delivered:0,opened:0,clicked:0,bounced:0}},perfPcts(){return this.performance.percentages||{delivered:0,opened:0,clicked:0,bounced:0}}},methods:{onGlobalDateChange(){this.fetchPerformance(),this.fetchAllCharts()},fetchPerformance(){this.loading.performance=!0,this.$get("reports/email-performance",{date_range:this.chartDateRange||[]}).then(t=>{this.performance=t.stats}).catch(t=>this.handleError(t)).finally(()=>{this.loading.performance=!1})},fetchAllCharts(){this.fetchSentChart(),this.fetchClickChart(),this.fetchUnsubChart()},fetchCampaignCharts(){this.fetchSentChart(),this.fetchClickChart()},fetchSentChart(){this.loading.sents=!0;const t={date_range:this.chartDateRange||[]};this.selectedCampaignId&&(t.campaign_id=this.selectedCampaignId),this.$get("reports/email-sents",t).then(t=>{this.sentChartData=[{label:this.$t("Emails Sent"),data:t.stats}]}).catch(t=>this.handleError(t)).finally(()=>{this.loading.sents=!1})},fetchClickChart(){this.loading.clicks=!0;const t={date_range:this.chartDateRange||[]};this.selectedCampaignId&&(t.campaign_id=this.selectedCampaignId),this.$get("reports/email-clicks",t).then(t=>{this.clickChartData=[{label:this.$t("Link Clicks"),data:t.stats,borderColor:"#5F52FE",backgroundColor:"#5F52FE"}]}).catch(t=>this.handleError(t)).finally(()=>{this.loading.clicks=!1})},fetchUnsubChart(){this.loading.unsubs=!0,this.$get("reports/email-unsubs",{date_range:this.chartDateRange||[]}).then(t=>{this.unsubChartData=[{label:this.$t("Unsubscribes"),data:t.stats,borderColor:"#ef4444",backgroundColor:"#ef4444"}]}).catch(t=>this.handleError(t)).finally(()=>{this.loading.unsubs=!1})},handleCampaignSelectVisibility(t){t&&!this.campaignOptions.length&&this.fetchCampaignOptions("")},fetchCampaignOptions(t){this.campaignSearchLoading=!0,this.$get("reports/campaign-options",{search:t||"",per_page:50}).then(t=>{this.campaignOptions=t.options||[]}).catch(()=>{}).finally(()=>{this.campaignSearchLoading=!1})},fetchCampaigns(){this.loading.campaigns=!0,this.$get("reports/campaigns-list",{page:1,per_page:5}).then(t=>{this.campaigns=t.campaigns}).catch(t=>this.handleError(t)).finally(()=>{this.loading.campaigns=!1})},getPercent:(t,e)=>e&&t?parseFloat(t/e*100).toFixed(2)+"%":"0%",formatNumber:t=>Number(t).toLocaleString(),handleCampaignClick(t){let e="campaign-view";"draft"===t.status&&(e="campaign");const a=parseInt(t.next_step);this.$router.push({name:e,params:{id:t.id},query:{t:(new Date).getTime(),step:a&&a<=3?a:0}})}},mounted(){this.fetchPerformance(),this.fetchAllCharts(),this.fetchCampaigns(),this.fetchCampaignOptions("")}},[["render",function(t,e,i,l,c,d){const p=A("Icons"),u=o,h=a,m=A("page-header"),_=A("stat-card"),g=r,f=s,v=A("chart-builder"),b=A("TopCampaigns"),y=A("BaseCard"),C=n;return x(),S("div",Xe,[V(m,null,{title:Y(()=>[T("div",Je,[V(u,{enterable:!1,transition:"none",content:d.isMenuOpen?t.$t("Close Sidebar"):t.$t("Open Sidebar"),placement:"right"},{default:Y(()=>[T("span",{class:N(["fcrm-report-collapse-sidebar-btn cursor_pointer",{"is-collapsed":d.isMenuOpen}]),onClick:e[0]||(e[0]=(...t)=>d.toggleMenu&&d.toggleMenu(...t))},[V(p,{"icon-name":"sidebar",class:"d-block"})],2)],void 0,!0),_:1},8,["content"]),P(" "+E(t.$t("Emails")),1)])]),actions:Y(()=>[T("div",ta,[V(h,{modelValue:c.chartDateRange,"onUpdate:modelValue":e[1]||(e[1]=t=>c.chartDateRange=t),type:"daterange","value-format":"YYYY-MM-DD",format:"MMM D, YYYY",shortcuts:c.dateShortcuts,"range-separator":t.$t("to"),"start-placeholder":t.$t("Start"),"end-placeholder":t.$t("End"),onChange:d.onGlobalDateChange,"prefix-icon":c.CalendarIcon},null,8,["modelValue","shortcuts","range-separator","start-placeholder","end-placeholder","onChange","prefix-icon"])])]),_:1}),T("div",ea,[B((x(),S("div",aa,[V(_,{icon:"envelope",label:t.$t("Sent"),value:d.formatNumber(d.perfTotals.sent),variant:"default"},null,8,["label","value"]),V(_,{icon:"envelope",label:t.$t("Delivered"),value:d.formatNumber(d.perfTotals.delivered),sub:d.perfPcts.delivered+"%",variant:"success"},null,8,["label","value","sub"]),V(_,{icon:"envelopeOpen",label:t.$t("Opened"),value:d.formatNumber(d.perfTotals.opened),sub:d.perfPcts.opened+"%",variant:"info"},null,8,["label","value","sub"]),V(_,{icon:"envelopeWithClick",label:t.$t("Clicked"),value:d.formatNumber(d.perfTotals.clicked),sub:d.perfPcts.clicked+"%",variant:"info"},null,8,["label","value","sub"]),V(_,{icon:"unsubscribe",label:t.$t("Bounced"),value:d.formatNumber(d.perfTotals.bounced),sub:d.perfPcts.bounced+"%",variant:"danger"},null,8,["label","value","sub"])])),[[C,c.loading.performance]]),T("div",ra,[T("div",sa,[T("h4",null,E(t.$t("Email Sending Stats")),1),T("div",na,[V(f,{modelValue:c.selectedCampaignId,"onUpdate:modelValue":e[2]||(e[2]=t=>c.selectedCampaignId=t),filterable:"",clearable:"",size:"small","aria-label":t.$t("Filter by campaign"),placeholder:t.$t("All Campaigns"),"remote-method":d.fetchCampaignOptions,loading:c.campaignSearchLoading,onVisibleChange:d.handleCampaignSelectVisibility,onChange:d.fetchCampaignCharts},{default:Y(()=>[(x(!0),S(j,null,G(c.campaignOptions,t=>(x(),I(g,{key:t.id,label:t.title,value:t.id},null,8,["label","value"]))),128))],void 0),_:1},8,["modelValue","aria-label","placeholder","remote-method","loading","onVisibleChange","onChange"])])]),B((x(),S("div",oa,[c.sentChartData.length?(x(),I(v,{key:0,data_sets:c.sentChartData,currency_sign:"",height:350},null,8,["data_sets"])):c.loading.sents?D("",!0):(x(),S("div",ia,[V(p,{"icon-name":"common-empty-state"}),T("div",la,[T("span",null,E(t.$t("No data available")),1)])]))])),[[C,c.loading.sents]])]),T("div",ca,[T("div",da,[T("h4",null,E(t.$t("Link Clicks Stats")),1)]),B((x(),S("div",pa,[c.clickChartData.length?(x(),I(v,{key:0,data_sets:c.clickChartData,currency_sign:"",height:350},null,8,["data_sets"])):c.loading.clicks?D("",!0):(x(),S("div",ua,[V(p,{"icon-name":"common-empty-state"}),T("div",ha,[T("span",null,E(t.$t("No data available")),1)])]))])),[[C,c.loading.clicks]])]),T("div",ma,[T("div",_a,[T("h4",null,E(t.$t("Unsubscribe Stats")),1)]),B((x(),S("div",ga,[c.unsubChartData.length?(x(),I(v,{key:0,data_sets:c.unsubChartData,currency_sign:"",height:350},null,8,["data_sets"])):c.loading.unsubs?D("",!0):(x(),S("div",fa,[V(p,{"icon-name":"common-empty-state"}),T("div",va,[T("span",null,E(t.$t("No data available")),1)])]))])),[[C,c.loading.unsubs]])]),t.has_campaign_pro?(x(),I(b,{key:0})):D("",!0),V(y,{body_class:"fcrm_p_12"},{title:Y(()=>[T("h4",null,E(t.$t("Recent Campaigns")),1)]),body:Y(()=>[T("div",ba,[c.campaigns.data&&c.campaigns.data.length?(x(!0),S(j,{key:0},G(c.campaigns.data,(a,r)=>(x(),S("div",{class:"fcrm_dashboard_entity_card__item",key:r,onClick:t=>d.handleCampaignClick(a),onKeydown:[U(q(t=>d.handleCampaignClick(a),["prevent"]),["enter"]),U(q(t=>d.handleCampaignClick(a),["prevent"]),["space"])],role:"button",tabindex:"0"},[T("div",Ca,[T("p",wa,E(a.title),1),T("div",ka,[V(u,{content:t.$t("Total emails sent"),placement:"top"},{default:Y(()=>[T("div",$a,[T("span",xa,[V(p,{"icon-name":"envelope"})]),T("span",null,E(a.stats.sent||"0"),1)])],void 0,!0),_:2},1032,["content"]),e[3]||(e[3]=T("span",{class:"fcrm_dashboard_entity_card__stat_dot"},null,-1)),V(u,{content:t.$t("Total emails opened"),placement:"top"},{default:Y(()=>[T("div",Sa,[T("span",Ta,[V(p,{"icon-name":"envelopeOpen"})]),T("span",null,E(a.stats.views||"0"),1)])],void 0,!0),_:2},1032,["content"]),e[4]||(e[4]=T("span",{class:"fcrm_dashboard_entity_card__stat_dot"},null,-1)),V(u,{content:t.$t("Total clicks"),placement:"top"},{default:Y(()=>[T("div",Ma,[T("span",Da,[V(p,{"icon-name":"click"})]),T("span",null,E(a.stats.clicks||"0"),1)])],void 0,!0),_:2},1032,["content"]),V(u,{content:t.$t("Percentage of emails opened"),placement:"top"},{default:Y(()=>[T("div",Ra,E(t.$t("Open rate:"))+" "+E(d.getPercent(a.stats.views,a.stats.sent)),1)],void 0,!0),_:2},1032,["content"])])])],40,ya))),128)):(x(),S("div",Oa,[T("span",La,[V(p,{"icon-name":"common-empty-state"})]),T("p",Aa,E(t.$t("Looks like you don't have any campaigns now.")),1)]))])]),_:1})])])}]]);function Va(t,e){return e?(t-e)/e*100:null}function Ea(t){if(null==t)return{text:"—",cssClass:"fcrm_change_badge fcrm_change_neutral"};const e=Number(t);return isNaN(e)||0===e?{text:"0%",cssClass:"fcrm_change_badge fcrm_change_neutral"}:e>0?{text:"↑ "+Math.abs(e).toFixed(1)+"%",cssClass:"fcrm_change_badge fcrm_change_positive"}:{text:"↓ "+Math.abs(e).toFixed(1)+"%",cssClass:"fcrm_change_badge fcrm_change_negative"}}const Pa={class:"fcrm_commerce_growth"},Na={class:"fcrm_report_card"},Ya={class:"fcrm_report_card_header"},ja={class:"fcrm_period_selector"},Ga=["onClick"],Fa={class:"fcrm_report_card_actions"},za={class:"fcrm_commerce_chart_nav"},Ba={class:"fcrm_commerce_chart_controls"},Ua={key:0,class:"fcrm_product_filter_wrap"},qa={key:0,class:"fcrm_period_selector"},Ha={class:"fcrm_report_card_body"},Wa={key:0,class:"fcrm_chart_date_summary"},Za={key:1,class:"fcrm_chart_empty"},Ka={class:"fcrm_chart_empty_title"},Qa={class:"fcrm_chart_empty_hint"},Xa={class:"fcrm_report_card fcrm_comparison_report_table"},Ja={class:"fcrm_report_card_header"},tr={class:"fcrm_report_card_title_group"},er={class:"fcrm_report_card_actions"},ar={class:"icon"},rr={class:"fcrm_report_card_body"},sr={class:"fcrm_global_table"},nr={key:0},or={class:"fcrm_compare_date_cell"},ir={class:"fcrm_date_primary"},lr={key:0,class:"fcrm_date_compare"},cr={key:0},dr={key:0},pr={key:1,class:"fcrm_report_card"},ur={class:"fcrm_report_card_body"};const hr={key:0,class:"fcrm-layout-width"},mr={class:"fcrm_skeleton_wrap"},_r={class:"d-flex items-center gap-4"},gr={class:"fcrm-layout-width"},fr={class:"fcrm_card_widgets fcrm_report_card_widget"},vr={class:"fcrm_card_widget_icon fcrm_icon_background_gray fcrm_mb-12"},br={class:"fcrm_card_widget_title"},yr={class:"fcrm_card_widget_content fcrm_mt-4"},Cr={class:"d-flex items-center"},wr={key:0},kr={class:"fcrm_card_widget"},$r={class:"fcrm_card_widget_icon fcrm_icon_background_gray fcrm_mb-12"},xr={class:"fcrm_card_widget_title"},Sr={class:"fcrm_card_widget_content fcrm_mt-4"},Tr={class:"d-flex items-center"},Mr={key:0},Dr={class:"fcrm_card_widget"},Rr={class:"fcrm_card_widget_icon fcrm_icon_background_gray fcrm_mb-12"},Or={class:"fcrm_card_widget_title"},Lr={class:"fcrm_card_widget_content fcrm_mt-4"},Ar={class:"fcrm_report_row two-col"},Ir={class:"fcrm_report_card"},Vr={class:"fcrm_report_card_header"},Er={key:0,class:"fcrm_report_card_actions"},Pr={class:"fcrm_report_card_body"},Nr=["aria-label"],Yr={class:"fcrm_top_product_info"},jr={class:"fcrm_top_product_name"},Gr={class:"fcrm_top_product_count"},Fr={key:0,class:"fcrm_top_product_revenue"},zr={key:0},Br={key:2,class:"fcrm-layout-width"},Ur={class:"fc_m_20 fc_onboarding"},qr={class:"fluentcrm_body fc_narrow_box"},Hr=["innerHTML"];const Wr=Z({name:"CommerceReports",inject:["toggleMenu","isReportsMenuOpen"],components:{PageHeader:st,Icons:X,CustomIcon:it,CommerceGrowth:Z({name:"CommerceGrowth",props:["provider","overview"],components:{ChartBuilder:gt,AjaxSelector:ot,RangePicker:Ft,Icons:X},data:()=>({activeTab:"product_growth",data_sets:[],range_settings:{date_range:[],compare_range:[],compare_type:"previous_period"},chartType:"line",sub_type:"all",sub_type_value:"",product_id:"",fetching:!1,app_ready:!1,showAllRows:!0}),computed:{current_report(){return this.overview&&this.overview.supports&&this.overview.supports[this.activeTab]||{}},isCustomersGrowth(){return"customers_growth"===this.activeTab},currencySign(){const t=this.current_report.is_money&&this.overview.currency_sign||"";if(!t)return t;const e=document.createElement("textarea");return e.innerHTML=t,e.value},hasData(){return!(!this.data_sets||!this.data_sets.length)&&this.data_sets.some(t=>t.data&&Object.keys(t.data).length>0)},chartDataSets(){if(!this.data_sets||!this.data_sets.length)return[];const t=this.isCustomersGrowth?"bar":this.chartType;return this.data_sets.map(e=>({...e,type:t}))},tabular_items(){const t=[],e={},a=[],r=[];return this.each(this.data_sets,(s,n)=>{if(!s||!s.data)return;e[n]=0,r.push(s.label);let o=0;this.each(s.data,(r,s)=>{t[o]||(t[o]=[]),a[o]||(a[o]=[]),t[o].push(Number(r)),-1===a[o].indexOf(s)&&a[o].push(s),e[n]+=Number(r),o++})}),{headers:r,items:t,totals:e,labels:a,is_compare:2===Object.values(e).length,prefix:this.current_report.is_money?this.currencySign:""}},tableRows(){const t=this.tabular_items,e=[];for(let a=0;a!t);!this.showAllRows&&n||e.push({labels:s,values:r})}return e},totalChangeBadge(){const t=Object.values(this.tabular_items.totals);return t.length<2||!t[1]?Ea(null):Ea(Va(t[0],t[1]))}},watch:{activeTab(){this.sub_type="all",this.sub_type_value="",this.loadSavedChartType(),this.fetch()},product_id(){this.fetch()},sub_type(t){const e=!this.sub_type_value||"all"===t;this.sub_type_value="",e&&this.fetch()},sub_type_value(t,e){t!==e&&("all"!==this.sub_type||t)&&this.fetch()}},methods:{fetch(){this.fetching=!0,this.data_sets=[],this.$get("commerce-reports/"+this.provider+"/report",{item_id:this.product_id,...this.range_settings,report_type:this.activeTab,sub_type:this.sub_type,sub_type_value:this.sub_type_value}).then(t=>{this.range_settings.date_range=t.current_range,this.data_sets=t.data_sets}).catch(t=>{this.handleError(t)}).finally(()=>{this.fetching=!1})},setChartType(t){this.chartType=t;try{localStorage.setItem("fcrm_report_chart_type_"+this.activeTab,t)}catch(e){}},loadSavedChartType(){try{const t=localStorage.getItem("fcrm_report_chart_type_"+this.activeTab);t&&(this.chartType=t)}catch(t){}},formatDateLabel(t){if(!t)return"";const e=window.dayjs?window.dayjs(t):null;if(!e||!e.isValid())return t;const a=window.dayjs();return a&&e.year()!==a.year()?e.format("MMM D, YYYY"):e.format("MMM D")},getRowChangeBadge:t=>!t||t.length<2||!t[1]?Ea(null):Ea(Va(t[0],t[1])),exportCSV(){const t=this.tabular_items,e=["Date",...t.headers];t.is_compare&&e.push("Change");const a=[e];for(let d=0;dt||0),s=[e,...r];if(t.is_compare&&r.length>=2){const t=Va(r[0],r[1]);s.push(null!==t?t.toFixed(2)+"%":"")}a.push(s)}const r=Object.values(t.totals),s=["Total",...r];if(t.is_compare&&r.length>=2){const t=Va(r[0],r[1]);s.push(null!==t?t.toFixed(2)+"%":"")}a.push(s);const n=a.map(t=>t.map(t=>'"'+String(t).replace(/"/g,'""')+'"').join(",")).join("\n"),o=new Blob([n],{type:"text/csv;charset=utf-8;"}),i=URL.createObjectURL(o),l=document.createElement("a"),c=this.range_settings.date_range||[];l.href=i,l.download="fcrm_"+this.activeTab+"_"+(c[0]||"")+"_"+(c[1]||"")+".csv",document.body.appendChild(l),l.click(),document.body.removeChild(l),URL.revokeObjectURL(i)}},created(){if(this.overview&&this.overview.supports){const t=Object.keys(this.overview.supports);t.length&&!this.overview.supports[this.activeTab]&&(this.activeTab=t[0])}this.loadSavedChartType()},mounted(){this.app_ready=!0,this.fetch()}},[["render",function(e,a,n,o,c,d){const p=A("ajax-selector"),u=A("range-picker"),h=r,m=s,_=A("Icons"),g=t,f=A("chart-builder"),v=i,b=l;return x(),S("div",Pa,[c.app_ready?(x(),S(j,{key:0},[T("div",Na,[T("div",Ya,[T("div",ja,[(x(!0),S(j,null,G(n.overview.supports,(t,e)=>(x(),S("span",{key:e,class:N(["fcrm_period_btn",{fc_active:c.activeTab===e}]),onClick:t=>c.activeTab=e},E(t.title),11,Ga))),128))]),T("div",Fa,[T("div",za,[T("div",Ba,[d.current_report.has_product?(x(),S("span",Ua,[V(p,{field:{placeholder:e.$t("All Products"),is_multiple:!1,option_key:"product_selector_"+n.provider,size:"small",clearable:!0},modelValue:c.product_id,"onUpdate:modelValue":a[0]||(a[0]=t=>c.product_id=t)},null,8,["field","modelValue"])])):D("",!0),V(u,{onChanged:a[1]||(a[1]=t=>d.fetch()),range_settings:c.range_settings},null,8,["range_settings"]),d.current_report.sub_types?(x(),S(j,{key:1},[V(m,{modelValue:c.sub_type,"onUpdate:modelValue":a[2]||(a[2]=t=>c.sub_type=t),size:"small",placeholder:e.$t("Select"),style:{"min-width":"140px"}},{default:Y(()=>[(x(!0),S(j,null,G(d.current_report.sub_types,(t,e)=>(x(),I(h,{key:e,label:t.label,value:e},null,8,["label","value"]))),128))],void 0),_:1},8,["modelValue","placeholder"]),"all"!==c.sub_type&&(d.current_report.tags||d.current_report.lists)?(x(),I(m,{key:0,modelValue:c.sub_type_value,"onUpdate:modelValue":a[3]||(a[3]=t=>c.sub_type_value=t),size:"small",filterable:"",placeholder:e.$t("Select"),style:{"min-width":"140px"}},{default:Y(()=>[(x(!0),S(j,null,G("tag"===c.sub_type?d.current_report.tags:d.current_report.lists,t=>(x(),I(h,{key:t.id,label:t.title,value:t.id},null,8,["label","value"]))),128))],void 0),_:1},8,["modelValue","placeholder"])):D("",!0)],64)):D("",!0)]),d.isCustomersGrowth?D("",!0):(x(),S("div",qa,[V(g,{class:N(["small only-icon-btn",{is_active:"bar"===c.chartType}]),"aria-label":e.$t("Bar chart"),"aria-pressed":"bar"===c.chartType,onClick:a[4]||(a[4]=t=>d.setChartType("bar"))},{default:Y(()=>[V(_,{"icon-name":"bar-chart"})],void 0),_:1},8,["class","aria-label","aria-pressed"]),V(g,{class:N(["small only-icon-btn",{is_active:"line"===c.chartType}]),size:"small","aria-label":e.$t("Line chart"),"aria-pressed":"line"===c.chartType,onClick:a[5]||(a[5]=t=>d.setChartType("line"))},{default:Y(()=>[V(_,{"icon-name":"line-chart"})],void 0),_:1},8,["class","aria-label","aria-pressed"])]))])])]),T("div",Ha,[d.hasData?(x(),S(j,{key:0},[V(f,{data_sets:d.chartDataSets,currency_sign:d.currencySign,height:350},null,8,["data_sets","currency_sign"]),2===c.range_settings.date_range.length?(x(),S("p",Wa,E(e.$t("Showing stats from"))+" "+E(d.formatDateLabel(c.range_settings.date_range[0]))+" "+E(e.$t("to"))+" "+E(d.formatDateLabel(c.range_settings.date_range[1])),1)):D("",!0)],64)):(x(),S("div",Za,[a[8]||(a[8]=H('
',1)),T("p",Ka,E(e.$t("No activity in this period")),1),T("p",Qa,E(e.$t("Try a wider date range or check order status settings")),1)]))])]),T("div",Xa,[T("div",Ja,[T("div",tr,[T("h4",null,E(e.$t("Comparison Table")),1)]),T("div",er,[V(v,{modelValue:c.showAllRows,"onUpdate:modelValue":a[6]||(a[6]=t=>c.showAllRows=t),label:e.$t("Show All Rows")},null,8,["modelValue","label"]),V(g,{size:"small",onClick:a[7]||(a[7]=t=>d.exportCSV())},{default:Y(()=>[T("span",ar,[V(_,{"icon-name":"export"})]),P(" "+E(e.$t("Export CSV")),1)],void 0),_:1})])]),T("div",rr,[T("div",{class:N(["fcrm_global_table_wrapper",{fcrm_table_sticky:d.tableRows.length>8}])},[T("table",sr,[T("thead",null,[T("tr",null,[T("th",null,E(e.$t("Date")),1),(x(!0),S(j,null,G(d.tabular_items.headers,t=>(x(),S("th",{key:t},E(t),1))),128)),d.tabular_items.is_compare?(x(),S("th",nr,E(e.$t("Change")),1)):D("",!0)])]),T("tbody",null,[(x(!0),S(j,null,G(d.tableRows,(t,a)=>(x(),S("tr",{key:a},[T("td",null,[T("div",or,[T("span",ir,E(d.formatDateLabel(t.labels[0])),1),t.labels[1]?(x(),S("span",lr,E(e.$t("vs"))+" "+E(d.formatDateLabel(t.labels[1])),1)):D("",!0)])]),(x(!0),S(j,null,G(t.values,(t,a)=>(x(),S("td",{key:a},[t?(x(),S(j,{key:0},[P(E(d.tabular_items.prefix)+E(e.formatMoney(t)),1)],64)):(x(),S(j,{key:1},[P("—")],64))]))),128)),d.tabular_items.is_compare?(x(),S("td",cr,[T("span",{class:N(d.getRowChangeBadge(t.values).cssClass)},E(d.getRowChangeBadge(t.values).text),3)])):D("",!0)]))),128))]),T("tfoot",null,[T("tr",null,[T("td",null,[T("strong",null,E(e.$t("Total")),1)]),(x(!0),S(j,null,G(d.tabular_items.totals,(t,a)=>(x(),S("td",{key:a},E(d.tabular_items.prefix)+E(e.formatMoney(t)),1))),128)),d.tabular_items.is_compare?(x(),S("td",dr,[T("span",{class:N(d.totalChangeBadge.cssClass)},E(d.totalChangeBadge.text),3)])):D("",!0)])])])],2)])])],64)):(x(),S("div",pr,[T("div",ur,[V(b,{class:"fc_skeleton_loader",rows:3,animated:""}),V(b,{class:"fc_skeleton_loader",rows:6,animated:""})])]))])}]]),InfoFilled:c},props:["provider"],data:()=>({loading:!1,overview:!1,loading_top_products:!1,date_range:["",""],dateShortcuts:et}),computed:{isMenuOpen(){return!!this.isReportsMenuOpen&&this.isReportsMenuOpen()}},methods:{decodeCurrencySign(t){if(t&&t.currency_sign){const e=document.createElement("textarea");e.innerHTML=t.currency_sign,t.currency_sign=e.value}return t},getOverview(){this.loading=!0,this.$get("commerce-reports/"+this.provider,{with:["top_products"]}).then(t=>{this.overview=this.decodeCurrencySign(t.report)}).catch(t=>{this.handleError(t)}).finally(()=>{this.loading=!1})},getTopProducts(){this.loading_top_products=!0,this.$get("commerce-reports/"+this.provider,{top_products_only:"yes",date_range:this.date_range}).then(t=>{this.overview.top_products=t.report.top_products}).catch(t=>{this.handleError(t)}).finally(()=>{this.loading_top_products=!1})}},mounted(){this.getOverview()}},[["render",function(e,r,s,i,c,h){const m=l,_=A("Icons"),g=o,f=A("InfoFilled"),v=d,b=A("page-header"),y=A("custom-icon"),C=A("commerce-growth"),w=a,k=t,$=p,M=u,R=n;return x(),S("div",null,[c.loading?B((x(),S("div",hr,[T("div",mr,[V(m,{class:"fc_skeleton_loader",rows:2,animated:""}),V(m,{class:"fc_skeleton_loader",rows:5,animated:""})])])),[[R,c.loading]]):c.overview&&c.overview.enabled?(x(),S("div",{key:1,class:N(["fcrm_report_section fc_advanced_report","fc_report_"+s.provider])},[V(b,null,{title:Y(()=>[T("div",_r,[V(g,{enterable:!1,transition:"none",content:h.isMenuOpen?e.$t("Close Sidebar"):e.$t("Open Sidebar"),placement:"right"},{default:Y(()=>[T("span",{class:N(["fcrm-report-collapse-sidebar-btn cursor_pointer",{"is-collapsed":h.isMenuOpen}]),onClick:r[0]||(r[0]=(...t)=>h.toggleMenu&&h.toggleMenu(...t))},[V(_,{"icon-name":"sidebar",class:"d-block"})],2)],void 0,!0),_:1},8,["content"]),P(" "+E(c.overview.title)+" ",1),c.overview.title_info?(x(),I(g,{key:0,effect:"dark",content:c.overview.title_info,placement:"top-start"},{default:Y(()=>[V(v,null,{default:Y(()=>[V(f)],void 0,!0),_:1})],void 0,!0),_:1},8,["content"])):D("",!0)])]),_:1}),T("div",gr,[T("div",fr,[(x(!0),S(j,null,G(c.overview.widgets,(t,a)=>(x(),S("div",{key:a,class:"fcrm_card_widget"},[T("div",vr,[V(y,{type:a},null,8,["type"])]),T("div",br,E(t.label),1),T("div",yr,[T("div",Cr,[t.is_money&&c.overview.currency_sign?(x(),S("span",wr,E(c.overview.currency_sign),1)):D("",!0),P(E(e.formatMoney(t.value)),1)])])]))),128)),c.overview.store_average?(x(),S(j,{key:0},[T("div",kr,[T("div",$r,[V(y,{type:"avarage_order_value"})]),T("div",xr,E(e.$t("Average Orders Value (AOV)")),1),T("div",Sr,[T("div",Tr,[c.overview.currency_sign?(x(),S("span",Mr,E(c.overview.currency_sign),1)):D("",!0),P(E(e.formatMoney(c.overview.store_average.aov)),1)])])]),T("div",Dr,[T("div",Rr,[V(y,{type:"avarage_order_or_customer"})]),T("div",Or,E(e.$t("Average Orders Per Customer (AOC)")),1),T("div",Lr,E(e.formatMoney(c.overview.store_average.aoc)),1)])],64)):D("",!0)]),V(C,{overview:c.overview,provider:s.provider},null,8,["overview","provider"]),T("div",Ar,[T("div",Ir,[T("div",Vr,[T("h4",null,E(e.$t("Top Selling Products")),1),c.overview.has_top_products_filter?(x(),S("div",Er,[V($,{placement:"left",width:"400",trigger:"click"},{reference:Y(()=>[V(k,{class:"small only-icon-btn",size:"small"},{default:Y(()=>[V(_,{"icon-name":"calendar"})],void 0,!0),_:1})]),default:Y(()=>[V(w,{onChange:r[1]||(r[1]=t=>h.getTopProducts()),modelValue:c.date_range,"onUpdate:modelValue":r[2]||(r[2]=t=>c.date_range=t),type:"daterange","value-format":"YYYY-MM-DD",format:"MMM D, YYYY",shortcuts:c.dateShortcuts,"range-separator":e.$t("To"),"start-placeholder":e.$t("Start date"),"end-placeholder":e.$t("End date")},null,8,["modelValue","shortcuts","range-separator","start-placeholder","end-placeholder"])],void 0),_:1})])):D("",!0)]),B((x(),S("div",Pr,[c.overview.top_products&&c.overview.top_products.length?(x(),S("ul",{key:0,class:"fcrm_top_products_list","aria-label":e.$t("Top selling products")},[(x(!0),S(j,null,G(c.overview.top_products,t=>(x(),S("li",{key:t.item_id,class:"fcrm_top_product_item"},[T("div",Yr,[T("span",jr,E(t.post_title),1),T("span",Gr,[P(E(e.$t("Total:"))+" ",1),T("b",null,E(t.count),1)])]),t.revenue?(x(),S("span",Fr,[c.overview.currency_sign?(x(),S("em",zr,E(c.overview.currency_sign),1)):D("",!0),P(E(e.formatMoney(t.revenue)),1)])):D("",!0)]))),128))],8,Nr)):(x(),I(M,{key:1,description:e.$t("No products data")},null,8,["description"]))])),[[R,c.loading_top_products]])])])])],2)):c.overview?(x(),S("div",Br,[T("div",Ur,[T("div",qr,[T("h3",null,E(e.$t("Data Sync is required for"))+" "+E(c.overview.title),1),T("p",{innerHTML:c.overview.enable_instruction},null,8,Hr)])])])):D("",!0)])}]]),Zr={class:"fcrm-abandon-report-widgets fcrm_card_widgets fcrm_report_card_widget"},Kr={class:"fcrm_card_widget_icon fcrm_icon_background_gray fcrm_mb-12"},Qr={class:"fcrm_card_widget_title"},Xr={key:0,class:"count"},Jr=["innerHTML"];const ts=Z({name:"AbandonReportWidgets",components:{CustomIcon:it},props:["widgets"]},[["render",function(t,e,a,r,s,n){const o=A("custom-icon");return x(),S("div",Zr,[(x(!0),S(j,null,G(a.widgets,(t,e)=>(x(),S("div",{key:e,class:"fcrm_card_widget"},[T("div",Kr,[V(o,{type:e},null,8,["type"])]),T("div",Qr,[P(E(t.title)+" ",1),""!=t.count?(x(),S("span",Xr,E(t.count),1)):D("",!0)]),T("div",{class:"fcrm_card_widget_content fcrm_mt-4",innerHTML:t.value},null,8,Jr)]))),128))])}]]),es={class:"fcrm_table_wrapper"},as={class:"fcrm_table_header"},rs={key:0},ss={class:"fcrm_table_header_bulk_actions"},ns={key:0},os={class:"fcrm_table_body"},is={class:"fc_name_avatar"},ls={class:"fc_avatar"},cs=["src"],ds={class:"fc_names"},ps={class:"fc_name"},us={key:1},hs={class:"fc_email"},ms={key:1},_s=["onClick"],gs={key:0,style:{"margin-left":"5px","line-height":"1"}},fs=["src","alt"],vs=["title","href"],bs={style:{"max-width":"400px"}},ys={class:"el-popover__reference"},Cs={class:"icon"},ws={class:"el-popover__reference"},ks={class:"icon"},$s={class:"fcrm_empty_state"},xs={class:"fcrm_empty_state_text"},Ss={key:0,class:"fcrm_abandon_cart_details_wrap"},Ts={class:"fcrm_abandon_cart_address_wrap"},Ms={class:"fcrm_abandon_cart_address"},Ds={class:"fcrm_abandon_cart_address"},Rs={class:"cart-details-table"},Os={class:"product_image"},Ls={class:"product_image"},As=["src","alt"],Is={key:0,class:"discount-tr"},Vs={key:0,class:"fc_cart_coupons"},Es={class:"shipping-tr"},Ps={class:"tax-tr"},Ns={class:"total-tr"},Ys={key:0,class:"fcrm_cart_details_extra_info"},js={key:1,class:"fcrm_cart_details_extra_info"},Gs={key:2,class:"fcrm_cart_details_extra_info"},Fs=["href"];const zs={fluent_cart:{docUrl:"https://docs.fluentcrm.com/fluentcart-abandon-cart-automation",templateUrl:"https://fluentcrm.com/wp-content/uploads/fluent-template-files/fluentcart-abandon-cart-automation-33-3.json"},woo:{docUrl:"https://docs.fluentcrm.com/abandon-cart-automation",templateUrl:"https://fluentcrm.com/wp-content/uploads/fluent-template-files/abandon-cart-automation-1-1-1.json"}},Bs={class:"fcrm-abandon-reports-wrapper fcrm_report_section"},Us={class:"icon"},qs={class:"fcrm-layout-width"},Hs={class:"fcrm_notice_text"},Ws={key:0,class:"fcrm_notice_cta"},Zs=["onClick"],Ks=["href"],Qs={key:0,class:"fc_block_white"},Xs={class:"fcrm-abandon-report-carts-wrap"},Js={key:0,class:"fcrm_table_wrapper"},tn={class:"fcrm_table_body"},en={class:"fcrm_table_header_inner"},an={class:"fcrm_table_header_inner_left fcrm_table_header_inner_abcart"},rn={class:"fcrm_table_header_inner_actions"};const sn=Z({name:"AbandonReports",components:{PageHeader:st,CustomIcon:it,AbandonReportCarts:Z({name:"AbandonReportCarts",props:{carts:Array,drivers:{type:Object,default:()=>({})}},emits:["refetch"],components:{ItemCopier:ct,Confirm:lt,LazyIndividualProgress:dt,InfoFilled:c,View:m,Delete:h,Icons:X},computed:{hasMultipleDrivers(){return Object.keys(this.drivers).length>1},couponCodes(){var t,e;return((null==(e=null==(t=this.singleCart)?void 0:t.cart)?void 0:e.coupons_detail)||[]).map(t=>t.code).filter(Boolean).join(", ")}},data:()=>({dialogVisible:!1,singleCart:"",countries:window.fcAdmin.countries,deletingCart:!1,selection:!1,selectedCarts:[]}),methods:{handleSelectedCarts(){_.confirm(this.$t("Are you sure you want to delete selected carts?"),this.$t("Warning"),{confirmButtonText:this.$t("Yes"),cancelButtonText:this.$t("No"),type:"warning"}).then(()=>{this.deleteCarts(this.selectedCarts.map(t=>t.id))})},deleteCarts(t){this.deletingCart=!0,this.$post("abandon-carts/bulk-delete",{cart_ids:t}).then(t=>{this.$notify.success(t.message),this.selection=!1,this.selectedCarts=[],this.$emit("refetch")}).catch(t=>{this.handleError(t)}).finally(()=>{this.deletingCart=!1})},editCart(t){this.dialogVisible=!0,this.singleCart=t},addressHandler(t,e){if(!this.singleCart||!t)return this.$t("No Address found");let a=t.shippingAddress;if("billing"==e&&(a=t.billingAddress),a){let t=[(a.first_name+" "+a.last_name).trim(),(a.address_1+" "+(a.address_2||"")).trim(),a.postcode,a.city,this.countryName(a.country)];return t=t.filter(function(t){return!!t&&""!==t.trim()}),t.join(", ")}return this.$t("No Address found")},countryName(t){let e="";return this.countries.map(a=>{a.code==t&&(e=a.title)}),e||t},onSelection(t){this.selection=!!t.length,this.selectedCarts=t}}},[["render",function(e,a,r,s,n,i){const l=t,c=g,p=A("router-link"),u=A("lazy-individual-progress"),h=A("Icons"),m=d,_=A("InfoFilled"),w=o,k=b,$=A("confirm"),M=v,R=f,O=y,L=A("item-copier"),F=C;return x(),S("div",es,[T("div",as,[n.selection?D("",!0):(x(),S("div",rs,[W(e.$slots,"table_header")])),T("div",ss,[n.selection?(x(),S("div",ns,[V(l,{loading:n.deletingCart,onClick:i.handleSelectedCarts,type:"danger",size:"small"},{default:Y(()=>[P(E(e.$t("Delete Selected Carts")),1)],void 0),_:1},8,["loading","onClick"])])):D("",!0)])]),T("div",os,[V(O,{ref:"cartsTable",border:"",data:r.carts,style:{width:"100%"},stripe:"",onSelectionChange:i.onSelection},{empty:Y(()=>[T("div",$s,[V(h,{"icon-name":"common-empty-state"}),T("div",xs,[T("span",null,E(e.$t("Abandoned carts will appear here.")),1)])])]),default:Y(()=>[V(c,{type:"selection",width:45}),V(c,{label:e.$t("Name"),prop:"date",width:"250"},{default:Y(t=>[T("div",is,[T("div",ls,[T("img",{src:t.row.customer_avatar},null,8,cs)]),T("div",ds,[T("div",ps,[t.row.contact_id?(x(),I(p,{key:0,to:{name:"subscriber",params:{id:t.row.contact_id}}},{default:Y(()=>[P(E(t.row.full_name),1)],void 0,!0),_:2},1032,["to"])):(x(),S("span",us,E(t.row.full_name),1))]),T("div",hs,E(t.row.email),1)])])]),_:1},8,["label"]),V(c,{label:e.$t("Automation"),prop:"automation_id"},{default:Y(t=>[t.row.automation&&t.row.contact_id?(x(),I(u,{key:0,subscriber_id:t.row.contact_id,funnel:t.row.automation},null,8,["subscriber_id","funnel"])):(x(),S("span",ms," -- "))]),_:1},8,["label"]),V(c,{label:e.$t("Cart Total"),prop:"cart_total",sortable:"",width:"180"},{default:Y(t=>{var a;return[T("span",{onClick:e=>i.editCart(t.row),class:"cart-total fluentcrm_clickable"},[P(E(t.row.currency)+" "+E(t.row.total)+" ",1),(null==(a=r.drivers[t.row.provider])?void 0:a.logo)?(x(),S("span",gs,[T("img",{src:r.drivers[t.row.provider].logo,alt:r.drivers[t.row.provider].label,style:{width:"16px",height:"auto"}},null,8,fs)])):D("",!0)],8,_s),t.row.order_url?(x(),S("a",{key:0,target:"_blank",rel:"noopener",title:e.$t("View order"),href:t.row.order_url},[V(m,{class:"external-link-icon",style:{"margin-left":"5px",color:"var(--fc-secondary-text)"}},{default:Y(()=>[V(h,{"icon-name":"externalLink"})],void 0,!0),_:1})],8,vs)):D("",!0)]}),_:1},8,["label"]),V(c,{label:e.$t("Order Status"),prop:"cart_total",width:"180"},{default:Y(t=>[T("span",{class:N([t.row.status,"status"])},[P(E(t.row.status)+" ",1),"skipped"==t.row.status&&t.row.note?(x(),I(w,{key:0,"popper-class":"sidebar-popper",effect:"dark",placement:"top"},{content:Y(()=>[T("div",bs,E(t.row.note),1)]),default:Y(()=>[V(m,{class:"tooltip-icon",style:{cursor:"help"}},{default:Y(()=>[V(_)],void 0,!0),_:1})],void 0,!0),_:2},1024)):D("",!0)],2)]),_:1},8,["label"]),V(c,{label:e.$t("Time"),prop:"cart_total",sortable:"",width:"180"},{default:Y(t=>[P(E(e.$nsHumanDiffTime(t.row.created_at)),1)]),_:1},8,["label"]),V(c,{fixed:"right",align:"center",width:"60","class-name":"fcrm_table_actions_cell"},{default:Y(t=>[V(R,{trigger:"click",placement:"bottom-end"},{dropdown:Y(()=>[V(M,null,{default:Y(()=>[V(k,{onClick:e=>i.editCart(t.row)},{default:Y(()=>[T("span",ys,[T("span",Cs,[V(h,{"icon-name":"eye"})]),P(" "+E(e.$t("View Cart")),1)])],void 0,!0),_:1},8,["onClick"]),V(k,{class:"fcrm_danger_action"},{default:Y(()=>[V($,{placement:"top-start",message:e.$t("Are you sure you want to delete this cart?"),onYes:e=>i.deleteCarts([t.row.id])},{reference:Y(()=>[T("span",ws,[T("span",ks,[V(h,{"icon-name":"delete"})]),P(" "+E(e.$t("Delete Cart")),1)])]),_:1},8,["message","onYes"])],void 0,!0),_:2},1024)],void 0,!0),_:2},1024)]),default:Y(()=>[V(l,{link:"",class:"el-dropdown-link","aria-label":e.$t("Row actions")},{default:Y(()=>[V(h,{"icon-name":"more_actions"})],void 0,!0),_:1},8,["aria-label"])],void 0,!0),_:2},1024)]),_:1})],void 0),_:1},8,["data","onSelectionChange"]),W(e.$slots,"pagination_block")]),V(F,{"append-to-body":!0,title:e.$t("Cart Details"),modelValue:n.dialogVisible,"onUpdate:modelValue":a[0]||(a[0]=t=>n.dialogVisible=t),"custom-class":"fcrm_abandon_cart_details_popover"},{default:Y(()=>{var t,r,s,o,l,c,d,p,u,h;return[n.singleCart?(x(),S("div",Ss,[T("div",Ts,[T("div",Ms,[T("h4",null,E(e.$t("Billing Address")),1),T("p",null,E(i.addressHandler(null==(r=null==(t=n.singleCart)?void 0:t.cart)?void 0:r.customer_data,"billing")),1)]),T("div",Ds,[T("h4",null,E(e.$t("Shipping Address")),1),T("p",null,E(i.addressHandler(null==(o=null==(s=n.singleCart)?void 0:s.cart)?void 0:o.customer_data,"shipping")),1)])]),T("table",Rs,[T("thead",null,[T("tr",null,[T("th",Os,E(e.$t("Image")),1),T("th",null,E(e.$t("Items Details")),1),T("th",null,E(e.$t("Quantity")),1),T("th",null,E(e.$t("Unit Price")),1),T("th",null,E(e.$t("Amount")),1)])]),T("tbody",null,[(x(!0),S(j,null,G(null==(l=n.singleCart.cart)?void 0:l.cart_contents,(t,e)=>(x(),S("tr",{key:e},[T("td",Ls,[T("img",{src:t.product_image,alt:t.title},null,8,As)]),T("td",null,E(t.title),1),T("td",null,E(t.quantity),1),T("td",null,E(n.singleCart.currency)+" "+E(t.quantity?(t.line_total/t.quantity).toFixed(2):"0.00"),1),T("td",null,E(n.singleCart.currency)+" "+E(t.line_total),1)]))),128))]),T("tfoot",null,[Number(null==(c=n.singleCart)?void 0:c.discounts)>0?(x(),S("tr",Is,[a[1]||(a[1]=T("td",null,null,-1)),a[2]||(a[2]=T("td",null,null,-1)),a[3]||(a[3]=T("td",null,null,-1)),T("td",null,[P(E(e.$t("Discount"))+" ",1),i.couponCodes?(x(),S("span",Vs,"( "+E(e.$t("Coupon"))+": "+E(i.couponCodes)+" )",1)):D("",!0)]),T("td",null,"-"+E(n.singleCart.currency)+" "+E(n.singleCart.discounts),1)])):D("",!0),T("tr",Es,[a[4]||(a[4]=T("td",null,null,-1)),a[5]||(a[5]=T("td",null,null,-1)),a[6]||(a[6]=T("td",null,null,-1)),T("td",null,E(e.$t("Shipping")),1),T("td",null,E(n.singleCart.currency)+" "+E(null==(d=n.singleCart)?void 0:d.shipping),1)]),T("tr",Ps,[a[7]||(a[7]=T("td",null,null,-1)),a[8]||(a[8]=T("td",null,null,-1)),a[9]||(a[9]=T("td",null,null,-1)),T("td",null,E(e.$t("Tax(es)")),1),T("td",null,E(n.singleCart.currency)+" "+E(n.singleCart.tax),1)]),T("tr",Ns,[a[10]||(a[10]=T("td",null,null,-1)),a[11]||(a[11]=T("td",null,null,-1)),a[12]||(a[12]=T("td",null,null,-1)),T("td",null,E(e.$t("Total")),1),T("td",null,E(n.singleCart.currency)+" "+E(null==(p=n.singleCart)?void 0:p.total),1)])])]),(null==(h=null==(u=n.singleCart.cart)?void 0:u.customer_data)?void 0:h.order_comments)?(x(),S("div",Ys,[a[13]||(a[13]=T("hr",null,null,-1)),T("h4",null,E(e.$t("Order Comments")),1),T("p",null,E(n.singleCart.cart.customer_data.order_comments),1)])):D("",!0),n.singleCart.recovery_url?(x(),S("div",js,[T("h4",null,E(e.$t("Recovery URL")),1),V(L,{text:n.singleCart.recovery_url},null,8,["text"])])):D("",!0),n.singleCart.order_url?(x(),S("div",Gs,[T("a",{target:"_blank",rel:"noopener",href:n.singleCart.order_url},E(e.$t("View Original Order")),9,Fs)])):D("",!0)])):D("",!0)]},void 0),_:1},8,["title","modelValue"])])}]]),AbandonReportWidgets:ts,PaginationBar:pt,CalendarIcon:rt},data(){return{reportSummary:[],CalendarIcon:z(rt),loading:!1,cartsLoading:!1,dateRange:at(),disabledDate:t=>t.getTime()>Date.now(),shortcuts:[{text:this.$t("Last week"),value:()=>{const t=new Date,e=new Date;return e.setTime(e.getTime()-6048e5),[e,t]}},{text:this.$t("Last month"),value:()=>{const t=new Date,e=new Date;return e.setTime(e.getTime()-2592e6),[e,t]}},{text:this.$t("Last 3 months"),value:()=>{const t=new Date,e=new Date;return e.setTime(e.getTime()-7776e6),[e,t]}}],carts:[],query:{status:"all",search:""},pagination:{total:0,per_page:10,current_page:1},cartStatuses:{all:this.$t("All carts"),draft:this.$t("Draft Carts"),processing:this.$t("In Progress"),recovered:this.$t("Recovered Carts"),lost:this.$t("Lost Carts"),opt_out:this.$t("Opt-Out Carts"),skipped:this.$t("Skipped Carts")},haveAutomation:!0,missingAutomations:[],drivers:{},creatingProvider:""}},methods:{getWidgetAndCartsReports(){this.getReports(),this.getCartsReports()},getReports(){this.loading=!0,this.$get("abandon-carts/report-summary",{date_range:this.dateRange}).then(t=>{this.reportSummary=t.widgets}).catch(t=>{this.handleError(t)}).finally(()=>{this.loading=!1})},getCartsReports(){this.cartsLoading=!0,this.$get("abandon-carts",{date_range:this.dateRange,query:this.query,per_page:this.pagination.per_page,page:this.pagination.current_page}).then(t=>{this.carts=t.carts.data,this.haveAutomation=t.haveAutomation||!1,this.missingAutomations=t.missingAutomations||[],this.pagination.total=t.carts.total,this.drivers=t.drivers||{}}).catch(t=>{this.handleError(t)}).finally(()=>{this.cartsLoading=!1})},goToAbandonedSettings(){this.$router.push({name:"abandoned_cart_settings"})},goToFunnel(){this.$router.push({name:"funnels",query:{add:1}})},providerDocUrl:t=>(zs[t]||{}).docUrl||"https://docs.fluentcrm.com/abandon-cart-automation",providerTemplateUrl:t=>(zs[t]||{}).templateUrl||"",startWithTemplate(t){if(this.creatingProvider)return;const e=this.providerTemplateUrl(t);e&&(this.creatingProvider=t,this.$post("funnels/create-from-template",{template:{content:e}}).then(t=>{this.$notify.success(t.message),this.$router.push({name:"edit_funnel",params:{funnel_id:t.funnel.id}})}).catch(t=>{this.handleError(t),this.goToFunnel()}).finally(()=>{this.creatingProvider=""}))}},mounted(){this.changeTitle(this.$t("Abandoned Carts")),this.getReports(),this.getCartsReports()}},[["render",function(n,o,i,c,d,p){const u=a,h=A("custom-icon"),m=t,_=A("page-header"),g=l,f=A("abandon-report-widgets"),v=e,b=r,y=s,C=A("pagination-bar"),w=A("abandon-report-carts");return x(),S("div",Bs,[V(_,null,{title:Y(()=>[P(E(n.$t("Abandon Carts - Reports")),1)]),actions:Y(()=>[V(u,{modelValue:d.dateRange,"onUpdate:modelValue":o[0]||(o[0]=t=>d.dateRange=t),type:"daterange","start-placeholder":n.$t("Start date"),"end-placeholder":n.$t("End date"),"range-separator":"-",format:"MMM DD","value-format":"YYYY-MM-DD","disabled-date":d.disabledDate,shortcuts:d.shortcuts,"unlink-panels":"",onChange:p.getWidgetAndCartsReports,"prefix-icon":d.CalendarIcon},null,8,["modelValue","start-placeholder","end-placeholder","disabled-date","shortcuts","onChange","prefix-icon"]),V(m,{onClick:p.goToAbandonedSettings,class:"small only-icon-btn","aria-label":n.$t("Settings"),title:n.$t("Settings")},{default:Y(()=>[T("span",Us,[V(h,{type:"settings"})])],void 0,!0),_:1},8,["onClick","aria-label","title"])]),_:1}),T("div",qs,[(x(!0),S(j,null,G(d.missingAutomations,t=>(x(),S("div",{key:t.provider,class:"fcrm_notice",style:{display:"flex","align-items":"center",gap:"5px","border-left":"3px solid #FFB020",background:"var(--fc-warning-bg)"}},[o[7]||(o[7]=T("span",{style:{width:"20px",display:"flex"}},[T("svg",{xmlns:"http://www.w3.org/2000/svg",width:"16",height:"16",viewBox:"0 0 16 16",fill:"none"},[T("path",{d:"M8.49364 2.62164L13.9236 12.1352C13.9737 12.2228 14 12.3223 14 12.4235C14 12.5247 13.9737 12.6241 13.9236 12.7117C13.8736 12.7994 13.8017 12.8722 13.715 12.9228C13.6283 12.9734 13.5301 13 13.43 13H2.57C2.46995 13 2.37165 12.9734 2.285 12.9228C2.19835 12.8722 2.12639 12.7994 2.07636 12.7117C2.02634 12.6241 2 12.5247 2 12.4235C2 12.3223 2.02634 12.2228 2.07637 12.1352L7.50636 2.62164C7.5564 2.53399 7.62835 2.46121 7.715 2.41061C7.80165 2.36001 7.89995 2.33337 8 2.33337C8.10005 2.33337 8.19835 2.36001 8.285 2.41061C8.37165 2.46121 8.4436 2.53399 8.49364 2.62164ZM7.42998 10.1172V11.2703H8.57002V10.1172H7.42998ZM7.42998 6.08111V8.964H8.57002V6.08111H7.42998Z",fill:"var(--fc-warning)"})])],-1)),T("span",Hs,[P(E(n.$t("No active Abandoned Cart automation for %s.",t.label))+" ",1),T("a",{href:"#",onClick:o[1]||(o[1]=q(t=>p.goToFunnel(),["prevent"]))},E(n.$t("Set up an automation")),1),o[5]||(o[5]=P(E(" ")+" ",-1)),p.providerTemplateUrl(t.provider)?(x(),S("span",Ws,[P(E(n.$t("or"))+" ",1),T("a",{href:"#",class:N({"is-disabled":d.creatingProvider===t.provider}),onClick:q(e=>p.startWithTemplate(t.provider),["prevent"])},E(d.creatingProvider===t.provider?n.$t("Creating automation..."):n.$t("start with the built-in template")),11,Zs)])):D("",!0),P(" "+E(n.$t("to recover lost sales."))+" "+E(n.$t("To learn more about %s Abandoned Cart features,",t.label))+" ",1),T("a",{href:p.providerDocUrl(t.provider),target:"_blank",rel:"noopener noreferrer"},E(n.$t("click here")),9,Ks),o[6]||(o[6]=P(". ",-1))])]))),128)),d.loading?(x(),S("div",Qs,[V(g,{rows:6,animated:""})])):(x(),I(f,{key:1,widgets:d.reportSummary},null,8,["widgets"])),T("div",Xs,[d.loading||d.cartsLoading?(x(),S("div",Js,[T("div",tn,[V(g,{style:{padding:"20px"},rows:7,animated:""})])])):(x(),I(w,{key:1,drivers:d.drivers,onRefetch:p.getCartsReports,carts:d.carts},{table_header:Y(()=>[T("div",en,[T("div",an,[V(v,{clearable:"",modelValue:d.query.search,"onUpdate:modelValue":o[2]||(o[2]=t=>d.query.search=t),onClear:p.getCartsReports,onKeyup:U(p.getCartsReports,["enter"]),placeholder:n.$t("Search by Name/ Email")},null,8,["modelValue","onClear","onKeyup","placeholder"])]),T("div",rn,[V(y,{class:"fcrm_abandon_status_select",onChange:o[3]||(o[3]=t=>p.getCartsReports()),size:"small",modelValue:d.query.status,"onUpdate:modelValue":o[4]||(o[4]=t=>d.query.status=t)},{default:Y(()=>[(x(!0),S(j,null,G(d.cartStatuses,(t,e)=>(x(),I(b,{key:e,label:t,value:e},null,8,["label","value"]))),128))],void 0,!0),_:1},8,["modelValue"])])])]),pagination_block:Y(()=>[V(C,{pagination:d.pagination,onFetch:p.getCartsReports},null,8,["pagination","onFetch"])]),_:1},8,["drivers","onRefetch","carts"]))])])])}],["__scopeId","data-v-527fc7c7"]]),nn={class:"fluentcrm-view-wrapper fluentcrm_view fcrm_reports_home"},on={class:"fcrm_reports_sidebar_header"},ln=["aria-label","aria-expanded"],cn={class:"fcrm_reports_sidebar_header--title"},dn=["aria-selected","onClick","onKeydown"],pn={class:"fcrm_nav_icon"},un=["src"],hn={class:"fcrm_nav_label"},mn={class:"fcrm_reports_content"},_n={key:1,class:"fcrm_pad_around fcrm_upgrade_wrapper",style:{display:"flex","justify-content":"center","align-items":"center","min-height":"60vh"}},gn={class:"fcrm_upgrade_banner fcrm_upgrade_banner--text-only",style:{"text-align":"center","border-radius":"8px","max-width":"520px"}},fn={class:"fcrm_upgrade_banner__content"},vn={class:"fcrm_upgrade_banner__title"},bn={class:"fcrm_upgrade_banner__description"},yn=["href"],Cn={class:"icon"};const wn=Z({name:"ReportsHome",provide(){return{toggleMenu:this.toggleMenu,isReportsMenuOpen:()=>this.isMenuOpen}},components:{ContactReports:Ve,EmailReports:Ia,CommerceReports:Wr,AbandonReports:sn,Icons:X},data(){return{activeTab:"contacts",isMenuOpen:!1,isSidebarCollapsed:!1,isMenuHovered:!1,tooltipPosition:"right",switching:!1,proProviders:{},coreTabs:{contacts:{title:this.$t("Contacts"),icon:"contacts",condition:()=>this.hasPermission("fcrm_manage_settings")},emails:{title:this.$t("Emails"),icon:"envelope",condition:()=>this.hasPermission("fcrm_manage_settings")},abandoned_carts:{title:this.$t("Abandoned Carts"),icon:"cart",condition:()=>!!this.appVars.has_abandon_carts&&!!this.appVars.can_read_abandon_carts}}}},computed:{visibleTabs(){const t={};for(const[e,a]of Object.entries(this.coreTabs))a.condition&&!a.condition()||(t[e]=a);for(const[e,a]of Object.entries(this.proProviders))"crm"!==e&&(t[e]={title:a.title,iconSrc:a.icon||""});return t}},watch:{activeTab(t){this.$router.replace({name:"reports",query:{tab:t}}),this.isProTab(t)&&(this.switching=!0,setTimeout(()=>{this.switching=!1},100))},"$route.query.tab"(t){t&&t!==this.activeTab&&(this.activeTab=t)},visibleTabs:{handler(){this.ensureValidActiveTab()},deep:!0}},methods:{ensureValidActiveTab(){const t=Object.keys(this.visibleTabs);t.length&&(t.includes(this.activeTab)||(this.activeTab=t[0]))},toggleMenu(){this.isMenuOpen=!this.isMenuOpen},toggleSidebar(){this.isSidebarCollapsed=!this.isSidebarCollapsed},setActiveTab(t){this.activeTab=t,this.isMenuOpen=!1},isProTab(t){return!!this.proProviders[t]&&"crm"!==t},getReportProviders(){this.has_campaign_pro&&this.$get("reports/advanced-providers").then(t=>{this.proProviders=t.providers||{}}).catch(t=>{this.handleError(t)})}},mounted(){this.changeTitle(this.$t("Reports")),this.getReportProviders(),window.fcAdmin&&window.fcAdmin.is_rtl&&(this.tooltipPosition="left"),this.$route.query.tab&&(this.activeTab=this.$route.query.tab),this.ensureValidActiveTab()}},[["render",function(t,e,a,r,s,n){const i=A("Icons"),l=o,c=A("contact-reports"),d=A("email-reports"),p=A("abandon-reports"),u=A("commerce-reports");return x(),S("div",nn,[t.has_campaign_pro?(x(),S("div",{key:0,class:N(["fcrm_reports_layout",{"is-collapsed":s.isSidebarCollapsed,"is-hover-expanded":s.isMenuHovered}])},[T("div",{class:N(["fcrm_reports_sidebar_overlay",{"is-open":s.isMenuOpen}]),onClick:e[0]||(e[0]=(...t)=>n.toggleMenu&&n.toggleMenu(...t))},null,2),T("div",{id:"fcrm_reports_sidebar",class:N(["fcrm_reports_sidebar",{"is-open":s.isMenuOpen}])},[T("div",on,[V(l,{enterable:!1,transition:"none",content:s.isSidebarCollapsed?t.$t("Open Sidebar"):t.$t("Close Sidebar"),placement:s.tooltipPosition},{default:Y(()=>[T("button",{type:"button",class:"fcrm_reports_sidebar_header--btn","aria-label":s.isSidebarCollapsed?t.$t("Open Sidebar"):t.$t("Close Sidebar"),"aria-expanded":!s.isSidebarCollapsed,"aria-controls":"fcrm_reports_sidebar",onClick:e[1]||(e[1]=(...t)=>n.toggleSidebar&&n.toggleSidebar(...t))},[V(i,{"icon-name":"sidebar"})],8,ln)],void 0),_:1},8,["content","placement"]),T("span",cn,E(t.$t("Reports")),1)]),T("ul",{class:"fcrm_reports_nav",role:"tablist",onMouseenter:e[2]||(e[2]=t=>s.isMenuHovered=!0),onMouseleave:e[3]||(e[3]=t=>s.isMenuHovered=!1)},[(x(!0),S(j,null,G(n.visibleTabs,(t,e)=>(x(),S("li",{key:e,role:"tab",tabindex:"0","aria-selected":s.activeTab===e,class:N({fc_active:s.activeTab===e}),onClick:t=>n.setActiveTab(e),onKeydown:[U(t=>n.setActiveTab(e),["enter"]),U(q(t=>n.setActiveTab(e),["prevent"]),["space"])]},[T("span",pn,[t.iconSrc?(x(),S("img",{key:0,src:t.iconSrc,width:"16",height:"16"},null,8,un)):(x(),I(i,{key:1,"icon-name":t.icon||"el-icon-set-up"},null,8,["icon-name"]))]),T("span",hn,E(t.title),1)],42,dn))),128))],32)],2),T("div",mn,["contacts"===s.activeTab?(x(),I(c,{key:0})):"emails"===s.activeTab?(x(),I(d,{key:1})):"abandoned_carts"===s.activeTab?(x(),I(p,{key:2})):n.isProTab(s.activeTab)?(x(),S(j,{key:3},[s.switching?D("",!0):(x(),I(u,{key:0,provider:s.activeTab},null,8,["provider"]))],64)):D("",!0)])],2)):(x(),S("div",_n,[T("div",gn,[T("div",fn,[T("h2",vn,E(t.$t("Upgrade to FluentCRM Pro to Get Advanced Reports")),1),T("p",bn,E(t.$t("Reports_Pro_Description")),1),T("a",{class:"el-button el-button--primary",style:{"border-radius":"8px"},href:t.appVars.crm_pro_url,target:"_blank",rel:"noopener"},[T("span",null,[T("span",Cn,[V(i,{"icon-name":"crown"})]),P(" "+E(t.$t("Get FluentCRM Pro")),1)])],8,yn)])])]))])}]]);export{wn as default}; diff --git a/wp-content/plugins/fluent-crm/assets/admin/Modules/Settings/AddOns.js b/wp-content/plugins/fluent-crm/assets/admin/Modules/Settings/AddOns.js new file mode 100644 index 0000000..0759794 --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/admin/Modules/Settings/AddOns.js @@ -0,0 +1 @@ +import{k as e,az as a,ax as t,aw as s,aE as l,aF as n,e as d,aK as o,aL as i,ay as r,aT as _,aD as c,aA as m,aB as u,aO as f}from"../../../vendor-element-plus.js?ver=3.1.8";import{aQ as p,W as g,X as v,Z as b,a9 as h,aa as y,a0 as $,Y as x,a5 as k,ab as S,a8 as C,a6 as V,J as M,az as w}from"../../../vendor.js?ver=3.1.8";import{_ as E,I as A}from"../../../fc-bits-ui.js?ver=3.1.8";import{I as P}from"../../../ItemCopier.js?ver=3.1.8";import{B as D}from"../../../BaseCard.js?ver=3.1.8";const F={name:"FrontendPortalFeature",components:{ItemCopier:P},props:{settings:{type:Object,required:!0},direction:{type:String,default:"rtl"},saving:{type:Boolean,default:!1},hasCampaignPro:{type:Boolean,default:!1}},emits:["save"],data:()=>({drawer:!1,pageLoading:!1,pageOptions:[]}),computed:{standaloneUrl(){const e=this.settings.frontend_portal_slug||"fluentcrm";return this.appVars.site_url.replace(/\/$/,"")+"/"+e}},watch:{drawer(e){e&&this.searchPages(""),this.syncDrawerQuery(e)},"$route.query.frontportal"(e){this.drawer="1"===e&&this.hasCampaignPro},"settings.frontend_portal_page_id"(){"shortcode"===this.settings.frontend_portal_render_type&&this.searchPages("")}},methods:{syncDrawerQuery(e){const a={...this.$route.query};if(e){if("1"===a.frontportal)return;return a.frontportal="1",void this.$router.push({query:a})}void 0!==a.frontportal&&(delete a.frontportal,this.$router.replace({query:a}))},searchPages(e=""){this.pageLoading=!0,this.$get("reports/ajax-options",{option_key:"post_type",sub_option_key:"page",search:e,values:this.settings.frontend_portal_page_id?[this.settings.frontend_portal_page_id]:[]}).then(e=>{const a=e.options||[];this.pageOptions=a.map(e=>({...e,id:Number(e.id),title:`${e.title||this.$t("Page")} (#${e.id})`}))}).catch(e=>{this.$handleError(e)}).finally(()=>{this.pageLoading=!1})}},mounted(){this.settings.frontend_portal_render_type||(this.settings.frontend_portal_render_type="standalone"),this.settings.frontend_portal_slug||(this.settings.frontend_portal_slug="fluentcrm"),this.settings.frontend_portal_page_id&&(this.settings.frontend_portal_page_id=Number(this.settings.frontend_portal_page_id)),this.hasCampaignPro&&"1"===this.$route.query.frontportal&&(this.drawer=!0),this.searchPages("")}},U={class:"fcrm_addons_features_box"},L={class:"fcrm_addons_features_box--content"},z={class:"fcrm_addons_features_box--content-title"},T={class:"fcrm_addons_features_box--content-desc"},B={class:"fcrm_addons_features_box--actions"},I={class:"fcrm_addons_popover_content"},R={class:"fc_inline_help"},O=["href"],W={class:"fcrm_secondary_text small fcrm_mt_4"},N={class:"dialog-footer"};const j={class:"fcrm_addons_page"},q={class:"fcrm_max_w_800"},H={class:"fcrm_addons_features_lists"},G={class:"fcrm_addons_features_box"},Q={class:"fcrm_addons_features_box--content"},K={class:"fcrm_addons_features_box--content-title"},Y={class:"fcrm_addons_features_box--content-desc"},J={class:"fcrm_addons_features_box--actions"},X={class:"fcrm_addons_features_box"},Z={class:"fcrm_addons_features_box--content"},ee={class:"fcrm_addons_features_box--content-title"},ae={class:"fcrm_addons_features_box--content-desc"},te={class:"fcrm_addons_features_box--actions"},se={class:"fcrm_addons_popover_body"},le={class:"fcrm_addons_popover_body--header"},ne={class:"fcrm_addons_popover_body--header-title"},de={class:"fcrm_addons_popover_content"},oe={key:0,class:"condition_fields"},ie={class:"fcrm_input_hit"},re={class:"fcrm_addons_popover_footer"},_e={class:"fcrm_addons_features_box"},ce={class:"fcrm_addons_features_box--content"},me={class:"fcrm_addons_features_box--content-title"},ue={class:"fcrm_addons_features_box--content-desc"},fe={class:"fcrm_addons_features_box--actions"},pe={class:"fcrm_addons_features_box"},ge={class:"fcrm_addons_features_box--content"},ve={class:"fcrm_addons_features_box--content-title"},be={class:"fcrm_addons_features_box--content-desc"},he={class:"fcrm_addons_features_box--actions"},ye={class:"fcrm_addons_features_box"},$e={class:"fcrm_addons_features_box--content"},xe={class:"fcrm_addons_features_box--content-title"},ke={class:"fcrm_addons_features_box--content-desc"},Se={class:"fcrm_addons_features_box--actions"},Ce={class:"fcrm_addons_popover_content"},Ve={style:{margin:"0"}},Me={style:{margin:"0"},class:"help_text"},we={style:{margin:"0"}},Ee={key:1},Ae={class:"text-align-center"},Pe={class:"fcrm_info_box"},De={class:"dialog-footer"},Fe={class:"fcrm_addons_features_box"},Ue={class:"fcrm_addons_features_box--content"},Le={class:"fcrm_addons_features_box--content-title"},ze={class:"fcrm_addons_features_box--content-desc"},Te={href:"https://fluentcrm.com/docs/event-tracking-automation/",target:"_blank",rel:"noopener noreferrer"},Be={class:"fcrm_addons_features_box--actions"},Ie={class:"fcrm_addons_popover_body"},Re={class:"fcrm_addons_popover_content"},Oe={class:"fcrm_addons_popover_footer"},We={class:"fcrm_addons_features_box"},Ne={class:"fcrm_addons_features_box--content"},je={class:"fcrm_addons_features_box--content-title"},qe={class:"fcrm_addons_features_box--content-desc"},He={class:"fcrm_addons_features_box--actions"},Ge={class:"fcrm_addons_popover_body"},Qe={class:"fcrm_addons_popover_content"},Ke={class:"fcrm_addons_popover_footer"},Ye={class:"fcrm_addons_features_box"},Je={class:"fcrm_addons_features_box--content"},Xe={class:"fcrm_addons_features_box--content-title"},Ze={class:"fcrm_addons_features_box--content-desc"},ea={key:0,class:"fcrm_addons_features_box--alert"},aa={class:"fcrm_list"},ta={class:"fcrm_addons_features_box--actions"},sa={class:"fcrm_addons_popover_body"},la={class:"fcrm_addons_popover_content"},na={class:"fcrm_addons_popover_footer"},da={class:"fcrm_addons_features_box"},oa={class:"fcrm_addons_features_box--content"},ia={class:"fcrm_addons_features_box--content-title"},ra={class:"fcrm_addons_features_box--content-desc"},_a={class:"fcrm_addons_features_box--actions"},ca={class:"fcrm_addons_popover_body"},ma={class:"fcrm_addons_popover_content"},ua={class:"fcrm_addons_popover_footer"},fa={class:"fcrm_recommended_plugins_lists"},pa={key:0,class:"fcrm_recommended_plugins_box"},ga={class:"fcrm_recommended_plugins_box--header"},va={class:"fcrm_recommended_plugins_box--content"},ba={class:"fcrm_recommended_plugins_box--title"},ha={class:"fcrm_recommended_plugins_box--desc"},ya={class:"fcrm_recommended_plugins_box--footer"},$a={class:"fcrm_recommended_plugins_box--header"},xa={class:"fcrm_recommended_plugins_box--icon"},ka=["src","alt"],Sa={class:"fcrm_recommended_plugins_box--content"},Ca={class:"fcrm_recommended_plugins_box--title"},Va=["href"],Ma={key:0,class:"fcrm_badge fcrm_badge_success"},wa={class:"fcrm_recommended_plugins_box--desc"},Ea={class:"fcrm_recommended_plugins_box--footer"},Aa={class:"icon"};const Pa=E({name:"Addons",components:{Icons:A,FrontendPortalFeature:E(F,[["render",function(c,m,u,f,E,A){const P=e,D=a,F=n,j=l,q=s,H=d,G=p("item-copier"),Q=i,K=o,Y=t,J=_,X=r;return g(),v("div",U,[b("div",L,[b("div",z,[h(y(c.$t("Frontend Portal"))+" ",1),b("span",{class:$("no"==u.settings.frontend_portal?"fcrm_badge":"fcrm_badge fcrm_badge_success")},y("yes"==u.settings.frontend_portal?c.$t("Enabled"):c.$t("Disabled")),3)]),b("div",T,y(c.$t("Expose the full FluentCRM app on the frontend for logged-in CRM users.")),1)]),b("div",B,[u.hasCampaignPro?(g(),x(P,{key:0,size:"small",onClick:m[0]||(m[0]=e=>E.drawer=!0)},{default:k(()=>[h(y(c.$t("Settings")),1)],void 0),_:1})):(g(),x(P,{key:1,size:"small",type:"primary",tag:"a",target:"_blank",rel:"noopener noreferrer",href:c.appVars.crm_pro_url},{default:k(()=>[h(y(c.$t("Upgrade to Pro")),1)],void 0),_:1},8,["href"])),S(J,{modelValue:E.drawer,"onUpdate:modelValue":m[6]||(m[6]=e=>E.drawer=e),title:c.$t("Frontend Portal Settings"),direction:u.direction,size:"520px","append-to-body":""},{footer:k(()=>[b("div",N,[V((g(),x(P,{type:"primary",size:"small",onClick:m[5]||(m[5]=e=>c.$emit("save")),disabled:u.saving||!u.hasCampaignPro},{default:k(()=>[h(y(c.$t("Save Settings")),1)],void 0,!0),_:1},8,["disabled"])),[[X,u.saving]])])]),default:k(()=>[b("div",I,[S(D,{modelValue:u.settings.frontend_portal,"onUpdate:modelValue":m[1]||(m[1]=e=>u.settings.frontend_portal=e),"true-value":"yes","false-value":"no"},{default:k(()=>[h(y(c.$t("Enable Frontend Portal")),1)],void 0,!0),_:1},8,["modelValue"]),"yes"==u.settings.frontend_portal?(g(),x(Y,{key:0,"label-position":"top",class:"fcrm_frontend_portal_form"},{default:k(()=>[S(q,{label:c.$t("Preferred Render Type")},{default:k(()=>[S(j,{modelValue:u.settings.frontend_portal_render_type,"onUpdate:modelValue":m[2]||(m[2]=e=>u.settings.frontend_portal_render_type=e)},{default:k(()=>[S(F,{label:"standalone"},{default:k(()=>[h(y(c.$t("Show in a standalone Frontend URL")),1)],void 0,!0),_:1}),S(F,{label:"shortcode"},{default:k(()=>[h(y(c.$t("Use a pre-defined page via shortcode")),1)],void 0,!0),_:1})],void 0,!0),_:1},8,["modelValue"])],void 0,!0),_:1},8,["label"]),"standalone"===u.settings.frontend_portal_render_type?(g(),x(q,{key:0,label:c.$t("URL Slug for the frontend panel (eg: fluentcrm)")},{default:k(()=>[S(H,{modelValue:u.settings.frontend_portal_slug,"onUpdate:modelValue":m[3]||(m[3]=e=>u.settings.frontend_portal_slug=e),placeholder:c.$t("fluentcrm"),type:"text"},null,8,["modelValue","placeholder"])],void 0,!0),_:1},8,["label"])):C("",!0),"standalone"===u.settings.frontend_portal_render_type?(g(),x(q,{key:1,label:c.$t("Standalone Frontend URL")},{default:k(()=>[S(G,{text:A.standaloneUrl,showViewButton:!0},null,8,["text"]),b("p",R,[b("a",{href:A.standaloneUrl,class:"el-button is-link",target:"_blank",rel:"noopener noreferrer"},y(c.$t("Open the frontend portal")),9,O)])],void 0,!0),_:1},8,["label"])):C("",!0),"shortcode"===u.settings.frontend_portal_render_type?(g(),x(q,{key:2,label:c.$t("Select Page")},{default:k(()=>[V((g(),x(K,{modelValue:u.settings.frontend_portal_page_id,"onUpdate:modelValue":m[4]||(m[4]=e=>u.settings.frontend_portal_page_id=e),filterable:"",remote:"",clearable:"","reserve-keyword":"",placeholder:c.$t("Select Page for shortcode"),"remote-method":A.searchPages},{default:k(()=>[(g(!0),v(M,null,w(E.pageOptions,e=>(g(),x(Q,{key:e.id,label:e.title,value:e.id},null,8,["label","value"]))),128))],void 0,!0),_:1},8,["modelValue","placeholder","remote-method"])),[[X,E.pageLoading]]),b("p",W,y(c.$t("Please add this shortcode to your selected page.")),1)],void 0,!0),_:1},8,["label"])):C("",!0),"shortcode"===u.settings.frontend_portal_render_type?(g(),x(q,{key:3,label:c.$t("Shortcode")},{default:k(()=>[S(G,{text:"[fluent_crm]",showViewButton:!1})],void 0,!0),_:1},8,["label"])):C("",!0)],void 0,!0),_:1})):C("",!0)])],void 0),_:1},8,["modelValue","title","direction"])])])}]]),BaseCard:D},data(){return{addOns:[],loading:!0,installing:null,experimental_features:{},saving:!1,campaignArchiveDrawer:!1,campaigns:[],campaignLoading:!1,searchQuery:"",smsModule:{enabled:"no",loading:!1},abandonedCartModule:{enabled:"no",loading:!1},aiWritingModule:{enabled:"no",loading:!1},statuses:[{key:"all",label:this.$t("All")},{key:"draft",label:this.$t("Draft")},{key:"pending",label:this.$t("Pending")},{key:"archived",label:this.$t("Archived")},{key:"incomplete",label:this.$t("Incomplete")},{key:"purged",label:this.$t("Purged")},{key:"processing",label:this.$t("Processing")},{key:"pending-scheduled",label:this.$t("Scheduled (pending)")},{key:"scheduled",label:this.$t("Scheduled")}],direction:"rtl"}},methods:{fetchPageData(){return this.loading=!0,Promise.allSettled([this.$get("docs/addons",{with:["experimental_features"]}),this.fetchSmsModule(),this.fetchAbandonedCartModule(),this.fetchAiWritingModule()]).then(([e])=>{"fulfilled"===e.status?(this.addOns=e.value.addons,this.experimental_features=e.value.experimental_features):this.$handleError(e.reason)}).finally(()=>{this.loading=!1})},fetchSmsModule(){return this.has_campaign_pro?(this.smsModule.loading=!0,this.$get("campaign-pro-settings/sms").then(e=>{var a;this.smsModule.enabled=(null==(a=e.settings)?void 0:a.enabled)||"no"}).catch(e=>{this.smsModule.enabled="no",this.$handleError(e)}).finally(()=>{this.smsModule.loading=!1})):(this.smsModule.enabled="no",Promise.resolve())},fetchAbandonedCartModule(){return this.abandonedCartModule.loading=!0,this.$get("setting/abandon-cart").then(e=>{var a;this.abandonedCartModule.enabled=(null==(a=e.settings)?void 0:a.enabled)||"no"}).catch(e=>{this.$handleError(e)}).finally(()=>{this.abandonedCartModule.loading=!1})},fetchAiWritingModule(){return this.aiWritingModule.loading=!0,this.$get("ai/settings").then(e=>{var a;this.aiWritingModule.enabled=(null==(a=e.settings)?void 0:a.is_enabled)||"no"}).catch(e=>{this.$handleError(e)}).finally(()=>{this.aiWritingModule.loading=!1})},goToSettings(e){this.$router.push(e)},installPlugin(e,a={}){this.installing=e,this.$post(a.install_route||"setting/install-"+e).then(e=>{e.is_installed?this.$notify.success(e.message):this.$notify.error(this.$t("Sorry, the selected plugins could not be installed")),this.fetchPageData()}).catch(e=>{this.handleError(e)}).finally(()=>{this.installing=null})},saveExperimentalSettings(){this.saving=!0,this.$post("setting/experiments",{...this.experimental_features}).then(e=>{this.$notify.success({title:this.$t("Great!"),message:e.message,offset:19}),setTimeout(()=>{window.location.reload()},500)}).finally(()=>{this.saving=!1})},fetchCampaign(e){this.campaignLoading=!0,this.searchQuery=e,this.$get("campaigns",{searchBy:this.searchQuery}).then(e=>{this.campaignLoading=!1,this.campaigns=e.campaigns.data}).catch(e=>{console.log(e)})}},mounted(){window.fcAdmin&&window.fcAdmin.is_rtl&&(this.direction="ltr"),this.fetchPageData(),this.fetchCampaign(""),this.changeTitle(this.$t("Addons"))}},[["render",function(l,n,E,A,P,D){const F=u,U=m,L=c,z=e,T=a,B=f,I=d,R=s,O=i,W=o,N=t,Pa=_,Da=p("frontend-portal-feature"),Fa=p("base-card"),Ua=p("Icons"),La=r;return g(),v("div",j,[b("div",q,[P.loading?(g(),x(F,{key:0,style:{width:"100%"},animated:""},{template:k(()=>[S(L,{gutter:30},{default:k(()=>[S(U,{span:24},{default:k(()=>[S(F,{style:{background:"white",padding:"15px"},rows:5})],void 0,!0),_:1}),S(U,{span:24},{default:k(()=>[S(F,{style:{background:"white",padding:"15px"},rows:5})],void 0,!0),_:1}),S(U,{span:24},{default:k(()=>[S(F,{style:{background:"white",padding:"15px"},rows:5})],void 0,!0),_:1})],void 0,!0),_:1})]),_:1})):(g(),v(M,{key:1},[S(Fa,null,{title:k(()=>[b("h4",null,y(l.$t("Advanced Features")),1)]),body:k(()=>[b("div",H,[b("div",G,[b("div",Q,[b("div",K,[h(y(l.$t("SMS"))+" ",1),b("span",{class:$("yes"===P.smsModule.enabled?"fcrm_badge fcrm_badge_success":"fcrm_badge")},y("yes"===P.smsModule.enabled?l.$t("Enabled"):l.$t("Disabled")),3)]),b("div",Y,y(l.$t("Enable SMS Module description")),1)]),b("div",J,[S(z,{size:"small",class:"small",onClick:n[0]||(n[0]=e=>D.goToSettings("/settings/sms_settings"))},{default:k(()=>[h(y(l.$t("Configure")),1)],void 0,!0),_:1})])]),b("div",X,[b("div",Z,[b("div",ee,[h(y(l.$t("Company Module"))+" ",1),b("span",{class:$("no"==P.experimental_features.company_module?"fcrm_badge":"fcrm_badge fcrm_badge_success")},y("yes"==P.experimental_features.company_module?l.$t("Enabled"):l.$t("Disabled")),3)]),b("div",ae,y(l.$t("Company_Module_Help")),1)]),b("div",te,[S(B,{placement:"top",width:"500",trigger:"click"},{reference:k(()=>[S(z,{size:"small"},{default:k(()=>[h(y(l.$t("Settings")),1)],void 0,!0),_:1})]),default:k(()=>[b("div",se,[b("div",le,[b("h3",ne,y(l.$t("Company Module Settings")),1)]),b("div",de,[S(T,{modelValue:P.experimental_features.company_module,"onUpdate:modelValue":n[1]||(n[1]=e=>P.experimental_features.company_module=e),"true-value":"yes","false-value":"no"},{default:k(()=>[h(y(l.$t("Enable Company Module for Contacts")),1)],void 0,!0),_:1},8,["modelValue"]),"yes"==P.experimental_features.company_module?(g(),v("div",oe,[S(T,{modelValue:P.experimental_features.company_auto_logo,"onUpdate:modelValue":n[2]||(n[2]=e=>P.experimental_features.company_auto_logo=e),"true-value":"yes","false-value":"no"},{default:k(()=>[h(y(l.$t("Company_Logo_Auto_Download_Help"))+" ",1),b("p",ie,y(l.$t("Company_Logo_Auto_Download_Note")),1)],void 0,!0),_:1},8,["modelValue"])])):C("",!0)]),b("div",re,[V((g(),x(z,{size:"small",onClick:n[3]||(n[3]=e=>D.saveExperimentalSettings()),disabled:P.saving},{default:k(()=>[h(y(l.$t("Save Settings")),1)],void 0,!0),_:1},8,["disabled"])),[[La,P.saving]])])])],void 0,!0),_:1})])]),b("div",_e,[b("div",ce,[b("div",me,[h(y(l.$t("Abandoned Cart Settings"))+" ",1),b("span",{class:$("yes"===P.abandonedCartModule.enabled?"fcrm_badge fcrm_badge_success":"fcrm_badge")},y("yes"===P.abandonedCartModule.enabled?l.$t("Enabled"):l.$t("Disabled")),3)]),b("div",ue,y(l.$t("Recover Abandon Carts for your eCommerce")),1)]),b("div",fe,[S(z,{size:"small",onClick:n[4]||(n[4]=e=>D.goToSettings("/settings/abandoned_cart_settings"))},{default:k(()=>[h(y(l.$t("Configure")),1)],void 0,!0),_:1})])]),b("div",pe,[b("div",ge,[b("div",ve,[h(y(l.$t("AI Configuration"))+" ",1),b("span",{class:$("yes"===P.aiWritingModule.enabled?"fcrm_badge fcrm_badge_success":"fcrm_badge")},y("yes"===P.aiWritingModule.enabled?l.$t("Enabled"):l.$t("Disabled")),3)]),b("div",be,y(l.$t("When enabled, AI features will be available across FluentCRM.")),1)]),b("div",he,[S(z,{size:"small",onClick:n[5]||(n[5]=e=>D.goToSettings("/settings/ai_settings"))},{default:k(()=>[h(y(l.$t("Configure")),1)],void 0,!0),_:1})])]),b("div",ye,[b("div",$e,[b("div",xe,[h(y(l.$t("Campaign Archives"))+" ",1),b("span",{class:$("no"==P.experimental_features.campaign_archive?"fcrm_badge":"fcrm_badge fcrm_badge_success")},y("yes"==P.experimental_features.campaign_archive?l.$t("Enabled"):l.$t("Disabled")),3)]),b("div",ke,y(l.$t("Email_Camp_Enable_Help")),1)]),b("div",Se,[S(z,{size:"small",onClick:n[6]||(n[6]=e=>P.campaignArchiveDrawer=!0)},{default:k(()=>[h(y(l.$t("Settings")),1)],void 0,!0),_:1}),S(Pa,{modelValue:P.campaignArchiveDrawer,"onUpdate:modelValue":n[13]||(n[13]=e=>P.campaignArchiveDrawer=e),title:l.$t("Campaign Archive Settings"),direction:P.direction,size:"520px","append-to-body":""},{footer:k(()=>[b("div",De,[V((g(),x(z,{size:"small",onClick:n[12]||(n[12]=e=>D.saveExperimentalSettings()),disabled:P.saving||!l.has_campaign_pro},{default:k(()=>[h(y(l.$t("Save Settings")),1)],void 0,!0),_:1},8,["disabled"])),[[La,P.saving]])])]),default:k(()=>[b("div",Ce,[S(T,{modelValue:P.experimental_features.campaign_archive,"onUpdate:modelValue":n[7]||(n[7]=e=>P.experimental_features.campaign_archive=e),"true-value":"yes","false-value":"no"},{default:k(()=>[h(y(l.$t("Enable Campaign Archive Frontend Feature")),1)],void 0,!0),_:1},8,["modelValue"]),"yes"==P.experimental_features.campaign_archive?(g(),x(N,{key:0,"label-position":"top",model:P.experimental_features},{default:k(()=>[S(R,{label:l.$t("List_Campaigns_Label")},{default:k(()=>[S(I,{modelValue:P.experimental_features.campaign_search,"onUpdate:modelValue":n[8]||(n[8]=e=>P.experimental_features.campaign_search=e),placeholder:l.$t("Campaign Search Keyword"),type:"text"},null,8,["modelValue","placeholder"]),b("p",Ve,y(l.$t("List_Campaigns_Help")),1)],void 0,!0),_:1},8,["label"]),S(R,{label:l.$t("Select Campaigns")},{default:k(()=>[V((g(),x(W,{modelValue:P.experimental_features.campaign_ids,"onUpdate:modelValue":n[9]||(n[9]=e=>P.experimental_features.campaign_ids=e),multiple:"",placeholder:l.$t("Select Campaigns"),filterable:"","popper-class":"fc_select_campaigns_popover",remote:!0,clearable:!0,"remote-method":D.fetchCampaign},{default:k(()=>[(g(!0),v(M,null,w(P.campaigns,e=>(g(),x(O,{key:e.id,label:e.title,value:e.id},{default:k(()=>[h(y(e.title)+" ",1),b("span",{class:$(["fcrm_badge","fcrm_badge_"+e.status])},y(e.status),3)],void 0,!0),_:2},1032,["label","value"]))),128))],void 0,!0),_:1},8,["modelValue","placeholder","remote-method"])),[[La,P.campaignLoading]]),b("p",Me,y(l.$t("Leave it blank to display all campaigns")),1)],void 0,!0),_:1},8,["label"]),S(R,{label:l.$t("Filter by status")},{default:k(()=>[S(W,{modelValue:P.experimental_features.campaign_status,"onUpdate:modelValue":n[10]||(n[10]=e=>P.experimental_features.campaign_status=e),placeholder:l.$t("Filter by status")},{default:k(()=>[(g(!0),v(M,null,w(P.statuses,e=>(g(),x(O,{key:e.key,value:e.key,label:e.label},null,8,["value","label"]))),128))],void 0,!0),_:1},8,["modelValue","placeholder"])],void 0,!0),_:1},8,["label"]),S(R,{label:l.$t("Max Campaigns to list (max 50)")},{default:k(()=>[S(I,{modelValue:P.experimental_features.campaign_max_number,"onUpdate:modelValue":n[11]||(n[11]=e=>P.experimental_features.campaign_max_number=e),placeholder:l.$t("Campaign Search Keyword"),min:1,max:50,type:"number"},null,8,["modelValue","placeholder"])],void 0,!0),_:1},8,["label"]),S(R,null,{default:k(()=>[b("p",we,y(l.$t("AdvFeat_Shortcode_Past_Campaigns")),1),l.has_campaign_pro?(g(),x(I,{key:0,readonly:!0,value:"[fluent_crm_campaign_archives]"})):(g(),v("div",Ee,[b("h2",Ae,y(l.$t("Campaign_Feature_Note")),1)])),b("div",Pe,[b("p",null,y(l.$t("AdvFeat_Multi_Archive_Shortcode_Help")),1)])],void 0,!0),_:1})],void 0,!0),_:1},8,["model"])):C("",!0)])],void 0,!0),_:1},8,["modelValue","title","direction"])])]),S(Da,{settings:P.experimental_features,direction:P.direction,saving:P.saving,"has-campaign-pro":l.has_campaign_pro,onSave:n[14]||(n[14]=e=>D.saveExperimentalSettings())},null,8,["settings","direction","saving","has-campaign-pro"]),b("div",Fe,[b("div",Ue,[b("div",Le,[h(y(l.$t("Event Tracking Module"))+" ",1),b("span",{class:$("no"==P.experimental_features.event_tracking?"fcrm_badge":"fcrm_badge fcrm_badge_success")},y("yes"==P.experimental_features.event_tracking?l.$t("Enabled"):l.$t("Disabled")),3)]),b("div",ze,[h(y(l.$t("Event_Tracking_Module_Help"))+" ",1),b("a",Te,y(l.$t("Learn More")),1)])]),b("div",Be,[S(B,{placement:"top",width:"500",trigger:"click"},{reference:k(()=>[S(z,{size:"small"},{default:k(()=>[h(y(l.$t("Settings")),1)],void 0,!0),_:1})]),default:k(()=>[b("div",Ie,[b("div",Re,[S(T,{modelValue:P.experimental_features.event_tracking,"onUpdate:modelValue":n[15]||(n[15]=e=>P.experimental_features.event_tracking=e),"true-value":"yes","false-value":"no",label:l.$t("Enable Event Tracking Module")},null,8,["modelValue","label"])]),b("div",Oe,[V((g(),x(z,{size:"small",onClick:n[16]||(n[16]=e=>D.saveExperimentalSettings()),disabled:P.saving},{default:k(()=>[h(y(l.$t("Save Settings")),1)],void 0,!0),_:1},8,["disabled"])),[[La,P.saving]])])])],void 0,!0),_:1})])]),b("div",We,[b("div",Ne,[b("div",je,[h(y(l.$t("Disable AI in Visual Builder"))+" ",1),b("span",{class:$("no"==P.experimental_features.disable_visual_ai?"fcrm_badge":"fcrm_badge fcrm_badge_success")},y("yes"==P.experimental_features.disable_visual_ai?l.$t("Enabled"):l.$t("Disabled")),3)]),b("div",qe,y(l.$t("AdvFeat_AI_Visual_Builder")),1)]),b("div",He,[S(B,{placement:"top",width:"300",trigger:"click"},{reference:k(()=>[S(z,{size:"small"},{default:k(()=>[h(y(l.$t("Settings")),1)],void 0,!0),_:1})]),default:k(()=>[b("div",Ge,[b("div",Qe,[S(T,{modelValue:P.experimental_features.disable_visual_ai,"onUpdate:modelValue":n[17]||(n[17]=e=>P.experimental_features.disable_visual_ai=e),"true-value":"yes","false-value":"no",label:l.$t("Disable AI")},null,8,["modelValue","label"])]),b("div",Ke,[V((g(),x(z,{size:"small",onClick:n[18]||(n[18]=e=>D.saveExperimentalSettings()),disabled:P.saving},{default:k(()=>[h(y(l.$t("Save Settings")),1)],void 0,!0),_:1},8,["disabled"])),[[La,P.saving]])])])],void 0,!0),_:1})])]),b("div",Ye,[b("div",Je,[b("div",Xe,[h(y(l.$t("Enable Multi-Threading Email Sending"))+" ",1),b("span",{class:$("no"==P.experimental_features.multi_threading_emails?"fcrm_badge":"fcrm_badge fcrm_badge_success")},y("yes"==P.experimental_features.multi_threading_emails?l.$t("Enabled"):l.$t("Disabled")),3)]),b("div",Ze,y(l.$t("AdvFeat_Multi_Thread_Email_Desc")),1),"yes"==P.experimental_features.multi_threading_emails?(g(),v("div",ea,[b("p",null,[b("b",null,y(l.$t("AdvFeat_Server_Reqs_Optimal")),1)]),b("ul",aa,[b("li",null,y(l.$t("Multiple CPU on the server.")),1),b("li",null,y(l.$t("At least 4GB Server Memory (RAM).")),1),b("li",null,y(l.$t("AdvFeat_SMTP_Rate_In_Email")),1),b("li",null,y(l.$t("AdvFeat_PHP_Max_Exec_50_60")),1),b("li",null,y(l.$t("AdvFeat_Good_Speed_Do_Not_Enable")),1)])])):C("",!0)]),b("div",ta,[S(B,{placement:"top",width:"400",trigger:"click"},{reference:k(()=>[S(z,{size:"small"},{default:k(()=>[h(y(l.$t("Settings")),1)],void 0,!0),_:1})]),default:k(()=>[b("div",sa,[b("div",la,[S(T,{modelValue:P.experimental_features.multi_threading_emails,"onUpdate:modelValue":n[19]||(n[19]=e=>P.experimental_features.multi_threading_emails=e),"true-value":"yes","false-value":"no",label:l.$t("Enable Multi-Threading Email Sending")},null,8,["modelValue","label"])]),b("div",na,[V((g(),x(z,{size:"small",onClick:n[20]||(n[20]=e=>D.saveExperimentalSettings()),disabled:P.saving},{default:k(()=>[h(y(l.$t("Save Settings")),1)],void 0,!0),_:1},8,["disabled"])),[[La,P.saving]])])])],void 0,!0),_:1})])]),b("div",da,[b("div",oa,[b("div",ia,[h(y(l.$t("__ENABLE_SYSTEM_LOG"))+" ",1),b("span",{class:$("no"==P.experimental_features.system_logs?"fcrm_badge":"fcrm_badge fcrm_badge_success")},y("yes"==P.experimental_features.system_logs?l.$t("Enabled"):l.$t("Disabled")),3)]),b("div",ra,y(l.$t("AdvFeat_System_Log_Debug_Desc")),1)]),b("div",_a,[S(B,{placement:"top",width:"500",trigger:"click"},{reference:k(()=>[S(z,{size:"small"},{default:k(()=>[h(y(l.$t("Settings")),1)],void 0,!0),_:1})]),default:k(()=>[b("div",ca,[b("div",ma,[S(T,{modelValue:P.experimental_features.system_logs,"onUpdate:modelValue":n[21]||(n[21]=e=>P.experimental_features.system_logs=e),"true-value":"yes","false-value":"no",label:l.$t("__ENABLE_SYSTEM_LOG")},null,8,["modelValue","label"])]),b("div",ua,[V((g(),x(z,{size:"small",onClick:n[22]||(n[22]=e=>D.saveExperimentalSettings()),disabled:P.saving},{default:k(()=>[h(y(l.$t("Save Settings")),1)],void 0,!0),_:1},8,["disabled"])),[[La,P.saving]])])])],void 0,!0),_:1})])])])]),_:1}),S(Fa,{class:"fcrm_recommended_plugins_wrapper"},{title:k(()=>[b("h4",null,y(l.$t("Recommended Plugins")),1)]),body:k(()=>[b("div",fa,[l.has_campaign_pro?C("",!0):(g(),v("div",pa,[b("div",ga,[b("div",va,[b("div",ba,y(l.$t("Addons.fluentcrm_pro.title")),1),b("div",ha,y(l.$t("with_fluentcrm_pro_integrate_with_other_plugins")),1)])]),b("div",ya,[S(z,{type:"primary",tag:"a",target:"_blank",rel:"noopener noreferrer",href:"https://fluentcrm.com?utm_source=dashboard&utm_medium=plugin&utm_campaign=pro&utm_id=wp"},{default:k(()=>[h(y(l.$t("Get FluentCRM Pro Now")),1)],void 0,!0),_:1})])])),(g(!0),v(M,null,w(P.addOns,(e,a)=>(g(),v("div",{key:a,class:"fcrm_recommended_plugins_box"},[b("div",$a,[b("div",xa,[b("img",{src:e.logo,alt:e.title},null,8,ka)]),b("div",Sa,[b("div",Ca,[b("a",{href:e.learn_more_url,target:"_blank",rel:"noopener noreferrer"},y(e.title),9,Va),e.is_installed?(g(),v("span",Ma,y(l.$t("Installed")),1)):C("",!0)]),b("div",wa,y(e.description),1)])]),b("div",Ea,[!e.is_installed&&e.install_url?(g(),x(z,{key:0,tag:"a",href:e.install_url,target:"_blank",rel:"noopener noreferrer"},{default:k(()=>[h(y(e.action_text),1)],void 0,!0),_:2},1032,["href"])):e.is_installed?(g(),x(z,{key:2,tag:"a",href:e.settings_url,target:"_blank"},{default:k(()=>[b("span",Aa,[S(Ua,{"icon-name":"settings"})]),h(" "+y(l.$t("View Settings")),1)],void 0,!0),_:1},8,["href"])):V((g(),x(z,{key:1,disabled:P.installing,onClick:t=>D.installPlugin(a,e)},{default:k(()=>[h(y(e.action_text),1)],void 0,!0),_:2},1032,["disabled","onClick"])),[[La,P.installing===a]])])]))),128))])]),_:1})],64))])])}]]);export{Pa as default}; diff --git a/wp-content/plugins/fluent-crm/assets/admin/adminbar-search.js b/wp-content/plugins/fluent-crm/assets/admin/adminbar-search.js new file mode 100644 index 0000000..553d8f8 --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/admin/adminbar-search.js @@ -0,0 +1 @@ +if(document.head){const r=document.createElement("style");r.textContent=':root{--fc-primary-bg: #FFFFFF;--fc-secondary-bg: #F5F7FA;--fc-light-bg: #E1E4EA;--fc-deep-bg: #222530;--fc-weak-bg-25: #F9FAFB;--fc-ai-background: #efebff;--fc-ai-color: #8762F0;--fc-primary-text: #0E121B;--fc-secondary-text: #525866;--fc-text-muted: #99A0AE;--fc-text-inverse: #FFFFFF;--fc-primary-border: #E1E4EA;--fc-secondary-border: #CACFD8;--fc-primary-button: #222530;--fc-text-link: #335CFF;--fc-success: #1FC16B;--fc-success-bg: #E0FAEC;--fc-error: #FB3748;--fc-error-bg: #FFEBEC;--fc-warning: #F6B51E;--fc-warning-bg: #FFFAEB;--fc-text-link-bg: #EEF2FF;--fc-badge-unsubscribed-text: #222530;--fc-badge-unsubscribed-bg: #F2F5F8;--fc-badge-subscribed-text: #0B4627;--fc-badge-subscribed-bg: #E0FAEC;--fc-badge-pending-text: #624C18;--fc-badge-pending-bg: #FFFAEB;--fc-badge-transactional-text: #351A75;--fc-badge-transactional-bg: #EFEBFF;--fc-badge-bounced-text: #122368;--fc-badge-bounced-bg: #EBF1FF;--fc-badge-complained-text: #71330A;--fc-badge-complained-bg: #FFF3EB;--fc-badge-spammed-text: #681219;--fc-badge-spammed-bg: #FFEBEC;--el-color-primary: var(--fc-deep-bg);--el-color-primary-light-3: var(--fc-secondary-text);--el-color-primary-light-5: var(--fc-text-muted);--el-color-primary-light-7: var(--fc-secondary-border);--el-color-primary-light-8: var(--fc-primary-border);--el-color-primary-light-9: var(--fc-secondary-bg);--el-color-primary-dark-2: var(--fc-primary-text);--el-color-success: var(--fc-success);--el-color-warning: var(--fc-warning);--el-color-danger: var(--fc-error);--el-color-error: var(--fc-error);--el-color-info: var(--fc-primary-text);--el-text-color-primary: var(--fc-primary-text);--el-text-color-regular: var(--fc-secondary-text);--el-text-color-secondary: var(--fc-text-muted);--el-text-color-placeholder: var(--fc-text-muted);--el-text-color-disabled: var(--fc-secondary-border);--el-border-color: var(--fc-primary-border);--el-border-color-light: var(--fc-primary-border);--el-border-color-lighter: var(--fc-secondary-border);--el-border-color-dark: var(--fc-secondary-border);--el-fill-color-light: var(--fc-secondary-bg);--el-fill-color-lighter: var(--fc-secondary-bg);--el-bg-color: var(--fc-primary-bg);--el-bg-color-page: var(--fc-secondary-bg);--el-button-text-color: var(--fc-text-inverse);--el-fill-color-blank: var(--fc-primary-bg);--el-bg-color-overlay: var(--fc-primary-bg);--el-color-info-light-9: var(--fc-badge-unsubscribed-bg);--fcrm-border-radius-8: 8px;--el-border-radius-base: var(--fcrm-border-radius-8);--wp-editor-canvas-background: var(--fc-primary-bg)}@keyframes fcrm_spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}li#wp-admin-bar-fcrm_adminbar_search *{box-sizing:border-box}li#wp-admin-bar-fcrm_adminbar_search .fcrm_search_container{display:none;clear:both;background:var(--fc-primary-bg);position:absolute;top:100%;right:0;width:min(500px,100vw - 16px);max-width:calc(100vw - 16px);z-index:1000;overflow:hidden;border-radius:var(--fcrm-border-radius-8);box-shadow:inset 0 -1px 1px -.5px var(--fc-primary-border),0 0 0 1px var(--fc-primary-border),0 48px 48px -24px #3333330a,0 24px 24px -12px #3333330a,0 12px 12px -6px #3333330a,0 6px 6px -3px #3333330a,0 3px 3px -1.5px #33333305,0 1px 1px .5px #3333330a}li#wp-admin-bar-fcrm_adminbar_search .fcrm_search_container.fcrm_show{display:block}li#wp-admin-bar-fcrm_adminbar_search .fcrm_search_container .fcrm_search_header{display:flex;align-items:center;gap:8px;border-bottom:1px solid var(--fc-primary-border);padding:8px 20px}li#wp-admin-bar-fcrm_adminbar_search .fcrm_search_container .fcrm_search_header .fcrm_search_input_wrapper{position:relative;width:100%;display:flex;align-items:center;gap:8px}li#wp-admin-bar-fcrm_adminbar_search .fcrm_search_container #fcrm_search_input{width:100%;display:block;border:none;background:none;color:var(--fc-primary-text);height:20px}li#wp-admin-bar-fcrm_adminbar_search .fcrm_search_container #fcrm_search_input::placeholder{color:var(--fc-text-muted)}li#wp-admin-bar-fcrm_adminbar_search .fcrm_search_container .fcrm_search_icon{color:var(--fc-text-muted)}li#wp-admin-bar-fcrm_adminbar_search .fcrm_search_container .fcrm_search_close{border:none;height:32px;width:32px;border-radius:var(--fcrm-border-radius-8);display:flex;flex:none;align-items:center;justify-content:center;cursor:pointer;background:none;color:var(--fc-secondary-text)}li#wp-admin-bar-fcrm_adminbar_search .fcrm_search_container .fcrm_search_close:hover{background:var(--fc-secondary-bg)}li#wp-admin-bar-fcrm_adminbar_search .fcrm_search_container .fcrm_search_results{padding:16px 8px 0;max-height:320px;min-height:320px;overflow-y:auto;position:relative}li#wp-admin-bar-fcrm_adminbar_search .fcrm_search_container .fcrm_search_results .fcrm_results_label{margin:0 0 6px;padding:0 12px;color:var(--fc-text-muted);font-weight:500;font-size:12px;line-height:16px}li#wp-admin-bar-fcrm_adminbar_search .fcrm_search_container .fcrm_search_results #fcrm_search_result_wrapper .fcrm_no_result{padding:12px 20px;color:var(--fc-text-muted, #9CA3AF);font-size:14px;margin:0}li#wp-admin-bar-fcrm_adminbar_search .fcrm_search_container .fcrm_search_results #fcrm_search_result_wrapper.fcrm_has_results .fcrm_result_lists{display:flex;flex-direction:column}li#wp-admin-bar-fcrm_adminbar_search .fcrm_search_container .fcrm_search_results #fcrm_search_result_wrapper.fcrm_has_results .fcrm_result_lists .fcrm_contact_item{display:flex;align-items:center;gap:10px;padding:6px 12px;text-decoration:none;transition:background .15s;height:auto;border-radius:var(--fcrm-border-radius-8)}li#wp-admin-bar-fcrm_adminbar_search .fcrm_search_container .fcrm_search_results #fcrm_search_result_wrapper.fcrm_has_results .fcrm_result_lists .fcrm_contact_item:focus,li#wp-admin-bar-fcrm_adminbar_search .fcrm_search_container .fcrm_search_results #fcrm_search_result_wrapper.fcrm_has_results .fcrm_result_lists .fcrm_contact_item:hover{background:var(--fc-secondary-bg)}li#wp-admin-bar-fcrm_adminbar_search .fcrm_search_container .fcrm_search_results #fcrm_search_result_wrapper.fcrm_has_results .fcrm_result_lists .fcrm_contact_item:focus .fcrm_contact_arrow,li#wp-admin-bar-fcrm_adminbar_search .fcrm_search_container .fcrm_search_results #fcrm_search_result_wrapper.fcrm_has_results .fcrm_result_lists .fcrm_contact_item:hover .fcrm_contact_arrow{opacity:1}li#wp-admin-bar-fcrm_adminbar_search .fcrm_search_container .fcrm_search_results #fcrm_search_result_wrapper.fcrm_has_results .fcrm_result_lists .fcrm_contact_item .fcrm_contact_avatar{width:32px;height:32px;border-radius:50%;object-fit:cover;flex-shrink:0;border:2px solid var(--fc-text-inverse)}li#wp-admin-bar-fcrm_adminbar_search .fcrm_search_container .fcrm_search_results #fcrm_search_result_wrapper.fcrm_has_results .fcrm_result_lists .fcrm_contact_item .fcrm_contact_info{flex:1;display:flex;align-items:center;gap:14px;min-width:0}li#wp-admin-bar-fcrm_adminbar_search .fcrm_search_container .fcrm_search_results #fcrm_search_result_wrapper.fcrm_has_results .fcrm_result_lists .fcrm_contact_item .fcrm_contact_info .fcrm_contact_name{color:var(--fc-primary-text);display:block;font-weight:500;font-size:14px;line-height:20px;margin:0;position:relative;white-space:nowrap}li#wp-admin-bar-fcrm_adminbar_search .fcrm_search_container .fcrm_search_results #fcrm_search_result_wrapper.fcrm_has_results .fcrm_result_lists .fcrm_contact_item .fcrm_contact_info .fcrm_contact_name:before{content:"";position:absolute;right:-9px;top:50%;transform:translateY(-50%);width:3px;height:3px;background:var(--fc-secondary-border);border-radius:50%}li#wp-admin-bar-fcrm_adminbar_search .fcrm_search_container .fcrm_search_results #fcrm_search_result_wrapper.fcrm_has_results .fcrm_result_lists .fcrm_contact_item .fcrm_contact_info .fcrm_contact_email{color:var(--fc-text-muted);display:block;font-weight:400;font-size:12px;line-height:16px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;margin:0}li#wp-admin-bar-fcrm_adminbar_search .fcrm_search_container .fcrm_search_results #fcrm_search_result_wrapper.fcrm_has_results .fcrm_result_lists .fcrm_contact_item .fcrm_contact_arrow{color:var(--fc-secondary-text);width:20px;height:20px;border-radius:6px;display:flex;align-items:center;justify-content:center;background:var(--fc-primary-bg);flex:none;opacity:0;transition:opacity .15s;flex-shrink:0}li#wp-admin-bar-fcrm_adminbar_search .fcrm_search_container .fcrm_search_results #fcrm_search_result_wrapper.fcrm_loading .fcrm_result_lists{opacity:0;text-align:center;border:3px solid var(--fc-secondary-bg, #F5F7FA);border-top:3px solid var(--fc-text-link, #335CFF);border-radius:50%;width:30px;height:30px;margin:12px auto;-webkit-animation:fcrm_spin 1s linear infinite;animation:fcrm_spin 1s linear infinite}li#wp-admin-bar-fcrm_adminbar_search .fcrm_search_container .fcrm_quick_links_wrapper{background:var(--fc-weak-bg-25);padding:16px 20px}li#wp-admin-bar-fcrm_adminbar_search .fcrm_search_container .fcrm_quick_links_wrapper h4{padding:0;margin:0 0 12px;color:var(--fc-text-muted);font-weight:500;font-size:12px;line-height:16px}li#wp-admin-bar-fcrm_adminbar_search .fcrm_search_container .fcrm_quick_links_wrapper .fcrm_quick_links{display:flex;flex-wrap:wrap;align-items:flex-start;row-gap:10px;column-gap:24px}li#wp-admin-bar-fcrm_adminbar_search .fcrm_search_container .fcrm_quick_links_wrapper .fcrm_quick_links .fcrm_quick_link{display:flex;align-items:center;gap:4px;margin:0;padding:0;color:var(--fc-primary-text);font-weight:500;font-size:12px;line-height:16px;height:auto}li#wp-admin-bar-fcrm_adminbar_search .fcrm_search_container .fcrm_quick_links_wrapper .fcrm_quick_links .fcrm_quick_link span{display:block;margin:0;padding:0;color:var(--fc-primary-text);font-weight:500;font-size:12px;line-height:16px}li#wp-admin-bar-fcrm_adminbar_search .fcrm_search_container .fcrm_quick_links_wrapper .fcrm_quick_links .fcrm_quick_link svg{display:block;width:16px;height:16px}li#wp-admin-bar-fcrm_adminbar_search .fcrm_search_container .fcrm_quick_links_wrapper .fcrm_quick_links .fcrm_quick_link:hover{text-decoration:underline}li#wp-admin-bar-fcrm_adminbar_search .fcrm_search_container .fcrm_load_more{text-align:center;display:none;padding:8px 20px 12px;margin-top:8px;position:sticky;bottom:0;background:var(--fc-primary-bg)}li#wp-admin-bar-fcrm_adminbar_search .fcrm_search_container .fcrm_load_more.fcrm_has_more{display:flex;align-items:center;justify-content:center}li#wp-admin-bar-fcrm_adminbar_search .fcrm_search_container .fcrm_load_more button#fcrm_load_more_result{background:var(--fc-secondary-bg);display:flex;align-items:center;gap:8px;border-radius:var(--fcrm-border-radius-8);padding:4px 12px;color:var(--fc-secondary-text);font-weight:500;font-size:14px;line-height:20px;border:none;box-shadow:none;cursor:pointer;min-height:32px}li#wp-admin-bar-fcrm_adminbar_search .fcrm_search_container .fcrm_load_more button#fcrm_load_more_result:hover{background:var(--fc-secondary-bg);color:var(--fc-primary-text)}html[dir=rtl] li#wp-admin-bar-fcrm_adminbar_search .fcrm_search_container.fcrm_show{right:auto;left:0}',document.head.appendChild(r)}const r=function(e,a,c,t=null){let n;if("string"==typeof e?n=["svg","g","path","circle","rect","line","polyline","polygon","ellipse"].includes(e.toLowerCase())?document.createElementNS("http://www.w3.org/2000/svg",e):document.createElement(e):e instanceof Element&&(n=e),a)for(let r in a)n.setAttribute(r,a[r]);return(c||0==c)&&r.append(n,c,t),n};function e(r){var e,a;return(null==(a=null==(e=window.fcrm_adminbar_search_vars)?void 0:e.trans)?void 0:a[r])||r}function a(r,e="#"){if(!r)return e;try{const e=new URL(r,window.location.origin);if(["http:","https:"].includes(e.protocol))return e.toString()}catch(a){}return e}r.append=function(e,a,c){e instanceof HTMLTextAreaElement||e instanceof HTMLInputElement?a instanceof Text||"string"==typeof a||"number"==typeof a?e.value=a:a instanceof Array?a.forEach(function(a){r.append(e,a)}):"function"==typeof a&&r.append(e,a()):a instanceof Element||a instanceof Text?e.appendChild(a):"string"==typeof a||"number"==typeof a?c?e.innerHTML+=a:e.appendChild(document.createTextNode(a)):a instanceof Array?a.forEach(function(a){r.append(e,a)}):"function"==typeof a&&r.append(e,a())};({init(){window.fcrm_adminbar_search_vars&&window.jQuery&&("loading"!==document.readyState?(this.initButton(),window.fcrm_adminbar_search_vars.edit_user_vars&&window.fcrm_adminbar_search_vars.edit_user_vars.crm_profile_url&&this.maybeUserProfile(window.fcrm_adminbar_search_vars.edit_user_vars)):document.addEventListener("DOMContentLoaded",()=>this.initButton(),{once:!0}))},current_page:1,searchDebounceMs:1e3,minAutoSearchLength:3,searchDebounceTimer:null,activeSearchToken:0,pendingSearchRequests:0,invalidateSearchResponses(){this.activeSearchToken++},setLoadingState(r){r?this.pendingSearchRequests++:this.pendingSearchRequests=Math.max(0,this.pendingSearchRequests-1);const e=this.pendingSearchRequests>0,a=jQuery("#fcrm_search_result_wrapper");e?a.attr("aria-busy","true"):a.removeAttr("aria-busy"),a.toggleClass("fcrm_loading",e)},clearSearchDebounce(){this.searchDebounceTimer&&(clearTimeout(this.searchDebounceTimer),this.searchDebounceTimer=null)},queueSearch(r){this.clearSearchDebounce(),r&&(this.searchDebounceTimer=setTimeout(()=>{jQuery("#fcrm_search_input").attr("data-searched")!==r&&(this.current_page=1,this.performSearch(r))},this.searchDebounceMs))},renderNoResult(a=e("Type and press enter")){const c=document.getElementById("fcrm_search_result_wrapper");c&&(c.innerHTML="",c.appendChild(r("p",{class:"fcrm_no_result"},a)))},resetSearchState({focusInput:r=!1,preserveInput:a=!1}={}){this.clearSearchDebounce(),this.invalidateSearchResponses();const c=jQuery("#fcrm_search_input");a||c.val(""),c.attr("data-searched",""),this.current_page=1,this.renderNoResult(e("Type and press enter")),jQuery("#fcrm_search_result_wrapper").removeClass("fcrm_has_results").removeClass("fcrm_loading"),jQuery(".fcrm_load_more").removeClass("fcrm_has_more"),r&&c.trigger("focus")},initButton(){const r=this,e=document.getElementById("wp-admin-bar-fcrm_adminbar_search");if(!e)return;if(e.querySelector(".fcrm_search_container"))return;const a=jQuery("#wp-admin-bar-fcrm_adminbar_search"),c=this.getSearchDom();e.append(c),a.on("mouseenter",function(){const r=a.find(".fcrm_search_container");r.addClass("fcrm_show"),r.hasClass("fcrm_show")&&r.find("input").focus()}).on("mouseleave",function(){a.find(".fcrm_search_container").removeClass("fcrm_show")}).on("focusin",function(){a.find(".fcrm_search_container").addClass("fcrm_show")}).on("focusout",function(e){setTimeout(()=>{const e=r.pendingSearchRequests>0,c=a.is(":hover"),t=a.find(":focus").length>0;e||c||t||a.find(".fcrm_search_container").removeClass("fcrm_show")},0)}),jQuery(".fcrm_search_close").on("click",function(e){e.preventDefault(),e.stopPropagation(),r.resetSearchState(),this.blur(),a.find(".fcrm_search_container").removeClass("fcrm_show")}),jQuery("#fcrm_search_input").on("keydown",function(e){if("Escape"===e.key)return e.preventDefault(),r.resetSearchState(),void a.find(".fcrm_search_container").removeClass("fcrm_show");if(r.current_page=1,"Enter"!==e.key)return;r.clearSearchDebounce();const c=jQuery.trim(jQuery(this).val());jQuery("#fcrm_search_input").attr("data-searched")!==c&&(e.preventDefault(),r.current_page=1,r.performSearch(c))}).on("input",function(){const e=jQuery.trim(jQuery(this).val());e?e.length{r.performSearch(jQuery("#fcrm_search_input").val())},1e3)})},getSearchDom(){return r("div",{class:"fcrm_search_container"},[r("div",{class:"fcrm_search_header"},[r("div",{class:"fcrm_search_input_wrapper"},[r("svg",{class:"fcrm_search_icon",viewBox:"0 0 20 20",width:"20",height:"20",fill:"none"},[r("path",{d:"M9.25 2.5C12.976 2.5 16 5.524 16 9.25C16 12.976 12.976 16 9.25 16C5.524 16 2.5 12.976 2.5 9.25C2.5 5.524 5.524 2.5 9.25 2.5ZM9.25 14.5C12.1502 14.5 14.5 12.1502 14.5 9.25C14.5 6.349 12.1502 4 9.25 4C6.349 4 4 6.349 4 9.25C4 12.1502 6.349 14.5 9.25 14.5ZM15.6137 14.5532L17.7355 16.6742L16.6742 17.7355L14.5532 15.6137L15.6137 14.5532V14.5532Z",fill:"currentColor"})]),r("input",{type:"text",placeholder:e("Search in CRM"),autocomplete:"off",id:"fcrm_search_input",autocorrect:"off",autocapitalize:"none",spellcheck:"false"})]),r("button",{type:"button",class:"fcrm_search_close","aria-label":e("Close")},[r("svg",{viewBox:"0 0 20 20",width:"20",height:"20",fill:"none"},[r("path",{d:"M9.99956 8.93906L13.7121 5.22656L14.7726 6.28706L11.0601 9.99956L14.7726 13.7121L13.7121 14.7726L9.99956 11.0601L6.28706 14.7726L5.22656 13.7121L8.93906 9.99956L5.22656 6.28706L6.28706 5.22656L9.99956 8.93906Z",fill:"#525866"})])])]),r("div",{class:"fcrm_search_results"},[r("div",{class:"fcrm_results_label"},e("Best Matches")),r("div",{id:"fcrm_search_result_wrapper"},e("Type to search contacts")),r("div",{class:"fcrm_load_more"},[r("button",{id:"fcrm_load_more_result"},e("Load More"))])]),this.getQuickLinks()])},getQuickLinks(){const c=[];return jQuery.each(window.fcrm_adminbar_search_vars.links,(e,t)=>{const n={href:a(t.url),class:"fcrm_quick_link"};t.is_external&&(n.target="_blank",n.rel="noopener noreferrer"),c.push(r("a",n,[r("span",{},t.title),r("svg",{viewBox:"0 0 24 24",width:"16",height:"16"},[r("path",{d:"M19 19H5V5h7V3H5c-1.11 0-2 .9-2 2v14c0 1.1.89 2 2 2h14c1.1 0 2-.9 2-2v-7h-2v7zM14 3v2h3.59l-9.83 9.83 1.41 1.41L19 6.41V10h2V3h-7z",fill:"currentColor"})])]))}),r("div",{class:"fcrm_quick_links_wrapper"},[r("h4",{},e("Quick links")),r("div",{class:"fcrm_quick_links"},c)])},performSearch(r){if(!r)return"";const e=++this.activeSearchToken;this.setLoadingState(!0);const a=10*(Math.max(1,parseInt(this.current_page,10)||1)-1);this.$get("subscribers/search-contacts",{search:r,offset:a,limit:11}).then(a=>{if(e!==this.activeSearchToken)return;const c=Object.values(a.contacts||{}),t=c.length>10;this.pushSearchResult(c.slice(0,10),t),jQuery("#fcrm_search_input").attr("data-searched",r)}).catch(r=>{}).finally(()=>{this.setLoadingState(!1)})},pushSearchResult(c,t=!1){const n=jQuery("#fcrm_search_result_wrapper");if(!c.length)return jQuery(".fcrm_load_more").removeClass("fcrm_has_more"),this.renderNoResult(e("Sorry no contact found")),void n.removeClass("fcrm_has_results");const i=[];jQuery.each(c,(e,c)=>{const t=""!==jQuery.trim(c.full_name||""),n=a(window.fcrm_adminbar_search_vars.subscriber_base+c.id+"?t="+encodeURIComponent(c.hash||"")),s=[];t&&s.push(r("span",{class:"fcrm_contact_name"},c.full_name)),s.push(r("span",{class:"fcrm_contact_email"},c.email)),i.push(r("a",{href:n,class:"fcrm_contact_item"},[r("img",{src:c.photo||"",alt:c.full_name,class:"fcrm_contact_avatar",width:"40",height:"40"}),r("div",{class:"fcrm_contact_info"},s),r("svg",{class:"fcrm_contact_arrow",viewBox:"0 0 24 24",width:"20",height:"20"},[r("path",{d:"M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z",fill:"currentColor"})])]))});const s=r("div",{class:"fcrm_result_lists"},i);n.html(s).addClass("fcrm_has_results"),t?jQuery(".fcrm_load_more").addClass("fcrm_has_more"):jQuery(".fcrm_load_more").removeClass("fcrm_has_more")},$get(r,e={}){const a=`${window.fcrm_adminbar_search_vars.rest.url}/${r}`;return new Promise((r,c)=>{window.jQuery.ajax({url:a,type:"GET",data:e,beforeSend:function(r){r.setRequestHeader("X-WP-Nonce",window.fcrm_adminbar_search_vars.rest.nonce)}}).then(e=>r(e)).catch(r=>c(r.responseJSON))})},maybeUserProfile(r){const e=a(r.crm_profile_url);"#"!==e&&window.jQuery("",{style:"background: #7757e6;color: white;border-color: #7757e6;",class:"page-title-action",href:e,text:"View CRM Profile"}).insertBefore("#profile-page > .wp-header-end")}}).init(); diff --git a/wp-content/plugins/fluent-crm/assets/admin/app.js b/wp-content/plugins/fluent-crm/assets/admin/app.js new file mode 100644 index 0000000..b9d2539 --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/admin/app.js @@ -0,0 +1 @@ +import{_ as e,C as t}from"../fc-bits.js?ver=3.1.8";import{c as s,E as a,e as i,g as r,h as n,i as o,j as m,k as l,l as c,n as p,w as _,o as u,p as d,v as h,u as g,q as f,r as v,t as b,s as E,x as T,y,z as C,A as w,B as L,C as M,D as A,F as R,G as I,H as P,I as k,J as S,K as D,L as V,M as O,N as j,O as x,P as F,Q as $,R as q,m as B,S as U,T as N,U as Z,V as H,W as G,X as W,Y as K,Z as Q,_ as z,f as Y,$ as J,a0 as X,a1 as ee,a2 as te,a3 as se,a4 as ae,a5 as ie,d as re,a6 as ne,a7 as oe,a8 as me,a9 as le,aa as ce,ab as pe,ac as _e,ad as ue,ae as de,af as he,ag as ge,ah as fe,ai as ve,aj as be,ak as Ee,al as Te,am as ye,an as Ce,ao as we,ap as Le,aq as Me,ar as Ae,as as Re,at as Ie,a as Pe,au as ke}from"../vendor-element-plus.js?ver=3.1.8";import{W as Se,X as De,bB as Ve,a8 as Oe,aQ as je,ab as xe,Z as Fe,J as $e,az as qe,b2 as Be,ax as Ue,a0 as Ne,_ as Ze,Y as He,a1 as Ge,a5 as We,aa as Ke,a9 as Qe,bC as ze,bD as Ye,bE as Je,by as Xe,bF as et,bG as tt,bH as st}from"../vendor.js?ver=3.1.8";import{_ as at,I as it,T as rt,a as nt,n as ot}from"../fc-bits-ui.js?ver=3.1.8";const mt=[{name:"no_permission",path:"/no_permission",component:()=>e(()=>import("./Modules/Dashboard/NoPermission.js?ver=3.1.8"),[],import.meta.url)},{name:"dashboard",path:"/",component:()=>e(()=>import("./Modules/Dashboard/Dashboard.js?ver=3.1.8"),[],import.meta.url),props:!0,meta:{active_menu:"dashboard",permission:"fcrm_view_dashboard",side_path:"/"}},{name:"subscribers",path:"/subscribers",component:()=>e(()=>import("../v3app/src/Modules/Contacts/Contacts.js?ver=3.1.8"),[],import.meta.url),props:!0,meta:{active_menu:"contacts",permission:"fcrm_read_contacts",side_path:"/subscribers"}},{path:"/email",component:()=>e(()=>import("./Modules/Email/EmailView.js?ver=3.1.8"),[],import.meta.url),props:!0,meta:{parent:"email",permission:"fcrm_read_emails"},children:[{name:"campaigns",path:"campaigns",component:()=>e(()=>import("./Modules/Email/Campaigns/Campaigns.js?ver=3.1.8"),[],import.meta.url),props:!0,meta:{parent:"email",active_menu:"campaigns",permission:"fcrm_read_emails",side_path:"/email/campaigns"}},{name:"campaign-view",path:"campaigns/:id/view",component:()=>e(()=>import("./Modules/Email/Campaigns/ViewCampaign.js?ver=3.1.8"),[],import.meta.url),props:!0,meta:{parent:"campaigns",active_menu:"campaigns",permission:"fcrm_read_emails",side_path:"/email/campaigns"}},{name:"campaign",path:"campaigns/:id",component:()=>e(()=>import("./Modules/Email/Campaigns/Campaign.js?ver=3.1.8"),[],import.meta.url),props:!0,meta:{parent:"campaigns",active_menu:"campaigns",permission:"fcrm_read_emails",side_path:"/email/campaigns"}},{name:"templates",path:"templates",component:()=>e(()=>import("./Modules/Email/Templates/Templates.js?ver=3.1.8"),[],import.meta.url),props:!0,meta:{parent:"campaigns",active_menu:"campaigns",permission:"fcrm_read_emails",side_path:"/email/templates"}},{name:"edit_template",path:"templates/:template_id",component:()=>e(()=>import("./Modules/Email/Templates/EditTemplate.js?ver=3.1.8"),[],import.meta.url),props:!0,meta:{parent:"templates",active_menu:"campaigns",permission:"fcrm_read_emails",side_path:"/email/templates"}},{name:"email_patterns",path:"patterns",component:()=>e(()=>import("./Modules/Email/Templates/Patterns.js?ver=3.1.8"),[],import.meta.url),props:!0,meta:{parent:"campaigns",active_menu:"campaigns",permission:"fcrm_read_emails",side_path:"/email/patterns"}},{name:"edit_pattern",path:"patterns/:pattern_id",component:()=>e(()=>import("./Modules/Email/Templates/EditPattern.js?ver=3.1.8"),[],import.meta.url),props:!0,meta:{parent:"email_patterns",active_menu:"campaigns",permission:"fcrm_manage_emails",side_path:"/email/patterns"}},{path:"sequences",component:()=>e(()=>import("./Modules/Email/EmailSequences/SequenceView.js?ver=3.1.8"),[],import.meta.url),props:!0,children:[{name:"email-sequences",path:"",component:()=>e(()=>import("./Modules/Email/EmailSequences/AllSequences.js?ver=3.1.8"),[],import.meta.url),meta:{parent:"email-sequences",active_menu:"campaigns",permission:"fcrm_read_emails",side_path:"/email/sequences"}},{name:"edit-sequence",path:"edit/:id",component:()=>e(()=>import("./Modules/Email/EmailSequences/ViewSequence.js?ver=3.1.8"),[],import.meta.url),props:!0,meta:{parent:"email-sequences",active_menu:"campaigns",permission:"fcrm_read_emails",side_path:"/email/sequences"}},{name:"edit-sequence-email",path:"edit/:sequence_id/email/:email_id",component:()=>e(()=>import("./Modules/Email/EmailSequences/EditEmail.js?ver=3.1.8"),[],import.meta.url),props:!0,meta:{parent:"email-sequences",active_menu:"campaigns",permission:"fcrm_read_emails",side_path:"/email/sequences"}},{name:"sequence-subscribers",path:"subscribers/:id/view",component:()=>e(()=>import("./Modules/Email/EmailSequences/ViewSequenceSubscribers.js?ver=3.1.8"),[],import.meta.url),props:!0,meta:{parent:"email-sequences",active_menu:"campaigns",permission:"fcrm_read_emails",side_path:"/email/sequences"}}]},{path:"recurring-campaigns",component:()=>e(()=>import("./Modules/Email/RecurringCampaigns/RecurringCampaignsView.js?ver=3.1.8"),[],import.meta.url),props:!0,children:[{name:"recurring_campaigns",path:"",component:()=>e(()=>import("./Modules/Email/RecurringCampaigns/RecurringCampaigns.js?ver=3.1.8"),[],import.meta.url),meta:{parent:"email",active_menu:"campaigns",permission:"fcrm_read_emails",side_path:"/email/recurring-campaigns"}},{name:"create_recurring_campaign",path:"create-new",component:()=>e(()=>import("./Modules/Email/RecurringCampaigns/CreateFlow.js?ver=3.1.8"),[],import.meta.url),meta:{parent:"recurring_campaigns",active_menu:"campaigns",permission:"fcrm_read_emails"}},{path:"emails/:campaign_id",component:()=>e(()=>import("./Modules/Email/RecurringCampaigns/ViewSingleCampaign.js?ver=3.1.8"),[],import.meta.url),props:!0,children:[{path:"view",name:"view_recurring_campaign",component:()=>e(()=>import("./Modules/Email/RecurringCampaigns/Campaign/EmailConfiguration.js?ver=3.1.8"),[],import.meta.url),props:!0,meta:{parent:"recurring_campaigns",active_menu:"campaigns",side_path:"/email/recurring-campaigns"}},{path:"history",name:"past_recurring_emails",component:()=>e(()=>import("./Modules/Email/RecurringCampaigns/Campaign/EmailHistory.js?ver=3.1.8"),[],import.meta.url),props:!0,meta:{parent:"recurring_campaigns",active_menu:"campaigns",side_path:"/email/recurring-campaigns"}},{path:"settings",name:"recurring_campaign_settings",component:()=>e(()=>import("./Modules/Email/RecurringCampaigns/Campaign/Settings.js?ver=3.1.8"),[],import.meta.url),props:!0,meta:{parent:"recurring_campaigns",active_menu:"campaigns",side_path:"/email/recurring-campaigns"}}]},{path:"emails/:campaign_id/history/:email_id",name:"recurring_email_report",component:()=>e(()=>import("./Modules/Email/RecurringCampaigns/Campaign/EmailReport.js?ver=3.1.8"),[],import.meta.url),props:!0,meta:{parent:"recurring_campaigns",active_menu:"campaigns",side_path:"/email/recurring-campaigns"}}]},{name:"all_emails",path:"all-emails",component:()=>e(()=>import("./Modules/Email/AllEmails.js?ver=3.1.8"),[],import.meta.url),props:!0,meta:{parent:"email",active_menu:"campaigns",permission:"fcrm_read_emails"}}]},{path:"/contact-groups",component:()=>e(()=>import("../v3app/src/Modules/Contacts/ContactGroups.js?ver=3.1.8"),[],import.meta.url),props:!0,meta:{parent:"contacts",permission:"fcrm_manage_contact_cats"},children:[{name:"lists",path:"lists",component:()=>e(()=>import("../v3app/src/Modules/Lists/Lists.js?ver=3.1.8"),[],import.meta.url),props:!0,meta:{parent:"subscribers",active_menu:"contacts",permission:"fcrm_manage_contact_cats",side_path:"/subscribers"}},{name:"tags",path:"tags",component:()=>e(()=>import("../v3app/src/Modules/Tags/Tags.js?ver=3.1.8"),[],import.meta.url),props:!0,meta:{parent:"subscribers",active_menu:"contacts",permission:"fcrm_manage_contact_cats",side_path:"/subscribers"}},{name:"dynamic_segments",path:"dynamic-segments",component:()=>e(()=>import("../v3app/src/Modules/DynamicSegments/AllSegments.js?ver=3.1.8"),[],import.meta.url),props:!0,meta:{parent:"subscribers",active_menu:"contacts",permission:"fcrm_manage_contact_cats",side_path:"/subscribers"}},{name:"create_custom_segment",path:"dynamic-segments/create-custom",component:()=>e(()=>import("../v3app/src/Modules/DynamicSegments/CreateCustomSegment.js?ver=3.1.8"),[],import.meta.url),meta:{parent:"subscribers",active_menu:"contacts",permission:"fcrm_manage_contact_cats",side_path:"/subscribers"}},{name:"view_segment",path:"dynamic-segments/:slug/view/:id",props:!0,component:()=>e(()=>import("../v3app/src/Modules/DynamicSegments/SegmentViewer.js?ver=3.1.8"),[],import.meta.url),meta:{permission:"fcrm_read_contacts",parent:"subscribers",active_menu:"contacts",side_path:"/subscribers"}},{path:"companies",component:()=>e(()=>import("../v3app/src/Modules/Companies/CompaniesRoute.js?ver=3.1.8"),[],import.meta.url),props:!0,meta:{active_menu:"contacts",parent:"contacts",permission:"fcrm_read_contacts"},children:[{name:"companies",path:"",component:()=>e(()=>import("../v3app/src/Modules/Companies/AllCompanies.js?ver=3.1.8"),[],import.meta.url),meta:{active_menu:"contacts",parent:"contacts",permission:"fcrm_read_contacts"}}]}]},{name:"list",path:"/lists/:listId",component:()=>e(()=>import("../v3app/src/Modules/Lists/List.js?ver=3.1.8"),[],import.meta.url),props:!0,meta:{parent:"subscribers",active_menu:"contacts",permission:"fcrm_manage_contact_cats",side_path:"/subscribers"}},{name:"tag",path:"/tags/:tagId",component:()=>e(()=>import("../v3app/src/Modules/Tags/Tag.js?ver=3.1.8"),[],import.meta.url),props:!0,meta:{permission:"fcrm_read_contacts",parent:"subscribers",active_menu:"contacts",side_path:"/subscribers"}},{name:"import",path:"/import",component:()=>e(()=>import("./Modules/Importer/Importer.js?ver=3.1.8"),[],import.meta.url),props:!0,meta:{permission:"fcrm_manage_contacts",side_path:"/subscribers"}},{name:"forms",path:"/forms",component:()=>e(()=>import("./Modules/Forms/Forms.js?ver=3.1.8"),[],import.meta.url),props:!0,meta:{parent:"forms",active_menu:"forms",permission:"fcrm_manage_forms",side_path:"/forms"}},{path:"/settings",component:()=>e(()=>import("../v3app/src/Modules/Settings/Settings.js?ver=3.1.8"),[],import.meta.url),meta:{active_menu:"settings",permission:"fcrm_manage_settings",side_path:"/settings"},children:[{name:"email_settings",path:"email_settings",component:()=>e(()=>import("../v3app/src/Modules/Settings/_EmailSettings.js?ver=3.1.8"),[],import.meta.url),meta:{active_menu:"settings",permission:"fcrm_manage_settings",side_path:"/settings"}},{name:"business_settings",path:"",component:()=>e(()=>import("../v3app/src/Modules/Settings/_BusinessSetup.js?ver=3.1.8"),[],import.meta.url),meta:{active_menu:"settings",permission:"fcrm_manage_settings",side_path:"/settings"}},{name:"general_settings",path:"general_settings",component:()=>e(()=>import("../v3app/src/Modules/Settings/_GeneralSettings.js?ver=3.1.8"),[],import.meta.url),meta:{active_menu:"settings",permission:"fcrm_manage_settings",side_path:"/settings"}},{name:"custom_contact_fields",path:"custom_contact_fields",component:()=>e(()=>import("../v3app/src/Modules/Settings/parts/CustomContactFields.js?ver=3.1.8"),[],import.meta.url),meta:{active_menu:"settings",permission:"fcrm_manage_settings",side_path:"/settings"}},{name:"smart_links",path:"smart_links",component:()=>e(()=>import("../v3app/src/Modules/Settings/SmartLinks/Links.js?ver=3.1.8"),[],import.meta.url),meta:{active_menu:"settings",permission:"fcrm_manage_settings",side_path:"/settings"}},{name:"sms_settings",path:"sms_settings",component:()=>e(()=>import("../v3app/src/Modules/Settings/_SMSSettings.js?ver=3.1.8"),[],import.meta.url),meta:{active_menu:"settings",permission:"fcrm_manage_settings",side_path:"/settings"}},{name:"double-optin-settings",path:"double_optin_settings",component:()=>e(()=>import("../v3app/src/Modules/Settings/_DoubleOptinSettings.js?ver=3.1.8"),[],import.meta.url),meta:{active_menu:"settings",permission:"fcrm_manage_settings",side_path:"/settings"}},{name:"incoming_webhooks",path:"incoming_webhooks",component:()=>e(()=>import("../v3app/src/Modules/Settings/DeveloperWebhooks/_IncomingWebhooks.js?ver=3.1.8"),[],import.meta.url),meta:{active_menu:"settings",permission:"fcrm_manage_settings",side_path:"/settings"}},{name:"settings_tools",path:"settings_tools",component:()=>e(()=>import("../v3app/src/Modules/Settings/SystemAdminTools/_CronJobMonitor.js?ver=3.1.8"),[],import.meta.url),meta:{active_menu:"settings",permission:"fcrm_manage_settings",side_path:"/settings"}},{name:"data_cleanup",path:"data_cleanup",component:()=>e(()=>import("../v3app/src/Modules/Settings/SystemAdminTools/_DataCleanupPage.js?ver=3.1.8"),[],import.meta.url),meta:{active_menu:"settings",permission:"fcrm_manage_settings",side_path:"/settings"}},{name:"database_health",path:"database_health",component:()=>e(()=>import("../v3app/src/Modules/Settings/SystemAdminTools/_DatabaseHealth.js?ver=3.1.8"),[],import.meta.url),meta:{active_menu:"settings",permission:"fcrm_manage_settings",side_path:"/settings"}},{name:"database_reset",path:"database_reset",component:()=>e(()=>import("../v3app/src/Modules/Settings/SystemAdminTools/_DatabaseReset.js?ver=3.1.8"),[],import.meta.url),meta:{active_menu:"settings",permission:"fcrm_manage_settings",side_path:"/settings"}},{name:"managers",path:"managers",component:()=>e(()=>import("../v3app/src/Modules/Settings/_Managers.js?ver=3.1.8"),[],import.meta.url),meta:{active_menu:"settings",permission:"fcrm_manage_settings",side_path:"/settings"}},{name:"settings_compliance",path:"settings_compliance",component:()=>e(()=>import("../v3app/src/Modules/Settings/_ComplianceSettings.js?ver=3.1.8"),[],import.meta.url),meta:{active_menu:"settings",permission:"fcrm_manage_settings",side_path:"/settings"}},{name:"smtp_settings",path:"smtp_settings",component:()=>e(()=>import("../v3app/src/Modules/Settings/_SmtpEmailSetup.js?ver=3.1.8"),[],import.meta.url),meta:{active_menu:"settings",permission:"fcrm_manage_settings",side_path:"/settings"}},{name:"license_management",path:"license_management",component:()=>e(()=>import("../v3app/src/Modules/Settings/_LicenseManagement.js?ver=3.1.8"),[],import.meta.url),meta:{active_menu:"settings",permission:"fcrm_manage_settings",side_path:"/settings"}},{name:"integrations",path:"integrations",component:()=>e(()=>import("../v3app/src/Modules/Settings/_IntegrationSettings.js?ver=3.1.8"),[],import.meta.url),meta:{active_menu:"settings",permission:"fcrm_manage_settings",side_path:"/settings"}},{name:"advanced_features",path:"advanced_features",component:()=>e(()=>import("../v3app/src/Modules/Settings/_AdvancedFeatures.js?ver=3.1.8"),[],import.meta.url),meta:{active_menu:"settings",permission:"fcrm_manage_settings",side_path:"/settings"}},{name:"system_logs",path:"system_logs",component:()=>e(()=>import("../v3app/src/Modules/Settings/_SystemLogs.js?ver=3.1.8"),[],import.meta.url),meta:{active_menu:"settings",side_path:"/settings"}},{name:"abandoned_cart_settings",path:"abandoned_cart_settings",component:()=>e(()=>import("../v3app/src/Modules/Settings/_AbandonedCartSettings.js?ver=3.1.8"),[],import.meta.url),meta:{active_menu:"settings",side_path:"/settings"}},{name:"ai_settings",path:"ai_settings",component:()=>e(()=>import("../v3app/src/Modules/Settings/_AiSettings.js?ver=3.1.8"),[],import.meta.url),meta:{active_menu:"settings",permission:"fcrm_manage_settings",side_path:"/settings"}},{name:"mcp_settings",path:"mcp_settings",component:()=>e(()=>import("../v3app/src/Modules/Settings/_McpSettings.js?ver=3.1.8"),[],import.meta.url),meta:{active_menu:"settings",permission:"fcrm_manage_settings",side_path:"/settings"}}]},{path:"/funnels",component:()=>e(()=>import("./Modules/Funnels/FunnelRoute.js?ver=3.1.8"),[],import.meta.url),meta:{active_menu:"funnels",permission:"fcrm_read_funnels",side_path:"/funnels"},children:[{name:"funnels",path:"",component:()=>e(()=>import("./Modules/Funnels/Funnels.js?ver=3.1.8"),[],import.meta.url),meta:{active_menu:"funnels",permission:"fcrm_read_funnels",side_path:"/funnels"}},{name:"edit_funnel",path:"funnel/:funnel_id/edit",component:()=>e(()=>import("./Modules/Funnels/FunnelEditor/Edit.js?ver=3.1.8"),[],import.meta.url),props:!0,meta:{active_menu:"funnels",permission:"fcrm_read_funnels",side_path:"/funnels"}},{name:"funnel_subscribers",path:"funnel/:funnel_id/subscribers",component:()=>e(()=>import("./Modules/Funnels/FunnelSubscribers.js?ver=3.1.8"),[],import.meta.url),props:!0,meta:{active_menu:"funnels",permission:"fcrm_read_funnels",side_path:"/funnels"}},{name:"import_funnel",path:"funnel/import",component:()=>e(()=>import("./Modules/Funnels/ImportFunnel.js?ver=3.1.8"),[],import.meta.url),meta:{active_menu:"funnels",permission:"fcrm_write_funnels",side_path:"/funnels"}},{name:"funnel_activities",path:"funnel/all-activities",component:()=>e(()=>import("./Modules/Funnels/FunnelActivities.js?ver=3.1.8"),[],import.meta.url),meta:{active_menu:"funnels",permission:"fcrm_read_funnels",side_path:"/funnels"}}]},{name:"docs",path:"/documentation",component:()=>e(()=>import("./Modules/Documentation/Docs.js?ver=3.1.8"),[],import.meta.url),meta:{side_path:"/documentation",active_menu:"documentation"}},{name:"addons",path:"/add-ons",component:()=>e(()=>import("./Modules/Settings/AddOns.js?ver=3.1.8"),[],import.meta.url),meta:{side_path:"/add-ons",active_menu:"addons"}},{name:"reports",path:"/reports",component:()=>e(()=>import("./Modules/Reports/ReportsHome.js?ver=3.1.8"),[],import.meta.url),meta:{active_menu:"reports",side_path:"/reports"}},{name:"abandon_carts_legacy_redirect",path:"/abandon-carts",redirect:{name:"reports",query:{tab:"abandoned_carts"}},meta:{active_menu:"reports",side_path:"/reports"}},{name:"crm_migrations",path:"/crm_migrations",component:()=>e(()=>import("./Modules/Migrator/Home.js?ver=3.1.8"),[],import.meta.url),meta:{active_menu:"contacts",permission:"fcrm_manage_settings",side_path:"/subscribers"}},{name:"sms_campaigns",path:"/sms/campaigns",component:()=>e(()=>import("../v3app/src/Modules/SMS/Campaigns/Campaigns.js?ver=3.1.8"),[],import.meta.url),props:!0,meta:{active_menu:"sms",permission:"fcrm_read_emails",side_path:"/sms/campaigns"}},{name:"all_sms",path:"/sms/all-sms",component:()=>e(()=>import("../v3app/src/Modules/SMS/AllSMS.js?ver=3.1.8"),[],import.meta.url),props:!0,meta:{active_menu:"sms",permission:"fcrm_read_emails",side_path:"/sms/all-sms"}},{name:"sms_campaign",path:"/sms/campaign",component:()=>e(()=>import("../v3app/src/Modules/SMS/Campaigns/CreateFlow.js?ver=3.1.8"),[],import.meta.url),props:!0,meta:{active_menu:"sms",permission:"fcrm_read_emails",side_path:"/sms/campaign"}},{name:"sms_campaign_edit",path:"/sms/campaign/:id",component:()=>e(()=>import("../v3app/src/Modules/SMS/Campaigns/CreateFlow.js?ver=3.1.8"),[],import.meta.url),props:!0,meta:{active_menu:"sms",permission:"fcrm_manage_emails",side_path:"/sms/campaign"}},{name:"sms_campaign_view",path:"/sms/campaigns/:id/view",component:()=>e(()=>import("../v3app/src/Modules/SMS/Campaigns/ViewSMSCampaign.js?ver=3.1.8"),[],import.meta.url),props:!0,meta:{active_menu:"sms",permission:"fcrm_read_emails",side_path:"/sms/campaigns"}},{name:"import_sms_campaigns",path:"/sms/campaigns/import/new",component:()=>e(()=>import("../v3app/src/Modules/SMS/Campaigns/Import.js?ver=3.1.8"),[],import.meta.url),props:!0,meta:{active_menu:"sms",permission:"fcrm_read_emails",side_path:"/sms/campaigns"}},{name:"fallback",path:"/:pathMatch(.*)*",redirect:"/",meta:{side_path:"/"}}];var lt={path:"/subscribers/:id",component:()=>e(()=>import("./Modules/Profile/Profile.js?ver=3.1.8"),[],import.meta.url),props:!0,meta:{parent:"subscribers",active_menu:"contacts",permission:"fcrm_read_contacts",side_path:"/subscribers"},children:[{name:"subscriber",path:"",component:()=>e(()=>import("../v3app/src/Modules/Profile/Parts/ProfileOverview.js?ver=3.1.8"),[],import.meta.url),meta:{parent:"subscribers",active_menu:"contacts",permission:"fcrm_read_contacts",side_path:"/subscribers",no_scroll:!0}},{name:"subscriber_emails",path:"emails",component:()=>e(()=>import("./Modules/Profile/Parts/ProfileEmails.js?ver=3.1.8"),[],import.meta.url),meta:{parent:"subscribers",active_menu:"contacts",permission:"fcrm_read_contacts",side_path:"/subscribers",no_scroll:!0}},{name:"subscriber_sms",path:"sms",component:()=>e(()=>import("../v3app/src/Modules/Profile/Parts/ProfileSMS.js?ver=3.1.8"),[],import.meta.url),meta:{parent:"subscribers",active_menu:"contacts",permission:"fcrm_read_contacts",side_path:"/subscribers"}},{name:"subscriber_form_submissions",path:"form-submissions",component:()=>e(()=>import("./Modules/Profile/Parts/ProfileFormSubmissions.js?ver=3.1.8"),[],import.meta.url),meta:{parent:"subscribers",active_menu:"contacts",permission:"fcrm_read_contacts",side_path:"/subscribers",no_scroll:!0}},{name:"subscriber_notes",path:"notes",component:()=>e(()=>import("./Modules/Profile/Parts/ProfileNotes.js?ver=3.1.8"),[],import.meta.url),meta:{parent:"subscribers",active_menu:"contacts",permission:"fcrm_read_contacts",side_path:"/subscribers",no_scroll:!0}},{name:"subscriber_purchases",path:"purchases",component:()=>e(()=>import("./Modules/Profile/Parts/ProfilePurchaseHistory.js?ver=3.1.8"),[],import.meta.url),meta:{parent:"subscribers",active_menu:"contacts",permission:"fcrm_read_contacts",side_path:"/subscribers",no_scroll:!0}},{name:"subscriber_support_tickets",path:"support-tickets",component:()=>e(()=>import("./Modules/Profile/Parts/ProfileSupportTickets.js?ver=3.1.8"),[],import.meta.url),meta:{parent:"subscribers",active_menu:"contacts",permission:"fcrm_read_contacts",side_path:"/subscribers",no_scroll:!0}},{name:"subscriber_files",path:"files",component:()=>e(()=>import("./Modules/Profile/Parts/ProfileFiles.js?ver=3.1.8"),[],import.meta.url),meta:{parent:"subscribers",active_menu:"contacts",permission:"fcrm_read_contacts",side_path:"/subscribers",no_scroll:!0}},{name:"fluentcrm_profile_extended",path:"profile_section",component:()=>e(()=>import("./Modules/Profile/Parts/SubscriberExternalView.js?ver=3.1.8"),[],import.meta.url),meta:{parent:"subscribers",active_menu:"contacts",permission:"fcrm_read_contacts",side_path:"/subscribers",no_scroll:!0}}]},ct={path:"/companies/:company_id",props:!0,component:()=>e(()=>import("../v3app/src/Modules/Companies/ViewCompany.js?ver=3.1.8"),[],import.meta.url),meta:{active_menu:"contacts",parent:"contacts",permission:"fcrm_read_contacts"},children:[{name:"view_company",path:"",component:()=>e(()=>import("../v3app/src/Modules/Companies/CompanyContacts.js?ver=3.1.8"),[],import.meta.url),meta:{parent:"view_company",active_menu:"contacts",permission:"fcrm_read_contacts",side_path:"/companies"}},{name:"company_activities",path:"activities",component:()=>e(()=>import("../v3app/src/Modules/Companies/CompanyActivities.js?ver=3.1.8"),[],import.meta.url),meta:{parent:"view_company",active_menu:"contacts",permission:"fcrm_read_contacts",side_path:"/companies"}},{name:"fluent_crm_company_section_extended",path:"custom_section",component:()=>e(()=>import("../v3app/src/Modules/Companies/CompanyExternalView.js?ver=3.1.8"),[],import.meta.url),meta:{parent:"view_company",active_menu:"contacts",permission:"fcrm_read_contacts",side_path:"/companies"}}]};const pt={class:"fcrm_empty_state"},_t=["innerHTML"],ut={class:"navigable_list_container fcrm_global_search_result_container"},dt={key:0,class:"no_results fcrm_global_search_result_not_found"},ht={key:1},gt={ref:"listContainer",class:"navigable_list fcrm_global_search_result_list"},ft=["tabindex","onClick","onKeydown"];const vt=[{key:"subscribers",title:"Contacts",scope:"subscribers"},{key:"emails",title:"Emails",scope:"campaigns"},{key:"automations",title:"Automations",scope:"funnels"},{key:"subscriber_notes",title:"Notes",scope:"subscriber_notes"}],bt={subscriber:{name:"subscriber",key:"id",action:"edit"},campaign:{name:"campaign",key:"id"},funnel:{name:"edit_funnel",key:"funnel_id"},company:{name:"view_company",key:"company_id"}},Et=["title","aria-label"],Tt={class:"icon"},yt={class:"fcrm_global_search_input_container"},Ct={class:"icon"},wt={key:0,class:"searched_item"},Lt={key:0,class:"fcrm_global_search_commands"},Mt={class:"fcrm_global_search_commands_label"},At={key:0,class:"fcrm_global_search_commands_list"},Rt=["onClick","onMouseenter"],It={class:"fcrm_command_slash"},Pt={class:"fcrm_command_desc"},kt={key:1,class:"fcrm_global_search_commands_empty"},St={class:"fcrm_global_searching_for"},Dt={class:"fcrm_global_searching_for_label"},Vt={class:"fcrm_global_searching_suggestion_list"},Ot=["onClick"],jt={key:2,class:"fc-global-remote-result-container"},xt={class:"w-full"},Ft={key:0,class:"fcrm_global_search_subscriber"},$t={class:"fcrm_global_search_subscriber_avatar"},qt=["src","alt"],Bt={key:1,class:"fcrm_global_search_subscriber_avatar_placeholder"},Ut={class:"fcrm_global_search_subscriber_info"},Nt={class:"fcrm_global_search_subscriber_name"},Zt={class:"fcrm_global_search_subscriber_meta"},Ht={class:"fcrm_global_search_subscriber_email"},Gt={class:"fcrm_global_search_result_type"},Wt={key:1,class:"fcrm_global_search_subscriber"},Kt={class:"fcrm_global_search_subscriber_avatar"},Qt=["src","alt"],zt={key:1,class:"fcrm_global_search_subscriber_avatar_placeholder"},Yt={class:"fcrm_global_search_subscriber_info"},Jt={class:"fcrm_global_search_subscriber_name"},Xt={class:"fcrm_global_search_subscriber_meta"},es={class:"fcrm_global_search_result_type"},ts={key:2,class:"fcrm_global_search_subscriber fcrm_global_search_title_item"},ss={class:"fcrm_global_search_subscriber_name"},as={class:"fcrm_global_search_subscriber_meta"},is={class:"fcrm_global_search_subscriber_email"},rs={class:"fcrm_global_search_result_type"},ns={key:3,class:"fcrm_global_search_subscriber fcrm_global_search_title_item"},os={class:"fcrm_global_search_subscriber_name"},ms={class:"fcrm_global_search_subscriber_meta"},ls={key:0,class:"fcrm_global_search_subscriber_email"},cs={class:"fcrm_global_search_result_type"},ps={class:"dialog-footer is-border"},_s={key:0,class:"fcrm_search_tip"},us={class:"fcrm_search_tip_label"},ds={class:"fcrm_search_tip_text"},hs={class:"fcrm_search_commands"},gs={class:"fcrm_label"},fs={class:"fcrm_label"},vs={class:"fcrm_label"};const bs=at({name:"GlobalSearch",components:{Icons:it,NavigableList:at({name:"NavigableList",components:{EmptyState:{__name:"EmptyState",props:{title:{type:String,default:"No Data Found"}},setup:e=>(t,s)=>(Se(),De("div",pt,[s[0]||(s[0]=Ve('',1)),e.title?(Se(),De("p",{key:0,innerHTML:e.title,class:"fcrm_empty_state_text"},null,8,_t)):Oe("",!0)]))}},props:{items:{type:Array,required:!0,default:()=>[]},noResultsText:{type:String,default:"No results found"},onAction:{type:Function,required:!0}},data:()=>({selectedIndex:-1}),methods:{navigate(e){const t=this.items.length;if(0===t)return;const s=t-1;"up"===e?this.selectedIndex=this.selectedIndex<=0?s:this.selectedIndex-1:"down"===e&&(this.selectedIndex=this.selectedIndex>=s?0:this.selectedIndex+1),this.scrollToSelectedElement()},scrollToSelectedElement(){const e=this.$refs.listContainer,t=this.$refs.listItem&&this.$refs.listItem[this.selectedIndex];t&&e&&(t.scrollIntoView({behavior:"smooth",block:"nearest"}),this.$nextTick(()=>{t.focus()}))},handleItemClick(e){this.selectedIndex=e,this.performAction(e)},performAction(e){e>=0&&e=0&&(this.selectedIndex=e,this.$nextTick(()=>{const t=this.$refs.listItem&&this.$refs.listItem[e];t&&t.focus()}))},performFirstAction(){this.items.length>0?(this.selectedIndex=0,this.performAction(0)):this.selectedIndex=-1},performSelectedAction(){this.items.length<1?this.selectedIndex=-1:this.selectedIndex<0||this.selectedIndex>=this.items.length?this.performFirstAction():this.performAction(this.selectedIndex)}},watch:{items:{handler(e){this.resetSelection(),e.length>0&&this.$nextTick(()=>{})},immediate:!0},selectedIndex(){}}},[["render",function(e,t,s,a,i,r){const n=je("EmptyState");return Se(),De("div",ut,[0===s.items.length?(Se(),De("div",dt,[xe(n,{title:s.noResultsText},null,8,["title"])])):(Se(),De("div",ht,[Fe("ul",gt,[(Se(!0),De($e,null,qe(s.items,(s,a)=>(Se(),De("li",{key:a,ref_for:!0,ref:"listItem",class:Ne({selected:a===i.selectedIndex}),tabindex:a,onClick:e=>r.handleItemClick(a),onKeydown:[t[0]||(t[0]=Be(Ue(e=>r.navigate("up"),["prevent"]),["arrow-up"])),t[1]||(t[1]=Be(Ue(e=>r.navigate("down"),["prevent"]),["arrow-down"])),Be(e=>r.performAction(a),["enter"])]},[Ze(e.$slots,"default",{item:s,index:a,isSelected:a===i.selectedIndex},void 0,!0)],42,ft))),128))],512)]))])}],["__scopeId","data-v-a5646a37"]]),Close:s},data:()=>({visible:!1,query:"",scopeTag:null,loading:!1,results:[],payload:null,lastScope:null,searchTimer:null,activeTip:"",commandIndex:-1}),computed:{scopeTags(){var e;const t=[...vt];return"undefined"!=typeof window&&(null==(e=window.fcAdmin.addons)?void 0:e.company_module)&&t.push({key:"companies",title:"Companies",scope:"companies"}),t},scope(){return this.scopeTag&&this.scopeTag.scope?this.scopeTag.scope:"all"},isCommandMode(){return this.query.startsWith("/")},commandQuery(){return this.query.slice(1).toLowerCase().trim()},filteredCommands(){if(!this.isCommandMode)return[];const e=this.commandQuery;return this.scopeTags.filter(t=>!e||t.title.toLowerCase().includes(e))}},watch:{scopeTag(){this.query.trim()&&this.search()},filteredCommands(){this.commandIndex=this.filteredCommands.length?0:-1}},mounted(){window.addEventListener("keydown",this.onKeydown)},beforeUnmount(){clearTimeout(this.searchTimer),window.removeEventListener("keydown",this.onKeydown)},methods:{open(){this.setRandomTip(),this.visible=!0},onClosed(){var e;this.visible=!1,this.query="",this.scopeTag=null,this.results=[],this.payload=null,null==(e=this.$refs.listRef)||e.resetSelection()},focusInput(){this.$nextTick(()=>{var e;return null==(e=this.$refs.inputRef)?void 0:e.focus()})},onInput(){clearTimeout(this.searchTimer),this.isCommandMode?this.results=[]:this.query.trim()?this.searchTimer=setTimeout(()=>this.search(),400):this.results=[]},search(){const e=this.query.trim();e?this.payload&&this.lastScope===this.scope&&this.payload._query===e?this.applyPayload():(this.loading=!0,this.$get("global-search",{search:e,scope:this.scope}).then(t=>{this.payload={...t,_query:e},this.lastScope=this.scope,this.applyPayload()}).catch(()=>{this.results=[]}).finally(()=>{this.loading=!1})):this.results=[]},applyPayload(){if(!this.payload)return;const e=this.scopeTag&&{subscribers:"subscribers",emails:"campaigns",automations:"funnels",companies:"companies",subscriber_notes:"subscriber_notes"}[this.scopeTag.key];if(e)this.results=Array.isArray(this.payload[e])?this.payload[e]:[];else{const e=(this.payload.subscribers||[]).map(e=>({...e,_resultType:"subscriber"})),t=(this.payload.campaigns||[]).map(e=>({...e,_resultType:"campaign"})),s=(this.payload.funnels||[]).map(e=>({...e,_resultType:"funnel"})),a=(this.payload.companies||[]).map(e=>({...e,_resultType:"company"}));this.results=[...e,...t,...s,...a]}},focusList(e="down"){if(this.isCommandMode){const t=this.filteredCommands.length;if(!t)return;return void(this.commandIndex="up"===e?this.commandIndex<=0?t-1:this.commandIndex-1:this.commandIndex>=t-1?0:this.commandIndex+1)}const t=this.$refs.listRef;t&&this.results.length&&("up"!==e?t.focusList(0):t.focusList(this.results.length-1))},selectFromKeyboard(){if(this.isCommandMode){const e=this.filteredCommands[this.commandIndex];return void(e&&this.selectCommand(e))}const e=this.$refs.listRef;e&&this.results.length&&e.performSelectedAction()},selectCommand(e){this.scopeTag=e,this.query="",this.commandIndex=-1,this.$nextTick(()=>{var e;return null==(e=this.$refs.inputRef)?void 0:e.focus()})},goToItem(e){const t=e._resultType||(this.scopeTag&&"subscribers"===this.scopeTag.key?"subscriber":null)||(this.scopeTag&&"emails"===this.scopeTag.key?"campaign":null)||(this.scopeTag&&"automations"===this.scopeTag.key?"funnel":null)||(this.scopeTag&&"companies"===this.scopeTag.key?"company":null)||(this.scopeTag&&"subscriber_notes"===this.scopeTag.key?"subscriber_note":null);if("subscriber_note"===t)return this.$router.push({name:"subscriber_notes",params:{id:e.subscriber_id},query:{note_id:e.id}}),void(this.visible=!1);const s=t?bt[t]:null;if(!s)return;const a=e.id??e[s.key],i={[s.key]:a};s.action&&(i.action=s.action),this.$router.push({name:s.name,params:i}),this.visible=!1},resultType(e){if(null==e?void 0:e._resultType)return e._resultType;if(this.scopeTag){return{subscribers:"subscriber",emails:"campaign",automations:"funnel",companies:"company",subscriber_notes:"subscriber_note"}[this.scopeTag.key]||null}return null},resultTypeLabel(e){return{subscriber:"Contact",campaign:"Email",funnel:"Automation",company:"Company",subscriber_note:"Note"}[this.resultType(e)]||"Item"},setRandomTip(){const e=[this.$t('Press "/" anywhere to open Search quickly'),this.$t("Use Contacts, Emails, or Automations chips to narrow results"),this.$t("Use ↑↓ keys to move through results"),this.$t("Press Enter to open the selected result"),this.$t("Type more words for more accurate matches")],t=e[Math.floor(Math.random()*e.length)];this.activeTip=t||""},onKeydown(e){var t;if("/"!==(null==(t=e.key)?void 0:t.toLowerCase())||e.metaKey||e.ctrlKey||e.altKey)return;const s=e.target,a=s&&s.tagName?s.tagName.toUpperCase():"";s&&(s.isContentEditable||"INPUT"===a||"TEXTAREA"===a||"SELECT"===a)||(e.preventDefault(),this.open(),this.$nextTick(()=>{var e;return null==(e=this.$refs.inputRef)?void 0:e.focus()}))}}},[["render",function(e,t,s,n,o,m){const l=je("Icons"),c=je("Search"),p=a,_=je("Close"),u=i,d=je("NavigableList"),h=r;return Se(),De($e,null,[(Se(),He(Ge,{to:"#fcrm_admin_menu_search"},[Fe("button",{title:e.$t("Global Search"),onClick:t[0]||(t[0]=(...e)=>m.open&&m.open(...e)),type:"button",class:"fcrm_icon_btn fcrm_global_search","aria-label":e.$t("Global Search")},[Fe("span",Tt,[xe(l,{"icon-name":"search"})]),t[7]||(t[7]=Fe("span",{class:"slash-icon"}," / ",-1))],8,Et)])),xe(h,{modelValue:o.visible,"onUpdate:modelValue":t[6]||(t[6]=e=>o.visible=e),onOpened:m.focusInput,onClosed:m.onClosed,class:"fcrm_global_search_container","modal-class":"fcrm_global_search_modal","append-to-body":!0},{header:We(()=>[Fe("div",yt,[Fe("span",Ct,[xe(p,null,{default:We(()=>[xe(c)],void 0,!0),_:1})]),o.scopeTag?(Se(),De("div",wt,[Qe(Ke(o.scopeTag.title)+" ",1),Fe("span",{class:"icon",onClick:t[1]||(t[1]=e=>o.scopeTag=null)},[xe(p,null,{default:We(()=>[xe(_)],void 0,!0),_:1})])])):Oe("",!0),xe(u,{ref:"inputRef",modelValue:o.query,"onUpdate:modelValue":t[2]||(t[2]=e=>o.query=e),class:"mousetrap",placeholder:o.scopeTag?"Search "+o.scopeTag.title:"Type to search or / to filter",clearable:"",autofocus:"",onInput:m.onInput,onKeydown:[t[3]||(t[3]=Be(Ue(e=>m.focusList("down"),["prevent"]),["arrow-down"])),t[4]||(t[4]=Be(Ue(e=>m.focusList("up"),["prevent"]),["arrow-up"])),Be(Ue(m.selectFromKeyboard,["prevent"]),["enter"]),t[5]||(t[5]=Be(e=>""===o.query&&(o.scopeTag=null),["backspace"]))]},null,8,["modelValue","placeholder","onInput","onKeydown"])])]),default:We(()=>[m.isCommandMode?(Se(),De("div",Lt,[Fe("div",Mt,Ke(e.$t("Filter by")),1),m.filteredCommands.length?(Se(),De("ul",At,[(Se(!0),De($e,null,qe(m.filteredCommands,(t,s)=>(Se(),De("li",{key:t.key,class:Ne({selected:s===o.commandIndex}),onClick:e=>m.selectCommand(t),onMouseenter:e=>o.commandIndex=s},[Fe("span",It,"/"+Ke(t.title.toLowerCase()),1),Fe("span",Pt,Ke(e.$t("Search in %s",t.title)),1)],42,Rt))),128))])):(Se(),De("div",kt,Ke(e.$t("No matching filters")),1))])):o.query?(Se(),De("div",jt,[xe(d,{ref:"listRef",items:o.results,"no-results-text":o.query?e.$t("No matches found"):e.$t("Type to search"),"on-action":m.goToItem},{default:We(({item:s})=>[Fe("div",xt,["subscriber"===s._resultType||o.scopeTag&&"subscribers"===o.scopeTag.key?(Se(),De("div",Ft,[Fe("div",$t,[s.photo?(Se(),De("img",{key:0,class:"fcrm_global_search_subscriber_photo",src:s.photo,alt:s.full_name},null,8,qt)):(Se(),De("div",Bt,Ke((s.full_name||s.email||"?").charAt(0).toUpperCase()),1))]),Fe("div",Ut,[Fe("span",Nt,Ke(s.full_name||s.email),1),Fe("div",Zt,[Fe("span",Ht,Ke(s.email),1),Fe("span",Gt,Ke(m.resultTypeLabel(s)),1)])])])):"company"===s._resultType||o.scopeTag&&"companies"===o.scopeTag.key?(Se(),De("div",Wt,[Fe("div",Kt,[s.logo?(Se(),De("img",{key:0,class:"fcrm_global_search_subscriber_photo",src:s.logo,alt:s.name},null,8,Qt)):(Se(),De("div",zt,Ke((s.name||"?").charAt(0).toUpperCase()),1))]),Fe("div",Yt,[Fe("span",Jt,Ke(s.name),1),Fe("div",Xt,[Fe("span",es,Ke(m.resultTypeLabel(s)),1)])])])):"subscriber_note"===s._resultType||o.scopeTag&&"subscriber_notes"===o.scopeTag.key?(Se(),De("div",ts,[Fe("span",ss,Ke(s.title||e.$t("(Untitled)")),1),Fe("div",as,[Fe("span",is,Ke(s.subscriber_name),1),Fe("span",rs,Ke(m.resultTypeLabel(s)),1)])])):(Se(),De("div",ns,[Fe("span",os,Ke(s.title),1),Fe("div",ms,[s.status?(Se(),De("span",ls,"("+Ke(s.status)+")",1)):Oe("",!0),Fe("span",cs,Ke(m.resultTypeLabel(s)),1)])]))]),t[9]||(t[9]=Fe("div",{class:"action-icon"},[Fe("svg",{width:"6",height:"9",viewBox:"0 0 6 9",fill:"none",xmlns:"http://www.w3.org/2000/svg"},[Fe("path",{d:"M3.34125 4.2957L0 0.95445L0.95445 0L5.25015 4.2957L0.95445 8.5914L0 7.63695L3.34125 4.2957Z",fill:"var(--fc-secondary-text)"})])],-1))]),_:1},8,["items","no-results-text","on-action"])])):(Se(),De($e,{key:1},[Fe("div",St,[Fe("div",Dt,Ke(e.$t("Searching for")),1),Fe("div",Vt,[(Se(!0),De($e,null,qe(m.scopeTags,e=>(Se(),De("div",{key:e.key,class:Ne(["fcrm_global_searching_suggestion",{active:o.scopeTag&&o.scopeTag.key===e.key}]),onClick:t=>o.scopeTag=e},Ke(e.title),11,Ot))),128))])]),t[8]||(t[8]=Fe("div",{class:"fcrm_global_search_no_result"},[Fe("span",{class:"icon"},[Fe("svg",{viewBox:"0 0 32 32",fill:"none",xmlns:"http://www.w3.org/2000/svg"},[Fe("path",{d:"M14.8 4C20.7616 4 25.6 8.8384 25.6 14.8C25.6 20.7616 20.7616 25.6 14.8 25.6C8.8384 25.6 4 20.7616 4 14.8C4 8.8384 8.8384 4 14.8 4ZM14.8 23.2C19.4404 23.2 23.2 19.4404 23.2 14.8C23.2 10.1584 19.4404 6.4 14.8 6.4C10.1584 6.4 6.4 10.1584 6.4 14.8C6.4 19.4404 10.1584 23.2 14.8 23.2ZM24.982 23.2852L28.3768 26.6788L26.6788 28.3768L23.2852 24.982L24.982 23.2852Z",fill:"var(--fc-text-muted)"})])]),Fe("p",null,"Search in your [CRM]")],-1))],64)),Fe("div",ps,[o.activeTip?(Se(),De("div",_s,[Fe("span",us,Ke(e.$t("Tip"))+":",1),Fe("span",ds,Ke(o.activeTip),1)])):Oe("",!0),Fe("ul",hs,[Fe("li",null,[t[10]||(t[10]=Fe("span",{class:"fcrm_command_keys"},[Fe("span",{class:"fcrm_command_key"},"↑"),Fe("span",{class:"fcrm_command_key"},"↓")],-1)),Fe("span",gs,Ke(e.$t("Navigate")),1)]),Fe("li",null,[t[11]||(t[11]=Fe("span",{class:"fcrm_command_key"},"↵",-1)),Fe("span",fs,Ke(e.$t("Select")),1)]),Fe("li",null,[t[12]||(t[12]=Fe("span",{class:"fcrm_command_key"},"/",-1)),Fe("span",vs,Ke(e.$t("Filter")),1)])])])],void 0),_:1},8,["modelValue","onOpened","onClosed"])],64)}]]);const Es=at({name:"RouteLoadingBar",data:()=>({loading:!1,finishing:!1,showTimer:null,hideTimer:null}),mounted(){this.$bus.on("route-loading-start",this.start),this.$bus.on("route-loading-finish",this.finish)},beforeUnmount(){this.$bus.off("route-loading-start",this.start),this.$bus.off("route-loading-finish",this.finish),clearTimeout(this.showTimer),clearTimeout(this.hideTimer)},methods:{start(){clearTimeout(this.hideTimer),clearTimeout(this.showTimer),this.finishing=!1,this.loading||(this.showTimer=setTimeout(()=>{this.loading=!0},150))},finish(){clearTimeout(this.showTimer),clearTimeout(this.hideTimer),this.loading&&(this.finishing=!0,this.hideTimer=setTimeout(()=>{this.loading=!1,this.finishing=!1},500))}}},[["render",function(e,t,s,a,i,r){return i.loading?(Se(),De("div",{key:0,class:Ne(["fcrm-route-loading-bar",{"is-finishing":i.finishing}])},null,2)):Oe("",!0)}]]),Ts=["aria-label"],ys={key:0,xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24","stroke-width":"1.5",stroke:"currentColor","aria-hidden":"true"},Cs={key:1,xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24","stroke-width":"1.5",stroke:"currentColor","aria-hidden":"true"},ws={class:"fcrm_theme_mode--item"},Ls={key:0,class:"fcrm_theme_mode--check",xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24","stroke-width":"2",stroke:"currentColor","aria-hidden":"true"},Ms={class:"fcrm_theme_mode--item"},As={key:0,class:"fcrm_theme_mode--check",xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24","stroke-width":"2",stroke:"currentColor","aria-hidden":"true"},Rs={class:"fcrm_theme_mode--item"},Is={key:0,class:"fcrm_theme_mode--check",xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24","stroke-width":"2",stroke:"currentColor","aria-hidden":"true"};const Ps={class:"dialog-footer"};const ks=at({name:"Application",components:{ThemeMode:at({name:"ThemeMode",data:()=>({theme_mode:rt.getCurrentTheme()}),computed:{resolvedTheme(){return"system"===this.theme_mode?rt.getSystemTheme():this.theme_mode}},mounted(){this.onThemeChanged=()=>{this.theme_mode=rt.getCurrentTheme()},window.addEventListener(nt,this.onThemeChanged)},beforeUnmount(){this.onThemeChanged&&window.removeEventListener(nt,this.onThemeChanged)},methods:{selectTheme(e){this.theme_mode!==e&&(this.theme_mode=e,rt.apply(e))}}},[["render",function(e,t,s,a,i,r){const l=o,c=n,p=m;return Se(),He(Ge,{to:"#fcrm_theme"},[xe(p,{class:"fcrm_theme_mode",trigger:"click",onCommand:r.selectTheme,"popper-class":"fcrm_theme_mode--popper"},{dropdown:We(()=>[xe(c,null,{default:We(()=>[xe(l,{command:"light",class:Ne({"fcrm_theme_mode--active":"light"===i.theme_mode})},{default:We(()=>[Fe("div",ws,[t[3]||(t[3]=Fe("svg",{xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24","stroke-width":"1.5",stroke:"currentColor","aria-hidden":"true"},[Fe("path",{"stroke-linecap":"round","stroke-linejoin":"round",d:"M12 3v2.25m6.364.386-1.591 1.591M21 12h-2.25m-.386 6.364-1.591-1.591M12 18.75V21m-4.773-4.227-1.591 1.591M5.25 12H3m4.227-4.773L5.636 5.636M15.75 12a3.75 3.75 0 1 1-7.5 0 3.75 3.75 0 0 1 7.5 0Z"})],-1)),Fe("span",null,Ke(e.$t("Light")),1),"light"===i.theme_mode?(Se(),De("svg",Ls,[...t[2]||(t[2]=[Fe("path",{"stroke-linecap":"round","stroke-linejoin":"round",d:"m4.5 12.75 6 6 9-13.5"},null,-1)])])):Oe("",!0)])],void 0,!0),_:1},8,["class"]),xe(l,{command:"dark",class:Ne({"fcrm_theme_mode--active":"dark"===i.theme_mode})},{default:We(()=>[Fe("div",Ms,[t[5]||(t[5]=Fe("svg",{xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24","stroke-width":"1.5",stroke:"currentColor","aria-hidden":"true"},[Fe("path",{"stroke-linecap":"round","stroke-linejoin":"round",d:"M21.752 15.002A9.72 9.72 0 0 1 18 15.75c-5.385 0-9.75-4.365-9.75-9.75 0-1.33.266-2.597.748-3.752A9.753 9.753 0 0 0 3 11.25C3 16.635 7.365 21 12.75 21a9.753 9.753 0 0 0 9.002-5.998Z"})],-1)),Fe("span",null,Ke(e.$t("Dark")),1),"dark"===i.theme_mode?(Se(),De("svg",As,[...t[4]||(t[4]=[Fe("path",{"stroke-linecap":"round","stroke-linejoin":"round",d:"m4.5 12.75 6 6 9-13.5"},null,-1)])])):Oe("",!0)])],void 0,!0),_:1},8,["class"]),xe(l,{command:"system",class:Ne({"fcrm_theme_mode--active":"system"===i.theme_mode})},{default:We(()=>[Fe("div",Rs,[t[7]||(t[7]=Fe("svg",{xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24","stroke-width":"1.5",stroke:"currentColor","aria-hidden":"true"},[Fe("path",{"stroke-linecap":"round","stroke-linejoin":"round",d:"M10.5 6h9.75M10.5 6a1.5 1.5 0 1 1-3 0m3 0a1.5 1.5 0 1 0-3 0M3.75 6H7.5m3 12h9.75m-9.75 0a1.5 1.5 0 0 1-3 0m3 0a1.5 1.5 0 0 0-3 0m-3.75 0H7.5m9-6h3.75m-3.75 0a1.5 1.5 0 0 1-3 0m3 0a1.5 1.5 0 0 0-3 0m-9.75 0h9.75"})],-1)),Fe("span",null,Ke(e.$t("System")),1),"system"===i.theme_mode?(Se(),De("svg",Is,[...t[6]||(t[6]=[Fe("path",{"stroke-linecap":"round","stroke-linejoin":"round",d:"m4.5 12.75 6 6 9-13.5"},null,-1)])])):Oe("",!0)])],void 0,!0),_:1},8,["class"])],void 0,!0),_:1})]),default:We(()=>[Fe("button",{class:"fcrm_theme_mode--trigger","aria-label":e.$t("Theme mode")},["dark"===r.resolvedTheme?(Se(),De("svg",ys,[...t[0]||(t[0]=[Fe("path",{"stroke-linecap":"round","stroke-linejoin":"round",d:"M21.752 15.002A9.72 9.72 0 0 1 18 15.75c-5.385 0-9.75-4.365-9.75-9.75 0-1.33.266-2.597.748-3.752A9.753 9.753 0 0 0 3 11.25C3 16.635 7.365 21 12.75 21a9.753 9.753 0 0 0 9.002-5.998Z"},null,-1)])])):(Se(),De("svg",Cs,[...t[1]||(t[1]=[Fe("path",{"stroke-linecap":"round","stroke-linejoin":"round",d:"M12 3v2.25m6.364.386-1.591 1.591M21 12h-2.25m-.386 6.364-1.591-1.591M12 18.75V21m-4.773-4.227-1.591 1.591M5.25 12H3m4.227-4.773L5.636 5.636M15.75 12a3.75 3.75 0 1 1-7.5 0 3.75 3.75 0 0 1 7.5 0Z"},null,-1)])]))],8,Ts)],void 0),_:1},8,["onCommand"])])}]]),GlobalSearch:bs,RouteLoadingBar:Es},data:()=>({last_path:"",errorMessage:"",showErrorModal:!1,routeLoading:!1,routeLoadTimer:null}),methods:{verifyLicense(){this.$get("campaign-pro-settings/license",{verify:!0}).then(e=>{}).catch(e=>{}).finally(()=>{})},pingToServer(){this.$get("reports/ping")},onRouteLoadStart(){clearTimeout(this.routeLoadTimer),this.routeLoadTimer=setTimeout(()=>{this.routeLoading=!0},150)},onRouteLoadFinish(){clearTimeout(this.routeLoadTimer),this.routeLoading=!1}},computed:{bodyClasses(){const e=[];return this.$route&&this.$route.path.includes("/settings")&&"recurring_campaign_settings"!==this.$route.name&&e.push("is-settings-page"),e}},beforeUnmount(){this.$bus.off("route-loading-start",this.onRouteLoadStart),this.$bus.off("route-loading-finish",this.onRouteLoadFinish),clearTimeout(this.routeLoadTimer)},mounted(){jQuery(".update-nag,.notice, #wpbody-content > .updated, #wpbody-content > .error, .tutor-user-registration-notice-wrapper").not(".fc_notice").remove(),window.fcAdmin.require_verify_request&&this.verifyLicense(),this.$bus.on("renew_options",e=>{this.renewOptions(e)}),setInterval(()=>{this.pingToServer()},5e4),this.$bus.on("route-loading-start",this.onRouteLoadStart),this.$bus.on("route-loading-finish",this.onRouteLoadFinish),this.$bus.on("show-error-modal",e=>{e.responseText&&(this.errorMessage=e.responseText,this.showErrorModal=!0)})}},[["render",function(e,t,s,a,n,o){const m=je("RouteLoadingBar"),c=je("GlobalSearch"),p=je("ThemeMode"),_=je("router-view"),u=i,d=l,h=r;return Se(),De($e,null,[xe(m),Fe("div",{class:Ne(["fluentcrm-app",o.bodyClasses])},[xe(c),xe(p),Fe("div",{class:Ne(["fluentcrm-body",{"is-route-loading":n.routeLoading}])},[xe(_,{key:"main_route"})],2),xe(h,{modelValue:n.showErrorModal,"onUpdate:modelValue":t[1]||(t[1]=e=>n.showErrorModal=e),title:"Server Response (Error)","append-to-body":!0,"close-on-click-modal":!1,width:"50%","show-close":!1},{footer:We(()=>[Fe("div",Ps,[xe(d,{onClick:t[0]||(t[0]=e=>n.showErrorModal=!1)},{default:We(()=>[...t[3]||(t[3]=[Qe("Close",-1)])],void 0,!0),_:1})])]),default:We(()=>[Fe("div",null,[xe(u,{type:"textarea",rows:15,value:n.errorMessage,readonly:""},null,8,["value"]),t[2]||(t[2]=Fe("p",null,"FluentCRM is expecting JSON data but HTML returned",-1))])],void 0),_:1},8,["modelValue"])],2)],64)}]]);const Ss={AlarmClock:Pe,ArrowDown:Ie,ArrowDownBold:Re,ArrowLeft:Ae,ArrowRight:Me,ArrowRightBold:Le,ArrowUp:we,Back:Ce,Bottom:ye,Calendar:Te,CaretBottom:Ee,Check:be,CircleCheck:ve,CircleCheckFilled:fe,CircleClose:ge,CircleCloseFilled:he,CirclePlus:de,Close:s,CollectionTag:ue,CopyDocument:_e,Cpu:pe,DArrowLeft:ce,DArrowRight:le,DataAnalysis:me,DataLine:oe,Delete:ne,Document:re,DocumentCopy:ie,Download:ae,Edit:se,EditPen:te,Files:ee,Filter:X,Finished:J,Folder:Y,FolderOpened:z,FullScreen:Q,Hide:K,HomeFilled:W,InfoFilled:G,Link:H,Loading:Z,Location:N,Menu:U,Message:B,Money:q,More:$,MoreFilled:F,OfficeBuilding:x,Operation:j,Picture:O,Plus:V,Position:D,PriceTag:S,QuestionFilled:k,Refresh:P,RefreshRight:I,Right:R,Search:A,Select:M,Setting:L,Share:w,Sort:C,SuccessFilled:y,Tickets:T,Tools:E,TopRight:b,Upload:v,UploadFilled:f,User:g,VideoCamera:h,VideoPlay:d,View:u,WarningFilled:_},Ds=new Date;!function({Application:e,routes:s,profileRoute:a=null,companyProfileRoute:i=null,preload:r=null,mountSelector:n="#fluentcrm_app",routeFilterHook:o="fluentcrm_global_routes"}){if(window.__FLUENTCRM_MOUNT_STARTED)return void console.warn("FluentCRM: duplicate mountFluentCrmApp() call blocked. The app is already mounted.");window.__FLUENTCRM_MOUNT_STARTED=!0;const m=Xe(e),l=et(),_=function(){let e="50%",t="60%";return window.innerWidth<600?(e="90%",t="90%"):window.innerWidth<800&&(t="80%",e="80%"),{drawerWidth:e,modalWidth:t}}();m.config.errorHandler=(e,t,s)=>{var a,i;const r=t&&((null==(a=t.$options)?void 0:a.name)||(null==(i=t.type)?void 0:i.name))||"";console.error("FluentCRM Vue error in "+r+" ("+s+"):",e)},m.config.warnHandler=(e,t,s)=>{var a,i;const r=t&&((null==(a=t.$options)?void 0:a.name)||(null==(i=t.type)?void 0:i.name))||"";console.warn("FluentCRM Vue warn in "+r+": "+e,s)};for(const[t,c]of Object.entries(Ss))m.component(t,c);function u(e){m.use(e.eventBus),function(e,t){e.config.globalProperties.$notify=ot,e.config.globalProperties.$sanitize=e=>Je.sanitize(e||""),e.config.globalProperties.$message=c,e.config.globalProperties.$confirm=p.confirm,e.config.globalProperties.$prompt=p.prompt,e.config.globalProperties.$messageBox=p,e.config.globalProperties.currentTimeZoneName=window.dayjs.tz.guess(),e.config.globalProperties.$rest=t.Rest,e.config.globalProperties.$get=t.Rest.get,e.config.globalProperties.$post=t.Rest.post,e.config.globalProperties.$del=t.Rest.del,e.config.globalProperties.$put=t.Rest.put,e.config.globalProperties.$patch=t.Rest.patch}(m,e),m.mixin(function({bootData:e,responsiveSizes:s,appStartTime:a}){const i=window.fcAdmin;return{data:()=>({isMobile:window.innerWidth<=768,storage:e.Storage,permissions:i.auth.permissions,has_campaign_pro:i.addons&&i.addons.fluentcampaign,has_company_module:i.addons&&i.addons.company_module,globalDrawerSize:s.drawerWidth,appStartTime:a}),watch:{$route(e,t){e.meta.active_menu&&jQuery(document).trigger("fluentcrm_route_change",e.meta.active_menu);let s=e.meta.side_path;s="/"==s?"":"#"+s,document.dispatchEvent(new CustomEvent("fc_route_changed",{detail:{route_to:e,route_from:t,path:s}})),window.fcrm_last_path!=s&&(window.fcrm_last_path=s,jQuery("li#toplevel_page_fluentcrm-admin ul.wp-submenu li").removeClass("current"),jQuery('li#toplevel_page_fluentcrm-admin ul.wp-submenu a[href="admin.php?page=fluentcrm-admin'+s+'"]').parent().addClass("current"))}},methods:{each:Ye,isEmptyValue:ze,addFilter:e.addFilter,applyFilters:e.applyFilters,doAction:e.doAction,addAction:e.addAction,removeAllActions:e.removeAllActions,changeTitle(e){document.title=e+" - FluentCRM"},renewOptions(e){e+="s",this.$get("reports/options",{fields:e}).then(t=>{this.appVars["available_"+e]=t.options[e]})},renewOptionCache(e,t){const s={fields:e};this.$get("reports/options",s).then(s=>{window.fc_options_cache||(window.fc_options_cache={}),s.options[e]&&(window.fc_options_cache[e]=s.options[e],t&&t(s.options[e]))})},currentDateTime(e="YYYY-MM-DD h:mma"){const t=new Date-a;return window.dayjs(window.fcAdmin.server_time).add(t,"milliseconds").format(e)},logConsole(e){console.log(e)},ucFirst:t.ucFirst,ucWords:t.ucWords,slugify:t.slugify,convertToText:t.convertToText,$t:t.$t,trans:t.trans,$_n:t.$_n,percent:t.percent,nsDateFormat:t.nsDateFormat,smartDate:t.smartDate,humanDiffTime:t.humanDiffTime,hasPermission:t.hasPermission,nsHumanDiffTime:t.humanDiffTime,$nsHumanDiffTime:t.humanDiffTime,formatMoney:t.formatMoney,doNothing(){},handleError(e){if(!e)return;let t="";t="string"==typeof e?e:e&&e.message?e.message:this.convertToText(e),t||(t="Something is wrong!"),this.$notify({type:"error",title:"Error",message:this.$sanitize(t),dangerouslyUseHTMLString:!0})},$handleError(e){this.handleError(e)},unmountBlockEditor(){const e=document.getElementById("fluentcrm_block_editor_x");e&&window.wp.element.unmountComponentAtNode(e)}},mounted(){window.fcrm_mounted||(this.isMobile&&document.body.classList.add("frm-is-mobile"),window.fcrm_mounted=!0,this.$route&&this.$route.meta.active_menu&&(jQuery(document).trigger("fluentcrm_route_change",this.$route.meta.active_menu),document.dispatchEvent(new CustomEvent("fc_route_changed",{detail:{route_to:this.$route}}))))}}}({bootData:e,responsiveSizes:_,appStartTime:Ds}));const u=[...s];a&&u.push(e.applyFilters("fluentcrm_profile_routes",a)),i&&u.push(e.applyFilters("fluentcrm_company_profile_routes",i));const d=tt({history:st(),routes:e.applyFilters(o,u),scrollBehavior:(e,t,s)=>!e.meta.no_scroll&&(s||(e.hash&&document.querySelector(e.hash)?{el:e.hash}:{left:0,top:0}))}),h=m.config.globalProperties.$bus;d.beforeEach((e,s,a)=>{e.path!==s.path&&h.emit("route-loading-start"),!e.meta.permission||t.hasPermission(e.meta.permission)?a():a({name:"no_permission",query:{permission:e.meta.permission}})}),d.afterEach(()=>{h.emit("route-loading-finish")}),d.onError(()=>{h.emit("route-loading-finish")}),m.use(l),window.FLUENTCRM.app=m;try{window.FLUENTCRM.instance=m.use(d).mount(n)}catch(g){throw console.error("FluentCRM: failed to mount admin app at "+n+" (container exists: "+!!document.querySelector(n)+")",g),g}if("function"==typeof r){const e=()=>r();window.requestIdleCallback?requestIdleCallback(e,{timeout:3e3}):setTimeout(e,1)}}m.config.globalProperties.appVars=window.fcAdmin,m.config.globalProperties.drawerWidth=_.drawerWidth,m.config.globalProperties.modalWidth=_.modalWidth,m.directive("loading",ke.directive),window.FLUENTCRM?u(window.FLUENTCRM):document.addEventListener("fluentCRMBootReady",function(){u(window.FLUENTCRM)})}({Application:ks,routes:mt,profileRoute:lt,companyProfileRoute:ct,preload:()=>{e(()=>import("../v3app/src/Modules/Contacts/Contacts.js?ver=3.1.8"),[],import.meta.url).catch(()=>{}),e(()=>import("./Modules/Email/Campaigns/Campaigns.js?ver=3.1.8"),[],import.meta.url).catch(()=>{}),e(()=>import("./Modules/Funnels/Funnels.js?ver=3.1.8"),[],import.meta.url).catch(()=>{}),e(()=>import("../v3app/src/Modules/Contacts/ContactGroups.js?ver=3.1.8"),[],import.meta.url).catch(()=>{});const t=window.fcAdmin&&window.fcAdmin.crm_editor_frame;if(t&&!document.querySelector("iframe[data-fcrm-editor-warm]")){const e=document.createElement("iframe");e.src=t+"&bid=0&block_type=campaign&disable_autosave=1",e.setAttribute("data-fcrm-editor-warm","1"),e.setAttribute("aria-hidden","true"),e.setAttribute("tabindex","-1"),e.style.cssText="position:absolute;left:-9999px;top:0;width:1px;height:1px;border:0;visibility:hidden;",document.body.appendChild(e),e.addEventListener("load",()=>{setTimeout(()=>e.remove(),8e3)})}}}); diff --git a/wp-content/plugins/fluent-crm/assets/admin/boot.js b/wp-content/plugins/fluent-crm/assets/admin/boot.js new file mode 100644 index 0000000..be0eb83 --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/admin/boot.js @@ -0,0 +1 @@ +import{e,S as t,R as s}from"../fc-bits.js?ver=3.1.8";import{aE as a,bI as d,bJ as n,bK as o,bL as i,bM as b,bN as r,bO as w,bP as l,bQ as m,bR as v}from"../vendor.js?ver=3.1.8";a.extend(d),a.extend(n),a.extend(o),a.extend(i),a.extend(b),window.dayjs=a,window.FLUENTCRM={Rest:s,Storage:t,eventBus:e,addFilter:v,addAction:m,applyFilters:l,doAction:w,removeAllActions:r},document.dispatchEvent(new Event("fluentCRMBootReady")),delete window._wpemojiSettings; diff --git a/wp-content/plugins/fluent-crm/assets/admin/css/admin_rtl.css b/wp-content/plugins/fluent-crm/assets/admin/css/admin_rtl.css new file mode 100644 index 0000000..a7e39e0 --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/admin/css/admin_rtl.css @@ -0,0 +1 @@ +@charset "UTF-8";html[dir=rtl] .fluentcrm-app{margin-right:0;margin-left:20px}html[dir=rtl] .el-table-fixed-column--right.el-table__cell{right:auto!important;left:0;text-align:left}@layer element-plus{html[dir=rtl] .el-input__prefix-inner>:last-child{margin-right:0;margin-left:8px}html[dir=rtl] .el-switch__label--right,html[dir=rtl] .el-radio__label,html[dir=rtl] .el-checkbox__label{padding-left:0;padding-right:8px}html[dir=rtl] .el-form-item--label-top .el-form-item__label,html[dir=rtl] .el-input__inner,html[dir=rtl] .el-select__wrapper,html[dir=rtl] .el-table .el-table__cell{text-align:right}html[dir=rtl] .el-tabs__nav{float:right}html[dir=rtl] .el-dialog__headerbtn{right:auto;left:0}html[dir=rtl] .el-breadcrumb__item{float:none}html[dir=rtl] .el-checkbox{margin-right:0}html[dir=rtl] .el-input-group__append,html[dir=rtl] .el-input-group__prepend{border-radius:4px 0 0 4px}html[dir=rtl] .el-input-group__append{box-shadow:none;border:1px solid #c0c4cc;border-right:none}html[dir=rtl] .el-input-group--append>.el-input__wrapper{border-radius:0 4px 4px 0;box-shadow:none;border:1px solid #c0c4cc}html[dir=rtl] .el-notification{padding-right:13px;padding-left:26px}html[dir=rtl] .el-notification .el-notification__closeBtn{right:auto;left:15px}html[dir=rtl] .el-notification .el-notification__content{text-align:right!important}html[dir=rtl] .el-picker-panel [slot=sidebar]+.el-picker-panel__body,html[dir=rtl] .el-picker-panel__sidebar+.el-picker-panel__body{margin-left:0!important;margin-right:160px}html[dir=rtl] .el-tag .el-tag__close{margin-left:0;margin-right:6px}html[dir=rtl] .el-select-dropdown.is-multiple .el-select-dropdown__item.is-selected:after{right:auto;left:20px}html[dir=rtl] .el-timeline-item__node--normal{left:auto;right:-1px}html[dir=rtl] .el-timeline-item__tail{left:auto;right:4px}html[dir=rtl] .el-timeline-item__wrapper{padding-left:0;padding-right:28px}html[dir=rtl] .el-picker-panel__shortcut{text-align:right}html[dir=rtl] .el-alert .el-alert__icon{margin-right:0;margin-left:8px}html[dir=rtl] .el-input__suffix-inner>:first-child{margin-left:0;margin-right:8px}html[dir=rtl] .el-cascader-node{padding-left:30px;padding-right:20px}html[dir=rtl] .el-cascader-node__label{text-align:right}html[dir=rtl] .el-cascader-node__postfix{right:auto;left:10px;transform:rotateY(180deg)}}html[dir=rtl] .el-popover.fc-funnel-actions-popover .fc_funnel_acton_field .el-button,html[dir=rtl] .el-popover{text-align:right}html[dir=rtl] .el-popper .el-select-dropdown__list .el-select-dropdown__item:after,html[dir=rtl] .el-popper .el-select-dropdown__list .el-dropdown-menu__item:after,html[dir=rtl] .el-popper .el-dropdown-menu .el-select-dropdown__item:after,html[dir=rtl] .el-popper .el-dropdown-menu .el-dropdown-menu__item:after{right:auto;left:10px}html[dir=rtl] .el-popper.el-picker__popper .el-picker-panel__body-wrapper .el-picker-panel__sidebar{border-right:none;border-left:1px solid var(--fc-primary-border)}html[dir=rtl] .el-radio-group .el-radio-button .el-radio-button__inner{border-color:var(--fc-primary-border)}html[dir=rtl] .el-radio-group .el-radio-button:first-child .el-radio-button__inner{border-radius:0 8px 8px 0}html[dir=rtl] .el-radio-group .el-radio-button:last-child .el-radio-button__inner{border-radius:8px 0 0 8px;border-left:1px solid var(--fc-primary-border)}html[dir=rtl] .el-radio-group.fcrm_global_radio_group .el-radio-button .el-radio-button__inner{border-radius:8px}html[dir=rtl] .fcrm_email_campaign_recipient_tagger_selector .el-radio-group .el-radio-button .el-radio-button__inner{border-radius:0;border-bottom-color:transparent}html[dir=rtl] .fcrm_email_campaign_recipient_tagger_selector .el-radio-group .el-radio-button:last-child .el-radio-button__inner{border-left:none}html[dir=rtl] .el-overlay.fcrm_import_template_modal .el-radio-button__inner{border:none;border-bottom:2px solid transparent}html[dir=rtl] .el-overlay.fcrm_import_template_modal .el-radio-button:last-child .el-radio-button__inner{border:none;border-bottom:2px solid transparent}html[dir=rtl] .fcrm_edit_email_sequence_schedule--datetime .fcrm_edit_email_sequence_schedule--delay .el-input .el-input__wrapper{padding-left:0;padding-right:10px}html[dir=rtl] .fcrm_edit_email_sequence_schedule--datetime .fcrm_edit_email_sequence_schedule--delay .el-input.el-input--suffix .el-input__suffix .el-select__wrapper{border-left:none;border-right:1px solid var(--fc-primary-border);border-radius:var(--fcrm-border-radius-8) 0 0 var(--fcrm-border-radius-8)!important}html[dir=rtl] .fluentcrm-campaign .fcrm_inline_title_actions{border-left:none;border-right:1px solid var(--fc-primary-border);padding-left:0;margin-left:0;padding-right:8px;margin-right:8px}html[dir=rtl] .fc_bulk_campaign_actions_right{margin-left:0;margin-right:auto}html[dir=rtl] .el-form .el-form-item .el-form-item__content .el-radio-group .el-radio{margin-right:0;margin-left:8px}html[dir=rtl] .fcrm-radio-group .el-radio .el-radio__label{padding-right:0}html[dir=rtl] .fcrm_topbar{left:0;right:auto;margin-right:-17px;margin-left:0}html[dir=rtl] .fcrm_topbar_left{margin-left:8px;margin-right:0}html[dir=rtl] .fcrm_topbar_right{margin-right:auto;margin-left:0}html[dir=rtl] .fcrm_topbar_left a span{right:34px;left:auto;margin-right:6px;margin-left:0}html[dir=rtl] .fcrm_icon_menu .fcrm_submenu_items,html[dir=rtl] ul.fcrm_menu li .fcrm_submenu_items{left:0;right:auto}html[dir=rtl] .fcrm_icon_menu .fcrm_submenu_items.fcrm_2_col_menu,html[dir=rtl] ul.fcrm_menu li .fcrm_submenu_items.fcrm_2_col_menu{left:-235px;right:auto}html[dir=rtl] span.fc_li_value{float:left}html[dir=rtl] .el-dropdown-menu{right:auto}html[dir=rtl] .wp_vue_editor_wrapper .popover-wrapper{right:120px!important}html[dir=rtl] .el-table__header-wrapper .el-table__header thead tr th:first-child .cell{padding-right:20px;padding-left:12px}html[dir=rtl] .el-table__body-wrapper .el-table__body tbody tr td .fcrm_table_row_expand{padding-left:0;padding-right:70px}html[dir=rtl] .el-table__body-wrapper .el-table__body tbody tr td.el-table__expanded-cell{padding-left:24px;padding-right:70px}html[dir=rtl] .el-table__body-wrapper .el-table__body tbody tr td .fcrm_individual_progress .el-timeline-item__node.el-timeline-item__node--large{left:auto;right:-1px}html[dir=rtl] .el-table__body-wrapper .el-table__body tbody tr td .fcrm_table_body_actions{justify-content:flex-end}html[dir=rtl] .el-table__body-wrapper .el-table__body tbody tr td:first-child .cell{padding-right:20px;padding-left:12px}html[dir=rtl] .el-table .el-table__expand-icon:not(.el-table__expand-icon--expanded):before{content:"◀"}html[dir=rtl] .fcrm_quick_stats li:after{right:auto;left:-12px}html[dir=rtl] .fc_rich_container .fc_rich_filter .fc_rich_filters .fc_filter_intro>button{margin-left:5px;margin-top:1px;padding:8px 9px}html[dir=rtl] .fc_block_type_conditional .fluentcrm_blockin{max-width:420px}html[dir=rtl] span.fc_b_no_node{left:auto;right:0}html[dir=rtl] span.fc_b_yes_node{right:auto;left:0}html[dir=rtl] .fc_condition_node_point span{left:auto;right:-18px}html[dir=rtl] .fc_condition_node_point.fc_dom_path_right span{right:auto;left:-18px}html[dir=rtl] .fc_dom_path.fc_condition_node_point.fc_dom_path_right{border-color:var(--fc-primary-text);border-width:2px 0 0 2px!important;border-left-style:solid!important;border-top-right-radius:0!important;border-top-left-radius:20px;transform:translate(-69px);padding:8px 20px 0 0!important;margin-left:0!important;margin-right:-2px}html[dir=rtl] .fc_dom_path.fc_condition_node_point.fc_dom_path_left{border-width:2px 2px 0 0!important;border-right-style:solid!important;border-top-left-radius:0!important;border-top-right-radius:20px!important;transform:translate(69px);padding:8px 0 0 20px!important}html[dir=rtl] .fc_dom_path.fc_ab_test.fc_ab_test_b.fc_dom_path_right{border-right-style:initial!important;border-left-style:solid!important;border-top-right-radius:0!important;border-top-left-radius:20px!important;transform:translate(-69px);padding:8px 20px 0 0!important;margin-left:0!important;margin-right:-2px}html[dir=rtl] .fc_dom_path.fc_ab_test.fc_ab_test_a.fc_dom_path_left{border-left-style:initial!important;border-right-style:solid!important;border-top-left-radius:0!important;border-top-right-radius:20px!important;transform:translate(69px);padding:8px 0 0 20px!important}html[dir=rtl] .fc_dom_path.fc_ab_test.fc_dom_path_right span{right:auto;left:-50%}html[dir=rtl] .fc_dom_path.fc_ab_test.fc_dom_path_left span{left:auto;right:-50%}html[dir=rtl] .fcrm_menu_item .fcrm_menu_icon{left:auto;right:0}html[dir=rtl] .fcrm_menu_item .fcrm_menu_card.fcrm_menu_card_with_icon{padding-left:0;padding-right:28px}html[dir=rtl] .fcrm_review_card .el-button.close_card{left:10px;right:auto}html[dir=rtl] .el-notification.left{top:auto!important;right:auto!important;left:20px!important;bottom:20px!important}html[dir=rtl] .fcrm_page_header_top_nav_wrapper{margin-right:-20px}html[dir=rtl] .fcrm_rich_container .fcrm_rich_wrap .fcrm_rich_filter .fcrm_filter_group_header .fcrm_rich_filters .fcrm_table tbody tr td:last-child{text-align:left!important}html[dir=rtl] .fluentcrm_profile-photo{margin-right:0;margin-left:20px}html[dir=rtl] .fcrm_contact_companies .fcrm_company_card_actions{right:auto;left:12px}html[dir=rtl] .fcrm_active_filters_bar .fcrm_filter_group_clear{border-left:none;border-right:1px solid var(--fc-primary-border)}html[dir=rtl] .fcrm_active_filters_bar .fcrm_filter_group_label{border-right:none;border-left:1px solid var(--fc-primary-border)}html[dir=rtl] .fcrm_profile_wrapper .fcrm_profile_container .fcrm_profile_sidebar .fc_sidebar_card .fcrm_commerce_purchase_history_widget .fc_full_listed li .fcrm_product_badges .fcrm_product_badge:before{left:auto;right:-8px}html[dir=rtl] .fcrm_profile_emails_table_header_actions .el-switch .el-switch__label,html[dir=rtl] .fcrm_purchase_history_table_header_actions .el-switch .el-switch__label{margin-left:0;margin-right:8px}html[dir=rtl] .el-popover.fcrm_sort_popover .fcrm_sorting_action_wrap .el-radio-group .el-radio .el-radio__label{padding-left:0;padding-right:8px}html[dir=rtl] .fcrm_subscriber_growth_card .fcrm_base_card_header .el-date-editor .el-range__close-icon{right:auto;left:5px}html[dir=rtl] .fc_input_popover_wrapper .el-input .el-input__wrapper{padding:0 10px 0 0!important}html[dir=rtl] .fc_input_popover_wrapper .el-input .el-input__suffix{border-radius:8px 0 0 8px;border-left:none;border-right:1px solid var(--stroke-soft-200, #E1E4EA)}html[dir=rtl] .fcrm-smartcodes-popover .el_pop_data_group .el_pop_data_body ul li{text-align:right}html[dir=rtl] .fcrm_profile_sms_wrapper .fcrm_profile_sms_layout_stats_panel{border-right:none;border-left:1px solid var(--stroke-soft-200, #E1E4EA)}html[dir=rtl] .fcrm_profile_emails_table_body .el-table .el-table__body tr td:last-child,html[dir=rtl] .fcrm_purchase_history_table_body .el-table .el-table__body tr td:last-child{text-align:left}html[dir=rtl] .fcrm_object_notes_template .fcrm_notes_search_bar{margin-left:0;margin-right:auto}html[dir=rtl] .fcrm_profile_wrapper .fcrm_profile_container .fcrm_profile_sidebar{margin-right:0;margin-left:-20px}html[dir=rtl] .fcrm-input-popover .fcrm-input-with-button .fcrm-input-button{border-left:none;border-right:1px solid #e1e4ea}html[dir=rtl] .fcrm_company_social_input_icon{border-right:none;border-left:1px solid #e1e4ea}html[dir=rtl] .fcrm_table_header_inner_left .el-input__prefix .icon{margin:0 0 0 6px}html[dir=rtl] .fcrm_perf_outside_label{left:auto;right:0}html[dir=rtl] .fcrm_trigger_selection_wrapper .el-form .fcrm_trigger_selection_sidebar{border-right:none;border-left:1px solid var(--stroke-soft-200, #E1E4EA)}html[dir=rtl] .fcrm_trigger_selection_item{padding-right:8px;padding-left:70px}html[dir=rtl] .fluentcrm_blockin:hover .fc_action_abs_right{right:auto;left:15px}html[dir=rtl] .fluentcrm-app.is-settings-page .fluentcrm-body .fcrm_settings_inner{padding-left:0;padding-right:432px}html[dir=rtl] .fluentcrm-app.is-settings-page .fluentcrm-body .fcrm_settings_inner.is-collapsed{padding-left:0;padding-right:238px}html[dir=rtl] .fcrm-settings-row.fcrm-settings-row-child{padding-left:0;padding-right:24px}html[dir=rtl] .fcrm_compliance_settings .fcrm-settings-row-child:before{left:auto;right:10px}html[dir=rtl] .fcrm_settings_sidebar{left:auto;right:160px;border-right:none;border-left:1px solid var(--stroke-soft-200, #E1E4EA)}html[dir=rtl] .fcrm_settings_sidebar .el-menu-item.is-active:before{left:auto;right:-16px;border-radius:4px 0 0 4px}html[dir=rtl] .fcrm_settings_sidebar .el-menu-item .fcrm_menu_title .menu_right_icon{right:auto;left:-5px}html[dir=rtl] .folded .fluentcrm-app.is-settings-page .fluentcrm-body .fcrm_settings_inner{padding-left:0;padding-right:308px}html[dir=rtl] .folded .fluentcrm-app.is-settings-page .fluentcrm-body .fcrm_settings_inner.is-collapsed{padding-left:0;padding-right:114px}html[dir=rtl] .folded .fcrm_settings_sidebar{left:auto;right:36px}html[dir=rtl] .fcrm_smtp_email_setup .fcrm_verified_title{text-align:right}html[dir=rtl] .fcrm_form_builder_new .fcrm_options_selector .fcrm_with_select{border-left:none;border-right:1px solid rgba(34,36,38,.15);border-radius:8px 0 0 8px}html[dir=rtl] .fcrm_form_builder_new .fcrm_input_row .fcrm_input .el-input__wrapper .el-input__inner{padding-left:0;padding-right:10px}html[dir=rtl] .fcrm_smart_links_wrap .el-table .el-table__expanded-cell{padding-left:20px;padding-right:50px}html[dir=rtl] .fcrm_option_creatable .fcrm_with_select{right:auto;left:1px;border-radius:8px 0 0 8px;border-left:none;border-right:1px solid var(--fc-primary-border)}html[dir=rtl] .fcrm_settings.fcrm_integration_settings .fcrm_integration_config .fcrm_options_selector .fcrm_with_select{border-radius:8px 0 0 8px}html[dir=rtl] .fcrm_options_selector.fcrm_option_creatable .el-select .el-select__wrapper .el-select__suffix{right:auto;left:55px}html[dir=rtl] .fcrm_options_selector .el-select .el-select__wrapper{padding-left:37px;padding-right:10px}html[dir=rtl] .fcrm_incoming_webhooks_wrap .el-table .el-table__header-wrapper .el-table__header th:first-child .cell,html[dir=rtl] .fcrm_incoming_webhooks_wrap .el-table .el-table__body-wrapper .el-table__body tr td:first-child .cell{padding-left:12px;padding-right:20px}html[dir=rtl] .fcrm_settings_sidebar .el-menu .fcrm-admin-tools-submenu .el-menu{padding-left:0;padding-right:40px}html[dir=rtl] .fcrm_settings_sidebar .el-menu .fcrm-admin-tools-submenu .el-menu:before{left:auto;right:21px}html[dir=rtl] .fcrm_settings_sidebar .el-menu .fcrm-admin-tools-submenu .el-menu .el-menu-item:before{left:auto;right:-19px}html[dir=rtl] .fcrm_settings_sidebar .el-menu .fcrm-admin-tools-submenu .el-sub-menu__title .el-sub-menu__icon-arrow{right:auto;left:8px}html[dir=rtl] .fcrm-time-range-input .el-input .el-input__wrapper{border-right:none!important;border-left:1px solid #e1e4ea!important}html[dir=rtl] .fcrm_alert_box .fcrm_list{padding-left:0;padding-right:21px}html[dir=rtl] .fcrm_block_composer_editor_wrapper .fcrm_block_composer_editor--body{padding:0}html[dir=rtl] .fcrm_block_composer_editor_wrapper .fcrm_block_composer_editor--body .fcrm_block_composer_editor--compose-body-row .fcrm_block_composer_editor--compose-sidebar{border-left:none;border-right:1px solid var(--fc-primary-border)}html[dir=rtl] .fcrm_block_composer_editor_wrapper .fcrm_block_composer_editor--body .fcrm_block_composer_editor--compose-body-row .fcrm_block_composer_editor--compose-sidebar-header-actions{right:auto;left:20px}html[dir=rtl] .fcrm_form_builder_item.fcrm_form_builder_item__dependency{padding-left:0;padding-right:24px}html[dir=rtl] .fcrm_perf_value .fcrm_perf_count:before{right:auto;left:0}html[dir=rtl] .fcrm_reports_home .fcrm_reports_sidebar{left:auto;right:0}html[dir=rtl] .fcrm_note_description .fcrm_note_description_inner{padding-left:20px;padding-right:100px}@media (max-width: 1100px){html[dir=rtl] ul.fcrm_menu{right:auto;left:0}}@media (max-width: 1024px){html[dir=rtl] .fcrm_settings_sidebar.is-open{right:160px;left:auto}html[dir=rtl] .fluentcrm-app.is-settings-page .fluentcrm-body .fcrm_settings_inner{padding-left:0;padding-right:160px}html[dir=rtl] .folded .fluentcrm-app.is-settings-page .fluentcrm-body .fcrm_settings_inner{padding-left:0;padding-right:36px}html[dir=rtl] .folded .fcrm_settings_sidebar{left:auto;right:-272px}html[dir=rtl] .folded .fcrm_settings_sidebar.is-open{left:auto;right:36px}}@media (max-width: 960px){html[dir=rtl] .auto-fold .fluentcrm-app.is-settings-page .fluentcrm-body .fcrm_settings_inner{padding-left:0;padding-right:36px}}@media (max-width: 782px){html[dir=rtl] .fcrm_settings_sidebar{left:auto;right:-272px}html[dir=rtl] .auto-fold #wpcontent{padding:0}html[dir=rtl] .auto-fold .fluentcrm-app.is-settings-page .fluentcrm-body .fcrm_settings_inner,html[dir=rtl] .auto-fold .folded .fluentcrm-app.is-settings-page .fluentcrm-body .fcrm_settings_inner{padding-right:0}html[dir=rtl] .auto-fold .fcrm_settings_sidebar.is-open{right:0}html[dir=rtl] .fluentcrm-app.is-settings-page,html[dir=rtl] .fcrm_topbar,html[dir=rtl] .fluentcrm-app{margin:0}}@media (max-width: 425px){html[dir=rtl] .auto-fold #wpcontent{padding-left:0!important}} diff --git a/wp-content/plugins/fluent-crm/assets/admin/css/app3.css b/wp-content/plugins/fluent-crm/assets/admin/css/app3.css new file mode 100644 index 0000000..70d01e3 --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/admin/css/app3.css @@ -0,0 +1 @@ +@charset "UTF-8";:root{--fc-primary-bg: #FFFFFF;--fc-secondary-bg: #F5F7FA;--fc-light-bg: #E1E4EA;--fc-deep-bg: #222530;--fc-weak-bg-25: #F9FAFB;--fc-ai-background: #efebff;--fc-ai-color: #8762F0;--fc-primary-text: #0E121B;--fc-secondary-text: #525866;--fc-text-muted: #99A0AE;--fc-text-inverse: #FFFFFF;--fc-primary-border: #E1E4EA;--fc-secondary-border: #CACFD8;--fc-primary-button: #222530;--fc-text-link: #335CFF;--fc-success: #1FC16B;--fc-success-bg: #E0FAEC;--fc-error: #FB3748;--fc-error-bg: #FFEBEC;--fc-warning: #F6B51E;--fc-warning-bg: #FFFAEB;--fc-text-link-bg: #EEF2FF;--fc-badge-unsubscribed-text: #222530;--fc-badge-unsubscribed-bg: #F2F5F8;--fc-badge-subscribed-text: #0B4627;--fc-badge-subscribed-bg: #E0FAEC;--fc-badge-pending-text: #624C18;--fc-badge-pending-bg: #FFFAEB;--fc-badge-transactional-text: #351A75;--fc-badge-transactional-bg: #EFEBFF;--fc-badge-bounced-text: #122368;--fc-badge-bounced-bg: #EBF1FF;--fc-badge-complained-text: #71330A;--fc-badge-complained-bg: #FFF3EB;--fc-badge-spammed-text: #681219;--fc-badge-spammed-bg: #FFEBEC;--el-color-primary: var(--fc-deep-bg);--el-color-primary-light-3: var(--fc-secondary-text);--el-color-primary-light-5: var(--fc-text-muted);--el-color-primary-light-7: var(--fc-secondary-border);--el-color-primary-light-8: var(--fc-primary-border);--el-color-primary-light-9: var(--fc-secondary-bg);--el-color-primary-dark-2: var(--fc-primary-text);--el-color-success: var(--fc-success);--el-color-warning: var(--fc-warning);--el-color-danger: var(--fc-error);--el-color-error: var(--fc-error);--el-color-info: var(--fc-primary-text);--el-text-color-primary: var(--fc-primary-text);--el-text-color-regular: var(--fc-secondary-text);--el-text-color-secondary: var(--fc-text-muted);--el-text-color-placeholder: var(--fc-text-muted);--el-text-color-disabled: var(--fc-secondary-border);--el-border-color: var(--fc-primary-border);--el-border-color-light: var(--fc-primary-border);--el-border-color-lighter: var(--fc-secondary-border);--el-border-color-dark: var(--fc-secondary-border);--el-fill-color-light: var(--fc-secondary-bg);--el-fill-color-lighter: var(--fc-secondary-bg);--el-bg-color: var(--fc-primary-bg);--el-bg-color-page: var(--fc-secondary-bg);--el-button-text-color: var(--fc-text-inverse);--el-fill-color-blank: var(--fc-primary-bg);--el-bg-color-overlay: var(--fc-primary-bg);--el-color-info-light-9: var(--fc-badge-unsubscribed-bg);--fcrm-border-radius-8: 8px;--el-border-radius-base: var(--fcrm-border-radius-8);--wp-editor-canvas-background: var(--fc-primary-bg)}body{color:var(--fc-primary-text)}body *{box-sizing:border-box}body.el-popup-parent--hidden{width:100%!important;padding-right:0!important}.fluentcrm-app *{box-sizing:border-box}.fluentcrm-app a:hover{text-decoration:none}.spining{animation:spining 1s linear infinite}@keyframes spining{0%{transform:rotate(0)}to{transform:rotate(360deg)}}@keyframes indeterminate{0%{left:-35%;right:100%}60%{left:100%;right:-90%}to{left:100%;right:-90%}}@keyframes fc_spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}.text-info{color:var(--fc-text-link)}.text-danger{color:var(--fc-error)}.text-align-right{text-align:right}.text-align-left{text-align:left}.text-align-center{text-align:center}.fcrm_force_hide{display:none!important}.app{padding:16px}.fcrm_max_w_800{max-width:800px;margin-left:auto;margin-right:auto}.fcrm_icon_90degree{transform:rotate(90deg)}.button-like{background:var(--fc-deep-bg);color:var(--fc-text-inverse);padding:6px 10px;border-radius:4px}.fcrm_contact_cell:active{opacity:.7}.url{color:var(--fc-text-link);cursor:pointer}.fcrm_no-margin{margin:0}.fcrm_mt_0{margin-top:0}.fcrm_mt_4{margin-top:4px}.fcrm_mt_6{margin-top:6px}.fcrm_mt_8{margin-top:8px}.fcrm_mt_10{margin-top:10px}.fcrm_mt_12{margin-top:12px}.fcrm_mt_16{margin-top:16px}.fcrm_mt_24{margin-top:24px}.fcrm_mb_4{margin-bottom:4px}.fcrm_mb_6{margin-bottom:6px}.fcrm_mb_8{margin-bottom:8px}.fcrm_mb_10{margin-bottom:10px}.fcrm_mb_12{margin-bottom:12px}.fcrm_mb_14{margin-bottom:14px}.fcrm_mb_16{margin-bottom:16px}.fcrm_mb_18{margin-bottom:18px}.fcrm_mb_20{margin-bottom:20px}.fcrm_mt_20{margin-top:20px}.fcrm_mb_24{margin-bottom:24px}.fcrm_mr_2{margin-right:2px}.fcrm_mr_4{margin-right:4px}.fcrm_mr_6{margin-right:6px}.fcrm_mr_8{margin-right:8px}.fcrm_mr_10{margin-right:10px}.fcrm_mr_12{margin-right:12px}.fcrm_p_0{padding:0}.fcrm_p_2{padding:2px}.fcrm_p_4{padding:4px}.fcrm_p_6{padding:6px}.fcrm_p_8{padding:8px}.fcrm_p_10{padding:10px}.fcrm_p_12{padding:12px}.fcrm_p_14{padding:14px}.fcrm_p_16{padding:16px}.fcrm_p_18{padding:18px}.fcrm_p_20{padding:20px}.fcrm_p_24{padding:24px}.fcrm_pt_0{padding-top:0}.fcrm_pt_8{padding-top:8px}.fcrm_pt_10{padding-top:10px}.fcrm_pt_12{padding-top:12px}.fcrm_pt_16{padding-top:16px}.fcrm_pt_20{padding-top:20px}.fcrm_pt_24{padding-top:24px}.fcrm_pr_0{padding-right:0}.fcrm_pr_8{padding-right:8px}.fcrm_pr_10{padding-right:10px}.fcrm_pr_12{padding-right:12px}.fcrm_pr_16{padding-right:16px}.fcrm_pr_20{padding-right:20px}.fcrm_pr_24{padding-right:24px}.fcrm_pb_0{padding-bottom:0}.fcrm_pb_8{padding-bottom:8px}.fcrm_pb_10{padding-bottom:10px}.fcrm_pb_12{padding-bottom:12px}.fcrm_pb_16{padding-bottom:16px}.fcrm_pb_20{padding-bottom:20px}.fcrm_pb_24{padding-bottom:24px}.fcrm_pl_0{padding-left:0}.fcrm_pl_8{padding-left:8px}.fcrm_pl_10{padding-left:10px}.fcrm_pl_12{padding-left:12px}.fcrm_pl_16{padding-left:16px}.fcrm_pl_20{padding-left:20px}.fcrm_pl_24{padding-left:24px}.content-center{display:flex;align-items:center;justify-content:center}.d-block{display:block}.d-none{display:none}.d-flex{display:flex}.flex-wrap{flex-wrap:wrap}.gap-4{gap:4px}.gap-6{gap:6px}.gap-8{gap:8px}.gap-10{gap:10px}.gap-12{gap:12px}.gap-14{gap:14px}.gap-16{gap:16px}.gap-18{gap:18px}.gap-20{gap:20px}.w-full{width:100%}.h-full{height:100%}.items-center{align-items:center}.items-start{align-items:flex-start}.items-end{align-items:flex-end}.flex-row{flex-direction:row}.flex-column{flex-direction:column}.justify-start{justify-content:flex-start}.justify-center{justify-content:center}.justify-end{justify-content:flex-end}.justify-between{justify-content:space-between}.justify-around{justify-content:space-around}.text-center{text-align:center}.text-secondary{color:var(--fc-secondary-text)}.text-primary{color:var(--fc-primary-text)}.text-success{color:var(--fc-success)}.text-error{color:var(--fc-error)}.text-warning{color:var(--fc-warning)}.text-muted{color:var(--fc-text-muted)}.mr-5{margin-right:5px}.ml-0{margin-left:0!important}.mt-5{margin-top:5px}.ml-5{margin-left:5px}.ml-0-im{margin-left:0!important}.mr-10{margin-right:10px}.mt-20{margin-top:20px}.mb-10{margin-bottom:10px}.mb-16{margin-bottom:16px}.pl-24{padding-left:24px}.font-regular,.font-normal{font-weight:400}.font-medium{font-weight:500}.font-semibold{font-weight:600}.font-bold{font-weight:700}.no-hover:hover,.no-hover:focus{color:var(--fc-secondary-text)!important;background:transparent!important}.no-margin{margin:0}.no-margin-bottom{margin-bottom:initial!important}.hidden{display:none}.icon-90degree{transform:rotate(90deg)}.fcrm_download_icon{margin-right:4px;flex-shrink:0}.fc_m_30{margin-bottom:30px}.fc_m_20{margin-bottom:20px}.fc_m_24{margin-bottom:24px}.fc_t_30{margin-top:30px}.fc_t_10{margin-top:10px}.fc_disc{list-style:disc;padding-left:30px}.fc_counting_heading span{background-color:var(--fc-deep-bg);padding:2px 15px;border-radius:4px;color:var(--fc-text-inverse)}.min_textarea_40 textarea{min-height:40px!important}.fcrm_padding_20{padding:20px}span.fc_positive{font-weight:700;color:var(--fc-success)}span.fc_negative{color:var(--fc-error);font-weight:500}.fluentcrm-app a:focus{box-shadow:none!important}.show_on_parent .show_on_hover{display:none}.show_on_parent:hover .show_on_hover{display:initial}.fluentcrm_pad_around{padding:25px}.fluentcrm_pad_around .el-table--scrollable-x:before{display:none}.fluentcrm_pad_around .el-table--scrollable-x .el-table__body-wrapper{padding-bottom:10px;background:var(--fc-secondary-bg)}.fluentcrm_pad_around .el-table__body-wrapper table tbody tr td:last-child .el-button+.el-button{margin-left:0}.fluentcrm_pad_30{padding:30px}.fluentcrm_pad_b_30{padding-bottom:30px}.fluentcrm_pad_b_20{padding-bottom:20px}.fluentcrm_pad_b_10{padding-bottom:10px}.fluentcrm_pad_b_15{padding-bottom:15px}.fluentcrm_clickable{cursor:pointer}@font-face{font-family:fontello;src:url(../../scss/fonts/fontello.eot?37598903);src:url(../../scss/fonts/fontello.eot?37598903#iefix) format("embedded-opentype"),url(../../scss/fonts/fontello.woff2?37598903) format("woff2"),url(../../scss/fonts/fontello.woff?37598903) format("woff"),url(../../scss/fonts/fontello.ttf?37598903) format("truetype"),url(../../scss/fonts/fontello.svg?37598903#fontello) format("svg");font-weight:400;font-style:normal}.fc-icon-cancel_automation:before{content:"";font-family:fontello;font-style:normal;font-weight:400;speak:never;display:inline-block;text-decoration:inherit;width:1em;text-align:center;font-variant:normal;text-transform:none;line-height:1em;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}@font-face{font-family:icomoon;src:url(../../scss/fonts/icomoon.eot?cwznoa);src:url(../../scss/fonts/icomoon.eot?cwznoa#iefix) format("embedded-opentype"),url(../../scss/fonts/icomoon.ttf?cwznoa) format("truetype"),url(../../scss/fonts/icomoon.woff?cwznoa) format("woff"),url(../../scss/fonts/icomoon.svg?cwznoa#icomoon) format("svg");font-weight:400;font-style:normal;font-display:block}[class^=fc-icon-],[class*=" fc-icon-"]{font-family:icomoon!important;speak:never;font-style:normal;font-weight:400;font-variant:normal;text-transform:none;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.fc-icon-action:before{content:""}.fc-icon-apply_list:before{content:""}.fc-icon-apply_tag:before{content:""}.fc-icon-benchmark:before{content:""}.fc-icon-cancel_automation .path1:before{content:"";color:#000}.fc-icon-cancel_automation .path2:before{content:"";margin-left:-1em;color:#000}.fc-icon-cancel_automation .path3:before{content:"";margin-left:-1em;color:#000}.fc-icon-cancel_automation .path4:before{content:"";margin-left:-1em;color:#000}.fc-icon-cancel_automation .path5:before{content:"";margin-left:-1em;color:#000}.fc-icon-cancel_automation .path6:before{content:"";margin-left:-1em;color:#000}.fc-icon-cancel_automation .path7:before{content:"";margin-left:-1em;color:#000}.fc-icon-cancel_automation .path8:before{content:"";margin-left:-1em;color:#000}.fc-icon-cancel_automation .path9:before{content:"";margin-left:-1em;color:#000}.fc-icon-cancel_automation .path10:before{content:"";margin-left:-1em;color:#000}.fc-icon-cancel_automation .path11:before{content:"";margin-left:-1em;color:#000}.fc-icon-cancel_automation .path12:before{content:"";margin-left:-1em;color:#000}.fc-icon-cancel_automation .path13:before{content:"";margin-left:-1em;color:#000}.fc-icon-cancel_automation .path14:before{content:"";margin-left:-1em;color:#000}.fc-icon-cancel_automation .path15:before{content:"";margin-left:-1em;color:#000}.fc-icon-cancel_automation .path16:before{content:"";margin-left:-1em;color:#000}.fc-icon-cancel_automation .path17:before{content:"";margin-left:-1em;color:#000}.fc-icon-cancel_automation .path18:before{content:"";margin-left:-1em;color:#000}.fc-icon-cancel_automation .path19:before{content:"";margin-left:-1em;color:#000}.fc-icon-cancel_automation .path20:before{content:"";margin-left:-1em;color:#000}.fc-icon-cancel_automation .path21:before{content:"";margin-left:-1em;color:#000}.fc-icon-cancel_automation .path22:before{content:"";margin-left:-1em;color:#000}.fc-icon-cancel_automation .path23:before{content:"";margin-left:-1em;color:#000}.fc-icon-cancel_automation .path24:before{content:"";margin-left:-1em;color:#000}.fc-icon-cancel_automation .path25:before{content:"";margin-left:-1em;color:#000}.fc-icon-cancel_automation .path26:before{content:"";margin-left:-1em;color:#000}.fc-icon-cancel_automation .path27:before{content:"";margin-left:-1em;color:#000}.fc-icon-cancel_automation .path28:before{content:"";margin-left:-1em;color:#000}.fc-icon-cancel_automation .path29:before{content:"";margin-left:-1em;color:#fff}.fc-icon-cancel_automation .path30:before{content:"";margin-left:-1em;color:#000}.fc-icon-cancel_sequence:before{content:""}.fc-icon-check_contact_property_conditional:before{content:""}.fc-icon-conditions:before{content:""}.fc-icon-create_wp_user:before{content:""}.fc-icon-edd_new_order_success:before{content:""}.fc-icon-edd:before{content:""}.fc-icon-end_funnel:before{content:""}.fc-icon-fluentforms:before{content:""}.fc-icon-has_list:before{content:""}.fc-icon-has_wp_role:before{content:""}.fc-icon-learndash_complete_course:before{content:""}.fc-icon-learndash_complete_lesson:before{content:""}.fc-icon-learndash_complete_topic:before{content:""}.fc-icon-learndash_course_group:before{content:""}.fc-icon-learndash_enroll_course:before{content:""}.fc-icon-learndash:before{content:""}.fc-icon-lifter_lms_complete_course:before{content:""}.fc-icon-lifter_lms_complete_lession-t2:before{content:""}.fc-icon-lifter_lms_course_enrollment:before{content:""}.fc-icon-lifter_lms_membership:before{content:""}.fc-icon-lifter_lms:before{content:""}.fc-icon-link_clicked:before{content:""}.fc-icon-list_applied_2:before{content:""}.fc-icon-list_applied:before{content:""}.fc-icon-list_removed_2:before{content:""}.fc-icon-list_removed:before{content:""}.fc-icon-memberpress_expired:before{content:""}.fc-icon-memberpress_membership:before{content:""}.fc-icon-memberpress:before{content:""}.fc-icon-membership_level_ex_pmp:before{content:""}.fc-icon-new_order_woo:before{content:""}.fc-icon-paid_membership_pro_user_level:before{content:""}.fc-icon-paid_membership_pro:before{content:""}.fc-icon-rcp_membership_cancle:before{content:""}.fc-icon-rcp_membership_level:before{content:""}.fc-icon-remove_from_course_lms:before{content:""}.fc-icon-remove_from_membership_lms:before{content:""}.fc-icon-remove_tag:before{content:""}.fc-icon-removed_list:before{content:""}.fc-icon-restric_content:before{content:""}.fc-icon-send_campaign:before{content:""}.fc-icon-set_sequence:before{content:""}.fc-icon-tag_applied_2:before{content:""}.fc-icon-tag_applied:before{content:""}.fc-icon-tag_removed_2:before{content:""}.fc-icon-tag_removed:before{content:""}.fc-icon-trigger:before{content:""}.fc-icon-tutor_lms_complete_course:before{content:""}.fc-icon-tutor_lms_enrollment_course:before{content:""}.fc-icon-tutorlms:before{content:""}.fc-icon-wait_time:before{content:""}.fc-icon-webhooks:before{content:""}.fc-icon-wishlist:before{content:""}.fc-icon-woo_new_order:before{content:""}.fc-icon-woo_order_complete:before{content:""}.fc-icon-woo_purchased:before{content:""}.fc-icon-woo_refund:before{content:""}.fc-icon-woo:before{content:""}.fc-icon-wordpress:before{content:""}.fc-icon-wp_new_user_signup:before{content:""}.fc-icon-wp_user_meta:before{content:""}.fc-icon-wp_user_role:before{content:""}.fc-icon-writing:before{content:""}.el-input{width:100%}.el-input .el-input__wrapper{border-radius:8px;box-shadow:0 1px 2px #0a0d1408;border:1px solid var(--fc-primary-border);padding:2px 10px;background:var(--fc-primary-bg)}.el-input .el-input__wrapper.is-focus,.el-input .el-input__wrapper.is-focused,.el-input .el-input__wrapper:focus-within{border-color:var(--fc-primary-text)}.el-input .el-input__wrapper.is-disabled{opacity:.5}.el-input .el-input__wrapper .el-input__inner{border:none!important;font-size:14px;background:none;box-shadow:none;color:var(--fc-secondary-text);padding:0;line-height:1;min-height:30px}.el-textarea .el-textarea__inner{border-radius:var(--fcrm-border-radius-8);box-shadow:0 1px 2px #0a0d1408;border:1px solid var(--fc-primary-border);padding:4px 10px;color:var(--fc-secondary-text);font-size:14px}.el-textarea .el-textarea__inner.is-disabled{opacity:.5}.el-textarea,.el-select,.el-input{height:auto}.el-textarea__inner.is-focused,.el-textarea__inner.is-focus,.el-textarea__wrapper.is-focused,.el-textarea__wrapper.is-focus,.el-select__inner.is-focused,.el-select__inner.is-focus,.el-select__wrapper.is-focused,.el-select__wrapper.is-focus,.el-input__inner.is-focused,.el-input__inner.is-focus,.el-input__wrapper.is-focused,.el-input__wrapper.is-focus{border-color:var(--fc-primary-text)}.el-textarea input,.el-select input,.el-input input{padding:0;margin:0;box-shadow:none;border:none;background:none;color:var(--fc-primary-text);min-height:30px}.el-input-number .el-input__inner{margin:0}.el-input-number .el-input-number__decrease,.el-input-number .el-input-number__increase{background:transparent;border:none;color:var(--fc-secondary-text)}.el-input-number .el-input-number__decrease:hover,.el-input-number .el-input-number__increase:hover{color:var(--fc-primary-text)}.el-date-editor{justify-content:flex-start;padding:0}.el-date-editor .el-input__wrapper{padding:7px 10px}.el-date-editor .el-range-input{height:auto;line-height:20px;font-weight:400;font-size:14px;color:var(--fc-primary-text)}.el-date-editor .el-range-separator{line-height:1;padding:0;width:16px;font-weight:700}.fcrm_checkbox_group,.fcrm_checkbox_group .el-checkbox-group{display:flex;flex-direction:column;gap:12px;flex-wrap:wrap}.el-input__inner{border:none!important}.fcrm_searcher .el-input__wrapper,.fcrm_dynamic_segments .fcrm_table_header_inner_left .el-input .el-input__wrapper{height:32px;min-height:32px;border:none;background:var(--fc-secondary-bg)}.fcrm_searcher .el-input__wrapper:hover,.fcrm_searcher .el-input__wrapper.is-focused,.fcrm_searcher .el-input__wrapper.is-focus,.fcrm_dynamic_segments .fcrm_table_header_inner_left .el-input .el-input__wrapper:hover,.fcrm_dynamic_segments .fcrm_table_header_inner_left .el-input .el-input__wrapper.is-focused,.fcrm_dynamic_segments .fcrm_table_header_inner_left .el-input .el-input__wrapper.is-focus{border:none;box-shadow:none}.fcrm_searcher{--el-input-hover-border-color: var(--fc-primary-border);--el-input-focus-border-color: var(--fc-primary-border);width:100%}.fcrm_searcher .el-input__suffix{cursor:pointer}.fcrm_searcher .el-input__suffix .el-icon{color:var(--fc-text-muted)}.fcrm_searcher .fcrm-searcher-suffix{display:inline-flex;align-items:center}select,textarea,input{outline:none;box-shadow:none;border-color:var(--fc-secondary-border)!important;background:var(--fc-primary-bg)}select:focus,textarea:focus,input:focus{border-color:var(--fc-primary-text)!important;box-shadow:none!important;outline:none}.el-date-editor .el-range-separator{padding:0;width:16px;font-weight:700}.el-tag--white{background:var(--fc-secondary-bg);color:var(--fc-primary-text);border-color:var(--fc-text-inverse);margin-bottom:5px;margin-left:10px}.el-tag--white .el-tag__close{color:var(--fc-text-muted);-webkit-transition:.2s;-moz-transition:.2s;-o-transition:.2s;-ms-transition:.2s;transition:.2s}.el-tag--white .el-tag__close:hover{background-color:var(--fc-text-muted);color:var(--fc-text-inverse);line-height:17px;font-size:10px;padding-right:1px}.el-input-group .el-input-group__append{border-color:var(--fc-secondary-border);transition:.2s}.el-input-group .el-input-group__append:hover{background:#2225301a;color:var(--fc-deep-bg)}.el-input-group input{font-weight:500}.el-input-group input:focus~.el-input-group__append{border-color:var(--fc-text-link)}.el-picker-panel .el-date-table tr td.in-range .el-date-table-cell__text{background-color:var(--fc-secondary-bg)}.el-picker-panel .el-date-table tr td.end-date .el-date-table-cell__text,.el-picker-panel .el-date-table tr td.start-date .el-date-table-cell__text{background-color:var(--fc-deep-bg);color:var(--fc-text-inverse)}.el-picker-panel .el-date-table tr td.available:hover{color:var(--fc-deep-bg)}.el-picker-panel .el-picker-panel__footer{display:flex;align-items:center;gap:8px;justify-content:flex-end}.el-picker-panel .el-picker-panel__footer .el-button{margin:0}.el-picker-panel .el-picker-panel__footer .el-button.is-text{background:var(--fc-primary-bg);color:var(--fc-secondary-text);font-weight:500;font-size:14px;line-height:20px;height:auto;border:1px solid var(--fc-primary-border);box-shadow:0 1px 2px #0a0d1408;border-radius:8px;padding:5px 10px}.el-picker-panel .el-picker-panel__footer .el-button.is-text:hover{border-color:var(--fc-primary-border);background:var(--fc-secondary-bg);color:var(--fc-primary-text)}.el-picker-panel .el-picker-panel__footer .el-button:not(.is-text){background:var(--fc-deep-bg);border-color:var(--fc-deep-bg);border-radius:8px;color:var(--fc-text-inverse);font-size:14px;line-height:20px;height:auto;padding:5px 10px}.el-picker-panel .el-picker-panel__footer .el-button:not(.is-text):hover{background:var(--fc-primary-text);border-color:var(--fc-primary-text);color:var(--fc-text-inverse)}.fluentcrm_width_input .el-date-editor--timerange.el-input__inner{width:450px;max-width:100%}.fcrm_option_selector{display:flex;align-items:center}.fc-item-copier-input{border-radius:8px!important;overflow:hidden!important}.fc-item-copier-input .el-input__wrapper{background:var(--fc-weak-bg-25);border-right:none;box-shadow:none;padding:1px 11px;border-radius:var(--fcrm-border-radius-8) 0 0 var(--fcrm-border-radius-8)}.fc-item-copier-input .el-input__wrapper .el-input__inner{color:var(--fc-primary-text);font-weight:500;background:none}.fc-item-copier-input .el-input-group__append{background:none;border:1px solid var(--fc-primary-border);border-left:none;padding:0;margin:0}.fc-item-copier-input .el-input-group__append .el-button{margin:0;border:none;background:var(--fc-primary-bg);border-radius:0;padding:0 8px;height:100%;display:flex;align-items:center}.fc-item-copier-input .el-input-group__append .el-button:last-child{border-radius:0 var(--fcrm-border-radius-8) var(--fcrm-border-radius-8) 0;border-left:1px solid var(--fc-primary-border)}.fc-item-copier-input .el-input-group__append .el-button.copy-btn{background:var(--fc-weak-bg-25)}.fc-item-copier-input .el-input-group__append .el-button:hover{color:var(--fc-primary-text)}.fc-item-copier-input .el-input-group__append .el-button:hover svg{color:var(--fc-primary-text)}.fc-item-copier-input .el-input-group__append .el-button svg{width:16px;height:16px;display:block;color:var(--fc-secondary-text)}.fluentcrm-app .el-table--striped .el-table__body tr.el-table__row--striped td.el-table__cell{background:var(--fc-secondary-bg)}.fluentcrm-app .el-table--enable-row-hover .el-table__body tr:hover>td.el-table__cell{background-color:var(--fc-secondary-bg)}.el-table .el-table__header-wrapper .el-table__cell,.el-table .el-table__body-wrapper .el-table__cell{border-color:var(--fc-primary-border)}.el-table .el-table__expand-icon .el-icon,.el-table .el-table__expand-icon svg{display:none!important}.el-table .el-table__expand-icon:before{content:"▶";font-size:12px;display:inline-flex;align-items:center;justify-content:center}.el-table .el-table__expand-icon.el-table__expand-icon--expanded{transform:none}.el-table .el-table__expand-icon.el-table__expand-icon--expanded:before{content:"▼"}.el-table-column--selection .el-checkbox .el-checkbox__input{width:20px;height:20px;display:flex;align-items:center;justify-content:center}.el-table-column--selection .el-checkbox .el-checkbox__input input{opacity:0;margin:0}.el-table-column--selection .el-checkbox .el-checkbox__input .el-checkbox__inner{background-color:var(--fc-primary-bg);position:relative;border-width:1.5px}.el-table-column--selection .el-checkbox .el-checkbox__input .el-checkbox__inner:before{content:"";position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);width:13px;height:13px;background-color:var(--fc-primary-bg);border-radius:2.6px}.el-table-column--selection .el-checkbox .el-checkbox__input.is-indeterminate .el-checkbox__inner:after{transform:translate(-50%,-50%) rotate(0);display:block;border:none;width:8px;height:1px;background-color:var(--fc-primary-bg)}.el-table-column--selection .el-checkbox .el-checkbox__input.is-checked .el-checkbox__inner,.el-table-column--selection .el-checkbox .el-checkbox__input.is-indeterminate .el-checkbox__inner{background-color:var(--fc-deep-bg)}.el-table-column--selection .el-checkbox .el-checkbox__input.is-checked .el-checkbox__inner:before,.el-table-column--selection .el-checkbox .el-checkbox__input.is-indeterminate .el-checkbox__inner:before{display:none}.el-table-column--selection .el-checkbox .el-checkbox__input.is-checked:hover .el-checkbox__inner,.el-table-column--selection .el-checkbox .el-checkbox__input.is-indeterminate:hover .el-checkbox__inner{background-color:var(--fc-primary-text)}td.fcrm_table_actions_cell .el-dropdown-link{cursor:pointer}.el-table .el-table__inner-wrapper:after{display:unset!important;background:var(--fc-primary-border)}.el-table .el-table__inner-wrapper:before{display:none!important}.el-table .el-table__inner-wrapper .el-table__body-wrapper .el-scrollbar .el-scrollbar__wrap .el-scrollbar__view .el-table__body .el-table__row:last-child td{border-bottom:none!important}.el-table .el-table__body-wrapper .el-table__cell{padding:12px 0}.el-table,.el-table tr{background:none}.el-table:after,.el-table:before{display:none!important}.el-table__inner-wrapper:after,.el-table__inner-wrapper:before{display:none!important}.el-table__border-left-patch{display:none!important}.el-table__header-wrapper .el-table__header thead tr th{background:var(--fc-secondary-bg);border-right:none;border-left:none;font-size:14px;font-weight:500;line-height:20px;color:var(--fc-secondary-text);padding-top:8px;padding-bottom:8px;border-bottom:1px solid var(--fc-primary-border);border-top:1px solid var(--fc-primary-border)}.el-table__header-wrapper .el-table__header thead tr th .cell{padding-left:12px;padding-right:12px}.el-table__header-wrapper .el-table__header thead tr th:first-child .cell{padding-left:20px}.el-table__header-wrapper .el-table__header thead tr th input[type=checkbox]:disabled{opacity:0;margin:0}.el-table__body-wrapper:after{background:var(--fc-primary-border)}.el-table__body-wrapper .el-table__body tbody tr td{border-right:none!important;padding-top:12px;padding-bottom:12px;color:var(--fc-primary-text);font-weight:400;font-size:14px;line-height:20px;background:var(--fc-primary-bg)!important;border-bottom-color:var(--fc-primary-border)}.el-table__body-wrapper .el-table__body tbody tr td:first-child .cell{padding-left:20px}.el-table__body-wrapper .el-table__body tbody tr td .cell{padding-left:12px;padding-right:12px}.el-table__body-wrapper .el-table__body tbody tr td .cell .el-switch{--el-switch-on-color: var(--fc-deep-bg)}.el-table__body-wrapper .el-table__body tbody tr td .title{display:flex;align-items:center;gap:8px;flex-wrap:wrap}.el-table__body-wrapper .el-table__body tbody tr td .automation_title{color:var(--fc-primary-text);font-weight:400;font-size:14px;line-height:20px;margin:0}.el-table__body-wrapper .el-table__body tbody tr td .automation_title a{color:var(--fc-primary-text);display:block}.el-table__body-wrapper .el-table__body tbody tr td a{color:var(--fc-primary-text)}.el-table__body-wrapper .el-table__body tbody tr td .stats_badge_inline{background:var(--fc-primary-bg);color:var(--fc-secondary-text);font-weight:400;font-size:12px;line-height:18px;height:auto;border:1px solid var(--fc-primary-border);box-shadow:0 1px 2px #0a0d1408;border-radius:8px;padding:2px 6px;display:inline-flex;align-items:center;gap:2px}.el-table__body-wrapper .el-table__body tbody tr td .fcrm_stats_badge_segment{display:inline-flex;align-items:center;gap:3px;margin-inline-start:4px;padding-inline-start:5px;border-inline-start:1px solid var(--fc-primary-border);color:var(--fc-secondary-text)}.el-table__body-wrapper .el-table__body tbody tr td .fcrm_stats_badge_segment_icon{color:var(--fc-secondary-text);font-size:13px}.el-table__body-wrapper .el-table__body tbody tr td .fcrm_stats_badge_link{cursor:pointer;text-decoration:none;transition:border-color .15s ease,color .15s ease,background-color .15s ease}.el-table__body-wrapper .el-table__body tbody tr td .fcrm_stats_badge_link:hover,.el-table__body-wrapper .el-table__body tbody tr td .fcrm_stats_badge_link:focus{background:#0079ff14;border-color:var(--fc-text-link);color:var(--fc-text-link)}.el-table__body-wrapper .el-table__body tbody tr td .item_description{display:block;color:var(--fc-secondary-text);font-weight:400;font-size:12px;line-height:18px;margin:2px 0 0}.el-table__body-wrapper .el-table__body tbody tr td .fcrm_individual_progress{padding:10px 0}.el-table__body-wrapper .el-table__body tbody tr td .fcrm_individual_progress .el-timeline-item{margin-bottom:0}.el-table__body-wrapper .el-table__body tbody tr td .fcrm_individual_progress .el-timeline-item.fcrm_timeline_empty{opacity:1}.el-table__body-wrapper .el-table__body tbody tr td .fcrm_individual_progress .el-timeline-item.fcrm_timeline_empty .el-timeline-item__node{background:var(--fc-primary-border)}.el-table__body-wrapper .el-table__body tbody tr td .fcrm_individual_progress .el-timeline-item.fcrm_timeline_empty .el-timeline-item__timestamp,.el-table__body-wrapper .el-table__body tbody tr td .fcrm_individual_progress .el-timeline-item.fcrm_timeline_empty .el-timeline-item__content{color:var(--fc-text-muted)}.el-table__body-wrapper .el-table__body tbody tr td .fcrm_individual_progress .el-timeline-item:last-child{padding-bottom:0}.el-table__body-wrapper .el-table__body tbody tr td .fcrm_individual_progress .el-timeline-item__tail{border-left:2px solid var(--fc-primary-border);height:calc(100% - 28px);top:20px}.el-table__body-wrapper .el-table__body tbody tr td .fcrm_individual_progress .el-timeline-item__node{box-shadow:0 1px 2px #0a0d1408;border:2px solid var(--fc-primary-bg);width:12px;height:12px}.el-table__body-wrapper .el-table__body tbody tr td .fcrm_individual_progress .el-timeline-item__node.el-timeline-item__node--large{left:-1px}.el-table__body-wrapper .el-table__body tbody tr td .fcrm_individual_progress .el-timeline-item__node.el-timeline-item__node--primary{background:var(--fc-text-link)}.el-table__body-wrapper .el-table__body tbody tr td .fcrm_individual_progress .el-timeline-item__node.el-timeline-item__node--primary .el-timeline-item__icon{display:none}.el-table__body-wrapper .el-table__body tbody tr td .fcrm_individual_progress .el-timeline-item__content{font-weight:400;font-size:14px;line-height:20px;color:var(--fc-primary-text)}.el-table__body-wrapper .el-table__body tbody tr td .fcrm_individual_progress .el-timeline-item__timestamp{margin-top:4px;color:var(--fc-secondary-text);font-weight:400;font-size:14px;line-height:20px}.el-table__body-wrapper .el-table__body tbody tr td .fcrm_individual_progress .el-timeline-item.fc_timeline_empty{opacity:1}.el-table__body-wrapper .el-table__body tbody tr td .fcrm_individual_progress .el-timeline-item.fc_timeline_empty .el-timeline-item__node{background:var(--fc-primary-border)}.el-table__body-wrapper .el-table__body tbody tr td .fcrm_individual_progress .el-timeline-item.fc_timeline_empty .el-timeline-item__content{color:var(--fc-text-muted)}.el-table__body-wrapper .el-table__body tbody tr td .fc_funnel_labels .el-tag{padding:4px 4px 4px 6px;border-radius:6px;color:#0e121b}.el-table__body-wrapper .el-table__body tbody tr td .fc_funnel_labels .el-tag__content{font-size:12px;display:flex;align-content:center;gap:4px}.el-table__body-wrapper .el-table__body tbody tr td .fc_funnel_labels .el-tag__close{margin:0;color:#0e121b}.el-table__body-wrapper .el-table__body tbody tr td .fc_funnel_labels .el-tag__close:hover{background:#f5f7fa}.el-table__body-wrapper .el-table__body tbody tr td .fcrm_table_row_expand{padding:4px 0 4px 70px}.el-table__body-wrapper .el-table__body tbody tr td .fcrm_table_body_actions{display:flex;flex-wrap:wrap;align-items:flex-start;gap:6px}.el-table__body-wrapper .el-table__body tbody tr td .fcrm_table_body_actions .el-button{margin:0}.el-table__body-wrapper .el-table__body tbody tr td .subscriber-stats{display:flex;flex-wrap:wrap;align-items:flex-start;gap:6px}.el-table__body-wrapper .el-table__body tbody tr td .subscriber-stats .ns_counter{margin:0;height:auto;display:flex;align-items:center;gap:4px;line-height:16px;padding:3px 4px;min-height:24px}.el-table__body-wrapper .el-table__body tbody tr td h4{font-size:14px;font-weight:400;line-height:20px;letter-spacing:-.084px;color:var(--fc-primary-text);margin:0}.el-table__body-wrapper .el-table__body tbody tr td.el-table__expanded-cell{padding-left:70px;padding-right:24px}.el-dialog{padding:0!important;border-radius:8px;margin-top:40px!important}@media (max-width: 768px){.el-dialog{width:90%!important}}.el-dialog__headerbtn{height:100%}.el-dialog__title{font-weight:500;font-size:16px;line-height:24px;color:var(--fc-primary-text)}.el-dialog .el-dialog__header{display:flex;align-items:center;gap:12px;justify-content:space-between;background:none;padding:16px 20px;border-bottom:1px solid var(--fc-primary-border);position:relative}.el-dialog__footer{padding:0}.el-dialog .dialog-footer{border-top:1px solid var(--fc-primary-border);padding:16px 20px;text-align:right;box-sizing:border-box;background:none;border-bottom-left-radius:5px;border-bottom-right-radius:5px;width:auto;display:block}.el-dialog__body{padding:20px;word-break:inherit}.el-dialog__headerbtn:hover .el-dialog__close{color:var(--fc-primary-text)}.el-drawer__header{font-size:16px;font-weight:700;background-color:var(--fc-secondary-bg);padding:16px 20px!important;margin-bottom:0!important}.fcrm_drawer .el-drawer__body{padding:0!important}.el-overlay .el-message-box{max-width:440px;width:100%;border-radius:var(--fcrm-border-radius-8);padding:0}.el-overlay .el-message-box__header{display:none}.el-overlay .el-message-box__content{padding:20px}.el-overlay .el-message-box__message p{margin:0}.el-overlay .el-message-box__btns{border-top:1px solid var(--fc-primary-border);padding:12px 20px;display:flex;align-items:center;gap:12px}.el-overlay .el-message-box__btns .el-button{margin:0}.el-overlay .el-message-box__btns .el-button:not(.el-button--primary){background:var(--fc-primary-bg);color:var(--fc-secondary-text);font-weight:500;font-size:14px;line-height:20px;height:auto;border:1px solid var(--fc-primary-border);box-shadow:0 1px 2px #0a0d1408;border-radius:8px;padding:7px 10px}.el-overlay .el-message-box__btns .el-button:not(.el-button--primary):hover{border-color:var(--fc-primary-border);background:var(--fc-secondary-bg);color:var(--fc-primary-text)}.el-overlay .el-overlay-message-box .fcrm-status-confirm-dialog .el-message-box__title{font-size:34px;line-height:1.15;font-weight:600;color:var(--fc-primary-text)}.el-overlay .el-overlay-message-box .fcrm-status-confirm-dialog .el-message-box__message{margin:0}.el-overlay .el-overlay-message-box .fcrm-status-confirm-dialog .el-message-box__container{align-items:flex-start}.el-overlay .el-overlay-message-box .fcrm-status-confirm-dialog .el-message-box__btns .el-button{margin:0;border-radius:8px;background:var(--fc-primary-bg);height:36px;min-height:36px;color:var(--fc-secondary-text);border:1px solid var(--fc-primary-border);box-shadow:0 1px 2px #0a0d1408}.el-overlay .el-overlay-message-box .fcrm-status-confirm-dialog .el-message-box__btns .el-button:hover{border-color:var(--fc-primary-border);background:var(--fc-secondary-bg);color:var(--fc-primary-text)}.el-overlay .el-overlay-message-box .fcrm-status-confirm-dialog .el-message-box__btns .el-button--primary{background:var(--fc-deep-bg);border-color:var(--fc-deep-bg);color:var(--fc-text-inverse)}.el-overlay .el-overlay-message-box .fcrm-status-confirm-dialog .el-message-box__btns .el-button--primary:hover{background:var(--fc-primary-text);border-color:var(--fc-primary-text);color:var(--fc-text-inverse)}.el-overlay .el-overlay-message-box .fcrm-status-confirm-dialog .fcrm-status-confirm__body{display:flex;gap:16px}.el-overlay .el-overlay-message-box .fcrm-status-confirm-dialog .fcrm-status-confirm__icon-wrap{flex:none}.el-overlay .el-overlay-message-box .fcrm-status-confirm-dialog .fcrm-status-confirm__icon-wrap svg{display:block}.el-overlay .el-overlay-message-box .fcrm-status-confirm-dialog .el-message-box__status{background:var(--fc-secondary-bg);width:40px;height:40px;display:flex;align-items:center;justify-content:center;color:var(--fc-text-muted);border-radius:8px;flex:none;font-size:20px}.el-overlay .el-overlay-message-box .fcrm-status-confirm-dialog .el-message-box__status.el-message-box-icon--warning{background:var(--fc-warning-bg);color:var(--fc-warning)}.el-overlay .el-overlay-message-box .fcrm-status-confirm-dialog .fcrm-status-confirm__icon{background:var(--fc-secondary-bg);width:40px;height:40px;display:flex;align-items:center;justify-content:center;color:var(--fc-text-muted);border-radius:8px}.el-overlay .el-overlay-message-box .fcrm-status-confirm-dialog .fcrm-status-confirm__title{margin:0 0 4px;color:var(--fc-primary-text);font-weight:500;font-size:16px;line-height:24px}.el-overlay .el-overlay-message-box .fcrm-status-confirm-dialog .fcrm-status-confirm__text{margin:0;font-weight:400;font-size:14px;line-height:20px;color:var(--fc-secondary-text)}body.el-popup-parent--hidden #adminmenumain,body.el-popup-parent--hidden #wpwrap{z-index:10}.el-dialog__wrapper.fc_smtp_email_dialog .el-dialog{min-width:auto!important;width:50%}@media (max-width: 1200px){.el-dialog__wrapper.fc_smtp_email_dialog .el-dialog{width:90%}}.fc-verified-email-input-dialog .el-dialog{padding:0}.fc-verified-email-input-dialog .el-dialog .fc-verified-email-input-dialog-footer{padding:10px;text-align:right;box-sizing:border-box;background:var(--fc-secondary-bg);border-bottom-left-radius:5px;border-bottom-right-radius:5px;width:auto;display:block}.el-drawer.ltr{direction:rtl}.el-drawer .fcrm_dialog_footer_actions{display:flex;gap:12px;justify-content:flex-end;align-items:center}.el-drawer .fcrm_dialog_footer_actions .el-button{margin:0}.el-drawer__footer{border-top:1px solid var(--fc-primary-border);padding:12px 20px}.el-drawer .dialog-footer{display:flex;align-items:center;gap:8px}.el-drawer .dialog-footer .el-button{margin:0}@media (max-width: 768px){.el-drawer{width:90%!important}}.el-checkbox__label{white-space:normal}.el-checkbox-group.fluentcrm-filter-options{max-height:300px;max-width:300px;overflow-x:hidden}.fc_rich_checkboxes .el-checkbox{display:flex;margin-bottom:10px;width:100%;align-items:center}.fc_rich_checkboxes .el-checkbox span.el-checkbox__label{display:block;flex:1}.el-radio-group.fcrm_global_radio_group{border:none;box-shadow:none;gap:4px}.el-radio-group.fcrm_global_radio_group .el-radio-button{border:none;background:none}.el-radio-group.fcrm_global_radio_group .el-radio-button.is-active .el-radio-button__original-radio:not(:disabled)+.el-radio-button__inner{background:var(--fc-secondary-bg);color:var(--fc-primary-text);border:none;outline:none}.el-radio-group.fcrm_global_radio_group .el-radio-button:last-child .el-radio-button__inner,.el-radio-group.fcrm_global_radio_group .el-radio-button:first-child .el-radio-button__inner{border-radius:8px}.el-radio-group.fcrm_global_radio_group .el-radio-button .el-radio-button__inner{border:none!important;box-shadow:none;border-radius:8px;color:var(--fc-secondary-text);font-weight:500;font-size:14px;line-height:20px;background:none;outline:none}.el-switch{--el-switch-on-color: var(--fc-deep-bg);--el-switch-off-color: var(--fc-light-bg);height:20px}.el-switch.is-checked .el-switch__core{border-color:var(--fc-primary-text)!important;background-color:var(--fc-primary-text)!important}.el-switch.is-checked .el-switch__core .el-switch__action{background:var(--fc-primary-bg)}.el-switch .el-switch__label.is-active{color:var(--fc-primary-text)}.fc-general-settings .el-form-item__content{line-height:120%}.el-tabs--border-card>.el-tabs__header .el-tabs__item.is-active{color:var(--fc-deep-bg)}.el-tabs--border-card>.el-tabs__header .el-tabs__item:not(.is-disabled):hover{color:var(--fc-deep-bg)}.el-input.fc_input input{border:1px solid var(--fc-secondary-border);border-radius:8px;box-shadow:none;padding:2px 16px;height:auto;margin:0;line-height:32px}.el-input.fc_input input:focus{border-color:var(--fc-deep-bg)!important}.el-checkbox.fc_checkbox{display:flex;align-items:center}.el-checkbox.fc_checkbox .el-checkbox__label{color:var(--fc-primary-text);font-size:14px;white-space:initial;padding-left:8px}.el-radio-group .el-radio-button .el-radio-button__inner{color:var(--fc-primary-text);padding:7px 15px;line-height:20px;background:none}.el-radio-group .el-radio-button.el-radio-button--small .el-radio-button__inner{padding:5px 15px}.el-radio-group .el-radio-button:first-child .el-radio-button__inner{border-radius:8px 0 0 8px}.el-radio-group .el-radio-button:last-child .el-radio-button__inner{border-radius:0 8px 8px 0}.el-radio-group .el-radio-button.is-active .el-radio-button__original-radio:not(:disabled)+.el-radio-button__inner{background-color:var(--fc-primary-text);border-color:var(--fc-deep-bg);color:var(--fc-text-inverse);box-shadow:none}.el-switch.is-checked .el-switch__core{background:var(--fc-deep-bg);border-color:var(--fc-deep-bg)}.el-radio-group.fluentcrm_line_items{width:100%;display:block;margin-bottom:20px;margin-top:20px}.el-radio-group.fluentcrm_line_items>label{display:block;margin-bottom:15px}.el-radio-group.fluentcrm_line_items>label:last-child{margin-bottom:0}.el-form .el-form-item .el-form-item__content .el-radio-group .el-radio{margin-right:8px}.el-form .el-form-item .el-form-item__content .el-radio-group .el-radio:last-child{margin-right:0}.fc-input-number-field{height:40px}.fc-input-number-field .el-input-number__decrease{border-top-left-radius:8px;border-bottom-left-radius:8px}.fc-input-number-field .el-input-number__increase{border-top-right-radius:8px;border-bottom-right-radius:8px}.fc-input-email,.fc-input-text{width:100%;padding:0 15px;height:40px;line-height:40px;font-size:14px;border:1px solid var(--fc-primary-border);border-radius:4px;transition:border-color .2s cubic-bezier(.645,.045,.355,1);box-sizing:border-box}.fc-input-text:focus{outline:none;border-color:var(--fc-deep-bg)}.fc-input-text:hover{border-color:var(--fc-secondary-border)}.el-form .el-form-item__label{margin:0 0 4px;color:var(--fc-primary-text);font-weight:500;font-size:14px;line-height:20px;padding:0}.el-menu-vertical-demo{min-height:80vh}.el-menu-item{margin-bottom:0;line-height:52px;height:52px}.el-menu-item.is-active{background:#2225301a}.el-breadcrumb.fcrm_breadcrumb_inline_edit .el-breadcrumb__item .fc_breadcrumb_title,.el-breadcrumb.fcrm_breadcrumb_inline_edit .el-breadcrumb__item .fc_funnel_breadcrumb_title{max-width:600px;cursor:pointer}.el-breadcrumb.fcrm_breadcrumb_inline_edit .el-breadcrumb__item.fc_breadcrumb_item .el-breadcrumb__inner{display:flex;align-items:flex-start;gap:6px}.el-breadcrumb.fcrm_breadcrumb_inline_edit .el-breadcrumb__item.fc_breadcrumb_item .fc_inline_editable{display:flex;align-items:center;gap:4px}.el-breadcrumb.fcrm_breadcrumb_inline_edit .el-breadcrumb__item.fc_breadcrumb_item .fc_inline_editable .fc_clickable_icon{cursor:pointer}.el-breadcrumb.fcrm_breadcrumb_inline_edit .el-breadcrumb__item.fc_breadcrumb_item .fc_inline_editable .el-button{margin:0;padding:8px 14px;border-radius:4px}.el-step__icon.is-text{vertical-align:middle}.el-scrollbar.el-cascader-menu{max-height:400px;overflow:auto}.el-cascader-menu__wrap.el-scrollbar__wrap{margin:0!important}.el-breadcrumb.fluentcrm_spaced_bottom{margin:0 0 20px;padding:0 0 10px}.fluentcrm_header_title .el-breadcrumb{padding-top:10px;margin-bottom:0}.el-notification.bottom_right.right,.el-notification.fc_bottom-right.right,body .el-notification.right{top:auto!important;bottom:20px!important}.fc_notify_z{z-index:9999999!important}.el-popover{padding:10px;text-align:left;word-break:break-all;border-color:var(--fc-primary-border)}.el-popover h3,.el-tooltip__popper h3{margin:0 0 10px}.fcrm_send_test_email_popover{z-index:999999!important}.el-tooltip__popper{z-index:10020!important}.el-tag+.el-tag{margin-left:10px}.el-tag{font-weight:400}ul.el-dropdown-menu{padding-top:5px}.el-dropdown-list-wrapper{padding:0}.el-dropdown-list-wrapper .group-title{display:block;padding:5px 10px;background-color:var(--fc-text-muted);color:var(--fc-text-inverse)}.el-dropdown-list-wrapper.el-popover{z-index:9999999999999!important}.el-progress_animated .el-progress-bar__inner{transform:translateZ(0);animation:indeterminate 2s infinite}.el-progress_animated .el-progress-bar__outer{background-color:#fbfb3f}.fc_abs_sidebar .el-badge__content.is-fixed{font-size:9px;border-radius:50%;line-height:16px}.fc_range_picker .el-range-editor--mini .el-range-separator{font-size:9px!important;min-width:20px}.fc_range_picker .el-range-editor--mini.el-input__inner{height:28px;max-width:200px}span.el-range-separator{min-width:20px}.el-picker-panel__sidebar .el-picker-panel__shortcut{line-height:20px;font-size:13px;padding:7px 10px}.el-picker-panel__sidebar .el-picker-panel__shortcut:hover{background:#409eff0f}.el-notification__content{text-align:left!important}.el-popover.fcrm_sort_popover{padding:20px!important;border-radius:8px!important;box-sizing:border-box;border:1px solid var(--fc-primary-border);max-height:350px;overflow-x:hidden}.el-popover.fcrm_sort_popover .el-popover__title{margin:0 0 16px;font-size:16px;font-weight:600;color:var(--fc-primary-text)}.el-popover.fcrm_sort_popover .fcrm_sorting_action_wrap{padding-left:10px}.el-popover.fcrm_sort_popover .fcrm_sorting_action_wrap .el-radio-group{display:flex;flex-direction:column;align-items:flex-start;gap:12px;width:100%}.el-popover.fcrm_sort_popover .fcrm_sorting_action_wrap .el-radio-group .el-radio{height:auto;margin:0;width:100%;display:flex;align-items:center}.el-popover.fcrm_sort_popover .fcrm_sorting_action_wrap .el-radio-group .el-radio .el-radio__label{font-size:14px;color:var(--fc-primary-text);white-space:initial;padding-left:8px}.el-popover.fcrm_link_stats_popover{-webkit-backdrop-filter:blur(7px);backdrop-filter:blur(7px);border:none;border-radius:var(--fcrm-border-radius-8, 8px);padding:0!important}.el-popover.fcrm_link_stats_popover *{box-sizing:border-box}.el-popover.fcrm_link_stats_popover .popper__arrow:after{border-bottom-color:var(--fc-deep-bg)}.el-popover.fcrm_link_stats_popover a:hover{text-decoration:underline}.el-popover.fcrm_link_stats_popover .fcrm_table_wrapper{overflow-x:auto;overflow-y:hidden}.el-popover.fcrm_link_stats_popover .fcrm_table_body .el-table__header tr th:first-child{border-top-left-radius:8px}.el-popover.fcrm_link_stats_popover .fcrm_table_body .el-table__header tr th:last-child{border-top-right-radius:8px}.el-popover.fcrm_link_stats_popover .fcrm_link_stats_metrics .fcrm_loader_wrap{padding:20px}.el-popover.fcrm_link_stats_popover .fcrm_link_stats_metrics .el-table{background:none}.el-popover.fcrm_link_stats_popover .fcrm_link_stats_metrics .el-table:before{display:none}.el-popover.fcrm_link_stats_popover .fcrm_link_stats_metrics .fluentcrm-pagination{padding-bottom:0}.el-popover.fc_addons_campaign_popover{max-height:500px;overflow-x:hidden}.el-popper.fc_select_campaigns_popover{border:1px solid var(--fc-primary-border);border-radius:8px;box-shadow:0 8px 30px #1b25331a;padding:12px;box-sizing:border-box}.el-popper.fc_select_campaigns_popover .el-select-dropdown__list{padding:0}.el-popper.fc_select_campaigns_popover .el-select-dropdown__list .el-select-dropdown__item{margin:0;border:none;width:100%;text-align:left;line-height:24px;padding:4px 12px;border-radius:8px;display:flex;align-items:center;gap:10px;flex-wrap:wrap}.el-dialog.fcrm_abandon_cart_details_popover{max-width:800px;width:100%;min-width:auto!important}.el-dialog.fcrm_abandon_cart_details_popover .el-dialog__header{background:none;padding:15px 20px;border-color:var(--fc-secondary-bg)}.el-dialog.fcrm_abandon_cart_details_popover .el-dialog__header .el-dialog__title{color:var(--fc-primary-text)}.el-notification h1,.el-notification h2,.el-notification h3,.el-notification h4,.el-notification h5,.el-notification h6,.el-notification p{margin:0;padding:0;color:var(--fc-secondary-text)}.el-notification h1,.el-notification h2,.el-notification h3,.el-notification h4,.el-notification h5,.el-notification h6,.el-notification .el-notification__title{color:var(--fc-primary-text)}.el-progress-bar__outer{background:var(--fc-light-bg)}body.toplevel_page_fluentcrm-admin .el-popper.el-picker__popper{padding:0}.el-picker-panel{--el-bg-color-overlay: var(--fc-primary-bg)}.el-popper.el-picker__popper{padding:0}.el-popper.el-picker__popper .el-picker-panel [slot=sidebar]+.el-picker-panel__body,.el-popper.el-picker__popper .el-picker-panel__sidebar+.el-picker-panel__body{margin-left:160px}.el-popper.el-picker__popper .el-picker-panel{border-radius:var(--fcrm-border-radius-8)}.el-popper.el-picker__popper .el-picker-panel__body-wrapper .el-picker-panel__sidebar{padding:16px;width:160px;border-right:1px solid var(--fc-primary-border)}.el-popper.el-picker__popper .el-picker-panel__body-wrapper .el-picker-panel__sidebar .el-picker-panel__shortcut{border-radius:8px;font-weight:500;font-size:12px;line-height:16px;color:var(--fc-secondary-text);padding:8px 8px 8px 10px;margin-bottom:4px;transition:.2s;-webkit-transition:.2s}.el-popper.el-picker__popper .el-picker-panel__body-wrapper .el-picker-panel__sidebar .el-picker-panel__shortcut:last-child{margin-bottom:0}.el-popper.el-picker__popper .el-picker-panel__body-wrapper .el-picker-panel__sidebar .el-picker-panel__shortcut:hover{color:var(--fc-primary-text);background:var(--fc-secondary-bg)}.el-popper.el-picker__popper .el-picker-panel__body-wrapper .el-picker-panel__body .el-date-range-picker__header{background:var(--fc-secondary-bg);border-radius:var(--fcrm-border-radius-8);padding:6px;height:auto}.el-popper.el-picker__popper .el-picker-panel__body-wrapper .el-picker-panel__body .el-date-range-picker__header>div{line-height:1;display:flex;align-items:center;gap:8px;justify-content:center}.el-popper.el-picker__popper .el-picker-panel__body-wrapper .el-picker-panel__body .el-date-range-picker__header .el-date-range-picker__header-label{display:block;font-weight:500;font-size:14px;line-height:20px;text-align:center;color:var(--fc-secondary-text)}.el-popper.el-picker__popper .el-picker-panel__body-wrapper .el-picker-panel__body .el-date-range-picker__header .el-picker-panel__icon-btn{margin-top:3px}.el-popper.el-picker__popper .el-picker-panel__body-wrapper .el-picker-panel__body .el-date-table tbody tr th{color:var(--fc-text-muted);font-weight:400;font-size:14px;line-height:20px;padding-left:0;padding-right:0;border:none}.el-popper.el-picker__popper .el-picker-panel__body-wrapper .el-picker-panel__body .el-date-table tbody tr td.next-month .el-date-table-cell__text,.el-popper.el-picker__popper .el-picker-panel__body-wrapper .el-picker-panel__body .el-date-table tbody tr td.prev-month .el-date-table-cell__text{color:var(--fc-secondary-border)}.el-popper.el-picker__popper .el-picker-panel__body-wrapper .el-picker-panel__body .el-date-table tbody tr td.available.in-range.start-date .el-date-table-cell{border-radius:var(--fcrm-border-radius-8) 0 0 var(--fcrm-border-radius-8)}.el-popper.el-picker__popper .el-picker-panel__body-wrapper .el-picker-panel__body .el-date-table tbody tr td.available.in-range.start-date .el-date-table-cell .el-date-table-cell__text{border-radius:var(--fcrm-border-radius-8);font-weight:500}.el-popper.el-picker__popper .el-picker-panel__body-wrapper .el-picker-panel__body .el-date-table tbody tr td.available.in-range.end-date .el-date-table-cell{border-radius:0 var(--fcrm-border-radius-8) var(--fcrm-border-radius-8) 0}.el-popper.el-picker__popper .el-picker-panel__body-wrapper .el-picker-panel__body .el-date-table tbody tr td.available.in-range.end-date .el-date-table-cell .el-date-table-cell__text{font-weight:500;border-radius:var(--fcrm-border-radius-8)}.el-popper.el-picker__popper.fcrm_date_time_picker .el-picker-panel__content,.el-popper.fcrm_mail_config_datetime .el-picker-panel__content{width:auto}.el-date-editor{justify-content:flex-start;padding:0;background:none}.el-date-editor .el-input__wrapper{padding:2px 10px}.el-date-editor .el-range-input{height:auto;line-height:20px;font-weight:400;font-size:13px;color:var(--fc-primary-text)}.el-date-editor .el-range-separator{line-height:1}.fcrm_range_picker{display:flex;align-items:center;gap:8px;flex-wrap:wrap}.fcrm_range_picker .el-date-editor.el-range-editor{--el-date-editor-width: 240px;width:240px!important;min-width:0;flex:0 0 auto}.fcrm_range_picker .el-select{min-width:150px;flex:0 0 auto}.fcrm_range_picker .fcrm_range_compare_date.el-date-editor.el-range-editor{--el-date-editor-width: 240px;width:240px!important}@media (max-width: 768px){.fcrm_range_picker{flex-wrap:wrap}.fcrm_range_picker .el-date-editor.el-range-editor{--el-date-editor-width: 100%;width:100%!important}}.fcrm_range_picker .el-date-editor .el-input__icon{font-size:20px}.fcrm_range_picker .el-date-editor .el-input__icon svg{width:20px;height:20px}.el-select{width:100%}.el-select input{margin:0;padding:0}.el-select.el-select--small .el-select__wrapper{min-height:32px}.el-select__wrapper{min-height:36px;background:var(--fc-primary-bg);border-radius:var(--fcrm-border-radius-8);box-shadow:0 1px 2px #0a0d1408;border:1px solid var(--fc-primary-border);padding:2px 10px}.el-select__wrapper.is-focus,.el-select__wrapper.is-focused{border-color:var(--fc-primary-text)}.el-select__wrapper.is-disabled{opacity:.5}.el-select__wrapper .el-select__selection{display:flex;align-items:center;gap:4px;max-width:100%}.el-select__wrapper .el-select__selection .el-select__selected-item{flex-shrink:1;min-width:0;overflow:hidden}.el-select__wrapper .el-select__selection .el-select__selected-item.el-select__placeholder.is-transparent,.el-select__wrapper .el-select__selection .el-select__selected-item .el-select__placeholder.is-transparent{color:var(--el-text-color-placeholder)}.el-select__wrapper .el-select__selection .el-select__selected-item .el-tag{background:var(--fc-secondary-bg);color:var(--fc-secondary-text);border-radius:6px;font-weight:500;font-size:12px;line-height:16px;padding:2px 8px;border:none;height:auto;margin:0 2px;max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.el-select__wrapper .el-select__selection .el-select__selected-item .el-tag .el-select__tags-text{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;display:block;max-width:100%}.el-select__wrapper .el-select__selection .el-select__selected-item .el-tag .el-tag__close{color:var(--fc-secondary-text);font-size:12px;margin-inline-start:4px}.el-select__wrapper .el-select__selection .el-select__selected-item .el-tag .el-tag__close:hover{background:transparent;color:var(--fc-primary-text)}.el-select__wrapper .el-select__selection .el-select__selected-item .el-tag.el-tag--info{background:var(--fc-secondary-bg);color:var(--fc-secondary-text)}.el-select__wrapper .el-select__selection .el-select__collapse-tag{background:var(--fc-secondary-bg);border:none;border-radius:6px;padding:4px 8px;height:auto;font-size:12px;font-weight:500;line-height:16px;color:var(--fc-secondary-text);margin:0 2px}.el-select__wrapper .el-select__selection .el-select__collapse-tag .el-select__tags-text{color:var(--fc-secondary-text)}.el-select__wrapper .el-select__selection .el-select__input-wrapper{flex-shrink:0;min-width:20px}.el-select__wrapper .el-select__placeholder,.el-select__wrapper .el-select__selected-item{font-size:14px;font-weight:500;line-height:20px;color:var(--fc-text-muted)}.el-select__wrapper .el-select__selected-item{color:var(--fc-secondary-text);font-weight:400}.el-select__wrapper .el-select__caret{color:var(--fc-secondary-text);flex-shrink:0}.el-select .el-tag__close.el-icon-close{right:-5px}.el-select.fcrm_background_select .el-select__wrapper{border:1px solid var(--fc-primary-border);background:var(--fc-secondary-bg)}.el-select__tags{padding-left:10px}.el-select__tags input{border:none}.el-select__tags input:focus{border:none;box-shadow:none;outline:none}.el-radio{margin:0;height:auto}.el-radio__input input{opacity:0;margin:0}.el-radio__input.is-checked .el-radio__inner{background:var(--fc-deep-bg);border-color:var(--fc-deep-bg)}.el-radio__input.is-checked+.el-radio__label{color:var(--fc-deep-bg)}.el-radio__label{font-weight:400;font-size:14px;line-height:20px;color:var(--fc-primary-text)}.el-radio__inner{width:16px;height:16px;border:1.5px solid var(--fc-primary-border);background:none}.el-radio__inner:after{width:8px;height:8px;background:var(--fc-primary-bg)}.fcrm-radio-group{display:flex;flex-direction:column;gap:12px;align-items:flex-start}.fcrm-radio-group .el-radio{white-space:break-spaces;display:flex;align-items:center;gap:8px;margin-right:0;height:auto}.fcrm-radio-group .el-radio .el-radio__input{margin-top:0}.fcrm-radio-group .el-radio .el-radio__label{padding-left:0}.fcrm-radio-group .el-radio:hover .el-radio__inner{border-color:var(--fc-deep-bg)}.fcrm-radio-content{display:flex;gap:4px;align-items:center;flex-wrap:wrap}.fcrm-radio-label{font-size:14px;font-weight:400;line-height:20px;letter-spacing:-.084px;color:var(--fc-primary-text)}.fcrm-radio-sublabel{font-size:12px;font-weight:400;line-height:16px;color:var(--fc-secondary-text)}.el-radio-button__inner{border-color:var(--fc-primary-border)}.el-checkbox{height:auto}.el-checkbox__input.is-indeterminate .el-checkbox__inner,.el-checkbox__input.is-checked .el-checkbox__inner{background:var(--fc-deep-bg);border-color:var(--fc-deep-bg)}.el-checkbox__input.is-indeterminate .el-checkbox__inner:after,.el-checkbox__input.is-checked .el-checkbox__inner:after{border-color:var(--fc-text-inverse)}.el-checkbox__original{margin:0}.el-checkbox__inner{width:16px;height:16px;border-radius:4px;background:none;border-color:var(--fc-primary-border)}.el-checkbox__inner:hover{border-color:var(--fc-deep-bg)}.el-checkbox .fcrm_checkbox_text{display:flex;flex-direction:column}.el-checkbox .fcrm_checkbox_text_title{color:var(--fc-primary-text)}.el-checkbox .fcrm_checkbox_text_desc{margin-top:4px;color:var(--fc-secondary-text);font-weight:400;font-size:12px;font-style:normal;line-height:16px}.el-dropdown-menu__item{line-height:22px;padding:7px 20px}.el-popper.is-dark{background:#151d26}.el-popper.is-dark>.el-popper__arrow:before{background:#151d26}.el-popper.fcrm_ai_summary_popover{padding:4px}.el-popper{z-index:100000!important;box-shadow:0 16px 32px -12px #0e121b1a;border-radius:8px;padding:8px;box-sizing:border-box}.el-popper *{box-sizing:border-box}.el-popper .el-select-dropdown{min-width:auto!important;max-width:300px}.el-popper .el-select-dropdown.is-multiple .el-select-dropdown__list .el-select-dropdown__item,.el-popper .el-select-dropdown.is-multiple .el-select-dropdown__list .el-dropdown-menu__item,.el-popper .el-select-dropdown.is-multiple .el-dropdown-menu .el-select-dropdown__item,.el-popper .el-select-dropdown.is-multiple .el-dropdown-menu .el-dropdown-menu__item{padding-inline-end:30px}.el-popper.is-light{border:1px solid var(--fc-primary-border);background:var(--fc-primary-bg)}.el-popper .el-select-dropdown__list,.el-popper .el-dropdown-menu{padding:0;background:none;box-shadow:none;border:none}.el-popper .el-select-dropdown__list .el-select-dropdown__item,.el-popper .el-select-dropdown__list .el-dropdown-menu__item,.el-popper .el-dropdown-menu .el-select-dropdown__item,.el-popper .el-dropdown-menu .el-dropdown-menu__item{height:auto;font-weight:400;font-size:14px;line-height:20px;border-radius:8px;padding:7px 10px;margin:0;word-break:break-word;overflow-wrap:break-word;white-space:wrap;display:flex;align-items:center;gap:8px;transition:.2s;-webkit-transition:.2s}.el-popper .el-select-dropdown__list .el-select-dropdown__item:not(.is-disabled):focus,.el-popper .el-select-dropdown__list .el-select-dropdown__item:not(.is-disabled):active,.el-popper .el-select-dropdown__list .el-select-dropdown__item:hover,.el-popper .el-select-dropdown__list .el-dropdown-menu__item:not(.is-disabled):focus,.el-popper .el-select-dropdown__list .el-dropdown-menu__item:not(.is-disabled):active,.el-popper .el-select-dropdown__list .el-dropdown-menu__item:hover,.el-popper .el-dropdown-menu .el-select-dropdown__item:not(.is-disabled):focus,.el-popper .el-dropdown-menu .el-select-dropdown__item:not(.is-disabled):active,.el-popper .el-dropdown-menu .el-select-dropdown__item:hover,.el-popper .el-dropdown-menu .el-dropdown-menu__item:not(.is-disabled):focus,.el-popper .el-dropdown-menu .el-dropdown-menu__item:not(.is-disabled):active,.el-popper .el-dropdown-menu .el-dropdown-menu__item:hover{background:var(--fc-secondary-bg)}.el-popper .el-select-dropdown__list .el-select-dropdown__item:not(.is-disabled):focus.fcrm_danger_action,.el-popper .el-select-dropdown__list .el-select-dropdown__item:not(.is-disabled):active.fcrm_danger_action,.el-popper .el-select-dropdown__list .el-select-dropdown__item:hover.fcrm_danger_action,.el-popper .el-select-dropdown__list .el-dropdown-menu__item:not(.is-disabled):focus.fcrm_danger_action,.el-popper .el-select-dropdown__list .el-dropdown-menu__item:not(.is-disabled):active.fcrm_danger_action,.el-popper .el-select-dropdown__list .el-dropdown-menu__item:hover.fcrm_danger_action,.el-popper .el-dropdown-menu .el-select-dropdown__item:not(.is-disabled):focus.fcrm_danger_action,.el-popper .el-dropdown-menu .el-select-dropdown__item:not(.is-disabled):active.fcrm_danger_action,.el-popper .el-dropdown-menu .el-select-dropdown__item:hover.fcrm_danger_action,.el-popper .el-dropdown-menu .el-dropdown-menu__item:not(.is-disabled):focus.fcrm_danger_action,.el-popper .el-dropdown-menu .el-dropdown-menu__item:not(.is-disabled):active.fcrm_danger_action,.el-popper .el-dropdown-menu .el-dropdown-menu__item:hover.fcrm_danger_action{background:var(--el-color-danger-light-9);color:var(--el-color-danger)}.el-popper .el-select-dropdown__list .el-select-dropdown__item .el-button .el-icon,.el-popper .el-select-dropdown__list .el-dropdown-menu__item .el-button .el-icon,.el-popper .el-dropdown-menu .el-select-dropdown__item .el-button .el-icon,.el-popper .el-dropdown-menu .el-dropdown-menu__item .el-button .el-icon{margin:0}.el-popper .el-select-dropdown__list .el-select-dropdown__item .el-button>span,.el-popper .el-select-dropdown__list .el-dropdown-menu__item .el-button>span,.el-popper .el-dropdown-menu .el-select-dropdown__item .el-button>span,.el-popper .el-dropdown-menu .el-dropdown-menu__item .el-button>span{gap:6px;min-width:0}.el-popper .el-select-dropdown__list .el-select-dropdown__item .el-button:not(.el-button--primary),.el-popper .el-select-dropdown__list .el-dropdown-menu__item .el-button:not(.el-button--primary),.el-popper .el-dropdown-menu .el-select-dropdown__item .el-button:not(.el-button--primary),.el-popper .el-dropdown-menu .el-dropdown-menu__item .el-button:not(.el-button--primary){width:100%;justify-content:flex-start;background:none;border:none;color:var(--fc-primary-text);padding:0}.el-popper .el-select-dropdown__list .el-select-dropdown__item .el-tooltip__trigger,.el-popper .el-select-dropdown__list .el-select-dropdown__item .el-popover__reference,.el-popper .el-select-dropdown__list .el-dropdown-menu__item .el-tooltip__trigger,.el-popper .el-select-dropdown__list .el-dropdown-menu__item .el-popover__reference,.el-popper .el-dropdown-menu .el-select-dropdown__item .el-tooltip__trigger,.el-popper .el-dropdown-menu .el-select-dropdown__item .el-popover__reference,.el-popper .el-dropdown-menu .el-dropdown-menu__item .el-tooltip__trigger,.el-popper .el-dropdown-menu .el-dropdown-menu__item .el-popover__reference{padding:0;display:flex;align-items:center;gap:4px;width:100%}.el-popper .el-select-dropdown__list .el-select-dropdown__item .el-tooltip__trigger .el-icon,.el-popper .el-select-dropdown__list .el-select-dropdown__item .el-popover__reference .el-icon,.el-popper .el-select-dropdown__list .el-dropdown-menu__item .el-tooltip__trigger .el-icon,.el-popper .el-select-dropdown__list .el-dropdown-menu__item .el-popover__reference .el-icon,.el-popper .el-dropdown-menu .el-select-dropdown__item .el-tooltip__trigger .el-icon,.el-popper .el-dropdown-menu .el-select-dropdown__item .el-popover__reference .el-icon,.el-popper .el-dropdown-menu .el-dropdown-menu__item .el-tooltip__trigger .el-icon,.el-popper .el-dropdown-menu .el-dropdown-menu__item .el-popover__reference .el-icon{display:block;margin:0;width:auto;height:auto}.el-popper .el-select-dropdown__list .el-select-dropdown__item .icon svg,.el-popper .el-select-dropdown__list .el-dropdown-menu__item .icon svg,.el-popper .el-dropdown-menu .el-select-dropdown__item .icon svg,.el-popper .el-dropdown-menu .el-dropdown-menu__item .icon svg{display:block;width:18px;height:18px}.el-popper .el-select-dropdown__list .el-select-dropdown__item.fcrm_danger_action:hover,.el-popper .el-select-dropdown__list .el-dropdown-menu__item.fcrm_danger_action:hover,.el-popper .el-dropdown-menu .el-select-dropdown__item.fcrm_danger_action:hover,.el-popper .el-dropdown-menu .el-dropdown-menu__item.fcrm_danger_action:hover{background:var(--el-color-danger-light-9);color:var(--el-color-danger)}.el-popper .el-select-dropdown__list .el-select-dropdown__item:after,.el-popper .el-select-dropdown__list .el-dropdown-menu__item:after,.el-popper .el-dropdown-menu .el-select-dropdown__item:after,.el-popper .el-dropdown-menu .el-dropdown-menu__item:after{right:10px}.el-popper .el-select-dropdown__list .fc-dropdown-items-label,.el-popper .el-dropdown-menu .fc-dropdown-items-label{background:none!important;cursor:text}.el-popper .el-select-dropdown__list .fc-dropdown-items-label:hover,.el-popper .el-dropdown-menu .fc-dropdown-items-label:hover{background:none!important}.el-popper .el-select .el-popper{width:100%}.el-popper.fcrm_action_selector_popover{border-radius:8px;padding:12px;border:1px solid var(--fc-primary-border);box-shadow:0 16px 32px -12px #0e121b1a}.el-popper.fcrm_send_test_email_popover{border-radius:8px;padding:12px;border:1px solid var(--fc-primary-border);box-shadow:0 16px 32px -12px #0e121b24}.el-popper.fcrm_send_test_email_popover .fcrm_send_test_email_content{display:flex;flex-direction:column;gap:16px}.el-popper.fcrm_send_test_email_popover .fcrm_send_test_email_content_header{display:flex;flex-direction:column;gap:4px}.el-popper.fcrm_send_test_email_popover .fcrm_send_test_email_content .fcrm_input_hint{display:flex;align-items:flex-start;gap:4px;color:var(--fc-secondary-text);font-size:12px;line-height:16px;font-weight:400;margin:4px 0 0}.el-popper.fcrm_send_test_email_popover .fcrm_send_test_email_content .fcrm_input_hint .icon,.el-popper.fcrm_send_test_email_popover .fcrm_send_test_email_content .fcrm_input_hint .el-icon{color:var(--fc-text-muted);display:block}.el-popper.fcrm_send_test_email_popover .fcrm_send_test_email_content .fcrm_input_hint .icon svg,.el-popper.fcrm_send_test_email_popover .fcrm_send_test_email_content .fcrm_input_hint .el-icon svg{display:block;width:14px;height:14px}.el-popper.fcrm_send_test_email_popover .fcrm_send_test_email_title{margin:0;color:var(--fc-primary-text);font-weight:500;font-size:16px;line-height:24px}.el-popper.fcrm_send_test_email_popover .fcrm_send_test_email_description{margin:0;color:var(--fc-secondary-text);font-size:14px;line-height:20px;font-weight:400}.el-popper.fcrm_send_test_email_popover .fcrm_send_test_email_input_wrap{display:flex;gap:8px}.el-popper.fcrm_send_test_email_popover .fcrm_send_test_email_input_wrap .el-input__wrapper{box-shadow:0 1px 2px #0a0d1408;border:1px solid var(--fc-primary-border);border-radius:8px}.el-popper.fcrm_send_test_email_popover .fcrm_send_test_email_input_wrap .el-input__wrapper.is-focused,.el-popper.fcrm_send_test_email_popover .fcrm_send_test_email_input_wrap .el-input__wrapper.is-focus{border-color:var(--fc-primary-text)}.el-popper.fcrm_select_options_wordbreak .el-select-dropdown__list .el-select-dropdown__item{white-space:normal}.el-cascader-node.in-active-path{font-weight:500;background:var(--fc-weak-bg-25)}.el-alert{padding:14px;align-items:flex-start;gap:12px}.el-alert__icon{font-size:15px;width:auto;height:auto;margin:2px 0 0;color:var(--fc-text-muted)}.el-alert__title{font-weight:500;font-size:14px;line-height:20px;margin:0}.el-alert__description{color:var(--fc-secondary-text);margin:0;font-weight:400;font-size:14px;line-height:20px}.fcrm-layout-width{margin-left:auto;margin-right:auto;max-width:1260px}@media (min-width: 1920px){.fcrm-layout-width{max-width:1600px}}.fluentcrm_min_bg{min-height:80vh;background-color:var(--fc-secondary-bg)}table.fc_horizontal_table{width:100%;background:var(--fc-primary-bg);margin-bottom:20px;border-collapse:separate;border:1px solid var(--fc-primary-border);border-radius:var(--fcrm-border-radius-8);border-spacing:0}table.fc_horizontal_table.v_top tr td{vertical-align:top}table.fc_horizontal_table thead tr th{padding:8px 12px;text-align:left;background:var(--fc-secondary-bg);border-right:none;border-right-color:currentcolor;border-left:none;border-left-color:currentcolor;font-size:14px;font-weight:500;line-height:20px;color:var(--fc-secondary-text);border-bottom:1px solid var(--fc-primary-border)}table.fc_horizontal_table tr td{padding:12px;border-bottom:1px solid var(--fc-primary-border);text-align:left}table.fc_horizontal_table tr:last-child td{border-bottom:none}.fluentcrm_body.fluentcrm_tile_bg{background-image:url(../../images/tile.png);background-repeat:repeat;filter:alpha(opacity=1);background-size:30px 30px;background-color:var(--fc-secondary-bg)}.fcrm_fluentcrm_header{display:flex;justify-content:space-between;align-items:center;gap:12px;margin:24px 0;min-height:40px}.fcrm_fluentcrm_header h3{letter-spacing:-.27px;color:var(--fc-primary-text);margin:0}.fcrm_fluentcrm_header .fcrm_fluentcrm_header_title{flex:1 0 0;display:flex;flex-direction:column;justify-content:center;gap:4px;min-height:40px}.fcrm_fluentcrm_header .fcrm_fluentcrm_header_title h3{letter-spacing:-.27px;color:var(--fc-primary-text);margin:0;line-height:24px}.fcrm_fluentcrm_header .fcrm_fluentcrm_header_title p{letter-spacing:-.084px;color:var(--fc-secondary-text);margin:0;line-height:20px}.fcrm_fluentcrm_header .fcrm_fluentcrm-templates-action-buttons{display:flex;gap:12px;align-items:center;min-height:40px;flex-shrink:0}.fcrm_fluentcrm_header .fcrm_fluentcrm-templates-action-buttons .el-button{min-height:36px;height:auto;display:inline-flex;align-items:center;justify-content:center;font-size:14px;font-weight:500;line-height:20px;letter-spacing:-.084px;border-radius:8px;padding:8px;transition:all .2s ease}.fcrm_fluentcrm_header .fcrm_fluentcrm-templates-action-buttons .el-button.fcrm_contacts_import_button,.fcrm_fluentcrm_header .fcrm_fluentcrm-templates-action-buttons .el-button.fcrm_contacts_export_button,.fcrm_fluentcrm_header .fcrm_fluentcrm-templates-action-buttons .el-button.fcrm_dynamic_segments_import_button,.fcrm_fluentcrm_header .fcrm_fluentcrm-templates-action-buttons .el-button.fcrm_lists_import_button,.fcrm_fluentcrm_header .fcrm_fluentcrm-templates-action-buttons .el-button.fcrm_tags_import_button,.fcrm_fluentcrm_header .fcrm_fluentcrm-templates-action-buttons .el-button.fcrm_lists_export_button,.fcrm_fluentcrm_header .fcrm_fluentcrm-templates-action-buttons .el-button.fcrm_tags_export_button,.fcrm_fluentcrm_header .fcrm_fluentcrm-templates-action-buttons .el-button.fcrm_companies_export_button,.fcrm_fluentcrm_header .fcrm_fluentcrm-templates-action-buttons .el-button.fcrm_clear_filters_button{background:var(--fc-primary-bg)!important;border:1px solid var(--fc-primary-border)!important;color:var(--fc-secondary-text)!important;box-shadow:none!important}.fcrm_fluentcrm_header .fcrm_fluentcrm-templates-action-buttons .el-button.fcrm_contacts_import_button:hover,.fcrm_fluentcrm_header .fcrm_fluentcrm-templates-action-buttons .el-button.fcrm_contacts_export_button:hover,.fcrm_fluentcrm_header .fcrm_fluentcrm-templates-action-buttons .el-button.fcrm_dynamic_segments_import_button:hover,.fcrm_fluentcrm_header .fcrm_fluentcrm-templates-action-buttons .el-button.fcrm_lists_import_button:hover,.fcrm_fluentcrm_header .fcrm_fluentcrm-templates-action-buttons .el-button.fcrm_tags_import_button:hover,.fcrm_fluentcrm_header .fcrm_fluentcrm-templates-action-buttons .el-button.fcrm_lists_export_button:hover,.fcrm_fluentcrm_header .fcrm_fluentcrm-templates-action-buttons .el-button.fcrm_tags_export_button:hover,.fcrm_fluentcrm_header .fcrm_fluentcrm-templates-action-buttons .el-button.fcrm_companies_export_button:hover,.fcrm_fluentcrm_header .fcrm_fluentcrm-templates-action-buttons .el-button.fcrm_clear_filters_button:hover{background:var(--fc-secondary-bg)!important;border-color:var(--fc-secondary-border)!important;color:var(--fc-primary-text)!important}.fcrm_fluentcrm_header .fcrm_fluentcrm-templates-action-buttons .el-button.fcrm_contacts_import_button:active,.fcrm_fluentcrm_header .fcrm_fluentcrm-templates-action-buttons .el-button.fcrm_contacts_import_button:focus,.fcrm_fluentcrm_header .fcrm_fluentcrm-templates-action-buttons .el-button.fcrm_contacts_export_button:active,.fcrm_fluentcrm_header .fcrm_fluentcrm-templates-action-buttons .el-button.fcrm_contacts_export_button:focus,.fcrm_fluentcrm_header .fcrm_fluentcrm-templates-action-buttons .el-button.fcrm_dynamic_segments_import_button:active,.fcrm_fluentcrm_header .fcrm_fluentcrm-templates-action-buttons .el-button.fcrm_dynamic_segments_import_button:focus,.fcrm_fluentcrm_header .fcrm_fluentcrm-templates-action-buttons .el-button.fcrm_lists_import_button:active,.fcrm_fluentcrm_header .fcrm_fluentcrm-templates-action-buttons .el-button.fcrm_lists_import_button:focus,.fcrm_fluentcrm_header .fcrm_fluentcrm-templates-action-buttons .el-button.fcrm_tags_import_button:active,.fcrm_fluentcrm_header .fcrm_fluentcrm-templates-action-buttons .el-button.fcrm_tags_import_button:focus,.fcrm_fluentcrm_header .fcrm_fluentcrm-templates-action-buttons .el-button.fcrm_lists_export_button:active,.fcrm_fluentcrm_header .fcrm_fluentcrm-templates-action-buttons .el-button.fcrm_lists_export_button:focus,.fcrm_fluentcrm_header .fcrm_fluentcrm-templates-action-buttons .el-button.fcrm_tags_export_button:active,.fcrm_fluentcrm_header .fcrm_fluentcrm-templates-action-buttons .el-button.fcrm_tags_export_button:focus,.fcrm_fluentcrm_header .fcrm_fluentcrm-templates-action-buttons .el-button.fcrm_companies_export_button:active,.fcrm_fluentcrm_header .fcrm_fluentcrm-templates-action-buttons .el-button.fcrm_companies_export_button:focus,.fcrm_fluentcrm_header .fcrm_fluentcrm-templates-action-buttons .el-button.fcrm_clear_filters_button:active,.fcrm_fluentcrm_header .fcrm_fluentcrm-templates-action-buttons .el-button.fcrm_clear_filters_button:focus{background:var(--fc-secondary-bg)!important;border-color:var(--fc-secondary-border)!important;color:var(--fc-primary-text)!important;box-shadow:none!important}.fcrm_fluentcrm_header .fcrm_fluentcrm-templates-action-buttons .el-button.fcrm_contacts_import_button svg,.fcrm_fluentcrm_header .fcrm_fluentcrm-templates-action-buttons .el-button.fcrm_contacts_export_button svg,.fcrm_fluentcrm_header .fcrm_fluentcrm-templates-action-buttons .el-button.fcrm_dynamic_segments_import_button svg,.fcrm_fluentcrm_header .fcrm_fluentcrm-templates-action-buttons .el-button.fcrm_lists_import_button svg,.fcrm_fluentcrm_header .fcrm_fluentcrm-templates-action-buttons .el-button.fcrm_tags_import_button svg,.fcrm_fluentcrm_header .fcrm_fluentcrm-templates-action-buttons .el-button.fcrm_lists_export_button svg,.fcrm_fluentcrm_header .fcrm_fluentcrm-templates-action-buttons .el-button.fcrm_tags_export_button svg,.fcrm_fluentcrm_header .fcrm_fluentcrm-templates-action-buttons .el-button.fcrm_companies_export_button svg,.fcrm_fluentcrm_header .fcrm_fluentcrm-templates-action-buttons .el-button.fcrm_clear_filters_button svg{margin-right:4px;width:16px;height:16px}.fcrm_fluentcrm_header .fcrm_fluentcrm-templates-action-buttons .el-button.fcrm_contacts_import_button span,.fcrm_fluentcrm_header .fcrm_fluentcrm-templates-action-buttons .el-button.fcrm_contacts_export_button span,.fcrm_fluentcrm_header .fcrm_fluentcrm-templates-action-buttons .el-button.fcrm_dynamic_segments_import_button span,.fcrm_fluentcrm_header .fcrm_fluentcrm-templates-action-buttons .el-button.fcrm_lists_import_button span,.fcrm_fluentcrm_header .fcrm_fluentcrm-templates-action-buttons .el-button.fcrm_tags_import_button span,.fcrm_fluentcrm_header .fcrm_fluentcrm-templates-action-buttons .el-button.fcrm_lists_export_button span,.fcrm_fluentcrm_header .fcrm_fluentcrm-templates-action-buttons .el-button.fcrm_tags_export_button span,.fcrm_fluentcrm_header .fcrm_fluentcrm-templates-action-buttons .el-button.fcrm_companies_export_button span,.fcrm_fluentcrm_header .fcrm_fluentcrm-templates-action-buttons .el-button.fcrm_clear_filters_button span{display:flex;align-items:center;gap:4px}.fcrm_fluentcrm_header .fcrm_fluentcrm-templates-action-buttons .el-button.fcrm_dynamic_segments_create_button,.fcrm_fluentcrm_header .fcrm_fluentcrm-templates-action-buttons .el-button.fcrm_lists_create_button,.fcrm_fluentcrm_header .fcrm_fluentcrm-templates-action-buttons .el-button.fcrm_tags_create_button,.fcrm_fluentcrm_header .fcrm_fluentcrm-templates-action-buttons .el-button.fcrm_companies_create_button,.fcrm_fluentcrm_header .fcrm_fluentcrm-templates-action-buttons .el-button.el-button--primary{background:var(--fc-deep-bg)!important;border-color:var(--fc-deep-bg)!important;color:var(--fc-text-inverse)!important;box-shadow:none!important}.fcrm_fluentcrm_header .fcrm_fluentcrm-templates-action-buttons .el-button.fcrm_dynamic_segments_create_button:hover,.fcrm_fluentcrm_header .fcrm_fluentcrm-templates-action-buttons .el-button.fcrm_lists_create_button:hover,.fcrm_fluentcrm_header .fcrm_fluentcrm-templates-action-buttons .el-button.fcrm_tags_create_button:hover,.fcrm_fluentcrm_header .fcrm_fluentcrm-templates-action-buttons .el-button.fcrm_companies_create_button:hover,.fcrm_fluentcrm_header .fcrm_fluentcrm-templates-action-buttons .el-button.el-button--primary:hover{background:var(--fc-primary-text)!important;border-color:var(--fc-primary-text)!important}.fcrm_fluentcrm_header .fcrm_fluentcrm-templates-action-buttons .el-button.fcrm_dynamic_segments_create_button:active,.fcrm_fluentcrm_header .fcrm_fluentcrm-templates-action-buttons .el-button.fcrm_dynamic_segments_create_button:focus,.fcrm_fluentcrm_header .fcrm_fluentcrm-templates-action-buttons .el-button.fcrm_lists_create_button:active,.fcrm_fluentcrm_header .fcrm_fluentcrm-templates-action-buttons .el-button.fcrm_lists_create_button:focus,.fcrm_fluentcrm_header .fcrm_fluentcrm-templates-action-buttons .el-button.fcrm_tags_create_button:active,.fcrm_fluentcrm_header .fcrm_fluentcrm-templates-action-buttons .el-button.fcrm_tags_create_button:focus,.fcrm_fluentcrm_header .fcrm_fluentcrm-templates-action-buttons .el-button.fcrm_companies_create_button:active,.fcrm_fluentcrm_header .fcrm_fluentcrm-templates-action-buttons .el-button.fcrm_companies_create_button:focus,.fcrm_fluentcrm_header .fcrm_fluentcrm-templates-action-buttons .el-button.el-button--primary:active,.fcrm_fluentcrm_header .fcrm_fluentcrm-templates-action-buttons .el-button.el-button--primary:focus{background:var(--fc-primary-text)!important;border-color:var(--fc-primary-text)!important;box-shadow:none!important}.fcrm_fluentcrm_header .fcrm_fluentcrm-templates-action-buttons .el-button.fcrm_dynamic_segments_create_button .el-icon,.fcrm_fluentcrm_header .fcrm_fluentcrm-templates-action-buttons .el-button.fcrm_lists_create_button .el-icon,.fcrm_fluentcrm_header .fcrm_fluentcrm-templates-action-buttons .el-button.fcrm_tags_create_button .el-icon,.fcrm_fluentcrm_header .fcrm_fluentcrm-templates-action-buttons .el-button.fcrm_companies_create_button .el-icon,.fcrm_fluentcrm_header .fcrm_fluentcrm-templates-action-buttons .el-button.el-button--primary .el-icon{width:20px;height:20px;color:var(--fc-text-inverse)}.fcrm_fluentcrm_header .fcrm_fluentcrm-templates-action-buttons .el-button.fcrm_dynamic_segments_create_button span,.fcrm_fluentcrm_header .fcrm_fluentcrm-templates-action-buttons .el-button.fcrm_lists_create_button span,.fcrm_fluentcrm_header .fcrm_fluentcrm-templates-action-buttons .el-button.fcrm_tags_create_button span,.fcrm_fluentcrm_header .fcrm_fluentcrm-templates-action-buttons .el-button.fcrm_companies_create_button span,.fcrm_fluentcrm_header .fcrm_fluentcrm-templates-action-buttons .el-button.el-button--primary span{display:flex;align-items:center;gap:4px}.fcrm_fluentcrm_header .fcrm_fluentcrm-templates-action-buttons .el-button.fcrm_dynamic_segments_create_button span span,.fcrm_fluentcrm_header .fcrm_fluentcrm-templates-action-buttons .el-button.fcrm_lists_create_button span span,.fcrm_fluentcrm_header .fcrm_fluentcrm-templates-action-buttons .el-button.fcrm_tags_create_button span span,.fcrm_fluentcrm_header .fcrm_fluentcrm-templates-action-buttons .el-button.fcrm_companies_create_button span span,.fcrm_fluentcrm_header .fcrm_fluentcrm-templates-action-buttons .el-button.el-button--primary span span{margin:0;padding:0}.fcrm_fluentcrm_header .fcrm_fluentcrm-templates-action-buttons .el-button svg{margin-right:4px;width:16px;height:16px}.fcrm_fluentcrm_header .fcrm_fluentcrm-templates-action-buttons .el-button .el-icon{width:20px;height:20px;color:var(--fc-text-inverse)}.fcrm_fluentcrm_header .fcrm_fluentcrm-templates-action-buttons .el-button span{display:flex;align-items:center;gap:4px}.fcrm_fluentcrm_header .fcrm_fluentcrm-templates-action-buttons .el-button span span{margin:0;padding:0}.fcrm_fluentcrm_header .fcrm_fluentcrm-templates-action-buttons .fcrm_lists_create_button .el-button-group .el-button,.fcrm_fluentcrm_header .fcrm_fluentcrm-templates-action-buttons .fcrm_tags_create_button .el-button-group .el-button,.fcrm_fluentcrm_header .fcrm_fluentcrm-templates-action-buttons .fcrm_dynamic_segments_create_button .el-button-group .el-button,.fcrm_fluentcrm_header .fcrm_fluentcrm-templates-action-buttons .fcrm_companies_create_button .el-button-group .el-button{background:var(--fc-deep-bg)!important;border-color:var(--fc-deep-bg)!important;color:var(--fc-text-inverse)!important;border-radius:8px}.fcrm_fluentcrm_header .fcrm_fluentcrm-templates-action-buttons .fcrm_lists_create_button .el-button-group .el-button:hover,.fcrm_fluentcrm_header .fcrm_fluentcrm-templates-action-buttons .fcrm_tags_create_button .el-button-group .el-button:hover,.fcrm_fluentcrm_header .fcrm_fluentcrm-templates-action-buttons .fcrm_dynamic_segments_create_button .el-button-group .el-button:hover,.fcrm_fluentcrm_header .fcrm_fluentcrm-templates-action-buttons .fcrm_companies_create_button .el-button-group .el-button:hover{background:var(--fc-primary-text)!important;border-color:var(--fc-primary-text)!important}@media (max-width: 1024px){.fcrm_fluentcrm_header{flex-direction:column!important;align-items:flex-start!important}}.fcrm_page_header{display:flex;align-items:center;justify-content:space-between;flex-wrap:wrap;gap:12px;padding:24px 0}.fcrm_page_header_title{color:var(--fc-primary-text);font-weight:500;font-size:18px;line-height:24px;margin:0}.fcrm_page_header_description{color:var(--fc-secondary-text);font-weight:400;font-size:14px;line-height:20px}.fcrm_page_header_actions{display:flex;align-items:center;gap:12px;flex-wrap:wrap}.fcrm_page_header_actions .fcrm_btns{align-items:center}.fcrm_page_header_actions .el-select .el-select__wrapper{height:auto;min-height:inherit;padding:10px 8px}.fcrm_page_header_actions .el-button{margin:0}.fcrm_page_header_breadcrumb .el-breadcrumb{display:flex;align-items:center;height:auto}.fcrm_page_header_breadcrumb .el-breadcrumb__item:last-child .el-breadcrumb__inner{color:var(--fc-secondary-text)}.fcrm_page_header_breadcrumb .el-breadcrumb__item:last-child .el-breadcrumb__separator{display:none}.fcrm_page_header_breadcrumb .el-breadcrumb__inner{font-weight:500;font-size:14px;line-height:20px;color:var(--fc-secondary-text);display:flex;align-items:center;gap:4px}.fcrm_page_header_breadcrumb .el-breadcrumb__inner.is-link{font-weight:500;color:var(--fc-primary-text)}.fcrm_page_header_breadcrumb .el-breadcrumb__inner.is-link:hover{color:var(--fc-primary-text)}.fcrm_page_header_breadcrumb .el-breadcrumb__separator{display:block;margin:0 8px;font-size:10px}.fluentcrm_header .fluentcrm_header_title{float:left;color:var(--fc-primary-text);font-feature-settings:"ss11" on,"liga" off,"calt" off;font-size:16px;font-style:normal;font-weight:500;line-height:24px;letter-spacing:-.176px}.fcrm_page_header_top_nav .fcrm_page_header_top_nav--title{margin:0;font-weight:500;font-size:14px;line-height:20px;color:var(--fc-primary-text)}.fcrm_page_header_top_nav_wrapper{display:flex;align-items:center;justify-content:space-between;margin:-15px -20px 0;background:var(--fc-primary-bg);border-bottom:1px solid var(--fc-primary-border);padding:0 20px;flex-wrap:wrap;gap:8px}.fcrm_page_header_top_nav .el-breadcrumb__inner.is-link:hover{color:var(--fc-primary-text)}.fcrm_page_header_top_nav_links{margin:0;display:flex;align-items:center;gap:24px;flex-wrap:wrap;row-gap:0}.fcrm_page_header_top_nav_links li{margin:0}.fcrm_page_header_top_nav_links li a{display:block;color:var(--fc-secondary-text);font-weight:500;font-size:14px;line-height:20px;padding:13px 0;border-bottom:2px solid transparent}.fcrm_page_header_top_nav_links li a.router-link-exact-active{border-bottom-color:var(--fc-primary-text)}.fcrm_page_header_top_actions{display:flex;align-items:center;gap:8px;flex-wrap:wrap}.fcrm_page_header_top_actions .el-button{margin:0}.fcrm_action_buttons{display:flex;gap:12px;align-items:center}.fcrm_action_buttons .el-button{margin:0}.fcrm_action_bar{background:var(--fc-primary-bg);padding:12px 20px;margin-bottom:0;border-radius:0;display:flex;gap:12px;align-items:center;width:100%;min-height:60px}.fcrm_table_search_section{background:var(--fc-primary-bg);padding:12px 20px;margin-bottom:0}.fcrm_table_search_section .fcrm_table_search_input .el-input__wrapper{height:32px;border-radius:8px;border:none;background:var(--fc-secondary-bg);padding:6px 6px 6px 8px;box-shadow:none}.fcrm_table_search_section .fcrm_table_search_input .el-input__wrapper:hover,.fcrm_table_search_section .fcrm_table_search_input .el-input__wrapper.is-focused,.fcrm_table_search_section .fcrm_table_search_input .el-input__wrapper.is-focus{border:none;box-shadow:none}.fcrm_table_search_section .fcrm_table_search_input .el-input__wrapper .el-input__prefix{margin-right:6px}.fcrm_table_search_section .fcrm_table_search_input .el-input__wrapper .el-input__prefix .el-icon{width:20px;height:20px;color:var(--fc-text-muted);font-size:18px;margin-top:5px}.fcrm_table_search_section .fcrm_table_search_input .el-input__wrapper .el-input__inner{letter-spacing:-.084px;color:var(--fc-primary-text);background:transparent}.fcrm_table_search_section .fcrm_table_search_input .el-input__wrapper .el-input__inner::placeholder{color:var(--fc-text-muted)}.fcrm_table_wrapper{background:var(--fc-primary-bg);border-radius:var(--fcrm-border-radius-8, 8px);position:relative;overflow:hidden}.fcrm_table_wrapper.fcrm_table_wrapper_border{border:1px solid var(--fc-primary-border)}.fcrm_table_wrapper .fcrm_notes_search_bar{display:flex;align-items:center;gap:12px;min-width:32px;width:32px;overflow:hidden;transition:width 1s ease,min-width 1s ease;margin-left:auto}.fcrm_table_wrapper .fcrm_notes_search_bar.fcrm_notes_search_bar-is_expanded{min-width:200px;width:100%;max-width:200px}.fcrm_table_wrapper .fcrm_notes_search_bar .el-input .el-input-group__append{padding:0;width:38px;font-size:15px;border:none;box-shadow:none;outline:none;background:none}.fcrm_table_wrapper .fcrm_notes_search_bar .el-input .el-input-group__append .el-button{margin:0;padding:0;border:none;box-shadow:none;outline:none;height:32px;width:32px}.fcrm_table_wrapper .fcrm_notes_search_bar .el-input .el-input-group__append .el-button:hover{border:none!important;box-shadow:none;outline:none}.fcrm_table_wrapper .fcrm_notes_search_bar .el-input .el-input-group__append .el-button>span{height:100%}.fcrm_table_wrapper .fcrm_notes_search_bar .el-input .el-input-group__append .el-button>span .icon svg{width:16px;height:16px}.fcrm_table_wrapper .fcrm_notes_search_bar .el-input .el-input__wrapper{height:32px;border:1px solid var(--fc-primary-border);box-shadow:0 1px 2px #0a0d1408;border-radius:8px 0 0 8px;padding:4px 10px;flex:1;min-width:0}.fcrm_table_wrapper .fcrm_notes_search_bar .el-input .el-input__wrapper.is-focused,.fcrm_table_wrapper .fcrm_notes_search_bar .el-input .el-input__wrapper.is-focus{outline:none;box-shadow:0 1px 2px #0a0d1408}.fcrm_table_wrapper .fcrm_notes_search_bar .el-button.fcrm_notes_search_cancel_btn{color:var(--fc-deep-bg);font-weight:500;font-size:14px;line-height:20px;height:auto;padding:0;margin:0}.fcrm_table_wrapper .fcrm_notes_search_bar .fcrm_notes_search_input{flex:1;min-width:0;border:1px solid var(--fc-primary-border);box-shadow:0 1px 2px #0a0d1408;border-radius:8px;overflow:hidden;height:32px}.fcrm_table_wrapper .fcrm_notes_search_bar .fcrm_notes_search_input .el-input__wrapper{display:inline-flex;border:none;box-shadow:none;outline:none;min-width:0;max-width:0;opacity:0;overflow:hidden;padding:0;transition:max-width .25s ease,opacity .2s ease,padding .05s ease .1s}.fcrm_table_wrapper .fcrm_notes_search_bar .fcrm_notes_search_input .el-input__wrapper.is-focused,.fcrm_table_wrapper .fcrm_notes_search_bar .fcrm_notes_search_input .el-input__wrapper.is-focus,.fcrm_table_wrapper .fcrm_notes_search_bar .fcrm_notes_search_input .el-input__wrapper:focus,.fcrm_table_wrapper .fcrm_notes_search_bar .fcrm_notes_search_input .el-input__wrapper:focus-within{outline:none}.fcrm_table_wrapper .fcrm_notes_search_bar .fcrm_notes_search_input-is_expanded .el-input__wrapper{max-width:500px;opacity:1;padding:4px 10px;transition:max-width .25s ease,opacity .2s ease,padding .05s ease .1s}.fcrm_table_wrapper .fcrm_notes_search_bar .fcrm_notes_search_btn{margin:0;padding:0;height:100%;border:1px solid var(--fc-primary-border);box-shadow:0 1px 2px #0a0d1408;color:var(--fc-secondary-text)}.fcrm_table_wrapper .fcrm_notes_search_bar .fcrm_notes_search_btn:hover{border:1px solid var(--fc-primary-border)!important;box-shadow:0 1px 2px #0a0d1408;color:var(--fc-secondary-text)}.fcrm_table_wrapper .fcrm_notes_search_bar .fcrm_notes_search_close_btn{flex-shrink:0;padding:0 4px;font-weight:500;font-size:14px}.fcrm_table_header{padding:12px 20px;position:relative}.fcrm_table_header_inner{display:flex;align-items:center;gap:12px;position:relative;flex-wrap:wrap}.fcrm_table_header_inner_left{flex:1}.fcrm_table_header_inner_left .el-input{min-width:120px}.fcrm_table_header_inner_left .el-input__prefix .icon{margin:0 6px 0 0}.fcrm_table_header_inner_left .el-input__prefix .icon svg{display:block}.fcrm_table_header_inner_left .el-input__wrapper{border:none;box-shadow:none;outline:none;background:var(--fc-secondary-bg);height:auto;color:var(--fc-text-muted);font-weight:400;font-size:14px;line-height:20px;padding:1px 8px}.fcrm_table_header_inner_left .el-input__wrapper input{margin:0;background:none}.fcrm_table_header_inner_left .el-radio-group{gap:4px}.fcrm_table_header_inner_left .el-radio-button{height:auto}.fcrm_table_header_inner_left .el-radio-button.is-active .el-radio-button__original-radio:not(:disabled)+.el-radio-button__inner{background:var(--fc-secondary-bg);color:var(--fc-primary-text)}.fcrm_table_header_inner_left .el-radio-button:first-child .el-radio-button__inner{border-radius:8px}.fcrm_table_header_inner_left .el-radio-button:last-child .el-radio-button__inner{border-radius:8px}.fcrm_table_header_inner_left .el-radio-button .el-radio-button__inner{background:none;border-radius:8px;color:var(--fc-secondary-text);font-weight:500;font-size:14px;line-height:20px;padding:6px 12px!important;border:none}.fcrm_table_header_inner_left .fcrm_table_header_inner_left_title{margin:0;font-size:14px;font-weight:500;line-height:20px;color:var(--fc-primary-text)}.fcrm_table_header_inner_actions{display:flex;align-items:center;gap:12px}@media (max-width: 1024px){.fcrm_table_header_inner_actions{flex-wrap:wrap}}.fcrm_table_header_inner_actions .el-button{margin:0}.fcrm_table_header_inner_actions .el-select{min-width:100px}.fcrm_table_body{position:relative}.fcrm_table_body .el-scrollbar{background:var(--fc-primary-bg)}.fcrm_table_header_bulk_actions{margin-top:0}.fcrm_global_table{width:100%;border-spacing:0;text-align:left}.fcrm_global_table thead tr th{border-bottom:1px solid var(--fc-primary-border);background:var(--fc-weak-bg-25);color:var(--fc-secondary-text);font-weight:500;font-size:14px;line-height:20px;padding:8px 12px}.fcrm_global_table thead tr th:first-child{padding-left:20px}.fcrm_global_table tfoot tr td,.fcrm_global_table tbody tr td{padding:20px 12px;border-bottom:1px solid var(--fc-primary-border);font-weight:400;font-size:14px;line-height:20px;color:var(--fc-primary-text)}.fcrm_global_table tfoot tr td:first-child,.fcrm_global_table tbody tr td:first-child{padding-left:20px}.fcrm_global_table tfoot tr td .fcrm_compare_date_cell,.fcrm_global_table tbody tr td .fcrm_compare_date_cell{display:flex;gap:4px;align-items:center;font-weight:400;font-size:14px;line-height:20px;color:var(--fc-primary-text)}.fcrm_global_table tfoot tr td .fcrm_compare_date_cell .fcrm_date_primary,.fcrm_global_table tbody tr td .fcrm_compare_date_cell .fcrm_date_primary{display:block;color:var(--fc-primary-text)}.fcrm_global_table tfoot tr td .fcrm_compare_date_cell .fcrm_date_compare,.fcrm_global_table tbody tr td .fcrm_compare_date_cell .fcrm_date_compare{color:var(--fc-text-muted)}.fcrm_global_table tfoot tr td .fcrm_change_badge,.fcrm_global_table tbody tr td .fcrm_change_badge{background:none;padding:0}.fcrm_global_table tfoot tr:last-child td,.fcrm_global_table tbody tr:last-child td{border-bottom:none}.fcrm_global_table tfoot tr td{padding-top:12px;padding-bottom:12px;background:var(--fc-primary-bg);border-top:1px solid var(--fc-primary-border)}.fcrm_global_table_wrapper.fcrm_table_sticky{max-height:500px;overflow:auto}.fcrm_global_table_wrapper.fcrm_table_sticky thead{position:sticky;top:0;z-index:1}.fcrm_global_table_wrapper.fcrm_table_sticky tfoot{position:sticky;bottom:0;z-index:1}.fcrm_drawer_header{display:flex;align-items:center;gap:12px;padding:20px;border-bottom:1px solid var(--fc-primary-border);background:var(--fc-primary-bg);flex-shrink:0}.fcrm_drawer_header .fcrm_drawer_title{font-size:18px;line-height:24px;font-weight:500;letter-spacing:-.27px;color:var(--fc-primary-text);margin:0;flex:1 0 0}.fcrm_drawer_header .fcrm_drawer_close{display:flex;align-items:center;justify-content:center;width:20px;height:20px;padding:0;margin:0;border:none;background:transparent;cursor:pointer;color:var(--fc-primary-text);flex-shrink:0;transition:opacity .2s ease}.fcrm_drawer_header .fcrm_drawer_close:hover{opacity:.7}.fcrm_drawer_header .fcrm_drawer_close .el-icon{width:20px;height:20px;font-size:20px}.fc_drawer_footer_wrap{position:sticky;bottom:0;background-color:var(--fc-secondary-bg);padding:15px;top:90%;z-index:999999}.fcrm_bulk_action_bar{display:flex;align-items:center;justify-content:space-between;width:100%;background:var(--fc-primary-bg);border-radius:var(--fcrm-border-radius-8, 8px) var(--fcrm-border-radius-8, 8px) 0 0;padding:0}.fcrm_bulk_action_bar .fcrm_bulk_action_left{display:flex;align-items:center;gap:12px;flex:1;flex-wrap:wrap}.fcrm_bulk_action_bar .fcrm_bulk_action_left .fcrm_bulk_action_inline{display:flex;align-items:center;gap:12px;flex-shrink:0}.fcrm_bulk_action_bar .fcrm_bulk_action_left .fcrm_bulk_action_inline .fcrm_bulk_wrap{display:flex;align-items:center;gap:12px;flex-wrap:nowrap}.fcrm_bulk_action_bar .fcrm_bulk_action_left .fcrm_bulk_action_inline .fcrm_bulk_wrap .fcrm_bulk_item{display:flex;flex-direction:row;align-items:center;gap:12px;flex-shrink:0;margin:0!important}.fcrm_bulk_action_bar .fcrm_bulk_action_left .fcrm_bulk_action_inline .fcrm_bulk_wrap .fcrm_bulk_item label{display:none}.fcrm_bulk_action_bar .fcrm_bulk_action_left .fcrm_bulk_action_inline .fcrm_bulk_navs{display:none!important}.fcrm_bulk_action_bar .fcrm_bulk_action_left .fcrm_bulk_action_dropdown{height:32px;padding:6px 12px;border-radius:8px;background:var(--fc-secondary-bg);border:1px solid var(--fc-primary-border);color:var(--fc-secondary-text);font-weight:500;font-size:14px;line-height:20px;display:inline-flex;align-items:center;gap:4px;transition:border-color .2s,box-shadow .2s,background-color .2s}.fcrm_bulk_action_bar .fcrm_bulk_action_left .fcrm_bulk_action_dropdown:hover{background:var(--fc-secondary-bg);border-color:var(--fc-secondary-border)}.fcrm_bulk_action_bar .fcrm_bulk_divider{width:1px;height:16px;background:var(--fc-primary-border);flex-shrink:0}.fcrm_bulk_action_bar .fcrm_selection_count{display:flex;align-items:center;gap:0;font-size:14px;font-weight:400;line-height:20px;white-space:nowrap}.fcrm_bulk_action_bar .fcrm_selection_count .fcrm_selection_count_number,.fcrm_bulk_action_bar .fcrm_selection_count .fcrm_selection_count_text{color:var(--fc-primary-text)}.fcrm_bulk_action_bar .fcrm_selection_count .fcrm_selection_count_number{font-weight:500;margin-inline-end:4px}.fcrm_bulk_action_bar .fcrm_select_all_link,.fcrm_bulk_action_bar .fcrm_deselect_all_link{font-size:14px;font-weight:400;color:var(--fc-primary-text);text-decoration:underline;cursor:pointer;white-space:nowrap;background:none;border:none;padding:0;margin:0;line-height:1}.fluentcrm-bulk-action-menu{display:flex;align-items:center;justify-content:space-between}.fluentcrm-bulk-action-menu .fc_search_box{display:flex;align-items:center}.fluentcrm-bulk-action-menu .fc_search_box .fc_advanced_toggle{margin-right:7px}.fluentcrm-bulk-action-menu .fc_search_box .el-input,.fluentcrm-bulk-action-menu .fc_search_box .el-button{height:30px!important}.fluentcrm-body .fc_bulk_wrap{width:100%;display:flex;align-items:flex-end;flex-wrap:wrap}.fluentcrm-body .fc_bulk_wrap .el-select .el-select__wrapper .el-select__selection .el-select__input-wrapper .el-select__input{border:none}.fluentcrm-body .fc_bulk_wrap .fc_bulk_item{margin-right:10px}.fluentcrm-body .fc_bulk_wrap>.el-select{width:200px}.fluentcrm-body .fc_bulk_wrap>.el-select .el-input input{font-weight:500}.fluentcrm-body .fc_bulk_wrap>.el-button{height:30px}.fluentcrm-body .fc_bulk_wrap>label{width:100%;display:block;color:var(--fc-primary-text)}.fluentcrm-body .fc_bulk_wrap.fc_bulk_campaign_actions{align-items:center;align-content:center}.fluentcrm-body .fcrm_bulk_action_btn{background:var(--fc-secondary-bg);border:1px solid var(--fc-primary-border);color:var(--fc-secondary-text);border-radius:8px;font-weight:500;height:32px;padding:5px 12px}.fluentcrm-body .fcrm_bulk_action_btn .el-icon{color:var(--fc-secondary-text);margin-left:4px}.fluentcrm-body .fcrm_bulk_action_btn:hover{background:var(--fc-secondary-bg);border-color:var(--fc-primary-border);color:var(--fc-secondary-text)}.fcrm_bulk_wrap .fcrm_bulk_select{width:200px}.fcrm_bulk_wrap .fcrm_bulk_select .el-select__popper{padding:0!important;border-radius:12px!important;border:1px solid var(--fc-primary-border)!important;box-shadow:0 16px 32px -12px #0e121b1a!important;overflow:hidden}.fcrm_bulk_wrap .fcrm_bulk_select .el-select__popper .el-select-dropdown{padding:8px 0;background:var(--fc-primary-bg)}.fcrm_bulk_wrap .fcrm_bulk_select .el-select__popper .el-select-dropdown__item{padding:10px 12px;font-size:14px;font-weight:400;line-height:20px;color:var(--fc-primary-text);transition:background-color .2s ease,color .2s ease}.fcrm_bulk_wrap .fcrm_bulk_select .el-select__popper .el-select-dropdown__item:hover{background-color:var(--fc-secondary-bg)}.fcrm_bulk_wrap .fcrm_bulk_select .el-select__popper .el-select-dropdown__item.selected{background-color:var(--fc-secondary-bg);color:var(--fc-primary-text);font-weight:500}.fcrm_fixed_bulk_actions_to_bottom{position:fixed;bottom:10px;z-index:1000;left:50%;transform:translate(-50%);width:max-content;max-width:100%;border-radius:var(--fcrm-border-radius-8);padding:0 12px}.fcrm_fixed_bulk_actions_to_bottom .fcrm_fixed_bulk_actions_to_bottom--actions{display:none}.fcrm_fixed_bulk_actions_to_bottom .fcrm_bulk_action_bar{gap:12px;flex-wrap:nowrap;padding:10px;border-radius:var(--fcrm-border-radius-8);box-shadow:0 6px 32px 2px #0e121b1a;overflow-y:hidden;scrollbar-width:none;min-height:52px}.fcrm_fixed_bulk_actions_to_bottom .fcrm_bulk_action_bar .fcrm_bulk_action_left{flex-wrap:nowrap}.fcrm_fixed_bulk_actions_to_bottom .fcrm_bulk_action_bar .fcrm_bulk_action_left .fcrm_bulk_wrap{flex-wrap:nowrap;width:max-content}.fcrm-force-light{--fc-primary-bg: #FFFFFF;--fc-secondary-bg: #F5F7FA;--fc-light-bg: #E1E4EA;--fc-deep-bg: #222530;--fc-weak-bg-25: #F9FAFB;--fc-primary-text: #0E121B;--fc-secondary-text: #525866;--fc-text-muted: #99A0AE;--fc-text-inverse: #FFFFFF;--fc-primary-border: #E1E4EA;--fc-secondary-border: #CACFD8;--fc-primary-button: #222530;--fc-text-link: #335CFF;--fc-success: #1FC16B;--fc-success-bg: #E0FAEC;--fc-error: #FB3748;--fc-error-bg: #FFEBEC;--fc-warning: #F6B51E;--fc-warning-bg: #FFFAEB;--fc-text-link-bg: #EEF2FF;--el-color-primary: var(--fc-deep-bg);--el-color-primary-light-3: var(--fc-secondary-text);--el-color-primary-light-5: var(--fc-text-muted);--el-color-primary-light-7: var(--fc-secondary-border);--el-color-primary-light-8: var(--fc-primary-border);--el-color-primary-light-9: var(--fc-secondary-bg);--el-color-primary-dark-2: var(--fc-primary-text);--el-color-success: var(--fc-success);--el-color-warning: var(--fc-warning);--el-color-danger: var(--fc-error);--el-color-error: var(--fc-error);--el-color-info: var(--fc-text-muted);--el-text-color-primary: var(--fc-primary-text);--el-text-color-regular: var(--fc-secondary-text);--el-text-color-secondary: var(--fc-text-muted);--el-text-color-placeholder: var(--fc-text-muted);--el-text-color-disabled: var(--fc-secondary-border);--el-border-color: var(--fc-primary-border);--el-border-color-light: var(--fc-primary-border);--el-border-color-lighter: var(--fc-secondary-border);--el-border-color-dark: var(--fc-secondary-border);--el-fill-color-light: var(--fc-secondary-bg);--el-fill-color-lighter: var(--fc-secondary-bg);--el-bg-color: var(--fc-primary-bg);--el-bg-color-page: var(--fc-secondary-bg);--el-button-text-color: var(--fc-text-inverse);--el-fill-color-blank: var(--fc-primary-bg)}.fcrm-force-light .el-button.el-button--primary{--el-color-white: #FFFFFF !important}.fcrm-force-light .el-popper.is-light{border:1px solid var(--fc-primary-border);background:var(--fc-primary-bg)}.fcrm_badge{display:inline-flex;align-items:center;gap:4px;padding:2px 8px;border-radius:6px;font-weight:500;font-size:12px;line-height:16px;color:var(--fc-secondary-text);background:var(--fc-secondary-bg)}.fcrm_badge .icon{display:block}.fcrm_badge .icon svg{display:block;width:14px;height:14px}.fcrm_badge_complete,.fcrm_badge_completed,.fcrm_badge_paid,.fcrm_badge_active,.fcrm_badge_publish,.fcrm_badge_shipped,.fcrm_badge_success,.fcrm_badge_licensed,.fcrm_badge_succeeded,.fcrm_badge_sent,.fcrm_badge_published,.fcrm_badge_archived,.fcrm_badge_subscribed,.fcrm_badge_sms_subscribed{color:var(--fc-badge-subscribed-text);background:var(--fc-badge-subscribed-bg)}.fcrm_badge_unsubscribed,.fcrm_badge_sms_unsubscribed{color:var(--fc-badge-unsubscribed-text);background:var(--fc-badge-unsubscribed-bg)}.fcrm_badge_failed,.fcrm_badge_cancelled,.fcrm_badge_error,.fcrm_badge_canceled,.fcrm_badge_expired{color:var(--fc-primary-text);background:var(--fc-error-bg)}.fcrm_badge_draft,.fcrm_badge_scheduled,.fcrm_badge_on-hold,.fcrm_badge_unpaid,.fcrm_badge_warning,.fcrm_badge_processing,.fcrm_badge_future,.fcrm_badge_dispute,.fcrm_badge_inactive,.fcrm_badge_working,.fcrm_badge_pending-scheduled,.fcrm_badge_pending,.fcrm_badge_sms_pending{color:var(--fc-badge-pending-text);background:var(--fc-badge-pending-bg)}.fcrm_badge_bounced,.fcrm_badge_sms_bounced{color:var(--fc-badge-bounced-text);background:var(--fc-badge-bounced-bg)}.fcrm_badge_paused{color:var(--fc-text-inverse);background:var(--fc-text-link)}.fcrm_badge_transactional{color:var(--fc-badge-transactional-text);background:var(--fc-badge-transactional-bg)}.fcrm_badge_complained{color:var(--fc-badge-complained-text);background:var(--fc-badge-complained-bg)}.fcrm_badge_spammed{color:var(--fc-badge-spammed-text);background:var(--fc-badge-spammed-bg)}.fcrm_badge_plain{padding:0;background:none;font-size:14px;line-height:20px;color:var(--fc-primary-text)}.fcrm_badge_plain .el-icon,.fcrm_badge_plain .icon{color:var(--fc-text-muted);display:block}.fcrm_badge_plain .el-icon svg,.fcrm_badge_plain .icon svg{display:block}.fcrm_pro_badge{background:var(--fc-secondary-bg);display:inline-flex;align-items:center;color:var(--fc-deep-bg);font-size:12px;line-height:16px;font-weight:500;border-radius:30px;gap:2px;padding:2px 6px}.fcrm_pro_badge .icon svg{display:block}.fcrm_sms_type_badge{display:inline-block;padding:2px 8px;border-radius:4px;font-size:12px;font-weight:500;line-height:1.5;background:var(--fc-secondary-bg);color:var(--fc-secondary-text)}.fc_card_widgets{display:flex;flex-wrap:wrap;gap:25px}.fc_card_widgets .fc_card_widget{cursor:pointer;background:var(--fc-primary-bg);border:1.3px solid var(--fc-primary-border);border-radius:8px;padding:25px 30px;width:calc(33.3333% - 17px);transition:.4s}@media (max-width: 720px){.fc_card_widgets .fc_card_widget{width:calc(50% - 13px)}}@media (max-width: 426px){.fc_card_widgets .fc_card_widget{width:100%}}.fc_card_widgets .fc_card_widget .fluentcrm_body{font-size:36px;font-weight:600;line-height:1.2;margin:0 0 5px}.fc_card_widgets .fc_card_widget .stat_title{font-size:15px;line-height:1.5;color:var(--fc-primary-text);opacity:.8}.fc_card_widgets .fc_card_widget:hover{border-color:var(--fc-deep-bg);box-shadow:0 8px 10px #0000000d}.fc_card_header{display:flex;justify-content:space-between;align-items:center}.fc_shadow{box-shadow:0 1px 8px #0000001a;transition:background .3s,border .3s,border-radius .3s,box-shadow .3s}.fc_shadow:hover{box-shadow:0 4px 16px #0003}.fc_shadow_hover{transition:background .2s,border .2s,border-radius .2s,box-shadow .2s}.fc_shadow_hover:hover{box-shadow:0 1px 6px #0003}.fcrm_base_card{background:var(--fc-primary-bg);border-radius:var(--fcrm-border-radius-8);margin-bottom:20px}.fcrm_base_card.fcrm_danger_border{border:1px solid var(--fc-error)}.fcrm_base_card.fcrm_warning_border{border:1px solid var(--fc-warning)}.fcrm_base_card.fcrm_success_border{border:1px solid var(--fc-success)}.fcrm_base_card.fcrm_primary_border{border:1px solid var(--fc-primary-border)}.fcrm_base_card .fcrm_base_card_header{display:flex;align-items:center;justify-content:space-between;padding:10px 20px;border-bottom:1px solid var(--fc-primary-border);flex-wrap:wrap;gap:10px;min-height:56px}.fcrm_base_card .fcrm_base_card_header h4{margin:0;font-size:15px;font-weight:500;color:var(--fc-primary-text);display:flex;align-items:center;gap:4px}.fcrm_base_card .fcrm_base_card_header h4 .icon{display:block}.fcrm_base_card .fcrm_base_card_header h4 .icon svg{display:block;width:14px;height:14px}.fcrm_base_card .fcrm_base_card_title_wrap{display:flex;align-items:center;gap:12px}.fcrm_base_card .fcrm_base_card_header_actions{display:flex;align-items:center;gap:10px;flex-wrap:nowrap}.fcrm_base_card .fcrm_base_card_header_actions .el-select{width:180px;flex-shrink:0}.fcrm_base_card .fcrm_base_card_header_actions .el-date-editor{width:auto;max-width:280px;border-radius:8px;flex-shrink:0;padding:6px 10px;height:auto}@media (max-width: 768px){.fcrm_base_card .fcrm_base_card_header_actions{flex-wrap:wrap}}.fcrm_base_card .fcrm_base_card_body:not([class*=fcrm_p]){padding:20px}.fcrm_base_card .fcrm_base_card_footer{display:flex;align-items:center;justify-content:flex-end;gap:12px;padding:12px 20px;border-top:1px solid var(--fc-primary-border)}.fcrm_base_card .fcrm_base_card_footer .el-button{margin:0}.fcrm_base_card.fcrm_base_card--contacts-by-country .fcrm_base_card_body{padding:0}.fcrm-smartcodes-popover{padding:0;border-radius:8px;border:1px solid var(--fc-primary-border);box-shadow:0 4px 12px #0e121b14;width:auto!important}.fcrm-smartcodes-popover input[type=text],.fcrm-smartcodes-popover input[type=email],.fcrm-smartcodes-popover input[type=url],.fcrm-smartcodes-popover input[type=password],.fcrm-smartcodes-popover input[type=search],.fcrm-smartcodes-popover input[type=number],.fcrm-smartcodes-popover input[type=tel],.fcrm-smartcodes-popover input[type=date],.fcrm-smartcodes-popover input[type=time]{padding:0!important;line-height:20px!important;border:none!important;box-shadow:none!important;background:none!important}.fcrm-smartcodes-popover .el_pop_data_group{overflow:hidden;display:flex}.fcrm-smartcodes-popover .el_pop_data_group *{box-sizing:border-box}.fcrm-smartcodes-popover .el_pop_data_group .el_pop_data_headings{width:190px;min-width:190px;background:var(--fc-secondary-bg);border-right:1px solid var(--fc-primary-border);padding:0;display:flex;flex-direction:column;height:400px}.fcrm-smartcodes-popover .el_pop_data_group .el_pop_data_headings ul{padding:12px;margin:0;list-style:none;flex:1;overflow-y:auto}.fcrm-smartcodes-popover .el_pop_data_group .el_pop_data_headings ul li{cursor:pointer;color:var(--fc-secondary-text);font-size:14px;font-weight:400;line-height:20px;padding:8px 10px;border-radius:6px;margin-bottom:4px;transition:all .2s ease}.fcrm-smartcodes-popover .el_pop_data_group .el_pop_data_headings ul li:hover{background:var(--fc-secondary-bg);color:var(--fc-primary-text)}.fcrm-smartcodes-popover .el_pop_data_group .el_pop_data_headings ul li.active_item_selected{background:var(--fc-primary-text);color:var(--fc-text-inverse);font-weight:500}.fcrm-smartcodes-popover .el_pop_data_group .el_pop_data_headings .pop_doc{width:100%;padding:10px 12px;border-top:1px solid var(--fc-primary-border);margin-top:auto}.fcrm-smartcodes-popover .el_pop_data_group .el_pop_data_headings .pop_doc a{background:var(--fc-secondary-bg);color:var(--fc-secondary-text);text-align:center;display:block;padding:6px 8px;border-radius:6px;font-size:13px;font-weight:500;text-decoration:none}.fcrm-smartcodes-popover .el_pop_data_group .el_pop_data_headings .pop_doc a:hover{background:var(--fc-primary-text);color:var(--fc-text-inverse)}.fcrm-smartcodes-popover .el_pop_data_group .el_pop_data_body{background:var(--fc-primary-bg);width:370px;min-width:370px;height:400px;overflow:auto}.fcrm-smartcodes-popover .el_pop_data_group .el_pop_data_body .el_pop_search{padding:12px 14px;position:sticky;top:0;border-bottom:1px solid var(--fc-primary-border);background:var(--fc-primary-bg);z-index:1}.fcrm-smartcodes-popover .el_pop_data_group .el_pop_data_body ul{padding:8px;margin:0;list-style:none}.fcrm-smartcodes-popover .el_pop_data_group .el_pop_data_body ul li{color:var(--fc-primary-text);padding:6px 8px;display:block;margin-bottom:0;cursor:pointer;text-align:left;border-bottom:1px solid var(--fc-primary-border);font-size:14px;line-height:16px;transition:.2s;-webkit-transition:.2s}.fcrm-smartcodes-popover .el_pop_data_group .el_pop_data_body ul li:last-child{border-bottom:none}.fcrm-smartcodes-popover .el_pop_data_group .el_pop_data_body ul li:hover{background:var(--fc-secondary-bg)}.fcrm-smartcodes-popover .el_pop_data_group .el_pop_data_body ul li span{font-size:11px;color:var(--fc-text-muted);margin:2px 0 0;display:block}.fcrm_action_menu_trigger{display:flex;align-items:center;justify-content:center;width:32px;height:32px;padding:6px;border:none;background:transparent;border-radius:8px;cursor:pointer;color:var(--fc-primary-text);transition:background-color .2s ease}.fcrm_action_menu_trigger:hover{background:var(--fc-secondary-bg)}.fcrm_action_menu_trigger .el-icon{font-size:16px;rotate:90deg;color:var(--fc-secondary-text)}.fcrm_action_menu_btn{padding:2px;border-radius:var(--radius-6, 6px);min-height:auto;height:auto;width:auto;border:none;background:transparent;color:var(--fc-secondary-text);display:flex;align-items:center;justify-content:center;gap:2px;transition:all .2s ease;overflow:hidden}.fcrm_action_menu_btn .el-icon{width:20px;height:20px;font-size:16px;transform:rotate(90deg);color:currentColor}.fcrm_action_menu_btn:hover{background:var(--fc-secondary-bg);color:var(--fc-primary-text)}.fcrm_action_menu_btn:active{background:var(--fc-primary-border)}.fcrm_action_menu_btn:focus{outline:none;box-shadow:none}.fcrm_action_dropdown_cell{display:flex;align-items:center;justify-content:flex-end;gap:8px}.fcrm_data_table .el-dropdown .el-icon{display:none!important}.fcrm_data_table .el-dropdown .fcrm_action_menu_trigger .el-icon{display:flex!important}.fcrm_data_table .el-dropdown:before,.fcrm_data_table .el-dropdown:after{display:none!important;content:none!important}.fcrm_data_table .el-dropdown .el-dropdown__caret-button,.fcrm_data_table .el-dropdown .el-icon--right{display:none!important}.fcrm_action_dropdown_menu{border:1px solid var(--fc-primary-border);border-radius:8px;box-shadow:0 4px 12px #0e121b14;padding:4px;min-width:160px}.fcrm_action_dropdown_menu .el-dropdown-menu__item{font-size:14px;font-weight:400;line-height:20px;letter-spacing:-.084px;color:var(--fc-primary-text);padding:8px 12px;border-radius:6px;margin:2px 0;display:flex;align-items:center;gap:8px;transition:all .2s ease}.fcrm_action_dropdown_menu .el-dropdown-menu__item .el-icon{font-size:16px;width:16px;height:16px;color:var(--fc-secondary-text)}.fcrm_action_dropdown_menu .el-dropdown-menu__item:hover:not(.is-disabled){background:var(--fc-secondary-bg);color:var(--fc-primary-text)}.fcrm_action_dropdown_menu .el-dropdown-menu__item:hover:not(.is-disabled) .el-icon{color:var(--fc-primary-text)}.fcrm_action_dropdown_menu .el-dropdown-menu__item.is-disabled{opacity:.4;cursor:not-allowed}.fcrm_action_dropdown_menu .el-dropdown-menu__item.is-disabled:hover{background:transparent}.fcrm_action_dropdown_menu .el-dropdown-menu__item.fcrm_danger_item,.fcrm_action_dropdown_menu .el-dropdown-menu__item.fcrm_danger_item .el-icon{color:var(--fc-error)}.fcrm_action_dropdown_menu .el-dropdown-menu__item.fcrm_danger_item:hover:not(.is-disabled){background:var(--fc-error-bg);color:var(--fc-error)}.fcrm_action_dropdown_menu .el-dropdown-menu__item.fcrm_danger_item:hover:not(.is-disabled) .el-icon{color:var(--fc-error)}a.el-button:not(.el-button--danger):not(.el-button--warning):not(.el-button--success):not(.el-button--info){color:var(--fc-secondary-text)}a.el-button:not(.el-button--danger):not(.el-button--warning):not(.el-button--success):not(.el-button--info).el-button--primary{color:var(--fc-text-inverse);text-decoration:none}.el-button{font-size:14px;line-height:20px;height:auto;padding:7px 10px}.el-button .cmd{display:block;background:var(--alpha-white-alpha-10, rgba(255, 255, 255, .1019607843));color:var(--fc-text-muted);border-radius:4px;font-weight:500;font-size:12px;line-height:16px;padding:2px 6px;text-transform:uppercase}.el-button>span{gap:4px}.el-button .el-icon,.el-button .icon{display:block}.el-button .el-icon svg,.el-button .icon svg{display:block}.el-button.el-button--small,.el-button.small{padding:5px 10px}.el-button.fcrm_setup_btn{background:var(--fc-secondary-bg);color:var(--fc-deep-bg);border:none;font-size:12px;font-weight:500;cursor:pointer;transition:background-color .2s ease}.el-button.fcrm_setup_btn:hover{background:var(--fc-deep-bg);color:var(--fc-text-inverse)}.el-button.only-icon-btn{width:36px;height:36px;padding:4px;flex:none}.el-button.only-icon-btn.small{width:32px;height:32px}.el-button.fcrm_pro_btn,.el-button.fcrm_pro_btn:hover{background:var(--fc-deep-bg);border-color:var(--fc-deep-bg)}.el-button.is-link{color:var(--fc-primary-text);text-decoration:underline;padding:0;line-height:1}.el-button.is-link.el-button--small{font-size:12px}.el-button.is-link.el-button--small .el-icon{font-size:16px}.el-button.is-link.el-button--small .icon svg{width:16px;height:16px}.fcrm_upgrade_wrapper{margin:20px}.fcrm_upgrade_banner{display:flex;align-items:center;justify-content:space-between;gap:32px;padding:32px;background:var(--fc-primary-bg);border-radius:16px;box-shadow:0 20px 40px -24px #0e121b29;margin:0 auto;max-width:800px}.fcrm_upgrade_banner__mock{flex:0 0 372px;max-width:100%;background:#476cff1a;border-radius:var(--fcrm-border-radius-8, 8px);padding:12px;display:flex;align-items:center;justify-content:center;overflow:hidden}.fcrm_upgrade_banner__mock img{width:100%;height:auto;display:block;object-fit:cover}.fcrm_upgrade_banner__content{display:flex;flex-direction:column;gap:12px;max-width:100%}.fcrm_upgrade_banner__title{margin:0;font-size:20px;font-weight:500;line-height:28px;letter-spacing:-.2px;color:var(--fc-primary-text);max-width:462px}.fcrm_upgrade_banner__description{margin:0;font-size:14px;font-weight:400;line-height:20px;letter-spacing:-.084px;color:var(--fc-secondary-text);max-width:462px}.fcrm_upgrade_banner__cta{display:inline-flex;align-items:center;justify-content:center;gap:8px;padding:10px 16px;border-radius:8px;background:#476cff1a;color:var(--fc-text-link);font-size:14px;font-weight:500;line-height:20px;letter-spacing:-.084px;text-decoration:none;transition:background .2s ease,color .2s ease,box-shadow .2s ease}.fcrm_upgrade_banner__cta svg{width:20px;height:20px;display:block;flex-shrink:0}.fcrm_upgrade_banner__cta:hover{background:#476cff29;color:var(--fc-text-link);box-shadow:0 8px 16px -12px #335cff99}.fcrm_upgrade_banner__cta:active{background:#476cff33}@media (max-width: 1024px){.fcrm_upgrade_banner{flex-direction:column;text-align:center;gap:24px}.fcrm_upgrade_banner__mock{width:100%;max-width:420px}.fcrm_upgrade_banner__content{align-items:center}}@media (max-width: 768px){.fcrm_upgrade_banner{padding:24px}.fcrm_upgrade_banner__title,.fcrm_upgrade_banner__description{max-width:none}.fcrm_upgrade_banner__cta{width:100%}}@media (max-width: 480px){.fcrm_upgrade_banner{padding:20px;gap:20px}.fcrm_upgrade_banner__mock{padding:10px}.fcrm_upgrade_banner__cta{padding:10px 14px}}.fcrm_import_dialog .el-dialog__header{display:flex;padding:16px 16px 16px 20px!important;align-items:center;gap:12px;justify-content:space-between;color:var(--fc-primary-text);font-size:16px;font-weight:500;line-height:24px;letter-spacing:-.176px}.fcrm_import_dialog .el-dialog__header h4{margin:0}.fcrm_import_dialog .el-dialog__header svg{cursor:pointer}.fcrm_import_dialog .el-dialog__footer{padding:16px 20px!important;border-top:1px solid var(--fc-secondary-bg)}.fcrm_import_dialog .fcrm_import_footer{display:flex;justify-content:flex-end;align-items:center;gap:12px;flex:1 0 0}.fcrm_import_dialog .fcrm_import_footer .el-button{margin:0;height:36px;display:flex;padding:8px 12px;justify-content:center;align-items:center;gap:4px;border-radius:var(--radius-8, 8px)}.fcrm_import_dialog .fcrm_import_footer .fcrm_import_back_btn{border:1px solid var(--fc-primary-border);background:var(--fc-primary-bg);box-shadow:0 1px 2px #0a0d1408;color:var(--fc-deep-bg)}.fcrm_import_dialog .fcrm_import_footer .fcrm_import_submit_btn{border-radius:var(--radius-8, 8px);background:var(--fc-deep-bg);border:1px solid var(--fc-deep-bg)}.fcrm_import_dialog .fcrm_upload_area .el-upload .el-upload-dragger{margin:0;padding:32px;border:none;display:flex;flex-direction:column;justify-content:center;align-items:center;gap:20px;overflow:hidden;border-radius:var(--radius-8, 8px);border:1px dashed var(--fc-secondary-border);background:var(--fc-primary-bg)}.fcrm_import_dialog .fcrm_upload_area .el-upload .el-upload-dragger:hover{border:1px dashed var(--fc-primary-text)}.fcrm_import_dialog .fcrm_upload_area .el-upload__tip{display:flex;align-items:center;justify-content:space-between;padding:8px;margin-top:12px;border-radius:var(--radius-8, 8px)}.fcrm_import_dialog .fcrm_import_preview .fcrm_import_subtitle{margin:0 0 16px;font-weight:500;color:var(--fc-secondary-text)}.fcrm_import_dialog .fcrm_import_preview .el-table__row{border:none}.fcrm_import_dialog .fcrm_import_preview .el-table__row:hover{background-color:transparent!important}.fcrm_import_dialog .fcrm_import_preview .el-table__row:hover .el-table__cell{background:transparent!important}.fcrm_import_dialog .fcrm_import_preview .el-table__cell{border:none!important;padding:8px}.fcrm_import_dialog .fcrm_import_preview .el-table__cell .cell{padding:0}.fcrm_import_dialog .fcrm_import_preview .el-table__cell .cell .el-input__wrapper{border-radius:var(--radius-8, 8px);border:1px solid var(--fc-primary-border);background:var(--fc-primary-bg);box-shadow:0 1px 2px #0a0d1408}.fcrm_import_dialog .fcrm_import_preview .el-table__inner-wrapper:before{display:none}.fcrm_import_dialog .fcrm_import_preview .el-table__header-wrapper .el-table__header .el-table__cell{color:var(--fc-text-muted);font-size:12px;font-weight:500;line-height:16px;letter-spacing:.48px;text-transform:uppercase}.fluentcrm-app a:not(.el-button){color:var(--fc-primary-text)}body.toplevel_page_fluentcrm-admin{background-color:var(--fc-secondary-bg)!important;scrollbar-width:thin;scrollbar-color:var(--fc-secondary-border) transparent}body.toplevel_page_fluentcrm-admin .fui-app-content{background-color:var(--fc-secondary-bg)}.fui-app-content .fluentcrm_app_wrapper{background:var(--fc-secondary-bg)}.fcrm_no_permission{border-radius:var(--radius-8, 8px);background:var(--fc-primary-bg);box-shadow:0 1px 2px #0a0d1408;padding:32px 20px}.fcrm_no_permission h3{margin:0;color:var(--fc-primary-text);text-align:center;font-size:14px;font-style:normal;font-weight:500;line-height:20px;letter-spacing:-.084px}.fcrm_no_permission p{margin:8px 0 0;color:var(--fc-secondary-text);text-align:center;font-size:12px;font-style:normal;font-weight:400;line-height:16px}.fcrm_form_builder_item.fcrm_form_builder_item__dependency{padding-left:24px}.fcrm_live_indicator{width:8px;height:8px;border-radius:50%;display:block;position:relative;background:var(--fc-success)}.fcrm_live_indicator:before{content:"";position:absolute;top:0;left:0;width:100%;height:100%;border-radius:50%;background:var(--fc-success);animation:fcrm-live-indicator-pulse 1.4s ease-in-out infinite}@keyframes fcrm-live-indicator-pulse{0%{transform:scale(.5);opacity:1}to{transform:scale(2);opacity:0}}.echarts-tooltip{z-index:10000!important}.fluentcrm-templates-action-buttons .el-date-editor .el-range-separator{width:7%!important}.fcrm-dashboard-filters{display:flex;align-items:center;gap:8px}.fcrm-dashboard-filters .el-date-picker,.fcrm-dashboard-filters .el-button,.fcrm-dashboard-filters .el-date-editor{height:36px!important}.fcrm-dashboard-filters .el-button{padding:8px 20px!important}.fcrm-dashboard-filters .el-date-editor .el-range-input::placeholder{font-size:12px!important}.fcrm_onboarding_complete_dialog_mask .el-overlay-dialog{display:flex;align-items:center;justify-content:center}.fcrm_onboarding_complete_dialog{border-radius:8px}.fcrm_onboarding_complete_dialog .el-dialog__header{display:none}.fcrm_onboarding_complete_dialog .el-dialog__body{padding:32px 20px 20px}.fcrm_onboarding_complete_popover_content{text-align:center;display:flex;align-items:center;justify-content:center;flex-direction:column}.fcrm_onboarding_complete_popover_content img{max-width:150px;display:block;margin-bottom:24px}.fcrm_onboarding_complete_popover_content h3{color:var(--fc-primary-text);font-weight:600;font-size:24px;line-height:32px;margin:0}.fcrm_onboarding_complete_popover_content p{font-weight:400;font-size:16px;line-height:24px;margin:4px 0 0;color:var(--fc-secondary-text);max-width:300px}.fcrm_onboarding--quick-actions{width:100%;margin:24px 0;border:1px solid var(--fc-primary-border);border-radius:var(--fcrm-border-radius-8);overflow:hidden}.fcrm_onboarding--quick-actions-item{text-align:left;padding:12px;border-bottom:1px solid var(--fc-primary-border);cursor:pointer;position:relative;transition:.2s;-webkit-transition:.2s}.fcrm_onboarding--quick-actions-item:last-child{border-bottom:none}.fcrm_onboarding--quick-actions-item:hover{background:var(--fc-weak-bg-25)}.fcrm_onboarding--quick-actions-item:hover .fcrm_onboarding--quick-actions-item-icon{background:var(--fc-primary-bg)}.fcrm_onboarding--quick-actions-item:hover .fcrm_onboarding--quick-actions-item-arrow{opacity:1}.fcrm_onboarding--quick-actions-item-icon{border:1px solid var(--fc-primary-border);background:var(--fc-weak-bg-25);border-radius:var(--fcrm-border-radius-8);width:40px;height:40px;flex:none;display:flex;align-items:center;justify-content:center;color:var(--fc-secondary-text);transition:.2s;-webkit-transition:.2s}.fcrm_onboarding--quick-actions-item-icon svg{display:block;width:20px;height:20px}.fcrm_onboarding--quick-actions-item-title{margin:0 0 4px;color:var(--fc-primary-text);font-weight:500;font-size:16px;line-height:24px}.fcrm_onboarding--quick-actions-item-content{flex:1}.fcrm_onboarding--quick-actions-item-arrow{position:absolute;top:50%;right:12px;transform:translateY(-50%);z-index:2;opacity:0;transition:.2s;-webkit-transition:.2s}.fcrm_onboarding--quick-actions-item-arrow svg{display:block}.fcrm_onboarding_steps--lists{display:flex;flex-direction:column;gap:10px}.fcrm_onboarding_steps--item{display:flex;align-items:center;gap:10px;color:var(--fc-secondary-text);font-weight:400;font-size:14px;line-height:20px;cursor:pointer}.fcrm_onboarding_steps--item:hover{color:var(--fc-primary-text);text-decoration:underline}.fcrm_onboarding_steps--item .icon{flex:none;width:16px;height:16px;border:1.5px dashed var(--fc-secondary-border);border-radius:50%;display:flex;align-items:center;justify-content:center}.fcrm_onboarding_steps--item .icon .el-icon{font-size:10px;color:var(--fc-text-inverse)}.fcrm_onboarding_steps--item .icon .el-icon svg{width:12px;height:12px}.fcrm_onboarding_steps--item.completed_step .icon{background:var(--fc-primary-text);border-style:solid;border-color:var(--fc-primary-text)}.list-metrics{color:var(--fc-text-muted);font-size:13px;line-height:1;display:block}.fluentcrm-campaigns .error{color:var(--fc-error);font-size:12px}.fluentcrm-campaigns .save-campaign-dialog-footer{margin-top:30px}.fcrm_forms_filter_popover{padding:8px!important}.fcrm_forms_filter_popover .fcrm_filter_menu_items_container{display:flex;flex-direction:column;gap:4px}.fcrm_forms_filter_popover .fcrm_filter_menu_items_container .fcrm_filter_menu_item{display:flex;align-items:center;gap:8px;padding:8px 12px;border-radius:6px;cursor:pointer;transition:background-color .2s ease}.fcrm_forms_filter_popover .fcrm_filter_menu_items_container .fcrm_filter_menu_item:hover{background-color:var(--fc-secondary-bg)}.fcrm_forms_filter_popover .fcrm_filter_menu_items_container .fcrm_filter_menu_item .el-icon{width:16px;height:16px;color:var(--fc-secondary-text)}.fcrm_forms_filter_popover .fcrm_filter_menu_items_container .fcrm_filter_menu_item .fcrm_filter_menu_text{font-size:14px;font-weight:400;line-height:20px;color:var(--fc-primary-text)}.fcrm_forms_filter_popover .fcrm_filter_menu_items_container .fcrm_filter_empty_item{display:flex;align-items:center;gap:8px;padding:8px 12px;border-radius:6px;cursor:not-allowed;opacity:.5}.fcrm_forms_filter_popover .fcrm_filter_menu_items_container .fcrm_filter_empty_item .el-icon{width:16px;height:16px;color:var(--fc-secondary-text)}.fcrm_forms_filter_popover .fcrm_filter_menu_items_container .fcrm_filter_empty_item .fcrm_filter_empty_text{font-size:14px;font-weight:400;line-height:20px;color:var(--fc-secondary-text);font-style:italic}.fcrm_forms_filter_popover .fcrm_filter_category_header{display:flex;align-items:center;gap:8px;padding-bottom:12px;border-bottom:1px solid var(--fc-primary-border);margin-bottom:12px}.fcrm_forms_filter_popover .fcrm_filter_category_header .fcrm_filter_back_button{display:flex;align-items:center;justify-content:center;width:24px;height:24px;border-radius:6px;border:none;background:transparent;cursor:pointer;transition:background-color .2s ease;padding:0}.fcrm_forms_filter_popover .fcrm_filter_category_header .fcrm_filter_back_button:hover{background-color:var(--fc-secondary-bg)}.fcrm_forms_filter_popover .fcrm_filter_category_header .fcrm_filter_back_button .el-icon{width:16px;height:16px;color:var(--fc-secondary-text)}.fcrm_forms_filter_popover .fcrm_filter_category_header .fcrm_filter_category_title{font-size:14px;font-weight:500;line-height:20px;color:var(--fc-primary-text)}.fcrm_forms_filter_popover .fcrm_filter_search_item{margin-bottom:12px}.fcrm_forms_filter_popover .fcrm_filter_search_item .el-input__wrapper{border-radius:6px;background-color:var(--fc-secondary-bg);box-shadow:none;border:1px solid transparent}.fcrm_forms_filter_popover .fcrm_filter_search_item .el-input__wrapper:hover,.fcrm_forms_filter_popover .fcrm_filter_search_item .el-input__wrapper.is-focused,.fcrm_forms_filter_popover .fcrm_filter_search_item .el-input__wrapper.is-focus{border-color:var(--fc-secondary-border)}.fcrm_forms_filter_popover .fcrm_filter_options_container{max-height:300px;overflow-y:auto}.fcrm_forms_filter_popover .fcrm_filter_options_container .fcrm_filter_options_list{display:flex;flex-direction:column;gap:4px}.fcrm_forms_filter_popover .fcrm_filter_options_container .fcrm_filter_options_list .fcrm_filter_option_item .fcrm_filter_checkbox{width:100%;padding:4px 8px;border-radius:6px;transition:background-color .2s ease}.fcrm_forms_filter_popover .fcrm_filter_options_container .fcrm_filter_options_list .fcrm_filter_option_item .fcrm_filter_checkbox:hover{background-color:var(--fc-secondary-bg)}.fcrm_forms_filter_popover .fcrm_filter_options_container .fcrm_filter_options_list .fcrm_filter_option_item .fcrm_filter_checkbox .el-checkbox__label{font-size:14px;font-weight:400;line-height:20px;color:var(--fc-primary-text)}.fcrm_forms_filter_popover .fcrm_filter_options_container .fcrm_filter_no_results{padding:16px;text-align:center}.fcrm_forms_filter_popover .fcrm_filter_options_container .fcrm_filter_no_results .fcrm_filter_no_results_text{font-size:14px;font-weight:400;line-height:20px;color:var(--fc-secondary-text);margin:0}.el-table .fc_table_row_completed{background:var(--fc-success-bg)}.el-table .fc_table_row_completed td{background:var(--fc-success-bg)!important}.el-table .fc_table_row_pending{background:#fdf5e6}.el-table .fc_table_row_pending td{background:#fdf5e6!important}.fc_individual_progress{padding:20px 5px 0 42px}.fc_individual_progress *{box-sizing:border-box}.fc_individual_progress .fc_progress_card:nth-child(4n+1){clear:left}.fc_progress_item{text-align:center;margin-bottom:20px;padding:20px;border:1px solid var(--fc-deep-bg);border-radius:10px;cursor:pointer;background:var(--fc-secondary-bg);transition:.2s}.fc_progress_item:hover{background:#fff;box-shadow:0 0 20px #7756e633}.fc_progress_item .stats_badges{display:block}.fc_progress_item.fc_sequence_type_benchmark{border:2px solid var(--fc-error)}.fc_progress_item.fc_sequence_type_result{border:2px solid var(--fc-deep-bg)}.fc_step_picker .fc_step_picker_hint{margin:0 0 16px;color:var(--el-text-color-secondary);font-size:13px}.fc_step_picker .el-radio.is-current{background:var(--el-color-primary-light-9);border-radius:6px;padding:8px 12px}.fc_step_picker .el-radio.is-completed{opacity:.5;cursor:not-allowed}.fc_step_picker .el-radio.fc_step_conditional{margin-left:20px}.fc_step_picker .el-tag{margin-left:4px}.text-align-right .fcrm_update_contact_btn{margin-left:auto}.fc_condition_group,.fcrm_condition_group{background:var(--fc-secondary-bg);padding:10px 15px;border-radius:10px;margin-bottom:20px}.fc_condition_group h4,.fcrm_condition_group h4{margin:0}.fcrm_option_creatable{display:block;width:100%;border-radius:4px}.fcrm_option_creatable .fcrm_with_select{position:absolute;right:1px;background-color:var(--fc-secondary-bg);color:var(--fc-secondary-text);border:none;border-left:1px solid var(--fc-primary-border);height:calc(100% - 2px);top:1px;padding:0 8px;display:flex;align-items:center;justify-content:center;box-shadow:0 1px 2px #0a0d1408;border-bottom-right-radius:8px;border-top-right-radius:8px}.fcrm_options_selector{display:flex;width:100%;border-radius:8px;box-sizing:border-box;overflow:inherit!important}.fcrm_options_selector .fcrm_options{flex:1 1 auto}.fcrm_options_selector .el-select .el-select__wrapper{border-radius:var(--fcrm-border-radius-8)}.fcrm_options_selector.fcrm_option_creatable .el-select .el-select__wrapper{padding-right:37px}.fcrm_options_selector .fcrm_with_select{flex:0 0 48px;border-radius:0 8px 8px 0}.fcrm_options_selector .fcrm_with_select .el-button{border:none!important;box-shadow:none!important;outline:none!important;background-color:var(--fc-secondary-bg)!important;color:var(--fc-secondary-text)!important;height:40px;cursor:pointer;display:flex;align-items:center;justify-content:center}.fcrm_options_selector .fcrm_with_select .el-button .fcrm_plus_icon{width:20px;height:20px;display:block}.fc_input_popover_wrapper,.fc_input_popover_wrapper .el-input.fc_pop_append,.fc_input_popover_wrapper.is-textarea{width:100%}.fc_input_popover_wrapper.is-textarea .fc_textarea_with_popover{display:flex;width:100%;align-items:flex-start;gap:6px}.fc_input_popover_wrapper.is-textarea .fc_textarea_with_popover .el-input{flex:1 1 auto;width:100%}.fc_input_popover_wrapper .fluentcrm_url{cursor:pointer}.fluentcrm_photo_holder img{max-height:100px;margin-right:6px}.fcrm_fluentcrm_photo_card .fcrm_fluentcrm_photo_holder{display:flex;flex-direction:column;gap:12px;align-items:flex-start}.fcrm_fluentcrm_photo_card .fcrm_fluentcrm_photo_holder .fcrm_photo_image_wrapper{display:flex;align-items:center;justify-content:center}.fcrm_fluentcrm_photo_card .fcrm_fluentcrm_photo_holder .fcrm_photo_image_wrapper .fcrm_photo_image{max-height:100px;max-width:100%;border-radius:8px}.fcrm_date_parts_picker{width:100%;display:flex;gap:10px}.fcrm_date_parts_picker>div{max-width:100px}.fcrm_date_parts_picker>div p{margin:0;font-size:10px;padding-left:10px;color:var(--fc-text-muted)}.fcrm_date_parts_picker>div{width:100%}.fcrm_sample a{cursor:pointer}.fcrm_form_builder_new{display:flex;flex-direction:column}.fcrm_promo_wrapper img{max-width:100%}.fc_visual_modal .el-dialog{margin-top:0!important}.fc_builder_modal_wrap{position:fixed;z-index:99999;background-color:#00000080;top:0;right:0;bottom:0;left:0;display:flex;align-items:center;justify-content:center}.fc_builder_modal_wrap .fc_visual_modal{background-color:var(--fc-primary-bg);padding:0;width:100%;height:100%;position:relative;overflow:hidden}.fc_builder_modal_wrap .fc_visual_modal iframe{width:100%;height:100%;z-index:9999}.fc_designer_wrapper iframe{width:100%;min-height:calc(100vh - 150px)}.fc_design_template_visual_builder_wrapper .fc_composer_body{width:100%!important;padding:0 15px}.fc_visual_intro{margin:20px auto;max-width:600px;text-align:center;background:#fff;padding:30px;position:absolute;top:40px;z-index:9999;left:calc(50% - 225px);box-shadow:0 0 12px 12px var(--fc-secondary-text)}.fc_locked{overflow:hidden}.fc_visual_preview_inline{max-width:900px;margin:0 auto;position:relative}.fc_visual_preview_inline .fc_iframe_wrap{width:100%;height:800px;overflow:hidden;position:relative}.fc_visual_preview_inline .fc_iframe_wrap:before{content:" ";top:0;left:0;right:0;bottom:0;background:#2b303b3d;position:absolute}.fc_visual_parent{position:absolute;right:10px;top:12px;visibility:hidden;z-index:0}.fc_editor_header{box-shadow:1px 1px 3px #7371711a;position:relative;display:flex;padding:8px 50px 7px 20px;justify-content:space-between}.fc_editor_header .fc_head_left img{width:32px;height:32px}.fc_editor_header .fc_head_left span{position:absolute;top:10px;color:#000;padding-left:5px;font-size:10px}.fc_editor_header .fc_head_right{display:flex;align-items:center}body.fc_locked_loaded.fc_locked .el-dialog{margin:0!important}body.fc_locked_loaded.fc_locked .el-dialog .fc_funnel_editor>div{display:none!important;z-index:0!important}body.fc_locked_loaded.fc_locked .el-dialog .fc_funnel_editor>div.fc_email_writer{display:block!important;z-index:999999!important}body.fc_locked_loaded.fc_locked .el-dialog .fluentcrm_visual_editor{margin:0!important}body.fc_locked_loaded.fc_locked .el-dialog .fluentcrm-sequence_control{display:none}body.fc_locked_loaded.fc_locked .el-dialog .fluentcrm_block_editor_body>form>div{display:none}body.fc_locked_loaded.fc_locked .el-dialog .fluentcrm_block_editor_body>form>div.fc_funnel_editor{display:inherit}body.fc_locked_loaded.fc_locked .el-dialog .fc_design_template_visual_builder_wrapper .el-row>div{display:none}body.fc_locked_loaded.fc_locked .el-dialog .fc_design_template_visual_builder_wrapper .el-row>div.fc_composer_body{display:inherit}body.fc_locked_loaded div#wpwrap{z-index:0}.fc_visual_starter{margin:30px 0;text-align:center}.fc_visual_starter h1{color:var(--fc-secondary-text);font-size:24px;margin-bottom:20px}.fc_visual_blocks{display:flex;margin:0 auto;max-width:1000px}.fc_visual_blocks .fc_visual_block{cursor:pointer;background:#fff;text-align:center;margin:20px;min-width:150px;border:1px solid var(--fc-primary-border);border-radius:4px}.fc_visual_blocks .fc_visual_block img{border-top-left-radius:4px;border-top-right-radius:4px;max-width:100%}.fc_visual_blocks .fc_visual_block:hover{border:1px solid var(--fc-text-link)}.fc_visual_blocks .fc_visual_block:hover h3{color:var(--fc-text-link)}.wp_vue_editor{width:100%;min-height:100px}.wp_vue_editor_wrapper{position:relative;display:flex;flex-direction:column;gap:8px;width:100%}.wp_vue_editor_wrapper .wp-media-buttons,.wp_vue_editor_wrapper .wp-editor-tabs{display:none!important}.wp_vue_editor_wrapper .fcrm-editor-header-row{display:flex;align-items:center;justify-content:space-between;width:100%;margin-bottom:0}.wp_vue_editor_wrapper .fcrm-editor-header-row .fcrm-editor-label{display:flex;align-items:center;gap:4px;flex:1}.wp_vue_editor_wrapper .fcrm-editor-header-row .fcrm-editor-label .label-text{font-weight:500!important;font-size:14px!important;line-height:20px!important;color:var(--fc-primary-text)!important;letter-spacing:-.084px!important}.wp_vue_editor_wrapper .fcrm-editor-header-row .fcrm-editor-label .tooltip-icon{color:var(--fc-text-muted);font-size:14px;cursor:help}.wp_vue_editor_wrapper .fcrm-editor-header-row .fcrm-editor-label .tooltip-icon:hover{color:var(--fc-primary-text)}@media (max-width: 1100px){.wp_vue_editor_wrapper .fcrm-editor-header-row{flex-direction:column;align-items:flex-start;gap:8px}.wp_vue_editor_wrapper .fcrm-editor-header-row .fcrm-editor-actions{flex-wrap:wrap;justify-content:flex-start}}.wp_vue_editor_wrapper .fcrm-editor-actions{display:flex;gap:12px;align-items:center;justify-content:flex-end}.wp_vue_editor_wrapper .fcrm-editor-actions.full-width{justify-content:flex-end;width:100%}.wp_vue_editor_wrapper .fcrm-editor-actions .fcrm_secondary_btn{height:28px}.wp_vue_editor_wrapper .fcrm-editor-actions .fcrm-editor-btn-add-media{background:var(--fc-primary-bg)!important;border:1px solid var(--fc-primary-border)!important;border-radius:8px!important;padding:4px 6px!important;color:var(--fc-secondary-text)!important;font-weight:500!important;font-size:14px!important;line-height:20px!important;letter-spacing:-.084px!important;height:auto!important;min-height:auto!important;box-shadow:none!important}.wp_vue_editor_wrapper .fcrm-editor-actions .fcrm-editor-btn-add-media:hover{background:var(--fc-secondary-bg)!important;border-color:var(--fc-secondary-border)!important;color:var(--fc-secondary-text)!important;box-shadow:none!important}.wp_vue_editor_wrapper .fcrm-editor-actions .fcrm-editor-btn-add-media:focus,.wp_vue_editor_wrapper .fcrm-editor-actions .fcrm-editor-btn-add-media:active{background:var(--fc-primary-bg)!important;border-color:var(--fc-primary-border)!important;box-shadow:none!important}.wp_vue_editor_wrapper .fcrm-editor-actions .fcrm-editor-btn-add-media span{display:flex;align-items:center;gap:2px}.wp_vue_editor_wrapper .fcrm-editor-actions .fcrm-editor-btn-add-media .el-icon{font-size:20px}.wp_vue_editor_wrapper .fcrm-editor-actions .fcrm-editor-shortcode-popover{line-height:1}.wp_vue_editor_wrapper .fcrm-editor-actions .fcrm-editor-toggle .fcrm-toggle-switch{background:var(--fc-secondary-bg);border-radius:6px;padding:4px;display:flex;gap:4px;width:128px;height:28px}.wp_vue_editor_wrapper .fcrm-editor-actions .fcrm-editor-toggle .fcrm-toggle-switch button{flex:1;background:transparent;border:none;border-radius:4px;padding:2px 4px;font-weight:500!important;font-size:12px!important;line-height:16px!important;color:var(--fc-text-muted)!important;cursor:pointer;transition:all .2s ease;text-align:center}.wp_vue_editor_wrapper .fcrm-editor-actions .fcrm-editor-toggle .fcrm-toggle-switch button.active{background:var(--fc-primary-bg)!important;color:var(--fc-primary-text)!important;box-shadow:0 6px 10px #0e121b0f,0 2px 4px #0e121b08!important}.wp_vue_editor_wrapper .fcrm-editor-actions .fcrm-editor-toggle .fcrm-toggle-switch button:hover:not(.active){color:var(--fc-secondary-text)!important}.wp_vue_editor_wrapper .popover-wrapper{z-index:2;margin-left:auto}.wp_vue_editor_wrapper .popover-wrapper-plaintext{left:auto;right:0;top:-32px}.wp_vue_editor_wrapper .popover-wrapper .el-button-group .editor-add-shortcode{height:auto;border:1px solid var(--fc-primary-border);background:var(--fc-primary-bg);box-shadow:0 1px 2px #0a0d1408;color:var(--fc-secondary-text);font-weight:500;font-size:14px;line-height:20px;border-radius:8px;padding:4px 8px}.wp_vue_editor_wrapper .wp-core-ui{border-radius:8px;overflow:hidden}.wp_vue_editor_wrapper .mce-edit-area{border:1px solid var(--fc-primary-border)!important;border-top:none!important;border-bottom:none!important}.wp_vue_editor_wrapper .mce-edit-area iframe{min-height:160px!important}.wp_vue_editor_wrapper .mce-statusbar{border:1px solid var(--fc-primary-border)!important;border-top:none!important;border-radius:0 0 8px 8px!important;background:var(--fc-primary-bg)!important;padding:4px 12px!important}.wp_vue_editor_wrapper .wp-editor-tools>.wp-editor-container{border:1px solid var(--fc-primary-border);background:var(--fc-primary-bg)}.wp_vue_editor_wrapper .wp-editor-tools .wp-editor-container .mce-tinymce .mce-container{border:none!important;box-shadow:none}.wp_vue_editor_wrapper .wp-editor-tools .wp-editor-container .mce-top-part:before{box-shadow:none}.wp_vue_editor_wrapper .wp-editor-tools .wp-editor-container .mce-top-part .mce-container-body .mce-toolbar-grp{border-radius:8px 8px 0 0!important}.wp_vue_editor_wrapper .wp_vue_editor_plain{border:1px solid var(--fc-primary-border)!important;border-radius:8px!important;padding:12px!important;font-size:14px!important;line-height:20px!important;color:var(--fc-secondary-text)!important;resize:vertical;min-height:160px!important;box-shadow:none!important}.wp_vue_editor_wrapper .wp_vue_editor_plain:focus{outline:none!important;border-color:var(--fc-text-link)!important;box-shadow:none!important}.wp_vue_editor_wrapper .wp_vue_editor_plain:hover{border-color:var(--fc-secondary-border)!important}.wp_vue_editor_wrapper .fcrm-editor-info-alert{background:var(--fc-secondary-bg);border-radius:8px;padding:8px;display:flex;gap:8px;align-items:center}.wp_vue_editor_wrapper .fcrm-editor-info-alert .info-icon{color:var(--fc-text-muted);font-size:16px;flex-shrink:0}.wp_vue_editor_wrapper .fcrm-editor-info-alert p{margin:0;font-weight:400;font-size:12px;line-height:16px;color:var(--fc-primary-text);flex:1}.fcrm_topbar{display:flex;align-items:center;gap:16px;position:sticky;top:32px;z-index:2000;margin-left:-20px;padding:10px 20px;background:var(--fc-primary-bg);border-bottom:1px solid var(--fc-primary-border);transition:top .3s ease}.fcrm_topbar *{box-sizing:border-box}.fcrm_topbar.fcrm_has_fixed_composer{top:-32px}.fluentcrm_app_wrapper{container:fcrm-topbar-shell/inline-size}.fcrm_topbar_left{display:flex;align-items:center;margin-right:8px;padding:0}.fcrm_topbar_left a{display:flex;align-items:center;line-height:0;position:relative}.fcrm_topbar_left a img{height:36px;padding:0}.fcrm_topbar_left a span{position:absolute;top:18px;left:34px;color:var(--fc-primary-text);font-size:10px;margin-left:6px}.fcrm_topbar_center{flex:1;display:flex;justify-content:center}.fcrm_topbar_right{display:flex;gap:8px;align-items:center;margin-left:auto}.fcrm_icon_btn{display:inline-flex;align-items:center;justify-content:center;padding:8px;border-radius:8px;background:transparent;border:none;cursor:pointer;color:var(--fc-secondary-text)}.fcrm_icon_btn:hover{background:var(--fc-secondary-bg);color:var(--fc-primary-text)}.fcrm_icon_btn svg,.fcrm_icon_btn .dashicons{width:20px;height:20px;font-size:20px;line-height:20px}.fcrm_icon_btn.fcrm_icon_weak{background:var(--fc-secondary-bg)}.fcrm_icon_menu{position:relative;display:inline-flex}.fcrm_icon_menu.fcrm_active .fcrm_icon_btn{background:var(--fc-light-bg);color:var(--fc-primary-text)}.fcrm_icon_menu .fcrm_icon_btn svg path{fill:var(--fc-secondary-text)}.fcrm_icon_menu .fcrm_icon_btn:hover svg path,.fcrm_icon_menu.fcrm_active .fcrm_icon_btn svg path{fill:var(--fc-primary-text)}.fcrm_icon_menu a:active,.fcrm_icon_menu a:focus{outline:none;box-shadow:none}.fcrm_icon_menu .fcrm_submenu_items{display:none;position:absolute;z-index:999999;top:calc(100% + 4px);right:0;min-width:180px;width:230px;padding:8px;background:var(--fc-primary-bg);border:none;border-radius:8px;box-shadow:0 0 20px #1c273214}.fcrm_icon_menu .fcrm_submenu_items:before{content:"";position:absolute;top:-8px;left:0;right:0;height:8px}.fcrm_icon_menu .fcrm_submenu_items a{display:block;padding:8px;border-radius:8px;color:var(--fc-secondary-text);text-decoration:none}.fcrm_icon_menu .fcrm_submenu_items a:hover{background:var(--fc-secondary-bg);color:var(--fc-primary-text)}.fcrm_icon_menu .fcrm_submenu_items a:focus{box-shadow:none}.fcrm_icon_menu:hover>.fcrm_submenu_items,.fcrm_icon_menu.fcrm_open>.fcrm_submenu_items{display:block}#fcrm_admin_menu_search .fcrm_icon_btn:hover svg path{fill:var(--fc-primary-text)}.fcrm_global_search{padding:4px 4px 4px 8px;border-radius:8px;background:var(--fc-secondary-bg);font-weight:700;display:inline-flex;align-items:center;gap:6px;cursor:pointer}.fcrm_global_search .icon{display:block}.fcrm_global_search .icon svg{display:block}.fcrm_global_search .slash-icon{display:flex;align-items:center;justify-content:center;background:var(--fc-primary-bg);width:28px;height:28px;border-radius:6px}ul.fcrm_menu{display:flex;align-items:center;gap:8px;list-style:none;margin:0;padding:0;float:none!important}ul.fcrm_menu li{position:relative;margin:0;padding:0}ul.fcrm_menu li .fcrm_menu_primary{display:inline-flex;align-items:center;gap:8px;padding:8px 12px;border-radius:8px;text-decoration:none;color:var(--fc-secondary-text);font-size:14px;font-weight:500;line-height:20px;white-space:nowrap}ul.fcrm_menu li .fcrm_menu_primary:focus{box-shadow:none}ul.fcrm_menu li .fcrm_menu_primary:active{outline:none;box-shadow:none}ul.fcrm_menu li .fcrm_menu_primary:hover{color:var(--fc-primary-text);background-color:var(--fc-secondary-bg)}ul.fcrm_menu li .fcrm_menu_primary .fcrm_submenu_handler svg{display:block}ul.fcrm_menu li .fcrm_menu_primary .fcrm_submenu_handler,ul.fcrm_menu li .fcrm_menu_primary .dashicons{font-size:20px;line-height:20px;height:20px;width:20px;opacity:.9}ul.fcrm_menu li.fcrm_active .fcrm_menu_primary{color:var(--fc-primary-text);background-color:var(--fc-secondary-bg)}ul.fcrm_menu li .fcrm_submenu_items{display:none;position:absolute;z-index:999999;top:calc(100% + 4px);right:0;min-width:180px;width:230px;padding:8px;background:var(--fc-primary-bg);border:none;border-radius:8px;box-shadow:0 0 20px #1c273214}ul.fcrm_menu li .fcrm_submenu_items:before{content:"";position:absolute;top:-8px;left:0;right:0;height:8px}ul.fcrm_menu li .fcrm_submenu_items a{display:block;padding:8px;border-radius:8px;color:var(--fc-secondary-text);text-decoration:none}ul.fcrm_menu li .fcrm_submenu_items a:hover{background:var(--fc-secondary-bg);color:var(--fc-primary-text)}ul.fcrm_menu li .fcrm_submenu_items a:focus{box-shadow:none}ul.fcrm_menu li .fcrm_submenu_items.fcrm_force_hide{display:none!important}ul.fcrm_menu li .fcrm_submenu_items.fcrm_2_col_menu{display:none;width:600px;right:-235px;padding:10px;gap:10px;white-space:normal;grid-template-columns:repeat(2,minmax(30px,1fr))}ul.fcrm_menu li .fcrm_submenu_items.fcrm_2_col_menu.fcrm_force_hide{display:none!important}ul.fcrm_menu li:hover>.fcrm_submenu_items{display:block}ul.fcrm_menu li:hover>.fcrm_submenu_items.fcrm_2_col_menu{display:grid}.fcrm_submenu_items:hover{display:block}.fcrm_menu_card .fcrm_menu_title{color:var(--fc-primary-text);display:block;font-weight:400;font-size:14px;line-height:20px;margin:0}.fcrm_menu_card.fcrm_menu_card_with_icon{padding-left:28px}.fcrm_menu_item .fcrm_menu_card{position:relative}.fcrm_menu_item .fcrm_menu_icon{display:block;position:absolute;left:0;top:1px}.fcrm_menu_item .fcrm_menu_icon svg{display:block}.fcrm_menu_description{margin:4px 0 0;color:var(--fc-secondary-text);font-weight:400;font-size:12px;line-height:16px;display:none}.fcrm_handheld{display:none;cursor:pointer;padding:8px;border-radius:8px}.fcrm_handheld.fcrm_humberg_menu{padding:0;height:20px;width:20px;flex-direction:column;justify-content:space-around}.fcrm_handheld.fcrm_humberg_menu span{display:block;height:1.5px;background:var(--fc-secondary-text);width:65%;margin-left:auto}.fcrm_handheld.fcrm_humberg_menu span:nth-child(2){width:90%}.fcrm_handheld.fcrm_humberg_menu span:last-child{margin-right:auto;margin-left:0}ul.fcrm_menu.fcrm_menu_open li.fcrm_close_menu_btn_wrap{display:none;margin-bottom:8px}ul.fcrm_menu.fcrm_menu_open .fcrm_close_menu_btn{display:flex;height:28px;width:28px;cursor:pointer;align-items:center;justify-content:center;border-radius:9999px;border-width:0;background:var(--fc-primary-bg)}ul.fcrm_menu.fcrm_menu_open .fcrm_close_menu_btn:hover{background-color:var(--fc-secondary-bg)}ul.fcrm_menu.fcrm_menu_open .fcrm_close_menu_btn svg{width:16px;height:16px;display:block}.fcrm_settings_pro_banner_details_get_btn{display:flex;padding:8px;justify-content:center;align-items:center;gap:4px;width:100%;border-radius:8px;background:#7742e61a}.fcrm_settings_pro_banner_details_get_btn svg{width:20px;height:20px}.fcrm_settings_pro_banner_details_get_btn span{color:#8762f0;font-size:14px;font-style:normal;font-weight:500;line-height:20px;letter-spacing:-.084px}@media (max-width: 1224px){.fcrm_topbar{gap:8px;padding:10px 20px}.fcrm_topbar_left{min-width:60px;margin-right:4px}.fcrm_topbar_left a img{height:28px}ul.fcrm_menu{gap:4px}ul.fcrm_menu li .fcrm_menu_primary{padding:6px 10px;font-size:13px}.fcrm_topbar_right{gap:4px}.fcrm_icon_menu .fcrm_submenu_items.fcrm_2_col_menu,ul.fcrm_menu li .fcrm_submenu_items.fcrm_2_col_menu{width:500px;right:-200px}}@container fcrm-topbar-shell (max-width: 1224px){.fcrm_topbar{gap:8px;padding:10px 20px}.fcrm_topbar_left{min-width:60px;margin-right:4px}.fcrm_topbar_left a img{height:28px}ul.fcrm_menu{gap:4px}ul.fcrm_menu li .fcrm_menu_primary{padding:6px 10px;font-size:13px}.fcrm_topbar_right{gap:4px}.fcrm_icon_menu .fcrm_submenu_items.fcrm_2_col_menu,ul.fcrm_menu li .fcrm_submenu_items.fcrm_2_col_menu{width:500px;right:-200px}}@media (max-width: 1024px){.fcrm_topbar{padding:10px 12px;gap:8px;flex-wrap:nowrap}.fcrm_topbar_left{min-width:auto;margin-right:0}.fcrm_topbar_left a img{height:26px}.fcrm_topbar_left a span{font-size:9px;top:16px;left:28px}.fcrm_handheld{display:inline-flex;align-items:center}.fcrm_handheld.fcrm_humberg_menu{padding:0;height:20px;width:20px;flex-direction:column;justify-content:space-around}.fcrm_topbar_center{position:static;flex:0}ul.fcrm_menu{transition:.3s;overflow-x:hidden;visibility:hidden;position:fixed;left:auto;top:0;right:0;height:100vh;width:0;background-color:var(--fc-primary-bg);padding:70px 12px 12px;opacity:0;z-index:9999;align-items:flex-start;display:flex;flex-direction:column}ul.fcrm_menu.fcrm_menu_open{visibility:visible;width:270px;opacity:1}ul.fcrm_menu.fcrm_menu_open li{display:block;margin:0;width:100%}ul.fcrm_menu.fcrm_menu_open li.fcrm_close_menu_btn_wrap{display:block}ul.fcrm_menu.fcrm_menu_open .fcrm_menu_primary{width:100%;border-radius:8px;padding:8px 12px;font-size:14px;font-weight:500;justify-content:space-between}ul.fcrm_menu.fcrm_menu_open .fcrm_menu_primary:hover{background:var(--fc-secondary-bg)}ul.fcrm_menu.fcrm_menu_open li .fcrm_submenu_items{position:static;display:none!important;box-shadow:none;border-radius:0;border:none;border-top:1px solid var(--fc-primary-border);padding:8px 0;margin:0;background:var(--fc-secondary-bg)}ul.fcrm_menu.fcrm_menu_open li.fcrm_submenu_open .fcrm_submenu_items{display:block!important}ul.fcrm_menu.fcrm_menu_open .fcrm_submenu_items a{padding:10px 16px 10px 22px;background:transparent}ul.fcrm_menu.fcrm_menu_open .fcrm_submenu_items a:hover{background:var(--fc-primary-bg)}ul.fcrm_menu.fcrm_menu_open .fcrm_submenu_items.fcrm_2_col_menu{display:none!important;grid-template-columns:1fr;width:100%;padding:8px 0}ul.fcrm_menu.fcrm_menu_open li.fcrm_submenu_open .fcrm_submenu_items.fcrm_2_col_menu{display:grid!important}.fcrm_topbar_right{gap:6px}.fcrm_icon_btn{padding:6px}.fcrm_icon_btn svg,.fcrm_icon_btn .dashicons{width:18px;height:18px;font-size:18px}.fcrm_icon_menu .fcrm_submenu_items{right:0;min-width:200px}}@container fcrm-topbar-shell (max-width: 1024px){.fcrm_topbar{padding:10px 12px;gap:8px;flex-wrap:nowrap}.fcrm_topbar_left{min-width:auto;margin-right:0}.fcrm_topbar_left a img{height:26px}.fcrm_topbar_left a span{font-size:9px;top:16px;left:28px}.fcrm_handheld{display:inline-flex;align-items:center}.fcrm_handheld.fcrm_humberg_menu{padding:0;height:20px;width:20px;flex-direction:column;justify-content:space-around}.fcrm_topbar_center{position:static;flex:0}ul.fcrm_menu{transition:.3s;overflow-x:hidden;visibility:hidden;position:fixed;left:auto;top:0;right:0;height:100vh;width:0;background-color:var(--fc-primary-bg);padding:70px 12px 12px;opacity:0;z-index:9999;align-items:flex-start;display:flex;flex-direction:column}ul.fcrm_menu.fcrm_menu_open{visibility:visible;width:270px;opacity:1}ul.fcrm_menu.fcrm_menu_open li{display:block;margin:0;width:100%}ul.fcrm_menu.fcrm_menu_open li.fcrm_close_menu_btn_wrap{display:block}ul.fcrm_menu.fcrm_menu_open .fcrm_menu_primary{width:100%;border-radius:8px;padding:8px 12px;font-size:14px;font-weight:500;justify-content:space-between}ul.fcrm_menu.fcrm_menu_open .fcrm_menu_primary:hover{background:var(--fc-secondary-bg)}ul.fcrm_menu.fcrm_menu_open li .fcrm_submenu_items{position:static;display:none!important;box-shadow:none;border-radius:0;border:none;border-top:1px solid var(--fc-primary-border);padding:8px 0;margin:0;background:var(--fc-secondary-bg)}ul.fcrm_menu.fcrm_menu_open li.fcrm_submenu_open .fcrm_submenu_items{display:block!important}ul.fcrm_menu.fcrm_menu_open .fcrm_submenu_items a{padding:10px 16px 10px 22px;background:transparent}ul.fcrm_menu.fcrm_menu_open .fcrm_submenu_items a:hover{background:var(--fc-primary-bg)}ul.fcrm_menu.fcrm_menu_open .fcrm_submenu_items.fcrm_2_col_menu{display:none!important;grid-template-columns:1fr;width:100%;padding:8px 0}ul.fcrm_menu.fcrm_menu_open li.fcrm_submenu_open .fcrm_submenu_items.fcrm_2_col_menu{display:grid!important}.fcrm_topbar_right{gap:6px}.fcrm_icon_btn{padding:6px}.fcrm_icon_btn svg,.fcrm_icon_btn .dashicons{width:18px;height:18px;font-size:18px}.fcrm_icon_menu .fcrm_submenu_items{right:0;min-width:200px}}@media (max-width: 782px){.fcrm_topbar{position:relative;top:0;margin:0}}@media (max-width: 480px){.fcrm_topbar{padding:8px 10px}.fcrm_topbar_left a img{height:24px}.fcrm_topbar_right{gap:2px}.fcrm_icon_btn{padding:5px}}html.fluentcrm_go_full{padding-top:0}html.fluentcrm_go_full body{background:var(--fc-primary-bg)}html.fluentcrm_go_full div#wpadminbar{display:none}html.fluentcrm_go_full div#adminmenumain{display:none;margin-left:0}html.fluentcrm_go_full div#wpcontent{margin-left:0;padding-left:0}html.fluentcrm_go_full .fluentcrm-app{max-width:1200px;margin:0 auto;padding:0 20px 40px;background:var(--fc-secondary-bg);color:var(--fc-secondary-text);box-shadow:0 5px 5px 6px var(--fc-light-bg)}html.fluentcrm_go_full .fluentcrm-header{margin:0 -20px;border:1px solid var(--fc-light-bg);border-bottom:0}html.fluentcrm_go_full .fluentcrm-view-wrapper{margin:-15px -20px 0}.fluentcrm-navigation .el-menu-item:last-child{border:none;float:right}.fcrm_global_search_modal .el-overlay-dialog{display:flex;align-items:flex-start;justify-content:center}.fcrm_global_search_modal .fcrm_global_search_container{width:100%;max-width:600px;max-height:min(520px,100vh - 120px);border-radius:var(--fcrm-border-radius-8, 8px);margin-top:90px!important}.fcrm_global_search_modal .fcrm_global_search_container .el-dialog__header{background:none;display:flex;align-items:center;justify-content:space-between;padding:10px 20px;border-bottom:1px solid var(--fc-primary-border)}.fcrm_global_search_modal .fcrm_global_search_container .el-dialog__header .fcrm_global_search_input_container{position:relative;display:flex;width:100%;align-items:center;gap:8px;padding-left:24px}.fcrm_global_search_modal .fcrm_global_search_container .el-dialog__header .fcrm_global_search_input_container>.icon{position:absolute;left:0;z-index:10;display:flex;align-items:center;justify-content:center;width:20px;height:20px;background:transparent;pointer-events:none}.fcrm_global_search_modal .fcrm_global_search_container .el-dialog__header .fcrm_global_search_input_container>.icon svg{display:block}.fcrm_global_search_modal .fcrm_global_search_container .el-dialog__header .fcrm_global_search_input_container .searched_item{display:flex;align-items:center;gap:2px;border-radius:4px;background:var(--fc-secondary-bg);color:var(--fc-secondary-text);font-size:12px;line-height:1rem;padding:2px 4px 2px 8px}.fcrm_global_search_modal .fcrm_global_search_container .el-dialog__header .fcrm_global_search_input_container .searched_item .icon{display:flex;width:16px;height:16px;flex:none;align-items:center;justify-content:center;cursor:pointer}.fcrm_global_search_modal .fcrm_global_search_container .el-dialog__header .fcrm_global_search_input_container .searched_item .icon:hover{color:var(--fc-primary-text)}.fcrm_global_search_modal .fcrm_global_search_container .el-dialog__header .fcrm_global_search_input_container .el-input .el-input__wrapper{min-height:30px;border-radius:0;padding:0;border:none;box-shadow:none}.fcrm_global_search_modal .fcrm_global_search_container .el-dialog__header .fcrm_global_search_input_container .el-input .el-input__wrapper input{margin:0}.fcrm_global_search_modal .fcrm_global_search_container .el-dialog__header .el-dialog__headerbtn{position:relative;top:0;left:0;display:flex;height:30px;width:30px;align-items:center;justify-content:center;flex:none}.fcrm_global_search_modal .fcrm_global_search_container .el-dialog__body{padding:10px}.fcrm_global_search_modal .fcrm_global_search_container .fcrm_global_searching_for{margin-left:-10px;margin-right:-10px;margin-bottom:12px;padding-left:20px;padding-right:20px;padding-bottom:12px;border-bottom:1px solid var(--fc-primary-border)}.fcrm_global_search_modal .fcrm_global_search_container .fcrm_global_searching_for_label{font-size:12px;line-height:16px;color:var(--fc-secondary-text);font-weight:500;margin:0 0 8px}.fcrm_global_search_modal .fcrm_global_search_container .fcrm_global_searching_for .fcrm_global_searching_suggestion{cursor:pointer;border-radius:4px;background-color:var(--fc-secondary-bg);color:var(--fc-secondary-text);padding:2px 8px;font-size:12px;font-weight:500;line-height:1rem}.fcrm_global_search_modal .fcrm_global_search_container .fcrm_global_searching_for .fcrm_global_searching_suggestion_list{display:flex;flex-wrap:wrap;gap:8px}.fcrm_global_search_modal .fcrm_global_search_container .fcrm_global_search_result_container .fcrm_global_search_result_list{margin:0;max-height:min(320px,100vh - 300px);list-style-type:none;overflow-y:auto;overflow-x:hidden;padding:0 0 8px}.fcrm_global_search_modal .fcrm_global_search_container .fcrm_global_search_result_container .fcrm_global_search_result_list li{position:relative;margin-bottom:4px;display:flex;cursor:pointer;align-items:center;justify-content:space-between;border:1px solid transparent;border-radius:8px;padding:12px;font-size:14px;font-weight:400;line-height:20px;color:var(--fc-primary-text);transition:background-color .16s ease,border-color .16s ease,box-shadow .16s ease;-webkit-transition:background-color .16s ease,border-color .16s ease,box-shadow .16s ease}.fcrm_global_search_modal .fcrm_global_search_container .fcrm_global_search_result_container .fcrm_global_search_result_list li.selected,.fcrm_global_search_modal .fcrm_global_search_container .fcrm_global_search_result_container .fcrm_global_search_result_list li:hover{background:var(--fc-secondary-bg);border-color:var(--fc-primary-border);box-shadow:0 1px 2px #0e121b0a}.fcrm_global_search_modal .fcrm_global_search_container .fcrm_global_search_result_container .fcrm_global_search_result_list li.selected .action-icon,.fcrm_global_search_modal .fcrm_global_search_container .fcrm_global_search_result_container .fcrm_global_search_result_list li:hover .action-icon{visibility:visible;opacity:1}.fcrm_global_search_modal .fcrm_global_search_container .fcrm_global_search_result_container .fcrm_global_search_result_list li .action-icon{visibility:hidden;position:absolute;right:10px;display:flex;align-items:center;justify-content:center;width:20px;height:20px;border-radius:4px;font-size:14px;opacity:0;transition:opacity .16s ease}.fcrm_global_search_modal .fcrm_global_search_container .fcrm_global_search_result_container .fcrm_global_search_result_list li .action-icon .icon{width:10px;color:var(--fc-secondary-text)}.fcrm_global_search_modal .fcrm_global_search_container .fcrm_global_search_result_container .fcrm_global_search_result_list li .fcrm_global_search_subscriber{display:flex;align-items:center;gap:12px;min-width:0}.fcrm_global_search_modal .fcrm_global_search_container .fcrm_global_search_result_container .fcrm_global_search_result_list li .fcrm_global_search_subscriber .fcrm_global_search_subscriber_avatar{width:40px;height:40px;flex-shrink:0;border-radius:999px;overflow:hidden;background:var(--fc-secondary-bg);display:flex;align-items:center;justify-content:center}.fcrm_global_search_modal .fcrm_global_search_container .fcrm_global_search_result_container .fcrm_global_search_result_list li .fcrm_global_search_subscriber .fcrm_global_search_subscriber_avatar .fcrm_global_search_subscriber_photo{width:100%;height:100%;object-fit:cover;object-position:center}.fcrm_global_search_modal .fcrm_global_search_container .fcrm_global_search_result_container .fcrm_global_search_result_list li .fcrm_global_search_subscriber .fcrm_global_search_subscriber_avatar .fcrm_global_search_subscriber_avatar_placeholder{width:100%;height:100%;display:flex;align-items:center;justify-content:center;font-size:16px;font-weight:500;color:var(--fc-secondary-text);background:var(--fc-warning-bg)}.fcrm_global_search_modal .fcrm_global_search_container .fcrm_global_search_result_container .fcrm_global_search_result_list li .fcrm_global_search_subscriber .fcrm_global_search_subscriber_info{display:flex;flex-direction:column;gap:2px;min-width:0}.fcrm_global_search_modal .fcrm_global_search_container .fcrm_global_search_result_container .fcrm_global_search_result_list li .fcrm_global_search_subscriber .fcrm_global_search_subscriber_meta{display:flex;align-items:center;gap:8px;min-width:0}.fcrm_global_search_modal .fcrm_global_search_container .fcrm_global_search_result_container .fcrm_global_search_result_list li .fcrm_global_search_subscriber_name{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.fcrm_global_search_modal .fcrm_global_search_container .fcrm_global_search_result_container .fcrm_global_search_result_list li .fcrm_global_search_subscriber_email{color:var(--fc-text-muted);font-size:12px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.fcrm_global_search_modal .fcrm_global_search_container .fcrm_global_search_result_container .fcrm_global_search_result_list li .fcrm_global_search_subscriber .fcrm_global_search_result_type{display:inline-flex;align-items:center;padding:2px 6px;border-radius:999px;background:var(--fc-secondary-bg);color:var(--fc-secondary-text);font-size:11px;line-height:1;font-weight:500;white-space:nowrap;flex-shrink:0}.fcrm_global_search_modal .fcrm_global_search_container .fcrm_global_search_commands{margin-left:-10px;margin-right:-10px;padding:0 10px}.fcrm_global_search_modal .fcrm_global_search_container .fcrm_global_search_commands_label{font-size:12px;line-height:16px;color:var(--fc-secondary-text);font-weight:500;margin:0 0 6px 10px}.fcrm_global_search_modal .fcrm_global_search_container .fcrm_global_search_commands_list{list-style:none;margin:0;padding:0}.fcrm_global_search_modal .fcrm_global_search_container .fcrm_global_search_commands_list li{display:flex;align-items:center;justify-content:space-between;padding:10px 12px;border-radius:8px;cursor:pointer;border:1px solid transparent;transition:background-color .16s ease,border-color .16s ease}.fcrm_global_search_modal .fcrm_global_search_container .fcrm_global_search_commands_list li.selected,.fcrm_global_search_modal .fcrm_global_search_container .fcrm_global_search_commands_list li:hover{background:var(--fc-secondary-bg);border-color:var(--fc-primary-border)}.fcrm_global_search_modal .fcrm_global_search_container .fcrm_global_search_commands .fcrm_command_slash{font-size:14px;font-weight:500;color:var(--fc-primary-text);font-family:ui-monospace,SFMono-Regular,SF Mono,Menlo,Consolas,monospace}.fcrm_global_search_modal .fcrm_global_search_container .fcrm_global_search_commands .fcrm_command_desc{font-size:12px;color:var(--fc-text-muted)}.fcrm_global_search_modal .fcrm_global_search_container .fcrm_global_search_commands_empty{padding:20px;text-align:center;color:var(--fc-text-muted);font-size:13px}.fcrm_global_search_modal .fcrm_global_search_container .fcrm_global_search_no_result{display:flex;flex-direction:column;align-items:center;justify-content:center;padding-bottom:20px}.fcrm_global_search_modal .fcrm_global_search_container .fcrm_global_search_no_result .icon{display:block;width:32px}.fcrm_global_search_modal .fcrm_global_search_container .fcrm_global_search_no_result p{padding:0;margin:8px 0 0;color:var(--fc-secondary-text);font-size:12px;line-height:16px}.fcrm_global_search_modal .fcrm_global_search_container .dialog-footer{background:none;border-top:1px solid var(--fc-primary-border);margin-left:-10px;margin-right:-10px;padding:14px 20px 4px}.fcrm_global_search_modal .fcrm_global_search_container .dialog-footer .fcrm_search_tip{display:flex;align-items:center;gap:6px;margin:0 0 8px;font-size:12px;line-height:16px}.fcrm_global_search_modal .fcrm_global_search_container .dialog-footer .fcrm_search_tip_label{color:var(--fc-text-muted);font-weight:600;flex:none}.fcrm_global_search_modal .fcrm_global_search_container .dialog-footer .fcrm_search_tip_text{color:var(--fc-secondary-text)}.fcrm_global_search_modal .fcrm_global_search_container .dialog-footer .fcrm_search_commands{margin:0;display:flex;list-style-type:none;align-items:center;gap:12px;padding:0}.fcrm_global_search_modal .fcrm_global_search_container .dialog-footer .fcrm_search_commands li{display:flex;align-items:center;gap:8px;margin:0}.fcrm_global_search_modal .fcrm_global_search_container .dialog-footer .fcrm_search_commands li .fcrm_command_keys{display:flex;align-items:center;gap:8px}.fcrm_global_search_modal .fcrm_global_search_container .dialog-footer .fcrm_search_commands li .fcrm_command_key{display:flex;height:24px;width:24px;align-items:center;justify-content:center;border:1px solid var(--fc-primary-border);background-color:var(--fc-secondary-bg);border-radius:4px;color:var(--fc-secondary-text);font-size:12px;line-height:16px}.fcrm_global_search_modal .fcrm_global_search_container .dialog-footer .fcrm_search_commands li .fcrm_label{color:var(--fc-secondary-text);font-size:12px;line-height:16px}.doc_read img{display:block;max-width:100%;height:auto}.doc_read figure{margin-left:0;margin-right:0}.doc_read iframe{max-width:100%}.fcrm_docs_wrapper{padding-top:16px}.fcrm_docs_section_header_input{margin-bottom:20px}.fcrm_docs_section_header_input .el-input .el-input__wrapper{background:var(--fc-secondary-bg);box-shadow:none;border-radius:8px;border:1px solid var(--fc-secondary-bg);padding:6px 10px;line-height:1;height:auto;gap:8px}.fcrm_docs_section_header_input .el-input .el-input__wrapper.is-focus,.fcrm_docs_section_header_input .el-input .el-input__wrapper.is-focused{border-color:var(--fc-primary-text)}.fcrm_docs_section_header_input .el-input .el-input__wrapper input{height:auto;min-height:inherit;margin:0;border-radius:0}.fcrm_docs_section_header_input .el-input .el-input__wrapper input::placeholder{color:var(--fc-text-muted)}.fcrm_docs_section_header_input .el-input .el-input__prefix .el-button{margin:0;background:none;border:none;box-shadow:none;outline:none;padding:0;height:auto;color:var(--fc-text-muted)}.fcrm_docs_section_content h1{margin:0 0 14px;color:var(--fc-primary-text);font-weight:500;font-size:18px;line-height:24px}.fcrm_docs_section_content p{font-weight:400;font-size:14px;line-height:20px;margin:0 0 10px;color:var(--fc-primary-text)}.fcrm_docs_section_content p a{color:var(--fc-primary-text);text-decoration:underline}.fcrm_docs_section_content .fcrm_docs_hint{margin:0;color:var(--fc-secondary-text)}.fcrm_docs_list_card ul{display:grid;align-items:flex-start;grid-template-columns:repeat(auto-fit,minmax(250px,1fr));margin:0;padding:0;list-style:none;column-gap:20px;row-gap:10px}.fcrm_docs_list_card ul li{width:100%;display:flex;align-items:center;gap:8px;font-weight:400;font-size:14px;line-height:20px}.fcrm_docs_list_card ul li .external-link{display:block;color:var(--fc-secondary-text)}.fcrm_docs_list_card ul li .external-link svg{display:block}.fcrm_docs_list_card ul li .doc-title{display:block}.fcrm_docs_list_card ul li .doc-title:hover{text-decoration:underline}.fcrm_reading_doc_card .fcrm_card_header--title .external-link{display:flex;align-items:center;gap:8px;color:var(--fc-secondary-text)}.fcrm_reading_doc_card .fcrm_card_header--title .external-link span{color:var(--fc-primary-text)}.fcrm_reading_doc_card .fcrm_card_header--title .external-link svg{display:block}.fcrm_reading_doc_card img{max-width:100%;height:auto;display:block}.fcrm_reading_doc_card figure{margin:0}.fcrm_reading_doc_card .simple_dic{background:var(--fc-secondary-bg);color:var(--fc-primary-text);padding:15px;border-radius:8px;margin-bottom:20px;font-size:14px;line-height:20px}.fcrm_reading_doc_card .simple_dic a{text-decoration:underline;color:var(--fc-primary-text);font-weight:500}.fcrm_docs_navigation{margin-top:16px;border-top:1px solid var(--fc-primary-border);padding-top:16px;display:flex;align-items:center;gap:8px;justify-content:flex-end}.fcrm_docs_navigation .el-button{margin:0}.fcrm-pref-shortcode-wrapper .fcrm-checkbox-with-description{align-items:flex-start!important}.fcrm-pref-shortcode-wrapper .fcrm-checkbox-with-description .el-checkbox__input{margin-top:2px}.fcrm-pref-shortcode-wrapper .fcrm-checkbox-with-description .el-checkbox__label{width:100%;white-space:normal;word-wrap:break-word;overflow-wrap:break-word}.fcrm-pref-shortcode-wrapper .fcrm-checkbox-content-vertical{display:flex;flex-direction:column;gap:4px;width:100%;max-width:100%;word-wrap:break-word;overflow-wrap:break-word}.fcrm-pref-shortcode-wrapper .fcrm-checkbox-text{display:flex;flex-direction:column;gap:4px;width:100%;max-width:100%}.fcrm-pref-shortcode-wrapper .fcrm-checkbox-label{font-size:14px;font-weight:500;line-height:20px;letter-spacing:-.084px;color:var(--fc-primary-text);word-wrap:break-word;overflow-wrap:break-word;word-break:break-word}.fcrm-pref-shortcode-wrapper .fcrm-checkbox-description{font-size:12px;font-weight:400;line-height:16px;color:var(--fc-secondary-text);margin:0;word-wrap:break-word;overflow-wrap:break-word;word-break:break-word;max-width:100%}.fcrm-pref-shortcode-wrapper .fcrm-pref-shortcode-section{margin-top:16px;padding-left:28px;display:flex;flex-direction:column;gap:12px}.fcrm-pref-shortcode-wrapper .fcrm-shortcode-header{margin:0 0 8px;font-size:15px;font-weight:500;color:var(--fc-primary-text)}.fcrm-pref-shortcode-wrapper .fcrm-shortcode-copier{cursor:pointer;width:100%;max-width:220px;margin-bottom:4px}.fcrm-pref-shortcode-wrapper .fcrm-shortcode-copier .fcrm_smart_url_box{padding:12px 16px;border:1px solid var(--fc-primary-border);border-radius:8px;background:var(--fc-secondary-bg);min-height:36px;display:flex;align-items:center;gap:10px}.fcrm-pref-shortcode-wrapper .fcrm-shortcode-copier .fcrm_smart_url_box .fcrm_smart_url_text{flex:1;font-size:14px;font-weight:500;color:var(--fc-primary-text);letter-spacing:-.02em}.fcrm-pref-shortcode-wrapper .fcrm-shortcode-copier .fcrm_smart_url_box .fcrm_copy_btn{flex-shrink:0}.fcrm-pref-shortcode-wrapper .fcrm-helper-text{display:block;margin-top:8px;font-size:12px;color:var(--fc-secondary-text);line-height:16px}.fcrm_readable_recipient_tagger .fcrm_readable_recipient_tagger__section{margin-bottom:20px}.fcrm_readable_recipient_tagger .fcrm_readable_recipient_tagger__section:last-child{margin-bottom:0}.fcrm_readable_recipient_tagger .fcrm_readable_recipient_tagger__section .fc_rich_container{padding:0}.fcrm_readable_recipient_tagger .fcrm_readable_recipient_tagger__section_heading h3{font-weight:500;font-size:14px;line-height:20px;color:var(--fc-primary-text);margin:0 0 12px}.fcrm_readable_recipient_tagger .fcrm_readable_recipient_tagger__section_table{width:100%;text-align:left;border:1px solid var(--fc-primary-border);border-radius:8px;border-spacing:0;border-collapse:separate;overflow:hidden}.fcrm_readable_recipient_tagger .fcrm_readable_recipient_tagger__section_table thead tr th{background:var(--fc-secondary-bg);border-bottom:1px solid var(--fc-primary-border);border-right:1px solid var(--fc-primary-border);color:var(--fc-secondary-text);font-weight:500;font-size:14px;line-height:20px;padding:8px 12px}.fcrm_readable_recipient_tagger .fcrm_readable_recipient_tagger__section_table thead tr th:last-child{border-right:none}.fcrm_readable_recipient_tagger .fcrm_readable_recipient_tagger__section_table tbody tr td{border-bottom:1px solid var(--fc-primary-border);border-right:1px solid var(--fc-primary-border);padding:14px 12px}.fcrm_readable_recipient_tagger .fcrm_readable_recipient_tagger__section_table tbody tr td:last-child{border-right:none}.fcrm_readable_recipient_tagger .fcrm_readable_recipient_tagger__section_table tbody tr:last-child td{border-bottom:none}.fcrm-route-loading-bar{position:fixed;top:32px;left:0;width:0;height:3px;background:var(--fc-deep-bg);z-index:2001;pointer-events:none;animation:fcrm-loading-progress 15s cubic-bezier(.1,.45,0,1) forwards}.fcrm-route-loading-bar.is-finishing{animation:none;width:100%;opacity:0;transition:width .3s ease,opacity .3s ease .15s}.fluentcrm-body{transition:opacity .2s ease}.fluentcrm-body.is-route-loading{opacity:.55;pointer-events:none}@keyframes fcrm-loading-progress{0%{width:0}5%{width:15%}15%{width:40%}30%{width:60%}50%{width:75%}70%{width:85%}90%{width:93%}to{width:97%}}.fcrm_view_company_wrapper .fcrm_page_header{padding-top:8px}.fluentcrm_view .fcrm_view_company_content{margin-top:0}.fluentcrm_view .fcrm_view_company_content .fcrm_segment_menu{background:var(--fc-primary-bg);border-bottom:1px solid var(--fc-primary-border);border-left:none;border-right:none;border-top:none;padding:14px 20px;display:flex;gap:24px;align-items:flex-start;margin-bottom:0;border-top-left-radius:8px;border-top-right-radius:8px}.fluentcrm_view .fcrm_view_company_content .fcrm_segment_menu .el-menu-item{border-bottom:none!important;padding:0;height:auto;line-height:16px;font-size:12px;font-weight:500;letter-spacing:0;color:var(--fc-secondary-text);position:relative;display:flex;align-items:center;gap:4px;justify-content:center;cursor:pointer;transition:color .2s ease}.fluentcrm_view .fcrm_view_company_content .fcrm_segment_menu .el-menu-item:hover{color:var(--fc-primary-text);background:transparent}.fluentcrm_view .fcrm_view_company_content .fcrm_segment_menu .el-menu-item.is-active{color:var(--fc-primary-text)!important;font-weight:500;background:transparent}.fluentcrm_view .fcrm_view_company_content .fcrm_segment_menu .el-menu-item.is-active:after{content:"";position:absolute;bottom:-14px;left:0;right:0;height:2px;background:var(--fc-primary-text)}.fluentcrm_view .fcrm_view_company_content .fcrm_segment_menu .el-menu-item span{text-align:center;line-height:16px}.fcrm_view_mode{display:flex;flex-direction:column;gap:24px;align-items:flex-start;position:relative;width:100%}.fcrm_view_mode .fcrm_view_cards{position:relative;width:100%}.fcrm_view_mode .fcrm_company_profile_address{display:flex;flex-direction:column;gap:6px;color:var(--fc-primary-text)}.fcrm_view_mode .fcrm_company_profile_address p{margin:0;font-size:15px;line-height:22px;font-weight:400}.fcrm_view_mode .fcrm_company_profile_empty{color:var(--fc-secondary-text);font-size:14px;line-height:20px}.fcrm_view_mode .fcrm_view_save_wrap{display:flex;flex-direction:column;gap:12px;align-items:flex-start;padding-top:8px}.fcrm_view_mode .fcrm_view_save_wrap .fcrm_view_save_btn{background:var(--fc-deep-bg);border-color:var(--fc-deep-bg);color:var(--fc-text-inverse);border-radius:8px;padding:10px 16px;font-size:14px;font-weight:500;line-height:20px;letter-spacing:-.084px;transition:background-color .2s ease,border-color .2s ease}.fcrm_view_mode .fcrm_view_save_wrap .fcrm_view_save_btn:hover:not(:disabled){background:var(--fc-primary-text);border-color:var(--fc-primary-text)}.fcrm_view_mode .fcrm_view_save_wrap .fcrm_view_save_btn:disabled{opacity:.5;cursor:not-allowed}.fcrm_view_mode .fcrm_view_save_wrap .fcrm_view_link{font-size:14px;font-weight:400;line-height:20px;letter-spacing:-.084px;color:var(--fc-deep-bg);text-decoration:none;transition:color .2s ease}.fcrm_view_mode .fcrm_view_save_wrap .fcrm_view_link:hover{color:var(--fc-primary-text);text-decoration:underline}.fcrm_view_mode .fcrm_view_cf_wrap{display:flex;flex-direction:column;gap:16px;width:100%}.fcrm_view_mode .fcrm_view_cf_wrap .fcrm_custom_field_wrapper{display:flex;flex-direction:column;gap:16px;width:100%;margin-bottom:0}.fcrm_view_mode .fcrm_view_cf_wrap .fcrm_custom_field_wrapper .fluentcrm_custom_fields{width:100%}.fcrm_view_mode .fcrm_view_cf_wrap .fcrm_custom_data_grid{grid-template-columns:1fr}.fcrm_company_custom_field_wrapper{width:100%;container:fcrm-company-custom-fields/inline-size}.fcrm_company_custom_field_wrapper .fcrm_custom_data_header{display:flex;align-items:center;justify-content:space-between;gap:16px;margin-bottom:16px}.fcrm_company_custom_field_wrapper .fcrm_custom_data_header h3{margin:0;color:var(--fc-primary-text);font-size:18px;font-weight:600;line-height:24px}.fcrm_company_custom_field_wrapper .fluentcrm_custom_fields.fcrm_custom_data_fields{padding:0;border:none;width:100%}.fcrm_company_custom_field_wrapper .fcrm_custom_data_form{display:flex;flex-direction:column;gap:18px;width:100%}.fcrm_company_custom_field_wrapper .fcrm_custom_data_group{background:var(--fc-weak-bg-25);border-radius:8px;padding:15px 20px 10px}.fcrm_company_custom_field_wrapper .fcrm_custom_data_group.fcrm_custom_data_group_plain{padding:0;border:none;border-radius:0;background:transparent}.fcrm_company_custom_field_wrapper .fcrm_custom_data_group:hover .fcrm_custom_data_group_head h4 .icon{opacity:1}.fcrm_company_custom_field_wrapper .fcrm_custom_data_group_head{display:flex;align-items:center;justify-content:space-between;gap:12px;margin-bottom:14px}.fcrm_company_custom_field_wrapper .fcrm_custom_data_group_head h4{margin:0;color:var(--fc-primary-text);font-size:15px;font-weight:600;line-height:20px;display:flex;align-items:center;gap:6px}.fcrm_company_custom_field_wrapper .fcrm_custom_data_group_head h4 .icon{display:block;width:auto;height:auto;opacity:0}.fcrm_company_custom_field_wrapper .fcrm_custom_data_group_head h4 .icon svg{display:block}.fcrm_company_custom_field_wrapper .fcrm_custom_data_group_head span{color:var(--fc-secondary-text);font-size:12px;line-height:16px;white-space:nowrap}.fcrm_company_custom_field_wrapper .fcrm_custom_data_grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:16px}.fcrm_company_custom_field_wrapper .fcrm_custom_data_item{margin-bottom:0}.fcrm_company_custom_field_wrapper .fcrm_custom_data_item .el-select,.fcrm_company_custom_field_wrapper .fcrm_custom_data_item .el-date-editor{width:100%}.fcrm_company_custom_field_wrapper .fcrm_custom_data_item .el-textarea__inner{min-height:72px}.fcrm_company_custom_field_wrapper .fcrm_custom_data_options{display:flex;flex-wrap:wrap;gap:8px 16px}.fcrm_company_custom_field_wrapper .fcrm_custom_data_options .el-radio,.fcrm_company_custom_field_wrapper .fcrm_custom_data_options .el-checkbox{margin-right:0}.fcrm_company_custom_field_wrapper .fcrm_custom_data_empty{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:16px;border:1px dashed var(--fc-primary-border);border-radius:8px;background:var(--fc-secondary-bg)}.fcrm_company_custom_field_wrapper .fcrm_custom_data_empty p{margin:0;color:var(--fc-secondary-text);font-size:14px;line-height:20px}@container fcrm-company-custom-fields (max-width: 560px){.fcrm_company_custom_field_wrapper .fcrm_custom_data_header,.fcrm_company_custom_field_wrapper .fcrm_custom_data_empty{align-items:flex-start;flex-direction:column}.fcrm_company_custom_field_wrapper .fcrm_custom_data_grid{grid-template-columns:1fr}}.fcrm_contact_adder_drawer .el-drawer__body{padding:0}.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content{height:100%}.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_tab_menu{display:flex;padding:14px 20px;align-items:flex-start;gap:24px;align-self:stretch;border-bottom:1px solid var(--fc-primary-border);background:var(--fc-primary-bg);position:relative}.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_tab_menu .fcrm_tab_item{display:flex;gap:4px;align-items:center;justify-content:center;padding:0;cursor:pointer;position:relative;flex-shrink:0}.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_tab_menu .fcrm_tab_item .fcrm_tab_text{font-weight:500;font-size:12px;line-height:16px;color:var(--fc-secondary-text);text-align:center}.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_tab_menu .fcrm_tab_item.fcrm_tab_active .fcrm_tab_text{color:var(--fc-primary-text)}.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_tab_menu .fcrm_tab_item.fcrm_tab_active .fcrm_tab_line{position:absolute;bottom:-15.5px;left:0;right:0;height:2px;background:var(--fc-primary-text)}.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_adder_content{height:100%;padding:0;overflow-y:auto;flex:1 0 0;min-height:0;display:flex;flex-direction:column}.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_adder_content>div{display:flex;flex-direction:column;flex:1 0 0;min-height:0}.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_adder_content .fcrm_assign_search{margin-bottom:0;border:1px solid var(--fc-primary-border);border-radius:8px;width:100%;overflow:hidden}.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_adder_content .fcrm_assign_search .el-input__wrapper{background:var(--fc-primary-bg);border:none;border-radius:0;box-shadow:none;padding:4px 10px;gap:8px}.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_adder_content .fcrm_assign_search .el-input__inner{font-size:14px;font-weight:400;line-height:20px;letter-spacing:-.084px;color:var(--fc-primary-text);padding:0}.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_adder_content .fcrm_assign_search .el-input__inner::placeholder{color:var(--fc-text-muted)}.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_adder_content .fcrm_assign_search .el-input__prefix{padding-right:0;margin-right:0}.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_adder_content .fcrm_assign_search .el-input__prefix .el-icon{width:20px;height:20px;color:var(--fc-secondary-text);font-size:18px;margin:0}.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_adder_content .fcrm_assign_existing{padding:20px;flex:1 0 0;overflow-y:auto;min-height:0}.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_adder_content .fcrm_assign_create_new{flex:1 0 0;overflow-y:auto;min-height:0;display:flex;flex-direction:column;width:100%;padding:20px}.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_adder_content .fcrm_assign_create_new .fcrm_contact_form_handler{display:flex;flex-direction:column;height:100%;min-height:0;background:var(--fc-primary-bg)}.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_adder_content .fcrm_assign_create_new .fcrm_contact_form_handler .fcrm_assign_add_form{flex:1 0 0;overflow-y:auto;min-height:0;padding:20px}.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_adder_content .fcrm_assign_create_new .fcrm_contact_form_handler .fcrm_assign_add_form .el-form-item{margin-bottom:16px}.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_adder_content .fcrm_assign_create_new .fcrm_contact_form_handler .fcrm_assign_add_form .el-form-item__label{font-size:14px;font-weight:500;line-height:20px;letter-spacing:-.084px;color:var(--fc-primary-text);padding-bottom:6px;margin-bottom:0}.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_adder_content .fcrm_assign_create_new .fcrm_contact_form_handler .fcrm_assign_add_form .el-input__wrapper,.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_adder_content .fcrm_assign_create_new .fcrm_contact_form_handler .fcrm_assign_add_form .el-select__wrapper,.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_adder_content .fcrm_assign_create_new .fcrm_contact_form_handler .fcrm_assign_add_form .el-date-editor .el-input__wrapper{height:38px;border-radius:8px;border:1px solid var(--fc-primary-border);background:var(--fc-primary-bg);padding:8px 10px;box-shadow:none;transition:border-color .2s ease,box-shadow .2s ease}.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_adder_content .fcrm_assign_create_new .fcrm_contact_form_handler .fcrm_assign_add_form .el-input__wrapper:hover,.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_adder_content .fcrm_assign_create_new .fcrm_contact_form_handler .fcrm_assign_add_form .el-select__wrapper:hover,.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_adder_content .fcrm_assign_create_new .fcrm_contact_form_handler .fcrm_assign_add_form .el-date-editor .el-input__wrapper:hover{border-color:var(--fc-secondary-border)}.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_adder_content .fcrm_assign_create_new .fcrm_contact_form_handler .fcrm_assign_add_form .el-input__wrapper.is-focus,.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_adder_content .fcrm_assign_create_new .fcrm_contact_form_handler .fcrm_assign_add_form .el-input__wrapper.is-focused,.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_adder_content .fcrm_assign_create_new .fcrm_contact_form_handler .fcrm_assign_add_form .el-select__wrapper.is-focus,.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_adder_content .fcrm_assign_create_new .fcrm_contact_form_handler .fcrm_assign_add_form .el-select__wrapper.is-focused,.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_adder_content .fcrm_assign_create_new .fcrm_contact_form_handler .fcrm_assign_add_form .el-date-editor .el-input__wrapper.is-focus,.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_adder_content .fcrm_assign_create_new .fcrm_contact_form_handler .fcrm_assign_add_form .el-date-editor .el-input__wrapper.is-focused{border-color:var(--fc-primary-text);box-shadow:0 0 0 2px #335cff1f}.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_adder_content .fcrm_assign_create_new .fcrm_contact_form_handler .fcrm_assign_add_form .el-date-editor{width:100%;padding:0}.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_adder_content .fcrm_assign_create_new .fcrm_contact_form_handler .fcrm_assign_add_form .el-date-editor .el-input__prefix .el-icon{color:var(--fc-text-muted);width:20px;height:20px;font-size:20px}.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_adder_content .fcrm_assign_create_new .fcrm_contact_form_handler .fcrm_assign_add_form .el-input__inner{font-size:14px;font-weight:400;line-height:20px;letter-spacing:-.084px;color:var(--fc-primary-text);padding:0}.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_adder_content .fcrm_assign_create_new .fcrm_contact_form_handler .fcrm_assign_add_form .el-input__inner::placeholder{color:var(--fc-text-muted)}.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_adder_content .fcrm_assign_create_new .fcrm_contact_form_handler .fcrm_assign_add_form .el-checkbox__inner{width:20px;height:20px;border-radius:6px;border:1px solid var(--fc-primary-border);position:relative;flex-shrink:0}.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_adder_content .fcrm_assign_create_new .fcrm_contact_form_handler .fcrm_assign_add_form .el-checkbox.is-checked .el-checkbox__inner{background-color:var(--fc-deep-bg);border-color:var(--fc-deep-bg)}.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_adder_content .fcrm_assign_create_new .fcrm_contact_form_handler .fcrm_assign_add_form .el-checkbox.is-checked .el-checkbox__inner:before{display:none}.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_adder_content .fcrm_assign_create_new .fcrm_contact_form_handler .fcrm_assign_add_form .el-checkbox__label{font-size:14px;font-weight:400;line-height:20px;letter-spacing:-.084px;color:var(--fc-primary-text);padding-left:8px}.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_adder_content .fcrm_assign_create_new .fcrm_contact_form_handler .fcrm_assign_add_form .fcrm_address_block{padding:0 0 0 28px;display:flex;flex-direction:column;gap:16px;width:100%;margin-bottom:16px}.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_adder_content .fcrm_assign_create_new .fcrm_contact_form_handler .fcrm_assign_add_form .fcrm_address_block .el-row{margin-left:0!important;margin-right:0!important;display:flex;flex-wrap:wrap;gap:16px;width:100%}.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_adder_content .fcrm_assign_create_new .fcrm_contact_form_handler .fcrm_assign_add_form .fcrm_address_block .el-row .el-col{padding-left:0!important;padding-right:0!important;display:flex;flex-direction:column}.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_adder_content .fcrm_assign_create_new .fcrm_contact_form_handler .fcrm_assign_add_form .fcrm_address_block .el-row .el-col:nth-child(1),.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_adder_content .fcrm_assign_create_new .fcrm_contact_form_handler .fcrm_assign_add_form .fcrm_address_block .el-row .el-col:nth-child(2){flex:0 0 100%;max-width:100%}.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_adder_content .fcrm_assign_create_new .fcrm_contact_form_handler .fcrm_assign_add_form .fcrm_address_block .el-row .el-col:nth-child(3),.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_adder_content .fcrm_assign_create_new .fcrm_contact_form_handler .fcrm_assign_add_form .fcrm_address_block .el-row .el-col:nth-child(4),.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_adder_content .fcrm_assign_create_new .fcrm_contact_form_handler .fcrm_assign_add_form .fcrm_address_block .el-row .el-col:nth-child(5),.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_adder_content .fcrm_assign_create_new .fcrm_contact_form_handler .fcrm_assign_add_form .fcrm_address_block .el-row .el-col:nth-child(6){flex:1 0 0;min-width:0;max-width:none}.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_adder_content .fcrm_assign_create_new .fcrm_contact_form_handler .fcrm_assign_add_form .fcrm_address_block .el-row .el-col.el-col-md-12{flex:1 0 0;min-width:0;max-width:none}.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_adder_content .fcrm_assign_create_new .fcrm_contact_form_handler .fcrm_assign_add_form .fcrm_address_block .el-row .el-col.el-col-24{width:100%;flex:0 0 100%;max-width:100%}.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_adder_content .fcrm_assign_create_new .fcrm_contact_form_handler .fcrm_assign_add_form .fcrm_address_block .el-form-item{margin-bottom:0;width:100%;display:flex;flex-direction:column;gap:4px}.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_adder_content .fcrm_assign_create_new .fcrm_contact_form_handler .fcrm_assign_add_form .fcrm_address_block .el-form-item__label{font-size:14px;font-weight:500;line-height:20px;letter-spacing:-.084px;color:var(--fc-primary-text);margin-bottom:0;padding-bottom:0}.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_adder_content .fcrm_assign_create_new .fcrm_contact_form_handler .fcrm_assign_add_form .fcrm_address_block .el-form-item__label span,.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_adder_content .fcrm_assign_create_new .fcrm_contact_form_handler .fcrm_assign_add_form .fcrm_address_block .el-form-item__label .el-form-item__label-text{font-weight:400;color:var(--fc-secondary-text)}.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_adder_content .fcrm_assign_create_new .fcrm_contact_form_handler .fcrm_assign_add_form .fcrm_address_block .el-form-item__content{width:100%}.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_adder_content .fcrm_assign_create_new .fcrm_contact_form_handler .fcrm_assign_add_form .fcrm_address_block .el-input__wrapper,.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_adder_content .fcrm_assign_create_new .fcrm_contact_form_handler .fcrm_assign_add_form .fcrm_address_block .el-select__wrapper,.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_adder_content .fcrm_assign_create_new .fcrm_contact_form_handler .fcrm_assign_add_form .fcrm_address_block .el-date-editor .el-input__wrapper{height:38px;border-radius:8px;border:1px solid var(--fc-primary-border);background:var(--fc-primary-bg);padding:8px 10px;box-shadow:none;transition:border-color .2s ease,box-shadow .2s ease}.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_adder_content .fcrm_assign_create_new .fcrm_contact_form_handler .fcrm_assign_add_form .fcrm_address_block .el-input__wrapper:hover,.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_adder_content .fcrm_assign_create_new .fcrm_contact_form_handler .fcrm_assign_add_form .fcrm_address_block .el-select__wrapper:hover,.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_adder_content .fcrm_assign_create_new .fcrm_contact_form_handler .fcrm_assign_add_form .fcrm_address_block .el-date-editor .el-input__wrapper:hover{border-color:var(--fc-secondary-border)}.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_adder_content .fcrm_assign_create_new .fcrm_contact_form_handler .fcrm_assign_add_form .fcrm_address_block .el-input__wrapper.is-focus,.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_adder_content .fcrm_assign_create_new .fcrm_contact_form_handler .fcrm_assign_add_form .fcrm_address_block .el-input__wrapper.is-focused,.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_adder_content .fcrm_assign_create_new .fcrm_contact_form_handler .fcrm_assign_add_form .fcrm_address_block .el-select__wrapper.is-focus,.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_adder_content .fcrm_assign_create_new .fcrm_contact_form_handler .fcrm_assign_add_form .fcrm_address_block .el-select__wrapper.is-focused,.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_adder_content .fcrm_assign_create_new .fcrm_contact_form_handler .fcrm_assign_add_form .fcrm_address_block .el-date-editor .el-input__wrapper.is-focus,.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_adder_content .fcrm_assign_create_new .fcrm_contact_form_handler .fcrm_assign_add_form .fcrm_address_block .el-date-editor .el-input__wrapper.is-focused{border-color:var(--fc-primary-text);box-shadow:0 0 0 2px #335cff1f}.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_adder_content .fcrm_assign_create_new .fcrm_contact_form_handler .fcrm_assign_add_form .fcrm_address_block .el-date-editor{width:100%}.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_adder_content .fcrm_assign_create_new .fcrm_contact_form_handler .fcrm_assign_add_form .fcrm_address_block .el-date-editor .el-input__prefix .el-icon{color:var(--fc-text-muted);width:20px;height:20px;font-size:20px}.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_adder_content .fcrm_assign_create_new .fcrm_contact_form_handler .fcrm_assign_add_form .fcrm_address_block .el-input__inner{font-size:14px;font-weight:400;line-height:20px;letter-spacing:-.084px;color:var(--fc-primary-text);padding:0}.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_adder_content .fcrm_assign_create_new .fcrm_contact_form_handler .fcrm_assign_add_form .fcrm_address_block .el-input__inner::placeholder{color:var(--fc-text-muted)}.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_adder_content .fcrm_assign_create_new .fcrm_contact_form_handler .fcrm_assign_add_form .fcrm_address_block .el-select__placeholder{color:var(--fc-text-muted);font-size:14px;font-weight:400;line-height:20px;letter-spacing:-.084px}.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_adder_content .fcrm_assign_create_new .fcrm_contact_form_handler .fcrm_assign_add_form .fcrm_address_block .el-select__caret{color:var(--fc-primary-text);width:20px;height:20px;font-size:20px}.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_adder_content .fcrm_assign_create_new .fcrm_contact_form_handler .fcrm_assign_add_form .fcrm_custom_field_wrapper{display:flex;flex-direction:column;gap:16px;width:100%;padding-left:28px}.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_adder_content .fcrm_assign_create_new .fcrm_contact_form_handler .fcrm_assign_add_form .fcrm_custom_field_wrapper .fcrm_custom_fields{display:flex;flex-direction:column;gap:16px;width:100%;margin-bottom:16px}.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_adder_content .fcrm_assign_create_new .fcrm_contact_form_handler .fcrm_assign_add_form .fcrm_custom_field_wrapper .fcrm_custom_fields .fcrm_custom_fields_header{display:flex;align-items:center;justify-content:space-between;padding:0;margin-bottom:0}.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_adder_content .fcrm_assign_create_new .fcrm_contact_form_handler .fcrm_assign_add_form .fcrm_custom_field_wrapper .fcrm_custom_fields .fcrm_custom_fields_header .fcrm_custom_fields_header_label h3{font-size:14px;font-weight:500;line-height:20px;letter-spacing:-.084px;color:var(--fc-primary-text);margin:0}.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_adder_content .fcrm_assign_create_new .fcrm_contact_form_handler .fcrm_assign_add_form .fcrm_custom_field_wrapper .fcrm_custom_fields .fcrm_custom_fields_form{padding:0;display:flex;flex-direction:column;gap:16px;margin-bottom:0}.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_adder_content .fcrm_assign_create_new .fcrm_contact_form_handler .fcrm_assign_add_form .fcrm_custom_field_wrapper .fcrm_custom_fields .fcrm_custom_fields_form .fcrm_custom_fields_layout{display:flex;flex-wrap:wrap;gap:16px;align-items:flex-start}.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_adder_content .fcrm_assign_create_new .fcrm_contact_form_handler .fcrm_assign_add_form .fcrm_custom_field_wrapper .fcrm_custom_fields .fcrm_custom_fields_form .fcrm_custom_fields_half{flex:0 0 calc(50% - 8px);min-width:278px}.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_adder_content .fcrm_assign_create_new .fcrm_contact_form_handler .fcrm_assign_add_form .fcrm_custom_field_wrapper .fcrm_custom_fields .fcrm_custom_fields_form .fcrm_custom_fields_half .el-form-item__label{font-size:14px;font-weight:500;line-height:20px;letter-spacing:-.084px;color:var(--fc-primary-text);margin-bottom:4px;padding-bottom:0}.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_adder_content .fcrm_assign_create_new .fcrm_contact_form_handler .fcrm_assign_add_form .fcrm_custom_field_wrapper .fcrm_custom_fields .fcrm_custom_fields_form .fcrm_custom_fields_half .el-input__wrapper,.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_adder_content .fcrm_assign_create_new .fcrm_contact_form_handler .fcrm_assign_add_form .fcrm_custom_field_wrapper .fcrm_custom_fields .fcrm_custom_fields_form .fcrm_custom_fields_half .el-select__wrapper,.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_adder_content .fcrm_assign_create_new .fcrm_contact_form_handler .fcrm_assign_add_form .fcrm_custom_field_wrapper .fcrm_custom_fields .fcrm_custom_fields_form .fcrm_custom_fields_half .el-date-editor .el-input__wrapper{height:38px;border-radius:8px;border:1px solid var(--fc-primary-border);background-color:var(--fc-primary-bg);padding:8px 10px;box-shadow:none;transition:border-color .2s ease,box-shadow .2s ease}.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_adder_content .fcrm_assign_create_new .fcrm_contact_form_handler .fcrm_assign_add_form .fcrm_custom_field_wrapper .fcrm_custom_fields .fcrm_custom_fields_form .fcrm_custom_fields_half .el-input__wrapper:hover,.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_adder_content .fcrm_assign_create_new .fcrm_contact_form_handler .fcrm_assign_add_form .fcrm_custom_field_wrapper .fcrm_custom_fields .fcrm_custom_fields_form .fcrm_custom_fields_half .el-select__wrapper:hover,.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_adder_content .fcrm_assign_create_new .fcrm_contact_form_handler .fcrm_assign_add_form .fcrm_custom_field_wrapper .fcrm_custom_fields .fcrm_custom_fields_form .fcrm_custom_fields_half .el-date-editor .el-input__wrapper:hover{border-color:var(--fc-secondary-border)}.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_adder_content .fcrm_assign_create_new .fcrm_contact_form_handler .fcrm_assign_add_form .fcrm_custom_field_wrapper .fcrm_custom_fields .fcrm_custom_fields_form .fcrm_custom_fields_half .el-input__wrapper.is-focus,.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_adder_content .fcrm_assign_create_new .fcrm_contact_form_handler .fcrm_assign_add_form .fcrm_custom_field_wrapper .fcrm_custom_fields .fcrm_custom_fields_form .fcrm_custom_fields_half .el-input__wrapper.is-focused,.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_adder_content .fcrm_assign_create_new .fcrm_contact_form_handler .fcrm_assign_add_form .fcrm_custom_field_wrapper .fcrm_custom_fields .fcrm_custom_fields_form .fcrm_custom_fields_half .el-select__wrapper.is-focus,.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_adder_content .fcrm_assign_create_new .fcrm_contact_form_handler .fcrm_assign_add_form .fcrm_custom_field_wrapper .fcrm_custom_fields .fcrm_custom_fields_form .fcrm_custom_fields_half .el-select__wrapper.is-focused,.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_adder_content .fcrm_assign_create_new .fcrm_contact_form_handler .fcrm_assign_add_form .fcrm_custom_field_wrapper .fcrm_custom_fields .fcrm_custom_fields_form .fcrm_custom_fields_half .el-date-editor .el-input__wrapper.is-focus,.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_adder_content .fcrm_assign_create_new .fcrm_contact_form_handler .fcrm_assign_add_form .fcrm_custom_field_wrapper .fcrm_custom_fields .fcrm_custom_fields_form .fcrm_custom_fields_half .el-date-editor .el-input__wrapper.is-focused{border-color:var(--fc-primary-text);box-shadow:0 0 0 2px #335cff1f}.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_adder_content .fcrm_assign_create_new .fcrm_contact_form_handler .fcrm_assign_add_form .fcrm_custom_field_wrapper .fcrm_custom_fields .fcrm_custom_fields_form .fcrm_custom_fields_half .el-date-editor .el-input__prefix .el-icon{color:var(--fc-text-muted);width:20px;height:20px;font-size:20px}.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_adder_content .fcrm_assign_create_new .fcrm_contact_form_handler .fcrm_assign_add_form .fcrm_custom_field_wrapper .fcrm_custom_fields .fcrm_custom_fields_form .fcrm_custom_fields_half .el-textarea__inner{min-height:38px;padding:8px 10px;border-radius:8px;border:1px solid var(--fc-primary-border);background-color:var(--fc-primary-bg);box-shadow:none;font-size:14px;font-weight:400;line-height:20px;letter-spacing:-.084px;color:var(--fc-primary-text);transition:border-color .2s ease,box-shadow .2s ease;resize:none}.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_adder_content .fcrm_assign_create_new .fcrm_contact_form_handler .fcrm_assign_add_form .fcrm_custom_field_wrapper .fcrm_custom_fields .fcrm_custom_fields_form .fcrm_custom_fields_half .el-textarea__inner::placeholder{color:var(--fc-text-muted)}.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_adder_content .fcrm_assign_create_new .fcrm_contact_form_handler .fcrm_assign_add_form .fcrm_custom_field_wrapper .fcrm_custom_fields .fcrm_custom_fields_form .fcrm_custom_fields_half .el-textarea__inner:hover{border-color:var(--fc-secondary-border)}.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_adder_content .fcrm_assign_create_new .fcrm_contact_form_handler .fcrm_assign_add_form .fcrm_custom_field_wrapper .fcrm_custom_fields .fcrm_custom_fields_form .fcrm_custom_fields_half .el-textarea__inner:focus{border-color:var(--fc-text-link);box-shadow:0 0 0 2px #335cff1f;outline:none}.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_adder_content .fcrm_assign_create_new .fcrm_contact_form_handler .fcrm_assign_add_form .fcrm_custom_field_wrapper .fcrm_custom_fields .fcrm_custom_fields_form .fcrm_custom_fields_half .el-input__inner{font-size:14px;font-weight:400;line-height:20px;letter-spacing:-.084px;color:var(--fc-primary-text);padding:0}.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_adder_content .fcrm_assign_create_new .fcrm_contact_form_handler .fcrm_assign_add_form .fcrm_custom_field_wrapper .fcrm_custom_fields .fcrm_custom_fields_form .fcrm_custom_fields_half .el-input__inner::placeholder{color:var(--fc-text-muted)}.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_adder_content .fcrm_assign_create_new .fcrm_contact_form_handler .fcrm_assign_add_form .fcrm_custom_field_wrapper .fcrm_custom_fields .fcrm_custom_fields_form .fcrm_custom_fields_half .el-radio-group{display:flex;flex-wrap:wrap;gap:16px}.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_adder_content .fcrm_assign_create_new .fcrm_contact_form_handler .fcrm_assign_add_form .fcrm_custom_field_wrapper .fcrm_custom_fields .fcrm_custom_fields_form .fcrm_custom_fields_half .el-radio-group .el-radio{margin-right:0}.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_adder_content .fcrm_assign_create_new .fcrm_contact_form_handler .fcrm_assign_add_form .fcrm_custom_field_wrapper .fcrm_custom_fields .fcrm_custom_fields_form .fcrm_custom_fields_half .el-radio-group .el-radio .el-radio__inner{width:16px;height:16px;border-radius:50%;border:2px solid var(--fc-primary-border);background-color:var(--fc-primary-bg)}.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_adder_content .fcrm_assign_create_new .fcrm_contact_form_handler .fcrm_assign_add_form .fcrm_custom_field_wrapper .fcrm_custom_fields .fcrm_custom_fields_form .fcrm_custom_fields_half .el-radio-group .el-radio.is-checked .el-radio__inner{border-color:var(--fc-deep-bg);background-color:var(--fc-deep-bg)}.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_adder_content .fcrm_assign_create_new .fcrm_contact_form_handler .fcrm_assign_add_form .fcrm_custom_field_wrapper .fcrm_custom_fields .fcrm_custom_fields_form .fcrm_custom_fields_half .el-radio-group .el-radio.is-checked .el-radio__inner:after{width:6px;height:6px;background-color:var(--fc-primary-bg)}.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_adder_content .fcrm_assign_create_new .fcrm_contact_form_handler .fcrm_assign_add_form .fcrm_custom_field_wrapper .fcrm_custom_fields .fcrm_custom_fields_form .fcrm_custom_fields_half .el-radio-group .el-radio .el-radio__label{font-size:14px;font-weight:400;line-height:20px;letter-spacing:-.084px;color:var(--fc-primary-text);padding-left:8px}.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_adder_content .fcrm_assign_create_new .fcrm_contact_form_handler .fcrm_assign_add_form .fcrm_custom_field_wrapper .fcrm_custom_fields .fcrm_custom_fields_form .fcrm_custom_fields_half .fcrm_checkbox_group{display:flex;flex-wrap:wrap;gap:16px}.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_adder_content .fcrm_assign_create_new .fcrm_contact_form_handler .fcrm_assign_add_form .fcrm_custom_field_wrapper .fcrm_custom_fields .fcrm_custom_fields_form .fcrm_custom_fields_half .fcrm_checkbox_group .fcrm_checkbox{margin-right:0}.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_adder_content .fcrm_assign_create_new .fcrm_contact_form_handler .fcrm_assign_add_form .fcrm_custom_field_wrapper .fcrm_custom_fields .fcrm_custom_fields_form .fcrm_custom_fields_half .fcrm_checkbox_group .fcrm_checkbox .el-checkbox__inner{width:16px;height:16px;border-radius:4px;border:none;background-color:var(--fc-primary-border);position:relative}.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_adder_content .fcrm_assign_create_new .fcrm_contact_form_handler .fcrm_assign_add_form .fcrm_custom_field_wrapper .fcrm_custom_fields .fcrm_custom_fields_form .fcrm_custom_fields_half .fcrm_checkbox_group .fcrm_checkbox .el-checkbox__inner:before{content:"";position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);width:13px;height:13px;background-color:var(--fc-primary-bg);border-radius:2.6px}.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_adder_content .fcrm_assign_create_new .fcrm_contact_form_handler .fcrm_assign_add_form .fcrm_custom_field_wrapper .fcrm_custom_fields .fcrm_custom_fields_form .fcrm_custom_fields_half .fcrm_checkbox_group .fcrm_checkbox.is-checked .el-checkbox__inner{background-color:var(--fc-deep-bg);border-color:var(--fc-deep-bg)}.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_adder_content .fcrm_assign_create_new .fcrm_contact_form_handler .fcrm_assign_add_form .fcrm_custom_field_wrapper .fcrm_custom_fields .fcrm_custom_fields_form .fcrm_custom_fields_half .fcrm_checkbox_group .fcrm_checkbox.is-checked .el-checkbox__inner:before{display:none}.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_adder_content .fcrm_assign_create_new .fcrm_contact_form_handler .fcrm_assign_add_form .fcrm_custom_field_wrapper .fcrm_custom_fields .fcrm_custom_fields_form .fcrm_custom_fields_half .fcrm_checkbox_group .fcrm_checkbox .el-checkbox__label{font-size:14px;font-weight:400;line-height:20px;letter-spacing:-.084px;color:var(--fc-primary-text);padding-left:8px}.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_adder_content .fcrm_assign_create_new .fcrm_contact_form_handler .fcrm_assign_add_form .fcrm_custom_field_wrapper .fcrm_custom_fields .fcrm_custom_fields_form .fcrm_custom_fields_half .el-date-editor{width:100%}.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_adder_content .fcrm_assign_create_new .fcrm_contact_form_handler .fcrm_assign_add_form .fcrm_custom_field_wrapper .fcrm_custom_fields .fcrm_custom_fields_form .fcrm_custom_fields_half .el-date-editor .el-input__wrapper{height:38px}.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_adder_content .fcrm_assign_create_new .fcrm_contact_form_handler .fcrm_identifier_section{width:100%;background:var(--fc-primary-bg);border-radius:0;margin-top:0;margin-bottom:0}.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_adder_content .fcrm_assign_create_new .fcrm_contact_form_handler .fcrm_identifier_section .fcrm_section_heading{margin:0;padding:8px 20px;background-color:var(--fc-secondary-bg);font-size:12px;font-weight:500;line-height:16px;letter-spacing:.48px;text-transform:uppercase;color:var(--fc-text-muted);width:100%;border:none}.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_adder_content .fcrm_assign_create_new .fcrm_contact_form_handler .fcrm_identifier_section .fcrm_identifier_content{padding:20px;display:flex;flex-direction:column;gap:20px;background:var(--fc-primary-bg)}.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_adder_content .fcrm_assign_create_new .fcrm_contact_form_handler .fcrm_identifier_section .fcrm_identifier_row{display:flex;gap:20px;align-items:flex-start;width:100%}@media (max-width: 600px){.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_adder_content .fcrm_assign_create_new .fcrm_contact_form_handler .fcrm_identifier_section .fcrm_identifier_row{flex-direction:column}}.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_adder_content .fcrm_assign_create_new .fcrm_contact_form_handler .fcrm_identifier_section .fcrm_identifier_field{flex:1 0 0;min-width:0}@media (max-width: 600px){.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_adder_content .fcrm_assign_create_new .fcrm_contact_form_handler .fcrm_identifier_section .fcrm_identifier_field{flex:1;width:100%}}.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_adder_content .fcrm_assign_create_new .fcrm_contact_form_handler .fcrm_identifier_section .fcrm_identifier_field .el-form-item{margin-bottom:0}.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_adder_content .fcrm_assign_create_new .fcrm_contact_form_handler .fcrm_identifier_section .fcrm_identifier_field .el-form-item__label{font-size:14px;font-weight:500;line-height:20px;letter-spacing:-.084px;color:var(--fc-primary-text);margin-bottom:4px;padding-bottom:0}.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_adder_content .fcrm_assign_create_new .fcrm_contact_form_handler .fcrm_identifier_section .fcrm_identifier_field_full{width:100%}.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_adder_content .fcrm_assign_create_new .fcrm_contact_form_handler .fcrm_identifier_section .fcrm_identifier_field_full .el-form-item{margin-bottom:0}.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_adder_content .fcrm_assign_create_new .fcrm_contact_form_handler .fcrm_assign_footer{flex-shrink:0;border-top:1px solid var(--fc-primary-border);border-bottom:none;border-left:none;border-right:none;padding:20px;background:var(--fc-primary-bg);display:flex;align-items:center;gap:16px}.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_adder_content .fcrm_assign_create_new .fcrm_contact_form_handler .fcrm_assign_footer .fcrm_assign_foot_link{flex-shrink:0}.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_adder_content .fcrm_assign_create_new .fcrm_contact_form_handler .fcrm_assign_footer .fcrm_assign_foot_link .fcrm_assign_foot_link_text{font-size:14px;font-weight:500;line-height:20px;letter-spacing:-.084px;color:var(--fc-secondary-text);text-decoration:underline;text-underline-position:from-font;text-decoration-skip-ink:none;cursor:pointer;margin:0;transition:color .2s ease}.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_adder_content .fcrm_assign_create_new .fcrm_contact_form_handler .fcrm_assign_footer .fcrm_assign_foot_link .fcrm_assign_foot_link_text:hover{color:var(--fc-primary-text)}.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_adder_content .fcrm_assign_create_new .fcrm_contact_form_handler .fcrm_assign_footer .fcrm_assign_foot_actions{display:flex;gap:12px;align-items:center;justify-content:flex-end;flex:1 0 0;min-width:0}.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_adder_content .fcrm_assign_create_new .fcrm_contact_form_handler .fcrm_assign_footer .fcrm_assign_foot_actions .el-button{border-radius:10px;padding:10px;height:auto;min-height:auto;font-size:14px;font-weight:500;line-height:20px;letter-spacing:-.084px;transition:background-color .2s ease,border-color .2s ease,color .2s ease;display:flex;align-items:center;justify-content:center;white-space:nowrap}.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_adder_content .fcrm_assign_create_new .fcrm_contact_form_handler .fcrm_assign_footer .fcrm_assign_foot_actions .el-button:not(.el-button--primary){background:var(--fc-primary-bg);border:1px solid var(--fc-primary-border);color:var(--fc-secondary-text);padding-left:14px;padding-right:14px}.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_adder_content .fcrm_assign_create_new .fcrm_contact_form_handler .fcrm_assign_footer .fcrm_assign_foot_actions .el-button:not(.el-button--primary):hover{background:var(--fc-secondary-bg);border-color:var(--fc-secondary-border)}.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_adder_content .fcrm_assign_create_new .fcrm_contact_form_handler .fcrm_assign_footer .fcrm_assign_foot_actions .el-button:not(.el-button--primary):focus-visible{outline:none;border-color:var(--fc-text-link);box-shadow:0 0 0 2px #335cff1f}.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_adder_content .fcrm_assign_create_new .fcrm_contact_form_handler .fcrm_assign_footer .fcrm_assign_foot_actions .el-button.el-button--primary{background:var(--fc-deep-bg);border-color:var(--fc-deep-bg);color:var(--fc-text-inverse);padding-left:14px;padding-right:14px}.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_adder_content .fcrm_assign_create_new .fcrm_contact_form_handler .fcrm_assign_footer .fcrm_assign_foot_actions .el-button.el-button--primary:hover:not(:disabled){background:var(--fc-primary-text);border-color:var(--fc-primary-text)}.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_adder_content .fcrm_assign_create_new .fcrm_contact_form_handler .fcrm_assign_footer .fcrm_assign_foot_actions .el-button.el-button--primary:focus-visible{outline:none;border-color:var(--fc-text-link);box-shadow:0 0 0 2px #335cff1f}.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_adder_content .fcrm_assign_create_new .fcrm_contact_form_handler .fcrm_assign_footer .fcrm_assign_foot_actions .el-button.el-button--primary:disabled{opacity:.5;cursor:not-allowed}.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_adder_content .fcrm_assign_list{display:flex;flex-direction:column;flex:1 0 0;min-height:0;width:100%;margin-top:20px}.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_adder_content .fcrm_assign_list .fcrm_assign_chk_grp{display:flex;flex-direction:column;width:100%;border:1px solid var(--fc-primary-border);border-radius:var(--fcrm-border-radius-8, 8px);overflow:hidden;background:var(--fc-primary-bg)}.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_adder_content .fcrm_assign_list .fcrm_assign_chk_grp .fcrm_assign_item{display:flex;align-items:center;width:100%;height:64px;padding:12px 20px 12px 12px;border-bottom:1px solid var(--fc-primary-border);background:var(--fc-primary-bg);margin:0;transition:background-color .2s ease}.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_adder_content .fcrm_assign_list .fcrm_assign_chk_grp .fcrm_assign_item:last-child{border-bottom:none}.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_adder_content .fcrm_assign_list .fcrm_assign_chk_grp .fcrm_assign_item:hover{background:var(--fc-secondary-bg)}.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_adder_content .fcrm_assign_list .fcrm_assign_chk_grp .fcrm_assign_item .el-checkbox__input{width:20px;height:20px;flex-shrink:0;margin-right:12px}.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_adder_content .fcrm_assign_list .fcrm_assign_chk_grp .fcrm_assign_item .el-checkbox__input .el-checkbox__inner{width:16px;height:16px;border-radius:4px;border:none;background-color:var(--fc-primary-border);position:relative}.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_adder_content .fcrm_assign_list .fcrm_assign_chk_grp .fcrm_assign_item .el-checkbox__input .el-checkbox__inner:before{content:"";position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);width:13px;height:13px;background-color:var(--fc-primary-bg);border-radius:2.6px}.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_adder_content .fcrm_assign_list .fcrm_assign_chk_grp .fcrm_assign_item .el-checkbox__input.is-checked .el-checkbox__inner{background-color:var(--fc-deep-bg)}.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_adder_content .fcrm_assign_list .fcrm_assign_chk_grp .fcrm_assign_item .el-checkbox__input.is-checked .el-checkbox__inner:before{display:none}.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_adder_content .fcrm_assign_list .fcrm_assign_chk_grp .fcrm_assign_item .el-checkbox__input:hover .el-checkbox__inner{background-color:var(--fc-secondary-border)}.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_adder_content .fcrm_assign_list .fcrm_assign_chk_grp .fcrm_assign_item .el-checkbox__input:hover .el-checkbox__inner:before{background-color:var(--fc-primary-bg)}.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_adder_content .fcrm_assign_list .fcrm_assign_chk_grp .fcrm_assign_item .el-checkbox__input.is-checked:hover .el-checkbox__inner{background-color:var(--fc-primary-text)}.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_adder_content .fcrm_assign_list .fcrm_assign_chk_grp .fcrm_assign_item .el-checkbox__label{padding-left:0;width:100%;display:flex;align-items:center}.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_adder_content .fcrm_assign_list .fcrm_assign_chk_grp .fcrm_assign_item .fcrm_assign_item_body{display:flex;gap:12px;align-items:center;flex:1 0 0;min-width:0}.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_adder_content .fcrm_assign_list .fcrm_assign_chk_grp .fcrm_assign_item .fcrm_assign_avatar{width:40px;height:40px;flex-shrink:0;border-radius:999px;overflow:hidden;background:var(--fc-primary-border);display:flex;align-items:center;justify-content:center}.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_adder_content .fcrm_assign_list .fcrm_assign_chk_grp .fcrm_assign_item .fcrm_assign_avatar img{width:100%;height:100%;object-fit:cover;object-position:center}.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_adder_content .fcrm_assign_list .fcrm_assign_chk_grp .fcrm_assign_item .fcrm_assign_avatar .fcrm_assign_avatar_ph{width:100%;height:100%;display:flex;align-items:center;justify-content:center;font-size:16px;font-weight:500;color:var(--fc-secondary-text);background:var(--fc-warning-bg)}.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_adder_content .fcrm_assign_list .fcrm_assign_chk_grp .fcrm_assign_item .fcrm_assign_info{display:flex;flex-direction:column;gap:2px;flex:1 0 0;min-width:0}.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_adder_content .fcrm_assign_list .fcrm_assign_chk_grp .fcrm_assign_item .fcrm_assign_name{font-size:14px;font-weight:500;line-height:20px;letter-spacing:-.084px;color:var(--fc-primary-text);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_adder_content .fcrm_assign_list .fcrm_assign_chk_grp .fcrm_assign_item .fcrm_assign_email{font-size:12px;font-weight:400;line-height:16px;color:var(--fc-secondary-text);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_adder_content .fcrm_assign_list .fcrm_assign_actions{padding:20px;flex-shrink:0;border-top:1px solid var(--fc-primary-border);background:var(--fc-primary-bg);margin-top:20px;margin-left:-20px;margin-right:-20px}.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_adder_content .fcrm_assign_list .fcrm_assign_actions .fcrm_assign_attach_btn{background:var(--fc-deep-bg);border-color:var(--fc-deep-bg);color:var(--fc-text-inverse);border-radius:8px;padding:6px 12px;font-size:14px;font-weight:500;line-height:20px;letter-spacing:-.084px;transition:background-color .2s ease,border-color .2s ease}.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_adder_content .fcrm_assign_list .fcrm_assign_actions .fcrm_assign_attach_btn:hover:not(:disabled){background:var(--fc-primary-text);border-color:var(--fc-primary-text)}.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_adder_content .fcrm_assign_list .fcrm_assign_actions .fcrm_assign_attach_btn:focus-visible{outline:none;border-color:var(--fc-text-link);box-shadow:0 0 0 2px #335cff1f}.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_adder_content .fcrm_assign_list .fcrm_assign_actions .fcrm_assign_attach_btn:disabled{opacity:.5;cursor:not-allowed}.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_adder_content .fcrm_assign_empty{padding:16px 20px;display:flex;flex-direction:column;align-items:center;justify-content:center;flex:1 0 0;min-height:0;width:100%}.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_adder_content .fcrm_assign_empty .fcrm_assign_empty_body{display:flex;flex-direction:column;gap:16px;align-items:center;width:100%}.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_adder_content .fcrm_assign_empty .fcrm_assign_empty_body .fcrm_illust{width:96px;height:87px;flex-shrink:0;position:relative;display:block;overflow:visible}.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_adder_content .fcrm_assign_empty .fcrm_assign_empty_body .fcrm_illust svg{position:absolute;display:block}.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_adder_content .fcrm_assign_empty .fcrm_assign_empty_body .fcrm_illust .fcrm_illust_dots{bottom:0;left:6.92%;right:5.77%;top:10.14%;width:auto;height:auto;z-index:1}.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_adder_content .fcrm_assign_empty .fcrm_assign_empty_body .fcrm_illust .fcrm_illust_cloud{bottom:12.16%;left:0;right:0;top:3.42%;width:140px;height:106px;transform:translate(-22px);z-index:2}.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_adder_content .fcrm_assign_empty .fcrm_assign_empty_body .fcrm_illust .fcrm_illust_x_sm{top:49.83%;right:54.62%;bottom:42.57%;left:38.46%;width:7px;height:7px;z-index:3}.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_adder_content .fcrm_assign_empty .fcrm_assign_empty_body .fcrm_illust .fcrm_illust_x_md{top:49.83%;right:38.46%;bottom:42.57%;left:53.85%;width:8px;height:7px;z-index:3}.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_adder_content .fcrm_assign_empty .fcrm_assign_empty_body .fcrm_illust .fcrm_illust_line_short{top:16.51%;right:75.9%;bottom:73.76%;left:15.38%;width:7.752px;height:8.614px;transform:rotate(196.067deg);z-index:3}.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_adder_content .fcrm_assign_empty .fcrm_assign_empty_body .fcrm_illust .fcrm_illust_rec{width:16px;height:2.5px;background:var(--fc-text-muted);position:absolute;top:63%;left:42%;z-index:3;border-radius:2px}.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_adder_content .fcrm_assign_empty .fcrm_assign_empty_body .fcrm_illust .fcrm_illust_line_long{top:4.31%;right:71.68%;bottom:82.55%;left:19.59%;width:10.844px;height:15.188px;transform:rotate(358.043deg);z-index:3}.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_adder_content .fcrm_assign_empty .fcrm_assign_empty_body .fcrm_illust .fcrm_illust_line_vert{top:0;left:29.64%;width:9.844px;height:15.188px;transform:rotate(8.015deg);z-index:3}.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_adder_content .fcrm_assign_empty .fcrm_assign_empty_body .fcrm_illust .fcrm_illust_line_sm{top:65.03%;right:40.77%;bottom:32.43%;left:52.54%;width:3px;height:6px;z-index:3}.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_adder_content .fcrm_assign_empty .fcrm_assign_empty_body .fcrm_illust .fcrm_illust_excl_circle{top:8.45%;right:6.92%;bottom:57.77%;left:61.54%;width:31px;height:30px;z-index:4}.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_adder_content .fcrm_assign_empty .fcrm_assign_empty_body .fcrm_illust .fcrm_illust_excl_mark{top:14.36%;right:20.77%;bottom:72.97%;left:75.38%;width:6px;height:14px;z-index:5}.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_adder_content .fcrm_assign_empty .fcrm_assign_empty_body .fcrm_illust .fcrm_illust_excl_dot{top:31.25%;right:20.77%;bottom:64.53%;left:75.38%;width:6px;height:6px;z-index:5}.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_adder_content .fcrm_assign_empty .fcrm_assign_empty_body .fcrm_assign_empty_text{font-size:12px;font-weight:400;line-height:16px;color:var(--fc-text-muted);text-align:center;margin:0;white-space:pre-wrap}.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_adder_content .fcrm_assign_empty .fcrm_assign_empty_body .fcrm_assign_create_btn{background:var(--fc-deep-bg);border-color:var(--fc-deep-bg);color:var(--fc-text-inverse);border-radius:8px;padding:4px 6px;display:inline-flex;align-items:center;justify-content:center;gap:2px;flex-shrink:0;transition:background-color .2s ease,border-color .2s ease}.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_adder_content .fcrm_assign_empty .fcrm_assign_empty_body .fcrm_assign_create_btn:hover:not(:disabled){background:var(--fc-primary-text);border-color:var(--fc-primary-text)}.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_adder_content .fcrm_assign_empty .fcrm_assign_empty_body .fcrm_assign_create_btn:focus-visible{outline:none;border-color:var(--fc-text-link);box-shadow:0 0 0 2px #335cff1f}.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_adder_content .fcrm_assign_empty .fcrm_assign_empty_body .fcrm_assign_create_btn .el-icon{width:20px;height:20px;color:var(--fc-text-inverse)}.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_adder_content .fcrm_assign_empty .fcrm_assign_empty_body .fcrm_assign_create_btn span{font-size:14px;font-weight:500;line-height:20px;letter-spacing:-.084px;color:var(--fc-text-inverse);padding:0 4px;white-space:nowrap}.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_add_contact_footer{background-color:var(--fc-primary-bg);padding-bottom:20px;display:flex;justify-content:space-between;align-items:center;width:100%;flex-wrap:wrap;gap:10px}.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_add_contact_footer .fcrm_add_contact_footer_actions{display:flex;gap:12px;justify-content:flex-end;min-width:0;flex-wrap:wrap}.fcrm_contact_adder_drawer .el-drawer__body .fcrm_drawer_content .fcrm_add_contact_footer .fcrm_add_contact_footer_actions .el-button{margin:0}.fcrm_act_wrap{padding:20px;background:var(--fc-primary-bg);width:100%}.fcrm_act_wrap .fcrm_act_card{border:1px solid var(--fc-primary-border);border-radius:var(--fcrm-border-radius-8, 8px);background:var(--fc-primary-bg);overflow:hidden}.fcrm_act_wrap .fcrm_act_head{background:var(--fc-primary-bg);border-bottom:1px solid var(--fc-primary-border);padding:12px 20px}.fcrm_act_wrap .fcrm_act_head_inner{display:flex;gap:16px;align-items:center;flex:1 0 0}.fcrm_act_wrap .fcrm_act_title_wrap{flex:1 0 0;display:flex;gap:4px;align-items:center}.fcrm_act_wrap .fcrm_act_title{font-size:16px;font-weight:500;line-height:24px;letter-spacing:-.176px;color:var(--fc-primary-text);margin:0}.fcrm_act_wrap .fcrm_act_actions{display:flex;gap:12px;align-items:center;flex-shrink:0}.fcrm_act_wrap .fcrm_act_actions .el-button{margin:0}.fcrm_act_wrap .fcrm_act_avatar{flex-shrink:0;width:40px;height:40px}.fcrm_act_wrap .fcrm_act_avatar img{width:40px;height:40px;border-radius:100vh;object-fit:cover}.fcrm_act_wrap .fcrm_act_avatar .fcrm_act_avatar_init{width:40px;height:40px;border-radius:100vh;background:var(--fc-primary-border);color:var(--fc-secondary-text);display:flex;align-items:center;justify-content:center;font-size:14px;font-weight:500;line-height:1}.fcrm_act_wrap .fcrm_act_chevron{flex-shrink:0;transition:transform .2s ease;color:var(--fc-text-muted)}.fcrm_act_wrap .fcrm_act_empty{padding:40px 20px;text-align:center}.fcrm_act_wrap .fcrm_pg{background:var(--fc-primary-bg);display:flex;align-items:center;justify-content:space-between;padding:12px 20px;border-top:1px solid var(--fc-primary-border)}.fcrm_act_wrap .fcrm_pg_left{display:flex;gap:10px;align-items:center;flex-shrink:0;flex-grow:0;min-width:0}.fcrm_act_wrap .fcrm_pg_text{font-size:14px;font-weight:400;line-height:20px;letter-spacing:-.084px;color:var(--fc-secondary-text);margin:0;padding:6px 0;white-space:nowrap;flex-shrink:0}.fcrm_act_wrap .fcrm_pg_per_page{flex-shrink:0}.fcrm_act_wrap .fcrm_pg_per_page .el-select__wrapper{background:var(--fc-secondary-bg);border:1px solid var(--fc-primary-border);border-radius:8px;padding:4px 6px;box-shadow:none;min-height:auto;height:auto}.fcrm_act_wrap .fcrm_pg_per_page .el-select__wrapper:hover{border-color:var(--fc-primary-border)}.fcrm_act_wrap .fcrm_pg_per_page .el-select__wrapper.is-focus,.fcrm_act_wrap .fcrm_pg_per_page .el-select__wrapper.is-focused{border-color:var(--fc-primary-border);box-shadow:none}.fcrm_act_wrap .fcrm_pg_per_page .el-select__placeholder{font-size:14px;font-weight:500;line-height:20px;letter-spacing:-.084px;color:var(--fc-secondary-text)}.fcrm_act_wrap .fcrm_pg_per_page .el-select__selected-item{font-size:14px;font-weight:500;line-height:20px;color:var(--fc-secondary-text)}.fcrm_act_wrap .fcrm_pg_per_page .el-select__caret{color:var(--fc-primary-text);font-size:20px;width:20px;height:20px}.fcrm_act_wrap .fcrm_pg_right{display:flex;gap:8px;align-items:center;justify-content:center;flex-shrink:0}.fcrm_act_wrap .fcrm_pg_btn{background:transparent;border:none;border-radius:8px;padding:6px;display:inline-flex;align-items:center;justify-content:center;min-width:32px;width:32px;height:32px;box-shadow:none;transition:background-color .2s ease}.fcrm_act_wrap .fcrm_pg_btn:hover:not(:disabled){background:var(--fc-secondary-bg)}.fcrm_act_wrap .fcrm_pg_btn:active:not(:disabled){background:var(--fc-secondary-bg)}.fcrm_act_wrap .fcrm_pg_btn:focus-visible{outline:none}.fcrm_act_wrap .fcrm_pg_btn:disabled{opacity:.5;cursor:not-allowed}.fcrm_act_wrap .fcrm_pg_btn .el-icon{width:20px;height:20px;color:var(--fc-primary-text)}.fcrm_act_wrap .fcrm_pg_page{background:var(--fc-primary-bg);border:1px solid var(--fc-primary-text);border-radius:8px;padding:4px;display:flex;align-items:center;justify-content:center;min-width:28px;height:28px}.fcrm_act_wrap .fcrm_pg_page_num{font-size:14px;font-weight:500;line-height:20px;letter-spacing:-.084px;color:var(--fc-primary-text);text-align:center;width:20px}.fcrm_note_drawer .el-drawer__body .fc_global_form_builder .el-form .el-form-item__content .el-date-editor{padding:0;width:100%}.fcrm_note_drawer .el-drawer__body .fc_global_form_builder .el-form .el-form-item__content .fc-input-text{height:36px;border:1px solid var(--fc-primary-border);box-shadow:0 1px 2px #0a0d1408;background:none;color:var(--fc-secondary-text)}.fcrm_drawer_content{display:flex;flex-direction:column;height:100%;background:var(--fc-primary-bg)}.fcrm_drawer_body{flex:1 0 0;overflow-x:hidden;min-height:0;display:flex;flex-direction:column}.fcrm_drawer_body .fcrm_global_form_builder .el-form{display:flex;flex-direction:column;gap:20px}.fcrm_drawer_body .fcrm_global_form_builder .el-form-item{margin-bottom:0}.fcrm_drawer_body .fcrm_global_form_builder .el-form-item .el-form-item__label .tooltip-icon{margin-left:4px;color:var(--fc-text-muted);cursor:help}.fcrm_drawer_body .fcrm_global_form_builder .el-form-item .el-form-item__content .el-select,.fcrm_drawer_body .fcrm_global_form_builder .el-form-item .el-form-item__content .el-input,.fcrm_drawer_body .fcrm_global_form_builder .el-form-item .el-form-item__content .el-date-editor{width:100%}.fcrm_drawer_footer{padding:12px 20px;border-top:1px solid var(--fc-primary-border);background:var(--fc-primary-bg);display:flex;flex-wrap:wrap;align-items:center;gap:12px;justify-content:space-between;flex-shrink:0;width:100%}.fcrm_drawer_footer .el-button{margin:0}.fcrm_company_info_drawer .el-drawer__body{display:flex;flex-direction:column;height:100%;padding:0}.fcrm_company_info_drawer .el-drawer__footer{border-top:1px solid var(--fc-primary-border);padding:14px 20px;background:var(--fc-primary-bg);flex-shrink:0}.fcrm_company_info_drawer .fcrm_company_drawer_content{display:flex;flex-direction:column;height:100%}.fcrm_company_info_drawer .fcrm_company_info_wrapper{flex:1 0 0;overflow-y:auto;min-height:0}.fcrm_company_info_drawer .fcrm_company_drawer_main{display:flex;flex-direction:column;min-height:100%}.fcrm_company_info_drawer .fcrm_company_create_body{padding:0 0 16px;flex:1 0 0}.fcrm_company_info_drawer .fcrm_company_create_form{display:flex;flex-direction:column;gap:18px}.fcrm_company_info_drawer .fcrm_company_create_form .el-row{row-gap:14px}.fcrm_company_info_drawer .fcrm_company_create_section{padding:0 20px}.fcrm_company_info_drawer .fcrm_company_optional_toggle{display:flex;align-items:center;min-height:32px}.fcrm_company_info_drawer .fcrm_company_optional_fields{padding-top:12px}.fcrm_company_info_drawer .fcrm_social_icon_svg{width:20px;height:20px;color:var(--fc-primary-text);display:block;flex-shrink:0}.fcrm_company_info_drawer .fcrm_company_custom_section .fcrm_custom_field_wrapper{margin-bottom:0}.fcrm_company_info_drawer .fcrm_company_custom_section .fcrm_custom_field_wrapper .fluentcrm_custom_fields{padding:0;border:none}.fcrm_company_info_drawer .fcrm_company_logo_section{padding:0 20px 18px;display:flex;flex-direction:column;gap:16px}.fcrm_company_info_drawer .fcrm_company_logo_section .fcrm_company_logo_upload{display:flex;gap:20px;align-items:flex-start}.fcrm_company_info_drawer .fcrm_company_logo_section .fcrm_company_logo_upload .fcrm_company_logo_placeholder{position:relative;width:64px;height:64px;flex-shrink:0}.fcrm_company_info_drawer .fcrm_company_logo_section .fcrm_company_logo_upload .fcrm_company_logo_placeholder .fcrm_company_logo_image{width:64px;height:64px;border-radius:50%;background-size:cover;background-position:center;background-repeat:no-repeat}.fcrm_company_info_drawer .fcrm_company_logo_section .fcrm_company_logo_upload .fcrm_company_logo_placeholder .fcrm_company_logo_empty_icon{width:64px;height:64px;border-radius:50%;background:var(--fc-primary-border);position:relative;overflow:hidden;display:flex;align-items:center;justify-content:center}.fcrm_company_info_drawer .fcrm_company_logo_section .fcrm_company_logo_upload .fcrm_company_logo_placeholder .fcrm_company_logo_empty_icon:before{content:"";position:absolute;top:0;left:0;right:0;bottom:0;background:var(--fc-secondary-bg);border-radius:50%}.fcrm_company_info_drawer .fcrm_company_logo_section .fcrm_company_logo_upload .fcrm_company_logo_placeholder .fcrm_company_logo_empty_icon .fcrm_building_icon{position:relative;z-index:1;width:24px;height:24px}.fcrm_company_info_drawer .fcrm_company_logo_section .fcrm_company_logo_upload .fcrm_company_logo_info{display:flex;flex-direction:column;gap:4px;flex:1 0 0}.fcrm_company_info_drawer .fcrm_company_logo_section .fcrm_company_logo_upload .fcrm_company_logo_info .fcrm_company_logo_title{font-size:16px;font-weight:500;line-height:24px;letter-spacing:-.176px;color:var(--fc-primary-text)}.fcrm_company_info_drawer .fcrm_company_logo_section .fcrm_company_logo_upload .fcrm_company_logo_info .fcrm_company_logo_subtitle{font-size:14px;font-weight:400;line-height:20px;letter-spacing:-.084px;color:var(--fc-secondary-text)}.fcrm_company_info_drawer .fcrm_company_logo_section .fcrm_company_logo_upload .fcrm_company_logo_info .fcrm_photo_widget{margin-top:12px;display:flex;align-items:center;gap:8px}.fcrm_company_info_drawer .fcrm_company_logo_section .fcrm_company_logo_upload .fcrm_company_logo_info .fcrm_photo_widget .fcrm_photo_actions{display:flex;align-items:center;gap:8px}.fcrm_company_info_drawer .fcrm_company_logo_section .fcrm_company_basic_form{display:flex;flex-direction:column;gap:16px}.fcrm_company_info_drawer .fcrm_company_section_divider{padding:0 0 12px;display:flex;align-items:center;flex-shrink:0}.fcrm_company_info_drawer .fcrm_company_section_divider span{font-size:12px;font-weight:500;line-height:16px;letter-spacing:.48px;text-transform:uppercase;color:var(--fc-text-muted);flex:1 0 0}.fcrm_company_info_drawer .fcrm_company_about_section{padding:20px;display:flex;flex-direction:column;gap:20px}.fcrm_company_info_drawer .fcrm_company_about_section .fcrm_company_form{display:flex;flex-direction:column;gap:16px}.fcrm_company_info_drawer .fcrm_company_address_section{padding:20px;display:flex;flex-direction:column;gap:20px}.fcrm_company_info_drawer .fcrm_company_address_section .fcrm_company_section_header{display:flex;align-items:center;gap:10px}.fcrm_company_info_drawer .fcrm_company_address_section .fcrm_company_section_header h4{font-size:16px;font-weight:500;line-height:24px;color:var(--fc-primary-text);margin:0}.fcrm_company_info_drawer .fcrm_company_address_section .fcrm_company_section_header .fcrm_company_edit_icon{display:inline-flex;align-items:center;justify-content:center;width:20px;height:20px;padding:0;border:none;background:none;color:var(--fc-secondary-text);cursor:pointer;transition:opacity .2s ease}.fcrm_company_info_drawer .fcrm_company_address_section .fcrm_company_section_header .fcrm_company_edit_icon:hover{opacity:.7}.fcrm_company_info_drawer .fcrm_company_address_section .fcrm_company_indented_fields{margin-top:16px;display:flex;flex-direction:column;gap:16px}.fcrm_company_info_drawer .fcrm_company_address_section .fcrm_company_address_row{margin:0}.fcrm_company_info_drawer .fcrm_company_address_section .fcrm_company_address_display{font-size:14px;font-weight:400;line-height:20px;color:var(--fc-secondary-text);margin:0}.fcrm_company_info_drawer .fcrm_company_social_section{padding:20px;display:flex;flex-direction:column;gap:20px}.fcrm_company_info_drawer .fcrm_company_social_section .fcrm_company_section_header{display:flex;align-items:center;gap:10px}.fcrm_company_info_drawer .fcrm_company_social_section .fcrm_company_section_header h4{font-size:16px;font-weight:500;line-height:24px;color:var(--fc-primary-text);margin:0}.fcrm_company_info_drawer .fcrm_company_social_section .fcrm_company_section_header .fcrm_company_edit_icon{display:inline-flex;align-items:center;justify-content:center;width:20px;height:20px;padding:0;border:none;background:none;color:var(--fc-secondary-text);cursor:pointer;transition:opacity .2s ease}.fcrm_company_info_drawer .fcrm_company_social_section .fcrm_company_section_header .fcrm_company_edit_icon:hover{opacity:.7}.fcrm_company_info_drawer .fcrm_company_social_section .fcrm_company_indented_fields{margin-top:16px;display:flex;flex-direction:column;gap:16px}.fcrm_company_info_drawer .fcrm_company_social_section .fcrm_custom_field_wrapper{margin-bottom:0;margin-top:24px}.fcrm_company_info_drawer .fcrm_company_social_section .fcrm_custom_field_wrapper .fluentcrm_custom_fields{padding:0;border:none}.fcrm_company_info_drawer .fcrm_company_form_item{margin-bottom:0}.fcrm_company_info_drawer .fcrm_company_textarea{width:100%}.fcrm_company_info_drawer .fcrm_company_textarea .el-textarea__inner{padding:10px 12px}.fcrm_company_info_drawer .fcrm_company_textarea .el-textarea__inner::placeholder{color:var(--fc-text-muted)}.fcrm_company_info_drawer .fcrm_company_drawer_footer{border-top:1px solid var(--fc-primary-border);padding:14px 20px;background:var(--fc-primary-bg);flex-shrink:0}.fcrm_company_info_drawer .fcrm_company_drawer_footer_actions{display:flex;gap:12px;align-items:center;justify-content:flex-end}.fcrm_company_info_drawer .fcrm_company_drawer_footer_actions .el-button{margin:0}.fcrm_company_social_input{width:100%}.fcrm_company_social_input .el-input__wrapper{padding-left:12px;padding-right:12px;box-shadow:none;transition:border-color .2s ease,box-shadow .2s ease}.fcrm_company_social_input .el-input__wrapper:hover{border-color:var(--fc-secondary-border)}.fcrm_company_social_input .el-input__wrapper.is-focused,.fcrm_company_social_input .el-input__wrapper.is-focus{border-color:var(--fc-primary-text)}.fcrm_company_social_input .el-input__wrapper .el-input__inner{padding-left:12px}.fcrm_company_social_input .el-input__wrapper .el-input__inner::placeholder{color:var(--fc-text-muted)}.fcrm_company_social_input .el-input__prefix{display:flex;align-items:center;justify-content:center;width:36px;padding:0;border-right:1px solid var(--fc-primary-border);margin-right:0;margin-left:-12px}.fcrm_company_social_input .el-input__prefix .fcrm_social_icon{width:20px;height:20px;font-size:20px;color:var(--fc-primary-text);margin:0}.fcrm_company_social_input .el-input__prefix .fcrm_social_icon_svg{display:block}.fcrm_company_info_modal{align-items:center;position:unset;max-height:80vh;overflow:scroll}.fcrm_company_info_modal .el-dialog__body .el-upload{border-radius:var(--radius-12, var(--fcrm-border-radius-8, 8px));border:1px dashed var(--fc-secondary-border);background:var(--fc-primary-bg)}.fcrm_company_info_modal .el-dialog__body .el-upload:hover{border:1px dashed var(--fc-primary-text)}.fcrm_company_info_modal .fcrm_csv_upload_container{display:flex;flex-direction:column;justify-content:center;gap:20px}.fcrm_company_info_modal .fcrm_csv_delimiter_container{display:flex;flex-direction:column;align-items:flex-start;gap:4px;align-self:stretch}.fcrm_company_info_modal .fcrm_csv_file_upload_container .el-upload{border-radius:var(--radius-12, 8px);border:1px dashed var(--fc-secondary-border);background:var(--fc-primary-bg)}.fcrm_company_info_modal .fcrm_csv_file_upload_container .el-upload:hover{border:1px dashed var(--fc-primary-text)}.fcrm_company_info_modal .el-upload-dragger{margin:0;border:none;border-radius:8px;padding:0}.fcrm_company_info_modal .fcrm_file_uploader{padding:32px;display:flex;flex-direction:column;justify-content:center;align-items:center;gap:20px;align-self:stretch;overflow:hidden}.fcrm_company_info_modal .fcrm_upload_icon svg{width:24px;height:24px}.fcrm_company_info_modal .fcrm_sample_warning_container{margin-top:12px}.fcrm_company_info_modal .el-upload__tip{display:flex;align-items:center;justify-content:space-between;gap:8px;padding:8px;border-radius:var(--fcrm-border-radius-8);background:transparent}.fcrm_company_info_modal .el-upload__tip>svg,.fcrm_company_info_modal .el-upload__tip>.el-icon{display:none}.fcrm_company_field_mapping_title{font-size:16px;font-weight:500;line-height:24px;letter-spacing:-.176px;color:var(--fc-primary-text);margin:0}.fcrm_company_field_mapping_table{width:calc(100% + 32px);max-width:calc(100% + 32px);margin-left:-16px;margin-right:-16px;border-collapse:separate;border-spacing:16px 8px;background:transparent;margin-bottom:20px}.fcrm_company_field_mapping_table thead tr th{background:transparent;border:none;padding:8px 12px 8px 0;text-align:left;height:36px;vertical-align:middle;width:50%;color:var(--fc-text-muted);font-size:12px;font-style:normal;font-weight:500;line-height:16px;text-transform:uppercase}.fcrm_company_field_mapping_table thead tr th:first-child{border-top-left-radius:8px;border-bottom-left-radius:8px}.fcrm_company_field_mapping_table thead tr th:last-child{padding-right:20px;border-top-right-radius:8px;border-bottom-right-radius:8px}.fcrm_company_field_mapping_table tbody tr td{background:transparent;border:none;padding:0;text-align:left;vertical-align:middle;width:50%;margin:0}.fcrm_company_update_section{margin-top:24px;display:flex;flex-direction:column;gap:16px}.fcrm_company_update_section .el-form .el-form-item{margin:0}.fcrm_company_update_form{display:flex;flex-direction:column;gap:12px}.fcrm_company_update_item{margin-bottom:0}.fcrm_company_update_label_row{display:flex;align-items:center;gap:6px}.fcrm_company_update_label{color:var(--fc-primary-text);font-size:14px;font-style:normal;font-weight:500;line-height:20px;letter-spacing:-.084px}.fcrm_company_update_radios{display:flex;gap:16px}.fcrm_company_update_radios .el-radio{margin-right:0}.fcrm_company_save_wrap{display:flex;justify-content:flex-start}.fcrm_company_summary_shared .fcrm_company_summary_logo_section{padding:0 20px 18px;display:flex;gap:20px;align-items:flex-start;flex-shrink:0}.fcrm_company_summary_shared .fcrm_company_summary_form{display:flex;flex-direction:column;gap:18px;width:100%}.fcrm_company_summary_shared .fcrm_company_summary_form .el-row{row-gap:14px}.fcrm_company_summary_shared .fcrm_company_summary_section{padding:0 20px}.fcrm_company_summary_shared .fcrm_company_section_divider{padding:0 0 12px;display:flex;align-items:center;flex-shrink:0}.fcrm_company_summary_shared .fcrm_company_section_divider span{font-size:12px;font-weight:500;line-height:16px;letter-spacing:.48px;text-transform:uppercase;color:var(--fc-text-muted);flex:1 0 0}.fcrm_company_summary_shared .fcrm_company_form_item{margin-bottom:0}.fcrm_company_edit_shared{display:flex;flex-direction:column;gap:18px;padding-bottom:16px}.fcrm_company_edit_shared .fcrm_company_edit_section{padding:0 20px}.fcrm_company_edit_shared .fcrm_company_optional_toggle{display:flex;align-items:center;min-height:32px}.fcrm_company_edit_shared .fcrm_company_summary_form{display:flex;flex-direction:column;gap:18px;width:100%}.fcrm_company_edit_shared .fcrm_company_summary_form .el-row{row-gap:14px}.fcrm_company_edit_shared .fcrm_company_section_divider{padding:0 0 12px;display:flex;align-items:center;flex-shrink:0}.fcrm_company_edit_shared .fcrm_company_section_divider span{font-size:12px;font-weight:500;line-height:16px;letter-spacing:.48px;text-transform:uppercase;color:var(--fc-text-muted);flex:1 0 0}.fcrm_company_edit_shared .fcrm_company_form_item{margin-bottom:0}.fcrm_company_edit_shared .fcrm_company_edit_social_grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:14px 16px}.fcrm_company_edit_shared .fcrm_company_edit_social_full{grid-column:1/-1}.fcrm_company_edit_shared .fcrm_company_custom_section .fcrm_custom_field_wrapper{margin-bottom:0}.fcrm_company_edit_shared .fcrm_company_custom_section .fcrm_custom_field_wrapper .fluentcrm_custom_fields{padding:0;border:none}.fcrm_company_edit_section.fcrm_company_custom_section .fcrm_company_custom_field_wrapper{padding:16px;border-radius:var(--fcrm-border-radius-8);background:var(--fc-secondary-bg)}.fcrm_company_edit_section.fcrm_company_custom_section .fcrm_company_custom_field_wrapper .fcrm_custom_data_group:not(.fcrm_custom_data_group_plain){background:var(--fc-primary-bg);padding-bottom:20px}@media (max-width: 767px){.fcrm_company_edit_shared .fcrm_company_edit_social_grid{grid-template-columns:1fr}}.fcrm_company_summary_edit_drawer .el-drawer__body{padding:0}.fcrm_company_summary_edit_drawer .el-drawer__footer{border-top:1px solid var(--fc-primary-border);padding:14px 20px;background:var(--fc-primary-bg);flex-shrink:0}.fcrm_company_summary_edit_drawer .fcrm_company_summary_body{padding:0 0 16px;overflow-y:auto}.fcrm_company_summary_edit_drawer .fcrm_company_summary_logo_section{padding:0 20px 18px;display:flex;gap:20px;align-items:flex-start;flex-shrink:0}.fcrm_company_summary_edit_drawer .fcrm_company_summary_form{display:flex;flex-direction:column;gap:18px;width:100%}.fcrm_company_summary_edit_drawer .fcrm_company_summary_form .el-row{row-gap:14px}.fcrm_company_summary_edit_drawer .fcrm_company_summary_section{padding:0 20px}.fcrm_company_summary_edit_drawer .fcrm_company_section_divider{padding:0 0 12px;display:flex;align-items:center;flex-shrink:0}.fcrm_company_summary_edit_drawer .fcrm_company_section_divider span{font-size:12px;font-weight:500;line-height:16px;letter-spacing:.48px;text-transform:uppercase;color:var(--fc-text-muted);flex:1 0 0}.fcrm_company_summary_edit_drawer .fcrm_company_form_item{margin-bottom:0}.fcrm_company_summary_edit_drawer .fcrm_company_summary_footer_actions{display:flex;gap:12px;align-items:center;justify-content:flex-end}.fcrm_company_summary_edit_drawer .fcrm_company_summary_footer_actions .el-button{margin:0}.fcrm_drawer_section{display:flex;flex-direction:column;width:100%}.fcrm_company_summary_edit_inline .fcrm_section_divider{margin-bottom:16px}.fcrm_company_summary_edit_inline .fcrm_drawer_section{margin-bottom:24px}.fcrm_drawer_section_body{display:flex;flex-direction:column;gap:20px;width:100%}.fcrm_drawer_section_body .fcrm_edit_form{display:flex;flex-direction:column;gap:16px}.fcrm_section_divider{background:var(--fc-secondary-bg);display:flex;align-items:center;justify-content:center;padding:8px 20px;width:100%;flex-shrink:0}.fcrm_section_divider span{flex:1 0 0;font-size:12px;font-weight:500;line-height:16px;letter-spacing:.48px;text-transform:uppercase;color:var(--fc-text-muted)}.fcrm_logo_upload{display:flex;gap:20px;align-items:flex-start}.fcrm_logo_placeholder{width:64px;height:64px;border-radius:999px;background:var(--fc-primary-border);display:flex;align-items:center;justify-content:center;flex-shrink:0;overflow:hidden;position:relative}.fcrm_logo_placeholder .fcrm_logo_img{width:100%;height:100%;background-size:cover;background-position:center;background-repeat:no-repeat}.fcrm_logo_placeholder .fcrm_logo_empty{width:24px;height:24px;display:flex;align-items:center;justify-content:center}.fcrm_logo_info{display:flex;flex-direction:column;gap:12px;align-items:flex-start;flex:1 0 0}.fcrm_logo_title{font-size:16px;font-weight:500;line-height:24px;letter-spacing:-.176px;color:var(--fc-primary-text)}.fcrm_logo_subtitle{font-size:14px;font-weight:400;line-height:20px;letter-spacing:-.084px;color:var(--fc-secondary-text)}.fcrm_edit_form{display:flex;flex-direction:column;gap:16px;width:100%}.fcrm_edit_form_item{width:100%;margin-bottom:0!important}.fcrm_edit_form_item .el-form-item__content{width:100%}.fcrm_drawer_footer_end{justify-content:end!important;padding-bottom:10px!important}.fcrm_edit_section{display:flex;flex-direction:column;gap:20px;width:100%}.fcrm_edit_section_title{font-size:16px;font-weight:500;line-height:24px;letter-spacing:-.176px;color:var(--fc-primary-text)}.fcrm_social_input{display:flex;width:100%;border:1px solid var(--fc-primary-border);border-radius:10px;overflow:hidden;background:var(--fc-primary-bg);position:relative}.fcrm_social_input_icon{display:flex;align-items:center;justify-content:center;padding:8px;flex-shrink:0;background:var(--fc-primary-bg);border-right:1px solid var(--fc-primary-border)}.fcrm_social_input_icon svg{width:20px;height:20px;display:block;flex-shrink:0}.fcrm_social_input_icon .fcrm_social_icon_large{width:20px;height:20px;font-size:20px;display:flex;align-items:center;justify-content:center}.fcrm_social_input_field{flex:1 0 0;min-width:0}.fcrm_social_input_field .el-input__wrapper{border:none;border-radius:0;box-shadow:none}.fcrm_social_input_field .el-input__wrapper:hover,.fcrm_social_input_field .el-input__wrapper.is-focused,.fcrm_social_input_field .el-input__wrapper.is-focus{box-shadow:none}.fcrm_company_info_wrapper .fcrm_view_sections{display:flex;flex-direction:column;gap:24px;width:100%}.fcrm_company_info_wrapper .fcrm_view_sections .fcrm_base_card{margin-bottom:0}.fcrm_company_info_wrapper .fcrm_view_section{display:flex;flex-direction:column;gap:12px;width:100%}.fcrm_company_info_wrapper .fcrm_view_section_head{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:0 2px;min-height:32px}.fcrm_company_info_wrapper .fcrm_view_section_title{font-size:16px;font-weight:500;line-height:24px;letter-spacing:-.176px;color:var(--fc-primary-text);margin:0}.fcrm_company_info_wrapper .fcrm_view_section_body{display:flex;flex-direction:column;gap:16px;width:100%}.fcrm_company_info_wrapper .fcrm_view_section_body .fcrm_base_card{margin:0}.fcrm_company_info_wrapper .fcrm_view_logo_wrap{flex-shrink:0}.fcrm_company_info_wrapper .fcrm_view_logo{width:64px;height:64px;border-radius:999px;background:var(--fc-text-link);display:flex;align-items:center;justify-content:center;overflow:hidden;position:relative;background-size:cover;background-position:center;background-repeat:no-repeat}.fcrm_company_info_wrapper .fcrm_view_logo .fcrm_view_logo_icon{width:24px;height:24px;display:flex;align-items:center;justify-content:center}.fcrm_company_info_wrapper .fcrm_view_head_text{flex:1 0 0;display:flex;flex-direction:column;min-width:0}.fcrm_company_info_wrapper .fcrm_view_head_text_grp{display:flex;flex-direction:column;gap:4px;align-items:flex-start;width:100%}.fcrm_company_info_wrapper .fcrm_view_name{font-size:16px;font-weight:500;line-height:24px;letter-spacing:-.176px;color:var(--fc-primary-text);margin:0}.fcrm_company_info_wrapper .fcrm_view_contact{font-size:14px;font-weight:400;line-height:20px;letter-spacing:-.084px;color:var(--fc-secondary-text);margin:0}.fcrm_company_info_wrapper .fcrm_company_quick_view{display:flex;flex-direction:column;gap:16px;padding:0 20px 20px;width:100%}.fcrm_company_info_wrapper .fcrm_company_quick_hero{display:grid;grid-template-columns:auto minmax(0,1fr) auto;gap:12px;align-items:flex-start;padding:20px 0 18px;border-bottom:1px solid var(--fc-primary-border)}.fcrm_company_info_wrapper .fcrm_company_quick_logo{width:56px;height:56px;border-radius:50%;background:var(--fc-deep-bg);background-size:cover;background-position:center;background-repeat:no-repeat;display:flex;align-items:center;justify-content:center;flex-shrink:0}.fcrm_company_info_wrapper .fcrm_company_quick_identity{min-width:0}.fcrm_company_info_wrapper .fcrm_company_quick_identity h3{margin:0 0 4px;font-weight:500;font-size:16px;line-height:24px}.fcrm_company_info_wrapper .fcrm_company_quick_contacts{display:flex;flex-wrap:wrap;gap:4px 12px}.fcrm_company_info_wrapper .fcrm_company_quick_contacts a{color:var(--fc-secondary-text);font-weight:400;font-size:14px;line-height:20px;display:block;text-decoration:none}.fcrm_company_info_wrapper .fcrm_company_quick_contacts a:hover{color:var(--fc-text-link)}.fcrm_company_info_wrapper .fcrm_company_quick_actions{display:flex;gap:8px;align-items:center;justify-content:flex-end;flex-wrap:wrap}.fcrm_company_info_wrapper .fcrm_company_quick_actions .el-button,.fcrm_company_info_wrapper .fcrm_company_quick_actions .el-button.el-button--small{margin:0}.fcrm_company_info_wrapper .fcrm_company_quick_stats{display:flex;flex-direction:column;gap:14px}.fcrm_company_info_wrapper .fcrm_company_quick_stat{display:grid;grid-template-columns:20px minmax(0,1fr);gap:4px;align-items:center;min-width:0}.fcrm_company_info_wrapper .icon{width:20px;height:20px;color:var(--fc-text-muted);display:inline-flex;align-items:center;justify-content:center;font-size:16px}.fcrm_company_info_wrapper .fcrm_company_quick_stat_text{min-width:0}.fcrm_company_info_wrapper .fcrm_company_quick_stat_text span{display:inline-block;font-weight:400;font-size:14px;line-height:20px;color:var(--fc-secondary-text);min-width:90px}.fcrm_company_info_wrapper .fcrm_company_quick_stat_text strong{display:inline;color:var(--fc-primary-text);font-size:14px;line-height:20px;overflow-wrap:anywhere;font-weight:400}.fcrm_company_info_wrapper .fcrm_company_quick_stat_text a{color:var(--fc-primary-text);text-decoration:none}.fcrm_company_info_wrapper .fcrm_company_quick_stat_text a:hover{color:var(--fc-text-link)}.fcrm_company_info_wrapper .fcrm_company_quick_panel{padding:16px 0;border-top:1px solid var(--fc-primary-border)}.fcrm_company_info_wrapper .fcrm_company_quick_panel.fcrm_pb_0{padding-bottom:0}.fcrm_company_info_wrapper .fcrm_company_quick_panel.fcrm_pt_0{padding-top:0}.fcrm_company_info_wrapper .fcrm_company_quick_panel_head{display:flex;align-items:center;justify-content:space-between;margin-bottom:8px}.fcrm_company_info_wrapper .fcrm_company_quick_panel_head h4{margin:0;color:var(--fc-primary-text);font-weight:500;font-size:14px;line-height:20px}.fcrm_company_info_wrapper .fcrm_company_quick_description,.fcrm_company_info_wrapper .fcrm_company_quick_address p,.fcrm_company_info_wrapper .fcrm_company_quick_empty{margin:0;font-size:14px;line-height:22px;font-weight:400}.fcrm_company_info_wrapper .fcrm_company_quick_description,.fcrm_company_info_wrapper .fcrm_company_quick_address p{color:var(--fc-primary-text)}.fcrm_company_info_wrapper .fcrm_company_quick_empty{color:var(--fc-secondary-text)}.fcrm_company_info_wrapper .fcrm_company_quick_address{display:flex;flex-direction:column;gap:4px}.fcrm_company_info_wrapper .fcrm_company_quick_socials{display:flex;flex-wrap:wrap;gap:8px}.fcrm_company_info_wrapper .fcrm_company_quick_socials a{margin:0;text-decoration:none}.fcrm_company_info_wrapper .fcrm_company_quick_socials a:hover{color:var(--fc-text-link)!important}.fcrm_company_info_wrapper .fcrm_company_quick_custom{margin:0 20px 20px}.fcrm_company_info_wrapper .fcrm_company_quick_custom .fcrm_view_cf_wrap{gap:12px}.fcrm_company_info_wrapper .fcrm_company_quick_custom .fcrm_custom_field_wrapper{margin-bottom:0}.fcrm_company_info_wrapper .fcrm_company_quick_custom .fcrm_custom_field_wrapper .fcrm_custom_data_grid{grid-template-columns:1fr}.fcrm_company_info_wrapper .fcrm_company_quick_custom .fcrm_custom_field_wrapper .fluentcrm_custom_fields{padding:0;border:none}.fcrm_company_info_wrapper:not(.fcrm_company_in_drawer) .fcrm_company_quick_view,.fcrm_company_info_wrapper:not(.fcrm_company_in_drawer) .fcrm_company_quick_custom{background:var(--fc-primary-bg);border-radius:var(--fcrm-border-radius-8);overflow:hidden}.fcrm_company_info_wrapper:not(.fcrm_company_in_drawer) .fcrm_company_quick_view{padding:0 18px 18px}.fcrm_company_info_wrapper:not(.fcrm_company_in_drawer) .fcrm_company_quick_hero{grid-template-columns:auto minmax(0,1fr)}.fcrm_company_info_wrapper:not(.fcrm_company_in_drawer) .fcrm_company_quick_actions{grid-column:1/-1;justify-content:flex-start}.fcrm_company_info_wrapper:not(.fcrm_company_in_drawer) .fcrm_company_quick_custom{margin:0;padding:16px 18px 18px}@media (max-width: 782px){.fcrm_company_info_wrapper .fcrm_company_quick_hero{grid-template-columns:auto minmax(0,1fr)}.fcrm_company_info_wrapper .fcrm_company_quick_actions{grid-column:1/-1;justify-content:flex-start}}.fcrm_assign_drawer .fcrm_company_info_wrapper .fcrm_company_quick_custom{padding-top:20px;border-top:1px solid var(--fc-primary-border)}.fluentcrm-app.is-settings-page{margin:-16px 0 0 -20px;overflow-x:hidden}.fluentcrm-app.is-settings-page .fluentcrm-body{position:fixed;bottom:0;right:0;z-index:50;height:calc(100vh - 88px);width:100%}.fluentcrm-app.is-settings-page .fcrm_settings_inner{padding-left:432px;position:relative;transition:.3s}.fluentcrm-app.is-settings-page .fcrm_settings_inner.is-collapsed{padding-left:238px}.fluentcrm-app.is-settings-page .fcrm_settings_inner.is-collapsed .fcrm_max_w_800,.fluentcrm-app.is-settings-page .fcrm_settings_inner.is-collapsed .fcrm_content_card,.fluentcrm-app.is-settings-page .fcrm_settings_inner.is-collapsed .fcrm_data_cleanup_wrap{max-width:1024px}.fluentcrm-app.is-settings-page .fcrm_settings_content .fcrm_settings{overflow-x:auto;overflow-y:auto;overscroll-behavior:contain;max-height:calc(100vh - 89px);min-height:calc(100vh - 89px);scrollbar-width:none}.folded .fluentcrm-app.is-settings-page .fcrm_settings_inner{padding-left:308px}.folded .fluentcrm-app.is-settings-page .fcrm_settings_inner.is-collapsed{padding-left:114px}.folded .fcrm_settings_sidebar{left:36px}@media (max-width: 960px){.auto-fold .fluentcrm-app.is-settings-page .fcrm_settings_inner,.auto-fold .fluentcrm-app.is-settings-page .fcrm_settings_inner.is-collapsed{padding-left:36px}.auto-fold .fcrm_topbar{width:calc(100% - 36px)}}.fcrm_settings_menu_collapsable{display:flex;align-items:center;gap:10px;padding:12px 20px 12px 26px;background:var(--fc-primary-bg);border-bottom:1px solid var(--fc-primary-border);justify-content:flex-start;height:57px;position:sticky;top:0;z-index:9;transition:.3s}.fcrm_settings_menu_collapsable.for-mobile{display:none}.fcrm_settings_menu_collapsable.is-collapsed .fcrm_settings_menu_collapsable--btn:hover{cursor:e-resize}.fcrm_settings_menu_collapsable--btn{display:flex;align-items:center;justify-content:center;cursor:pointer;transition:all .3s ease;color:var(--fc-secondary-text)}.fcrm_settings_menu_collapsable--btn svg{display:block}.fcrm_settings_menu_collapsable--btn:hover{cursor:w-resize}.fcrm_settings_menu_collapsable--title{font-size:16px;font-weight:600;color:var(--fc-primary-text);transition:opacity .3s ease,width .3s ease}.fcrm_settings_menu_collapsable.is-collapsed .fcrm_settings_menu_collapsable--title{display:none}.fcrm_settings_inner{display:flex}.fcrm_settings_content{overflow:hidden;flex:1}.fcrm_settings_menu_overlay{position:fixed;height:calc(100vh - 139px);left:0;right:0;bottom:0;background:#00000080;z-index:998;opacity:0;visibility:hidden;transition:.3s}@media (max-width: 1024px){.fcrm_settings_menu_overlay.is-open{opacity:1;visibility:visible}}.fcrm_settings .fcrm_settings_menu_toggle{position:fixed;bottom:12px;right:12px;z-index:999;display:none;box-shadow:0 0 20px #1c273214}.fcrm_settings .fcrm_settings_menu_toggle .el-button{border-radius:8px;padding:6px 12px;background:var(--fc-primary-bg);border:1px solid var(--fc-primary-border);color:#2f3448}.fcrm_settings .fcrm_settings_menu_toggle .el-button:hover{background:var(--fc-secondary-bg)}.fcrm_settings .fcrm_settings_menu_toggle .el-button>span{gap:6px}.fcrm_settings .fcrm_settings_topbar{display:flex;align-items:center;justify-content:space-between;padding:12px 32px;background:var(--fc-primary-bg);border-bottom:1px solid var(--fc-primary-border);border-top:1px solid var(--fc-primary-border);box-shadow:0 8px 12px -12px #0e121b05;min-height:57px;position:sticky;top:0;z-index:9}.fcrm_settings .fcrm_settings_topbar h3{margin:0;font-size:18px;line-height:24px;font-weight:500;color:var(--fc-primary-text);display:flex;align-items:center;gap:10px}.fcrm_settings .fcrm_settings_topbar--breadcrumb{display:flex;align-items:center;gap:10px}.fcrm_settings .fcrm_settings_topbar--actions{display:flex;align-items:center;gap:8px}.fcrm_settings .fcrm_settings_topbar--actions .el-button{margin:0}.fcrm_settings .fcrm_settings_topbar .fcrm_settings_menu_collapsable--btn{cursor:e-resize}.fcrm_settings .fcrm_settings_topbar .fcrm_settings_menu_collapsable--btn.is-collapsed{cursor:w-resize}@media (min-width: 1025px){.fcrm_settings .fcrm_settings_topbar .fcrm_settings_menu_collapsable--btn{display:none}}.fcrm_settings .fcrm-divider{width:100%;height:1px;background:var(--fc-primary-border)}.fcrm_settings.fcrm_general_settings .fcrm_comment_settings_section .el-form .fcrm_form_builder_item{margin-bottom:16px}.fcrm_settings .fcrm_tag_mappings table{box-shadow:none;border:1px solid var(--fc-primary-border);border-radius:8px;border-collapse:separate;border-spacing:0;margin:0}.fcrm_settings .fcrm_tag_mappings table thead tr th{font-weight:500;font-size:14px;line-height:20px;background:var(--fc-secondary-bg);border:none;border-right:1px solid var(--fc-primary-border);border-bottom:1px solid var(--fc-primary-border);padding:8px 12px}.fcrm_settings .fcrm_tag_mappings table thead tr th:first-child{border-radius:8px 0 0;padding-left:16px}.fcrm_settings .fcrm_tag_mappings table thead tr th:nth-child(2),.fcrm_settings .fcrm_tag_mappings table thead tr th:nth-child(3){width:240px}.fcrm_settings .fcrm_tag_mappings table thead tr th:last-child{border-right:none}.fcrm_settings .fcrm_tag_mappings table tbody tr:last-child td{border-bottom:none}.fcrm_settings .fcrm_tag_mappings table tbody tr td{font-weight:400;font-size:14px;line-height:20px;color:var(--fc-primary-text);padding:12px;border:none;box-shadow:none;border-right:1px solid var(--fc-primary-border);border-bottom:1px solid var(--fc-primary-border)}.fcrm_settings .fcrm_tag_mappings table tbody tr td:first-child{padding-left:16px}.fcrm_settings .fcrm_tag_mappings table tbody tr td:last-child{border-right:none}.fcrm_settings .fcrm_tag_mappings table tbody tr td .fcrm_options_selector{border-radius:8px;position:relative}.fcrm_settings .fcrm_tag_mappings table tbody tr td .fcrm_options_selector .el-select{height:auto;min-height:inherit}.fcrm_settings.fcrm_integration_settings .fcrm_integration_config .fcrm_options_selector .fcrm_with_select{border-radius:0 8px 8px 0;background:var(--fc-secondary-bg);color:var(--fc-secondary-text);padding:10px;border:none}.fcrm_settings.fcrm_abandoned_cart_settings .el-input.el-input--suffix{height:auto}.fcrm_settings.fcrm_abandoned_cart_settings .el-input.el-input--suffix .el-input__wrapper{padding:0 0 0 10px;height:auto}.fcrm_settings.fcrm_abandoned_cart_settings .el-input.el-input--suffix .el-input__wrapper input{height:auto}.fcrm_settings.fcrm_abandoned_cart_settings .el-input.el-input--suffix .el-input__suffix{background:var(--fc-secondary-bg);color:var(--fc-secondary-text);padding:7px 12px;font-weight:500;font-size:14px;line-height:20px;border-radius:0 8px 8px 0}.fcrm_settings.fcrm_abandoned_cart_settings .fcrm_abcart_not_providers_available{background:var(--fc-secondary-bg);padding:14px 14px 14px 40px;border-radius:8px;position:relative}.fcrm_settings.fcrm_abandoned_cart_settings .fcrm_abcart_not_providers_available .icon{position:absolute;left:14px;top:14px;color:var(--fc-text-muted)}.fcrm_settings.fcrm_abandoned_cart_settings .fcrm_abcart_not_providers_available p{color:var(--fc-primary-text);opacity:.72;font-weight:400;font-size:14px;line-height:20px;margin:0}.fcrm_settings .fcrm_report_card .el-form .fcrm_form_builder_item+.fcrm_form_builder_item{margin-top:16px}.fcrm_settings .fcrm_report_card .el-form .fcrm_input_label{color:var(--fc-primary-text);font-size:14px;font-style:normal;font-weight:500;line-height:20px;letter-spacing:-.084px;margin:0 0 4px}.fcrm_settings img{max-width:100%}.fcrm_settings .fcrm_report_card .fcrm_report_card_body{min-height:inherit}.fcrm_settings .fc_inline_help{margin:0;color:var(--fc-secondary-text);font-weight:400;font-size:12px;font-style:normal;line-height:16px}.fcrm_setting_switch{display:flex;width:100%;gap:10px}.fcrm_setting_switch .fcrm_switch .el-switch.is-checked{--el-switch-on-color: var(--fc-primary-text)}.fcrm_setting_switch .fcrm_switch_label p{margin:0;color:var(--fc-primary-text);font-size:14px;font-style:normal;font-weight:500;line-height:20px;letter-spacing:-.084px}.fcrm_setting_switch .fcrm_switch_label span{color:var(--fc-secondary-text);font-weight:400;font-size:12px;font-style:normal;line-height:16px}.fcrm_settings_pro_banner img{max-width:100%}@media (max-width: 768px){.fcrm_settings_pro_banner{flex-direction:column;align-items:stretch;gap:16px}.fcrm_settings_pro_banner_preview{flex:0 0 auto;width:100%}.fcrm_settings_pro_banner_details,.fcrm_settings_pro_banner_details_get_btn{width:100%}}.fcrm_double_optin_settings_body .el-form .el-form-item .el-radio-group.fcrm_image_radios .el-radio{height:auto}.fcrm_settings_sidebar{background:var(--fc-primary-bg);width:272px;flex:none;border-top:1px solid var(--fc-primary-border);border-right:1px solid var(--fc-primary-border);overflow-x:hidden;height:calc(100vh - 89px);transition:width .3s ease;position:absolute;left:160px;top:0;z-index:99;scrollbar-width:none}.fcrm_settings_sidebar::-webkit-scrollbar{width:0}.fcrm_settings_sidebar::-webkit-scrollbar-track{background:transparent;width:0}.fcrm_settings_sidebar::-webkit-scrollbar-thumb{background:transparent;width:0}.fcrm_settings_sidebar.is-collapsed{width:78px}.fcrm_settings_sidebar.is-collapsed.is-hover-expanded{width:272px;box-shadow:0 16px 32px -12px #0e121b1a}.fcrm_settings_sidebar.is-collapsed.is-hover-expanded .fcrm_settings_menu_collapsable.is-collapsed{padding-right:20px}.fcrm_settings_sidebar.is-collapsed.is-hover-expanded .fcrm_settings_menu_collapsable.is-collapsed .fcrm_settings_menu_collapsable--title{display:block}.fcrm_settings_sidebar.is-collapsed.is-hover-expanded .el-menu .el-menu-item{padding-top:8px;padding-bottom:8px}.fcrm_settings_sidebar.is-collapsed.is-hover-expanded .el-menu .el-menu-item .fcrm_menu_title{display:flex;opacity:1;visibility:visible}.fcrm_settings_sidebar.is-collapsed.is-hover-expanded .el-menu .el-sub-menu .el-menu-item{justify-content:flex-start}.fcrm_settings_sidebar.is-collapsed.is-hover-expanded .el-menu .el-sub-menu .el-sub-menu__title{opacity:1;visibility:visible}.fcrm_settings_sidebar.is-collapsed.is-hover-expanded .el-menu .el-sub-menu .el-sub-menu__icon-arrow{display:flex}.fcrm_settings_sidebar.is-collapsed.is-hover-expanded .el-menu .el-sub-menu .fcrm_menu_title{opacity:1;visibility:visible}.fcrm_settings_sidebar.is-collapsed .el-menu .el-menu-item .fcrm_menu_title{opacity:0;visibility:hidden}.fcrm_settings_sidebar.is-collapsed .el-menu .el-menu-item.is-active:before{left:-16px}.fcrm_settings_sidebar.is-collapsed .el-menu .el-sub-menu .el-sub-menu__icon-arrow{display:none}.fcrm_settings_sidebar.is-collapsed .el-menu .el-sub-menu .fcrm_menu_title{opacity:0;visibility:hidden}.fcrm_settings_sidebar.is-collapsed .fcrm-admin-tools-submenu :deep(.el-sub-menu__title){padding:8px!important;justify-content:center}.fcrm_settings_sidebar.is-collapsed .fcrm-admin-tools-submenu :deep(.el-sub-menu__title) .fcrm_menu_icon{margin:0}.fcrm_settings_sidebar.is-collapsed .fcrm-admin-tools-submenu :deep(.el-sub-menu__title) .fcrm_menu_title{opacity:0;visibility:hidden}.fcrm_settings_sidebar.is-collapsed .fcrm-admin-tools-submenu :deep(.el-sub-menu__title) .el-sub-menu__icon-arrow{display:none}.fcrm_settings_sidebar.is-collapsed .fcrm-admin-tools-submenu.is-parent-active :deep(.el-sub-menu__title::before){left:-12px}.fcrm_settings_sidebar.is-collapsed .fcrm-admin-tools-submenu :deep(.el-menu){display:none}.fcrm_settings_sidebar .el-menu{padding:16px;border-right:none;transition:.2s;background:none}.fcrm_settings_sidebar .el-menu.el-menu--vertical:not(.el-menu--collapse):not(.el-menu--popup-container) .el-menu-item{padding-left:12px}.fcrm_settings_sidebar .el-menu .el-menu-item{color:var(--fc-secondary-text);font-weight:500;font-size:14px;line-height:20px;margin:0 0 4px;display:flex;align-items:center;gap:8px;padding:8px 12px;border-radius:8px;height:auto;transition:.3s}.fcrm_settings_sidebar .el-menu .el-menu-item:last-child{margin-bottom:0}.fcrm_settings_sidebar .el-menu .el-menu-item.is-active .fcrm_menu_title .menu_right_icon{display:block}.fcrm_settings_sidebar .el-menu .el-menu-item .fcrm_menu_icon{flex-shrink:0;display:flex;align-items:center;justify-content:center;width:20px;height:20px;border-radius:6px;overflow:hidden;transition:.3s}.fcrm_settings_sidebar .el-menu .el-menu-item .fcrm_menu_title{flex:1;min-width:0;white-space:nowrap;position:relative;display:flex;align-items:center;justify-content:space-between;transition:.3s}.fcrm_settings_sidebar .el-menu .el-menu-item .fcrm_menu_title .menu_right_icon{display:none;position:absolute;right:-5px;font-size:12px;margin:0;width:auto}.fcrm_settings_sidebar .el-menu .el-menu-item:hover{background:var(--fc-secondary-bg);color:var(--fc-secondary-text)}.fcrm_settings_sidebar .el-menu:not(.el-menu--collapse):not(.el-menu--popup-container) .fcrm-admin-tools-submenu .el-sub-menu__title{padding-left:12px}.fcrm_settings_sidebar .el-menu .fcrm-admin-tools-submenu{height:auto;line-height:1}.fcrm_settings_sidebar .el-menu .fcrm-admin-tools-submenu .el-sub-menu__title{padding:8px 12px;display:flex;align-items:center;gap:8px;font-size:14px;line-height:20px;height:auto;border-radius:8px;color:var(--fc-secondary-text);font-weight:500;transition:.3s}.fcrm_settings_sidebar .el-menu .fcrm-admin-tools-submenu .el-sub-menu__title:hover{background:var(--fc-secondary-bg)}.fcrm_settings_sidebar .el-menu .fcrm-admin-tools-submenu .el-sub-menu__title .fcrm_menu_icon{display:block;flex:none;transition:.3s}.fcrm_settings_sidebar .el-menu .fcrm-admin-tools-submenu .el-sub-menu__title .el-sub-menu__icon-arrow{display:none;right:8px}.fcrm_settings_sidebar .el-menu .fcrm-admin-tools-submenu.is-active .el-sub-menu__title{color:var(--fc-primary-text)}.fcrm_settings_sidebar .el-menu .fcrm-admin-tools-submenu.is-active .el-sub-menu__title .el-sub-menu__icon-arrow{display:block}.fcrm_settings_sidebar .el-menu .fcrm-admin-tools-submenu .el-menu{padding:0 0 0 40px;border:none;position:relative;margin-top:4px;margin-bottom:4px}.fcrm_settings_sidebar .el-menu .fcrm-admin-tools-submenu .el-menu:before{content:"";position:absolute;left:21px;top:50%;height:calc(100% - 16px);width:2px;background:var(--fc-primary-border);border-radius:10px;transform:translateY(-50%)}.fcrm_settings_sidebar .el-menu .fcrm-admin-tools-submenu .el-menu .el-menu-item:before{border-radius:10px;width:2px;left:-19px}.fcrm_settings_sidebar .el-menu-item.is-active{background:var(--fc-secondary-bg);color:var(--fc-primary-text);position:relative}.fcrm_settings_sidebar .el-menu-item.is-active:before{content:"";position:absolute;left:-16px;top:50%;transform:translateY(-50%);width:4px;height:20px;background:var(--fc-primary-text);border-radius:0 4px 4px 0;transition:.2s}.fcrm_settings_sidebar :deep(.el-menu-item:focus),.fcrm_settings_sidebar :deep(.el-menu-item:focus-visible),.fcrm_settings_sidebar :deep(.el-sub-menu__title:focus),.fcrm_settings_sidebar :deep(.el-sub-menu__title:focus-visible){outline:none;box-shadow:none}.fcrm_settings_sidebar .fcrm-admin-tools-submenu :deep(.el-sub-menu__title){height:36px;line-height:20px;margin:4px 0;padding:8px 10px 8px 20px;border-radius:8px;font-weight:500;font-size:14px;letter-spacing:-.084px;color:var(--fc-secondary-text);display:flex;align-items:center;gap:8px}.fcrm_settings_sidebar .fcrm-admin-tools-submenu :deep(.el-sub-menu__title) .fcrm_menu_icon{flex-shrink:0;display:flex;align-items:center;justify-content:center;width:20px;height:20px;border-radius:6px;overflow:hidden}.fcrm_settings_sidebar .fcrm-admin-tools-submenu :deep(.el-sub-menu__title) .fcrm_menu_title{flex:1;min-width:0}.fcrm_settings_sidebar .fcrm-admin-tools-submenu :deep(.el-sub-menu__title:hover){background:var(--fc-secondary-bg);color:var(--fc-secondary-text)}.fcrm_settings_sidebar .fcrm-admin-tools-submenu.is-parent-active :deep(.el-sub-menu__title){background:var(--fc-secondary-bg);color:var(--fc-primary-text);position:relative}.fcrm_settings_sidebar .fcrm-admin-tools-submenu.is-parent-active :deep(.el-sub-menu__title::before){content:"";position:absolute;left:0;top:50%;transform:translateY(-50%);width:4px;height:20px;background:var(--fc-primary-text);border-radius:4px}.fcrm_settings_sidebar .fcrm-admin-tools-submenu :deep(.el-menu){padding-left:29px;position:relative}.fcrm_settings_sidebar .fcrm-admin-tools-submenu :deep(.el-menu):before{content:"";position:absolute;left:27px;top:0;bottom:0;width:2px;background:var(--fc-primary-border);border-radius:999px}.fcrm_settings_sidebar .fcrm-admin-tools-submenu :deep(.el-menu-item){height:36px;line-height:36px;margin:2px 0 2px 17px;padding:0 8px 0 12px;border-radius:8px;font-weight:500;font-size:14px;position:relative;justify-content:flex-start}.fcrm_settings_sidebar .fcrm-admin-tools-submenu :deep(.el-menu-item).is-active:before{content:"";position:absolute;left:-19px;top:50%;transform:translateY(-50%);width:2px;height:16px;background:var(--fc-primary-text);border-radius:999px;z-index:1}@media (max-width: 768px){.fcrm_settings_sidebar .el-menu-item{justify-content:flex-start}.fcrm_settings_sidebar .el-menu-item .fcrm_menu_icon{margin:0 auto}.fcrm_settings_sidebar .el-menu-item.is-active:before{width:3px;height:16px}.fcrm_settings_sidebar .fcrm-admin-tools-submenu :deep(.el-sub-menu__title){padding:8px;justify-content:center}.fcrm_settings_sidebar .fcrm-admin-tools-submenu :deep(.el-sub-menu__title) .fcrm_menu_icon{margin:0}.fcrm_settings_sidebar .fcrm-admin-tools-submenu.is-parent-active :deep(.el-sub-menu__title::before){width:3px;height:16px}}.fcrm_business_setup .fcrm_form_label{width:300px}.fcrm_business_setup .fcrm_form_label .fcrm_inline_help{margin-top:4px;font-size:12px;line-height:16px;color:var(--fc-secondary-text)}.fcrm_business_setup .fcrm_field--logo{display:flex;align-items:center;gap:12px}.fcrm_business_setup .fcrm_field--logo .fcrm_logo_avatar{background:var(--fc-text-link);color:var(--fc-text-inverse);flex-shrink:0}.fcrm_business_setup .fcrm_field--logo .fcrm_logo_avatar.fcrm_has_logo{background:none}.fcrm_business_setup .fcrm_field--logo .fcrm_logo_avatar img{border-radius:50%}.fcrm_business_setup .fcrm_field--logo-actions{display:flex;align-items:center;gap:8px}.fcrm_business_setup .fcrm_field--logo-actions .el-button{margin:0}.fcrm-pref-shortcode-wrapper{width:100%}.fcrm-pref-shortcode-wrapper *{box-sizing:border-box}.fcrm-pref-shortcode-wrapper .fcrm-pref-shortcode-section .el-form-item{margin-bottom:16px}.fcrm-pref-shortcode-wrapper .fcrm-pref-shortcode-section .el-form-item:last-child{margin-bottom:4px}.fcrm-pref-shortcode-wrapper .fcrm-pref-shortcode-section .el-form-item__label{font-weight:500;font-size:14px;line-height:20px;margin-bottom:12px}.fcrm-pref-shortcode-wrapper .fcrm-pref-shortcode-section .el-form-item__label .tooltip-icon{color:var(--fc-text-muted)}.fcrm_form_group.fcrm_url_input_group .fcrm_smart_url_box .fcrm_smart_url_text{display:none}@media (max-width: 1024px){.fluentcrm-app.is-settings-page .fluentcrm-body{height:calc(100vh - 82px)}.fluentcrm-app.is-settings-page .fluentcrm-body .fcrm_settings_content .fcrm_settings{max-height:calc(100vh - 82px);min-height:calc(100vh - 82px)}.fcrm_settings_sidebar{height:calc(100vh - 82px)}}@media (max-width: 1024px){.fcrm_settings .fcrm_settings_menu_toggle{display:flex}.fcrm_settings .fcrm_settings_content{width:100%;flex:none}.fluentcrm-app.is-settings-page .fcrm_settings_inner,.fluentcrm-app.is-settings-page .fcrm_settings_inner.is-collapsed{padding-left:160px}.fluentcrm-app.is-settings-page .fcrm_settings_content .fcrm_settings{max-height:calc(100vh - 122px);min-height:calc(100vh - 122px)}.folded .fluentcrm-app.is-settings-page .fcrm_settings_inner,.folded .fluentcrm-app.is-settings-page .fcrm_settings_inner.is-collapsed{padding-left:36px}.folded .fcrm_settings_menu_collapsable.for-mobile{padding-left:68px}.folded .fcrm_settings_sidebar{left:-272px}.folded .fcrm_settings_sidebar.is-open{left:36px}.fcrm_settings_sidebar{visibility:hidden;position:fixed;top:auto;bottom:0;left:-272px;right:auto;z-index:9999;height:calc(100vh - 139px);width:100%;max-width:272px;border-radius:0;padding-top:0;opacity:0;transition:.3s;box-shadow:0 0 20px #1c273214;overflow-x:hidden}.fcrm_settings_sidebar .fcrm_settings_menu_collapsable{display:none}.fcrm_settings_sidebar.is-open{overscroll-behavior:contain;visibility:visible;left:160px;opacity:1;width:272px}.fcrm_settings_sidebar.is-open .el-menu-item{padding-top:8px;padding-bottom:8px}.fcrm_settings_sidebar.is-open .el-menu-item .fcrm_menu_title,.fcrm_settings_sidebar.is-open .el-sub-menu .fcrm_menu_title{display:flex}.fcrm_settings_sidebar.is-open .el-sub-menu__title{padding-top:8px;padding-bottom:8px}.auto-fold .fcrm_settings_menu_collapsable.for-mobile,.folded .fcrm_settings_menu_collapsable.for-mobile{padding-left:68px}.fcrm_settings_menu_collapsable.for-mobile{display:flex;justify-content:flex-start;padding-left:186px;height:auto;gap:10px;border-bottom:none}.fcrm_settings_menu_collapsable.for-mobile.is-collapsed .fcrm_settings_menu_collapsable--btn{transform:rotate(0)}.fcrm_settings_menu_collapsable.for-mobile .fcrm_settings_menu_collapsable--title{display:block;font-weight:500;line-height:24px}}@media (max-width: 782px){.toplevel_page_fluentcrm-admin #wpcontent,.auto-fold .fluentcrm-app.is-settings-page .fcrm_settings_inner,.folded .fluentcrm-app.is-settings-page .fcrm_settings_inner,.auto-fold .fluentcrm-app.is-settings-page .fcrm_settings_inner.is-collapsed,.folded .fluentcrm-app.is-settings-page .fcrm_settings_inner.is-collapsed{padding-left:0}.auto-fold .fcrm_topbar,.folded .fcrm_topbar{width:100%}.fcrm_settings .fcrm_body{padding-left:20px;padding-right:20px}.fluentcrm-app.is-settings-page{margin:0;padding:0}.fluentcrm-app.is-settings-page .fluentcrm-body{position:relative;height:auto;margin:0}.fluentcrm-app.is-settings-page .fluentcrm-body .fcrm_settings_content .fcrm_settings{max-height:inherit;min-height:inherit;overflow:auto}.auto-fold .fcrm_settings_menu_collapsable.for-mobile,.folded .fcrm_settings_menu_collapsable.for-mobile,.fcrm_settings_menu_collapsable.for-mobile{padding-left:18px}.fcrm_settings_menu_overlay{height:100vh}.folded .fcrm_settings_sidebar{height:100vh;padding-top:96px;left:-272px}.folded .fcrm_settings_sidebar.is-open,.auto-fold .fcrm_settings_sidebar.is-open{left:0}.fcrm_settings_sidebar{height:100vh;padding-top:100px;left:-272px}.fcrm_settings_sidebar.is-open{left:0}.fcrm_settings .fcrm_settings_topbar{padding:6px 20px;min-height:48px;position:relative;flex-wrap:wrap;gap:6px}.fcrm_settings .fcrm_settings_topbar h3{font-size:16px;line-height:20px}.fcrm_settings .fcrm_settings_topbar .fcrm_settings_topbar--actions{flex-wrap:wrap}}.fcrm_integrations_actions{margin-left:auto;display:flex;gap:5px;align-items:center}.fcrm_integrations_actions .el-button{margin:0}.fcrm_loader_spinner{width:20px;height:20px;border:2px solid var(--fc-primary-border);border-top-color:var(--fc-text-link);border-radius:50%;animation:fcrm_spin .8s linear infinite}@keyframes fcrm_spin{to{transform:rotate(360deg)}}@media (max-width: 767px){.fcrm_settings_menu_collapsable{padding-top:8px;padding-bottom:8px}}.fcrm-settings-row{display:flex;gap:24px;align-items:flex-start;width:100%}.fcrm-settings-row.fcrm-settings-row-child{padding-left:24px}@media (max-width: 767px){.fcrm-settings-row{flex-direction:column;gap:16px}}.fcrm-settings-row .fcrm-settings-field{flex:1;display:flex;flex-direction:column;gap:4px}.fcrm-settings-row .fcrm-settings-field.fcrm-field-small{flex:0 0 200px}@media (max-width: 767px){.fcrm-settings-row .fcrm-settings-field{width:100%}.fcrm-settings-row .fcrm-settings-field.fcrm-field-small{flex:none}}.fcrm_compliance_settings .fcrm-settings-row-child{position:relative}.fcrm_compliance_settings .fcrm-settings-row-child:before{content:"";position:absolute;left:10px;top:0;bottom:0;width:1px;background:var(--fc-primary-border)}.fcrm-settings-fields{display:flex;flex-direction:column;gap:20px;width:100%;margin-top:20px}.fcrm_report_card_body .fcrm-settings-fields:first-child{margin-top:0}.fcrm-settings-label{width:300px;flex-shrink:0;display:flex;flex-direction:column;gap:4px}.fcrm-settings-label.fcrm-settings-label-narrow{width:300px}@media (max-width: 767px){.fcrm-settings-label,.fcrm-settings-label.fcrm-settings-label-narrow{width:100%;max-width:100%}}.fcrm-label-title{font-size:14px;font-weight:500;line-height:20px;letter-spacing:-.084px;color:var(--fc-primary-text)}.fcrm-label-description{font-size:12px;font-weight:400;line-height:16px;color:var(--fc-secondary-text);margin:0}.fcrm_settings .fcrm-settings-field-full{width:100%}.fcrm_settings .fcrm-checkbox-content{display:flex;gap:4px;align-items:center}.fcrm_settings .fcrm-checkbox-label{font-size:14px;font-weight:400;line-height:20px;letter-spacing:-.084px;color:var(--fc-primary-text)}.fcrm_settings .fcrm-footer-info{background:var(--fc-secondary-bg);border-radius:8px;padding:16px 14px}.fcrm_settings .fcrm-footer-info p{font-size:14px;font-weight:400;line-height:20px;color:var(--fc-primary-text);margin:0 0 12px}.fcrm_settings .fcrm-footer-info p:last-child{margin-bottom:0}.fcrm_settings .fcrm-footer-info ul{margin:12px 0;padding-left:21px;list-style-type:disc}.fcrm_settings .fcrm-footer-info ul li{font-size:14px;font-weight:400;line-height:20px;color:var(--fc-primary-text);margin-bottom:0}.fcrm_settings .fcrm-footer-info ul li:last-child{margin-bottom:0}.fcrm_settings .fcrm-footer-info ul li strong{font-weight:500}.fcrm_settings .el-input__prefix .el-icon{color:var(--fc-secondary-text)}.fcrm_settings .el-input-number{width:100%}.fcrm_integration_settings .fcrm_integrations_container{background:var(--fc-primary-bg);border-radius:var(--fcrm-border-radius-8, 8px);overflow:hidden}.fcrm_integration_settings .fcrm_integrations_toolbar{background:var(--fc-primary-bg);border-bottom:1px solid var(--fc-primary-border);padding:12px 20px;display:flex;align-items:center;justify-content:space-between;gap:16px}@media (max-width: 690px){.fcrm_integration_settings .fcrm_integrations_toolbar{flex-direction:column;gap:8px;align-items:flex-start}}.fcrm_integration_settings .fcrm_integrations_filters{background:none;display:flex;gap:4px;align-items:center}.fcrm_integration_settings .fcrm_filter_btn{color:var(--fc-text-muted);font-weight:500;font-size:14px;line-height:20px;padding:6px 12px;border:none;background:none;border-radius:8px;cursor:pointer}.fcrm_integration_settings .fcrm_filter_btn.active{background:var(--fc-secondary-bg);color:var(--fc-primary-text)}.fcrm_integration_settings .fcrm_integrations_search{width:300px}.fcrm_integration_settings .fcrm_integrations_search .el-input__prefix{display:flex;align-items:center;margin-right:6px}.fcrm_integration_settings .fcrm_integrations_search .el-input__prefix .el-icon{font-size:16px;color:var(--fc-text-muted)}.fcrm_integration_settings .fcrm_integrations_search .el-input__inner{font-size:14px;font-weight:400;line-height:20px;letter-spacing:-.084px;color:var(--fc-primary-text);padding:0;height:20px}.fcrm_integration_settings .fcrm_integrations_search .el-input__inner::placeholder{color:var(--fc-text-muted)}@media (max-width: 690px){.fcrm_integration_settings .fcrm_integrations_search{width:100%}}.fcrm_integration_settings .fcrm_integrations_list{background:var(--fc-primary-bg)}.fcrm_integration_settings .fcrm_integration_item{background:var(--fc-primary-bg);border-bottom:1px solid var(--fc-primary-border);padding:16px;display:flex;align-items:center;gap:14px;box-shadow:0 1px 2px #0a0d1408}.fcrm_integration_settings .fcrm_integration_item:last-child{border-bottom:none}.fcrm_integration_settings .fcrm_integration_item.is-installing{opacity:.7;pointer-events:none}@media (max-width: 479px){.fcrm_integration_settings .fcrm_integration_item{flex-direction:column;gap:10px;align-items:flex-start}}.fcrm_integration_settings .fcrm_integration_brand{flex-shrink:0}.fcrm_integration_settings .fcrm_brand_icon{width:40px;height:40px;border:1px solid var(--fc-primary-border);border-radius:999px;padding:8px;display:flex;align-items:center;justify-content:center;background:var(--fc-primary-bg)}.fcrm_integration_settings .fcrm_brand_icon img{width:24px;height:24px;object-fit:contain}.fcrm_integration_settings .fcrm_brand_icon span{font-size:16px;font-weight:600;color:var(--fc-primary-text)}.fcrm_integration_settings .fcrm_integration_info{flex:1;display:flex;flex-direction:column;gap:4px}.fcrm_integration_settings .fcrm_integration_title{font-weight:500;font-size:14px;line-height:20px;color:var(--fc-primary-text);margin:0}.fcrm_integration_settings .fcrm_integration_desc{font-weight:400;font-size:12px;line-height:16px;margin:0;color:var(--fc-secondary-text)}.fcrm_integration_settings .fcrm_integrations_empty{padding:20px;background:var(--fc-primary-bg)}.fcrm_integration_settings .fcrm_integrations_empty .fcrm_empty_intro{font-size:14px;font-weight:400;line-height:20px;color:var(--fc-secondary-text);margin:0 0 24px}.fcrm_integration_settings .fcrm_integrations_empty .fcrm_empty_intro a{color:var(--fc-text-link);text-decoration:underline}.fcrm_integration_settings .fcrm_integrations_empty .fcrm_empty_intro a:hover{color:var(--fc-primary-text)}.fcrm_integration_settings .fcrm_integrations_empty h4{font-size:16px;font-weight:600;line-height:24px;color:var(--fc-primary-text);margin:24px 0 12px}.fcrm_integration_settings .fcrm_integrations_empty h4:first-of-type{margin-top:0}.fcrm_integration_settings .fcrm_integrations_empty ul{list-style-position:outside;list-style-type:disc;padding-left:21px;margin:0 0 16px;display:flex;flex-direction:column;gap:8px}.fcrm_integration_settings .fcrm_integrations_empty ul li{font-size:14px;font-weight:400;line-height:20px;color:var(--fc-secondary-text)}.fcrm_integration_settings .fcrm_integrations_empty ul li b{font-weight:500;color:var(--fc-primary-text)}.fcrm_integration_settings .fcrm_breadcrumb{display:flex;align-items:center;gap:6px}.fcrm_integration_settings .fcrm_breadcrumb_item{font-size:14px;font-weight:500;line-height:20px;letter-spacing:-.084px;color:var(--fc-secondary-text);cursor:pointer}.fcrm_integration_settings .fcrm_breadcrumb_item:hover{color:var(--fc-primary-text)}.fcrm_integration_settings .fcrm_breadcrumb_item.fcrm_breadcrumb_active{color:var(--fc-primary-text);cursor:default}.fcrm_integration_settings .fcrm_breadcrumb_item.fcrm_breadcrumb_active:hover{color:var(--fc-primary-text)}.fcrm_integration_settings .fcrm_breadcrumb_separator{color:var(--fc-secondary-border);font-size:15px}.fcrm_integration_settings .fcrm_breadcrumb_separator svg{display:block}.fcrm_integration_settings .fcrm_integration_detail_card{background:var(--fc-primary-bg);border-radius:var(--fcrm-border-radius-8, 8px);padding:16px}.fcrm_integration_settings .fcrm_integration_config{display:flex;flex-direction:column;gap:20px}.fcrm_integration_settings .fcrm_integration_header{display:flex;flex-direction:column;gap:4px}.fcrm_integration_settings .fcrm_integration_title{font-weight:500;font-size:16px;line-height:24px;color:var(--fc-primary-text);margin:0}.fcrm_integration_settings .fcrm_integration_subtitle{margin:0;font-weight:400;font-size:14px;line-height:20px;color:var(--fc-primary-text);opacity:.72}.fcrm_integration_settings .fcrm_integration_content,.fcrm_integration_settings .fcrm_integration_content .fcrm_integration_form{display:flex;flex-direction:column;gap:16px}.fcrm_integration_settings .fcrm_integration_content .fcrm_integration_form .fcrm_form_item{margin-bottom:0!important}.fcrm_integration_settings .fcrm_integration_content .fcrm_integration_form .fcrm_form_item .el-form-item__label{padding:0;margin-bottom:4px;line-height:normal}.fcrm_integration_settings .fcrm_integration_content .fcrm_integration_form .fcrm_form_item .el-form-item__content{line-height:normal;display:flex;flex-direction:column;gap:4px}.fcrm_integration_settings .fcrm_integration_content .fcrm_integration_form .fcrm_form_item .el-form-item__content .fcrm_form_option_selector{width:100%}.fcrm_integration_settings .fcrm_integration_alert{background:var(--fc-secondary-bg);border-radius:var(--fcrm-border-radius-8, 8px);padding:14px 14px 16px;display:flex;gap:12px;align-items:flex-start}.fcrm_integration_settings .fcrm_alert_content{display:flex;flex-direction:column;gap:4px;flex:1;min-width:0}.fcrm_integration_settings .fcrm_alert_title{font-size:14px;font-weight:500;line-height:20px;letter-spacing:-.084px;color:var(--fc-primary-text);margin:0;width:100%}.fcrm_integration_settings .fcrm_alert_description{font-size:14px;font-weight:400;line-height:20px;letter-spacing:-.084px;color:var(--fc-primary-text);opacity:.72;margin:0;width:100%}.fcrm_integration_settings .fcrm_integration_form{display:flex;flex-direction:column;gap:16px}.fcrm_integration_settings .fcrm_form_item{margin-bottom:0!important}.fcrm_integration_settings .fcrm_form_item .el-form-item__label{padding:0;margin-bottom:4px;line-height:normal}.fcrm_integration_settings .fcrm_form_item .el-form-item__content{line-height:normal;display:flex;flex-direction:column;gap:4px}.fcrm_integration_settings .fcrm_form_item .el-form-item__content>div{width:100%}.fcrm_integration_settings .fcrm_form_label{font-weight:500;font-size:14px;line-height:20px;color:var(--fc-primary-text);display:block;width:100%}.fcrm_integration_settings .fcrm_field_hint{font-size:12px;font-weight:400;line-height:16px;color:var(--fc-secondary-text);margin:0;width:100%}.fcrm_integration_settings .fcrm_cli_info{background:var(--fc-secondary-bg);border:1px solid var(--fc-primary-border);border-radius:8px;padding:7px 10px;box-shadow:0 1px 2px #0a0d1408}.fcrm_integration_settings .fcrm_cli_info p{font-weight:400;font-size:14px;line-height:20px;color:var(--fc-primary-text);margin:0}.fcrm_integration_settings .fcrm_cli_info .fcrm_cli_link{color:var(--fc-deep-bg);font-weight:500;text-decoration:underline}.fcrm_integration_settings .fcrm_cli_info .fcrm_cli_link:hover{text-decoration:underline}.fcrm_integration_settings .fcrm_sync_progress{text-align:center;padding:40px 20px}.fcrm_integration_settings .fcrm_sync_title{font-size:20px;font-weight:600;line-height:28px;color:var(--fc-primary-text);margin:0 0 8px}.fcrm_integration_settings .fcrm_sync_subtitle{font-size:14px;font-weight:400;line-height:20px;color:var(--fc-secondary-text);margin:0 0 24px}.fcrm_integration_settings .fcrm_sync_count{font-size:32px;font-weight:600;line-height:40px;color:var(--fc-primary-text);margin:0 0 16px}.fcrm_integration_settings .fcrm_sync_loading{font-size:14px;font-weight:400;line-height:20px;color:var(--fc-secondary-text);margin:16px 0 0}.fcrm_integration_settings .fcrm_sync_error_title{font-size:20px;font-weight:600;line-height:28px;color:var(--fc-primary-text);margin:0 0 24px}.fcrm_integration_settings .fcrm_sync_error_subtitle{font-size:14px;font-weight:600;line-height:20px;color:var(--fc-primary-text);margin:24px 0 12px}.fcrm_integration_settings .fcrm_sync_error_details{background:var(--fc-secondary-bg);border:1px solid var(--fc-primary-border);border-radius:8px;padding:16px;text-align:left;font-size:12px;line-height:18px;color:var(--fc-primary-text);overflow-x:auto;margin:0}.fcrm_info_top .el-form .el-form-item__label{margin-bottom:0}.fcrm_info_top .el-form-item .el-form-item__content{flex-direction:column-reverse;align-items:flex-start}.fcrm_info_top .el-form-item .el-form-item__content .fcrm_secondary_text{margin-bottom:10px;margin-top:1px;line-height:14px}.fcrm_info_top .el-form-item .el-form-item__content .el-checkbox{margin:0}.fcrm_info_top .el-form-item .el-form-item__content .el-checkbox-group{display:flex;flex-wrap:wrap;column-gap:30px;row-gap:10px}.fcrm_license_management{width:100%}.fcrm_license_management .fcrm_license_header{display:flex;flex-direction:column;gap:4px;align-items:flex-start;margin-bottom:12px}.fcrm_license_management .fcrm_license_main_title{color:var(--fc-primary-text);margin:0;font-weight:500;font-size:16px;line-height:24px}.fcrm_license_management .fcrm_license_main_description{margin:0;color:var(--fc-secondary-text);font-weight:400;font-size:14px;line-height:20px}.fcrm_license_management .fcrm_license_form{display:flex;flex-direction:column;gap:8px;width:100%}.fcrm_license_management .fcrm_license_input_wrapper{display:flex;flex-direction:column;gap:4px;width:100%}.fcrm_license_management .fcrm_license_input_container{background:var(--fc-primary-bg);border:1px solid var(--fc-primary-border);border-radius:10px;display:flex;align-items:stretch;overflow:hidden;width:100%}.fcrm_license_management .fcrm_license_input_field{flex:1;border-right:1px solid var(--fc-primary-border);background:var(--fc-primary-bg)}.fcrm_license_management .fcrm_license_input{width:100%;padding:10px 12px;border:none;outline:none;font-weight:400;font-size:14px;line-height:20px;letter-spacing:-.084px;color:var(--fc-text-muted);background:transparent}.fcrm_license_management .fcrm_license_input::placeholder{color:var(--fc-text-muted)}.fcrm_license_management .fcrm_license_input:focus{color:var(--fc-primary-text)}.fcrm_license_management .fcrm_license_verify_button{padding:10px;display:flex;align-items:center;justify-content:center;gap:4px;flex-shrink:0;cursor:pointer;transition:background-color .2s ease;font-weight:500;font-size:14px;line-height:20px;background:var(--fc-secondary-bg);color:var(--fc-secondary-text)}.fcrm_license_management .fcrm_license_verify_button .icon svg path{fill:var(--fc-secondary-text)}.fcrm_license_management .fcrm_license_verify_button.disabled{background:var(--fc-secondary-bg);color:var(--fc-secondary-border)}.fcrm_license_management .fcrm_license_verify_button.disabled .icon svg path{fill:var(--fc-secondary-border)}.fcrm_license_management .fcrm_license_verify_button.disabled .fcrm_license_verify_text{color:var(--fc-secondary-border)}.fcrm_license_management .fcrm_license_verify_button .fcrm_license_verify_text{font-weight:500;font-size:14px;line-height:20px}.fcrm_license_management .fcrm_license_verify_button .icon svg{display:block}.fcrm_license_management .fcrm_license_verify_button:hover{background:var(--fc-light-bg)}.fcrm_license_management .fcrm_license_purchase_text{margin:0;font-weight:400;font-size:14px;line-height:20px;color:var(--fc-primary-text)}.fcrm_license_management .fcrm_license_purchase_text a{color:var(--fc-deep-bg);text-decoration:underline;font-weight:500}.fcrm_license_management .fcrm_license_card{max-width:400px;margin:0 auto;display:flex;flex-direction:column;align-items:center;justify-content:center}.fcrm_license_management .fcrm_license_card--header{display:flex;flex-direction:column;align-items:center;justify-content:center;margin-bottom:20px;text-align:center}.fcrm_license_management .fcrm_license_card--header-icon{display:block;margin-bottom:16px}.fcrm_license_management .fcrm_license_card--header-icon svg{display:block}.fcrm_license_management .fcrm_license_card--header-title{font-weight:500;font-size:16px;line-height:24px;color:var(--fc-primary-text);margin:0 0 4px;letter-spacing:-.1px}.fcrm_license_management .fcrm_license_card--header-subtitle{color:var(--fc-secondary-text);font-weight:400;font-size:12px;line-height:16px;margin:0}.fcrm_license_management .fcrm_license_card--header-subtitle a{color:var(--fc-primary-text);font-weight:500;text-decoration:underline}.fcrm_license_management .fcrm_license_card--body{width:100%;border:1px solid var(--fc-primary-border);border-radius:8px}.fcrm_license_management .fcrm_license_card--body-item{padding:12px 16px;border-bottom:1px solid var(--fc-primary-border);display:flex;align-items:center;justify-content:space-between}.fcrm_license_management .fcrm_license_card--body-item:last-child{border-bottom:none}.fcrm_license_management .fcrm_license_card--body-item-label{font-weight:500;font-size:12px;line-height:16px;color:var(--fc-primary-text)}.fcrm_license_management .fcrm_license_card--body-item-value{color:var(--fc-secondary-text);font-weight:400;font-size:12px;line-height:16px}.fcrm_header .fcrm_header_title h3 .list-name{font-weight:400;color:var(--fc-secondary-text)}.fcrm-info-alert{background:var(--fc-secondary-bg);color:var(--fc-primary-text);border-radius:8px;padding:8px;display:flex;gap:8px;align-items:center;width:100%;margin-top:4px}.fcrm-info-alert .icon,.fcrm-info-alert .el-icon,.fcrm-info-alert .fcrm-info-icon{color:var(--fc-text-muted);font-size:16px;flex-shrink:0}.fcrm-info-alert p{margin:0;font-weight:400;font-size:12px;line-height:16px;color:var(--fc-primary-text);flex:1}.el-form-item:has(.fcrm-mapper-container) .el-form-item__label div{background:var(--fc-secondary-bg);border-radius:8px;padding:8px;display:flex;gap:8px;align-items:center;width:100%;margin-bottom:12px;font-weight:400;font-size:12px;line-height:16px;color:var(--fc-primary-text)}.el-form-item:has(.fcrm-mapper-container) .el-form-item__label div .tooltip-icon{color:var(--fc-text-muted);font-size:16px;order:-1}.el-form-item:has(.fcrm-mapper-container) .el-form-item__label div .fcrm-with-label-text{display:flex;align-items:center;gap:8px}.fcrm-mapper-container{display:flex;flex-direction:column;gap:12px;width:100%;align-items:flex-start}.fcrm-mapper-container .fcrm_horizontal_table{border:1px solid var(--fc-primary-border)}.fcrm_horizontal_table{width:100%;border-radius:8px;border-collapse:separate;border-spacing:0;overflow:hidden;background:var(--fc-primary-bg)}.fcrm_horizontal_table thead tr th{background:var(--fc-secondary-bg);border-bottom:1px solid var(--fc-primary-border);padding:8px 12px;text-align:left;font-weight:500;font-size:14px;line-height:20px;color:var(--fc-secondary-text);letter-spacing:-.084px;vertical-align:middle}.fcrm_horizontal_table thead tr th:first-child{padding-left:20px;padding-right:12px}.fcrm_horizontal_table thead tr th:nth-child(2){padding-left:12px;padding-right:16px}.fcrm_horizontal_table thead tr th:last-child{background:var(--fc-secondary-bg);border-bottom:1px solid var(--fc-primary-border);width:40px;padding:12px;text-align:right}.fcrm_horizontal_table tbody tr td{background:var(--fc-primary-bg);padding:12px;vertical-align:middle;font-size:14px;font-weight:400;line-height:20px;letter-spacing:-.084px;color:var(--fc-primary-text)}.fcrm_horizontal_table tbody tr td:first-child{padding-left:20px;padding-right:12px}.fcrm_horizontal_table tbody tr td:nth-child(2){padding-left:12px;padding-right:16px}.fcrm_horizontal_table tbody tr td:last-child{padding:12px;text-align:right}.fcrm_horizontal_table tbody tr:last-child td{border-bottom:none}.fcrm_horizontal_table .text-align-right{text-align:right;display:flex;justify-content:flex-end;gap:8px;align-items:center}.fcrm_horizontal_table .el-button--danger{background:transparent;border:none;padding:2px;border-radius:6px;color:var(--fc-secondary-text);min-width:auto;width:24px;height:24px;display:inline-flex;align-items:center;justify-content:center;transition:all .2s ease}.fcrm_horizontal_table .el-button--danger:hover{background:var(--fc-error-bg);color:var(--fc-error)}.fcrm_horizontal_table .el-button--danger:focus{background:transparent;color:var(--fc-secondary-text);outline:none;box-shadow:none}.fcrm_horizontal_table .el-button--danger .el-icon{font-size:20px;line-height:1}.fcrm_horizontal_table .el-button--danger span{display:flex;align-items:center;justify-content:center}.fcrm_horizontal_table .el-button-group{display:none}.fcrm_integration_settings.fcrm-widget-section{background:var(--fc-primary-bg);border-radius:8px;margin-bottom:24px;overflow:hidden;border:1px solid var(--fc-primary-border);box-shadow:0 1px 3px #0000000d}.fcrm_integration_settings.fcrm-widget-section .fcrm-widget-header{background:var(--fc-primary-bg);border-bottom:1px solid var(--fc-primary-border);padding:16px 20px;display:flex;align-items:center;gap:16px}.fcrm_integration_settings.fcrm-widget-section .fcrm-widget-header h3{margin:0;font-weight:500;font-size:16px;line-height:24px;color:var(--fc-primary-text)}.fcrm_integration_settings.fcrm-widget-section .fcrm-widget-body{background:var(--fc-primary-bg);padding:20px}.fcrm-template-selector{width:100%}.fcrm-template-selector .fcrm-template-grid{display:flex;gap:16px;flex-wrap:wrap}.fcrm-template-selector .fcrm-template-card{width:115px;display:flex;flex-direction:column;gap:8px;cursor:pointer;position:relative;transition:all .2s ease}.fcrm-template-selector .fcrm-template-card:hover .fcrm-template-preview,.fcrm-template-selector .fcrm-template-card.fcrm-template-selected .fcrm-template-preview{outline:1px solid var(--fc-primary-text);outline-offset:-1px}.fcrm-template-selector .fcrm-template-preview{width:100%;outline:1px solid var(--fc-primary-border);outline-offset:-1px;border-radius:8px;overflow:hidden;background:var(--fc-primary-bg);transition:.2s}.fcrm-template-selector .fcrm-template-preview img{width:100%;height:auto;display:block}.fcrm-template-selector .fcrm-template-details{padding:0 4px}.fcrm-template-selector .fcrm-template-details .fcrm-template-title{font-weight:500;font-size:14px;line-height:20px;color:var(--fc-primary-text);letter-spacing:-.084px;margin-bottom:4px}.fcrm-template-selector .fcrm-template-details .fcrm-template-description{font-weight:400;font-size:12px;line-height:16px;color:var(--fc-secondary-text)}.fcrm-template-selector .fcrm-template-checkmark{position:absolute;top:6px;right:6px;width:15px;height:15px;background:var(--fc-primary-text);border-radius:50%;display:flex;align-items:center;justify-content:center}.fcrm-template-selector .fcrm-template-checkmark .el-icon{color:var(--fc-text-inverse);font-size:10px}.fcrm-input-popover{width:100%}.fcrm-input-popover .fcrm-input-with-button{display:flex;width:100%;border:1px solid var(--fc-primary-border);border-radius:8px;overflow:hidden;transition:border-color .2s ease}.fcrm-input-popover .fcrm-input-with-button:focus-within{border-color:var(--fc-primary-text)}.fcrm-input-popover .fcrm-input-with-button .fcrm-input-main{flex:1}.fcrm-input-popover .fcrm-input-with-button .fcrm-input-main .el-input__wrapper{border:none}.fcrm-input-popover .fcrm-input-with-button .fcrm-input-main .el-input__wrapper:hover,.fcrm-input-popover .fcrm-input-with-button .fcrm-input-main .el-input__wrapper:focus,.fcrm-input-popover .fcrm-input-with-button .fcrm-input-main .el-input__wrapper:focus-within{border:none;box-shadow:none}.fcrm-input-popover .fcrm-input-with-button .fcrm-input-main .el-input__inner{font-size:14px;line-height:20px;color:var(--fc-primary-text);letter-spacing:-.084px}.fcrm-input-popover .fcrm-input-with-button .fcrm-input-button{background:var(--fc-secondary-bg);border:none;border-left:1px solid var(--fc-primary-border);padding:8px;cursor:pointer;display:flex;align-items:center;justify-content:center;transition:all .2s ease;flex-shrink:0}.fcrm-input-popover .fcrm-input-with-button .fcrm-input-button svg{width:20px;height:20px;color:var(--fc-secondary-text)}.fcrm-input-popover .fcrm-input-with-button .fcrm-input-button:hover{background:var(--fc-secondary-bg)}.fcrm-input-popover .fcrm-input-with-button .fcrm-input-button:hover svg{color:var(--fc-primary-text)}.fcrm-input-popover .fcrm-input-with-button .fcrm-input-button:active{background:var(--fc-primary-border)}.el-popper.fcrm-double-optin-settings-tooltip{max-width:400px}.fcrm_double_optin_settings_wrapper .fcrm_settings-section{margin-bottom:30px}.fcrm_double_optin_settings_wrapper .fcrm_double_optin_settings_header--title{display:flex;font-weight:500;font-size:18px;line-height:24px;flex-wrap:wrap;gap:4px}.fcrm_double_optin_settings_wrapper .fcrm_double_optin_settings--optin-settings-switcher{margin-bottom:20px}.fcrm_double_optin_settings_wrapper .fcrm_double_optin_settings--optin-settings-switcher--title{display:flex;align-items:center;gap:4px;font-weight:500;font-size:14px;line-height:20px;color:var(--fc-primary-text);margin-bottom:12px}.fcrm_double_optin_settings_wrapper .fcrm_double_optin_settings--optin-settings-switcher--title .el-icon{color:var(--fc-secondary-border);font-size:12px}.fcrm_double_optin_settings_wrapper .fcrm_double_optin_settings--optin-settings-switcher .el-radio-group{display:flex;flex-direction:column;align-items:flex-start;gap:8px;margin:8px 0 0}.fcrm_double_optin_settings_wrapper .fcrm_double_optin_settings_body .el-form-item__label .el-icon{color:var(--fc-secondary-border);font-size:12px}.fcrm_double_optin_settings_wrapper .fcrm_double_optin_settings_body .el-form-item .fc_inline_help{margin:4px 0 0}.fcrm_double_optin_settings_wrapper .fcrm_double_optin_settings_body .el-form .fcrm-radio-group{flex-direction:column;align-items:flex-start;gap:8px}.fcrm_double_optin_settings_wrapper .fcrm_double_optin_settings_body .el-form .fcrm_html{width:100%}.fcrm_double_optin_settings_wrapper .fcrm_double_optin_settings_body .el-form .fcrm_html--title{margin:0;font-weight:500;font-size:16px;line-height:20px;color:var(--fc-primary-text)}.fcrm_double_optin_settings_wrapper .fcrm_double_optin_settings_body .el-form .fcrm_html--info{color:var(--fc-secondary-text);font-weight:400;font-size:14px;line-height:20px;margin:0}.fcrm_double_optin_settings_wrapper .fcrm_double_optin_settings_body .fcrm-template-selector{gap:16px;border-bottom:1px solid var(--fc-primary-border);width:100%;padding-bottom:20px}.fcrm_double_optin_settings_wrapper .fcrm_double_optin_settings_body .fcrm_global_form_builder{border-top:1px solid var(--fc-primary-border);padding-top:20px}.fcrm_double_optin_settings_wrapper .fcrm_double_optin_settings_footer{border-top:1px solid var(--fc-primary-border);display:flex;align-items:center;gap:8px;justify-content:flex-end;padding:20px}.el-overlay.fcrm_double_optin_settings_drawer .el-drawer__body{padding:0}.el-overlay.fcrm_double_optin_settings_drawer .fcrm_double_optin_settings_header{border-bottom:1px solid var(--fc-primary-border);padding:16px 20px}.el-overlay.fcrm_double_optin_settings_drawer .fcrm_double_optin_settings_body{padding:20px}.fcrm-widget-section .fcrm-widget-body .fcrm_global_form_builder .el-form .el-form-item .el-form-item__content .fcrm-radio-group{align-items:flex-start}.fcrm_smtp_email_setup .fcrm_settings{width:100%}.fcrm_smtp_email_setup .fcrm_min_bg{background:var(--fc-secondary-bg);min-height:100vh}.fcrm_smtp_email_setup .fcrm_view{display:flex;flex-direction:column}.fcrm_smtp_email_setup .fcrm_pad_around{padding:0 90px}.fcrm_smtp_email_setup .fcrm_card_content{display:flex;gap:24px;align-items:center;width:100%;justify-content:center}.fcrm_smtp_email_setup .fcrm_illustration_section{width:364px;height:224px;flex-shrink:0;position:relative;display:flex;align-items:center;justify-content:center}.fcrm_smtp_email_setup .fcrm_dashboard_illustration{width:100%;height:auto;max-width:364px;max-height:224px;object-fit:contain;border-radius:var(--fcrm-border-radius-8, 8px)}.fcrm_smtp_email_setup .fcrm_plugin_section{flex:1;display:flex;flex-direction:column;align-items:flex-start}.fcrm_smtp_email_setup .fcrm_badge{margin-bottom:16px}.fcrm_smtp_email_setup .fcrm_plugin_description{flex:1;display:flex;align-items:center;margin-bottom:16px}.fcrm_smtp_email_setup .fcrm_text_content{display:flex;flex-direction:column;gap:12px}.fcrm_smtp_email_setup .fcrm_plugin_title{font-weight:500;font-size:18px;line-height:24px;color:var(--fc-primary-text);letter-spacing:-.27px;margin:0}.fcrm_smtp_email_setup .fcrm_plugin_subtitle{font-weight:400;font-size:12px;line-height:16px;color:var(--fc-secondary-text);margin:0}.fcrm_smtp_email_setup .fcrm_learn_more_link{color:var(--fc-primary-text);text-decoration:underline;text-underline-position:from-font}.fcrm_smtp_email_setup .fcrm_install_section{width:100%}.fcrm_smtp_email_setup .fcrm_spinner{animation:fcrm_smtp_spin 1s linear infinite}@keyframes fcrm_smtp_spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}.fcrm_smtp_email_setup .fcrm_content_divider{margin-top:24px;width:100%}.fcrm_smtp_email_setup .fcrm_bottom_section{display:flex;gap:16px;margin-top:24px;width:100%}.fcrm_smtp_email_setup .fcrm_bottom_left,.fcrm_smtp_email_setup .fcrm_bottom_right{flex:1;display:flex;flex-direction:column;gap:16px}.fcrm_smtp_email_setup .fcrm_bottom_right{width:400px;flex-shrink:0}.fcrm_smtp_email_setup .fcrm_bottom_title{font-weight:500;font-size:16px;line-height:24px;color:var(--fc-primary-text);margin:0}.fcrm_smtp_email_setup .fcrm_providers_list,.fcrm_smtp_email_setup .fcrm_features_list{display:flex;flex-direction:column;gap:16px}.fcrm_smtp_email_setup .fcrm_provider_item,.fcrm_smtp_email_setup .fcrm_feature_item{display:flex;align-items:center;gap:8px}.fcrm_smtp_email_setup .fcrm_provider_item .fcrm_check_icon,.fcrm_smtp_email_setup .fcrm_feature_item .fcrm_check_icon{width:20px;height:20px;border-radius:50%;display:flex;align-items:center;justify-content:center;background:var(--fc-success-bg);color:var(--fc-primary-text)}.fcrm_smtp_email_setup .fcrm_check_icon{width:20px;height:20px;color:var(--fc-success);flex-shrink:0}.fcrm_smtp_email_setup .fcrm_provider_text,.fcrm_smtp_email_setup .fcrm_feature_text{font-weight:400;font-size:14px;line-height:20px;margin:0;color:var(--fc-primary-text)}.fcrm_smtp_email_setup .fcrm_bounce_form{display:flex;flex-direction:column;gap:20px;width:100%}.fcrm_smtp_email_setup .fcrm_form_group{display:flex;flex-direction:column;gap:4px;width:100%;max-width:300px}.fcrm_smtp_email_setup .fcrm_form_group.fcrm_url_input_group{max-width:100%}.fcrm_smtp_email_setup .fcrm_form_group.fcrm_url_input_group .fcrm_smart_url_box .fcrm_smart_url_text{display:none}.fcrm_smtp_email_setup .fcrm_form_label{font-weight:500;font-size:14px;line-height:20px;letter-spacing:-.084px;color:var(--fc-primary-text);margin:0}.fcrm_smtp_email_setup .fcrm_dropdown{background:var(--fc-primary-bg);border:1px solid var(--fc-primary-border);border-radius:8px;padding:8px 32px 8px 10px;width:100%;font-weight:400;font-size:14px;line-height:20px;letter-spacing:-.084px;color:var(--fc-primary-text);-webkit-appearance:none;-moz-appearance:none;appearance:none;cursor:pointer;max-width:100%}.fcrm_smtp_email_setup .fcrm_dropdown:focus{outline:none;border-color:var(--fc-deep-bg);box-shadow:0 0 0 3px #6366f11a}.fcrm_smtp_email_setup .fcrm_dropdown_arrow{position:absolute;right:8px;top:50%;transform:translateY(-50%);width:20px;height:20px;color:var(--fc-primary-text);pointer-events:none}.fcrm_smtp_email_setup .fcrm_bounce_handler_section{display:flex;flex-direction:column;gap:12px;width:100%}.fcrm_smtp_email_setup .fcrm_handler_info{display:flex;flex-direction:column;gap:4px}.fcrm_smtp_email_setup .fcrm_handler_title{font-weight:500;font-size:14px;line-height:20px;color:var(--fc-primary-text);margin:0}.fcrm_smtp_email_setup .fcrm_handler_description{font-weight:400;font-size:14px;line-height:20px;color:var(--fc-secondary-text);margin:0}.fcrm_smtp_email_setup .fcrm_url_input_container{position:relative;width:100%;background:var(--fc-secondary-bg);border:1px solid var(--fc-primary-border);border-radius:8px;display:flex;align-items:center;padding:2px 10px;gap:8px;transition:border-color .2s ease}.fcrm_smtp_email_setup .fcrm_url_input_container:focus-within{border-color:var(--fc-deep-bg);box-shadow:0 0 0 4px #7c3aed1a}.fcrm_smtp_email_setup .fcrm_url_input{flex:1;background:transparent;border:none;outline:none;font-weight:400;font-size:14px;line-height:20px;letter-spacing:-.084px;color:var(--fc-primary-text);padding:0;min-width:0}.fcrm_smtp_email_setup .fcrm_copy_button{background:transparent;border:none;cursor:pointer;padding:0;width:20px;height:20px;color:var(--fc-text-muted);display:flex;align-items:center;justify-content:center;flex-shrink:0;border-radius:4px;transition:all .2s ease}.fcrm_smtp_email_setup .fcrm_copy_button:hover{color:var(--fc-primary-text);background:var(--fc-secondary-bg)}.fcrm_smtp_email_setup .fcrm_copy_button i{font-size:16px}.fcrm_smtp_email_setup .fcrm_hint_text{font-weight:400;font-size:12px;line-height:16px;color:var(--fc-secondary-text);margin:4px 0 0}.fcrm_smtp_email_setup .fcrm_header_title{flex:1;display:flex;align-items:center;gap:12px}.fcrm_smtp_email_setup .fcrm_header_title h3{flex:1;font-size:18px;font-weight:500;line-height:24px;letter-spacing:-.27px;color:var(--fc-primary-text);margin:0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.fcrm_smtp_email_setup .fcrm_just_installed_title{font-size:20px;font-weight:600;line-height:28px;color:var(--fc-primary-text);margin:0 0 4px;text-align:center}.fcrm_smtp_email_setup .fcrm_just_installed_subtitle{font-size:14px;font-weight:400;line-height:20px;color:var(--fc-text-muted);margin:0 auto 16px;text-align:center;max-width:500px}.fcrm_smtp_email_setup .fcrm_just_installed_actions{display:flex;justify-content:center}.fcrm_smtp_email_setup .fcrm_unconfigured_section{display:flex;flex-direction:column;align-items:center;gap:8px}.fcrm_smtp_email_setup .fcrm_success_icon,.fcrm_smtp_email_setup .fcrm_warning_icon{margin-bottom:8px}.fcrm_smtp_email_setup .fcrm_success_title,.fcrm_smtp_email_setup .fcrm_unconfigured_title{font-size:20px;font-weight:600;line-height:28px;color:var(--fc-primary-text);margin:0;text-align:center}.fcrm_smtp_email_setup .fcrm_unconfigured_subtitle{font-size:14px;font-weight:400;line-height:20px;color:var(--fc-secondary-text);margin:0;text-align:center}.fcrm_smtp_email_setup .fcrm_configured_card{background:var(--fc-primary-bg);border-radius:8px;padding:0}.fcrm_smtp_email_setup .fcrm_verified_senders_section{margin-bottom:24px}.fcrm_smtp_email_setup .fcrm_verified_title{font-size:16px;font-weight:600;line-height:20px;color:var(--fc-secondary-text);margin:0 0 12px;text-align:left}.fcrm_smtp_email_setup .fcrm_verified_list{list-style:none;padding:0;margin:0}.fcrm_smtp_email_setup .fcrm_verified_item{display:flex;align-items:center;padding:4px 0;font-size:14px;line-height:20px;color:var(--fc-secondary-text)}.fcrm_smtp_email_setup .fcrm_verified_bullet{color:var(--fc-primary-text);margin-right:8px;font-weight:700;font-size:20px}.fcrm_smtp_email_setup .fcrm_verified_email{color:var(--fc-secondary-text);font-size:16px}.fcrm_smtp_email_setup .fcrm_configured_actions{display:flex;justify-content:flex-start}.fcrm_smtp_email_setup .fcrm_verified_senders{width:100%;max-width:400px;margin-bottom:24px}@media (max-width: 768px){.fcrm_smtp_email_setup .fcrm_pad_around{padding:10px 16px}.fcrm_smtp_email_setup .fcrm_card_content{flex-direction:column;gap:20px}.fcrm_smtp_email_setup .fcrm_illustration_section{width:100%;max-width:364px;margin:0 auto}.fcrm_smtp_email_setup .fcrm_form_group{max-width:100%}.fcrm_smtp_email_setup .fcrm_bottom_section{flex-direction:column;gap:24px}.fcrm_smtp_email_setup .fcrm_bottom_right{width:100%}.fcrm_smtp_email_setup .fcrm_provider_text,.fcrm_smtp_email_setup .fcrm_feature_text{white-space:normal}.fcrm_smtp_email_setup .fcrm_success_title,.fcrm_smtp_email_setup .fcrm_unconfigured_title{font-size:18px;line-height:24px}.fcrm_smtp_email_setup .fcrm_verified_senders{max-width:100%}}@media (max-width: 480px){.fcrm_smtp_email_setup .fcrm_illustration_section{height:180px}.fcrm_smtp_email_setup .fcrm_gradient_bg{padding:12px}.fcrm_smtp_email_setup .fcrm_feature_item{gap:8px}.fcrm_smtp_email_setup .fcrm_feature_text{font-size:13px;line-height:18px}.fcrm_smtp_email_setup .fcrm_bottom_section{margin-top:16px}.fcrm_smtp_email_setup .fcrm_bottom_title{font-size:15px;line-height:22px}.fcrm_smtp_email_setup .fcrm_provider_text,.fcrm_smtp_email_setup .fcrm_feature_text{font-size:13px;line-height:18px}.fcrm_smtp_email_setup .fcrm_check_icon{width:18px;height:18px}}.settings-general td.el-table__expanded-cell{padding-left:58px}.fluentcrm_settings_wrapper .el-menu .el-menu-item{height:52px;display:flex;align-items:center;gap:8px}.fluentcrm_settings_wrapper .el-menu .el-menu-item svg{margin:0;fill:var(--fc-text-muted)}.fluentcrm_settings_wrapper .el-menu .el-menu-item.is-active svg{fill:var(--el-menu-active-color)}.fcrm_body_boxed{background:var(--fc-primary-bg);padding:20px;border-radius:8px;margin-bottom:24px}.fcrm_sms_message_cell{max-width:100%;word-break:break-word;line-height:1.4}.fcrm_sms_message_cell>span:not(.fcrm_sms_message_cell_full){display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}.fcrm_sms_message_cell .fcrm_sms_message_cell_full{display:block;-webkit-line-clamp:unset;overflow:visible}.fcrm_sms_message_cell .fcrm_sms_message_toggle{margin-top:2px;display:block;opacity:0;pointer-events:none;transition:opacity .15s ease}.fcrm_sms_message_cell .fcrm_sms_message_toggle .el-button{font-size:12px}.fcrm_sms_message_cell:hover .fcrm_sms_message_toggle,.fcrm_sms_message_cell:has(.fcrm_sms_message_cell_full) .fcrm_sms_message_toggle{opacity:1;pointer-events:auto}.fcrm_input_color_picker{display:flex;align-items:center;padding:4px 10px 4px 4px;width:100%;background:var(--fc-secondary-bg);border-radius:8px;gap:8px}.fcrm_input_color_picker .el-color-picker{width:24px;height:24px}.fcrm_input_color_picker .el-color-picker__trigger{border:1px solid var(--fc-primary-border);box-shadow:none;border-radius:6px;padding:0;background:var(--fc-primary-bg)}.fcrm_input_color_picker .el-color-picker__color{border:none;box-shadow:none}.fcrm_input_color_picker .el-color-picker__color-inner{border-radius:6px}.fcrm_input_color_picker .el-color-picker__color-inner .el-icon{display:none}.fcrm_input_color_picker .clean-icon{margin-left:auto;cursor:pointer;line-height:1}.fcrm_input_color_code{margin:0;padding:0;color:var(--fc-secondary-text);font-weight:400;font-size:12px;line-height:16px;display:block}.fcrm_sms_labels .el-tag{color:var(--fc-primary-text);border:none}.fcrm_sms_labels .el-tag__content{display:flex;line-height:10px}.fcrm_sms_labels .el-tag--small{padding:2px 7px}.fcrm_sms_labels .el-tag .el-icon{color:var(--fc-primary-text);cursor:pointer}.fcrm_sms_labels .el-tag .el-icon:hover{background:var(--fc-primary-text);color:var(--fc-text-inverse)}.fcrm_quick_stats{display:flex;align-items:center;gap:20px;margin:0}.fcrm_quick_stats_label{color:var(--fc-primary-text);font-weight:500;font-size:14px;line-height:20px;margin:0 0 8px}.fcrm_quick_stats .is-link{cursor:pointer}.fcrm_quick_stats li{margin:0;display:flex;align-items:center;gap:4px;position:relative}.fcrm_quick_stats li:last-child:after{display:none}.fcrm_quick_stats li:after{content:"";position:absolute;top:50%;right:-12px;transform:translateY(-50%);width:3px;height:3px;background:var(--fc-text-muted);border-radius:50%}.fcrm_quick_stats li .icon{color:var(--fc-text-muted)}.fcrm_quick_stats li .icon svg{display:block}.fcrm_quick_stats li p{margin:0;color:var(--fc-secondary-text);font-weight:400;font-size:14px;line-height:20px}.fcrm_modal_wrapper .el-drawer__header{background:none;border-bottom:1px solid var(--fc-primary-border);padding:15px 20px!important}.fcrm_modal_wrapper .el-drawer__title{color:var(--fc-primary-text);font-weight:500;font-size:18px;line-height:24px;margin:0}.fcrm_modal_wrapper .el-drawer__footer{border-top:1px solid var(--fc-primary-border);padding:15px 20px}.el-overlay.fcrm_import_dialog .el-dialog{border-radius:8px}.el-overlay.fcrm_import_dialog .el-dialog__header{background:none;border-bottom:1px solid var(--fc-primary-border);padding:16px 20px;position:relative}.el-overlay.fcrm_import_dialog .el-dialog__title{color:var(--fc-primary-text);font-weight:500;font-size:16px;line-height:24px;margin:0}.el-overlay.fcrm_import_dialog .el-dialog__headerbtn{height:100%}.el-overlay.fcrm_import_dialog .el-dialog__body{padding:20px}.el-overlay.fcrm_import_dialog .el-dialog__body .fcrm_import_content h3{margin:0 0 4px;font-weight:500;font-size:14px;line-height:20px;color:var(--fc-primary-text)}.el-overlay.fcrm_import_dialog .el-dialog__body .fcrm_import_content .el-upload{margin-bottom:8px}.el-overlay.fcrm_import_dialog .el-dialog__body .fcrm_import_content .el-upload-dragger{border:1px dashed var(--fc-secondary-border);border-radius:var(--fcrm-border-radius-8, 8px);display:flex;flex-direction:column;align-items:center;justify-content:center;padding:32px}.el-overlay.fcrm_import_dialog .el-dialog__body .fcrm_import_content .el-upload__text{font-weight:500;font-size:14px;line-height:20px;color:var(--fc-primary-text);margin:0 0 20px}.el-overlay.fcrm_import_dialog .el-dialog__body .fcrm_import_content .el-upload .upload-icon{margin-bottom:20px}.el-overlay.fcrm_import_dialog .el-dialog__body .fcrm_import_content .el-upload .upload-icon svg{display:block}.el-overlay.fcrm_import_dialog .el-dialog__body .fcrm_import_content .el-alert{background:var(--fc-secondary-bg);border-radius:8px;padding:8px}.el-overlay.fcrm_import_dialog .el-dialog__body .fcrm_import_content .el-alert--error{margin-top:8px}.el-overlay.fcrm_import_dialog .el-dialog__body .fcrm_import_content .el-alert--error .el-alert__title{color:var(--fc-error)}.el-overlay.fcrm_import_dialog .el-dialog__body .fcrm_import_content .el-alert .el-icon{font-size:14px}.el-overlay.fcrm_import_dialog .el-dialog__body .fcrm_import_content .el-alert__title{color:var(--fc-primary-text);font-weight:400;font-size:12px;line-height:16px}.fcrm_email_recurring_campaigns_page .fcrm_table_body{display:flex;flex-direction:column}.fcrm_view_sequence_emails_wrapper{position:relative}.fcrm_view_sequence_list{background:var(--fc-primary-bg);border-radius:8px}.fcrm_view_sequence_email_stat{display:flex;align-items:center;gap:4px}.fcrm_view_sequence_email_stat_divider{width:3px;height:3px;border-radius:50%;display:block;flex:none;background:var(--fc-text-muted)}.fcrm_view_sequence_email_stat_label{color:var(--fc-secondary-text);display:block}.fcrm_view_sequence_email_stat_value{color:var(--fc-secondary-text)}.fcrm_view_sequence_email_stat_icon svg{display:block}.fcrm_view_sequence_email_stat--clicked,.fcrm_view_sequence_email_stat--sent{cursor:pointer}.fcrm_view_sequence_email_item{border-bottom:1px solid var(--fc-primary-border);padding:16px 20px;position:relative}.fcrm_view_sequence_email_item_stats{display:flex;align-items:center;gap:6px}.fcrm_view_sequence_email_item_header{display:flex;align-items:center;gap:8px;margin:0 0 4px}.fcrm_view_sequence_email_item_title{color:var(--fc-primary-text);font-weight:500;font-size:16px;line-height:24px;margin:0}.fcrm_view_sequence_email_item_title a{color:var(--fc-primary-text);display:block}.fcrm_view_sequence_email_item_title:hover a{text-decoration:underline}.fcrm_view_sequence_email_item_number{width:20px;height:20px;background:var(--fc-secondary-bg);border-radius:6px;color:var(--fc-text-muted);font-weight:500;font-size:12px;line-height:16px;display:flex;align-items:center;justify-content:center;flex:none}.fcrm_view_sequence_email_item_timing{color:var(--fc-secondary-text);font-weight:400;font-size:14px;line-height:20px;margin:0 0 12px;display:block}.fcrm_view_sequence_email_item_menu{position:absolute;top:16px;right:20px}.fcrm_view_sequence_email_item_menu .el-dropdown-link{width:32px;height:32px;display:flex;align-items:center;justify-content:center;transform:rotate(90deg);cursor:pointer}.fcrm_view_sequence_page .fcrm_page_header_actions .settings_btn{padding:10px}.fcrm_view_sequence_subscribers_page .fcrm_page_header{padding-top:5px}.fluentcrm_sequence_sub_adder{padding:20px;background:var(--fc-primary-bg);border-radius:var(--fcrm-border-radius-8)}.fcrm_loading_wrapper{background:var(--fc-primary-bg);border-radius:8px}.fcrm_hero_box{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:12px}.fcrm_hero_box .icon{display:block;width:120px}.fcrm_hero_box .icon svg{display:block}.fcrm_hero_box h2{color:var(--fc-text-muted);font-weight:400;font-size:14px;line-height:20px;margin:0}.fcrm_hero_box_wrapper{background:var(--fc-primary-bg);border-radius:8px;padding:20px}.fcrm_built_in_templates .el-skeleton{display:grid;grid-template-columns:repeat(auto-fill,minmax(200px,1fr));gap:10px;padding:0 15px;width:100%;box-sizing:border-box}.fcrm_built_in_templates .fcrm_template_create_from_scratch{max-width:400px;width:100%;margin:auto;border:2px dashed var(--fc-secondary-border);border-radius:var(--fcrm-border-radius-8, 8px);padding:32px;display:flex;flex-direction:column;align-items:center;justify-content:center;text-align:center;cursor:pointer}.fcrm_built_in_templates .fcrm_template_create_from_scratch_icon{width:40px;height:40px;display:flex;align-items:center;justify-content:center;border-radius:50%;background:var(--fc-secondary-bg);margin-bottom:12px}.fcrm_built_in_templates .fcrm_template_create_from_scratch_icon svg{display:block}.fcrm_built_in_templates .fcrm_template_create_from_scratch_title{margin:0 0 4px;color:var(--fc-primary-text);font-weight:500;font-size:14px;line-height:20px}.fcrm_built_in_templates .fcrm_template_create_from_scratch_description{color:var(--fc-secondary-text);font-weight:400;font-size:12px;line-height:16px;margin:0}.fcrm_built_in_templates_wrap{border-top:1px solid var(--fc-primary-border);margin-top:20px;padding-top:20px}.fcrm_built_in_templates_section_title{margin:0 0 20px;color:var(--fc-primary-text);font-weight:500;font-size:16px;line-height:24px}.fcrm_build_in_temp_list{display:grid;grid-template-columns:repeat(auto-fill,minmax(230px,1fr));row-gap:30px;column-gap:20px}.fcrm_build_in_temp_box:hover .fcrm_build_in_temp_box_actions{opacity:1;visibility:visible}.fcrm_build_in_temp_box_title{color:var(--fc-primary-text);font-weight:500;font-size:14px;line-height:20px;margin:0}.fcrm_build_in_temp_box_image{position:relative;height:272px;overflow:hidden;margin-bottom:12px;border-radius:8px;background:var(--fc-secondary-bg)}.fcrm_build_in_temp_box_image img{display:block;max-width:100%;object-fit:contain}.fcrm_build_in_temp_box_actions{position:absolute;bottom:0;width:100%;background:linear-gradient(180deg,#17171700,#171717 297.37%);height:50%;display:flex;justify-content:center;align-items:flex-end;padding:12px;transition:.3s;opacity:0;visibility:hidden}.fcrm_build_in_temp_box_actions_inner{display:flex;justify-content:center;align-items:center;gap:10px}.fcrm_build_in_temp_box_actions_inner .el-button.fcrm_secondary_btn{padding:3px 8px}.fcrm_build_in_temp_box_actions_inner .el-link{background:var(--fc-primary-bg);color:var(--fc-secondary-text);font-weight:500;font-size:14px;line-height:20px;height:auto;border:1px solid var(--fc-primary-border);box-shadow:0 1px 2px #0a0d1408;border-radius:8px;padding:3px 8px;text-decoration:none}.fcrm_build_in_temp_box_actions_inner .el-link:hover:after{display:none}.fcrm_build_in_temp_box_actions_inner .el-link__inner{display:flex;align-items:center;gap:4px}.fcrm_build_in_temp_box_actions_inner .el-link__inner .icon svg{display:block}.fcrm_sms_edit_page .fcrm_sms_smartcode_popover .fcrm-compose-smartcodes{width:32px;height:32px;padding:6px;border-color:transparent;background:transparent;color:var(--fc-primary-text);box-shadow:none}.fcrm_sms_edit_page .fcrm_sms_smartcode_popover .fcrm-compose-smartcodes:hover,.fcrm_sms_edit_page .fcrm_sms_smartcode_popover .fcrm-compose-smartcodes:focus{border-color:transparent;background:var(--fc-secondary-bg);color:var(--fc-primary-text)}.fcrm_sms_edit_page .fcrm_page_header_top_nav_wrapper{padding-left:32px;padding-right:32px;margin-bottom:24px}.fcrm_sms_edit_page .fcrm_campaign_review_wrapper .fcrm_campaign_review_row{gap:24px}.fcrm_sms_edit_page .fcrm_campaign_review_wrapper .fcrm_campaign_review_list{flex:1}.fcrm_sms_edit_page .fcrm_campaign_review_wrapper .fcrm_review_sms_preview{background:var(--fc-primary-bg);color:var(--fc-primary-text);border-radius:var(--fcrm-border-radius-8, 8px) var(--fcrm-border-radius-8, 8px) var(--fcrm-border-radius-8, 8px) 0;padding:12px;font-weight:400;font-size:14px;line-height:20px;max-width:240px;width:fit-content}.fcrm_sms_edit_page .fcrm_campaign_review_wrapper .fcrm_review_sms_preview .sms_date{text-align:right;margin-top:8px;color:var(--fc-text-muted);display:block;font-weight:400;font-size:12px;line-height:16px}.fcrm_sms_edit_page .fcrm_campaign_review_wrapper .fcrm_review_sms_preview_wrap{width:320px;flex:none;padding:16px;background:var(--fc-secondary-bg);border-radius:8px}.fcrm_sms_edit_page .fcrm_campaign_review_wrapper .fcrm_campaign_review_item--broadcast{border:none;margin-bottom:0;padding:0}.fcrm_sms_edit_page .fcrm_campaign_review_wrapper .fcrm_campaign_review_actions{border:none;padding:0;justify-content:flex-start}.fcrm_textarea_with_count{position:relative}.fcrm_textarea_with_count .el-textarea textarea{padding:10px 12px 55px;height:166px;resize:none}.fcrm_textarea_with_count .el-textarea textarea::-webkit-input-placeholder{color:var(--fc-text-muted)}.fcrm_textarea_with_count .el-textarea .el-input__count{right:auto;left:10px;bottom:42px;font-size:11px;color:var(--fc-secondary-text);line-height:12px}.fcrm_textarea_with_count .fcrm_sms_message_count{position:absolute;bottom:1px;margin:0;color:var(--fc-text-muted);background:var(--fc-secondary-bg);font-weight:500;font-size:13px;line-height:20px;padding:7px 12px;left:1px;right:1px;border-top:1px solid var(--fc-primary-border);border-radius:0 0 8px 8px}.fcrm_textarea_with_count .fcrm_sms_message_count .count{color:var(--fc-secondary-text)}.fcrm_edit_campaign_steps{margin-bottom:0}.fcrm_edit_campaign_steps .el-steps .el-step__head.is-process .el-step__icon{border:1px solid var(--fc-deep-bg);background:var(--fc-secondary-bg)}.fcrm_edit_campaign_steps .el-steps .el-step__head.is-process .el-step__icon:before{opacity:1}.fcrm_edit_campaign_steps .el-steps .el-step__head.is-success .el-step__line{background:var(--fc-deep-bg)}.fcrm_edit_campaign_steps .el-steps .el-step__head.is-success .el-step__icon{background:var(--fc-deep-bg);border:1px solid var(--fc-deep-bg)}.fcrm_edit_campaign_steps .el-steps .el-step__head.is-success .el-step__icon:before{opacity:1;background:none;width:4px;border-bottom:1.5px solid var(--fc-primary-bg);border-right:1.5px solid var(--fc-primary-bg);border-radius:0;transform-origin:top;transform:rotate(45deg) translate(-50%,-50%)}.fcrm_edit_campaign_steps .el-steps .el-step__line{background:var(--fc-primary-border);height:1px;width:calc(100% - 40px);left:calc(50% + 20px)}.fcrm_edit_campaign_steps .el-steps .el-step__line-inner{display:none}.fcrm_edit_campaign_steps .el-steps .el-step__title{color:var(--fc-primary-text);font-weight:500;font-size:14px;line-height:20px;margin:4px 0 0}.fcrm_edit_campaign_steps .el-steps .el-step__description{margin:0;color:var(--fc-secondary-text);font-weight:400;font-size:12px;line-height:16px}.fcrm_edit_campaign_steps .el-steps .el-step__icon{background:var(--fc-secondary-bg);border:1px solid var(--fc-light-bg);width:24px;height:24px;position:relative}.fcrm_edit_campaign_steps .el-steps .el-step__icon:before{content:"";width:8px;height:8px;border-radius:50%;background:var(--fc-deep-bg);position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);opacity:0}.fcrm_edit_campaign_steps .el-steps .el-step__icon-inner{display:none}.fcrm_edit_campaign_page .fcrm_page_header{padding-top:5px}.fcrm_campaign_review_wrapper .fcrm_campaign_review_row{display:flex;gap:20px}.fcrm_campaign_review_wrapper .fcrm_campaign_review_row .fcrm_campaign_review_list{flex:1}.fcrm_campaign_review_wrapper .fcrm_campaign_review_row .fcrm_campaign_review_email_body{width:880px}.fcrm_campaign_review_wrapper .fcrm_campaign_review_row .fcrm_campaign_review_email_body_preview{background:var(--fc-secondary-bg);padding:32px;border-radius:var(--fcrm-border-radius-8, 8px);min-height:350px}.fcrm_campaign_review_wrapper .fcrm_campaign_review_row .fcrm_campaign_review_email_body_preview .fcrm_preview_device_toggle{justify-content:center;margin-bottom:16px}.fcrm_campaign_review_wrapper .fcrm_campaign_review_row .fcrm_campaign_review_email_body .fcrm_campaign_review_item{border:none;padding-bottom:0;margin-bottom:0}.fcrm_campaign_review_wrapper .fcrm_campaign_review_item{border-bottom:1px solid var(--fc-primary-border);padding-bottom:20px;margin-bottom:20px}.fcrm_campaign_review_wrapper .fcrm_campaign_review_item.fcrm_campaign_review_item--recipients .fcrm_campaign_review_item_body_item{cursor:pointer}.fcrm_campaign_review_wrapper .fcrm_campaign_review_item.fcrm_campaign_review_item--recipients .fcrm_readable_recipient_tagger{margin-bottom:16px}.fcrm_campaign_review_wrapper .fcrm_campaign_review_item:last-child{border-bottom:0;padding-bottom:0;margin-bottom:0}.fcrm_campaign_review_wrapper .fcrm_campaign_review_item .el-radio-group{flex-direction:column;align-items:flex-start;gap:8px}.fcrm_campaign_review_wrapper .fcrm_campaign_review_item_header{display:flex;align-items:center;justify-content:space-between;margin-bottom:16px}.fcrm_campaign_review_wrapper .fcrm_campaign_review_item_header--title{color:var(--fc-primary-text);font-weight:500;font-size:16px;line-height:24px;margin:0;display:flex;align-items:center;gap:4px}.fcrm_campaign_review_wrapper .fcrm_campaign_review_item_header--title .icon{display:block;line-height:1;color:var(--fc-secondary-text)}.fcrm_campaign_review_wrapper .fcrm_campaign_review_item_header--action .el-button.is-plain{font-weight:500;border:none;padding:0}.fcrm_campaign_review_wrapper .fcrm_campaign_review_item_header--action .el-button.is-plain:hover{background:none;color:var(--fc-primary-text)}.fcrm_campaign_review_wrapper .fcrm_campaign_review_item_header--action .el-button.is-plain>span{align-items:center;gap:7px}.fcrm_campaign_review_wrapper .fcrm_campaign_review_item_body_item{margin-bottom:16px}.fcrm_campaign_review_wrapper .fcrm_campaign_review_item_body_item:last-child{margin-bottom:0}.fcrm_campaign_review_wrapper .fcrm_campaign_review_item_body--label{color:var(--fc-secondary-text);font-weight:500;font-size:12px;line-height:16px;margin:0 0 4px}.fcrm_campaign_review_wrapper .fcrm_campaign_review_item_body--value{margin:0;color:var(--fc-primary-text);font-weight:400;font-size:14px;line-height:20px}.fcrm_campaign_review_wrapper .fcrm_campaign_review_item--broadcast .fcrm_campaign_review_item_body_item h4{color:var(--fc-primary-text);font-weight:500;font-size:14px;line-height:20px;margin:0 0 12px}.fcrm_campaign_review_wrapper .fcrm_campaign_review_item--broadcast .fcrm_campaign_review_item_body_broadcast--range-schedule h4,.fcrm_campaign_review_wrapper .fcrm_campaign_review_item--broadcast .fcrm_campaign_review_item_body_broadcast--schedule h4{margin:0 0 4px;font-weight:500;font-size:14px;line-height:20px}.fcrm_campaign_review_wrapper .fcrm_campaign_review_item--broadcast .fcrm_campaign_review_item_body_broadcast--range-schedule .fcrm_input_hint,.fcrm_campaign_review_wrapper .fcrm_campaign_review_item--broadcast .fcrm_campaign_review_item_body_broadcast--schedule .fcrm_input_hint{margin-top:8px}.fcrm_campaign_review_wrapper .fcrm_campaign_review_item--broadcast .fcrm_campaign_review_item_body_broadcast--range-schedule .fcrm_input_hint code,.fcrm_campaign_review_wrapper .fcrm_campaign_review_item--broadcast .fcrm_campaign_review_item_body_broadcast--schedule .fcrm_input_hint code{background:var(--fc-secondary-bg);border-radius:6px;color:var(--fc-primary-text);font-weight:400;font-size:12px;line-height:16px;padding:4px 6px}.fcrm_campaign_review_wrapper .fcrm_campaign_review_item--broadcast .fcrm_campaign_review_item_body_broadcast--range-schedule .el-date-editor,.fcrm_campaign_review_wrapper .fcrm_campaign_review_item--broadcast .fcrm_campaign_review_item_body_broadcast--schedule .el-date-editor{width:100%;border-radius:8px}.fcrm_campaign_review_wrapper .fcrm_campaign_review_item--broadcast .fcrm_campaign_review_item_body_broadcast--range-schedule .el-date-editor:not(.el-input),.fcrm_campaign_review_wrapper .fcrm_campaign_review_item--broadcast .fcrm_campaign_review_item_body_broadcast--schedule .el-date-editor:not(.el-input){padding-left:10px;padding-right:10px}.fcrm_campaign_review_wrapper .fcrm_campaign_review_item--recipients .fcrm_campaign_review_item_body--label{cursor:pointer}.fcrm_campaign_review_wrapper .fcrm_campaign_review_actions{display:flex;align-items:center;justify-content:flex-end;border-top:1px solid var(--fc-primary-border);margin-top:20px;padding-top:20px}.fcrm_campaign_subject_lines{display:flex;flex-direction:column;gap:6px}.fcrm_campaign_subject_lines--item{align-items:center;display:flex;gap:8px}.fcrm_campaign_subject_lines--value{color:var(--fc-primary-text)}.fcrm_campaign_subject_lines--priority{background:var(--fc-secondary-bg);border-radius:4px;color:var(--fc-secondary-text);font-size:11px;line-height:16px;padding:1px 6px;white-space:nowrap}.fcrm_preview_meta_subject{border-left:1px solid var(--fc-primary-border);display:inline-flex;margin-left:10px;padding-left:10px}.fcrm_email_campaign_recipient_tagger .fcrm_rich_container{padding-top:20px}.fcrm_email_campaign_recipient_tagger_selector{border-bottom:1px solid var(--fc-primary-border);margin-left:-20px;margin-right:-20px;padding:0 20px}.fcrm_email_campaign_recipient_tagger_selector .el-radio-group{gap:24px}.fcrm_email_campaign_recipient_tagger_selector .el-radio-group .el-radio-button{border:none;box-shadow:none}.fcrm_email_campaign_recipient_tagger_selector .el-radio-group .el-radio-button:last-child .el-radio-button__inner,.fcrm_email_campaign_recipient_tagger_selector .el-radio-group .el-radio-button:first-child .el-radio-button__inner{border-radius:0;border-left:none}.fcrm_email_campaign_recipient_tagger_selector .el-radio-group .el-radio-button .el-radio-button__inner{outline:none}.fcrm_email_campaign_recipient_tagger_selector .el-radio-group .el-radio-button.is-active .el-radio-button__inner{background:none;color:var(--fc-primary-text);border-color:var(--fc-primary-text);box-shadow:none;border-radius:0;padding-left:0;padding-right:0}.fcrm_email_campaign_recipient_tagger_selector .el-radio-group .el-radio-button.is-active .el-radio-button__original-radio:not(:disabled)+.el-radio-button__inner{background:none;color:var(--fc-deep-bg);outline:none}.fcrm_email_campaign_recipient_tagger_selector .el-radio-group .el-radio-button__inner{background:none;border:none;border-bottom:2px solid transparent;box-shadow:none;border-radius:0;color:var(--fc-secondary-text);font-weight:500;font-size:14px;line-height:20px;padding:0 0 12px;outline:none}.fcrm_email_campaign_recipient_tagger .fcrm_email_campaign_recipient_section_heading{margin-bottom:20px}.fcrm_email_campaign_recipient_tagger .fcrm_email_campaign_recipient_section_heading h3{margin:0 0 4px;color:var(--fc-primary-text);font-weight:500;font-size:16px;line-height:24px}.fcrm_email_campaign_recipient_tagger .fcrm_email_campaign_recipient_section_heading p{color:var(--fc-secondary-text);font-weight:400;font-size:14px;line-height:20px;margin:0}.fcrm_email_campaign_recipient_tagger .fcrm_email_campaign_included_contacts{border-bottom:1px solid var(--fc-primary-border);padding-bottom:24px;margin-bottom:24px;margin-top:20px}.fcrm_email_campaign_recipient_tagger .fcrm_email_campaign_excluded_contacts table,.fcrm_email_campaign_recipient_tagger .fcrm_email_campaign_included_contacts table{width:100%;border:1px solid var(--fc-primary-border);border-radius:8px;border-spacing:0;overflow:hidden}.fcrm_email_campaign_recipient_tagger .fcrm_email_campaign_excluded_contacts table thead tr th,.fcrm_email_campaign_recipient_tagger .fcrm_email_campaign_included_contacts table thead tr th{background:var(--fc-secondary-bg);border-bottom:1px solid var(--fc-primary-border);border-right:1px solid var(--fc-primary-border);text-align:left;color:var(--fc-secondary-text);font-weight:500;font-size:14px;line-height:20px;padding:8px 12px}.fcrm_email_campaign_recipient_tagger .fcrm_email_campaign_excluded_contacts table thead tr th:first-child,.fcrm_email_campaign_recipient_tagger .fcrm_email_campaign_included_contacts table thead tr th:first-child{padding-left:16px}.fcrm_email_campaign_recipient_tagger .fcrm_email_campaign_excluded_contacts table thead tr th:last-child,.fcrm_email_campaign_recipient_tagger .fcrm_email_campaign_included_contacts table thead tr th:last-child{border-right:none}.fcrm_email_campaign_recipient_tagger .fcrm_email_campaign_excluded_contacts table thead tr th.action_th_col,.fcrm_email_campaign_recipient_tagger .fcrm_email_campaign_included_contacts table thead tr th.action_th_col{width:60px}.fcrm_email_campaign_recipient_tagger .fcrm_email_campaign_excluded_contacts table tbody tr:last-child td,.fcrm_email_campaign_recipient_tagger .fcrm_email_campaign_included_contacts table tbody tr:last-child td{border-bottom:none}.fcrm_email_campaign_recipient_tagger .fcrm_email_campaign_excluded_contacts table tbody tr td,.fcrm_email_campaign_recipient_tagger .fcrm_email_campaign_included_contacts table tbody tr td{padding:14px 12px;border-bottom:1px solid var(--fc-primary-border);border-right:1px solid var(--fc-primary-border)}.fcrm_email_campaign_recipient_tagger .fcrm_email_campaign_excluded_contacts table tbody tr td:last-child,.fcrm_email_campaign_recipient_tagger .fcrm_email_campaign_included_contacts table tbody tr td:last-child{border-right:none}.fcrm_email_campaign_recipient_tagger .fcrm_email_campaign_excluded_contacts table tbody tr td:first-child,.fcrm_email_campaign_recipient_tagger .fcrm_email_campaign_included_contacts table tbody tr td:first-child{padding-left:16px}.fcrm_email_campaign_recipient_tagger .fcrm_email_campaign_excluded_contacts .fcrm_email_campaign_recipient_adder_action,.fcrm_email_campaign_recipient_tagger .fcrm_email_campaign_included_contacts .fcrm_email_campaign_recipient_adder_action{margin-top:8px}.fcrm_email_campaign_recipient_tagger .fcrm_email_campaign_dynamic_segment{max-width:588px;margin-top:20px}.fcrm_email_campaign_recipient_tagger .fcrm_email_campaign_dynamic_segment .fcrm_dynamic_select{max-width:300px}.fcrm_email_campaign_recipient_tagger .fcrm_email_campaign_advanced_filters .fcrm_email_campaign_recipient_section_heading{margin-bottom:0}.fcrm_email_campaign_recipient_tagger .fc_counting_heading{margin:16px 0 0;color:var(--fc-secondary-text);border-radius:6px;font-weight:500;text-align:center}.fcrm_email_campaign_recipient_tagger .fc_counting_heading span{background:var(--fc-primary-border);color:var(--fc-deep-bg);padding:2px 4px;min-width:24px;height:24px;line-height:18px;text-align:center;display:inline-block}.fcrm_email_subject_ab_test_wrap{border-bottom:1px solid var(--fc-primary-border);padding-bottom:20px;margin-bottom:20px}.fcrm_email_subject_ab_test_wrap .fcrm_email_subject_ab_test_hint{margin:0 0 6px;color:var(--fc-secondary-text);font-weight:400;font-size:12px;line-height:16px;display:flex;align-items:center;gap:3px}.fcrm_email_subject_ab_test_wrap table{border:1px solid var(--fc-primary-border);border-radius:8px;border-spacing:0;border-collapse:separate;box-shadow:none;width:100%}.fcrm_email_subject_ab_test_wrap table thead tr th{text-align:left;color:var(--fc-secondary-text);background:var(--fc-secondary-bg);border-right:1px solid var(--fc-primary-border);border-bottom:1px solid var(--fc-primary-border);font-weight:500;font-size:14px;line-height:20px;padding:8px 16px}.fcrm_email_subject_ab_test_wrap table thead tr th:last-child{border-right:none}.fcrm_email_subject_ab_test_wrap table thead tr th.action_th_col{width:70px}.fcrm_email_subject_ab_test_wrap table tbody tr:last-child td{border-bottom:none}.fcrm_email_subject_ab_test_wrap table tbody tr td{border-right:1px solid var(--fc-primary-border);border-bottom:1px solid var(--fc-primary-border);padding:14px 12px}.fcrm_email_subject_ab_test_wrap table tbody tr td:last-child{border-right:none;padding-right:16px}.fcrm_email_subject_ab_test_wrap table tbody tr td:first-child{padding-left:16px}.fcrm_email_subject_ab_test_wrap table tbody tr td .el-input-number{width:100%}.fcrm_email_subject_ab_test_wrap table tbody tr td .el-input-number__increase,.fcrm_email_subject_ab_test_wrap table tbody tr td .el-input-number__decrease{background:none;border:none;color:var(--fc-secondary-text)}.fcrm_email_subject_ab_test_wrap table tbody tr td .el-input-number__increase:hover~.el-input .el-input__wrapper,.fcrm_email_subject_ab_test_wrap table tbody tr td .el-input-number__decrease:hover~.el-input .el-input__wrapper{box-shadow:none}.fcrm_email_subject_ab_test_wrap table tbody tr td .el-input-number input{color:var(--fc-primary-text)}.fcrm_email_subject_ab_test_wrap table tbody tr td.action_td_col{text-align:center}.fcrm_email_subject_ab_test_wrap table tbody tr td.action_td_col .el-button{background:none;border:none;color:var(--fc-secondary-text);padding:10px 0;width:100%;height:auto;font-size:15px}.fcrm_email_subject_ab_test_wrap table tbody tr td.action_td_col .el-button.is-disabled{color:var(--fc-secondary-border)}.fcrm_email_subject_ab_test_wrap .fcrm_email_subject_ab_test_action{margin-top:12px}.fcrm_email_subject_ab_test_wrap .fcrm_email_subject_ab_test_action .el-button{padding:3px 11px 3px 7px}.toplevel_page_fluentcrm-admin .el-picker__popper{z-index:9999!important}.fc_rich_container .fcrm_input_hint{color:var(--fc-secondary-text);font-weight:400;font-size:12px;line-height:16px;margin:4px 0 0;display:flex;align-items:center;gap:3px}.fc_rich_container .fcrm_input_hint .el-icon{color:var(--fc-text-muted)}.fc_rich_container .el-select__wrapper,.fc_rich_container .el-input__wrapper{min-height:36px}.fc_rich_container .el-select .el-select__wrapper{height:auto}.fc_rich_container .el-select .el-select__wrapper.is-hovering{box-shadow:none}.fc_rich_container .el-textarea textarea{border:1px solid var(--fc-primary-border);border-radius:8px;box-shadow:none!important}.fc_rich_container .el-textarea textarea:focus{outline:none}.fc_rich_container .fc_rich_wrap .fc_cond_or{border:none;height:48px;position:relative;display:flex;align-items:center;justify-content:center;padding:0;margin:0 0 8px;z-index:1}.fc_rich_container .fc_rich_wrap .fc_cond_or:before{content:"";width:1px;height:100%;background:var(--fc-primary-border);z-index:-1;position:absolute}.fc_rich_container .fc_rich_wrap .fc_cond_or em{margin:0;padding:4px 0;color:var(--fc-text-muted);font-weight:500;font-size:12px;line-height:16px;text-transform:uppercase;background:var(--fc-primary-bg);top:0}.fc_rich_container .fc_rich_container_actions{display:flex;justify-content:space-between;align-items:center;margin-top:16px}.fc_rich_container .fc_rich_filter{background:var(--fc-secondary-bg);border:none;border-radius:8px;padding:16px;margin-bottom:8px}.fc_rich_container .fc_rich_filter .fc_rich_filters .fcrm_rich_filter_property_type{color:var(--fc-text-muted);font-weight:500;font-size:12px;line-height:16px;text-transform:uppercase;display:block;margin-bottom:12px}.fc_rich_container .fc_rich_filter .fc_rich_filters .fc_filter_intro{display:flex;flex-wrap:wrap;align-items:center;gap:12px}.fc_rich_container .fc_rich_filter .fc_rich_filters .fc_filter_intro .el-button.fcrm_secondary_btn{padding:3px 9px;border-radius:6px}.fc_rich_container .fc_rich_filter .fc_rich_filters .fc_filter_intro .el-button--danger{width:32px;height:32px;border-radius:8px;border:1px solid var(--fc-primary-border);box-shadow:0 1px 2px #0a0d1408;background:var(--fc-primary-bg);padding:0;color:var(--fc-secondary-text);margin-left:auto}.fc_rich_container .fc_rich_filter .fc_rich_filters .fc_table{width:100%;border-spacing:0}.fc_rich_container .fc_rich_filter .fc_rich_filters .fc_table tbody tr td{padding:0 4px 12px}.fc_rich_container .fc_rich_filter .fc_rich_filters .fc_table tbody tr td:last-child{padding-right:0}.fc_rich_container .fc_rich_filter .fc_rich_filters .fc_table tbody tr td:first-child{padding-left:8px}.fc_rich_container .fc_rich_filter .fc_rich_filters .fc_table tbody tr td.fc_filter_remove_col .el-button{width:32px;height:32px;border-radius:8px;border:1px solid var(--fc-primary-border);box-shadow:0 1px 2px #0a0d1408;background:var(--fc-primary-bg);padding:0;color:var(--fc-secondary-text)}.fc_rich_container .fc_rich_filter .fc_rich_filters .fc_table tbody tr td .el-input .el-input__wrapper{min-height:32px;border-radius:8px}.fc_rich_container .fc_rich_filter .fc_rich_filters .fc_table tbody tr td .el-select .el-select__wrapper{min-height:32px}.fc_rich_container .fc_rich_filter .fc_rich_filters .fc_table tbody tr:last-child td{padding-bottom:0}.fluentcrm_visual_editor .fcrm_visual_editor_header{flex-direction:column;align-items:flex-start;border:none;border-bottom:1px solid var(--fc-primary-border);padding:0 0 20px}.fluentcrm_visual_editor .fcrm_visual_editor_header_inner{display:flex;align-items:center;justify-content:space-between;width:100%;padding:0 20px}.fluentcrm_visual_editor .fcrm_visual_editor_header_inner_title{display:flex;align-items:center;gap:12px}.fluentcrm_visual_editor .fcrm_visual_editor_header_inner_title h3{margin:0;color:var(--fc-primary-text);font-weight:500;font-size:16px;line-height:24px;display:flex;align-items:center;gap:8px}.fluentcrm_visual_editor .fcrm_visual_editor_header_inner_title h3 .fcrm_secondary_btn{width:24px;height:24px;padding:0}.fluentcrm_visual_editor .fcrm_visual_editor_header_inner_title_actions{border:1px solid var(--fc-primary-border);border-radius:8px;display:flex}.fluentcrm_visual_editor .fcrm_visual_editor_header_inner_title_actions .el-button{margin:0;background:none;border:none;color:var(--fc-secondary-text);padding:0;width:32px;height:32px;border-radius:0}.fluentcrm_visual_editor .fcrm_visual_editor_header_inner_title_actions .fc_email_preview .el-button,.fluentcrm_visual_editor .fcrm_visual_editor_header_inner_title_actions .fc_style_editor .el-button{border-right:1px solid var(--fc-primary-border)}.fluentcrm_visual_editor .fcrm_visual_editor_header_inner_actions{display:flex;align-items:center;gap:10px}.fluentcrm_visual_editor .fcrm_visual_editor_header_inner_actions .el-button{margin:0}.fluentcrm_visual_editor .fcrm_visual_editor_header_inner_actions .el-button.fcrm_primary_btn{padding:7px 10px}.fluentcrm_visual_editor .fcrm_visual_editor_header_actions{width:100%;border-top:1px solid var(--fc-primary-border);margin-top:20px;padding:20px}.fluentcrm_visual_editor .fcrm_visual_editor_header_actions .fcrm_visual_editor_template_selector{position:relative}.fluentcrm_visual_editor .fcrm_visual_editor_header_actions .fcrm_visual_editor_template_selector_close{cursor:pointer;position:absolute;top:0;right:0}.fluentcrm_visual_editor .fcrm_visual_editor_header_actions .fcrm_visual_editor_template_selector_close svg{display:block}.fluentcrm_visual_editor .fcrm_visual_editor_header_actions .fcrm_visual_editor_template_selector .fc_image_radio_tooltips{gap:16px}.fluentcrm_visual_editor .fcrm_visual_editor_header_actions .fcrm_visual_editor_template_selector .fc_image_radio_tooltips .el-radio{margin:0;border:1px solid var(--fc-primary-border);border-radius:8px;overflow:hidden}.fluentcrm_visual_editor .fcrm_visual_editor_header_actions .fcrm_visual_editor_template_selector .fc_image_radio_tooltips .el-radio.is-checked{border-color:var(--fc-deep-bg)}.fluentcrm_visual_editor .fcrm_visual_editor_header_actions .fcrm_visual_editor_template_selector .fc_image_radio_tooltips .el-radio .fc_image_box{border:none}.fcrm_single_recurring_camp_page .fcrm_page_header{padding-top:5px}.fcrm_single_recurring_camp_body.fcrm_body_boxed{padding:0}.fcrm_single_recurring_camp_body_header{border-bottom:1px solid var(--fc-primary-border);display:flex;align-items:center;justify-content:space-between;gap:12px;flex-wrap:wrap;padding:0 20px}.fcrm_single_recurring_camp_body_header .fcrm_action_menu{display:flex;gap:24px;margin:0;padding:0}.fcrm_single_recurring_camp_body_header .fcrm_action_menu li{margin:0}.fcrm_single_recurring_camp_body_header .fcrm_action_menu li a{display:block;color:var(--fc-secondary-text);font-weight:500;font-size:12px;line-height:16px;padding:13px 0;border-bottom:2px solid transparent}.fcrm_single_recurring_camp_body_header .fcrm_action_menu li a.router-link-exact-active{border-bottom-color:var(--fc-primary-text);color:var(--fc-primary-text)}.fcrm_single_recurring_camp_body .fcrm_edit_email_sequence_schedule--config{margin-bottom:0}.fcrm_single_recurring_camp_body .fcrm_edit_email_sequence_schedule--config .textarea-h-36 textarea{height:36px}.fcrm_single_recurring_camp_body .fcrm_single_recurring_camp_send_test_email{padding:0 20px 20px}.fcrm_single_recurring_camp_body .fluentcrm_visual_editor .fcrm_visual_editor_header{padding-top:20px;border-top:1px solid var(--fc-primary-border)}.fcrm_single_recurring_camp_body .fluentcrm_visual_editor .fcrm_visual_editor_header_actions{padding-bottom:0}.fcrm_single_recurring_camp_settings{padding:20px;width:100%;margin:0 auto}.fcrm_single_recurring_camp_settings--form-item{border:1px solid var(--fc-primary-border);border-radius:8px;padding:16px;margin-bottom:20px}.fcrm_single_recurring_camp_settings--form-item-header{border-bottom:1px solid var(--fc-primary-border);padding-bottom:12px;margin-bottom:12px}.fcrm_single_recurring_camp_settings--form-item h3{margin:0;color:var(--fc-primary-text);font-weight:500;font-size:16px;line-height:24px}.fcrm_single_recurring_camp_settings--form-item .fcrm_email_campaign_recipient_tagger_selector{margin-left:-16px;margin-right:-16px;padding-left:16px;padding-right:16px}.fcrm_single_recurring_camp_settings--form-schedule .el-radio-group{gap:12px;flex-wrap:wrap;margin-top:8px}.fcrm_single_recurring_camp_settings--form-recipients .fc_counting_heading{margin:16px 0 0}.fcrm_single_recurring_camp_settings--form-footer{display:flex;align-items:center;justify-content:flex-end}.fcrm_recurring_campaign_conditions .fcrm_recurring_campaign_conditions_or{margin:8px 0;position:relative;display:flex;align-items:center;justify-content:center;height:48px;z-index:1}.fcrm_recurring_campaign_conditions .fcrm_recurring_campaign_conditions_or:before{content:"";position:absolute;left:50%;top:0;transform:translate(-50%);z-index:-1;width:1px;height:100%;background:var(--fc-primary-border)}.fcrm_recurring_campaign_conditions .fcrm_recurring_campaign_conditions_or span{display:block;background:var(--fc-primary-bg);color:var(--fc-text-muted);font-weight:500;font-size:12px;line-height:16px;text-transform:uppercase;padding:4px 0}.fcrm_recurring_campaign_conditions .fcrm_recurring_campaign_conditions_more{display:flex;align-items:center;justify-content:center;position:relative;z-index:1;margin-top:16px}.fcrm_recurring_campaign_conditions .fcrm_recurring_campaign_conditions_more:before{content:"";background:var(--fc-primary-border);position:absolute;height:1px;width:100%;top:50%;transform:translateY(-50%);z-index:-1}.fcrm_recurring_campaign_conditions .fcrm_recurring_campaign_conditions_more_inner{padding:0 10px;background:var(--fc-primary-bg)}.fcrm_recurring_campaign_conditions .fcrm_recurring_campaign_conditions_more_inner .el-button{padding:3px 8px}.fcrm_recurring_campaign_conditions_block_inner{background:var(--fc-secondary-bg);border-radius:8px;padding:16px;display:flex;align-items:center;justify-content:space-between}.fcrm_recurring_campaign_conditions_block .el-select .el-select__wrapper{border:1px solid var(--fc-primary-border);box-shadow:0 1px 2px #0a0d1408;border-radius:8px}.fcrm_recurring_campaign_conditions_block_title{color:var(--fc-secondary-text);font-weight:500;font-size:14px;line-height:20px;display:block;margin:0}.fcrm_recurring_campaign_conditions_block_right{display:flex;align-items:center;gap:12px}.fcrm_recurring_campaign_conditions_block_actions .fcrm_secondary_btn{padding:8px 10px}.fcrm_recurring_campaign_conditions_block_actions .fcrm_secondary_btn:hover{background:var(--fc-primary-bg)}.fcrm_recurring_email_history_page h3{color:var(--fc-primary-text)}.fcrm_recurring_email_history_wrapper{padding:20px}.fcrm_recurring_email_history_header{display:flex;align-items:center;justify-content:space-between;margin-bottom:20px}.fcrm_recurring_email_history_header_title{color:var(--fc-primary-text);margin:0;font-weight:500;font-size:16px;line-height:24px}.fcrm_recurring_email_history_header_description{color:var(--fc-secondary-text);margin:4px 0 0;font-weight:400;font-size:14px;line-height:20px}.fcrm_recurring_email_history_body{border:1px solid var(--fc-primary-border);border-radius:8px}.fcrm_recurring_email_history_item{padding:16px 20px;border-bottom:1px solid var(--fc-primary-border);display:flex;align-items:flex-start;justify-content:space-between}.fcrm_recurring_email_history_item:last-child{border-bottom:0}.fcrm_recurring_email_history_item--subject{color:var(--fc-primary-text);font-weight:500;font-size:16px;line-height:24px;margin:0}.fcrm_recurring_email_history_item--actions .el-dropdown-link{transform:rotate(90deg);cursor:pointer}.fcrm_recurring_email_history_item--meta{color:var(--fc-secondary-text);font-weight:400;font-size:14px;line-height:20px;margin:4px 0 0;display:flex;align-items:center;gap:6px}.fcrm_recurring_email_history_item--meta .dotted{width:2px;height:2px;border-radius:50%;display:block;background:var(--fc-secondary-text)}.el-overlay.fcrm_manage_labels_drawer .el-drawer{width:100%!important;max-width:640px}.el-overlay.fcrm_manage_labels_drawer .el-drawer .el-table__body tr:last-child td{border-bottom:none}.fcrm_manage_labels_table .fcrm_label_name{display:flex;align-items:center;gap:7px}.fcrm_manage_labels_table .fcrm_label_color{display:block;width:13px;height:13px;border-radius:50%;flex:none}.fcrm_manage_labels_form .el-form-item .el-form-item__content .el-input{height:auto}.fcrm_manage_labels_form .el-form-item .fcrm_manage_labels_radio{display:flex;flex-wrap:wrap;gap:10px;margin-top:10px}.fcrm_manage_labels_form .el-form-item .fcrm_manage_labels_radio .el-radio{margin:0;width:36px;height:36px;border-radius:6px;overflow:hidden;position:relative;transition:.3s}.fcrm_manage_labels_form .el-form-item .fcrm_manage_labels_radio .el-radio.is-checked{box-shadow:0 0 0 2.5px var(--fc-primary-bg),0 0 0 4px var(--fc-primary-text)}.fcrm_manage_labels_form .el-form-item .fcrm_manage_labels_radio .el-radio.is-checked .el-radio__label{display:block}.fcrm_manage_labels_form .el-form-item .fcrm_manage_labels_radio .el-radio .el-radio__input{width:100%;height:100%;opacity:0}.fcrm_manage_labels_form .el-form-item .fcrm_manage_labels_radio .el-radio .el-radio__label{display:none;margin:0;padding:0;position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);line-height:1;color:var(--fc-primary-text)}.fcrm_manage_labels_form .el-input{height:auto}.fcrm_manage_labels_form .el-input .el-input__wrapper{box-shadow:0 1px 2px #0a0d1408;border:1px solid var(--fc-primary-border);line-height:20px;font-size:14px}.fcrm_sms_campaign_view--body-inner{display:flex;gap:24px;align-items:flex-start;max-width:1400px;margin:0 auto}@media (max-width: 782px){.fcrm_sms_campaign_view--body-inner{flex-direction:column}}.fcrm_sms_campaign_view--link-list,.fcrm_sms_campaign_view--stats-list{list-style:none;margin:0;padding:0}.fcrm_sms_campaign_view--main{flex:1;min-width:0;background:var(--fc-primary-bg);border-radius:8px;overflow:hidden}@media (max-width: 782px){.fcrm_sms_campaign_view--main{width:100%}}.fcrm_sms_campaign_view--main .el-tabs{border:none;border-radius:8px;background:none}.fcrm_sms_campaign_view--main .el-tabs .el-tabs__header{background:none;border-bottom:1px solid var(--fc-primary-border);margin:0;padding:0 20px}.fcrm_sms_campaign_view--main .el-tabs .el-tabs__header .el-tabs__nav{gap:24px}.fcrm_sms_campaign_view--main .el-tabs .el-tabs__header .el-tabs__nav .el-tabs__item{margin:0;background:none;box-shadow:none;border:none;border-bottom:2px solid transparent;font-weight:500;font-size:12px;line-height:16px;padding:14px 0!important;height:auto;color:var(--fc-secondary-text)}.fcrm_sms_campaign_view--main .el-tabs .el-tabs__header .el-tabs__nav .el-tabs__item:hover{color:var(--fc-primary-text)}.fcrm_sms_campaign_view--main .el-tabs .el-tabs__header .el-tabs__nav .el-tabs__item.is-active{border-left:none;border-right:none;color:var(--fc-primary-text);border-bottom-color:var(--fc-primary-text)}.fcrm_sms_campaign_view--main .el-tabs .el-tabs__content{padding:20px}.fcrm_sms_campaign_view--main .el-tabs--border-card{border:none;box-shadow:none}.fcrm_sms_campaign_view--main .el-tabs__active-bar{background:var(--fc-text-link)}.fcrm_sms_campaign_view--section-title{font-weight:500;font-size:16px;line-height:24px;margin:0 0 16px;color:var(--fc-primary-text)}.fcrm_sms_campaign_view--detail-row{display:flex;flex-direction:column;gap:4px}.fcrm_sms_campaign_view--detail-row .is-link{text-decoration:underline}.fcrm_sms_campaign_view--detail-row .fcrm_sms_campaign_view--detail-label{font-weight:500;font-size:12px;line-height:16px;display:block;color:var(--fc-secondary-text);margin:0}.fcrm_sms_campaign_view--detail-row .fcrm_sms_campaign_view--detail-value{font-weight:400;font-size:14px;line-height:20px;color:var(--fc-primary-text);display:block;margin:0}.fcrm_sms_campaign_view--details-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(300px,1fr));gap:24px}.fcrm_sms_campaign_view--details-grid.fcrm_sms_campaign_view--details-grid--1{grid-template-columns:1fr;gap:12px}@media (max-width: 782px){.fcrm_sms_campaign_view--details-grid{grid-template-columns:repeat(auto-fit,minmax(200px,1fr))}}.fcrm_sms_campaign_view--divider{height:1px;background:var(--fc-primary-border);margin:20px 0}.fcrm_sms_campaign_view--sms-preview-card{background:var(--fc-secondary-bg);border:1px solid var(--fc-primary-border);border-radius:8px;padding:20px}.fcrm_sms_campaign_view--sms-preview-date{text-align:center;font-weight:500;font-size:12px;line-height:16px;color:var(--fc-secondary-text);background:var(--fc-secondary-bg);border-radius:6px;margin-bottom:16px;display:block;width:max-content;margin-left:auto;margin-right:auto;padding:4px 8px}.fcrm_sms_campaign_view--sms-bubble{background:#2225301a;color:var(--fc-primary-text);font-weight:400;font-size:14px;line-height:20px;max-width:440px;padding:12px;border-radius:0 8px 8px;width:max-content}.fcrm_sms_campaign_view--stat-item{display:flex;align-items:center;gap:12px;margin:0 0 10px;font-size:14px;line-height:20px;color:var(--fc-primary-text)}.fcrm_sms_campaign_view--stat-item:last-child{margin-bottom:0}.fcrm_sms_campaign_view--stat-item.failed .fcrm_sms_campaign_view--stat-dot{background:var(--fc-text-link)}.fcrm_sms_campaign_view--stat-item.total-smse .fcrm_sms_campaign_view--stat-dot{background:var(--fc-warning)}.fcrm_sms_campaign_view--stat-item .fcrm_sms_campaign_view--stat-label{font-weight:500;font-size:14px;line-height:20px;color:var(--fc-secondary-text);display:block}.fcrm_sms_campaign_view--stat-item .fcrm_sms_campaign_view--stat-dot{width:12px;height:12px;border-radius:50%;flex-shrink:0;background:var(--fc-secondary-text);box-shadow:0 2px 4px #1b1c1d0a;border:2px solid var(--fc-primary-bg)}.fcrm_sms_campaign_view--processing-status{max-width:800px;margin:0 auto}.fcrm_sms_campaign_view--processing-status-progress{margin-bottom:20px}.fcrm_sms_campaign_view--processing-status-details{background:var(--fc-primary-bg);border-radius:8px;padding:20px}.fcrm_sms_campaign_view--progress-card{background:var(--fc-primary-bg);border-radius:10px;padding:20px 24px;box-shadow:0 1px 3px #0000000f}.fcrm_sms_campaign_view--progress-card.fcrm_sms_campaign_view--paused-card{display:flex;flex-direction:column;align-items:center;justify-content:center;margin-bottom:24px}.fcrm_sms_campaign_view--progress-card.fcrm_sms_campaign_view--sending-card{margin-bottom:24px}.fcrm_sms_campaign_view--progress-card.fcrm_sms_campaign_view--scheduled-card{display:flex;justify-content:space-between;align-items:center;flex-wrap:wrap}.fcrm_sms_campaign_view--progress-card.fcrm_sms_campaign_view--scheduled-card .fcrm_sms_campaign_view--progress-header{margin-bottom:0}.fcrm_sms_campaign_view--progress-card.fcrm_sms_campaign_view--scheduled-card .fcrm_sms_campaign_view--progress-header{flex-direction:column;align-items:flex-start}.fcrm_sms_campaign_view--progress-card .fcrm_sms_campaign_view--scheduled-date{display:flex;align-items:center;gap:4px;color:var(--fc-secondary-text);font-size:14px;line-height:20px;font-weight:400}.fcrm_sms_campaign_view--progress-card .fcrm_sms_campaign_view--scheduled-date .icon svg{display:block}.fcrm_sms_campaign_view--progress-card .fcrm_sms_campaign_view--scheduled-date .date{color:var(--fc-primary-text);font-weight:400;font-size:14px;line-height:20px;margin:0}.fcrm_sms_campaign_view--progress-header{display:flex;align-items:center;justify-content:space-between;margin-bottom:16px}.fcrm_sms_campaign_view--progress-title{font-weight:500;font-size:16px;line-height:24px;color:var(--fc-primary-text);margin:0}.fcrm_sms_campaign_view--live-status{display:inline-flex;align-items:center;gap:8px;margin-left:12px;color:var(--fc-secondary-text);font-weight:400;white-space:nowrap;vertical-align:middle}.fcrm_sms_campaign_view--live-spinner{font-size:16px;color:var(--fc-primary-text)}.fcrm_sms_campaign_view--progress-description{font-weight:400;font-size:14px;line-height:20px;color:var(--fc-secondary-text);margin:4px 0 0}.fcrm_sms_campaign_view--progress-percent{font-weight:400;font-size:14px;line-height:20px;color:var(--fc-secondary-text)}.fcrm_sms_campaign_view--progress-bar .el-progress-bar__outer{background-color:var(--fc-secondary-bg);border-radius:4px}.fcrm_sms_campaign_view--progress-bar .el-progress-bar__inner{border-radius:4px}.fcrm_sms_campaign_view--scheduling-note{margin:8px 0 0;color:var(--fc-secondary-text)}.fcrm_sms_campaign_view--cancel-schedule{margin-top:20px;border-top:1px solid var(--fc-primary-border);padding-top:20px}.fcrm_sms_campaign_view--cancel-schedule p{margin:6px 0 0}.fcrm_campaign_action_processing h3{margin:0 0 4px;color:var(--fc-primary-text);font-weight:500;font-size:16px;line-height:24px;display:flex;align-items:center;gap:4px}.fcrm_campaign_action_processing h3 .icon svg{display:block}.fcrm_campaign_action_processing h4{margin:0;color:var(--fc-secondary-text);font-weight:400;font-size:14px;line-height:20px}.fcrm_campaign_action_processing .fcrm_campaign_action_processing-progress-bar{margin-top:16px;display:flex;align-items:center;gap:16px}.fcrm_campaign_action_processing .fcrm_campaign_action_processing-progress-bar .el-progress{flex:1}.fcrm_campaign_action_processing .fcrm_campaign_action_processing-progress-bar .fcrm_campaign_action_processing-progress-count{flex:none;min-width:40px;text-align:right;line-height:16px;display:block;color:var(--fc-secondary-text);font-weight:400;font-size:12px}.fcrm_campaign_action_processing .fcrm_campaign_action_processing-total-processed{color:var(--fc-secondary-text);font-size:12px;line-height:16px;font-weight:400;display:block;margin:2px 0 0}.fcrm_campaign_action_processing.fcrm_campaign_action_processing--completed{display:flex;flex-direction:column;align-items:center;justify-content:center}.fcrm_campaign_action_processing.fcrm_campaign_action_processing--completed .el-button{margin-top:16px}.fcrm_sms_campaign_view_actions_header{margin-bottom:16px}.fcrm_sms_campaign_view_actions_header h3{font-weight:500;font-size:16px;line-height:24px;margin:0 0 4px;color:var(--fc-primary-text)}.fcrm_sms_campaign_view_actions_header p{color:var(--fc-secondary-text);font-weight:400;font-size:14px;line-height:20px;margin:0}.fcrm_sms_campaign_view_actions .fcrm_campaign_action_wrapper .el-form-item:last-child{margin-bottom:0}.fcrm_sms_campaign_view_actions .fcrm_campaign_action_wrapper .el-form-item__label{margin-bottom:12px;color:var(--fc-primary-text);font-weight:500;font-size:14px;line-height:20px}.fcrm_sms_campaign_view_actions .fcrm_campaign_action_wrapper .el-form-item .el-radio-group{flex-direction:column;align-items:flex-start;gap:8px}.fcrm_sms_campaign_view_actions .fcrm_campaign_action_wrapper .el-form-item .el-radio-group .el-radio{margin:0}.fcrm_sms_campaign_view--sidebar{width:320px;flex-shrink:0}.fcrm_sms_campaign_view--stat-label{flex:1}.fcrm_sms_campaign_view--stat-value{color:var(--fc-secondary-text);font-size:13px}.fcrm_sms_campaign_view--link-item{display:flex;flex-direction:column;gap:4px;padding:10px 0;border-bottom:1px solid var(--fc-primary-border);font-size:12px}.fcrm_sms_campaign_view--link-item:last-child{border-bottom:none}.fcrm_sms_campaign_view--link-url{color:var(--fc-secondary-text);word-break:break-all;line-height:16px}.fcrm_sms_campaign_view--link-count{color:var(--fc-primary-text);font-weight:500}@media (max-width: 1099px){.fcrm_campaign_review_wrapper .fcrm_campaign_review_row{flex-direction:column;gap:40px}.fcrm_campaign_review_wrapper .fcrm_campaign_review_row .fcrm_campaign_review_email_body,.fcrm_campaign_review_wrapper .fcrm_campaign_review_row .fcrm_campaign_review_list{width:100%}}@media (max-width: 570px){.fcrm_recurring_campaign_conditions_block_inner{flex-wrap:wrap;gap:12px}}.fcrm_edit_email_sequence_schedule--config .fcrm_edit_email_sequence_schedule--datetime .el-form-item .el-form-item__content .el-date-editor{height:36px}.fcrm_edit_email_sequence_schedule--config .fcrm_edit_email_sequence_schedule--datetime .el-form-item .el-form-item__content .el-date-editor:not(.el-input){padding-left:10px;padding-right:10px}.fcrm_profile_sms_wrapper .fcrm_profile_sms_layout{margin:-20px}.fcrm_profile_sms_wrapper .fcrm_profile_sms_layout_inner{display:flex}.fcrm_profile_sms_wrapper .fcrm_profile_sms_layout_stats_panel{flex:none;width:272px;border-right:1px solid var(--fc-primary-border)}.fcrm_profile_sms_wrapper .fcrm_profile_sms_layout_stats_header{display:flex;align-items:center;justify-content:space-between;border-bottom:1px solid var(--fc-primary-border);padding:18px 20px;height:56px}.fcrm_profile_sms_wrapper .fcrm_profile_sms_layout_stats_header--title{color:var(--fc-primary-text);font-weight:500;font-size:14px;line-height:20px;margin:0}.fcrm_profile_sms_wrapper .fcrm_profile_sms_layout_stats_header--action .el-button{padding:0;border:none;box-shadow:none;border-radius:0;border-bottom:1px solid var(--fc-primary-text);font-size:12px;line-height:1.2}.fcrm_profile_sms_wrapper .fcrm_profile_sms_layout_stats_header--action .el-button:hover{background:none}.fcrm_profile_sms_wrapper .fcrm_profile_sms_layout_stats_body{padding:12px}.fcrm_profile_sms_wrapper .fcrm_profile_sms_layout_stats_card{padding:8px;display:flex;align-items:center;gap:8px;border-radius:6px}.fcrm_profile_sms_wrapper .fcrm_profile_sms_layout_stats_card--icon svg{display:block}.fcrm_profile_sms_wrapper .fcrm_profile_sms_layout_stats_card--label{color:var(--fc-secondary-text);font-weight:400;font-size:14px;line-height:20px}.fcrm_profile_sms_wrapper .fcrm_profile_sms_layout_stats_card--number{margin-left:auto;color:var(--fc-primary-text);font-weight:400;font-size:14px;line-height:20px}.fcrm_profile_sms_wrapper .fcrm_profile_sms_layout_chat_panel{flex:1;overflow:hidden;position:relative}.fcrm_profile_sms_wrapper .fcrm_profile_sms_layout_chat_header{border-bottom:1px solid var(--fc-primary-border);padding:12px 20px;height:56px}.fcrm_profile_sms_wrapper .fcrm_profile_sms_layout_chat_header--contact{display:flex;align-items:center;gap:10px}.fcrm_profile_sms_wrapper .fcrm_profile_sms_layout_chat_header--contact--avatar{width:32px;height:32px;border-radius:50%}.fcrm_profile_sms_wrapper .fcrm_profile_sms_layout_chat_header--contact--avatar img{display:block;width:100%;height:100%;object-fit:cover;border-radius:50%}.fcrm_profile_sms_wrapper .fcrm_profile_sms_layout_chat_header--contact--meta--name{color:var(--fc-primary-text);font-weight:500;font-size:14px;line-height:1}.fcrm_profile_sms_wrapper .fcrm_profile_sms_layout_chat_header--contact--meta--phone{font-size:12px;color:var(--fc-secondary-text);margin:2px 0 0;display:block}.fcrm_profile_sms_wrapper .fcrm_profile_sms_layout_chat_body{padding:20px;min-height:300px;max-height:500px;overflow-x:hidden;overflow-y:auto}.fcrm_profile_sms_wrapper .fcrm_profile_sms_layout_chat_body .no-messages{text-align:center}.fcrm_profile_sms_wrapper .fcrm_profile_sms_layout_chat--load-older{text-align:center;padding:12px 0;font-size:12px;color:var(--fc-text-muted);display:flex;align-items:center;justify-content:center;gap:8px}.fcrm_profile_sms_wrapper .fcrm_profile_sms_layout_chat--load-older-spinner{width:16px;height:16px;border:2px solid var(--fc-primary-border);border-top-color:var(--fc-secondary-text);border-radius:50%;animation:fcrm-sms-spin .6s linear infinite}.fcrm_profile_sms_wrapper .fcrm_profile_sms_layout_chat--message-group{margin-bottom:16px}.fcrm_profile_sms_wrapper .fcrm_profile_sms_layout_chat--message-group:last-child{margin-bottom:0}.fcrm_profile_sms_wrapper .fcrm_profile_sms_layout_chat--message-date-separator{display:flex;align-items:center;justify-content:center}.fcrm_profile_sms_wrapper .fcrm_profile_sms_layout_chat--message-date-separator span{background:var(--fc-secondary-bg);border-radius:6px;color:var(--fc-secondary-text);font-weight:500;font-size:12px;line-height:16px;padding:4px 8px}.fcrm_profile_sms_wrapper .fcrm_profile_sms_layout_chat--message-row{margin-top:16px}.fcrm_profile_sms_wrapper .fcrm_profile_sms_layout_chat--message-text{white-space:pre-wrap;word-break:break-word}.fcrm_profile_sms_wrapper .fcrm_profile_sms_layout_chat--message-meta{display:flex;align-items:center;gap:4px;justify-content:flex-end;margin-top:8px}.fcrm_profile_sms_wrapper .fcrm_profile_sms_layout_chat--message-time{color:var(--fc-text-muted);font-weight:400;font-size:12px;line-height:16px;display:block}.fcrm_profile_sms_wrapper .fcrm_profile_sms_layout_chat--message-status{display:block;font-size:10px}.fcrm_profile_sms_wrapper .fcrm_profile_sms_layout_chat--message-bubble{background:var(--fc-secondary-bg);border-radius:0 var(--fcrm-border-radius-8, 8px) var(--fcrm-border-radius-8, 8px) var(--fcrm-border-radius-8, 8px);padding:12px;width:max-content;max-width:65%}.fcrm_profile_sms_wrapper .fcrm_profile_sms_layout_chat--message-bubble.bubble-out{margin-left:auto;border-radius:var(--fcrm-border-radius-8, 8px) 0 var(--fcrm-border-radius-8, 8px) var(--fcrm-border-radius-8, 8px);background:var(--fc-secondary-bg)}.fcrm_profile_sms_wrapper .fcrm_profile_sms_layout_chat--scroll-to-bottom{position:absolute;bottom:90px;left:50%;transform:translate(-50%);z-index:10;width:36px;height:36px;border-radius:50%;border:1px solid var(--fc-primary-border);background:var(--fc-primary-bg);box-shadow:0 4px 12px #0e121b1f;cursor:pointer;display:flex;align-items:center;justify-content:center;color:var(--fc-secondary-text);transition:background .15s,box-shadow .15s}.fcrm_profile_sms_wrapper .fcrm_profile_sms_layout_chat--scroll-to-bottom:hover{background:var(--fc-secondary-bg);box-shadow:0 6px 16px #0e121b2e}.fcrm_profile_sms_wrapper .fcrm_profile_sms_layout_chat--scroll-to-bottom svg{display:block}.fcrm_profile_sms_wrapper .fcrm_profile_sms_layout_chat--phone-missing-notice{margin-bottom:10px;font-size:12px;color:var(--fc-secondary-text)}.fcrm_profile_sms_wrapper .fcrm_profile_sms_layout_chat--composer{border-top:1px solid var(--fc-primary-border);box-shadow:0 -8px 24px -6px #0e121b0a;background:var(--fc-primary-bg);padding:16px;border-radius:0 0 var(--fcrm-border-radius-8, 8px) 0}.fcrm_profile_sms_wrapper .fcrm_profile_sms_layout_chat--composer-input-wrap{position:relative;border:1px solid var(--fc-primary-border)!important;box-shadow:0 1px 2px #0a0d1408;background:var(--fc-primary-bg);border-radius:var(--fcrm-border-radius-8, 8px);padding:12px}.fcrm_profile_sms_wrapper .fcrm_profile_sms_layout_chat--composer-input-wrap textarea{width:100%;display:block;border:none!important;background:none;box-shadow:none;resize:none;height:24px;padding:0;border-radius:0;color:var(--fc-secondary-text)}.fcrm_profile_sms_wrapper .fcrm_profile_sms_layout_chat--composer-input-footer{display:flex;align-items:flex-end;justify-content:space-between}.fcrm_profile_sms_wrapper .fcrm_profile_sms_layout_chat--composer-input-footer-send .el-button{background:var(--fc-deep-bg);border-radius:8px;color:var(--fc-text-inverse);font-weight:500;font-size:14px;line-height:20px;height:auto;border:none;padding:4px 10px}.fcrm_profile_sms_wrapper .fcrm_profile_sms_layout_chat--composer-input-footer-send .el-button.is-disabled{opacity:.5;cursor:not-allowed}.fcrm_profile_sms_wrapper .fcrm_profile_sms_layout_chat--composer-input-footer-send .el-button .icon{display:block}.fcrm_profile_sms_wrapper .fcrm_profile_sms_layout_chat--composer-input-footer-send .el-button .icon svg{display:block}.fcrm_profile_sms_wrapper .fcrm_profile_sms_layout_chat--composer-input-footer-send .el-button>span{gap:4px}.fcrm_profile_sms_wrapper .fcrm_profile_sms_layout_chat--composer-input-footer .fcrm_profile_sms_layout_chat--sms-counter{color:var(--fc-text-muted);font-weight:500;font-size:11px;line-height:12px}@keyframes fcrm-sms-spin{to{transform:rotate(360deg)}}.fcrm_profile_sms_wrapper .fcrm-sms-fade-enter-active,.fcrm_profile_sms_wrapper .fcrm-sms-fade-leave-active{transition:opacity .2s ease}.fcrm_profile_sms_wrapper .fcrm-sms-fade-enter-from,.fcrm_profile_sms_wrapper .fcrm-sms-fade-leave-to{opacity:0}@media (max-width: 768px){.fcrm_profile_sms_wrapper .fcrm_profile_sms_layout{margin:-12px}.fcrm_profile_sms_wrapper .fcrm_profile_sms_layout_inner{flex-direction:column}.fcrm_profile_sms_wrapper .fcrm_profile_sms_layout_stats_panel{width:100%;border-right:none;border-bottom:1px solid var(--fc-primary-border)}.fcrm_profile_sms_wrapper .fcrm_profile_sms_layout_stats_body{display:flex;flex-wrap:wrap;gap:0}.fcrm_profile_sms_wrapper .fcrm_profile_sms_layout_stats_card{flex:1;min-width:50%}.fcrm_profile_sms_wrapper .fcrm_profile_sms_layout_chat_body{min-height:250px;max-height:400px;padding:12px}.fcrm_profile_sms_wrapper .fcrm_profile_sms_layout_chat--message-bubble{max-width:80%}.fcrm_profile_sms_wrapper .fcrm_profile_sms_layout_chat--composer{padding:12px;border-radius:0}}@media (max-width: 480px){.fcrm_profile_sms_wrapper .fcrm_profile_sms_layout{margin:-8px}.fcrm_profile_sms_wrapper .fcrm_profile_sms_layout_stats_card{min-width:100%}.fcrm_profile_sms_wrapper .fcrm_profile_sms_layout_chat_header{padding:10px 12px}.fcrm_profile_sms_wrapper .fcrm_profile_sms_layout_chat_body{min-height:200px;max-height:350px;padding:10px}.fcrm_profile_sms_wrapper .fcrm_profile_sms_layout_chat--message-bubble{max-width:90%}.fcrm_profile_sms_wrapper .fcrm_profile_sms_layout_chat--composer,.fcrm_profile_sms_wrapper .fcrm_profile_sms_layout_chat--composer-input-wrap{padding:10px}}.fcrm_all_sms_page .fcrm_page_header_actions .el-select{height:auto}.fcrm_all_sms_page .fcrm_page_header_actions .el-select__wrapper{border-radius:8px;height:auto;padding:11px 12px}.fc_step_header{margin-bottom:20px;border-bottom:1px solid var(--fc-secondary-border)}.fc_step_header h3{margin:0;padding:0}.fc_step_header p{margin:5px 0}.fc_driver_MailerLite img{width:auto!important;height:29px!important;margin:25px 0}.fcrm_cleanup-preview_dialog .el-dialog{max-width:442px}.fcrm_cleanup-preview_dialog .el-dialog .el-dialog__header{display:none}.fcrm_cleanup-preview_dialog .el-dialog .el-dialog__body{padding:0!important}.fcrm-data-cleanup.fcrm-widget-section{background:var(--fc-primary-bg);border-radius:8px;overflow:hidden;box-shadow:none}.fcrm-data-cleanup .fcrm-widget-body{background:var(--fc-primary-bg);padding:0}.fcrm-data-cleanup .fcrm-widget-body.fcrm_has_padding{padding:20px}.fcrm-data-cleanup{border:none}.fcrm-cleanup-form{display:flex;flex-direction:column;gap:20px;width:100%;padding:20px;align-items:flex-start}.fcrm-cleanup-form .fcrm-delete-button{font-weight:500;font-size:14px;line-height:20px;width:fit-content;background:var(--alpha-red-alpha-10, rgba(251, 55, 72, .1));color:var(--fc-error);border-radius:8px;border:none;padding:8px 10px}.fcrm-cleanup-field{display:flex;flex-direction:column;gap:4px;width:100%}.fcrm-cleanup-field.fcrm-cleanup-logs .fcrm-field-label{margin-bottom:4px}.fcrm-cleanup-field.fcrm-cleanup-logs .fcrm-cleanup-alert{margin-top:10px}.fcrm-field-label{font-size:14px;font-weight:500;line-height:20px;letter-spacing:-.084px;color:var(--fc-primary-text);margin:0}.fcrm-time-range-input{max-width:364px;border:1px solid var(--fc-primary-border);border-radius:8px;overflow:hidden;display:flex;background:var(--fc-primary-bg)}.fcrm-time-range-input .el-input{flex:1}.fcrm-time-range-input .el-input .el-input__wrapper{border:none!important;border-right:1px solid var(--fc-primary-border)!important;border-radius:0!important;box-shadow:none!important;background:var(--fc-primary-bg)}.fcrm-time-range-input .el-input .el-input__inner{font-size:14px;font-weight:400;line-height:20px;letter-spacing:-.084px;color:var(--fc-primary-text);padding:0}.fcrm-time-range-unit{background:var(--fc-secondary-bg);padding:8px 16px;display:flex;align-items:center;justify-content:center;font-size:14px;font-weight:400;line-height:20px;letter-spacing:-.084px;color:var(--fc-primary-text)}.fcrm-field-help{font-size:12px;line-height:16px;color:var(--fc-secondary-text);margin:0;white-space:nowrap;display:flex;gap:4px;flex-wrap:wrap}.fcrm-field-help span{font-weight:400;color:var(--fc-secondary-text)}.fcrm-field-help strong{font-weight:500;color:var(--fc-primary-text)}.fcrm-cleanup-checkboxes{border:1px solid var(--fc-primary-border);border-radius:8px;overflow:hidden;display:flex;flex-direction:column}.fcrm-cleanup-checkbox-item{border-bottom:1px solid var(--fc-primary-border);padding:12px 16px}.fcrm-cleanup-checkbox-item:last-child{border-bottom:none}.fcrm-cleanup-checkbox-content{display:flex;flex-direction:column;gap:4px}.fcrm-cleanup-checkbox-label{color:var(--fc-primary-text);font-weight:500;font-size:14px;line-height:20px;margin:0}.fcrm-cleanup-checkbox-desc{font-weight:400;font-size:12px;line-height:16px;color:var(--fc-secondary-text);margin:0}.fcrm-cleanup-alert{background:var(--fc-warning-bg);border:none;border-radius:8px;padding:8px;min-height:32px;display:flex;align-items:center;gap:8px}.fcrm-cleanup-alert svg{width:16px;height:16px;flex-shrink:0}.fcrm-cleanup-alert .fcrm-cleanup-alert-text{font-size:12px;font-weight:400;line-height:16px;color:var(--fc-primary-text);margin:0;flex:1}.fcrm-cleanup-success{text-align:center;padding:40px 20px}.fcrm-cleanup-success h4{font-size:18px;font-weight:600;line-height:24px;color:var(--fc-success);margin:0}.fcrm-cleanup-preview{background:var(--fc-primary-bg);border-radius:var(--fcrm-border-radius-8, 8px);overflow:hidden;box-shadow:0 1px 3px #0000001a}.fcrm-preview-header{background:var(--fc-primary-bg);border-bottom:1px solid var(--fc-primary-border);padding:16px 20px;display:flex;align-items:center;gap:12px;position:relative}.fcrm-preview-header h4{font-size:16px;font-weight:500;line-height:24px;letter-spacing:-.176px;color:var(--fc-primary-text);margin:0;flex:1;min-width:0}.fcrm-close-button{position:absolute;right:16px;top:16px;background:transparent;border:none;border-radius:6px;padding:2px;width:20px;height:20px;display:flex;align-items:center;justify-content:center;cursor:pointer;flex-shrink:0}.fcrm-close-button svg{width:20px;height:20px}.fcrm-close-button:hover{background:var(--fc-secondary-bg)}.fcrm-preview-body{background:var(--fc-primary-bg);padding:20px}.fcrm-preview-content{display:flex;flex-direction:column;gap:8px}.fcrm-preview-table-wrapper{border:1px solid var(--fc-primary-border);border-radius:var(--fcrm-border-radius-8, 8px);overflow:hidden;min-height:80px}.fcrm-preview-divtable{width:100%}.fcrm-divtable-header{display:grid;grid-template-columns:1fr 1fr;align-items:center;background:var(--fc-secondary-bg);padding:8px 12px;border-bottom:1px solid var(--fc-primary-border);background-color:var(--fc-secondary-bg)}.fcrm-divtable-body{display:block}.fcrm-divrow{display:grid;grid-template-columns:1fr 1fr;align-items:center;background:var(--fc-primary-bg);padding:12px;border-bottom:1px solid var(--fc-primary-border)}.fcrm-divrow:last-child{border-bottom:none}.fcrm-divcell{font-size:14px;line-height:20px;letter-spacing:-.084px;color:var(--fc-primary-text);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.fcrm-divtable-header .fcrm-divcell{font-weight:500;color:var(--fc-secondary-text)}.fcrm-divtable-empty{padding:24px;text-align:center;font-size:14px;color:var(--fc-secondary-text)}.fcrm-cell-type{padding-left:8px}.fcrm-cell-count{text-align:left;padding-right:8px;font-weight:500}.fcrm-preview-table .el-table__header-wrapper .el-table__header thead tr th{background:var(--fc-secondary-bg);border-bottom:none;padding:8px 12px;font-size:14px;font-weight:500;line-height:20px;letter-spacing:-.084px;color:var(--fc-secondary-text)}.fcrm-preview-table .el-table__header-wrapper .el-table__header thead tr th:first-child{padding-left:20px}.fcrm-preview-table .el-table__header-wrapper .el-table__header thead tr th:last-child{padding-right:20px}.fcrm-preview-table .el-table__body-wrapper .el-table__body tbody tr td{background:var(--fc-primary-bg);border-bottom:1px solid var(--fc-primary-border);padding:12px;height:48px}.fcrm-preview-table .el-table__body-wrapper .el-table__body tbody tr td:first-child{padding-left:20px;padding-right:16px}.fcrm-preview-table .el-table__body-wrapper .el-table__body tbody tr td:last-child{padding-left:12px;padding-right:20px}.fcrm-preview-table .el-table__body-wrapper .el-table__body tbody tr td .cell{font-size:14px;font-weight:400;line-height:20px;letter-spacing:-.084px;color:var(--fc-primary-text);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.fcrm-preview-table .el-table__body-wrapper .el-table__body tbody tr:last-child td{border-bottom:none}.fcrm-preview-help{font-size:12px;font-weight:400;line-height:16px;color:var(--fc-secondary-text);margin:0}.fcrm-preview-footer{background:var(--fc-primary-bg);border-top:1px solid var(--fc-primary-border);padding:16px 20px}.fcrm-preview-actions{display:flex;gap:12px;align-items:center;justify-content:flex-end}.fcrm-preview-actions .el-button{margin:0}.fcrm-status-card{background:var(--fc-secondary-bg);border-radius:8px;padding:8px 12px;display:flex;flex-direction:column;gap:4px;justify-content:center}.fcrm-status-card .fcrm-status-badge{background:var(--fc-primary-bg);border:1px solid var(--fc-primary-border);border-radius:6px;padding:4px 8px 4px 4px;display:flex;align-items:center;gap:4px;width:fit-content}.fcrm-status-card .fcrm-status-badge .icon svg{display:block}.fcrm-status-card .fcrm-status-badge span{font-weight:500;font-size:12px;line-height:16px;color:var(--fc-secondary-text);display:block}.fcrm-status-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:12px}.fcrm-status-label{font-size:12px;font-weight:500;line-height:16px;color:var(--fc-secondary-text);margin:0}.fcrm-cron-alert-wrapper{width:100%}.fcrm-cron-alert{border-radius:8px;padding:8px 10px;display:flex;align-items:center;gap:8px;background:var(--fc-primary-bg);color:var(--fc-primary-text)}.fcrm-cron-alert .fcrm-cron-alert-link{font-weight:500;font-size:14px;line-height:20px;text-decoration:underline;color:var(--fc-primary-text)}.fcrm-cron-alert .fcrm-cron-alert-text{flex:1;font-weight:400;font-size:14px;line-height:20px;color:var(--fc-primary-text);margin:0}.fcrm-cron-alert-icon{font-size:20px;color:var(--fc-primary-text);flex-shrink:0;width:20px;height:20px;display:flex;align-items:center;justify-content:center}.fcrm-cron-content{display:flex;flex-direction:column;gap:20px}.fcrm-server-stats{background:var(--fc-primary-bg);border:1px solid var(--fc-primary-border);border-radius:8px;padding:12px 20px;display:flex;align-items:center;gap:20px}.fcrm-server-stats .fcrm-stat-divider{width:1px;background:var(--fc-primary-border);align-self:stretch}.fcrm-server-stat{flex:1;display:flex;flex-direction:column;gap:4px}.fcrm-server-stat .fcrm-stat-label{font-weight:500;font-size:12px;line-height:16px;margin:0;color:var(--fc-secondary-text)}.fcrm-server-stat .fcrm-stat-value{font-weight:500;font-size:14px;line-height:20px;color:var(--fc-primary-text);margin:0}.fcrm-cron-jobs{border:1px solid var(--fc-primary-border);border-radius:8px;overflow:hidden}.fcrm-cron-job{border-bottom:1px solid var(--fc-primary-border);padding:20px;display:flex;align-items:center;justify-content:space-between;gap:20px;flex-wrap:wrap}.fcrm-cron-job:last-child{border-bottom:none}.fcrm-cron-job-info{flex:1;display:flex;flex-direction:column;gap:4px;max-width:340px}.fcrm-cron-job-name{font-weight:400;font-size:14px;line-height:20px;color:var(--fc-primary-text);margin:0}.fcrm-cron-job-meta{display:flex;gap:12px;flex-wrap:wrap}.fcrm-cron-meta-item{display:flex;gap:4px;align-items:center;font-size:12px;line-height:16px;white-space:nowrap}.fcrm-cron-meta-label{font-weight:500;font-size:12px;line-height:16px;color:var(--fc-secondary-text);margin:0}.fcrm-cron-meta-value{color:var(--fc-primary-text);font-weight:400;font-size:12px;line-height:16px;margin:0}.fcrm-cron-meta-value.fcrm-overdue{color:var(--fc-error);font-weight:500}@media (max-width: 578px){.fcrm-server-stats{display:block}.fcrm-server-stats .fcrm-server-stat{margin-bottom:16px}.fcrm-server-stats .fcrm-server-stat:last-child{margin-bottom:0}.fcrm-server-stats .fcrm-stat-divider{display:none}}.fcrm-dev-alert{background:var(--fc-primary-bg);border-radius:var(--fcrm-border-radius-8, 8px);padding:16px;display:flex;gap:12px}.fcrm-dev-alert .fcrm-dev-alert-title{font-weight:500;font-size:14px;line-height:20px;margin:0;color:var(--fc-primary-text)}.fcrm-dev-alert .fcrm-dev-alert-text{display:flex;align-items:center;gap:4px;flex-wrap:wrap;font-weight:400;font-size:14px;line-height:20px;color:var(--fc-primary-text)}.fcrm-dev-alert .fcrm-dev-alert-text code{background:var(--fc-secondary-bg);color:var(--fc-secondary-text);font-weight:500;font-size:12px;line-height:16px;padding:2px 4px;border-radius:6px}.fcrm-dev-alert-content{display:flex;flex-direction:column;gap:4px;flex:1}.fcrm-dev-alert-text span{font-size:14px;font-weight:400;line-height:20px;letter-spacing:-.084px;color:var(--fc-primary-text)}.fcrm-danger-content{display:flex;flex-direction:column;gap:12px}.fcrm-danger-title{color:var(--fc-primary-text);font-weight:500;font-size:14px;line-height:20px;margin:0}.fcrm-danger-list{display:flex;flex-direction:column;gap:16px}.fcrm-danger-list-row{display:grid;grid-template-columns:repeat(auto-fit,minmax(200px,1fr));gap:16px}.fcrm-danger-column{flex:1}.fcrm-danger-column ul{list-style-position:outside;list-style-type:disc;padding-left:21px;margin:0;display:flex;flex-direction:column;gap:8px}.fcrm-danger-column ul li{font-weight:400;font-size:14px;line-height:20px;color:var(--fc-secondary-text);margin:0}.fcrm-danger-confirm{background:var(--fc-primary-bg);border:1px solid var(--fc-primary-border);border-radius:var(--fcrm-border-radius-8, 8px);padding:16px;margin-top:20px;margin-bottom:20px}.fcrm-danger-checkbox{width:100%}.fcrm-danger-checkbox-content{display:flex;flex-direction:column;gap:4px}.fcrm-danger-checkbox-label{font-size:14px;font-weight:500;line-height:20px;letter-spacing:-.084px;color:var(--fc-primary-text);white-space:normal}.fcrm-danger-checkbox-desc{font-size:12px;font-weight:400;line-height:16px;color:var(--fc-secondary-text);white-space:normal}.fcrm_data_cleanup_wrap{max-width:800px;margin:0 auto;transition:.3s;-webkit-transition:.3s}.fcrm_email_sequences_view .fcrm_page_header{padding-top:8px}.fcrm_email_sequences_view .fcrm-sequence-title-text-wrap{align-items:center;display:flex;gap:6px}.fcrm_email_sequences_view .fcrm-sequence-title-text{color:var(--fc-primary-text);cursor:pointer;font-size:14px;font-weight:500;line-height:20px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.fcrm_email_sequences_view .fcrm-sequence-title-edit-icon{cursor:pointer;flex-shrink:0}.fcrm_email_sequences_view .fcrm-sequence-title-edit-icon svg{display:block}.fcrm_email_sequences_view .fcrm-sequence-title-input{max-width:280px}.fcrm_email_sequences_view .fcrm-sequence-list{background:var(--fc-primary-bg);border-radius:var(--fcrm-border-radius-8)}.fcrm_email_sequences_view .fcrm-sequence-card{border-bottom:none;padding:20px}.fcrm_email_sequences_view .fcrm-sequence-wait-divider{position:relative;height:1px;background:var(--fc-primary-border);display:flex;align-items:center;justify-content:center}.fcrm_email_sequences_view .fcrm-sequence-wait-divider:first-child{background:none;height:0}.fcrm_email_sequences_view .fcrm-sequence-wait-badge{position:absolute;display:inline-flex;align-items:center;gap:5px;background:var(--fc-primary-bg);border:1px solid var(--fc-primary-border);border-radius:100px;padding:4px 14px;font-size:12px;font-weight:500;color:var(--fc-text-muted);line-height:16px;white-space:nowrap}.fcrm_email_sequences_view .fcrm-sequence-wait-badge svg{flex-shrink:0}.fcrm_email_sequences_view .fcrm-sequence-wait-badge--editable{cursor:pointer;transition:border-color .2s,color .2s}.fcrm_email_sequences_view .fcrm-sequence-wait-badge--editable .fcrm-sequence-wait-edit-icon{opacity:0;transition:opacity .2s;color:var(--fc-text-muted)}.fcrm_email_sequences_view .fcrm-sequence-wait-badge--editable:hover{border-color:var(--fc-secondary-border);color:var(--fc-secondary-text)}.fcrm_email_sequences_view .fcrm-sequence-wait-badge--editable:hover .fcrm-sequence-wait-edit-icon{opacity:1}.fcrm_email_sequences_view .fcrm-sequence-card-main{display:flex;flex-direction:column;gap:8px}.fcrm_email_sequences_view .fcrm-sequence-card-title-row{align-items:flex-start;display:flex;gap:10px;justify-content:space-between}.fcrm_email_sequences_view .fcrm-sequence-card-title-wrap{align-items:center;display:flex;gap:8px;min-width:0}.fcrm_email_sequences_view .fcrm-sequence-number{align-items:center;background:var(--fc-secondary-bg);border-radius:6px;color:var(--fc-text-muted);display:inline-flex;font-size:12px;font-weight:500;height:20px;justify-content:center;min-width:20px;padding:0 6px}.fcrm_email_sequences_view .fcrm-sequence-card-title{color:var(--fc-primary-text);font-size:16px;font-weight:500;line-height:24px;overflow:hidden;text-decoration:none;text-overflow:ellipsis;white-space:nowrap}.fcrm_email_sequences_view .fcrm-sequence-card-title:hover{color:var(--fc-primary-text)}.fcrm_email_sequences_view .fcrm-sequence-card-actions{align-items:center;display:flex;gap:4px;flex-shrink:0}.fcrm_email_sequences_view .fcrm-sequence-card-actions .fc_email_preview .el-button{border:none;background:none;padding:0;width:32px;height:32px;display:inline-flex;align-items:center;justify-content:center;border-radius:8px;color:var(--fc-text-muted);font-size:18px}.fcrm_email_sequences_view .fcrm-sequence-card-actions .fc_email_preview .el-button:hover{background:var(--fc-secondary-bg);color:var(--fc-secondary-text)}.fcrm_email_sequences_view .fcrm-sequence-action-btn{align-items:center;border-radius:8px;color:var(--fc-text-muted);cursor:pointer;display:inline-flex;height:32px;justify-content:center;width:32px}.fcrm_email_sequences_view .fcrm-sequence-action-btn svg{display:block}.fcrm_email_sequences_view .fcrm-sequence-action-btn:hover{background:var(--fc-secondary-bg);color:var(--fc-secondary-text)}.fcrm_email_sequences_view .fcrm-sequence-more{align-items:center;border-radius:8px;color:var(--fc-text-muted);cursor:pointer;display:inline-flex;font-size:18px;height:32px;justify-content:center;width:32px}.fcrm_email_sequences_view .fcrm-sequence-more .el-icon{rotate:90deg}.fcrm_email_sequences_view .fcrm-sequence-more:hover{background:var(--fc-secondary-bg);color:var(--fc-secondary-text)}.fcrm_email_sequences_view .fcrm-sequence-schedule{overflow:hidden;color:var(--fc-secondary-text);text-overflow:ellipsis;font-size:14px;font-style:normal;font-weight:400;line-height:20px;letter-spacing:-.084px}.fcrm_email_sequences_view .fcrm-sequence-stats{align-items:center;color:var(--fc-secondary-text);display:flex;flex-wrap:wrap;gap:4px}.fcrm_email_sequences_view .fcrm-stat-item{align-items:center;background:transparent;border:0;color:var(--fc-secondary-text);display:flex;font-size:14px;gap:4px;line-height:20px;padding:0}.fcrm_email_sequences_view .fcrm-stat-item span{overflow:hidden;color:var(--fc-secondary-text);text-overflow:ellipsis;font-size:14px;font-style:normal;font-weight:400;line-height:20px;letter-spacing:-.084px}.fcrm_email_sequences_view .fcrm-stat-item.is-link{cursor:pointer}.fcrm_email_sequences_view .fcrm-stat-item .el-icon{color:var(--fc-text-muted);font-size:14px}.fcrm_email_sequences_view .fcrm-stat-dot{color:var(--fc-text-muted);font-size:14px;line-height:20px;margin:0 2px}@media (max-width: 1024px){.fcrm_email_sequences_view .fcrm-sequence-header{align-items:flex-start;flex-direction:column;gap:12px}}.fcrm_sequence_subscribers_body .fcrm_sequence_subscribers_table_wrap .fcrm_sequence_date_with_note{display:inline-flex;align-items:center;gap:4px}.fcrm_sequence_subscribers_body .fcrm_sequence_subscribers_table_wrap .fcrm_sequence_date_with_note .fcrm_sequence_note_btn{display:inline-flex;align-items:center;justify-content:center;padding:0;border:none;background:none;cursor:pointer;color:var(--fc-secondary-border)}.fcrm_sequence_subscribers_body .fcrm_sequence_subscribers_table_wrap .fcrm_sequence_date_with_note .fcrm_sequence_note_btn .fcrm_sequence_note_icon{display:block;flex-shrink:0}.fcrm-sequence-dropdown-menu .el-dropdown-menu__item .el-icon{margin-right:0!important}.fcrm-sequence-delay-popover{padding:0!important}.fcrm-sequence-delay-editor__header{display:flex;align-items:center;justify-content:space-between;padding:12px 16px;border-bottom:1px solid var(--fc-primary-border);font-size:14px;font-weight:500;color:var(--fc-primary-text)}.fcrm-sequence-delay-editor__close{cursor:pointer;color:var(--fc-text-muted);display:flex}.fcrm-sequence-delay-editor__close:hover{color:var(--fc-secondary-text)}.fcrm-sequence-delay-editor__body{padding:12px 16px}.fcrm-sequence-delay-editor__body .el-input.el-input--suffix{gap:6px}.fcrm-sequence-delay-editor__body .el-input.el-input--suffix .el-input__wrapper{padding:0 0 0 10px}.fcrm-sequence-delay-editor__body .el-input.el-input--suffix .el-input__suffix .el-select{margin:0}.fcrm-sequence-delay-editor__body .el-input.el-input--suffix .el-input__suffix .el-select__wrapper{border:none;border-radius:0 var(--fcrm-border-radius-8) var(--fcrm-border-radius-8) 0;border-left:1px solid var(--fc-primary-border);background:var(--fc-secondary-bg)}.fcrm-sequence-delay-editor__hint{margin:6px 0 0;font-size:12px;color:var(--fc-text-muted)}.fcrm-sequence-delay-editor__footer{display:flex;justify-content:flex-end;gap:8px;padding:12px 16px;border-top:1px solid var(--fc-primary-border)}.fcrm-sequence-delay-editor__footer .el-button{margin:0}span.fc_seq_number{padding:2px 5px;background:var(--fc-deep-bg);color:var(--fc-text-inverse);border-top-left-radius:4px;position:absolute}.fcrm_flow_condition{display:flex;align-items:center;column-gap:10px;font-size:16px}.fc_flow_items{background:var(--fc-secondary-bg)}.fc_flow_items .step-container{max-width:860px;margin:0 auto}.fc_flow_items .step-container .fc_flow_settings{background:var(--fc-primary-bg);padding:20px 30px;margin-bottom:30px;box-shadow:0 1px 8px #0000001a;transition:background .3s,border .3s,border-radius .3s,box-shadow .3s}.fc_flow_items .step-container .fc_flow_settings:hover{box-shadow:0 4px 16px #0003}.flow_nav{margin-top:20px}.fcrm_flow_condition_block{margin-bottom:15px;display:block;overflow:hidden}.fcrm_flow_conditions .fcrm_flow_more{margin-top:24px;display:flex;align-items:center;gap:0;overflow:visible}.fcrm_flow_conditions .fcrm_flow_more .fcrm_flow_more_line{flex:1;height:0;border-top:1px solid var(--fc-primary-border);min-width:12px}.fcrm_flow_conditions p.fcrm_or{display:flex;flex-direction:column;align-items:center;margin:4px 0 0;padding:0;border:none}.fcrm_flow_conditions p.fcrm_or:before,.fcrm_flow_conditions p.fcrm_or:after{content:"";width:1px;height:12px;background:var(--fc-primary-border)}.fcrm_flow_conditions p.fcrm_or span{position:relative;padding:0;font-size:12px;font-weight:500;line-height:16px;letter-spacing:.04em;color:var(--fc-text-muted);background:transparent;border:none}.fluentcrm_body.recurring_campaign_settings,.fluentcrm_body.past_recurring_emails,.fluentcrm_body.recurring_campaign_settings .fc_flow_items,.fluentcrm_body.past_recurring_emails .fc_flow_items,.fluentcrm_body.fc_flow_items{background-color:#f6f6fa!important}.fc_mail_card{background:var(--fc-primary-bg);margin:10px 0;padding:15px}.fc_mail_card p.fc_top_sub{padding:0;margin:0;color:var(--fc-secondary-text);font-size:14px}.fc_mail_card .fc_mail_desc h3{margin:5px 0}.fc_mail_card .fc_mail_actions{text-align:right;display:flex;align-items:center;justify-content:flex-end;flex-direction:row;margin-top:10px}.fcrm_campaign_performance{border:1px solid var(--fc-primary-border);border-radius:8px}.fcrm_campaign_performance h3{margin:0;padding:15px 20px;border-bottom:1px solid var(--fc-primary-border)}.fcrm_campaign_performance_list{margin:0;padding:20px}.fcrm_campaign_performance_item{display:flex;align-items:center;gap:6px;margin:0;padding:15px 0;border-bottom:1px solid var(--fc-primary-border)}.fcrm_campaign_performance_item:last-child{border-bottom:none;padding-bottom:0}.fcrm_campaign_performance_item:first-child{padding-top:0}.fcrm_campaign_performance_item_title{font-size:16px;display:flex;align-items:center;gap:6px}.fcrm_campaign_performance_item_value{margin-left:auto;font-size:16px}.fcrm_campaign_performance_item .icon svg{display:block}.fcrm_edit_campaign_page .fcrm_edit_campaign_steps_wrapper .fcrm_email_campaign_recipient_tagger_selector{margin-top:-8px}.fcrm_edit_campaign_page .fcrm_page_header_top_nav_wrapper{padding-left:32px;padding-right:32px;margin-bottom:24px;align-items:center}.fcrm_edit_campaign_page .fcrm_page_header_top_nav_wrapper .el-breadcrumb{display:flex;align-items:center}.fcrm_edit_campaign_page .fcrm_page_header_top_nav_wrapper .fcrm_page_header_top_nav .el-breadcrumb .el-breadcrumb__item .el-breadcrumb__inner{display:flex;align-items:center;gap:4px;color:var(--fc-primary-text);text-align:center;font-size:14px;font-style:normal;font-weight:500;line-height:20px;letter-spacing:-.084px}.fcrm_edit_campaign_page.fluentcrm-campaign .step-container{margin-top:0}.fcrm_edit_campaign_steps{margin:0;padding:12px 0}.fcrm_edit_campaign_steps .fcrm_edit_campaign_progress_label{font-weight:500;font-size:13px;line-height:20px;color:var(--fc-secondary-text);margin:0 0 6px}.fcrm_edit_campaign_steps .fcrm_edit_campaign_progress_bar{display:flex;gap:4px;width:100%;max-width:280px}.fcrm_edit_campaign_steps .fcrm_edit_campaign_progress_segment{background:var(--fc-secondary-bg);width:30px;flex:1;height:8px;border-radius:4px;cursor:pointer;transition:background-color .2s}.fcrm_edit_campaign_steps .fcrm_edit_campaign_progress_segment.is-filled{background:var(--fc-deep-bg)}.fcrm_all_email_activities_page .el-skeleton{padding:0;line-height:1}.fcrm_all_email_activities_page .el-skeleton__item{display:block}body.fcrm_custom_editor_page .fluentcrm-campaigns.fluentcrm-view-wrapper.fluentcrm_view .fluentcrm_header{display:none!important}.fcrm_recurring_campaign_basic_settings{display:flex;flex-direction:column;gap:16px}.fcrm_recurring_campaign_basic_settings .fcrm_basic_form_item{margin:0;display:flex;flex-direction:column;gap:4px}.fcrm_recurring_campaign_basic_settings .fcrm_basic_form_item label{margin:0;color:var(--fc-primary-text);font-size:14px;font-style:normal;font-weight:500;line-height:20px;letter-spacing:-.084px}.fcrm_recurring_campaign_basic_settings .fcrm_basic_form_item.fcrm_basic_form_item_frequency{gap:8px}.fcrm_recurring_campaign_basic_settings .fcrm_basic_form_item.fcrm_basic_form_item_auto_send .el-form-item__content .fcrm_basic_checkbox_auto_send .el-checkbox__label{color:var(--fc-primary-text);font-size:14px;font-style:normal;font-weight:400;line-height:20px;letter-spacing:-.084px}.fcrm_flow_conditions{display:flex;flex-direction:column;gap:8px}.fcrm_flow_conditions .fcrm_flow_condition_block{margin:0}.fcrm_flow_conditions .fcrm_flow_more{margin-top:8px}.fcrm_flow_conditions .fcrm_flow_condition_block .fcrm_flow_condition{padding:16px;border-radius:8px;background:var(--fc-secondary-bg);justify-content:space-between}.fcrm_flow_conditions .fcrm_flow_condition_block .fcrm_flow_condition .fcrm_conditions_remove_wrap{flex-shrink:0}.fcrm_flow_conditions .fcrm_flow_condition_block .fcrm_flow_condition .fcrm_conditions_row{display:flex;align-items:center;gap:8px}.fcrm_flow_conditions .fcrm_flow_condition_block .fcrm_flow_condition .fcrm_conditions_row .fcrm_conditions_label{color:var(--fc-secondary-text);font-size:14px;font-style:normal;font-weight:500;line-height:20px;letter-spacing:-.084px}.fcrm_flow_conditions .fcrm_flow_condition_block .fcrm_flow_condition .fcrm_conditions_row .fcrm_conditions_select_wrap .el-select .el-select__wrapper{width:96px}.fcrm_flow_conditions .fcrm_flow_condition_block .fcrm_flow_condition .fcrm_conditions_row .fcrm_conditions_input_wrap{width:330px}.fcrm_flow_conditions .fcrm_flow_condition_block .fcrm_flow_condition .fcrm_conditions_row .fcrm_conditions_input_wrap .el-input__wrapper{background:var(--fc-primary-bg)}.fcrm_rate_header_cell{display:flex;align-items:center;gap:6px}.fcrm_rate_header_cell span{display:flex}.fluentcrm-campaigns .fcrm_editor_recommendation_alert{margin-top:12px}.fluentcrm-campaigns .action-buttons{margin:0 0 15px;text-align:right}.fluentcrm-campaigns .action-buttons .el-input__inner{background:var(--fc-primary-bg)!important}.fluentcrm-campaigns .status{display:inline-block;font-size:10px;width:80px}.fluentcrm-campaigns .status-draft{color:var(--fc-text-muted);border:solid 1px var(--fc-text-muted)}.fluentcrm-campaigns .status-pending{color:var(--fc-deep-bg);border:solid 1px var(--fc-deep-bg)}.fluentcrm-campaigns .status-archived{color:var(--fc-success);border:solid 1px var(--fc-success)}.fluentcrm-campaigns .status-failed,.fluentcrm-campaigns .status-incomplete{color:var(--fc-error);border:solid 1px var(--fc-error)}.fluentcrm-campaigns .status-working{color:var(--fc-success-bg);border:solid 1px var(--fc-success-bg);opacity:1;position:relative;transition:opacity linear .1s}.fluentcrm-campaigns .status-working:before{animation:2s linear infinite working;border:solid 3px var(--fc-secondary-bg);border-bottom-color:var(--fc-success-bg);border-radius:50%;content:"";height:10px;left:10px;opacity:inherit;position:absolute;top:50%;transform:translate3d(-50%,-50%,0);transform-origin:center;width:10px;will-change:transform}.fluentcrm-campaigns .status-purged{color:var(--fc-warning);border:solid 1px var(--fc-warning)}.fluentcrm-campaigns .status-purged,.fluentcrm-campaigns .status-failed,.fluentcrm-campaigns .status-working,.fluentcrm-campaigns .status-draft,.fluentcrm-campaigns .status-incomplete,.fluentcrm-campaigns .status-archived,.fluentcrm-campaigns .status-pending{padding:0 4px;display:inline-block;border-radius:4px}.fcrm_recurring_email_report_page .fcrm_page_header{padding-top:5px}@keyframes working{0%{transform:translate3d(-50%,-50%,0) rotate(0)}to{transform:translate3d(-50%,-50%,0) rotate(360deg)}}.fc_promo_heading{padding:30px}.promo_block{margin-bottom:50px}.promo_block:nth-child(2n){background:var(--fc-secondary-bg);padding:25px;margin:0 -25px 50px}.promo_block h2{line-height:160%}.promo_block p{font-size:17px}.promo_image{max-width:100%}.fc_adv_report_demo{min-height:100vh;background-image:url(../../images/promo/advanced_report_demo.png);background-size:cover;max-width:900px;margin:0 auto;position:relative}.fc_adv_report_demo:before{content:"";position:absolute;top:0;right:0;bottom:0;left:0;background-color:#00000040}.fc_adv_report_demo .fc_report_wrap{padding-top:130px}.fc_adv_report_demo .fc_report_promo{background:var(--fc-primary-bg);width:500px;padding:20px;border-radius:8px;margin:0 auto;text-align:center;box-shadow:0 0 10px #0000001a;z-index:999999999;position:relative}.fcrm_pro_modal_body{text-align:center;background:var(--fc-primary-bg)}.fcrm_pro_modal_body.align-left{text-align:left}.fcrm_pro_modal_body.align-left .fcrm_pro_modal_actions{justify-content:flex-start}.fcrm_pro_modal_body.align-right{text-align:right}.fcrm_pro_modal_body.align-right .fcrm_pro_modal_actions{justify-content:flex-end}.fcrm_pro_modal_body .fcrm_pro_icon{width:40px;height:40px;background:var(--fc-warning-bg);color:var(--fc-warning);border-radius:8px;display:flex;align-items:center;justify-content:center;margin:0 auto 16px}.fcrm_pro_modal_body .fcrm_pro_icon svg{width:24px;height:24px;display:block}.fcrm_pro_modal_body h3{font-size:20px;line-height:28px;margin:0 0 4px;font-weight:500;color:var(--fc-primary-text)}.fcrm_pro_modal_body p{color:var(--fc-secondary-text);margin:0;padding:0;font-weight:400;font-size:14px;line-height:20px}.fcrm_pro_modal_body .fcrm_pro_modal_actions{margin-top:20px;display:flex;align-items:center;justify-content:center;gap:12px;flex-wrap:wrap}.fcrm_pro_modal_body .fcrm_pro_modal_actions .el-button{margin:0}.fcrm_pro_modal{border-radius:8px;overflow:hidden}.fcrm_pro_modal .el-dialog__header{padding:15px 20px;border-bottom:1px solid var(--fc-primary-border);margin-right:0}.fcrm_pro_modal .el-dialog__header .el-dialog__title{font-size:16px;font-weight:500;color:var(--fc-primary-text)}.fcrm_pro_modal .el-dialog__body{padding:0}.fcrm_pro_modal .fcrm_pro_modal_body{padding:32px}.fcrm_pro_modal .fcrm_pro_modal_body p{color:var(--fc-secondary-text);text-align:center;font-size:14px;font-style:normal;font-weight:400;line-height:20px;letter-spacing:-.084px}.fc_bulk_campaign_actions_center{display:flex;align-items:center;gap:16px;flex-wrap:wrap}.fc_bulk_selection_count{font-size:14px;color:var(--fc-secondary-text);font-weight:400}.fc_bulk_selection_count strong{font-weight:600;color:var(--fc-secondary-text)}.fc_bulk_divider{width:1px;height:16px;background:var(--fc-primary-border);flex-shrink:0}.fc_bulk_link{background:none;border:none;padding:0;font-size:14px;color:var(--fc-secondary-text);text-decoration:underline;cursor:pointer;font-weight:500}.fc_bulk_link:hover{color:var(--fc-primary-text)}.fcrm_bulk_apply_label_btn{height:32px!important}.fluentcrm-logo{max-height:40px;margin-right:10px;margin-left:10px}.fluentcrm-app{margin-right:20px;color:var(--fc-primary-text)}.fluentcrm-app a{cursor:pointer;text-decoration:none}.fluentcrm-body{margin-top:15px}.fluentcrm_header{display:flex;gap:6px;align-items:center;width:auto;clear:both;overflow:hidden;margin:0;padding:12px 20px;border-bottom:1px solid var(--fc-primary-border);background:var(--fc-primary-bg);font-weight:700;color:var(--fc-secondary-text)}.fluentcrm_header .fluentcrm_header_title{float:left;color:var(--fc-primary-text);font-size:16px;font-style:normal;font-weight:500;line-height:24px;letter-spacing:-.176px}.fluentcrm_header .fluentcrm_header_title h3{display:inline-block;margin:8px 20px 8px 0;padding:0;font-size:20px}.fluentcrm_header .fluentcrm_header_title p{font-weight:400;margin:0}.fluentcrm_header .fluentcrm-actions{float:right;margin-left:auto;display:flex;align-items:center;gap:8px}.fluentcrm_header .fluentcrm-actions .el-input .el-input__wrapper{width:150px}.fluentcrm_header .fluentcrm-actions .el-input .el-input__wrapper .el-input__inner{border:none!important}.fluentcrm_header .fluentcrm-actions .el-select .el-select__wrapper .el-select__selection .el-select__input-wrapper .el-select__input{border:none}.fluentcrm_inner_header{display:block;width:auto;clear:both;overflow:hidden}.fluentcrm_inner_header .fluentcrm_inner_title{float:left}.fluentcrm_inner_header .fluentcrm_inner_actions{float:right}.fluentcrm_inner_header .fluentcrm_inner_actions .fluentcrm_inner_action{display:flex;align-items:center;gap:5px}.fluentcrm_inner_header .fluentcrm_inner_actions .el-radio-group .el-radio-button.non-open .el-radio-button__inner .non-open-icons{position:relative}.fluentcrm_inner_header .fluentcrm_inner_actions .el-radio-group .el-radio-button.non-open .el-radio-button__inner .non-open-icons span{position:absolute;right:-4px;top:-3px;border:1px solid var(--fc-error);color:var(--fc-error);padding:0;width:10px;height:10px;line-height:7px;text-align:center;font-size:10px;border-radius:50%;background:var(--fc-primary-bg);transform:rotate(45deg)}.fluentcrm_inner_header .fluentcrm_inner_actions .el-radio-group .el-radio-button.non-open .el-radio-button__inner .failed-email-icons{position:relative}.fluentcrm_inner_header .fluentcrm_inner_actions .el-radio-group .el-radio-button.non-open .el-radio-button__inner .failed-email-icons .warning-icon{position:absolute;right:-4px;top:-3px;margin:0;width:10px;height:10px;background:var(--fc-primary-bg);border-radius:50%;display:flex;align-items:center;justify-content:center}.fluentcrm_inner_header .fluentcrm_inner_actions .el-radio-group .el-radio-button.non-open .el-radio-button__inner .failed-email-icons .warning-icon svg{width:80%;display:block}.fluentcrm_inner_header .fluentcrm_inner_actions .el-radio-group .el-radio-button.non-open .el-radio-button__inner .failed-email-icons .warning-icon svg path{stroke-width:8px}.fluentcrm_body_boxed{padding:20px;background:var(--fc-secondary-bg);margin-bottom:20px}.fluentcrm-navigation .el-menu-item .dashboard-link{display:flex;align-items:center}.fluentcrm-navigation .el-menu-item:first-of-type{padding-left:0}.fluentcrm-navigation .el-menu-item.is-active{font-weight:500}body.toplevel_page_fluentcrm-admin{background-color:var(--fc-secondary-bg)}body.toplevel_page_fluentcrm-admin *::-webkit-scrollbar{width:8px;height:8px}body.toplevel_page_fluentcrm-admin *::-webkit-scrollbar-track{border-radius:10px;background:transparent}body.toplevel_page_fluentcrm-admin *::-webkit-scrollbar-thumb{background:var(--fc-secondary-border);border-radius:10px}.fluentcrm-body .el-progress_animated .el-progress-bar__outer{background-color:var(--fc-secondary-bg)}.fluentcrm-body .el-progress_animated .el-progress-bar__outer .el-progress-bar__inner{background-color:var(--fc-deep-bg)}.fcrm_email_sequences_view .fcrm_sequence_hero_card{background:var(--fc-primary-bg);border-radius:8px;padding:48px 32px;margin:0 auto;text-align:center;box-shadow:0 1px 3px #0000000f}.fcrm_email_sequences_view .fcrm_sequence_hero_title{font-size:16px;font-weight:500;color:var(--fc-primary-text);margin:0 0 8px;line-height:1.3}.fluentcrm_body{background:var(--fc-primary-bg);display:block;overflow:hidden}.fluentcrm_body .fc_highlight_gray h3{margin:0 0 5px;color:var(--fc-primary-text);text-align:center;font-size:16px;font-style:normal;font-weight:500;line-height:24px;letter-spacing:-.176px}.el-time-range-picker__content .el-time-spinner__item{margin-bottom:0}.el-collapse.fluentcrm_accordion .el-collapse-item__wrap{padding:20px;background:var(--fc-secondary-bg);border:1px solid var(--fc-primary-border)}.el-collapse.fluentcrm_accordion .el-collapse-item__header{padding:10px 15px;border:1px solid var(--fc-secondary-border);background:var(--fc-secondary-border)}.fluentcrm-subscribers .fluentcrm-header-secondary{background:var(--fc-primary-bg);overflow:auto}.fluentcrm-subscribers .fluentcrm_header .fluentcrm-actions .el-button{font-size:12px}li#toplevel_page_fluentcrm-admin ul.wp-submenu a[href="admin.php?page=fluentcrm-admin#/email/campaigns"],li#toplevel_page_fluentcrm-admin ul.wp-submenu a[href="admin.php?page=fluentcrm-admin#/subscribers"],li#toplevel_page_fluentcrm-admin ul.wp-submenu a[href="admin.php?page=fluentcrm-admin#/settings"],li#toplevel_page_fluentcrm-admin ul.wp-submenu a[href="admin.php?page=fluentcrm-admin#/forms"]{margin-top:5px;padding-top:8px;border-top:2px solid rgba(240,246,252,.2)}li#toplevel_page_fluentcrm-admin ul.wp-submenu a[href="admin.php?page=fluentcrm-admin#/email/recurring-campaigns"],li#toplevel_page_fluentcrm-admin ul.wp-submenu a[href="admin.php?page=fluentcrm-admin#/email/sequences"],li#toplevel_page_fluentcrm-admin ul.wp-submenu a[href="admin.php?page=fluentcrm-admin#/email/templates"],li#toplevel_page_fluentcrm-admin ul.wp-submenu a[href="admin.php?page=fluentcrm-admin#/contact-groups/companies"],li#toplevel_page_fluentcrm-admin ul.wp-submenu a[href="admin.php?page=fluentcrm-admin#/contact-groups/lists"],li#toplevel_page_fluentcrm-admin ul.wp-submenu a[href="admin.php?page=fluentcrm-admin#/contact-groups/tags"]{margin-left:8px;font-size:90%}@media (max-width: 782px){.fluentcrm-app{padding:0 20px 20px;margin:0}}.fluentcrm_settings_wrapper .el-row .el-col .el-menu .el-menu-item{height:52px;display:flex;align-items:center;gap:8px}.fluentcrm_settings_wrapper .el-row .el-col .el-menu .el-menu-item svg{margin:0;fill:var(--fc-text-muted)}.fluentcrm_settings_wrapper .el-row .el-col .el-menu .el-menu-item.is-active svg{fill:var(--el-menu-active-color)}.fc_global_form_builder_label_with_info .el-form-item__label{display:flex!important;align-items:center;gap:6px;margin-bottom:0!important}.fc_global_form_builder_label_with_info .el-form-item__label .el-tooltip__trigger{display:flex;align-items:center}.fc_global_form_builder_label_with_info .el-form-item__label .el-tooltip__trigger svg{fill:var(--fc-primary-text);width:15px;height:15px}.fluentcrm-profile h1,.fluentcrm-profile h2{margin-top:0;margin-bottom:0}.fluentcrm-profile .header{display:flex;align-items:center;justify-content:space-between;margin-bottom:10px}.fluentcrm-profile .noter label{display:flex;justify-content:space-between;padding:initial}.fluentcrm-profile .info-item .items{margin-bottom:10px;display:flex;flex-wrap:wrap}.fluentcrm-profile .info-item .el-dropdown button{padding:5px 15px;background:var(--fc-primary-bg);border:1px solid var(--fc-primary-border)}.fluentcrm_profile_header{display:flex;align-items:flex-start;gap:12px}.fluentcrm_profile_header .fluentcrm_profile-photo{margin:0}.fluentcrm_profile_header .profile_title{margin-bottom:10px;display:flex;flex-wrap:wrap;gap:4px}.fluentcrm_profile_header .profile_title h3{margin:0;padding:0;display:inline-block}.fluentcrm_profile_header p{margin:0 0 5px;padding:0}.fluentcrm_profile_header .actions{margin-left:auto;margin-bottom:auto}@media (max-width: 500px){.fluentcrm_profile_header{flex-wrap:wrap;gap:10px}}ul.fluentcrm_profile_nav{margin:30px 0 0;padding:0;display:block;width:100%;list-style:none;background-color:var(--fc-primary-bg)!important;position:relative;border-top-left-radius:6px;border-top-right-radius:6px;border-bottom:1px solid hsla(0,0%,87%,.659)}ul.fluentcrm_profile_nav li{display:inline-block;margin-right:0;cursor:pointer;font-size:14px;font-weight:500;color:var(--fc-secondary-text)!important;text-transform:none;letter-spacing:.5px;margin-bottom:0;padding:17px 22px}ul.fluentcrm_profile_nav li.item_active,ul.fluentcrm_profile_nav li:hover{color:var(--fc-text-link)!important;border-bottom:2px solid var(--fc-text-link)}.profile_title h1,.profile_title .fcrm_profile_action{display:inline-block;vertical-align:top}.profile_title .fcrm_profile_action .el-popover__reference-wrapper .el-tag{cursor:pointer;line-height:18px}.fluentcrm_profile-photo{position:relative}.fluentcrm_profile-photo .fc_photo_holder_mini{width:80px;height:80px;border:none;border-radius:50%;vertical-align:middle;background-position:center center;background-repeat:no-repeat;background-size:cover}.fluentcrm_profile-photo .fc_photo_holder{width:128px;height:128px;margin-right:25px;border:6px solid var(--fc-secondary-bg);border-radius:50%;vertical-align:middle;background-position:center center;background-repeat:no-repeat;background-size:cover}.fluentcrm_profile-photo .fcrm_profile_photo_actions{position:absolute;top:0;right:0;display:flex;flex-direction:column;gap:8px;align-items:center;justify-content:center;width:100%;height:100%;background:#3333333d;border-radius:50%;opacity:0;visibility:hidden;transition:.3s;-webkit-transition:.3s}.fluentcrm_profile-photo .fcrm_profile_photo_actions .fluentcrm_photo_holder{gap:8px}.fluentcrm_profile-photo .fcrm_profile_photo_actions .el-button{margin:0;padding:2px;width:28px;height:28px;transition:.5s;transform:scale(.1)}.fluentcrm_profile-photo .fcrm_profile_photo_actions .el-button:first-child{transition:.3s}.fluentcrm_profile-photo:hover .fcrm_profile_photo_actions{opacity:1;visibility:visible}.fluentcrm_profile-photo:hover .fcrm_profile_photo_actions .el-button{transform:scale(1)}.fluentcrm_profile-photo .fc_photo_actions{position:absolute;top:-32px;right:-22px}.stats_badges{display:flex}.stats_badges>span{border:1px solid var(--fc-text-link);margin:0 -1px 0 0;padding:3px 6px;display:inline-block;background:#2225301a;color:var(--fc-deep-bg)}.stats_badges>span:first-child{border-bottom-left-radius:3px;border-top-left-radius:3px}.stats_badges>span:last-child{border-bottom-right-radius:3px;border-top-right-radius:3px}.stats_badges .stats_link{cursor:pointer}.fcrm_contact_popover .fcrm_contact_popover_header{display:flex;align-items:center;gap:2px;margin-bottom:6px;padding:4px 0}.fcrm_contact_popover .fcrm_contact_popover_header svg{flex-shrink:0}.fcrm_contact_popover .fcrm_contact_popover_header h3{margin:0;color:var(--fc-text-muted);font-size:11px;font-style:normal;font-weight:500;line-height:12px;letter-spacing:.22px;text-transform:uppercase}.fcrm_contact_popover .fcrm_contact_popover_header .fcrm_popover_header .fcrm_back_button{padding-left:0;padding-right:5px}.fcrm_contact_popover .fcrm_contact_popover_body{display:flex;align-items:flex-start;gap:8px}.fcrm_contact_popover .fcrm_contact_popover_body .el-select{flex:2}.fcrm_contact_popover .fcrm_popover_email_section{flex-direction:column}.fcrm_contact_popover .fcrm_popover_email_section .fcrm_primary_btn1{margin-right:auto;margin-top:5px}.fcrm_contact_popover .fcrm_popover_body_flex_direction_column{flex-direction:column}.fcrm_contact_popover .fcrm_popover_body_flex_direction_column .fcrm_full_width{width:100%}.fcrm_contact_popover .fcrm_popover_body_flex_direction_column .fcrm_popover_actions,.fcrm_contact_popover .fcrm_popover_body_flex_direction_column .el-radio-group{margin-right:auto}.fcrm_contact_popover .fcrm_popover_body_flex_direction_column .el-radio-group .fcrm-radio{margin-right:10px}.fluentcrm_photo_holder{display:inline-flex;align-items:flex-end}.fluentcrm_photo_holder img{max-height:100px;margin-right:6px;border-radius:4px;border:1px solid var(--fc-primary-border)}span.ns_counter{padding:2px 7px;border:1px solid var(--fc-primary-border);margin:0 10px 0 0;border-radius:var(--fcrm-border-radius-8, 8px);background:var(--fc-secondary-bg);color:var(--fc-primary-text)}.fluentcrm_hero_box{max-width:700px;margin:30px auto;text-align:center;padding:45px 20px;background:var(--fc-secondary-bg);border-radius:10px}.fluentcrm_sub_info_body .fluentcrm_databox{border-top-left-radius:0;border-top-right-radius:0;margin-top:0}.fc_half_field{width:50%;padding-right:10px;position:relative;overflow:hidden;display:inline-block;vertical-align:top}.fc_half_field:nth-child(2n+2){display:inline-block;clear:both;padding-right:0;padding-left:10px}.fc_2col_inline{padding-right:10px;position:relative;overflow:hidden;display:inline-block;vertical-align:top;max-width:50%}.fc_2col_inline:nth-child(2n+2){display:inline-block;clear:both}.fluentcrm_profile_header_warpper h2{font-size:16px}.fluentcrm_profile_header_warpper .el-tag--white{height:auto;padding:0 8px;line-height:24px;font-size:11px;white-space:normal;word-break:break-all}.fluentcrm_profile_header_warpper .el-tag--white+.el-tag--white{margin-left:10px}.fluentcrm_profile_header_warpper .fc_profile_tagger .header{justify-content:left}.fluentcrm_profile_header_warpper .fc_profile_tagger .header h2{padding-right:20px}.fc_visual_body{z-index:2;position:relative}.fc_visual_body .editor-add-shortcode{margin-bottom:5px}.fluentcrm_edit_basic .el-select .el-input__inner{height:40px}.fcrm_fullscreen_active{position:fixed!important;top:0!important;left:0!important;right:0!important;bottom:0!important;z-index:999999!important;background:var(--fc-primary-bg)!important;padding:0!important;margin:0!important;overflow:hidden!important;width:100%!important;height:100%!important}.fc_2col_form_wrapper{display:block;width:100%;overflow:hidden}.fc_2col_form_wrapper .el-form-item{width:49%;float:left;padding-right:0;padding-left:15px}.fc_2col_form_wrapper .el-form-item:nth-child(odd){clear:left;padding-left:0}.fc_2col_form_wrapper .el-form-item :nth-child(2n){padding-right:5px;gap:10px}.fc_3col_form_wrapper{display:block;width:100%;overflow:hidden}.fc_3col_form_wrapper .el-form{display:grid;grid-template-columns:1fr 1fr 1fr;gap:30px}.fc_3col_form_wrapper .el-form .el-form-item{margin:0}.fc_3col_form_wrapper .el-form .el-form-item:nth-child(odd){clear:left;padding-left:0}.fc_3col_form_wrapper .el-form .el-form-item :nth-child(2n){padding-right:5px}.fc_settings_popup .fc_block_collapse{border:none}.fc_settings_popup .fc_block_collapse .el-collapse-item+.el-collapse-item{border-top:1px solid var(--fc-primary-border)}.fc_settings_popup .fc_block_collapse .el-collapse-item.is-active .el-collapse-item__header{background:var(--fc-secondary-bg);color:var(--fc-deep-bg)}.fc_settings_popup .fc_block_collapse .el-collapse-item .el-collapse-item__header{border:none;padding:0 10px 0 15px;transition:.3s}.fc_settings_popup .fc_block_collapse .el-collapse-item .el-collapse-item__wrap{border:none}.fc_settings_popup .fc_block_collapse .el-collapse-item .el-collapse-item__content{padding:15px 15px 20px}.fc_editor_warnning{color:var(--fc-error)}#fluentcrm_block_editor_x{overflow:auto}.fc_complience_suggest{padding:10px 20px}.fluentcrm_visual_editor .fc_visual_header{display:flex;justify-content:space-between;align-items:center}.fluentcrm_visual_editor .fc_visual_header .fluentcrm_header_title{float:none;display:flex;align-items:center}.fluentcrm_visual_editor .fc_visual_header .fluentcrm_header_title h3{margin:0 0 0 5px;display:inline-flex;align-items:center}.fluentcrm_visual_editor .fc_visual_header .fluentcrm_header_title h3>span{margin-right:5px}.fluentcrm_visual_editor .fc_visual_header .fc_editor_segmented_buttons,.fluentcrm_visual_editor .fc_visual_header .fc_editor_segmented_buttons .fc_style_editor,.fluentcrm_visual_editor .fc_visual_header .fc_editor_segmented_buttons .fc_email_preview{margin-right:10px}.fluentcrm_visual_editor .fc_visual_header .fc_editor_segmented_buttons .fc_style_editor .el-button,.fluentcrm_visual_editor .fc_visual_header .fc_editor_segmented_buttons .fc_email_preview .el-button{height:32px;border-radius:4px}.fluentcrm_visual_editor .fc_visual_header .fc_editor_segmented_buttons .fc_segmented_btn{margin-left:0!important;border-radius:0;min-width:36px;padding:8px 10px;border:1px solid var(--fc-primary-border);background:var(--fc-primary-bg);color:var(--fc-secondary-text)}.fluentcrm_visual_editor .fc_visual_header .fc_editor_segmented_buttons .fc_segmented_btn .el-icon{color:inherit}.fluentcrm_visual_editor .fc_visual_header .fc_editor_segmented_buttons .fc_segmented_btn:hover{background:var(--fc-secondary-bg);color:var(--fc-secondary-text);border-color:var(--fc-secondary-border)}.fluentcrm_visual_editor .fc_visual_header .fc_editor_segmented_buttons .fc_style_editor .fc_segmented_btn{border-right-width:0}.fluentcrm_visual_editor .fc_visual_header .fc_editor_segmented_buttons .fc_style_editor:only-child .fc_segmented_btn{border-radius:6px;border-right-width:1px}.fluentcrm_visual_editor .fc_visual_header .fc_editor_segmented_buttons .fc_email_preview .fc_segmented_btn{border-left-width:1px}.fluentcrm_visual_editor .fc_visual_header .fc_editor_segmented_buttons .fc_email_preview:only-child .fc_segmented_btn{border-radius:6px}.fluentcrm_visual_editor .fc_visual_header .fc_editor_segmented_buttons .fc_segmented_btn.fc_segmented_active{background:var(--fc-secondary-text);border-color:var(--fc-secondary-text);color:var(--fc-text-inverse)}.fluentcrm_visual_editor .fc_visual_header .fc_editor_segmented_buttons .fc_segmented_btn.fc_segmented_active .el-icon{color:var(--fc-text-inverse)}.fluentcrm_visual_editor .fc_visual_header .fc_editor_segmented_buttons .fc_segmented_btn.fc_segmented_active:hover{background:var(--fc-deep-bg);border-color:var(--fc-deep-bg);color:var(--fc-text-inverse)}.fluentcrm_visual_editor .fc_visual_header .fc_editor_segmented_buttons .fc_segmented_active+.fc_email_preview .fc_segmented_btn,.fluentcrm_visual_editor .fc_visual_header .fc_editor_segmented_buttons .fc_style_editor .fc_segmented_active~* .fc_segmented_btn{border-left-color:var(--fc-primary-border)}.fluentcrm_visual_editor .fc_visual_body.fcrm_not_guten{z-index:inherit}.fcrm_preview_stage{padding:24px 16px 40px;transition:background .3s ease}.fc_device_frame{margin:0 auto;transition:all .35s ease;max-width:100%;position:relative}.fc_device_frame_desktop{width:100%}.fc_device_frame_tablet{width:768px;border:10px solid #1e293b;border-radius:20px;box-shadow:0 0 0 1px #334155,0 24px 60px #00000040;overflow:hidden}.fc_device_frame_tablet :deep(iframe){border-radius:10px}.fc_device_frame_mobile{width:375px;border:12px solid #1e293b;border-radius:44px;box-shadow:0 0 0 1px #334155,0 28px 70px #0000004d;overflow:hidden}.fc_device_frame_mobile :deep(iframe){border-radius:0}.fc_device_notch{height:28px;background:#1e293b;display:flex;align-items:center;justify-content:center}.fc_device_notch:after{content:"";width:90px;height:10px;background:#0f172a;border-radius:5px}.fc_device_home{height:20px;background:#1e293b;display:flex;align-items:center;justify-content:center}.fc_device_home:after{content:"";width:100px;height:4px;background:#475569;border-radius:2px}.fcrm_device_label{font-size:12px;color:var(--fc-secondary-text);margin-left:8px;white-space:nowrap}.fcrm_preview_empty{padding:40px 20px;text-align:center;color:var(--fc-secondary-text)}.fluentcrm_view .el-form .el-form-item textarea,.fluentcrm_view.fcrm-abandon-reports-wrapper .fcrm_page_header{padding-top:8px}.fluentcrm_view.fcrm-abandon-reports-wrapper .fcrm_page_header .el-date-editor{width:200px;border-radius:var(--fcrm-border-radius-8);padding:0 10px;background:var(--fc-primary-bg)}.fluentcrm_view.fcrm-abandon-reports-wrapper .fcrm-abandon-report-carts-wrap{margin-top:30px}.fluentcrm_view.fcrm-abandon-reports-wrapper .fcrm-alert-warning{margin-bottom:10px}.fluentcrm_view.fcrm-abandon-reports-wrapper .fcrm-alert-warning .el-alert__content{padding:0}.fluentcrm_view.fcrm-abandon-reports-wrapper .fcrm-alert-warning .el-alert__content .el-alert__closebtn{display:none}.fluentcrm_view.fcrm-abandon-reports-wrapper .fcrm-alert-warning p{margin:0}.fluentcrm_view.fcrm-abandon-reports-wrapper .fcrm-alert-warning p a{text-decoration:underline;font-weight:500}.fc_smtp_email_dialog_view .fc_smtp_email_log_items{display:grid;grid-template-columns:repeat(2,1fr);column-gap:2px;row-gap:10px;margin:0}.fc_smtp_email_dialog_view .fc_smtp_email_log_items .fail{color:var(--fc-error)}.fc_smtp_email_dialog_view .fc_smtp_email_log_items .success{color:var(--fc-deep-bg)}.fc_smtp_email_dialog_view .fc_smtp_email_log_items .resent{color:var(--fc-success-bg)}.fc_smtp_email_dialog_view .fc_smtp_email_log_items li{display:flex;align-items:center;gap:10px;border-bottom:1px solid var(--fc-primary-border);padding:10px 0;margin:0}.fc_smtp_email_dialog_view .fc_smtp_email_log_items li .item_header{font-weight:500}.fc_smtp_email_logs_table .el-table .el-table__body-wrapper tbody tr.row_type_failed{background:var(--fc-error-bg)}.fcrm_abandon_cart_details_wrap .fcrm_abandon_cart_address_wrap{display:grid;grid-template-columns:repeat(2,1fr);gap:24px;align-items:flex-start;margin-bottom:24px}.fcrm_abandon_cart_details_wrap .fcrm_abandon_cart_address_wrap .fcrm_abandon_cart_address h4{margin:0 0 .5rem;color:var(--fc-secondary-text);font-size:12px;font-style:normal;font-weight:500;line-height:16px}.fcrm_abandon_cart_details_wrap .fcrm_abandon_cart_address_wrap .fcrm_abandon_cart_address p{color:var(--fc-primary-text);font-size:14px;font-style:normal;font-weight:400;line-height:20px;letter-spacing:-.084px}.fcrm_abandon_cart_details_wrap table{width:100%;text-align:left;margin:0;padding:0;border-spacing:0}.fcrm_abandon_cart_details_wrap table thead tr th{padding:14px 20px;color:var(--fc-primary-text);border-bottom:1px solid var(--fc-primary-border)!important;background-color:transparent!important}.fcrm_abandon_cart_details_wrap table thead tr th:nth-child(3){text-align:center;width:100px}.fcrm_abandon_cart_details_wrap table thead tr th:last-child{text-align:right;width:150px}.fcrm_abandon_cart_details_wrap table tbody tr td{padding:4px 20px;color:var(--fc-primary-text)}.fcrm_abandon_cart_details_wrap table tbody tr td:nth-child(3){text-align:center;width:100px}.fcrm_abandon_cart_details_wrap table tbody tr td:last-child{text-align:right;font-weight:600;width:130px}.fcrm_abandon_cart_details_wrap table tbody tr td.product_image{width:60px}.fcrm_abandon_cart_details_wrap table tbody tr td.product_image img{width:60px;height:60px;object-fit:contain;display:block}.fcrm_abandon_cart_details_wrap table tfoot tr:first-child td{border-top:1px solid var(--fc-light-bg)}.fcrm_abandon_cart_details_wrap table tfoot tr td{padding:12px 20px;color:var(--fc-primary-text);font-weight:400}.fcrm_abandon_cart_details_wrap table tfoot tr td:last-child{text-align:right;font-weight:600}.fcrm_abandon_cart_details_wrap table tfoot tr.total-tr td{font-weight:600}.fcrm_abandon_cart_details_wrap table tfoot tr.discount-tr .fc_cart_coupons{margin-left:6px;font-size:12px;color:var(--fc-secondary-text)}.fcrm_abandon_cart_details_wrap .fcrm_cart_details_extra_info{margin-bottom:24px}.fcrm_abandon_cart_details_wrap .fcrm_cart_details_extra_info hr{margin:24px 0;border:none;border-top:1px solid var(--fc-light-bg)}.fcrm_abandon_cart_details_wrap .fcrm_cart_details_extra_info h4{color:var(--fc-primary-text);font-size:14px;font-style:normal;font-weight:500;line-height:20px;letter-spacing:-.084px;margin:0}.fcrm_abandon_cart_details_wrap .fcrm_cart_details_extra_info p{color:var(--fc-primary-text);font-size:14px;font-style:normal;font-weight:400;line-height:20px;letter-spacing:-.084px;margin:0}.fcrm_abandon_cart_details_wrap .fcrm_cart_details_extra_info .fc-item-copier-input{width:60%;margin-top:5px}.fcrm_reports_home .fcrm_reports_layout{display:flex;gap:0;min-height:600px;margin:-15px -20px 0;position:relative;padding-inline-start:272px;transition:.3s;-webkit-transition:.3s}.fcrm_reports_home .fcrm_reports_layout.is-collapsed{padding-inline-start:78px}.fcrm_reports_home .fcrm_reports_layout.is-collapsed .fcrm_reports_sidebar{width:78px}.fcrm_reports_home .fcrm_reports_layout.is-collapsed .fcrm_reports_sidebar .fcrm_reports_sidebar_header--btn:hover{cursor:e-resize}.fcrm_reports_home .fcrm_reports_layout.is-collapsed .fcrm_reports_sidebar .fcrm_reports_sidebar_header--title,.fcrm_reports_home .fcrm_reports_layout.is-collapsed .fcrm_reports_sidebar .fcrm_nav_label{opacity:0;visibility:hidden}.fcrm_reports_home .fcrm_reports_layout.is-collapsed.is-hover-expanded .fcrm_reports_sidebar{width:272px;box-shadow:0 16px 32px -12px #0e121b1a}.fcrm_reports_home .fcrm_reports_layout.is-collapsed.is-hover-expanded .fcrm_reports_sidebar .fcrm_reports_sidebar_header--title,.fcrm_reports_home .fcrm_reports_layout.is-collapsed.is-hover-expanded .fcrm_reports_sidebar .fcrm_nav_label{opacity:1;visibility:visible}.fcrm_reports_home .fcrm_reports_sidebar{width:272px;flex:none;background:var(--fc-primary-bg);border-right:1px solid var(--fc-primary-border);padding:0;overflow-x:hidden;transition:width .3s ease;position:absolute;left:0;top:0;height:100%}@media (max-width: 768px){.fcrm_reports_home .fcrm_reports_sidebar{width:180px;min-width:180px}}@media (max-width: 480px){.fcrm_reports_home .fcrm_reports_sidebar{width:180px;min-width:180px}}.fcrm_reports_home .fcrm_reports_sidebar_header{display:flex;align-items:center;gap:10px;padding:12px 20px 12px 26px;background:var(--fc-primary-bg);border-bottom:1px solid var(--fc-primary-border);height:52px}.fcrm_reports_home .fcrm_reports_sidebar_header--btn{display:flex;align-items:center;justify-content:center;cursor:pointer;color:var(--fc-secondary-text);flex-shrink:0;transition:color .2s ease;border:none;background:transparent;padding:0;-webkit-appearance:none;-moz-appearance:none;appearance:none}.fcrm_reports_home .fcrm_reports_sidebar_header--btn:hover{cursor:w-resize}.fcrm_reports_home .fcrm_reports_sidebar_header--btn:focus-visible{outline:2px solid var(--fc-primary-text);outline-offset:2px;border-radius:2px}.fcrm_reports_home .fcrm_reports_sidebar_header--btn svg{display:block}.fcrm_reports_home .fcrm_reports_sidebar_header--btn:hover{color:var(--fc-primary-text)}.fcrm_reports_home .fcrm_reports_sidebar_header--title{font-size:16px;font-weight:600;color:var(--fc-primary-text);transition:opacity .3s ease,visibility .3s ease;white-space:nowrap}.fcrm_reports_home .fcrm_reports_nav{list-style:none;margin:0;padding:16px;border-right:none;transition:.2s;background:none}.fcrm_reports_home .fcrm_reports_nav li{display:flex;align-items:center;gap:8px;position:relative;cursor:pointer;padding:8px 12px;border-radius:var(--fcrm-border-radius-8);margin:0 0 4px}.fcrm_reports_home .fcrm_reports_nav li:last-child{margin-bottom:0}.fcrm_reports_home .fcrm_reports_nav li:hover,.fcrm_reports_home .fcrm_reports_nav li.fc_active{background:var(--fc-secondary-bg)}.fcrm_reports_home .fcrm_reports_nav li.fc_active .fcrm_nav_label,.fcrm_reports_home .fcrm_reports_nav li.fc_active .fcrm_nav_icon{color:var(--fc-primary-text)}.fcrm_reports_home .fcrm_reports_nav li.fc_active:before{opacity:1;visibility:visible}.fcrm_reports_home .fcrm_reports_nav li .fcrm_nav_icon{display:flex;align-items:center;justify-content:center;flex-shrink:0;width:20px;height:20px;color:var(--fc-secondary-text)}.fcrm_reports_home .fcrm_reports_nav li .fcrm_nav_icon svg{display:block}.fcrm_reports_home .fcrm_reports_nav li .fcrm_nav_icon img{width:16px;height:16px;object-fit:contain}.fcrm_reports_home .fcrm_reports_nav li .fcrm_nav_label{display:block;color:var(--fc-secondary-text);font-weight:500;font-size:14px;line-height:20px;max-width:200px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.fcrm_reports_home .fcrm_reports_nav li:before{content:"";position:absolute;top:50%;left:-16px;width:4px;height:calc(100% - 16px);transform:translateY(-50%);background:var(--fc-primary-text);border-radius:0 var(--fcrm-border-radius-8) var(--fcrm-border-radius-8) 0;transition:.2s;opacity:0;visibility:hidden}.fcrm_reports_home .fcrm_reports_content{flex:1;padding:20px;min-width:0;overflow-x:auto}.fcrm-report-collapse-sidebar-btn{display:none}@media (max-width: 1024px){.fcrm-report-collapse-sidebar-btn{display:block}}.fcrm_report_section .fcrm_report_section_title{font-size:18px;font-weight:600;color:var(--fc-primary-text);margin:0 0 20px;letter-spacing:-.27px}.fcrm_report_section .fcrm_page_header{margin:-20px -20px 20px;background:var(--fc-primary-bg);border-bottom:1px solid var(--fc-primary-border);padding:8px 20px;min-height:52px}.fcrm_report_section .fcrm_page_header_title{margin:0;display:flex;align-items:center;gap:4px}.fcrm_report_section .fcrm_page_header_title .el-tooltip__trigger{font-size:14px;color:var(--fc-text-muted)}.fcrm_report_section .fcrm_page_header_actions .el-date-editor{background:var(--fc-primary-bg);padding:0 8px;border-radius:var(--fcrm-border-radius-8);max-width:230px}.fcrm_report_section .fcrm_card_widgets{grid-template-columns:repeat(auto-fit,minmax(210px,1fr))}.fcrm_report_section .fcrm_card_widget{cursor:auto}.fcrm_period_selector{display:inline-flex;gap:4px}.fcrm_period_selector .el-button{margin:0;color:var(--fc-text-muted)}.fcrm_period_selector .el-button.is_active{border-color:var(--fc-primary-text);color:var(--fc-primary-text);background:var(--fc-secondary-bg)}.fcrm_period_selector .el-button.is_active:hover{border-color:var(--fc-primary-text)}.fcrm_period_selector .fcrm_period_btn{border:none;box-shadow:none;border-radius:8px;color:var(--fc-secondary-text);font-weight:500;font-size:14px;line-height:20px;background:none;padding:6px 12px;cursor:pointer}.fcrm_period_selector .fcrm_period_btn:hover,.fcrm_period_selector .fcrm_period_btn.fc_active{background:var(--fc-secondary-bg);color:var(--fc-primary-text)}.fcrm_report_card{background:var(--fc-primary-bg);border-radius:var(--fcrm-border-radius-8);margin-bottom:20px}.fcrm_report_card.fcrm_danger_border{border:1px solid var(--fc-error)}.fcrm_report_card.fcrm_warning_border{border:1px solid var(--fc-warning)}.fcrm_report_card.fcrm_success_border{border:1px solid var(--fc-success)}.fcrm_report_card.fcrm_primary_border{border:1px solid var(--fc-primary-border)}.fcrm_report_card .fcrm_report_card_header{display:flex;align-items:center;justify-content:space-between;padding:10px 20px;border-bottom:1px solid var(--fc-primary-border);flex-wrap:wrap;gap:10px;min-height:56px}.fcrm_report_card .fcrm_report_card_header h4{margin:0;font-size:15px;font-weight:500;color:var(--fc-primary-text)}.fcrm_report_card .fcrm_report_card_title_group{display:flex;align-items:center;gap:12px}.fcrm_report_card .fcrm_report_card_actions{display:flex;align-items:center;gap:10px;flex-wrap:nowrap}.fcrm_report_card .fcrm_report_card_actions .el-select{width:180px;flex-shrink:0}.fcrm_report_card .fcrm_report_card_actions .el-date-editor{width:auto;max-width:280px;border-radius:8px;flex-shrink:0;padding:6px 10px;height:auto}@media (max-width: 768px){.fcrm_report_card .fcrm_report_card_actions{flex-wrap:wrap}}.fcrm_report_card .fcrm_report_card_body{padding:20px;min-height:100px}.fcrm_report_card.fcrm_report_card--contacts-by-country .fcrm_report_card_body{padding:0}.fcrm_country_map_layout{display:flex;min-height:420px}@media (max-width: 1100px){.fcrm_country_map_layout{flex-direction:column}}.fcrm_country_map_col{flex:1;min-width:0;position:relative}.fcrm_country_map{width:100%}.fcrm_country_map_empty{min-height:200px;display:flex;align-items:center;justify-content:center;color:var(--fc-text-muted, #999);font-size:13px}.fcrm_country_map_controls{position:absolute;top:10px;right:10px;z-index:10;display:flex;flex-direction:column;gap:4px}.fcrm_country_map_controls .el-button{margin:0}.fcrm_country_map_controls .el-button .icon svg{width:16px;height:16px}.fcrm_country_list_col{width:280px;min-width:260px;border-left:1px solid var(--fc-primary-border, #e5e7eb);display:flex;flex-direction:column}@media (max-width: 1024px){.fcrm_country_list_col{border-left:none;width:100%}}.fcrm_country_list_search{padding:12px 20px}.fcrm_country_list_search .el-input .el-input__wrapper{background:var(--fc-secondary-bg);box-shadow:none;border:none}.fcrm_country_list_scroll{flex:1;overflow-y:auto;max-height:400px}.fcrm_comparison_report_table .fcrm_report_card_body{padding:0}.fcrm_chart_date_summary{text-align:center;font-size:12px;color:var(--fc-secondary-text);margin:8px 0 0}.fcrm_chart_empty{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:60px 20px;color:var(--fc-secondary-text)}.fcrm_chart_empty_icon{margin-bottom:16px;color:var(--fc-primary-text)}.fcrm_chart_empty_title{font-size:15px;font-weight:600;color:var(--fc-primary-text);margin:0 0 4px}.fcrm_chart_empty_hint{font-size:13px;color:var(--fc-text-muted, #999);margin:0}.fcrm_country_list_table{width:100%;border-collapse:collapse;font-size:14px}.fcrm_country_list_table thead th{text-align:left;font-weight:400;font-size:14px;line-height:20px;color:var(--fc-secondary-text);border-top:1px solid var(--fc-primary-border);border-bottom:1px solid var(--fc-primary-border);background:var(--fc-weak-bg-25);padding:8px 12px}.fcrm_country_list_table thead th:first-child{padding-left:20px}.fcrm_country_list_table thead th:last-child{padding-right:28px}.fcrm_country_list_table tbody td{color:var(--fc-primary-text);border-bottom:1px solid var(--fc-primary-border);font-weight:400;font-size:14px;line-height:20px;padding:10px 12px}.fcrm_country_list_table tbody td:first-child{padding-left:20px}.fcrm_country_list_table tbody td:last-child{padding-right:28px}.fcrm_country_list_table tbody tr:last-child td{border-bottom:none}.fcrm_country_list_table .fcrm_country_list_name{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:0;width:100%}.fcrm_country_list_table .fcrm_country_list_count{text-align:center;white-space:nowrap}.fcrm_campaign_link{color:var(--fc-deep-bg);font-weight:500;text-decoration:none}.fcrm_campaign_link:hover{text-decoration:underline}.fcrm_campaign_date{font-size:12px;color:var(--fc-secondary-text);margin-top:2px}.fcrm_rate_bar_wrap{display:flex;align-items:center;gap:8px;min-width:100px}.fcrm_rate_bar_wrap .fcrm_rate_text{font-size:12px;font-weight:600;color:var(--fc-primary-text);white-space:nowrap;min-width:40px;text-align:right}.fcrm_rate_bar{flex:1;height:6px;background:var(--fc-secondary-bg);border-radius:3px;overflow:hidden;min-width:50px}.fcrm_rate_bar .fcrm_rate_bar_fill{height:100%;border-radius:3px;transition:width .4s ease}.fcrm_rate_bar.fcrm_rate_good .fcrm_rate_bar_fill{background:var(--fc-success)}.fcrm_rate_bar.fcrm_rate_ok .fcrm_rate_bar_fill{background:var(--fc-warning)}.fcrm_rate_bar.fcrm_rate_low .fcrm_rate_bar_fill{background:var(--fc-error)}.fcrm_report_pagination{display:flex;justify-content:center;margin-top:16px;padding-top:16px;border-top:1px solid var(--fc-primary-border)}.fcrm_step_list{display:flex;flex-direction:column;gap:8px}.fcrm_step_item{border:1px solid var(--fc-primary-border);border-radius:var(--fcrm-border-radius-8);padding:14px 16px}.fcrm_step_item .fcrm_step_header{display:flex;align-items:center;gap:10px;margin-bottom:10px}.fcrm_step_item .fcrm_step_index{display:flex;align-items:center;justify-content:center;width:24px;height:24px;border-radius:50%;background:var(--fc-secondary-bg);font-size:12px;font-weight:600;color:var(--fc-secondary-text);flex-shrink:0}.fcrm_step_item .fcrm_step_label{font-weight:500;color:var(--fc-primary-text);flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0}.fcrm_step_item .fcrm_step_type_badge{font-size:11px;padding:2px 8px;border-radius:4px;background:var(--fc-secondary-bg);color:var(--fc-secondary-text);text-transform:capitalize}.fcrm_step_item .fcrm_step_metrics{display:flex;gap:16px;flex-wrap:wrap;margin-bottom:10px}.fcrm_step_item .fcrm_step_metric .fcrm_metric_label{font-size:11px;color:var(--fc-secondary-text);display:block}.fcrm_step_item .fcrm_step_metric .fcrm_metric_value{font-size:15px;font-weight:600;color:var(--fc-primary-text)}.fcrm_step_item .fcrm_step_metric.fcrm_metric_danger .fcrm_metric_value{color:var(--fc-error)}.fcrm_step_item .fcrm_step_bar{height:4px;background:var(--fc-secondary-bg);border-radius:2px;overflow:hidden}.fcrm_step_item .fcrm_step_bar .fcrm_step_bar_fill{height:100%;background:var(--fc-deep-bg);border-radius:2px;transition:width .5s ease}.purchase_history_block:last-child{border-bottom:none}.purchase_history_block span.fc_change_ref{margin-left:14px;padding:2px 4px;border:1px solid;border-radius:3px}.purchase_history_block span.fc_change_ref.fc_positive{color:var(--fc-deep-bg)}.purchase_history_block span.fc_change_ref.fc_negative{color:var(--fc-error)}ul.fc_full_listed{margin:0;padding:0;list-style:none;border:1px solid var(--fc-primary-border);background-color:var(--fc-secondary-bg);border-radius:4px}ul.fc_full_listed>li{display:block;padding:10px 15px;margin:0;border-bottom:1px solid var(--fc-primary-border)}ul.fc_full_listed>li span.fc_list_sub{font-weight:500}ul.fc_full_listed>li span.fc_list_value{float:right}ul.fc_full_listed>li.fc_product_name span.variation_name{font-size:90%;color:var(--fc-secondary-text);font-style:italic}ul.fc_full_listed.fc_memberpress_subscription_lists{max-height:400px;overflow-x:hidden}ul.fc_full_listed.fc_memberpress_subscription_lists li .fc_mepr_subscription_header{margin:0 0 5px;display:flex;align-items:center;gap:6px}ul.fc_full_listed.fc_memberpress_subscription_lists li .fc_mepr_subscription_header .fc_mepr_subscription_status{display:block;padding:1px 6px;border-radius:4px;line-height:1.2;text-transform:capitalize;font-size:12px;color:var(--fc-primary-text);background-color:var(--fc-warning-bg);border:1px solid var(--fc-warning-bg)}ul.fc_full_listed.fc_memberpress_subscription_lists li .fc_mepr_subscription_header .fc_mepr_subscription_status.complete,ul.fc_full_listed.fc_memberpress_subscription_lists li .fc_mepr_subscription_header .fc_mepr_subscription_status.active{color:var(--fc-primary-text);background-color:var(--fc-success-bg);border:1px solid var(--fc-success-bg)}ul.fc_full_listed.fc_memberpress_subscription_lists li .fc_mepr_subscription_header .fc_mepr_subscription_status.failed,ul.fc_full_listed.fc_memberpress_subscription_lists li .fc_mepr_subscription_header .fc_mepr_subscription_status.cancelled{color:var(--fc-primary-text);background-color:var(--fc-error-bg);border:1px solid var(--fc-error-bg)}ul.fc_full_listed.fc_memberpress_subscription_lists li .fc_mepr_subscription_header .fc_mepr_subscription_status.suspended{color:var(--fc-primary-text);background-color:var(--fc-secondary-bg);border:1px solid var(--fc-secondary-bg)}ul.fc_full_listed.fc_memberpress_subscription_lists li .fc_mepr_subscription_header .fc_mepr_subscription_status.refunded{color:var(--fc-primary-text);background-color:var(--fc-warning-bg);border:1px solid var(--fc-error-bg)}ul.fc_full_listed.fc_memberpress_subscription_lists li .fc_mepr_subscription_header .fc_mepr_subscription_price{font-weight:600;color:var(--fc-primary-text)}ul.fc_full_listed.fc_memberpress_subscription_lists li .fc_mepr_subscription_header .fc_mepr_subscription_price small{font-weight:400;margin-left:2px;color:var(--fc-text-muted)}ul.fc_full_listed.fc_memberpress_subscription_lists li .fc_mepr_subscription_title{display:block}ul.fc_full_listed.fc_memberpress_subscription_lists li .fc_date{color:var(--fc-text-muted);display:block;font-size:10px;line-height:1;margin-top:6px}.fc-contact-profile-mepr-subscription-widget .fc_memberpress_subscription_lists h4{margin:0;padding:12px 15px;border-bottom:1px solid var(--fc-primary-border)}@media (max-width: 426px){.fc_advanced_report{width:100%;margin:0!important}}.fc_advanced_report .fc_report_body .fc_card_widgets{margin-bottom:35px}.fc_advanced_report.fc_report_crm .fc_report_body{width:100%;max-width:100%;flex:0 0 100%!important}.fc_advanced_report.fc_report_crm .fc_report_body .fc_card_widgets .fc_card_widget{width:calc(25% - 19px)}@media (max-width: 720px){.fc_advanced_report.fc_report_crm .fc_report_body .fc_card_widgets .fc_card_widget{width:calc(50% - 13px)}}@media (max-width: 426px){.fc_advanced_report.fc_report_crm .fc_report_body .fc_card_widgets .fc_card_widget{width:100%}}.fcrm_commerce_chart_nav{display:flex;align-items:flex-start;justify-content:space-between;flex-wrap:wrap;gap:12px}.fcrm_commerce_chart_controls{display:flex;align-items:center;flex-wrap:wrap;gap:8px}.fcrm_product_filter_wrap{display:inline-block;min-width:160px;max-width:200px}.fcrm_card_widget_stat:hover{box-shadow:0 2px 8px #0000000f;transform:none}.fcrm_card_widget_stat.fcrm_card_stat_success .fcrm_card_widget_icon:not(.fcrm_icon_background_gray){background:#22c55e1a;color:#16a34a}.fcrm_card_widget_stat.fcrm_card_stat_info .fcrm_card_widget_icon:not(.fcrm_icon_background_gray){background:#7b61ff1a;color:#7b61ff}.fcrm_card_widget_stat.fcrm_card_stat_danger .fcrm_card_widget_icon:not(.fcrm_icon_background_gray){background:#ef44441a;color:#ef4444}.fcrm_card_widget_stat.fcrm_card_stat_warning .fcrm_card_widget_icon:not(.fcrm_icon_background_gray){background:#f59e0b1a;color:#f59e0b}.fcrm_card_stat_sub{display:block;font-size:11px;font-weight:600;color:var(--fc-secondary-text);line-height:1}.fcrm_card_stat_success .fcrm_card_stat_sub{color:var(--fc-success)}.fcrm_card_stat_info .fcrm_card_stat_sub{color:var(--fc-text-muted)}.fcrm_card_stat_danger .fcrm_card_stat_sub{color:var(--fc-error)}.fcrm_card_stat_warning .fcrm_card_stat_sub{color:var(--fc-warning)}.fcrm_top_products_list{list-style:none;margin:0;padding:0;display:flex;flex-direction:column}.fcrm_top_product_item{display:flex;align-items:flex-start;justify-content:space-between;padding:0 0 12px;margin-bottom:12px;border-bottom:1px solid var(--fc-primary-border);border-radius:0}.fcrm_top_product_item:last-child{border-bottom:none;padding-bottom:0;margin-bottom:0}.fcrm_top_product_item:hover{background:none}.fcrm_top_product_info{display:flex;flex-direction:column;gap:6px;min-width:0;flex:1}.fcrm_top_product_name{font-size:14px;line-height:20px;font-weight:500;color:var(--fc-primary-text);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.fcrm_top_product_count{color:var(--fc-secondary-text, #999);white-space:nowrap;font-weight:400;font-size:12px;line-height:16px}.fcrm_top_product_count b{color:var(--fc-primary-text);font-weight:500}.fcrm_top_product_revenue{color:var(--fc-primary-text);white-space:nowrap;flex-shrink:0;font-weight:400;font-size:14px;line-height:20px}.fcrm_top_product_revenue em{font-style:normal}.fcrm_change_badge{display:inline-block;padding:2px 8px;border-radius:12px;font-size:12px;font-weight:600;white-space:nowrap}.fcrm_change_badge.fcrm_change_positive{background:#34c7591f;color:var(--fc-success, #1b8a3e)}.fcrm_change_badge.fcrm_change_negative{background:#ff3b301f;color:var(--fc-error, #d32f2f)}.fcrm_change_badge.fcrm_change_neutral{background:#8e8e931f;color:var(--fc-text-muted, #8e8e93)}.fcrm_report_row{display:flex;gap:20px;margin-bottom:0}.fcrm_report_row.two-col{display:grid;grid-template-columns:repeat(2,1fr)}@media (max-width: 1024px){.fcrm_report_row.two-col{grid-template-columns:1fr}}@media (max-width: 1024px){.fcrm_report_row{flex-direction:column}}@media (max-width: 768px){.fcrm_report_row{flex-direction:column}}.fcrm_report_row .fcrm_report_card_half{flex:1;min-width:0}.fcrm_report_row .fcrm_report_card_main{flex:2;min-width:0}.fcrm_report_row .fcrm_report_card_side{flex:1;min-width:260px;max-width:320px}@media (max-width: 1024px){.fcrm_report_row .fcrm_report_card_side{max-width:100%}}.fcrm_report_card_body_flush{padding:0}.fcrm_ranked_list{display:flex;flex-direction:column;gap:8px}.fcrm_ranked_item{display:flex;align-items:center;gap:12px;padding:8px 4px;border-radius:6px;transition:background .15s}.fcrm_ranked_item:hover{background:var(--fc-primary-bg, #f8f9fb)}.fcrm_ranked_position{flex-shrink:0;width:24px;height:24px;border-radius:50%;background:var(--fc-primary-bg, #f0f1f4);color:var(--fc-text-muted, #888);font-size:12px;font-weight:600;display:flex;align-items:center;justify-content:center}.fcrm_ranked_info{flex:1;min-width:0}.fcrm_ranked_label{display:block;font-size:13px;font-weight:500;color:var(--fc-text-color);margin-bottom:4px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.fcrm_ranked_label.fcrm_campaign_link{text-decoration:none}.fcrm_ranked_label.fcrm_campaign_link:hover{color:var(--fc-primary-text)}.fcrm_ranked_bar_wrap{height:6px;background:var(--fc-secondary-bg);border-radius:3px;overflow:hidden}.fcrm_ranked_bar{height:100%;background:var(--fc-primary-text);border-radius:3px;transition:width .4s ease;min-width:2px}.fcrm_ranked_value{flex-shrink:0;font-size:13px;font-weight:600;color:var(--fc-text-color);min-width:40px;text-align:right}.fcrm_empty_hint{text-align:center;padding:40px 20px;color:var(--fc-text-muted, #999);font-size:14px}.fcrm_campaign_report_list{display:flex;flex-direction:column}.fcrm_campaign_report_item{padding:14px 16px;border-bottom:1px solid var(--fc-border-color, #ebeef5)}.fcrm_campaign_report_item:last-child{border-bottom:none}.fcrm_campaign_report_header{display:flex;align-items:center;justify-content:space-between;gap:8px;margin-bottom:4px;min-width:0}.fcrm_campaign_report_header .fcrm_campaign_link{font-weight:600;font-size:14px;color:var(--fc-text-color);text-decoration:none;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;flex:1;min-width:0}.fcrm_campaign_report_header .fcrm_campaign_link:hover{color:var(--fc-primary-text)}.fcrm_campaign_report_header .fcrm_campaign_date{font-size:13px;color:var(--fc-text-muted, #999);white-space:nowrap;flex-shrink:0}.fcrm_campaign_report_metrics{display:flex;align-items:center;justify-content:space-between;gap:16px}.fcrm_automation_item{cursor:pointer;border:2px solid transparent;transition:border-color .15s,box-shadow .15s}.fcrm_automation_item:hover{border-color:#7756e633}.fcrm_automation_item.fcrm_automation_selected{border-color:var(--fc-primary-text);background:#7756e60a}.fcrm_step_drawer_body{padding:0 4px}.fcrm_step_drawer_body .fcrm_funnel_revenue{padding:12px 16px;background:var(--fc-secondary-bg);border-radius:8px;margin-bottom:20px;display:flex;align-items:center;gap:8px}.fcrm_step_drawer_body .fcrm_funnel_revenue .fcrm_revenue_label{font-weight:500;color:var(--fc-secondary-text)}.fcrm_step_drawer_body .fcrm_funnel_revenue .fcrm_revenue_value{font-size:18px;font-weight:700;color:var(--fc-primary-text)}.fcrm_drawer_header{display:flex;align-items:center;justify-content:space-between;width:100%}.fcrm_drawer_header h4{margin:0;font-size:16px;font-weight:600}.fcrm_automation_recent{display:flex;align-items:center;gap:10px;margin-top:10px;padding-top:10px;border-top:1px solid var(--fc-primary-border, #eee)}.fcrm_recent_avatars{display:flex;align-items:center}.fcrm_recent_avatars .el-tooltip__trigger{margin-left:-6px}.fcrm_recent_avatars .el-tooltip__trigger:first-child{margin-left:0}.fcrm_avatar_tiny{width:28px;height:28px;border-radius:50%;border:2px solid #fff;object-fit:cover;display:flex;align-items:center;justify-content:center;flex-shrink:0}.fcrm_avatar_initials{background:var(--fc-primary-text);color:var(--fc-text-inverse);font-size:10px;font-weight:600;letter-spacing:.5px}.fcrm_recent_label{font-size:12px;color:var(--fc-text-muted, #888)}.fcrm_view_all_link{font-size:12px;font-weight:500;color:var(--fc-primary-text);text-decoration:none;margin-left:auto;white-space:nowrap}.fcrm_view_all_link:hover{text-decoration:underline}.fcrm_report_pagination{display:flex;justify-content:center;padding:16px 0 4px}@media (max-width: 1024px){.fcrm_reports_home .fcrm_reports_layout,.fcrm_reports_home .fcrm_reports_layout.is-collapsed{padding-inline-start:0}.fcrm_reports_home .fcrm_reports_sidebar_header{display:none}.fcrm_reports_home .fcrm_reports_sidebar_overlay{display:block;position:fixed;top:auto;bottom:0;left:0;right:0;z-index:998;height:calc(100vh - 139px);background:#1c273259;opacity:0;visibility:hidden;pointer-events:none;transition:opacity .3s ease,visibility .3s ease}.fcrm_reports_home .fcrm_reports_sidebar_overlay.is-open{opacity:1;visibility:visible;pointer-events:auto}.fcrm_reports_home .fcrm_reports_sidebar{visibility:hidden;position:fixed;top:auto;bottom:0;left:0;right:auto;z-index:999;height:calc(100vh - 139px);width:272px;max-width:100%;border-radius:0;padding-top:0;opacity:0;transform:translate(-100%);transition:transform .3s ease,opacity .3s ease,visibility .3s ease;box-shadow:0 0 20px #1c273214;overflow-x:hidden}.fcrm_reports_home .fcrm_reports_sidebar.is-open{overscroll-behavior:contain;visibility:visible;opacity:1;transform:translate(160px)}}@media (max-width: 960px){.fcrm_reports_home .fcrm_reports_sidebar.is-open{transform:translate(36px)}}@media (max-width: 782px){.fcrm_reports_home .fcrm_reports_sidebar{height:calc(100vh - 157px)}.fcrm_reports_home .fcrm_reports_sidebar.is-open{transform:translate(0)}.fcrm_reports_home .fcrm_reports_sidebar_overlay{height:calc(100vh - 157px)}}.fluentcrm_title_cards{display:block;padding:25px 25px 0}.fluentcrm_title_cards .fluentcrm_title_card{margin-bottom:30px;background:var(--fc-secondary-bg);border:1px solid var(--fc-secondary-bg);border-radius:4px;transition:.2s;-webkit-transition:.2s;-moz-webkit-transition:.2s;-ms-transition:.2s;-o-transition:.2s}.fluentcrm_title_cards .fluentcrm_title_card:hover{box-shadow:0 0 10px var(--fc-light-bg)}.fluentcrm_title_cards .fluentcrm_title_card_row{width:100%;margin:0}.fluentcrm_title_cards .fluentcrm_title_card_row .fluentcrm_inline_stats li{padding-top:0}.fluentcrm_title_cards .fluentcrm_card_stats{text-align:right}.fluentcrm_title_cards .fluentcrm_card_desc{padding:20px 0 10px 30px}.fluentcrm_title_cards .fluentcrm_card_desc .fluentcrm_card_sub{color:var(--fc-secondary-text);font-size:14px}.fluentcrm_title_cards .fluentcrm_card_desc .fluentcrm_card_title{margin-top:5px;font-size:18px}.fluentcrm_title_cards .fluentcrm_card_desc .fluentcrm_card_title span{cursor:pointer}.fluentcrm_title_cards .fluentcrm_card_desc .fluentcrm_card_title a{color:var(--fc-primary-text)}.fluentcrm_title_cards .fluentcrm_card_desc .fluentcrm_card_title a:hover{color:var(--fc-deep-bg)}.fluentcrm_title_cards .fluentcrm_card_desc .fluentcrm_card_actions_hidden{visibility:hidden}.fluentcrm_title_cards .fluentcrm_card_desc:hover .fluentcrm_card_actions_hidden{visibility:visible}.fluentcrm_title_cards .fluentcrm_cart_cta{padding:30px 20px 0}.fluentcrm_title_cards ul.fluentcrm_inline_stats{margin:0;padding:0;list-style:none}.fluentcrm_title_cards ul.fluentcrm_inline_stats li{margin:0;display:inline-block;padding:10px 40px 0 0;text-align:center}.fluentcrm_title_cards ul.fluentcrm_inline_stats li p{padding:0;margin:0;color:var(--fc-secondary-text)}.fluentcrm_title_cards ul.fluentcrm_inline_stats li .fluentcrm_digit{font-size:18px;font-weight:500}.fc_narrow_box{max-width:860px;margin:0 auto 30px;padding:30px 45px;border-radius:5px;box-shadow:0 1px 2px #22242626}.fc_white_inverse{background:#f3f3f3}.fc_white_inverse .el-input--suffix .el-input__inner,.fc_white{background:var(--fc-primary-bg)}.fc_purchase_badge{color:var(--fc-text-link);margin-right:5px;padding:3px 5px;font-size:90%;border:1px solid var(--fc-text-link);border-radius:3px;background:#2225301a}.fc_purchase_badge *{display:inline-block;color:var(--fc-text-link)}.fc_inline_image_radio{display:block;width:100%}.fc_inline_image_radio>label{text-align:center;display:inline-block;padding:10px 25px;border:2px solid var(--fc-light-bg);margin-bottom:15px;border-radius:5px}.fc_inline_image_radio>label.is-checked{border-color:var(--fc-deep-bg)}.fc_inline_image_radio>label .fc_text_label{padding-top:10px;display:block}.fc_inline_image_radio>label span.el-radio__input{display:none}.fc_inline_image_radio>label .el-radio__label{padding-left:0}.fluentcrm-importer .dialog-footer{margin-top:20px!important}.el-overlay .el-drawer__header{background:none;border-bottom:1px solid var(--fc-primary-border)}.el-overlay .el-drawer__title{font-weight:500;font-size:16px;line-height:24px;color:var(--fc-primary-text)}.el-overlay .el-drawer__close-btn{color:var(--fc-secondary-text)}.el-overlay .el-drawer__close-btn:hover,.el-overlay .el-drawer__close-btn:hover i{color:var(--fc-primary-text)}.el-overlay.fcrm_share_newsletter_modal .el-dialog__header{display:none}.fc_visual_body.fcrm_has_fullscreen_bar .iframe-container iframe{height:100vh!important}.fcrm_view_campaign_page .fcrm_page_header{padding-top:5px}.fcrm_highlight_gray{background:var(--fc-secondary-bg);border-radius:8px;padding:16px}.fcrm_edit_campaign_steps_wrapper .fcrm_compose_editor_step{margin-top:-24px}.fcrm_fixed_bottom_actions{display:flex;align-items:center;justify-content:flex-end;gap:12px;background:var(--fc-primary-bg);padding:10px 32px;position:fixed;bottom:0;left:0;width:100%;box-shadow:0 -16px 32px -12px #0e121b0a;z-index:9}.fcrm_fixed_bottom_actions .el-button{margin:0}.fcrm_campaign_email_subject_settings .fluentcrm_email_composer .fcrm_input_hint{margin:4px 0 0;font-weight:400;font-size:12px;line-height:16px;color:var(--fc-secondary-text);display:flex;align-items:center;gap:6px}.fcrm_campaign_email_subject_settings .fluentcrm_email_composer .fcrm_input_hint .el-icon{color:var(--fc-text-muted);font-size:14px}.fcrm_campaign_email_subject_settings .fcrm_campaign_ab_testing_email_subjects{margin-bottom:20px;padding-left:24px}.fc_input_popover_wrapper .el-input .el-input__wrapper{padding:0 0 0 10px!important}.fc_input_popover_wrapper .el-input .el-input__suffix{width:36px;background:var(--fc-secondary-bg);border-left:1px solid var(--fc-primary-border);border-radius:0 8px 8px 0;display:flex;align-items:center;justify-content:center;padding-bottom:2px}.fc_input_popover_wrapper .el-input .el-input__suffix .fluentcrm_clickable{margin:0}.fcrm_campaign_setup_wrapper .el-form .el-form-item:last-child{margin-bottom:0}.el-radio-group.fcrm_layout_choice_group{display:grid;grid-template-columns:repeat(auto-fit,minmax(90px,1fr));gap:16px;width:100%;align-items:flex-start}.el-radio-group.fcrm_layout_choice_group .el-radio{height:auto;position:relative;margin:0!important;white-space:break-spaces}.el-radio-group.fcrm_layout_choice_group .el-radio.is-checked .fcrm_layout_choice_icon{opacity:1;transform:scale(1)}.el-radio-group.fcrm_layout_choice_group .el-radio.is-checked .fcrm_layout_choice_image{outline:1px solid var(--fc-primary-text);outline-offset:-1px}.el-radio-group.fcrm_layout_choice_group .el-radio .el-radio__input{display:none}.el-radio-group.fcrm_layout_choice_group .el-radio .el-radio__label{margin:0;padding:0}.el-radio-group.fcrm_layout_choice_group .el-radio .fcrm_layout_choice_icon{position:absolute;top:4px;right:4px;transform:scale(.3);opacity:0;transition:.3s}.el-radio-group.fcrm_layout_choice_group .el-radio .fcrm_layout_choice_icon svg{display:block}.el-radio-group.fcrm_layout_choice_group .el-radio .fcrm_layout_choice_image{margin-bottom:8px;transition:.3s;border-radius:8px;outline:1px solid transparent;outline-offset:-1px}.el-radio-group.fcrm_layout_choice_group .el-radio .fcrm_layout_choice_image img{max-width:100%;display:block}.el-radio-group.fcrm_layout_choice_group .el-radio .fcrm_layout_choice_label{display:block;font-weight:500;font-size:13px;line-height:20px;color:var(--fc-primary-text);margin:0 0 4px;padding:0 4px}.el-radio-group.fcrm_layout_choice_group .el-radio .fcrm_layout_choice_hint{display:block;font-weight:400;font-size:12px;line-height:16px;color:var(--fc-secondary-text);margin:0;padding:0 4px}.fcrm_block_composer_editor_wrapper .fcrm_block_composer_editor--header{display:flex;align-items:center;justify-content:space-between;padding:12px 20px;border-bottom:1px solid var(--fc-primary-border)}.fcrm_block_composer_editor_wrapper .fcrm_block_composer_editor--header-title{margin:0;font-size:16px;line-height:24px;font-weight:500;color:var(--fc-primary-text)}.fcrm_block_composer_editor_wrapper .fcrm_block_composer_editor--header-actions{display:flex;align-items:center;gap:12px}.fcrm_block_composer_editor_wrapper .fcrm_block_composer_editor--header-actions .el-button{margin:0}.fcrm_block_composer_editor_wrapper .fcrm_block_composer_editor--header-actions .el-dropdown .fcrm_editor_more_link{cursor:pointer}.fcrm_block_composer_editor_wrapper .fcrm_block_composer_editor--header-actions .el-dropdown .fcrm_editor_more_link svg{display:block}.fcrm_block_composer_editor_wrapper .fcrm_block_composer_editor--header-actions .fc_email_preview .fc_segmented_btn{height:32px}.fcrm_block_composer_editor_wrapper .fcrm_block_composer_editor--header-actions .popover-wrapper .el-button.editor-add-shortcode{margin:0;padding:0 0 2px;background:var(--fc-primary-bg);color:var(--fc-secondary-text);font-weight:500;font-size:14px;line-height:20px;border:1px solid var(--fc-primary-border);box-shadow:0 1px 2px #0a0d1408;border-radius:8px;width:32px;height:32px}.fcrm_block_composer_editor_wrapper .fcrm_block_composer_editor--header-actions .popover-wrapper .el-button.editor-add-shortcode:hover{color:var(--fc-primary-text);border-color:var(--fc-primary-text)}.fcrm_block_composer_editor_wrapper .fcrm_block_composer_editor--body{padding-left:20px}.fcrm_block_composer_editor_wrapper .fcrm_block_composer_editor--body .fcrm_block_composer_editor--compose-body-row{display:flex;gap:20px}.fcrm_block_composer_editor_wrapper .fcrm_block_composer_editor--body .fcrm_block_composer_editor--compose-body-row .fcrm_block_composer_editor--compose-body{flex:1}.fcrm_block_composer_editor_wrapper .fcrm_block_composer_editor--body .fcrm_block_composer_editor--compose-body-row .fcrm_block_composer_editor--compose-body .fc_composer_classic,.fcrm_block_composer_editor_wrapper .fcrm_block_composer_editor--body .fcrm_block_composer_editor--compose-body-row .fcrm_block_composer_editor--compose-body .fc_composer_raw_hrml{padding-top:20px}.fcrm_block_composer_editor_wrapper .fcrm_block_composer_editor--body .fcrm_block_composer_editor--compose-body-row .fcrm_block_composer_editor--compose-sidebar{flex:none;width:272px;border-left:1px solid var(--fc-primary-border);overflow-x:hidden;transition:.3s}.fcrm_block_composer_editor_wrapper .fcrm_block_composer_editor--body .fcrm_block_composer_editor--compose-body-row .fcrm_block_composer_editor--compose-sidebar.fcrm_is_collapsed{width:70px}.fcrm_block_composer_editor_wrapper .fcrm_block_composer_editor--body .fcrm_block_composer_editor--compose-body-row .fcrm_block_composer_editor--compose-sidebar.fcrm_is_collapsed .fc_template_info,.fcrm_block_composer_editor_wrapper .fcrm_block_composer_editor--body .fcrm_block_composer_editor--compose-body-row .fcrm_block_composer_editor--compose-sidebar.fcrm_is_collapsed .fcrm_image_radio_tooltips,.fcrm_block_composer_editor_wrapper .fcrm_block_composer_editor--body .fcrm_block_composer_editor--compose-body-row .fcrm_block_composer_editor--compose-sidebar.fcrm_is_collapsed .fcrm_block_composer_editor--compose-sidebar-header-title{opacity:0;visibility:hidden}.fcrm_block_composer_editor_wrapper .fcrm_block_composer_editor--body .fcrm_block_composer_editor--compose-body-row .fcrm_block_composer_editor--compose-sidebar.fcrm_is_collapsed .fcrm_block_composer_editor--compose-sidebar-header-actions .fcrm_template_sidebar_toggle{cursor:w-resize}.fcrm_block_composer_editor_wrapper .fcrm_block_composer_editor--body .fcrm_block_composer_editor--compose-body-row .fcrm_block_composer_editor--compose-sidebar-header{padding:16px 20px;border-bottom:1px solid var(--fc-primary-border);position:relative}.fcrm_block_composer_editor_wrapper .fcrm_block_composer_editor--body .fcrm_block_composer_editor--compose-body-row .fcrm_block_composer_editor--compose-sidebar-header-title{font-size:16px;line-height:24px;color:var(--fc-primary-text);margin:0;font-weight:500;white-space:nowrap;transition:.3s}.fcrm_block_composer_editor_wrapper .fcrm_block_composer_editor--body .fcrm_block_composer_editor--compose-body-row .fcrm_block_composer_editor--compose-sidebar-header-actions{position:absolute;right:20px;top:16px;display:flex;align-items:center;gap:6px}.fcrm_block_composer_editor_wrapper .fcrm_block_composer_editor--body .fcrm_block_composer_editor--compose-body-row .fcrm_block_composer_editor--compose-sidebar-header-actions .el-button{margin:0}.fcrm_block_composer_editor_wrapper .fcrm_block_composer_editor--body .fcrm_block_composer_editor--compose-body-row .fcrm_block_composer_editor--compose-sidebar-header-actions .fcrm_template_sidebar_toggle{border:0;background:transparent;display:inline-flex;align-items:center;justify-content:center;width:28px;height:28px;border-radius:6px;cursor:e-resize;color:var(--fc-secondary-text)}.fcrm_block_composer_editor_wrapper .fcrm_block_composer_editor--body .fcrm_block_composer_editor--compose-body-row .fcrm_block_composer_editor--compose-sidebar-header-actions .fcrm_template_sidebar_toggle svg{display:block}.fcrm_block_composer_editor_wrapper .fcrm_block_composer_editor--body .fcrm_block_composer_editor--compose-body-row .fcrm_block_composer_editor--compose-sidebar-header-actions .fcrm_template_sidebar_toggle:hover{background:var(--fc-secondary-bg)}.fcrm_block_composer_editor_wrapper .fcrm_block_composer_editor--body .fcrm_block_composer_editor--compose-body-row .fcrm_block_composer_editor--compose-sidebar-body{padding:20px}.fcrm_block_composer_editor_wrapper .fcrm_image_radio_tooltips{grid-template-columns:repeat(auto-fit,minmax(80px,1fr))}.fcrm_block_composer_editor_wrapper .fcrm_image_radio_tooltips .el-radio{max-width:100%}.fcrm_ai_email_generator_trigger{border-color:#c7d2fe;color:#4f46e5;background:linear-gradient(135deg,#f8fbff,#eef2ff);font-weight:600}.fcrm_ai_email_generator_trigger:hover,.fcrm_ai_email_generator_trigger:focus{border-color:#818cf8;color:#4338ca;background:linear-gradient(135deg,#eef2ff,#e0e7ff)}.fcrm_ai_email_generator_icon{display:inline-flex;align-items:center;justify-content:center;margin-right:6px;color:#6366f1}.fcrm_ai_email_generator_dialog .el-dialog__header{margin-right:0;padding-bottom:16px;border-bottom:1px solid var(--fc-primary-border)}.fcrm_ai_email_generator_dialog .el-dialog__body{padding:20px}.fcrm_ai_email_generator_dialog .el-dialog__footer{padding:16px 20px 20px}.fcrm_ai_email_generator_footer{display:flex;justify-content:flex-end;align-items:center;gap:12px}.fcrm_ai_email_generator_footer .el-button+.el-button{margin-left:0}.fcrm_ai_email_generator_footer .el-button--primary{min-width:146px}.fcrm_ai_email_generator_dialog_header{display:flex;align-items:center;gap:12px}.fcrm_ai_email_generator_dialog_header h3{margin:0 0 4px;font-size:16px;color:var(--fc-primary-text)}.fcrm_ai_email_generator_dialog_header p{margin:0;font-size:12px;color:var(--fc-secondary-text)}.fcrm_ai_email_generator_dialog_icon{display:inline-flex;align-items:center;justify-content:center;width:38px;height:38px;border-radius:12px;color:#4f46e5;background:#eef2ff}.fcrm_ai_email_generator_form label{display:block;margin:0 0 7px;font-size:12px;font-weight:600;color:var(--fc-primary-text)}.fcrm_ai_email_generator_grid{display:grid;grid-template-columns:1fr 1fr;gap:12px;margin-top:14px}.fcrm_image_radio_tooltips{display:grid;grid-template-columns:repeat(auto-fit,minmax(80px,80px));gap:16px;width:100%;align-items:flex-start}.fcrm_image_radio_tooltips .el-radio{margin:0!important;height:auto;position:relative;max-width:100px}.fcrm_image_radio_tooltips .el-radio .el-radio__input{display:none}.fcrm_image_radio_tooltips .el-radio .el-radio__label{width:100%}.fcrm_image_radio_tooltips .el-radio .el-radio__label .fcrm_image_box{width:auto;height:auto;outline:1px solid transparent;outline-offset:-1px;border:none}.fcrm_image_radio_tooltips .el-radio .el-radio__label .fcrm_image_box .icon{position:absolute;right:4px;top:4px;opacity:0;transform:scale(.4);transition:.3s}.fcrm_image_radio_tooltips .el-radio .el-radio__label .fcrm_image_box .icon svg{display:block}.fcrm_image_radio_tooltips .el-radio .el-radio__label .fcrm_image_box img{display:block;max-width:100%}.fcrm_image_radio_tooltips .el-radio .el-radio__label .fcrm_image_box.fcrm_image_active{border:none;outline:1px solid var(--fc-primary-text)}.fcrm_image_radio_tooltips .el-radio .el-radio__label .fcrm_image_box.fcrm_image_active .icon{transform:scale(1);opacity:1}.contact_selector{padding:2px 0}.contact_selector .el-select .el-popper{width:100%}.fcrm_campaign_ab_testing_email_subjects table{width:100%;border:1px solid var(--fc-primary-border);border-radius:8px;text-align:left;border-spacing:0}.fcrm_campaign_ab_testing_email_subjects table thead tr th{border-bottom:1px solid var(--fc-primary-border);border-right:1px solid var(--fc-primary-border);background:var(--fc-secondary-bg);color:var(--fc-secondary-text);font-weight:500;font-size:14px;line-height:20px;padding:8px 12px}.fcrm_campaign_ab_testing_email_subjects table thead tr th:first-child{padding-left:16px}.fcrm_campaign_ab_testing_email_subjects table thead tr th:last-child{border-right:none}.fcrm_campaign_ab_testing_email_subjects table tbody tr td{border-bottom:1px solid var(--fc-primary-border);border-right:1px solid var(--fc-primary-border);padding:10px 12px}.fcrm_campaign_ab_testing_email_subjects table tbody tr td:first-child{padding-left:16px}.fcrm_campaign_ab_testing_email_subjects table tbody tr td:last-child{border-right:none}.fcrm_campaign_ab_testing_email_subjects table tbody tr td .el-input-number{width:130px}.fcrm_campaign_ab_testing_email_subjects table tbody tr td .el-input-number .el-input-number__increase,.fcrm_campaign_ab_testing_email_subjects table tbody tr td .el-input-number .el-input-number__decrease{background:none;border:none;color:var(--fc-secondary-text)}.fcrm_campaign_ab_testing_email_subjects table tbody tr td .el-input-number .el-input .el-input__wrapper{box-shadow:0 1px 2px #0a0d1408;border:1px solid var(--fc-primary-border);border-radius:8px;padding:2px 10px}.fcrm_campaign_ab_testing_email_subjects table tbody tr td .el-input-number .el-input .el-input__wrapper.is-focused,.fcrm_campaign_ab_testing_email_subjects table tbody tr td .el-input-number .el-input .el-input__wrapper.is-focus{border-color:var(--fc-primary-text)}.fcrm_campaign_ab_testing_email_subjects table tbody tr td .el-input-number .el-input-number__decrease:hover,.fcrm_campaign_ab_testing_email_subjects table tbody tr td .el-input-number .el-input-number__increase:hover{color:var(--fc-primary-text)}.fcrm_campaign_ab_testing_email_subjects table tbody tr td .el-input-number .el-input-number__decrease:hover~.el-input:not(.is-disabled) .el-input__wrapper,.fcrm_campaign_ab_testing_email_subjects table tbody tr td .el-input-number .el-input-number__increase:hover~.el-input:not(.is-disabled) .el-input__wrapper{box-shadow:0 1px 2px #0a0d1408;border-color:var(--fc-primary-text)}.fcrm_campaign_ab_testing_email_subjects table tbody tr td .fcrm_delete_subject_btn{background:none;width:32px;height:32px;color:var(--fc-secondary-text);border:none}.fcrm_campaign_ab_testing_email_subjects table tbody tr td .fcrm_delete_subject_btn:hover{background:none;border:none;color:var(--fc-secondary-text)}.fcrm_campaign_ab_testing_email_subjects table tbody tr td .fcrm_delete_subject_btn.is-disabled{color:var(--fc-secondary-border)}.fcrm_campaign_ab_testing_email_subjects table tbody tr:last-child td{border-bottom:none}.fcrm_campaign_ab_testing_email_subjects--header-title{margin-bottom:6px}.fcrm_campaign_ab_testing_email_subjects--bottom{margin-top:12px}.fcrm_email_preview_drawer .el-drawer__header{background:none;border-bottom:1px solid var(--fc-primary-border);padding:12px 20px!important}.fcrm_email_preview_drawer .el-drawer__title{font-weight:500;font-size:16px;line-height:24px;color:var(--fc-primary-text);margin:0}.fcrm_email_preview_drawer .el-drawer__title small{display:block;line-height:1;font-weight:400;margin-top:4px}.fcrm_email_preview_drawer .el-drawer__body{padding:0}.fcrm_email_preview_drawer .fcrm_preview_toolbar{position:sticky;top:0;z-index:2;display:flex;align-items:center;justify-content:space-between;gap:12px;padding:12px 20px;background:var(--fc-primary-bg);border-bottom:1px solid var(--fc-primary-border)}.fcrm_email_preview_drawer .fcrm_preview_toolbar_actions{display:inline-flex;align-items:center;gap:8px;flex-wrap:wrap}.fcrm_email_preview_drawer .fcrm_preview_toolbar_actions .el-button{margin:0}.fcrm_edit_email_sequence_page .fcrm_page_header{padding-top:9px}.fcrm_edit_email_sequence_schedule--config{background:var(--fc-primary-bg);border-radius:8px;padding:20px;margin-bottom:24px}.fcrm_edit_email_sequence_schedule--config .el-form .el-form-item{margin-bottom:16px}.fcrm_edit_email_sequence_schedule--config .el-form .el-form-item__label{display:flex;align-items:center;gap:4px}.fcrm_edit_email_sequence_schedule--config .el-form .el-form-item__label .el-icon{color:var(--fc-secondary-border)}.fcrm_edit_email_sequence_schedule--config .el-form .el-form-item .el-input{height:auto}.fcrm_edit_email_sequence_schedule--config .el-form .el-form-item .el-textarea textarea{padding:6px 10px;line-height:20px}.fcrm_edit_email_sequence_schedule--config .el-form .el-form-item .el-checkbox{align-items:flex-start}.fcrm_edit_email_sequence_schedule--config .el-form .el-form-item .el-checkbox__input{margin-top:2px}.fcrm_edit_email_sequence_schedule--config .el-form .el-form-item .el-checkbox__label{font-weight:500;font-size:14px;line-height:20px;color:var(--fc-primary-text)}.fcrm_edit_email_sequence_schedule--config .el-form .el-form-item .el-checkbox__label p{margin:4px 0 0;color:var(--fc-secondary-text);font-weight:400;font-size:12px;line-height:16px}.fcrm_edit_email_sequence_schedule--datetime{display:flex;align-items:flex-start;gap:16px}@media (max-width: 624px){.fcrm_edit_email_sequence_schedule--datetime{flex-wrap:wrap}}.fcrm_edit_email_sequence_schedule--datetime .fcrm_edit_email_sequence_schedule--delay .el-input.el-input--suffix .el-input__suffix .el-select__wrapper{border-radius:0 8px 8px 0!important}.fcrm_edit_email_sequence_schedule--datetime .fcrm_edit_email_sequence_schedule--delay .el-input .el-input__wrapper{padding-right:0;padding-top:0;padding-bottom:0}.fcrm_edit_email_sequence_schedule--datetime .fcrm_edit_email_sequence_schedule--delay .el-input__suffix .el-select{width:100px;height:100%}.fcrm_edit_email_sequence_schedule--datetime .fcrm_edit_email_sequence_schedule--delay .el-input__suffix .el-select__wrapper{height:auto!important;line-height:20px;min-height:inherit;border:none;border-radius:0 8px 8px 0;border-left:1px solid var(--fc-primary-border);background:var(--fc-secondary-bg);padding:10px}.fcrm_edit_email_sequence_schedule--specific-days .el-checkbox-group{gap:16px;display:flex;flex-wrap:wrap;padding-left:24px}.fcrm_edit_email_sequence_schedule--specific-days .el-checkbox-group .el-checkbox{margin:0}.fcrm_edit_email_sequence_schedule--utm-row{padding-left:24px}.el-overlay.fcrm_import_template_modal .el-dialog{width:760px;max-width:100%}.el-overlay.fcrm_import_template_modal .el-dialog__body{padding:15px 20px 20px}.el-overlay.fcrm_import_template_modal .el-radio-group{gap:24px}.el-overlay.fcrm_import_template_modal .el-radio-button{background:none;border-radius:0}.el-overlay.fcrm_import_template_modal .el-radio-button:first-child .el-radio-button__inner{border-left:none}.el-overlay.fcrm_import_template_modal .el-radio-button__inner{border:none;border-bottom:2px solid transparent;background:none;border-radius:0!important;padding:0 0 12px;color:var(--fc-secondary-text);outline:none!important}.el-overlay.fcrm_import_template_modal .el-radio-button.is-active .el-radio-button__original-radio:not(:disabled)+.el-radio-button__inner{color:var(--fc-primary-text);border:none;border-bottom:2px solid var(--fc-primary-text);background:none}.el-overlay.fcrm_import_template_modal .fcrm_import_template_table_wrapper{margin-bottom:20px}.el-overlay.fcrm_import_template_modal .fcrm_import_template_table_wrapper .el-table .el-table__inner-wrapper:after{background:var(--fc-primary-border)}.el-overlay.fcrm_import_template_modal .fcrm_import_template_table_wrapper.fcrm_table_wrapper{border:1px solid var(--fc-primary-border);border-radius:8px}.el-overlay.fcrm_import_template_modal .fcrm_table_body .el-table{border-radius:0 0 8px 8px}.el-overlay.fcrm_import_template_modal .fcrm-action-btns{display:flex;align-items:center;gap:8px;justify-content:flex-end}.el-overlay.fcrm_import_template_modal .fcrm-action-btns .el-button{margin:0;height:32px}.el-overlay.fcrm_import_template_modal .template-name{display:flex;align-items:center;gap:6px}.el-overlay.fcrm_import_template_modal .fcrm_template_type_icon{box-shadow:0 1px 2px #0a0d1408;border:1px solid var(--fc-primary-border);border-radius:6px;width:24px;height:24px;flex:none;display:flex;align-items:center;justify-content:center;color:var(--fc-deep-bg)}.el-overlay.fcrm_import_template_modal .fcrm_template_type_icon svg{display:block}.el-overlay.fcrm_import_template_modal .fcrm_import_template_tabs{margin:0 -20px;padding:0 20px;border-bottom:1px solid var(--fc-primary-border)}.el-overlay.fcrm_import_template_modal .fcrm_import_template_tab_content{padding-top:20px}.el-overlay.fcrm_import_template_modal .fcrm_import_template_tab_content .fcrm-pagination-bar{padding-bottom:0!important;margin-inline-start:-20px;width:calc(100% + 40px)}.fcrm_email_templates_page .fcrm_table_wrapper .el-table .template-title{display:inline-flex;align-items:center;gap:6px}.fcrm_email_templates_page .fcrm_table_wrapper .el-table .fcrm_template_type_icon{box-shadow:0 1px 2px #0a0d1408;border:1px solid var(--fc-primary-border);border-radius:6px;width:24px;height:24px;flex:none;display:flex;align-items:center;justify-content:center;color:var(--fc-deep-bg)}.fcrm_email_templates_page .fcrm_table_wrapper .el-table .fcrm_template_type_icon svg{display:block}.fcrm_email_templates_page .fcrm_table_wrapper .el-table .preview-btn{margin-top:-8px}.fcrm_email_templates_page .fcrm_table_wrapper .el-table .fcrm_preview_text_with_icon_btn{display:flex;align-items:center;padding:0;cursor:pointer}.fcrm_email_templates_page .fcrm_table_wrapper .el-table .fcrm_preview_text_with_icon_btn .fcrm_template_preview_pill_btn{display:inline-flex;align-items:center;gap:4px;padding:4px 8px;border-radius:var(--radius-8, 8px);background:var(--fc-primary-bg);border:1px solid var(--fc-primary-border);box-shadow:0 1px 2px #0a0d1408;color:var(--fc-secondary-text);font-size:12px;font-weight:500;line-height:16px;letter-spacing:-.12px;white-space:nowrap;transition:background-color .2s ease,border-color .2s ease}.fcrm_email_templates_page .fcrm_table_wrapper .el-table .fcrm_preview_text_with_icon_btn .fcrm_template_preview_pill_btn .el-icon{font-size:14px;color:var(--fc-secondary-text)}.fcrm_email_templates_page .fcrm_table_wrapper .el-table .fcrm_preview_text_with_icon_btn:hover .fcrm_template_preview_pill_btn{background:var(--fc-secondary-bg);border-color:var(--fc-secondary-border)}.fcrm_edit_template_page .fcrm_page_header_top_nav_wrapper{padding-top:12px;padding-bottom:12px;margin-bottom:24px}.fcrm_edit_template_page .fluentcrm_body{overflow:visible}.fcrm_build_in_templates_list{display:grid;grid-template-columns:repeat(auto-fit,minmax(155px,1fr));gap:20px}.fcrm_build_in_templates_item--image{height:152px;overflow:hidden;position:relative;padding:16px 16px 0;background:var(--fc-secondary-bg);border-radius:8px}.fcrm_build_in_templates_item--image img{display:block;width:100%}.fcrm_build_in_templates_item--actions{position:absolute;left:0;top:0;width:100%;height:100%;display:flex;align-items:center;justify-content:center;z-index:9;background:#2b303b3d;-webkit-backdrop-filter:blur(2px);backdrop-filter:blur(2px);transition:.3s;gap:8px;opacity:0;visibility:hidden}.fcrm_build_in_templates_item--actions .el-button{height:28px!important;margin:0;padding:4px!important}.fcrm_build_in_templates_item--actions .el-button.preview-btn{width:28px}.fcrm_build_in_templates_item--title{font-weight:500;font-size:12px;line-height:16px;margin:12px 0 0;color:var(--fc-primary-text);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.fcrm_build_in_templates_item:hover .fcrm_build_in_templates_item--actions{opacity:1;visibility:visible}.fcrm_editor_recommendation_alert{background:var(--fc-secondary-bg);color:var(--fc-primary-text);font-size:12px;line-height:20px;display:flex;align-items:flex-start;gap:6px;padding:8px;border-radius:8px;margin-top:12px;border-left:3px solid var(--fc-text-muted)}.fcrm_editor_recommendation_alert .icon{color:var(--fc-text-muted);font-size:12px;display:block;margin-top:2px}.fcrm_editor_recommendation_alert .icon svg{width:16px;height:16px;display:block}.fc_preview_container{transition:all .3s ease;margin:0 auto;max-width:100%}.fc_preview_desktop{width:100%;max-width:100%}.fc_preview_tablet{width:768px;max-width:100%;margin:0 auto}.fc_preview_mobile{width:375px;max-width:100%;margin:0 auto}.fcrm_preview_device_toggle{display:flex;justify-content:flex-start;padding:0}.fcrm_preview_device_toggle .fcrm_device_btn_group{display:inline-flex;border:1px solid var(--fc-primary-border);border-radius:8px;background-color:var(--fc-primary-bg);overflow:hidden}.fcrm_preview_device_toggle .fcrm_device_btn{display:flex;align-items:center;justify-content:center;width:36px;height:36px;border:none;background-color:transparent;cursor:pointer;color:var(--fc-secondary-border);transition:all .2s ease;border-right:1px solid var(--fc-primary-border)}.fcrm_preview_device_toggle .fcrm_device_btn svg{display:block}.fcrm_preview_device_toggle .fcrm_device_btn:last-child{border-right:none}.fcrm_preview_device_toggle .fcrm_device_btn:hover{color:var(--fc-primary-text);background-color:var(--fc-secondary-bg)}.fcrm_preview_device_toggle .fcrm_device_btn.active{color:var(--fc-primary-text);background-color:var(--fc-primary-bg);position:relative;z-index:1}.fcrm_email_campaigns_page .fcrm_table_body .el-table__body-wrapper tr td{padding-top:19px;padding-bottom:19px}.fcrm_campaign_emails_wrapper--title{font-weight:500;font-size:16px;line-height:24px;color:var(--fc-primary-text);margin-bottom:16px}.fcrm_campaign_emails_wrapper .fcrm_table_wrapper{border:1px solid var(--fc-primary-border);border-radius:8px}.fcrm_campaign_emails_wrapper .fcrm_table_wrapper .fcrm_table_header_bulk_actions{margin-top:8px}.fcrm_campaign_emails_wrapper .fcrm_notes_search_bar{display:flex;align-items:center;gap:12px;min-width:32px;width:32px;overflow:hidden;transition:width 1s ease,min-width 1s ease;margin-left:auto}.fcrm_campaign_emails_wrapper .fcrm_notes_search_bar.fcrm_notes_search_bar-is_expanded{min-width:200px;width:100%;max-width:280px}.fcrm_campaign_emails_wrapper .fcrm_notes_search_bar .el-input .el-input__wrapper{height:32px;border:1px solid var(--fc-primary-border);box-shadow:0 1px 2px #0a0d1408;border-radius:8px 0 0 8px;padding:4px 10px;flex:1;min-width:0}.fcrm_campaign_emails_wrapper .fcrm_notes_search_bar .el-input .el-input__wrapper.is-focused,.fcrm_campaign_emails_wrapper .fcrm_notes_search_bar .el-input .el-input__wrapper.is-focus{outline:none;box-shadow:0 1px 2px #0a0d1408}.fcrm_campaign_emails_wrapper .fcrm_notes_search_bar .el-input .el-input-group__append{padding:0;width:38px;font-size:15px;border:none;box-shadow:none;outline:none;background:none}.fcrm_campaign_emails_wrapper .fcrm_notes_search_bar .el-input .el-input-group__append .el-button{margin:0;padding:0;border:none;box-shadow:none;outline:none;display:flex}.fcrm_campaign_emails_wrapper .fcrm_notes_search_bar .el-input .el-input-group__append .el-button:hover{border:none!important;box-shadow:none;outline:none}.fcrm_campaign_emails_wrapper .fcrm_notes_search_bar .el-button.fcrm_notes_search_cancel_btn{color:var(--fc-deep-bg);font-weight:500;font-size:14px;line-height:20px;height:auto;padding:0;margin:0}.fcrm_campaign_emails_wrapper .fcrm_notes_search_bar .fcrm_notes_search_input{flex:1;min-width:0;border:1px solid var(--fc-primary-border);box-shadow:0 1px 2px #0a0d1408;border-radius:8px;overflow:hidden}.fcrm_campaign_emails_wrapper .fcrm_notes_search_bar .fcrm_notes_search_input .el-input__wrapper{display:inline-flex;border:none;box-shadow:none;outline:none;min-width:0;max-width:0;opacity:0;overflow:hidden;padding:0;transition:max-width .25s ease,opacity .2s ease,padding .05s ease .1s}.fcrm_campaign_emails_wrapper .fcrm_notes_search_bar .fcrm_notes_search_input .el-input__wrapper.is-focused,.fcrm_campaign_emails_wrapper .fcrm_notes_search_bar .fcrm_notes_search_input .el-input__wrapper.is-focus,.fcrm_campaign_emails_wrapper .fcrm_notes_search_bar .fcrm_notes_search_input .el-input__wrapper:focus,.fcrm_campaign_emails_wrapper .fcrm_notes_search_bar .fcrm_notes_search_input .el-input__wrapper:focus-within{outline:none}.fcrm_campaign_emails_wrapper .fcrm_notes_search_bar .fcrm_notes_search_input-is_expanded .el-input__wrapper{max-width:500px;opacity:1;padding:4px 10px;transition:max-width .25s ease,opacity .2s ease,padding .05s ease .1s}.fcrm_campaign_emails_wrapper .fcrm_notes_search_bar .fcrm_notes_search_btn{margin:0;padding:0;height:100%;border:1px solid var(--fc-primary-border);box-shadow:0 1px 2px #0a0d1408;color:var(--fc-secondary-text)}.fcrm_campaign_emails_wrapper .fcrm_notes_search_bar .fcrm_notes_search_btn:hover{border:1px solid var(--fc-primary-border)!important;box-shadow:0 1px 2px #0a0d1408;color:var(--fc-secondary-text)}.fcrm_campaign_emails_wrapper .fcrm_notes_search_bar .fcrm_notes_search_close_btn{flex-shrink:0;padding:0 4px;font-weight:500;font-size:14px}.fcrm_campaign_emails_wrapper .fcrm_pro_modal_body{padding:20px;border-radius:8px}.fluentcrm_link_metrics .el-table .el-table__inner-wrapper:after,.fluentcrm_link_metrics .el-table .el-table__border-left-patch,.fluentcrm_link_metrics .el-table:after,.fluentcrm_link_metrics .el-table:before{display:none!important}.fluentcrm_link_metrics .el-table .el-table__header tr th:last-child{border-right:none}.fluentcrm_link_metrics .el-table .el-table__body tr td:last-child{border-right:none}.fluentcrm_link_metrics .el-form-item .el-checkbox{margin:0;display:flex;align-items:center;gap:8px}.fluentcrm_link_metrics .el-form-item .el-checkbox-group{gap:8px;display:flex;flex-direction:column;align-items:flex-start}.fluentcrm_link_metrics .el-form-item .el-checkbox__label{padding:0}.fcrm_email_campaign_view--main .fcrm_preview_device_toggle{justify-content:center;margin-bottom:16px}.fcrm_email_campaign_view--main .fcrm_sms_campaign_view--sms-preview-card{background:var(--fc-secondary-bg);border:none}.fcrm_email_campaign_view--main .fcrm_sms_campaign_view--details-grid{grid-template-columns:repeat(auto-fit,minmax(250px,1fr))}.fcrm_email_campaign_view--body .fcrm_preview_device_toggle{justify-content:center;margin-bottom:16px}.fcrm_email_campaign_view--preview-email-title{color:var(--fc-primary-text);font-size:16px;line-height:24px;font-weight:500;margin-bottom:16px}.fcrm_view_newsletter_content h3{color:var(--fc-primary-text);font-weight:500;font-size:16px;line-height:24px;margin:0 0 4px}.fcrm_view_newsletter_content p{color:var(--fc-secondary-text);font-weight:400;font-size:14px;line-height:20px;margin:0 0 16px}.campaigns-table .fluentcrm_card_actions{margin-top:5px}.campaigns-table .fluentcrm_card_actions button{padding:0}.campaigns-table .fluentcrm_card_actions>*{margin:0 10px 0 0}.campaigns-table .fluentcrm_card_actions>*:last-child{margin-right:0}.fluentcrm_card_actions{margin-top:6px}.fluentcrm_card_actions>a:first-child button{padding-left:0}.fluentcrm_card_actions .fluentcrm-report-text-btn,.fluentcrm_card_actions .fluentcrm-report-text-btn:hover{color:var(--fc-success)}.fluentcrm_card_actions .fluentcrm-duplicate-campaign,.fluentcrm_card_actions .fluentcrm-duplicate-campaign:hover{color:var(--fc-text-muted)}.fluentcrm_card_actions .fluentcrm-delete-campaign{color:var(--fc-error)}.fluentcrm_card_actions .fluentcrm-delete-campaign:hover{color:var(--fc-error-bg)}.fluentcrm-body>.fluentcrm-campaigns .fluentcrm_title_cards{padding:15px 0 10px 58px}.fluentcrm-body>.fluentcrm-campaigns .fluentcrm_title_cards .fluentcrm_inline_stats{margin-top:8px}.fluentcrm-body>.fluentcrm-campaigns .fluentcrm_card_actions{padding-left:58px;padding-bottom:15px}.fluentcrm-body>.fluentcrm-campaigns .fluentcrm_contact_header .input-search-notes{height:32px}.fluentcrm-body>.fluentcrm-campaigns .fluentcrm_contact_header .input-search-notes input{height:32px}.fluentcrm_campaign_emails .refresh-campaign-btn{padding-top:7px;padding-bottom:7px}.fluentcrm_campaign_emails .fluentcrm-searcher{position:relative}.fluentcrm_campaign_emails .fluentcrm-searcher .el-input-group__append{position:absolute;right:1px;top:1px;width:40px;height:28px;text-align:center;overflow:hidden;padding:0;border:none}.fluentcrm_campaign_emails .fluentcrm-searcher .el-input-group__append button{padding:0;width:100%;height:100%;text-align:center;display:inline-block;margin:0;transition:.3s;-moz-transition:.3s;-ms-transition:.3s;-o-transition:.3s}.fluentcrm_campaign_emails .fluentcrm-searcher .el-input-group__append button:hover{color:var(--fc-deep-bg);background-color:#2225301a}.fluentcrm_campaign_emails .fluentcrm-searcher input{width:242px;margin:0;border:1px solid var(--fc-primary-border);border-radius:4px;outline:none;box-shadow:none}.fluentcrm_campaign_emails .fluentcrm-searcher input:focus{border-color:var(--fc-text-link)}.fluentcrm-templates .templates-table{padding-bottom:20px}.fluentcrm-templates-action-buttons{display:flex;align-items:center}@media (max-width: 767px){.fluentcrm-templates-action-buttons{flex-wrap:wrap;align-items:flex-start;gap:8px}.fluentcrm-templates-action-buttons>*{margin-left:0;margin-right:0}}.fluentcrm-templates-action-buttons>*:last-child{margin-left:5px}.fluentcrm-templates-action-buttons .fc-search-box .el-input{height:32px}.fluentcrm-templates-action-buttons .fc-search-box .el-input .el-input__inner{height:100%}.fluentcrm-templates-action-buttons .fc_right_search{text-align:right;padding:0;margin:0 5px 0 0}.fluentcrm-templates-action-buttons .fc_right_search>div{max-width:200px}.fluentcrm-templates-action-buttons .refresh-setting-icon{background:var(--fc-primary-bg);width:40px;text-align:center;border:1px solid var(--fc-light-bg)}.fluentcrm_import_email_templates .fc-select-template-header{display:flex;justify-content:space-between;align-items:center;margin-bottom:15px}.fluentcrm_import_email_templates .fc-select-template-header .fc-search-box{width:100%;text-align:right}.fluentcrm_import_email_templates .fc-select-template-header .fc-search-box>.el-input{width:250px}.fluentcrm_import_email_templates .template-name{margin:0;cursor:pointer;color:var(--fc-primary-text);font-size:14px;font-weight:400}.fluentcrm_import_email_templates .fc-action-btns{display:flex;align-items:center;gap:4px}.fluentcrm_import_email_templates .fc-action-btns .el-button{margin:0;border:1px solid var(--fc-primary-border);color:var(--fc-primary-text)}.fc_skeleton_loader{padding:20px;margin-bottom:20px;background:var(--fc-primary-bg)}.fc_links_inline li a{text-decoration:none}.fc_built_in_templates .el-skeleton{display:grid;grid-template-columns:1fr 1fr 1fr;gap:10px;padding:0 15px;width:100%;box-sizing:border-box}.fc_built_in_templates .fc_built_in_templates_wrap{display:grid;grid-template-columns:1fr 1fr 1fr;gap:10px}.fc_built_in_templates .fc_built_in_templates_wrap .fc_create_new{grid-column:1/-1}.fc_built_in_templates .fc_built_in_templates_wrap .fc_build_in_temp_box{box-shadow:none;transition:.3s}.fc_built_in_templates .fc_built_in_templates_wrap .fc_build_in_temp_box:hover{box-shadow:0 5px 15px #0000000d}.fc_built_in_templates .fc_built_in_templates_wrap .fc_build_in_temp_box .el-card__body{padding:5px 5px 10px}.fc_built_in_templates .fc_built_in_templates_wrap .fc_build_in_temp_box .fc_build_in_temp_box_inner .img-box{height:180px;display:flex;align-items:center;justify-content:center;background:var(--fc-secondary-bg);overflow:hidden}.fc_built_in_templates .fc_built_in_templates_wrap .fc_build_in_temp_box .fc_build_in_temp_box_inner .img-box p{margin:0}.fc_built_in_templates .fc_built_in_templates_wrap .fc_build_in_temp_box .fc_build_in_temp_box_inner img{height:100%;object-fit:cover;display:block;margin-left:auto;margin-right:auto;max-width:100%}.fc_built_in_templates .fc_built_in_templates_wrap .fc_build_in_temp_box .fc_build_in_temp_box_inner .img-empty{width:100%}.fc_built_in_templates .fc_built_in_templates_wrap .fc_build_in_temp_box .fc_build_in_temp_box_inner .content{padding:0 10px}.fc_built_in_templates .fc_built_in_templates_wrap .fc_build_in_temp_box .fc_build_in_temp_box_inner .content h2{margin:10px 0 2px;font-weight:600;font-size:14px;line-height:1.2}.fc_built_in_templates .fc_built_in_templates_wrap .fc_build_in_temp_box .fc_build_in_temp_box_inner .content p{margin:0 0 10px;font-size:12px;line-height:1.3}.fc_built_in_templates .fc_built_in_templates_wrap .fc_build_in_temp_box .fc_build_in_temp_box_inner .actions{display:flex;align-items:center;justify-content:space-between;border-top:1px solid rgba(0,0,0,.1);padding-top:12px}.fc_built_in_templates .fc_built_in_templates_wrap .fc_build_in_temp_box .fc_build_in_temp_box_inner .actions .el-button .el-loading-mask{background-color:#fffc}.fc_built_in_templates .fc_built_in_templates_wrap .fc_build_in_temp_box .fc_build_in_temp_box_inner .actions .el-button .el-loading-mask .el-loading-spinner{margin-top:0;transform:translateY(-50%)}.fc_built_in_templates .fc_built_in_templates_wrap .fc_build_in_temp_box .fc_build_in_temp_box_inner .actions .el-link{font-size:12px}.fc_built_in_templates .fc_built_in_templates_wrap .fc_build_in_temp_box .fc_build_in_temp_box_inner .actions .el-link:focus{outline:none;box-shadow:none}.fcrm_create_from_scratch_box{max-width:400px;width:100%;margin:0 auto 20px;border-radius:var(--fcrm-border-radius-8);border:1px dashed var(--fc-secondary-border);padding:32px;display:flex;flex-direction:column;justify-content:center;align-items:center;gap:12px;text-align:center;cursor:pointer;transition:border-color .2s ease}.fcrm_create_from_scratch_box:hover{border-color:var(--fc-primary-text)}.fcrm_create_from_scratch_box .icon{width:40px;height:40px;border-radius:50%;display:flex;align-items:center;justify-content:center;position:relative;background:var(--fc-secondary-bg);color:var(--fc-secondary-text)}.fluentcrm-campaign .el-steps .el-step.is-simple:not(:last-of-type) .el-step__title{max-width:60%}.fluentcrm-campaign .steps-nav{margin:0 auto;text-align:center}.fluentcrm-campaign .step-container{margin-top:30px}.fluentcrm-campaign .action-buttons{margin:0;text-align:right}.fluentcrm-campaign .action-buttons .campaign-title{padding:10px;float:left;font-weight:500;font-size:20px;display:inline-block}.fluentcrm-campaign .action-buttons .campaign-title span.status{font-weight:400;font-size:12px;color:var(--fc-text-muted)}.fluentcrm-campaign .action-buttons .campaign-title>span{cursor:pointer;font-weight:400;font-size:12px;color:var(--fc-text-muted)}.fluentcrm-campaign .action-buttons .campaign-title>span>span{color:var(--fc-deep-bg)}.fluentcrm-campaign .fcrm_breadcrumb_inline_edit .fcrm_breadcrumb_item{display:inline-flex;align-items:center;gap:8px}.fluentcrm-campaign .fc_breadcrumb_edit_icon{margin-left:4px;color:var(--fc-text-muted);font-size:14px}.fluentcrm-campaign .fc_breadcrumb_edit_icon:hover{color:var(--fc-deep-bg)}.fluentcrm-campaign .fcrm_inline_title_wrap{display:flex;align-items:center}.fluentcrm-campaign .fcrm_inline_title_input{width:180px}.fluentcrm-campaign .fcrm_inline_title_input .el-input__wrapper{height:32px}.fluentcrm-campaign .fcrm_inline_title_input input{font-weight:500;font-size:14px;color:var(--el-text-color-primary, var(--fc-primary-text))}.fluentcrm-campaign .fcrm_inline_title_actions{display:inline-flex;align-items:center;gap:8px;margin-left:2px;padding-left:8px;border-left:1px solid var(--el-border-color-lighter, var(--fc-primary-border))}.fluentcrm-campaign .fcrm_inline_title_actions .el-button{margin:0}.campaign_review_items{border:solid 1px var(--fc-secondary-border);border-radius:4px;margin-bottom:30px}.campaign_review_items .camapign_review_item{padding:18px;border-top:1px dotted var(--fc-primary-border)}.campaign_review_items .camapign_review_item h3{margin:0 0 10px;padding:0;font-size:17px;color:var(--fc-text-muted)}.campaign_review_items .camapign_review_item:first-child{border-top:0}.fluentcrm_email_body_preview{max-width:700px;padding:20px;border:2px dashed #dac71b;opacity:.8;max-height:250px;overflow:auto}.fluentcrm_email_body_preview img{max-width:100%}ul.fluentcrm_stat_cards{display:flex;margin:20px 0 0;padding:0;list-style:none;gap:20px}ul.fluentcrm_stat_cards li{display:flex;flex-direction:column;gap:5px;flex-wrap:wrap;flex:1 1 175px;border-radius:8px;margin-bottom:20px;border:1px solid var(--fc-primary-border);background:var(--fc-primary-bg);padding:16px}ul.fluentcrm_stat_cards li.fc_camp_data_revenue,ul.fluentcrm_stat_cards li.fc_camp_data_unsubscribe{cursor:pointer}ul.fluentcrm_stat_cards li.fc_camp_data_revenue:hover,ul.fluentcrm_stat_cards li.fc_camp_data_unsubscribe:hover{border:1px solid var(--fc-secondary-bg);box-shadow:1px 2px 2px 3px var(--fc-secondary-bg)}ul.fluentcrm_stat_cards li h4{margin:0;overflow:hidden;color:var(--fc-secondary-text);font-size:12px;font-style:normal;font-weight:500;line-height:16px}ul.fluentcrm_stat_cards li .fluentcrm_cart_counter{color:var(--fc-primary-text);font-size:18px;font-style:normal;font-weight:500;line-height:24px;letter-spacing:-.27px}.fc_campaign_report{border:1px solid var(--fc-primary-border);border-radius:8px;display:block}.fc_campaign_report .fcrm_perf_bars{padding:20px}.fc_campaign_report h3{margin:0;padding:15px 20px;border-bottom:1px solid var(--fc-primary-border)}.fc_campaign_report ul{margin:0;padding:0 20px;list-style:none}.fc_campaign_report ul li{display:block;padding:15px 0;margin:0;font-size:120%;border-bottom:1px solid var(--fc-primary-border);color:var(--fc-primary-text)}.fc_campaign_report ul li span.fc_report_value{float:right}.fc_campaign_report ul li:last-child{border-bottom:0}.fc_campaign_report ul li>i{margin-right:10px}.fc_campaign_report .fc_campaign_report_body_{padding:0 20px}.fc_campaign_archived_wrapper{margin-bottom:30px}.fcrm_campaign_actions_popover{min-width:140px}.fcrm_campaign_actions_popover .fcrm_campaign_actions_menu{display:flex;flex-direction:column}.fcrm_campaign_actions_popover .fcrm_campaign_action_item,.fcrm_campaign_actions_popover .fcrm_campaign_action_item_inner{display:flex;align-items:center;gap:8px;padding:7px 10px;font-size:14px;color:var(--el-text-color-regular, var(--fc-secondary-text));cursor:pointer;white-space:nowrap;font-weight:400;border-radius:8px}.fcrm_campaign_actions_popover .fcrm_campaign_action_item:hover,.fcrm_campaign_actions_popover .fcrm_campaign_action_item_inner:hover{background-color:var(--el-fill-color-light, var(--fc-secondary-bg));color:var(--fc-primary-text)}.fcrm_campaign_actions_popover .fcrm_campaign_action_item .icon,.fcrm_campaign_actions_popover .fcrm_campaign_action_item_inner .icon{display:block}.fcrm_campaign_actions_popover .fcrm_campaign_action_item .icon svg,.fcrm_campaign_actions_popover .fcrm_campaign_action_item_inner .icon svg{display:block}.fcrm_campaign_actions_popover .fcrm_campaign_action_item .el-button,.fcrm_campaign_actions_popover .fcrm_campaign_action_item_inner .el-button{font-weight:400}.fcrm_campaign_actions_popover .fcrm_campaign_action_item .el-icon,.fcrm_campaign_actions_popover .fcrm_campaign_action_item_inner .el-icon{font-size:16px;width:16px;min-width:16px;flex-shrink:0;display:inline-flex;align-items:center;justify-content:center}.fcrm_campaign_actions_popover .fcrm_campaign_action_item_inner{width:100%;border:none;background:none;text-align:left}.fluentcrm_databox{background:var(--fc-primary-bg);padding:20px;margin-top:20px;margin-bottom:20px;border-radius:6px;box-shadow:0 0 35px #10407026}.fluentcrm_databox .fc_global_form_builder .el-form .el-form-item:nth-child(2n){padding-right:0}.fluentcrm_databox .fc_global_form_builder .el-form .fcrm_options_selector .el-select__tags .el-tag{margin-left:10px}.fc_proc_sum{max-width:900px;margin:20px auto;padding:20px 30px;background:var(--fc-secondary-bg);border-radius:5px}.fc_proc_sum h3.no_spaced{margin:0}.fc_proc_sum .camapign_review_item{margin:0 -30px;padding:15px 20px;border-bottom:1px dotted var(--fc-secondary-text)}.fc_proc_sum .camapign_review_item:last-child{border-bottom:none}.fc_proc_heading ul{list-style:disc;margin:0;padding:0 0 0 15px}.fc_proc_heading ul li{font-size:110%}.fcrm_email_campaign_stats_card_wrapper{display:grid;grid-template-columns:repeat(auto-fit,minmax(250px,1fr));gap:24px}.fcrm_email_campaign_stats_card .fcrm_perf_bars{padding:20px}.fcrm_email_campaign_stats_card .fcrm_sms_campaign_view--stat-item{margin-bottom:16px}.fcrm_email_campaign_stats_card .fcrm_sms_campaign_view--stat-item:last-child{margin-bottom:0}.fcrm_email_campaign_stats_card .fcrm_sms_campaign_view--stat-label{display:flex;align-items:center;gap:4px}.fcrm_email_campaign_stats_card .fcrm_sms_campaign_view--stat-label .el-tooltip__trigger{color:var(--fc-text-muted)}.fcrm_email_campaign_stats_card .fcrm_sms_campaign_view--link-list{padding:0}.fcrm_email_campaign_stats_card .fcrm_sms_campaign_view--link-list .fcrm_table_wrapper{border-radius:0}.fcrm_email_campaign_stats_card .fcrm_sms_campaign_view--link-list .el-table .el-table__header tr th{border-top:none;border-radius:0}.fcrm_email_campaign_stats_card .fcrm_sms_campaign_view--link-list .el-table .el-table__body tr td a{display:block}.fcrm_email_campaign_stats_card .fcrm_sms_campaign_view--link-list .el-table .el-table__body tr td a:hover{text-decoration:underline}.fcrm_email_camp_recipients_processing_card .fcrm_sms_campaign_view--progress-card{box-shadow:none;padding:0;border-radius:0}.fcrm_email_camp_recipients_processing_card .fcrm_sms_campaign_view--progress-header{margin-bottom:10px}.fcrm_settings,.fcrm_view{background:var(--fc-secondary-bg);min-height:100vh}.fcrm_header{background:var(--fc-primary-bg);border:none;border-bottom:1px solid var(--fc-primary-border);padding:10px 32px;margin:0;display:flex;align-items:center;gap:12px;flex-shrink:0;box-shadow:0 8px 12px -12px #0e121b05}.fcrm_header .fcrm_header_title{flex:1;display:flex;align-items:center;gap:12px}.fcrm_header .fcrm_header_title h3{flex:1;font-size:18px;font-weight:500;line-height:24px;letter-spacing:-.27px;color:var(--fc-primary-text);margin:0;padding:0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.fcrm_smart_links_wrap{box-sizing:border-box}.fcrm_smart_links_wrap .el-table .is-vertical{display:none!important}.fcrm_smart_links_wrap .el-table .el-table__expand-icon{color:var(--fc-secondary-text);font-size:14px}.fcrm_smart_links_wrap .el-table .el-table__expand-icon:hover{color:var(--fc-primary-text)}.fcrm_smart_links_wrap .el-table .el-table__expanded-cell{background:var(--fc-primary-bg)!important;border-bottom:1px solid var(--fc-primary-border);padding:16px 20px 16px 52px}.fcrm_smart_links_wrap .el-table .el-table__expanded-cell .fcrm_expand_content{display:flex;flex-direction:column;gap:20px}.fcrm_smart_links_wrap .el-table .el-table__expanded-cell .fcrm_expand_content .fcrm_expand_section{display:flex;flex-direction:column;gap:8px;width:100%}.fcrm_smart_links_wrap .el-table .el-table__expanded-cell .fcrm_expand_content .fcrm_expand_section .fcrm_notes_content{font-size:14px;font-weight:400;line-height:20px;letter-spacing:-.084px;color:var(--fc-primary-text);margin:0;padding:0;max-width:859px}.fcrm_smart_links_wrap .el-table .el-table__expanded-cell .fcrm_expand_content .fcrm_expand_section .fcrm_notes_content p{margin:0;padding:0}.fcrm_smart_links_wrap .el-table .el-table__expanded-cell .fcrm_expand_content .fcrm_expand_section .fcrm_notes_content p:not(:last-child){margin-bottom:0}.fcrm_smart_links_wrap .el-table .el-table__expanded-cell .fcrm_expand_content .fcrm_stats_section{display:flex;gap:24px;align-items:flex-start;flex-shrink:0}.fcrm_smart_links_wrap .el-table .el-table__header-wrapper th:nth-child(1) .cell{display:none}.fcrm_smart_links_wrap .el-table .title{font-size:14px;font-weight:500;line-height:20px;letter-spacing:-.084px;color:var(--fc-primary-text);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.fcrm_smart_links_wrap .el-table .el-table__body tr:hover>td.el-table__expanded-cell{background-color:var(--fc-primary-bg)!important}.fcrm_item_copier_wrapper{width:100%;max-width:100%;min-width:0}.fcrm_smart_url_box{display:flex;align-items:center;gap:8px;background:var(--fc-secondary-bg);border-radius:6px;padding:4px 6px;width:100%;max-width:100%;min-width:0;overflow:hidden;transition:background .2s ease}.fcrm_smart_url_box:hover{background:var(--fc-secondary-bg)}.fcrm_smart_url_box:hover .fcrm_copy_btn{opacity:1}.fcrm_smart_url_box .fcrm_smart_url_text{flex:1;display:block;min-width:0;font-size:14px;font-weight:400;line-height:20px;letter-spacing:-.084px;color:var(--fc-primary-text);white-space:normal;overflow:visible;text-overflow:unset;overflow-wrap:anywhere;word-break:break-word;-webkit-user-select:text;user-select:text;cursor:text}.fcrm_smart_url_box .fcrm_copy_btn{background:transparent;border:none;padding:4px;height:20px;width:20px;display:flex;align-items:center;justify-content:center;border-radius:4px;cursor:pointer;transition:all .2s ease;flex-shrink:0;opacity:.7}.fcrm_smart_url_box .fcrm_copy_btn:hover{background:var(--fc-secondary-border);opacity:1}.fcrm_smart_url_box .fcrm_copy_btn:active{background:var(--fc-secondary-border);transform:scale(.95)}.fcrm_smart_url_box .fcrm_copy_btn .el-icon{font-size:16px;color:var(--fc-secondary-text);line-height:1;display:flex;align-items:center;justify-content:center}.fcrm_smart_url_box .fcrm_copy_btn .el-icon svg{width:16px;height:16px;fill:currentColor}.fcrm_smart_url_box .fcrm_copy_btn:hover .el-icon{color:var(--fc-primary-text)}.fcrm_smart_url_box .fcrm_check_icon{color:var(--fc-success)!important;animation:checkSuccess .3s ease}.fcrm-shortcode-display .fcrm-shortcode-copier .fcrm_smart_url_box{padding:0 8px}@keyframes checkSuccess{0%{transform:scale(.8);opacity:0}50%{transform:scale(1.2)}to{transform:scale(1);opacity:1}}.fcrm_smart_links_wrap .el-table .el-table__body-wrapper td:nth-child(3){max-width:400px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.fcrm_target_url_link{color:var(--fc-secondary-text);text-decoration:underline;text-decoration-color:var(--fc-primary-border);text-underline-offset:2px;font-size:14px;font-weight:400;line-height:20px;letter-spacing:-.084px;display:inline-block;max-width:100%;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;transition:all .2s ease}.fcrm_target_url_link:hover{color:var(--fc-primary-text);text-decoration-color:var(--fc-primary-text)}.fcrm_target_url_link:visited{color:var(--fc-secondary-text)}.fcrm_smart_links_wrap .el-table .fcrm_actions_column{text-align:center}.fcrm_smart_links_wrap .el-table .fcrm_actions_column .cell{display:flex;align-items:center;justify-content:center;padding:0}.fcrm_smart_links_wrap .el-table .el-table__header-wrapper .fcrm_actions_column{text-align:center}.fcrm_smart_links_wrap .el-table .el-table__header-wrapper .fcrm_actions_column .cell{justify-content:center}.fcrm_pagination{padding:10px 20px!important;justify-content:flex-end;align-items:center}.fcrm_pagination .el-pagination__sizes{min-width:100px!important}.fcrm_smart_link_activation_required{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:20px;max-width:600px;margin:0 auto}.fcrm_smart_link_activation_required h3{font-size:20px;line-height:28px;text-align:center;margin:0 0 4px;font-weight:500;color:var(--fc-primary-text)}.fcrm_smart_link_activation_required p{color:var(--fc-secondary-text);margin:0;padding:0;font-weight:400;font-size:14px;line-height:20px;text-align:center}.fcrm_smart_link_activation_required .el-button{margin-top:20px}@media (max-width: 1024px){.fcrm_header{padding:10px 16px}}@media (max-width: 768px){.el-table .el-table__header-wrapper td:nth-child(2),.el-table .el-table__header-wrapper th:nth-child(2),.el-table .el-table__body-wrapper td:nth-child(2),.el-table .el-table__body-wrapper th:nth-child(2){width:120px!important;min-width:120px!important}.fcrm_pagination{flex-wrap:wrap;justify-content:center!important;gap:10px}}@media (max-width: 480px){.fcrm_header{padding:8px 12px}.fcrm_header .fcrm_header_title h3{font-size:16px}.el-table{font-size:12px}.el-table .el-table__header-wrapper th,.el-table .el-table__body-wrapper td{padding:0}.fcrm_smart_url_box{height:auto;min-height:32px;padding:6px}.fcrm_smart_url_box .fcrm_smart_url_text{font-size:12px;word-break:break-all;white-space:normal}}.fcrm_create_link_wrapper{background:var(--fc-primary-bg)}.fcrm_create_link_wrapper input[type=text],.fcrm_create_link_wrapper input[type=email],.fcrm_create_link_wrapper input[type=url],.fcrm_create_link_wrapper input[type=password],.fcrm_create_link_wrapper input[type=search],.fcrm_create_link_wrapper input[type=number],.fcrm_create_link_wrapper input[type=tel],.fcrm_create_link_wrapper select{padding:0;line-height:normal;min-height:auto;box-shadow:none;border-radius:0;border:none;background-color:transparent;color:inherit}.fcrm_create_link_wrapper .fcrm_global_form_builder,.fcrm_create_link_wrapper .fcrm_global_form_builder .el-form{display:flex;flex-direction:column;gap:20px}.fcrm_create_link_wrapper .fcrm_global_form_builder .el-form-item{margin-bottom:0;width:100%}.fcrm_create_link_wrapper .fcrm_global_form_builder .el-form-item__label{display:flex;align-items:center;gap:1px}.fcrm_create_link_wrapper .fcrm_global_form_builder .el-form-item__label>div{display:flex;align-items:center;gap:4px}.fcrm_create_link_wrapper .fcrm_global_form_builder .el-form-item__label .tooltip-icon{font-size:14px;color:var(--fc-text-muted);margin-left:0}.fcrm_create_link_wrapper .fcrm_global_form_builder .el-form-item__content{line-height:normal!important;margin-left:0!important;width:100%}.fcrm_create_link_wrapper .fcrm_global_form_builder .el-form-item__content>div{width:100%}.fcrm_create_link_wrapper .fcrm_global_form_builder .el-textarea{width:100%}.fcrm_create_link_wrapper .fcrm_global_form_builder .el-textarea textarea,.fcrm_create_link_wrapper .fcrm_global_form_builder .el-textarea textarea.el-textarea__inner{padding:10px 12px;min-height:128px;max-height:400px;resize:vertical}.fcrm_create_link_wrapper .fcrm_global_form_builder .el-textarea textarea::placeholder,.fcrm_create_link_wrapper .fcrm_global_form_builder .el-textarea textarea::-webkit-input-placeholder,.fcrm_create_link_wrapper .fcrm_global_form_builder .el-textarea textarea::-moz-placeholder,.fcrm_create_link_wrapper .fcrm_global_form_builder .el-textarea textarea:-ms-input-placeholder,.fcrm_create_link_wrapper .fcrm_global_form_builder .el-textarea textarea.el-textarea__inner::placeholder,.fcrm_create_link_wrapper .fcrm_global_form_builder .el-textarea textarea.el-textarea__inner::-webkit-input-placeholder,.fcrm_create_link_wrapper .fcrm_global_form_builder .el-textarea textarea.el-textarea__inner::-moz-placeholder,.fcrm_create_link_wrapper .fcrm_global_form_builder .el-textarea textarea.el-textarea__inner:-ms-input-placeholder{color:var(--fc-text-muted);font-size:14px;font-weight:400;line-height:20px;opacity:1}.fcrm_create_link_wrapper .fcrm_global_form_builder .fcrm_help_text{font-size:12px;font-weight:400;line-height:16px;color:var(--fc-secondary-text);margin:0}.fcrm_create_link_wrapper .fcrm_global_form_builder .fcrm_group_field_half{display:flex;flex-direction:column;gap:24px;width:100%}.fcrm_create_link_wrapper .fcrm_global_form_builder .fcrm_group_field_half>.el-form-item__content{display:flex;flex-direction:column;gap:24px;width:100%}.fcrm_create_link_wrapper .fcrm_global_form_builder .fcrm_group_field_half .fcrm_form_sub_group{display:flex;flex-direction:column;gap:4px;width:100%}.fcrm_create_link_wrapper .fcrm_global_form_builder .fcrm_group_field_half .fcrm_tag_list_wrapper{display:flex;flex-direction:column;gap:24px;width:100%}.fcrm_create_link_wrapper .fcrm_global_form_builder .fcrm_tag_list_wrapper{width:100%}.fcrm_create_link_wrapper .fcrm_global_form_builder .fcrm_tag_list_wrapper .el-form-item{margin-bottom:0;width:100%}.fcrm_create_link_wrapper .fcrm_global_form_builder .fcrm_tag_list_wrapper .el-form-item .el-form-item__content{line-height:normal;margin-left:0;width:100%}.fcrm_create_link_wrapper .fcrm_global_form_builder .fcrm_tag_list_wrapper .fcrm_inline_help{font-size:12px;font-weight:400;line-height:16px;color:var(--fc-secondary-text);margin:4px 0 0;padding:0}.fcrm_create_link_wrapper .fcrm_global_form_builder .fcrm_checkbox_wrapper>.el-form-item__label{display:none!important}.fcrm_create_link_wrapper .fcrm_global_form_builder .fcrm_checkbox_wrapper .fcrm-info-alert{display:flex;gap:4px;align-items:flex-start;margin:4px 0 0;background:none;padding:0 0 0 26px}.fcrm_create_link_wrapper .fcrm_global_form_builder .fcrm_checkbox_wrapper .fcrm-info-alert .fcrm-info-icon{display:none}.fcrm_create_link_wrapper .fcrm_global_form_builder .fcrm_checkbox_wrapper .fcrm-info-alert p{font-weight:400;font-size:12px;line-height:16px;color:var(--fc-secondary-text);margin:0}.fcrm_create_link_wrapper .fcrm_form_actions{display:flex;gap:12px;justify-content:flex-end;align-items:center;padding:20px;border-top:1px solid var(--fc-primary-border);margin-top:20px}.fcrm_create_link_wrapper .fcrm_form_actions .el-button{margin:0}.fcrm_create_link_wrapper .fcrm_narrow_box{padding:24px;border-radius:var(--fcrm-border-radius-8, 8px)}.fcrm_create_link_wrapper .fcrm_narrow_box.fcrm_white_inverse{background:var(--fc-secondary-bg);border:1px solid var(--fc-primary-border)}.fcrm_create_link_wrapper .fcrm_narrow_box p{font-size:14px;font-weight:400;line-height:20px;letter-spacing:-.084px;color:var(--fc-primary-text);margin-bottom:12px}.fcrm_create_link_wrapper .fcrm_narrow_box .el-input textarea,.fcrm_create_link_wrapper .fcrm_narrow_box .el-textarea textarea,.fcrm_create_link_wrapper .fcrm_narrow_box .el-input .el-textarea__inner,.fcrm_create_link_wrapper .fcrm_narrow_box .el-textarea .el-textarea__inner{background:var(--fc-primary-bg);border:1px solid var(--fc-primary-border);border-radius:8px;padding:10px 12px;font-size:14px;font-weight:400;line-height:20px;letter-spacing:-.084px;color:var(--fc-primary-text);min-height:80px;box-shadow:none}.fcrm_smart_link_drawer .el-drawer__body{padding:20px 20px 0;overflow-y:auto}.fcrm_smart_link_drawer .fcrm_create_link_wrapper{padding:0}.fcrm_smart_link_drawer .fcrm_create_link_wrapper .fcrm_form_actions{margin-left:-20px;margin-right:-20px;padding:15px 20px}.fcrm_smart_link_dialog .el-dialog{overflow:hidden;box-shadow:0 4px 16px #0000001a;padding:0;max-width:640px;width:100%}.fcrm_smart_link_dialog .el-dialog__header{background:var(--fc-primary-bg)!important;border-bottom:1px solid var(--fc-primary-border)!important;padding:16px 16px 16px 20px!important;margin:0!important;border-radius:20px 20px 0 0!important}.fcrm_smart_link_dialog .el-dialog__header .el-dialog__title{font-size:16px!important;font-weight:500!important;line-height:24px!important;letter-spacing:-.176px!important;color:var(--fc-primary-text)!important}.fcrm_smart_link_dialog .el-dialog__header .el-dialog__headerbtn{position:absolute!important;top:16px!important;right:16px!important;width:24px!important;height:24px!important;padding:2px!important;border-radius:6px!important;transition:background .2s ease}.fcrm_smart_link_dialog .el-dialog__header .el-dialog__headerbtn:hover{background:var(--fc-secondary-bg)!important}.fcrm_smart_link_dialog .el-dialog__header .el-dialog__headerbtn .el-dialog__close{color:var(--fc-secondary-text)!important;font-size:20px!important;width:20px!important;height:20px!important}.fcrm_smart_link_dialog .el-dialog__header .el-dialog__headerbtn .el-dialog__close:hover{color:var(--fc-primary-text)!important}.fcrm_smart_link_dialog .el-dialog__body{padding:0!important;background:var(--fc-primary-bg)!important}.fcrm_smart_link_dialog .el-dialog__footer{padding:0!important;background:var(--fc-primary-bg)!important;border-radius:0 0 20px 20px!important}.fcrm_incoming_webhooks_wrap{background:var(--fc-primary-bg);border-radius:8px;margin:0 auto;overflow:hidden}.fcrm_incoming_webhooks_wrap .settings-section{margin-bottom:0}.fcrm_incoming_webhooks_wrap .fcrm_webhooks_action_bar{padding:12px 20px;margin-bottom:0;border-radius:0}.fcrm_incoming_webhooks_wrap .title{font-size:14px;font-weight:400;line-height:20px;letter-spacing:-.084px;color:var(--fc-primary-text);white-space:normal;overflow:visible;text-overflow:unset;word-break:break-word;overflow-wrap:break-word}.fcrm_incoming_webhooks_wrap .el-table__body-wrapper td:nth-child(1) .cell,.fcrm_incoming_webhooks_wrap .el-table__body-wrapper td:nth-child(2) .cell,.fcrm_incoming_webhooks_wrap .el-table__header-wrapper th:nth-child(1) .cell,.fcrm_incoming_webhooks_wrap .el-table__header-wrapper th:nth-child(2) .cell{min-width:0}.fcrm_incoming_webhooks_wrap .fcrm_item_copier_wrapper{min-width:0;width:100%}.fcrm_incoming_webhooks_wrap .fcrm_smart_url_box{min-width:0;align-items:flex-start}.fcrm_incoming_webhooks_wrap .fcrm_smart_url_box .fcrm_smart_url_text{white-space:normal;overflow:visible;text-overflow:unset;word-break:break-all;overflow-wrap:break-word}@media (max-width: 1024px){.fcrm_incoming_webhooks_wrap{border-radius:8px}.fcrm_incoming_webhooks_wrap .fcrm_webhooks_action_bar{flex-direction:column;align-items:stretch!important;padding:12px 16px;gap:12px}.fcrm_incoming_webhooks_wrap .fcrm_webhooks_action_bar .fcrm_action_buttons{width:100%;justify-content:flex-start}}@media (max-width: 768px){.fcrm_incoming_webhooks_wrap .fcrm_webhooks_action_bar{padding:12px}.fcrm_incoming_webhooks_wrap .fcrm_webhooks_action_bar .fcrm_action_buttons{width:100%;flex-wrap:wrap;justify-content:flex-start}.fcrm_incoming_webhooks_wrap .el-table .el-table__header-wrapper td:nth-child(2),.fcrm_incoming_webhooks_wrap .el-table .el-table__header-wrapper th:nth-child(2),.fcrm_incoming_webhooks_wrap .el-table .el-table__body-wrapper td:nth-child(2),.fcrm_incoming_webhooks_wrap .el-table .el-table__body-wrapper th:nth-child(2){width:120px!important;min-width:120px!important}}@media (max-width: 480px){.fcrm_incoming_webhooks_wrap{margin:0;border-radius:0}.fcrm_incoming_webhooks_wrap .fcrm_webhooks_action_bar{padding:10px}.fcrm_incoming_webhooks_wrap .el-table{font-size:12px}.fcrm_incoming_webhooks_wrap .el-table .el-table__header-wrapper th,.fcrm_incoming_webhooks_wrap .el-table .el-table__body-wrapper td{padding:8px 6px}.fcrm_incoming_webhooks_wrap .el-table .el-table__header-wrapper th:first-child,.fcrm_incoming_webhooks_wrap .el-table .el-table__body-wrapper td:first-child{padding-left:10px}.fcrm_incoming_webhooks_wrap .fcrm_action_dropdown_cell .fcrm_action_menu_btn .el-icon{width:16px;height:16px;font-size:14px}}.fcrm_create_webhook_modal .fcrm_modal_header{background:var(--fc-primary-bg);border-bottom:1px solid var(--fc-primary-border);border-radius:20px 20px 0 0;padding:0}.fcrm_create_webhook_modal .fcrm_modal_header .fcrm_modal_header_content{display:flex;align-items:center;justify-content:space-between;padding:16px 20px;gap:12px}.fcrm_create_webhook_modal .fcrm_modal_header .fcrm_modal_title{font-weight:500;font-size:16px;line-height:24px;color:var(--fc-primary-text);margin:0;flex:1;letter-spacing:-.176px}.fcrm_create_webhook_modal .fcrm_modal_header .fcrm_modal_close_btn{background:transparent;border:none;padding:2px;border-radius:6px;cursor:pointer;display:flex;align-items:center;justify-content:center;width:24px;height:24px;color:var(--fc-secondary-text)}.fcrm_create_webhook_modal .fcrm_modal_header .fcrm_modal_close_btn:hover{background:var(--fc-secondary-bg)}.fcrm_create_webhook_modal .fcrm_modal_header .fcrm_modal_close_btn i{font-size:14px}.fcrm_create_webhook_modal .fcrm_modal_body{padding:20px;flex:1}.fcrm_create_webhook_modal .fcrm_modal_body .fcrm_form_item{margin-bottom:20px}.fcrm_create_webhook_modal .fcrm_modal_body .fcrm_form_item:last-child{margin-bottom:0}.fcrm_create_webhook_modal .fcrm_modal_body .fcrm_form_item .el-form-item__content{line-height:normal}.fcrm_create_webhook_modal .fcrm_modal_body .fcrm_input,.fcrm_create_webhook_modal .fcrm_modal_body .fcrm_select{width:100%}.fcrm_create_webhook_modal .fcrm_modal_body .fcrm_select .el-select__tags{max-width:calc(100% - 30px)}.fcrm_create_webhook_modal .fcrm_modal_body .fcrm_select .el-input__suffix{right:8px}.fcrm_create_webhook_modal .fcrm_modal_body .fcrm_select .el-select__caret{color:var(--fc-secondary-text);font-size:14px}.fcrm_create_webhook_modal .fcrm_modal_body .fcrm_webhook_url_section,.fcrm_create_webhook_modal .fcrm_modal_body .webhook-url-section{margin:20px 0}.fcrm_create_webhook_modal .fcrm_modal_body .fcrm_webhook_url_section h4,.fcrm_create_webhook_modal .fcrm_modal_body .webhook-url-section h4{margin-bottom:10px;color:var(--fc-primary-text);font-weight:600}.fcrm_create_webhook_modal .fcrm_modal_body .fcrm_webhook_url_section .url-display .el-input .el-input__wrapper,.fcrm_create_webhook_modal .fcrm_modal_body .webhook-url-section .url-display .el-input .el-input__wrapper{background-color:var(--fc-secondary-bg)}.fcrm_create_webhook_modal .fcrm_modal_body .field-mapping-section{margin:20px 0}.fcrm_create_webhook_modal .fcrm_modal_footer{background:var(--fc-primary-bg);border-top:1px solid var(--fc-primary-border);border-radius:0 0 var(--fcrm-border-radius-8, 8px) var(--fcrm-border-radius-8, 8px);padding:12px 20px}.fcrm_create_webhook_modal .fcrm_modal_footer .fcrm_modal_actions{display:flex;align-items:center;justify-content:flex-end;gap:12px}.fcrm_create_webhook_modal .fcrm_modal_footer .fcrm_modal_actions .el-button{margin:0}@media (max-width: 480px){.fcrm_create_webhook_modal .fcrm_modal_header .fcrm_modal_header_content{padding:12px 16px}.fcrm_create_webhook_modal .fcrm_modal_body{padding:16px}.fcrm_create_webhook_modal .fcrm_modal_footer{padding:12px 16px}.fcrm_create_webhook_modal .fcrm_modal_footer .fcrm_modal_actions{flex-direction:column-reverse;gap:8px}}.fcrm_settings{background:var(--fc-secondary-bg)}.fcrm_settings .fcrm_header{background:var(--fc-primary-bg);border-bottom:1px solid var(--fc-primary-border);padding:10px 32px;display:flex;justify-content:space-between;align-items:center;height:56px}.fcrm_settings .fcrm_header .fcrm_header_title h3{font-size:18px;font-weight:500;line-height:24px;letter-spacing:-.27px;color:var(--fc-primary-text);margin:0}.fcrm_settings .fcrm_header .fcrm_templates_action_buttons .el-button{background:var(--fc-deep-bg);border-color:var(--fc-deep-bg);color:var(--fc-primary-bg);font-size:14px;font-weight:500;line-height:20px;letter-spacing:-.084px;padding:6px 10px;border-radius:8px;height:auto}.fcrm_settings .fcrm_header .fcrm_templates_action_buttons .el-button:hover,.fcrm_settings .fcrm_header .fcrm_templates_action_buttons .el-button:active{background:var(--fc-primary-text);border-color:var(--fc-primary-text)}.fcrm_settings .fcrm_body{padding:24px 32px}@media (max-width: 768px){.fcrm_settings .fcrm_body{padding:20px}}.fcrm_settings .fcrm_body .fcrm_content_card{background:var(--fc-primary-bg);border-radius:0;padding:20px;box-shadow:none;max-width:800px;margin:auto;border-radius:var(--fcrm-border-radius-8, 8px);transition:.3s;-webkit-transition:.3s}.fcrm_setting_card{background:transparent;border:none;padding:0;margin-bottom:0}.fcrm_setting_card+.fcrm_setting_card{margin-top:20px;position:relative;border-top:1px solid var(--fc-primary-border);padding-top:20px}.fcrm_setting_card .fcrm_setting_header{display:flex;flex-direction:column;align-items:flex-start;gap:0;margin-bottom:0}.fcrm_setting_card .fcrm_setting_header .fcrm_setting_title{display:flex;align-items:center;gap:8px;order:2;margin-top:8px}.fcrm_setting_card .fcrm_setting_header .fcrm_setting_title h4,.fcrm_setting_card .fcrm_setting_header .fcrm_setting_title h5{font-size:14px;font-weight:500;line-height:20px;letter-spacing:-.084px;color:var(--fc-primary-text);margin:0}.fcrm_setting_card .fcrm_setting_header .fcrm_setting_title .el-icon{display:none}.fcrm_setting_card .fcrm_setting_description{color:var(--fc-secondary-text);margin:4px 0 0;font-weight:400;font-size:12px;line-height:16px}.fcrm_setting_card .el-radio-group{display:flex;flex-direction:column;gap:20px;padding-left:0;margin-top:0}.fcrm_settings_row{display:flex;gap:24px;align-items:flex-start;width:100%}.fcrm_settings_row .fcrm_content_label{width:300px;flex-shrink:0}.fcrm_settings_row .fcrm_content_label .fcrm_setting_title h4{font-size:14px;font-weight:500;line-height:20px;letter-spacing:-.084px;color:var(--fc-primary-text);margin:0 0 4px}.fcrm_settings_row .fcrm_content_label .fcrm_setting_description{font-size:12px;font-weight:400;line-height:16px;color:var(--fc-secondary-text);margin:0;padding-left:0}.fcrm_settings_row .fcrm_toggle_controls{flex:1;display:flex;flex-direction:column;gap:20px}.fcrm_settings_row .fcrm_toggle_controls .el-radio-group{display:flex;flex-direction:column;gap:20px;padding-left:0;margin-top:0;align-items:flex-start}.fcrm_sublabel{font-size:12px;font-weight:400;line-height:16px;color:var(--fc-secondary-text)}.fcrm_nested_checkbox{margin-top:16px;padding-left:28px}.fcrm_nested_card{border:1px solid var(--fc-primary-border);border-radius:8px;margin-top:16px;margin-left:28px;overflow:hidden}.fcrm_nested_card>h5{font-weight:500;font-size:14px;line-height:20px;margin:0;color:var(--fc-primary-text);border-bottom:1px solid var(--fc-primary-border);padding:12px 20px}.fcrm_nested_card .fcrm_divider{display:none}.fcrm_nested_card .el-form-item .el-form-item__content .el-input{height:auto;border-radius:8px}.fcrm_nested_card .el-form-item .el-form-item__content .el-input .el-input__wrapper{height:auto;min-height:inherit}.fcrm_nested_card .el-form-item{margin-bottom:16px}.fcrm_nested_card .el-form-item:first-of-type{padding:16px 16px 0}.fcrm_nested_card .el-form-item:not(:first-of-type){padding:0 16px}.fcrm_nested_card .el-form-item:last-of-type{padding:0 16px 16px;margin-bottom:0}.fcrm_nested_card .el-form-item .el-form-item__label{font-weight:500;font-size:14px;line-height:20px;color:var(--fc-primary-text)}.fcrm_nested_card .el-form-item .el-input__wrapper,.fcrm_nested_card .el-form-item .el-select__wrapper{width:100%}.fcrm_nested_card .el-form-item p,.fcrm_nested_card .el-form-item .help_text{font-weight:400;font-size:12px;line-height:16px;color:var(--fc-secondary-text);margin:4px 0 0}.fcrm_alert_box{background:var(--fc-secondary-bg);border:none;border-radius:8px;padding:14px;margin-top:16px;margin-left:28px}.fcrm_alert_box p{font-weight:500;font-size:14px;line-height:20px;color:var(--fc-primary-text);margin:0 0 4px}.fcrm_alert_box .fcrm_list{margin:0;padding-left:21px;list-style-type:disc}.fcrm_alert_box .fcrm_list li{color:var(--fc-primary-text);font-weight:400;font-size:14px;line-height:20px;margin:0;opacity:.72}.fcrm_shortcode_section .el-form-item__label{font-weight:400!important;margin-bottom:4px}.fcrm_shortcode_section .fcrm_shortcode_input{width:100%;margin-bottom:12px}.fcrm_shortcode_section .fcrm_shortcode_input .el-input,.fcrm_shortcode_section .fcrm_shortcode_input .el-input .el-input__wrapper,.fcrm_shortcode_section .fcrm_shortcode_input .el-input .el-input__wrapper .el-input__inner{background:var(--fc-secondary-bg)}.fcrm_info_box{background:var(--fc-secondary-bg);border-radius:8px;padding:14px}.fcrm_info_box p{color:var(--fc-primary-text);margin:0;font-weight:400;font-size:14px;line-height:20px}.fcrm_info_box p b{color:var(--fc-primary-text);font-weight:500}@media (max-width: 768px){.fcrm_settings_row{flex-direction:column;gap:16px}.fcrm_settings_row .fcrm_content_label{width:100%}.fcrm_nested_checkbox{padding-left:20px}}@media (max-width: 480px){.fcrm_nested_checkbox{padding-left:0}}.fcrm_addons_features_box{display:flex;align-items:flex-start;justify-content:space-between;gap:40px;border-bottom:1px solid var(--fc-primary-border);padding-bottom:20px;margin-bottom:20px}.fcrm_addons_features_box:last-child{border-bottom:none;margin-bottom:0;padding-bottom:0}.fcrm_addons_features_box--content-title{margin:0 0 4px;color:var(--fc-primary-text);font-weight:500;font-size:16px;line-height:24px}.fcrm_addons_features_box--content-desc{margin:0;color:var(--fc-secondary-text);font-weight:400;font-size:13px;line-height:20px}.fcrm_addons_features_box--content-desc a{color:var(--fc-primary-text);font-weight:500;text-decoration:underline}.fcrm_addons_features_box--actions{display:flex;align-items:center;justify-content:flex-end;gap:12px;flex-wrap:wrap;flex:none}.fcrm_addons_features_box--alert{border-radius:8px;background:var(--fc-secondary-bg);padding:12px 16px;margin-top:12px}.fcrm_addons_features_box--alert p{margin:0 0 8px}.fcrm_addons_features_box--alert ul{list-style:disc;margin:0;padding-left:24px}.fcrm_addons_popover_body{padding:12px}.fcrm_addons_popover_body--header{margin-bottom:16px}.fcrm_addons_popover_body--header .fcrm_addons_popover_body--header-title{margin:0;color:var(--fc-primary-text);font-weight:500;font-size:16px;line-height:20px}.fcrm_addons_popover_body .el-checkbox{align-items:flex-start}.fcrm_addons_popover_body .fcrm_input_hit{margin:4px 0 0;font-size:12px;color:var(--fc-secondary-text);font-weight:400}.fcrm_addons_popover_content{display:flex;flex-direction:column;gap:12px}.fcrm_addons_popover_content .fcrm_info_box{margin-top:4px}.fcrm_addons_popover_content .fcrm_info_box p{font-weight:400;font-size:12px;line-height:16px;color:var(--fc-secondary-text)}.fcrm_addons_popover_content .condition_fields{padding-left:24px}.fcrm_addons_popover_footer{display:flex;align-items:center;gap:12px;flex-wrap:wrap;margin-top:16px}.fcrm_recommended_plugins_lists{display:grid;grid-template-columns:repeat(auto-fill,minmax(300px,1fr));gap:24px}.fcrm_recommended_plugins_box{border:1px solid var(--fc-primary-border);border-radius:8px;padding:16px;display:flex;flex-direction:column;gap:14px}.fcrm_recommended_plugins_box--header{display:flex;align-items:flex-start;gap:12px}.fcrm_recommended_plugins_box--icon{width:32px;height:32px;flex:none;border-radius:8px}.fcrm_recommended_plugins_box--icon img{display:block;width:100%;height:100%;object-fit:contain}.fcrm_recommended_plugins_box--title{margin:0 0 4px;color:var(--fc-primary-text);font-weight:500;font-size:14px;line-height:20px;display:flex;align-items:center;gap:4px}.fcrm_recommended_plugins_box--title a{color:var(--fc-primary-text)}.fcrm_recommended_plugins_box--desc{margin:0;font-weight:400;font-size:12px;line-height:16px;color:var(--fc-secondary-text)}.fcrm_recommended_plugins_box--footer .el-button{width:100%;text-align:center}.fcrm-user-search-popover .fcrm-user-not-found{padding:8px 12px;font-size:14px;color:var(--fc-text-muted);font-style:italic;text-align:center}.fcrm_board_member_add p{font-size:12px;font-weight:400;line-height:16px;color:var(--fc-secondary-text);margin:4px 0 0}.fcrm_manager_drawer.el-drawer{overflow:hidden}.fcrm_manager_drawer .el-drawer__footer{background-color:var(--fc-primary-bg);border-top:1px solid var(--fc-primary-border);padding:16px 20px!important}.fcrm_manager_drawer .el-form .el-form-item{margin-bottom:20px}.fcrm_manager_drawer .el-form .el-form-item .fcrm_board_member_add{width:100%}.fcrm_manager_drawer .el-form .el-form-item hr{border:none;border-top:1px solid var(--fc-primary-border);margin:8px 0 16px}.fcrm_manager_drawer .el-form .el-form-item .el-checkbox-group{display:flex;flex-wrap:wrap;gap:12px;padding-left:52px;margin-top:12px}.fcrm_manager_drawer .el-form .el-form-item .el-checkbox-group .el-checkbox{min-width:240px}.fcrm_manager_drawer .el-form .el-form-item .fcrm_select_all_wrapper{margin-bottom:16px;width:100%}.fcrm_manager_drawer .el-form .el-form-item .fcrm_select_all_wrapper .el-checkbox{font-weight:400}.fcrm_manager_drawer .el-form .el-form-item .fcrm_permission_group{padding-left:28px;margin-bottom:16px}.fcrm_manager_drawer .el-form .el-form-item .fcrm_permission_group:last-child{margin-bottom:0}.fcrm_manager_drawer .el-form .el-form-item .fcrm_permission_group .fcrm_permission_group_title{font-size:14px;font-weight:500;line-height:20px;letter-spacing:-.084px;color:var(--fc-primary-text);margin:0 0 12px}.fcrm_manager_drawer .el-form .el-form-item .fcrm_permission_group .fcrm_permission_checkboxes{display:flex;flex-wrap:wrap;gap:12px;padding-left:24px}.fcrm_manager_drawer .el-form .el-form-item .fcrm_permission_group .fcrm_permission_checkboxes .el-checkbox{min-width:240px}.fcrm-user-search-popover .fcrm-user-search-list-group{list-style:none;margin:0;padding:0}.fcrm-user-search-popover .fcrm-user-search-list-group .fcrm-search-user-list{padding:8px 12px;cursor:pointer;font-size:14px;line-height:20px;color:var(--fc-primary-text);border-radius:4px;transition:background .2s ease}.fcrm-user-search-popover .fcrm-user-search-list-group .fcrm-search-user-list:hover{background:var(--fc-secondary-bg)}.fcrm-user-search-popover .fcrm-user-not-found{padding:8px 12px;font-size:14px;color:var(--fc-secondary-text);text-align:center}.fcrm_manager_permissions_popover{width:370px!important;display:flex;flex-wrap:wrap;gap:4px;align-items:center}.fcrm_custom_fields_config{background:var(--fc-primary-bg);min-height:100%}.fcrm_custom_fields_config.fcrm_custom_fields_config_pop{background:transparent}.fcrm_custom_fields_content_pop.fcrm_body{padding:0}.fcrm_custom_field_group_box{background:var(--fc-weak-bg-25);border-radius:8px;padding:15px 20px 10px;margin-bottom:20px}.fcrm_custom_field_group_box:hover>.fcrm_item_label .icon{opacity:1}.fcrm_custom_field_group_box>.fcrm_item_label{font-weight:500;font-size:16px;line-height:24px;margin-bottom:16px;display:flex;align-items:center;gap:6px}.fcrm_custom_field_group_box>.fcrm_item_label .icon{display:block;opacity:0}.fcrm_custom_field_group_box>.fcrm_item_label .icon svg{display:block}.fcrm_custom_field_dialog .el-dialog{border-radius:var(--fcrm-border-radius-8, 8px);overflow:hidden;padding:0;max-width:640px}.fcrm_custom_field_dialog .el-dialog .el-dialog__body .el-form .el-form-item__content .fcrm_manage_labels_radio .el-radio{position:relative}.fcrm_custom_field_dialog .el-dialog .el-dialog__body .el-form .el-form-item__content .fcrm_manage_labels_radio .el-radio .el-radio__input{opacity:0}.fcrm_custom_field_dialog .el-dialog .el-dialog__body .el-form .el-form-item__content .fcrm_manage_labels_radio .el-radio .el-radio__label{position:absolute;left:4px;top:10px}.fcrm_custom_field_dialog .el-dialog .el-dialog__footer{background-color:var(--fc-primary-bg);border-top:1px solid var(--fc-primary-border);padding:16px 20px!important}.fcrm_custom_field_dialog .el-dialog .el-dialog__footer .dialog-footer{display:flex;gap:8px;justify-content:flex-end;background:none;padding:0;border:none}.fcrm_custom_field_dialog .el-dialog .el-dialog__footer .el-button{margin:0}.fcrm_custom_field_dialog .el-dialog .el-form .el-form-item{margin-bottom:20px}.fcrm_option_lists{list-style:none;padding:0;margin:0;display:flex;flex-direction:column;gap:6px}.fcrm_option_lists .fcrm_option_item{display:flex;align-items:center;gap:12px;margin:0}.fcrm_option_lists .fcrm_option_item .el-button-group{flex-shrink:0;display:flex;flex-direction:column;gap:2px}.fcrm_option_lists .fcrm_option_item .el-button-group .el-button{padding:0;border-radius:6px;min-height:auto;height:16px;width:16px;border:none;background:transparent;color:var(--fc-secondary-text);display:inline-flex;align-items:center;justify-content:center;transition:all .2s ease;flex-shrink:0}.fcrm_option_lists .fcrm_option_item .el-button-group .el-button .el-icon{font-size:14px;width:14px;height:14px}.fcrm_option_lists .fcrm_option_item .el-button-group .el-button:first-child{border-top-left-radius:6px;border-bottom-left-radius:6px}.fcrm_option_lists .fcrm_option_item .el-button-group .el-button:last-child{border-top-right-radius:6px;border-bottom-right-radius:6px}.fcrm_option_lists .fcrm_option_item .el-button-group .el-button:hover:not(:disabled){background:var(--fc-primary-bg);color:var(--fc-primary-text)}.fcrm_option_lists .fcrm_option_item .el-button-group .el-button:active:not(:disabled){background:var(--fc-primary-border)}.fcrm_option_lists .fcrm_option_item .el-button-group .el-button:disabled{opacity:.4;cursor:not-allowed;background:transparent}.fcrm_option_lists .fcrm_option_item .el-button-group .el-button:focus{outline:none}.fcrm_option_lists .fcrm_option_item .fcrm_option_text{flex:1;cursor:pointer;border:1px solid var(--fc-primary-border);box-shadow:0 1px 2px #0a0d1408;border-radius:8px;background:var(--fc-primary-bg);height:36px;padding:8px 10px;line-height:18px;font-size:14px;font-weight:400;color:var(--fc-primary-text)}.fcrm_option_lists .fcrm_option_item .fcrm_option_edit_input{flex:1;padding:8px 10px;border:1px solid var(--fc-primary-border);box-shadow:0 1px 2px #0a0d1408;background:none;height:36px;border-radius:8px;color:var(--fc-primary-text)}.fcrm_option_lists .fcrm_option_item .fcrm_option_edit_input:focus{border-color:var(--fc-deep-bg)!important;box-shadow:none}.fcrm_option_lists .fcrm_option_item .fcrm_option_remove{cursor:pointer;color:var(--fc-secondary-text);font-size:16px;padding:4px;border-radius:4px;transition:all .2s ease;display:flex;align-items:center;justify-content:center;width:24px;height:24px}.fcrm_option_lists .fcrm_option_item .fcrm_option_remove:hover{background:var(--fc-error-bg);color:var(--fc-error)}.fcrm_option_lists .fcrm_option_item .fcrm_option_remove:active{background:var(--fc-error-bg)}.fcrm_add_new_option_wrap{padding-left:27px;margin-top:10px;display:flex;gap:12px;align-items:center}.fcrm_custom_fields_repeater_options_wrap{width:100%}.fcrm_custom_field_tabs{display:inline-flex;align-items:center}.fcrm_custom_field_tabs .fcrm_custom_field_tab{padding:6px 14px;font-size:14px;line-height:20px;font-weight:500;color:var(--fc-text-muted);background:transparent;border:none;border-radius:6px;cursor:pointer;transition:background .15s ease,color .15s ease;text-align:center}.fcrm_custom_field_tabs .fcrm_custom_field_tab:hover:not(.is-active){color:var(--fc-primary-text)}.fcrm_custom_field_tabs .fcrm_custom_field_tab.is-active{border-radius:var(--radius-8, 8px);background:var(--fc-secondary-bg);color:var(--fc-primary-text)}.fcrm_custom_field_tabs.fcrm_custom_field_tabs_pop{padding:10px 0 0;margin-bottom:16px}@keyframes progress-bar-stripes{0%{background-position:1rem 0}to{background-position:0 0}}@media (max-width: 768px){.fcrm_custom_fields_wrap{margin:0;padding:20px}.fcrm_custom_fields_wrap .el-table .el-table__header-wrapper td,.fcrm_custom_fields_wrap .el-table .el-table__header-wrapper th,.fcrm_custom_fields_wrap .el-table .el-table__body-wrapper td,.fcrm_custom_fields_wrap .el-table .el-table__body-wrapper th{padding:8px 6px}.fcrm_custom_fields_wrap .el-table .el-table__header-wrapper td:first-child,.fcrm_custom_fields_wrap .el-table .el-table__header-wrapper th:first-child,.fcrm_custom_fields_wrap .el-table .el-table__body-wrapper td:first-child,.fcrm_custom_fields_wrap .el-table .el-table__body-wrapper th:first-child{padding-left:10px}.fcrm_option_lists .fcrm_option_item{flex-wrap:wrap;gap:6px}}@media (max-width: 480px){.fcrm_custom_fields_wrap{margin:0;border-radius:0}.fcrm_custom_field_dialog .el-dialog{width:95%!important;margin:0 auto}}.fcrm_drag_handle{cursor:grab;color:var(--fc-secondary-border);display:inline-flex;padding:4px;border-radius:4px;line-height:1;transition:color .15s,background-color .15s}.fcrm_drag_handle:hover{color:var(--fc-deep-bg);background:#2225301a}.fcrm_drag_handle:active{cursor:grabbing}.fcrm_row_dragging td{opacity:.4;background:var(--fc-secondary-bg)!important}.fcrm_drag_over td{background-color:#2225301a!important}.fcrm_drag_over td:first-child{box-shadow:inset 3px 0 0 0 var(--fc-deep-bg)}.fcrm_drag_col .cell{padding:0 4px}.fcrm_drag_ghost{position:fixed;top:-1000px;left:-1000px;padding:6px 12px;background:var(--fc-deep-bg);color:var(--fc-text-inverse);border-radius:4px;font-size:13px;white-space:nowrap;box-shadow:0 2px 8px #00000026;pointer-events:none}.fcrm_inline_actions{display:flex;align-items:center;gap:4px}.fcrm_inline_actions .el-button{margin:0}.fcrm_inline_action_btn{display:inline-flex;align-items:center;justify-content:center;width:28px;height:28px;border:none;border-radius:4px;background:transparent;color:var(--fc-text-muted);cursor:pointer;transition:color .15s,background-color .15s;padding:0;font-size:14px}.fcrm_inline_action_btn:hover{color:var(--fc-deep-bg);background:#2225301a}.fcrm_inline_action_btn.fcrm_inline_action_danger:hover{color:var(--fc-error);background:var(--fc-error-bg)}.el-table__empty-text{font-size:14px;font-weight:400;line-height:20px;color:var(--fc-text-muted);padding:40px 20px}.fc_sidebar_card{margin-bottom:10px}.fc_sidebar_card>h3{margin:10px 0}.fc_custom_field_wrapper{margin-bottom:20px}.fc_custom_field_wrapper .fluentcrm_custom_fields .fc_custom_field_box .fc_checkbox_group{display:flex;flex-wrap:wrap;row-gap:8px}.fc_custom_field_wrapper .fluentcrm_custom_fields .fc_custom_field_box .fc_checkbox_group .el-checkbox+.el-checkbox{margin-top:0}.fc_custom_field_wrapper .fluentcrm_custom_fields .el-form-item .el-form-item__label{display:block;width:100%;font-size:14px;font-weight:500;line-height:1.4;color:var(--fc-primary-text);margin-bottom:8px;padding:0}.fc_custom_field_wrapper .fluentcrm_custom_fields .el-form-item .el-input input{height:auto}.fc_custom_field_wrapper .fluentcrm_custom_fields .el-form-item .el-textarea textarea,.fc_custom_field_wrapper .fluentcrm_custom_fields .el-form-item .el-textarea input,.fc_custom_field_wrapper .fluentcrm_custom_fields .el-form-item .el-input textarea,.fc_custom_field_wrapper .fluentcrm_custom_fields .el-form-item .el-input input{margin:0;padding:4px 16px;border-radius:6px;width:100%}.fc_custom_field_wrapper .fluentcrm_custom_fields .el-form-item .el-textarea textarea:focus,.fc_custom_field_wrapper .fluentcrm_custom_fields .el-form-item .el-textarea input:focus,.fc_custom_field_wrapper .fluentcrm_custom_fields .el-form-item .el-input textarea:focus,.fc_custom_field_wrapper .fluentcrm_custom_fields .el-form-item .el-input input:focus{border-color:var(--fc-deep-bg)!important}.fc_custom_field_wrapper .fluentcrm_custom_fields .el-form-item .el-date-editor{width:100%}.fc_custom_field_wrapper .fluentcrm_custom_fields .el-form-item .el-date-editor input{padding-left:30px}.fc_custom_field_wrapper .fluentcrm_custom_fields .el-form-item .el-date-editor input:focus{border-color:var(--fc-deep-bg)}.fc_custom_field_wrapper .fluentcrm_custom_fields .el-form-item .el-checkbox{line-height:2rem}.fc_custom_field_wrapper .fluentcrm_custom_fields .el-form-item .el-checkbox .el-checkbox__label{color:var(--fc-primary-text);overflow-wrap:anywhere;white-space:break-spaces}.fc_custom_field_wrapper .fluentcrm_custom_fields .el-form-item .el-checkbox.is-checked .el-checkbox__input.is-checked .el-checkbox__inner{background-color:var(--fc-deep-bg);border-color:var(--fc-deep-bg)}.fc_custom_field_wrapper .fluentcrm_custom_fields .el-form-item .el-checkbox.is-checked .el-checkbox__label{color:var(--fc-deep-bg)}.fc_custom_field_wrapper .fluentcrm_custom_fields .el-form-item .el-radio-group .el-radio .el-radio__input.is-checked .el-radio__inner{background:var(--fc-deep-bg);border-color:var(--fc-deep-bg)}.fc_custom_field_wrapper .fluentcrm_custom_fields .el-form-item .el-radio-group .el-radio .el-radio__input.is-checked~.el-radio__label{color:var(--fc-deep-bg)}.fc_custom_field_wrapper .fluentcrm_custom_fields .el-form-item .el-radio-group .el-radio .el-radio__label{color:var(--fc-primary-text)}.fc_custom_field_wrapper .fluentcrm_custom_fields .fc_custom_field_group_box{margin-top:30px;margin-bottom:20px;padding:10px 15px 20px;border:1px solid var(--fc-primary-border);border-radius:8px;background:var(--fc-secondary-bg)}.fc_custom_field_wrapper .fluentcrm_custom_fields .fc_custom_field_group_box .fc_item_label{display:flex;align-items:center;font-size:16px;line-height:20px;color:var(--fc-primary-text);gap:10px;font-weight:500;border-bottom:1px solid var(--fc-primary-border);padding-bottom:10px;margin-bottom:10px}.fcrm_coming_soon_item{cursor:default;opacity:.9}.fcrm_coming_soon_item .fcrm_coming_soon_badge{margin-left:6px;font-size:10px;font-weight:500;color:var(--el-text-color-secondary, var(--fc-text-muted));background:var(--el-fill-color-light, var(--fc-secondary-bg));padding:1px 6px;border-radius:4px}.fcrm_dynamic_segments .fcrm_table_wrapper,.fcrm_dynamic_segments .fcrm_table_body{overflow-x:auto;overflow-y:hidden}.fcrm_dynamic_segments .fcrm_table_body .el-table .el-table__header-wrapper .el-table__header th{border-bottom:1px solid var(--fc-primary-border)!important;border-top:1px solid var(--fc-primary-border)!important;border-radius:0}.fcrm_dynamic_segments .fcrm_table_body .el-table .el-table__body-wrapper .el-table__body .el-table__row{background-color:var(--fc-primary-bg)!important}.fcrm_dynamic_segments .fcrm_table_body .el-table .el-table__body-wrapper .el-table__body .el-table__row:last-child td{border-bottom:none}.fcrm_dynamic_segments .fcrm_table_body .el-table .el-table__body-wrapper .el-table__body .el-table__row:hover>td{background-color:var(--fc-primary-bg)!important}.fcrm_dynamic_segments .fcrm_table_body .el-table .fcrm_segment_title_wrapper{display:flex;flex-direction:column;gap:2px;width:100%}.fcrm_dynamic_segments .fcrm_table_body .el-table .fcrm_segment_title_link{text-decoration:none;display:block}.fcrm_dynamic_segments .fcrm_table_body .el-table .fcrm_url{font-weight:400;font-size:14px;line-height:20px;color:var(--fc-primary-text);margin:0 0 2px;display:inline-flex;align-items:center;gap:2px}.fcrm_dynamic_segments .fcrm_table_body .el-table .fcrm_list-description{font-weight:400;font-size:12px;line-height:16px;margin:0;color:var(--fc-secondary-text)}.fcrm_dynamic_segments .fcrm_table_body .el-table .fcrm_text-align-left{display:flex;align-items:center;justify-content:flex-end;gap:4px}.fcrm_dynamic_segments .fcrm_table_body .el-table .el-dropdown-link{display:flex;align-items:center;justify-content:center;width:32px;height:32px;padding:6px;border-radius:8px;cursor:pointer;transition:background-color .2s ease}.fcrm_dynamic_segments .fcrm_table_body .el-table .el-dropdown-link:hover{background-color:var(--fc-secondary-bg)}.fcrm_dynamic_segments .fcrm_table_body .el-table .el-dropdown-link .el-icon{width:20px;height:20px;color:var(--fc-secondary-text);transform:rotate(90deg)}.fcrm_create_custom_segment{min-height:100vh;padding-left:24px;padding-right:24px}.fcrm_create_custom_segment .fcrm_create_segment_header{display:flex;gap:12px;align-items:center;width:100%;margin-bottom:24px;height:36px}.fcrm_create_custom_segment .fcrm_create_segment_content{background:var(--fc-primary-bg);border-radius:var(--fcrm-border-radius-8, 8px);padding:20px;display:flex;flex-direction:column;gap:20px;width:100%}.fcrm_create_custom_segment .fcrm_create_segment_content .fcrm_segment_name_input{display:flex;flex-direction:column;gap:4px;width:100%}.fcrm_create_custom_segment .fcrm_create_segment_content .fcrm_segment_name_input .fcrm_input_label{font-size:14px;font-weight:500;line-height:20px;letter-spacing:-.084px;color:var(--fc-primary-text);margin:0}.fcrm_create_custom_segment .fcrm_create_segment_content .fcrm_segment_name_input .fcrm_segment_name_field{width:100%}.fcrm_create_custom_segment .fcrm_create_segment_content .fcrm_segment_fields{display:flex;flex-direction:column;gap:8px;width:100%}.fcrm_create_custom_segment .fcrm_create_segment_content .fcrm_segment_fields .fcrm_rich_container,.fcrm_create_custom_segment .fcrm_create_segment_content .fcrm_segment_fields .fcrm_rich_container .fcrm_rich_wrap{display:flex;flex-direction:column;gap:16px;width:100%}.fcrm_create_custom_segment .fcrm_create_segment_content .fcrm_segment_fields .fcrm_rich_container .fcrm_rich_wrap .fcrm_rich_filter{background:var(--fc-secondary-bg)!important;border-radius:var(--fcrm-border-radius-8);padding:16px;display:flex;flex-direction:column;gap:12px;width:100%}.fcrm_create_custom_segment .fcrm_create_segment_content .fcrm_segment_fields .fcrm_rich_container .fcrm_rich_wrap .fcrm_rich_filter .fcrm_filter_group_header{display:flex;flex-direction:column;gap:12px;width:100%}.fcrm_create_custom_segment .fcrm_create_segment_content .fcrm_segment_fields .fcrm_rich_container .fcrm_rich_wrap .fcrm_rich_filter .fcrm_filter_group_header .fcrm_and_label{font-size:12px!important;font-weight:500!important;line-height:16px!important;letter-spacing:.48px!important;color:var(--fc-text-muted)!important;text-transform:uppercase!important;margin:0!important}.fcrm_create_custom_segment .fcrm_create_segment_content .fcrm_segment_fields .fcrm_rich_container .fcrm_rich_wrap .fcrm_rich_filter .fcrm_filter_group_header .fcrm_rich_filters{width:100%}.fcrm_create_custom_segment .fcrm_create_segment_content .fcrm_segment_fields .fcrm_rich_container .fcrm_rich_wrap .fcrm_rich_filter .fcrm_filter_group_header .fcrm_rich_filters .fcrm_table{width:100%;border-collapse:collapse;margin:0;background:transparent}.fcrm_create_custom_segment .fcrm_create_segment_content .fcrm_segment_fields .fcrm_rich_container .fcrm_rich_wrap .fcrm_rich_filter .fcrm_filter_group_header .fcrm_rich_filters .fcrm_table tbody tr{background:transparent;display:table-row}.fcrm_create_custom_segment .fcrm_create_segment_content .fcrm_segment_fields .fcrm_rich_container .fcrm_rich_wrap .fcrm_rich_filter .fcrm_filter_group_header .fcrm_rich_filters .fcrm_table tbody tr:first-child td{padding-top:0}.fcrm_create_custom_segment .fcrm_create_segment_content .fcrm_segment_fields .fcrm_rich_container .fcrm_rich_wrap .fcrm_rich_filter .fcrm_filter_group_header .fcrm_rich_filters .fcrm_table tbody tr:not(:last-child){margin-bottom:0}.fcrm_create_custom_segment .fcrm_create_segment_content .fcrm_segment_fields .fcrm_rich_container .fcrm_rich_wrap .fcrm_rich_filter .fcrm_filter_group_header .fcrm_rich_filters .fcrm_table tbody tr td{padding:12px 0;vertical-align:middle;font-size:14px;font-weight:400;line-height:20px;letter-spacing:-.084px;color:var(--fc-primary-text);display:table-cell}.fcrm_create_custom_segment .fcrm_create_segment_content .fcrm_segment_fields .fcrm_rich_container .fcrm_rich_wrap .fcrm_rich_filter .fcrm_filter_group_header .fcrm_rich_filters .fcrm_table tbody tr td:first-child{padding-left:8px;padding-right:12px;width:200px;min-width:200px;flex-shrink:0;color:var(--fc-secondary-text);font-weight:500}.fcrm_create_custom_segment .fcrm_create_segment_content .fcrm_segment_fields .fcrm_rich_container .fcrm_rich_wrap .fcrm_rich_filter .fcrm_filter_group_header .fcrm_rich_filters .fcrm_table tbody tr td:first-child .fcrm_fs_provider_separator{color:var(--fc-text-muted);margin:0 4px}.fcrm_create_custom_segment .fcrm_create_segment_content .fcrm_segment_fields .fcrm_rich_container .fcrm_rich_wrap .fcrm_rich_filter .fcrm_filter_group_header .fcrm_rich_filters .fcrm_table tbody tr td.fcrm_filter_operator{width:200px;min-width:200px;flex-shrink:0;padding-left:0;padding-right:12px}.fcrm_create_custom_segment .fcrm_create_segment_content .fcrm_segment_fields .fcrm_rich_container .fcrm_rich_wrap .fcrm_rich_filter .fcrm_filter_group_header .fcrm_rich_filters .fcrm_table tbody tr td.fcrm_filter_value{flex:1 0 0;min-width:0;padding-left:0;padding-right:12px}.fcrm_create_custom_segment .fcrm_create_segment_content .fcrm_segment_fields .fcrm_rich_container .fcrm_rich_wrap .fcrm_rich_filter .fcrm_filter_group_header .fcrm_rich_filters .fcrm_table tbody tr td:last-child{padding-right:0;padding-left:0;width:auto;flex-shrink:0;text-align:right}.fcrm_create_custom_segment .fcrm_create_segment_content .fcrm_segment_fields .fcrm_rich_container .fcrm_rich_wrap .fcrm_rich_filter .fcrm_filter_group_header .fcrm_rich_filters .fcrm_filter_intro{display:flex;align-items:center;justify-content:flex-start;gap:10px;width:100%;margin:0;padding-top:0}.fcrm_create_custom_segment .fcrm_create_segment_content .fcrm_segment_fields .fcrm_rich_container .fcrm_rich_wrap .fcrm_rich_filter .fcrm_filter_group_header .fcrm_rich_filters .fcrm_filter_intro .fcrm_filter_intro_actions{display:flex;align-items:center;gap:10px;width:100%}.fcrm_create_custom_segment .fcrm_create_segment_content .fcrm_segment_fields .fcrm_rich_container .fcrm_rich_wrap .fcrm_rich_filter .fcrm_filter_group_header .fcrm_rich_filters .fcrm_filter_intro .el-popover{display:inline-block!important;position:relative!important}.fcrm_create_custom_segment .fcrm_create_segment_content .fcrm_segment_fields .fcrm_rich_container .fcrm_rich_wrap .fcrm_rich_filter .fcrm_filter_group_header .fcrm_rich_filters .fcrm_filter_intro>*:not(.el-button):not(.el-popover):not(.fcrm_filter_intro_actions){display:none}.fcrm_create_custom_segment .fcrm_create_segment_content .fcrm_segment_fields .fcrm_rich_container .fcrm_rich_wrap .fcrm_cond_or{display:flex;flex-direction:row;align-items:center;justify-content:center;width:100%;position:relative;margin-top:16px;gap:12px}.fcrm_create_custom_segment .fcrm_create_segment_content .fcrm_segment_fields .fcrm_rich_container .fcrm_rich_wrap .fcrm_cond_or .fcrm_or_divider_line{flex:1;height:1px;background-color:var(--fc-primary-border);border:none}.fcrm_create_custom_segment .fcrm_create_segment_content .fcrm_segment_fields .fcrm_rich_container .fcrm_cond_or{display:flex;align-items:center;justify-content:center;gap:10px;width:100%;position:relative;margin:0}.fcrm_create_custom_segment .fcrm_create_segment_content .fcrm_segment_fields .fcrm_rich_container .fcrm_cond_or .fcrm_or_divider_line{flex:1 0 0;height:0;border-bottom:1px solid var(--fc-primary-border)}.fcrm_create_custom_segment .fcrm_create_segment_content .fcrm_segment_fields .fcrm_estimated_count{background:var(--fc-primary-bg);border-radius:8px;display:flex;align-items:center;justify-content:center;gap:6px;margin-top:0;padding:6px 8px 6px 12px}.fcrm_create_custom_segment .fcrm_create_segment_content .fcrm_segment_fields .fcrm_estimated_count .fcrm_estimated_label{font-size:16px;font-weight:500;line-height:24px;letter-spacing:-.176px;color:var(--fc-secondary-text);white-space:nowrap}.fcrm_create_custom_segment .fcrm_create_segment_content .fcrm_segment_fields .fcrm_estimated_count .fcrm_estimated_badge{background:var(--fc-primary-border);border-radius:6px;padding:2px;display:inline-flex;align-items:center;justify-content:center;min-width:24px;flex-shrink:0}.fcrm_create_custom_segment .fcrm_create_segment_content .fcrm_segment_fields .fcrm_estimated_count .fcrm_estimated_badge .fcrm_estimated_badge_text{font-size:14px;font-weight:500;line-height:20px;letter-spacing:-.084px;color:var(--fc-deep-bg);text-align:center;white-space:pre-wrap;min-width:16px}.fcrm_fluentcrm-lists.fcrm_fluentcrm-view-wrapper.fcrm_fluentcrm_view{min-height:100vh}.fcrm_fluentcrm-lists.fcrm_fluentcrm-view-wrapper.fcrm_fluentcrm_view .fcrm_fluentcrm_header .fcrm_fluentcrm_header_title{flex:1;height:100%}.fcrm_fluentcrm-lists.fcrm_fluentcrm-view-wrapper.fcrm_fluentcrm_view .fcrm_fluentcrm_header .fcrm_fluentcrm_header_title .fcrm_fluentcrm_spaced_bottom{margin-bottom:0;display:flex;gap:6px;align-items:center}.fcrm_fluentcrm-lists.fcrm_fluentcrm-view-wrapper.fcrm_fluentcrm_view .fcrm_fluentcrm_header .fcrm_fluentcrm_header_title .fcrm_fluentcrm_spaced_bottom .el-breadcrumb__item{display:inline-flex;align-items:center}.fcrm_fluentcrm-lists.fcrm_fluentcrm-view-wrapper.fcrm_fluentcrm_view .fcrm_fluentcrm_header .fcrm_fluentcrm_header_title .fcrm_fluentcrm_spaced_bottom .el-breadcrumb__item .el-breadcrumb__inner{font-size:14px;font-weight:500;line-height:20px;letter-spacing:-.084px;color:var(--fc-secondary-text);text-decoration:none;transition:color .2s ease}.fcrm_fluentcrm-lists.fcrm_fluentcrm-view-wrapper.fcrm_fluentcrm_view .fcrm_fluentcrm_header .fcrm_fluentcrm_header_title .fcrm_fluentcrm_spaced_bottom .el-breadcrumb__item .el-breadcrumb__inner.is-link{color:var(--fc-secondary-text);cursor:pointer}.fcrm_fluentcrm-lists.fcrm_fluentcrm-view-wrapper.fcrm_fluentcrm_view .fcrm_fluentcrm_header .fcrm_fluentcrm_header_title .fcrm_fluentcrm_spaced_bottom .el-breadcrumb__item .el-breadcrumb__inner.is-link:hover{color:var(--fc-primary-text)}.fcrm_fluentcrm-lists.fcrm_fluentcrm-view-wrapper.fcrm_fluentcrm_view .fcrm_fluentcrm_header .fcrm_fluentcrm_header_title .fcrm_fluentcrm_spaced_bottom .el-breadcrumb__item:last-child .el-breadcrumb__inner{color:var(--fc-primary-text);font-weight:500}.fcrm_fluentcrm-lists.fcrm_fluentcrm-view-wrapper.fcrm_fluentcrm_view .fcrm_fluentcrm_header .fcrm_fluentcrm_header_title .fcrm_fluentcrm_spaced_bottom .el-breadcrumb__item:not(:last-child) .el-breadcrumb__inner:after{content:"";width:20px;height:20px;flex-shrink:0;display:inline-block;background-image:url("data:image/svg+xml,%3Csvg width='20' height='20' viewBox='0 0 20 20' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M7.5 5L12.5 10L7.5 15' stroke='%230e121b' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");background-size:20px 20px;background-repeat:no-repeat;background-position:center;margin-left:8px;margin-right:0;vertical-align:middle;position:relative}.fcrm_fluentcrm-lists.fcrm_fluentcrm-view-wrapper.fcrm_fluentcrm_view .fcrm_fluentcrm_header .fcrm_fluentcrm_header_title .fcrm_fluentcrm_spaced_bottom .el-breadcrumb__separator{display:none}.fcrm_fluentcrm-lists.fcrm_fluentcrm-view-wrapper.fcrm_fluentcrm_view .fcrm_fluentcrm_header .fcrm_fluentcrm-templates-action-buttons.fcrm_fluentcrm-actions{display:flex;gap:10px;align-items:center;flex-shrink:0}.fcrm_fluentcrm-lists.fcrm_fluentcrm-view-wrapper.fcrm_fluentcrm_view .fcrm_fluentcrm_header .fcrm_fluentcrm-templates-action-buttons.fcrm_fluentcrm-actions .el-button{margin:0}.fcrm_fluentcrm-lists.fcrm_fluentcrm-view-wrapper.fcrm_fluentcrm_view .fcrm_fluentcrm_header .fcrm_fluentcrm-templates-action-buttons.fcrm_fluentcrm-actions .fcrm_clear_filters_button{background:var(--fc-primary-bg)!important;border:1px solid var(--fc-primary-border)!important;border-radius:8px!important;padding:8px!important;font-size:14px!important;font-weight:500!important;line-height:20px!important;letter-spacing:-.084px!important;color:var(--fc-secondary-text)!important;display:flex!important;align-items:center!important;justify-content:center!important;gap:4px!important;box-shadow:none!important;height:auto!important;min-height:auto!important}.fcrm_fluentcrm-lists.fcrm_fluentcrm-view-wrapper.fcrm_fluentcrm_view .fcrm_fluentcrm_header .fcrm_fluentcrm-templates-action-buttons.fcrm_fluentcrm-actions .fcrm_clear_filters_button:hover{background:var(--fc-secondary-bg)!important;border-color:var(--fc-secondary-border)!important;color:var(--fc-secondary-text)!important}.fcrm_fluentcrm-lists.fcrm_fluentcrm-view-wrapper.fcrm_fluentcrm_view .fcrm_fluentcrm_header .fcrm_fluentcrm-templates-action-buttons.fcrm_fluentcrm-actions .fcrm_clear_filters_button:active,.fcrm_fluentcrm-lists.fcrm_fluentcrm-view-wrapper.fcrm_fluentcrm_view .fcrm_fluentcrm_header .fcrm_fluentcrm-templates-action-buttons.fcrm_fluentcrm-actions .fcrm_clear_filters_button:focus{background:var(--fc-secondary-bg)!important;border-color:var(--fc-secondary-border)!important;color:var(--fc-secondary-text)!important;box-shadow:none!important}.fcrm_fluentcrm-lists.fcrm_fluentcrm-view-wrapper.fcrm_fluentcrm_view .fcrm_fluentcrm_header .fcrm_fluentcrm-templates-action-buttons.fcrm_fluentcrm-actions .fcrm_update_segment_button{background:var(--fc-deep-bg)!important;border-color:var(--fc-deep-bg)!important;color:var(--fc-text-inverse)!important;border-radius:8px!important;padding:8px!important;font-size:14px!important;font-weight:500!important;line-height:20px!important;letter-spacing:-.084px!important;display:flex!important;align-items:center!important;justify-content:center!important;gap:4px!important;box-shadow:none!important;height:auto!important;min-height:auto!important;transition:all .2s ease!important}.fcrm_fluentcrm-lists.fcrm_fluentcrm-view-wrapper.fcrm_fluentcrm_view .fcrm_fluentcrm_header .fcrm_fluentcrm-templates-action-buttons.fcrm_fluentcrm-actions .fcrm_update_segment_button:hover{background:var(--fc-primary-text)!important;border-color:var(--fc-primary-text)!important;color:var(--fc-text-inverse)!important;box-shadow:none!important}.fcrm_fluentcrm-lists.fcrm_fluentcrm-view-wrapper.fcrm_fluentcrm_view .fcrm_fluentcrm_header .fcrm_fluentcrm-templates-action-buttons.fcrm_fluentcrm-actions .fcrm_update_segment_button:active,.fcrm_fluentcrm-lists.fcrm_fluentcrm-view-wrapper.fcrm_fluentcrm_view .fcrm_fluentcrm_header .fcrm_fluentcrm-templates-action-buttons.fcrm_fluentcrm-actions .fcrm_update_segment_button:focus{background:var(--fc-primary-text)!important;border-color:var(--fc-primary-text)!important;color:var(--fc-text-inverse)!important;box-shadow:none!important}.fcrm_fluentcrm-lists.fcrm_fluentcrm-view-wrapper.fcrm_fluentcrm_view .fcrm_fluentcrm_pad_around{padding:0 24px 24px}.fcrm_fluentcrm-lists.fcrm_fluentcrm-view-wrapper.fcrm_fluentcrm_view .fcrm_fluentcrm_pad_around .fcrm_segment_editor_wrapper{background:var(--fc-primary-bg);border-radius:var(--fcrm-border-radius-8, 8px);padding:20px;display:flex;flex-direction:column;gap:20px;width:100%;margin-bottom:24px}.fcrm_fluentcrm-lists.fcrm_fluentcrm-view-wrapper.fcrm_fluentcrm_view .fcrm_fluentcrm_pad_around .fcrm_segment_editor_wrapper .fcrm_segment_name_section{display:flex;flex-direction:column;gap:4px;width:100%}.fcrm_fluentcrm-lists.fcrm_fluentcrm-view-wrapper.fcrm_fluentcrm_view .fcrm_fluentcrm_pad_around .fcrm_segment_editor_wrapper .fcrm_segment_name_section .fcrm_segment_name_label{font-size:14px;font-weight:500;line-height:20px;letter-spacing:-.084px;color:var(--fc-primary-text);margin:0}.fcrm_fluentcrm-lists.fcrm_fluentcrm-view-wrapper.fcrm_fluentcrm_view .fcrm_fluentcrm_pad_around .fcrm_segment_editor_wrapper .fcrm_segment_name_section .fcrm_segment_name_input{width:100%}.fcrm_fluentcrm-lists.fcrm_fluentcrm-view-wrapper.fcrm_fluentcrm_view .fcrm_fluentcrm_pad_around .fcrm_segment_editor_wrapper .fcrm_segment_fields{display:flex;flex-direction:column;gap:8px;width:100%}.fcrm_fluentcrm-lists.fcrm_fluentcrm-view-wrapper.fcrm_fluentcrm_view .fcrm_fluentcrm_pad_around .fcrm_segment_editor_wrapper .fcrm_segment_fields .fcrm_rich_container,.fcrm_fluentcrm-lists.fcrm_fluentcrm-view-wrapper.fcrm_fluentcrm_view .fcrm_fluentcrm_pad_around .fcrm_segment_editor_wrapper .fcrm_segment_fields .fcrm_rich_container .fcrm_rich_wrap{display:flex;flex-direction:column;gap:16px;width:100%}.fcrm_fluentcrm-lists.fcrm_fluentcrm-view-wrapper.fcrm_fluentcrm_view .fcrm_fluentcrm_pad_around .fcrm_segment_editor_wrapper .fcrm_segment_fields .fcrm_rich_container .fcrm_rich_wrap .fcrm_rich_filter{background:var(--fc-secondary-bg)!important;border-radius:var(--fcrm-border-radius-8);padding:16px;display:flex;flex-direction:column;gap:12px;width:100%}.fcrm_fluentcrm-lists.fcrm_fluentcrm-view-wrapper.fcrm_fluentcrm_view .fcrm_fluentcrm_pad_around .fcrm_segment_editor_wrapper .fcrm_segment_fields .fcrm_rich_container .fcrm_rich_wrap .fcrm_rich_filter .fcrm_filter_group_header{display:flex;flex-direction:column;gap:12px;width:100%}.fcrm_fluentcrm-lists.fcrm_fluentcrm-view-wrapper.fcrm_fluentcrm_view .fcrm_fluentcrm_pad_around .fcrm_segment_editor_wrapper .fcrm_segment_fields .fcrm_rich_container .fcrm_rich_wrap .fcrm_rich_filter .fcrm_filter_group_header .fcrm_and_label{font-size:12px!important;font-weight:500!important;line-height:16px!important;letter-spacing:.48px!important;color:var(--fc-text-muted)!important;text-transform:uppercase!important;margin:0!important}.fcrm_fluentcrm-lists.fcrm_fluentcrm-view-wrapper.fcrm_fluentcrm_view .fcrm_fluentcrm_pad_around .fcrm_segment_editor_wrapper .fcrm_segment_fields .fcrm_rich_container .fcrm_rich_wrap .fcrm_rich_filter .fcrm_filter_group_header .fcrm_rich_filters{width:100%}.fcrm_fluentcrm-lists.fcrm_fluentcrm-view-wrapper.fcrm_fluentcrm_view .fcrm_fluentcrm_pad_around .fcrm_segment_editor_wrapper .fcrm_segment_fields .fcrm_rich_container .fcrm_rich_wrap .fcrm_rich_filter .fcrm_filter_group_header .fcrm_rich_filters .fcrm_table{width:100%;border-collapse:collapse;margin:0;background:transparent}.fcrm_fluentcrm-lists.fcrm_fluentcrm-view-wrapper.fcrm_fluentcrm_view .fcrm_fluentcrm_pad_around .fcrm_segment_editor_wrapper .fcrm_segment_fields .fcrm_rich_container .fcrm_rich_wrap .fcrm_rich_filter .fcrm_filter_group_header .fcrm_rich_filters .fcrm_table tbody tr{background:transparent;display:table-row}.fcrm_fluentcrm-lists.fcrm_fluentcrm-view-wrapper.fcrm_fluentcrm_view .fcrm_fluentcrm_pad_around .fcrm_segment_editor_wrapper .fcrm_segment_fields .fcrm_rich_container .fcrm_rich_wrap .fcrm_rich_filter .fcrm_filter_group_header .fcrm_rich_filters .fcrm_table tbody tr:first-child td{padding-top:0}.fcrm_fluentcrm-lists.fcrm_fluentcrm-view-wrapper.fcrm_fluentcrm_view .fcrm_fluentcrm_pad_around .fcrm_segment_editor_wrapper .fcrm_segment_fields .fcrm_rich_container .fcrm_rich_wrap .fcrm_rich_filter .fcrm_filter_group_header .fcrm_rich_filters .fcrm_table tbody tr:not(:last-child){margin-bottom:0}.fcrm_fluentcrm-lists.fcrm_fluentcrm-view-wrapper.fcrm_fluentcrm_view .fcrm_fluentcrm_pad_around .fcrm_segment_editor_wrapper .fcrm_segment_fields .fcrm_rich_container .fcrm_rich_wrap .fcrm_rich_filter .fcrm_filter_group_header .fcrm_rich_filters .fcrm_table tbody tr td{padding:12px 0;vertical-align:middle;font-size:14px;font-weight:400;line-height:20px;letter-spacing:-.084px;color:var(--fc-primary-text);display:table-cell}.fcrm_fluentcrm-lists.fcrm_fluentcrm-view-wrapper.fcrm_fluentcrm_view .fcrm_fluentcrm_pad_around .fcrm_segment_editor_wrapper .fcrm_segment_fields .fcrm_rich_container .fcrm_rich_wrap .fcrm_rich_filter .fcrm_filter_group_header .fcrm_rich_filters .fcrm_table tbody tr td:first-child{padding-left:8px;padding-right:12px;width:200px;min-width:200px;flex-shrink:0;color:var(--fc-secondary-text);font-weight:500}.fcrm_fluentcrm-lists.fcrm_fluentcrm-view-wrapper.fcrm_fluentcrm_view .fcrm_fluentcrm_pad_around .fcrm_segment_editor_wrapper .fcrm_segment_fields .fcrm_rich_container .fcrm_rich_wrap .fcrm_rich_filter .fcrm_filter_group_header .fcrm_rich_filters .fcrm_table tbody tr td:first-child .fcrm_fs_provider_separator{color:var(--fc-text-muted);margin:0 4px}.fcrm_fluentcrm-lists.fcrm_fluentcrm-view-wrapper.fcrm_fluentcrm_view .fcrm_fluentcrm_pad_around .fcrm_segment_editor_wrapper .fcrm_segment_fields .fcrm_rich_container .fcrm_rich_wrap .fcrm_rich_filter .fcrm_filter_group_header .fcrm_rich_filters .fcrm_table tbody tr td.fcrm_filter_operator{width:200px;min-width:200px;flex-shrink:0;padding-left:0;padding-right:12px}.fcrm_fluentcrm-lists.fcrm_fluentcrm-view-wrapper.fcrm_fluentcrm_view .fcrm_fluentcrm_pad_around .fcrm_segment_editor_wrapper .fcrm_segment_fields .fcrm_rich_container .fcrm_rich_wrap .fcrm_rich_filter .fcrm_filter_group_header .fcrm_rich_filters .fcrm_table tbody tr td.fcrm_filter_value{flex:1 0 0;min-width:0;padding-left:0;padding-right:12px}.fcrm_fluentcrm-lists.fcrm_fluentcrm-view-wrapper.fcrm_fluentcrm_view .fcrm_fluentcrm_pad_around .fcrm_segment_editor_wrapper .fcrm_segment_fields .fcrm_rich_container .fcrm_rich_wrap .fcrm_rich_filter .fcrm_filter_group_header .fcrm_rich_filters .fcrm_table tbody tr td:last-child{padding-right:0;padding-left:0;width:auto;flex-shrink:0;text-align:right}.fcrm_fluentcrm-lists.fcrm_fluentcrm-view-wrapper.fcrm_fluentcrm_view .fcrm_fluentcrm_pad_around .fcrm_segment_editor_wrapper .fcrm_segment_fields .fcrm_rich_container .fcrm_rich_wrap .fcrm_rich_filter .fcrm_filter_group_header .fcrm_rich_filters .fcrm_filter_intro{display:flex;align-items:center;justify-content:flex-start;gap:10px;width:100%;margin:0;padding-top:0}.fcrm_fluentcrm-lists.fcrm_fluentcrm-view-wrapper.fcrm_fluentcrm_view .fcrm_fluentcrm_pad_around .fcrm_segment_editor_wrapper .fcrm_segment_fields .fcrm_rich_container .fcrm_rich_wrap .fcrm_rich_filter .fcrm_filter_group_header .fcrm_rich_filters .fcrm_filter_intro .fcrm_filter_intro_actions{display:flex;align-items:center;gap:10px;width:100%}.fcrm_fluentcrm-lists.fcrm_fluentcrm-view-wrapper.fcrm_fluentcrm_view .fcrm_fluentcrm_pad_around .fcrm_segment_editor_wrapper .fcrm_segment_fields .fcrm_rich_container .fcrm_rich_wrap .fcrm_rich_filter .fcrm_filter_group_header .fcrm_rich_filters .fcrm_filter_intro .el-popover{display:inline-block!important;position:relative!important}.fcrm_fluentcrm-lists.fcrm_fluentcrm-view-wrapper.fcrm_fluentcrm_view .fcrm_fluentcrm_pad_around .fcrm_segment_editor_wrapper .fcrm_segment_fields .fcrm_rich_container .fcrm_rich_wrap .fcrm_rich_filter .fcrm_filter_group_header .fcrm_rich_filters .fcrm_filter_intro>*:not(.el-button):not(.el-popover):not(.fcrm_filter_intro_actions){display:none}.fcrm_fluentcrm-lists.fcrm_fluentcrm-view-wrapper.fcrm_fluentcrm_view .fcrm_fluentcrm_pad_around .fcrm_segment_editor_wrapper .fcrm_segment_fields .fcrm_rich_container .fcrm_rich_wrap .fcrm_cond_or{display:flex;flex-direction:row;align-items:center;justify-content:center;width:100%;position:relative;margin-top:16px;gap:12px}.fcrm_fluentcrm-lists.fcrm_fluentcrm-view-wrapper.fcrm_fluentcrm_view .fcrm_fluentcrm_pad_around .fcrm_segment_editor_wrapper .fcrm_segment_fields .fcrm_rich_container .fcrm_rich_wrap .fcrm_cond_or .fcrm_or_divider_line{flex:1;height:1px;background-color:var(--fc-primary-border);border:none}.fcrm_fluentcrm-lists.fcrm_fluentcrm-view-wrapper.fcrm_fluentcrm_view .fcrm_fluentcrm_pad_around .fcrm_segment_editor_wrapper .fcrm_segment_fields .fcrm_rich_container .fcrm_cond_or{display:flex;align-items:center;justify-content:center;gap:10px;width:100%;position:relative;margin:0}.fcrm_fluentcrm-lists.fcrm_fluentcrm-view-wrapper.fcrm_fluentcrm_view .fcrm_fluentcrm_pad_around .fcrm_segment_editor_wrapper .fcrm_segment_fields .fcrm_rich_container .fcrm_cond_or .fcrm_or_divider_line{flex:1 0 0;height:0;border-bottom:1px solid var(--fc-primary-border)}.fcrm_fluentcrm-lists.fcrm_fluentcrm-view-wrapper.fcrm_fluentcrm_view .fcrm_fluentcrm_pad_around .fcrm_segment_editor_wrapper .fcrm_segment_fields .fcrm_estimated_count{background:var(--fc-primary-bg);border-radius:8px;display:flex;align-items:center;justify-content:center;gap:6px;margin-top:0;padding:6px 8px 6px 12px}.fcrm_fluentcrm-lists.fcrm_fluentcrm-view-wrapper.fcrm_fluentcrm_view .fcrm_fluentcrm_pad_around .fcrm_segment_editor_wrapper .fcrm_segment_fields .fcrm_estimated_count .fcrm_estimated_label{font-size:16px;font-weight:500;line-height:24px;letter-spacing:-.176px;color:var(--fc-secondary-text);white-space:nowrap}.fcrm_fluentcrm-lists.fcrm_fluentcrm-view-wrapper.fcrm_fluentcrm_view .fcrm_fluentcrm_pad_around .fcrm_segment_editor_wrapper .fcrm_segment_fields .fcrm_estimated_count .fcrm_estimated_badge{background:var(--fc-primary-border);border-radius:6px;padding:2px;display:inline-flex;align-items:center;justify-content:center;min-width:24px;flex-shrink:0}.fcrm_fluentcrm-lists.fcrm_fluentcrm-view-wrapper.fcrm_fluentcrm_view .fcrm_fluentcrm_pad_around .fcrm_segment_editor_wrapper .fcrm_segment_fields .fcrm_estimated_count .fcrm_estimated_badge .fcrm_estimated_badge_text{font-size:14px;font-weight:500;line-height:20px;letter-spacing:-.084px;color:var(--fc-deep-bg);text-align:center;white-space:pre-wrap;min-width:16px}.fcrm_fluentcrm-lists.fcrm_fluentcrm-view-wrapper.fcrm_fluentcrm_view .fcrm_fluentcrm_pad_around .fcrm_lists-table.fcrm_segment_contacts{border-radius:var(--fcrm-border-radius-8, 8px);overflow:hidden}.fcrm_dynamic_segments_import_dialog .el-dialog__header{display:flex;padding:16px 16px 16px 20px!important;align-items:center;gap:12px;justify-content:space-between;color:var(--fc-primary-text);font-feature-settings:"ss11" on,"liga" off,"calt" off;font-size:16px;font-style:normal;font-weight:500;line-height:24px;letter-spacing:-.176px}.fcrm_dynamic_segments_import_dialog .el-dialog__header h4{margin:0}.fcrm_dynamic_segments_import_dialog .el-dialog__header svg{cursor:pointer}.fcrm_dynamic_segments_import_dialog .el-dialog__body .el-upload .el-upload-dragger{margin:0;border:none;display:flex;padding:32px;flex-direction:column;justify-content:center;align-items:center;gap:20px;align-self:stretch;overflow:hidden;border-radius:var(--radius-8, 8px);border:1px dashed var(--fc-secondary-border);background:var(--fc-primary-bg)}.fcrm_dynamic_segments_import_dialog .el-dialog__body .el-upload .el-upload-dragger:hover{border:1px dashed var(--fc-primary-text)}.fcrm_dynamic_segments_import_dialog .el-dialog__body .el-upload .el-upload-dragger .fcrm_dynamic_segments_upload_text{display:flex;flex-direction:column;align-items:center;gap:12px;align-self:center}.fcrm_dynamic_segments_import_dialog .el-dialog__body .el-upload .el-upload-dragger .fcrm_dynamic_segments_upload_text .fcrm_dynamic_segments_upload_text_title{color:var(--fc-primary-text);text-align:center;font-feature-settings:"ss11" on,"liga" off,"calt" off;font-size:14px;font-style:normal;font-weight:500;line-height:20px;letter-spacing:-.084px}.fcrm_dynamic_segments_import_dialog .el-dialog__body .el-upload .el-upload-dragger .fcrm_dynamic_segments_upload_text .fcrm_dynamic_segments_upload_text_desc{color:var(--fc-primary-text);text-align:center;font-feature-settings:"ss11" on,"liga" off,"calt" off;font-size:12px;font-style:normal;font-weight:400;line-height:16px}.fcrm_dynamic_segments_import_dialog .el-dialog__body .fcrm_dynamic_segments_upload_tip{display:flex;align-items:center;justify-content:space-between;padding:8px;margin-top:12px;border-radius:var(--radius-8, 8px);background:var(--fc-secondary-bg)}.fcrm_segments_search_input .fcrm-searcher-prefix{margin-right:6px}.fcrm_segments_search_input .fcrm-searcher-prefix .el-icon{width:20px;height:20px;color:var(--fc-text-muted);font-size:18px;margin-top:5px}.fcrm_setting_wrap .fcrm_fluentcrm-lists .fcrm_dynamic_segments_page_header{padding:24px;margin:0}.fcrm_dashboard_loader .el-skeleton{padding:0;line-height:1}.fcrm_dashboard_loader .el-skeleton__item{line-height:1;display:block}.fcrm_dashboard_sidebar_card--header{display:flex;justify-content:space-between;align-items:center}.fcrm_dashboard_sidebar_card--body{padding:20px}.fcrm_perf_bars{display:flex;flex-direction:column;gap:16px}.fcrm_perf_bar{height:6px;background:var(--fc-secondary-bg);position:relative;border-radius:30px;overflow:hidden}.fcrm_perf_bar_inner{height:100%}.fcrm_perf_bar_header{display:flex;align-items:center;justify-content:space-between;gap:8px;margin-bottom:6px}.fcrm_perf_bar_label{color:var(--fc-primary-text);font-weight:500;font-size:14px;line-height:20px;margin:0;display:block}.fcrm_perf_value{display:flex;align-items:center;gap:4px}.fcrm_perf_value .fcrm_perf_count{font-weight:500;font-size:14px;line-height:20px;color:var(--fc-secondary-text);display:block;position:relative;padding-inline-end:8px}.fcrm_perf_value .fcrm_perf_count:before{content:"";position:absolute;right:0;top:50%;width:3px;height:3px;border-radius:50%;background:var(--fc-text-muted)}.fcrm_perf_value .fcrm_perf_pct{color:var(--fc-text-muted);font-weight:500;font-size:14px;line-height:20px;display:block;margin:0}.fcrm_review_card{background:var(--fc-primary-bg);border-radius:8px;padding:24px;position:relative}.fcrm_review_card .el-button.close_card{position:absolute;top:10px;right:10px;border-radius:0;border:none;background:none;color:var(--fc-secondary-text);box-shadow:none}.fcrm_review_card .el-button.close_card:hover{background:none;color:var(--fc-primary-text)}.fcrm_review_card .fcrm_review_card--title{margin:0 0 4px;font-weight:500;font-size:16px;line-height:24px;color:var(--fc-primary-text)}.fcrm_review_card p{font-weight:400;font-size:12px;line-height:16px;margin:0 0 16px;color:var(--fc-secondary-text)}.fcrm_subscriber_growth_card .fcrm_base_card_header .el-dropdown-link:focus-visible{box-shadow:none;outline:none}.fcrm_subscriber_growth_card .fcrm_base_card_header h4 .icon{width:20px;height:20px;border:1px solid var(--fc-primary-border);color:var(--fc-secondary-text);background:var(--fc-secondary-bg);border-radius:4px;box-shadow:0 1px 2px #0a0d1408;display:flex;align-items:center;justify-content:center}.fcrm_subscriber_growth_card .fcrm_base_card_header .el-date-editor{padding:0 10px;font-weight:500;height:32px;margin:0;border:1px solid var(--fc-primary-border);box-shadow:none;border-radius:8px;width:190px;position:relative}.fcrm_subscriber_growth_card .fcrm_base_card_header .el-date-editor.is-active{border-color:var(--fc-primary-text)}.fcrm_subscriber_growth_card .fcrm_base_card_header .el-date-editor .el-icon{width:20px}.fcrm_subscriber_growth_card .fcrm_base_card_header .el-date-editor .el-icon svg{width:20px;height:20px}.fcrm_dashboard_entity_card__item{width:100%;display:flex;align-items:center;gap:12px;padding:8px;border-radius:var(--fcrm-border-radius-8);cursor:pointer;transition:background-color .2s ease}.fcrm_dashboard_entity_card__item:hover{background:var(--fc-secondary-bg)}.fcrm_dashboard_entity_card__item:focus-visible{outline:2px solid var(--fc-primary-text);outline-offset:2px;background:var(--fc-secondary-bg)}.fcrm_dashboard_entity_card__item_media img{width:40px;height:40px;border-radius:999px;flex-shrink:0;display:block}.fcrm_dashboard_entity_card__item_content{display:flex;flex:1;min-width:0;flex-direction:column;align-items:flex-start}.fcrm_dashboard_entity_card__item_title{margin:0 0 4px;width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--fc-primary-text);font-size:14px;font-weight:500;line-height:20px;letter-spacing:-.084px}.fcrm_dashboard_entity_card__item_subtitle{width:100%;display:flex;align-items:center;gap:4px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;text-align:left;color:var(--fc-secondary-text);font-size:12px;font-weight:400;line-height:16px}.fcrm_dashboard_entity_card__item_subtitle .icon{display:block}.fcrm_dashboard_entity_card__item_subtitle .icon svg{display:block}.fcrm_dashboard_entity_card__stats{width:100%;display:flex;align-items:center;gap:8px;padding:0}.fcrm_dashboard_entity_card__stat{display:flex;align-items:center;gap:4px;color:var(--fc-secondary-text);font-size:12px;font-weight:400;line-height:16px;white-space:nowrap}.fcrm_dashboard_entity_card__stat_icon{display:block;color:var(--fc-text-muted)}.fcrm_dashboard_entity_card__stat_icon svg{display:block;width:16px;height:16px}.fcrm_dashboard_entity_card__stat_dot{width:3px;height:3px;border-radius:50%;background:var(--fc-text-muted);display:block;flex:none}.fcrm_dashboard_entity_card__stat_open_rate{margin-inline-start:auto;font-weight:500}.fcrm_dashboard_entity_card__item_meta{margin-inline-start:auto}.fc_chart_box{margin-bottom:30px}.fcrm_quick_links{margin:0;padding:0}.fcrm_quick_links li{display:flex;gap:8px;align-items:center;padding:6px 8px;list-style:none;margin:0;font-size:14px;line-height:24px;border-radius:8px}.fcrm_quick_links li:hover{background:var(--fc-secondary-bg)}.fcrm_quick_links li .fcrm_quick_link_icon{width:20px;height:20px;display:flex;align-items:center;justify-content:center;flex:none}.fcrm_quick_links li .fcrm_quick_link_icon svg{display:block}.fcrm_quick_links li .el-icon{color:var(--fc-secondary-text)}.fcrm_quick_links li a{color:var(--fc-primary-text);font-size:14px;font-style:normal;font-weight:400;letter-spacing:-.084px}.fcrm_quick_links_wrap .fcrm_base_card_body{padding:12px}.fcrm_system_tips_wrap .fcrm_base_card_body *{margin:0}.fcrm_system_tips_wrap .fcrm_base_card_body>div{display:flex;flex-direction:column;align-items:flex-start;gap:8px}.fcrm_system_tips_wrap .fcrm_base_card_body ul{list-style:disc;padding:0 0 0 20px}.fcrm_funnel_top_nav{display:flex;align-items:center;gap:24px;margin:0}.fcrm_funnel_top_nav_wrapper{display:flex;align-items:center;justify-content:space-between;background:var(--fc-primary-bg);margin:-15px -20px 0;padding:0 20px;border-bottom:1px solid var(--fc-primary-border);flex-wrap:wrap;gap:12px}.fcrm_funnel_top_nav_wrapper .el-breadcrumb{display:flex;align-items:center}.fcrm_funnel_top_nav_wrapper .el-breadcrumb .el-breadcrumb__separator{font-size:12px;margin-left:8px;margin-right:8px}.fcrm_funnel_top_nav_wrapper .el-breadcrumb .el-breadcrumb__inner{display:flex;align-items:center;gap:6px;color:var(--fc-primary-text);font-weight:500;font-size:14px;line-height:20px}.fcrm_funnel_top_nav_wrapper .el-breadcrumb .el-breadcrumb__inner:hover{color:var(--fc-primary-text);font-weight:500}.fcrm_funnel_top_nav_wrapper .fcrm_funnel_title_editable_wrap{display:flex;align-items:center;gap:6px}.fcrm_funnel_top_nav_wrapper .fcrm_funnel_title_editable_wrap .fcrm_funnel_breadcrumb_title{display:inline-flex;align-items:center;gap:6px;color:var(--fc-primary-text);font-weight:500;font-size:14px;line-height:20px}.fcrm_funnel_top_nav_wrapper .fcrm_funnel_title_editable_wrap .fcrm_funnel_breadcrumb_title_text{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:280px}.fcrm_funnel_top_nav_wrapper .fcrm_funnel_title_editable_wrap .icon-edit{cursor:pointer}.fcrm_funnel_top_nav_wrapper .fcrm_funnel_title_editable_wrap .icon-edit svg{display:block}.fcrm_funnel_top_nav li{margin:0;font-weight:500;font-size:12px;line-height:16px;color:var(--fc-secondary-text);border-bottom:2px solid transparent}.fcrm_funnel_top_nav li a{color:var(--fc-secondary-text);padding:12px 0;display:block}.fcrm_funnel_top_nav li.is-active{color:var(--fc-primary-text);border-bottom-color:var(--fc-primary-text)}.fcrm_funnel_top_nav li.is-active a{color:var(--fc-primary-text)}.fcrm_funnel_top_nav li.fcrm_funnel_title_editable_wrap{display:flex;align-items:center;gap:6px}.fcrm_funnel_top_nav li.fcrm_funnel_title_editable_wrap .fcrm_funnel_breadcrumb_title{color:var(--fc-primary-text);font-weight:500;font-size:14px;line-height:20px}.fcrm_funnel_top_nav li.fcrm_funnel_title_editable_wrap .icon-edit{cursor:pointer}.fcrm_funnel_top_nav li.fcrm_funnel_title_editable_wrap .icon-edit svg{display:block}.fcrm_funnel_top_nav_actions{display:flex;align-items:center;gap:12px}.fcrm_funnel_top_nav_actions .el-button{margin:0}.fcrm_funnel_top_nav_actions .el-button+.el-button{margin:0}.fcrm_funnel_top_nav_actions .el-button.configure-btn,.fcrm_funnel_top_nav_actions .el-button.report-btn{width:36px}.fcrm_funnel_top_nav_actions .el-checkbox.fcrm_funnel_stats_checkbox{height:auto;display:flex;align-items:center;gap:8px}.fcrm_funnel_top_nav_actions .el-checkbox.fcrm_funnel_stats_checkbox .el-checkbox__label{padding:0}.fcrm_funnel_top_nav_actions .el-checkbox.fcrm_funnel_stats_checkbox .el-checkbox__input .el-checkbox__original{margin:0}.fcrm_funnel_top_nav_actions .el-switch.is-checked .el-switch__core{background:var(--fc-deep-bg);border-color:var(--fc-deep-bg)}.fcrm_funnel_top_nav_actions .el-switch .el-switch__label{color:var(--fc-primary-text);font-weight:400;font-size:14px;line-height:20px;margin-left:8px}.fcrm_funnel_top_nav_actions .el-switch .el-switch__label span{display:block;font-size:inherit}.fcrm_inline_editable_input{display:flex;align-items:center;gap:8px}.fcrm_inline_editable_input .el-button{margin:0;padding:5px 10px}.fcrm_inline_editable_input .el-input__wrapper{background:var(--fc-primary-bg);padding:0 11px!important}.fcrm_inline_editable_input .icon-edit{cursor:pointer}.fcrm_inline_editable_input .icon-edit svg{display:block}.fc_funnel_head{display:flex;align-items:center;justify-content:space-between;gap:12px}.fc_funnel_head .fc_funel_head_title h3{color:var(--fc-primary-text)}.fc_funnel_head .fc_funel_head_title p{color:var(--fc-secondary-text)}.fcrm_sync_new_steps_content h3{color:var(--fc-primary-text);margin:0 0 4px}.fcrm_funnel_edit_wrapper .fcrm_funnel_top_nav_wrapper{padding:10px 20px}.fcrm_funnel_edit_wrapper .fcrm_funnel_edit_body{background-image:url(../../images/tile.png);background-repeat:repeat;filter:alpha(opacity=1);background-size:30px 30px;background-color:var(--fc-secondary-bg);margin:0 -20px;padding:30px 20px}.fcrm_funnel_edit_wrapper .fcrm_funnel_edit_blocks .fcrm_funnel_edit_block_item_add{display:flex;justify-content:center;align-items:center;width:100%}.fcrm_funnel_edit_wrapper .fcrm_funnel_edit_blocks .fcrm_funnel_edit_block_item_add_circle{width:16px;height:16px;border-radius:50%;border:1px solid var(--fc-primary-text);background:var(--fc-primary-bg);flex:none;position:absolute;left:50%;bottom:calc(100% - 8px);transform:translate(-50%);z-index:3}.fcrm_funnel_edit_wrapper .fcrm_funnel_edit_blocks .fcrm_funnel_edit_block_item_add_line{width:1.5px;height:calc(100% - 8px);background:var(--fc-secondary-border);flex:none;position:absolute;left:50%;top:0;transform:translate(-50%);z-index:-1}.fcrm_funnel_edit_wrapper .fcrm_funnel_edit_blocks .fcrm_funnel_edit_block_item_add_icon{position:absolute;color:var(--fc-secondary-border);bottom:3px;line-height:1}.fcrm_funnel_edit_wrapper .fcrm_funnel_edit_blocks .fcrm_funnel_edit_block_item_add_inner{min-height:72px;position:relative;display:flex;align-items:center;justify-content:center;z-index:1}.fcrm_funnel_edit_wrapper .fcrm_funnel_edit_blocks .fcrm_funnel_edit_block_item_add_inner .fcrm_show_add_block_plus{z-index:3}.fcrm_funnel_edit_wrapper .fcrm_funnel_edit_blocks .fcrm_funnel_edit_block_item_add_inner .fcrm_show_add_block_plus .fcrm_add_block_icon{width:20px;height:20px;display:flex;align-items:center;justify-content:center;border-radius:4px;border:1px solid var(--fc-primary-border);box-shadow:0 1px 2px #0a0d1408;background:var(--fc-primary-bg);color:var(--fc-secondary-text);cursor:pointer}.fcrm_funnel_edit_wrapper .fcrm_funnel_edit_block_wrapper .fcrm_funnel_edit_block{display:flex;justify-content:center;align-items:center}.fcrm_funnel_edit_wrapper .fcrm_funnel_edit_block_wrapper .fcrm_funnel_edit_block .fcrm_funnel_edit_blockin_inner{border-radius:0 12px 12px}.fcrm_funnel_edit_wrapper .fcrm_funnel_edit_blockin{position:relative;min-width:300px;z-index:1}.fcrm_funnel_edit_wrapper .fcrm_funnel_edit_blockin_top_badge{border:1px solid var(--fc-primary-border);border-bottom:none;border-radius:12px 12px 0 0;padding:4px 12px;display:inline-block;background:var(--fc-primary-bg);color:var(--fc-secondary-text);font-size:12px;line-height:16px;font-weight:500}.fcrm_funnel_edit_wrapper .fcrm_funnel_edit_blockin:hover .fcrm_funnel_edit_block_controls{transform:scale(1) translateY(-50%);opacity:1;visibility:visible}.fcrm_funnel_edit_wrapper .fcrm_funnel_edit_blockin:hover .fcrm_action_abs_right{transform:scale(1);opacity:1;visibility:visible}.fcrm_funnel_edit_wrapper .fcrm_funnel_edit_blockin .fcrm_action_abs_right{position:absolute;right:0;display:flex;bottom:100%;padding-bottom:4px;width:100%;justify-content:flex-end;z-index:5;transform-origin:right;transform:scale(.9);opacity:0;visibility:hidden;transition:.2s}.fcrm_funnel_edit_wrapper .fcrm_funnel_edit_blockin .fcrm_action_abs_right_inner{display:flex;align-items:center;border:1px solid var(--fc-primary-border);border-radius:6px;background:var(--fc-primary-bg)}.fcrm_funnel_edit_wrapper .fcrm_funnel_edit_blockin .fcrm_action_abs_right .el-button{margin:0;width:24px;height:24px;background:none;border:none;border-radius:0;color:var(--fc-secondary-text);padding:4px}.fcrm_funnel_edit_wrapper .fcrm_funnel_edit_blockin .fcrm_action_abs_right .el-button+.el-button{border-left:1px solid var(--fc-primary-border)}.fcrm_funnel_edit_wrapper .fcrm_funnel_edit_blockin_inner{border:1px solid var(--fc-primary-border);box-shadow:0 1px 2px #0a0d1408;border-radius:12px;background:var(--fc-primary-bg);padding:12px 16px;cursor:pointer}.fcrm_funnel_edit_wrapper .fcrm_funnel_edit_blockin_inner .fcrm_funnel_edit_block_header{border-bottom:1px solid var(--fc-primary-border);padding-bottom:12px;margin-bottom:12px}.fcrm_funnel_edit_wrapper .fcrm_funnel_edit_blockin_inner .fcrm_funnel_edit_block_title{display:flex;align-items:center;flex-wrap:wrap;gap:4px;color:var(--fc-primary-text);font-size:14px;line-height:20px;font-weight:500}.fcrm_funnel_edit_wrapper .fcrm_funnel_edit_blockin_inner .fcrm_funnel_edit_block_icon{width:20px;height:20px;display:flex;align-items:center;justify-content:center;color:var(--fc-secondary-text)}.fcrm_funnel_edit_wrapper .fcrm_funnel_edit_blockin_inner .fcrm_funnel_edit_block_subtitle{color:var(--fc-secondary-text);font-size:12px;line-height:16px;font-weight:400}.fcrm_funnel_edit_wrapper .fcrm_funnel_edit_blockin_inner .fcrm_funnel_edit_block_desc{display:flex;flex-wrap:wrap;gap:8px}.fcrm_funnel_edit_wrapper .fcrm_funnel_edit_blockin_inner .fcrm_funnel_edit_block_desc_badge{background:var(--fc-secondary-bg);display:block;color:var(--fc-secondary-text);font-size:12px;line-height:16px;font-weight:500;border-radius:6px;padding:4px 8px}.fcrm_funnel_edit_wrapper .fcrm_funnel_edit_blockin_inner .fcrm_funnel_edit_block_stats{display:flex;align-items:center;flex-wrap:wrap;gap:16px;margin-top:8px}.fcrm_funnel_edit_wrapper .fcrm_funnel_edit_blockin_inner .fcrm_funnel_edit_block_stats .el-tag{border:none;background:none;padding:0;position:relative;margin:0}.fcrm_funnel_edit_wrapper .fcrm_funnel_edit_blockin_inner .fcrm_funnel_edit_block_stats .el-tag:after{content:"";width:2px;height:2px;border-radius:50%;background:var(--fc-secondary-text);position:absolute;right:-8px;top:50%;transform:translateY(-50%)}.fcrm_funnel_edit_wrapper .fcrm_funnel_edit_blockin_inner .fcrm_funnel_edit_block_stats .el-tag:last-child:after{display:none}.fcrm_funnel_edit_wrapper .fcrm_funnel_edit_blockin_inner .fcrm_funnel_edit_block_stats .el-tag.el-tag--danger .el-tag__content{color:var(--fc-error)}.fcrm_funnel_edit_wrapper .fcrm_funnel_edit_blockin_inner .fcrm_funnel_edit_block_stats .el-tag__content{display:flex;align-items:center;gap:2px;font-size:12px;line-height:16px;color:var(--fc-secondary-text)}.fcrm_funnel_edit_wrapper .fcrm_funnel_edit_blockin_inner .fcrm_funnel_edit_block_stats .el-tag .el-icon{font-size:13px}.fcrm_funnel_edit_wrapper .fcrm_funnel_edit_blockin_inner .fcrm_funnel_edit_block_stats .el-tag .icon svg{display:block;width:14px}.fcrm_funnel_edit_wrapper .fcrm_funnel_edit_blockin .fcrm_funnel_edit_block_controls{position:absolute;top:50%;transform:scale(.9) translateY(-50%);opacity:0;visibility:hidden;right:calc(100% + 4px);display:flex;flex-direction:column;border:1px solid var(--fc-primary-border);border-radius:6px;background:var(--fc-primary-bg);transition:.2s}.fcrm_funnel_edit_wrapper .fcrm_funnel_edit_blockin .fcrm_funnel_edit_block_controls .el-button{width:24px;height:24px;padding:0;border:none;background:none;border-radius:0}.fcrm_funnel_edit_wrapper .fcrm_funnel_edit_blockin .fcrm_funnel_edit_block_controls .el-button:not(.is-disabled){color:var(--fc-secondary-text)}.fcrm_funnel_edit_wrapper .fcrm_funnel_edit_blockin .fcrm_funnel_edit_block_controls .el-button+.el-button{border-top:1px solid var(--fc-primary-border)}.fcrm_funnel_edit_wrapper .fcrm_funnel_edit_inner_block_wrapper .fcrm_funnel_edit_block_item_holder{display:flex;flex-direction:column;align-items:center;justify-content:center}.fcrm_funnel_edit_wrapper .fcrm_funnel_edit_inner_block_wrapper .fcrm_funnel_edit_block_item_holder:last-child .fcrm_funnel_edit_block_item_add_inner{align-items:flex-end}.fcrm_funnel_edit_wrapper .fcrm_funnel_edit_inner_block_wrapper .fcrm_funnel_edit_block_item_holder:last-child .fcrm_funnel_edit_block_item_add_icon{display:none}.fcrm_funnel_edit_wrapper .fcrm_funnel_edit_inner_block_wrapper .fcrm_funnel_edit_block_item_holder:last-child .fcrm_funnel_edit_block_item_add .fcrm_show_add_block_plus .fcrm_add_block_icon{background:var(--fc-deep-bg);border-color:var(--fc-deep-bg);color:var(--fc-text-inverse)}.fcrm_funnel_edit_wrapper .fcrm_funnel_edit_block{transition:.2s}.fcrm_funnel_edit_wrapper .fcrm_funnel_edit_block:hover .fcrm_funnel_edit_blockin_inner{border-color:var(--fc-text-link)}.fcrm_funnel_edit_wrapper .fcrm_funnel_edit_block:hover.fc_block_type_conditional .fcrm_funnel_edit_blockin_inner{border-color:var(--fc-error)}.fcrm_funnel_edit_wrapper .fcrm_funnel_edit_block:hover.fc_block_type_benchmark .fcrm_funnel_edit_blockin_inner{border-color:var(--fc-warning)}.fcrm_funnel_edit_block_type{display:inline-flex;align-items:center;gap:2px;background:var(--fc-text-link);border-radius:6px;padding:2px 8px 2px 6px;color:var(--fc-text-link);font-size:12px;line-height:16px;font-weight:500}.fcrm_funnel_edit_block_type .icon svg{display:block}.fcrm_funnel_edit_block_type.benchmark,.fcrm_funnel_edit_block_type.goal{background:var(--fc-warning-bg);color:var(--fc-warning)}.fcrm_funnel_edit_block_type.conditional{background:var(--fc-error-bg);color:var(--fc-error)}.fcrm_funnel_edit_cond_block_border_no_top{max-width:525px;width:100%;height:80px;margin:-20px auto 0;border:2px solid var(--fc-primary-border);border-top:0;border-bottom-left-radius:24px;border-bottom-right-radius:24px}.fcrm_funnel_edit_block_conditional_connector{position:absolute;width:calc(100% + 220px);z-index:-1;left:-110px;top:50%}.fcrm_funnel_edit_block_conditional_connector .fc_dom_path{border-color:var(--fc-primary-border)!important}.fcrm_funnel_edit_block_conditional_connector .fc_dom_path.fc_ab_test,.fcrm_funnel_edit_block_conditional_connector .fc_dom_path.fc_condition_node_point{top:0!important}.fcrm_funnel_edit_block_conditional_connector .fc_dom_path.fc_dom_path_right{left:auto!important;right:0}.fcrm_funnel_edit_block_conditional_connector .fc_dom_path_left .fcrm_condition_node_point_text{left:-20%}.fcrm_funnel_edit_block_conditional_connector .fc_dom_path_right .fcrm_condition_node_point_text{right:-20%;border-color:var(--fc-success)}.fcrm_funnel_edit_block_conditional_connector .fc_dom_path_right .fcrm_condition_node_point_icon{left:auto;right:-7.5px}.fcrm_funnel_edit_block_conditional_connector .fcrm_condition_node_point_text{position:absolute;border-radius:8px;color:var(--fc-secondary-text);background:var(--fc-primary-bg);font-size:14px;line-height:20px;padding:4px 10px;border:1px solid var(--fc-error);box-shadow:0 1px 2px #0a0d1408;top:30px}.fcrm_funnel_edit_block_conditional_connector .fcrm_condition_node_point_icon{position:absolute;left:-7.5px;bottom:-5px;color:var(--fc-primary-border);line-height:1}.fcrm_funnel_edit_block_conditional_wrapper{position:relative;margin:45px auto 0;display:flex;justify-content:space-between;z-index:2;max-width:850px;width:100%}.fcrm_funnel_edit_block_cond_holder_holder{background:var(--fc-secondary-bg);border:1px solid var(--fc-primary-border);padding:16px;border-radius:12px;width:370px}.fcrm_funnel_edit_block_cond_holder_holder .el-button.fcrm_add_child_block_btn{width:100%;padding:3px 10px}.fcrm_funnel_edit_block_cond_holder_holder .fc_child_blocks .fcrm_funnel_edit_block{margin-bottom:16px}.fcrm_funnel_step_reports--list{display:grid;grid-template-columns:repeat(auto-fill,minmax(280px,1fr));gap:16px}.fcrm_funnel_step_reports--card{border:1px solid var(--fc-primary-border);border-radius:8px;padding:16px;display:flex;flex-direction:column;align-items:flex-start}.fcrm_funnel_step_reports--card .el-progress{width:100%;flex-direction:column-reverse;align-items:flex-start;margin-bottom:7px}.fcrm_funnel_step_reports--card .el-progress__text{min-width:inherit;margin:0 0 12px;font-weight:500;font-size:16px;line-height:24px;color:var(--fc-primary-text)}.fcrm_funnel_step_reports--card .el-progress-bar{width:100%}.fcrm_funnel_step_reports--card .fcrm_funnel_step_reports--card-title{font-weight:500;font-size:12px;line-height:16px;margin:0 0 2px;color:var(--fc-text-muted)}.fcrm_block_stats{margin:5px 0 0;display:flex;align-items:center;flex-wrap:wrap;justify-content:center;gap:6px}.fcrm_block_stats .el-tag{border:1px solid var(--fc-primary-border);min-height:24px;height:auto;border-radius:6px;color:var(--fc-secondary-text);font-size:12px;line-height:16px;margin:0;padding:0 4px;background:var(--fc-primary-bg)}.fcrm_block_stats .el-tag__content{display:flex;align-items:center;gap:2px;font-weight:500;font-size:12px;line-height:16px;color:var(--fc-secondary-text)}.fcrm_block_stats .el-tag__content .el-icon{display:block;color:var(--fc-text-muted)}.fcrm_block_stats .el-tag__content .icon{color:var(--fc-text-muted);display:block}.fcrm_block_stats .el-tag__content .icon svg{display:block;width:16px;height:16px}.fcrm_block_stats .el-tag.el-tag--danger{background:var(--fc-error-bg);border-color:var(--fc-error-bg);color:var(--fc-error)}.fcrm_block_stats .el-tag.el-tag--danger .el-tag__content,.fcrm_block_stats .el-tag.el-tag--danger .icon,.fcrm_block_stats .el-tag.el-tag--danger .el-icon{color:var(--fc-error)}.el-popover.fcrm_trigger_selection_popover{box-shadow:0 16px 32px -12px #0e121b1a;border:1px solid var(--fc-primary-border);border-radius:16px}.el-popper.fcrm-smartcodes-popover{width:auto!important}.fcrm_funnel_head{display:flex;align-items:center;justify-content:space-between}.el-overlay.fcrm_trigger_modal_wrapper .el-dialog{width:100%;max-width:760px;border-radius:8px}.el-overlay.fcrm_trigger_modal_wrapper .el-dialog__header{position:relative;background:none;border-bottom:1px solid var(--fc-primary-border);padding:12px 20px}.el-overlay.fcrm_trigger_modal_wrapper .el-dialog__title{color:var(--fc-primary-text);font-weight:500;font-size:18px;line-height:24px;display:flex;align-items:center;gap:12px}.el-overlay.fcrm_trigger_modal_wrapper .el-dialog__title .icon svg{display:block}.el-overlay.fcrm_trigger_modal_wrapper .el-dialog__headerbtn{color:var(--fc-secondary-text);height:100%}.el-overlay.fcrm_trigger_modal_wrapper .el-dialog__headerbtn .el-dialog__close{color:var(--fc-secondary-text)}.el-overlay.fcrm_trigger_modal_wrapper .el-dialog__body{padding:20px}.el-overlay.fcrm_trigger_modal_wrapper .el-dialog__body .fcrm_trigger_prebuild_template_wrapper .fcrm_trigger_create_from_scratch_wrapper{margin-bottom:16px}.el-overlay.fcrm_trigger_modal_wrapper .el-dialog__body .fcrm_trigger_prebuild_template_wrapper .fcrm_trigger_create_from_scratch_box{width:100%;max-width:400px;margin-left:auto;margin-right:auto;border:1px dashed var(--fc-secondary-border);border-radius:12px;display:flex;flex-direction:column;align-items:center;justify-content:center;padding:16px 32px;cursor:pointer}.el-overlay.fcrm_trigger_modal_wrapper .el-dialog__body .fcrm_trigger_prebuild_template_wrapper .fcrm_trigger_create_from_scratch_box:hover{border-color:var(--fc-primary-text)}.el-overlay.fcrm_trigger_modal_wrapper .el-dialog__body .fcrm_trigger_prebuild_template_wrapper .fcrm_trigger_create_from_scratch_box .plus-icon{background:var(--fc-secondary-bg);border-radius:50%;display:flex;align-items:center;justify-content:center;width:32px;height:32px;margin-bottom:12px}.el-overlay.fcrm_trigger_modal_wrapper .el-dialog__body .fcrm_trigger_prebuild_template_wrapper .fcrm_trigger_create_from_scratch_box .plus-icon svg{display:block}.el-overlay.fcrm_trigger_modal_wrapper .el-dialog__body .fcrm_trigger_prebuild_template_wrapper .fcrm_trigger_create_from_scratch_box h4{color:var(--fc-primary-text);font-weight:500;font-size:14px;line-height:20px;margin:0 0 4px}.el-overlay.fcrm_trigger_modal_wrapper .el-dialog__body .fcrm_trigger_prebuild_template_wrapper .fcrm_trigger_create_from_scratch_box p{margin:0;color:var(--fc-secondary-text);font-weight:400;font-size:12px;line-height:16px}.el-overlay.fcrm_trigger_modal_wrapper .el-dialog__body .fcrm_trigger_prebuild_template_wrapper .fcrm_trigger_or_line{margin-bottom:16px}.el-overlay.fcrm_trigger_modal_wrapper .el-dialog .el-dialog__footer{padding:0}.el-overlay.fcrm_trigger_modal_wrapper .el-dialog .el-dialog__footer .dialog-footer{background:none;border-top:1px solid var(--fc-primary-border);padding:16px 20px}.el-overlay.fcrm_funnel_blocks_model .el-drawer__header{border-bottom:1px solid var(--fc-primary-border);background:none;padding:12px 16px!important}.el-overlay.fcrm_funnel_blocks_model .el-drawer__body .fcrm_funnel_blocks_panel_header{position:relative}.el-overlay.fcrm_funnel_blocks_model .el-drawer__body .fcrm_funnel_blocks_panel_header .el-menu{height:auto;min-height:auto;display:flex;align-items:center;gap:12px;padding:0 16px;border-bottom:1px solid var(--fc-primary-border);background:none}.el-overlay.fcrm_funnel_blocks_model .el-drawer__body .fcrm_funnel_blocks_panel_header .el-menu.el-menu--horizontal>.el-menu-item.is-active{background:none;color:var(--fc-primary-text)!important;border-bottom-color:var(--fc-primary-text)}.el-overlay.fcrm_funnel_blocks_model .el-drawer__body .fcrm_funnel_blocks_panel_header .el-menu.el-menu--horizontal>.el-menu-item.is-active .icon{color:var(--fc-primary-text)}.el-overlay.fcrm_funnel_blocks_model .el-drawer__body .fcrm_funnel_blocks_panel_header .el-menu .el-menu-item{color:var(--fc-secondary-text);font-weight:500;font-size:12px;line-height:16px;display:flex;align-items:center;gap:4px;padding:14px 0;border-bottom:2px solid transparent}.el-overlay.fcrm_funnel_blocks_model .el-drawer__body .fcrm_funnel_blocks_panel_header .el-menu .el-menu-item:hover{background:none}.el-overlay.fcrm_funnel_blocks_model .el-drawer__body .fcrm_funnel_blocks_panel_header .el-menu .el-menu-item.is-active{background:none;color:var(--fc-primary-text)!important;border-bottom-color:var(--fc-primary-text)}.el-overlay.fcrm_funnel_blocks_model .el-drawer__body .fcrm_funnel_blocks_panel_header .el-menu .el-menu-item.is-active .icon{color:var(--fc-primary-text)}.el-overlay.fcrm_funnel_blocks_model .el-drawer__body .fcrm_funnel_blocks_panel_header .el-menu .el-menu-item .icon{color:var(--fc-secondary-text)}.el-overlay.fcrm_funnel_blocks_model .el-drawer__body .fcrm_funnel_blocks_panel_header .el-menu .el-menu-item .icon svg{display:block}.el-overlay.fcrm_funnel_blocks_model .el-drawer__body .fcrm_funnel_blocks_panel_header .el-dialog__close{position:absolute;right:16px;top:0;color:var(--fc-secondary-text);cursor:pointer;font-size:18px;height:100%;z-index:3}.el-overlay.fcrm_funnel_blocks_model .el-drawer__body .fcrm_funnel_blocks_panel_header_bottom{padding:16px 20px}.el-overlay.fcrm_funnel_blocks_model .el-drawer__body .fcrm_funnel_blocks_panel_header_bottom .el-input{margin:0}.el-overlay.fcrm_funnel_blocks_model .el-drawer__body .fcrm_funnel_blocks_panel_header_bottom .el-input__wrapper{box-shadow:0 1px 2px #0a0d1408;border:1px solid var(--fc-primary-border);border-radius:8px}.el-overlay.fcrm_funnel_blocks_model .el-drawer__body .fcrm_funnel_blocks_panel_header_bottom_content{padding-top:20px}.el-overlay.fcrm_funnel_blocks_model .el-drawer__body .fcrm_funnel_blocks_panel_header_bottom_content h4{margin:0 0 4px;color:var(--fc-primary-text);font-weight:500;font-size:14px;line-height:20px}.el-overlay.fcrm_funnel_blocks_model .el-drawer__body .fcrm_funnel_blocks_panel_header_bottom_content p{color:var(--fc-secondary-text);font-weight:400;font-size:12px;line-height:16px;margin:0}.el-overlay.fcrm_funnel_blocks_model .fcrm_choice_action_drawer .el-drawer__body{padding:0!important}.el-overlay.fcrm_funnel_blocks_model .fcrm_choice_action_drawer .el-drawer__body .fluentcrm_funnel_header{padding-top:0;padding-left:20px;padding-right:20px;margin-left:-20px;margin-right:-20px}.el-overlay.fcrm_funnel_edit_block_modal .el-drawer{padding-top:10px}.el-overlay.fcrm_funnel_edit_block_modal .el-drawer__body{padding:16px}.el-overlay.fcrm_funnel_edit_block_modal .el-drawer__body .fluentcrm_block_editor_body .el-form .fluentcrm_funnel_header{margin:-16px -16px 0;padding:12px 16px;border-bottom:1px solid var(--fc-primary-border)}.el-overlay.fcrm_funnel_edit_block_modal .el-drawer__body .fluentcrm_block_editor_body .el-form .fluentcrm_funnel_header .fcrm_funnel_head_title h3{margin:0 0 4px;font-weight:500;font-size:14px;line-height:20px;color:var(--fc-primary-text);display:flex;align-items:center;gap:8px}.el-overlay.fcrm_funnel_edit_block_modal .el-drawer__body .fluentcrm_block_editor_body .el-form .fluentcrm_funnel_header .fcrm_funnel_head_title h3 .ff_funnel_badge{font-weight:500;font-size:12px;line-height:16px;padding:2px 8px;border-radius:6px;display:inline-flex;align-items:center;gap:2px;color:var(--fc-text-link);background:var(--fc-text-link)}.el-overlay.fcrm_funnel_edit_block_modal .el-drawer__body .fluentcrm_block_editor_body .el-form .fluentcrm_funnel_header .fcrm_funnel_head_title p{font-size:12px;line-height:16px;font-weight:400;color:var(--fc-secondary-text);margin:0}.el-overlay.fcrm_funnel_edit_block_modal .el-drawer__body .fluentcrm_block_editor_body .el-form .fluentcrm_funnel_header .fcrm_funnel_head_action{display:flex;align-items:center;gap:12px}.el-overlay.fcrm_funnel_edit_block_modal .el-drawer__body .fluentcrm_block_editor_body .el-form .fluentcrm_funnel_header .fcrm_funnel_head_action_btns{border:1px solid var(--fc-primary-border);border-radius:8px;display:flex;align-items:center}.el-overlay.fcrm_funnel_edit_block_modal .el-drawer__body .fluentcrm_block_editor_body .el-form .fluentcrm_funnel_header .fcrm_funnel_head_action_btns .el-button{width:32px;height:32px;padding:0;background:none;color:var(--fc-secondary-text);border:none;font-size:14px;border-radius:0}.el-overlay.fcrm_funnel_edit_block_modal .el-drawer__body .fluentcrm_block_editor_body .el-form .fluentcrm_funnel_header .fcrm_funnel_head_action_btns .el-button-group{margin:0}.el-overlay.fcrm_funnel_edit_block_modal .el-drawer__body .fluentcrm_block_editor_body .el-form .fluentcrm_funnel_header .fcrm_funnel_head_action_btns .fcrm_funnel_head_action_btn_delete{border-left:1px solid var(--fc-primary-border)}.el-overlay.fcrm_funnel_edit_block_modal .el-drawer__body .fluentcrm_block_editor_body .el-form .fluentcrm_funnel_header .fcrm_funnel_head_action_btns .fc_header_merge_codes .el-button{font-size:12px}.el-overlay.fcrm_funnel_edit_block_modal .el-drawer__body .fluentcrm_block_editor_body .el-form .fluentcrm_funnel_header .fcrm_funnel_head_action .fcrm_funnel_head_action_btn_close{color:var(--fc-secondary-text);padding:0;width:20px;height:20px}.el-overlay.fcrm_funnel_edit_block_modal .el-drawer__body .fluentcrm_block_editor_body .el-form .fcrm_funnel_edit_block_internal_fields{margin-top:16px}.el-overlay.fcrm_funnel_edit_block_modal .el-drawer__body .fluentcrm_block_editor_body .el-form-item__label{color:var(--fc-primary-text);font-weight:500;font-size:14px;line-height:20px;padding:0;margin-bottom:4px}.el-overlay.fcrm_funnel_edit_block_modal .el-drawer__body .fluentcrm_block_editor_body .el-form-item .el-select__wrapper,.el-overlay.fcrm_funnel_edit_block_modal .el-drawer__body .fluentcrm_block_editor_body .el-form-item .el-select,.el-overlay.fcrm_funnel_edit_block_modal .el-drawer__body .fluentcrm_block_editor_body .el-form-item .el-textarea,.el-overlay.fcrm_funnel_edit_block_modal .el-drawer__body .fluentcrm_block_editor_body .el-form-item .el-input{height:auto;min-height:34px}.el-overlay.fcrm_funnel_edit_block_modal .el-drawer__body .fluentcrm_block_editor_body .el-form-item .el-select__inner,.el-overlay.fcrm_funnel_edit_block_modal .el-drawer__body .fluentcrm_block_editor_body .el-form-item .el-select__wrapper,.el-overlay.fcrm_funnel_edit_block_modal .el-drawer__body .fluentcrm_block_editor_body .el-form-item .el-textarea__inner,.el-overlay.fcrm_funnel_edit_block_modal .el-drawer__body .fluentcrm_block_editor_body .el-form-item .el-textarea__wrapper,.el-overlay.fcrm_funnel_edit_block_modal .el-drawer__body .fluentcrm_block_editor_body .el-form-item .el-input__inner,.el-overlay.fcrm_funnel_edit_block_modal .el-drawer__body .fluentcrm_block_editor_body .el-form-item .el-input__wrapper{box-shadow:0 1px 2px #0a0d1408;border:1px solid var(--fc-primary-border);border-radius:8px}.el-overlay.fcrm_funnel_edit_block_modal .el-drawer__body .fluentcrm_block_editor_body .el-form-item .el-select__inner.is-focused,.el-overlay.fcrm_funnel_edit_block_modal .el-drawer__body .fluentcrm_block_editor_body .el-form-item .el-select__inner.is-focus,.el-overlay.fcrm_funnel_edit_block_modal .el-drawer__body .fluentcrm_block_editor_body .el-form-item .el-select__wrapper.is-focused,.el-overlay.fcrm_funnel_edit_block_modal .el-drawer__body .fluentcrm_block_editor_body .el-form-item .el-select__wrapper.is-focus,.el-overlay.fcrm_funnel_edit_block_modal .el-drawer__body .fluentcrm_block_editor_body .el-form-item .el-textarea__inner.is-focused,.el-overlay.fcrm_funnel_edit_block_modal .el-drawer__body .fluentcrm_block_editor_body .el-form-item .el-textarea__inner.is-focus,.el-overlay.fcrm_funnel_edit_block_modal .el-drawer__body .fluentcrm_block_editor_body .el-form-item .el-textarea__wrapper.is-focused,.el-overlay.fcrm_funnel_edit_block_modal .el-drawer__body .fluentcrm_block_editor_body .el-form-item .el-textarea__wrapper.is-focus,.el-overlay.fcrm_funnel_edit_block_modal .el-drawer__body .fluentcrm_block_editor_body .el-form-item .el-input__inner.is-focused,.el-overlay.fcrm_funnel_edit_block_modal .el-drawer__body .fluentcrm_block_editor_body .el-form-item .el-input__inner.is-focus,.el-overlay.fcrm_funnel_edit_block_modal .el-drawer__body .fluentcrm_block_editor_body .el-form-item .el-input__wrapper.is-focused,.el-overlay.fcrm_funnel_edit_block_modal .el-drawer__body .fluentcrm_block_editor_body .el-form-item .el-input__wrapper.is-focus{border-color:var(--fc-primary-text)!important;box-shadow:0 1px 2px #0a0d1408!important;outline:none}.el-overlay.fcrm_funnel_edit_block_modal .el-drawer .email_composer_wrapper{width:100%}.el-overlay.fcrm_funnel_edit_block_modal .el-drawer .email_composer_wrapper .fluentcrm_visual_editor{margin:0}.fcrm_funnel_editor_fields_wrapper .fcrm_funnel_editor_field .el-checkbox.is-checked .el-checkbox__input .el-checkbox__inner{background:var(--fc-deep-bg);border-color:var(--fc-deep-bg)}.fcrm_funnel_editor_fields_wrapper .fcrm_funnel_editor_field .el-checkbox__inner{border-radius:4px;width:16px;height:16px}.fcrm_funnel_editor_fields_wrapper .fcrm_funnel_editor_field .el-checkbox__original{margin:0}.fcrm_funnel_editor_fields_wrapper .fcrm_funnel_editor_field .el-checkbox__label{color:var(--fc-primary-text);font-weight:400;font-size:14px;line-height:20px;white-space:wrap}.fcrm_funnel_editor_fields_wrapper .fcrm_funnel_editor_field .fc_rich_container{padding:0;margin-top:10px}.fcrm_funnel_editor_fields_wrapper .fcrm_funnel_editor_field .fc_rich_container .fc_rich_wrap .fc_rich_filter{background:none;border:1px solid var(--fc-primary-border);padding:12px;border-radius:12px}.fcrm_funnel_editor_fields_wrapper .fcrm_funnel_editor_field .fc_rich_container .fc_rich_wrap .fc_rich_filter .fc_table{margin-bottom:16px}.fcrm_funnel_editor_footer{display:flex;align-items:center;justify-content:flex-end}.fcrm_trigger_prebuild_template_list{display:grid;grid-template-columns:repeat(auto-fill,minmax(200px,1fr));gap:16px}.fcrm_trigger_prebuild_template_list--section-title{margin:0 0 16px;color:var(--fc-primary-text);font-weight:500;font-size:16px;line-height:24px}.fcrm_trigger_prebuild_template_item{border:1px solid var(--fc-primary-border);border-radius:12px;padding:16px;position:relative}.fcrm_trigger_prebuild_template_item:hover{border-color:var(--fc-primary-text)}.fcrm_trigger_prebuild_template_item:hover .fcrm_trigger_prebuild_template_item_overlay{opacity:1;visibility:visible}.fcrm_trigger_prebuild_template_item_icon{width:20px;height:20px;display:flex;align-items:center;justify-content:center;font-size:14px;margin-bottom:8px}.fcrm_trigger_prebuild_template_item_title{color:var(--fc-primary-text);font-weight:500;font-size:14px;line-height:20px;margin:0 0 4px}.fcrm_trigger_prebuild_template_item_description{color:var(--fc-secondary-text);font-weight:400;font-size:12px;line-height:16px;margin:0}.fcrm_trigger_prebuild_template_item .fcrm_pro_badge{position:absolute;top:16px;right:16px}.fcrm_trigger_prebuild_template_item_overlay{background:#2b303b3d;-webkit-backdrop-filter:blur(3px);backdrop-filter:blur(3px);border-radius:10px;position:absolute;z-index:3;left:0;top:0;width:100%;height:100%;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:12px;opacity:0;visibility:hidden;transition:.3s}.fcrm_trigger_prebuild_template_item_overlay .el-button{margin:0;padding:3px 10px}.fcrm_trigger_prebuild_template_item_pro,.fcrm_trigger_prebuild_template_item_pro:hover{border-color:var(--fc-primary-border)}.fcrm_trigger_prebuild_template_item_pro .fcrm_trigger_prebuild_template_item_title,.fcrm_trigger_prebuild_template_item_pro .fcrm_trigger_prebuild_template_item_description,.fcrm_trigger_prebuild_template_item_pro .fcrm_trigger_prebuild_template_item_icon{color:var(--fc-text-muted)}.fcrm_trigger_or_line{position:relative;display:flex;align-items:center;justify-content:center;z-index:1}.fcrm_trigger_or_line:before{content:"";height:1px;width:100%;background:var(--fc-primary-border);position:absolute;top:50%;left:0;transform:translateY(-50%);z-index:-1}.fcrm_trigger_or_line span{color:var(--fc-text-muted);font-weight:500;font-size:11px;line-height:12px;text-transform:uppercase;display:block;z-index:2;background:var(--fc-primary-bg);padding:0 10px}.fcrm_trigger_selection_wrapper{margin:-20px}.fcrm_trigger_selection_wrapper .el-form .fcrm_trigger_selection_inner{display:flex}.fcrm_trigger_selection_wrapper .el-form .fcrm_trigger_selection_internal_label_wrapper{margin-bottom:16px}.fcrm_trigger_selection_wrapper .el-form .fcrm_trigger_selection_internal_label_wrapper .el-input .el-input__prefix .el-input__prefix-inner .icon{display:block}.fcrm_trigger_selection_wrapper .el-form .fcrm_trigger_selection_internal_label_wrapper .el-input .el-input__prefix .el-input__prefix-inner .icon svg{display:block}.fcrm_trigger_selection_wrapper .el-form .fcrm_trigger_selection_header{margin-bottom:16px}.fcrm_trigger_selection_wrapper .el-form .fcrm_trigger_selection_header_title{color:var(--fc-primary-text);font-weight:500;font-size:14px;line-height:20px;margin:0}.fcrm_trigger_selection_wrapper .el-form .fcrm_trigger_selection_sidebar{width:200px;border-right:1px solid var(--fc-primary-border);display:flex;flex-direction:column}.fcrm_trigger_selection_wrapper .el-form .fcrm_trigger_selection_sidebar .go-back-button{display:flex;align-items:center;gap:6px;padding:10px 20px;color:var(--fc-secondary-text);cursor:pointer;margin-top:10px}.fcrm_trigger_selection_wrapper .el-form .fcrm_trigger_selection_sidebar .go-back-button:hover{color:var(--fc-primary-text)}.fcrm_trigger_selection_wrapper .el-form .fcrm_trigger_selection_sidebar .go-back-button .icon svg{display:block;width:18px;height:18px}.fcrm_trigger_selection_wrapper .el-form .fcrm_trigger_selection_sidebar .el-menu{background:none;border:none;padding:10px 10px 10px 8px}.fcrm_trigger_selection_wrapper .el-form .fcrm_trigger_selection_sidebar .el-menu-item{display:flex;align-items:center;height:auto;color:var(--fc-secondary-text);font-weight:500;font-size:14px;line-height:20px;gap:8px;padding:8px 12px;margin-bottom:4px;border-radius:8px;background:none}.fcrm_trigger_selection_wrapper .el-form .fcrm_trigger_selection_sidebar .el-menu-item.is-active,.fcrm_trigger_selection_wrapper .el-form .fcrm_trigger_selection_sidebar .el-menu-item:hover{background:var(--fc-secondary-bg);color:var(--fc-primary-text)}.fcrm_trigger_selection_wrapper .el-form .fcrm_trigger_selection_sidebar .el-menu-item:last-child{margin-bottom:0}.fcrm_trigger_selection_wrapper .el-form .fcrm_trigger_selection_sidebar .el-menu-item .fcrm_trigger_label{max-width:160px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.fcrm_trigger_selection_wrapper .el-form .fcrm_trigger_selection_sidebar .el-menu-item .fc_trigger_icon{display:block;margin:0;width:20px}.fcrm_trigger_selection_wrapper .el-form .fcrm_trigger_selection_sidebar .el-menu-item .fc_trigger_icon span{display:block;width:20px;height:20px}.fcrm_trigger_selection_wrapper .el-form .fcrm_trigger_selection_sidebar--footer{margin-top:auto;padding:10px;background:var(--fc-primary-bg)}.fcrm_trigger_selection_wrapper .el-form .fcrm_trigger_selection_sidebar--footer .pre-built-triggers-btn{width:100%}.fcrm_trigger_selection_wrapper .el-form .fcrm_trigger_selection_main{flex:1;padding:20px}.fcrm_trigger_selection_list{max-height:500px;overflow-x:hidden;scrollbar-width:thin}.fcrm_trigger_selection_item{display:flex;align-items:center;gap:12px;padding:8px 70px 8px 8px;border-radius:10px;cursor:pointer;margin-bottom:4px;position:relative;transition:.2s;border:1px solid transparent;overflow:hidden}.fcrm_trigger_selection_item:last-child{margin-bottom:0}.fcrm_trigger_selection_item:hover,.fcrm_trigger_selection_item.fc_trigger_selected{background:var(--fc-secondary-bg)}.fcrm_trigger_selection_item.fc_trigger_selected{border-color:var(--fc-primary-text)}.fcrm_trigger_selection_item_icon{width:32px;height:32px;box-shadow:0 1px 2px #0a0d1408;border:1px solid var(--fc-primary-border);border-radius:50%;display:flex;align-items:center;justify-content:center;color:var(--fc-secondary-text);background:var(--fc-primary-bg);flex:none;overflow:hidden}.fcrm_trigger_selection_item_icon .icon svg{display:block}.fcrm_trigger_selection_item_title{color:var(--fc-secondary-text);font-weight:500;font-size:14px;line-height:20px;margin:0 0 4px!important}.fcrm_trigger_selection_item_description{color:var(--fc-secondary-text);font-weight:400;font-size:12px;line-height:16px;margin:0}.fcrm_trigger_selection_item .fcrm_pro_badge{position:absolute;right:8px;top:50%;transform:translateY(-50%)}.fcrm_trigger_selection_item.fc_trigger_selected .fcrm_trigger_selection__pro{right:12px;opacity:1}.fcrm_trigger_selection__pro{position:absolute;top:50%;transform:translateY(-50%);right:-160px;opacity:0;transition:.3s;-webkit-transition:.3s}.fcrm_funnel_blocks_wrapper_item_inner{padding:0 20px 20px}.fcrm_funnel_blocks_wrapper_item_inner .fcrm_funnel_blocks_item_category{border-bottom:1px solid var(--fc-primary-border);padding-bottom:16px;margin-bottom:16px}.fcrm_funnel_blocks_wrapper_item_inner .fcrm_funnel_blocks_item_category:last-child{border-bottom:none;margin-bottom:0;padding-bottom:0}.fcrm_funnel_blocks_wrapper_item_inner .fcrm_funnel_blocks_item_category_title{margin:0;cursor:pointer;display:flex;align-items:center;font-weight:500;font-size:12px;line-height:16px;gap:8px;color:var(--fc-text-muted)}.fcrm_funnel_blocks_wrapper_item_inner .fcrm_funnel_blocks_item_category_title .el-icon{transition:.3s}.fcrm_funnel_blocks_wrapper_item_inner .fcrm_funnel_blocks_item_category_title:not(.is_collapsed) .el-icon{transform:rotate(180deg)}.fcrm_funnel_blocks_wrapper_item_inner .fcrm_funnel_blocks_item_category_list{margin-top:4px}.fcrm_funnel_blocks_wrapper_item .fcrm_funnel_blocks_wrapper_item_title{margin:0 0 16px;color:var(--fc-primary-text);font-weight:500;font-size:14px;line-height:20px}.fcrm_funnel_conditions h3{margin:0 0 16px;color:var(--fc-primary-text)}.fcrm_funnel_conditions .el-form-item .el-form-item__content p{margin:2px 0 0;font-size:12px;line-height:16px}.fcrm_funnel_conditions .fcrm_funnel_condition_field .el-checkbox__label{line-height:1.4}.fcrm_funnel_conditions .fcrm_funnel_condition_field:last-child .el-form-item{margin-bottom:0}.fcrm_funnel_email_card{border-bottom:1px solid var(--fc-primary-border);padding-bottom:16px;margin-bottom:16px;width:100%;display:flex;align-items:flex-start;justify-content:space-between}.fcrm_funnel_email_card:last-child{padding-bottom:0;margin-bottom:0;border-bottom:none}.fcrm_funnel_email_card .fcrm_funnel_email_card--title{margin:0;font-weight:500;font-size:16px;line-height:24px;cursor:pointer}.fcrm_funnel_email_card .fcrm_funnel_email_card--meta{display:flex;align-items:center;gap:8px;margin-top:8px}.fcrm_funnel_email_card .fcrm_quick_stats_label{color:var(--fc-secondary-text);font-weight:500;font-size:14px;line-height:20px;margin-bottom:12px}.fcrm_funnel_email_reports h3{color:var(--fc-primary-text)}.fcrm_funnel_subscribers_report .fcrm_page_header_top_nav_wrapper{padding-top:10px;padding-bottom:10px;margin-bottom:24px}.fcrm_funnel_subscribers_report .fcrm_page_header_breadcrumb{min-width:0;flex:1 1 auto}.fcrm_funnel_subscribers_report .fcrm_funnel_report_breadcrumb .el-breadcrumb__item:last-child .el-breadcrumb__separator{display:none}.fcrm_funnel_subscribers_report .fcrm_table_wrapper .fcrm_table_header_inner_actions .el-select{width:200px}.fcrm_funnel_matrics_wrapper{background:var(--fc-primary-bg);border-radius:8px}.fcrm_funnel_matrics_wrapper .fcrm_funnel_matrics_header{border-bottom:1px solid var(--fc-primary-border);padding:14px 20px}.fcrm_funnel_matrics_wrapper .fcrm_funnel_matrics_body{padding:20px}.fcrm_funnel_matrics_wrapper .fcrm_funnel_matrics_body .fcrm_funnel_matrics_stat{text-align:center;color:var(--fc-secondary-text);font-size:12px;line-height:16px;margin:10px 0 0;font-weight:500}.fcrm_funnel_matrics_wrapper .fcrm_funnel_matrics_body .fcrm_funnel_matrics_stat .count{display:inline-block;background:var(--fc-primary-border);color:var(--fc-deep-bg);border-radius:4px;font-size:11px;line-height:12px;padding:2px;font-weight:500}.fcrm_value_property_group{width:100%}.fcrm_value_property_group table{width:100%;border:1px solid var(--fc-primary-border);border-radius:8px;border-spacing:0;overflow:hidden}.fcrm_value_property_group table thead tr th{background:var(--fc-secondary-bg);border-bottom:1px solid var(--fc-primary-border);border-right:1px solid var(--fc-primary-border);text-align:left;color:var(--fc-secondary-text);font-weight:500;font-size:14px;line-height:20px;padding:8px 12px}.fcrm_value_property_group table thead tr th:last-child{border-right:none}.fcrm_value_property_group table tbody tr td{padding:14px 12px;border-bottom:1px solid var(--fc-primary-border);border-right:1px solid var(--fc-primary-border)}.fcrm_value_property_group table tbody tr td:first-child{padding-left:16px}.fcrm_value_property_group table tbody tr td:last-child{border-right:none}.fcrm_value_property_group table tbody tr:last-child td{border-bottom:none}.fcrm_value_property_group .fcrm_value_property_group_footer{display:flex;align-items:center;gap:8px;margin-top:8px}.fcrm_coupon_settings{width:100%}.fcrm_coupon_settings .fcrm_coupon_config_settings_title{margin:0 0 16px;background:var(--fc-secondary-bg);color:var(--fc-primary-text);font-weight:500;line-height:20px;font-size:16px;padding:20px;border-radius:8px;text-align:center;display:flex;flex-direction:column;align-items:center;justify-content:center;width:100%;gap:8px}.fcrm_coupon_settings .fcrm_coupon_config_settings_title .fc-item-copier-input .el-input__wrapper{background:var(--fc-primary-bg);border-radius:8px 0 0 8px!important}.fcrm_coupon_settings .fcrm_coupon_config_settings_title .fc-item-copier-input .el-input__wrapper input{background:none!important}.fcrm_coupon_settings .fcrm_coupon_config_settings .el-form-item{margin-bottom:16px}.fcrm_coupon_settings .fcrm_coupon_config_settings .el-form-item:last-child{margin-bottom:0}.fcrm_coupon_settings .fcrm_coupon_config_settings .el-form-item .el-form-item__content p{margin:0}.fcrm_coupon_settings .el-tabs{margin-top:24px;border:none;background:none}.fcrm_coupon_settings .el-tabs__header{background:none;border-bottom:1px solid var(--fc-primary-border)}.fcrm_coupon_settings .el-tabs__header .el-tabs__nav-wrap{margin:0}.fcrm_coupon_settings .el-tabs__header .el-tabs__nav{gap:24px}.fcrm_coupon_settings .el-tabs__header .el-tabs__item{border:none;border-bottom:2px solid transparent;padding:0 0 12px!important;height:auto;color:var(--fc-secondary-text);font-weight:500;font-size:12px;line-height:16px;margin:0;background:none}.fcrm_coupon_settings .el-tabs__header .el-tabs__item:first-child{margin:0}.fcrm_coupon_settings .el-tabs__header .el-tabs__item.is-active{border-bottom-color:var(--fc-primary-text);color:var(--fc-primary-text)}.fcrm_coupon_settings .el-tabs__content{padding:20px 0 0}.fcrm_coupon_settings .el-tabs__content .el-tab-pane .el-form-item__label .el-tooltip__trigger{color:var(--fc-text-muted)}.fcrm_coupon_settings .el-tabs__content .el-tab-pane .el-form-item .el-radio-group{gap:16px}.fcrm_coupon_settings .el-tabs__content .el-tab-pane>.el-form-item{margin-bottom:16px}.fcrm_coupon_settings .el-tabs__content .el-tab-pane>.el-form-item:last-child{margin-bottom:0}.fcrm_coupon_settings .el-tabs__content .el-tab-pane .row-section-title{margin:0 0 8px;color:var(--fc-primary-text);font-weight:500;font-size:16px;line-height:20px;padding:16px 0 0;border-top:1px solid var(--fc-primary-border)}.fcrm_coupon_settings .el-tabs__content .el-tab-pane .fc_info p{margin:0}.fcrm_upload_file_box .fcrm_upload_file_box_label{margin:0 0 4px;padding:0;color:var(--fc-primary-text);font-size:14px;line-height:20px;font-weight:500}.fcrm_upload_file_box .el-upload-dragger{height:180px;display:flex;flex-direction:column;justify-content:center;align-items:center;gap:20px;padding:20px}.fcrm_upload_file_box .el-upload-dragger:hover{border-color:var(--fc-primary-text)}.fcrm_upload_file_box .el-upload-dragger .upload-icon{display:block;color:var(--fc-secondary-text)}.fcrm_upload_file_box .el-upload-dragger .upload-icon svg{display:block}.fcrm_upload_file_box .el-upload-dragger .el-upload__text{color:var(--fc-primary-text);font-size:14px;line-height:20px;font-weight:500}.fcrm_upload_file_box .el-upload-dragger .el-upload__text em{color:var(--fc-primary-text)}.fcrm_upload_file_box .el-upload-list__item{margin:0;border:1px solid var(--fc-primary-border);border-radius:8px;padding:16px 14px}.fcrm_upload_file_box .el-upload-list__item:hover .el-progress__text{display:block}.fcrm_upload_file_box .el-upload-list__item-file-name{color:var(--fc-primary-text);font-weight:500;font-size:14px;line-height:20px}.fcrm_upload_file_box .el-upload-list__item-name{padding:0}.fcrm_upload_file_box .el-upload-list__item-name .el-icon{font-size:20px}.fcrm_upload_file_box .el-upload-list__item .el-icon--close{right:16px;top:16px;transform:translate(0)}.fcrm_upload_file_box .el-upload-list__item .el-icon--close:hover{color:var(--fc-primary-text)}.fcrm_upload_file_box .el-upload-list__item-status-label{right:16px;top:16px;transform:translate(0);height:auto}.fcrm_upload_file_box .el-upload-list__item .el-progress{position:relative;top:0;margin-top:10px;display:flex;align-items:center}.fcrm_upload_file_box .el-upload-list__item .el-progress-bar__outer{background:var(--fc-secondary-bg);height:6px!important}.fcrm_upload_file_box .el-upload-list__item .el-progress-bar__inner{background:var(--fc-deep-bg)}.fcrm_upload_file_box .el-upload-list__item .el-progress__text{position:relative;margin:0;top:0;text-align:right}.fcrm_funnel_import_page .el-input-number .el-input-number__decrease,.fcrm_funnel_import_page .el-input-number .el-input-number__increase{background:transparent;border:none;color:var(--fc-secondary-text)}.fcrm_funnel_import_page .el-input-number .el-input-number__decrease:hover~.el-input:not(.is-disabled) .el-input__wrapper,.fcrm_funnel_import_page .el-input-number .el-input-number__increase:hover~.el-input:not(.is-disabled) .el-input__wrapper{box-shadow:none;border-color:var(--fc-primary-text)}.fcrm_funnel_import_page .fcrm_schedule_date_time .el-form-item__content{flex-direction:column;align-items:flex-start}.fcrm_funnel_import_page .fcrm_schedule_date_time .el-form-item__content p{margin:0;font-size:14px;line-height:20px;color:var(--fc-primary-text)}.fcrm_funnel_import_page .el-date-editor--datetime{padding:0}.fcrm_funnel_import_page .el-checkbox-group{display:flex;flex-wrap:wrap;gap:12px}.fcrm_funnel_import_page .el-checkbox-group .el-checkbox{margin:0}.fcrm_funnel_import_page .fcrm_funnel_top_nav_wrapper{padding-top:18px;padding-bottom:18px;margin-bottom:24px}.fcrm_funnel_import_page .fcrm_funnel_import_upload_step{background:var(--fc-primary-bg);border-radius:8px}.fcrm_funnel_import_page .fcrm_funnel_import_body .fcrm_funnel_import_root_editor{background:var(--fc-primary-bg);border-radius:8px;display:flex;flex-direction:column;gap:20px;padding:20px}.fcrm_funnel_import_page .fcrm_funnel_import_body .fcrm_funnel_import_root_editor .fluentcrm_funnel_header{padding:10px 20px;margin:-20px -20px 20px}.fcrm_funnel_import_page .fcrm_funnel_import_body .fcrm_funnel_import_root_editor .fluentcrm_funnel_header .fc_funel_head_title p{margin-top:4px}.fcrm_funnel_import_page .fcrm_funnel_import_body .fcrm_funnel_import_root_editor .fc_funnel_head_action .close-field-editor-btn{display:none}.fcrm_funnel_import_page .fcrm_funnel_import_body .fcrm_funnel_import_root_editor .fcrm_funnel_editor_after_header_slot{margin-bottom:20px}.fcrm_funnel_import_page .fcrm_funnel_import_body .fcrm_pro_modal_body{border-radius:8px;padding:20px}.fcrm_funnel_import_card--header{padding:10px 20px;display:flex;align-items:center;justify-content:space-between;border-bottom:1px solid var(--fc-primary-border)}.fcrm_funnel_import_card--header-title{margin:0;color:var(--fc-primary-text);font-weight:500;font-size:14px;line-height:20px}.fcrm_funnel_import_card--header-description{color:var(--fc-secondary-text);font-weight:400;font-size:12px;line-height:16px;margin:4px 0 0}.fcrm_funnel_import_card--body{padding:20px}@media (max-width: 500px){.fcrm_trigger_selection_wrapper .el-form .fcrm_trigger_selection_inner{flex-direction:column}.fcrm_trigger_selection_wrapper .el-form .fcrm_trigger_selection_sidebar{border:none;width:100%}.fcrm_trigger_selection_wrapper .el-form .fcrm_trigger_selection_sidebar .el-menu{display:flex;flex-wrap:wrap;gap:10px;align-items:flex-start;justify-content:flex-start;height:auto;min-height:auto;max-height:inherit}}.fc_funnel_root .fluentcrm_body{background-color:var(--fc-secondary-bg)}.fluentcrm_blocks_wrapper{box-sizing:border-box;z-index:2;width:100%;min-height:90vh;padding:20px 20px 40px}.fluentcrm_blocks_wrapper>h3{padding:0 20px;font-size:20px;font-weight:700;color:var(--fc-primary-text)}.fluentcrm_blocks_wrapper .fluentcrm_blocks{list-style:none;padding:0;min-height:450px;text-align:center}.fluentcrm_blocks_wrapper .fluentcrm_blocks .fluentcrm_blockin{width:auto;display:inline-block;margin:10px auto 0!important;text-align:center;-moz-transition:.2s;-ms-transition:.2s;-o-transition:.2s;background:var(--fc-primary-bg);border-style:solid;border-width:1px;border-color:transparent;border-radius:8px;box-shadow:0 1px 1px #b5b9bf4d;padding:0 20px 10px;transition:all .3s ease}.fluentcrm_blocks_wrapper .fluentcrm_blocks .fluentcrm_blockin:hover{border:1px solid var(--fc-deep-bg);box-shadow:0 16px 16px #0000000a}.fluentcrm_blocks_wrapper .fluentcrm_block_end_this_funnel .fluentcrm_blockin{background-color:#fff3dc!important}.fluentcrm_blocks_wrapper .fluentcrm_block_end_this_funnel .fluentcrm_blockin .fluentcrm_block_title{color:#0e121b}.fluentcrm_blocks_wrapper .fluentcrm_block_end_this_funnel .fluentcrm_blockin .fluentcrm_block_title .fc-icon-end_funnel{background:#fff3dc;color:#0e121b}.fluentcrm_blocks_wrapper .fluentcrm_block_end_this_funnel .fluentcrm_blockin .fluentcrm_block_desc{color:#0e121b}.fluentcrm_blocks_wrapper .fluentcrm_block_end_this_funnel .fluentcrm_blockin span.el-tag,.fluentcrm_blocks_wrapper .fluentcrm_block_end_this_funnel .fluentcrm_blockin span.el-tag *{color:var(--fc-deep-bg)!important}.fluentcrm_blocks_wrapper .fluentcrm_block_end_this_funnel .fluentcrm_blockin .el-tag--plain.el-tag--danger{background-color:var(--fc-primary-bg)!important;border-color:var(--fc-error-bg)!important;color:var(--fc-error)!important}.fluentcrm_blocks_wrapper .fluentcrm_block_end_this_funnel .fluentcrm_blockin .el-tag--plain.el-tag--danger *{color:var(--fc-error)!important}.fluentcrm_blocks_wrapper .fluentcrm_block_send_custom_email .fluentcrm_blockin{background-color:#e9fbfb!important}.fluentcrm_blocks_wrapper .fluentcrm_block_send_custom_email .fluentcrm_blockin .fluentcrm_block_desc,.fluentcrm_blocks_wrapper .fluentcrm_block_send_custom_email .fluentcrm_blockin .fluentcrm_block_title{color:#0e121b!important}.fluentcrm_blocks_wrapper .fluentcrm_block_fluentcrm_wait_times .fluentcrm_blockin{background-color:var(--fc-primary-bg)!important}.fluentcrm_blocks_wrapper .fluentcrm_block_add_contact_to_tag .fluentcrm_blockin{background-color:var(--fc-text-link)}.fluentcrm_blocks_wrapper .fluentcrm_block_add_contact_to_tag .fluentcrm_blockin .fluentcrm_block_desc{color:var(--fc-secondary-text)}.fluentcrm_funnel_header{margin:0 0 20px;border-bottom:1px solid var(--fc-light-bg);padding-top:10px;padding-bottom:10px;position:relative}.fluentcrm_funnel_header h3{margin:0}.fluentcrm_funnel_header p{margin:10px 0 0}.fluentcrm_funnel_header .close-drawer{position:absolute;right:10px;top:50%;transform:translateY(-50%);font-size:20px;cursor:pointer;width:50px;height:50px;text-align:center;line-height:50px;transition:.3s}.fluentcrm_funnel_header .close-drawer:hover{color:var(--fc-error)}.el-overlay.fcrm_funnel_block_modal .fcrm_schedule_date_time .el-form-item__content{flex-direction:column;align-items:flex-start}.el-overlay.fcrm_funnel_block_modal .fcrm_schedule_date_time .el-form-item__content p{margin:0;font-size:14px;line-height:20px;color:var(--fc-primary-text)}.el-overlay.fcrm_funnel_block_modal .el-date-editor--datetime{padding:0}.el-overlay.fcrm_funnel_block_modal .el-checkbox-group{display:flex;flex-wrap:wrap;gap:12px}.el-overlay.fcrm_funnel_block_modal .el-checkbox-group .el-checkbox{margin:0}.el-overlay.fcrm_funnel_block_modal .el-input-number .el-input-number__decrease,.el-overlay.fcrm_funnel_block_modal .el-input-number .el-input-number__increase{background:transparent;border:none;color:var(--fc-secondary-text)}.el-overlay.fcrm_funnel_block_modal .el-input-number .el-input-number__decrease:hover~.el-input:not(.is-disabled) .el-input__wrapper,.el-overlay.fcrm_funnel_block_modal .el-input-number .el-input-number__increase:hover~.el-input:not(.is-disabled) .el-input__wrapper{box-shadow:none;border-color:var(--fc-primary-text)}.el-drawer__wrapper .el-drawer__body{background-repeat:repeat;filter:alpha(opacity=1);background-size:30px 30px}.el-drawer__wrapper .el-drawer__body .fluentcrm_block_editor_body .fc_funnel_editor>div{margin-bottom:20px}.el-drawer__wrapper .el-drawer__body .fluentcrm_block_editor_body .fc_funnel_editor>div:empty{margin-bottom:0!important;padding:0!important}.el-drawer__wrapper .el-drawer__body .fluentcrm_block_editor_body .fc_funnel_editor>div:last-child{margin-bottom:0}.el-drawer__wrapper .el-drawer__body .fluentcrm_block_editor_body .fc_funnel_editor .el-checkbox .el-checkbox__label{word-wrap:normal;vertical-align:text-top;white-space:break-spaces}.el-drawer__wrapper .el-drawer__body .fluentcrm_block_editor_body .fc_funnel_editor .fc_2col_inline .el-checkbox .el-checkbox__label{white-space:normal!important}.el-drawer__wrapper .el-drawer__body .fluentcrm_block_editor_body .fc_funnel_editor .fc_rich_container{padding-left:0;padding-bottom:0}.el-drawer__wrapper .el-drawer__body .fluentcrm_block_editor_body .fc_funnel_editor>div>.el-form-item{margin-bottom:0}.el-drawer__wrapper .el-drawer__body .fluentcrm_block_editor_body .fc_funnel_editor>div>.el-form-item .el-form-item__content .el-date-editor.el-input{width:100%}.el-drawer__wrapper .el-drawer__body .fluentcrm_block_editor_body .fc_funnel_editor .fc_coupon_settings .el-form-item .el-checkbox .el-checkbox__label{white-space:normal}.el-drawer__wrapper .el-drawer__body .fluentcrm_block_editor_body .fc_funnel_conditions>h3{margin-top:6px}.el-drawer__wrapper .el-drawer__body .fluentcrm_block_editor_body .fc_funnel_conditions>div .el-form-item .el-form-item__content>p{margin:5px 0 0}.el-drawer__wrapper .el-drawer__body .fluentcrm_block_editor_body .fc_funnel_conditions>div .el-form-item .el-form-item__content .el-checkbox .el-checkbox__label{white-space:break-spaces;word-wrap:normal;vertical-align:text-top}.el-drawer__wrapper .fluentcrm_block_editor_body{padding:0 20px}.fcrm_funnel_editor_footer{padding:14px 20px;border-radius:0;background:var(--fc-primary-bg);overflow:hidden;display:flex;align-items:center;justify-content:space-between;position:sticky;gap:8px;bottom:0;z-index:2}.fcrm_funnel_editor_footer .el-button{margin:0}.fcrm_funnel_editor_footer .fluentcrm_pull_right,.fcrm_funnel_editor_footer .fluentcrm_pull_left{display:flex;align-items:center;gap:8px}.fcrm_funnel_editor_footer .fluentcrm_pull_right .el-button,.fcrm_funnel_editor_footer .fluentcrm_pull_left .el-button{margin:0}.fluentcrm_block{-webkit-user-select:none;user-select:none;cursor:pointer;padding:19px 10px;border:1px solid transparent;transition-property:box-shadow,height;transition-duration:.2s;transition-timing-function:cubic-bezier(.05,.03,.35,1);border-radius:5px;box-shadow:0 0 30px #16214a00;box-sizing:border-box;display:table;position:relative;margin:0 auto}.fluentcrm_block:hover .fc_block_controls{display:inline-block}.fluentcrm_block .fc_block_controls{position:absolute;top:50%;transform:translateY(-50%);z-index:9;display:none}.fluentcrm_block .fluentcrm_block_title{position:relative;margin:0 0 5px!important;padding:0!important;display:flex;flex-direction:column;justify-content:center;align-items:center;color:#0e121b;font-size:16px;font-weight:600;line-height:1.28}.fluentcrm_block .fluentcrm_block_title i{font-size:14px;color:var(--fc-secondary-text);background:var(--fc-primary-bg);border:2px solid var(--fc-secondary-bg);border-radius:100px;display:flex;justify-content:center;align-items:center;height:30px;width:30px;margin-top:-15px;margin-bottom:0;z-index:2}.fluentcrm_block .fluentcrm_block_title .fc-icon-writing{font-size:20px;left:-38px}.fluentcrm_block .fluentcrm_block_desc{color:#0e121b;font-size:14px;font-weight:400;line-height:1.28;max-width:310px;width:100%;margin:5px auto auto;word-break:break-word}.fluentcrm_block:first-child:after{top:25px}.fluentcrm_block:last-child:after{bottom:30px}.fluentcrm_float_label{display:table-cell;vertical-align:middle;width:50px;padding-right:10px}.fluentcrm_round_block{background:var(--fc-deep-bg);width:30px;height:30px;border-radius:50%;text-align:center;vertical-align:middle;line-height:30px;color:var(--fc-text-inverse);z-index:9;position:absolute;top:23px}.fluentcrm_block_start{display:block;background:var(--fc-secondary-bg);padding:10px 20px;box-sizing:border-box;position:relative}.fluentcrm_block_start h3{margin:0 0 10px;padding:0}.fluentcrm_block_start p{padding:0;margin:0}.fluentcrm_block_add{padding:20px 15px;background:var(--fc-primary-border)}.block_full{width:100%}.fluentcrm_block_editor .fluentcrm_block_editor_header{background:var(--fc-primary-bg);padding:15px 20px}.fluentcrm_block_editor .fluentcrm_block_editor_header h3{margin:0 0 10px}.fluentcrm_block_editor .fluentcrm_block_editor_header p{margin:0}.fluentcrm_block_editor .fluentcrm_block_editor_body{padding:20px}.fc_block_type_benchmark .fluentcrm_blockin{background-color:#e3ffe9!important;border-color:#e3ffe9}.fc_block_type_benchmark.fc_funnel_benchmark_required .fluentcrm_blockin{background-color:#e3ffe9!important}.fluentcrm_block.fluentcrm_block_trigger .fluentcrm_blockin{background-color:#fff3dc!important;border-color:#fff3dc}.fluentcrm_block.fluentcrm_block_trigger .fluentcrm_blockin .fluentcrm_block_title{color:var(--fc-primary-text)}.fluentcrm_block.fluentcrm_block_trigger .fluentcrm_blockin .fluentcrm_block_title i{background:#fff3dc}.fluentcrm_block.fluentcrm_block_trigger .fluentcrm_blockin .fluentcrm_block_desc{color:#0e121b}.block_item_holder:last-child .block_item_add .fc_show_plus:after{content:none}.block_item_add .fc_show_plus{z-index:2;font-size:15px;display:inline-block;background:var(--fc-primary-bg);padding:3px;border-radius:50%;cursor:pointer;position:relative}.block_item_add .fc_show_plus:before{content:"";position:absolute;left:47%;right:50%;height:20px;width:2px;background:var(--fc-primary-text);top:-20px;z-index:-1}.block_item_add .fc_show_plus:after{content:"";position:absolute;left:47%;right:50%;height:20px;width:2px;background:var(--fc-primary-text);bottom:-20px;z-index:0}.block_item_add .fc_show_plus i{z-index:1;position:relative}.block_item_add:hover .fc_show_plus{color:var(--fc-deep-bg)}.block_item_add .fc_plus_text{font-size:12px}span.stats_badge_inline{display:inline-block;padding:4px 7px;border:1px solid var(--fc-text-link);font-size:80%;background:#2225301a;border-radius:3px;line-height:100%;color:var(--fc-deep-bg);height:20px}.fcrm_option_creatable{overflow:hidden;position:relative;display:flex;align-items:flex-start}.fc_pro_ribbon{position:absolute;top:0;right:0;background:var(--fc-deep-bg);color:var(--fc-text-inverse);padding:0 8px;border-radius:0 5px;font-size:12px}.fc_sequence_import .fluentcrm_funnel_header{margin-left:-45px;margin-right:-45px;border-bottom:1px solid var(--fc-primary-border)}.fc_sequence_import .fc_sequence_navigation{text-align:right;background:var(--fc-text-link);margin:0 -45px -30px;padding:20px 45px}.fc_funnel_head_action{display:flex;align-items:center;gap:8px}.fc_funnel_head_action .fc_header_merge_codes>.el-button-group{margin:0}.fc_funnel_head_action .fc_header_merge_codes>.el-button-group .editor-add-shortcode{padding-bottom:6px;border-radius:8px!important}.fc_funnel_head_action .el-button-group{margin:0}.fcrm_automation_table_header_bulk_actions .fcrm_bulk_action_bar{padding:0}.ff_funnel_badge{background:var(--fc-primary-text);color:var(--fc-text-inverse);font-size:10px;padding:0 10px;display:inline-block;margin-top:1px;border-radius:15px;vertical-align:text-top;text-transform:capitalize}.ff_funnel_badge.ff_funnel_badge-trigger{background:var(--fc-deep-bg)}.fc_choice_block{margin-bottom:20px}.fc_choice_block:nth-child(3n+1){clear:both}.fc_choice_block .fc_choice_card{width:auto;display:flex;flex-direction:column;justify-content:center;align-items:center;margin:0;text-align:center;-moz-transition:.2s;-ms-transition:.2s;-o-transition:.2s;cursor:pointer;border:1px solid var(--fc-light-bg);border-radius:8px;box-shadow:0 1px 1px #b5b9bf4d;padding:15px 20px;transition:all .3s ease;position:relative;background:var(--fc-secondary-bg)}.fc_choice_block .fc_choice_card>*{word-break:keep-all}.fc_choice_block .fc_choice_card:hover{border:1px solid var(--fc-deep-bg);box-shadow:0 16px 16px #0000000a}.fc_choice_block .fc_choice_card:hover .fc_choice_card_overlay{opacity:1}.fc_choice_block .fc_choice_card h3{position:relative;margin:0 0 6px;padding:0;display:flex;flex-direction:column;justify-content:center;align-items:center;color:var(--fc-primary-text);font-size:16px;font-weight:600;line-height:1.28}.fc_choice_block .fc_choice_card p{color:var(--fc-text-muted);font-size:14px;font-weight:400;line-height:1.28;max-width:310px;width:100%;margin:5px 0 0;word-break:break-word}.fc_choice_block .fc_choice_card .fc_choice_card_overlay{position:absolute;left:0;top:0;width:100%;height:100%;z-index:2;display:flex;align-items:center;justify-content:center;gap:8px;background:#ffffff8c;cursor:auto;border-radius:8px;opacity:0;transition:.3s}.fc_choice_block .fc_choice_card .fc_choice_card_overlay .el-button{margin:0}.fc_choice_block .fc_choice_card .fc_choice_card_overlay .preview-btn{color:var(--fc-text-inverse);background-color:var(--fc-text-muted);border-color:var(--fc-text-muted);font-size:12px;padding:6px 15px;border-radius:4px}.fc_choice_block .fc_choice_card.fc_trigger_selected{background:var(--fc-deep-bg);box-shadow:0 16px 16px #0000000a;border-color:var(--fc-deep-bg)}.fc_choice_block .fc_choice_card.fc_trigger_selected *{color:var(--fc-text-inverse)}.fc_choice_block .fc_choice_card.fc_trigger_selected h3 i{color:var(--fc-deep-bg)}.fc_choice_block .fc_choice_card.fc_trigger_selected .fc_pro_ribbon{background:var(--fc-secondary-bg);color:var(--fc-deep-bg)}.fc_choice_block .fc_choice_card img{position:absolute;left:2px;top:-2px;opacity:.5;width:20px}.fc_choice_block .fc_choice_card i{font-size:16px;color:var(--fc-secondary-text);background:var(--fc-primary-bg);border:2px solid var(--fc-secondary-bg);border-radius:100px;display:flex;justify-content:center;align-items:center;height:35px;width:35px;margin-bottom:4px;z-index:2;transition:.3s}.fc_choice_block .fc_choice_card .fc-icon-writing{font-size:28px;left:-38px}.fc_choice_block .fc_choice_card.fc_choice_card_create_from_scratch{background:none;border:1px dashed var(--fc-light-bg);box-shadow:none}.fc_choice_block .fc_choice_card.fc_choice_card_create_from_scratch:hover{border-color:#7e61e6;background:#7e61e61a}.fc_choice_block .fc_choice_card.fc_choice_card_create_from_scratch:hover h3 i{border-color:#7e61e633;background:#7e61e680;color:var(--fc-text-inverse)}.fcrm_back_to_template{display:flex;align-items:center;margin-bottom:15px;color:var(--fc-primary-text)}.fcrm_back_to_template h3{display:flex;align-items:center;gap:8px;font-size:16px;margin:0;cursor:pointer}.fcrm_back_to_template h3:hover .icon i:first-child{margin-left:-16px;transition:.5s}.fcrm_back_to_template h3 .icon{display:flex;flex:none;position:relative;width:16px;height:16px;overflow:hidden}.fcrm_back_to_template h3 .icon i{color:var(--fc-secondary-text)}.fcrm_back_to_template h3 .icon i:last-child{color:var(--fc-primary-text)}.fc_trigger_dialog_wrap .el-dialog__header{padding:12px 20px;border:none}.fc_trigger_dialog_wrap .fc_a_cat_selection_wrapper .el-row{display:flex}.fc_trigger_dialog_wrap .fc_a_cat_selection_wrapper .fc_trigger_left_col .fc_trigger_selectors{height:100%;border-right:none;border-radius:4px;padding:8px}.fc_trigger_dialog_wrap .fc_a_cat_selection_wrapper .fc_trigger_left_col .fc_trigger_selectors .el-menu-item{border-radius:4px}.fc_trigger_dialog_wrap .fc_a_cat_selection_wrapper .fc_trigger_left_col .fc_trigger_selectors .el-menu-item+.el-menu-item{margin-top:4px}.fc_trigger_dialog_wrap .fc_a_cat_selection_wrapper .fc_trigger_left_col .fc_trigger_selectors .el-menu-item.is-active{background-color:var(--fc-deep-bg)!important}.fc_trigger_dialog_wrap .fc_a_cat_selection_wrapper .fc_choice_header{border-bottom:1px solid var(--fc-primary-border);margin-bottom:20px;padding-bottom:10px}.fc_trigger_dialog_wrap .fc_a_cat_selection_wrapper .fc_choice_header p{padding:0;font-size:15px;margin:10px 0}.fc_trigger_dialog_wrap .fc_a_cat_selection_wrapper .fc_choice_header h2{color:var(--fc-primary-text);font-size:18px;font-weight:500;margin:0}.fc_trigger_dialog_wrap .el-form .el-form-item .el-input input{margin:0;border-color:var(--fc-primary-border)!important;padding:0 12px}.fc_trigger_dialog_wrap .el-form .el-form-item .el-input input:focus{border-color:var(--fc-deep-bg)!important}.fc_choice_row{width:100%;display:flex;flex-flow:row wrap}.fc_choice_row .fc_choice_block{display:flex;align-items:stretch;flex-direction:column}.fc_choice_row .fc_choice_block .fc_choice_card{height:100%}.fc_choice_row.fc_items_benchmark .fc_choice_card,.fc_choice_row.fc_items_benchmark .fc_choice_card:hover{background:var(--fc-warning-bg);border-color:var(--fc-warning-bg)}.fc_choice_row.fc_items_conditional .fc_choice_card{background:var(--fc-secondary-bg)}.fc_choice_blocks .fc_choice_wrap_benchmark{background:var(--fc-secondary-bg);padding:26px 0 1px 20px;margin-right:20px;margin-bottom:30px;margin-top:40px;position:relative;border:1px solid var(--fc-light-bg);border-radius:3px}.fc_choice_blocks .fc_choice_wrap_benchmark .fc_choice_card{background:var(--fc-warning-bg);border:1px solid var(--fc-warning-bg)}.fc_choice_blocks .fc_choice_wrap_benchmark .fc_choice_card:hover{background:var(--fc-warning-bg);border-color:var(--fc-warning-bg)}.fc_choice_blocks .fc_choice_wrap_benchmark .fc_choice_category>h3{display:none}.fc_choice_blocks .fc_choice_wrap_benchmark>h2{position:absolute;top:-15px;background:var(--fc-secondary-bg);padding:4px 10px;border:1px solid var(--fc-light-bg);margin:0;font-size:16px;border-radius:3px}.fc_choice_blocks .fc_choice_wrap_action{background:var(--fc-primary-bg);padding:26px 0 1px 20px;margin-right:20px;margin-bottom:30px;margin-top:40px;position:relative;border:1px solid var(--fc-light-bg);border-radius:3px}.fc_choice_blocks .fc_choice_wrap_action>h2{position:absolute;top:-15px;background:var(--fc-primary-bg);padding:4px 10px;border:1px solid var(--fc-light-bg);margin:0;border-radius:3px;font-size:16px}.fc_choice_blocks .fc_choice_selected_action .fc_choice_category{background:var(--fc-primary-bg);padding:30px 0 1px 12px;margin-right:0;margin-bottom:30px;margin-top:40px;position:relative;border:1px solid var(--fc-light-bg);border-radius:3px}.fc_choice_blocks .fc_choice_selected_action .fc_choice_category>h3{position:absolute;top:-14px;background:var(--fc-primary-bg);padding:4px 10px;border:1px solid var(--fc-light-bg);margin:0;border-radius:4px;font-size:16px}.fc_choice_blocks .fc_choice_selected_action .fc_choice_category:nth-child(2n){background:var(--fc-deep-bg);border-color:var(--fc-deep-bg)}.fc_choice_blocks .fc_choice_selected_action .fc_choice_category:nth-child(2n)>h3{background:var(--fc-primary-bg)}.fc_choice_blocks .fc_choice_wrap_conditional{background:var(--fc-secondary-bg);padding:26px 0 1px 20px;margin-right:20px;margin-bottom:30px;margin-top:40px;position:relative;border:1px solid var(--fc-light-bg);border-radius:3px}.fc_choice_blocks .fc_choice_wrap_conditional>h2{position:absolute;top:-15px;background:var(--fc-secondary-bg);padding:4px 10px;border:1px solid var(--fc-light-bg);margin:0;font-size:16px;border-radius:3px}.fc_choice_blocks .fc_choice_wrap_conditional .fc_choice_card{background:var(--fc-secondary-bg)!important;border:1px solid var(--fc-secondary-bg)}.fc_choice_blocks .fc_choice_wrap_conditional .fc_choice_card:hover{background:var(--fc-secondary-bg)!important;border-color:var(--fc-secondary-border)}.fc_choice_blocks .fc_choice_wrap_conditional .fc_choice_category>h3{display:none}.fc_block_type_action .fluentcrm_blockin{background:var(--fc-primary-bg)!important;min-width:331px}.fc_block_type_action .fluentcrm_blockin .fluentcrm_block_title{color:var(--fc-primary-text)!important}.fc_block_type_action .fluentcrm_blockin .fluentcrm_block_desc{color:var(--fc-secondary-text)!important}.fc_block_type_action .fluentcrm_blockin .fcrm_block_stats span{border-color:var(--fc-secondary-bg)!important}.fc_block_type_conditional .fluentcrm_blockin{background:#ffefed!important;min-width:331px}.fc_block_type_conditional .fluentcrm_blockin .fluentcrm_block_desc{color:#0e121b!important}.fc_block_type_conditional .fluentcrm_blockin .fcrm_block_stats span{border-color:var(--fc-secondary-bg)!important}.el-dropdown-list-wrapper{border:none}.el-dropdown-list-wrapper ul.el-dropdown-menu.el-dropdown-list.fc_limit_height{height:200px;overflow:auto;margin:0}.fc_child_blocks .fluentcrm_blockin{width:100%!important;margin-bottom:20px!important}.fc_child_blocks .fluentcrm_blockin:hover .fc_action_abs_right{top:32px;right:7px}.fc_child_blocks .fluentcrm_block{width:100%;padding:0;margin-bottom:20px}.fc_child_blocks .fluentcrm_block:last-child{margin-bottom:0}.fc_block_choice_wrapper ul.el-menu--horizontal.el-menu{margin-bottom:20px}.fc_block_choice_wrapper .fc_choice_blocks{padding:0 20px}.fc_block_choice_wrapper li.is-active svg{fill:var(--fc-primary-text)}.fc_block_choice_wrapper i{font-size:17px;margin-right:5px}.fc_block_choice_wrapper i.el-dialog__close.el-icon.el-icon-close{position:absolute;top:0;right:0;padding:20px;font-weight:700;cursor:pointer}.fc_block_choice_wrapper i.el-dialog__close.el-icon.el-icon-close:hover{color:var(--fc-deep-bg)}.fc_block_choice_wrapper .fc_funnel_block_search_wrap{padding:0 20px}.fcrm_funnel_active_dot{width:7px;height:7px;border-radius:50%;background:#22c55e;box-shadow:0 0 0 2px #22c55e1f;flex:0 0 auto}.fcrm_funnels_page .funnel-title{margin:0;font-weight:400;font-size:14px;line-height:20px}@media (max-width: 1440px){.el-drawer.fc_funnel_block_modal{min-width:800px}}@media (max-width: 950px){.el-drawer.fc_funnel_block_modal{min-width:700px}}@media (max-width: 720px){.el-drawer.fc_funnel_block_modal{min-width:600px}}@media (max-width: 510px){.el-drawer.fc_funnel_block_modal{min-width:95%}}.el-drawer.fc_funnel_block_modal .el-drawer__body{padding-top:0}.el-drawer.fc_funnel_block_modal .el-drawer__body .fluentcrm_funnel_header{background:var(--fc-primary-bg);border-top-left-radius:5px;border-top-right-radius:5px;padding:12px 20px 14px;position:sticky;top:0;z-index:2}.el-drawer.fc_funnel_block_modal .el-drawer__body .fluentcrm_funnel_header p{margin-top:8px}[class^=fc_drawer_for_] .el-drawer__body,[class*=" fc_drawer_for_"] .el-drawer__body{padding-top:35px!important}.fc_drawer_edit_block .el-drawer__body{padding-top:0!important}.fc_drawer_edit_block .el-drawer__body,.fc_funnel_block_modal .el-drawer__body,.fcrm_drawer .el-drawer__body{padding:0}.fcrm_templates_action_stats_checkbox{height:32px!important}.fc_drawer_for_send_custom_email,.fc_drawer_for_funnel_condition{width:80%!important}@media (min-width: 510px){.fc_drawer_for_send_custom_email .el-drawer__container .el-drawer,.fc_drawer_for_funnel_condition .el-drawer__container .el-drawer{min-width:98%!important}}@media (min-width: 720px){.fc_drawer_for_send_custom_email .el-drawer__container .el-drawer,.fc_drawer_for_funnel_condition .el-drawer__container .el-drawer{min-width:95%!important}}@media (min-width: 950px){.fc_drawer_for_send_custom_email .el-drawer__container .el-drawer,.fc_drawer_for_funnel_condition .el-drawer__container .el-drawer{min-width:90%!important}}@media (min-width: 1200px){.fc_drawer_for_send_custom_email .el-drawer__container .el-drawer,.fc_drawer_for_funnel_condition .el-drawer__container .el-drawer{min-width:80%!important}}@media (min-width: 1201px){.fc_drawer_for_send_custom_email .el-drawer__container .el-drawer,.fc_drawer_for_funnel_condition .el-drawer__container .el-drawer{min-width:1200px!important}}.fc_sequence_import .fluentcrm_funnel_header{border-top-left-radius:5px;border-top-right-radius:5px;margin:-30px -45px 20px;padding:20px 45px}.fc_funnel_block_modal .fluentcrm_funnel_header{margin:-20px -20px 20px}.fc_funnel_block_modal .fcrm_funnel_conditions{margin-top:20px}.fc_funnel_block_modal .fcrm_funnel_editor_footer{justify-content:space-between;margin-left:-20px;margin-right:-20px;margin-top:20px;border-top:1px solid var(--fc-primary-border)}.el-row.fcrm_funnel_editor_internal_config{row-gap:20px}.el-row.fcrm_funnel_editor_internal_config .el-form-item{margin-bottom:0}.fcrm_funnel_editor_boxed{padding:20px}.fcrm_funnel_editor_boxed_with_border{border:1px solid var(--fc-primary-border);padding:20px;border-radius:8px}.fcrm_funnel_editor_drawer_body{padding:20px}.fcrm_funnel_editor_drawer_body .fcrm_funnel_editor_after_header_slot{margin-bottom:20px}img.fc_choice_icon{width:16px;height:16px;filter:invert(1)}.fc_trigger_icon{margin-right:6px}.fc_trigger_icon img{width:20px;height:20px}.fc_trigger_icon i{font-size:20px;vertical-align:middle;display:block}.fc_trigger_icon span svg{vertical-align:middle!important;width:20px;height:20px;display:block}.fc_trigger_selectors{min-height:400px;max-height:550px;overflow-x:hidden}.funnel_trigger_inst{color:var(--fc-error);font-weight:700}.fc_cond_and{text-align:center;font-size:18px;font-weight:500;margin-bottom:10px}.block_item_holder_conditional{position:relative}.block_cond_holder{padding:15px;width:400px;border:1px solid var(--fc-secondary-border);transition-property:box-shadow,height;transition-duration:.2s;transition-timing-function:cubic-bezier(.05,.03,.35,1);border-radius:5px;box-shadow:0 0 30px #16214a00;background:var(--fc-secondary-bg);position:relative}@media (max-width: 895px){.block_cond_holder{float:none;width:auto;flex:1}}.block_conditional_wrapper{position:relative;width:940px;margin:45px auto 0;display:flex;justify-content:space-between}@media (max-width: 895px){.block_conditional_wrapper{max-width:100%;gap:10px}}.fc_cond_border_no_top{width:500px;height:100px;margin:-20px auto 25px;border:2px solid black;border-top:0;border-bottom-left-radius:20px;border-bottom-right-radius:20px}.block_cond_holder.block_cond_yes{float:right;border-color:var(--fc-primary-text)}span.fc_b_no_node{width:7px;height:7px;background:transparent;position:absolute;bottom:50%;left:0;border-radius:50%}span.fc_b_yes_node{width:7px;height:7px;background:transparent;position:absolute;bottom:50%;right:0;border-radius:50%}span.fc_b_no_node_point,span.fc_b_yes_node_point{width:7px;height:7px;background:transparent;position:absolute;top:0;right:50%;border-radius:50%}.block_cond_holder.block_cond_no,.fc_dom_path.fc_dom_path_left{border-color:var(--fc-error-bg)!important}.fc_dom_path.fc_ab_test,.fc_dom_path.fc_condition_node_point{top:90px!important}.fc_dom_path.fc_ab_test>span,.fc_condition_node_point span{display:inline-block;position:absolute;background:var(--fc-primary-bg);width:35px;height:35px;text-align:center;line-height:30px;border-radius:50%;left:-18px;border:2px solid var(--fc-error-bg);box-shadow:0 0 10px #0000001a;top:20px}.fc_condition_node_point.fc_dom_path_right span{left:auto;right:-18px;border-color:var(--fc-primary-text)}.fc_dom_path.fc_ab_test{border-color:#bacd00!important;border-width:3px!important}.fc_dom_path.fc_ab_test>span{border-radius:30px;border-color:#bacd00;width:fit-content;padding:0 10px;left:-50%;font-size:12px}.fc_dom_path.fc_ab_test.fc_dom_path_right span{left:auto;right:-50%}.fcrm_action_selector ul{margin:0}.fcrm_action_selector ul li{cursor:pointer;display:flex;align-items:center;gap:12px;padding:8px;border-radius:8px;margin:0 0 4px}.fcrm_action_selector ul li:hover{background:var(--fc-secondary-bg)}.fcrm_action_selector ul li:last-child{margin-bottom:0}.fcrm_action_selector ul li .icon{width:32px;height:32px;border-radius:50%;display:flex;align-items:center;justify-content:center;background:var(--fc-primary-bg);border:1px solid var(--fc-primary-border);color:var(--fc-secondary-text);flex:none}.fcrm_action_selector ul li .icon svg{display:block}.fcrm_action_selector ul li .content{display:flex;flex-direction:column;gap:4px}.fcrm_action_selector ul li h4{color:var(--fc-secondary-text);font-weight:500;font-size:14px;line-height:20px;margin:0}.fcrm_action_selector ul li p{color:var(--fc-secondary-text);font-weight:400;font-size:12px;line-height:16px;margin:0}.fc_item_num_selector>.el-select{width:69%;display:inline-block}.fc_item_num_selector>.el-input{display:inline-block;width:24%}.fluentcrm_blockin{background:#fff!important}.fluentcrm_blockin .fc_action_abs_right{display:none}.fluentcrm_blockin:hover .fc_action_abs_right{position:absolute;top:55px;right:15px;display:block}.fc_clickable_pop span.el-popover__reference{display:block;width:100%;margin:-7px -20px;padding:7px 20px}.fc_path_no .el-timeline-item__tail,.fc_path_no_A .el-timeline-item__tail{border-left:2px solid var(--fc-text-link)}.fc_path_yes .el-timeline-item__tail,.fc_path_yes_B .el-timeline-item__tail{border-left:2px solid #ff9800}.el-timeline-item{margin-bottom:0}.el-timeline-item.fc_timeline_empty{opacity:.4}.fc_funnel_conditions .fc_rich_container{background:var(--fc-secondary-bg)}.fc_funnel_conditions label.el-checkbox{display:flex;align-items:flex-start}.fc_funnel_conditions label.el-checkbox .el-checkbox__input{margin-top:3px}.fc_drawer_for_fcrm_create_woo_coupon.fc_blocked_no_id .fluentcrm-sequence_control>button{display:none}.fc_drawer_for_fcrm_create_woo_coupon .fc_block_white{padding:0;background:var(--fc-primary-bg);margin-bottom:30px;border-radius:0;box-shadow:none}.fc_drawer_for_fcrm_create_woo_coupon .fc_block_white .el-form-item__content{line-height:1.6}.fc_drawer_for_fcrm_create_woo_coupon .fc_block_white .el-form-item{margin-bottom:20px}.fc_header_merge_codes>.el-button-group{margin-top:-10px;margin-right:10px}.el-form.fc_label_form .el-form-item{margin-bottom:10px}.el-form.fc_label_form .el-form-item:last-child{margin-bottom:0}.el-form.fc_label_form .el-form-item .el-form-item__content{line-height:1}.el-form.fc_label_form .el-form-item .el-form-item__label{font-size:14px;font-weight:500;line-height:20px;color:var(--fc-secondary-text);margin-bottom:8px;padding:0}.el-form.fc_label_form .el-form-item .el-input input{margin:0;box-shadow:none;border:1px solid var(--fc-secondary-border);border-radius:8px;padding:4px 16px;display:block}.el-popover.fc-funnel-actions-popover{border:1px solid var(--fc-primary-border);border-radius:8px;box-shadow:0 8px 30px #1b25331a;padding:12px;box-sizing:border-box}.el-popover.fc-funnel-actions-popover *,.el-popover.fc-funnel-actions-popover *:after,.el-popover.fc-funnel-actions-popover *:before{box-sizing:border-box}.el-popover.fc-funnel-actions-popover .fc_funnel_action_header{cursor:pointer;margin-bottom:10px}.el-popover.fc-funnel-actions-popover .fc_funnel_acton_field{display:flex;flex-direction:column;align-items:flex-start}.el-popover.fc-funnel-actions-popover .fc_funnel_acton_field .el-button{display:block;margin:0;border:none;width:100%;text-align:left;line-height:24px;padding:4px 10px;border-radius:8px;color:var(--fc-primary-text);background:none}.el-popover.fc-funnel-actions-popover .fc_funnel_acton_field .el-button:hover{background:var(--fc-secondary-bg)}.el-popover.fc-funnel-actions-popover .fc_funnel_acton_field .el-button>span{gap:8px}.el-popover.fc-funnel-actions-popover .fc_funnel_acton_field .el-button .icon svg{width:16px;height:16px;display:block}.el-popover.fc_individual_progress_popover{max-height:450px;overflow-x:hidden;overscroll-behavior:contain}.el-dialog__wrapper.fc_modal{box-sizing:border-box}.el-dialog__wrapper.fc_modal .el-dialog{max-width:560px;width:100%;padding:20px;border-radius:8px;min-width:inherit!important}.el-dialog__wrapper.fc_modal *{box-sizing:border-box}.el-dialog__wrapper.fc_modal .el-dialog__header{border:none;padding:0 0 16px;background:transparent;border-bottom:1px solid var(--fc-primary-border);margin-bottom:20px}.el-dialog__wrapper.fc_modal .el-dialog__header .el-dialog__title{font-size:20px;font-weight:600;line-height:28px;color:var(--fc-primary-text);margin:0}.el-dialog__wrapper.fc_modal .el-dialog__body,.el-dialog__wrapper.fc_modal .el-dialog__footer{padding:0}.el-dialog__wrapper.fc_modal .el-dialog__footer .dialog-footer{background:transparent;border-top:1px solid var(--fc-primary-border);padding:18px 0 0;gap:12px;display:flex;align-items:center;margin:0}.el-dialog__wrapper.fc_modal .el-dialog__footer .dialog-footer .el-button{margin:0}.fcrm_funnels_page .fcrm_funnel_title_line{display:flex;align-items:center;gap:7px;min-width:0}.fcrm_funnels_page .fcrm_funnel_title_line a{min-width:0}.fcrm_funnels_page .funnel_title_text{overflow:hidden;color:var(--fc-primary-text);text-overflow:ellipsis;font-size:14px;font-style:normal;font-weight:400;line-height:20px;letter-spacing:-.084px;white-space:nowrap;margin:0}.fcrm_funnels_page .fcrm_funnel_trigger_cell{display:flex;align-items:flex-start;column-gap:3px;line-height:110%}.fcrm_funnels_page .fcrm_funnel_trigger_icon{font-size:120%}.fcrm_funnels_page .fcrm_funnel_trigger_segments{display:flex;flex-wrap:wrap;gap:4px;margin-top:4px}.el-radio-group.fc_labels_radio{display:flex;flex-wrap:wrap;gap:10px}.el-radio-group.fc_labels_radio .el-radio{margin:0;width:36px;height:36px;border-radius:6px;overflow:hidden;position:relative;transition:.3s}.el-radio-group.fc_labels_radio .el-radio.is-checked{box-shadow:0 0 0 1.5px var(--fc-primary-bg),0 0 0 3px var(--fc-deep-bg)}.el-radio-group.fc_labels_radio .el-radio .el-radio__input{width:100%;height:100%;opacity:0}.el-radio-group.fc_labels_radio .el-radio .el-radio__label{display:none}.fc_funnel_labels{display:flex;flex-wrap:wrap;align-items:flex-start;gap:8px}.fc_funnel_labels .el-tag{margin:0;border:none;color:var(--fc-primary-text);height:auto;padding:1px 5px;display:flex;align-items:center;gap:4px}.fc_funnel_labels .el-tag .el-popover__reference-wrapper i{cursor:pointer;margin:0;transform:scale(1);right:0;width:14px;height:14px;line-height:14px;color:var(--fc-primary-text);transition:.3s}.fc_funnel_labels .el-tag .el-popover__reference-wrapper i:hover{background:var(--fc-primary-text);color:var(--fc-text-inverse)}.fcrm_form_row{margin-left:10px!important;margin-right:10px!important}.email_composer_wrapper.fc_into_modal{width:100%}.fc_funnel_block_modal .fc_into_modal .el-form-item,.fc_create_link_wrapper .fc_into_modal .el-form-item{margin-bottom:0}.fc_funnel_block_modal .fc_into_modal .el-form-item__content .el-textarea textarea,.fc_create_link_wrapper .fc_into_modal .el-form-item__content .el-textarea textarea{padding-top:8px}.fc_funnel_block_modal .fc_into_modal .fc_checkbox_note,.fc_create_link_wrapper .fc_into_modal .fc_checkbox_note{font-size:10px}.fc_funnel_block_modal .fc_narrow_box .el-textarea textarea,.fc_create_link_wrapper .fc_narrow_box .el-textarea textarea{padding-top:8px}.fc_funnel_block_modal .el-form .el-form-item__content .fc_group_field_half,.fc_create_link_wrapper .el-form .el-form-item__content .fc_group_field_half{display:flex;align-items:flex-start;justify-content:space-between}.fc_funnel_block_modal .el-form .el-form-item__content .fc_group_field_half .el-form-item,.fc_create_link_wrapper .el-form .el-form-item__content .fc_group_field_half .el-form-item{padding:0;width:49%}.fc_funnel_block_modal .el-form .el-form-item__content .fcrm_options_selector .el-select .el-select__tags .el-tag+.el-tag,.fc_create_link_wrapper .el-form .el-form-item__content .fcrm_options_selector .el-select .el-select__tags .el-tag+.el-tag{margin-left:10px}.fc_integration_sync .fcrm_option_creatable>.el-select .el-select__tags .el-tag{margin-left:10px}.fc_integration_sync .fcrm_option_creatable>.el-select .el-select__tags .el-tag:first-child{margin-left:0}.fc_funnel_root .fluentcrm-templates-action-buttons .input-with-select{position:relative;width:250px;overflow:hidden;border-radius:4px}.fc_funnel_root .fluentcrm-templates-action-buttons .input-with-select input{margin:0;border-radius:4px;height:32px}.fc_funnel_root .fluentcrm-templates-action-buttons .input-with-select .el-input-group__append{position:absolute;right:1px;top:1px;height:30px;padding:0;width:40px;line-height:30px;text-align:center;border:none}.fc_funnel_root .fluentcrm-templates-action-buttons .input-with-select .el-input-group__append button{padding:0;width:100%;height:100%}.fc_funnel_root .fluentcrm-templates-action-buttons .input-with-select .el-input-group__append button:hover{color:var(--fc-deep-bg);background-color:#2225301a}.fc_funnel_root .fluentcrm-templates-action-buttons .el-button-group{display:flex;align-items:center}@media (max-width: 767px){.fc_funnel_root .fluentcrm-templates-action-buttons .el-button-group{flex-wrap:wrap}}.fc_section_conditions{padding:20px 30px 0;background:var(--fc-primary-bg);margin-top:30px;border-top-left-radius:5px;border-top-right-radius:5px}.fc_section_condition_match{padding:20px 30px 30px;background:var(--fc-primary-bg);margin-bottom:30px;border-bottom-left-radius:5px;border-bottom-right-radius:5px}.fc_section_heading{margin-bottom:20px}.fc_section_heading p{margin:10px 0;font-size:15px}.fc_section_heading h3{margin:0;font-size:18px;color:var(--fc-text-link)}.fc_days_ago_operator_field{margin-bottom:30px}.fc_days_ago_operator_field label{font-size:14px;display:block;width:100%;margin-bottom:10px;font-weight:700}.fc_segment_desc_block{text-align:center;margin-bottom:30px}.fc_segment_desc_block .fc_segment_editor{padding:30px;background:var(--fc-secondary-bg);margin-top:20px}.fc_rich_wrap>div:last-child .fc_cond_or{display:none}.fc_rich_wrap>div .fc_cond_or{margin:-15px 0 20px}.fcrm_options_selector.fcrm_option_creatable .el-select .el-select__wrapper .el-select__suffix{position:absolute;right:55px}.fluentcrm_body.fluentcrm_title_cards.fc_narrow_box.fc_white_inverse .fc_funnel_upload .el-upload .el-upload-dragger{height:180px;width:360px;margin:auto}.el-pagination{padding:12px 20px;margin:0}.el-pagination__sizes{margin-right:auto;margin-left:10px}.el-pagination__sizes .el-select__wrapper{border:1px solid var(--fc-primary-border);box-shadow:0 1px 2px #0a0d1408;background:var(--fc-secondary-bg);border-radius:8px;font-weight:500;font-size:14px;line-height:20px;color:var(--fc-secondary-text);height:auto;padding:6px 12px;min-height:inherit}.el-pagination__sizes .el-select__wrapper.is-hovering{box-shadow:0 1px 2px #0a0d1408;outline:none}.el-pagination__total{color:var(--fc-secondary-text);font-weight:400;font-size:14px;line-height:20px}.el-pagination .el-pager{gap:8px}.el-pagination .el-pager .number{border:1px solid var(--fc-primary-border);border-radius:8px;width:28px;height:28px;display:flex;align-items:center;justify-content:center;flex:none;font-weight:500;font-size:14px;line-height:20px;color:var(--fc-secondary-text)}.el-pagination .el-pager .number.is-active{border:1px solid var(--fc-primary-text);color:var(--fc-primary-text)}.el-pagination .btn-next,.el-pagination .btn-prev{color:var(--fc-secondary-text);width:32px;height:32px;margin:0;background:none}.el-overlay.fcrm_note_drawer .el-drawer__header{background:none;border-bottom:1px solid var(--fc-primary-border)}.el-overlay.fcrm_note_drawer .el-drawer__title{color:var(--fc-primary-text);font-weight:500;font-size:18px;line-height:24px}.el-overlay.fcrm_note_drawer .el-drawer__close-btn{color:var(--fc-primary-text)}.el-overlay.fcrm_note_drawer .el-drawer__body{padding:20px 20px 0!important}.el-overlay.fcrm_note_drawer .el-drawer .fc_global_form_builder .el-form-item{margin-bottom:20px}.el-overlay.fcrm_note_drawer .el-drawer .fc_global_form_builder .el-form-item__label{color:var(--fc-primary-text);font-weight:500;font-size:14px;line-height:20px;padding:0;margin:0 0 4px}.el-overlay.fcrm_note_drawer .el-drawer .fc_global_form_builder .el-form-item__content>input{border:1px solid var(--fc-primary-border);box-shadow:0 1px 2px #0a0d1408;border-radius:8px;margin:0}.el-overlay.fcrm_note_drawer .el-drawer .fc_company_save_wrap{padding-top:15px;padding-bottom:15px}.el-overlay.fcrm_drawer_wrapper .el-drawer__header{border-bottom:1px solid var(--fc-primary-border);background:none;padding:20px}.el-overlay.fcrm_drawer_wrapper .el-drawer__title{color:var(--fc-primary-text);font-weight:500;font-size:18px;line-height:24px;margin:0}.el-overlay.fcrm_email_preview_dialog .fcrm_email_preview_email_header ul{margin:0 0 16px;padding:0;border:1px solid var(--fc-primary-border);border-radius:8px}.el-overlay.fcrm_email_preview_dialog .fcrm_email_preview_email_header ul li{display:flex;gap:8px;margin:0;padding-left:12px;padding-right:32px}.el-overlay.fcrm_email_preview_dialog .fcrm_email_preview_email_header ul li .label{flex:none;display:block;width:90px;color:var(--fc-secondary-text);font-weight:400;font-size:12px;line-height:16px;padding:14px 0}.el-overlay.fcrm_email_preview_dialog .fcrm_email_preview_email_header ul li .content{border-bottom:1px solid var(--fc-primary-border);flex:1;padding:14px 0}.el-overlay.fcrm_email_preview_dialog .fcrm_email_preview_email_header ul li .content p,.el-overlay.fcrm_email_preview_dialog .fcrm_email_preview_email_header ul li .content .value{display:block;font-weight:400;font-size:12px;line-height:16px;color:var(--fc-primary-text);margin:0 0 6px;overflow-wrap:anywhere}.el-overlay.fcrm_email_preview_dialog .fcrm_email_preview_email_header ul li .content p:last-child,.el-overlay.fcrm_email_preview_dialog .fcrm_email_preview_email_header ul li .content .value:last-child{margin-bottom:0}.el-overlay.fcrm_email_preview_dialog .fcrm_email_preview_email_header ul li:last-child .content{border-bottom:none}.el-overlay.fcrm_email_preview_dialog .fcrm_email_preview_email_header ul li.fcrm_email_preview_email_stats .content{display:flex;align-items:center;gap:6px;padding:2px 0}.el-overlay.fcrm_email_preview_dialog .fcrm_email_preview_email_header ul li.fcrm_email_preview_email_stats .dotted-shape{width:3px;height:3px;border-radius:50%;background:var(--fc-text-muted)}.el-overlay.fcrm_email_preview_dialog .fcrm_email_preview_email_header ul li.fcrm_email_preview_email_stats .fcrm_email_preview_email_stat_item{display:flex;align-items:center;gap:4px}.el-overlay.fcrm_email_preview_dialog .fcrm_email_preview_email_header ul li.fcrm_email_preview_email_stats .fcrm_email_preview_email_stat_item .icon svg{display:block}.el-overlay.fcrm_email_preview_dialog .fcrm_email_preview_email_warning{background:var(--fc-warning-bg);border-radius:8px;padding:8px;align-items:flex-start;margin-bottom:16px}.el-overlay.fcrm_email_preview_dialog .fcrm_email_preview_email_warning .el-alert__icon{color:var(--fc-warning)}.el-overlay.fcrm_email_preview_dialog .fcrm_email_preview_email_warning .el-alert__title{color:var(--fc-primary-text);font-weight:400;font-size:12px;line-height:16px}.el-overlay.fcrm_email_preview_dialog .fcrm_email_preview_email_body .fcrm_preview_toolbar{margin-bottom:16px}.el-overlay.fcrm_email_preview_dialog .fcrm_email_preview_email_body .fcrm_preview_device_toggle{justify-content:center}.el-popper .fcrm_filter_dropdown{min-width:220px}.el-popper .fcrm_filter_dropdown .fcrm_column_toggler_checks{max-height:400px;overflow-x:hidden;flex-wrap:inherit;gap:0}.el-popper .fcrm_filter_dropdown .fcrm_column_toggler_checks .fcrm_checkbox_group_label{font-size:14px;line-height:1;margin:8px 0;font-weight:500;color:var(--fc-secondary-text)}.el-popper.fcrm_profile_card_popover{padding:12px!important}.fcrm_profile_card_popover_body{display:flex;align-items:center;gap:12px}.fcrm_profile_card_popover_body .fcrm_profile_card_popover_photo{width:80px;height:80px;border-radius:50%;position:relative;flex:none}.fcrm_profile_card_popover_body .fcrm_profile_card_popover_photo img{display:block;width:100%;height:100%;object-fit:cover;border-radius:50%}.fcrm_profile_card_popover_body .fcrm_profile_card_popover_profile_title{display:flex;align-items:center;gap:8px}.fcrm_profile_card_popover_body .fcrm_profile_card_popover_profile_title h3{font-size:14px;font-weight:500;line-height:20px;margin:0}.fcrm_profile_card_popover_body .fcrm_profile_card_popover_profile_email{font-size:12px;font-weight:400;line-height:16px;color:var(--fc-secondary-text)}.fcrm_profile_card_popover_body .fcrm_profile_card_popover_profile_email p{display:flex;align-items:center;gap:6px;margin:0}.fcrm_profile_card_popover_body .fcrm_profile_card_popover_profile_email .user_profile_link{display:flex;align-items:center;gap:4px}.fcrm_profile_card_popover_body .fcrm_profile_card_popover_profile_email .user_profile_link a{display:flex;align-items:center;gap:4px;color:var(--fc-primary-text)}.fcrm_profile_card_popover_body .fcrm_profile_card_popover_profile_email .user_profile_link a:hover{text-decoration:underline}.fcrm_profile_card_popover_body .fcrm_profile_card_popover_profile_email .user_profile_link a svg{display:block;width:16px;height:16px}.fcrm_profile_card_popover_body .fcrm_profile_card_popover_profile_added{font-size:12px;font-weight:400;line-height:16px;color:var(--fc-secondary-text);margin:4px 0 12px}.fcrm_intl_tel_input{width:100%;position:relative}.fcrm_intl_tel_input input{background:none;color:var(--fc-primary-text)}.fcrm_intl_tel_input input.iti__tel-input{padding:5px 12px;font-size:14px;border:none;min-height:32px;line-height:24px;border-radius:8px;width:100%}.fcrm_intl_tel_input input.iti__tel-input::placeholder{color:var(--fc-secondary-border)}.fcrm_intl_tel_input .iti{width:100%;border-radius:8px;border:1px solid var(--fc-primary-border);box-shadow:0 1px 2px #0a0d1408}.fcrm_intl_tel_input .iti:hover{border-color:var(--fc-secondary-border)}.fcrm_intl_tel_input .iti.iti--container-active,.fcrm_intl_tel_input .iti input:focus{border-color:var(--fc-deep-bg);outline:none}.fcrm_intl_tel_input .iti .iti__dropdown-content{padding:8px;min-width:100%;box-sizing:border-box;background:var(--fc-secondary-bg);border:1px solid var(--fc-primary-border);border-radius:8px}.fcrm_intl_tel_input .iti .iti__search-input-wrapper{padding:0;margin-bottom:4px;min-height:36px;border-bottom:1px solid var(--fc-primary-border)}.fcrm_intl_tel_input .iti .iti__search-input{padding:4px 32px;min-height:30px;box-sizing:border-box;font-size:14px;border:1px solid var(--fc-primary-border);border-radius:8px}.fcrm_intl_tel_input .iti .iti__search-icon{left:10px;width:18px;height:18px;align-items:center;justify-content:center}.fcrm_intl_tel_input .iti .iti__search-input+.iti__country-list{border-top:1px solid var(--fc-primary-border);margin-top:4px}.fcrm_external_profile_wrapper{border:1px solid var(--fc-primary-border);border-radius:var(--fcrm-border-radius-8, 8px)}.fcrm_external_profile_wrapper .fcrm_external_profile_data table{width:100%;background:none;border:none;border-collapse:collapse;border-spacing:0;border-radius:0;margin:0;box-shadow:none}.fcrm_external_profile_wrapper .fcrm_external_profile_data table thead tr th{border-top:1px solid var(--fc-primary-border);border-bottom:1px solid var(--fc-primary-border);border-right:1px solid var(--fc-primary-border);background:var(--fc-secondary-bg);color:var(--fc-secondary-text);font-weight:500;font-size:14px;line-height:20px;padding:7px 12px}.fcrm_external_profile_wrapper .fcrm_external_profile_data table thead tr th:first-child{padding-left:20px}.fcrm_external_profile_wrapper .fcrm_external_profile_data table thead tr th:last-child{border-right:none}.fcrm_external_profile_wrapper .fcrm_external_profile_data table tbody tr td{border-bottom:1px solid var(--fc-primary-border);border-right:1px solid var(--fc-primary-border);background:none;padding:12px;color:var(--fc-primary-text);font-weight:400;font-size:14px;line-height:20px}.fcrm_external_profile_wrapper .fcrm_external_profile_data table tbody tr td:first-child{padding-left:20px}.fcrm_external_profile_wrapper .fcrm_external_profile_data table tbody tr td:last-child{border-right:none}.fcrm_external_profile_wrapper .fcrm_external_profile_data table tbody tr:last-child td{border-bottom:none}.fcrm_external_profile_header{display:flex;align-items:center;justify-content:space-between;padding:12px 20px;flex-wrap:wrap;gap:12px}.fcrm_external_profile_header--title{color:var(--fc-primary-text);font-weight:500;font-size:16px;line-height:24px;margin:0}.fcrm_external_profile_header--actions{display:flex;align-items:center;gap:12px;flex-wrap:wrap}.fcrm_external_profile_header--actions .el-button{margin:0}.fcrm_subscribers .fcrm_company_contacts_wrap .fcrm_rich_container .fcrm_rich_wrap{padding:0 20px}.fcrm_profile_wrapper{position:relative}.fcrm_profile_wrapper.fc_side_closed{margin-right:0}.fcrm_profile_wrapper .fcrm_profile_container{display:flex;gap:32px}@media (max-width: 1054px){.fcrm_profile_wrapper .fcrm_profile_container{flex-direction:column}}.fcrm_profile_wrapper .fcrm_profile_container .fcrm_profile_main_content{flex:1;overflow:hidden;transition:.3s}.fcrm_profile_wrapper .fcrm_profile_container .fcrm_profile_main_content .fcrm_profile_header{background:var(--fc-primary-bg);border-radius:var(--fcrm-border-radius-8, 8px);padding:20px;margin-bottom:24px}.fcrm_profile_wrapper .fcrm_profile_container .fcrm_profile_main_content .fcrm_profile_header .fluentcrm_profile_header .profile-info{flex:1}.fcrm_profile_wrapper .fcrm_profile_container .fcrm_profile_main_content .fcrm_profile_header .fluentcrm_profile_header .profile-info .fcrm_profile_header_info_row{display:flex;align-items:flex-start;justify-content:space-between;gap:24px}@media (max-width: 1054px){.fcrm_profile_wrapper .fcrm_profile_container .fcrm_profile_main_content .fcrm_profile_header .fluentcrm_profile_header .profile-info .fcrm_profile_header_info_row{flex-wrap:wrap;gap:16px}}.fcrm_profile_wrapper .fcrm_profile_container .fcrm_profile_main_content .fcrm_profile_header .fluentcrm_profile_header .profile-info .fcrm_profile_header_info_row .profile_title{margin-bottom:4px}.fcrm_profile_wrapper .fcrm_profile_container .fcrm_profile_main_content .fcrm_profile_header .fluentcrm_profile_header .profile-info .fcrm_profile_header_info_row .fcrm_profile_header_name{display:flex;align-items:center;color:var(--fc-primary-text);margin:0;font-weight:500;font-size:16px;line-height:24px;gap:6px;flex-wrap:wrap}.fcrm_profile_wrapper .fcrm_profile_container .fcrm_profile_main_content .fcrm_profile_header .fluentcrm_profile_header .profile-info .fcrm_profile_header_info_row .fcrm_profile_header_name .dot-separator{width:3px;height:3px;display:block;flex:none;background:var(--fc-secondary-text);border-radius:50%}.fcrm_profile_wrapper .fcrm_profile_container .fcrm_profile_main_content .fcrm_profile_header .fluentcrm_profile_header .profile-info .fcrm_profile_header_info_row .fcrm_profile_header_date{color:var(--fc-text-muted);font-size:12px;line-height:16px;font-weight:400}.fcrm_profile_wrapper .fcrm_profile_container .fcrm_profile_main_content .fcrm_profile_header .fluentcrm_profile_header .profile-info .fcrm_profile_header_info_row .fcrm_profile_header_meta{color:var(--fc-secondary-text);display:block;margin:0}.fcrm_profile_wrapper .fcrm_profile_container .fcrm_profile_main_content .fcrm_profile_header .fluentcrm_profile_header .profile-info .fcrm_profile_header_info_row .fcrm_profile_header_meta .fcrm_profile_header_meta_user_id{display:flex;align-items:center;gap:4px}.fcrm_profile_wrapper .fcrm_profile_container .fcrm_profile_main_content .fcrm_profile_header .fluentcrm_profile_header .profile-info .fcrm_profile_header_info_row .fcrm_profile_header_meta .fcrm_profile_header_meta_user_id .fc_middot{margin:0}.fcrm_profile_wrapper .fcrm_profile_container .fcrm_profile_main_content .fcrm_profile_header .fluentcrm_profile_header .profile-info .fcrm_profile_header_info_row .fcrm_profile_header_meta .fcrm_profile_header_meta_user_id a{color:var(--fc-secondary-text);display:flex;align-items:center;gap:4px}.fcrm_profile_wrapper .fcrm_profile_container .fcrm_profile_main_content .fcrm_profile_header .fluentcrm_profile_header .profile-info .fcrm_profile_header_info_row .fcrm_profile_header_meta .fcrm_profile_header_meta_user_id a .dashicons{font-size:14px;width:auto;height:auto}.fcrm_profile_wrapper .fcrm_profile_container .fcrm_profile_main_content .fcrm_profile_header .fluentcrm_profile_header .profile-info .fcrm_profile_header_info_row .fcrm_profile_header_meta_user,.fcrm_profile_wrapper .fcrm_profile_container .fcrm_profile_main_content .fcrm_profile_header .fluentcrm_profile_header .profile-info .fcrm_profile_header_info_row .fcrm_profile_header_meta_userrole{display:flex;align-items:center;gap:4px}.fcrm_profile_wrapper .fcrm_profile_container .fcrm_profile_main_content .fcrm_profile_header .fluentcrm_profile_header .profile-info .fcrm_profile_header_info_row .fcrm_profile_header_meta_userrole .items{display:flex;align-items:center;gap:8px;flex-wrap:wrap}.fcrm_profile_wrapper .fcrm_profile_container .fcrm_profile_main_content .fcrm_profile_header .fluentcrm_profile_header .profile-info .fcrm_profile_header_info_row .fcrm_profile_header_actions{display:flex;align-items:center;gap:8px;justify-content:flex-end}@media (max-width: 1310px){.fcrm_profile_wrapper .fcrm_profile_container .fcrm_profile_main_content .fcrm_profile_header .fluentcrm_profile_header .profile-info .fcrm_profile_header_info_row .fcrm_profile_header_actions{flex-wrap:wrap}}@media (max-width: 1054px){.fcrm_profile_wrapper .fcrm_profile_container .fcrm_profile_main_content .fcrm_profile_header .fluentcrm_profile_header .profile-info .fcrm_profile_header_info_row .fcrm_profile_header_actions{justify-content:flex-start}}.fcrm_profile_wrapper .fcrm_profile_container .fcrm_profile_main_content .fcrm_profile_header .fluentcrm_profile_header .profile-info .fcrm_profile_header_info_row .fcrm_profile_header_actions .fcrm_profile_action .el-tag{box-shadow:0 1px 2px #0a0d1408;border:1px solid var(--fc-primary-border);background:var(--fc-secondary-bg);border-radius:6px;color:var(--fc-secondary-text);font-size:12px;line-height:16px;height:auto;padding:1px 6px;cursor:pointer}.fcrm_profile_wrapper .fcrm_profile_container .fcrm_profile_main_content .fcrm_profile_header .fluentcrm_profile_header .profile-info .fcrm_profile_header_info_row .fcrm_profile_header_actions .fcrm_profile_action .el-tag__content{display:flex;align-items:center;gap:4px}.fcrm_profile_wrapper .fcrm_profile_container .fcrm_profile_main_content .fcrm_profile_header .fluentcrm_profile_header .profile-info .fcrm_profile_header_info_row .fcrm_profile_header_actions .fcrm_profile_action .el-tag .el-icon{margin-left:4px}.fcrm_profile_wrapper .fcrm_profile_container .fcrm_profile_main_content .fcrm_profile_header .fluentcrm_profile_header .profile-info .fcrm_profile_header_info_row .fcrm_profile_header_actions .fcrm_profile_action .el-tag .icon{display:flex;align-items:center;justify-content:center;width:20px;height:20px;flex:none}.fcrm_profile_wrapper .fcrm_profile_container .fcrm_profile_main_content .fcrm_profile_header .fluentcrm_profile_header .profile-info .fcrm_profile_header_info_row .fcrm_profile_header_actions .fcrm_profile_action .el-tag .icon svg{display:block;width:16px}.fcrm_profile_wrapper .fcrm_profile_container .fcrm_profile_main_content .fcrm_profile_header .fluentcrm_profile_header .profile-info .fcrm_profile_header_info_row .fcrm_profile_header_actions .fcrm_profile_action .el-tag .fcrm_profile_contact_type_text{text-transform:capitalize}.fcrm_profile_wrapper .fcrm_profile_container .fcrm_profile_main_content .fcrm_profile_body{background:var(--fc-primary-bg);border-radius:var(--fcrm-border-radius-8, 8px)}.fcrm_profile_wrapper .fcrm_profile_container .fcrm_profile_main_content .fcrm_profile_body_nav{display:flex;gap:16px;padding:0 20px;margin:0;border-bottom:1px solid var(--fc-primary-border)}.fcrm_profile_wrapper .fcrm_profile_container .fcrm_profile_main_content .fcrm_profile_body_nav li{font-weight:500;font-size:14px;line-height:16px;color:var(--fc-secondary-text);padding:14px 0;margin:0;border-bottom:2px solid transparent;cursor:pointer}.fcrm_profile_wrapper .fcrm_profile_container .fcrm_profile_main_content .fcrm_profile_body_nav li.item_active{color:var(--fc-primary-text);border-bottom-color:var(--fc-primary-text)}.fcrm_profile_wrapper .fcrm_profile_container .fcrm_profile_main_content .fcrm_profile_body_inner{padding:20px}.fcrm_profile_wrapper .fcrm_profile_container .fcrm_profile_main_content .fcrm_profile_body_select_wrapper{display:none;padding:20px;margin:0;border-bottom:1px solid var(--fc-primary-border)}.fcrm_profile_wrapper .fcrm_profile_container .fcrm_profile_main_content .fcrm_profile_body_select_wrapper .fcrm_profile_body_select_label{font-weight:500;font-size:14px;line-height:20px;color:var(--fc-primary-text);margin:0 0 8px;padding:0}.fcrm_profile_wrapper .fcrm_profile_container .fcrm_profile_main_content .fcrm_profile_body_select_wrapper .el-select__wrapper{border:1px solid var(--fc-primary-border);box-shadow:0 1px 2px #0a0d1408;border-radius:8px;padding:12px 10px;height:auto;min-height:auto}.fcrm_profile_wrapper .fcrm_profile_container .fcrm_profile_main_content .fcrm_profile_body_select_wrapper .el-select__wrapper input{margin:0}@media (max-width: 900px){.fcrm_profile_wrapper .fcrm_profile_container .fcrm_profile_main_content .fcrm_profile_body .fcrm_profile_body_nav{display:none}.fcrm_profile_wrapper .fcrm_profile_container .fcrm_profile_main_content .fcrm_profile_body .fcrm_profile_body_select_wrapper{display:block}}.fcrm_profile_wrapper .fcrm_profile_container .fcrm_profile_sidebar{flex-shrink:0;width:56px;transition:.3s;background:var(--fc-primary-bg);overflow-x:hidden;margin:-16px -20px 0 0}@media (max-width: 1054px){.fcrm_profile_wrapper .fcrm_profile_container .fcrm_profile_sidebar{width:100%}}.fcrm_profile_wrapper .fcrm_profile_container .fcrm_profile_sidebar.is_active{width:340px}@media (max-width: 1054px){.fcrm_profile_wrapper .fcrm_profile_container .fcrm_profile_sidebar.is_active{width:100%}}.fcrm_profile_wrapper .fcrm_profile_container .fcrm_profile_sidebar.is_active .fcrm_profile_sidebar_header{padding:12px 20px}.fcrm_profile_wrapper .fcrm_profile_container .fcrm_profile_sidebar.is_active .fcrm_profile_sidebar_header .el-button{transform:rotate(0)}.fcrm_profile_wrapper .fcrm_profile_container .fcrm_profile_sidebar_inner{padding:20px}.fcrm_profile_wrapper .fcrm_profile_container .fcrm_profile_sidebar_header{display:flex;align-items:center;justify-content:space-between;border-bottom:1px solid var(--fc-primary-border);padding:12px}@media (max-width: 1054px){.fcrm_profile_wrapper .fcrm_profile_container .fcrm_profile_sidebar_header{pointer-events:none}}.fcrm_profile_wrapper .fcrm_profile_container .fcrm_profile_sidebar_header_title{font-weight:500;font-size:16px;line-height:24px;color:var(--fc-primary-text)}.fcrm_profile_wrapper .fcrm_profile_container .fcrm_profile_sidebar_header .el-button{border:none;background:none;padding:0;margin:0;display:flex;align-items:center;justify-content:center;height:auto;width:auto;transform:rotate(180deg);transition:.3s}@media (max-width: 1054px){.fcrm_profile_wrapper .fcrm_profile_container .fcrm_profile_sidebar_header .el-button{display:none}}.fcrm_profile_wrapper .fcrm_profile_container .fcrm_profile_sidebar_header .el-button .icon{width:32px;height:32px;display:flex;align-items:center;justify-content:center}.fcrm_profile_wrapper .fcrm_profile_container .fcrm_profile_sidebar .fc_sidebar_card{margin-bottom:20px;padding-bottom:20px;border-bottom:1px solid var(--fc-primary-border)}.fcrm_profile_wrapper .fcrm_profile_container .fcrm_profile_sidebar .fc_sidebar_card:last-child{margin-bottom:0;padding-bottom:0;border-bottom:none}.fcrm_profile_wrapper .fcrm_profile_container .fcrm_profile_sidebar .fc_sidebar_card .fc_card_header{margin-bottom:16px}.fcrm_profile_wrapper .fcrm_profile_container .fcrm_profile_sidebar .fc_sidebar_card .fc_card_header h3{font-weight:500;font-size:14px;line-height:20px;color:var(--fc-primary-text);margin:0}.fcrm_profile_wrapper .fcrm_profile_container .fcrm_profile_sidebar .fc_sidebar_card .fc_full_listed{background:none;border:none}.fcrm_profile_wrapper .fcrm_profile_container .fcrm_profile_sidebar .fc_sidebar_card .fc_full_listed li{border:none;height:auto;padding:0;margin:0 0 16px}.fcrm_profile_wrapper .fcrm_profile_container .fcrm_profile_sidebar .fc_sidebar_card .fc_full_listed li:last-child{margin-bottom:0}.fcrm_profile_wrapper .fcrm_profile_container .fcrm_profile_sidebar .fc_sidebar_card .fc_full_listed li .fc_list_sub{font-weight:500;font-size:12px;line-height:16px;color:var(--fc-secondary-text);display:block;margin:0}.fcrm_profile_wrapper .fcrm_profile_container .fcrm_profile_sidebar .fc_sidebar_card .fc_full_listed li .fc_list_value{float:none;font-weight:400;font-size:14px;line-height:20px;color:var(--fc-primary-text);margin:4px 0 0;display:flex;align-items:center;gap:6px}.fcrm_profile_wrapper .fcrm_profile_container .fcrm_profile_sidebar .fc_sidebar_card .fc_full_listed li .fc_list_value a{color:var(--fc-primary-text)}.fcrm_profile_wrapper .fcrm_profile_container .fcrm_profile_sidebar .fc_sidebar_card .fc_full_listed li .fc_list_value .fc_change_ref{font-weight:400}.fcrm_profile_wrapper .fcrm_profile_container .fcrm_profile_sidebar .fc_sidebar_card.fcrm_contact_companies_widget .fc_card_header{display:flex;align-items:center;justify-content:space-between;padding:0;border:none}.fcrm_profile_wrapper .fcrm_profile_container .fcrm_profile_sidebar .fc_sidebar_card.fcrm_contact_companies_widget .fc_card_header .fluentcrm-actions .el-button{color:var(--fc-deep-bg);font-weight:500;font-size:14px;line-height:20px;padding:0}.fcrm_profile_wrapper .fcrm_profile_container .fcrm_profile_sidebar .fc_sidebar_card.fcrm_contact_companies_widget .fcrm_no_company p{color:var(--fc-secondary-text);font-weight:400;font-size:14px;line-height:20px;margin:0}.fcrm_profile_wrapper .fcrm_profile_container .fcrm_profile_sidebar .fc_sidebar_card .fcrm_purchased_history_customer_summary{margin-bottom:20px;padding-bottom:20px;border-bottom:1px solid var(--fc-primary-border)}.fcrm_profile_wrapper .fcrm_profile_container .fcrm_profile_sidebar .fc_sidebar_card .fcrm_commerce_purchase_history_widget .fc_full_listed li{border-bottom:1px solid var(--fc-primary-border);padding-bottom:12px;margin-bottom:12px}.fcrm_profile_wrapper .fcrm_profile_container .fcrm_profile_sidebar .fc_sidebar_card .fcrm_commerce_purchase_history_widget .fc_full_listed li:last-child{border-bottom:none;padding-bottom:0;margin-bottom:0}.fcrm_profile_wrapper .fcrm_profile_container .fcrm_profile_sidebar .fc_sidebar_card .fcrm_commerce_purchase_history_widget .fc_full_listed li .fcrm_product_name{display:block;color:var(--fc-primary-text);font-weight:500;font-size:12px;line-height:16px;margin:0}.fcrm_profile_wrapper .fcrm_profile_container .fcrm_profile_sidebar .fc_sidebar_card .fcrm_commerce_purchase_history_widget .fc_full_listed li .fcrm_product_name a{color:var(--fc-primary-text)}.fcrm_profile_wrapper .fcrm_profile_container .fcrm_profile_sidebar .fc_sidebar_card .fcrm_commerce_purchase_history_widget .fc_full_listed li .fcrm_product_badges{display:flex;align-items:center;gap:12px;margin-top:6px}.fcrm_profile_wrapper .fcrm_profile_container .fcrm_profile_sidebar .fc_sidebar_card .fcrm_commerce_purchase_history_widget .fc_full_listed li .fcrm_product_badges .fcrm_product_badge{color:var(--fc-primary-text);font-weight:400;font-size:12px;line-height:16px;display:block;margin:0;position:relative}.fcrm_profile_wrapper .fcrm_profile_container .fcrm_profile_sidebar .fc_sidebar_card .fcrm_commerce_purchase_history_widget .fc_full_listed li .fcrm_product_badges .fcrm_product_badge a{color:var(--fc-primary-text)}.fcrm_profile_wrapper .fcrm_profile_container .fcrm_profile_sidebar .fc_sidebar_card .fcrm_commerce_purchase_history_widget .fc_full_listed li .fcrm_product_badges .fcrm_product_badge:first-child{padding-left:0}.fcrm_profile_wrapper .fcrm_profile_container .fcrm_profile_sidebar .fc_sidebar_card .fcrm_commerce_purchase_history_widget .fc_full_listed li .fcrm_product_badges .fcrm_product_badge:first-child:before{display:none}.fcrm_profile_wrapper .fcrm_profile_container .fcrm_profile_sidebar .fc_sidebar_card .fcrm_commerce_purchase_history_widget .fc_full_listed li .fcrm_product_badges .fcrm_product_badge:before{content:"";width:3px;height:3px;border-radius:50%;position:absolute;left:-8px;background:var(--fc-text-muted);top:50%;transform:translateY(-50%)}.fcrm_profile_overview .el-form .el-form-item{margin-bottom:16px}.fcrm_profile_overview .el-form .fcrm_profile_overview_address_info,.fcrm_profile_overview .el-form .fcrm_profile_overview_basic_info{border:1px solid var(--fc-primary-border);padding:16px 20px 4px;border-radius:var(--fcrm-border-radius-8, 8px);margin-bottom:20px}.fcrm_profile_overview .el-form .fcrm_profile_overview_address_info h3,.fcrm_profile_overview .el-form .fcrm_profile_overview_basic_info h3{margin:0 0 20px;font-weight:500;font-size:16px;line-height:24px;color:var(--fc-primary-text)}.fcrm_profile_tagger{border-top:1px solid var(--fc-primary-border);padding-top:12px;margin-top:12px}.fcrm_profile_tagger_row{display:grid;grid-template-columns:repeat(auto-fit,minmax(300px,1fr));gap:16px}.fcrm_profile_tagger_row .info-item .header h2{font-weight:500;font-size:14px;line-height:20px;color:var(--fc-primary-text);margin:0 0 8px;text-transform:capitalize;padding:0}.fcrm_profile_tagger_row .info-item .items_inner{display:flex;flex-wrap:wrap;gap:8px}.fcrm_profile_tagger_row .info-item .items_inner .el-tag{margin:0;background:var(--fc-secondary-bg);border-radius:6px;border:none;font-weight:500;font-size:12px;line-height:16px;padding:4px 8px;color:var(--fc-secondary-text)}.fcrm_profile_tagger_row .info-item .items_inner .el-tag__content{display:flex;align-items:center;gap:5px}.fcrm_profile_tagger_row .info-item .items_inner .el-tag__content .icon{display:block}.fcrm_profile_tagger_row .info-item .items_inner .el-tag__content .icon svg{display:block;width:16px}.fcrm_profile_tagger_row .info-item .items_inner .el-tag__close{color:var(--fc-text-muted);width:16px;height:16px;padding:0;font-size:13px}.fcrm_profile_tagger_row .info-item .items_inner .el-tag__close:hover{background:none;color:var(--fc-secondary-text)}.fcrm_profile_tagger_row .info-item .items_inner .el-dropdown.fluentcrm-filter .el-button{width:24px;height:24px;padding:0;box-shadow:0 1px 2px #0a0d1408;border:1px solid var(--fc-primary-border);color:var(--fc-secondary-text);border-radius:6px;display:flex;align-items:center;justify-content:center}.fcrm_profile_header_stats_badges{display:flex;align-items:center;gap:6px;margin-top:12px}.fcrm_profile_header_stats_badges .fcrm_profile_header_stat{display:flex;align-items:center;gap:4px}.fcrm_profile_header_stats_badges .fcrm_profile_header_stat.stats_link{cursor:pointer}.fcrm_profile_header_stats_badges .fcrm_profile_header_stat .icon{width:20px;display:block}.fcrm_profile_header_stats_badges .fcrm_profile_header_stat .icon svg{display:block}.fcrm_profile_header_stats_badges .fcrm_profile_header_stat_count{color:var(--fc-secondary-text);font-weight:400;font-size:14px;line-height:20px}.fcrm_custom_field_wrapper{margin-bottom:24px}.fcrm_custom_fields,.fluentcrm_custom_fields{border:1px solid var(--fc-primary-border);padding:16px 20px 4px;border-radius:var(--fcrm-border-radius-8, 8px)}.fcrm_custom_fields h3,.fluentcrm_custom_fields h3{margin:0 0 20px;font-weight:500;font-size:16px;line-height:24px;color:var(--fc-primary-text);display:flex;align-items:center;justify-content:space-between;flex-wrap:wrap;gap:6px}.fcrm_custom_fields h3 .el-button,.fluentcrm_custom_fields h3 .el-button{color:var(--fc-deep-bg);border:none;padding:0;font-weight:500;font-size:14px;line-height:20px;background:none}.fcrm_custom_fields h3 .el-button>span,.fluentcrm_custom_fields h3 .el-button>span{gap:4px}.fcrm_custom_fields h3 .el-button .el-icon,.fluentcrm_custom_fields h3 .el-button .el-icon{font-size:16px}.fcrm_custom_fields h3 .el-button .text,.fluentcrm_custom_fields h3 .el-button .text{border-bottom:1px solid var(--fc-deep-bg)}.fcrm_custom_fields .fcrm_custom_fields_header,.fluentcrm_custom_fields .fcrm_custom_fields_header{display:flex;align-items:center;justify-content:space-between;flex-wrap:wrap;gap:16px;margin-bottom:20px}.fcrm_custom_fields .fcrm_custom_fields_header_label h3,.fluentcrm_custom_fields .fcrm_custom_fields_header_label h3{margin:0}.fcrm_custom_fields .fcrm_custom_fields_header .fcrm_custom_fields_title,.fluentcrm_custom_fields .fcrm_custom_fields_header .fcrm_custom_fields_title{margin:0;color:var(--fc-primary-text);font-size:16px;font-style:normal;font-weight:500;line-height:24px;letter-spacing:-.176px}.fcrm_custom_fields .el-radio-group,.fluentcrm_custom_fields .el-radio-group{gap:12px}.fcrm_profile_email_drawer .el-drawer__body .fc_block_white{margin-bottom:20px}.fcrm_profile_email_drawer .fcrm_profile_email_drawer_footer{padding:0 20px 20px;justify-content:flex-end}.fcrm_notes_wrapper_inner{border:1px solid var(--fc-primary-border);border-radius:var(--fcrm-border-radius-8, 8px);background:var(--fc-primary-bg)}.fcrm_notes_header{display:flex;align-items:center;justify-content:space-between;gap:8px;flex-wrap:wrap;padding:12px 20px;border-bottom:1px solid var(--fc-primary-border)}.fcrm_notes_header_title{font-weight:500;font-size:16px;line-height:24px;color:var(--fc-primary-text)}.fcrm_notes_header_actions{display:flex;flex-wrap:wrap;gap:8px;align-items:center;flex:1}.fcrm_notes_header_actions .el-button{margin:0!important}.fcrm_notes_header_actions .el-button.fcrm_notes_search_btn{border:1px solid var(--fc-primary-border);box-shadow:0 1px 2px #0a0d1408;background:none;width:32px;height:32px;border-radius:8px;padding:0}.fcrm_notes_header_actions .el-button.fcrm_notes_search_btn.is-disabled,.fcrm_notes_header_actions .el-button.fcrm_notes_search_btn:hover{background:var(--fc-secondary-bg)}.fcrm_notes_header_actions .el-button.fcrm_notes_search_btn .icon svg{display:block}.fcrm_notes_header .fcrm_notes_search_bar{width:32px;display:flex;align-items:center;gap:12px}.fcrm_notes_header .fcrm_notes_search_bar .el-input{justify-content:center}.fcrm_notes_header .fcrm_notes_search_bar .el-input .el-input__wrapper{border:1px solid var(--fc-primary-border);box-shadow:0 1px 2px #0a0d1408;border-radius:8px 0 0 8px;padding:4px 10px;flex:1;min-width:0}.fcrm_notes_header .fcrm_notes_search_bar .el-input .el-input__wrapper.is-focused,.fcrm_notes_header .fcrm_notes_search_bar .el-input .el-input__wrapper.is-focus{outline:none;box-shadow:0 1px 2px #0a0d1408}.fcrm_notes_header .fcrm_notes_search_bar .el-input .el-input-group__append{padding:0;font-size:15px;border:none;box-shadow:none;outline:none;background:none}.fcrm_notes_header .fcrm_notes_search_bar .el-button.fcrm_notes_search_cancel_btn{color:var(--fc-deep-bg);font-weight:500;font-size:14px;line-height:20px;height:auto;padding:0;margin:0}.fcrm_note_row{border-bottom:1px solid var(--fc-primary-border);transition:background-color .15s ease}.fcrm_note_row:last-child{border-bottom:0}.fcrm_note_row:hover{background-color:var(--fc-secondary-bg)}.fcrm_note_row:hover .fcrm_note_actions{display:flex}.fcrm_note_row.is-expanded .fcrm_note_chevron{transform:rotate(90deg)}.fcrm_note_row.is-targeted{animation:fcrm-note-highlight 3s ease-out}.fcrm_note_header{display:flex;align-items:center;justify-content:space-between;padding:14px 20px;cursor:pointer;gap:12px}.fcrm_note_header_left{display:flex;align-items:center;gap:12px;min-width:0;flex:1}.fcrm_note_header_right{display:flex;align-items:center;gap:4px;flex-shrink:0}.fcrm_note_avatar{flex-shrink:0;width:40px;height:40px}.fcrm_note_avatar img{width:40px;height:40px;border-radius:100vh;object-fit:cover}.fcrm_note_avatar .fcrm_note_avatar_initials{width:40px;height:40px;border-radius:100vh;background:var(--fc-light-bg);color:var(--fc-secondary-text);display:flex;align-items:center;justify-content:center;font-size:14px;font-weight:500;line-height:1}.fcrm_note_info{min-width:0;flex:1}.fcrm_note_title{color:var(--fc-primary-text);font-weight:500;font-size:14px;line-height:20px;margin:0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.fcrm_note_meta{color:var(--fc-text-muted);font-weight:400;font-size:13px;line-height:16px;margin:2px 0 0}.fcrm_note_meta .fcrm_note_meta_dot{margin:0 4px}.fcrm_note_chevron{flex-shrink:0;transition:transform .2s ease;color:var(--fc-text-muted)}.fcrm_note_actions{display:none;align-items:center;gap:4px}.fcrm_note_actions .el-button{margin:0;padding:0;border:none;width:32px;height:32px}.fcrm_note_actions .el-button:hover{background:none}.fcrm_note_description{padding:0 20px 16px 100px;font-size:14px;line-height:1.6;color:var(--fc-secondary-text);word-wrap:break-word;overflow-wrap:break-word}.fcrm_note_description ul{padding-left:25px;margin:8px 0;list-style-type:disc}.fcrm_note_description ul li{margin-bottom:4px}@keyframes fcrm-note-highlight{0%,70%{background-color:#409eff1f;box-shadow:inset 3px 0 0 0 var(--fc-deep-bg)}to{background-color:transparent;box-shadow:none}}.fcrm_object_notes_template .fc_company_info_drawer .el-drawer__body{padding:0}.fcrm_object_notes_template .fc_company_info_drawer .el-drawer__body .fc_note_type_note .fc_global_form_builder .el-form-item__content .el-input{width:100%}.fcrm_object_notes_template .fc_company_info_drawer .el-drawer__body .fc_note_type_note .fc_global_form_builder .el-form-item__content .el-date-editor{padding:0}.fcrm_object_notes_template .fcrm_notes_search_bar{display:flex;align-items:center;gap:12px;min-width:32px;width:32px;overflow:hidden;transition:width 1s ease,min-width 1s ease;margin-left:auto}.fcrm_object_notes_template .fcrm_notes_search_bar.fcrm_notes_search_bar-is_expanded{min-width:200px;width:100%;max-width:280px}.fcrm_object_notes_template .fcrm_notes_search_bar .fcrm_notes_search_input{flex:1;min-width:0;border:1px solid var(--fc-primary-border);box-shadow:0 1px 2px #0a0d1408;border-radius:8px;overflow:hidden;height:32px}.fcrm_object_notes_template .fcrm_notes_search_bar .fcrm_notes_search_input .el-input-group__append .el-button>span{height:100%}.fcrm_object_notes_template .fcrm_notes_search_bar .fcrm_notes_search_input .el-input-group__append .el-button>span .icon svg{width:16px;height:16px}.fcrm_object_notes_template .fcrm_notes_search_bar .fcrm_notes_search_input .el-input__wrapper{display:inline-flex;border:none;box-shadow:none;outline:none;min-width:0;max-width:0;opacity:0;overflow:hidden;padding:0;transition:max-width .25s ease,opacity .2s ease,padding .05s ease .1s}.fcrm_object_notes_template .fcrm_notes_search_bar .fcrm_notes_search_input .el-input__wrapper.is-focused,.fcrm_object_notes_template .fcrm_notes_search_bar .fcrm_notes_search_input .el-input__wrapper.is-focus,.fcrm_object_notes_template .fcrm_notes_search_bar .fcrm_notes_search_input .el-input__wrapper:focus,.fcrm_object_notes_template .fcrm_notes_search_bar .fcrm_notes_search_input .el-input__wrapper:focus-within{outline:none}.fcrm_object_notes_template .fcrm_notes_search_bar .el-input-group__append .el-button{color:var(--el-text-color-regular)}.fcrm_object_notes_template .fcrm_notes_search_bar .fcrm_notes_search_input-is_expanded .el-input__wrapper{max-width:500px;opacity:1;padding:4px 10px;transition:max-width .25s ease,opacity .2s ease,padding .05s ease .1s}.fcrm_object_notes_template .fcrm_notes_search_bar .fcrm_notes_search_btn{margin:0;padding:0;height:100%;border:1px solid var(--fc-primary-border);box-shadow:0 1px 2px #0a0d1408;color:var(--fc-secondary-text)}.fcrm_object_notes_template .fcrm_notes_search_bar .fcrm_notes_search_btn:hover{border:1px solid var(--fc-primary-border)!important;box-shadow:0 1px 2px #0a0d1408;color:var(--fc-secondary-text)}.fcrm_object_notes_template .fcrm_notes_search_bar .fcrm_notes_search_close_btn{flex-shrink:0;padding:0 4px;font-weight:500;font-size:14px}.fcrm_object_notes_template .fcrm_notes_empty_text{padding:20px}.fcrm_purchase_history_table_body .el-table__body tr td .order_id{display:block;color:var(--fc-secondary-text);font-weight:400;font-size:12px;line-height:16px;margin-bottom:2px}.fcrm_purchase_history_table_body .el-table__body tr td .order_date{display:block;font-weight:400;font-size:14px;line-height:20px;color:var(--fc-secondary-text)}.fcrm_purchase_history_table_body .el-table__body tr td .order_status{display:inline-flex;align-items:center;font-weight:500;font-size:12px;line-height:16px;background:var(--fc-secondary-bg);color:var(--fc-secondary-text);border-radius:6px;padding:2px 8px}.fcrm_purchase_history_table_body .el-table__body tr td .order_status.status-cancelled{color:var(--fc-primary-text);background:var(--fc-error-bg)}.fcrm_purchase_history_table_body .el-table__body tr td .order_status.status-processing{color:var(--fc-primary-text);background:var(--fc-warning-bg)}.fcrm_purchase_history_table_body .el-table__body tr td .order_status.status-paid,.fcrm_purchase_history_table_body .el-table__body tr td .order_status.status-complete,.fcrm_purchase_history_table_body .el-table__body tr td .order_status.status-completed{color:var(--fc-primary-text);background:var(--fc-success-bg)}.fcrm_purchase_history_table_body .fc_history_before{padding:0 20px;margin-bottom:20px}.fcrm_purchase_history_table_body .fc_history_before p{margin:0}.fcrm_contact_companies{border:1px solid var(--fc-primary-border);border-radius:8px}.fcrm_contact_companies .fcrm_company_card{padding:12px 40px 12px 12px;border-bottom:1px solid var(--fc-primary-border);position:relative}.fcrm_contact_companies .fcrm_company_card_content{flex:1}.fcrm_contact_companies .fcrm_company_card:last-child{border-bottom:none}.fcrm_contact_companies .fcrm_company_card_body{display:flex;align-items:flex-start;gap:8px}.fcrm_contact_companies .fcrm_company_card_image{width:40px;height:40px;flex:none;border-radius:50%;overflow:hidden;background:var(--fc-primary-border)}.fcrm_contact_companies .fcrm_company_card_image img{width:100%;height:100%;object-fit:cover;object-position:center}.fcrm_contact_companies .fcrm_company_card_name{display:flex;align-items:flex-start;gap:4px;justify-content:space-between}.fcrm_contact_companies .fcrm_company_card_name a{display:block;font-weight:500;font-size:14px;line-height:20px;color:var(--fc-primary-text)}.fcrm_contact_companies .fcrm_company_card_primary_badge{background:#2225301a;color:var(--fc-deep-bg);font-weight:500;font-size:12px;line-height:16px;border-radius:6px;padding:2px 8px}.fcrm_contact_companies .fcrm_company_card_domain{margin-top:2px}.fcrm_contact_companies .fcrm_company_card_domain a{display:flex;align-items:center;gap:2px;font-weight:400;font-size:12px;line-height:16px;color:var(--fc-secondary-text);text-decoration:underline}.fcrm_contact_companies .fcrm_company_card_domain a .fc_dash_external{width:auto;height:auto}.fcrm_contact_companies .fcrm_company_card_email{font-weight:400;font-size:12px;line-height:16px;color:var(--fc-secondary-text);margin-top:2px}.fcrm_contact_companies .fcrm_company_card_actions{position:absolute;right:12px;top:12px}.fcrm_contact_companies .fcrm_company_card_actions .el-dropdown-link{transform:rotate(90deg);cursor:pointer}.fcrm_event_tracking_lists{margin:0;padding:0}.fcrm_event_tracking_lists li{margin:0 0 16px}.fcrm_event_tracking_lists li:last-child{margin-bottom:0}.fcrm_event_tracking_lists li .fcrm_event_tracking_title{margin:0 0 4px;color:var(--fc-primary-text);font-weight:400;font-size:14px;line-height:20px}.fcrm_event_tracking_lists li .fcrm_event_tracking_value{margin:0 0 4px;color:var(--fc-secondary-text);font-weight:400;font-size:12px;line-height:16px}.fcrm_event_tracking_lists li .fcrm_event_tracking_footer{display:flex;align-items:center;flex-wrap:wrap;gap:6px}.fcrm_event_tracking_lists li .fcrm_event_tracking_badge{display:flex;align-items:center;gap:4px;background:var(--fc-secondary-bg);color:var(--fc-text-muted);font-weight:500;font-size:11px;line-height:12px;border-radius:4px;padding:2px 6px 3px}.fcrm_event_tracking_lists li .fcrm_event_tracking_date{font-weight:400;font-size:12px;line-height:16px;color:var(--fc-secondary-text)}.el-overlay.fcrm_assign_drawer .el-drawer__body{overflow-x:hidden}.el-overlay.fcrm_assign_drawer .fcrm_assign_drawer--body-header{border-bottom:1px solid var(--fc-primary-border);padding-left:20px;padding-right:20px}.el-overlay.fcrm_assign_drawer .fcrm_assign_drawer--body-header .el-radio-group{gap:24px}.el-overlay.fcrm_assign_drawer .fcrm_assign_drawer--body-header .el-radio-button__inner{background:none;border:none;border-radius:0!important;border-bottom:2px solid transparent;padding-bottom:8px;padding-left:0;padding-right:0;color:var(--fc-secondary-text)}.el-overlay.fcrm_assign_drawer .fcrm_assign_drawer--body-header .el-radio-button.is-active .el-radio-button__original-radio:not(:disabled)+.el-radio-button__inner{background:none;color:var(--fc-primary-text)}.el-overlay.fcrm_assign_drawer .fcrm_assign_co_existing{padding:20px}.el-overlay.fcrm_assign_drawer .fcrm_assign_co_new{padding-top:20px}.el-overlay.fcrm_assign_drawer .fcrm_assign_co_list .el-checkbox-group{margin-bottom:20px}.fcrm_assign_co_list .el-checkbox{width:100%;padding:14px 24px 14px 16px;border-bottom:1px solid var(--fc-primary-border)}.fcrm_assign_co_list .el-checkbox:last-child{border-bottom:none}.fcrm_assign_co_list .el-checkbox-group{flex-direction:column;border:1px solid var(--fc-primary-border);border-radius:8px}.fcrm_assign_selector--search{margin-bottom:20px}.fcrm_assign_selector--search .el-input__wrapper{gap:4px}.fcrm_assign_selector--search .el-input__prefix .el-button{background:none;border:none;padding:0;margin:0;color:var(--fc-secondary-text);pointer-events:none}.fcrm_assign_card{display:flex;align-items:center;gap:14px}.fcrm_assign_card .fcrm_middot{display:block;width:3px;height:3px;border-radius:50%;flex:none;background:var(--fc-secondary-text)}.fcrm_assign_card--image{width:40px;height:40px;border-radius:50%;position:relative}.fcrm_assign_card--image-placeholder{display:inline-block;width:40px;height:40px;border-radius:100%;background:var(--fc-primary-border)}.fcrm_assign_card--image img{display:block;width:100%;height:100%;border-radius:50%;object-fit:cover}.fcrm_assign_card--name{display:flex;align-items:center;color:var(--fc-primary-text);font-weight:500;font-size:14px;line-height:20px;gap:5px;margin-bottom:4px}.fcrm_assign_card--website{color:var(--fc-secondary-text);font-weight:400;font-size:14px;line-height:20px;margin:0}.fcrm_assign_card--email-phone{display:flex;align-items:center;gap:5px;color:var(--fc-secondary-text);font-weight:400;font-size:14px;line-height:20px}.fcrm_assign_card--email-phone .fcrm_middot{background:var(--fc-text-muted)}#fluent_contact_nav{position:fixed;bottom:8px;z-index:99999;left:50%;transform:translate(-50%)}#fluent_contact_nav ul{margin:0;display:flex;align-items:center;box-shadow:0 16px 32px -12px #0e121b3d;background:var(--fc-primary-text);border-radius:8px;padding:8px;gap:4px}#fluent_contact_nav ul li{margin:0}#fluent_contact_nav ul li a{display:flex;align-items:center;gap:8px;color:var(--fc-secondary-border);font-weight:400;font-size:14px;line-height:20px;padding:8px;border-radius:8px}#fluent_contact_nav ul li a .icon svg{display:block}#fluent_contact_nav ul li a:hover{background:var(--fc-deep-bg)}#fluent_contact_nav ul li a:hover .icon{color:var(--fc-secondary-border)}#fluent_contact_nav ul li.next{margin-left:12px}#fluent_contact_nav ul li.prev{margin-right:12px}.fcrm_profile_navigation{display:flex;align-items:center;gap:10px}.fcrm_profile_navigation .el-button-group .el-button.small{min-height:32px}.fcrm_profile_navigation .el-button-group .el-button:first-child{border-radius:8px 0 0 8px;border-right:none}.fcrm_profile_navigation .el-button-group .el-button:last-child{border-radius:0 8px 8px 0}.fcrm_profile_nav_position{font-size:13px;color:var(--fc-secondary-text);white-space:nowrap}.fc-fade-enter-active,.fc-fade-leave-active{transition:opacity .15s ease}.fc-fade-enter-from,.fc-fade-leave-to{opacity:0}.fcrm_header_breadcrumb_actions{display:flex;align-items:center;gap:16px}.fcrm_ai_summary_trigger{border-color:var(--fc-ai-background);color:var(--fc-ai-color);background:var(--fc-ai-background);font-weight:600}.fcrm_ai_summary_trigger:hover,.fcrm_ai_summary_trigger:focus{border-color:var(--fc-ai-color);color:var(--fc-ai-color);background:var(--fc-primary-bg)}.fcrm_ai_summary_trigger .fcrm_ai_summary_trigger_icon svg{width:16px;height:16px}.fcrm_ai_summary_trigger_icon,.fcrm_ai_summary_small_icon{display:inline-flex;align-items:center;justify-content:center}.fcrm_ai_summary_box{max-height:560px;overflow-y:auto}@keyframes onOffInterval{0%{offset-distance:0%}to{offset-distance:100%}}.fcrm_ai_button_anim_wrapper{position:relative;overflow:hidden;border-radius:8px;clip-path:inset(0px round 8px);z-index:1;padding:1px;background:var(--fc-ai-background)}.fcrm_ai_button_anim_wrapper .fcrm_ai_button_anim{position:absolute;top:0;right:0;bottom:0;left:0;clip-path:inset(0px round 8px);border-radius:8px;background:transparent;z-index:-1;border:0px;container-type:inline-size}.fcrm_ai_button_anim_wrapper .fcrm_ai_button_anim_inner{width:50cqmin;aspect-ratio:1/1;position:absolute;border-radius:8px;background:radial-gradient(circle at 100% 8px,rgb(135,98,240),transparent 70%);offset-path:border-box;offset-anchor:100% 50%;animation:4s linear infinite onOffInterval}.fcrm_ai_button_anim_wrapper .fcrm_ai_button{z-index:2;position:relative;padding:6px 10px}.fcrm_ai_button_anim_wrapper .fcrm_ai_button.el-button--small{padding:4px 10px}.fcrm_ai_summary_banner{display:flex;align-items:center;gap:12px;padding:20px;margin-bottom:20px;border-radius:6px;background:linear-gradient(90deg,#7742e629,#ffffff1a,#7742e629 99.99%)}.fcrm_ai_summary_banner_icon{width:48px;height:48px;border-radius:var(--fcrm-border-radius-8);display:flex;align-items:center;justify-content:center;background:var(--fc-primary-bg);box-shadow:0 1px 3px -1.5px #33333329,0 5px 5px -2.5px #33333314,0 12px 6px -6px #33333305,0 16px 8px -8px #33333303,0 0 0 1px #3333330a,inset 0 -.5px .5px #33333314}.fcrm_ai_summary_banner_title{font-weight:500;font-size:16px;line-height:24px;color:var(--fc-primary-text);margin:0}.fcrm_ai_summary_banner_subtitle{display:flex;align-items:center;flex-wrap:wrap;font-weight:400;font-size:12px;line-height:16px;gap:8px;margin-top:3px;color:var(--fc-secondary-text)}.fcrm_ai_summary_status{display:inline-flex;align-items:center;padding:2px 8px;border-radius:999px;color:#15803d;background:#dcfce7;font-size:11px;font-weight:600;text-transform:capitalize}.fcrm_ai_summary_header{display:flex;align-items:flex-start;justify-content:space-between;gap:16px;margin-bottom:20px;padding:0 16px}.fcrm_ai_summary_header h3{margin:0 0 4px;font-weight:500;font-size:16px;line-height:20px;color:var(--fc-secondary-text)}.fcrm_ai_summary_header p{margin:0;font-weight:400;font-size:16px;line-height:24px;color:var(--fc-primary-text)}.fcrm_ai_summary_box_body{padding:0 16px 16px}.fcrm_ai_summary_loading{padding:10px 2px 2px}.fcrm_ai_summary_markdown{padding:14px 16px 4px;border:1px solid var(--fc-primary-border);border-radius:12px;background:var(--fc-primary-bg);color:var(--fc-primary-text);font-size:13px;line-height:1.55}.fcrm_ai_summary_markdown h3,.fcrm_ai_summary_markdown h4,.fcrm_ai_summary_markdown h5,.fcrm_ai_summary_markdown h6{margin:16px 0 8px;font-size:14px;font-weight:600}.fcrm_ai_summary_markdown h3:first-child,.fcrm_ai_summary_markdown h4:first-child,.fcrm_ai_summary_markdown h5:first-child,.fcrm_ai_summary_markdown h6:first-child{margin-top:0}.fcrm_ai_summary_markdown p{margin:0 0 10px}.fcrm_ai_summary_markdown ul{margin:0 0 12px 18px;padding:0}.fcrm_ai_summary_markdown li{margin-bottom:6px}.fcrm_ai_summary_btn_content{display:inline-flex;align-items:center}.el-dropdown-menu.fc_filter_dropdown{border:1px solid var(--fc-primary-border);box-shadow:0 8px 30px #1b25331a;border-radius:8px;padding:20px;box-sizing:border-box}.el-dropdown-menu.fc_filter_dropdown *{box-sizing:border-box}.el-dropdown-menu.fc_filter_dropdown .el-dropdown-menu__item{margin:0;padding:0;transition:.3s}.el-dropdown-menu.fc_filter_dropdown .el-dropdown-menu__item:hover{background:none;color:var(--fc-deep-bg)}.el-dropdown-menu.fc_filter_dropdown .el-dropdown-menu__item.fc-dropdown-search-item{margin-bottom:15px}.el-dropdown-menu.fc_filter_dropdown .el-dropdown-menu__item.fc-dropdown-items-label{font-weight:500;color:var(--fc-primary-text);margin-bottom:15px}.el-dropdown-menu.fc_filter_dropdown .el-dropdown-menu__item.fc-dropdown-items-label:hover{color:var(--fc-primary-text)}.el-dropdown-menu.fc_filter_dropdown .el-checkbox-group .el-checkbox .el-checkbox__input{position:relative}.el-dropdown-menu.fc_filter_dropdown .el-checkbox-group .el-checkbox .el-checkbox__input input[type=checkbox]{left:0;top:0}.el-dropdown-menu.fc_filter_dropdown .fc_tagger_footer{margin-top:10px}.el-dropdown-menu.fc_filter_dropdown .fc_no_match_search_tagger p{margin:0 0 15px}.fc_with_c_fields>ul{max-height:400px;overflow:scroll}.fc_scrolled_lists{max-height:400px;overflow-x:hidden}.fluentcrm_history_table_wrap{max-height:500px;overflow-x:auto}.fluentcrm_history_table_wrap table thead{position:sticky;top:-1px;background:var(--fc-primary-bg)}.fc_name_avatar{display:flex;align-items:center;gap:10px}.fc_name_avatar .fc_avatar img{width:32px;height:32px;border-radius:50%}.fc_name_avatar .fc_name{font-weight:600;font-size:12px;line-height:1}.fc_name_avatar .fc_email{font-size:12px;color:var(--fc-secondary-text)}.fluentcrm-subscribers .el-table .cell{word-break:initial}.fluentcrm-subscribers .el-table .is-leaf{background:var(--fc-secondary-bg)}.fluentcrm-subscribers-import-radio li{margin-bottom:23px}.fluentcrm-pagination{display:flex;margin-top:15px;justify-content:flex-end}.fluentcrm-pagination input{background:transparent}.fluentcrm-meta{display:flex;align-items:center;margin-left:10px}.fluentcrm-meta .el-tag{margin-left:10px}.fluentcrm-filterer{display:flex;justify-content:center}.fluentcrm-filter-manager{margin-right:10px}.fluentcrm-filter-option:first-of-type{margin-bottom:5px}.fluentcrm-filter-options .el-checkbox{display:block;width:100%;box-sizing:border-box}.fluentcrm-filter-options .el-checkbox.fc_checkbox{display:flex}.fluentcrm-filter-options .el-checkbox+.el-checkbox{margin-left:initial}.fluentcrm-body .fc_filter_boxes{flex:1;display:flex}.fluentcrm-body .fc_filter_boxes .help_msg{display:block;width:100%;margin:5px 0 0;font-size:80%}.fluentcrm-body .fc_filter_boxes .fc-bulk-contact-custom-fields{display:flex;align-items:flex-end}.fluentcrm-body .fc_filter_boxes .fc-bulk-contact-custom-fields .el-select{width:200px;flex:none}.fluentcrm-body .fc_filter_boxes .fc-bulk-contact-custom-fields .el-radio-group{max-width:600px;margin-top:0;margin-bottom:2px}.fluentcrm-body .fc_filter_boxes .fc-bulk-contact-custom-fields .el-radio-group .el-radio{margin-right:8px}.fluentcrm-body .fc_filter_boxes .fc-bulk-contact-custom-fields .el-radio-group .el-radio:last-child{margin-right:0}.fluentcrm-body .fc_filter_boxes .fc-bulk-contact-custom-fields .el-checkbox-group{max-width:600px;display:flex;flex-wrap:wrap;gap:10px}.fluentcrm-body .fc_filter_boxes .fc-bulk-contact-custom-fields .el-checkbox-group .el-checkbox{margin:0}.fluentcrm-body .fc_filter_boxes .fc-bulk-contact-custom-fields .el-checkbox-group .el-checkbox .el-checkbox__label{overflow-wrap:anywhere;white-space:break-spaces}.fluentcrm-filterer .fluentcrm-meta{max-width:600px;flex-wrap:wrap}.fcrm_segment_contacts .fluentcrm-header-secondary{background:var(--fc-primary-border)}.fc_each_text_option{margin-bottom:10px}.fcrm_bulk_processing_dialog .el-dialog__header{display:none}.fcrm_bulk_processing_dialog .el-overlay-dialog{align-items:center;justify-content:center;display:flex}.fcrm_bulk_processing--title{margin:0 0 4px;font-weight:500;font-size:16px;line-height:24px;color:var(--fc-primary-text);display:flex;align-items:center;gap:6px}.fcrm_bulk_processing--title .spin{animation:fcrm_spin 1.5s infinite linear}.fcrm_bulk_processing--bar .el-progress{flex:1}.fcrm_bulk_processing--bar .el-progress__text{color:var(--fc-secondary-text);font-weight:500;font-size:12px;line-height:16px;text-align:right}.fcrm_bulk_processing--bar .el-progress-bar__outer{background:var(--fc-light-bg)}.fcrm_bulk_processing--bar .el-progress-bar__inner--striped{background-image:linear-gradient(45deg,rgba(255,255,255,.1) 25%,rgba(255,255,255,0) 0,transparent 50%,rgba(255,255,255,.1) 0,rgba(255,255,255,.1) 75%,rgba(255,255,255,0) 0,transparent)}.fcrm_course_item{padding-bottom:12px;margin-bottom:12px;border-bottom:1px solid var(--fc-primary-border)}.fcrm_course_item:last-child{border-bottom:none;margin-bottom:0;padding-bottom:0}.fcrm_course_item .fcrm_course__title{font-weight:500;font-size:13px;line-height:20px;display:flex;align-items:flex-start;justify-content:space-between;gap:8px;color:var(--fc-primary-text)}.fcrm_course_item .fcrm_course__title a{color:var(--fc-primary-text)}.fcrm_course_item .fcrm_course__duration{margin-top:4px;color:var(--fc-secondary-text);font-weight:400;font-size:12px;line-height:16px;display:flex;align-items:center;gap:2px}.fcrm_forms_page .fcrm_page_header{padding-top:5px}.fc-crm-form-submission-view table tr th{background:var(--fc-secondary-bg)!important;color:var(--fc-secondary-text)}.fc_form_entries .fcrm_expand-enter-active,.fc_form_entries .fcrm_expand-leave-active{transition:all .3s ease-out;max-height:500px}.fc_form_entries .fcrm_expand-enter-from,.fc_form_entries .fcrm_expand-leave-to{opacity:0;max-height:0;transform:scaleY(0);margin-top:0;padding-top:0}.fc_form_entries .fcrm_expand-enter-to,.fc_form_entries .fcrm_expand-leave-from{opacity:1;max-height:500px;transform:scaleY(1)}.fcrm_form_preview{display:flex;flex-direction:column;width:190px;gap:8px}.fcrm_form_preview .fcrm_form_preview_image{display:flex;flex-direction:column;border:1px solid var(--fc-light-bg);border-radius:8px}.fcrm_form_preview .fcrm_form_preview_image:hover{border-color:var(--fc-deep-bg)}.fcrm_form_preview .fcrm_form_preview_image .fcrm_form_preview_image_header{display:flex;height:28px;padding:0 145.333px 0 12px;align-items:center;flex-shrink:0;align-self:stretch;border-bottom:1px solid var(--fc-primary-border);background:var(--static-static-white, #FFF);border-top-left-radius:8px;border-top-right-radius:8px;gap:5px}.fcrm_form_preview .fcrm_form_preview_image .fcrm_form_preview_image_header .fcrm_form_preview_action{width:8px;height:8px;aspect-ratio:1/1;border-radius:6px}.fcrm_form_preview .fcrm_form_preview_image .fcrm_form_preview_image_header .fcrm_bg_red{background:var(--bg-red-500, var(--fc-error))}.fcrm_form_preview .fcrm_form_preview_image .fcrm_form_preview_image_header .fcrm_bg_yellow{background:var(--bg-yellow-400, #F6E05E)}.fcrm_form_preview .fcrm_form_preview_image .fcrm_form_preview_image_header .fcrm_bg_green{background:var(--bg-green-500, #48BB78)}.fcrm_form_preview .fcrm_form_preview_image .fcrm_form_preview_image_body{display:flex;height:160px;padding:12px;flex-direction:column;align-items:flex-start;gap:8px;flex-shrink:0;align-self:stretch;background:var(--fc-secondary-bg);border-bottom-left-radius:8px;border-bottom-right-radius:8px}.fcrm_form_preview .fcrm_form_preview_image .fcrm_form_preview_image_body .fcrm_form_preview_image_container{display:flex;padding:8px;flex-direction:column;justify-content:center;align-items:flex-start;gap:10px;align-self:stretch;border-radius:var(--radius-10, 10px);background:var(--fc-primary-bg)}.fcrm_form_preview .fcrm_form_preview_image .fcrm_form_preview_image_body .fcrm_form_preview_image_container .fcrm_form_preview_common_form{display:flex;padding:10px;width:100%;height:24px;align-items:center;align-self:stretch;border-radius:var(--radius-6, 6px);background:var(--fc-primary-bg);box-shadow:0 -1px 1px -.5px #3333330f inset,0 0 0 1px #3333330a,0 4px 8px -2px #3333330f,0 2px 4px #3333330a,0 1px 2px #3333330a}.fcrm_form_preview .fcrm_form_preview_image .fcrm_form_preview_image_body .fcrm_form_preview_image_container .fcrm_form_preview_common_form .fcrm_form_preview_input{width:61.333px;height:6px;border-radius:4px;background:var(--illustration-soft-200, var(--fc-primary-border))}.fcrm_form_preview .fcrm_form_preview_image .fcrm_form_preview_image_body .fcrm_form_preview_image_container .fcrm_form_inline{justify-content:space-between}.fcrm_form_preview .fcrm_form_preview_image .fcrm_form_preview_image_body .fcrm_form_preview_image_container .fcrm_form_simple{flex-direction:column;gap:8px;height:26px}.fcrm_form_preview .fcrm_form_preview_image .fcrm_form_preview_image_body .fcrm_form_preview_image_container .fcrm_form_simple .fcrm_form_preview_input{margin-right:auto}.fcrm_form_preview .fcrm_form_preview_image .fcrm_form_preview_image_body .fcrm_form_preview_image_container .fcrm_form_preview_button{width:36px;height:12px;border-radius:4px;background:var(--fc-deep-bg);box-shadow:0 4px 4px #ffffff52 inset}.fcrm_form_preview .fcrm_form_preview_details{padding-left:5px}.fcrm_form_preview .fcrm_form_preview_details .fcrm_form_preview_title{color:var(--fc-primary-text);font-size:14px;font-style:normal;font-weight:500;line-height:20px}.fcrm_form_preview .fcrm_form_preview_details .fcrm_form_preview_description{align-self:stretch;color:var(--fc-secondary-text);font-size:14px;font-style:normal;font-weight:500;line-height:20px;font-size:12px;font-weight:400;line-height:16px}.fcrm_form_info_drawer{width:50%!important}@media (max-width: 768px){.fcrm_form_info_drawer{width:90%!important}}.fcrm_form_info_drawer .el-drawer__body>div{height:100%}.fcrm_form_info_drawer a.el-button{text-decoration:none}.fcrm_form_info_drawer .fcrm_create_form_wrapper{height:100%}.fcrm_form_info_drawer .fcrm_create_form_wrapper .fc_created_form .fcrm_success_icon{margin:auto;display:flex;width:32px;height:32px;justify-content:center;align-items:center;aspect-ratio:1/1;border-radius:16777200px;background:var(--fc-success-bg)}.fcrm_form_info_drawer .fcrm_create_form_wrapper .fc_created_form .fcrm_success_icon .el-icon{color:var(--fc-success);font-size:18px}.fcrm_form_info_drawer .fcrm_create_form_wrapper .fc_created_form h3{color:var(--fc-primary-text);font-size:18px;font-style:normal;font-weight:500;line-height:24px;letter-spacing:-.27px;margin-bottom:5px!important}.fcrm_form_info_drawer .fcrm_create_form_wrapper .fc_created_form p{color:var(--fc-secondary-text);text-align:center;font-size:14px;font-style:normal;font-weight:400;line-height:20px;letter-spacing:-.084px;margin:0!important}.fcrm_form_info_drawer .fcrm_create_form_wrapper .fc_created_form .fcrm_item_copier_wrapper{padding-top:0;padding-bottom:0}.fcrm_form_info_drawer .fcrm_create_form_wrapper .fcrm_drawer_footer_wrap{display:flex;justify-content:space-between;background:var(--fc-primary-bg)}.fcrm_form_info_drawer .fcrm_create_form_wrapper .fcrm_drawer_footer_wrap_actions{display:flex;gap:8px}.fcrm_form_info_drawer .fcrm_create_form_wrapper .fcrm_drawer_footer_wrap_actions .el-button{margin:0}.fcrm_form_info_drawer .fcrm_create_form_wrapper .fc_select_template_wrapper h3{margin:0 0 10px;color:var(--fc-primary-text);font-size:14px;font-style:normal;font-weight:500;line-height:20px;letter-spacing:-.084px}.fcrm_form_info_drawer .fcrm_create_form_wrapper .fc_select_template_wrapper .el-radio-group{display:flex;flex-wrap:wrap;gap:20px}.fcrm_form_info_drawer .fcrm_create_form_wrapper .fc_select_template_wrapper .el-radio{margin-right:0;display:block;height:auto;white-space:normal;align-items:flex-start;padding:0;line-height:normal}.fcrm_form_info_drawer .fcrm_create_form_wrapper .fc_select_template_wrapper .el-radio .el-radio__input{display:none;position:absolute;opacity:0;width:0;height:0}.fcrm_form_info_drawer .fcrm_create_form_wrapper .fc_select_template_wrapper .el-radio .el-radio__label{padding-left:0;display:block;line-height:normal}.fcrm_form_info_drawer .fcrm_create_form_wrapper .fc_select_template_wrapper .el-radio .fc_template_preview{cursor:pointer;border:2px solid var(--fc-primary-border);border-radius:6px;overflow:hidden;transition:all .3s;display:block;width:200px;height:168px}.fcrm_form_info_drawer .fcrm_create_form_wrapper .fc_select_template_wrapper .el-radio .fc_template_preview img{width:100%;height:100%;display:block;object-fit:contain}.fcrm_form_info_drawer .fcrm_create_form_wrapper .fc_select_template_wrapper .el-radio .fc_template_preview:hover{border-color:var(--fc-deep-bg)}.fcrm_form_info_drawer .fcrm_create_form_wrapper .fc_select_template_wrapper .el-radio.is-checked .fc_template_preview{border-color:var(--fc-deep-bg);box-shadow:0 0 0 2px #409eff33}.fcrm_form_info_drawer .fcrm_create_form_wrapper .fc_created_form code{display:inline-block;background:var(--fc-secondary-bg);padding:10px;margin:10px 0;border-radius:4px;font-size:14px}.fcrm_form_info_drawer .fcrm_create_form_wrapper .fc_drawer_footer_wrap{text-align:right;padding:20px;border-top:1px solid var(--fc-primary-border);margin-top:20px;align-items:center}.fcrm_form_info_drawer .fcrm_create_form_wrapper .fc_drawer_footer_wrap p{margin:0}.wp-switch-editor{height:inherit;margin:5px 5px 0 0}.fcrm-wp-editor-color-input{display:flex;align-items:center;gap:8px;width:100%;max-width:200px;height:32px;padding:4px;background:var(--fc-secondary-bg);border-radius:8px;position:relative;box-sizing:border-box}.fcrm-wp-editor-color-input .fcrm-wp-editor-color-input__swatch{flex-shrink:0;width:24px;height:24px;border-radius:6px;border:1px solid var(--fc-primary-border)}.fcrm-wp-editor-color-input .fcrm-wp-editor-color-input__hex{flex:1;min-width:0;font-size:13px;color:var(--fc-primary-text);-webkit-user-select:none;user-select:none;pointer-events:none}.fcrm-wp-editor-color-input .fcrm-wp-editor-color-input__clear{flex-shrink:0;display:flex;align-items:center;justify-content:center;width:20px;height:20px;padding:0;margin:0;background:transparent;border:none;border-radius:50%;color:var(--fc-text-muted);font-size:14px;line-height:1;cursor:pointer;position:relative;z-index:2;transition:color .15s,background-color .15s}.fcrm-wp-editor-color-input .fcrm-wp-editor-color-input__clear svg{display:block}.fcrm-wp-editor-color-input .fcrm-wp-editor-color-input__clear:hover{color:var(--fc-secondary-text);background-color:var(--fc-secondary-bg)}.fcrm-wp-editor-color-input .fcrm-wp-editor-color-picker-trigger{position:absolute;top:0;left:0;width:100%;height:100%;margin:0;padding:0;border:none;border-radius:20px;opacity:0;cursor:pointer;z-index:1}.fcrm-wp-editor-color-input .fcrm-wp-editor-color-picker-trigger .el-color-picker__trigger{width:100%!important;height:100%!important;padding:0!important;border:none!important;border-radius:20px!important;background:transparent!important}.fcrm-wp-editor-color-input .fcrm-wp-editor-color-picker-trigger .el-color-picker__color,.fcrm-wp-editor-color-input .fcrm-wp-editor-color-picker-trigger .el-color-picker__icon{display:none!important}.el-overlay.fcrm_button_designer_dialog .el-dialog__body{padding:20px}.el-overlay.fcrm_button_designer_dialog .fcrm_button_designer_dialog--preview{height:100%;display:flex;flex-direction:column;border:1px solid var(--fc-primary-border);border-radius:8px;overflow:hidden}@media (max-width: 991px){.el-overlay.fcrm_button_designer_dialog .fcrm_button_designer_dialog--preview{margin-top:24px;height:auto}}.el-overlay.fcrm_button_designer_dialog .fcrm_button_designer_dialog--preview-header{padding:10px 16px;border-bottom:1px solid var(--fc-primary-border)}.el-overlay.fcrm_button_designer_dialog .fcrm_button_designer_dialog--preview-header-title{font-weight:500;font-size:14px;line-height:20px;text-align:center;color:var(--fc-secondary-text);margin:0;padding:0}.el-overlay.fcrm_button_designer_dialog .fcrm_button_designer_dialog--preview-body{flex:1;display:flex;align-items:center;justify-content:center;background:var(--fc-secondary-bg);padding:50px 0}.el-overlay.fcrm_button_designer_dialog .fcrm_button_designer_dialog--preview-body a{display:block}.fcrm_button_designer_dialog--controls .el-form .el-form-item:last-child{margin-bottom:0}.fcrm_button_designer_dialog--controls .el-form .el-form-item .el-form-item__label{color:var(--fc-primary-text);font-size:14px;line-height:20px;font-weight:500;display:block;margin:0 0 4px;padding:0}.fcrm_button_designer_dialog--controls .el-form .el-form-item .fcrm-wp-editor-slider-input{display:flex;align-items:center;gap:12px;width:100%}.fcrm_button_designer_dialog--controls .el-form .el-form-item .fcrm-wp-editor-slider-input .fcrm-wp-editor-slider-input__number{flex-shrink:0;width:120px}.fcrm_button_designer_dialog--controls .el-form .el-form-item .fcrm-wp-editor-slider-input .fcrm-wp-editor-slider-input__number .el-input-number__decrease,.fcrm_button_designer_dialog--controls .el-form .el-form-item .fcrm-wp-editor-slider-input .fcrm-wp-editor-slider-input__number .el-input-number__increase{background:var(--fc-secondary-bg);border:none;color:var(--fc-primary-text);border-radius:0;height:50%}.fcrm_button_designer_dialog--controls .el-form .el-form-item .fcrm-wp-editor-slider-input .fcrm-wp-editor-slider-input__number .el-input-number__decrease:hover,.fcrm_button_designer_dialog--controls .el-form .el-form-item .fcrm-wp-editor-slider-input .fcrm-wp-editor-slider-input__number .el-input-number__increase:hover{color:var(--fc-primary-text)}.fcrm_button_designer_dialog--controls .el-form .el-form-item .fcrm-wp-editor-slider-input .fcrm-wp-editor-slider-input__number .el-input-number__decrease:hover~.el-input:not(.is-disabled) .el-input__wrapper,.fcrm_button_designer_dialog--controls .el-form .el-form-item .fcrm-wp-editor-slider-input .fcrm-wp-editor-slider-input__number .el-input-number__increase:hover~.el-input:not(.is-disabled) .el-input__wrapper{border-color:var(--fc-primary-text);box-shadow:0 1px 2px #0a0d1408}.fcrm_button_designer_dialog--controls .el-form .el-form-item .fcrm-wp-editor-slider-input .fcrm-wp-editor-slider-input__number .el-input-number__decrease{border-right:none;border-radius:0 0 8px}.fcrm_button_designer_dialog--controls .el-form .el-form-item .fcrm-wp-editor-slider-input .fcrm-wp-editor-slider-input__number .el-input-number__increase{border-left:none;border-radius:0 8px 0 0}.fcrm_button_designer_dialog--controls .el-form .el-form-item .fcrm-wp-editor-slider-input .fcrm-wp-editor-slider-input__number .el-input{height:32px}.fcrm_button_designer_dialog--controls .el-form .el-form-item .fcrm-wp-editor-slider-input .fcrm-wp-editor-slider-input__number .el-input__wrapper{background:none;border:1px solid var(--fc-primary-border);box-shadow:0 1px 2px #0a0d1408;padding-right:30px}.fcrm_button_designer_dialog--controls .el-form .el-form-item .fcrm-wp-editor-slider-input .fcrm-wp-editor-slider-input__number.is-controls-right .el-input__wrapper{border-radius:0}.fcrm_button_designer_dialog--controls .el-form .el-form-item .el-slider{flex:1;min-width:0;height:auto;margin:0}.fcrm_button_designer_dialog--controls .el-form .el-form-item .el-slider__bar{background:var(--fc-deep-bg)}.fcrm_button_designer_dialog--controls .el-form .el-form-item .el-slider__runway{background:var(--fc-light-bg)}.fcrm_button_designer_dialog--controls .el-form .el-form-item .el-slider__button{width:16px;height:16px;background:var(--fc-deep-bg);border:5px solid var(--fc-primary-bg);box-shadow:0 6px 10px #0e121b0f}.fcrm_button_designer_dialog--controls .el-form .el-form-item .el-checkbox-group{display:flex;flex-direction:column;gap:8px;margin-top:10px}.fcrm_button_designer_dialog--controls .el-form .el-form-item .el-checkbox__label{color:var(--fc-primary-text);font-size:14px;line-height:20px;font-weight:400}.wp-editor-wrap .mce-container,.wp-editor-wrap .wp-editor-container{border-radius:8px}.wp-editor-wrap .wp-editor-container textarea{border-radius:0 0 8px 8px;border:1px solid var(--fc-primary-border);border-top:none}.wp-editor-wrap .wp-editor-container .mce-top-part .mce-container-body .mce-toolbar-grp{background:var(--fc-secondary-bg);border:1px solid var(--fc-primary-border);border-bottom:none;border-radius:8px 8px 0 0;padding:7px 8px}.wp-editor-wrap .wp-editor-container .mce-top-part .mce-container-body .mce-toolbar-grp .mce-stack-layout{padding:0}.wp-editor-wrap .wp-editor-container .mce-top-part .mce-container-body .mce-toolbar-grp .mce-stack-layout .mce-btn-group .mce-btn.mce-listbox{border:none;border-radius:4px}.wp-editor-wrap .wp-editor-container .mce-top-part .mce-container-body .mce-toolbar-grp .mce-stack-layout .mce-flow-layout-item #mceu_25-body{display:flex;flex-wrap:wrap;align-items:flex-start;gap:4px}.wp-editor-wrap .wp-editor-container .mce-top-part .mce-container-body .mce-toolbar-grp .mce-stack-layout .mce-flow-layout-item #mceu_25-body .mce-widget{margin:0}.wp-editor-wrap .wp-editor-container .mce-top-part .mce-container-body .mce-toolbar-grp .mce-stack-layout .mce-flow-layout-item #mceu_25-body .mce-widget button{background:var(--fc-primary-bg);color:var(--fc-primary-text);border:none;margin:0;border-radius:4px}.wp-editor-wrap .wp-editor-container .mce-top-part .mce-container-body .mce-toolbar-grp .mce-stack-layout .mce-flow-layout-item #mceu_25-body .mce-widget.mce-fluentcrm_editor_btn button{border:1px solid var(--fc-primary-border);box-shadow:0 1px 2px #0a0d1408;height:24px;padding:4px 10px}.wp-editor-wrap .wp-editor-container .mce-top-part .mce-container-body .mce-toolbar-grp .mce-toolbar .mce-flow-layout{gap:8px}.wp-editor-wrap .wp-editor-container .mce-top-part .mce-container-body .mce-toolbar-grp .mce-btn-group{gap:4px}.cursor_pointer{cursor:pointer}.fcrm_primary_alert{display:flex;justify-content:space-between;align-items:center;background-color:var(--fc-primary-bg);border-radius:8px;padding:10px 10px 10px 14px;margin-bottom:20px;box-shadow:0 16px 32px -12px #0e121b1a;color:var(--fc-primary-text);font-size:14px;line-height:20px;font-weight:400;position:relative;overflow:hidden;z-index:1}.fcrm_primary_alert:before{content:"";position:absolute;left:0;top:0;height:100%;width:3px;background:var(--fc-text-link);z-index:-1}.fcrm_primary_alert p{margin:0}.fcrm_child_field{padding-left:24px}.fcrm_form_builder_new{width:100%}.fcrm_form_builder_new .fcrm_checkbox_row .fcrm_checkbox{display:flex;align-items:flex-start}.fcrm_form_builder_new .fcrm_input_row{display:flex;flex-direction:column;gap:4px;align-self:stretch}.fcrm_form_builder_new .fcrm_input_row .fcrm_input .el-input__wrapper{padding:1px!important}.fcrm_form_builder_new .fcrm_input_row .fcrm_input .el-input__wrapper .el-input__inner{padding-left:10px}.fcrm_form_builder_new .fcrm_input_row .fcrm_input_hint{margin:0!important}.fcrm_form_builder_new .fcrm_options_selector{display:flex}.fcrm_form_builder_new .fcrm_field_inline_help{color:var(--fc-secondary-text);font-weight:400;font-size:12px;font-style:normal;line-height:16px}.fcrm_settings{width:100%;min-width:0;overflow-x:auto}.fcrm_empty_state{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:16px 16px 30px;border-radius:var(--fcrm-border-radius-8);background:var(--fc-primary-bg);gap:16px}.fcrm_empty_state svg{display:block;max-width:100px;width:100%}.fcrm_empty_state .fcrm_empty_state_text{gap:16px}.fcrm_empty_state .fcrm_empty_state_text p{color:var(--fc-primary-text);text-align:center;font-size:14px;font-style:normal;font-weight:500;line-height:20px;letter-spacing:-.084px;margin:0}.fcrm_empty_state .fcrm_empty_state_text span{color:var(--fc-secondary-text);text-align:center;font-weight:400;font-size:12px;font-style:normal;line-height:16px}.fcrm_empty_state.is-installing{opacity:.7;pointer-events:none}.fcrm_empty_state .el-button{margin-top:5px}.fcrm_installation_loader{display:flex;align-items:center;justify-content:center;gap:8px;margin-top:8px}.fcrm_loader_spinner{width:20px;height:20px;border:2px solid var(--fc-primary-border);border-top-color:var(--fc-secondary-text);border-radius:50%;animation:spin .8s linear infinite}@keyframes spin{to{transform:rotate(360deg)}}.fcrm_loader_text{font-size:14px;font-weight:500;color:var(--fc-secondary-text)}.fcrm_header_breadcrumb_wrapper{display:flex;align-items:center;justify-content:space-between;margin-bottom:24px;flex-wrap:wrap;gap:16px}.fcrm_header_breadcrumb_wrapper .el-breadcrumb__inner{font-size:14px;font-style:normal;font-weight:500;line-height:20px;color:var(--fc-primary-text)}.fcrm_header_breadcrumb_wrapper .el-breadcrumb__inner.is-link{color:var(--fc-secondary-text)}.fcrm_header_breadcrumb_wrapper .el-breadcrumb__item .el-breadcrumb__separator{font-size:11px;margin:0 10px}.fcrm_header_breadcrumb_wrapper .el-breadcrumb__item:last-child .el-breadcrumb__inner{color:var(--fc-primary-text)}.fcrm_header_breadcrumb_actions{display:flex;align-items:center;gap:8px}.fcrm_header_breadcrumb_actions .el-button{margin:0}.fcrm_header_breadcrumb_actions .el-dropdown .el-button.el-dropdown-link{border:1px solid var(--fc-primary-border);color:var(--fc-secondary-text);height:auto;font-weight:500;font-size:14px;min-height:inherit}.el-overlay.fcrm_incoming_webhook_dialog .el-dialog__header{display:none}.el-overlay.fcrm_incoming_webhook_dialog .el-drawer__body,.el-overlay.fcrm_incoming_webhook_dialog .el-dialog__body{padding:0}.el-overlay.fcrm_incoming_webhook_dialog .el-dialog{max-width:640px;width:100%}.el-overlay.fcrm_incoming_webhook_dialog .fcrm_webhook_url_section .el-form-item__label{height:auto}.el-overlay.fcrm_incoming_webhook_dialog .fcrm_webhook_url_section .el-input.el-input-group--append .el-input__wrapper{border:1px solid var(--fc-primary-border);border-right:none;border-radius:8px 0 0 8px;box-shadow:none}.el-overlay.fcrm_incoming_webhook_dialog .fcrm_webhook_url_section .el-input.el-input-group--append .el-input__wrapper input{padding:0}.el-overlay.fcrm_incoming_webhook_dialog .fcrm_webhook_url_section .el-input.el-input-group--append .el-input-group__append{border-radius:0 8px 8px 0;border-color:var(--fc-primary-border);background:var(--fc-secondary-bg);padding:0 8px}.el-overlay.fcrm_incoming_webhook_dialog .fcrm_webhook_url_section .el-input.el-input-group--append .el-input-group__append:hover{background:var(--fc-secondary-bg)}.el-overlay.fcrm_incoming_webhook_dialog .field-mapping-section{margin-top:20px}.el-overlay.fcrm_incoming_webhook_dialog .field-mapping-section .el-form-item__label{height:auto}.el-overlay.fcrm_incoming_webhook_dialog .field-mapping-section .el-table{border-radius:8px;border:1px solid var(--fc-primary-border)}.el-overlay.fcrm_incoming_webhook_dialog .field-mapping-section .el-table .el-table__header-wrapper .el-table__header th{background-color:var(--fc-secondary-bg)!important;font-weight:500;font-size:14px;line-height:20px;color:var(--fc-secondary-text);padding-top:8px;padding-bottom:8px;border-bottom:1px solid var(--fc-primary-border)}.el-overlay.fcrm_incoming_webhook_dialog .field-mapping-section .el-table .el-table__header-wrapper .el-table__header th:first-child .cell{padding-left:20px}.el-overlay.fcrm_incoming_webhook_dialog .field-mapping-section .el-table .el-table__header-wrapper .el-table__header th .cell{padding-left:12px;padding-right:12px}.el-overlay.fcrm_incoming_webhook_dialog .field-mapping-section .el-table .el-table__inner-wrapper:before{display:none}.el-overlay.fcrm_incoming_webhook_dialog .field-mapping-section .el-table .el-table__body-wrapper .el-table__body tr:last-child td{border-bottom:none}.el-overlay.fcrm_incoming_webhook_dialog .field-mapping-section .el-table .el-table__body-wrapper .el-table__body tr td{font-weight:400;font-size:14px;line-height:20px;color:var(--fc-primary-text);padding-top:10px;padding-bottom:10px}.el-overlay.fcrm_incoming_webhook_dialog .field-mapping-section .el-table .el-table__body-wrapper .el-table__body tr td:first-child .cell{padding-left:20px}.el-overlay.fcrm_incoming_webhook_dialog .field-mapping-section .el-table .el-table__body-wrapper .el-table__body tr td .cell{padding-left:12px;padding-right:12px}td.fcrm_table_actions_cell .cell{display:flex;align-items:center;justify-content:center}.editor-container{position:relative}.editor-container iframe{border:none}.editor-container .fcrm_editor_loader_blocks_preview{position:absolute;left:0;top:0;width:100%;height:100%;display:flex;flex-direction:column;align-items:center;justify-content:center;background:#ffffffe6;border:1px solid var(--fc-primary-border);border-radius:8px}.fcrm_editor_loader_blocks_wrap{display:grid;grid-template-columns:1fr 1fr;grid-template-rows:1fr 1fr;gap:6px;width:80px;height:80px;margin-bottom:18px}.fcrm_editor_loader_block{background:var(--fc-primary-border);border-radius:6px;transform:scale(0);animation:blk-pulse 2.4s ease-in-out infinite}.fcrm_editor_loader_block:nth-child(1){animation-delay:0s;background:var(--fc-primary-border)}.fcrm_editor_loader_block:nth-child(2){animation-delay:.15s;background:var(--fc-text-muted)}.fcrm_editor_loader_block:nth-child(3){animation-delay:.3s;background:var(--fc-text-muted)}.fcrm_editor_loader_block:nth-child(4){animation-delay:.45s;background:var(--fc-primary-border)}.fcrm_editor_loader_block:nth-child(1),.fcrm_editor_loader_block:nth-child(2),.fcrm_editor_loader_block:nth-child(3){animation-duration:2.4s}.fcrm_editor_loader_block-text{font-size:13px;color:var(--fc-text-muted);font-weight:400}@keyframes blk-in{0%{transform:scale(0);opacity:0}80%{transform:scale(1.08)}to{transform:scale(1);opacity:1}}@keyframes blk-pulse{0%,to{transform:scale(0);opacity:0}20%,80%{transform:scale(1);opacity:1}50%{transform:scale(1.05);opacity:1}}.el-picker-panel .el-picker-panel__footer .el-button{font-size:12px}.fcrm_settings,.fcrm_view{background:none!important}.fcrm_top_menu{display:flex;padding:0 20px;margin:0 -20px;margin-bottom:0!important;align-items:flex-start;gap:24px;border-bottom:1px solid var(--fc-primary-border)!important;background:var(--fc-primary-bg)}.fcrm_top_menu.el-menu--horizontal>.el-menu-item{border-bottom:none!important}.fcrm_top_menu.el-menu--horizontal>.el-menu-item.is-active{border-bottom:2px solid var(--fc-primary-text)!important;color:var(--fc-primary-text)!important;font-weight:500}.fcrm_top_menu.el-menu--horizontal>.el-menu-item:hover{color:var(--fc-primary-text);background:var(--fc-primary-bg)}.fcrm_top_menu .fcrm_top_menu_item{display:flex;justify-content:center;align-items:center;gap:4px;color:var(--fc-primary-text);text-align:center;font-feature-settings:"ss11" on,"liga" off,"calt" off;font-size:14px;font-style:normal;line-height:20px;letter-spacing:-.084px;background:var(--fc-primary-bg);border:none;padding:14px 12px}.el-switch.fcrm_advanced_toggle{--el-switch-on-color: var(--fc-deep-bg) !important}.el-switch.fcrm_advanced_toggle .el-switch__core{background:var(--fc-light-bg)}.el-switch.fcrm_advanced_toggle .el-switch__core .el-switch__action{border:none;box-shadow:0 4px 8px #1b1c1d0f}.el-switch.fcrm_advanced_toggle.is-checked .el-switch__core{background-color:var(--fc-deep-bg)!important;border-color:var(--fc-deep-bg)!important}.fcrm_view.fcrm_view-wrapper.fcrm_subscribers .fcrm_subscribers{overflow:hidden}.fcrm_view.fcrm_view-wrapper.fcrm_subscribers .fcrm_body{background:var(--fc-primary-bg);border-radius:8px;padding:0}.fcrm_view.fcrm_view-wrapper.fcrm_subscribers .fcrm_action_buttons{display:flex;justify-content:space-between;align-items:center;padding:0 18px 18px;width:100%}.fcrm_view.fcrm_view-wrapper.fcrm_subscribers .fcrm_action_buttons .fcrm_action_buttons_right{display:flex;gap:12px;align-items:center}.fcrm_view.fcrm_view-wrapper.fcrm_subscribers .fcrm_action_buttons .fcrm_action_buttons_right .el-button{margin:0}.fcrm_rich_container{display:flex;flex-direction:column;gap:16px;width:100%;padding:0}.fcrm_rich_container .fcrm_rich_wrap{display:flex;flex-direction:column;gap:16px;width:100%}.fcrm_rich_container .fcrm_rich_wrap .fcrm_rich_filter{background:var(--fc-secondary-bg);border-radius:var(--fcrm-border-radius-8);padding:16px;display:flex;flex-direction:column;gap:16px;width:100%}.fcrm_rich_container .fcrm_rich_wrap .fcrm_rich_filter .fcrm_filter_group_header{display:flex;flex-direction:column;gap:12px;width:100%}.fcrm_rich_container .fcrm_rich_wrap .fcrm_rich_filter .fcrm_filter_group_header .fcrm_and_label{font-size:12px;font-weight:400;line-height:16px;font-weight:500;letter-spacing:.48px;color:var(--fc-text-muted);text-transform:uppercase;margin:0}.fcrm_rich_container .fcrm_rich_wrap .fcrm_rich_filter .fcrm_filter_group_header .fcrm_rich_filters{width:100%;display:flex;flex-direction:column;gap:12px}.fcrm_rich_container .fcrm_rich_wrap .fcrm_rich_filter .fcrm_filter_group_header .fcrm_rich_filters .fcrm_table{width:100%;border-collapse:collapse;margin:0;background:transparent}.fcrm_rich_container .fcrm_rich_wrap .fcrm_rich_filter .fcrm_filter_group_header .fcrm_rich_filters .fcrm_table tbody tr{background:transparent}.fcrm_rich_container .fcrm_rich_wrap .fcrm_rich_filter .fcrm_filter_group_header .fcrm_rich_filters .fcrm_table tbody tr:first-child td{padding-top:0}.fcrm_rich_container .fcrm_rich_wrap .fcrm_rich_filter .fcrm_filter_group_header .fcrm_rich_filters .fcrm_table tbody tr td{padding:12px 0;vertical-align:middle;font-size:14px;font-weight:400;line-height:20px;color:var(--fc-primary-text)}.fcrm_rich_container .fcrm_rich_wrap .fcrm_rich_filter .fcrm_filter_group_header .fcrm_rich_filters .fcrm_table tbody tr td:first-child{padding-left:8px;padding-right:16px;width:200px;color:var(--fc-secondary-text);font-weight:500}.fcrm_rich_container .fcrm_rich_wrap .fcrm_rich_filter .fcrm_filter_group_header .fcrm_rich_filters .fcrm_table tbody tr td:first-child .fcrm_fs_provider_separator{color:var(--fc-text-muted);margin:0 4px}.fcrm_rich_container .fcrm_rich_wrap .fcrm_rich_filter .fcrm_filter_group_header .fcrm_rich_filters .fcrm_table tbody tr td.fcrm_filter_operator{width:200px;padding-left:0;padding-right:12px}.fcrm_rich_container .fcrm_rich_wrap .fcrm_rich_filter .fcrm_filter_group_header .fcrm_rich_filters .fcrm_table tbody tr td .fcrm_composite_filters{display:flex;align-content:flex-start;gap:8px;flex-wrap:wrap}.fcrm_rich_container .fcrm_rich_wrap .fcrm_rich_filter .fcrm_filter_group_header .fcrm_rich_filters .fcrm_table tbody tr td.fcrm_filter_value{padding-left:0;padding-right:12px;flex:1 0 0}.fcrm_rich_container .fcrm_rich_wrap .fcrm_rich_filter .fcrm_filter_group_header .fcrm_rich_filters .fcrm_table tbody tr td.fcrm_filter_value .el-select .el-select__wrapper{min-height:36px}.fcrm_rich_container .fcrm_rich_wrap .fcrm_rich_filter .fcrm_filter_group_header .fcrm_rich_filters .fcrm_table tbody tr td.fcrm_filter_value .el-date-editor{width:100%}.fcrm_rich_container .fcrm_rich_wrap .fcrm_rich_filter .fcrm_filter_group_header .fcrm_rich_filters .fcrm_table tbody tr td:last-child{padding-right:0;width:auto;text-align:right}.fcrm_rich_container .fcrm_rich_wrap .fcrm_rich_filter .fcrm_filter_group_header .fcrm_rich_filters .fcrm_filter_intro{display:flex;align-items:center;justify-content:flex-start;gap:0;width:100%;margin:0;padding:0}.fcrm_rich_container .fcrm_rich_wrap .fcrm_rich_filter .fcrm_filter_group_header .fcrm_rich_filters .fcrm_filter_intro .el-popover{display:inline-block!important;position:relative!important}.fcrm_rich_container .fcrm_rich_wrap .fcrm_rich_filter .fcrm_filter_group_header .fcrm_rich_filters .fcrm_filter_intro>*:not(.el-button):not(.el-popover):not(.fcrm_filter_intro_actions){display:none}.fcrm_rich_container .fcrm_rich_wrap .fcrm_rich_filter .fcrm_filter_group_header .fcrm_rich_filters .fcrm_filter_intro .fcrm_filter_intro_actions{display:flex;align-items:center;gap:10px;width:100%}.fcrm_rich_container .fcrm_rich_wrap .fcrm_rich_filter .fcrm_filter_group_header .fcrm_rich_filters .fcrm_filter_intro .fcrm_filter_intro_actions .fcrm_delete_section_link{display:flex;align-items:center;gap:4px;background:transparent;border:none;padding:0;margin:0;cursor:pointer;font-size:12px;font-weight:400;line-height:16px;font-weight:500;letter-spacing:0;color:var(--fc-secondary-text);transition:color .2s ease}.fcrm_rich_container .fcrm_rich_wrap .fcrm_rich_filter .fcrm_filter_group_header .fcrm_rich_filters .fcrm_filter_intro .fcrm_filter_intro_actions .fcrm_delete_section_link .el-icon{width:16px;height:16px;font-size:16px;color:var(--fc-secondary-text);margin:0;transition:color .2s ease}.fcrm_rich_container .fcrm_rich_wrap .fcrm_rich_filter .fcrm_filter_group_header .fcrm_rich_filters .fcrm_filter_intro .fcrm_filter_intro_actions .fcrm_delete_section_link:hover,.fcrm_rich_container .fcrm_rich_wrap .fcrm_rich_filter .fcrm_filter_group_header .fcrm_rich_filters .fcrm_filter_intro .fcrm_filter_intro_actions .fcrm_delete_section_link:hover .el-icon{color:var(--fc-error)}.fcrm_rich_container .fcrm_rich_wrap .fcrm_rich_filter .fcrm_filter_group_header .fcrm_rich_filters .fcrm_filter_intro .fcrm_filter_intro_actions .fcrm_delete_section_link:active,.fcrm_rich_container .fcrm_rich_wrap .fcrm_rich_filter .fcrm_filter_group_header .fcrm_rich_filters .fcrm_filter_intro .fcrm_filter_intro_actions .fcrm_delete_section_link:focus{color:var(--fc-primary-text);outline:none}.fcrm_rich_container .fcrm_rich_wrap .fcrm_rich_filter .fcrm_filter_group_header .fcrm_rich_filters .fcrm_filter_intro .fcrm_filter_intro_actions .fcrm_delete_section_link:active .el-icon,.fcrm_rich_container .fcrm_rich_wrap .fcrm_rich_filter .fcrm_filter_group_header .fcrm_rich_filters .fcrm_filter_intro .fcrm_filter_intro_actions .fcrm_delete_section_link:focus .el-icon{color:var(--fc-primary-text)}.fcrm_rich_container .fcrm_cond_or{display:flex;align-items:center;justify-content:center;gap:10px;width:100%;position:relative}.fcrm_rich_container .fcrm_cond_or .fcrm_or_divider_line{flex:1 0 0;border-bottom:1px dashed var(--fc-primary-border)}.fcrm_company_contacts_wrap .fcrm_bulk_action_content{padding:16px;background:var(--fc-primary-bg);border-top:1px solid var(--fc-primary-border)}.fcrm_company_contacts_wrap .fcrm_bulk_action_content .fcrm_bulk_wrap{display:flex;flex-wrap:wrap;align-items:flex-start;gap:12px}.fcrm_company_contacts_wrap .fcrm_bulk_action_content .fcrm_bulk_wrap .fcrm_bulk_item{display:flex;flex-direction:column;gap:4px}.fcrm_company_contacts_wrap .fcrm_bulk_action_content .fcrm_bulk_wrap .fcrm_bulk_item label{font-size:14px;font-weight:500;line-height:20px;color:var(--fc-primary-text)}.fcrm_company_contacts_wrap .fcrm_bulk_action_content .fcrm_bulk_navs{margin-top:12px;padding-top:12px;border-top:1px solid var(--fc-primary-border)}.fcrm_company_contacts_wrap .fcrm_bulk_action_content .fcrm_bulk_navs p{margin:0;font-size:14px;font-weight:400;line-height:20px;color:var(--fc-secondary-text)}.fcrm_company_contacts_wrap .fcrm_bulk_action_content .fcrm_bulk_navs p .el-button{margin-left:8px}.fcrm_company_contacts_wrap .fcrm_filter_boxes{margin-right:20px;width:100%}.fcrm_company_contacts_wrap .fcrm_bulk_action_bar .fcrm_bulk_action_left .fcrm_bulk_action_inline>div{width:auto}.fcrm_company_contacts_wrap .fcrm_bulk_action_bar .fcrm_bulk_action_left .fcrm_bulk_action_inline .fcrm_bulk_wrap .fcrm_bulk_item .fcrm_ml-5,.fcrm_company_contacts_wrap .fcrm_bulk_action_bar .fcrm_bulk_action_left .fcrm_bulk_action_inline .fcrm_bulk_wrap .fcrm_bulk_item .fcrm_mt-5{margin-left:0!important;margin-top:0!important}.fcrm_company_contacts_wrap .fcrm_bulk_action_bar .fcrm_bulk_action_left .fcrm_bulk_action_inline .fcrm_bulk_wrap .fcrm_bulk_item .fcrm_bulk_select{width:170px}.fcrm_company_contacts_wrap .fcrm_bulk_action_bar .fcrm_bulk_action_left .fcrm_bulk_action_inline .fcrm_bulk_navs{display:none!important}.fcrm_company_contacts_wrap .fcrm_bulk_action_bar .fcrm_bulk_action_left .fcrm_bulk_action_dropdown_wrap{display:inline-flex}.fcrm_company_contacts_wrap .fcrm_bulk_action_bar .fcrm_bulk_action_left .fcrm_bulk_action_dropdown{height:32px;min-height:32px;padding:6px 12px;border-radius:8px;background:var(--fc-primary-bg);border:1px solid var(--fc-primary-border);color:var(--fc-secondary-text);font-size:14px;font-weight:500;line-height:20px;letter-spacing:-.6px;display:inline-flex;align-items:center;gap:4px;cursor:pointer;transition:border-color .2s ease,box-shadow .2s ease,background-color .2s ease;box-shadow:0 1px 2px #0a0d1408;-webkit-appearance:none;-moz-appearance:none;appearance:none;margin:0}.fcrm_company_contacts_wrap .fcrm_bulk_action_bar .fcrm_bulk_action_left .fcrm_bulk_action_dropdown span{color:var(--fc-secondary-text)}.fcrm_company_contacts_wrap .fcrm_bulk_action_bar .fcrm_bulk_action_left .fcrm_bulk_action_dropdown .el-icon--right{margin-left:4px;width:20px;height:20px;color:var(--fc-secondary-text);flex-shrink:0}.fcrm_company_contacts_wrap .fcrm_bulk_action_bar .fcrm_bulk_action_left .fcrm_bulk_action_dropdown:hover{background:var(--fc-secondary-bg);border-color:var(--fc-secondary-border)}.fcrm_company_contacts_wrap .fcrm_bulk_action_bar .fcrm_bulk_action_left .fcrm_bulk_action_dropdown:focus-visible{outline:none;border-color:var(--fc-text-link);box-shadow:0 0 0 2px #335cff1f}.fcrm_company_contacts_wrap .fcrm_bulk_action_bar .fcrm_bulk_action_left .el-dropdown-menu .el-dropdown-menu__item.is-active{background-color:var(--fc-secondary-bg);color:var(--fc-primary-text);font-weight:500}.fcrm_company_contacts_wrap .fcrm_bulk_action_bar .fcrm_bulk_action_left .el-dropdown-menu .el-dropdown-menu__item:hover{background-color:var(--fc-secondary-bg)}.fcrm_company_contacts_wrap .fcrm_bulk_action_bar .fcrm_bulk_action_right{display:flex;align-items:center;gap:12px}.fcrm_company_contacts_wrap .fcrm_bulk_action_bar .fcrm_bulk_action_right .fcrm_deselect_all_link{font-size:14px;font-weight:400;line-height:20px;color:var(--fc-primary-text);text-decoration:underline;text-underline-position:from-font;text-decoration-skip-ink:none;cursor:pointer;transition:color .2s ease;white-space:nowrap}.fcrm_company_contacts_wrap .fcrm_bulk_action_bar .fcrm_bulk_action_right .fcrm_deselect_all_link:hover{color:var(--fc-deep-bg)}.fcrm_company_contacts_wrap .fcrm_search_box{display:flex;align-items:center;gap:12px;flex:0 0 auto}.fcrm_company_contacts_wrap .fcrm_search_box .el-button.fcrm-search-toggle{width:32px;height:32px;padding:0;border-radius:8px;background:var(--fc-secondary-bg);border:1px solid var(--fc-primary-border);font-size:18px}.fcrm_company_contacts_wrap .fcrm_search_box .fcrm_column_toggler_checks,.fcrm_company_contacts_wrap .fcrm_search_box .el-dropdown__popper .fcrm_filter_dropdown .fcrm_column_toggler_checks{max-height:400px;overflow-y:auto;padding:8px 0}.fcrm_company_contacts_wrap .fcrm_search_box .fcrm_column_toggler_checks .fcrm_checkbox_group_label,.fcrm_company_contacts_wrap .fcrm_search_box .el-dropdown__popper .fcrm_filter_dropdown .fcrm_column_toggler_checks .fcrm_checkbox_group_label{padding:8px 12px;font-size:14px;font-weight:500;line-height:20px;color:var(--fc-primary-text);border-bottom:1px solid var(--fc-primary-border);margin-bottom:4px}.fcrm_company_contacts_wrap .fcrm_search_box .fcrm_primary_button,.fcrm_company_contacts_wrap .fcrm_search_box .el-dropdown__popper .fcrm_filter_dropdown .fcrm_primary_button{background-color:var(--fc-deep-bg);border-color:var(--fc-deep-bg);color:var(--fc-primary-bg);font-size:14px;font-weight:500;line-height:20px;border-radius:6px;transition:background-color .2s ease,border-color .2s ease}.fcrm_company_contacts_wrap .fcrm_search_box .fcrm_primary_button:hover,.fcrm_company_contacts_wrap .fcrm_search_box .el-dropdown__popper .fcrm_filter_dropdown .fcrm_primary_button:hover,.fcrm_company_contacts_wrap .fcrm_search_box .fcrm_primary_button:active,.fcrm_company_contacts_wrap .fcrm_search_box .el-dropdown__popper .fcrm_filter_dropdown .fcrm_primary_button:active{background-color:var(--fc-primary-text);border-color:var(--fc-primary-text)}.fcrm_company_contacts_wrap .fcrm_filter_dropdown{max-height:400px;overflow-y:auto}.fcrm_company_contacts_wrap .fcrm_contacts_table.fcrm_contacts_table--skeleton .el-skeleton{padding:0;gap:8px;display:flex;align-items:flex-start;flex-wrap:wrap}.fcrm_company_contacts_wrap .fcrm_contacts_table.fcrm_contacts_table--skeleton .fcrm_contact_cell--skeleton{display:flex;align-items:center;gap:12px}.fcrm_company_contacts_wrap .fcrm_contacts_table.fcrm_contacts_table--skeleton .fcrm_contact_cell--skeleton .fcrm_contact_info{display:flex;flex-direction:column}.fcrm_company_contacts_wrap .fcrm_contacts_table.fcrm_contacts_table--skeleton .fcrm_pills--skeleton,.fcrm_company_contacts_wrap .fcrm_contacts_table.fcrm_contacts_table--skeleton .fcrm_company_cell--skeleton,.fcrm_company_contacts_wrap .fcrm_contacts_table.fcrm_contacts_table--skeleton .fcrm_datetime_cell--skeleton{display:flex;align-items:center}.fcrm_company_contacts_wrap .fcrm_contacts_table .fcrm_contacts_table:after{display:none}.fcrm_company_contacts_wrap .fcrm_contacts_table .el-table__border-left-patch{display:none}.fcrm_company_contacts_wrap .fcrm_contacts_table #fluentcrm-subscribers-table:before,.fcrm_company_contacts_wrap .fcrm_contacts_table .el-table__inner-wrapper:before{display:none}.fcrm_company_contacts_wrap .fcrm_contacts_table .fcrm_empty_state{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:16px;color:var(--fc-secondary-text)}.fcrm_company_contacts_wrap .fcrm_contacts_table .fcrm_empty_state .fcrm_empty_icon{width:83px;height:64px;flex-shrink:0}.fcrm_company_contacts_wrap .fcrm_contacts_table .fcrm_empty_state .fcrm_empty_state_text{display:flex;align-items:center;gap:4px;flex-wrap:wrap;justify-content:center;flex-direction:column}.fcrm_company_contacts_wrap .fcrm_contacts_table .fcrm_empty_state .fcrm_empty_state_text span{font-size:14px;font-weight:400;line-height:20px;color:var(--fc-text-muted);font-style:normal;margin-bottom:16px}.fcrm_company_contacts_wrap .fcrm_contacts_table .fcrm_empty_state .fcrm_empty_state_text .fcrm_empty_state_link{color:var(--fc-deep-bg);font-feature-settings:"ss11" on,"cv09" on,"liga" off,"calt" off;font-size:14px;font-style:normal;font-weight:500;line-height:20px;letter-spacing:-.084px;cursor:pointer;transition:opacity .2s ease;margin-left:8px}.fcrm_company_contacts_wrap .fcrm_contacts_table .fcrm_empty_state .fcrm_empty_state_text .fcrm_empty_state_link:hover{opacity:.8}.fcrm_contact_cell,.fcrm_photo_text{display:flex;align-items:center;gap:12px;cursor:pointer;padding:0;text-decoration:none}.fcrm_contact_cell .fcrm_contact_cell_avatar,.fcrm_photo_text .fcrm_contact_cell_avatar{width:36px;height:36px;flex-shrink:0;border-radius:999px;overflow:hidden;background:var(--fc-secondary-bg);display:flex;align-items:center;justify-content:center}.fcrm_contact_cell .fcrm_contact_cell_avatar .fcrm_contact_photo,.fcrm_photo_text .fcrm_contact_cell_avatar .fcrm_contact_photo{width:100%;height:100%;object-fit:cover;object-position:center}.fcrm_contact_cell .fcrm_contact_cell_avatar .fcrm_contact_cell_avatar_placeholder,.fcrm_photo_text .fcrm_contact_cell_avatar .fcrm_contact_cell_avatar_placeholder{width:100%;height:100%;display:flex;align-items:center;justify-content:center;font-size:16px;font-weight:500;color:var(--fc-secondary-text);background:var(--fc-warning-bg)}.fcrm_contact_cell .fcrm_contact_photo,.fcrm_photo_text .fcrm_contact_photo{width:36px;height:36px;border-radius:999px;flex-shrink:0;object-fit:cover}.fcrm_contact_cell .fcrm_empty_image,.fcrm_photo_text .fcrm_empty_image{background:var(--fc-secondary-bg)}.fcrm_contact_cell .fcrm_contact_info,.fcrm_contact_cell>div:not(.fcrm_contact_photo),.fcrm_photo_text .fcrm_contact_info,.fcrm_photo_text>div:not(.fcrm_contact_photo){display:flex;flex-direction:column;flex:1;min-width:0}.fcrm_contact_cell .fcrm_contact_info .fcrm_contact_name,.fcrm_contact_cell>div:not(.fcrm_contact_photo) .fcrm_contact_name,.fcrm_photo_text .fcrm_contact_info .fcrm_contact_name,.fcrm_photo_text>div:not(.fcrm_contact_photo) .fcrm_contact_name{font-size:14px;font-weight:500;line-height:20px;color:var(--fc-primary-text);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;display:flex;align-items:center;gap:4px}.fcrm_contact_cell .fcrm_contact_info .fcrm_contact_name .el-tooltip__trigger,.fcrm_contact_cell>div:not(.fcrm_contact_photo) .fcrm_contact_name .el-tooltip__trigger,.fcrm_photo_text .fcrm_contact_info .fcrm_contact_name .el-tooltip__trigger,.fcrm_photo_text>div:not(.fcrm_contact_photo) .fcrm_contact_name .el-tooltip__trigger{color:var(--fc-text-muted)}.fcrm_contact_cell .fcrm_contact_info .fcrm_contact_email,.fcrm_contact_cell>div:not(.fcrm_contact_photo) .fcrm_contact_email,.fcrm_photo_text .fcrm_contact_info .fcrm_contact_email,.fcrm_photo_text>div:not(.fcrm_contact_photo) .fcrm_contact_email{font-size:12px;font-weight:400;line-height:16px;color:var(--fc-secondary-text);overflow:hidden;text-overflow:ellipsis;white-space:break-spaces;overflow-wrap:anywhere;display:flex;align-items:center;gap:4px}.fcrm_copy-text{cursor:pointer}.fcrm_contacts_table .el-table__body tr td .fcrm_copy-text{font-size:12px}.fcrm_contacts_table.fcrm_contacts_table--compact .el-table__header-wrapper .el-table__cell,.fcrm_contacts_table.fcrm_contacts_table--compact .el-table__body-wrapper .el-table__cell{padding:6px 0}.fcrm_contacts_table.fcrm_contacts_table--compact .el-table__header-wrapper .el-table__cell .cell,.fcrm_contacts_table.fcrm_contacts_table--compact .el-table__body-wrapper .el-table__cell .cell{padding-left:8px;padding-right:8px;line-height:20px}.fcrm_contacts_table.fcrm_contacts_table--compact .fcrm_contact_cell,.fcrm_contacts_table.fcrm_contacts_table--compact .fcrm_photo_text{gap:8px}.fcrm_contacts_table.fcrm_contacts_table--compact .fcrm_contact_cell .fcrm_contact_photo,.fcrm_contacts_table.fcrm_contacts_table--compact .fcrm_contact_cell .fcrm_contact_cell_avatar,.fcrm_contacts_table.fcrm_contacts_table--compact .fcrm_photo_text .fcrm_contact_photo,.fcrm_contacts_table.fcrm_contacts_table--compact .fcrm_photo_text .fcrm_contact_cell_avatar{width:30px;height:30px}.fcrm_contacts_table.fcrm_contacts_table--compact .fcrm_badge{padding:1px 6px;line-height:14px}.fcrm_contacts_table .fcrm_segment_more_badge{cursor:default;color:var(--fc-secondary-text);background:var(--fc-secondary-bg)}.fcrm_horizontal_filter_actions{display:flex;align-items:center;gap:8px}.fcrm_horizontal_filter_actions .fcrm_compact_view_toggle.is-active{color:var(--fc-deep-bg);border-color:var(--fc-deep-bg);background:var(--fc-secondary-bg)}.fcrm_header-secondary{width:100%}.fcrm_action_menu .fcrm_btns,.fcrm_actions .fcrm_btns{display:flex;gap:8px;flex-wrap:wrap}.fcrm_action_menu .fcrm_btns .el-button,.fcrm_actions .fcrm_btns .el-button{margin:0}.fcrm-normal-filter-popover{width:auto!important}.fcrm_header_breadcrumb_wrapper .el-breadcrumb .el-breadcrumb__item{float:none}.fcrm_contacts_filter_popover{padding:0!important;border:1px solid var(--fc-primary-border)!important;box-shadow:0 16px 32px -12px #0e121b1a!important;overflow:hidden}.fcrm_contacts_filter_popover .fcrm_filter_menu_items_container{display:flex;padding:8px;flex-direction:column;align-items:flex-start;gap:4px}.fcrm_contacts_filter_popover .fcrm_filter_menu_items_container .fcrm_filter_menu_item{display:flex;width:100%;padding:8px;align-items:center;gap:8px;cursor:pointer;border-radius:8px;transition:background-color .2s ease}.fcrm_contacts_filter_popover .fcrm_filter_menu_items_container .fcrm_filter_menu_item:hover{background-color:var(--fc-secondary-bg)}.fcrm_contacts_filter_popover .fcrm_filter_category_header{display:flex;align-items:center;gap:8px;padding:10px 12px;border-bottom:1px solid var(--fc-primary-border);background-color:var(--fc-primary-bg)}.fcrm_contacts_filter_popover .fcrm_filter_back_button{display:flex;align-items:center;justify-content:center;width:20px;height:20px;padding:2px;border-radius:6px;border:none;background:transparent;cursor:pointer;color:var(--fc-secondary-text);transition:background-color .2s ease}.fcrm_contacts_filter_popover .fcrm_filter_back_button:hover{background-color:var(--fc-secondary-bg)}.fcrm_contacts_filter_popover .fcrm_filter_back_button .el-icon{width:16px;height:16px;font-size:16px}.fcrm_contacts_filter_popover .fcrm_filter_category_title{font-size:14px;font-weight:500;line-height:20px;color:var(--fc-secondary-text)}.fcrm_contacts_filter_popover .fcrm_filter_search_item{padding:0;border-bottom:1px solid var(--fc-primary-border);cursor:pointer}.fcrm_contacts_filter_popover .fcrm_filter_search_input .el-input__wrapper{border:none!important;outline:none!important;box-shadow:none!important;padding:6px}.fcrm_contacts_filter_popover .fcrm_filter_search_input .el-input__wrapper .el-input__prefix{font-size:16px}.fcrm_contacts_filter_popover .fcrm_filter_options_container{padding:0;max-height:300px;overflow-y:auto;background-color:var(--fc-primary-bg)}.fcrm_contacts_filter_popover .fcrm_filter_options_list{padding:8px;display:flex;flex-direction:column;gap:4px}.fcrm_contacts_filter_popover .fcrm_filter_option_item{padding:8px;display:flex;align-items:flex-start;gap:8px}.fcrm_contacts_filter_popover .fcrm_filter_no_results{padding:24px 16px;text-align:center}.fcrm_contacts_filter_popover .fcrm_filter_no_results .fcrm_filter_no_results_text{margin:0;font-size:14px;font-weight:400;line-height:20px;color:var(--fc-text-muted)}.fcrm_contacts_filter_popover .fcrm_filter_menu_icon{width:20px;height:20px;font-size:20px;color:var(--fc-secondary-text);flex-shrink:0}.fcrm_contacts_filter_popover .fcrm_filter_menu_text{font-size:14px;font-weight:400;line-height:20px;color:var(--fc-primary-text);flex:1 0 0}.fcrm_contacts_filter_popover .fcrm_filter_confirm_footer{padding:10px;text-align:center;border-top:1px solid var(--fc-primary-border);background-color:var(--fc-primary-bg)}.fcrm_filter_toggle_btn.is-active{color:var(--fc-primary-text);border-color:var(--fc-primary-text);background:var(--fc-secondary-bg)}.fcrm_filter_editor .el-button{display:flex;padding:2px;justify-content:center;align-items:center;gap:2px;border-radius:6px;border:1px solid var(--fc-primary-border);background:var(--fc-primary-bg);box-shadow:0 1px 2px #0a0d1408}.fcrm_filter_editor .el-button:hover{background:var(--fc-secondary-bg)}.fcrm-pagination.fcrm-pagination-bar .fcrm_mt-5{margin-top:5px}.fcrm_alerts_wrapper{padding:0 20px;margin-bottom:16px}.fcrm_alerts_wrapper ul{margin:0;display:flex;flex-direction:column;gap:8px;align-items:flex-start;list-style:none;padding:0}.fcrm_alerts_wrapper ul li{border:1px solid var(--fc-primary-border);box-shadow:0 16px 32px -12px #0e121b1a;border-radius:8px;overflow:hidden;color:var(--fc-primary-text);font-size:14px;line-height:20px;padding:8px 8px 8px 12px;position:relative;margin:0}.fcrm_alerts_wrapper ul li:before{content:"";width:3px;height:100%;background:var(--fc-text-muted);left:0;top:0;position:absolute}.fcrm_alerts_wrapper ul li a{color:var(--fc-primary-text);text-decoration:underline;font-size:14px;line-height:20px;display:inline-block;font-weight:500}.fcrm_bulk_contact_custom_fields{display:flex;align-items:center}.fcrm_bulk_contact_custom_fields .fcrm_bulk_select{width:250px}.fcrm_bulk_contact_custom_fields .fcrm_custom_field_form{padding:10px 0}.fcrm_bulk_contact_custom_fields .fcrm_popover_header{display:flex;align-items:center;margin-bottom:15px;padding-bottom:10px;border-bottom:1px solid var(--fc-primary-border)}.fcrm_bulk_contact_custom_fields .fcrm_back_button{padding:5px 8px;margin-right:10px}.fcrm_bulk_contact_custom_fields .fcrm_field_label{font-weight:600;font-size:14px;color:var(--fc-primary-text)}.fcrm_bulk_contact_custom_fields .fcrm_full_width{width:100%}.fcrm_bulk_contact_custom_fields .el-date-picker{display:flex;height:32px;padding:6px 6px 6px 8px;align-items:center;gap:6px;align-self:stretch}.fcrm_bulk_contact_custom_fields .fcrm_popover_actions{margin-top:16px;display:flex;gap:8px;justify-content:flex-start}.fcrm_bulk_contact_custom_fields .fcrm_popover_actions .el-button{display:flex;padding:4px 6px;justify-content:center;align-items:center;gap:2px;border-radius:8px;background:var(--fc-deep-bg)}.fcrm_bulk_contact_custom_fields .fcrm_popover_actions .el-button span{color:var(--fc-primary-bg);font-feature-settings:"ss11" on,"liga" off,"calt" off;font-size:14px;font-style:normal;font-weight:500;line-height:20px;letter-spacing:-.084px}.fcrm_bulk_contact_custom_fields .fcrm_checkbox_group{margin:10px 0}.fcrm_bulk_contact_custom_fields .fcrm_checkbox{display:flex;align-items:flex-start;margin-bottom:0}.fcrm_bulk_contact_custom_fields .fcrm-radio{margin-right:10px}.fcrm_active_filters_bar{background-color:var(--fc-primary-bg);border-top:1px solid var(--fc-primary-border);padding:10px 20px 0;margin-top:10px;margin-left:-20px;margin-right:-20px}.fcrm_active_filters_bar .fcrm_active_filters_content{display:flex;align-items:center;justify-content:space-between;gap:20px;flex-wrap:wrap}.fcrm_active_filters_bar .fcrm_active_filters_groups{display:flex;flex-wrap:wrap;gap:8px;flex:1;align-items:center}.fcrm_active_filters_bar .fcrm_filter_group{display:flex;background-color:var(--fc-primary-bg);border:1px solid var(--fc-primary-border);border-radius:8px;overflow:hidden}.fcrm_active_filters_bar .fcrm_filter_group_label{display:flex;gap:5px;align-items:center;padding:4px 10px;background-color:var(--fc-primary-bg);border:none;border-right:1px solid var(--fc-primary-border);font-size:14px;font-weight:400;line-height:20px;letter-spacing:-.084px;color:var(--fc-secondary-text);cursor:pointer;white-space:nowrap}.fcrm_active_filters_bar .fcrm_filter_group_label:hover{background-color:var(--fc-secondary-bg)}.fcrm_active_filters_bar .fcrm_filter_group_label svg{width:18px;height:18px}.fcrm_active_filters_bar .fcrm_filter_group_items{display:flex;align-items:center;gap:6px;padding:4px 10px;flex-wrap:wrap}.fcrm_active_filters_bar .fcrm_filter_tag{display:flex;align-items:center;gap:2px;background-color:var(--fc-secondary-bg);border-radius:6px;padding:2px 4px 2px 8px}.fcrm_active_filters_bar .fcrm_filter_tag_text{font-size:12px;font-weight:500;line-height:16px;color:var(--fc-secondary-text)}.fcrm_active_filters_bar .fcrm_filter_tag_close{display:flex;align-items:center;justify-content:center;width:16px;height:16px;padding:0;border:none;background:transparent;cursor:pointer;color:var(--fc-secondary-text);flex-shrink:0}.fcrm_active_filters_bar .fcrm_filter_tag_close:hover{color:var(--fc-primary-text)}.fcrm_active_filters_bar .fcrm_filter_tag_close .el-icon{width:16px;height:16px;font-size:16px}.fcrm_active_filters_bar .fcrm_filter_group_dropdown{display:flex;align-items:center;justify-content:center;width:20px;height:20px;padding:0;border:none;background:transparent;cursor:pointer;color:var(--fc-secondary-text);flex-shrink:0}.fcrm_active_filters_bar .fcrm_filter_group_dropdown:hover{color:var(--fc-primary-text)}.fcrm_active_filters_bar .fcrm_filter_group_dropdown .el-icon{width:20px;height:20px;font-size:20px}.fcrm_active_filters_bar .fcrm_filter_group_clear{display:flex;align-items:center;justify-content:center;width:30px;padding:6px;border:none;border-left:1px solid var(--fc-primary-border);background:transparent;cursor:pointer;color:var(--fc-secondary-text);flex-shrink:0}.fcrm_active_filters_bar .fcrm_filter_group_clear:hover{background-color:var(--fc-secondary-bg);color:var(--fc-primary-text)}.fcrm_active_filters_bar .fcrm_filter_group_clear .el-icon{width:18px;height:18px;font-size:18px}.fcrm_active_filters_bar .fcrm_reset_all_filters{flex-shrink:0}.fcrm_filter_dropdown_popover{padding:0!important;border-radius:8px!important;border:1px solid var(--fc-primary-border)!important;box-shadow:0 4px 12px #0e121b14!important;overflow:hidden}.fcrm_filter_dropdown_popover .fcrm_filter_dropdown_content{padding:8px 0;max-height:300px;overflow-y:auto}.fcrm_filter_dropdown_popover .fcrm_filter_dropdown_title{padding:8px 12px;font-size:14px;font-weight:500;line-height:20px;letter-spacing:-.084px;color:var(--fc-primary-text);border-bottom:1px solid var(--fc-primary-border);margin-bottom:4px}.fcrm_filter_dropdown_popover .fcrm_filter_dropdown_checkboxes{display:flex;flex-direction:column;gap:0}.fcrm_filter_dropdown_popover .fcrm_filter_dropdown_item{padding:8px 12px}.fcrm_filter_dropdown_popover .fcrm_filter_dropdown_item:hover{background-color:var(--fc-secondary-bg)}.fcrm_filter_dropdown_popover .fcrm_filter_dropdown_checkbox{width:100%}.fcrm_subscribers-export-dialog .fcrm_export_content .fcrm_export_form_item h3{margin:0 0 16px;font-weight:600;font-size:16px;line-height:24px;letter-spacing:-.12px;color:var(--fc-primary-text)}.fcrm_subscribers-export-dialog .fcrm_export_content .fcrm_export_form_item .fcrm_check_all_wrapper{margin-bottom:16px}.fcrm_subscribers-export-dialog .fcrm_export_content .fcrm_export_form_item .fcrm_2_col_items{display:grid;grid-template-columns:repeat(2,1fr);gap:16px;margin-bottom:24px;margin-left:30px}.fcrm_subscribers-export-dialog .fcrm_export_content .fcrm_export_limit_section{background-color:var(--fc-secondary-bg);padding:20px;border-radius:8px;margin-top:20px}.fcrm_subscribers-export-dialog .fcrm_export_content .fcrm_export_status{margin:16px 0 0;font-size:14px;color:var(--fc-secondary-text)}.fcrm_subscribers-export-dialog .el-dialog__footer{padding:12px 20px;border-top:1px solid var(--fc-primary-border)}.fcrm_subscribers-export-dialog .el-dialog__footer .fcrm_export_footer_actions{display:flex;gap:12px;justify-content:flex-end}.fcrm_subscribers-export-dialog .el-dialog__footer .fcrm_export_footer_actions .el-button{margin:0}.fcrm_contacts_importer{max-width:640px;max-height:80vh;display:flex;flex-direction:column;overflow:hidden}.fcrm_contacts_importer.fcrm_import_done .el-dialog__header{border-bottom:none;padding-bottom:0}.fcrm_contacts_importer .el-dialog__header{flex-shrink:0;border-bottom:1px solid var(--fc-primary-border);background:var(--fc-primary-bg);padding:16px 20px!important;display:flex;align-items:center;justify-content:space-between}.fcrm_contacts_importer .el-dialog__header .el-dialog__title{color:var(--fc-primary-text);font-size:16px;font-style:normal;font-weight:500;line-height:24px;letter-spacing:-.176px}.fcrm_contacts_importer .el-dialog__header .el-dialog__headerbtn{position:relative;top:0;right:0;width:20px;height:20px;padding:0;margin:0;display:flex;align-items:center;justify-content:center;border-radius:6px;transition:background-color .2s ease;background:transparent}.fcrm_contacts_importer .el-dialog__header .el-dialog__headerbtn .el-icon,.fcrm_contacts_importer .el-dialog__header .el-dialog__headerbtn .el-dialog__close{color:var(--fc-secondary-text);font-size:20px;width:20px;height:20px;transition:color .2s ease}.fcrm_contacts_importer .el-dialog__header .el-dialog__headerbtn .el-icon svg,.fcrm_contacts_importer .el-dialog__header .el-dialog__headerbtn .el-dialog__close svg{width:20px;height:20px}.fcrm_contacts_importer .el-dialog__header .el-dialog__headerbtn:hover .el-icon,.fcrm_contacts_importer .el-dialog__header .el-dialog__headerbtn:hover .el-dialog__close{color:var(--fc-primary-text)}.fcrm_contacts_importer .el-dialog__footer{flex-shrink:0;padding:16px 20px;background:var(--fc-primary-bg)}.fcrm_contacts_importer .el-dialog__footer .fcrm_dialog_footer{padding:0;display:flex;flex-direction:row;gap:12px;align-items:center}.fcrm_contacts_importer .el-dialog__footer .fcrm_dialog_footer:has(.fcrm_dialog_footer_steps_section){justify-content:space-between}.fcrm_contacts_importer .el-dialog__footer .fcrm_dialog_footer:not(:has(.fcrm_dialog_footer_steps_section)){justify-content:flex-end}.fcrm_contacts_importer .el-dialog__footer .fcrm_dialog_footer .fcrm_dialog_footer_buttons{display:flex;flex-direction:row;gap:12px;align-items:center}.fcrm_contacts_importer .el-dialog__footer .fcrm_dialog_footer .fcrm_dialog_footer_buttons .el-button{margin:0}.fcrm_contacts_importer .el-dialog__footer .fcrm_dialog_footer .fcrm_dialog_footer_csv_upload{display:flex;align-items:center;gap:12px}.fcrm_contacts_importer .el-dialog__footer .fcrm_dialog_footer .fcrm_dialog_footer_steps_section{display:flex;flex-direction:column;gap:5px;align-items:flex-start}.fcrm_contacts_importer .el-dialog__footer .fcrm_dialog_footer .fcrm_dialog_footer_steps_section p{margin:0;font-size:14px;font-style:normal;font-weight:500;line-height:20px;letter-spacing:-.084px;color:var(--fc-secondary-text);font-weight:400;font-size:12px}.fcrm_contacts_importer .el-dialog__footer .fcrm_dialog_footer .fcrm_dialog_footer_steps_section .fcrm_dialog_footer_steps{display:flex;gap:5px}.fcrm_contacts_importer .el-dialog__footer .fcrm_dialog_footer .fcrm_dialog_footer_steps_section .fcrm_dialog_footer_steps .fcrm_dialog_footer_step{width:40px;height:6px;border-radius:var(--radius-10, 10px);background:var(--fc-light-bg)}.fcrm_contacts_importer .el-dialog__footer .fcrm_dialog_footer .fcrm_dialog_footer_steps_section .fcrm_dialog_footer_steps .fcrm_dialog_footer_step.active{border-radius:var(--radius-10, 10px);background:var(--fc-deep-bg)}.fcrm_contacts_importer .el-dialog__footer .fcrm_dialog_footer .fcrm_dialog_footer_button_csv_upload{min-width:160px}.fcrm_contacts_importer .el-dialog__footer .fcrm_dialog_footer .fcrm_dialog_footer_button{display:flex;padding:8px 12px;justify-content:center;align-items:center;gap:4px;border-radius:var(--radius-8, 8px)}.fcrm_contacts_importer .el-dialog__body{flex:1;min-height:0;overflow:auto;padding:20px;border-bottom:1px solid var(--fc-primary-border);background:var(--fc-primary-bg)}.fcrm_contacts_importer .el-dialog__body .fcrm_import_source_select{position:relative;display:flex;flex-direction:column;justify-content:center;align-items:flex-start;gap:12px;align-self:stretch}.fcrm_contacts_importer .el-dialog__body .fcrm_import_source_select h4{color:var(--fc-primary-text);font-size:14px;font-style:normal;font-weight:500;line-height:20px;letter-spacing:-.084px;margin:0}.fcrm_contacts_importer .el-dialog__body .fcrm_import_source_select .fcrm_inline_image_radio{margin-bottom:12px}.fcrm_contacts_importer .el-dialog__body .fcrm_import_source_select .fcrm_import_source_select_header{margin-bottom:24px}.fcrm_contacts_importer .el-dialog__body .fcrm_import_source_select .fcrm_import_source_select_header h4{color:var(--fc-primary-text);font-size:16px;font-style:normal;font-weight:600;line-height:24px;letter-spacing:-.096px;margin:0 0 8px}.fcrm_contacts_importer .el-dialog__body .fcrm_import_source_select .fcrm_import_source_select_header p{color:var(--fc-text-muted);font-size:14px;font-style:normal;font-weight:400;line-height:20px;letter-spacing:-.084px;margin:0}.fcrm_contacts_importer .el-dialog__body .fcrm_import_source_select .fcrm_import_others_doc_link{margin:0}.fcrm_contacts_importer .el-dialog__body .fcrm_import_source_select .fcrm_import_others_doc_link a{color:var(--fc-deep-bg);font-size:14px;font-style:normal;font-weight:500;line-height:20px;letter-spacing:-.084px;font-size:12px;display:inline-flex;align-items:center;gap:4px}.fcrm_contacts_importer .el-dialog__body .fcrm_import_source_select .fcrm_import_others_doc_link span{margin-left:5px;font-size:14px;font-style:normal;font-weight:500;line-height:20px;letter-spacing:-.084px;color:var(--fc-secondary-text);font-weight:400;font-size:12px}.fcrm_contacts_importer .el-dialog__body .fcrm_import_source_select .fcrm_inline_image_radio{display:grid;grid-template-columns:repeat(3,1fr);width:100%;gap:16px;margin-top:16px}.fcrm_contacts_importer .el-dialog__body .fcrm_import_source_select .fcrm_inline_image_radio .fcrm_option{position:relative;width:186.667px;padding:4px 4px 8px;gap:8px;border-radius:8px;border:1px solid var(--fc-primary-border);background:var(--fc-primary-bg)}.fcrm_contacts_importer .el-dialog__body .fcrm_import_source_select .fcrm_inline_image_radio .fcrm_option .fcrm_pro_badge{position:absolute;right:8px;top:8px}.fcrm_contacts_importer .el-dialog__body .fcrm_import_source_select .fcrm_inline_image_radio .fcrm_option .fcrm_sources_details{display:flex;flex-direction:column;align-items:flex-start;width:100%;height:100%;gap:12px}.fcrm_contacts_importer .el-dialog__body .fcrm_import_source_select .fcrm_inline_image_radio .fcrm_option .fcrm_sources_details .fcrm_source_logo{display:flex;padding:34px 0;justify-content:center;align-items:center;align-self:stretch;border-radius:4px;background:var(--fc-secondary-bg)}.fcrm_contacts_importer .el-dialog__body .fcrm_import_source_select .fcrm_inline_image_radio .fcrm_option .fcrm_sources_details .fcrm_source_logo .fcrm_source_logo_holder{display:flex;padding:8px;justify-content:center;align-items:center;border-radius:10px;border:1px solid var(--fc-primary-border);background:var(--fc-primary-bg);box-shadow:0 -.5px .5px #33333314 inset,0 0 0 1px #3333330a,0 16px 8px -8px #33333303,0 12px 6px -6px #33333305,0 5px 5px -2.5px #33333314,0 1px 3px -1.5px #33333329}.fcrm_contacts_importer .el-dialog__body .fcrm_import_source_select .fcrm_inline_image_radio .fcrm_option .fcrm_sources_details .fcrm_source_logo .fcrm_source_logo_holder.fcrm_csv_icon{position:relative}.fcrm_contacts_importer .el-dialog__body .fcrm_import_source_select .fcrm_inline_image_radio .fcrm_option .fcrm_sources_details .fcrm_source_logo .fcrm_source_logo_holder.fcrm_csv_icon .fcrm_csv_export_icon_document{height:32px;width:32px}.fcrm_contacts_importer .el-dialog__body .fcrm_import_source_select .fcrm_inline_image_radio .fcrm_option .fcrm_sources_details .fcrm_source_logo .fcrm_source_logo_holder.fcrm_csv_icon .fcrm_csv_export_icon_fold{position:absolute;top:9px;left:21px}.fcrm_contacts_importer .el-dialog__body .fcrm_import_source_select .fcrm_inline_image_radio .fcrm_option .fcrm_sources_details .fcrm_source_logo .fcrm_source_logo_holder.fcrm_csv_icon .fcrm_csv_export_icon_label{top:23px;left:5px;position:absolute;background:var(--fc-success);display:inline-flex;padding:2px 3px;align-items:center;gap:6.4px;border-radius:4px;color:var(--fc-text-inverse);font-size:8.8px;font-style:normal;font-weight:600;line-height:9.6px;letter-spacing:.176px;text-transform:uppercase}.fcrm_contacts_importer .el-dialog__body .fcrm_import_source_select .fcrm_inline_image_radio .fcrm_option .fcrm_sources_details .fcrm_source_logo .fcrm_source_logo_holder.fcrm_wordpress_icon .fcrm_wordpress_logo,.fcrm_contacts_importer .el-dialog__body .fcrm_import_source_select .fcrm_inline_image_radio .fcrm_option .fcrm_sources_details .fcrm_source_logo .fcrm_source_logo_holder.fcrm_fluent_cart_icon .fcrm_fluent_cart_logo{display:block;width:32px;height:32px}.fcrm_contacts_importer .el-dialog__body .fcrm_import_source_select .fcrm_inline_image_radio .fcrm_option .fcrm_sources_details .fcrm_source_logo .fcrm_source_logo_holder.fcrm_woocommerce_icon .fcrm_woocommerce_logo{display:block;width:auto;height:32px;max-width:100%}.fcrm_contacts_importer .el-dialog__body .fcrm_import_source_select .fcrm_inline_image_radio .fcrm_option .fcrm_sources_details .fcrm_source_label{display:flex;flex-direction:column;padding:8px;align-items:center;justify-content:center;gap:2px;flex:1 0 0;align-self:stretch}.fcrm_contacts_importer .el-dialog__body .fcrm_import_source_select .fcrm_inline_image_radio .fcrm_option .fcrm_sources_details .fcrm_source_label .fcrm_text_label{flex:0 0 auto;color:var(--fc-primary-text);text-align:center;font-size:14px;font-style:normal;font-weight:500;line-height:20px;letter-spacing:-.084px;word-wrap:break-word;white-space:break-spaces}.fcrm_contacts_importer .el-dialog__body .fcrm_import_source_select .fcrm_inline_image_radio .fcrm_option .fcrm_sources_details .fcrm_source_label .fcrm_source_formerly{flex:0 0 auto;color:var(--fc-primary-text);text-align:center;font-size:12px;font-weight:400;line-height:16px;letter-spacing:-.048px}.fcrm_contacts_importer .el-dialog__body .fcrm_import_source_select .fcrm_inline_image_radio .fcrm_option .fcrm_sources_details .fcrm_source_label .fcrm_source_formerly strong{font-weight:600}.fcrm_contacts_importer .el-dialog__body .fcrm_import_source_select .fcrm_inline_image_radio .fcrm_option.is-checked{border-color:var(--fc-deep-bg)}.fcrm_contacts_importer .el-dialog__body .fcrm_import_source_select .fcrm_inline_image_radio .fcrm_option:hover:not(.is-checked){border-color:var(--fc-secondary-border)}.fcrm_contacts_importer .el-dialog__body .fcrm_import_source_select .fcrm_inline_image_radio .fcrm_option span.el-radio__input{display:none}.fcrm_contacts_importer .el-dialog__body .fcrm_import_source_select .fcrm_inline_image_radio .fcrm_option .el-radio__label{width:100%;height:100%;padding-left:0}.fcrm_contacts_importer .el-dialog__body .fcrm_import_source_select .fcrm_selection_step{width:100%}.fcrm_contacts_importer .el-dialog__body .fcrm_import_source_select .fcrm_selection_step .fcrm_import_auto_mapping_row{margin-top:20px;margin-bottom:20px}.fcrm_contacts_importer .el-dialog__body .fcrm_import_source_select .fcrm_selection_step .fc_step_header,.fcrm_contacts_importer .el-dialog__body .fcrm_import_source_select .fcrm_selection_step .fcrm_step_header{border:none}.fcrm_contacts_importer .el-dialog__body .fcrm_import_source_select .fcrm_selection_step .fc_step_header h3,.fcrm_contacts_importer .el-dialog__body .fcrm_import_source_select .fcrm_selection_step .fcrm_step_header h3{color:var(--fc-primary-text);font-size:14px;font-style:normal;font-weight:500;line-height:20px;letter-spacing:-.084px;margin:0}.fcrm_contacts_importer .el-dialog__body .fcrm_import_source_select .fcrm_selection_step .fc_step_header p,.fcrm_contacts_importer .el-dialog__body .fcrm_import_source_select .fcrm_selection_step .fcrm_step_header p{color:var(--fc-text-muted);font-size:14px;font-style:normal;font-weight:500;line-height:20px;letter-spacing:-.084px;font-size:12px;margin:0 0 16px}.fcrm_contacts_importer .el-dialog__body .fcrm_import_source_select .fcrm_selection_step .fcrm_import_runner h3,.fcrm_contacts_importer .el-dialog__body .fcrm_import_source_select .fcrm_selection_step .fcrm_import_runner h4,.fcrm_contacts_importer .el-dialog__body .fcrm_import_source_select .fcrm_selection_step .fcrm_import_runner h2{color:var(--fc-primary-text);font-size:14px;font-style:normal;font-weight:500;line-height:20px;letter-spacing:-.084px;margin-bottom:0}.fcrm_contacts_importer .el-dialog__body .fcrm_import_source_select .fcrm_selection_step .fcrm_import_runner h2{font-size:18px}.fcrm_contacts_importer .el-dialog__body .fcrm_import_source_select .fcrm_selection_step .fcrm_import_runner .fcrm_dialog_footer{flex-direction:row;justify-content:center}.fcrm_contacts_importer .el-dialog__body .fcrm_import_source_select .fcrm_selection_step .fc_global_form_builder .el-form-item,.fcrm_contacts_importer .el-dialog__body .fcrm_import_source_select .fcrm_selection_step .fcrm_step_body .el-form-item{margin:0}.fcrm_contacts_importer .el-dialog__body .fcrm_import_source_select .fcrm_selection_step .fc_global_form_builder .el-form-item .el-form-item__content .fcrm_import_tag_mapper,.fcrm_contacts_importer .el-dialog__body .fcrm_import_source_select .fcrm_selection_step .fcrm_step_body .el-form-item .el-form-item__content .fcrm_import_tag_mapper{width:100%}.fcrm_contacts_importer .el-dialog__body .fcrm_import_source_select .fcrm_selection_step .fc_global_form_builder .el-form-item .el-form-item__content .fcrm_import_tag_mapper .fcrm_import_tag_mapper_hidden_selector,.fcrm_contacts_importer .el-dialog__body .fcrm_import_source_select .fcrm_selection_step .fcrm_step_body .el-form-item .el-form-item__content .fcrm_import_tag_mapper .fcrm_import_tag_mapper_hidden_selector{display:none!important;visibility:hidden!important}.fcrm_contacts_importer .el-dialog__body .fcrm_import_source_select .fcrm_selection_step .fc_global_form_builder .el-form-item .el-form-item__content .fcrm_import_tag_mapper .fcrm_import_tag_mapper_table,.fcrm_contacts_importer .el-dialog__body .fcrm_import_source_select .fcrm_selection_step .fcrm_step_body .el-form-item .el-form-item__content .fcrm_import_tag_mapper .fcrm_import_tag_mapper_table{width:100%;table-layout:fixed;border-spacing:0;border-radius:var(--fcrm-border-radius-8, 8px);border:1px solid var(--fc-primary-border);background:var(--fc-primary-bg);overflow:hidden}.fcrm_contacts_importer .el-dialog__body .fcrm_import_source_select .fcrm_selection_step .fc_global_form_builder .el-form-item .el-form-item__content .fcrm_import_tag_mapper .fcrm_import_tag_mapper_table thead,.fcrm_contacts_importer .el-dialog__body .fcrm_import_source_select .fcrm_selection_step .fcrm_step_body .el-form-item .el-form-item__content .fcrm_import_tag_mapper .fcrm_import_tag_mapper_table thead{background:var(--fc-secondary-bg)}.fcrm_contacts_importer .el-dialog__body .fcrm_import_source_select .fcrm_selection_step .fc_global_form_builder .el-form-item .el-form-item__content .fcrm_import_tag_mapper .fcrm_import_tag_mapper_table thead th,.fcrm_contacts_importer .el-dialog__body .fcrm_import_source_select .fcrm_selection_step .fcrm_step_body .el-form-item .el-form-item__content .fcrm_import_tag_mapper .fcrm_import_tag_mapper_table thead th{padding:10px 16px;text-align:left;font-size:12px;font-weight:500;line-height:16px;letter-spacing:.48px;text-transform:uppercase;color:var(--fc-text-muted);border-bottom:1px solid var(--fc-primary-border);border-left:1px solid var(--fc-primary-border)}.fcrm_contacts_importer .el-dialog__body .fcrm_import_source_select .fcrm_selection_step .fc_global_form_builder .el-form-item .el-form-item__content .fcrm_import_tag_mapper .fcrm_import_tag_mapper_table thead th:first-child,.fcrm_contacts_importer .el-dialog__body .fcrm_import_source_select .fcrm_selection_step .fc_global_form_builder .el-form-item .el-form-item__content .fcrm_import_tag_mapper .fcrm_import_tag_mapper_table thead th:nth-child(2),.fcrm_contacts_importer .el-dialog__body .fcrm_import_source_select .fcrm_selection_step .fcrm_step_body .el-form-item .el-form-item__content .fcrm_import_tag_mapper .fcrm_import_tag_mapper_table thead th:first-child,.fcrm_contacts_importer .el-dialog__body .fcrm_import_source_select .fcrm_selection_step .fcrm_step_body .el-form-item .el-form-item__content .fcrm_import_tag_mapper .fcrm_import_tag_mapper_table thead th:nth-child(2){width:calc((100% - 160px)/2)}.fcrm_contacts_importer .el-dialog__body .fcrm_import_source_select .fcrm_selection_step .fc_global_form_builder .el-form-item .el-form-item__content .fcrm_import_tag_mapper .fcrm_import_tag_mapper_table thead th:first-child,.fcrm_contacts_importer .el-dialog__body .fcrm_import_source_select .fcrm_selection_step .fcrm_step_body .el-form-item .el-form-item__content .fcrm_import_tag_mapper .fcrm_import_tag_mapper_table thead th:first-child{border-left:none}.fcrm_contacts_importer .el-dialog__body .fcrm_import_source_select .fcrm_selection_step .fc_global_form_builder .el-form-item .el-form-item__content .fcrm_import_tag_mapper .fcrm_import_tag_mapper_table thead th:last-child,.fcrm_contacts_importer .el-dialog__body .fcrm_import_source_select .fcrm_selection_step .fcrm_step_body .el-form-item .el-form-item__content .fcrm_import_tag_mapper .fcrm_import_tag_mapper_table thead th:last-child{width:160px}.fcrm_contacts_importer .el-dialog__body .fcrm_import_source_select .fcrm_selection_step .fc_global_form_builder .el-form-item .el-form-item__content .fcrm_import_tag_mapper .fcrm_import_tag_mapper_table tbody tr td,.fcrm_contacts_importer .el-dialog__body .fcrm_import_source_select .fcrm_selection_step .fcrm_step_body .el-form-item .el-form-item__content .fcrm_import_tag_mapper .fcrm_import_tag_mapper_table tbody tr td{padding:10px 16px;border-top:1px solid var(--fc-secondary-bg);border-left:1px solid var(--fc-secondary-bg);font-size:14px;font-weight:400;line-height:20px;color:var(--fc-secondary-text);vertical-align:middle}.fcrm_contacts_importer .el-dialog__body .fcrm_import_source_select .fcrm_selection_step .fc_global_form_builder .el-form-item .el-form-item__content .fcrm_import_tag_mapper .fcrm_import_tag_mapper_table tbody tr td:first-child,.fcrm_contacts_importer .el-dialog__body .fcrm_import_source_select .fcrm_selection_step .fc_global_form_builder .el-form-item .el-form-item__content .fcrm_import_tag_mapper .fcrm_import_tag_mapper_table tbody tr td:nth-child(2),.fcrm_contacts_importer .el-dialog__body .fcrm_import_source_select .fcrm_selection_step .fcrm_step_body .el-form-item .el-form-item__content .fcrm_import_tag_mapper .fcrm_import_tag_mapper_table tbody tr td:first-child,.fcrm_contacts_importer .el-dialog__body .fcrm_import_source_select .fcrm_selection_step .fcrm_step_body .el-form-item .el-form-item__content .fcrm_import_tag_mapper .fcrm_import_tag_mapper_table tbody tr td:nth-child(2){width:calc((100% - 160px)/2)}.fcrm_contacts_importer .el-dialog__body .fcrm_import_source_select .fcrm_selection_step .fc_global_form_builder .el-form-item .el-form-item__content .fcrm_import_tag_mapper .fcrm_import_tag_mapper_table tbody tr td:first-child,.fcrm_contacts_importer .el-dialog__body .fcrm_import_source_select .fcrm_selection_step .fcrm_step_body .el-form-item .el-form-item__content .fcrm_import_tag_mapper .fcrm_import_tag_mapper_table tbody tr td:first-child{border-left:none}.fcrm_contacts_importer .el-dialog__body .fcrm_import_source_select .fcrm_selection_step .fc_global_form_builder .el-form-item .el-form-item__content .fcrm_import_tag_mapper .fcrm_import_tag_mapper_table tbody tr td:last-child,.fcrm_contacts_importer .el-dialog__body .fcrm_import_source_select .fcrm_selection_step .fcrm_step_body .el-form-item .el-form-item__content .fcrm_import_tag_mapper .fcrm_import_tag_mapper_table tbody tr td:last-child{text-align:right;width:160px}.fcrm_contacts_importer .el-dialog__body .fcrm_import_source_select .fcrm_selection_step .fc_global_form_builder .el-form-item .el-form-item__content .fcrm_import_tag_mapper .fcrm_import_tag_mapper_auto_header,.fcrm_contacts_importer .el-dialog__body .fcrm_import_source_select .fcrm_selection_step .fcrm_step_body .el-form-item .el-form-item__content .fcrm_import_tag_mapper .fcrm_import_tag_mapper_auto_header{display:flex;flex-direction:column;align-items:flex-end;justify-content:center;gap:4px}.fcrm_contacts_importer .el-dialog__body .fcrm_import_source_select .fcrm_selection_step .fc_global_form_builder .el-form-item .el-form-item__content .fcrm_import_tag_mapper .fcrm_import_tag_mapper_auto_label,.fcrm_contacts_importer .el-dialog__body .fcrm_import_source_select .fcrm_selection_step .fcrm_step_body .el-form-item .el-form-item__content .fcrm_import_tag_mapper .fcrm_import_tag_mapper_auto_label{color:var(--fc-text-muted);text-align:right;white-space:normal}.fcrm_contacts_importer .el-dialog__body .fcrm_import_source_select .fcrm_selection_step .fc_global_form_builder .el-form-item .el-form-item__content .fcrm_import_tag_mapper .fcrm_import_tag_mapper_auto_th,.fcrm_contacts_importer .el-dialog__body .fcrm_import_source_select .fcrm_selection_step .fcrm_step_body .el-form-item .el-form-item__content .fcrm_import_tag_mapper .fcrm_import_tag_mapper_auto_th{width:160px}.fcrm_contacts_importer .el-dialog__body .fcrm_import_source_select .fcrm_selection_step .fc_global_form_builder .el-form-item .el-form-item__content .fcrm_import_tag_mapper .fcrm_import_tag_mapper_auto_all,.fcrm_contacts_importer .el-dialog__body .fcrm_import_source_select .fcrm_selection_step .fcrm_step_body .el-form-item .el-form-item__content .fcrm_import_tag_mapper .fcrm_import_tag_mapper_auto_all{display:inline-flex;align-items:center;gap:8px}.fcrm_contacts_importer .el-dialog__body .fcrm_import_source_select .fcrm_selection_step .fc_global_form_builder .el-form-item .el-form-item__content .fcrm_import_tag_mapper .fcrm_import_tag_mapper_select_all,.fcrm_contacts_importer .el-dialog__body .fcrm_import_source_select .fcrm_selection_step .fcrm_step_body .el-form-item .el-form-item__content .fcrm_import_tag_mapper .fcrm_import_tag_mapper_select_all{font-size:12px;font-weight:400;line-height:16px;color:var(--fc-secondary-text)}.fcrm_contacts_importer .el-dialog__body .fcrm_import_source_select .fcrm_selection_step .fc_global_form_builder .el-form-item .el-form-item__content .fcrm_import_tag_mapper .fcrm_import_tag_mapper_auto_created,.fcrm_contacts_importer .el-dialog__body .fcrm_import_source_select .fcrm_selection_step .fcrm_step_body .el-form-item .el-form-item__content .fcrm_import_tag_mapper .fcrm_import_tag_mapper_auto_created{font-style:italic;color:var(--fc-secondary-text)}.fcrm_contacts_importer .el-dialog__body .fcrm_import_source_select .fcrm_selection_step .fc_global_form_builder .el-form-item .el-form-item__content .fcrm_import_tag_mapper .fcrm_import_tag_mapper_remote_label,.fcrm_contacts_importer .el-dialog__body .fcrm_import_source_select .fcrm_selection_step .fcrm_step_body .el-form-item .el-form-item__content .fcrm_import_tag_mapper .fcrm_import_tag_mapper_remote_label{display:inline-block;color:var(--fc-primary-text);font-size:14px;font-weight:400;line-height:20px;letter-spacing:-.084px}.fcrm_contacts_importer .el-dialog__body .fcrm_import_source_select .fcrm_selection_step .fc_global_form_builder .el-form-item .el-form-item__content .fcrm_import_tag_mapper .el-switch,.fcrm_contacts_importer .el-dialog__body .fcrm_import_source_select .fcrm_selection_step .fcrm_step_body .el-form-item .el-form-item__content .fcrm_import_tag_mapper .el-switch{--el-switch-on-color: var(--fc-deep-bg);--el-switch-off-color: var(--fc-light-bg);height:20px}.fcrm_contacts_importer .el-dialog__body .fcrm_import_source_select .fcrm_selection_step .fc_global_form_builder .el-form-item .el-form-item__content .fcrm_import_tag_mapper .el-switch .el-switch__core,.fcrm_contacts_importer .el-dialog__body .fcrm_import_source_select .fcrm_selection_step .fcrm_step_body .el-form-item .el-form-item__content .fcrm_import_tag_mapper .el-switch .el-switch__core{height:20px;min-width:36px;border:none;border-radius:10px;background-color:var(--fc-light-bg);box-shadow:none;transition:background-color .2s ease}.fcrm_contacts_importer .el-dialog__body .fcrm_import_source_select .fcrm_selection_step .fc_global_form_builder .el-form-item .el-form-item__content .fcrm_import_tag_mapper .el-switch .el-switch__core .el-switch__action,.fcrm_contacts_importer .el-dialog__body .fcrm_import_source_select .fcrm_selection_step .fcrm_step_body .el-form-item .el-form-item__content .fcrm_import_tag_mapper .el-switch .el-switch__core .el-switch__action{width:16px;height:16px;border-radius:50%;background-color:var(--fc-primary-bg);box-shadow:0 1px 2px #00000014;border:1px solid var(--fc-secondary-border);top:2px;left:2px!important;transition:left .2s ease,border-color .2s ease,box-shadow .2s ease}.fcrm_contacts_importer .el-dialog__body .fcrm_import_source_select .fcrm_selection_step .fc_global_form_builder .el-form-item .el-form-item__content .fcrm_import_tag_mapper .el-switch.is-checked .el-switch__core,.fcrm_contacts_importer .el-dialog__body .fcrm_import_source_select .fcrm_selection_step .fcrm_step_body .el-form-item .el-form-item__content .fcrm_import_tag_mapper .el-switch.is-checked .el-switch__core{background-color:var(--fc-deep-bg)}.fcrm_contacts_importer .el-dialog__body .fcrm_import_source_select .fcrm_selection_step .fc_global_form_builder .el-form-item .el-form-item__content .fcrm_import_tag_mapper .el-switch.is-checked .el-switch__core .el-switch__action,.fcrm_contacts_importer .el-dialog__body .fcrm_import_source_select .fcrm_selection_step .fcrm_step_body .el-form-item .el-form-item__content .fcrm_import_tag_mapper .el-switch.is-checked .el-switch__core .el-switch__action{left:calc(100% - 18px)!important;margin-left:0;transform:none;border-color:#fff3;box-shadow:0 1px 2px #00000026}.fcrm_contacts_importer .el-dialog__body .fcrm_import_source_select .fcrm_selection_step .fc_global_form_builder .el-form-item .el-form-item__content .fcrm_import_contact_field_mapper,.fcrm_contacts_importer .el-dialog__body .fcrm_import_source_select .fcrm_selection_step .fcrm_step_body .el-form-item .el-form-item__content .fcrm_import_contact_field_mapper{width:100%}.fcrm_contacts_importer .el-dialog__body .fcrm_import_source_select .fcrm_selection_step .fc_global_form_builder .el-form-item .el-form-item__content .fc_inline_help,.fcrm_contacts_importer .el-dialog__body .fcrm_import_source_select .fcrm_selection_step .fcrm_step_body .el-form-item .el-form-item__content .fc_inline_help{font-size:14px;font-style:normal;font-weight:500;line-height:20px;letter-spacing:-.084px;color:var(--fc-secondary-text);font-size:12px;font-weight:400;display:flex;align-items:center;justify-content:center;gap:6px;text-align:center}.fcrm_contacts_importer .el-dialog__body .fcrm_import_source_select .fcrm_selection_step .fcrm_import_others_doc_link a{color:var(--fc-deep-bg);font-size:14px;font-style:normal;font-weight:500;line-height:20px;letter-spacing:-.084px;font-size:12px;display:inline-flex;align-items:center;gap:4px}.fcrm_contacts_importer .el-dialog__body .fcrm_import_source_select .fcrm_dialog_footer_steps_section{position:absolute;bottom:15px}.fcrm_contacts_importer .el-dialog__body .fcrm_import_source_select .fcrm_dialog_footer_steps_section p{margin:0 0 5px;font-size:14px;font-style:normal;font-weight:500;line-height:20px;letter-spacing:-.084px;color:var(--fc-secondary-text);font-weight:400}.fcrm_contacts_importer .el-dialog__body .fcrm_import_source_select .fcrm_dialog_footer_steps_section .fcrm_dialog_footer_steps{display:flex;gap:5px}.fcrm_contacts_importer .el-dialog__body .fcrm_import_source_select .fcrm_dialog_footer_steps_section .fcrm_dialog_footer_steps .fcrm_dialog_footer_step{width:40px;height:6px;border-radius:var(--radius-10, 10px);background:var(--fc-light-bg)}.fcrm_contacts_importer .el-dialog__body .fcrm_import_source_select .fcrm_dialog_footer_steps_section .fcrm_dialog_footer_steps .active{border-radius:var(--radius-10, 10px);background:var(--fc-deep-bg)}.fcrm_contacts_importer .el-dialog__body .fcrm_csv_uploader .fcrm_csv_upload_container{display:flex;flex-direction:column;gap:20px}.fcrm_contacts_importer .el-dialog__body .fcrm_csv_uploader .fcrm_csv_upload_container .fcrm_sample_warning_container .el-upload__tip{display:flex;border-radius:var(--radius-8, 8px);background:transparent;padding:8px;align-items:center;gap:8px;align-self:stretch;justify-content:space-between}.fcrm_contacts_importer .el-dialog__body .fcrm_csv_uploader .fcrm_csv_upload_container .fcrm_upload_warning p{display:flex;padding:8px;align-items:center;gap:8px;align-self:stretch;border-radius:var(--radius-8, 8px);background:var(--fc-secondary-bg);color:var(--fc-primary-text);font-size:12px;font-style:normal;font-weight:400;line-height:16px}.fcrm_contacts_importer .el-dialog__body .fcrm_csv_uploader .fcrm_csv_upload_container .el-upload .el-upload-dragger{display:flex;padding:32px;flex-direction:column;justify-content:center;align-items:center;gap:20px;align-self:stretch;border-radius:var(--radius-8, 8px);border:1px dashed var(--fc-secondary-border);background:var(--fc-primary-bg)}.fcrm_contacts_importer .el-dialog__body .fcrm_csv_uploader .fcrm_csv_upload_container .el-upload .el-upload-dragger:hover{border:1px dashed var(--fc-primary-text);background:var(--fc-primary-bg)}.fcrm_contacts_importer .el-dialog__body .fcrm_csv_uploader .fcrm_csv_upload_container .el-upload .el-upload-dragger .fcrm_file_uploader{width:100%;display:flex;flex-direction:column;gap:20px;align-items:center;justify-content:center}.fcrm_contacts_importer .el-dialog__body .fcrm_csv_uploader .fcrm_csv_upload_container .el-upload .el-upload-dragger .fcrm_file_uploader .fcrm_upload_icon{display:flex;align-items:center;justify-content:center}.fcrm_contacts_importer .el-dialog__body .fcrm_csv_uploader .fcrm_csv_upload_container .el-upload .el-upload-dragger .fcrm_file_uploader .fcrm_upload_icon svg{width:24px;height:24px;color:var(--fc-primary-text)}.fcrm_contacts_importer .el-dialog__body .fcrm_csv_uploader .fcrm_csv_upload_container .el-upload .el-upload-dragger .fcrm_file_uploader .fcrm_upload_icon svg path{fill:var(--fc-secondary-text)}.fcrm_contacts_importer .el-dialog__body .fcrm_dc_csv_mapper .fcrm_mapper_update_row,.fcrm_contacts_importer .el-dialog__body .fcrm_review_configurator .fcrm_mapper_update_row{margin:0}.fcrm_contacts_importer .el-dialog__body .fcrm_dc_csv_mapper .fcrm_mapper_update_row .fcrm_mapper_update_item,.fcrm_contacts_importer .el-dialog__body .fcrm_review_configurator .fcrm_mapper_update_row .fcrm_mapper_update_item{margin-bottom:0}.fcrm_contacts_importer .el-dialog__body .fcrm_dc_csv_mapper .fcrm_mapper_update_row .fcrm_mapper_update_item .el-form-item__label,.fcrm_contacts_importer .el-dialog__body .fcrm_review_configurator .fcrm_mapper_update_row .fcrm_mapper_update_item .el-form-item__label{color:var(--fc-primary-text);font-size:14px;font-weight:500;line-height:20px;letter-spacing:-.084px;margin-bottom:4px;display:inline-flex;align-items:center;gap:6px}.fcrm_contacts_importer .el-dialog__body .fcrm_dc_csv_mapper .fcrm_mapper_update_row .fcrm_mapper_update_item .el-form-item__label .el-tooltip__trigger,.fcrm_contacts_importer .el-dialog__body .fcrm_review_configurator .fcrm_mapper_update_row .fcrm_mapper_update_item .el-form-item__label .el-tooltip__trigger{display:flex;align-items:center}.fcrm_contacts_importer .el-dialog__body .fcrm_dc_csv_mapper .fcrm_mapper_update_row .fcrm_mapper_update_item .el-form-item__label .el-tooltip__trigger svg,.fcrm_contacts_importer .el-dialog__body .fcrm_review_configurator .fcrm_mapper_update_row .fcrm_mapper_update_item .el-form-item__label .el-tooltip__trigger svg{width:20px;height:20px}.fcrm_contacts_importer .el-dialog__body .fcrm_dc_csv_mapper .fcrm_mapper_update_row .fcrm_mapper_update_item .el-form-item__content .el-radio-group .el-radio,.fcrm_contacts_importer .el-dialog__body .fcrm_review_configurator .fcrm_mapper_update_row .fcrm_mapper_update_item .el-form-item__content .el-radio-group .el-radio{margin-right:8px}.fcrm_contacts_importer .el-dialog__body .fcrm_dc_csv_mapper .fcrm_mapper_update_row .fcrm_mapper_update_item .el-form-item__content .el-radio-group .el-radio:last-child,.fcrm_contacts_importer .el-dialog__body .fcrm_review_configurator .fcrm_mapper_update_row .fcrm_mapper_update_item .el-form-item__content .el-radio-group .el-radio:last-child{margin-right:0}.fcrm_contacts_importer .el-dialog__body .fcrm_dc_csv_mapper .fcrm_mapper_update_row .fcrm_mapper_update_item .el-form-item__content .el-radio-group .el-radio .el-radio__label,.fcrm_contacts_importer .el-dialog__body .fcrm_review_configurator .fcrm_mapper_update_row .fcrm_mapper_update_item .el-form-item__content .el-radio-group .el-radio .el-radio__label{color:var(--fc-primary-text);font-size:14px;font-weight:400;line-height:20px}.fcrm_contacts_importer .el-dialog__body .fcrm_dc_csv_mapper .fcrm_mapper_status_row,.fcrm_contacts_importer .el-dialog__body .fcrm_review_configurator .fcrm_mapper_status_row{margin:0}.fcrm_contacts_importer .el-dialog__body .fcrm_dc_csv_mapper .fcrm_mapper_status_row .fcrm_mapper_status_item,.fcrm_contacts_importer .el-dialog__body .fcrm_review_configurator .fcrm_mapper_status_row .fcrm_mapper_status_item{margin-bottom:0}.fcrm_contacts_importer .el-dialog__body .fcrm_dc_csv_mapper .fcrm_mapper_status_row .fcrm_mapper_status_item .el-form-item__label,.fcrm_contacts_importer .el-dialog__body .fcrm_review_configurator .fcrm_mapper_status_row .fcrm_mapper_status_item .el-form-item__label{color:var(--fc-primary-text);font-size:14px;font-weight:500;line-height:20px;letter-spacing:-.084px;margin-bottom:4px;display:inline-flex;align-items:center;gap:6px}.fcrm_contacts_importer .el-dialog__body .fcrm_dc_csv_mapper .fcrm_mapper_status_row .fcrm_mapper_status_item .el-form-item__label .el-tooltip__trigger,.fcrm_contacts_importer .el-dialog__body .fcrm_review_configurator .fcrm_mapper_status_row .fcrm_mapper_status_item .el-form-item__label .el-tooltip__trigger{display:flex;align-items:center}.fcrm_contacts_importer .el-dialog__body .fcrm_dc_csv_mapper .fcrm_mapper_status_row .fcrm_mapper_status_item .el-form-item__label .el-tooltip__trigger svg,.fcrm_contacts_importer .el-dialog__body .fcrm_review_configurator .fcrm_mapper_status_row .fcrm_mapper_status_item .el-form-item__label .el-tooltip__trigger svg{width:20px;height:20px}.fcrm_contacts_importer .el-dialog__body .fcrm_dc_csv_mapper .el-form,.fcrm_contacts_importer .el-dialog__body .fcrm_review_configurator .el-form{display:flex;flex-direction:column;gap:20px}.fcrm_contacts_importer .el-dialog__body .fcrm_dc_csv_mapper .el-form .el-form-item--label-top,.fcrm_contacts_importer .el-dialog__body .fcrm_review_configurator .el-form .el-form-item--label-top{margin:0}.fcrm_contacts_importer .el-dialog__body .fcrm_dc_csv_mapper .el-form .el-form-item--label-top .el-form-item__label,.fcrm_contacts_importer .el-dialog__body .fcrm_review_configurator .el-form .el-form-item--label-top .el-form-item__label{margin:0!important}.fcrm_contacts_importer .el-dialog__body .fcrm_dc_csv_mapper .el-form .el-form-item--label-top .el-form-item__label svg,.fcrm_contacts_importer .el-dialog__body .fcrm_review_configurator .el-form .el-form-item--label-top .el-form-item__label svg{display:flex}.fcrm_contacts_importer .el-dialog__body .fcrm_dc_csv_mapper .el-form .fcrm_review_criteria_label .el-form-item__label,.fcrm_contacts_importer .el-dialog__body .fcrm_review_configurator .el-form .fcrm_review_criteria_label .el-form-item__label{color:var(--fc-primary-text)!important;font-size:14px!important;font-style:normal!important;font-weight:500!important;line-height:20px!important;letter-spacing:-.084px!important;margin-bottom:0!important}.fcrm_contacts_importer .el-dialog__body .fcrm_dc_csv_mapper .el-form>.fcrm_import_preview_table .el-table__inner-wrapper:before,.fcrm_contacts_importer .el-dialog__body .fcrm_review_configurator .el-form>.fcrm_import_preview_table .el-table__inner-wrapper:before{display:none}.fcrm_contacts_importer .el-dialog__body .fcrm_dc_csv_mapper .el-form>.fcrm_import_preview_table .el-table,.fcrm_contacts_importer .el-dialog__body .fcrm_review_configurator .el-form>.fcrm_import_preview_table .el-table{border-radius:var(--radius-8, 8px);overflow:hidden!important;background:var(--fc-primary-bg);border:1px solid var(--fc-primary-border)}.fcrm_contacts_importer .el-dialog__body .fcrm_dc_csv_mapper .el-form>.fcrm_import_preview_table .el-table:before,.fcrm_contacts_importer .el-dialog__body .fcrm_review_configurator .el-form>.fcrm_import_preview_table .el-table:before{display:none!important}.fcrm_contacts_importer .el-dialog__body .fcrm_dc_csv_mapper .el-form>.fcrm_import_preview_table .el-table__header-wrapper th,.fcrm_contacts_importer .el-dialog__body .fcrm_review_configurator .el-form>.fcrm_import_preview_table .el-table__header-wrapper th{border-bottom:1px solid var(--fc-primary-border);background:var(--fc-secondary-bg);padding-top:8px;padding-bottom:8px;font-size:12px;font-weight:500;line-height:16px;letter-spacing:.48px;text-transform:uppercase;color:var(--fc-text-muted)}.fcrm_contacts_importer .el-dialog__body .fcrm_dc_csv_mapper .el-form>.fcrm_import_preview_table .el-table__header-wrapper th:last-child,.fcrm_contacts_importer .el-dialog__body .fcrm_review_configurator .el-form>.fcrm_import_preview_table .el-table__header-wrapper th:last-child{border-right:none}.fcrm_contacts_importer .el-dialog__body .fcrm_dc_csv_mapper .el-form>.fcrm_import_preview_table .el-table__body-wrapper td,.fcrm_contacts_importer .el-dialog__body .fcrm_review_configurator .el-form>.fcrm_import_preview_table .el-table__body-wrapper td{padding-top:8px;padding-bottom:8px;font-size:14px;font-weight:400;line-height:20px;letter-spacing:-.084px;color:var(--fc-primary-text);background:var(--fc-primary-bg)!important}.fcrm_contacts_importer .el-dialog__body .fcrm_dc_csv_mapper .el-form>.fcrm_import_preview_table .el-table__body-wrapper td:last-child,.fcrm_contacts_importer .el-dialog__body .fcrm_review_configurator .el-form>.fcrm_import_preview_table .el-table__body-wrapper td:last-child{border-right:none}.fcrm_contacts_importer .el-dialog__body .fcrm_dc_csv_mapper .el-form>.fcrm_import_preview_table .el-table__body-wrapper tr:last-child td,.fcrm_contacts_importer .el-dialog__body .fcrm_review_configurator .el-form>.fcrm_import_preview_table .el-table__body-wrapper tr:last-child td{border-bottom:none}.fcrm_contacts_importer .el-dialog__body .fcrm_dc_csv_mapper .el-form>.fcrm_total_found_result,.fcrm_contacts_importer .el-dialog__body .fcrm_review_configurator .el-form>.fcrm_total_found_result{margin:0;font-size:14px;font-weight:400;line-height:20px;color:var(--fc-secondary-text)}.fcrm_contacts_importer .el-dialog__body .fcrm_dc_csv_mapper .el-form .fcrm_review_lists_tags_row,.fcrm_contacts_importer .el-dialog__body .fcrm_review_configurator .el-form .fcrm_review_lists_tags_row{margin:0}.fcrm_contacts_importer .el-dialog__body .fcrm_dc_csv_mapper .el-form .fcrm_review_lists_tags_row .fcrm_review_lists_tags_item,.fcrm_contacts_importer .el-dialog__body .fcrm_review_configurator .el-form .fcrm_review_lists_tags_row .fcrm_review_lists_tags_item{margin-bottom:0}.fcrm_contacts_importer .el-dialog__body .fcrm_dc_csv_mapper .el-form .fcrm_review_lists_tags_row .fcrm_review_lists_tags_item .el-form-item__label,.fcrm_contacts_importer .el-dialog__body .fcrm_review_configurator .el-form .fcrm_review_lists_tags_row .fcrm_review_lists_tags_item .el-form-item__label{color:var(--fc-primary-text);font-size:14px;font-weight:500;line-height:20px;letter-spacing:-.084px;margin-bottom:4px}.fcrm_contacts_importer .el-dialog__body .fcrm_dc_csv_mapper .el-form .fcrm_review_update_row,.fcrm_contacts_importer .el-dialog__body .fcrm_review_configurator .el-form .fcrm_review_update_row{margin:0}.fcrm_contacts_importer .el-dialog__body .fcrm_dc_csv_mapper .el-form .fcrm_review_update_row .fcrm_review_update_item,.fcrm_contacts_importer .el-dialog__body .fcrm_review_configurator .el-form .fcrm_review_update_row .fcrm_review_update_item{margin-bottom:0}.fcrm_contacts_importer .el-dialog__body .fcrm_dc_csv_mapper .el-form .fcrm_review_update_row .fcrm_review_update_item .el-form-item__label,.fcrm_contacts_importer .el-dialog__body .fcrm_review_configurator .el-form .fcrm_review_update_row .fcrm_review_update_item .el-form-item__label{color:var(--fc-primary-text);font-size:14px;font-weight:500;line-height:20px;letter-spacing:-.084px;margin-bottom:4px;display:inline-flex;align-items:center;gap:6px}.fcrm_contacts_importer .el-dialog__body .fcrm_dc_csv_mapper .el-form .fcrm_review_update_row .fcrm_review_update_item .el-form-item__label .el-tooltip__trigger,.fcrm_contacts_importer .el-dialog__body .fcrm_review_configurator .el-form .fcrm_review_update_row .fcrm_review_update_item .el-form-item__label .el-tooltip__trigger{display:flex;align-items:center}.fcrm_contacts_importer .el-dialog__body .fcrm_dc_csv_mapper .el-form .fcrm_review_update_row .fcrm_review_update_item .el-form-item__label .el-tooltip__trigger svg,.fcrm_contacts_importer .el-dialog__body .fcrm_review_configurator .el-form .fcrm_review_update_row .fcrm_review_update_item .el-form-item__label .el-tooltip__trigger svg{width:20px;height:20px}.fcrm_contacts_importer .el-dialog__body .fcrm_dc_csv_mapper .el-form .fcrm_review_update_row .fcrm_review_update_item .el-form-item__content .el-radio-group .el-radio,.fcrm_contacts_importer .el-dialog__body .fcrm_review_configurator .el-form .fcrm_review_update_row .fcrm_review_update_item .el-form-item__content .el-radio-group .el-radio{margin-right:8px}.fcrm_contacts_importer .el-dialog__body .fcrm_dc_csv_mapper .el-form .fcrm_review_update_row .fcrm_review_update_item .el-form-item__content .el-radio-group .el-radio:last-child,.fcrm_contacts_importer .el-dialog__body .fcrm_review_configurator .el-form .fcrm_review_update_row .fcrm_review_update_item .el-form-item__content .el-radio-group .el-radio:last-child{margin-right:0}.fcrm_contacts_importer .el-dialog__body .fcrm_dc_csv_mapper .el-form .fcrm_review_update_row .fcrm_review_update_item .el-form-item__content .el-radio-group .el-radio .el-radio__label,.fcrm_contacts_importer .el-dialog__body .fcrm_review_configurator .el-form .fcrm_review_update_row .fcrm_review_update_item .el-form-item__content .el-radio-group .el-radio .el-radio__label{color:var(--fc-primary-text);font-size:14px;font-weight:400;line-height:20px}.fcrm_contacts_importer .el-dialog__body .fcrm_dc_csv_mapper .el-form .fcrm_review_status_row,.fcrm_contacts_importer .el-dialog__body .fcrm_review_configurator .el-form .fcrm_review_status_row{margin:0}.fcrm_contacts_importer .el-dialog__body .fcrm_dc_csv_mapper .el-form .fcrm_review_status_row .fcrm_review_status_item,.fcrm_contacts_importer .el-dialog__body .fcrm_review_configurator .el-form .fcrm_review_status_row .fcrm_review_status_item{margin-bottom:0}.fcrm_contacts_importer .el-dialog__body .fcrm_dc_csv_mapper .el-form .fcrm_review_status_row .fcrm_review_status_item .el-form-item__label,.fcrm_contacts_importer .el-dialog__body .fcrm_review_configurator .el-form .fcrm_review_status_row .fcrm_review_status_item .el-form-item__label{color:var(--fc-primary-text);font-size:14px;font-weight:500;line-height:20px;letter-spacing:-.084px;margin-bottom:4px;display:inline-flex;align-items:center;gap:6px}.fcrm_contacts_importer .el-dialog__body .fcrm_dc_csv_mapper .el-form .fcrm_review_status_row .fcrm_review_status_item .el-form-item__label .el-tooltip__trigger,.fcrm_contacts_importer .el-dialog__body .fcrm_review_configurator .el-form .fcrm_review_status_row .fcrm_review_status_item .el-form-item__label .el-tooltip__trigger{display:flex;align-items:center}.fcrm_contacts_importer .el-dialog__body .fcrm_dc_csv_mapper .el-form .fcrm_review_status_row .fcrm_review_status_item .el-form-item__label .el-tooltip__trigger svg,.fcrm_contacts_importer .el-dialog__body .fcrm_review_configurator .el-form .fcrm_review_status_row .fcrm_review_status_item .el-form-item__label .el-tooltip__trigger svg{width:20px;height:20px}.fcrm_contacts_importer .el-dialog__body .fcrm_dc_csv_mapper .el-form .el-form-item .el-form-item__label,.fcrm_contacts_importer .el-dialog__body .fcrm_review_configurator .el-form .el-form-item .el-form-item__label{color:var(--fc-primary-text);font-size:14px;font-style:normal;font-weight:500;line-height:20px;letter-spacing:-.084px;display:inline-flex;align-items:center;gap:6px;margin-bottom:4px!important}.fcrm_contacts_importer .el-dialog__body .fcrm_dc_csv_mapper .el-form .el-form-item .el-icon,.fcrm_contacts_importer .el-dialog__body .fcrm_review_configurator .el-form .el-form-item .el-icon{color:var(--fc-secondary-text)!important;font-size:16px}.fcrm_contacts_importer .el-dialog__body .fcrm_dc_csv_mapper .el-form .el-form-item .fcrm_horizontal_table,.fcrm_contacts_importer .el-dialog__body .fcrm_review_configurator .el-form .el-form-item .fcrm_horizontal_table{width:100%;border-collapse:separate;border-spacing:0 8px}.fcrm_contacts_importer .el-dialog__body .fcrm_dc_csv_mapper .el-form .el-form-item .fcrm_horizontal_table thead tr th,.fcrm_contacts_importer .el-dialog__body .fcrm_review_configurator .el-form .el-form-item .fcrm_horizontal_table thead tr th{width:50%;text-align:left;background:transparent;padding:0 0 8px;border:none;vertical-align:bottom;color:var(--fc-text-muted);font-size:12px;font-style:normal;font-weight:500;line-height:16px;letter-spacing:.48px;text-transform:uppercase}.fcrm_contacts_importer .el-dialog__body .fcrm_dc_csv_mapper .el-form .el-form-item .fcrm_horizontal_table tbody tr td,.fcrm_contacts_importer .el-dialog__body .fcrm_review_configurator .el-form .el-form-item .fcrm_horizontal_table tbody tr td{text-align:left;background:transparent;border-radius:8px;padding:0;vertical-align:middle}.fcrm_contacts_importer .el-dialog__body .fcrm_dc_csv_mapper .el-form .el-form-item .fcrm_horizontal_table tbody tr td:first-child,.fcrm_contacts_importer .el-dialog__body .fcrm_review_configurator .el-form .el-form-item .fcrm_horizontal_table tbody tr td:first-child{padding-right:16px}.fcrm_contacts_importer .el-dialog__body .fcrm_dc_csv_mapper .el-form .el-form-item .fcrm_horizontal_table tbody tr td .el-input,.fcrm_contacts_importer .el-dialog__body .fcrm_review_configurator .el-form .el-form-item .fcrm_horizontal_table tbody tr td .el-input,.fcrm_contacts_importer .el-dialog__body .fcrm_dc_csv_mapper .el-form .el-form-item .fcrm_horizontal_table tbody tr td .el-select,.fcrm_contacts_importer .el-dialog__body .fcrm_review_configurator .el-form .el-form-item .fcrm_horizontal_table tbody tr td .el-select{width:100%}.fcrm_contacts_importer .el-dialog__body .fcrm_dc_csv_mapper .el-form .el-form-item .fcrm_import_preview_table,.fcrm_contacts_importer .el-dialog__body .fcrm_review_configurator .el-form .el-form-item .fcrm_import_preview_table{margin-bottom:24px}.fcrm_contacts_importer .el-dialog__body .fcrm_dc_csv_mapper .el-form .el-form-item .fcrm_import_preview_table .el-table,.fcrm_contacts_importer .el-dialog__body .fcrm_review_configurator .el-form .el-form-item .fcrm_import_preview_table .el-table{border-radius:var(--radius-8, 8px);overflow:hidden;background:var(--fc-primary-bg);border:1px solid var(--fc-primary-border)}.fcrm_contacts_importer .el-dialog__body .fcrm_dc_csv_mapper .el-form .el-form-item .fcrm_import_preview_table .el-table:before,.fcrm_contacts_importer .el-dialog__body .fcrm_review_configurator .el-form .el-form-item .fcrm_import_preview_table .el-table:before{display:none}.fcrm_contacts_importer .el-dialog__body .fcrm_dc_csv_mapper .el-form .el-form-item .fcrm_import_preview_table .el-table__header-wrapper th,.fcrm_contacts_importer .el-dialog__body .fcrm_review_configurator .el-form .el-form-item .fcrm_import_preview_table .el-table__header-wrapper th{background:var(--fc-primary-bg)!important;border-bottom:1px solid var(--fc-primary-border);border-right:1px solid var(--fc-primary-border);padding:12px 16px;font-size:12px;font-weight:500;line-height:16px;letter-spacing:.48px;text-transform:uppercase;color:var(--fc-text-muted)}.fcrm_contacts_importer .el-dialog__body .fcrm_dc_csv_mapper .el-form .el-form-item .fcrm_import_preview_table .el-table__header-wrapper th:last-child,.fcrm_contacts_importer .el-dialog__body .fcrm_review_configurator .el-form .el-form-item .fcrm_import_preview_table .el-table__header-wrapper th:last-child{border-right:none}.fcrm_contacts_importer .el-dialog__body .fcrm_dc_csv_mapper .el-form .el-form-item .fcrm_import_preview_table .el-table__body-wrapper td,.fcrm_contacts_importer .el-dialog__body .fcrm_review_configurator .el-form .el-form-item .fcrm_import_preview_table .el-table__body-wrapper td{padding:12px 16px;border-bottom:1px solid var(--fc-primary-border);border-right:1px solid var(--fc-primary-border);font-size:14px;font-weight:400;line-height:20px;letter-spacing:-.084px;color:var(--fc-primary-text);background:var(--fc-primary-bg)!important}.fcrm_contacts_importer .el-dialog__body .fcrm_dc_csv_mapper .el-form .el-form-item .fcrm_import_preview_table .el-table__body-wrapper td:last-child,.fcrm_contacts_importer .el-dialog__body .fcrm_review_configurator .el-form .el-form-item .fcrm_import_preview_table .el-table__body-wrapper td:last-child{border-right:none}.fcrm_contacts_importer .el-dialog__body .fcrm_dc_csv_mapper .el-form .el-form-item .fcrm_import_preview_table .el-table__body-wrapper tr:last-child td,.fcrm_contacts_importer .el-dialog__body .fcrm_review_configurator .el-form .el-form-item .fcrm_import_preview_table .el-table__body-wrapper tr:last-child td{border-bottom:none}.fcrm_contacts_importer .el-dialog__body .fcrm_dc_csv_mapper .el-form .el-form-item .fcrm_total_found_result,.fcrm_contacts_importer .el-dialog__body .fcrm_review_configurator .el-form .el-form-item .fcrm_total_found_result{margin:0 0 20px;font-size:14px;color:var(--fc-secondary-text)}.fcrm_contacts_importer .el-dialog__body .fcrm_dc_csv_mapper .el-form hr,.fcrm_contacts_importer .el-dialog__body .fcrm_review_configurator .el-form hr{margin:0}.fcrm_contacts_importer .el-dialog__body .fcrm_dc_csv_mapper .fcrm_importing_stats .fcrm_importing_stats_banner,.fcrm_contacts_importer .el-dialog__body .fcrm_review_configurator .fcrm_importing_stats .fcrm_importing_stats_banner{display:flex;flex-direction:column;align-items:center;gap:6px;align-self:stretch;margin-bottom:20px}.fcrm_contacts_importer .el-dialog__body .fcrm_dc_csv_mapper .fcrm_importing_stats .fcrm_wrm_well,.fcrm_contacts_importer .el-dialog__body .fcrm_review_configurator .fcrm_importing_stats .fcrm_wrm_well{display:flex;flex-direction:column;padding:14px 14px 16px;align-items:flex-start;gap:12px;align-self:stretch;border-radius:var(--radius-12, var(--fcrm-border-radius-8, 8px));background:var(--fc-secondary-bg);color:var(--fc-primary-text);font-size:14px;font-style:normal;font-weight:500;line-height:20px;letter-spacing:-.084px;font-weight:400}.fcrm_contacts_importer .el-dialog__body .fcrm_dc_csv_mapper .fcrm_importing_stats .fcrm_wrm_well h4,.fcrm_contacts_importer .el-dialog__body .fcrm_review_configurator .fcrm_importing_stats .fcrm_wrm_well h4{margin:0;font-weight:500}.fcrm_contacts_importer .el-dialog__body .fcrm_dc_csv_mapper .fcrm_importing_stats .fcrm_wrm_well ul,.fcrm_contacts_importer .el-dialog__body .fcrm_review_configurator .fcrm_importing_stats .fcrm_wrm_well ul{margin:0}.fcrm_contacts_importer .el-dialog__body .fcrm_dc_csv_mapper .fcrm_importing_stats .fcrm_wrm_well ul li,.fcrm_contacts_importer .el-dialog__body .fcrm_review_configurator .fcrm_importing_stats .fcrm_wrm_well ul li{margin-bottom:6px}.fcrm_contacts_importer .el-dialog__body .fcrm_dc_csv_mapper .fcrm_importing_stats .fcrm_wrm_well ul li:last-child,.fcrm_contacts_importer .el-dialog__body .fcrm_review_configurator .fcrm_importing_stats .fcrm_wrm_well ul li:last-child{margin-bottom:0}.fcrm_contacts_importer .el-dialog__body .fcrm_dialog_footer{padding:20px 20px 10px;display:flex;flex-direction:row-reverse;gap:12px}.fcrm_contacts_importer .el-dialog__body .fcrm_dialog_footer .fcrm_dialog_footer_button{display:flex;padding:8px;justify-content:center;align-items:center;gap:4px;border-radius:var(--radius-8, 8px)}.fcrm_contacts_importer .el-dialog__body .fcrm_dialog_footer .frm_btn_black{color:var(--fc-text-inverse);background:var(--fc-deep-bg)}.fcrm_contacts_importer .el-dialog__body .fcrm_dialog_footer .frm_btn_black:hover{color:var(--fc-text-inverse)}.fcrm_contacts_importer .el-dialog__body .fcrm_importer_form .fcrm_global_form_builder .el-form{display:flex;flex-direction:column;gap:20px}.fcrm_contacts_importer .el-dialog__body .fcrm_importer_form .fcrm_global_form_builder .el-form .el-form-item{margin:0}.fcrm_contacts_importer .el-dialog__body .fcrm_importer_form .fcrm_global_form_builder .el-form .el-form-item .el-form-item__label{width:100%}.fcrm_contacts_importer .el-dialog__body .fcrm_importer_form .fcrm_global_form_builder .el-form .el-form-item .el-form-item__label>div{margin:0;padding:0;background-color:transparent;color:var(--fc-primary-text);font-size:14px;font-style:normal;font-weight:500;line-height:20px;letter-spacing:-.084px}.fcrm_contacts_importer .el-dialog__body .fcrm_importer_form .fcrm_global_form_builder .el-form .el-form-item .el-form-item__label .fcrm-info-alert--above-label{margin-bottom:16px;margin-top:0;display:flex;padding:8px;align-items:center;gap:8px;align-self:stretch;border-radius:var(--fcrm-border-radius-8);background:var(--fc-secondary-bg)}.fcrm_contacts_importer .el-dialog__body .fcrm_importer_form .fcrm_global_form_builder .el-form .el-form-item .el-form-item__label .fcrm-info-alert--above-label p{display:flex;align-items:center;gap:8px;color:var(--fc-primary-text);font-size:12px;font-style:normal;font-weight:400;line-height:16px}.fcrm_contacts_importer .el-dialog__body .fcrm_importer_form .fcrm_global_form_builder .el-form .el-form-item .el-form-item__label .fcrm-with-label-text{display:block}.fcrm_contacts_importer .el-dialog__body .fcrm_importer_form .fcrm_global_form_builder .el-form .el-form-item .el-form-item__content{font-weight:400}.fcrm_contacts_importer .el-dialog__body .fcrm_importer_form .fcrm_global_form_builder .el-form .el-form-item .el-form-item__content .fc_inline_help{display:flex;padding:8px;width:100%;margin:0;align-items:center;gap:8px;align-self:stretch;border-radius:var(--radius-8, 8px);background:var(--fc-secondary-bg)}.fcrm_contacts_importer .el-dialog__body .fcrm_importer_form .fcrm_global_form_builder .el-form .el-form-item .el-form-item__content .fc_checkbox_group .fluentcrm_2col_labels{padding-left:28px}.fcrm_contacts_importer .el-dialog__body .fcrm_importer_form .fcrm_global_form_builder .el-form .el-form-item .el-form-item__content .fcrm_checkbox_group{width:100%}.fcrm_contacts_importer .el-dialog__body .fcrm_importer_form .fcrm_global_form_builder .el-form .el-form-item .el-form-item__content .fcrm_checkbox_group div{margin:0!important;display:flex;padding-left:28px;align-items:flex-start;gap:16px;align-self:stretch}.fcrm_contacts_importer .el-dialog__body .fcrm_importer_form .fcrm_global_form_builder .el-form .el-form-item .el-form-item__content .fcrm_checkbox_group .fluentcrm_2col_labels{display:flex;flex-direction:row}.fcrm_contacts_importer .el-dialog__body .fcrm_importer_form .fcrm_global_form_builder .el-form .el-form-item .el-form-item__content .fcrm_checkbox_group .fluentcrm_2col_labels .fcrm-checkbox{width:48%;margin:0}.fcrm_contacts_importer .el-dialog__body .fcrm_importer_form .fcrm_global_form_builder .el-form .el-form-item .el-form-item__content .fcrm-info-alert{margin-bottom:16px}.fcrm_contacts_importer .el-dialog__body .fcrm_importer_form .fcrm_global_form_builder .el-form .el-form-item .el-form-item__content .fcrm-mapper-container{width:100%}.fcrm_contacts_importer .el-dialog__body .fcrm_importer_form .fcrm_global_form_builder .el-form .el-form-item .el-form-item__content .fcrm-mapper-container .fcrm_horizontal_table{width:100%;border-collapse:separate;border-spacing:0;border:1px solid var(--fc-primary-border);border-radius:var(--radius-8, 8px);overflow:hidden}.fcrm_contacts_importer .el-dialog__body .fcrm_importer_form .fcrm_global_form_builder .el-form .el-form-item .el-form-item__content .fcrm-mapper-container .fcrm_horizontal_table thead th{background:var(--fc-primary-bg);border-bottom:1px solid var(--fc-primary-border);border-right:1px solid var(--fc-primary-border);padding:12px 16px;font-size:12px;font-weight:500;line-height:16px;letter-spacing:.48px;text-transform:uppercase;color:var(--fc-text-muted);text-align:left}.fcrm_contacts_importer .el-dialog__body .fcrm_importer_form .fcrm_global_form_builder .el-form .el-form-item .el-form-item__content .fcrm-mapper-container .fcrm_horizontal_table thead th:last-child{border-right:none}.fcrm_contacts_importer .el-dialog__body .fcrm_importer_form .fcrm_global_form_builder .el-form .el-form-item .el-form-item__content .fcrm-mapper-container .fcrm_horizontal_table tbody td{padding:12px 16px;border-bottom:1px solid var(--fc-primary-border);border-right:1px solid var(--fc-primary-border);text-align:left;vertical-align:middle}.fcrm_contacts_importer .el-dialog__body .fcrm_importer_form .fcrm_global_form_builder .el-form .el-form-item .el-form-item__content .fcrm-mapper-container .fcrm_horizontal_table tbody td:last-child{border-right:none}.fcrm_contacts_importer .el-dialog__body .fcrm_importer_form .fcrm_global_form_builder .el-form .el-form-item .el-form-item__content .fcrm-mapper-container .fcrm_horizontal_table tbody td .fcrm_options_selector .fcrm_with_select{border-radius:0 8px 8px 0;background:var(--fc-secondary-bg);border:none}.fcrm_contacts_importer .el-dialog__body .fcrm_importer_form .fcrm_global_form_builder .el-form .el-form-item .el-form-item__content .fcrm-mapper-container .fcrm_horizontal_table tbody tr:last-child td{border-bottom:none}.fcrm_contacts_importer .el-dialog__body .fcrm_importer_form .fcrm_global_form_builder .el-form .el-form-item .el-form-item__content .fcrm-mapper-container .fcrm_horizontal_table .fcrm_text_align_center,.fcrm_contacts_importer .el-dialog__body .fcrm_importer_form .fcrm_global_form_builder .el-form .el-form-item .el-form-item__content .fcrm-mapper-container .fcrm_horizontal_table .text-align-right{text-align:right!important;padding:0 8px;white-space:nowrap}.fcrm_contacts_importer .el-dialog__body .fcrm_importer_form .fcrm_global_form_builder .el-form .el-form-item .el-form-item__content .fcrm-mapper-container .fcrm_horizontal_table .fcrm_text_align_center .el-button,.fcrm_contacts_importer .el-dialog__body .fcrm_importer_form .fcrm_global_form_builder .el-form .el-form-item .el-form-item__content .fcrm-mapper-container .fcrm_horizontal_table .text-align-right .el-button{background-color:transparent;border:none}.fcrm_contacts_importer .el-dialog__body .fcrm_importer_form .fcrm_global_form_builder .el-form .el-form-item .el-form-item__content .fcrm-mapper-container .fcrm_horizontal_table .fcrm_text_align_center .el-button svg,.fcrm_contacts_importer .el-dialog__body .fcrm_importer_form .fcrm_global_form_builder .el-form .el-form-item .el-form-item__content .fcrm-mapper-container .fcrm_horizontal_table .text-align-right .el-button svg{height:20px;width:20px}.fcrm_contacts_importer .el-dialog__body .fcrm_importer_form .fcrm_global_form_builder .el-form .el-form-item .el-form-item__content .fc_horizontal_table td,.fcrm_contacts_importer .el-dialog__body .fcrm_importer_form .fcrm_global_form_builder .el-form .el-form-item .el-form-item__content .fc_horizontal_table th{border:1px solid var(--fc-primary-border);text-align:left;padding:8px 12px}.fcrm_contacts_importer .el-dialog__body .fcrm_importer_form .fcrm_global_form_builder .el-form .el-form-item .el-form-item__content .fc_horizontal_table .fcrm_text_align_center{text-align:center!important;padding:0 8px;white-space:nowrap}.fcrm_contacts_importer .el-dialog__body .fcrm_importer_form .fcrm_global_form_builder .el-form .el-form-item .el-form-item__content .fc_horizontal_table .fcrm_text_align_center .el-button{background-color:transparent;border:none}.fcrm_contacts_importer .el-dialog__body .fcrm_importer_form .fcrm_global_form_builder .el-form .el-form-item .el-form-item__content .fc_horizontal_table .fcrm_text_align_center .el-button .el-icon{color:var(--fc-secondary-text);font-size:18px}.fcrm_contacts_importer .el-dialog__body .fcrm_importer_form .fcrm_global_form_builder .el-form .el-form-item .el-form-item__content .fc_horizontal_table .fcrm_add_new_row_old{display:none}.fcrm_contacts_importer .el-dialog__body .fcrm_importer_form .fcrm_global_form_builder .el-form .el-form-item .el-form-item__content .fc_horizontal_table .fcrm_add_new_row{padding:5px 10px}.fcrm_contacts_importer .el-dialog__body .fcrm_importer_form .fcrm_global_form_builder .el-form .el-form-item .el-form-item__content .fc_horizontal_table .fcrm_add_new_row .fcrm_add_new_button{display:flex;align-items:center;gap:5px;width:50%;border:none;cursor:pointer;padding:10px 0;color:var(--fc-secondary-text);font-size:14px;font-style:normal;font-weight:500;line-height:20px;letter-spacing:-.084px}.fcrm_dc_csv_mapper .el-progress__text,.fcrm_review_configurator .el-progress__text{text-align:right}.fcrm_dc_csv_mapper .fcrm_importing_stats--title,.fcrm_review_configurator .fcrm_importing_stats--title{margin:0 0 10px;font-weight:500;font-size:16px;line-height:24px;color:var(--fc-primary-text)}.fcrm_company_photo{display:inline-block;width:40px;height:40px;flex-shrink:0;border-radius:50%;overflow:hidden}.fcrm_company_photo img{width:40px;height:40px;object-fit:cover;object-position:center;display:block}.fcrm_import_tag_mapper .el-switch,.fcrm_import_contact_field_mapper .el-switch{--el-switch-on-color: var(--fc-deep-bg);--el-switch-off-color: var(--fc-light-bg);height:20px}.fcrm_import_tag_mapper .el-switch .el-switch__core,.fcrm_import_contact_field_mapper .el-switch .el-switch__core{height:20px;min-width:36px;border:none;border-radius:10px;background-color:var(--fc-light-bg);box-shadow:none;transition:background-color .2s ease}.fcrm_import_tag_mapper .el-switch .el-switch__core .el-switch__action,.fcrm_import_contact_field_mapper .el-switch .el-switch__core .el-switch__action{width:16px;height:16px;border-radius:50%;background-color:var(--fc-primary-bg);box-shadow:0 1px 2px #00000014;border:1px solid var(--fc-secondary-border);top:2px;left:2px!important;transition:left .2s ease,border-color .2s ease,box-shadow .2s ease}.fcrm_import_tag_mapper .el-switch.is-checked .el-switch__core,.fcrm_import_contact_field_mapper .el-switch.is-checked .el-switch__core{background-color:var(--fc-deep-bg)}.fcrm_import_tag_mapper .el-switch.is-checked .el-switch__core .el-switch__action,.fcrm_import_contact_field_mapper .el-switch.is-checked .el-switch__core .el-switch__action{left:calc(100% - 18px)!important;margin-left:0;transform:none;border-color:#fff3;box-shadow:0 1px 2px #00000026}.fcrm_company_info_drawer .fcrm_drawer_header,.fcrm_custom_fields_drawer .fcrm_drawer_header{margin-bottom:0;padding:20px!important;display:flex;align-items:center;gap:12px;border-bottom:1px solid var(--fc-primary-border);background:var(--fc-primary-bg)}.fcrm_company_info_drawer .fcrm_drawer_header .fcrm_drawer_back_button,.fcrm_custom_fields_drawer .fcrm_drawer_header .fcrm_drawer_back_button{display:flex;align-items:center;justify-content:center;width:20px;height:20px;padding:0;margin:0;border:none;background:transparent;cursor:pointer;color:var(--fc-primary-text);flex-shrink:0;transition:opacity .2s ease}.fcrm_company_info_drawer .fcrm_drawer_header .fcrm_drawer_back_button:hover,.fcrm_custom_fields_drawer .fcrm_drawer_header .fcrm_drawer_back_button:hover{opacity:.7}.fcrm_company_info_drawer .fcrm_drawer_header .fcrm_drawer_back_button .el-icon,.fcrm_custom_fields_drawer .fcrm_drawer_header .fcrm_drawer_back_button .el-icon{width:20px;height:20px;font-size:20px}.fcrm_company_info_drawer .fcrm_drawer_header .fcrm_drawer_title,.fcrm_custom_fields_drawer .fcrm_drawer_header .fcrm_drawer_title{font-size:18px;line-height:24px;font-weight:500;letter-spacing:-.27px;color:var(--fc-primary-text);margin:0;flex:1 0 0}.fcrm_company_info_drawer .fcrm_drawer_header .el-drawer__title,.fcrm_custom_fields_drawer .fcrm_drawer_header .el-drawer__title{font-size:18px;line-height:24px;font-weight:500;letter-spacing:-.27px;color:var(--fc-primary-text)}.fcrm_company_info_drawer .fcrm_drawer_header .el-drawer__close-btn,.fcrm_custom_fields_drawer .fcrm_drawer_header .el-drawer__close-btn{margin:0;padding:0;width:20px;height:20px;min-width:20px;border:none;display:flex;align-items:center;justify-content:center;color:var(--fc-primary-text)}.fcrm_company_info_drawer .fcrm_drawer_header .el-drawer__close-btn .el-drawer__close,.fcrm_custom_fields_drawer .fcrm_drawer_header .el-drawer__close-btn .el-drawer__close{font-size:18px;line-height:1}.fcrm_company_info_drawer .fcrm_contact_form_handler,.fcrm_custom_fields_drawer .fcrm_contact_form_handler{height:100%;display:flex;flex-direction:column}.fcrm_company_info_drawer .fcrm_contact_form_handler .fcrm_add_contact_form,.fcrm_custom_fields_drawer .fcrm_contact_form_handler .fcrm_add_contact_form{display:flex;flex-direction:column;gap:20px;margin-bottom:16px}.fcrm_company_info_drawer .fcrm_contact_form_handler .fcrm_add_contact_form .fcrm_custom_field_wrapper,.fcrm_custom_fields_drawer .fcrm_contact_form_handler .fcrm_add_contact_form .fcrm_custom_field_wrapper{margin-bottom:0}.fcrm_company_info_drawer .fcrm_contact_form_handler .fcrm_add_contact_form .fcrm_custom_field_wrapper .fcrm_custom_fields,.fcrm_custom_fields_drawer .fcrm_contact_form_handler .fcrm_add_contact_form .fcrm_custom_field_wrapper .fcrm_custom_fields{border:none;padding:0}.fcrm_company_info_drawer .fcrm_contact_form_handler .fcrm_add_contact_form .fcrm_custom_field_wrapper .fcrm_custom_fields .fcrm_custom_fields_form .fcrm_custom_field_box .fcrm_custom_fields_layout .el-row,.fcrm_custom_fields_drawer .fcrm_contact_form_handler .fcrm_add_contact_form .fcrm_custom_field_wrapper .fcrm_custom_fields .fcrm_custom_fields_form .fcrm_custom_field_box .fcrm_custom_fields_layout .el-row{row-gap:20px}.fcrm_company_info_drawer .fcrm_contact_form_handler .fcrm_add_contact_form .fcrm_custom_field_wrapper .fcrm_custom_fields .fcrm_custom_fields_form .fcrm_custom_field_box .fcrm_custom_fields_layout .fcrm_custom_fields_half,.fcrm_custom_fields_drawer .fcrm_contact_form_handler .fcrm_add_contact_form .fcrm_custom_field_wrapper .fcrm_custom_fields .fcrm_custom_fields_form .fcrm_custom_field_box .fcrm_custom_fields_layout .fcrm_custom_fields_half,.fcrm_company_info_drawer .fcrm_contact_form_handler .fcrm_add_contact_form .fcrm_custom_field_wrapper .fcrm_custom_fields .fcrm_custom_fields_form .fcrm_custom_field_box .fcrm_custom_fields_layout .el-form-item,.fcrm_custom_fields_drawer .fcrm_contact_form_handler .fcrm_add_contact_form .fcrm_custom_field_wrapper .fcrm_custom_fields .fcrm_custom_fields_form .fcrm_custom_field_box .fcrm_custom_fields_layout .el-form-item{margin-bottom:0}.fcrm_company_info_drawer .fcrm_contact_form_handler .fcrm_add_contact_form .el-input,.fcrm_custom_fields_drawer .fcrm_contact_form_handler .fcrm_add_contact_form .el-input{width:100%}.fcrm_company_info_drawer .fcrm_contact_form_handler .fcrm_add_contact_form h3,.fcrm_custom_fields_drawer .fcrm_contact_form_handler .fcrm_add_contact_form h3{margin:12px 0 8px;font-size:16px;line-height:24px;font-weight:600;letter-spacing:-.12px;color:var(--fc-primary-text)}.fcrm_company_info_drawer .fcrm_contact_form_handler .fcrm_add_contact_form .el-form-item,.fcrm_custom_fields_drawer .fcrm_contact_form_handler .fcrm_add_contact_form .el-form-item{margin-bottom:0;display:flex;flex-direction:column}.fcrm_company_info_drawer .fcrm_contact_form_handler .fcrm_add_contact_form .el-form-item .el-form-item__content .el-date-editor,.fcrm_custom_fields_drawer .fcrm_contact_form_handler .fcrm_add_contact_form .el-form-item .el-form-item__content .el-date-editor{padding:0}.fcrm_company_info_drawer .fcrm_contact_form_handler .fcrm_add_contact_form .fcrm_phone_input,.fcrm_custom_fields_drawer .fcrm_contact_form_handler .fcrm_add_contact_form .fcrm_phone_input{width:100%}.fcrm_company_info_drawer .fcrm_contact_form_handler .fcrm_add_contact_form .fcrm_phone_input .iti,.fcrm_company_info_drawer .fcrm_contact_form_handler .fcrm_add_contact_form .fcrm_intl_tel_input.fcrm_phone_input .iti,.fcrm_custom_fields_drawer .fcrm_contact_form_handler .fcrm_add_contact_form .fcrm_phone_input .iti,.fcrm_custom_fields_drawer .fcrm_contact_form_handler .fcrm_add_contact_form .fcrm_intl_tel_input.fcrm_phone_input .iti{height:36px;min-height:36px;border-radius:8px;border:1px solid var(--fc-primary-border);box-shadow:none}.fcrm_company_info_drawer .fcrm_contact_form_handler .fcrm_add_contact_form .fcrm_phone_input .iti:hover,.fcrm_company_info_drawer .fcrm_contact_form_handler .fcrm_add_contact_form .fcrm_intl_tel_input.fcrm_phone_input .iti:hover,.fcrm_custom_fields_drawer .fcrm_contact_form_handler .fcrm_add_contact_form .fcrm_phone_input .iti:hover,.fcrm_custom_fields_drawer .fcrm_contact_form_handler .fcrm_add_contact_form .fcrm_intl_tel_input.fcrm_phone_input .iti:hover{border-color:var(--fc-secondary-border)}.fcrm_company_info_drawer .fcrm_contact_form_handler .fcrm_add_contact_form .fcrm_phone_input .iti input.iti__tel-input,.fcrm_company_info_drawer .fcrm_contact_form_handler .fcrm_add_contact_form .fcrm_phone_input .iti .fcrm_phone_tel_input.iti__tel-input,.fcrm_company_info_drawer .fcrm_contact_form_handler .fcrm_add_contact_form .fcrm_intl_tel_input.fcrm_phone_input .iti input.iti__tel-input,.fcrm_company_info_drawer .fcrm_contact_form_handler .fcrm_add_contact_form .fcrm_intl_tel_input.fcrm_phone_input .iti .fcrm_phone_tel_input.iti__tel-input,.fcrm_custom_fields_drawer .fcrm_contact_form_handler .fcrm_add_contact_form .fcrm_phone_input .iti input.iti__tel-input,.fcrm_custom_fields_drawer .fcrm_contact_form_handler .fcrm_add_contact_form .fcrm_phone_input .iti .fcrm_phone_tel_input.iti__tel-input,.fcrm_custom_fields_drawer .fcrm_contact_form_handler .fcrm_add_contact_form .fcrm_intl_tel_input.fcrm_phone_input .iti input.iti__tel-input,.fcrm_custom_fields_drawer .fcrm_contact_form_handler .fcrm_add_contact_form .fcrm_intl_tel_input.fcrm_phone_input .iti .fcrm_phone_tel_input.iti__tel-input{height:34px;min-height:34px;padding:0 10px 0 52px;border:none;border-radius:8px}.fcrm_company_info_drawer .fcrm_contact_form_handler .fcrm_add_contact_form .fcrm_prefix_select .el-select,.fcrm_custom_fields_drawer .fcrm_contact_form_handler .fcrm_add_contact_form .fcrm_prefix_select .el-select{width:140px}.fcrm_company_info_drawer .fcrm_contact_form_handler .fcrm_add_contact_form .fcrm_is-required .el-form-item__label,.fcrm_custom_fields_drawer .fcrm_contact_form_handler .fcrm_add_contact_form .fcrm_is-required .el-form-item__label{position:relative}.fcrm_company_info_drawer .fcrm_contact_form_handler .fcrm_add_contact_form .fcrm_is-required .el-form-item__label:after,.fcrm_custom_fields_drawer .fcrm_contact_form_handler .fcrm_add_contact_form .fcrm_is-required .el-form-item__label:after{content:"*";color:var(--fc-text-link);margin-inline-start:4px}.fcrm_company_info_drawer .fcrm_contact_form_handler .fcrm_add_contact_form .is-error .el-input__wrapper,.fcrm_company_info_drawer .fcrm_contact_form_handler .fcrm_add_contact_form .is-error .el-select__wrapper,.fcrm_custom_fields_drawer .fcrm_contact_form_handler .fcrm_add_contact_form .is-error .el-input__wrapper,.fcrm_custom_fields_drawer .fcrm_contact_form_handler .fcrm_add_contact_form .is-error .el-select__wrapper{border-color:var(--fc-error);box-shadow:0 0 0 1px var(--fc-error) inset}.fcrm_company_info_drawer .fcrm_contact_form_handler .fcrm_add_contact_form .fcrm_address_block,.fcrm_custom_fields_drawer .fcrm_contact_form_handler .fcrm_add_contact_form .fcrm_address_block{padding:16px;border-radius:var(--radius-8, 8px);background:var(--fc-secondary-bg)}.fcrm_company_info_drawer .fcrm_contact_form_handler .fcrm_add_contact_form .fcrm_address_block .el-row,.fcrm_custom_fields_drawer .fcrm_contact_form_handler .fcrm_add_contact_form .fcrm_address_block .el-row{row-gap:16px}.fcrm_company_info_drawer .fcrm_contact_form_handler .fcrm_add_contact_form .fcrm_section_heading,.fcrm_custom_fields_drawer .fcrm_contact_form_handler .fcrm_add_contact_form .fcrm_section_heading{margin-bottom:16px;color:var(--fc-primary-text);font-size:16px;font-style:normal;font-weight:500;line-height:24px;letter-spacing:-.176px;width:100%}.fcrm_company_info_drawer .fcrm_contact_form_handler .fcrm_add_contact_form .fcrm_identifier_content,.fcrm_custom_fields_drawer .fcrm_contact_form_handler .fcrm_add_contact_form .fcrm_identifier_content{padding:16px;display:flex;flex-direction:column;gap:20px;border-radius:var(--radius-8, 8px);background:var(--fc-secondary-bg)}.fcrm_company_info_drawer .fcrm_contact_form_handler .fcrm_add_contact_form .fcrm_identifier_row,.fcrm_custom_fields_drawer .fcrm_contact_form_handler .fcrm_add_contact_form .fcrm_identifier_row{display:flex;gap:20px;align-items:flex-start}@media (max-width: 600px){.fcrm_company_info_drawer .fcrm_contact_form_handler .fcrm_add_contact_form .fcrm_identifier_row,.fcrm_custom_fields_drawer .fcrm_contact_form_handler .fcrm_add_contact_form .fcrm_identifier_row{flex-direction:column}}.fcrm_company_info_drawer .fcrm_contact_form_handler .fcrm_add_contact_form .fcrm_identifier_field,.fcrm_custom_fields_drawer .fcrm_contact_form_handler .fcrm_add_contact_form .fcrm_identifier_field{flex:1 0 0;min-width:0}@media (max-width: 600px){.fcrm_company_info_drawer .fcrm_contact_form_handler .fcrm_add_contact_form .fcrm_identifier_field,.fcrm_custom_fields_drawer .fcrm_contact_form_handler .fcrm_add_contact_form .fcrm_identifier_field{flex:1;width:100%}}.fcrm_company_info_drawer .fcrm_contact_form_handler .fcrm_add_contact_form .fcrm_identifier_field .el-form-item,.fcrm_custom_fields_drawer .fcrm_contact_form_handler .fcrm_add_contact_form .fcrm_identifier_field .el-form-item{margin-bottom:0}.fcrm_company_info_drawer .fcrm_contact_form_handler .fcrm_add_contact_form .fcrm_identifier_field .el-form-item__label,.fcrm_custom_fields_drawer .fcrm_contact_form_handler .fcrm_add_contact_form .fcrm_identifier_field .el-form-item__label{font-size:14px;font-weight:500;line-height:20px;letter-spacing:-.084px;color:var(--fc-primary-text);margin-bottom:4px;padding-bottom:0}.fcrm_company_info_drawer .fcrm_contact_form_handler .fcrm_add_contact_form .fcrm_identifier_field_full,.fcrm_custom_fields_drawer .fcrm_contact_form_handler .fcrm_add_contact_form .fcrm_identifier_field_full{width:100%}.fcrm_company_info_drawer .fcrm_contact_form_handler .fcrm_add_contact_form .fcrm_identifier_field_full .el-form-item,.fcrm_custom_fields_drawer .fcrm_contact_form_handler .fcrm_add_contact_form .fcrm_identifier_field_full .el-form-item{margin-bottom:0}.fcrm_company_info_drawer .fcrm_contact_form_handler .fcrm_add_contact_form .fcrm_identifier_field_full .el-form-item__label,.fcrm_custom_fields_drawer .fcrm_contact_form_handler .fcrm_add_contact_form .fcrm_identifier_field_full .el-form-item__label{font-size:14px;font-weight:500;line-height:20px;letter-spacing:-.084px;color:var(--fc-primary-text);margin-bottom:4px;padding-bottom:0}.fcrm_company_info_drawer .fcrm_contact_form_handler .fcrm_add_contact_form .fcrm_identifier_section .el-form-item,.fcrm_custom_fields_drawer .fcrm_contact_form_handler .fcrm_add_contact_form .fcrm_identifier_section .el-form-item{margin-bottom:0}.fcrm_company_info_drawer .fcrm_contact_form_handler .fcrm_add_contact_form .fcrm_identifier_section .el-select__placeholder,.fcrm_custom_fields_drawer .fcrm_contact_form_handler .fcrm_add_contact_form .fcrm_identifier_section .el-select__placeholder{color:var(--fc-secondary-text);font-size:14px;font-weight:400;line-height:20px;letter-spacing:-.084px}.fcrm_company_info_drawer .fcrm_contact_form_handler .fcrm_custom_field_wrapper,.fcrm_custom_fields_drawer .fcrm_contact_form_handler .fcrm_custom_field_wrapper{padding:16px;border-radius:var(--radius-8, 8px);background:var(--fc-secondary-bg)}.fcrm_company_info_drawer .fcrm_contact_form_handler .fcrm_custom_field_wrapper .fcrm_custom_field_group_box,.fcrm_custom_fields_drawer .fcrm_contact_form_handler .fcrm_custom_field_wrapper .fcrm_custom_field_group_box{background:var(--fc-primary-bg);padding-bottom:20px;margin-bottom:0}.fcrm_company_info_drawer .fcrm_contact_form_handler .fcrm_custom_fields_header,.fcrm_custom_fields_drawer .fcrm_contact_form_handler .fcrm_custom_fields_header{display:flex;align-items:center;justify-content:space-between;margin-bottom:16px;width:100%;box-sizing:border-box}.fcrm_company_info_drawer .fcrm_contact_form_handler .fcrm_custom_fields_header .fcrm_custom_fields_header_label h3,.fcrm_custom_fields_drawer .fcrm_contact_form_handler .fcrm_custom_fields_header .fcrm_custom_fields_header_label h3{font-size:14px;font-weight:500;color:var(--fc-primary-text);margin:0}.fcrm_company_info_drawer .fcrm_contact_form_handler .fcrm_custom_fields_form,.fcrm_custom_fields_drawer .fcrm_contact_form_handler .fcrm_custom_fields_form{padding:0;display:flex;flex-direction:column;gap:16px}.fcrm_company_info_drawer .fcrm_contact_form_handler .fcrm_custom_fields_form .fcrm_custom_fields_layout,.fcrm_custom_fields_drawer .fcrm_contact_form_handler .fcrm_custom_fields_form .fcrm_custom_fields_layout{display:flex;flex-wrap:wrap;gap:16px;align-items:flex-start}.fcrm_company_info_drawer .fcrm_contact_form_handler .fcrm_custom_fields_form .fcrm_custom_fields_half,.fcrm_custom_fields_drawer .fcrm_contact_form_handler .fcrm_custom_fields_form .fcrm_custom_fields_half{flex:0 0 calc(50% - 8px);min-width:278px;margin-bottom:16px}.fcrm_company_info_drawer .fcrm_contact_form_handler .fcrm_custom_fields_form .fcrm_custom_fields_half .el-form-item__label,.fcrm_custom_fields_drawer .fcrm_contact_form_handler .fcrm_custom_fields_form .fcrm_custom_fields_half .el-form-item__label{font-size:14px;font-weight:500;line-height:20px;letter-spacing:-.084px;color:var(--fc-primary-text);margin-bottom:4px;padding-bottom:0}.fcrm_company_info_drawer .fcrm_contact_form_handler .fcrm_add_contact_footer,.fcrm_custom_fields_drawer .fcrm_contact_form_handler .fcrm_add_contact_footer{background-color:var(--fc-primary-bg);border-top:1px solid var(--fc-primary-border);padding:12px 20px;display:flex;justify-content:space-between;align-items:center;width:100%;margin-top:auto;flex-wrap:wrap;gap:10px}.fcrm_company_info_drawer .fcrm_contact_form_handler .fcrm_add_contact_footer .fcrm_add_contact_footer_actions,.fcrm_custom_fields_drawer .fcrm_contact_form_handler .fcrm_add_contact_footer .fcrm_add_contact_footer_actions{display:flex;gap:12px;justify-content:flex-end;min-width:0;flex-wrap:wrap}.fcrm_company_info_drawer .fcrm_contact_form_handler .fcrm_add_contact_footer .fcrm_add_contact_footer_actions .el-button,.fcrm_custom_fields_drawer .fcrm_contact_form_handler .fcrm_add_contact_footer .fcrm_add_contact_footer_actions .el-button{margin:0}.fcrm-pagination-bar{width:100%;display:flex;align-items:center;justify-content:space-between;padding:12px 20px!important;font-size:14px;letter-spacing:-.084px;gap:10px;flex-wrap:wrap;font-family:inherit;border-top:1px solid var(--fc-primary-border)}.fcrm-pagination-bar .el-pagination{padding:0!important}.fcrm-pagination-bar .fcrm-pagination-bar__left{display:flex;align-items:center;gap:10px}.fcrm-pagination-bar .fcrm-pagination-bar__page-info{line-height:28px;white-space:nowrap;min-height:28px;display:inline-flex;align-items:center;color:var(--fc-secondary-text);text-align:center;font-size:14px;font-style:normal;font-weight:400;line-height:20px;letter-spacing:-.084px}.fcrm-pagination-bar .fcrm-pagination-bar__sizes{flex-shrink:0;width:auto;min-width:108px;max-width:108px}.fcrm-pagination-bar .fcrm-pagination-bar__right{margin:0;display:flex;align-items:center;gap:8px}.fcrm-pagination-bar .fcrm-pagination-bar__nav{display:inline-flex;align-items:center;justify-content:center;align-self:center;flex-shrink:0;min-width:28px;height:28px;padding:0 6px;margin:0;border-radius:8px;border:1px solid var(--fc-primary-border);background:var(--fc-primary-bg);color:var(--fc-secondary-text);font-size:14px;font-weight:500;line-height:1;font-family:inherit;cursor:pointer;transition:color .15s,border-color .15s,background .15s}.fcrm-pagination-bar .fcrm-pagination-bar__nav:hover:not(:disabled){color:var(--fc-primary-text);border-color:var(--fc-primary-text);background:var(--fc-primary-bg)}.fcrm-pagination-bar .fcrm-pagination-bar__nav:disabled{color:var(--fc-text-muted);cursor:not-allowed}.fcrm-pagination-bar .fcrm-pagination-bar__nav--first svg{rotate:180deg}.fcrm-pagination-bar .fcrm-pagination-bar__right-inner{display:flex;align-items:center;gap:8px;font-size:14px;font-weight:500;letter-spacing:-.084px}.fcrm-pagination-bar .fcrm-pagination-bar__right-inner.el-pagination{padding:0}.fcrm-pagination-bar .fcrm-pagination-bar__right-inner .btn-prev,.fcrm-pagination-bar .fcrm-pagination-bar__right-inner .btn-next{padding:0 6px;border-radius:8px;border:1px solid var(--fc-primary-border);background:var(--fc-primary-bg);height:28px;min-width:28px;display:inline-flex;align-items:center;justify-content:center;font:inherit;color:var(--fc-secondary-text);transition:color .15s,border-color .15s,background .15s}.fcrm-pagination-bar .fcrm-pagination-bar__right-inner .btn-prev:hover:not(:disabled),.fcrm-pagination-bar .fcrm-pagination-bar__right-inner .btn-next:hover:not(:disabled){color:var(--fc-primary-text);border-color:var(--fc-primary-text);background:var(--fc-primary-bg)}.fcrm-pagination-bar .fcrm-pagination-bar__right-inner .btn-prev:disabled,.fcrm-pagination-bar .fcrm-pagination-bar__right-inner .btn-next:disabled{color:var(--fc-text-muted)}.fcrm-pagination-bar .fcrm-pagination-bar__right-inner .el-pager{display:flex;align-items:center;gap:4px}.fcrm-pagination-bar .fcrm-pagination-bar__right-inner .el-pager li{width:auto;min-width:28px;height:28px;line-height:26px;border-radius:8px;border:1px solid var(--fc-primary-border);background:var(--fc-primary-bg);margin:0;transition:border-color .15s,color .15s;color:var(--fc-secondary-text);text-align:center;font-size:14px;font-style:normal;font-weight:500;line-height:20px;letter-spacing:-.084px}.fcrm-pagination-bar .fcrm-pagination-bar__right-inner .el-pager li.is-active{border-color:var(--fc-primary-text);background:var(--fc-primary-bg)}.fcrm-pagination-bar .fcrm-pagination-bar__right-inner .el-pager li:not(.is-active):hover{border-color:var(--fc-primary-text);color:var(--fc-primary-text)}.fcrm-pagination-bar .fcrm-pagination-bar__right-inner .el-pager li.more{color:var(--fc-secondary-text)}.fcrm-contacts-pagination{display:flex;align-items:center;justify-content:space-between;font-size:14px;letter-spacing:-.084px}.fcrm-contacts-pagination .fcrm-contacts-pagination-left{display:flex;align-items:center;gap:10px;flex:0 0 auto;white-space:nowrap}.fcrm-contacts-pagination .fcrm-contacts-page-info{color:var(--fc-secondary-text);line-height:28px;letter-spacing:-.084px;white-space:nowrap;min-height:28px;display:inline-flex;align-items:center}.fcrm-contacts-pagination .el-pagination{display:flex;align-items:center;padding:0;gap:8px;font-size:14px}.fcrm-contacts-pagination .fcrm-contacts-page-size{flex-shrink:0}.fcrm-contacts-pagination .fcrm-contacts-pagination-right{margin:0}.fcrm-contacts-pagination .btn-prev,.fcrm-contacts-pagination .btn-next{padding:0 6px;border-radius:8px;border:1px solid var(--fc-primary-border);background:var(--fc-primary-bg);height:28px;min-width:28px;display:inline-flex;align-items:center;justify-content:center;font:inherit}.fcrm-contacts-pagination .el-pager li{min-width:28px;height:28px;line-height:28px;border-radius:8px;border:1px solid var(--fc-primary-border);background:var(--fc-primary-bg);font-size:14px;font-weight:500;letter-spacing:-.084px;color:var(--fc-secondary-text);margin:0 4px}.fcrm-contacts-pagination .el-pager li.is-active{border-color:var(--fc-primary-text);color:var(--fc-primary-text)}.fcrm-contacts-pagination .fcrm-contacts-page-size.el-select--small{font:inherit}.fcrm-contacts-pagination .fcrm-contacts-page-size .el-select__wrapper{border-radius:8px;height:28px;min-height:28px;padding:0 8px;background:var(--fc-secondary-bg);border:1px solid var(--fc-primary-border);box-shadow:none}.fcrm-contacts-pagination .fcrm-contacts-page-size .el-select__placeholder span,.fcrm-contacts-pagination .fcrm-contacts-page-size .el-select__selected-item{color:var(--fc-secondary-text)}.fcrm-contacts-pagination .fcrm-contacts-page-size .el-select__caret{color:var(--fc-text-muted)}.fcrm-contacts-pagination .fcrm-contacts-page-size .el-select__selected-item,.fcrm-contacts-pagination .fcrm-contacts-page-size .el-select__placeholder,.fcrm-contacts-pagination .fcrm-contacts-page-size .el-select__input-wrapper{height:28px;line-height:28px;display:inline-flex;align-items:center}.fluentcrm-pagination .el-pager li{margin-bottom:0!important}.fluentcrm-pagination .el-pagination__sizes .el-select{width:100px}.fluent_theme_dark,.fcrm-dark{--fc-primary-bg: #151d26;--fc-secondary-bg: #1c2732;--fc-light-bg: #222530;--fc-deep-bg: #F5F7FA;--fc-weak-bg-25: #1a2330;--alpha-white-alpha-10: #0000001A;--fc-ai-background: #211f3e;--fc-ai-color: #8762F0;--fc-primary-text: #F5F7FA;--fc-secondary-text: #CACFD8;--fc-text-muted: #99A0AE;--fc-text-inverse: #0E121B;--fc-primary-border: #2c3c4e;--fc-secondary-border: #3A414F;--fc-primary-button: #F5F7FA;--fc-text-link: #7C97FF;--fc-success: #32D583;--fc-success-bg: #163828;--fc-error: #FF5A67;--fc-error-bg: #3D1C22;--fc-warning: #F7C948;--fc-warning-bg: #3B2E12;--fc-text-link-bg: #1C254D;--fc-badge-unsubscribed-text: #CACFD8;--fc-badge-unsubscribed-bg: #99A0AE29;--fc-badge-subscribed-text: #3EE089;--fc-badge-subscribed-bg: #1FC16B1A;--fc-badge-pending-text: #FFD268;--fc-badge-pending-bg: #FBC64B29;--fc-badge-transactional-text: #8C71F6;--fc-badge-transactional-bg: #784DEF29;--fc-badge-bounced-text: #6895FF;--fc-badge-bounced-bg: #476CFF29;--fc-badge-complained-text: #FFA468;--fc-badge-complained-bg: #FA731929;--fc-badge-spammed-text: #FF6875;--fc-badge-spammed-bg: #FB374829;--el-color-primary: var(--fc-deep-bg);--el-color-primary-light-3: var(--fc-secondary-text);--el-color-primary-light-5: var(--fc-text-muted);--el-color-primary-light-7: var(--fc-secondary-border);--el-color-primary-light-8: var(--fc-primary-border);--el-color-primary-light-9: var(--fc-secondary-bg);--el-color-primary-dark-2: #FFFFFF;--el-color-success: var(--fc-success);--el-color-warning: var(--fc-warning);--el-color-danger: var(--fc-error);--el-color-error: var(--fc-error);--el-color-info: var(--fc-text-muted);--el-text-color-primary: var(--fc-primary-text);--el-text-color-regular: var(--fc-secondary-text);--el-text-color-secondary: var(--fc-text-muted);--el-text-color-placeholder: var(--fc-text-muted);--el-text-color-disabled: #6B7280;--el-border-color: var(--fc-primary-border);--el-border-color-light: var(--fc-primary-border);--el-border-color-lighter: var(--fc-secondary-border);--el-border-color-dark: var(--fc-secondary-border);--el-fill-color-light: var(--fc-secondary-bg);--el-fill-color-lighter: #1D212B;--el-bg-color: var(--fc-primary-bg);--el-bg-color-page: var(--fc-secondary-bg);--el-mask-color: var(--fc-primary-bg);--el-fill-color-blank: var(--fc-primary-bg);--el-button-text-color: var(--fc-text-inverse);--el-bg-color-overlay: var(--fc-primary-bg);--el-skeleton-color: #1C2732;--el-skeleton-to-color: #283643}.fluent_theme_dark #wpcontent,.fcrm-dark #wpcontent{background-color:var(--fc-secondary-bg)}@layer element-plus{.fluent_theme_dark .el-input .el-input__count .el-input__count-inner,.fcrm-dark .el-input .el-input__count .el-input__count-inner{background:none}}.fluent_theme_dark .el-button.el-button--primary,.fcrm-dark .el-button.el-button--primary{--el-color-white: #0E121B}.fluent_theme_dark .el-popper.is-dark,.fluent_theme_dark .el-popper .el-tooltip,.fcrm-dark .el-popper.is-dark,.fcrm-dark .el-popper .el-tooltip{color:var(--fc-primary-text);background:var(--fc-primary-bg);border-color:var(--fc-primary-border)}.fluent_theme_dark .el-popper .el-popper__arrow:before,.fcrm-dark .el-popper .el-popper__arrow:before{background:var(--fc-primary-bg)}.fluent_theme_dark .el-upload-dragger,.fcrm-dark .el-upload-dragger{background:var(--fc-primary-bg)}.fluent_theme_dark .fcrm_notice,.fcrm-dark .fcrm_notice{background:var(--fc-primary-bg);border-top-color:var(--fc-primary-border);border-right-color:var(--fc-primary-border);border-bottom-color:var(--fc-primary-border)}.fluent_theme_dark .fluentcrm_block.fc_block_type_conditional .fluentcrm_blockin .fluentcrm_block_title,.fcrm-dark .fluentcrm_block.fc_block_type_conditional .fluentcrm_blockin .fluentcrm_block_title,.fluent_theme_dark .fluentcrm_block.fluentcrm_block_trigger .fluentcrm_blockin .fluentcrm_block_title,.fcrm-dark .fluentcrm_block.fluentcrm_block_trigger .fluentcrm_blockin .fluentcrm_block_title,.fluent_theme_dark .fluentcrm_block.fluentcrm_block_trigger .fluentcrm_blockin .fluentcrm_block_title i,.fcrm-dark .fluentcrm_block.fluentcrm_block_trigger .fluentcrm_blockin .fluentcrm_block_title i{color:#151d26}.fluent_theme_dark .fc_dom_path.fc_ab_test>span,.fluent_theme_dark .fc_condition_node_point:not(.fc_dom_path_right) span,.fluent_theme_dark .block_cond_holder.block_cond_no,.fluent_theme_dark .fc_dom_path.fc_dom_path_left,.fcrm-dark .fc_dom_path.fc_ab_test>span,.fcrm-dark .fc_condition_node_point:not(.fc_dom_path_right) span,.fcrm-dark .block_cond_holder.block_cond_no,.fcrm-dark .fc_dom_path.fc_dom_path_left{border-color:#f2a0ae!important}.fluent_theme_dark .fc_dom_path.fc_condition_node_point.fc_dom_path_right,.fcrm-dark .fc_dom_path.fc_condition_node_point.fc_dom_path_right{border-color:var(--fc-primary-text)!important}.fluent_theme_dark .fcrm_bulk_processing--bar .el-progress-bar__inner--striped,.fcrm-dark .fcrm_bulk_processing--bar .el-progress-bar__inner--striped{background-image:linear-gradient(45deg,rgba(0,0,0,.1) 25%,rgba(0,0,0,0) 0,transparent 50%,rgba(0,0,0,.1) 0,rgba(0,0,0,.1) 75%,rgba(0,0,0,0) 0,transparent)}.fluent_theme_dark .el-skeleton .el-skeleton__item,.fcrm-dark .el-skeleton .el-skeleton__item{--el-skeleton-color: #1C2732;--el-skeleton-to-color: #283643} diff --git a/wp-content/plugins/fluent-crm/assets/admin/css/app_global.css b/wp-content/plugins/fluent-crm/assets/admin/css/app_global.css new file mode 100644 index 0000000..3d93b68 --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/admin/css/app_global.css @@ -0,0 +1 @@ +.fluentcrm_force_hide{display:none!important}.fluentcrm_main_menu_items{display:block;position:relative;margin-bottom:20px;background:var(--fc-primary-bg);margin-left:-20px;padding-left:20px}.fluentcrm_main_menu_items *{box-sizing:border-box}.fluentcrm_main_menu_items .fluentcrm_menu_logo_holder{display:inline-block;margin-right:10px;vertical-align:middle;padding:10px 0}.fluentcrm_main_menu_items .fluentcrm_menu_logo_holder a{display:block;overflow:hidden;line-height:0}.fluentcrm_main_menu_items .fluentcrm_menu_logo_holder a img{padding:2px 0;height:36px;outline:none}.fluentcrm_main_menu_items .fluentcrm_menu_logo_holder a:focus{outline:none;box-shadow:none}.fluentcrm_main_menu_items .fluentcrm_menu_logo_holder a span{position:absolute;top:20px;color:var(--fc-primary-text);padding-left:5px;font-size:10px}.fluentcrm_main_menu_items ul.fluentcrm_menu{display:inline-block;list-style:none;margin:0;padding:0 20px 0 0;vertical-align:top;float:right}.fluentcrm_main_menu_items ul.fluentcrm_menu li{display:inline-block;padding:0;margin:0}.fluentcrm_main_menu_items ul.fluentcrm_menu li.fluentcrm_active{border-bottom:2px solid var(--fc-deep-bg);background:var(--fc-secondary-bg)}.fluentcrm_main_menu_items ul.fluentcrm_menu li a:focus{outline:0 solid transparent}.fluentcrm_main_menu_items ul.fluentcrm_menu li .fluentcrm_menu_primary{padding:20px 15px;display:block;font-size:14px;line-height:100%;text-decoration:none;color:var(--fc-text-muted)}.fluentcrm_main_menu_items ul.fluentcrm_menu li .fluentcrm_menu_primary span{font-size:14px;line-height:17px;margin-bottom:-6px}.fluentcrm_main_menu_items ul.fluentcrm_menu li .fluentcrm_menu_primary:hover{color:var(--fc-primary-text)}.fluentcrm_main_menu_items ul.fluentcrm_menu li .fluentcrm_submenu_items{display:none}.fluentcrm_main_menu_items ul.fluentcrm_menu li.fluentcrm_has_sub_items{position:relative}.fluentcrm_main_menu_items ul.fluentcrm_menu li.fluentcrm_has_sub_items .fluentcrm_submenu_items a{width:100%;display:block;text-decoration:none;padding:10px 15px}.fluentcrm_main_menu_items ul.fluentcrm_menu li.fluentcrm_has_sub_items:hover>.fluentcrm_submenu_items{display:block;z-index:9999999;overflow:hidden;top:90%;right:0;position:absolute;min-width:10rem;padding:.5rem 0;margin:.125rem 0 0;font-size:1rem;color:var(--fc-text-muted);text-align:left;list-style:none;background-color:var(--fc-primary-bg);background-clip:padding-box;border:1px solid rgba(0,0,0,.15);border-radius:.25rem;box-shadow:0 3px 22px #b0b8becc}.fluentcrm_main_menu_items ul.fluentcrm_menu li.fluentcrm_has_sub_items:hover>.fluentcrm_submenu_items.fc_2_col_menu{right:-235px;width:600px;display:grid;font-size:inherit;gap:10px;grid-auto-flow:row;grid-gap:10px;padding:10px;white-space:normal;grid-template-columns:repeat(2,minmax(30px,1fr))}.fluentcrm_main_menu_items ul.fluentcrm_menu li.fluentcrm_has_sub_items:hover>.fluentcrm_submenu_items.fc_2_col_menu a:hover{background:var(--fc-secondary-bg)}.fluentcrm_main_menu_items ul.fluentcrm_menu li.fluentcrm_has_sub_items:hover>.fluentcrm_submenu_items.fc_2_col_menu .fc_menu_title{font-weight:600;font-size:110%;color:var(--fc-primary-text)}.fluentcrm_main_menu_items ul.fluentcrm_menu li.fluentcrm_has_sub_items:hover>.fluentcrm_submenu_items.fc_2_col_menu .fc_menu_description{margin:5px 0 0;padding:0}@media all and (max-width: 768px){.fluentcrm_main_menu_items ul.fluentcrm_menu li.fluentcrm_has_sub_items:hover>.fluentcrm_submenu_items.fc_2_col_menu{grid-template-columns:repeat(1,minmax(30px,1fr));width:auto;right:0}.fluentcrm_main_menu_items ul.fluentcrm_menu li.fluentcrm_has_sub_items:hover>.fluentcrm_submenu_items.fc_2_col_menu a .fc_menu_card .fc_menu_description{display:none}}.fluentcrm_main_menu_items ul.fluentcrm_menu li.fluentcrm_has_sub_items:hover>.fluentcrm_submenu_items.sms_menu{font-size:inherit;width:200px;padding:10px;white-space:normal;grid-template-columns:repeat(2,minmax(30px,1fr))}.fluentcrm_main_menu_items ul.fluentcrm_menu li.fluentcrm_has_sub_items:hover>.fluentcrm_submenu_items.sms_menu a:hover{background:var(--fc-secondary-bg)}.fluentcrm_main_menu_items ul.fluentcrm_menu li.fluentcrm_has_sub_items:hover>.fluentcrm_submenu_items.sms_menu .fc_menu_title{font-weight:600;font-size:110%;color:var(--fc-primary-text)}.fluentcrm_main_menu_items ul.fluentcrm_menu li.fluentcrm_has_sub_items:hover>.fluentcrm_submenu_items.sms_menu .fc_menu_description{margin:5px 0 0;padding:0}.fluentcrm_main_menu_items ul.fluentcrm_menu li.fluentcrm_has_sub_items:hover>.fluentcrm_submenu_items a{color:var(--fc-text-muted)}.fluentcrm_main_menu_items ul.fluentcrm_menu li.fluentcrm_has_sub_items:hover>.fluentcrm_submenu_items a:hover{background-color:var(--fc-secondary-bg)}.fluentcrm_main_menu_items .fluentcrm_handheld{display:none}@media all and (max-width: 768px){.fluentcrm_main_menu_items{margin-right:10px}.fluentcrm_main_menu_items ul.fluentcrm_menu{display:none}.fluentcrm_main_menu_items ul.fluentcrm_menu.fluentcrm_menu_open{display:block;border-top:3px solid var(--fc-secondary-bg);position:fixed;right:0;z-index:99999;background:var(--fc-primary-bg)}.fluentcrm_main_menu_items ul.fluentcrm_menu.fluentcrm_menu_open li{margin:0 30px 0 0}.fluentcrm_main_menu_items ul.fluentcrm_menu.fluentcrm_menu_open li.fluentcrm_menu_item{display:block;width:100%}.fluentcrm_main_menu_items ul.fluentcrm_menu.fluentcrm_menu_open .fluentcrm_menu_primary span{float:right}.fluentcrm_main_menu_items ul.fluentcrm_menu.fluentcrm_menu_open span.fc_submenu_handler{position:absolute;right:-20px}.fluentcrm_main_menu_items ul.fluentcrm_menu.fluentcrm_menu_open .fluentcrm_submenu_items{position:relative!important;padding-left:30px!important}.fluentcrm_main_menu_items .fluentcrm_handheld{display:inline-block;float:right;margin:15px 15px 0 0}.fluentcrm_main_menu_items .fluentcrm_handheld span{font-size:30px;width:100%}}@media all and (max-width: 768px){.fluentcrm_settings_wrapper .el-menu .el-menu-item{padding:0 5px!important}.fluentcrm_settings_wrapper li.el-menu-item span{display:inline-block;white-space:break-spaces;line-height:130%;font-size:10px}}@media all and (max-width: 425px){.fc_segment_menu{display:none}.fluentcrm_header{display:block!important}.fluentcrm_header .fluentcrm_header_title{display:block;width:100%!important}.fluentcrm_header .fluentcrm-actions{text-align:left!important;width:100%!important}.fluentcrm_header .fluentcrm-actions .input-with-select{min-width:120px}.fluentcrm_settings_wrapper .el-menu .el-menu-item{padding:0 5px!important}.fluentcrm_settings_wrapper .el-menu .el-menu-item span{display:none}.fluentcrm_settings_wrapper .fluentcrm_header,.fluentcrm_settings_wrapper .fluentcrm_pad_around{padding:10px}.fluentcrm_settings_wrapper .el-col.el-col-5{width:10%}.fluentcrm_settings_wrapper .el-col.fc_settings_wrapper{width:90%}.fluentcrm_settings_wrapper .el-col.fc_settings_wrapper span.el-checkbox__label{white-space:break-spaces;vertical-align:text-top}.auto-fold #wpcontent{padding-left:5px!important}.fluentcrm-app{margin-right:5px!important}ul.fluentcrm_profile_nav{display:flex;overflow:scroll}}@media all and (min-width: 767px){.fluentcrm_settings_wrapper .fc_side_menu_wrap{max-width:192px}.fluentcrm_settings_wrapper .fc_setting_wrap{min-width:calc(100% - 192px)}}.fc_item_half_no_float,.fc_item_half_no_float .el-input-number{width:100%}@media all and (max-width: 426px){.fluentcrm-app{margin-right:10px}}@media all and (min-width: 1440px){.fc_item_half{width:50%;float:left;padding-right:20px}.fc_item_half_no_float{width:50%;padding-right:20px}}.fc_image_radios .el-radio__input{display:none}.fc_image_radios .fc_image_box{width:140px;height:140px;background-repeat:no-repeat;background-size:contain;position:relative}.fc_image_radios .fc_image_box.fc_image_active{border:2px solid var(--fc-deep-bg);border-radius:10px}.fc_image_radios .fc_image_box span{position:absolute;bottom:2px;width:100%;text-align:center;font-size:10px}.fc_image_radios .el-radio__label{padding-left:0}.fc_image_radio_tooltips .el-radio__input{display:none}.fc_image_radio_tooltips .fc_image_box{width:140px;height:140px;background-repeat:no-repeat;background-size:contain;position:relative}.fc_image_radio_tooltips .fc_image_box.fc_image_active{border:2px solid var(--fc-deep-bg);border-radius:5px}.fc_image_radio_tooltips .fcrm_image_radio_label{display:block;font-weight:500;font-size:12px;line-height:16px;color:var(--fc-primary-text);margin:4px 0 0;padding:0 4px;overflow-wrap:anywhere;white-space:break-spaces}.fc_image_radio_tooltips .el-radio__label{padding-left:0}.fc_image_radio_tooltips .el-radio{margin-right:10px}.fc_filter_boxes>div{display:inline-block;margin-bottom:5px;margin-top:5px}.fc_filter_boxes .fluentcrm-filter-manager .fluentcrm-filterer .fluentcrm-filter .el-button{height:34px}img.fc_contact_photo{width:24px;height:24px;display:block;border-radius:50%}.fc_block_white{padding:20px;background:var(--fc-primary-bg);margin-bottom:30px;border-radius:5px;box-shadow:0 0 1px 1px var(--fc-light-bg)}span.ff_small{font-size:12px;color:var(--fc-text-muted);vertical-align:middle}.el-time-spinner__list .el-time-spinner__item{margin-bottom:0!important}.fc_profile_pop .fluentcrm_profile-photo{flex-shrink:0;padding-right:20px;padding-left:10px}.fc_profile_pop .fluentcrm_profile-photo img{border-radius:50%;width:128px;max-width:128px;max-height:128px;object-fit:cover}.fc_profile_pop .profile-info{min-width:0;overflow:hidden}.fc_profile_pop .profile_title h3{word-break:break-word;overflow-wrap:break-word;max-width:100%}.fc_profile_pop p{word-break:break-all;overflow-wrap:anywhere}.fc_new_line_items>label{width:100%;display:block}.fc_2_col_items>label{display:inline-block;margin:0 0 10px;width:50%}.fc_group_field_half>.el-form-item{width:49%;display:inline-block;padding-right:20px}#adminmenuwrap{z-index:9991}.el-dialog__wrapper *{box-sizing:border-box}.el-date-table td.start-date span,.el-date-table td.end-date span{background-color:var(--fc-deep-bg)}.el-date-table td.today span{color:var(--fc-deep-bg)}.el-date-table td.current:not(.disabled) .el-date-table-cell__text{color:var(--fc-text-inverse)}.fc_dropdown{padding:10px;box-sizing:border-box;border-radius:6px;border:1px solid var(--fc-primary-border)}.fc_dropdown *{box-sizing:border-box}.fc_dropdown li{border-radius:6px;padding:8px 15px;height:auto;transition:.4s;line-height:22px;font-weight:500}.fc_dropdown li+li{margin-top:2px}.fc_dropdown li.selected,.fc_dropdown li:not(.is-disabled):hover{color:var(--fc-primary-text);background-color:var(--fc-secondary-bg);font-weight:500}.fluentcrm_admin_dashboard>.el-row{width:100%}.fluentcrm_admin_dashboard .fcrm_card_widgets{margin-bottom:24px}.fcrm_dashboard_user{margin-bottom:20px;display:flex!important;align-items:center;gap:12px}.fcrm_dashboard_user_image img{width:48px;height:48px;flex-shrink:0;border-radius:999px;display:block;object-fit:cover}.fcrm_dashboard_user_info strong{color:var(--fc-primary-text)}.fcrm_dashboard_user_info h3{color:var(--fc-primary-text);font-size:18px;font-style:normal;font-weight:400;line-height:24px;letter-spacing:-.27px;margin:0 0 4px}.fcrm_dashboard_user_info p{color:var(--fc-secondary-text);font-size:14px;font-style:normal;font-weight:300;line-height:20px;letter-spacing:-.084px;margin:0}.fcrm_card_widgets{display:grid;grid-template-columns:repeat(auto-fit,minmax(160px,1fr));gap:24px;margin-bottom:24px}.fcrm_card_widgets--header{display:flex;align-items:center;gap:8px}.fcrm_card_widgets .fcrm_card_widget{display:flex;flex-direction:column;cursor:pointer;background:var(--fc-primary-bg);border-radius:var(--fcrm-border-radius-8, 8px);padding:16px;width:100%;transition:.4s}.fcrm_card_widgets .fcrm_card_widget.with-border{border:1px solid var(--fc-primary-border)}.fcrm_card_widgets .fcrm_card_widget.pointer-auto{cursor:auto}.fcrm_card_widgets .fcrm_card_widget .fcrm_card_widget_icon{width:28px;height:28px;display:flex;padding:5px;justify-content:center;align-items:center;border-radius:var(--radius-6, 6px)}.fcrm_card_widgets .fcrm_card_widget .fcrm_card_widget_icon svg{width:18px;height:18px}.fcrm_card_widgets .fcrm_card_widget .fcrm_card_widget_icon.fcrm_p_0{padding:0}.fcrm_card_widgets .fcrm_card_widget .fcrm_card_widget_title{color:var(--fc-secondary-text);font-size:12px;font-style:normal;font-weight:500;line-height:16px;margin:0;display:flex;align-items:center;gap:6px}.fcrm_card_widgets .fcrm_card_widget .fcrm_card_widget_content{color:var(--fc-primary-text);font-size:20px;font-style:normal;font-weight:500;line-height:28px;margin:10px 0 0;display:flex;align-items:center;justify-content:space-between}.fcrm_card_widgets .fcrm_card_widget .count{background:var(--fc-primary-border);color:var(--fc-secondary-text);padding:4px;border-radius:4px;line-height:1;display:block}.fcrm_card_widgets .fcrm_card_widget .fcrm_mb-12{margin-bottom:12px}.fcrm_card_widgets .fcrm_card_widget .fcrm_mt-4{margin-top:4px}.fcrm_card_widgets .fcrm_card_widget .fcrm_icon_background_total_subscribers{background:var(--fc-text-link-bg);color:var(--fc-text-link)}.fcrm_card_widgets .fcrm_card_widget .fcrm_icon_background_total_campaigns{background:var(--fc-warning-bg);color:var(--fc-warning)}.fcrm_card_widgets .fcrm_card_widget .fcrm_icon_background_email_sent{background:var(--fc-error-bg);color:var(--fc-error)}.fcrm_card_widgets .fcrm_card_widget .fcrm_icon_background_tags{background:var(--fc-secondary-bg)}.fcrm_card_widgets .fcrm_card_widget .fcrm_icon_background_total_templates{background:var(--fc-warning-bg)}.fcrm_card_widgets .fcrm_card_widget .fcrm_icon_background_total_automations{background:var(--fc-secondary-bg);color:var(--fc-deep-bg)}.fcrm_card_widgets .fcrm_card_widget .fcrm_icon_background_gray{background:var(--fc-secondary-bg);color:var(--fc-secondary-text)}.fcrm_card_widgets .fcrm_card_widget:hover{color:var(--fc-primary-text)}.fcrm_report_card_widget{grid-template-columns:repeat(auto-fit,minmax(250px,1fr))}.fc_card_widgets{display:flex;flex-wrap:wrap;gap:25px}.fc_card_widgets .fc_card_widget{cursor:pointer;background:var(--fc-primary-bg);border:1.3px solid var(--fc-primary-border);border-radius:8px;padding:25px 30px;width:calc(33.3333% - 17px);transition:.4s}@media (max-width: 720px){.fc_card_widgets .fc_card_widget{width:calc(50% - 13px)}}@media all and (max-width: 426px){.fc_card_widgets .fc_card_widget{width:100%}}.fc_card_widgets .fc_card_widget .fluentcrm_body{font-size:36px;font-weight:600;line-height:1.2;margin:0 0 5px}.fc_card_widgets .fc_card_widget .stat_title{font-size:15px;line-height:1.5;color:var(--fc-primary-text);opacity:.8}.fc_card_widgets .fc_card_widget:hover{border-color:var(--fc-deep-bg);box-shadow:0 8px 10px #0000000d}span.fc_li_value{display:inline-block;float:right}.fc_lined_items{list-style:none;margin:0}.fc_lined_items li{list-style:none;padding-top:10px;padding-bottom:10px;margin:0;font-size:14px;font-style:normal;font-weight:400;line-height:20px;border-bottom:1px solid var(--fc-primary-border);color:var(--fc-primary-text)}.fc_lined_items li:first-child{padding-top:0}.fc_lined_items li:last-child{border-bottom:none;padding-bottom:0}.fc_lined_items li i{font-size:25px;display:block;float:left;margin-right:7px}.fc_lined_items li.fc_item_completed i{color:var(--fc-primary-text)}.fc_lined_items li.fc_item_completed{text-decoration:line-through}.fcrm_contact_automation_stats{display:grid;gap:24px;grid-template-columns:repeat(auto-fit,minmax(300px,1fr));width:100%;align-items:flex-start}@media all and (max-width: 768px){.fcrm_contact_automation_stats{grid-template-columns:1fr;gap:12px}.fcrm_contact_automation_stats>.fcrm_contact_automation_stat{flex:1 1 100%}}.fc_request_review_widget .fluentcrm_body{padding:20px;border-radius:6px;display:flex;flex-direction:column;gap:15px;position:relative;z-index:1;align-items:flex-start}.fc_request_review_widget .fluentcrm_body .fc_request_review_header{display:flex;align-items:center;width:100%}.fc_request_review_widget .fluentcrm_body .fc_request_review_header i{margin-left:auto;cursor:pointer}.fc_request_review_widget .fluentcrm_body svg{display:block;width:20px;height:auto;position:absolute;right:0;bottom:0;z-index:-1}.fc_request_review_widget .fluentcrm_body h4{margin:0;font-size:1rem}.fc_request_review_widget .fluentcrm_body p{margin:0;line-height:1.2rem;font-size:.8rem;color:var(--fc-secondary-text)}i.fc_el_circle{height:22px;width:22px;border:1px solid black;border-radius:50%}.fc_onboarding .fluentcrm-actions .el-button{font-size:16px}.fc_onboarding .fc_lined_items li{cursor:pointer;transition:all .2s}.fc_onboarding .fc_lined_items li:hover{color:var(--fc-primary-text);border-bottom:1px solid var(--fc-primary-text);transition:all .2s}.fc_onboarding .fc_lined_items li:hover i{color:var(--fc-primary-text)!important}.fc_onboarding .fc_top_products li{cursor:auto}.fc_onboarding .fc_top_products li:hover{color:var(--fc-primary-text);border-color:var(--fc-primary-border)}ul.fc_settings_sub_menu{margin:0;padding:0;list-style:none}ul.fc_settings_sub_menu li{display:inline-block;padding:15px;margin-bottom:-4px!important;overflow:hidden;cursor:pointer}ul.fc_settings_sub_menu li.fc_active{background:var(--fc-primary-bg);border-bottom:2px solid var(--fc-primary-text);color:var(--fc-primary-text)}.fc_integration_settings .fluentcrm_header{padding:0}.fc_integrations_list ul{list-style:disc;margin-left:40px}.fc_column_toggler_checks span.is-disabled{display:none}.fc_column_toggler_checks .is-disabled span.el-checkbox__label{color:var(--fc-primary-text);padding-left:20px;margin-top:10px}.fcrm_option_creatable .el-select>.el-input>input{border-top-right-radius:0;border-bottom-right-radius:0;border-right:0;border-color:var(--fc-text-muted)!important;border-right:1px solid var(--fc-text-muted)}.fcrm_option_creatable .fcrm_with_select{border-top-left-radius:0;border-bottom-left-radius:0}.fcrm_primary_text,.fcrm_secondary_text{color:var(--fc-secondary-text);margin:0;font-size:14px;line-height:20px}.fcrm_primary_text.small,.fcrm_secondary_text.small{font-size:12px;line-height:16px}.fcrm_primary_text .el-icon,.fcrm_secondary_text .el-icon{font-size:16px}.fcrm_primary_text .fc-inline-help-icon,.fcrm_primary_text .icon,.fcrm_secondary_text .fc-inline-help-icon,.fcrm_secondary_text .icon{color:var(--fc-text-muted)}.fcrm_primary_text .fc-inline-help-icon svg,.fcrm_primary_text .icon svg,.fcrm_secondary_text .fc-inline-help-icon svg,.fcrm_secondary_text .icon svg{width:14px;height:14px}.fcrm_primary_text{color:var(--fc-primary-text)}.el-form-item.fc_half_field.fc_half_even{padding-right:10px;padding-left:0}.el-form-item.fc_half_field.fc_half_odd{padding-right:1px;padding-left:10px}@media all and (max-width: 1024px){.fluentcrm_main_menu_items ul.fluentcrm_menu li .fluentcrm_menu_primary{padding:20px 15px;font-size:13px}.el-menu-vertical-demo{min-height:auto;margin-bottom:10px}} diff --git a/wp-content/plugins/fluent-crm/assets/admin/css/setup-wizard.css b/wp-content/plugins/fluent-crm/assets/admin/css/setup-wizard.css new file mode 100644 index 0000000..6e98c5b --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/admin/css/setup-wizard.css @@ -0,0 +1 @@ +@charset "UTF-8";:root{--fc-primary-bg: #FFFFFF;--fc-secondary-bg: #F5F7FA;--fc-light-bg: #E1E4EA;--fc-deep-bg: #222530;--fc-weak-bg-25: #F9FAFB;--fc-ai-background: #efebff;--fc-ai-color: #8762F0;--fc-primary-text: #0E121B;--fc-secondary-text: #525866;--fc-text-muted: #99A0AE;--fc-text-inverse: #FFFFFF;--fc-primary-border: #E1E4EA;--fc-secondary-border: #CACFD8;--fc-primary-button: #222530;--fc-text-link: #335CFF;--fc-success: #1FC16B;--fc-success-bg: #E0FAEC;--fc-error: #FB3748;--fc-error-bg: #FFEBEC;--fc-warning: #F6B51E;--fc-warning-bg: #FFFAEB;--fc-text-link-bg: #EEF2FF;--fc-badge-unsubscribed-text: #222530;--fc-badge-unsubscribed-bg: #F2F5F8;--fc-badge-subscribed-text: #0B4627;--fc-badge-subscribed-bg: #E0FAEC;--fc-badge-pending-text: #624C18;--fc-badge-pending-bg: #FFFAEB;--fc-badge-transactional-text: #351A75;--fc-badge-transactional-bg: #EFEBFF;--fc-badge-bounced-text: #122368;--fc-badge-bounced-bg: #EBF1FF;--fc-badge-complained-text: #71330A;--fc-badge-complained-bg: #FFF3EB;--fc-badge-spammed-text: #681219;--fc-badge-spammed-bg: #FFEBEC;--el-color-primary: var(--fc-deep-bg);--el-color-primary-light-3: var(--fc-secondary-text);--el-color-primary-light-5: var(--fc-text-muted);--el-color-primary-light-7: var(--fc-secondary-border);--el-color-primary-light-8: var(--fc-primary-border);--el-color-primary-light-9: var(--fc-secondary-bg);--el-color-primary-dark-2: var(--fc-primary-text);--el-color-success: var(--fc-success);--el-color-warning: var(--fc-warning);--el-color-danger: var(--fc-error);--el-color-error: var(--fc-error);--el-color-info: var(--fc-primary-text);--el-text-color-primary: var(--fc-primary-text);--el-text-color-regular: var(--fc-secondary-text);--el-text-color-secondary: var(--fc-text-muted);--el-text-color-placeholder: var(--fc-text-muted);--el-text-color-disabled: var(--fc-secondary-border);--el-border-color: var(--fc-primary-border);--el-border-color-light: var(--fc-primary-border);--el-border-color-lighter: var(--fc-secondary-border);--el-border-color-dark: var(--fc-secondary-border);--el-fill-color-light: var(--fc-secondary-bg);--el-fill-color-lighter: var(--fc-secondary-bg);--el-bg-color: var(--fc-primary-bg);--el-bg-color-page: var(--fc-secondary-bg);--el-button-text-color: var(--fc-text-inverse);--el-fill-color-blank: var(--fc-primary-bg);--el-bg-color-overlay: var(--fc-primary-bg);--el-color-info-light-9: var(--fc-badge-unsubscribed-bg);--fcrm-border-radius-8: 8px;--el-border-radius-base: var(--fcrm-border-radius-8);--wp-editor-canvas-background: var(--fc-primary-bg)}body{color:var(--fc-primary-text)}body *{box-sizing:border-box}body.el-popup-parent--hidden{width:100%!important;padding-right:0!important}.fluentcrm-app *{box-sizing:border-box}.fluentcrm-app a{cursor:pointer;text-decoration:none}.fluentcrm-app a:hover{text-decoration:none}.spining{animation:spining 1s linear infinite}@keyframes spining{0%{transform:rotate(0)}to{transform:rotate(360deg)}}@keyframes working{0%{transform:translate3d(-50%,-50%,0) rotate(0)}to{transform:translate3d(-50%,-50%,0) rotate(360deg)}}@keyframes indeterminate{0%{left:-35%;right:100%}60%{left:100%;right:-90%}to{left:100%;right:-90%}}@keyframes fc_spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}.text-info{color:var(--fc-text-link)}.text-danger{color:var(--fc-error)}.text-align-right{text-align:right}.text-align-left{text-align:left}.text-align-center{text-align:center}.fcrm_force_hide{display:none!important}.cursor_pointer{cursor:pointer}.app{padding:16px}.fcrm_max_w_800{max-width:800px;margin-left:auto;margin-right:auto}.fcrm_icon_90degree{transform:rotate(90deg)}.button-like{background:var(--fc-deep-bg);color:var(--fc-text-inverse);padding:6px 10px;border-radius:4px}.fcrm_contact_cell:active{opacity:.7}.url{color:var(--fc-text-link);cursor:pointer}.fcrm_no-margin{margin:0}.fcrm_mt_0{margin-top:0}.fcrm_mt_4{margin-top:4px}.fcrm_mt_6{margin-top:6px}.fcrm_mt_8{margin-top:8px}.fcrm_mt_10{margin-top:10px}.fcrm_mt_12{margin-top:12px}.fcrm_mt_16{margin-top:16px}.fcrm_mt_24{margin-top:24px}.fcrm_mb_4{margin-bottom:4px}.fcrm_mb_6{margin-bottom:6px}.fcrm_mb_8{margin-bottom:8px}.fcrm_mb_10{margin-bottom:10px}.fcrm_mb_12{margin-bottom:12px}.fcrm_mb_14{margin-bottom:14px}.fcrm_mb_16{margin-bottom:16px}.fcrm_mb_18{margin-bottom:18px}.fcrm_mb_20{margin-bottom:20px}.fcrm_mt_20{margin-top:20px}.fcrm_mb_24{margin-bottom:24px}.fcrm_mr_2{margin-right:2px}.fcrm_mr_4{margin-right:4px}.fcrm_mr_6{margin-right:6px}.fcrm_mr_8{margin-right:8px}.fcrm_mr_10{margin-right:10px}.fcrm_mr_12{margin-right:12px}.fcrm_p_2{padding:2px}.fcrm_p_4{padding:4px}.fcrm_p_6{padding:6px}.fcrm_p_8{padding:8px}.fcrm_p_10{padding:10px}.fcrm_p_12{padding:12px}.fcrm_p_14{padding:14px}.fcrm_p_16{padding:16px}.fcrm_p_18{padding:18px}.fcrm_p_24{padding:24px}.fcrm_pt_0{padding-top:0}.fcrm_pt_8{padding-top:8px}.fcrm_pt_10{padding-top:10px}.fcrm_pt_12{padding-top:12px}.fcrm_pt_16{padding-top:16px}.fcrm_pt_20{padding-top:20px}.fcrm_pt_24{padding-top:24px}.fcrm_pr_0{padding-right:0}.fcrm_pr_8{padding-right:8px}.fcrm_pr_10{padding-right:10px}.fcrm_pr_12{padding-right:12px}.fcrm_pr_16{padding-right:16px}.fcrm_pr_20{padding-right:20px}.fcrm_pr_24{padding-right:24px}.fcrm_pb_0{padding-bottom:0}.fcrm_pb_8{padding-bottom:8px}.fcrm_pb_10{padding-bottom:10px}.fcrm_pb_12{padding-bottom:12px}.fcrm_pb_16{padding-bottom:16px}.fcrm_pb_20{padding-bottom:20px}.fcrm_pb_24{padding-bottom:24px}.fcrm_pl_0{padding-left:0}.fcrm_pl_8{padding-left:8px}.fcrm_pl_10{padding-left:10px}.fcrm_pl_12{padding-left:12px}.fcrm_pl_16{padding-left:16px}.fcrm_pl_20{padding-left:20px}.fcrm_pl_24{padding-left:24px}.content-center{display:flex;align-items:center;justify-content:center}.d-block{display:block}.d-none{display:none}.d-flex{display:flex}.flex-wrap{flex-wrap:wrap}.gap-4{gap:4px}.gap-6{gap:6px}.gap-8{gap:8px}.gap-10{gap:10px}.gap-12{gap:12px}.gap-14{gap:14px}.gap-16{gap:16px}.gap-18{gap:18px}.gap-20{gap:20px}.w-full{width:100%}.h-full{height:100%}.items-center{align-items:center}.items-start{align-items:flex-start}.items-end{align-items:flex-end}.flex-row{flex-direction:row}.flex-column{flex-direction:column}.justify-start{justify-content:flex-start}.justify-center{justify-content:center}.justify-end{justify-content:flex-end}.justify-between{justify-content:space-between}.justify-around{justify-content:space-around}.text-center{text-align:center}.text-secondary{color:var(--fc-secondary-text)}.text-primary{color:var(--fc-primary-text)}.text-success{color:var(--fc-success)}.text-error{color:var(--fc-error)}.text-warning{color:var(--fc-warning)}.text-muted{color:var(--fc-text-muted)}.mr-5{margin-right:5px}.ml-0{margin-left:0!important}.mt-5{margin-top:5px}.ml-5{margin-left:5px}.ml-0-im{margin-left:0!important}.mr-10{margin-right:10px}.mt-20{margin-top:20px}.mb-10{margin-bottom:10px}.mb-16{margin-bottom:16px}.pl-24{padding-left:24px}.font-regular,.font-normal{font-weight:400}.font-medium{font-weight:500}.font-semibold{font-weight:600}.font-bold{font-weight:700}.no-hover:hover,.no-hover:focus{color:var(--fc-secondary-text)!important;background:transparent!important}.no-margin{margin:0}.no-margin-bottom{margin-bottom:initial!important}.hidden{display:none}.icon-90degree{transform:rotate(90deg)}.fcrm_download_icon{margin-right:4px;flex-shrink:0}.fc_m_30{margin-bottom:30px}.fc_m_20{margin-bottom:20px}.fc_m_24{margin-bottom:24px}.fc_t_30{margin-top:30px}.fc_t_10{margin-top:10px}.fc_disc{list-style:disc;padding-left:30px}.fc_counting_heading span{background-color:var(--fc-deep-bg);padding:2px 15px;border-radius:4px;color:var(--fc-text-inverse)}.min_textarea_40 textarea{min-height:40px!important}.fcrm_padding_20{padding:20px}span.fc_positive{font-weight:700;color:var(--fc-success)}span.fc_negative{color:var(--fc-error);font-weight:500}.fluentcrm-app a:focus{box-shadow:none!important}.show_on_parent .show_on_hover{display:none}.show_on_parent:hover .show_on_hover{display:initial}.fluentcrm_pad_around{padding:25px}.fluentcrm_pad_around .el-table--scrollable-x:before{display:none}.fluentcrm_pad_around .el-table--scrollable-x .el-table__body-wrapper{padding-bottom:10px;background:var(--fc-secondary-bg)}.fluentcrm_pad_around .el-table__body-wrapper table tbody tr td:last-child .el-button+.el-button{margin-left:0}.fluentcrm_pad_30{padding:30px}.fluentcrm_pad_b_30{padding-bottom:30px}.fluentcrm_pad_b_20{padding-bottom:20px}.fluentcrm_pad_b_10{padding-bottom:10px}.fluentcrm_pad_b_15{padding-bottom:15px}.fluentcrm_clickable{cursor:pointer}@font-face{font-family:fontello;src:url(../../scss/fonts/fontello.eot?37598903);src:url(../../scss/fonts/fontello.eot?37598903#iefix) format("embedded-opentype"),url(../../scss/fonts/fontello.woff2?37598903) format("woff2"),url(../../scss/fonts/fontello.woff?37598903) format("woff"),url(../../scss/fonts/fontello.ttf?37598903) format("truetype"),url(../../scss/fonts/fontello.svg?37598903#fontello) format("svg");font-weight:400;font-style:normal}.fc-icon-cancel_automation:before{content:"";font-family:fontello;font-style:normal;font-weight:400;speak:never;display:inline-block;text-decoration:inherit;width:1em;text-align:center;font-variant:normal;text-transform:none;line-height:1em;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}@font-face{font-family:icomoon;src:url(../../scss/fonts/icomoon.eot?cwznoa);src:url(../../scss/fonts/icomoon.eot?cwznoa#iefix) format("embedded-opentype"),url(../../scss/fonts/icomoon.ttf?cwznoa) format("truetype"),url(../../scss/fonts/icomoon.woff?cwznoa) format("woff"),url(../../scss/fonts/icomoon.svg?cwznoa#icomoon) format("svg");font-weight:400;font-style:normal;font-display:block}[class^=fc-icon-],[class*=" fc-icon-"]{font-family:icomoon!important;speak:never;font-style:normal;font-weight:400;font-variant:normal;text-transform:none;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.fc-icon-action:before{content:""}.fc-icon-apply_list:before{content:""}.fc-icon-apply_tag:before{content:""}.fc-icon-benchmark:before{content:""}.fc-icon-cancel_automation .path1:before{content:"";color:#000}.fc-icon-cancel_automation .path2:before{content:"";margin-left:-1em;color:#000}.fc-icon-cancel_automation .path3:before{content:"";margin-left:-1em;color:#000}.fc-icon-cancel_automation .path4:before{content:"";margin-left:-1em;color:#000}.fc-icon-cancel_automation .path5:before{content:"";margin-left:-1em;color:#000}.fc-icon-cancel_automation .path6:before{content:"";margin-left:-1em;color:#000}.fc-icon-cancel_automation .path7:before{content:"";margin-left:-1em;color:#000}.fc-icon-cancel_automation .path8:before{content:"";margin-left:-1em;color:#000}.fc-icon-cancel_automation .path9:before{content:"";margin-left:-1em;color:#000}.fc-icon-cancel_automation .path10:before{content:"";margin-left:-1em;color:#000}.fc-icon-cancel_automation .path11:before{content:"";margin-left:-1em;color:#000}.fc-icon-cancel_automation .path12:before{content:"";margin-left:-1em;color:#000}.fc-icon-cancel_automation .path13:before{content:"";margin-left:-1em;color:#000}.fc-icon-cancel_automation .path14:before{content:"";margin-left:-1em;color:#000}.fc-icon-cancel_automation .path15:before{content:"";margin-left:-1em;color:#000}.fc-icon-cancel_automation .path16:before{content:"";margin-left:-1em;color:#000}.fc-icon-cancel_automation .path17:before{content:"";margin-left:-1em;color:#000}.fc-icon-cancel_automation .path18:before{content:"";margin-left:-1em;color:#000}.fc-icon-cancel_automation .path19:before{content:"";margin-left:-1em;color:#000}.fc-icon-cancel_automation .path20:before{content:"";margin-left:-1em;color:#000}.fc-icon-cancel_automation .path21:before{content:"";margin-left:-1em;color:#000}.fc-icon-cancel_automation .path22:before{content:"";margin-left:-1em;color:#000}.fc-icon-cancel_automation .path23:before{content:"";margin-left:-1em;color:#000}.fc-icon-cancel_automation .path24:before{content:"";margin-left:-1em;color:#000}.fc-icon-cancel_automation .path25:before{content:"";margin-left:-1em;color:#000}.fc-icon-cancel_automation .path26:before{content:"";margin-left:-1em;color:#000}.fc-icon-cancel_automation .path27:before{content:"";margin-left:-1em;color:#000}.fc-icon-cancel_automation .path28:before{content:"";margin-left:-1em;color:#000}.fc-icon-cancel_automation .path29:before{content:"";margin-left:-1em;color:#fff}.fc-icon-cancel_automation .path30:before{content:"";margin-left:-1em;color:#000}.fc-icon-cancel_sequence:before{content:""}.fc-icon-check_contact_property_conditional:before{content:""}.fc-icon-conditions:before{content:""}.fc-icon-create_wp_user:before{content:""}.fc-icon-edd_new_order_success:before{content:""}.fc-icon-edd:before{content:""}.fc-icon-end_funnel:before{content:""}.fc-icon-fluentforms:before{content:""}.fc-icon-has_list:before{content:""}.fc-icon-has_wp_role:before{content:""}.fc-icon-learndash_complete_course:before{content:""}.fc-icon-learndash_complete_lesson:before{content:""}.fc-icon-learndash_complete_topic:before{content:""}.fc-icon-learndash_course_group:before{content:""}.fc-icon-learndash_enroll_course:before{content:""}.fc-icon-learndash:before{content:""}.fc-icon-lifter_lms_complete_course:before{content:""}.fc-icon-lifter_lms_complete_lession-t2:before{content:""}.fc-icon-lifter_lms_course_enrollment:before{content:""}.fc-icon-lifter_lms_membership:before{content:""}.fc-icon-lifter_lms:before{content:""}.fc-icon-link_clicked:before{content:""}.fc-icon-list_applied_2:before{content:""}.fc-icon-list_applied:before{content:""}.fc-icon-list_removed_2:before{content:""}.fc-icon-list_removed:before{content:""}.fc-icon-memberpress_expired:before{content:""}.fc-icon-memberpress_membership:before{content:""}.fc-icon-memberpress:before{content:""}.fc-icon-membership_level_ex_pmp:before{content:""}.fc-icon-new_order_woo:before{content:""}.fc-icon-paid_membership_pro_user_level:before{content:""}.fc-icon-paid_membership_pro:before{content:""}.fc-icon-rcp_membership_cancle:before{content:""}.fc-icon-rcp_membership_level:before{content:""}.fc-icon-remove_from_course_lms:before{content:""}.fc-icon-remove_from_membership_lms:before{content:""}.fc-icon-remove_tag:before{content:""}.fc-icon-removed_list:before{content:""}.fc-icon-restric_content:before{content:""}.fc-icon-send_campaign:before{content:""}.fc-icon-set_sequence:before{content:""}.fc-icon-tag_applied_2:before{content:""}.fc-icon-tag_applied:before{content:""}.fc-icon-tag_removed_2:before{content:""}.fc-icon-tag_removed:before{content:""}.fc-icon-trigger:before{content:""}.fc-icon-tutor_lms_complete_course:before{content:""}.fc-icon-tutor_lms_enrollment_course:before{content:""}.fc-icon-tutorlms:before{content:""}.fc-icon-wait_time:before{content:""}.fc-icon-webhooks:before{content:""}.fc-icon-wishlist:before{content:""}.fc-icon-woo_new_order:before{content:""}.fc-icon-woo_order_complete:before{content:""}.fc-icon-woo_purchased:before{content:""}.fc-icon-woo_refund:before{content:""}.fc-icon-woo:before{content:""}.fc-icon-wordpress:before{content:""}.fc-icon-wp_new_user_signup:before{content:""}.fc-icon-wp_user_meta:before{content:""}.fc-icon-wp_user_role:before{content:""}.fc-icon-writing:before{content:""}.el-input{width:100%}.el-input .el-input__wrapper{border-radius:8px;box-shadow:0 1px 2px #0a0d1408;border:1px solid var(--fc-primary-border);padding:2px 10px;background:var(--fc-primary-bg)}.el-input .el-input__wrapper.is-focus,.el-input .el-input__wrapper.is-focused,.el-input .el-input__wrapper:focus-within{border-color:var(--fc-primary-text)}.el-input .el-input__wrapper.is-disabled{opacity:.5}.el-input .el-input__wrapper .el-input__inner{border:none!important;font-size:14px;background:none;box-shadow:none;color:var(--fc-secondary-text);padding:0;line-height:1;min-height:30px}.el-textarea .el-textarea__inner{border-radius:var(--fcrm-border-radius-8);box-shadow:0 1px 2px #0a0d1408;border:1px solid var(--fc-primary-border);padding:4px 10px;color:var(--fc-secondary-text);font-size:14px}.el-textarea .el-textarea__inner.is-disabled{opacity:.5}.el-textarea,.el-select,.el-input{height:auto}.el-textarea__inner.is-focused,.el-textarea__inner.is-focus,.el-textarea__wrapper.is-focused,.el-textarea__wrapper.is-focus,.el-select__inner.is-focused,.el-select__inner.is-focus,.el-select__wrapper.is-focused,.el-select__wrapper.is-focus,.el-input__inner.is-focused,.el-input__inner.is-focus,.el-input__wrapper.is-focused,.el-input__wrapper.is-focus{border-color:var(--fc-primary-text)}.el-textarea input,.el-select input,.el-input input{padding:0;margin:0;box-shadow:none;border:none;background:none;color:var(--fc-primary-text);min-height:30px}.el-input-number .el-input-number__decrease,.el-input-number .el-input-number__increase{background:transparent;border:none;color:var(--fc-secondary-text)}.el-input-number .el-input-number__decrease:hover,.el-input-number .el-input-number__increase:hover{color:var(--fc-primary-text)}.el-date-editor{justify-content:flex-start;padding:0}.el-date-editor .el-input__wrapper{padding:7px 10px}.el-date-editor .el-range-input{height:auto;line-height:20px;font-weight:400;font-size:14px;color:var(--fc-primary-text)}.el-date-editor .el-range-separator{line-height:1;padding:0;width:16px;font-weight:700}.fcrm_checkbox_group,.fcrm_checkbox_group .el-checkbox-group{display:flex;flex-direction:column;gap:12px;flex-wrap:wrap}.el-input__inner{border:none!important}.fcrm_searcher .el-input__wrapper,.fcrm_dynamic_segments .fcrm_table_header_inner_left .el-input .el-input__wrapper{height:32px;min-height:32px;border:none;background:var(--fc-secondary-bg)}.fcrm_searcher .el-input__wrapper:hover,.fcrm_searcher .el-input__wrapper.is-focused,.fcrm_searcher .el-input__wrapper.is-focus,.fcrm_dynamic_segments .fcrm_table_header_inner_left .el-input .el-input__wrapper:hover,.fcrm_dynamic_segments .fcrm_table_header_inner_left .el-input .el-input__wrapper.is-focused,.fcrm_dynamic_segments .fcrm_table_header_inner_left .el-input .el-input__wrapper.is-focus{border:none;box-shadow:none}.fcrm_searcher{--el-input-hover-border-color: var(--fc-primary-border);--el-input-focus-border-color: var(--fc-primary-border);width:100%}.fcrm_searcher .el-input__suffix{cursor:pointer}.fcrm_searcher .el-input__suffix .el-icon{color:var(--fc-text-muted)}.fcrm_searcher .fcrm-searcher-suffix{display:inline-flex;align-items:center}select,textarea,input{outline:none;box-shadow:none;border-color:var(--fc-secondary-border)!important;background:var(--fc-primary-bg)}select:focus,textarea:focus,input:focus{border-color:var(--fc-primary-text)!important;box-shadow:none!important;outline:none}.el-date-editor .el-range-separator{padding:0;width:16px;font-weight:700}.el-tag--white{background:var(--fc-secondary-bg);color:var(--fc-primary-text);border-color:var(--fc-text-inverse);margin-bottom:5px;margin-left:10px}.el-tag--white .el-tag__close{color:var(--fc-text-muted);-webkit-transition:.2s;-moz-transition:.2s;-o-transition:.2s;-ms-transition:.2s;transition:.2s}.el-tag--white .el-tag__close:hover{background-color:var(--fc-text-muted);color:var(--fc-text-inverse);line-height:17px;font-size:10px;padding-right:1px}.el-input-group .el-input-group__append{border-color:var(--fc-secondary-border);transition:.2s}.el-input-group .el-input-group__append:hover{background:#2225301a;color:var(--fc-deep-bg)}.el-input-group input{font-weight:500}.el-input-group input:focus~.el-input-group__append{border-color:var(--fc-text-link)}.el-picker-panel .el-date-table tr td.in-range .el-date-table-cell__text{background-color:var(--fc-secondary-bg)}.el-picker-panel .el-date-table tr td.end-date .el-date-table-cell__text,.el-picker-panel .el-date-table tr td.start-date .el-date-table-cell__text{background-color:var(--fc-deep-bg);color:var(--fc-text-inverse)}.el-picker-panel .el-date-table tr td.available:hover{color:var(--fc-deep-bg)}.el-picker-panel .el-picker-panel__footer{display:flex;align-items:center;gap:8px;justify-content:flex-end}.el-picker-panel .el-picker-panel__footer .el-button{margin:0}.el-picker-panel .el-picker-panel__footer .el-button.is-text{background:var(--fc-primary-bg);color:var(--fc-secondary-text);font-weight:500;font-size:14px;line-height:20px;height:auto;border:1px solid var(--fc-primary-border);box-shadow:0 1px 2px #0a0d1408;border-radius:8px;padding:5px 10px}.el-picker-panel .el-picker-panel__footer .el-button.is-text:hover{border-color:var(--fc-primary-border);background:var(--fc-secondary-bg);color:var(--fc-primary-text)}.el-picker-panel .el-picker-panel__footer .el-button:not(.is-text){background:var(--fc-deep-bg);border-color:var(--fc-deep-bg);border-radius:8px;color:var(--fc-text-inverse);font-size:14px;line-height:20px;height:auto;padding:5px 10px}.el-picker-panel .el-picker-panel__footer .el-button:not(.is-text):hover{background:var(--fc-primary-text);border-color:var(--fc-primary-text);color:var(--fc-text-inverse)}.fluentcrm_width_input .el-date-editor--timerange.el-input__inner{width:450px;max-width:100%}.fcrm_option_selector{display:flex;align-items:center}.fc-item-copier-input{border-radius:8px!important;overflow:hidden!important}.fc-item-copier-input .el-input__wrapper{background:var(--fc-weak-bg-25);border-right:none;box-shadow:none;padding:1px 11px;border-radius:var(--fcrm-border-radius-8) 0 0 var(--fcrm-border-radius-8)}.fc-item-copier-input .el-input__wrapper .el-input__inner{color:var(--fc-primary-text);font-weight:500;background:none}.fc-item-copier-input .el-input-group__append{background:none;border:1px solid var(--fc-primary-border);border-left:none;padding:0;margin:0}.fc-item-copier-input .el-input-group__append .el-button{margin:0;border:none;background:var(--fc-primary-bg);border-radius:0;padding:0 8px;height:100%;display:flex;align-items:center}.fc-item-copier-input .el-input-group__append .el-button:last-child{border-radius:0 var(--fcrm-border-radius-8) var(--fcrm-border-radius-8) 0;border-left:1px solid var(--fc-primary-border)}.fc-item-copier-input .el-input-group__append .el-button.copy-btn{background:var(--fc-weak-bg-25)}.fc-item-copier-input .el-input-group__append .el-button:hover{color:var(--fc-primary-text)}.fc-item-copier-input .el-input-group__append .el-button:hover svg{color:var(--fc-primary-text)}.fc-item-copier-input .el-input-group__append .el-button svg{width:16px;height:16px;display:block;color:var(--fc-secondary-text)}.el-table .el-table__header-wrapper .el-table__cell,.el-table .el-table__body-wrapper .el-table__cell{border-color:var(--fc-primary-border)}.el-table .el-table__expand-icon .el-icon,.el-table .el-table__expand-icon svg{display:none!important}.el-table .el-table__expand-icon:before{content:"▶";font-size:12px;display:inline-flex;align-items:center;justify-content:center}.el-table .el-table__expand-icon.el-table__expand-icon--expanded{transform:none}.el-table .el-table__expand-icon.el-table__expand-icon--expanded:before{content:"▼"}.el-table-column--selection .el-checkbox .el-checkbox__input{width:20px;height:20px;display:flex;align-items:center;justify-content:center}.el-table-column--selection .el-checkbox .el-checkbox__input input{opacity:0;margin:0}.el-table-column--selection .el-checkbox .el-checkbox__input .el-checkbox__inner{background-color:var(--fc-primary-bg);position:relative;border-width:1.5px}.el-table-column--selection .el-checkbox .el-checkbox__input .el-checkbox__inner:before{content:"";position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);width:13px;height:13px;background-color:var(--fc-primary-bg);border-radius:2.6px}.el-table-column--selection .el-checkbox .el-checkbox__input.is-indeterminate .el-checkbox__inner:after{transform:translate(-50%,-50%) rotate(0);display:block;border:none;width:8px;height:1px;background-color:var(--fc-primary-bg)}.el-table-column--selection .el-checkbox .el-checkbox__input.is-checked .el-checkbox__inner,.el-table-column--selection .el-checkbox .el-checkbox__input.is-indeterminate .el-checkbox__inner{background-color:var(--fc-deep-bg)}.el-table-column--selection .el-checkbox .el-checkbox__input.is-checked .el-checkbox__inner:before,.el-table-column--selection .el-checkbox .el-checkbox__input.is-indeterminate .el-checkbox__inner:before{display:none}.el-table-column--selection .el-checkbox .el-checkbox__input.is-checked:hover .el-checkbox__inner,.el-table-column--selection .el-checkbox .el-checkbox__input.is-indeterminate:hover .el-checkbox__inner{background-color:var(--fc-primary-text)}td.fcrm_table_actions_cell .cell{display:flex;align-items:center;justify-content:center}td.fcrm_table_actions_cell .el-dropdown-link{cursor:pointer}.el-table .el-table__inner-wrapper:after{display:unset!important;background:var(--fc-primary-border)}.el-table .el-table__inner-wrapper:before{display:none!important}.el-table .el-table__inner-wrapper .el-table__body-wrapper .el-scrollbar .el-scrollbar__wrap .el-scrollbar__view .el-table__body .el-table__row:last-child td{border-bottom:none!important}.el-table .el-table__body-wrapper .el-table__cell{padding:12px 0}.el-table,.el-table tr{background:none}.el-table:after,.el-table:before{display:none!important}.el-table__inner-wrapper:after,.el-table__inner-wrapper:before{display:none!important}.el-table__border-left-patch{display:none!important}.el-table__header-wrapper .el-table__header thead tr th{background:var(--fc-secondary-bg);border-right:none;border-left:none;font-size:14px;font-weight:500;line-height:20px;color:var(--fc-secondary-text);padding-top:8px;padding-bottom:8px;border-bottom:1px solid var(--fc-primary-border);border-top:1px solid var(--fc-primary-border)}.el-table__header-wrapper .el-table__header thead tr th .cell{padding-left:12px;padding-right:12px}.el-table__header-wrapper .el-table__header thead tr th:first-child .cell{padding-left:20px}.el-table__header-wrapper .el-table__header thead tr th input[type=checkbox]:disabled{opacity:0;margin:0}.el-table__body-wrapper:after{background:var(--fc-primary-border)}.el-table__body-wrapper .el-table__body tbody tr td{border-right:none!important;padding-top:12px;padding-bottom:12px;color:var(--fc-primary-text);font-weight:400;font-size:14px;line-height:20px;background:var(--fc-primary-bg)!important;border-bottom-color:var(--fc-primary-border)}.el-table__body-wrapper .el-table__body tbody tr td:first-child .cell{padding-left:20px}.el-table__body-wrapper .el-table__body tbody tr td .cell{padding-left:12px;padding-right:12px}.el-table__body-wrapper .el-table__body tbody tr td .cell .el-switch{--el-switch-on-color: var(--fc-deep-bg)}.el-table__body-wrapper .el-table__body tbody tr td .title{display:flex;align-items:center;gap:8px;flex-wrap:wrap}.el-table__body-wrapper .el-table__body tbody tr td .automation_title{color:var(--fc-primary-text);font-weight:400;font-size:14px;line-height:20px;margin:0}.el-table__body-wrapper .el-table__body tbody tr td .automation_title a{color:var(--fc-primary-text);display:block}.el-table__body-wrapper .el-table__body tbody tr td a{color:var(--fc-primary-text)}.el-table__body-wrapper .el-table__body tbody tr td .stats_badge_inline{background:var(--fc-primary-bg);color:var(--fc-secondary-text);font-weight:400;font-size:12px;line-height:18px;height:auto;border:1px solid var(--fc-primary-border);box-shadow:0 1px 2px #0a0d1408;border-radius:8px;padding:2px 6px;display:inline-flex;align-items:center;gap:2px}.el-table__body-wrapper .el-table__body tbody tr td .fcrm_stats_badge_segment{display:inline-flex;align-items:center;gap:3px;margin-inline-start:4px;padding-inline-start:5px;border-inline-start:1px solid var(--fc-primary-border);color:var(--fc-secondary-text)}.el-table__body-wrapper .el-table__body tbody tr td .fcrm_stats_badge_segment_icon{color:var(--fc-secondary-text);font-size:13px}.el-table__body-wrapper .el-table__body tbody tr td .fcrm_stats_badge_link{cursor:pointer;text-decoration:none;transition:border-color .15s ease,color .15s ease,background-color .15s ease}.el-table__body-wrapper .el-table__body tbody tr td .fcrm_stats_badge_link:hover,.el-table__body-wrapper .el-table__body tbody tr td .fcrm_stats_badge_link:focus{background:#0079ff14;border-color:var(--fc-text-link);color:var(--fc-text-link)}.el-table__body-wrapper .el-table__body tbody tr td .item_description{display:block;color:var(--fc-secondary-text);font-weight:400;font-size:12px;line-height:18px;margin:2px 0 0}.el-table__body-wrapper .el-table__body tbody tr td .fcrm_individual_progress{padding:10px 0}.el-table__body-wrapper .el-table__body tbody tr td .fcrm_individual_progress .el-timeline-item{margin-bottom:0}.el-table__body-wrapper .el-table__body tbody tr td .fcrm_individual_progress .el-timeline-item.fcrm_timeline_empty{opacity:1}.el-table__body-wrapper .el-table__body tbody tr td .fcrm_individual_progress .el-timeline-item.fcrm_timeline_empty .el-timeline-item__node{background:var(--fc-primary-border)}.el-table__body-wrapper .el-table__body tbody tr td .fcrm_individual_progress .el-timeline-item.fcrm_timeline_empty .el-timeline-item__timestamp,.el-table__body-wrapper .el-table__body tbody tr td .fcrm_individual_progress .el-timeline-item.fcrm_timeline_empty .el-timeline-item__content{color:var(--fc-text-muted)}.el-table__body-wrapper .el-table__body tbody tr td .fcrm_individual_progress .el-timeline-item:last-child{padding-bottom:0}.el-table__body-wrapper .el-table__body tbody tr td .fcrm_individual_progress .el-timeline-item__tail{border-left:2px solid var(--fc-primary-border);height:calc(100% - 28px);top:20px}.el-table__body-wrapper .el-table__body tbody tr td .fcrm_individual_progress .el-timeline-item__node{box-shadow:0 1px 2px #0a0d1408;border:2px solid var(--fc-primary-bg);width:12px;height:12px}.el-table__body-wrapper .el-table__body tbody tr td .fcrm_individual_progress .el-timeline-item__node.el-timeline-item__node--large{left:-1px}.el-table__body-wrapper .el-table__body tbody tr td .fcrm_individual_progress .el-timeline-item__node.el-timeline-item__node--primary{background:var(--fc-text-link)}.el-table__body-wrapper .el-table__body tbody tr td .fcrm_individual_progress .el-timeline-item__node.el-timeline-item__node--primary .el-timeline-item__icon{display:none}.el-table__body-wrapper .el-table__body tbody tr td .fcrm_individual_progress .el-timeline-item__content{font-weight:400;font-size:14px;line-height:20px;color:var(--fc-primary-text)}.el-table__body-wrapper .el-table__body tbody tr td .fcrm_individual_progress .el-timeline-item__timestamp{margin-top:4px;color:var(--fc-secondary-text);font-weight:400;font-size:14px;line-height:20px}.el-table__body-wrapper .el-table__body tbody tr td .fcrm_individual_progress .el-timeline-item.fc_timeline_empty{opacity:1}.el-table__body-wrapper .el-table__body tbody tr td .fcrm_individual_progress .el-timeline-item.fc_timeline_empty .el-timeline-item__node{background:var(--fc-primary-border)}.el-table__body-wrapper .el-table__body tbody tr td .fcrm_individual_progress .el-timeline-item.fc_timeline_empty .el-timeline-item__content{color:var(--fc-text-muted)}.el-table__body-wrapper .el-table__body tbody tr td .fc_funnel_labels .el-tag{padding:4px 4px 4px 6px;border-radius:6px;color:#0e121b}.el-table__body-wrapper .el-table__body tbody tr td .fc_funnel_labels .el-tag__content{font-size:12px;display:flex;align-content:center;gap:4px}.el-table__body-wrapper .el-table__body tbody tr td .fc_funnel_labels .el-tag__close{margin:0;color:#0e121b}.el-table__body-wrapper .el-table__body tbody tr td .fc_funnel_labels .el-tag__close:hover{background:#f5f7fa}.el-table__body-wrapper .el-table__body tbody tr td .fcrm_table_row_expand{padding:4px 0 4px 70px}.el-table__body-wrapper .el-table__body tbody tr td .fcrm_table_body_actions{display:flex;flex-wrap:wrap;align-items:flex-start;gap:6px}.el-table__body-wrapper .el-table__body tbody tr td .fcrm_table_body_actions .el-button{margin:0}.el-table__body-wrapper .el-table__body tbody tr td .subscriber-stats{display:flex;flex-wrap:wrap;align-items:flex-start;gap:6px}.el-table__body-wrapper .el-table__body tbody tr td .subscriber-stats .ns_counter{margin:0;height:auto;display:flex;align-items:center;gap:4px;line-height:16px;padding:3px 4px;min-height:24px}.el-table__body-wrapper .el-table__body tbody tr td h4{font-size:14px;font-weight:400;line-height:20px;letter-spacing:-.084px;color:var(--fc-primary-text);margin:0}.el-table__body-wrapper .el-table__body tbody tr td.el-table__expanded-cell{padding-left:70px;padding-right:24px}.el-dialog{padding:0!important;border-radius:8px;margin-top:40px!important}@media (max-width: 768px){.el-dialog{width:90%!important}}.el-dialog__headerbtn{height:100%}.el-dialog__title{font-weight:500;font-size:16px;line-height:24px;color:var(--fc-primary-text)}.el-dialog .el-dialog__header{display:flex;align-items:center;gap:12px;justify-content:space-between;background:none;padding:16px 20px;border-bottom:1px solid var(--fc-primary-border);position:relative}.el-dialog__footer{padding:0}.el-dialog .dialog-footer{border-top:1px solid var(--fc-primary-border);padding:16px 20px;text-align:right;box-sizing:border-box;background:none;border-bottom-left-radius:5px;border-bottom-right-radius:5px;width:auto;display:block}.el-dialog__body{padding:20px;word-break:inherit}.el-dialog__headerbtn:hover .el-dialog__close{color:var(--fc-primary-text)}.el-overlay .el-message-box{max-width:440px;width:100%;border-radius:var(--fcrm-border-radius-8);padding:0}.el-overlay .el-message-box__header{display:none}.el-overlay .el-message-box__content{padding:20px}.el-overlay .el-message-box__message p{margin:0}.el-overlay .el-message-box__btns{border-top:1px solid var(--fc-primary-border);padding:12px 20px;display:flex;align-items:center;gap:12px}.el-overlay .el-message-box__btns .el-button{margin:0}.el-overlay .el-message-box__btns .el-button:not(.el-button--primary){background:var(--fc-primary-bg);color:var(--fc-secondary-text);font-weight:500;font-size:14px;line-height:20px;height:auto;border:1px solid var(--fc-primary-border);box-shadow:0 1px 2px #0a0d1408;border-radius:8px;padding:7px 10px}.el-overlay .el-message-box__btns .el-button:not(.el-button--primary):hover{border-color:var(--fc-primary-border);background:var(--fc-secondary-bg);color:var(--fc-primary-text)}.el-overlay .el-overlay-message-box .fcrm-status-confirm-dialog .el-message-box__title{font-size:34px;line-height:1.15;font-weight:600;color:var(--fc-primary-text)}.el-overlay .el-overlay-message-box .fcrm-status-confirm-dialog .el-message-box__message{margin:0}.el-overlay .el-overlay-message-box .fcrm-status-confirm-dialog .el-message-box__container{align-items:flex-start}.el-overlay .el-overlay-message-box .fcrm-status-confirm-dialog .el-message-box__btns .el-button{margin:0;border-radius:8px;background:var(--fc-primary-bg);height:36px;min-height:36px;color:var(--fc-secondary-text);border:1px solid var(--fc-primary-border);box-shadow:0 1px 2px #0a0d1408}.el-overlay .el-overlay-message-box .fcrm-status-confirm-dialog .el-message-box__btns .el-button:hover{border-color:var(--fc-primary-border);background:var(--fc-secondary-bg);color:var(--fc-primary-text)}.el-overlay .el-overlay-message-box .fcrm-status-confirm-dialog .el-message-box__btns .el-button--primary{background:var(--fc-deep-bg);border-color:var(--fc-deep-bg);color:var(--fc-text-inverse)}.el-overlay .el-overlay-message-box .fcrm-status-confirm-dialog .el-message-box__btns .el-button--primary:hover{background:var(--fc-primary-text);border-color:var(--fc-primary-text);color:var(--fc-text-inverse)}.el-overlay .el-overlay-message-box .fcrm-status-confirm-dialog .fcrm-status-confirm__body{display:flex;gap:16px}.el-overlay .el-overlay-message-box .fcrm-status-confirm-dialog .fcrm-status-confirm__icon-wrap{flex:none}.el-overlay .el-overlay-message-box .fcrm-status-confirm-dialog .fcrm-status-confirm__icon-wrap svg{display:block}.el-overlay .el-overlay-message-box .fcrm-status-confirm-dialog .el-message-box__status{background:var(--fc-secondary-bg);width:40px;height:40px;display:flex;align-items:center;justify-content:center;color:var(--fc-text-muted);border-radius:8px;flex:none;font-size:20px}.el-overlay .el-overlay-message-box .fcrm-status-confirm-dialog .el-message-box__status.el-message-box-icon--warning{background:var(--fc-warning-bg);color:var(--fc-warning)}.el-overlay .el-overlay-message-box .fcrm-status-confirm-dialog .fcrm-status-confirm__icon{background:var(--fc-secondary-bg);width:40px;height:40px;display:flex;align-items:center;justify-content:center;color:var(--fc-text-muted);border-radius:8px}.el-overlay .el-overlay-message-box .fcrm-status-confirm-dialog .fcrm-status-confirm__title{margin:0 0 4px;color:var(--fc-primary-text);font-weight:500;font-size:16px;line-height:24px}.el-overlay .el-overlay-message-box .fcrm-status-confirm-dialog .fcrm-status-confirm__text{margin:0;font-weight:400;font-size:14px;line-height:20px;color:var(--fc-secondary-text)}.el-dialog__wrapper.fc_smtp_email_dialog .el-dialog{min-width:auto!important;width:50%}@media (max-width: 1200px){.el-dialog__wrapper.fc_smtp_email_dialog .el-dialog{width:90%}}.fc-verified-email-input-dialog .el-dialog{padding:0}.fc-verified-email-input-dialog .el-dialog .fc-verified-email-input-dialog-footer{padding:10px;text-align:right;box-sizing:border-box;background:var(--fc-secondary-bg);border-bottom-left-radius:5px;border-bottom-right-radius:5px;width:auto;display:block}.el-drawer.ltr{direction:rtl}.el-drawer .fcrm_dialog_footer_actions{display:flex;gap:12px;justify-content:flex-end;align-items:center}.el-drawer .fcrm_dialog_footer_actions .el-button{margin:0}.el-drawer__footer{border-top:1px solid var(--fc-primary-border);padding:12px 20px}.el-drawer .dialog-footer{display:flex;align-items:center;gap:8px}.el-drawer .dialog-footer .el-button{margin:0}@media (max-width: 768px){.el-drawer{width:90%!important}}.el-checkbox__label{white-space:normal}.el-checkbox-group.fluentcrm-filter-options{max-height:300px;max-width:300px;overflow-x:hidden}.el-radio-group.fcrm_global_radio_group{border:none;box-shadow:none;gap:4px}.el-radio-group.fcrm_global_radio_group .el-radio-button{border:none;background:none}.el-radio-group.fcrm_global_radio_group .el-radio-button.is-active .el-radio-button__original-radio:not(:disabled)+.el-radio-button__inner{background:var(--fc-secondary-bg);color:var(--fc-primary-text);border:none;outline:none}.el-radio-group.fcrm_global_radio_group .el-radio-button:last-child .el-radio-button__inner,.el-radio-group.fcrm_global_radio_group .el-radio-button:first-child .el-radio-button__inner{border-radius:8px}.el-radio-group.fcrm_global_radio_group .el-radio-button .el-radio-button__inner{border:none!important;box-shadow:none;border-radius:8px;color:var(--fc-secondary-text);font-weight:500;font-size:14px;line-height:20px;background:none;outline:none}.el-switch{--el-switch-on-color: var(--fc-deep-bg);--el-switch-off-color: var(--fc-light-bg);height:20px}.el-switch.is-checked .el-switch__core{border-color:var(--fc-primary-text)!important;background-color:var(--fc-primary-text)!important}.el-switch.is-checked .el-switch__core .el-switch__action{background:var(--fc-primary-bg)}.el-switch .el-switch__label.is-active{color:var(--fc-primary-text)}.el-tabs--border-card>.el-tabs__header .el-tabs__item.is-active{color:var(--fc-deep-bg)}.el-tabs--border-card>.el-tabs__header .el-tabs__item:not(.is-disabled):hover{color:var(--fc-deep-bg)}.el-input.fc_input input{border:1px solid var(--fc-secondary-border);border-radius:8px;box-shadow:none;padding:2px 16px;height:auto;margin:0;line-height:32px}.el-input.fc_input input:focus{border-color:var(--fc-deep-bg)!important}.el-checkbox.fc_checkbox{display:flex;align-items:center}.el-checkbox.fc_checkbox .el-checkbox__label{color:var(--fc-primary-text);font-size:14px;white-space:initial;padding-left:8px}.el-radio-group .el-radio-button .el-radio-button__inner{color:var(--fc-primary-text);padding:7px 15px;line-height:20px;background:none}.el-radio-group .el-radio-button.el-radio-button--small .el-radio-button__inner{padding:5px 15px}.el-radio-group .el-radio-button:first-child .el-radio-button__inner{border-radius:8px 0 0 8px}.el-radio-group .el-radio-button:last-child .el-radio-button__inner{border-radius:0 8px 8px 0}.el-radio-group .el-radio-button.is-active .el-radio-button__original-radio:not(:disabled)+.el-radio-button__inner{background-color:var(--fc-primary-text);border-color:var(--fc-deep-bg);color:var(--fc-text-inverse);box-shadow:none}.el-switch.is-checked .el-switch__core{background:var(--fc-deep-bg);border-color:var(--fc-deep-bg)}.el-radio-group.fluentcrm_line_items{width:100%;display:block;margin-bottom:20px;margin-top:20px}.el-radio-group.fluentcrm_line_items>label{display:block;margin-bottom:15px}.el-radio-group.fluentcrm_line_items>label:last-child{margin-bottom:0}.el-form .el-form-item .el-form-item__content .el-radio-group .el-radio{margin-right:8px}.el-form .el-form-item .el-form-item__content .el-radio-group .el-radio:last-child{margin-right:0}.fc-input-number-field{height:40px}.fc-input-number-field .el-input-number__decrease{border-top-left-radius:8px;border-bottom-left-radius:8px}.fc-input-number-field .el-input-number__increase{border-top-right-radius:8px;border-bottom-right-radius:8px}.fc-input-email,.fc-input-text{width:100%;padding:0 15px;height:40px;line-height:40px;font-size:14px;border:1px solid var(--fc-primary-border);border-radius:4px;transition:border-color .2s cubic-bezier(.645,.045,.355,1);box-sizing:border-box}.fc-input-text:focus{outline:none;border-color:var(--fc-deep-bg)}.fc-input-text:hover{border-color:var(--fc-secondary-border)}.el-form .el-form-item__label{margin:0 0 4px;color:var(--fc-primary-text);font-weight:500;font-size:14px;line-height:20px;padding:0}.el-breadcrumb.fcrm_breadcrumb_inline_edit .el-breadcrumb__item .fc_breadcrumb_title,.el-breadcrumb.fcrm_breadcrumb_inline_edit .el-breadcrumb__item .fc_funnel_breadcrumb_title{max-width:600px;cursor:pointer}.el-breadcrumb.fcrm_breadcrumb_inline_edit .el-breadcrumb__item.fc_breadcrumb_item .el-breadcrumb__inner{display:flex;align-items:flex-start;gap:6px}.el-breadcrumb.fcrm_breadcrumb_inline_edit .el-breadcrumb__item.fc_breadcrumb_item .fc_inline_editable{display:flex;align-items:center;gap:4px}.el-breadcrumb.fcrm_breadcrumb_inline_edit .el-breadcrumb__item.fc_breadcrumb_item .fc_inline_editable .fc_clickable_icon{cursor:pointer}.el-breadcrumb.fcrm_breadcrumb_inline_edit .el-breadcrumb__item.fc_breadcrumb_item .fc_inline_editable .el-button{margin:0;padding:8px 14px;border-radius:4px}.el-breadcrumb.fluentcrm_spaced_bottom{margin:0 0 20px;padding:0 0 10px}.fluentcrm_header_title .el-breadcrumb{padding-top:10px;margin-bottom:0}.el-notification.bottom_right.right,.el-notification.fc_bottom-right.right,body .el-notification.right{top:auto!important;bottom:20px!important}.el-popover{padding:10px;text-align:left;word-break:break-all;border-color:var(--fc-primary-border)}.fcrm_send_test_email_popover{z-index:999999!important}.el-notification__content{text-align:left!important}.el-popover.fcrm_sort_popover{padding:20px!important;border-radius:8px!important;box-sizing:border-box;border:1px solid var(--fc-primary-border);max-height:350px;overflow-x:hidden}.el-popover.fcrm_sort_popover .el-popover__title{margin:0 0 16px;font-size:16px;font-weight:600;color:var(--fc-primary-text)}.el-popover.fcrm_sort_popover .fcrm_sorting_action_wrap{padding-left:10px}.el-popover.fcrm_sort_popover .fcrm_sorting_action_wrap .el-radio-group{display:flex;flex-direction:column;align-items:flex-start;gap:12px;width:100%}.el-popover.fcrm_sort_popover .fcrm_sorting_action_wrap .el-radio-group .el-radio{height:auto;margin:0;width:100%;display:flex;align-items:center}.el-popover.fcrm_sort_popover .fcrm_sorting_action_wrap .el-radio-group .el-radio .el-radio__label{font-size:14px;color:var(--fc-primary-text);white-space:initial;padding-left:8px}.el-popover.fcrm_link_stats_popover{-webkit-backdrop-filter:blur(7px);backdrop-filter:blur(7px);border:none;border-radius:var(--fcrm-border-radius-8, 8px);padding:0!important}.el-popover.fcrm_link_stats_popover *{box-sizing:border-box}.el-popover.fcrm_link_stats_popover .popper__arrow:after{border-bottom-color:var(--fc-deep-bg)}.el-popover.fcrm_link_stats_popover a:hover{text-decoration:underline}.el-popover.fcrm_link_stats_popover .fcrm_table_wrapper{overflow-x:auto;overflow-y:hidden}.el-popover.fcrm_link_stats_popover .fcrm_table_body .el-table__header tr th:first-child{border-top-left-radius:8px}.el-popover.fcrm_link_stats_popover .fcrm_table_body .el-table__header tr th:last-child{border-top-right-radius:8px}.el-popover.fcrm_link_stats_popover .fcrm_link_stats_metrics .fcrm_loader_wrap{padding:20px}.el-popover.fcrm_link_stats_popover .fcrm_link_stats_metrics .el-table{background:none}.el-popover.fcrm_link_stats_popover .fcrm_link_stats_metrics .el-table:before{display:none}.el-popover.fcrm_link_stats_popover .fcrm_link_stats_metrics .fluentcrm-pagination{padding-bottom:0}.el-popover.fc_addons_campaign_popover{max-height:500px;overflow-x:hidden}.el-popper.fc_select_campaigns_popover{border:1px solid var(--fc-primary-border);border-radius:8px;box-shadow:0 8px 30px #1b25331a;padding:12px;box-sizing:border-box}.el-popper.fc_select_campaigns_popover .el-select-dropdown__list{padding:0}.el-popper.fc_select_campaigns_popover .el-select-dropdown__list .el-select-dropdown__item{margin:0;border:none;width:100%;text-align:left;line-height:24px;padding:4px 12px;border-radius:8px;display:flex;align-items:center;gap:10px;flex-wrap:wrap}.el-dialog.fcrm_abandon_cart_details_popover{max-width:800px;width:100%;min-width:auto!important}.el-dialog.fcrm_abandon_cart_details_popover .el-dialog__header{background:none;padding:15px 20px;border-color:var(--fc-secondary-bg)}.el-dialog.fcrm_abandon_cart_details_popover .el-dialog__header .el-dialog__title{color:var(--fc-primary-text)}.el-notification h1,.el-notification h2,.el-notification h3,.el-notification h4,.el-notification h5,.el-notification h6,.el-notification p{margin:0;padding:0;color:var(--fc-secondary-text)}.el-notification h1,.el-notification h2,.el-notification h3,.el-notification h4,.el-notification h5,.el-notification h6,.el-notification .el-notification__title{color:var(--fc-primary-text)}.el-progress-bar__outer{background:var(--fc-light-bg)}body.toplevel_page_fluentcrm-admin .el-popper.el-picker__popper{padding:0}.el-picker-panel{--el-bg-color-overlay: var(--fc-primary-bg)}.el-popper.el-picker__popper{padding:0}.el-popper.el-picker__popper .el-picker-panel [slot=sidebar]+.el-picker-panel__body,.el-popper.el-picker__popper .el-picker-panel__sidebar+.el-picker-panel__body{margin-left:160px}.el-popper.el-picker__popper .el-picker-panel{border-radius:var(--fcrm-border-radius-8)}.el-popper.el-picker__popper .el-picker-panel__body-wrapper .el-picker-panel__sidebar{padding:16px;width:160px;border-right:1px solid var(--fc-primary-border)}.el-popper.el-picker__popper .el-picker-panel__body-wrapper .el-picker-panel__sidebar .el-picker-panel__shortcut{border-radius:8px;font-weight:500;font-size:12px;line-height:16px;color:var(--fc-secondary-text);padding:8px 8px 8px 10px;margin-bottom:4px;transition:.2s;-webkit-transition:.2s}.el-popper.el-picker__popper .el-picker-panel__body-wrapper .el-picker-panel__sidebar .el-picker-panel__shortcut:last-child{margin-bottom:0}.el-popper.el-picker__popper .el-picker-panel__body-wrapper .el-picker-panel__sidebar .el-picker-panel__shortcut:hover{color:var(--fc-primary-text);background:var(--fc-secondary-bg)}.el-popper.el-picker__popper .el-picker-panel__body-wrapper .el-picker-panel__body .el-date-range-picker__header{background:var(--fc-secondary-bg);border-radius:var(--fcrm-border-radius-8);padding:6px;height:auto}.el-popper.el-picker__popper .el-picker-panel__body-wrapper .el-picker-panel__body .el-date-range-picker__header>div{line-height:1;display:flex;align-items:center;gap:8px;justify-content:center}.el-popper.el-picker__popper .el-picker-panel__body-wrapper .el-picker-panel__body .el-date-range-picker__header .el-date-range-picker__header-label{display:block;font-weight:500;font-size:14px;line-height:20px;text-align:center;color:var(--fc-secondary-text)}.el-popper.el-picker__popper .el-picker-panel__body-wrapper .el-picker-panel__body .el-date-range-picker__header .el-picker-panel__icon-btn{margin-top:3px}.el-popper.el-picker__popper .el-picker-panel__body-wrapper .el-picker-panel__body .el-date-table tbody tr th{color:var(--fc-text-muted);font-weight:400;font-size:14px;line-height:20px;padding-left:0;padding-right:0;border:none}.el-popper.el-picker__popper .el-picker-panel__body-wrapper .el-picker-panel__body .el-date-table tbody tr td.next-month .el-date-table-cell__text,.el-popper.el-picker__popper .el-picker-panel__body-wrapper .el-picker-panel__body .el-date-table tbody tr td.prev-month .el-date-table-cell__text{color:var(--fc-secondary-border)}.el-popper.el-picker__popper .el-picker-panel__body-wrapper .el-picker-panel__body .el-date-table tbody tr td.available.in-range.start-date .el-date-table-cell{border-radius:var(--fcrm-border-radius-8) 0 0 var(--fcrm-border-radius-8)}.el-popper.el-picker__popper .el-picker-panel__body-wrapper .el-picker-panel__body .el-date-table tbody tr td.available.in-range.start-date .el-date-table-cell .el-date-table-cell__text{border-radius:var(--fcrm-border-radius-8);font-weight:500}.el-popper.el-picker__popper .el-picker-panel__body-wrapper .el-picker-panel__body .el-date-table tbody tr td.available.in-range.end-date .el-date-table-cell{border-radius:0 var(--fcrm-border-radius-8) var(--fcrm-border-radius-8) 0}.el-popper.el-picker__popper .el-picker-panel__body-wrapper .el-picker-panel__body .el-date-table tbody tr td.available.in-range.end-date .el-date-table-cell .el-date-table-cell__text{font-weight:500;border-radius:var(--fcrm-border-radius-8)}.el-popper.el-picker__popper.fcrm_date_time_picker .el-picker-panel__content,.el-popper.fcrm_mail_config_datetime .el-picker-panel__content{width:auto}.el-date-editor{justify-content:flex-start;padding:0;background:none}.el-date-editor .el-input__wrapper{padding:2px 10px}.el-date-editor .el-range-input{height:auto;line-height:20px;font-weight:400;font-size:13px;color:var(--fc-primary-text)}.el-date-editor .el-range-separator{line-height:1}.fcrm_range_picker{display:flex;align-items:center;gap:8px;flex-wrap:wrap}.fcrm_range_picker .el-date-editor.el-range-editor{--el-date-editor-width: 240px;width:240px!important;min-width:0;flex:0 0 auto}.fcrm_range_picker .el-select{min-width:150px;flex:0 0 auto}.fcrm_range_picker .fcrm_range_compare_date.el-date-editor.el-range-editor{--el-date-editor-width: 240px;width:240px!important}@media (max-width: 768px){.fcrm_range_picker{flex-wrap:wrap}.fcrm_range_picker .el-date-editor.el-range-editor{--el-date-editor-width: 100%;width:100%!important}}.fcrm_range_picker .el-date-editor .el-input__icon{font-size:20px}.fcrm_range_picker .el-date-editor .el-input__icon svg{width:20px;height:20px}.el-select{width:100%}.el-select input{margin:0;padding:0}.el-select.el-select--small .el-select__wrapper{min-height:32px}.el-select__wrapper{min-height:36px;background:var(--fc-primary-bg);border-radius:var(--fcrm-border-radius-8);box-shadow:0 1px 2px #0a0d1408;border:1px solid var(--fc-primary-border);padding:2px 10px}.el-select__wrapper.is-focus,.el-select__wrapper.is-focused{border-color:var(--fc-primary-text)}.el-select__wrapper.is-disabled{opacity:.5}.el-select__wrapper .el-select__selection{display:flex;align-items:center;gap:4px;max-width:100%}.el-select__wrapper .el-select__selection .el-select__selected-item{flex-shrink:1;min-width:0;overflow:hidden}.el-select__wrapper .el-select__selection .el-select__selected-item.el-select__placeholder.is-transparent,.el-select__wrapper .el-select__selection .el-select__selected-item .el-select__placeholder.is-transparent{color:var(--el-text-color-placeholder)}.el-select__wrapper .el-select__selection .el-select__selected-item .el-tag{background:var(--fc-secondary-bg);color:var(--fc-secondary-text);border-radius:6px;font-weight:500;font-size:12px;line-height:16px;padding:2px 8px;border:none;height:auto;margin:0 2px;max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.el-select__wrapper .el-select__selection .el-select__selected-item .el-tag .el-select__tags-text{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;display:block;max-width:100%}.el-select__wrapper .el-select__selection .el-select__selected-item .el-tag .el-tag__close{color:var(--fc-secondary-text);font-size:12px;margin-inline-start:4px}.el-select__wrapper .el-select__selection .el-select__selected-item .el-tag .el-tag__close:hover{background:transparent;color:var(--fc-primary-text)}.el-select__wrapper .el-select__selection .el-select__selected-item .el-tag.el-tag--info{background:var(--fc-secondary-bg);color:var(--fc-secondary-text)}.el-select__wrapper .el-select__selection .el-select__collapse-tag{background:var(--fc-secondary-bg);border:none;border-radius:6px;padding:4px 8px;height:auto;font-size:12px;font-weight:500;line-height:16px;color:var(--fc-secondary-text);margin:0 2px}.el-select__wrapper .el-select__selection .el-select__collapse-tag .el-select__tags-text{color:var(--fc-secondary-text)}.el-select__wrapper .el-select__selection .el-select__input-wrapper{flex-shrink:0;min-width:20px}.el-select__wrapper .el-select__placeholder,.el-select__wrapper .el-select__selected-item{font-size:14px;font-weight:500;line-height:20px;color:var(--fc-text-muted)}.el-select__wrapper .el-select__selected-item{color:var(--fc-secondary-text);font-weight:400}.el-select__wrapper .el-select__caret{color:var(--fc-secondary-text);flex-shrink:0}.el-select .el-tag__close.el-icon-close{right:-5px}.el-select.fcrm_background_select .el-select__wrapper{border:1px solid var(--fc-primary-border);background:var(--fc-secondary-bg)}.el-radio{margin:0;height:auto}.el-radio__input input{opacity:0;margin:0}.el-radio__input.is-checked .el-radio__inner{background:var(--fc-deep-bg);border-color:var(--fc-deep-bg)}.el-radio__input.is-checked+.el-radio__label{color:var(--fc-deep-bg)}.el-radio__label{font-weight:400;font-size:14px;line-height:20px;color:var(--fc-primary-text)}.el-radio__inner{width:16px;height:16px;border:1.5px solid var(--fc-primary-border);background:none}.el-radio__inner:after{width:8px;height:8px;background:var(--fc-primary-bg)}.fcrm-radio-group{display:flex;flex-direction:column;gap:12px;align-items:flex-start}.fcrm-radio-group .el-radio{white-space:break-spaces;display:flex;align-items:center;gap:8px;margin-right:0;height:auto}.fcrm-radio-group .el-radio .el-radio__input{margin-top:0}.fcrm-radio-group .el-radio .el-radio__label{padding-left:0}.fcrm-radio-group .el-radio:hover .el-radio__inner{border-color:var(--fc-deep-bg)}.fcrm-radio-content{display:flex;gap:4px;align-items:center;flex-wrap:wrap}.fcrm-radio-label{font-size:14px;font-weight:400;line-height:20px;letter-spacing:-.084px;color:var(--fc-primary-text)}.fcrm-radio-sublabel{font-size:12px;font-weight:400;line-height:16px;color:var(--fc-secondary-text)}.el-radio-button__inner{border-color:var(--fc-primary-border)}.el-checkbox{height:auto}.el-checkbox__input.is-indeterminate .el-checkbox__inner,.el-checkbox__input.is-checked .el-checkbox__inner{background:var(--fc-deep-bg);border-color:var(--fc-deep-bg)}.el-checkbox__input.is-indeterminate .el-checkbox__inner:after,.el-checkbox__input.is-checked .el-checkbox__inner:after{border-color:var(--fc-text-inverse)}.el-checkbox__original{margin:0}.el-checkbox__inner{width:16px;height:16px;border-radius:4px;background:none;border-color:var(--fc-primary-border)}.el-checkbox__inner:hover{border-color:var(--fc-deep-bg)}.el-checkbox .fcrm_checkbox_text{display:flex;flex-direction:column}.el-checkbox .fcrm_checkbox_text_title{color:var(--fc-primary-text)}.el-checkbox .fcrm_checkbox_text_desc{margin-top:4px;color:var(--fc-secondary-text);font-weight:400;font-size:12px;font-style:normal;line-height:16px}.el-dropdown-menu__item{line-height:22px;padding:7px 20px}.el-popper.is-dark{background:#151d26}.el-popper.is-dark>.el-popper__arrow:before{background:#151d26}.el-popper.fcrm_ai_summary_popover{padding:4px}.el-popper{z-index:100000!important;box-shadow:0 16px 32px -12px #0e121b1a;border-radius:8px;padding:8px;box-sizing:border-box}.el-popper *{box-sizing:border-box}.el-popper .el-select-dropdown{min-width:auto!important;max-width:300px}.el-popper .el-select-dropdown.is-multiple .el-select-dropdown__list .el-select-dropdown__item,.el-popper .el-select-dropdown.is-multiple .el-select-dropdown__list .el-dropdown-menu__item,.el-popper .el-select-dropdown.is-multiple .el-dropdown-menu .el-select-dropdown__item,.el-popper .el-select-dropdown.is-multiple .el-dropdown-menu .el-dropdown-menu__item{padding-inline-end:30px}.el-popper.is-light{border:1px solid var(--fc-primary-border);background:var(--fc-primary-bg)}.el-popper .el-select-dropdown__list,.el-popper .el-dropdown-menu{padding:0;background:none;box-shadow:none;border:none}.el-popper .el-select-dropdown__list .el-select-dropdown__item,.el-popper .el-select-dropdown__list .el-dropdown-menu__item,.el-popper .el-dropdown-menu .el-select-dropdown__item,.el-popper .el-dropdown-menu .el-dropdown-menu__item{height:auto;font-weight:400;font-size:14px;line-height:20px;border-radius:8px;padding:7px 10px;margin:0;word-break:break-word;overflow-wrap:break-word;white-space:wrap;display:flex;align-items:center;gap:8px;transition:.2s;-webkit-transition:.2s}.el-popper .el-select-dropdown__list .el-select-dropdown__item:not(.is-disabled):focus,.el-popper .el-select-dropdown__list .el-select-dropdown__item:not(.is-disabled):active,.el-popper .el-select-dropdown__list .el-select-dropdown__item:hover,.el-popper .el-select-dropdown__list .el-dropdown-menu__item:not(.is-disabled):focus,.el-popper .el-select-dropdown__list .el-dropdown-menu__item:not(.is-disabled):active,.el-popper .el-select-dropdown__list .el-dropdown-menu__item:hover,.el-popper .el-dropdown-menu .el-select-dropdown__item:not(.is-disabled):focus,.el-popper .el-dropdown-menu .el-select-dropdown__item:not(.is-disabled):active,.el-popper .el-dropdown-menu .el-select-dropdown__item:hover,.el-popper .el-dropdown-menu .el-dropdown-menu__item:not(.is-disabled):focus,.el-popper .el-dropdown-menu .el-dropdown-menu__item:not(.is-disabled):active,.el-popper .el-dropdown-menu .el-dropdown-menu__item:hover{background:var(--fc-secondary-bg)}.el-popper .el-select-dropdown__list .el-select-dropdown__item:not(.is-disabled):focus.fcrm_danger_action,.el-popper .el-select-dropdown__list .el-select-dropdown__item:not(.is-disabled):active.fcrm_danger_action,.el-popper .el-select-dropdown__list .el-select-dropdown__item:hover.fcrm_danger_action,.el-popper .el-select-dropdown__list .el-dropdown-menu__item:not(.is-disabled):focus.fcrm_danger_action,.el-popper .el-select-dropdown__list .el-dropdown-menu__item:not(.is-disabled):active.fcrm_danger_action,.el-popper .el-select-dropdown__list .el-dropdown-menu__item:hover.fcrm_danger_action,.el-popper .el-dropdown-menu .el-select-dropdown__item:not(.is-disabled):focus.fcrm_danger_action,.el-popper .el-dropdown-menu .el-select-dropdown__item:not(.is-disabled):active.fcrm_danger_action,.el-popper .el-dropdown-menu .el-select-dropdown__item:hover.fcrm_danger_action,.el-popper .el-dropdown-menu .el-dropdown-menu__item:not(.is-disabled):focus.fcrm_danger_action,.el-popper .el-dropdown-menu .el-dropdown-menu__item:not(.is-disabled):active.fcrm_danger_action,.el-popper .el-dropdown-menu .el-dropdown-menu__item:hover.fcrm_danger_action{background:var(--el-color-danger-light-9);color:var(--el-color-danger)}.el-popper .el-select-dropdown__list .el-select-dropdown__item .el-button .el-icon,.el-popper .el-select-dropdown__list .el-dropdown-menu__item .el-button .el-icon,.el-popper .el-dropdown-menu .el-select-dropdown__item .el-button .el-icon,.el-popper .el-dropdown-menu .el-dropdown-menu__item .el-button .el-icon{margin:0}.el-popper .el-select-dropdown__list .el-select-dropdown__item .el-button>span,.el-popper .el-select-dropdown__list .el-dropdown-menu__item .el-button>span,.el-popper .el-dropdown-menu .el-select-dropdown__item .el-button>span,.el-popper .el-dropdown-menu .el-dropdown-menu__item .el-button>span{gap:6px;min-width:0}.el-popper .el-select-dropdown__list .el-select-dropdown__item .el-button:not(.el-button--primary),.el-popper .el-select-dropdown__list .el-dropdown-menu__item .el-button:not(.el-button--primary),.el-popper .el-dropdown-menu .el-select-dropdown__item .el-button:not(.el-button--primary),.el-popper .el-dropdown-menu .el-dropdown-menu__item .el-button:not(.el-button--primary){width:100%;justify-content:flex-start;background:none;border:none;color:var(--fc-primary-text);padding:0}.el-popper .el-select-dropdown__list .el-select-dropdown__item .el-tooltip__trigger,.el-popper .el-select-dropdown__list .el-select-dropdown__item .el-popover__reference,.el-popper .el-select-dropdown__list .el-dropdown-menu__item .el-tooltip__trigger,.el-popper .el-select-dropdown__list .el-dropdown-menu__item .el-popover__reference,.el-popper .el-dropdown-menu .el-select-dropdown__item .el-tooltip__trigger,.el-popper .el-dropdown-menu .el-select-dropdown__item .el-popover__reference,.el-popper .el-dropdown-menu .el-dropdown-menu__item .el-tooltip__trigger,.el-popper .el-dropdown-menu .el-dropdown-menu__item .el-popover__reference{padding:0;display:flex;align-items:center;gap:4px;width:100%}.el-popper .el-select-dropdown__list .el-select-dropdown__item .el-tooltip__trigger .el-icon,.el-popper .el-select-dropdown__list .el-select-dropdown__item .el-popover__reference .el-icon,.el-popper .el-select-dropdown__list .el-dropdown-menu__item .el-tooltip__trigger .el-icon,.el-popper .el-select-dropdown__list .el-dropdown-menu__item .el-popover__reference .el-icon,.el-popper .el-dropdown-menu .el-select-dropdown__item .el-tooltip__trigger .el-icon,.el-popper .el-dropdown-menu .el-select-dropdown__item .el-popover__reference .el-icon,.el-popper .el-dropdown-menu .el-dropdown-menu__item .el-tooltip__trigger .el-icon,.el-popper .el-dropdown-menu .el-dropdown-menu__item .el-popover__reference .el-icon{display:block;margin:0;width:auto;height:auto}.el-popper .el-select-dropdown__list .el-select-dropdown__item .icon svg,.el-popper .el-select-dropdown__list .el-dropdown-menu__item .icon svg,.el-popper .el-dropdown-menu .el-select-dropdown__item .icon svg,.el-popper .el-dropdown-menu .el-dropdown-menu__item .icon svg{display:block;width:18px;height:18px}.el-popper .el-select-dropdown__list .el-select-dropdown__item.fcrm_danger_action:hover,.el-popper .el-select-dropdown__list .el-dropdown-menu__item.fcrm_danger_action:hover,.el-popper .el-dropdown-menu .el-select-dropdown__item.fcrm_danger_action:hover,.el-popper .el-dropdown-menu .el-dropdown-menu__item.fcrm_danger_action:hover{background:var(--el-color-danger-light-9);color:var(--el-color-danger)}.el-popper .el-select-dropdown__list .el-select-dropdown__item:after,.el-popper .el-select-dropdown__list .el-dropdown-menu__item:after,.el-popper .el-dropdown-menu .el-select-dropdown__item:after,.el-popper .el-dropdown-menu .el-dropdown-menu__item:after{right:10px}.el-popper .el-select-dropdown__list .fc-dropdown-items-label,.el-popper .el-dropdown-menu .fc-dropdown-items-label{background:none!important;cursor:text}.el-popper .el-select-dropdown__list .fc-dropdown-items-label:hover,.el-popper .el-dropdown-menu .fc-dropdown-items-label:hover{background:none!important}.el-popper .el-select .el-popper{width:100%}.el-popper.fcrm_action_selector_popover{border-radius:8px;padding:12px;border:1px solid var(--fc-primary-border);box-shadow:0 16px 32px -12px #0e121b1a}.el-popper.fcrm_send_test_email_popover{border-radius:8px;padding:12px;border:1px solid var(--fc-primary-border);box-shadow:0 16px 32px -12px #0e121b24}.el-popper.fcrm_send_test_email_popover .fcrm_send_test_email_content{display:flex;flex-direction:column;gap:16px}.el-popper.fcrm_send_test_email_popover .fcrm_send_test_email_content_header{display:flex;flex-direction:column;gap:4px}.el-popper.fcrm_send_test_email_popover .fcrm_send_test_email_content .fcrm_input_hint{display:flex;align-items:flex-start;gap:4px;color:var(--fc-secondary-text);font-size:12px;line-height:16px;font-weight:400;margin:4px 0 0}.el-popper.fcrm_send_test_email_popover .fcrm_send_test_email_content .fcrm_input_hint .icon,.el-popper.fcrm_send_test_email_popover .fcrm_send_test_email_content .fcrm_input_hint .el-icon{color:var(--fc-text-muted);display:block}.el-popper.fcrm_send_test_email_popover .fcrm_send_test_email_content .fcrm_input_hint .icon svg,.el-popper.fcrm_send_test_email_popover .fcrm_send_test_email_content .fcrm_input_hint .el-icon svg{display:block;width:14px;height:14px}.el-popper.fcrm_send_test_email_popover .fcrm_send_test_email_title{margin:0;color:var(--fc-primary-text);font-weight:500;font-size:16px;line-height:24px}.el-popper.fcrm_send_test_email_popover .fcrm_send_test_email_description{margin:0;color:var(--fc-secondary-text);font-size:14px;line-height:20px;font-weight:400}.el-popper.fcrm_send_test_email_popover .fcrm_send_test_email_input_wrap{display:flex;gap:8px}.el-popper.fcrm_send_test_email_popover .fcrm_send_test_email_input_wrap .el-input__wrapper{box-shadow:0 1px 2px #0a0d1408;border:1px solid var(--fc-primary-border);border-radius:8px}.el-popper.fcrm_send_test_email_popover .fcrm_send_test_email_input_wrap .el-input__wrapper.is-focused,.el-popper.fcrm_send_test_email_popover .fcrm_send_test_email_input_wrap .el-input__wrapper.is-focus{border-color:var(--fc-primary-text)}.el-popper.fcrm_select_options_wordbreak .el-select-dropdown__list .el-select-dropdown__item{white-space:normal}.el-cascader-node.in-active-path{font-weight:500;background:var(--fc-weak-bg-25)}.el-alert{padding:14px;align-items:flex-start;gap:12px}.el-alert__icon{font-size:15px;width:auto;height:auto;margin:2px 0 0;color:var(--fc-text-muted)}.el-alert__title{font-weight:500;font-size:14px;line-height:20px;margin:0}.el-alert__description{color:var(--fc-secondary-text);margin:0;font-weight:400;font-size:14px;line-height:20px}.el-dialog{padding:0!important;border-radius:5px;margin-top:40px!important}.el-dialog .el-dialog__header{background:var(--fc-secondary-bg);padding:10px}.el-dialog .dialog-footer{padding:10px;text-align:right;box-sizing:border-box;background:var(--fc-secondary-bg);border-bottom-left-radius:5px;border-bottom-right-radius:5px;width:auto;display:block}.el-dialog .warning{font-size:12px}.el-dialog__body{padding:15px 20px}.el-tag+.el-tag{margin-left:10px}.el-tag{font-weight:400}.el-popover{padding:10px;text-align:left}.el-popover .fluentcrm_status_change_wrapper,.el-popover .fluentcrm_type_change_wrapper{padding:0!important;margin-bottom:10px}.el-popover .fluentcrm_status_change_wrapper input,.el-popover .fluentcrm_type_change_wrapper input{border-color:var(--fc-primary-border);outline:none;box-shadow:none}.el-popover .fluentcrm_status_change_wrapper input:focus,.el-popover .fluentcrm_type_change_wrapper input:focus{border-color:var(--fc-primary-text);color:var(--fc-primary-text)}.el-popover .action-buttons{margin:0;text-align:center}.el-button--mini{padding:6px 8px}.el-input-number .el-input__inner{margin:0}.fluentcrm_checkable_block>label{display:block;margin:0;padding:0 10px 10px}.fcrm_p_0{padding:0}.fcrm_p_20{padding:20px}.el-select__tags{padding-left:10px}.el-select__tags input{border:none}.el-select__tags input:focus{border:none;box-shadow:none;outline:none}.fluentcrm-campaigns .el-select__tags>span .el-tag{margin-left:6px}.fluentcrm-campaigns .el-select__tags>span .el-tag:first-child{margin-left:0}.fluentcrm-campaign .fluentcrm-campaign-heading{background:var(--fc-primary-bg);padding:10px 12px;border-radius:4px}.fluentcrm-campaign .fluentcrm-campaign-heading>i{background:var(--fc-secondary-bg);border-radius:50%;font-size:14px;text-align:center;width:30px;height:30px;line-height:30px;margin-left:10px;transition:.3s;-webkit-transition:.3s;-moz-transition:.3s;-o-transition:.3s}.fluentcrm-campaign .fluentcrm-campaign-heading>i:hover{background:#7756e626;color:var(--fc-deep-bg)}.el-popover h3,.el-tooltip__popper h3{margin:0 0 10px}.el-popover{word-break:break-all;border-color:var(--fc-primary-border)}.el-step__icon.is-text{vertical-align:middle}.el-menu-vertical-demo{min-height:80vh}.el-menu-item.is-active{background:#2225301a}.fluentcrm_min_bg{min-height:80vh;background-color:var(--fc-secondary-bg)}ul.fc_list{margin:0;padding:0 0 0 20px}ul.fc_list li{line-height:26px;list-style:disc;padding-left:0}.el-dialog__body{word-break:inherit}.fc_highlight_gray{display:block;overflow:hidden;padding:20px;background:#f5f5f5;border-radius:10px;margin-bottom:10px}.el-notification.bottom_right.right{top:auto!important}.el-dialog__wrapper{background:#80808073}@media all and (min-width: 1100px){.el-dialog__wrapper .el-dialog{min-width:1080px!important}}@media all and (max-width: 1000px){.el-dialog__wrapper .el-dialog{min-width:95%!important}}.el-message-box__wrapper{z-index:10003!important;background:#80808073}.el-tooltip__popper{z-index:10020!important}body.el-popup-parent--hidden #adminmenumain,body.el-popup-parent--hidden #wpwrap{z-index:10}.fc-general-settings .el-form-item__content{line-height:120%}#fc_reports_tooltip{background:var(--fc-primary-bg);text-align:left}.fc_range_picker .el-range-editor--mini .el-range-separator{font-size:9px!important;min-width:20px}.fc_range_picker .el-range-editor--mini.el-input__inner{height:28px;max-width:200px}.el-picker-panel__sidebar .el-picker-panel__shortcut{line-height:20px;font-size:13px;padding:7px 10px}.el-picker-panel__sidebar .el-picker-panel__shortcut:hover{background:#409eff0f}.fc-icon-90degree{transform:rotate(90deg)}.el-dropdown-list-wrapper{padding:0}.el-dropdown-list-wrapper .group-title{display:block;padding:5px 10px;background-color:var(--fc-text-muted);color:var(--fc-text-inverse)}.el-dropdown-list-wrapper.el-popover{z-index:9999999999999!important}.input-textarea-value{position:relative}.input-textarea-value .icon{position:absolute;right:0;top:-18px;cursor:pointer}.fcrm-smartcodes-popover{padding:0;border-radius:8px}.fcrm-smartcodes-popover .el_pop_data_group{overflow:hidden;display:flex}.fcrm-smartcodes-popover .el_pop_data_group *{box-sizing:border-box}.fcrm-smartcodes-popover .el_pop_data_group .pop_doc{left:0;bottom:0;width:100%;padding:0}.fcrm-smartcodes-popover .el_pop_data_group .pop_doc a{background:var(--fc-light-bg);color:var(--fc-primary-text);text-align:center;display:block;padding:4px 5px;border-radius:4px;transition:.2s}.fcrm-smartcodes-popover .el_pop_data_group .pop_doc a:hover{background:var(--fc-primary-text);color:var(--fc-text-inverse)}.fcrm-smartcodes-popover .el_pop_data_group .el_pop_data_headings{max-width:190px;background:var(--fc-secondary-bg);border-radius:8px;padding:10px;position:relative}.fcrm-smartcodes-popover .el_pop_data_group .el_pop_data_headings ul{padding:0;margin:10px 0 0}.fcrm-smartcodes-popover .el_pop_data_group .el_pop_data_headings ul li{cursor:pointer;color:var(--fc-primary-text);font-size:13px;padding:6px 8px;border-radius:4px;margin-bottom:4px;position:relative;transition:.2s}.fcrm-smartcodes-popover .el_pop_data_group .el_pop_data_headings ul li.active_item_selected{background:var(--fc-primary-text);color:var(--fc-text-inverse)}.fcrm-smartcodes-popover .el_pop_data_group .el_pop_data_body{background:var(--fc-primary-bg);padding:0 14px;width:370px;height:400px;overflow:auto;border-radius:0 10px 10px 0}.fcrm-smartcodes-popover .el_pop_data_group .el_pop_data_body .el_pop_search{padding:14px 0 12px;position:sticky;top:0;border-bottom:1px solid var(--fc-secondary-bg);background:var(--fc-primary-bg)}.fcrm-smartcodes-popover .el_pop_data_group .el_pop_data_body .el_pop_search .el-input input{margin:0;border-radius:6px;height:auto;padding:0 10px}.fcrm-smartcodes-popover .el_pop_data_group .el_pop_data_body ul{padding:0;margin:0}.fcrm-smartcodes-popover .el_pop_data_group .el_pop_data_body ul li{color:var(--fc-primary-text);padding:10px;display:block;margin-bottom:0;cursor:pointer;text-align:left;border-bottom:1px solid var(--fc-secondary-bg)}.fcrm-smartcodes-popover .el_pop_data_group .el_pop_data_body ul li:last-child{border-bottom:none}.fcrm-smartcodes-popover .el_pop_data_group .el_pop_data_body ul li:hover{background:var(--fc-primary-bg)}.fcrm-smartcodes-popover .el_pop_data_group .el_pop_data_body ul li span{font-size:11px;color:var(--fc-text-muted);margin:2px 0 0;display:block}.max_height_340{max-height:340px;overflow:auto}.max_height_550{max-height:550px;overflow:auto}.el-progress_animated .el-progress-bar__inner{transform:translateZ(0);animation:indeterminate 2s infinite}.el-progress_animated .el-progress-bar__outer{background-color:#fbfb3f}.fc_loading_bar{position:absolute;z-index:9999;background:#f8ffa3;top:0;left:0;right:0;text-align:center}@keyframes progress{0%{background-position:0 0}to{background-position:32px 0}}@keyframes indeterminate{0%{left:-100%}to{left:100%}}.el-menu-item{margin-bottom:0;line-height:52px;height:52px}.fc_mb_0{margin-bottom:0}.fcrm_checkbox_group_label{display:block;padding:6px 0 4px;font-weight:600;color:var(--fc-secondary-text);font-size:14px;line-height:1}.fc_its_gray .fc_highlight_gray{background:var(--fc-primary-bg)}.fc_no_marg_b{margin-bottom:0!important}.fc_no_pad_l{padding-left:0!important}span.el-range-separator{min-width:20px}.fc_form_items_inline{display:flex;align-items:flex-start;flex-direction:row;flex-wrap:wrap}.fc_form_items_inline.fc_is_highlighted{margin:0 -20px;background:var(--fc-secondary-bg);padding:10px 20px 0;border-radius:5px}.fc_form_items_inline>div{min-width:280px;display:inline-block;padding-right:20px}.fc_notify_z{z-index:9999999!important}.fluentcrm-app .el-table--striped .el-table__body tr.el-table__row--striped td.el-table__cell{background:var(--fc-secondary-bg)}.fluentcrm-app .el-table--enable-row-hover .el-table__body tr:hover>td.el-table__cell{background-color:var(--fc-secondary-bg)}ul.el-dropdown-menu{padding-top:5px}li.el-dropdown-menu__item.fc_dropdown_action{padding:0 20px}li.el-dropdown-menu__item.fc_dropdown_action .el-popover__reference{padding:7px 0;display:block}.el-scrollbar.el-cascader-menu{max-height:400px;overflow:auto}.el-notification.fc_bottom-right.right{top:auto!important;bottom:20px!important}span.fc_tag_prof_info{margin-right:6px;padding:0;color:var(--fc-secondary-text);margin-bottom:5px!important;display:inline-block;border-bottom:1px solid var(--fc-secondary-text);line-height:110%}ul.inline_disc_lists{list-style:disc}ul.inline_disc_lists li{display:inline;margin-right:30px}ul.inline_disc_lists li:before{content:"• ";font-size:120%;font-weight:700}label.fcrm_inline_check{white-space:normal;display:flex;align-items:flex-start}label.fcrm_inline_check span.el-checkbox__label{margin-top:-3px}.fc_shadow{box-shadow:0 1px 8px #0000001a;transition:background .3s,border .3s,border-radius .3s,box-shadow .3s}.fc_shadow:hover{box-shadow:0 4px 16px #0003}.fc_shadow_hover{transition:background .2s,border .2s,border-radius .2s,box-shadow .2s}.fc_shadow_hover:hover{box-shadow:0 1px 6px #0003}body .el-notification.right{top:auto!important;bottom:20px!important}.fc_small{font-size:90%}.pop_doc{position:absolute;padding:5px;color:var(--fc-text-inverse);bottom:5px;left:5px}.pop_doc a{color:var(--fc-light-bg)}.pop_doc a:hover{color:var(--fc-text-inverse)}span.fc_middot{margin:0 6px;font-weight:700;color:var(--fc-secondary-text)}span.fc_company_name{display:inline-block}span.fc_company_name img{max-width:16px;display:inline-block;margin-bottom:-3px;border-radius:50%;max-height:16px}li.sub_item_active{color:var(--fc-text-link)}.fc_item_hidden{display:none!important;visibility:hidden!important}.fc_abs_sidebar{top:0;position:absolute;right:-16px;bottom:20px;background:var(--fc-secondary-bg);width:15px;border-left:4px solid rgb(203,214,226)}.fc_abs_sidebar .fc_sidebar_open_btn{position:sticky;padding:3px;border-radius:50%;font-size:19px;margin-left:-15px;top:0}.fc_sidebar_col{position:relative}.fc_side_closed,.fc_side_closed .fluentcrm-contact-view{margin-right:15px}.fc_abs_sidebar .el-badge__content.is-fixed{font-size:9px;border-radius:50%;line-height:16px}.fc_abs_sidebar_opened{position:absolute}.fc_abs_sidebar_opened .fc_sidebar_open_btn{position:sticky;padding:2px;border-radius:50%;font-size:15px;margin-left:-27px;top:0}.fc_side_opened .fc_contact_main_col{border-right:4px solid rgb(203,214,226)}.fc_side_opened ul.fluentcrm_profile_nav li{padding:17px 15px}.fc_dash_external{font-size:12px;line-height:inherit;color:var(--fc-text-muted)}.fc_company_card{padding:15px 52px 10px 15px;position:relative;background-color:var(--fc-secondary-bg);border-radius:4px;border:1px solid rgb(199,210,223)}.fc_company_card .company_name{font-weight:700;font-size:15px;display:block}.fc_company_card .fc_primary_badge{display:table;margin:-15px 0 7px -15px;border-top-left-radius:4px;font-size:12px;background-color:#f5f8fa;border-bottom:1px solid rgb(199,210,223);border-right:1px solid rgb(199,210,223);color:#33475b;line-height:22px;padding:0 8px;font-weight:500}.fc_company_card .fc_company_logo{position:absolute;right:10px;top:10px;height:42px;width:42px}.fc_company_card .fc_company_logo img{width:auto;height:42px}.fc_company_card .fc_company_actions{display:none;position:absolute;right:0;top:0;z-index:99999}.fc_company_card:hover .fc_company_actions,.fc_company_card:focus-within .fc_company_actions{display:block}.fc_companies .fc_company_card{margin-bottom:10px}.fc_companies .fc_company_card:last-child{margin-bottom:0}.fc_card_header{display:flex;justify-content:space-between;align-items:center}.el-drawer__header{font-size:16px;font-weight:700;background-color:var(--fc-secondary-bg);padding:16px 20px!important;margin-bottom:0!important}.fc_company_header{display:flex;align-items:center;gap:10px}.fc_company_header h3{margin:7px 0}.fc_company_header .fluentcrm_profile-photo .fc_photo_holder_mini{width:80px;height:80px;margin-right:0}.fc_company_header.fc_header_editing{gap:0;flex-direction:column-reverse;align-items:initial}.fc_company_header .fc_company_info .company_domain,.fc_company_header .fc_company_info .company_email{word-break:break-all}.fc_compact_form .el-form-item{margin-bottom:15px}.fc_compact_form.el-form--label-top .el-form-item__label{padding-bottom:5px;font-size:14px}.fc_compact_form .el-input__inner{height:32px!important;border:1px solid var(--fc-secondary-bg)}.fc_compact_form .el-input__inner:focus,.fc_compact_form .el-input__inner:active{border:1px solid var(--fc-text-link)!important}.fc_compact_form .el-form-item__content,.fc_compact_form .el-input__icon{line-height:32px;font-size:13px}.fc_compact_form .el-input-group__prepend,.fc_compact_form .el-input-group__append{padding:0 5px}.fc_company_unsaved .fc_company_save_wrap{position:sticky;bottom:0;background-color:var(--fc-secondary-bg);padding:15px;margin:10px -15px 0;z-index:9}.fc_drawer_footer_wrap{position:sticky;bottom:0;background-color:var(--fc-secondary-bg);padding:15px;top:90%;z-index:999999}.contact_form_handler .fc_drawer_footer_wrap{margin:0 -20px;display:flex;align-items:center;justify-content:space-between}.contact_form_handler form .el-form-item .fcrm_date_parts_picker{justify-content:space-between}.fcrm_drawer .el-drawer__body{padding:0!important}.fc_company_info_drawer.el-drawer__wrapper .el-drawer__header{margin-bottom:0}.fc_company_info_drawer.el-drawer__wrapper .fc_company_info_wrapper .el-form-item .el-date-editor{width:100%}.fc_has_selections .fc_search_box{display:none!important}.el-drawer__body .contact_form_handler{min-height:85vh}.fc_rich_checkboxes .el-checkbox{display:flex;margin-bottom:10px;width:100%;align-items:center}.fc_rich_checkboxes .el-checkbox span.el-checkbox__label{display:block;flex:1}.fc_empty_logo{width:24px;height:24px;display:block;background:var(--fc-secondary-border);border-radius:50%}.fc_company_sidebar{background:var(--fc-primary-bg)}.fc_company_sidebar .fc_company_header{padding:10px 15px}.fc_company_sidebar h3.fc_section_title{padding:0 15px}.fc_company_sidebar .fc_company_about{padding:0 15px 10px}.fc_company_sidebar .fc_company_about .el-form-item .el-form-item__label{display:block;width:100%;font-size:14px;font-weight:500;line-height:1.4;color:var(--fc-primary-text);margin-bottom:8px;padding:0}.fc_company_sidebar .fc_company_about .el-form-item .el-textarea textarea,.fc_company_sidebar .fc_company_about .el-form-item .el-textarea input,.fc_company_sidebar .fc_company_about .el-form-item .el-input textarea,.fc_company_sidebar .fc_company_about .el-form-item .el-input input{margin:0;padding:4px 16px;border-radius:6px;height:auto!important;width:100%}.fc_company_sidebar .fc_company_about .el-form-item .el-textarea textarea:focus,.fc_company_sidebar .fc_company_about .el-form-item .el-textarea input:focus,.fc_company_sidebar .fc_company_about .el-form-item .el-input textarea:focus,.fc_company_sidebar .fc_company_about .el-form-item .el-input input:focus{border-color:var(--fc-deep-bg)!important}.fc_company_sidebar .fc_company_about .el-form-item .el-date-editor{width:100%}.fc_company_sidebar .fc_company_about .el-form-item .el-date-editor input{padding-left:30px}.fc_company_sidebar .fc_company_about .el-form-item .el-date-editor input:focus{border-color:var(--fc-deep-bg)}.fc_company_sidebar .fc_company_about .el-form-item .el-checkbox .el-checkbox__label{color:var(--fc-primary-text)}.fc_company_sidebar .fc_company_about .el-form-item .el-checkbox.is-checked .el-checkbox__input.is-checked .el-checkbox__inner{background-color:var(--fc-deep-bg);border-color:var(--fc-deep-bg)}.fc_company_sidebar .fc_company_about .el-form-item .el-checkbox.is-checked .el-checkbox__label{color:var(--fc-deep-bg)}.fc_company_sidebar .fc_company_about .el-form-item .el-radio-group .el-radio .el-radio__input.is-checked .el-radio__inner{background:var(--fc-deep-bg);border-color:var(--fc-deep-bg)}.fc_company_sidebar .fc_company_about .el-form-item .el-radio-group .el-radio .el-radio__input.is-checked~.el-radio__label{color:var(--fc-deep-bg)}.fc_company_sidebar .fc_company_about .el-form-item .el-radio-group .el-radio .el-radio__label{color:var(--fc-primary-text)}.fc_photo_text{display:flex;gap:5px;align-items:center;line-height:105%}.fc_company_notes_wrap .fc_notes_header.fluentcrm_contact_header{display:block;width:auto;border-bottom:1px solid var(--fc-primary-border);clear:both;overflow:hidden;padding:10px 15px;background-color:var(--fc-secondary-bg);font-weight:700;color:var(--fc-secondary-text);margin:-20px -20px 20px}.fc_social_lists{margin-bottom:10px}.fc_social_lists .el-input{border:1px solid var(--fc-secondary-bg);margin-bottom:10px}.fc_social_lists .el-input .el-input__wrapper{padding:0}.fc_social_lists .el-input .el-input__wrapper .el-input__inner{padding:1px 7px}.fcrm_date_parts_picker{display:flex;gap:10px}.fcrm_date_parts_picker>div{max-width:100px}.fcrm_date_parts_picker>div p{margin:0;font-size:10px;padding-left:10px;color:var(--fc-text-muted)}.fc_notes_wrapper .fc_notes_header{margin-bottom:20px}.el-cascader-menu__wrap.el-scrollbar__wrap{margin:0!important}.fc_composite_filters{display:flex;gap:15px;margin-top:5px;margin-bottom:5px}.fc_composite_filters .fc_composite_filter{position:relative;min-width:100px}.fc_composite_filters .fc_composite_filter>label{font-size:9px;display:block;margin-top:-10px;line-height:9px}.fc_composite_filters .fc_composite_filter .el-input--mini .el-input__inner{min-height:26px;height:26px;line-height:26px}.fc_funnel_head{display:flex;align-items:center;justify-content:space-between}.fcrm_primary_text,.fcrm_secondary_text{color:var(--fc-secondary-text);margin:0;font-size:14px;line-height:20px}.fcrm_primary_text.small,.fcrm_secondary_text.small{font-size:12px;line-height:16px}.fcrm_primary_text .el-icon,.fcrm_secondary_text .el-icon{font-size:16px}.fcrm_primary_text .fc-inline-help-icon,.fcrm_primary_text .icon,.fcrm_secondary_text .fc-inline-help-icon,.fcrm_secondary_text .icon{color:var(--fc-text-muted)}.fcrm_primary_text .fc-inline-help-icon svg,.fcrm_primary_text .icon svg,.fcrm_secondary_text .fc-inline-help-icon svg,.fcrm_secondary_text .icon svg{width:14px;height:14px}.fcrm_primary_text{color:var(--fc-primary-text)}html *{box-sizing:border-box;font-family:var(--wp-admin-theme-font, var(--wp--preset--font-family--system-font, "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif))}.fcrm-info-alert{background:var(--fc-secondary-bg);color:var(--fc-primary-text);border-radius:8px;padding:8px;display:flex;gap:8px;align-items:center;width:100%;margin-top:4px}.fcrm-info-alert .icon,.fcrm-info-alert .el-icon,.fcrm-info-alert .fcrm-info-icon{color:var(--fc-text-muted);font-size:16px;flex-shrink:0}.fcrm-info-alert p{margin:0;font-weight:400;font-size:12px;line-height:16px;color:var(--fc-primary-text);flex:1}.el-overlay.fcrm_essential_modal .el-dialog{width:460px;max-width:calc(100vw - 24px);min-width:auto;overflow:hidden}.el-overlay.fcrm_essential_modal .el-overlay-dialog{display:flex;align-items:center;justify-content:center}.el-overlay.fcrm_essential_modal .el-dialog__header{display:none}.el-overlay.fcrm_essential_modal .el-dialog__body{padding:20px}.el-overlay.fcrm_essential_modal .fcrm_essential_modal__content{position:relative}.el-overlay.fcrm_essential_modal .fcrm_essential_modal__close{position:absolute;top:-8px;right:-10px;width:40px;height:40px;border:0;border-radius:10px;background:transparent;color:#5f6675;cursor:pointer;display:flex;align-items:center;justify-content:center;padding:0}.el-overlay.fcrm_essential_modal .fcrm_essential_modal__close .icon{width:24px;height:24px}.el-overlay.fcrm_essential_modal .fcrm_essential_modal__close svg{width:24px;height:24px;display:block}.el-overlay.fcrm_essential_modal .fcrm_essential_modal__logo{width:32px;height:32px;display:block;margin-bottom:20px}.el-overlay.fcrm_essential_modal .fcrm_essential_modal__header_title{font-weight:600;font-size:18px;line-height:24px;color:var(--fc-primary-text);margin:0 0 4px}.el-overlay.fcrm_essential_modal .fcrm_essential_modal__header_description{margin:0;font-weight:400;font-size:14px;line-height:20px;color:var(--fc-secondary-text)}.el-overlay.fcrm_essential_modal .fcrm_essential_modal__trust_box{background:#f2f4f7;border-radius:var(--fcrm-border-radius-8);margin-top:20px;padding:12px;display:flex;align-items:flex-start;gap:8px}.el-overlay.fcrm_essential_modal .fcrm_essential_modal__trust_icon{flex-shrink:0;line-height:0;color:var(--fc-success)}.el-overlay.fcrm_essential_modal .fcrm_essential_modal__trust_title{margin-bottom:4px}.el-overlay.fcrm_essential_modal .el-dialog__footer{padding:0 20px 20px;background:none}.el-overlay.fcrm_essential_modal .fcrm_essential_modal__footer .el-button{margin:0}a.el-button.el-button--primary{color:var(--fc-text-inverse)}.el-button{font-size:14px;line-height:20px;height:auto;padding:7px 10px}.el-button .cmd{display:block;background:var(--alpha-white-alpha-10, rgba(255, 255, 255, .1019607843));color:var(--fc-text-muted);border-radius:4px;font-weight:500;font-size:12px;line-height:16px;padding:2px 6px;text-transform:uppercase}.el-button>span{gap:4px}.el-button .el-icon,.el-button .icon{display:block}.el-button .el-icon svg,.el-button .icon svg{display:block}.el-button.el-button--small,.el-button.small{padding:5px 10px}.el-button.fcrm_setup_btn{background:var(--fc-secondary-bg);color:var(--fc-deep-bg);border:none;font-size:12px;font-weight:500;cursor:pointer;transition:background-color .2s ease}.el-button.fcrm_setup_btn:hover{background:var(--fc-deep-bg);color:var(--fc-text-inverse)}.el-button.only-icon-btn{width:36px;height:36px;padding:4px;flex:none}.el-button.only-icon-btn.small{width:32px;height:32px}.el-button.fcrm_pro_btn,.el-button.fcrm_pro_btn:hover{background:var(--fc-deep-bg);border-color:var(--fc-deep-bg)}.fcrm_onboarding_server_issues{max-width:640px;margin:0 auto 24px;background:var(--fc-error-bg);padding:14px 14px 14px 44px;border-radius:8px;position:relative}.fcrm_onboarding_server_issues .icon{color:var(--fc-error);position:absolute;left:16px;top:16px}.fcrm_onboarding_server_issues .icon svg{display:block}.fcrm_onboarding_server_issues h3{color:var(--fc-primary-text);margin:0 0 4px;font-weight:500;font-size:14px;line-height:20px}.fcrm_onboarding_server_issues p{font-weight:400;font-size:14px;line-height:20px;margin:0 0 10px;color:var(--fc-primary-text);opacity:.72}.fcrm_onboarding_server_issues .is-link{color:var(--fc-primary-text);font-size:14px;line-height:20px;font-weight:500}.fcrm_onboarding_server_issues .is-link.small{font-size:12px;line-height:16px}.is-link{color:var(--fc-deep-bg);text-decoration:underline;font-weight:400;font-size:14px;line-height:20px;cursor:pointer}.is-link.small{font-size:12px;line-height:16px}.fcrm_what_we_collect_toggle{display:inline;padding:0;margin:0;border:0;background:none;line-height:inherit}.fcrm_onboarding_container{background:var(--fc-secondary-bg);position:fixed;left:0;top:0;width:100%;height:100%;padding:64px 0;overflow-x:hidden}.fcrm_onboarding_container_inner{max-width:640px;width:100%;margin:auto;background:var(--fc-primary-bg);border-radius:8px}.fcrm_onboarding_container .cursor_pointer{cursor:pointer}.fcrm_onboarding_container .el-checkbox{height:auto}.fcrm_onboarding_container .el-checkbox .el-checkbox__input.is-checked+.el-checkbox__label{color:var(--fc-primary-text)}.fcrm_onboarding_container .el-checkbox .el-checkbox__input.is-checked .el-checkbox__inner{background:var(--fc-primary-text);border-color:var(--fc-primary-text)}.fcrm_onboarding_container .el-checkbox .el-checkbox__input .el-checkbox__inner{background:var(--fc-primary-bg)}.fcrm_onboarding_container .el-checkbox .el-checkbox__label{color:var(--fc-primary-text);font-size:14px;line-height:20px;font-weight:400;padding-left:10px}.fcrm_onboarding_container .el-form .el-form-item{margin-bottom:20px}.fcrm_onboarding_container .el-form .el-form-item:last-child{margin-bottom:0}.fcrm_onboarding_container .el-form .el-form-item__label{padding:0;margin:0 0 6px;color:var(--fc-primary-text);font-weight:500;font-size:14px;line-height:20px}.fcrm_onboarding_container .fluentcrm_photo_card .fluentcrm_photo_holder{display:flex;align-items:center;gap:8px}.fcrm_onboarding_container .fluentcrm_photo_card .fluentcrm_photo_holder img{width:40px;height:40px;margin:0 4px 0 0;object-fit:cover;border-radius:50%;background:var(--fc-text-link);color:var(--fc-text-inverse);flex-shrink:0}.fcrm_onboarding_container .fluentcrm_photo_card .fluentcrm_photo_holder .el-button{margin:0}.fcrm_onboarding_container .fcrm_onboarding_header{margin-bottom:32px;padding:20px 20px 0}.fcrm_onboarding_container .fcrm_onboarding_header_bar{display:flex;align-items:center;justify-content:space-between;gap:8px;margin-bottom:16px}.fcrm_onboarding_container .fcrm_onboarding_header_bar img{max-width:150px;display:block}.fcrm_onboarding_container .fcrm_onboarding_header_bar .fcrm_onboarding_step_count{color:var(--fc-secondary-text);font-weight:400;font-size:12px;line-height:16px}.fcrm_onboarding_container .fcrm_onboarding_step_header{margin-bottom:24px;padding:0 20px}.fcrm_onboarding_container .fcrm_onboarding_step_header--title{margin:0 0 4px;font-weight:600;font-size:18px;line-height:24px;color:var(--fc-primary-text)}.fcrm_onboarding_container .fcrm_onboarding_step_header--subtitle{font-weight:400;font-size:14px;line-height:20px;margin:0;color:var(--fc-secondary-text)}.fcrm_onboarding_container .fcrm_onboarding_step_body{padding:0 20px 20px}.fcrm_onboarding_container .fcrm_onboarding_step_body .fcrm-info-alert{margin-top:20px}.fcrm_onboarding_container .fcrm_onboarding_step.fcrm_onboarding_tags_lists_step table{width:100%;border:1px solid var(--fc-primary-border);border-radius:8px;border-spacing:0;overflow:hidden;margin-bottom:12px}.fcrm_onboarding_container .fcrm_onboarding_step.fcrm_onboarding_tags_lists_step table thead th{background:var(--fc-secondary-bg);border-bottom:1px solid var(--fc-primary-border);border-right:1px solid var(--fc-primary-border);text-align:left;color:var(--fc-secondary-text);font-weight:500;font-size:14px;line-height:20px;padding:8px 12px}.fcrm_onboarding_container .fcrm_onboarding_step.fcrm_onboarding_tags_lists_step table thead th:last-child{border-right:none;width:60px}.fcrm_onboarding_container .fcrm_onboarding_step.fcrm_onboarding_tags_lists_step table tbody tr:last-child td{border-bottom:none}.fcrm_onboarding_container .fcrm_onboarding_step.fcrm_onboarding_tags_lists_step table tbody tr td{padding:14px 12px;border-bottom:1px solid var(--fc-primary-border);border-right:1px solid var(--fc-primary-border)}.fcrm_onboarding_container .fcrm_onboarding_step.fcrm_onboarding_tags_lists_step table tbody tr td:last-child{border-right:none}.fcrm_onboarding_container .fcrm_onboarding_step.fcrm_onboarding_tags_lists_step table tbody tr td.action_td_col .el-button{background:none;border:none;color:var(--fc-secondary-text);padding:10px 0;width:100%;height:auto;font-size:15px}.fcrm_onboarding_step_suggest_box{border-bottom:1px solid var(--fc-primary-border);padding-bottom:20px;margin-bottom:20px}.fcrm_onboarding_step_suggest_box--header{margin-bottom:12px;display:flex;flex-direction:column;gap:6px}.fcrm_onboarding_step_suggest_box.email_optin{border-bottom:none;padding-bottom:0;margin-bottom:0}.fcrm_onboarding_step_suggest_box.email_optin label{margin:0 0 4px;display:block;font-weight:500;font-size:16px;line-height:24px;color:var(--fc-primary-text)}.fcrm_onboarding_step_suggest_box.email_optin .fcrm_input_hint{font-weight:400;font-size:14px;line-height:20px;margin:0;color:var(--fc-secondary-text)}.fcrm_onboarding_step_suggest_box.share_essential{background:var(--fc-weak-bg-25);border-radius:var(--fcrm-border-radius-8);padding:12px;border:none}.fcrm_onboarding_footer_actions{display:flex;gap:8px;justify-content:space-between;padding:14px 20px;width:100%;border-top:1px solid var(--fc-primary-border);align-items:center}.fcrm_onboarding_footer_actions .el-button{margin:0}.fcrm_onboarding_footer_actions .el-button.only-text{border:none;border-radius:0;background:none;padding:0;box-shadow:none;text-decoration:none}.fcrm_onboarding_footer_actions .actions-right{display:flex;align-items:center;gap:8px;flex-wrap:wrap}.fcrm_what_we_collect_box{background:var(--fc-primary-bg);border-radius:var(--fcrm-border-radius-8);padding:12px;border:1px solid var(--fc-primary-border)}.fcrm_recommended_plugins{border:1px solid var(--fc-primary-border);border-radius:var(--fcrm-border-radius-8);overflow:hidden;margin-bottom:20px;background:var(--fc-primary-bg)}.fcrm_recommended_plugins__item_head{display:flex;align-items:center;gap:4px;flex-wrap:wrap;margin-bottom:4px}.fcrm_recommended_plugins__badge{background:#c2f5da;color:#0b4627;font-weight:500;font-size:12px;line-height:16px;padding:2px 8px;display:inline-block;border-radius:6px}.fcrm_recommended_plugins__benefit{display:flex;align-items:center;gap:6px;font-weight:400;font-size:12px;line-height:16px;color:var(--fc-secondary-text)}.fcrm_recommended_plugins__benefit .icon{display:block;color:var(--fc-primary-text)}.fcrm_recommended_plugins__benefit .icon svg{display:block;width:16px;height:16px}.fcrm_recommended_plugins__item{padding:16px 50px 16px 16px;position:relative}.fcrm_recommended_plugins__item+.fcrm_recommended_plugins__item{border-top:1px solid var(--fc-primary-border)}.fcrm_recommended_plugins__item_title{margin:0;font-weight:500;font-size:16px;line-height:24px}.fcrm_recommended_plugins__item_description{color:var(--fc-secondary-text);margin:0 0 12px;font-weight:400;font-size:13px;line-height:20px}.fcrm_recommended_plugins .el-checkbox{position:absolute;top:18px;right:18px} diff --git a/wp-content/plugins/fluent-crm/assets/admin/css/style.css b/wp-content/plugins/fluent-crm/assets/admin/css/style.css new file mode 100644 index 0000000..5a1feca --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/admin/css/style.css @@ -0,0 +1,35 @@ +@layer element-plus{:root{--el-color-white:#fff;--el-color-black:#000;--el-color-primary-rgb:64, 158, 255;--el-color-success-rgb:103, 194, 58;--el-color-warning-rgb:230, 162, 60;--el-color-danger-rgb:245, 108, 108;--el-color-error-rgb:245, 108, 108;--el-color-info-rgb:144, 147, 153;--el-font-size-extra-large:20px;--el-font-size-large:18px;--el-font-size-medium:16px;--el-font-size-base:14px;--el-font-size-small:13px;--el-font-size-extra-small:12px;--el-font-family:"Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "微软雅黑", Arial, sans-serif;--el-font-weight-primary:500;--el-font-line-height-primary:24px;--el-index-normal:1;--el-index-top:1000;--el-index-popper:2000;--el-border-radius-base:4px;--el-border-radius-small:2px;--el-border-radius-round:20px;--el-border-radius-circle:100%;--el-transition-duration:.3s;--el-transition-duration-fast:.2s;--el-transition-function-ease-in-out-bezier:cubic-bezier(.645, .045, .355, 1);--el-transition-function-fast-bezier:cubic-bezier(.23, 1, .32, 1);--el-transition-all:all var(--el-transition-duration) var(--el-transition-function-ease-in-out-bezier);--el-transition-fade:opacity var(--el-transition-duration) var(--el-transition-function-fast-bezier);--el-transition-md-fade:transform var(--el-transition-duration) var(--el-transition-function-fast-bezier), opacity var(--el-transition-duration) var(--el-transition-function-fast-bezier);--el-transition-fade-linear:opacity var(--el-transition-duration-fast) linear;--el-transition-border:border-color var(--el-transition-duration-fast) var(--el-transition-function-ease-in-out-bezier);--el-transition-box-shadow:box-shadow var(--el-transition-duration-fast) var(--el-transition-function-ease-in-out-bezier);--el-transition-color:color var(--el-transition-duration-fast) var(--el-transition-function-ease-in-out-bezier);--el-component-size-large:40px;--el-component-size:32px;--el-component-size-small:24px;--lightningcss-light:initial;--lightningcss-dark: ;color-scheme:light;--el-color-primary:#409eff;--el-color-primary-light-3:#79bbff;--el-color-primary-light-5:#a0cfff;--el-color-primary-light-7:#c6e2ff;--el-color-primary-light-8:#d9ecff;--el-color-primary-light-9:#ecf5ff;--el-color-primary-dark-2:#337ecc;--el-color-success:#67c23a;--el-color-success-light-3:#95d475;--el-color-success-light-5:#b3e19d;--el-color-success-light-7:#d1edc4;--el-color-success-light-8:#e1f3d8;--el-color-success-light-9:#f0f9eb;--el-color-success-dark-2:#529b2e;--el-color-warning:#e6a23c;--el-color-warning-light-3:#eebe77;--el-color-warning-light-5:#f3d19e;--el-color-warning-light-7:#f8e3c5;--el-color-warning-light-8:#faecd8;--el-color-warning-light-9:#fdf6ec;--el-color-warning-dark-2:#b88230;--el-color-danger:#f56c6c;--el-color-danger-light-3:#f89898;--el-color-danger-light-5:#fab6b6;--el-color-danger-light-7:#fcd3d3;--el-color-danger-light-8:#fde2e2;--el-color-danger-light-9:#fef0f0;--el-color-danger-dark-2:#c45656;--el-color-error:#f56c6c;--el-color-error-light-3:#f89898;--el-color-error-light-5:#fab6b6;--el-color-error-light-7:#fcd3d3;--el-color-error-light-8:#fde2e2;--el-color-error-light-9:#fef0f0;--el-color-error-dark-2:#c45656;--el-color-info:#909399;--el-color-info-light-3:#b1b3b8;--el-color-info-light-5:#c8c9cc;--el-color-info-light-7:#dedfe0;--el-color-info-light-8:#e9e9eb;--el-color-info-light-9:#f4f4f5;--el-color-info-dark-2:#73767a;--el-bg-color:#fff;--el-bg-color-page:#f2f3f5;--el-bg-color-overlay:#fff;--el-text-color-primary:#303133;--el-text-color-regular:#606266;--el-text-color-secondary:#909399;--el-text-color-placeholder:#a8abb2;--el-text-color-disabled:#c0c4cc;--el-border-color:#dcdfe6;--el-border-color-light:#e4e7ed;--el-border-color-lighter:#ebeef5;--el-border-color-extra-light:#f2f6fc;--el-border-color-dark:#d4d7de;--el-border-color-darker:#cdd0d6;--el-fill-color:#f0f2f5;--el-fill-color-light:#f5f7fa;--el-fill-color-lighter:#fafafa;--el-fill-color-extra-light:#fafcff;--el-fill-color-dark:#ebedf0;--el-fill-color-darker:#e6e8eb;--el-fill-color-blank:#fff;--el-box-shadow:0px 12px 32px 4px #0000000a, 0px 8px 20px #00000014;--el-box-shadow-light:0px 0px 12px #0000001f;--el-box-shadow-lighter:0px 0px 6px #0000001f;--el-box-shadow-dark:0px 16px 48px 16px #00000014, 0px 12px 32px #0000001f, 0px 8px 16px -8px #00000029;--el-disabled-bg-color:var(--el-fill-color-light);--el-disabled-text-color:var(--el-text-color-placeholder);--el-disabled-border-color:var(--el-border-color-light);--el-overlay-color:#000c;--el-overlay-color-light:#000000b3;--el-overlay-color-lighter:#00000080;--el-mask-color:#ffffffe6;--el-mask-color-extra-light:#ffffff4d;--el-border-width:1px;--el-border-style:solid;--el-border-color-hover:var(--el-text-color-disabled);--el-border:var(--el-border-width) var(--el-border-style) var(--el-border-color);--el-svg-monochrome-grey:var(--el-border-color)}.fade-in-linear-enter-active,.fade-in-linear-leave-active{transition:var(--el-transition-fade-linear)}.fade-in-linear-enter-from,.fade-in-linear-leave-to{opacity:0}.el-fade-in-linear-enter-active,.el-fade-in-linear-leave-active{transition:var(--el-transition-fade-linear)}.el-fade-in-linear-enter-from,.el-fade-in-linear-leave-to{opacity:0}.el-fade-in-enter-active,.el-fade-in-leave-active{transition:all var(--el-transition-duration) cubic-bezier(.55,0,.1,1)}.el-fade-in-enter-from,.el-fade-in-leave-active{opacity:0}.el-zoom-in-center-enter-active,.el-zoom-in-center-leave-active{transition:all var(--el-transition-duration) cubic-bezier(.55,0,.1,1)}.el-zoom-in-center-enter-from,.el-zoom-in-center-leave-active{opacity:0;transform:scaleX(0)}.el-zoom-in-top-enter-active,.el-zoom-in-top-leave-active{opacity:1;transition:var(--el-transition-md-fade);transform-origin:top;transform:scaleY(1)}.el-zoom-in-top-enter-active[data-popper-placement^=top],.el-zoom-in-top-leave-active[data-popper-placement^=top]{transform-origin:bottom}.el-zoom-in-top-enter-from,.el-zoom-in-top-leave-active{opacity:0;transform:scaleY(0)}.el-zoom-in-bottom-enter-active,.el-zoom-in-bottom-leave-active{opacity:1;transition:var(--el-transition-md-fade);transform-origin:bottom;transform:scaleY(1)}.el-zoom-in-bottom-enter-from,.el-zoom-in-bottom-leave-active{opacity:0;transform:scaleY(0)}.el-zoom-in-left-enter-active,.el-zoom-in-left-leave-active{opacity:1;transition:var(--el-transition-md-fade);transform-origin:0 0;transform:scale(1)}.el-zoom-in-left-enter-from,.el-zoom-in-left-leave-active{opacity:0;transform:scale(.45)}.collapse-transition{transition:var(--el-transition-duration) height ease-in-out,var(--el-transition-duration) padding-top ease-in-out,var(--el-transition-duration) padding-bottom ease-in-out}.el-collapse-transition-leave-active,.el-collapse-transition-enter-active{transition:var(--el-transition-duration) max-height ease-in-out,var(--el-transition-duration) padding-top ease-in-out,var(--el-transition-duration) padding-bottom ease-in-out}.horizontal-collapse-transition{transition:var(--el-transition-duration) width ease-in-out,var(--el-transition-duration) padding-left ease-in-out,var(--el-transition-duration) padding-right ease-in-out}.el-list-enter-active,.el-list-leave-active{transition:all 1s}.el-list-enter-from,.el-list-leave-to{opacity:0;transform:translateY(-30px)}.el-list-leave-active{position:absolute!important}.el-opacity-transition{transition:opacity var(--el-transition-duration) cubic-bezier(.55,0,.1,1)}.el-icon--right{margin-left:5px}.el-icon--left{margin-right:5px}@keyframes rotating{0%{transform:rotate(0)}to{transform:rotate(360deg)}}.el-icon{--color:inherit;fill:currentColor;width:1em;height:1em;color:var(--color);line-height:1em;font-size:inherit;justify-content:center;align-items:center;display:inline-flex;position:relative}.el-icon.is-loading{animation:2s linear infinite rotating}.el-icon svg{width:1em;height:1em}}@layer element-plus{.el-overlay{z-index:2000;background-color:var(--el-overlay-color-lighter);height:100%;position:fixed;top:0;bottom:0;left:0;right:0;overflow:auto}.el-overlay .el-overlay-root{height:0}}@layer element-plus{:root{--el-popup-modal-bg-color:var(--el-color-black);--el-popup-modal-opacity:.5}.v-modal-enter{animation:v-modal-in var(--el-transition-duration-fast) ease}.v-modal-leave{animation:v-modal-out var(--el-transition-duration-fast) ease forwards}@keyframes v-modal-in{0%{opacity:0}}@keyframes v-modal-out{to{opacity:0}}.v-modal{width:100%;height:100%;opacity:var(--el-popup-modal-opacity);background:var(--el-popup-modal-bg-color);position:fixed;top:0;left:0}.el-popup-parent--hidden{overflow:hidden}.el-dialog{--el-dialog-width:50%;--el-dialog-margin-top:15vh;--el-dialog-bg-color:var(--el-bg-color);--el-dialog-box-shadow:var(--el-box-shadow);--el-dialog-title-font-size:var(--el-font-size-large);--el-dialog-content-font-size:14px;--el-dialog-font-line-height:var(--el-font-line-height-primary);--el-dialog-padding-primary:16px;--el-dialog-border-radius:var(--el-border-radius-base);margin:var(--el-dialog-margin-top,15vh) auto 50px;background:var(--el-dialog-bg-color);border-radius:var(--el-dialog-border-radius);box-shadow:var(--el-dialog-box-shadow);box-sizing:border-box;padding:var(--el-dialog-padding-primary);width:var(--el-dialog-width,50%);overflow-wrap:break-word;position:relative}.el-dialog:focus{outline:none!important}.el-dialog.is-align-center{margin:auto}.el-dialog.is-fullscreen{--el-dialog-width:100%;--el-dialog-margin-top:0;border-radius:0;height:100%;margin-bottom:0;overflow:auto}.el-dialog__wrapper{margin:0;position:fixed;top:0;bottom:0;left:0;right:0;overflow:auto}.el-dialog.is-draggable .el-dialog__header{cursor:move;-webkit-user-select:none;user-select:none}.el-dialog__header{padding-bottom:var(--el-dialog-padding-primary)}.el-dialog__header.show-close{padding-right:calc(var(--el-dialog-padding-primary) + var(--el-message-close-size,16px))}.el-dialog__headerbtn{cursor:pointer;width:48px;height:48px;font-size:var(--el-message-close-size,16px);background:0 0;border:none;outline:none;padding:0;position:absolute;top:0;right:0}.el-dialog__headerbtn .el-dialog__close{color:var(--el-color-info);font-size:inherit}.el-dialog__headerbtn:focus .el-dialog__close,.el-dialog__headerbtn:hover .el-dialog__close{color:var(--el-color-primary)}.el-dialog__title{line-height:var(--el-dialog-font-line-height);font-size:var(--el-dialog-title-font-size);color:var(--el-text-color-primary)}.el-dialog__body{color:var(--el-text-color-regular);font-size:var(--el-dialog-content-font-size)}.el-dialog__footer{padding-top:var(--el-dialog-padding-primary);text-align:right;box-sizing:border-box}.el-dialog--center{text-align:center}.el-dialog--center .el-dialog__body{text-align:initial}.el-dialog--center .el-dialog__footer{text-align:inherit}.el-modal-dialog.is-penetrable{pointer-events:none}.el-modal-dialog.is-penetrable .el-dialog{pointer-events:auto}.el-overlay-dialog{position:fixed;top:0;bottom:0;left:0;right:0;overflow:auto}.el-overlay-dialog.is-closing .el-dialog{pointer-events:none}.dialog-fade-enter-active{animation:modal-fade-in var(--el-transition-duration)}.dialog-fade-enter-active .el-overlay-dialog{animation:dialog-fade-in var(--el-transition-duration)}.dialog-fade-leave-active{animation:modal-fade-out var(--el-transition-duration)}.dialog-fade-leave-active .el-overlay-dialog{animation:dialog-fade-out var(--el-transition-duration)}@keyframes dialog-fade-in{0%{opacity:0;transform:translateY(-20px)}to{opacity:1;transform:translate(0)}}@keyframes dialog-fade-out{0%{opacity:1;transform:translate(0)}to{opacity:0;transform:translateY(-20px)}}@keyframes modal-fade-in{0%{opacity:0}to{opacity:1}}@keyframes modal-fade-out{0%{opacity:1}to{opacity:0}}}@layer element-plus{.el-button{--el-button-font-weight:var(--el-font-weight-primary);--el-button-border-color:var(--el-border-color);--el-button-bg-color:var(--el-fill-color-blank);--el-button-text-color:var(--el-text-color-regular);--el-button-disabled-text-color:var(--el-disabled-text-color);--el-button-disabled-bg-color:var(--el-fill-color-blank);--el-button-disabled-border-color:var(--el-border-color-light);--el-button-divide-border-color:#ffffff80;--el-button-hover-text-color:var(--el-color-primary);--el-button-hover-bg-color:var(--el-color-primary-light-9);--el-button-hover-border-color:var(--el-color-primary-light-7);--el-button-active-text-color:var(--el-button-hover-text-color);--el-button-active-border-color:var(--el-color-primary);--el-button-active-bg-color:var(--el-button-hover-bg-color);--el-button-outline-color:var(--el-color-primary-light-5);--el-button-hover-link-text-color:var(--el-text-color-secondary);--el-button-active-color:var(--el-text-color-primary);white-space:nowrap;cursor:pointer;height:32px;color:var(--el-button-text-color);text-align:center;box-sizing:border-box;line-height:1;font-weight:var(--el-button-font-weight);-webkit-user-select:none;user-select:none;vertical-align:middle;-webkit-appearance:none;background-color:var(--el-button-bg-color);border:var(--el-border);border-color:var(--el-button-border-color);outline:none;justify-content:center;align-items:center;transition:all .1s;display:inline-flex}.el-button:hover{color:var(--el-button-hover-text-color);border-color:var(--el-button-hover-border-color);background-color:var(--el-button-hover-bg-color);outline:none}.el-button:active{color:var(--el-button-active-text-color);border-color:var(--el-button-active-border-color);background-color:var(--el-button-active-bg-color);outline:none}.el-button:focus-visible{outline:2px solid var(--el-button-outline-color);outline-offset:1px;transition:outline-offset,outline}.el-button>span{align-items:center;display:inline-flex}.el-button+.el-button{margin-left:12px}.el-button{font-size:var(--el-font-size-base);border-radius:var(--el-border-radius-base);padding:8px 15px}.el-button.is-round{padding:8px 15px}.el-button::-moz-focus-inner{border:0}.el-button [class*=el-icon]+span{margin-left:6px}.el-button [class*=el-icon] svg{vertical-align:bottom}.el-button.is-plain{--el-button-hover-text-color:var(--el-color-primary);--el-button-hover-bg-color:var(--el-fill-color-blank);--el-button-hover-border-color:var(--el-color-primary)}.el-button.is-active{color:var(--el-button-active-text-color);border-color:var(--el-button-active-border-color);background-color:var(--el-button-active-bg-color);outline:none}.el-button.is-disabled,.el-button.is-disabled:hover{color:var(--el-button-disabled-text-color);cursor:not-allowed;background-image:none;background-color:var(--el-button-disabled-bg-color);border-color:var(--el-button-disabled-border-color)}.el-button.is-loading{pointer-events:none;position:relative}.el-button.is-loading:before{z-index:1;pointer-events:none;content:"";border-radius:inherit;background-color:var(--el-mask-color-extra-light);position:absolute;top:-1px;bottom:-1px;left:-1px;right:-1px}.el-button.is-round{border-radius:var(--el-border-radius-round)}.el-button.is-dashed{--el-button-hover-text-color:var(--el-color-primary);--el-button-hover-bg-color:var(--el-fill-color-blank);--el-button-hover-border-color:var(--el-color-primary);border-style:dashed}.el-button.is-circle{border-radius:50%;width:32px;padding:8px}.el-button.is-text{color:var(--el-button-text-color);background-color:#0000;border:0 solid #0000}.el-button.is-text.is-disabled{color:var(--el-button-disabled-text-color);background-color:#0000!important}.el-button.is-text:not(.is-disabled):hover{background-color:var(--el-fill-color-light)}.el-button.is-text:not(.is-disabled):focus-visible{outline:2px solid var(--el-button-outline-color);outline-offset:1px;transition:outline-offset,outline}.el-button.is-text:not(.is-disabled):active{background-color:var(--el-fill-color)}.el-button.is-text:not(.is-disabled).is-has-bg{background-color:var(--el-fill-color-light)}.el-button.is-text:not(.is-disabled).is-has-bg:hover{background-color:var(--el-fill-color)}.el-button.is-text:not(.is-disabled).is-has-bg:active{background-color:var(--el-fill-color-dark)}.el-button__text--expand{letter-spacing:.3em;margin-right:-.3em}.el-button.is-link{color:var(--el-button-text-color);background:0 0;border-color:#0000;height:auto;padding:2px}.el-button.is-link:hover{color:var(--el-button-hover-link-text-color)}.el-button.is-link.is-disabled{color:var(--el-button-disabled-text-color);background-color:#0000!important;border-color:#0000!important}.el-button.is-link:not(.is-disabled):hover{background-color:#0000;border-color:#0000}.el-button.is-link:not(.is-disabled):active{color:var(--el-button-active-color);background-color:#0000;border-color:#0000}.el-button--text{color:var(--el-color-primary);background:0 0;border-color:#0000;padding-left:0;padding-right:0}.el-button--text.is-disabled{color:var(--el-button-disabled-text-color);background-color:#0000!important;border-color:#0000!important}.el-button--text:not(.is-disabled):hover{color:var(--el-color-primary-light-3);background-color:#0000;border-color:#0000}.el-button--text:not(.is-disabled):active{color:var(--el-color-primary-dark-2);background-color:#0000;border-color:#0000}.el-button__link--expand{letter-spacing:.3em;margin-right:-.3em}.el-button--primary{--el-button-text-color:var(--el-color-white);--el-button-bg-color:var(--el-color-primary);--el-button-border-color:var(--el-color-primary);--el-button-outline-color:var(--el-color-primary-light-5);--el-button-active-color:var(--el-color-primary-dark-2);--el-button-hover-text-color:var(--el-color-white);--el-button-hover-link-text-color:var(--el-color-primary-light-5);--el-button-hover-bg-color:var(--el-color-primary-light-3);--el-button-hover-border-color:var(--el-color-primary-light-3);--el-button-active-bg-color:var(--el-color-primary-dark-2);--el-button-active-border-color:var(--el-color-primary-dark-2);--el-button-disabled-text-color:var(--el-color-white);--el-button-disabled-bg-color:var(--el-color-primary-light-5);--el-button-disabled-border-color:var(--el-color-primary-light-5)}.el-button--primary.is-plain,.el-button--primary.is-text,.el-button--primary.is-link{--el-button-text-color:var(--el-color-primary);--el-button-bg-color:var(--el-color-primary-light-9);--el-button-border-color:var(--el-color-primary-light-5);--el-button-hover-text-color:var(--el-color-white);--el-button-hover-bg-color:var(--el-color-primary);--el-button-hover-border-color:var(--el-color-primary);--el-button-active-text-color:var(--el-color-white)}.el-button--primary.is-plain.is-disabled,.el-button--primary.is-plain.is-disabled:hover,.el-button--primary.is-plain.is-disabled:focus,.el-button--primary.is-plain.is-disabled:active,.el-button--primary.is-text.is-disabled,.el-button--primary.is-text.is-disabled:hover,.el-button--primary.is-text.is-disabled:focus,.el-button--primary.is-text.is-disabled:active,.el-button--primary.is-link.is-disabled,.el-button--primary.is-link.is-disabled:hover,.el-button--primary.is-link.is-disabled:focus,.el-button--primary.is-link.is-disabled:active{color:var(--el-color-primary-light-5);background-color:var(--el-color-primary-light-9);border-color:var(--el-color-primary-light-8)}.el-button--primary.is-dashed{--el-button-text-color:var(--el-color-primary);--el-button-bg-color:var(--el-color-primary-light-9);--el-button-border-color:var(--el-color-primary-light-5);--el-button-hover-text-color:var(--el-color-primary);--el-button-hover-bg-color:var(--el-color-primary-light-9);--el-button-hover-border-color:var(--el-color-primary-light-3);--el-button-active-text-color:var(--el-color-primary-dark-2);--el-button-active-bg-color:var(--el-color-primary-light-9);--el-button-active-border-color:var(--el-color-primary-dark-2)}.el-button--primary.is-dashed.is-disabled,.el-button--primary.is-dashed.is-disabled:hover,.el-button--primary.is-dashed.is-disabled:focus,.el-button--primary.is-dashed.is-disabled:active{color:var(--el-color-primary-light-5);background-color:var(--el-color-primary-light-9);border-color:var(--el-color-primary-light-8)}.el-button--success{--el-button-text-color:var(--el-color-white);--el-button-bg-color:var(--el-color-success);--el-button-border-color:var(--el-color-success);--el-button-outline-color:var(--el-color-success-light-5);--el-button-active-color:var(--el-color-success-dark-2);--el-button-hover-text-color:var(--el-color-white);--el-button-hover-link-text-color:var(--el-color-success-light-5);--el-button-hover-bg-color:var(--el-color-success-light-3);--el-button-hover-border-color:var(--el-color-success-light-3);--el-button-active-bg-color:var(--el-color-success-dark-2);--el-button-active-border-color:var(--el-color-success-dark-2);--el-button-disabled-text-color:var(--el-color-white);--el-button-disabled-bg-color:var(--el-color-success-light-5);--el-button-disabled-border-color:var(--el-color-success-light-5)}.el-button--success.is-plain,.el-button--success.is-text,.el-button--success.is-link{--el-button-text-color:var(--el-color-success);--el-button-bg-color:var(--el-color-success-light-9);--el-button-border-color:var(--el-color-success-light-5);--el-button-hover-text-color:var(--el-color-white);--el-button-hover-bg-color:var(--el-color-success);--el-button-hover-border-color:var(--el-color-success);--el-button-active-text-color:var(--el-color-white)}.el-button--success.is-plain.is-disabled,.el-button--success.is-plain.is-disabled:hover,.el-button--success.is-plain.is-disabled:focus,.el-button--success.is-plain.is-disabled:active,.el-button--success.is-text.is-disabled,.el-button--success.is-text.is-disabled:hover,.el-button--success.is-text.is-disabled:focus,.el-button--success.is-text.is-disabled:active,.el-button--success.is-link.is-disabled,.el-button--success.is-link.is-disabled:hover,.el-button--success.is-link.is-disabled:focus,.el-button--success.is-link.is-disabled:active{color:var(--el-color-success-light-5);background-color:var(--el-color-success-light-9);border-color:var(--el-color-success-light-8)}.el-button--success.is-dashed{--el-button-text-color:var(--el-color-success);--el-button-bg-color:var(--el-color-success-light-9);--el-button-border-color:var(--el-color-success-light-5);--el-button-hover-text-color:var(--el-color-success);--el-button-hover-bg-color:var(--el-color-success-light-9);--el-button-hover-border-color:var(--el-color-success-light-3);--el-button-active-text-color:var(--el-color-success-dark-2);--el-button-active-bg-color:var(--el-color-success-light-9);--el-button-active-border-color:var(--el-color-success-dark-2)}.el-button--success.is-dashed.is-disabled,.el-button--success.is-dashed.is-disabled:hover,.el-button--success.is-dashed.is-disabled:focus,.el-button--success.is-dashed.is-disabled:active{color:var(--el-color-success-light-5);background-color:var(--el-color-success-light-9);border-color:var(--el-color-success-light-8)}.el-button--warning{--el-button-text-color:var(--el-color-white);--el-button-bg-color:var(--el-color-warning);--el-button-border-color:var(--el-color-warning);--el-button-outline-color:var(--el-color-warning-light-5);--el-button-active-color:var(--el-color-warning-dark-2);--el-button-hover-text-color:var(--el-color-white);--el-button-hover-link-text-color:var(--el-color-warning-light-5);--el-button-hover-bg-color:var(--el-color-warning-light-3);--el-button-hover-border-color:var(--el-color-warning-light-3);--el-button-active-bg-color:var(--el-color-warning-dark-2);--el-button-active-border-color:var(--el-color-warning-dark-2);--el-button-disabled-text-color:var(--el-color-white);--el-button-disabled-bg-color:var(--el-color-warning-light-5);--el-button-disabled-border-color:var(--el-color-warning-light-5)}.el-button--warning.is-plain,.el-button--warning.is-text,.el-button--warning.is-link{--el-button-text-color:var(--el-color-warning);--el-button-bg-color:var(--el-color-warning-light-9);--el-button-border-color:var(--el-color-warning-light-5);--el-button-hover-text-color:var(--el-color-white);--el-button-hover-bg-color:var(--el-color-warning);--el-button-hover-border-color:var(--el-color-warning);--el-button-active-text-color:var(--el-color-white)}.el-button--warning.is-plain.is-disabled,.el-button--warning.is-plain.is-disabled:hover,.el-button--warning.is-plain.is-disabled:focus,.el-button--warning.is-plain.is-disabled:active,.el-button--warning.is-text.is-disabled,.el-button--warning.is-text.is-disabled:hover,.el-button--warning.is-text.is-disabled:focus,.el-button--warning.is-text.is-disabled:active,.el-button--warning.is-link.is-disabled,.el-button--warning.is-link.is-disabled:hover,.el-button--warning.is-link.is-disabled:focus,.el-button--warning.is-link.is-disabled:active{color:var(--el-color-warning-light-5);background-color:var(--el-color-warning-light-9);border-color:var(--el-color-warning-light-8)}.el-button--warning.is-dashed{--el-button-text-color:var(--el-color-warning);--el-button-bg-color:var(--el-color-warning-light-9);--el-button-border-color:var(--el-color-warning-light-5);--el-button-hover-text-color:var(--el-color-warning);--el-button-hover-bg-color:var(--el-color-warning-light-9);--el-button-hover-border-color:var(--el-color-warning-light-3);--el-button-active-text-color:var(--el-color-warning-dark-2);--el-button-active-bg-color:var(--el-color-warning-light-9);--el-button-active-border-color:var(--el-color-warning-dark-2)}.el-button--warning.is-dashed.is-disabled,.el-button--warning.is-dashed.is-disabled:hover,.el-button--warning.is-dashed.is-disabled:focus,.el-button--warning.is-dashed.is-disabled:active{color:var(--el-color-warning-light-5);background-color:var(--el-color-warning-light-9);border-color:var(--el-color-warning-light-8)}.el-button--danger{--el-button-text-color:var(--el-color-white);--el-button-bg-color:var(--el-color-danger);--el-button-border-color:var(--el-color-danger);--el-button-outline-color:var(--el-color-danger-light-5);--el-button-active-color:var(--el-color-danger-dark-2);--el-button-hover-text-color:var(--el-color-white);--el-button-hover-link-text-color:var(--el-color-danger-light-5);--el-button-hover-bg-color:var(--el-color-danger-light-3);--el-button-hover-border-color:var(--el-color-danger-light-3);--el-button-active-bg-color:var(--el-color-danger-dark-2);--el-button-active-border-color:var(--el-color-danger-dark-2);--el-button-disabled-text-color:var(--el-color-white);--el-button-disabled-bg-color:var(--el-color-danger-light-5);--el-button-disabled-border-color:var(--el-color-danger-light-5)}.el-button--danger.is-plain,.el-button--danger.is-text,.el-button--danger.is-link{--el-button-text-color:var(--el-color-danger);--el-button-bg-color:var(--el-color-danger-light-9);--el-button-border-color:var(--el-color-danger-light-5);--el-button-hover-text-color:var(--el-color-white);--el-button-hover-bg-color:var(--el-color-danger);--el-button-hover-border-color:var(--el-color-danger);--el-button-active-text-color:var(--el-color-white)}.el-button--danger.is-plain.is-disabled,.el-button--danger.is-plain.is-disabled:hover,.el-button--danger.is-plain.is-disabled:focus,.el-button--danger.is-plain.is-disabled:active,.el-button--danger.is-text.is-disabled,.el-button--danger.is-text.is-disabled:hover,.el-button--danger.is-text.is-disabled:focus,.el-button--danger.is-text.is-disabled:active,.el-button--danger.is-link.is-disabled,.el-button--danger.is-link.is-disabled:hover,.el-button--danger.is-link.is-disabled:focus,.el-button--danger.is-link.is-disabled:active{color:var(--el-color-danger-light-5);background-color:var(--el-color-danger-light-9);border-color:var(--el-color-danger-light-8)}.el-button--danger.is-dashed{--el-button-text-color:var(--el-color-danger);--el-button-bg-color:var(--el-color-danger-light-9);--el-button-border-color:var(--el-color-danger-light-5);--el-button-hover-text-color:var(--el-color-danger);--el-button-hover-bg-color:var(--el-color-danger-light-9);--el-button-hover-border-color:var(--el-color-danger-light-3);--el-button-active-text-color:var(--el-color-danger-dark-2);--el-button-active-bg-color:var(--el-color-danger-light-9);--el-button-active-border-color:var(--el-color-danger-dark-2)}.el-button--danger.is-dashed.is-disabled,.el-button--danger.is-dashed.is-disabled:hover,.el-button--danger.is-dashed.is-disabled:focus,.el-button--danger.is-dashed.is-disabled:active{color:var(--el-color-danger-light-5);background-color:var(--el-color-danger-light-9);border-color:var(--el-color-danger-light-8)}.el-button--info{--el-button-text-color:var(--el-color-white);--el-button-bg-color:var(--el-color-info);--el-button-border-color:var(--el-color-info);--el-button-outline-color:var(--el-color-info-light-5);--el-button-active-color:var(--el-color-info-dark-2);--el-button-hover-text-color:var(--el-color-white);--el-button-hover-link-text-color:var(--el-color-info-light-5);--el-button-hover-bg-color:var(--el-color-info-light-3);--el-button-hover-border-color:var(--el-color-info-light-3);--el-button-active-bg-color:var(--el-color-info-dark-2);--el-button-active-border-color:var(--el-color-info-dark-2);--el-button-disabled-text-color:var(--el-color-white);--el-button-disabled-bg-color:var(--el-color-info-light-5);--el-button-disabled-border-color:var(--el-color-info-light-5)}.el-button--info.is-plain,.el-button--info.is-text,.el-button--info.is-link{--el-button-text-color:var(--el-color-info);--el-button-bg-color:var(--el-color-info-light-9);--el-button-border-color:var(--el-color-info-light-5);--el-button-hover-text-color:var(--el-color-white);--el-button-hover-bg-color:var(--el-color-info);--el-button-hover-border-color:var(--el-color-info);--el-button-active-text-color:var(--el-color-white)}.el-button--info.is-plain.is-disabled,.el-button--info.is-plain.is-disabled:hover,.el-button--info.is-plain.is-disabled:focus,.el-button--info.is-plain.is-disabled:active,.el-button--info.is-text.is-disabled,.el-button--info.is-text.is-disabled:hover,.el-button--info.is-text.is-disabled:focus,.el-button--info.is-text.is-disabled:active,.el-button--info.is-link.is-disabled,.el-button--info.is-link.is-disabled:hover,.el-button--info.is-link.is-disabled:focus,.el-button--info.is-link.is-disabled:active{color:var(--el-color-info-light-5);background-color:var(--el-color-info-light-9);border-color:var(--el-color-info-light-8)}.el-button--info.is-dashed{--el-button-text-color:var(--el-color-info);--el-button-bg-color:var(--el-color-info-light-9);--el-button-border-color:var(--el-color-info-light-5);--el-button-hover-text-color:var(--el-color-info);--el-button-hover-bg-color:var(--el-color-info-light-9);--el-button-hover-border-color:var(--el-color-info-light-3);--el-button-active-text-color:var(--el-color-info-dark-2);--el-button-active-bg-color:var(--el-color-info-light-9);--el-button-active-border-color:var(--el-color-info-dark-2)}.el-button--info.is-dashed.is-disabled,.el-button--info.is-dashed.is-disabled:hover,.el-button--info.is-dashed.is-disabled:focus,.el-button--info.is-dashed.is-disabled:active{color:var(--el-color-info-light-5);background-color:var(--el-color-info-light-9);border-color:var(--el-color-info-light-8)}.el-button--large{--el-button-size:40px;height:var(--el-button-size)}.el-button--large [class*=el-icon]+span{margin-left:8px}.el-button--large{font-size:var(--el-font-size-base);border-radius:var(--el-border-radius-base);padding:12px 19px}.el-button--large.is-round{padding:12px 19px}.el-button--large.is-circle{width:var(--el-button-size);padding:12px}.el-button--small{--el-button-size:24px;height:var(--el-button-size)}.el-button--small [class*=el-icon]+span{margin-left:4px}.el-button--small{border-radius:calc(var(--el-border-radius-base) - 1px);padding:5px 11px;font-size:12px}.el-button--small.is-round{padding:5px 11px}.el-button--small.is-circle{width:var(--el-button-size);padding:5px}}@layer element-plus{.el-textarea{--el-input-text-color:var(--el-text-color-regular);--el-input-border:var(--el-border);--el-input-hover-border:var(--el-border-color-hover);--el-input-focus-border:var(--el-color-primary);--el-input-transparent-border:0 0 0 1px transparent inset;--el-input-border-color:var(--el-border-color);--el-input-border-radius:var(--el-border-radius-base);--el-input-bg-color:var(--el-fill-color-blank);--el-input-icon-color:var(--el-text-color-placeholder);--el-input-placeholder-color:var(--el-text-color-placeholder);--el-input-hover-border-color:var(--el-border-color-hover);--el-input-clear-hover-color:var(--el-text-color-secondary);--el-input-focus-border-color:var(--el-color-primary);--el-input-width:100%;vertical-align:bottom;width:100%;font-size:var(--el-font-size-base);display:inline-block;position:relative}.el-textarea__inner{resize:vertical;box-sizing:border-box;width:100%;line-height:1.5;font-size:inherit;color:var(--el-input-text-color,var(--el-text-color-regular));background-color:var(--el-input-bg-color,var(--el-fill-color-blank));-webkit-appearance:none;box-shadow:0 0 0 1px var(--el-input-border-color,var(--el-border-color)) inset;border-radius:var(--el-input-border-radius,var(--el-border-radius-base));transition:var(--el-transition-box-shadow);background-image:none;border:none;padding:5px 11px;font-family:inherit;display:block;position:relative}.el-textarea__inner.is-clearable{padding:5px 26px 5px 11px}.el-textarea__inner::placeholder{color:var(--el-input-placeholder-color,var(--el-text-color-placeholder))}.el-textarea__inner:hover{box-shadow:0 0 0 1px var(--el-input-hover-border-color) inset}.el-textarea__inner:focus{box-shadow:0 0 0 1px var(--el-input-focus-border-color) inset;outline:none}.el-textarea__clear{color:var(--el-input-icon-color);cursor:pointer;font-size:14px;position:absolute;top:15px;right:11px;transform:translateY(-50%)}.el-textarea__clear:hover{color:var(--el-input-clear-hover-color)}.el-textarea .el-input__count{color:var(--el-color-info);background:var(--el-fill-color-blank);font-size:12px;line-height:14px;position:absolute;bottom:5px;right:10px}.el-textarea .el-input__count.is-outside{top:100%;right:0;bottom:unset;background:0 0;padding-top:2px;line-height:1;position:absolute}.el-textarea.is-disabled .el-textarea__inner{box-shadow:0 0 0 1px var(--el-disabled-border-color) inset;background-color:var(--el-disabled-bg-color);color:var(--el-disabled-text-color);cursor:not-allowed}.el-textarea.is-disabled .el-textarea__inner::placeholder{color:var(--el-text-color-placeholder)}.el-textarea.is-exceed .el-textarea__inner{box-shadow:0 0 0 1px var(--el-color-danger) inset}.el-textarea.is-exceed .el-input__count{color:var(--el-color-danger)}.el-input{--el-input-text-color:var(--el-text-color-regular);--el-input-border:var(--el-border);--el-input-hover-border:var(--el-border-color-hover);--el-input-focus-border:var(--el-color-primary);--el-input-transparent-border:0 0 0 1px transparent inset;--el-input-border-color:var(--el-border-color);--el-input-border-radius:var(--el-border-radius-base);--el-input-bg-color:var(--el-fill-color-blank);--el-input-icon-color:var(--el-text-color-placeholder);--el-input-placeholder-color:var(--el-text-color-placeholder);--el-input-hover-border-color:var(--el-border-color-hover);--el-input-clear-hover-color:var(--el-text-color-secondary);--el-input-focus-border-color:var(--el-color-primary);--el-input-width:100%;--el-input-height:var(--el-component-size);font-size:var(--el-font-size-base);width:var(--el-input-width);line-height:var(--el-input-height);box-sizing:border-box;vertical-align:middle;display:inline-flex;position:relative}.el-input::-webkit-scrollbar{z-index:11;width:6px}.el-input::-webkit-scrollbar:horizontal{height:6px}.el-input::-webkit-scrollbar-thumb{background:var(--el-text-color-disabled);border-radius:5px;width:6px}.el-input::-webkit-scrollbar-corner{background:var(--el-fill-color-blank)}.el-input::-webkit-scrollbar-track{background:var(--el-fill-color-blank)}.el-input::-webkit-scrollbar-track-piece{background:var(--el-fill-color-blank);width:6px}.el-input .el-input__clear,.el-input .el-input__password{color:var(--el-input-icon-color);cursor:pointer;font-size:14px}.el-input .el-input__clear:hover,.el-input .el-input__password:hover{color:var(--el-input-clear-hover-color)}.el-input .el-input__count{height:100%;color:var(--el-color-info);align-items:center;font-size:12px;display:inline-flex}.el-input .el-input__count .el-input__count-inner{background:var(--el-fill-color-blank);line-height:initial;padding-left:8px;display:inline-block}.el-input .el-input__count.is-outside{height:unset;padding-top:2px;position:absolute;top:100%;right:0}.el-input .el-input__count.is-outside .el-input__count-inner{background:0 0;padding-left:0;line-height:1}.el-input__wrapper{background-color:var(--el-input-bg-color,var(--el-fill-color-blank));border-radius:var(--el-input-border-radius,var(--el-border-radius-base));cursor:text;transition:var(--el-transition-box-shadow);box-shadow:0 0 0 1px var(--el-input-border-color,var(--el-border-color)) inset;background-image:none;flex-grow:1;justify-content:center;align-items:center;padding:1px 11px;display:inline-flex;transform:translate(0)}.el-input__wrapper:hover{box-shadow:0 0 0 1px var(--el-input-hover-border-color) inset}.el-input__wrapper.is-focus{box-shadow:0 0 0 1px var(--el-input-focus-border-color) inset}.el-input{--el-input-inner-height:calc(var(--el-input-height,32px) - 2px)}.el-input__inner{-webkit-appearance:none;width:100%;color:var(--el-input-text-color,var(--el-text-color-regular));font-size:inherit;height:var(--el-input-inner-height);line-height:var(--el-input-inner-height);box-sizing:border-box;background:0 0;border:none;outline:none;flex-grow:1;padding:0}.el-input__inner:focus{outline:none}.el-input__inner::placeholder{color:var(--el-input-placeholder-color,var(--el-text-color-placeholder))}.el-input__inner[type=password]::-ms-reveal{display:none}.el-input__inner[type=number]{line-height:1}.el-input__prefix{white-space:nowrap;height:100%;line-height:var(--el-input-inner-height);text-align:center;color:var(--el-input-icon-color,var(--el-text-color-placeholder));transition:all var(--el-transition-duration);pointer-events:none;flex-wrap:nowrap;flex-shrink:0;display:inline-flex}.el-input__prefix-inner{pointer-events:all;justify-content:center;align-items:center;display:inline-flex}.el-input__prefix-inner>:last-child{margin-right:8px}.el-input__prefix-inner>:first-child,.el-input__prefix-inner>:first-child.el-input__icon{margin-left:0}.el-input__suffix{white-space:nowrap;height:100%;line-height:var(--el-input-inner-height);text-align:center;color:var(--el-input-icon-color,var(--el-text-color-placeholder));transition:all var(--el-transition-duration);pointer-events:none;flex-wrap:nowrap;flex-shrink:0;display:inline-flex}.el-input__suffix-inner{pointer-events:all;justify-content:center;align-items:center;display:inline-flex}.el-input__suffix-inner>:first-child{margin-left:8px}.el-input .el-input__icon{height:inherit;line-height:inherit;transition:all var(--el-transition-duration);justify-content:center;align-items:center;margin-left:8px;display:flex}.el-input .el-input__clear{transition:color var(--el-transition-duration)}.el-input__validateIcon{pointer-events:none}.el-input.is-active .el-input__wrapper{box-shadow:0 0 0 1px var(--el-input-focus-color, ) inset}.el-input.is-disabled{cursor:not-allowed}.el-input.is-disabled .el-input__wrapper{background-color:var(--el-disabled-bg-color);cursor:not-allowed;box-shadow:0 0 0 1px var(--el-disabled-border-color) inset}.el-input.is-disabled .el-input__inner{color:var(--el-disabled-text-color);-webkit-text-fill-color:var(--el-disabled-text-color);cursor:not-allowed}.el-input.is-disabled .el-input__inner::placeholder{color:var(--el-text-color-placeholder)}.el-input.is-disabled .el-input__icon{cursor:not-allowed}.el-input.is-disabled .el-input__prefix-inner,.el-input.is-disabled .el-input__suffix-inner{pointer-events:none}.el-input.is-exceed .el-input__wrapper{box-shadow:0 0 0 1px var(--el-color-danger) inset}.el-input.is-exceed .el-input__suffix .el-input__count{color:var(--el-color-danger)}.el-input--large{--el-input-height:var(--el-component-size-large);font-size:14px}.el-input--large .el-input__wrapper{padding:1px 15px}.el-input--large{--el-input-inner-height:calc(var(--el-input-height,40px) - 2px)}.el-input--small{--el-input-height:var(--el-component-size-small);font-size:12px}.el-input--small .el-input__wrapper{padding:1px 7px}.el-input--small{--el-input-inner-height:calc(var(--el-input-height,24px) - 2px)}.el-input-group{align-items:stretch;width:100%;display:inline-flex}.el-input-group__append,.el-input-group__prepend{background-color:var(--el-fill-color-light);color:var(--el-color-info);border-radius:var(--el-input-border-radius);white-space:nowrap;justify-content:center;align-items:center;min-height:100%;padding:0 20px;display:inline-flex;position:relative}.el-input-group__append:focus,.el-input-group__prepend:focus{outline:none}.el-input-group__append .el-select,.el-input-group__append .el-button,.el-input-group__prepend .el-select,.el-input-group__prepend .el-button{flex:1;margin:0 -20px;display:inline-block}.el-input-group__append button.el-button,.el-input-group__append button.el-button:hover,.el-input-group__append div.el-select .el-select__wrapper,.el-input-group__append div.el-select:hover .el-select__wrapper,.el-input-group__prepend button.el-button,.el-input-group__prepend button.el-button:hover,.el-input-group__prepend div.el-select .el-select__wrapper,.el-input-group__prepend div.el-select:hover .el-select__wrapper{color:inherit;background-color:#0000;border-color:#0000}.el-input-group__append .el-button,.el-input-group__append .el-input,.el-input-group__prepend .el-button,.el-input-group__prepend .el-input{font-size:inherit}.el-input-group__prepend{box-shadow:1px 0 0 0 var(--el-input-border-color) inset,0 1px 0 0 var(--el-input-border-color) inset,0 -1px 0 0 var(--el-input-border-color) inset;border-right:0;border-top-right-radius:0;border-bottom-right-radius:0}.el-input-group__append{box-shadow:0 1px 0 0 var(--el-input-border-color) inset,0 -1px 0 0 var(--el-input-border-color) inset,-1px 0 0 0 var(--el-input-border-color) inset;border-left:0;border-top-left-radius:0;border-bottom-left-radius:0}.el-input-group--prepend>.el-input__wrapper{border-top-left-radius:0;border-bottom-left-radius:0}.el-input-group--prepend .el-input-group__prepend .el-select .el-select__wrapper{box-shadow:1px 0 0 0 var(--el-input-border-color) inset,0 1px 0 0 var(--el-input-border-color) inset,0 -1px 0 0 var(--el-input-border-color) inset;border-top-right-radius:0;border-bottom-right-radius:0}.el-input-group--append>.el-input__wrapper{border-top-right-radius:0;border-bottom-right-radius:0}.el-input-group--append .el-input-group__append .el-select .el-select__wrapper{box-shadow:0 1px 0 0 var(--el-input-border-color) inset,0 -1px 0 0 var(--el-input-border-color) inset,-1px 0 0 0 var(--el-input-border-color) inset;border-top-left-radius:0;border-bottom-left-radius:0}.el-input-hidden{display:none!important}}@layer element-plus{.el-scrollbar{--el-scrollbar-opacity:.3;--el-scrollbar-bg-color:var(--el-text-color-secondary);--el-scrollbar-hover-opacity:.5;--el-scrollbar-hover-bg-color:var(--el-text-color-secondary);height:100%;position:relative;overflow:hidden}.el-scrollbar__wrap{height:100%;overflow:auto}.el-scrollbar__wrap--hidden-default{scrollbar-width:none}.el-scrollbar__wrap--hidden-default::-webkit-scrollbar{display:none}.el-scrollbar__thumb{cursor:pointer;border-radius:inherit;background-color:var(--el-scrollbar-bg-color,var(--el-text-color-secondary));width:0;height:0;transition:var(--el-transition-duration) background-color;opacity:var(--el-scrollbar-opacity,.3);display:block;position:relative}.el-scrollbar__thumb:hover{background-color:var(--el-scrollbar-hover-bg-color,var(--el-text-color-secondary));opacity:var(--el-scrollbar-hover-opacity,.5)}.el-scrollbar__bar{z-index:1;border-radius:4px;position:absolute;bottom:2px;right:2px}.el-scrollbar__bar.is-vertical{width:6px;top:2px}.el-scrollbar__bar.is-vertical>div{width:100%}.el-scrollbar__bar.is-horizontal{height:6px;left:2px}.el-scrollbar__bar.is-horizontal>div{height:100%}.el-scrollbar-fade-enter-active{transition:opacity .34s ease-out}.el-scrollbar-fade-leave-active{transition:opacity .12s ease-out}.el-scrollbar-fade-enter-from,.el-scrollbar-fade-leave-active{opacity:0}}@layer element-plus{.el-popper{--el-popper-border-radius:var(--el-popover-border-radius,4px);--el-popper-bg-color-light:var(--el-bg-color-overlay);--el-popper-bg-color-dark:var(--el-text-color-primary);border-radius:var(--el-popper-border-radius);z-index:2000;overflow-wrap:break-word;word-break:normal;visibility:visible;min-width:10px;padding:5px 11px;font-size:12px;line-height:20px;position:absolute}.el-popper.is-dark{--el-fill-color-blank:var(--el-popper-bg-color-dark);color:var(--el-bg-color);background:var(--el-popper-bg-color-dark);border:1px solid var(--el-text-color-primary)}.el-popper.is-dark>.el-popper__arrow:before{border:1px solid var(--el-text-color-primary);background:var(--el-popper-bg-color-dark);right:0}.el-popper.is-light{--el-fill-color-blank:var(--el-popper-bg-color-light);background:var(--el-popper-bg-color-light);border:1px solid var(--el-border-color-light)}.el-popper.is-light>.el-popper__arrow:before{border:1px solid var(--el-border-color-light);background:var(--el-popper-bg-color-light);right:0}.el-popper.is-pure{padding:0}.el-popper__arrow{z-index:-1;width:10px;height:10px;position:absolute}.el-popper__arrow:before{z-index:-1;content:" ";background:var(--el-text-color-primary);box-sizing:border-box;width:10px;height:10px;position:absolute;transform:rotate(45deg)}.el-popper[data-popper-placement^=top]>.el-popper__arrow{bottom:-5px}.el-popper[data-popper-placement^=top]>.el-popper__arrow:before{border-bottom-right-radius:2px}.el-popper[data-popper-placement^=bottom]>.el-popper__arrow{top:-5px}.el-popper[data-popper-placement^=bottom]>.el-popper__arrow:before{border-top-left-radius:2px}.el-popper[data-popper-placement^=left]>.el-popper__arrow{right:-5px}.el-popper[data-popper-placement^=left]>.el-popper__arrow:before{border-top-right-radius:2px}.el-popper[data-popper-placement^=right]>.el-popper__arrow{left:-5px}.el-popper[data-popper-placement^=right]>.el-popper__arrow:before{border-bottom-left-radius:2px}.el-popper[data-popper-placement^=top]>.el-popper__arrow:before{border-top-color:#0000!important;border-left-color:#0000!important}.el-popper[data-popper-placement^=bottom]>.el-popper__arrow:before{border-bottom-color:#0000!important;border-right-color:#0000!important}.el-popper[data-popper-placement^=left]>.el-popper__arrow:before{border-bottom-color:#0000!important;border-left-color:#0000!important}.el-popper[data-popper-placement^=right]>.el-popper__arrow:before{border-top-color:#0000!important;border-right-color:#0000!important}}@layer element-plus{.el-button-group>.el-button+.el-button{margin-left:0}.el-button-group>.el-button:first-child:last-child{border-top-right-radius:var(--el-border-radius-base);border-bottom-right-radius:var(--el-border-radius-base);border-top-left-radius:var(--el-border-radius-base);border-bottom-left-radius:var(--el-border-radius-base)}.el-button-group>.el-button:first-child:last-child.is-round{border-radius:var(--el-border-radius-round)}.el-button-group>.el-button:first-child:last-child.is-circle{border-radius:50%}.el-button-group>.el-button:not(:first-child):not(:last-child){border-radius:0}.el-button-group>.el-button:hover,.el-button-group>.el-button:focus,.el-button-group>.el-button:active,.el-button-group>.el-button.is-active{z-index:1}.el-button-group--horizontal{vertical-align:middle;display:inline-block}.el-button-group--horizontal:before,.el-button-group--horizontal:after{content:"";display:table}.el-button-group--horizontal:after{clear:both}.el-button-group--horizontal>.el-button{float:left;position:relative}.el-button-group--horizontal>.el-button:first-child{border-top-right-radius:0;border-bottom-right-radius:0}.el-button-group--horizontal>.el-button:last-child{border-top-left-radius:0;border-bottom-left-radius:0}.el-button-group--horizontal>.el-button:not(:last-child){margin-right:-1px}.el-button-group--horizontal .el-button--primary:first-child{border-right-color:var(--el-button-divide-border-color)}.el-button-group--horizontal .el-button--primary:last-child{border-left-color:var(--el-button-divide-border-color)}.el-button-group--horizontal .el-button--primary:not(:first-child):not(:last-child){border-left-color:var(--el-button-divide-border-color);border-right-color:var(--el-button-divide-border-color)}.el-button-group--horizontal .el-button--success:first-child{border-right-color:var(--el-button-divide-border-color)}.el-button-group--horizontal .el-button--success:last-child{border-left-color:var(--el-button-divide-border-color)}.el-button-group--horizontal .el-button--success:not(:first-child):not(:last-child){border-left-color:var(--el-button-divide-border-color);border-right-color:var(--el-button-divide-border-color)}.el-button-group--horizontal .el-button--warning:first-child{border-right-color:var(--el-button-divide-border-color)}.el-button-group--horizontal .el-button--warning:last-child{border-left-color:var(--el-button-divide-border-color)}.el-button-group--horizontal .el-button--warning:not(:first-child):not(:last-child){border-left-color:var(--el-button-divide-border-color);border-right-color:var(--el-button-divide-border-color)}.el-button-group--horizontal .el-button--danger:first-child{border-right-color:var(--el-button-divide-border-color)}.el-button-group--horizontal .el-button--danger:last-child{border-left-color:var(--el-button-divide-border-color)}.el-button-group--horizontal .el-button--danger:not(:first-child):not(:last-child){border-left-color:var(--el-button-divide-border-color);border-right-color:var(--el-button-divide-border-color)}.el-button-group--horizontal .el-button--info:first-child{border-right-color:var(--el-button-divide-border-color)}.el-button-group--horizontal .el-button--info:last-child{border-left-color:var(--el-button-divide-border-color)}.el-button-group--horizontal .el-button--info:not(:first-child):not(:last-child){border-left-color:var(--el-button-divide-border-color);border-right-color:var(--el-button-divide-border-color)}.el-button-group--horizontal>.el-dropdown>.el-button{border-left-color:var(--el-button-divide-border-color);border-top-left-radius:0;border-bottom-left-radius:0}.el-button-group--vertical{flex-direction:column;align-items:stretch;display:inline-flex}.el-button-group--vertical>.el-button{margin-top:-1px}.el-button-group--vertical>.el-button:first-child{border-bottom-right-radius:0;border-bottom-left-radius:0}.el-button-group--vertical>.el-button:last-child{border-top-left-radius:0;border-top-right-radius:0}.el-button-group--vertical>.el-dropdown{margin-top:-1px}.el-button-group--vertical>.el-dropdown>.el-button{border-left-color:var(--el-button-divide-border-color);border-top-left-radius:0;border-top-right-radius:0}.el-button-group--vertical .el-button--primary:first-child{border-bottom-color:var(--el-button-divide-border-color)}.el-button-group--vertical .el-button--primary:last-child{border-top-color:var(--el-button-divide-border-color)}.el-button-group--vertical .el-button--primary:not(:first-child):not(:last-child){border-top-color:var(--el-button-divide-border-color);border-bottom-color:var(--el-button-divide-border-color)}.el-button-group--vertical .el-button--success:first-child{border-bottom-color:var(--el-button-divide-border-color)}.el-button-group--vertical .el-button--success:last-child{border-top-color:var(--el-button-divide-border-color)}.el-button-group--vertical .el-button--success:not(:first-child):not(:last-child){border-top-color:var(--el-button-divide-border-color);border-bottom-color:var(--el-button-divide-border-color)}.el-button-group--vertical .el-button--warning:first-child{border-bottom-color:var(--el-button-divide-border-color)}.el-button-group--vertical .el-button--warning:last-child{border-top-color:var(--el-button-divide-border-color)}.el-button-group--vertical .el-button--warning:not(:first-child):not(:last-child){border-top-color:var(--el-button-divide-border-color);border-bottom-color:var(--el-button-divide-border-color)}.el-button-group--vertical .el-button--danger:first-child{border-bottom-color:var(--el-button-divide-border-color)}.el-button-group--vertical .el-button--danger:last-child{border-top-color:var(--el-button-divide-border-color)}.el-button-group--vertical .el-button--danger:not(:first-child):not(:last-child){border-top-color:var(--el-button-divide-border-color);border-bottom-color:var(--el-button-divide-border-color)}.el-button-group--vertical .el-button--info:first-child{border-bottom-color:var(--el-button-divide-border-color)}.el-button-group--vertical .el-button--info:last-child{border-top-color:var(--el-button-divide-border-color)}.el-button-group--vertical .el-button--info:not(:first-child):not(:last-child){border-top-color:var(--el-button-divide-border-color);border-bottom-color:var(--el-button-divide-border-color)}}@layer element-plus{.el-dropdown{--el-dropdown-menu-box-shadow:var(--el-box-shadow-light);--el-dropdown-menuItem-hover-fill:var(--el-color-primary-light-9);--el-dropdown-menuItem-hover-color:var(--el-color-primary);--el-dropdown-menu-index:10;color:var(--el-text-color-regular);font-size:var(--el-font-size-base);vertical-align:top;line-height:1;display:inline-flex;position:relative}.el-dropdown.is-disabled{color:var(--el-text-color-placeholder);cursor:not-allowed}.el-dropdown__popper{--el-dropdown-menu-box-shadow:var(--el-box-shadow-light);--el-dropdown-menuItem-hover-fill:var(--el-color-primary-light-9);--el-dropdown-menuItem-hover-color:var(--el-color-primary);--el-dropdown-menu-index:10}.el-dropdown__popper.el-popper{background:var(--el-bg-color-overlay);border:1px solid var(--el-border-color-light);box-shadow:var(--el-dropdown-menu-box-shadow)}.el-dropdown__popper.el-popper .el-popper__arrow:before{border:1px solid var(--el-border-color-light)}.el-dropdown__popper.el-popper[data-popper-placement^=top] .el-popper__arrow:before{border-top-color:#0000;border-left-color:#0000}.el-dropdown__popper.el-popper[data-popper-placement^=bottom] .el-popper__arrow:before{border-bottom-color:#0000;border-right-color:#0000}.el-dropdown__popper.el-popper[data-popper-placement^=left] .el-popper__arrow:before{border-bottom-color:#0000;border-left-color:#0000}.el-dropdown__popper.el-popper[data-popper-placement^=right] .el-popper__arrow:before{border-top-color:#0000;border-right-color:#0000}.el-dropdown__popper .el-dropdown-menu{border:none}.el-dropdown__popper .el-dropdown__popper-selfdefine{outline:none}.el-dropdown__popper .el-scrollbar__bar{z-index:calc(var(--el-dropdown-menu-index) + 1)}.el-dropdown__popper .el-dropdown__list{box-sizing:border-box;margin:0;padding:0;list-style:none}.el-dropdown .el-dropdown__caret-button{border-left:none;justify-content:center;align-items:center;width:32px;padding-left:0;padding-right:0;display:inline-flex}.el-dropdown .el-dropdown__caret-button>span{display:inline-flex}.el-dropdown .el-dropdown__caret-button:before{content:"";background:var(--el-overlay-color-lighter);width:1px;display:block;position:absolute;top:-1px;bottom:-1px;left:0}.el-dropdown .el-dropdown__caret-button.el-button:before{background:var(--el-border-color);opacity:.5}.el-dropdown .el-dropdown__caret-button .el-dropdown__icon{font-size:inherit;padding-left:0}.el-dropdown .el-dropdown-selfdefine{outline:none}.el-dropdown--large .el-dropdown__caret-button{width:40px}.el-dropdown--small .el-dropdown__caret-button{width:24px}.el-dropdown-menu{z-index:var(--el-dropdown-menu-index);background-color:var(--el-bg-color-overlay);border-radius:var(--el-border-radius-base);box-shadow:none;border:none;margin:0;padding:5px 0;list-style:none;position:relative;top:0;left:0}.el-dropdown-menu__item{white-space:nowrap;line-height:22px;font-size:var(--el-font-size-base);color:var(--el-text-color-regular);cursor:pointer;outline:none;align-items:center;margin:0;padding:5px 16px;list-style:none;display:flex}.el-dropdown-menu__item:not(.is-disabled):hover,.el-dropdown-menu__item:not(.is-disabled):focus{background-color:var(--el-dropdown-menuItem-hover-fill);color:var(--el-dropdown-menuItem-hover-color)}.el-dropdown-menu__item i{margin-right:5px}.el-dropdown-menu__item--divided{border-top:1px solid var(--el-border-color-lighter);margin:6px 0}.el-dropdown-menu__item.is-disabled{cursor:not-allowed;color:var(--el-text-color-disabled)}.el-dropdown-menu--large{padding:7px 0}.el-dropdown-menu--large .el-dropdown-menu__item{padding:7px 20px;font-size:14px;line-height:22px}.el-dropdown-menu--large .el-dropdown-menu__item--divided{margin:8px 0}.el-dropdown-menu--small{padding:3px 0}.el-dropdown-menu--small .el-dropdown-menu__item{padding:2px 12px;font-size:12px;line-height:20px}.el-dropdown-menu--small .el-dropdown-menu__item--divided{margin:4px 0}}@layer element-plus;@layer element-plus;@layer element-plus{.el-notification{--el-notification-width:330px;--el-notification-padding:14px 26px 14px 13px;--el-notification-radius:8px;--el-notification-shadow:var(--el-box-shadow-light);--el-notification-border-color:var(--el-border-color-lighter);--el-notification-icon-size:24px;--el-notification-close-font-size:var(--el-message-close-size,16px);--el-notification-group-margin-left:13px;--el-notification-group-margin-right:8px;--el-notification-content-font-size:var(--el-font-size-base);--el-notification-content-color:var(--el-text-color-regular);--el-notification-title-font-size:16px;--el-notification-title-color:var(--el-text-color-primary);--el-notification-close-color:var(--el-text-color-secondary);--el-notification-close-hover-color:var(--el-text-color-regular);width:var(--el-notification-width);padding:var(--el-notification-padding);border-radius:var(--el-notification-radius);box-sizing:border-box;border:1px solid var(--el-notification-border-color);background-color:var(--el-bg-color-overlay);box-shadow:var(--el-notification-shadow);transition:opacity var(--el-transition-duration),transform var(--el-transition-duration),left var(--el-transition-duration),right var(--el-transition-duration),top .4s,bottom var(--el-transition-duration);overflow-wrap:break-word;z-index:9999;display:flex;position:fixed;overflow:hidden}.el-notification.right{right:16px}.el-notification.left{left:16px}.el-notification__group{min-width:0;margin-left:var(--el-notification-group-margin-left);margin-right:var(--el-notification-group-margin-right);flex:1}.el-notification__title{font-weight:700;font-size:var(--el-notification-title-font-size);line-height:var(--el-notification-icon-size);color:var(--el-notification-title-color);margin:0}.el-notification__content{font-size:var(--el-notification-content-font-size);color:var(--el-notification-content-color);margin:6px 0 0;line-height:24px}.el-notification__content p{margin:0}.el-notification .el-notification__icon{height:var(--el-notification-icon-size);width:var(--el-notification-icon-size);font-size:var(--el-notification-icon-size);flex-shrink:0}.el-notification .el-notification__closeBtn{cursor:pointer;color:var(--el-notification-close-color);font-size:var(--el-notification-close-font-size);position:absolute;top:18px;right:15px}.el-notification .el-notification__closeBtn:hover{color:var(--el-notification-close-hover-color)}.el-notification .el-notification--primary{--el-notification-icon-color:var(--el-color-primary);color:var(--el-notification-icon-color)}.el-notification .el-notification--success{--el-notification-icon-color:var(--el-color-success);color:var(--el-notification-icon-color)}.el-notification .el-notification--info{--el-notification-icon-color:var(--el-color-info);color:var(--el-notification-icon-color)}.el-notification .el-notification--warning{--el-notification-icon-color:var(--el-color-warning);color:var(--el-notification-icon-color)}.el-notification .el-notification--error{--el-notification-icon-color:var(--el-color-error);color:var(--el-notification-icon-color)}.el-notification-fade-enter-from.right{right:0;transform:translate(100%)}.el-notification-fade-enter-from.left{left:0;transform:translate(-100%)}.el-notification-fade-leave-to{opacity:0}}@layer element-plus{:root{--el-loading-spinner-size:42px;--el-loading-fullscreen-spinner-size:50px}.el-loading-parent--relative{position:relative!important}.el-loading-parent--hidden{overflow:hidden!important}.el-loading-mask{z-index:2000;background-color:var(--el-mask-color);transition:opacity var(--el-transition-duration);margin:0;position:absolute;top:0;bottom:0;left:0;right:0}.el-loading-mask.is-fullscreen{position:fixed}.el-loading-mask.is-fullscreen .el-loading-spinner{margin-top:calc((0px - var(--el-loading-fullscreen-spinner-size)) / 2)}.el-loading-mask.is-fullscreen .el-loading-spinner .circular{height:var(--el-loading-fullscreen-spinner-size);width:var(--el-loading-fullscreen-spinner-size)}.el-loading-spinner{margin-top:calc((0px - var(--el-loading-spinner-size)) / 2);text-align:center;width:100%;position:absolute;top:50%}.el-loading-spinner .el-loading-text{color:var(--el-color-primary);margin:3px 0;font-size:14px}.el-loading-spinner .circular{height:var(--el-loading-spinner-size);width:var(--el-loading-spinner-size);animation:2s linear infinite loading-rotate;display:inline}.el-loading-spinner .path{stroke-dasharray:90 150;stroke-dashoffset:0;stroke-width:2px;stroke:var(--el-color-primary);stroke-linecap:round;animation:1.5s ease-in-out infinite loading-dash}.el-loading-spinner i{color:var(--el-color-primary)}.el-loading-fade-enter-from,.el-loading-fade-leave-to{opacity:0}@keyframes loading-rotate{to{transform:rotate(360deg)}}@keyframes loading-dash{0%{stroke-dasharray:1 200;stroke-dashoffset:0}50%{stroke-dasharray:90 150;stroke-dashoffset:-40px}to{stroke-dasharray:90 150;stroke-dashoffset:-120px}}}@layer element-plus{:root{--el-popup-modal-bg-color:var(--el-color-black);--el-popup-modal-opacity:.5}.v-modal-enter{animation:v-modal-in var(--el-transition-duration-fast) ease}.v-modal-leave{animation:v-modal-out var(--el-transition-duration-fast) ease forwards}@keyframes v-modal-in{0%{opacity:0}}@keyframes v-modal-out{to{opacity:0}}.v-modal{width:100%;height:100%;opacity:var(--el-popup-modal-opacity);background:var(--el-popup-modal-bg-color);position:fixed;top:0;left:0}.el-popup-parent--hidden{overflow:hidden}.el-message-box{--el-messagebox-title-color:var(--el-text-color-primary);--el-messagebox-width:420px;--el-messagebox-border-radius:4px;--el-messagebox-box-shadow:var(--el-box-shadow);--el-messagebox-font-size:var(--el-font-size-large);--el-messagebox-content-font-size:var(--el-font-size-base);--el-messagebox-content-color:var(--el-text-color-regular);--el-messagebox-error-font-size:12px;--el-messagebox-padding-primary:12px;--el-messagebox-font-line-height:var(--el-font-line-height-primary);max-width:var(--el-messagebox-width);width:100%;padding:var(--el-messagebox-padding-primary);vertical-align:middle;background-color:var(--el-bg-color);border-radius:var(--el-messagebox-border-radius);font-size:var(--el-messagebox-font-size);box-shadow:var(--el-messagebox-box-shadow);text-align:left;-webkit-backface-visibility:hidden;backface-visibility:hidden;box-sizing:border-box;overflow-wrap:break-word;display:inline-block;position:relative;overflow:hidden}.el-message-box:focus{outline:none!important}.is-message-box .el-overlay-message-box{text-align:center;padding:16px;position:fixed;top:0;bottom:0;left:0;right:0;overflow:auto}.is-message-box .el-overlay-message-box:after{content:"";vertical-align:middle;width:0;height:100%;display:inline-block}.el-message-box.is-draggable .el-message-box__header{cursor:move;-webkit-user-select:none;user-select:none}.el-message-box__header{padding-bottom:var(--el-messagebox-padding-primary)}.el-message-box__header.show-close{padding-right:calc(var(--el-messagebox-padding-primary) + var(--el-message-close-size,16px))}.el-message-box__title{font-size:var(--el-messagebox-font-size);line-height:var(--el-messagebox-font-line-height);color:var(--el-messagebox-title-color)}.el-message-box__headerbtn{width:40px;height:40px;font-size:var(--el-message-close-size,16px);cursor:pointer;background:0 0;border:none;outline:none;padding:0;position:absolute;top:0;right:0}.el-message-box__headerbtn .el-message-box__close{color:var(--el-color-info);font-size:inherit}.el-message-box__headerbtn:focus .el-message-box__close,.el-message-box__headerbtn:hover .el-message-box__close{color:var(--el-color-primary)}.el-message-box__content{color:var(--el-messagebox-content-color);font-size:var(--el-messagebox-content-font-size)}.el-message-box__container{align-items:center;gap:12px;display:flex}.el-message-box__input{padding-top:12px}.el-message-box__input div.invalid>input,.el-message-box__input div.invalid>input:focus{border-color:var(--el-color-error)}.el-message-box__status{font-size:24px}.el-message-box__status.el-message-box-icon--primary{--el-messagebox-color:var(--el-color-primary);color:var(--el-messagebox-color)}.el-message-box__status.el-message-box-icon--success{--el-messagebox-color:var(--el-color-success);color:var(--el-messagebox-color)}.el-message-box__status.el-message-box-icon--info{--el-messagebox-color:var(--el-color-info);color:var(--el-messagebox-color)}.el-message-box__status.el-message-box-icon--warning{--el-messagebox-color:var(--el-color-warning);color:var(--el-messagebox-color)}.el-message-box__status.el-message-box-icon--error{--el-messagebox-color:var(--el-color-error);color:var(--el-messagebox-color)}.el-message-box__message{min-width:0;margin:0}.el-message-box__message p{line-height:var(--el-messagebox-font-line-height);margin:0}.el-message-box__errormsg{color:var(--el-color-error);font-size:var(--el-messagebox-error-font-size);line-height:var(--el-messagebox-font-line-height)}.el-message-box__btns{padding-top:var(--el-messagebox-padding-primary);flex-wrap:wrap;justify-content:flex-end;align-items:center;display:flex}.el-message-box--center .el-message-box__title{justify-content:center;align-items:center;gap:6px;display:flex}.el-message-box--center .el-message-box__status{font-size:inherit}.el-message-box--center .el-message-box__btns,.el-message-box--center .el-message-box__container{justify-content:center}.el-message-box-parent--hidden{overflow:hidden}.fade-in-linear-enter-active .el-overlay-message-box{animation:msgbox-fade-in var(--el-transition-duration)}.fade-in-linear-leave-active .el-overlay-message-box{animation:msgbox-fade-in var(--el-transition-duration) reverse}@keyframes msgbox-fade-in{0%{opacity:0;transform:translateY(-20px)}to{opacity:1;transform:translate(0)}}}@layer element-plus{.el-message{--el-message-bg-color:var(--el-color-info-light-9);--el-message-border-color:var(--el-border-color-lighter);--el-message-padding:11px 15px;--el-message-close-size:16px;--el-message-close-icon-color:var(--el-text-color-placeholder);--el-message-close-hover-color:var(--el-text-color-secondary);box-sizing:border-box;border-radius:var(--el-border-radius-base);border-width:var(--el-border-width);border-style:var(--el-border-style);border-color:var(--el-message-border-color);background-color:var(--el-message-bg-color);width:max-content;max-width:calc(100% - 32px);transition:opacity var(--el-transition-duration),transform .4s,top .4s,bottom .4s;padding:var(--el-message-padding);align-items:center;gap:8px;display:flex;position:fixed}.el-message.is-left{left:16px}.el-message.is-right{right:16px}.el-message.is-center{left:50%;transform:translate(-50%)}.el-message.is-plain{background-color:var(--el-bg-color-overlay);border-color:var(--el-bg-color-overlay);box-shadow:var(--el-box-shadow-light)}.el-message p{margin:0}.el-message--primary{--el-message-bg-color:var(--el-color-primary-light-9);--el-message-border-color:var(--el-color-primary-light-8);--el-message-text-color:var(--el-color-primary)}.el-message--primary .el-message__content{color:var(--el-message-text-color);overflow-wrap:break-word}.el-message .el-message-icon--primary{color:var(--el-message-text-color)}.el-message--success{--el-message-bg-color:var(--el-color-success-light-9);--el-message-border-color:var(--el-color-success-light-8);--el-message-text-color:var(--el-color-success)}.el-message--success .el-message__content{color:var(--el-message-text-color);overflow-wrap:break-word}.el-message .el-message-icon--success{color:var(--el-message-text-color)}.el-message--info{--el-message-bg-color:var(--el-color-info-light-9);--el-message-border-color:var(--el-color-info-light-8);--el-message-text-color:var(--el-color-info)}.el-message--info .el-message__content{color:var(--el-message-text-color);overflow-wrap:break-word}.el-message .el-message-icon--info{color:var(--el-message-text-color)}.el-message--warning{--el-message-bg-color:var(--el-color-warning-light-9);--el-message-border-color:var(--el-color-warning-light-8);--el-message-text-color:var(--el-color-warning)}.el-message--warning .el-message__content{color:var(--el-message-text-color);overflow-wrap:break-word}.el-message .el-message-icon--warning{color:var(--el-message-text-color)}.el-message--error{--el-message-bg-color:var(--el-color-error-light-9);--el-message-border-color:var(--el-color-error-light-8);--el-message-text-color:var(--el-color-error)}.el-message--error .el-message__content{color:var(--el-message-text-color);overflow-wrap:break-word}.el-message .el-message-icon--error{color:var(--el-message-text-color)}.el-message .el-message__badge{position:absolute;top:-8px;right:-8px}.el-message__content{padding:0;font-size:14px;line-height:1}.el-message__content:focus{outline-width:0}.el-message .el-message__closeBtn{cursor:pointer;color:var(--el-message-close-icon-color);font-size:var(--el-message-close-size)}.el-message .el-message__closeBtn:focus{outline-width:0}.el-message .el-message__closeBtn:hover{color:var(--el-message-close-hover-color)}.el-message-fade-enter-from,.el-message-fade-leave-to{opacity:0}.el-message-fade-enter-from.is-left,.el-message-fade-enter-from.is-right,.el-message-fade-leave-to.is-left,.el-message-fade-leave-to.is-right{transform:translateY(-100%)}.el-message-fade-enter-from.is-left.is-bottom,.el-message-fade-enter-from.is-right.is-bottom,.el-message-fade-leave-to.is-left.is-bottom,.el-message-fade-leave-to.is-right.is-bottom{transform:translateY(100%)}.el-message-fade-enter-from.is-center,.el-message-fade-leave-to.is-center{transform:translate(-50%,-100%)}.el-message-fade-enter-from.is-center.is-bottom,.el-message-fade-leave-to.is-center.is-bottom{transform:translate(-50%,100%)}}@layer element-plus{.el-checkbox{--el-checkbox-font-size:14px;--el-checkbox-font-weight:var(--el-font-weight-primary);--el-checkbox-text-color:var(--el-text-color-regular);--el-checkbox-input-height:14px;--el-checkbox-input-width:14px;--el-checkbox-border-radius:var(--el-border-radius-small);--el-checkbox-bg-color:var(--el-fill-color-blank);--el-checkbox-input-border:var(--el-border);--el-checkbox-disabled-border-color:var(--el-border-color);--el-checkbox-disabled-input-fill:var(--el-fill-color-light);--el-checkbox-disabled-icon-color:var(--el-text-color-placeholder);--el-checkbox-disabled-checked-input-fill:var(--el-border-color-extra-light);--el-checkbox-disabled-checked-input-border-color:var(--el-border-color);--el-checkbox-disabled-checked-icon-color:var(--el-text-color-placeholder);--el-checkbox-checked-text-color:var(--el-color-primary);--el-checkbox-checked-input-border-color:var(--el-color-primary);--el-checkbox-checked-bg-color:var(--el-color-primary);--el-checkbox-checked-icon-color:var(--el-color-white);--el-checkbox-input-border-color-hover:var(--el-color-primary);color:var(--el-checkbox-text-color);font-weight:var(--el-checkbox-font-weight);font-size:var(--el-font-size-base);cursor:pointer;white-space:nowrap;-webkit-user-select:none;user-select:none;height:var(--el-checkbox-height,32px);align-items:center;margin-right:30px;display:inline-flex;position:relative}.el-checkbox.is-disabled{cursor:not-allowed}.el-checkbox.is-bordered{border-radius:var(--el-border-radius-base);border:var(--el-border);box-sizing:border-box;padding:0 15px 0 9px}.el-checkbox.is-bordered.is-checked{border-color:var(--el-color-primary)}.el-checkbox.is-bordered.is-disabled{border-color:var(--el-border-color-lighter)}.el-checkbox.is-bordered.el-checkbox--large{border-radius:var(--el-border-radius-base);padding:0 19px 0 11px}.el-checkbox.is-bordered.el-checkbox--large .el-checkbox__label{font-size:var(--el-font-size-base)}.el-checkbox.is-bordered.el-checkbox--large .el-checkbox__inner{width:14px;height:14px}.el-checkbox.is-bordered.el-checkbox--small{border-radius:calc(var(--el-border-radius-base) - 1px);padding:0 11px 0 7px}.el-checkbox.is-bordered.el-checkbox--small .el-checkbox__label{font-size:12px}.el-checkbox.is-bordered.el-checkbox--small .el-checkbox__inner{width:12px;height:12px}.el-checkbox.is-bordered.el-checkbox--small .el-checkbox__inner:after{width:2px;height:6px}.el-checkbox input:focus-visible+.el-checkbox__inner{outline:2px solid var(--el-checkbox-input-border-color-hover);outline-offset:1px;border-radius:var(--el-checkbox-border-radius)}.el-checkbox__input{white-space:nowrap;cursor:pointer;outline:none;display:inline-flex;position:relative}.el-checkbox__input.is-disabled .el-checkbox__inner{background-color:var(--el-checkbox-disabled-input-fill);border-color:var(--el-checkbox-disabled-border-color);cursor:not-allowed}.el-checkbox__input.is-disabled .el-checkbox__inner:after{cursor:not-allowed;border-color:var(--el-checkbox-disabled-icon-color);will-change:transform}.el-checkbox__input.is-disabled.is-checked .el-checkbox__inner{background-color:var(--el-checkbox-disabled-checked-input-fill);border-color:var(--el-checkbox-disabled-checked-input-border-color)}.el-checkbox__input.is-disabled.is-checked .el-checkbox__inner:after{border-color:var(--el-checkbox-disabled-checked-icon-color)}.el-checkbox__input.is-disabled.is-indeterminate .el-checkbox__inner{background-color:var(--el-checkbox-disabled-checked-input-fill);border-color:var(--el-checkbox-disabled-checked-input-border-color)}.el-checkbox__input.is-disabled.is-indeterminate .el-checkbox__inner:before{background-color:var(--el-checkbox-disabled-checked-icon-color);border-color:var(--el-checkbox-disabled-checked-icon-color)}.el-checkbox__input.is-disabled+span.el-checkbox__label{color:var(--el-disabled-text-color);cursor:not-allowed}.el-checkbox__input.is-checked .el-checkbox__inner{background-color:var(--el-checkbox-checked-bg-color);border-color:var(--el-checkbox-checked-input-border-color)}.el-checkbox__input.is-checked .el-checkbox__inner:after{border-color:var(--el-checkbox-checked-icon-color);transform:translate(-45%,-60%)rotate(45deg)scaleY(1)}.el-checkbox__input.is-checked+.el-checkbox__label{color:var(--el-checkbox-checked-text-color)}.el-checkbox__input.is-focus:not(.is-checked) .el-checkbox__original:not(:focus-visible){border-color:var(--el-checkbox-input-border-color-hover)}.el-checkbox__input.is-indeterminate .el-checkbox__inner{background-color:var(--el-checkbox-checked-bg-color);border-color:var(--el-checkbox-checked-input-border-color)}.el-checkbox__input.is-indeterminate .el-checkbox__inner:before{content:"";background-color:var(--el-checkbox-checked-icon-color);height:2px;display:block;position:absolute;top:5px;left:0;right:0;transform:scale(.5)}.el-checkbox__input.is-indeterminate .el-checkbox__inner:after{display:none}.el-checkbox__inner{border:var(--el-checkbox-input-border);border-radius:var(--el-checkbox-border-radius);box-sizing:border-box;width:var(--el-checkbox-input-width);height:var(--el-checkbox-input-height);background-color:var(--el-checkbox-bg-color);z-index:var(--el-index-normal);transition:border-color .25s cubic-bezier(.71,-.46,.29,1.46),background-color .25s cubic-bezier(.71,-.46,.29,1.46),outline .25s cubic-bezier(.71,-.46,.29,1.46);display:inline-block;position:relative}.el-checkbox__inner:hover{border-color:var(--el-checkbox-input-border-color-hover)}.el-checkbox__inner:after{box-sizing:content-box;content:"";transform-origin:50%;border:1px solid #0000;border-top:0;border-left:0;width:3px;height:7px;transition:transform .15s ease-in 50ms;position:absolute;top:50%;left:50%;transform:translate(-45%,-60%)rotate(45deg)scaleY(0)}.el-checkbox__original{opacity:0;z-index:-1;outline:none;width:0;height:0;margin:0;position:absolute}.el-checkbox__label{line-height:1;font-size:var(--el-checkbox-font-size);padding-left:8px;display:inline-block}.el-checkbox.el-checkbox--large{height:40px}.el-checkbox.el-checkbox--large .el-checkbox__label{font-size:14px}.el-checkbox.el-checkbox--large .el-checkbox__inner{width:14px;height:14px}.el-checkbox.el-checkbox--small{height:24px}.el-checkbox.el-checkbox--small .el-checkbox__label{font-size:12px}.el-checkbox.el-checkbox--small .el-checkbox__inner{width:12px;height:12px}.el-checkbox.el-checkbox--small .el-checkbox__input.is-indeterminate .el-checkbox__inner:before{top:4px}.el-checkbox.el-checkbox--small .el-checkbox__inner:after{width:2px;height:6px}.el-checkbox:last-of-type{margin-right:0}}@layer element-plus{.el-form{--el-form-label-font-size:var(--el-font-size-base);--el-form-inline-content-width:220px}.el-form--inline .el-form-item{vertical-align:middle;margin-right:32px;display:inline-flex}.el-form--inline .el-form-item:last-child{margin-right:0}.el-form--inline.el-form--label-top{flex-wrap:wrap;display:flex}.el-form--inline.el-form--label-top .el-form-item{display:block}}@layer element-plus{.el-form-item{--font-size:14px;margin-bottom:18px;display:flex}.el-form-item .el-form-item{margin-bottom:0}.el-form-item .el-input__validateIcon{display:none}.el-form-item--large{--font-size:14px;--el-form-label-font-size:var(--font-size);margin-bottom:22px}.el-form-item--large .el-form-item__label{height:40px;line-height:40px}.el-form-item--large .el-form-item__content{line-height:40px}.el-form-item--large .el-form-item__error{padding-top:4px}.el-form-item--default{--font-size:14px;--el-form-label-font-size:var(--font-size);margin-bottom:18px}.el-form-item--default .el-form-item__label{height:32px;line-height:32px}.el-form-item--default .el-form-item__content{line-height:32px}.el-form-item--default .el-form-item__error{padding-top:2px}.el-form-item--small{--font-size:12px;--el-form-label-font-size:var(--font-size);margin-bottom:18px}.el-form-item--small .el-form-item__label{height:24px;line-height:24px}.el-form-item--small .el-form-item__content{line-height:24px}.el-form-item--small .el-form-item__error{padding-top:2px}.el-form-item--label-left .el-form-item__label{text-align:left;justify-content:flex-start}.el-form-item--label-right .el-form-item__label{text-align:right;justify-content:flex-end}.el-form-item--label-top{display:block}.el-form-item--label-top .el-form-item__label{text-align:left;width:-moz-fit-content;width:fit-content;height:auto;margin-bottom:8px;padding-right:0;line-height:22px;display:block}.el-form-item__label-wrap{display:flex}.el-form-item__label{font-size:var(--el-form-label-font-size);color:var(--el-text-color-regular);box-sizing:border-box;flex:none;align-items:flex-start;height:32px;padding:0 12px 0 0;line-height:32px;display:inline-flex}.el-form-item__content{line-height:32px;font-size:var(--font-size);flex-wrap:wrap;flex:1;align-items:center;min-width:0;display:flex;position:relative}.el-form-item__content .el-input-group{vertical-align:top}.el-form-item__error{color:var(--el-color-danger);padding-top:2px;font-size:12px;line-height:1;position:absolute;top:100%;left:0}.el-form-item__error--inline{margin-left:10px;display:inline-block;position:relative;top:auto;left:auto}.el-form-item.is-required:not(.is-no-asterisk).asterisk-left>.el-form-item__label:before,.el-form-item.is-required:not(.is-no-asterisk).asterisk-left>.el-form-item__label-wrap>.el-form-item__label:before{content:"*";color:var(--el-color-danger);margin-right:4px}.el-form-item.is-required:not(.is-no-asterisk).asterisk-right>.el-form-item__label:after,.el-form-item.is-required:not(.is-no-asterisk).asterisk-right>.el-form-item__label-wrap>.el-form-item__label:after{content:"*";color:var(--el-color-danger);margin-left:4px}.el-form-item.is-error .el-form-item__content .el-input__wrapper,.el-form-item.is-error .el-form-item__content .el-input__wrapper:hover,.el-form-item.is-error .el-form-item__content .el-input__wrapper:focus,.el-form-item.is-error .el-form-item__content .el-input__wrapper.is-focus,.el-form-item.is-error .el-form-item__content .el-textarea__inner,.el-form-item.is-error .el-form-item__content .el-textarea__inner:hover,.el-form-item.is-error .el-form-item__content .el-textarea__inner:focus,.el-form-item.is-error .el-form-item__content .el-textarea__inner.is-focus,.el-form-item.is-error .el-form-item__content .el-select__wrapper,.el-form-item.is-error .el-form-item__content .el-select__wrapper:hover,.el-form-item.is-error .el-form-item__content .el-select__wrapper:focus,.el-form-item.is-error .el-form-item__content .el-select__wrapper.is-focus,.el-form-item.is-error .el-form-item__content .el-input-tag__wrapper,.el-form-item.is-error .el-form-item__content .el-input-tag__wrapper:hover,.el-form-item.is-error .el-form-item__content .el-input-tag__wrapper:focus,.el-form-item.is-error .el-form-item__content .el-input-tag__wrapper.is-focus,.el-form-item.is-error .el-form-item__content :not(.el-input-otp--underlined) .el-input-otp__input-field,.el-form-item.is-error .el-form-item__content :not(.el-input-otp--underlined) .el-input-otp__input-field:hover,.el-form-item.is-error .el-form-item__content :not(.el-input-otp--underlined) .el-input-otp__input-field:focus,.el-form-item.is-error .el-form-item__content :not(.el-input-otp--underlined) .el-input-otp__input-field.is-focus,.el-form-item.is-error .el-form-item__content .el-input-otp--underlined .el-input-otp__input-field:after,.el-form-item.is-error .el-form-item__content .el-input-otp--underlined .el-input-otp__input-field:hover:after,.el-form-item.is-error .el-form-item__content .el-input-otp--underlined .el-input-otp__input-field:focus:after,.el-form-item.is-error .el-form-item__content .el-input-otp--underlined .el-input-otp__input-field.is-focus:after{box-shadow:0 0 0 1px var(--el-color-danger) inset}.el-form-item.is-error .el-form-item__content .el-input-group__append .el-input__wrapper,.el-form-item.is-error .el-form-item__content .el-input-group__prepend .el-input__wrapper{box-shadow:inset 0 0 0 1px #0000}.el-form-item.is-error .el-form-item__content .el-input-group__append .el-input__validateIcon,.el-form-item.is-error .el-form-item__content .el-input-group__prepend .el-input__validateIcon{display:none}.el-form-item.is-error .el-form-item__content .el-input__validateIcon{color:var(--el-color-danger)}.el-form-item--feedback .el-input__validateIcon{display:inline-flex}}@layer element-plus{.el-progress{align-items:center;line-height:1;display:flex;position:relative}.el-progress__text{color:var(--el-text-color-regular);min-width:50px;margin-left:5px;font-size:14px;line-height:1}.el-progress__text i{vertical-align:middle;display:block}.el-progress--circle,.el-progress--dashboard{display:inline-block}.el-progress--circle .el-progress__text,.el-progress--dashboard .el-progress__text{text-align:center;width:100%;margin:0;position:absolute;top:50%;left:0;transform:translateY(-50%)}.el-progress--circle .el-progress__text i,.el-progress--dashboard .el-progress__text i{vertical-align:middle;display:inline-block}.el-progress--without-text .el-progress__text{display:none}.el-progress--without-text .el-progress-bar{margin-right:0;padding-right:0;display:block}.el-progress--text-inside .el-progress-bar{margin-right:0;padding-right:0}.el-progress.is-success .el-progress-bar__inner{background-color:var(--el-color-success)}.el-progress.is-success .el-progress__text{color:var(--el-color-success)}.el-progress.is-warning .el-progress-bar__inner{background-color:var(--el-color-warning)}.el-progress.is-warning .el-progress__text{color:var(--el-color-warning)}.el-progress.is-exception .el-progress-bar__inner{background-color:var(--el-color-danger)}.el-progress.is-exception .el-progress__text{color:var(--el-color-danger)}.el-progress-bar{box-sizing:border-box;flex-grow:1}.el-progress-bar__outer{background-color:var(--el-border-color-lighter);vertical-align:middle;border-radius:100px;height:6px;position:relative;overflow:hidden}.el-progress-bar__inner{background-color:var(--el-color-primary);text-align:right;white-space:nowrap;border-radius:100px;height:100%;line-height:1;transition:width .6s;position:absolute;top:0;left:0}.el-progress-bar__inner:after{content:"";vertical-align:middle;height:100%;display:inline-block}.el-progress-bar__inner--indeterminate{animation:3s infinite indeterminate;transform:translateZ(0)}.el-progress-bar__inner--striped{background-image:linear-gradient(45deg,#0000001a 25%,#0000 25%,#0000 50%,#0000001a 50%,#0000001a 75%,#0000 75%,#0000);background-size:1.25em 1.25em}.el-progress-bar__inner--striped.el-progress-bar__inner--striped-flow{animation:3s linear infinite striped-flow}.el-progress-bar__innerText{vertical-align:middle;color:#fff;margin:0 5px;font-size:12px;display:inline-block}@keyframes progress{0%{background-position:0 0}to{background-position:32px 0}}@keyframes indeterminate{0%{left:-100%}to{left:100%}}@keyframes striped-flow{0%{background-position:-100%}to{background-position:100%}}}@layer element-plus{.el-popover{--el-popover-bg-color:var(--el-bg-color-overlay);--el-popover-font-size:var(--el-font-size-base);--el-popover-border-color:var(--el-border-color-lighter);--el-popover-padding:12px;--el-popover-padding-large:18px 20px;--el-popover-title-font-size:16px;--el-popover-title-text-color:var(--el-text-color-primary);--el-popover-border-radius:4px}.el-popover.el-popper{background:var(--el-popover-bg-color);border-radius:var(--el-popover-border-radius);border:1px solid var(--el-popover-border-color);min-width:150px;padding:var(--el-popover-padding);z-index:var(--el-index-popper);color:var(--el-text-color-regular);line-height:1.4;font-size:var(--el-popover-font-size);box-shadow:var(--el-box-shadow-light);overflow-wrap:break-word;box-sizing:border-box}.el-popover.el-popper--plain{padding:var(--el-popover-padding-large)}.el-popover__title{color:var(--el-popover-title-text-color);font-size:var(--el-popover-title-font-size);margin-bottom:12px;line-height:1}.el-popover__reference:focus:not(.focusing),.el-popover__reference:focus:hover{outline-width:0}.el-popover.el-popper.is-dark{--el-popover-bg-color:var(--el-text-color-primary);--el-popover-border-color:var(--el-text-color-primary);--el-popover-title-text-color:var(--el-bg-color);color:var(--el-bg-color)}.el-popover.el-popper:focus:active,.el-popover.el-popper:focus{outline-width:0}}@layer element-plus{.el-skeleton{--el-skeleton-circle-size:var(--el-avatar-size)}.el-skeleton__item{background:var(--el-skeleton-color);border-radius:var(--el-border-radius-base);width:100%;height:16px;display:inline-block}.el-skeleton__circle{width:var(--el-skeleton-circle-size);height:var(--el-skeleton-circle-size);line-height:var(--el-skeleton-circle-size);border-radius:50%}.el-skeleton__button{border-radius:4px;width:64px;height:40px}.el-skeleton__p{width:100%}.el-skeleton__p.is-last{width:61%}.el-skeleton__p.is-first{width:33%}.el-skeleton__text{width:100%;height:var(--el-font-size-small)}.el-skeleton__caption{height:var(--el-font-size-extra-small)}.el-skeleton__h1{height:var(--el-font-size-extra-large)}.el-skeleton__h3{height:var(--el-font-size-large)}.el-skeleton__h5{height:var(--el-font-size-medium)}.el-skeleton__image{width:unset;border-radius:0;justify-content:center;align-items:center;display:flex}.el-skeleton__image svg{color:var(--el-svg-monochrome-grey);fill:currentColor;width:22%;height:22%}}@layer element-plus{.el-skeleton{--el-skeleton-color:var(--el-fill-color);--el-skeleton-to-color:var(--el-fill-color-darker)}@keyframes el-skeleton-loading{0%{background-position:100%}to{background-position:0}}.el-skeleton{width:100%}.el-skeleton__first-line,.el-skeleton__paragraph{background:var(--el-skeleton-color);height:16px;margin-top:16px}.el-skeleton.is-animated .el-skeleton__item{background:linear-gradient(90deg,var(--el-skeleton-color) 25%,var(--el-skeleton-to-color) 37%,var(--el-skeleton-color) 63%);background-size:400% 100%;animation:1.4s infinite el-skeleton-loading}}@layer element-plus{.el-row{box-sizing:border-box;flex-wrap:wrap;display:flex;position:relative}.el-row.is-justify-center{justify-content:center}.el-row.is-justify-end{justify-content:flex-end}.el-row.is-justify-space-between{justify-content:space-between}.el-row.is-justify-space-around{justify-content:space-around}.el-row.is-justify-space-evenly{justify-content:space-evenly}.el-row.is-align-top{align-items:flex-start}.el-row.is-align-middle{align-items:center}.el-row.is-align-bottom{align-items:flex-end}}@layer element-plus{[class*=el-col-]{box-sizing:border-box}[class*=el-col-].is-guttered{min-height:1px;display:block}.el-col-0{flex:0 0;max-width:0%;display:none}.el-col-0.is-guttered{display:none}.el-col-offset-0{margin-left:0%}.el-col-pull-0{position:relative;right:0%}.el-col-push-0{position:relative;left:0%}.el-col-1{flex:0 0 4.16667%;max-width:4.16667%;display:block}.el-col-1.is-guttered{display:block}.el-col-offset-1{margin-left:4.16667%}.el-col-pull-1{position:relative;right:4.16667%}.el-col-push-1{position:relative;left:4.16667%}.el-col-2{flex:0 0 8.33333%;max-width:8.33333%;display:block}.el-col-2.is-guttered{display:block}.el-col-offset-2{margin-left:8.33333%}.el-col-pull-2{position:relative;right:8.33333%}.el-col-push-2{position:relative;left:8.33333%}.el-col-3{flex:0 0 12.5%;max-width:12.5%;display:block}.el-col-3.is-guttered{display:block}.el-col-offset-3{margin-left:12.5%}.el-col-pull-3{position:relative;right:12.5%}.el-col-push-3{position:relative;left:12.5%}.el-col-4{flex:0 0 16.6667%;max-width:16.6667%;display:block}.el-col-4.is-guttered{display:block}.el-col-offset-4{margin-left:16.6667%}.el-col-pull-4{position:relative;right:16.6667%}.el-col-push-4{position:relative;left:16.6667%}.el-col-5{flex:0 0 20.8333%;max-width:20.8333%;display:block}.el-col-5.is-guttered{display:block}.el-col-offset-5{margin-left:20.8333%}.el-col-pull-5{position:relative;right:20.8333%}.el-col-push-5{position:relative;left:20.8333%}.el-col-6{flex:0 0 25%;max-width:25%;display:block}.el-col-6.is-guttered{display:block}.el-col-offset-6{margin-left:25%}.el-col-pull-6{position:relative;right:25%}.el-col-push-6{position:relative;left:25%}.el-col-7{flex:0 0 29.1667%;max-width:29.1667%;display:block}.el-col-7.is-guttered{display:block}.el-col-offset-7{margin-left:29.1667%}.el-col-pull-7{position:relative;right:29.1667%}.el-col-push-7{position:relative;left:29.1667%}.el-col-8{flex:0 0 33.3333%;max-width:33.3333%;display:block}.el-col-8.is-guttered{display:block}.el-col-offset-8{margin-left:33.3333%}.el-col-pull-8{position:relative;right:33.3333%}.el-col-push-8{position:relative;left:33.3333%}.el-col-9{flex:0 0 37.5%;max-width:37.5%;display:block}.el-col-9.is-guttered{display:block}.el-col-offset-9{margin-left:37.5%}.el-col-pull-9{position:relative;right:37.5%}.el-col-push-9{position:relative;left:37.5%}.el-col-10{flex:0 0 41.6667%;max-width:41.6667%;display:block}.el-col-10.is-guttered{display:block}.el-col-offset-10{margin-left:41.6667%}.el-col-pull-10{position:relative;right:41.6667%}.el-col-push-10{position:relative;left:41.6667%}.el-col-11{flex:0 0 45.8333%;max-width:45.8333%;display:block}.el-col-11.is-guttered{display:block}.el-col-offset-11{margin-left:45.8333%}.el-col-pull-11{position:relative;right:45.8333%}.el-col-push-11{position:relative;left:45.8333%}.el-col-12{flex:0 0 50%;max-width:50%;display:block}.el-col-12.is-guttered{display:block}.el-col-offset-12{margin-left:50%}.el-col-pull-12{position:relative;right:50%}.el-col-push-12{position:relative;left:50%}.el-col-13{flex:0 0 54.1667%;max-width:54.1667%;display:block}.el-col-13.is-guttered{display:block}.el-col-offset-13{margin-left:54.1667%}.el-col-pull-13{position:relative;right:54.1667%}.el-col-push-13{position:relative;left:54.1667%}.el-col-14{flex:0 0 58.3333%;max-width:58.3333%;display:block}.el-col-14.is-guttered{display:block}.el-col-offset-14{margin-left:58.3333%}.el-col-pull-14{position:relative;right:58.3333%}.el-col-push-14{position:relative;left:58.3333%}.el-col-15{flex:0 0 62.5%;max-width:62.5%;display:block}.el-col-15.is-guttered{display:block}.el-col-offset-15{margin-left:62.5%}.el-col-pull-15{position:relative;right:62.5%}.el-col-push-15{position:relative;left:62.5%}.el-col-16{flex:0 0 66.6667%;max-width:66.6667%;display:block}.el-col-16.is-guttered{display:block}.el-col-offset-16{margin-left:66.6667%}.el-col-pull-16{position:relative;right:66.6667%}.el-col-push-16{position:relative;left:66.6667%}.el-col-17{flex:0 0 70.8333%;max-width:70.8333%;display:block}.el-col-17.is-guttered{display:block}.el-col-offset-17{margin-left:70.8333%}.el-col-pull-17{position:relative;right:70.8333%}.el-col-push-17{position:relative;left:70.8333%}.el-col-18{flex:0 0 75%;max-width:75%;display:block}.el-col-18.is-guttered{display:block}.el-col-offset-18{margin-left:75%}.el-col-pull-18{position:relative;right:75%}.el-col-push-18{position:relative;left:75%}.el-col-19{flex:0 0 79.1667%;max-width:79.1667%;display:block}.el-col-19.is-guttered{display:block}.el-col-offset-19{margin-left:79.1667%}.el-col-pull-19{position:relative;right:79.1667%}.el-col-push-19{position:relative;left:79.1667%}.el-col-20{flex:0 0 83.3333%;max-width:83.3333%;display:block}.el-col-20.is-guttered{display:block}.el-col-offset-20{margin-left:83.3333%}.el-col-pull-20{position:relative;right:83.3333%}.el-col-push-20{position:relative;left:83.3333%}.el-col-21{flex:0 0 87.5%;max-width:87.5%;display:block}.el-col-21.is-guttered{display:block}.el-col-offset-21{margin-left:87.5%}.el-col-pull-21{position:relative;right:87.5%}.el-col-push-21{position:relative;left:87.5%}.el-col-22{flex:0 0 91.6667%;max-width:91.6667%;display:block}.el-col-22.is-guttered{display:block}.el-col-offset-22{margin-left:91.6667%}.el-col-pull-22{position:relative;right:91.6667%}.el-col-push-22{position:relative;left:91.6667%}.el-col-23{flex:0 0 95.8333%;max-width:95.8333%;display:block}.el-col-23.is-guttered{display:block}.el-col-offset-23{margin-left:95.8333%}.el-col-pull-23{position:relative;right:95.8333%}.el-col-push-23{position:relative;left:95.8333%}.el-col-24{flex:0 0 100%;max-width:100%;display:block}.el-col-24.is-guttered{display:block}.el-col-offset-24{margin-left:100%}.el-col-pull-24{position:relative;right:100%}.el-col-push-24{position:relative;left:100%}@media only screen and (max-width:767px){.el-col-xs-0{flex:0 0;max-width:0%;display:none}.el-col-xs-0.is-guttered{display:none}.el-col-xs-offset-0{margin-left:0%}.el-col-xs-pull-0{position:relative;right:0%}.el-col-xs-push-0{position:relative;left:0%}.el-col-xs-1{flex:0 0 4.16667%;max-width:4.16667%;display:block}.el-col-xs-1.is-guttered{display:block}.el-col-xs-offset-1{margin-left:4.16667%}.el-col-xs-pull-1{position:relative;right:4.16667%}.el-col-xs-push-1{position:relative;left:4.16667%}.el-col-xs-2{flex:0 0 8.33333%;max-width:8.33333%;display:block}.el-col-xs-2.is-guttered{display:block}.el-col-xs-offset-2{margin-left:8.33333%}.el-col-xs-pull-2{position:relative;right:8.33333%}.el-col-xs-push-2{position:relative;left:8.33333%}.el-col-xs-3{flex:0 0 12.5%;max-width:12.5%;display:block}.el-col-xs-3.is-guttered{display:block}.el-col-xs-offset-3{margin-left:12.5%}.el-col-xs-pull-3{position:relative;right:12.5%}.el-col-xs-push-3{position:relative;left:12.5%}.el-col-xs-4{flex:0 0 16.6667%;max-width:16.6667%;display:block}.el-col-xs-4.is-guttered{display:block}.el-col-xs-offset-4{margin-left:16.6667%}.el-col-xs-pull-4{position:relative;right:16.6667%}.el-col-xs-push-4{position:relative;left:16.6667%}.el-col-xs-5{flex:0 0 20.8333%;max-width:20.8333%;display:block}.el-col-xs-5.is-guttered{display:block}.el-col-xs-offset-5{margin-left:20.8333%}.el-col-xs-pull-5{position:relative;right:20.8333%}.el-col-xs-push-5{position:relative;left:20.8333%}.el-col-xs-6{flex:0 0 25%;max-width:25%;display:block}.el-col-xs-6.is-guttered{display:block}.el-col-xs-offset-6{margin-left:25%}.el-col-xs-pull-6{position:relative;right:25%}.el-col-xs-push-6{position:relative;left:25%}.el-col-xs-7{flex:0 0 29.1667%;max-width:29.1667%;display:block}.el-col-xs-7.is-guttered{display:block}.el-col-xs-offset-7{margin-left:29.1667%}.el-col-xs-pull-7{position:relative;right:29.1667%}.el-col-xs-push-7{position:relative;left:29.1667%}.el-col-xs-8{flex:0 0 33.3333%;max-width:33.3333%;display:block}.el-col-xs-8.is-guttered{display:block}.el-col-xs-offset-8{margin-left:33.3333%}.el-col-xs-pull-8{position:relative;right:33.3333%}.el-col-xs-push-8{position:relative;left:33.3333%}.el-col-xs-9{flex:0 0 37.5%;max-width:37.5%;display:block}.el-col-xs-9.is-guttered{display:block}.el-col-xs-offset-9{margin-left:37.5%}.el-col-xs-pull-9{position:relative;right:37.5%}.el-col-xs-push-9{position:relative;left:37.5%}.el-col-xs-10{flex:0 0 41.6667%;max-width:41.6667%;display:block}.el-col-xs-10.is-guttered{display:block}.el-col-xs-offset-10{margin-left:41.6667%}.el-col-xs-pull-10{position:relative;right:41.6667%}.el-col-xs-push-10{position:relative;left:41.6667%}.el-col-xs-11{flex:0 0 45.8333%;max-width:45.8333%;display:block}.el-col-xs-11.is-guttered{display:block}.el-col-xs-offset-11{margin-left:45.8333%}.el-col-xs-pull-11{position:relative;right:45.8333%}.el-col-xs-push-11{position:relative;left:45.8333%}.el-col-xs-12{flex:0 0 50%;max-width:50%;display:block}.el-col-xs-12.is-guttered{display:block}.el-col-xs-offset-12{margin-left:50%}.el-col-xs-pull-12{position:relative;right:50%}.el-col-xs-push-12{position:relative;left:50%}.el-col-xs-13{flex:0 0 54.1667%;max-width:54.1667%;display:block}.el-col-xs-13.is-guttered{display:block}.el-col-xs-offset-13{margin-left:54.1667%}.el-col-xs-pull-13{position:relative;right:54.1667%}.el-col-xs-push-13{position:relative;left:54.1667%}.el-col-xs-14{flex:0 0 58.3333%;max-width:58.3333%;display:block}.el-col-xs-14.is-guttered{display:block}.el-col-xs-offset-14{margin-left:58.3333%}.el-col-xs-pull-14{position:relative;right:58.3333%}.el-col-xs-push-14{position:relative;left:58.3333%}.el-col-xs-15{flex:0 0 62.5%;max-width:62.5%;display:block}.el-col-xs-15.is-guttered{display:block}.el-col-xs-offset-15{margin-left:62.5%}.el-col-xs-pull-15{position:relative;right:62.5%}.el-col-xs-push-15{position:relative;left:62.5%}.el-col-xs-16{flex:0 0 66.6667%;max-width:66.6667%;display:block}.el-col-xs-16.is-guttered{display:block}.el-col-xs-offset-16{margin-left:66.6667%}.el-col-xs-pull-16{position:relative;right:66.6667%}.el-col-xs-push-16{position:relative;left:66.6667%}.el-col-xs-17{flex:0 0 70.8333%;max-width:70.8333%;display:block}.el-col-xs-17.is-guttered{display:block}.el-col-xs-offset-17{margin-left:70.8333%}.el-col-xs-pull-17{position:relative;right:70.8333%}.el-col-xs-push-17{position:relative;left:70.8333%}.el-col-xs-18{flex:0 0 75%;max-width:75%;display:block}.el-col-xs-18.is-guttered{display:block}.el-col-xs-offset-18{margin-left:75%}.el-col-xs-pull-18{position:relative;right:75%}.el-col-xs-push-18{position:relative;left:75%}.el-col-xs-19{flex:0 0 79.1667%;max-width:79.1667%;display:block}.el-col-xs-19.is-guttered{display:block}.el-col-xs-offset-19{margin-left:79.1667%}.el-col-xs-pull-19{position:relative;right:79.1667%}.el-col-xs-push-19{position:relative;left:79.1667%}.el-col-xs-20{flex:0 0 83.3333%;max-width:83.3333%;display:block}.el-col-xs-20.is-guttered{display:block}.el-col-xs-offset-20{margin-left:83.3333%}.el-col-xs-pull-20{position:relative;right:83.3333%}.el-col-xs-push-20{position:relative;left:83.3333%}.el-col-xs-21{flex:0 0 87.5%;max-width:87.5%;display:block}.el-col-xs-21.is-guttered{display:block}.el-col-xs-offset-21{margin-left:87.5%}.el-col-xs-pull-21{position:relative;right:87.5%}.el-col-xs-push-21{position:relative;left:87.5%}.el-col-xs-22{flex:0 0 91.6667%;max-width:91.6667%;display:block}.el-col-xs-22.is-guttered{display:block}.el-col-xs-offset-22{margin-left:91.6667%}.el-col-xs-pull-22{position:relative;right:91.6667%}.el-col-xs-push-22{position:relative;left:91.6667%}.el-col-xs-23{flex:0 0 95.8333%;max-width:95.8333%;display:block}.el-col-xs-23.is-guttered{display:block}.el-col-xs-offset-23{margin-left:95.8333%}.el-col-xs-pull-23{position:relative;right:95.8333%}.el-col-xs-push-23{position:relative;left:95.8333%}.el-col-xs-24{flex:0 0 100%;max-width:100%;display:block}.el-col-xs-24.is-guttered{display:block}.el-col-xs-offset-24{margin-left:100%}.el-col-xs-pull-24{position:relative;right:100%}.el-col-xs-push-24{position:relative;left:100%}}@media only screen and (min-width:768px){.el-col-sm-0{flex:0 0;max-width:0%;display:none}.el-col-sm-0.is-guttered{display:none}.el-col-sm-offset-0{margin-left:0%}.el-col-sm-pull-0{position:relative;right:0%}.el-col-sm-push-0{position:relative;left:0%}.el-col-sm-1{flex:0 0 4.16667%;max-width:4.16667%;display:block}.el-col-sm-1.is-guttered{display:block}.el-col-sm-offset-1{margin-left:4.16667%}.el-col-sm-pull-1{position:relative;right:4.16667%}.el-col-sm-push-1{position:relative;left:4.16667%}.el-col-sm-2{flex:0 0 8.33333%;max-width:8.33333%;display:block}.el-col-sm-2.is-guttered{display:block}.el-col-sm-offset-2{margin-left:8.33333%}.el-col-sm-pull-2{position:relative;right:8.33333%}.el-col-sm-push-2{position:relative;left:8.33333%}.el-col-sm-3{flex:0 0 12.5%;max-width:12.5%;display:block}.el-col-sm-3.is-guttered{display:block}.el-col-sm-offset-3{margin-left:12.5%}.el-col-sm-pull-3{position:relative;right:12.5%}.el-col-sm-push-3{position:relative;left:12.5%}.el-col-sm-4{flex:0 0 16.6667%;max-width:16.6667%;display:block}.el-col-sm-4.is-guttered{display:block}.el-col-sm-offset-4{margin-left:16.6667%}.el-col-sm-pull-4{position:relative;right:16.6667%}.el-col-sm-push-4{position:relative;left:16.6667%}.el-col-sm-5{flex:0 0 20.8333%;max-width:20.8333%;display:block}.el-col-sm-5.is-guttered{display:block}.el-col-sm-offset-5{margin-left:20.8333%}.el-col-sm-pull-5{position:relative;right:20.8333%}.el-col-sm-push-5{position:relative;left:20.8333%}.el-col-sm-6{flex:0 0 25%;max-width:25%;display:block}.el-col-sm-6.is-guttered{display:block}.el-col-sm-offset-6{margin-left:25%}.el-col-sm-pull-6{position:relative;right:25%}.el-col-sm-push-6{position:relative;left:25%}.el-col-sm-7{flex:0 0 29.1667%;max-width:29.1667%;display:block}.el-col-sm-7.is-guttered{display:block}.el-col-sm-offset-7{margin-left:29.1667%}.el-col-sm-pull-7{position:relative;right:29.1667%}.el-col-sm-push-7{position:relative;left:29.1667%}.el-col-sm-8{flex:0 0 33.3333%;max-width:33.3333%;display:block}.el-col-sm-8.is-guttered{display:block}.el-col-sm-offset-8{margin-left:33.3333%}.el-col-sm-pull-8{position:relative;right:33.3333%}.el-col-sm-push-8{position:relative;left:33.3333%}.el-col-sm-9{flex:0 0 37.5%;max-width:37.5%;display:block}.el-col-sm-9.is-guttered{display:block}.el-col-sm-offset-9{margin-left:37.5%}.el-col-sm-pull-9{position:relative;right:37.5%}.el-col-sm-push-9{position:relative;left:37.5%}.el-col-sm-10{flex:0 0 41.6667%;max-width:41.6667%;display:block}.el-col-sm-10.is-guttered{display:block}.el-col-sm-offset-10{margin-left:41.6667%}.el-col-sm-pull-10{position:relative;right:41.6667%}.el-col-sm-push-10{position:relative;left:41.6667%}.el-col-sm-11{flex:0 0 45.8333%;max-width:45.8333%;display:block}.el-col-sm-11.is-guttered{display:block}.el-col-sm-offset-11{margin-left:45.8333%}.el-col-sm-pull-11{position:relative;right:45.8333%}.el-col-sm-push-11{position:relative;left:45.8333%}.el-col-sm-12{flex:0 0 50%;max-width:50%;display:block}.el-col-sm-12.is-guttered{display:block}.el-col-sm-offset-12{margin-left:50%}.el-col-sm-pull-12{position:relative;right:50%}.el-col-sm-push-12{position:relative;left:50%}.el-col-sm-13{flex:0 0 54.1667%;max-width:54.1667%;display:block}.el-col-sm-13.is-guttered{display:block}.el-col-sm-offset-13{margin-left:54.1667%}.el-col-sm-pull-13{position:relative;right:54.1667%}.el-col-sm-push-13{position:relative;left:54.1667%}.el-col-sm-14{flex:0 0 58.3333%;max-width:58.3333%;display:block}.el-col-sm-14.is-guttered{display:block}.el-col-sm-offset-14{margin-left:58.3333%}.el-col-sm-pull-14{position:relative;right:58.3333%}.el-col-sm-push-14{position:relative;left:58.3333%}.el-col-sm-15{flex:0 0 62.5%;max-width:62.5%;display:block}.el-col-sm-15.is-guttered{display:block}.el-col-sm-offset-15{margin-left:62.5%}.el-col-sm-pull-15{position:relative;right:62.5%}.el-col-sm-push-15{position:relative;left:62.5%}.el-col-sm-16{flex:0 0 66.6667%;max-width:66.6667%;display:block}.el-col-sm-16.is-guttered{display:block}.el-col-sm-offset-16{margin-left:66.6667%}.el-col-sm-pull-16{position:relative;right:66.6667%}.el-col-sm-push-16{position:relative;left:66.6667%}.el-col-sm-17{flex:0 0 70.8333%;max-width:70.8333%;display:block}.el-col-sm-17.is-guttered{display:block}.el-col-sm-offset-17{margin-left:70.8333%}.el-col-sm-pull-17{position:relative;right:70.8333%}.el-col-sm-push-17{position:relative;left:70.8333%}.el-col-sm-18{flex:0 0 75%;max-width:75%;display:block}.el-col-sm-18.is-guttered{display:block}.el-col-sm-offset-18{margin-left:75%}.el-col-sm-pull-18{position:relative;right:75%}.el-col-sm-push-18{position:relative;left:75%}.el-col-sm-19{flex:0 0 79.1667%;max-width:79.1667%;display:block}.el-col-sm-19.is-guttered{display:block}.el-col-sm-offset-19{margin-left:79.1667%}.el-col-sm-pull-19{position:relative;right:79.1667%}.el-col-sm-push-19{position:relative;left:79.1667%}.el-col-sm-20{flex:0 0 83.3333%;max-width:83.3333%;display:block}.el-col-sm-20.is-guttered{display:block}.el-col-sm-offset-20{margin-left:83.3333%}.el-col-sm-pull-20{position:relative;right:83.3333%}.el-col-sm-push-20{position:relative;left:83.3333%}.el-col-sm-21{flex:0 0 87.5%;max-width:87.5%;display:block}.el-col-sm-21.is-guttered{display:block}.el-col-sm-offset-21{margin-left:87.5%}.el-col-sm-pull-21{position:relative;right:87.5%}.el-col-sm-push-21{position:relative;left:87.5%}.el-col-sm-22{flex:0 0 91.6667%;max-width:91.6667%;display:block}.el-col-sm-22.is-guttered{display:block}.el-col-sm-offset-22{margin-left:91.6667%}.el-col-sm-pull-22{position:relative;right:91.6667%}.el-col-sm-push-22{position:relative;left:91.6667%}.el-col-sm-23{flex:0 0 95.8333%;max-width:95.8333%;display:block}.el-col-sm-23.is-guttered{display:block}.el-col-sm-offset-23{margin-left:95.8333%}.el-col-sm-pull-23{position:relative;right:95.8333%}.el-col-sm-push-23{position:relative;left:95.8333%}.el-col-sm-24{flex:0 0 100%;max-width:100%;display:block}.el-col-sm-24.is-guttered{display:block}.el-col-sm-offset-24{margin-left:100%}.el-col-sm-pull-24{position:relative;right:100%}.el-col-sm-push-24{position:relative;left:100%}}@media only screen and (min-width:992px){.el-col-md-0{flex:0 0;max-width:0%;display:none}.el-col-md-0.is-guttered{display:none}.el-col-md-offset-0{margin-left:0%}.el-col-md-pull-0{position:relative;right:0%}.el-col-md-push-0{position:relative;left:0%}.el-col-md-1{flex:0 0 4.16667%;max-width:4.16667%;display:block}.el-col-md-1.is-guttered{display:block}.el-col-md-offset-1{margin-left:4.16667%}.el-col-md-pull-1{position:relative;right:4.16667%}.el-col-md-push-1{position:relative;left:4.16667%}.el-col-md-2{flex:0 0 8.33333%;max-width:8.33333%;display:block}.el-col-md-2.is-guttered{display:block}.el-col-md-offset-2{margin-left:8.33333%}.el-col-md-pull-2{position:relative;right:8.33333%}.el-col-md-push-2{position:relative;left:8.33333%}.el-col-md-3{flex:0 0 12.5%;max-width:12.5%;display:block}.el-col-md-3.is-guttered{display:block}.el-col-md-offset-3{margin-left:12.5%}.el-col-md-pull-3{position:relative;right:12.5%}.el-col-md-push-3{position:relative;left:12.5%}.el-col-md-4{flex:0 0 16.6667%;max-width:16.6667%;display:block}.el-col-md-4.is-guttered{display:block}.el-col-md-offset-4{margin-left:16.6667%}.el-col-md-pull-4{position:relative;right:16.6667%}.el-col-md-push-4{position:relative;left:16.6667%}.el-col-md-5{flex:0 0 20.8333%;max-width:20.8333%;display:block}.el-col-md-5.is-guttered{display:block}.el-col-md-offset-5{margin-left:20.8333%}.el-col-md-pull-5{position:relative;right:20.8333%}.el-col-md-push-5{position:relative;left:20.8333%}.el-col-md-6{flex:0 0 25%;max-width:25%;display:block}.el-col-md-6.is-guttered{display:block}.el-col-md-offset-6{margin-left:25%}.el-col-md-pull-6{position:relative;right:25%}.el-col-md-push-6{position:relative;left:25%}.el-col-md-7{flex:0 0 29.1667%;max-width:29.1667%;display:block}.el-col-md-7.is-guttered{display:block}.el-col-md-offset-7{margin-left:29.1667%}.el-col-md-pull-7{position:relative;right:29.1667%}.el-col-md-push-7{position:relative;left:29.1667%}.el-col-md-8{flex:0 0 33.3333%;max-width:33.3333%;display:block}.el-col-md-8.is-guttered{display:block}.el-col-md-offset-8{margin-left:33.3333%}.el-col-md-pull-8{position:relative;right:33.3333%}.el-col-md-push-8{position:relative;left:33.3333%}.el-col-md-9{flex:0 0 37.5%;max-width:37.5%;display:block}.el-col-md-9.is-guttered{display:block}.el-col-md-offset-9{margin-left:37.5%}.el-col-md-pull-9{position:relative;right:37.5%}.el-col-md-push-9{position:relative;left:37.5%}.el-col-md-10{flex:0 0 41.6667%;max-width:41.6667%;display:block}.el-col-md-10.is-guttered{display:block}.el-col-md-offset-10{margin-left:41.6667%}.el-col-md-pull-10{position:relative;right:41.6667%}.el-col-md-push-10{position:relative;left:41.6667%}.el-col-md-11{flex:0 0 45.8333%;max-width:45.8333%;display:block}.el-col-md-11.is-guttered{display:block}.el-col-md-offset-11{margin-left:45.8333%}.el-col-md-pull-11{position:relative;right:45.8333%}.el-col-md-push-11{position:relative;left:45.8333%}.el-col-md-12{flex:0 0 50%;max-width:50%;display:block}.el-col-md-12.is-guttered{display:block}.el-col-md-offset-12{margin-left:50%}.el-col-md-pull-12{position:relative;right:50%}.el-col-md-push-12{position:relative;left:50%}.el-col-md-13{flex:0 0 54.1667%;max-width:54.1667%;display:block}.el-col-md-13.is-guttered{display:block}.el-col-md-offset-13{margin-left:54.1667%}.el-col-md-pull-13{position:relative;right:54.1667%}.el-col-md-push-13{position:relative;left:54.1667%}.el-col-md-14{flex:0 0 58.3333%;max-width:58.3333%;display:block}.el-col-md-14.is-guttered{display:block}.el-col-md-offset-14{margin-left:58.3333%}.el-col-md-pull-14{position:relative;right:58.3333%}.el-col-md-push-14{position:relative;left:58.3333%}.el-col-md-15{flex:0 0 62.5%;max-width:62.5%;display:block}.el-col-md-15.is-guttered{display:block}.el-col-md-offset-15{margin-left:62.5%}.el-col-md-pull-15{position:relative;right:62.5%}.el-col-md-push-15{position:relative;left:62.5%}.el-col-md-16{flex:0 0 66.6667%;max-width:66.6667%;display:block}.el-col-md-16.is-guttered{display:block}.el-col-md-offset-16{margin-left:66.6667%}.el-col-md-pull-16{position:relative;right:66.6667%}.el-col-md-push-16{position:relative;left:66.6667%}.el-col-md-17{flex:0 0 70.8333%;max-width:70.8333%;display:block}.el-col-md-17.is-guttered{display:block}.el-col-md-offset-17{margin-left:70.8333%}.el-col-md-pull-17{position:relative;right:70.8333%}.el-col-md-push-17{position:relative;left:70.8333%}.el-col-md-18{flex:0 0 75%;max-width:75%;display:block}.el-col-md-18.is-guttered{display:block}.el-col-md-offset-18{margin-left:75%}.el-col-md-pull-18{position:relative;right:75%}.el-col-md-push-18{position:relative;left:75%}.el-col-md-19{flex:0 0 79.1667%;max-width:79.1667%;display:block}.el-col-md-19.is-guttered{display:block}.el-col-md-offset-19{margin-left:79.1667%}.el-col-md-pull-19{position:relative;right:79.1667%}.el-col-md-push-19{position:relative;left:79.1667%}.el-col-md-20{flex:0 0 83.3333%;max-width:83.3333%;display:block}.el-col-md-20.is-guttered{display:block}.el-col-md-offset-20{margin-left:83.3333%}.el-col-md-pull-20{position:relative;right:83.3333%}.el-col-md-push-20{position:relative;left:83.3333%}.el-col-md-21{flex:0 0 87.5%;max-width:87.5%;display:block}.el-col-md-21.is-guttered{display:block}.el-col-md-offset-21{margin-left:87.5%}.el-col-md-pull-21{position:relative;right:87.5%}.el-col-md-push-21{position:relative;left:87.5%}.el-col-md-22{flex:0 0 91.6667%;max-width:91.6667%;display:block}.el-col-md-22.is-guttered{display:block}.el-col-md-offset-22{margin-left:91.6667%}.el-col-md-pull-22{position:relative;right:91.6667%}.el-col-md-push-22{position:relative;left:91.6667%}.el-col-md-23{flex:0 0 95.8333%;max-width:95.8333%;display:block}.el-col-md-23.is-guttered{display:block}.el-col-md-offset-23{margin-left:95.8333%}.el-col-md-pull-23{position:relative;right:95.8333%}.el-col-md-push-23{position:relative;left:95.8333%}.el-col-md-24{flex:0 0 100%;max-width:100%;display:block}.el-col-md-24.is-guttered{display:block}.el-col-md-offset-24{margin-left:100%}.el-col-md-pull-24{position:relative;right:100%}.el-col-md-push-24{position:relative;left:100%}}@media only screen and (min-width:1200px){.el-col-lg-0{flex:0 0;max-width:0%;display:none}.el-col-lg-0.is-guttered{display:none}.el-col-lg-offset-0{margin-left:0%}.el-col-lg-pull-0{position:relative;right:0%}.el-col-lg-push-0{position:relative;left:0%}.el-col-lg-1{flex:0 0 4.16667%;max-width:4.16667%;display:block}.el-col-lg-1.is-guttered{display:block}.el-col-lg-offset-1{margin-left:4.16667%}.el-col-lg-pull-1{position:relative;right:4.16667%}.el-col-lg-push-1{position:relative;left:4.16667%}.el-col-lg-2{flex:0 0 8.33333%;max-width:8.33333%;display:block}.el-col-lg-2.is-guttered{display:block}.el-col-lg-offset-2{margin-left:8.33333%}.el-col-lg-pull-2{position:relative;right:8.33333%}.el-col-lg-push-2{position:relative;left:8.33333%}.el-col-lg-3{flex:0 0 12.5%;max-width:12.5%;display:block}.el-col-lg-3.is-guttered{display:block}.el-col-lg-offset-3{margin-left:12.5%}.el-col-lg-pull-3{position:relative;right:12.5%}.el-col-lg-push-3{position:relative;left:12.5%}.el-col-lg-4{flex:0 0 16.6667%;max-width:16.6667%;display:block}.el-col-lg-4.is-guttered{display:block}.el-col-lg-offset-4{margin-left:16.6667%}.el-col-lg-pull-4{position:relative;right:16.6667%}.el-col-lg-push-4{position:relative;left:16.6667%}.el-col-lg-5{flex:0 0 20.8333%;max-width:20.8333%;display:block}.el-col-lg-5.is-guttered{display:block}.el-col-lg-offset-5{margin-left:20.8333%}.el-col-lg-pull-5{position:relative;right:20.8333%}.el-col-lg-push-5{position:relative;left:20.8333%}.el-col-lg-6{flex:0 0 25%;max-width:25%;display:block}.el-col-lg-6.is-guttered{display:block}.el-col-lg-offset-6{margin-left:25%}.el-col-lg-pull-6{position:relative;right:25%}.el-col-lg-push-6{position:relative;left:25%}.el-col-lg-7{flex:0 0 29.1667%;max-width:29.1667%;display:block}.el-col-lg-7.is-guttered{display:block}.el-col-lg-offset-7{margin-left:29.1667%}.el-col-lg-pull-7{position:relative;right:29.1667%}.el-col-lg-push-7{position:relative;left:29.1667%}.el-col-lg-8{flex:0 0 33.3333%;max-width:33.3333%;display:block}.el-col-lg-8.is-guttered{display:block}.el-col-lg-offset-8{margin-left:33.3333%}.el-col-lg-pull-8{position:relative;right:33.3333%}.el-col-lg-push-8{position:relative;left:33.3333%}.el-col-lg-9{flex:0 0 37.5%;max-width:37.5%;display:block}.el-col-lg-9.is-guttered{display:block}.el-col-lg-offset-9{margin-left:37.5%}.el-col-lg-pull-9{position:relative;right:37.5%}.el-col-lg-push-9{position:relative;left:37.5%}.el-col-lg-10{flex:0 0 41.6667%;max-width:41.6667%;display:block}.el-col-lg-10.is-guttered{display:block}.el-col-lg-offset-10{margin-left:41.6667%}.el-col-lg-pull-10{position:relative;right:41.6667%}.el-col-lg-push-10{position:relative;left:41.6667%}.el-col-lg-11{flex:0 0 45.8333%;max-width:45.8333%;display:block}.el-col-lg-11.is-guttered{display:block}.el-col-lg-offset-11{margin-left:45.8333%}.el-col-lg-pull-11{position:relative;right:45.8333%}.el-col-lg-push-11{position:relative;left:45.8333%}.el-col-lg-12{flex:0 0 50%;max-width:50%;display:block}.el-col-lg-12.is-guttered{display:block}.el-col-lg-offset-12{margin-left:50%}.el-col-lg-pull-12{position:relative;right:50%}.el-col-lg-push-12{position:relative;left:50%}.el-col-lg-13{flex:0 0 54.1667%;max-width:54.1667%;display:block}.el-col-lg-13.is-guttered{display:block}.el-col-lg-offset-13{margin-left:54.1667%}.el-col-lg-pull-13{position:relative;right:54.1667%}.el-col-lg-push-13{position:relative;left:54.1667%}.el-col-lg-14{flex:0 0 58.3333%;max-width:58.3333%;display:block}.el-col-lg-14.is-guttered{display:block}.el-col-lg-offset-14{margin-left:58.3333%}.el-col-lg-pull-14{position:relative;right:58.3333%}.el-col-lg-push-14{position:relative;left:58.3333%}.el-col-lg-15{flex:0 0 62.5%;max-width:62.5%;display:block}.el-col-lg-15.is-guttered{display:block}.el-col-lg-offset-15{margin-left:62.5%}.el-col-lg-pull-15{position:relative;right:62.5%}.el-col-lg-push-15{position:relative;left:62.5%}.el-col-lg-16{flex:0 0 66.6667%;max-width:66.6667%;display:block}.el-col-lg-16.is-guttered{display:block}.el-col-lg-offset-16{margin-left:66.6667%}.el-col-lg-pull-16{position:relative;right:66.6667%}.el-col-lg-push-16{position:relative;left:66.6667%}.el-col-lg-17{flex:0 0 70.8333%;max-width:70.8333%;display:block}.el-col-lg-17.is-guttered{display:block}.el-col-lg-offset-17{margin-left:70.8333%}.el-col-lg-pull-17{position:relative;right:70.8333%}.el-col-lg-push-17{position:relative;left:70.8333%}.el-col-lg-18{flex:0 0 75%;max-width:75%;display:block}.el-col-lg-18.is-guttered{display:block}.el-col-lg-offset-18{margin-left:75%}.el-col-lg-pull-18{position:relative;right:75%}.el-col-lg-push-18{position:relative;left:75%}.el-col-lg-19{flex:0 0 79.1667%;max-width:79.1667%;display:block}.el-col-lg-19.is-guttered{display:block}.el-col-lg-offset-19{margin-left:79.1667%}.el-col-lg-pull-19{position:relative;right:79.1667%}.el-col-lg-push-19{position:relative;left:79.1667%}.el-col-lg-20{flex:0 0 83.3333%;max-width:83.3333%;display:block}.el-col-lg-20.is-guttered{display:block}.el-col-lg-offset-20{margin-left:83.3333%}.el-col-lg-pull-20{position:relative;right:83.3333%}.el-col-lg-push-20{position:relative;left:83.3333%}.el-col-lg-21{flex:0 0 87.5%;max-width:87.5%;display:block}.el-col-lg-21.is-guttered{display:block}.el-col-lg-offset-21{margin-left:87.5%}.el-col-lg-pull-21{position:relative;right:87.5%}.el-col-lg-push-21{position:relative;left:87.5%}.el-col-lg-22{flex:0 0 91.6667%;max-width:91.6667%;display:block}.el-col-lg-22.is-guttered{display:block}.el-col-lg-offset-22{margin-left:91.6667%}.el-col-lg-pull-22{position:relative;right:91.6667%}.el-col-lg-push-22{position:relative;left:91.6667%}.el-col-lg-23{flex:0 0 95.8333%;max-width:95.8333%;display:block}.el-col-lg-23.is-guttered{display:block}.el-col-lg-offset-23{margin-left:95.8333%}.el-col-lg-pull-23{position:relative;right:95.8333%}.el-col-lg-push-23{position:relative;left:95.8333%}.el-col-lg-24{flex:0 0 100%;max-width:100%;display:block}.el-col-lg-24.is-guttered{display:block}.el-col-lg-offset-24{margin-left:100%}.el-col-lg-pull-24{position:relative;right:100%}.el-col-lg-push-24{position:relative;left:100%}}@media only screen and (min-width:1920px){.el-col-xl-0{flex:0 0;max-width:0%;display:none}.el-col-xl-0.is-guttered{display:none}.el-col-xl-offset-0{margin-left:0%}.el-col-xl-pull-0{position:relative;right:0%}.el-col-xl-push-0{position:relative;left:0%}.el-col-xl-1{flex:0 0 4.16667%;max-width:4.16667%;display:block}.el-col-xl-1.is-guttered{display:block}.el-col-xl-offset-1{margin-left:4.16667%}.el-col-xl-pull-1{position:relative;right:4.16667%}.el-col-xl-push-1{position:relative;left:4.16667%}.el-col-xl-2{flex:0 0 8.33333%;max-width:8.33333%;display:block}.el-col-xl-2.is-guttered{display:block}.el-col-xl-offset-2{margin-left:8.33333%}.el-col-xl-pull-2{position:relative;right:8.33333%}.el-col-xl-push-2{position:relative;left:8.33333%}.el-col-xl-3{flex:0 0 12.5%;max-width:12.5%;display:block}.el-col-xl-3.is-guttered{display:block}.el-col-xl-offset-3{margin-left:12.5%}.el-col-xl-pull-3{position:relative;right:12.5%}.el-col-xl-push-3{position:relative;left:12.5%}.el-col-xl-4{flex:0 0 16.6667%;max-width:16.6667%;display:block}.el-col-xl-4.is-guttered{display:block}.el-col-xl-offset-4{margin-left:16.6667%}.el-col-xl-pull-4{position:relative;right:16.6667%}.el-col-xl-push-4{position:relative;left:16.6667%}.el-col-xl-5{flex:0 0 20.8333%;max-width:20.8333%;display:block}.el-col-xl-5.is-guttered{display:block}.el-col-xl-offset-5{margin-left:20.8333%}.el-col-xl-pull-5{position:relative;right:20.8333%}.el-col-xl-push-5{position:relative;left:20.8333%}.el-col-xl-6{flex:0 0 25%;max-width:25%;display:block}.el-col-xl-6.is-guttered{display:block}.el-col-xl-offset-6{margin-left:25%}.el-col-xl-pull-6{position:relative;right:25%}.el-col-xl-push-6{position:relative;left:25%}.el-col-xl-7{flex:0 0 29.1667%;max-width:29.1667%;display:block}.el-col-xl-7.is-guttered{display:block}.el-col-xl-offset-7{margin-left:29.1667%}.el-col-xl-pull-7{position:relative;right:29.1667%}.el-col-xl-push-7{position:relative;left:29.1667%}.el-col-xl-8{flex:0 0 33.3333%;max-width:33.3333%;display:block}.el-col-xl-8.is-guttered{display:block}.el-col-xl-offset-8{margin-left:33.3333%}.el-col-xl-pull-8{position:relative;right:33.3333%}.el-col-xl-push-8{position:relative;left:33.3333%}.el-col-xl-9{flex:0 0 37.5%;max-width:37.5%;display:block}.el-col-xl-9.is-guttered{display:block}.el-col-xl-offset-9{margin-left:37.5%}.el-col-xl-pull-9{position:relative;right:37.5%}.el-col-xl-push-9{position:relative;left:37.5%}.el-col-xl-10{flex:0 0 41.6667%;max-width:41.6667%;display:block}.el-col-xl-10.is-guttered{display:block}.el-col-xl-offset-10{margin-left:41.6667%}.el-col-xl-pull-10{position:relative;right:41.6667%}.el-col-xl-push-10{position:relative;left:41.6667%}.el-col-xl-11{flex:0 0 45.8333%;max-width:45.8333%;display:block}.el-col-xl-11.is-guttered{display:block}.el-col-xl-offset-11{margin-left:45.8333%}.el-col-xl-pull-11{position:relative;right:45.8333%}.el-col-xl-push-11{position:relative;left:45.8333%}.el-col-xl-12{flex:0 0 50%;max-width:50%;display:block}.el-col-xl-12.is-guttered{display:block}.el-col-xl-offset-12{margin-left:50%}.el-col-xl-pull-12{position:relative;right:50%}.el-col-xl-push-12{position:relative;left:50%}.el-col-xl-13{flex:0 0 54.1667%;max-width:54.1667%;display:block}.el-col-xl-13.is-guttered{display:block}.el-col-xl-offset-13{margin-left:54.1667%}.el-col-xl-pull-13{position:relative;right:54.1667%}.el-col-xl-push-13{position:relative;left:54.1667%}.el-col-xl-14{flex:0 0 58.3333%;max-width:58.3333%;display:block}.el-col-xl-14.is-guttered{display:block}.el-col-xl-offset-14{margin-left:58.3333%}.el-col-xl-pull-14{position:relative;right:58.3333%}.el-col-xl-push-14{position:relative;left:58.3333%}.el-col-xl-15{flex:0 0 62.5%;max-width:62.5%;display:block}.el-col-xl-15.is-guttered{display:block}.el-col-xl-offset-15{margin-left:62.5%}.el-col-xl-pull-15{position:relative;right:62.5%}.el-col-xl-push-15{position:relative;left:62.5%}.el-col-xl-16{flex:0 0 66.6667%;max-width:66.6667%;display:block}.el-col-xl-16.is-guttered{display:block}.el-col-xl-offset-16{margin-left:66.6667%}.el-col-xl-pull-16{position:relative;right:66.6667%}.el-col-xl-push-16{position:relative;left:66.6667%}.el-col-xl-17{flex:0 0 70.8333%;max-width:70.8333%;display:block}.el-col-xl-17.is-guttered{display:block}.el-col-xl-offset-17{margin-left:70.8333%}.el-col-xl-pull-17{position:relative;right:70.8333%}.el-col-xl-push-17{position:relative;left:70.8333%}.el-col-xl-18{flex:0 0 75%;max-width:75%;display:block}.el-col-xl-18.is-guttered{display:block}.el-col-xl-offset-18{margin-left:75%}.el-col-xl-pull-18{position:relative;right:75%}.el-col-xl-push-18{position:relative;left:75%}.el-col-xl-19{flex:0 0 79.1667%;max-width:79.1667%;display:block}.el-col-xl-19.is-guttered{display:block}.el-col-xl-offset-19{margin-left:79.1667%}.el-col-xl-pull-19{position:relative;right:79.1667%}.el-col-xl-push-19{position:relative;left:79.1667%}.el-col-xl-20{flex:0 0 83.3333%;max-width:83.3333%;display:block}.el-col-xl-20.is-guttered{display:block}.el-col-xl-offset-20{margin-left:83.3333%}.el-col-xl-pull-20{position:relative;right:83.3333%}.el-col-xl-push-20{position:relative;left:83.3333%}.el-col-xl-21{flex:0 0 87.5%;max-width:87.5%;display:block}.el-col-xl-21.is-guttered{display:block}.el-col-xl-offset-21{margin-left:87.5%}.el-col-xl-pull-21{position:relative;right:87.5%}.el-col-xl-push-21{position:relative;left:87.5%}.el-col-xl-22{flex:0 0 91.6667%;max-width:91.6667%;display:block}.el-col-xl-22.is-guttered{display:block}.el-col-xl-offset-22{margin-left:91.6667%}.el-col-xl-pull-22{position:relative;right:91.6667%}.el-col-xl-push-22{position:relative;left:91.6667%}.el-col-xl-23{flex:0 0 95.8333%;max-width:95.8333%;display:block}.el-col-xl-23.is-guttered{display:block}.el-col-xl-offset-23{margin-left:95.8333%}.el-col-xl-pull-23{position:relative;right:95.8333%}.el-col-xl-push-23{position:relative;left:95.8333%}.el-col-xl-24{flex:0 0 100%;max-width:100%;display:block}.el-col-xl-24.is-guttered{display:block}.el-col-xl-offset-24{margin-left:100%}.el-col-xl-pull-24{position:relative;right:100%}.el-col-xl-push-24{position:relative;left:100%}}}@layer element-plus{.el-checkbox-group{font-size:0;line-height:0}}@layer element-plus{.el-radio{--el-radio-font-size:var(--el-font-size-base);--el-radio-text-color:var(--el-text-color-regular);--el-radio-font-weight:var(--el-font-weight-primary);--el-radio-input-height:14px;--el-radio-input-width:14px;--el-radio-input-border-radius:var(--el-border-radius-circle);--el-radio-input-bg-color:var(--el-fill-color-blank);--el-radio-input-border:var(--el-border);--el-radio-input-border-color:var(--el-border-color);--el-radio-input-border-color-hover:var(--el-color-primary);color:var(--el-radio-text-color);font-weight:var(--el-radio-font-weight);cursor:pointer;white-space:nowrap;font-size:var(--el-font-size-base);-webkit-user-select:none;user-select:none;outline:none;align-items:center;height:32px;margin-right:30px;display:inline-flex;position:relative}.el-radio.el-radio--large{height:40px}.el-radio.el-radio--small{height:24px}.el-radio.is-bordered{border-radius:var(--el-border-radius-base);border:var(--el-border);box-sizing:border-box;padding:0 15px 0 9px}.el-radio.is-bordered.is-checked{border-color:var(--el-color-primary)}.el-radio.is-bordered.is-disabled{cursor:not-allowed;border-color:var(--el-border-color-lighter)}.el-radio.is-bordered.el-radio--large{border-radius:var(--el-border-radius-base);padding:0 19px 0 11px}.el-radio.is-bordered.el-radio--large .el-radio__label{font-size:var(--el-font-size-base)}.el-radio.is-bordered.el-radio--large .el-radio__inner{width:14px;height:14px}.el-radio.is-bordered.el-radio--small{border-radius:var(--el-border-radius-base);padding:0 11px 0 7px}.el-radio.is-bordered.el-radio--small .el-radio__label{font-size:12px}.el-radio.is-bordered.el-radio--small .el-radio__inner{width:12px;height:12px}.el-radio:last-child{margin-right:0}.el-radio__input{white-space:nowrap;cursor:pointer;vertical-align:middle;outline:none;display:inline-flex;position:relative}.el-radio__input.is-disabled .el-radio__inner{background-color:var(--el-disabled-bg-color);border-color:var(--el-disabled-border-color);cursor:not-allowed}.el-radio__input.is-disabled .el-radio__inner:after{cursor:not-allowed;background-color:var(--el-disabled-bg-color)}.el-radio__input.is-disabled .el-radio__inner+.el-radio__label{cursor:not-allowed}.el-radio__input.is-disabled.is-checked .el-radio__inner{background-color:var(--el-disabled-bg-color);border-color:var(--el-disabled-border-color)}.el-radio__input.is-disabled.is-checked .el-radio__inner:after{background-color:var(--el-text-color-placeholder)}.el-radio__input.is-disabled+span.el-radio__label{color:var(--el-text-color-placeholder);cursor:not-allowed}.el-radio__input.is-checked .el-radio__inner{border-color:var(--el-color-primary);background:var(--el-color-primary)}.el-radio__input.is-checked .el-radio__inner:after{background-color:var(--el-color-white);transform:translate(-50%,-50%)scale(1)}.el-radio__input.is-checked+.el-radio__label{color:var(--el-color-primary)}.el-radio__input.is-focus .el-radio__inner{border-color:var(--el-radio-input-border-color-hover)}.el-radio__inner{border:var(--el-radio-input-border);border-radius:var(--el-radio-input-border-radius);width:var(--el-radio-input-width);height:var(--el-radio-input-height);background-color:var(--el-radio-input-bg-color);cursor:pointer;box-sizing:border-box;transition:all .3s;display:inline-block;position:relative}.el-radio__inner:hover{border-color:var(--el-radio-input-border-color-hover)}.el-radio__inner:after{border-radius:var(--el-radio-input-border-radius);content:"";width:4px;height:4px;transition:transform .15s ease-in;position:absolute;top:50%;left:50%;transform:translate(-50%,-50%)scale(0)}.el-radio__original{opacity:0;z-index:-1;outline:none;margin:0;position:absolute;top:0;bottom:0;left:0;right:0}.el-radio__original:focus-visible+.el-radio__inner{outline:2px solid var(--el-radio-input-border-color-hover);outline-offset:1px;border-radius:var(--el-radio-input-border-radius)}.el-radio:focus:not(:focus-visible):not(.is-focus):not(:active):not(.is-disabled) .el-radio__inner{box-shadow:0 0 2px 2px var(--el-radio-input-border-color-hover)}.el-radio__label{font-size:var(--el-radio-font-size);padding-left:8px}.el-radio.el-radio--large .el-radio__label{font-size:14px}.el-radio.el-radio--large .el-radio__inner{width:14px;height:14px}.el-radio.el-radio--small .el-radio__label{font-size:12px}.el-radio.el-radio--small .el-radio__inner{width:12px;height:12px}}@layer element-plus{.el-radio-group{flex-wrap:wrap;align-items:center;font-size:0;display:inline-flex}}@layer element-plus;@layer element-plus{.el-date-table{-webkit-user-select:none;user-select:none;font-size:12px}.el-date-table.is-week-mode .el-date-table__row:hover .el-date-table-cell{background-color:var(--el-datepicker-inrange-bg-color)}.el-date-table.is-week-mode .el-date-table__row:hover td.available:hover{color:var(--el-datepicker-text-color)}.el-date-table.is-week-mode .el-date-table__row:hover td:first-child .el-date-table-cell{border-top-left-radius:15px;border-bottom-left-radius:15px;margin-left:5px}.el-date-table.is-week-mode .el-date-table__row:hover td:last-child .el-date-table-cell{border-top-right-radius:15px;border-bottom-right-radius:15px;margin-right:5px}.el-date-table.is-week-mode .el-date-table__row.current .el-date-table-cell{background-color:var(--el-datepicker-inrange-bg-color)}.el-date-table td{box-sizing:border-box;text-align:center;cursor:pointer;width:32px;height:30px;padding:4px 0;position:relative}.el-date-table td .el-date-table-cell{box-sizing:border-box;height:30px;padding:3px 0}.el-date-table td .el-date-table-cell .el-date-table-cell__text{border-radius:50%;width:24px;height:24px;margin:0 auto;line-height:24px;display:block;position:absolute;left:50%;transform:translate(-50%)}.el-date-table td.next-month,.el-date-table td.prev-month{color:var(--el-datepicker-off-text-color)}.el-date-table td.today{position:relative}.el-date-table td.today .el-date-table-cell__text{color:var(--el-color-primary);font-weight:700}.el-date-table td.today.start-date .el-date-table-cell__text,.el-date-table td.today.end-date .el-date-table-cell__text{color:#fff}.el-date-table td.available:hover{color:var(--el-datepicker-hover-text-color)}.el-date-table td.in-range .el-date-table-cell{background-color:var(--el-datepicker-inrange-bg-color)}.el-date-table td.in-range .el-date-table-cell:hover{background-color:var(--el-datepicker-inrange-hover-bg-color)}.el-date-table td.current:not(.disabled) .el-date-table-cell__text{color:#fff;background-color:var(--el-datepicker-active-color)}.el-date-table td.current:not(.disabled):focus-visible .el-date-table-cell__text{outline:2px solid var(--el-datepicker-active-color);outline-offset:1px}.el-date-table td.start-date .el-date-table-cell,.el-date-table td.end-date .el-date-table-cell{color:#fff}.el-date-table td.start-date .el-date-table-cell__text,.el-date-table td.end-date .el-date-table-cell__text{background-color:var(--el-datepicker-active-color)}.el-date-table td.start-date .el-date-table-cell{border-top-left-radius:15px;border-bottom-left-radius:15px;margin-left:5px}.el-date-table td.end-date .el-date-table-cell{border-top-right-radius:15px;border-bottom-right-radius:15px;margin-right:5px}.el-date-table td.disabled .el-date-table-cell{background-color:var(--el-fill-color-light);opacity:1;cursor:not-allowed;color:var(--el-text-color-placeholder)}.el-date-table td.selected .el-date-table-cell{border-radius:15px;margin-left:5px;margin-right:5px}.el-date-table td.selected .el-date-table-cell__text{background-color:var(--el-datepicker-active-color);color:#fff;border-radius:15px}.el-date-table td.week{color:var(--el-datepicker-off-text-color);cursor:default;font-size:80%}.el-date-table td:focus{outline:none}.el-date-table th{color:var(--el-datepicker-header-text-color);border-bottom:solid 1px var(--el-border-color-lighter);padding:5px;font-weight:400}.el-date-table th.el-date-table__week-header{width:24px;padding:0}.el-month-table{border-collapse:collapse;margin:-1px;font-size:12px}.el-month-table td{text-align:center;cursor:pointer;width:68px;padding:8px 0;position:relative}.el-month-table td .el-date-table-cell{box-sizing:border-box;height:48px;padding:6px 0}.el-month-table td.today .el-date-table-cell__text{color:var(--el-color-primary);font-weight:700}.el-month-table td.today.start-date .el-date-table-cell__text,.el-month-table td.today.end-date .el-date-table-cell__text{color:#fff}.el-month-table td.disabled .el-date-table-cell__text{background-color:var(--el-fill-color-light);cursor:not-allowed;color:var(--el-text-color-placeholder)}.el-month-table td.disabled .el-date-table-cell__text:hover{color:var(--el-text-color-placeholder)}.el-month-table td .el-date-table-cell__text{width:54px;height:36px;color:var(--el-datepicker-text-color);border-radius:18px;margin:0 auto;line-height:36px;display:block;position:absolute;left:50%;transform:translate(-50%)}.el-month-table td .el-date-table-cell__text:hover{color:var(--el-datepicker-hover-text-color)}.el-month-table td.in-range .el-date-table-cell{background-color:var(--el-datepicker-inrange-bg-color)}.el-month-table td.in-range .el-date-table-cell:hover{background-color:var(--el-datepicker-inrange-hover-bg-color)}.el-month-table td.start-date .el-date-table-cell,.el-month-table td.end-date .el-date-table-cell{color:#fff}.el-month-table td.start-date .el-date-table-cell__text,.el-month-table td.end-date .el-date-table-cell__text{color:#fff;background-color:var(--el-datepicker-active-color)}.el-month-table td.start-date .el-date-table-cell{border-top-left-radius:24px;border-bottom-left-radius:24px;margin-left:3px}.el-month-table td.end-date .el-date-table-cell{border-top-right-radius:24px;border-bottom-right-radius:24px;margin-right:3px}.el-month-table td.current:not(.disabled) .el-date-table-cell{border-radius:24px;margin-left:3px;margin-right:3px}.el-month-table td.current:not(.disabled) .el-date-table-cell__text{color:#fff;background-color:var(--el-datepicker-active-color)}.el-month-table td:focus-visible{outline:none}.el-month-table td:focus-visible .el-date-table-cell__text{outline:2px solid var(--el-datepicker-active-color);outline-offset:1px}.el-year-table{border-collapse:collapse;margin:-1px;font-size:12px}.el-year-table .el-icon{color:var(--el-datepicker-icon-color)}.el-year-table td{text-align:center;cursor:pointer;width:68px;padding:8px 0;position:relative}.el-year-table td .el-date-table-cell{box-sizing:border-box;height:48px;padding:6px 0}.el-year-table td.today .el-date-table-cell__text{color:var(--el-color-primary);font-weight:700}.el-year-table td.today.start-date .el-date-table-cell__text,.el-year-table td.today.end-date .el-date-table-cell__text{color:#fff}.el-year-table td.disabled .el-date-table-cell__text{background-color:var(--el-fill-color-light);cursor:not-allowed;color:var(--el-text-color-placeholder)}.el-year-table td.disabled .el-date-table-cell__text:hover{color:var(--el-text-color-placeholder)}.el-year-table td .el-date-table-cell__text{width:60px;height:36px;color:var(--el-datepicker-text-color);border-radius:18px;margin:0 auto;line-height:36px;display:block;position:absolute;left:50%;transform:translate(-50%)}.el-year-table td .el-date-table-cell__text:hover{color:var(--el-datepicker-hover-text-color)}.el-year-table td.in-range .el-date-table-cell{background-color:var(--el-datepicker-inrange-bg-color)}.el-year-table td.in-range .el-date-table-cell:hover{background-color:var(--el-datepicker-inrange-hover-bg-color)}.el-year-table td.start-date .el-date-table-cell,.el-year-table td.end-date .el-date-table-cell{color:#fff}.el-year-table td.start-date .el-date-table-cell__text,.el-year-table td.end-date .el-date-table-cell__text{color:#fff;background-color:var(--el-datepicker-active-color)}.el-year-table td.start-date .el-date-table-cell{border-top-left-radius:24px;border-bottom-left-radius:24px}.el-year-table td.end-date .el-date-table-cell{border-top-right-radius:24px;border-bottom-right-radius:24px}.el-year-table td.current:not(.disabled) .el-date-table-cell__text{color:#fff;background-color:var(--el-datepicker-active-color)}.el-year-table td:focus-visible{outline:none}.el-year-table td:focus-visible .el-date-table-cell__text{outline:2px solid var(--el-datepicker-active-color);outline-offset:1px}.el-time-spinner.has-seconds .el-time-spinner__wrapper{width:33.3%}.el-time-spinner__wrapper{vertical-align:top;width:50%;max-height:192px;display:inline-block;position:relative;overflow:auto}.el-time-spinner__wrapper.el-scrollbar__wrap:not(.el-scrollbar__wrap--hidden-default){padding-bottom:15px}.el-time-spinner__wrapper.is-arrow{box-sizing:border-box;text-align:center;overflow:hidden}.el-time-spinner__wrapper.is-arrow .el-time-spinner__list{transform:translateY(-32px)}.el-time-spinner__wrapper.is-arrow .el-time-spinner__item:hover:not(.is-disabled):not(.is-active){background:var(--el-fill-color-light);cursor:default}.el-time-spinner__arrow{color:var(--el-text-color-secondary);width:100%;z-index:var(--el-index-normal);text-align:center;cursor:pointer;height:30px;font-size:12px;line-height:30px;position:absolute;left:0}.el-time-spinner__arrow:hover{color:var(--el-color-primary)}.el-time-spinner__arrow.arrow-up{top:10px}.el-time-spinner__arrow.arrow-down{bottom:10px}.el-time-spinner__input.el-input{width:70%}.el-time-spinner__input.el-input .el-input__inner{text-align:center;padding:0}.el-time-spinner__list{text-align:center;margin:0;padding:0;list-style:none}.el-time-spinner__list:after,.el-time-spinner__list:before{content:"";width:100%;height:80px;display:block}.el-time-spinner__item{height:32px;color:var(--el-text-color-regular);font-size:12px;line-height:32px}.el-time-spinner__item:hover:not(.is-disabled):not(.is-active){background:var(--el-fill-color-light);cursor:pointer}.el-time-spinner__item.is-active:not(.is-disabled){color:var(--el-text-color-primary);font-weight:700}.el-time-spinner__item.is-disabled{color:var(--el-text-color-placeholder);cursor:not-allowed}.fade-in-linear-enter-active,.fade-in-linear-leave-active{transition:var(--el-transition-fade-linear)}.fade-in-linear-enter-from,.fade-in-linear-leave-to{opacity:0}.el-fade-in-linear-enter-active,.el-fade-in-linear-leave-active{transition:var(--el-transition-fade-linear)}.el-fade-in-linear-enter-from,.el-fade-in-linear-leave-to{opacity:0}.el-fade-in-enter-active,.el-fade-in-leave-active{transition:all var(--el-transition-duration) cubic-bezier(.55,0,.1,1)}.el-fade-in-enter-from,.el-fade-in-leave-active{opacity:0}.el-zoom-in-center-enter-active,.el-zoom-in-center-leave-active{transition:all var(--el-transition-duration) cubic-bezier(.55,0,.1,1)}.el-zoom-in-center-enter-from,.el-zoom-in-center-leave-active{opacity:0;transform:scaleX(0)}.el-zoom-in-top-enter-active,.el-zoom-in-top-leave-active{opacity:1;transition:var(--el-transition-md-fade);transform-origin:top;transform:scaleY(1)}.el-zoom-in-top-enter-active[data-popper-placement^=top],.el-zoom-in-top-leave-active[data-popper-placement^=top]{transform-origin:bottom}.el-zoom-in-top-enter-from,.el-zoom-in-top-leave-active{opacity:0;transform:scaleY(0)}.el-zoom-in-bottom-enter-active,.el-zoom-in-bottom-leave-active{opacity:1;transition:var(--el-transition-md-fade);transform-origin:bottom;transform:scaleY(1)}.el-zoom-in-bottom-enter-from,.el-zoom-in-bottom-leave-active{opacity:0;transform:scaleY(0)}.el-zoom-in-left-enter-active,.el-zoom-in-left-leave-active{opacity:1;transition:var(--el-transition-md-fade);transform-origin:0 0;transform:scale(1)}.el-zoom-in-left-enter-from,.el-zoom-in-left-leave-active{opacity:0;transform:scale(.45)}.collapse-transition{transition:var(--el-transition-duration) height ease-in-out,var(--el-transition-duration) padding-top ease-in-out,var(--el-transition-duration) padding-bottom ease-in-out}.el-collapse-transition-leave-active,.el-collapse-transition-enter-active{transition:var(--el-transition-duration) max-height ease-in-out,var(--el-transition-duration) padding-top ease-in-out,var(--el-transition-duration) padding-bottom ease-in-out}.horizontal-collapse-transition{transition:var(--el-transition-duration) width ease-in-out,var(--el-transition-duration) padding-left ease-in-out,var(--el-transition-duration) padding-right ease-in-out}.el-list-enter-active,.el-list-leave-active{transition:all 1s}.el-list-enter-from,.el-list-leave-to{opacity:0;transform:translateY(-30px)}.el-list-leave-active{position:absolute!important}.el-opacity-transition{transition:opacity var(--el-transition-duration) cubic-bezier(.55,0,.1,1)}.el-picker__popper{--el-datepicker-border-color:var(--el-disabled-border-color)}.el-picker__popper.el-popper{background:var(--el-bg-color-overlay);border:1px solid var(--el-datepicker-border-color);box-shadow:var(--el-box-shadow-light)}.el-picker__popper.el-popper .el-popper__arrow:before{border:1px solid var(--el-datepicker-border-color)}.el-picker__popper.el-popper[data-popper-placement^=top] .el-popper__arrow:before{border-top-color:#0000;border-left-color:#0000}.el-picker__popper.el-popper[data-popper-placement^=bottom] .el-popper__arrow:before{border-bottom-color:#0000;border-right-color:#0000}.el-picker__popper.el-popper[data-popper-placement^=left] .el-popper__arrow:before{border-bottom-color:#0000;border-left-color:#0000}.el-picker__popper.el-popper[data-popper-placement^=right] .el-popper__arrow:before{border-top-color:#0000;border-right-color:#0000}.el-date-editor{--el-date-editor-width:220px;--el-date-editor-monthrange-width:300px;--el-date-editor-daterange-width:350px;--el-date-editor-datetimerange-width:400px;--el-input-text-color:var(--el-text-color-regular);--el-input-border:var(--el-border);--el-input-hover-border:var(--el-border-color-hover);--el-input-focus-border:var(--el-color-primary);--el-input-transparent-border:0 0 0 1px transparent inset;--el-input-border-color:var(--el-border-color);--el-input-border-radius:var(--el-border-radius-base);--el-input-bg-color:var(--el-fill-color-blank);--el-input-icon-color:var(--el-text-color-placeholder);--el-input-placeholder-color:var(--el-text-color-placeholder);--el-input-hover-border-color:var(--el-border-color-hover);--el-input-clear-hover-color:var(--el-text-color-secondary);--el-input-focus-border-color:var(--el-color-primary);--el-input-width:100%;text-align:left;vertical-align:middle;position:relative}.el-date-editor.el-input__wrapper{box-shadow:0 0 0 1px var(--el-input-border-color,var(--el-border-color)) inset}.el-date-editor.el-input__wrapper:hover{box-shadow:0 0 0 1px var(--el-input-hover-border-color) inset}.el-date-editor.is-focus .el-input__wrapper{box-shadow:0 0 0 1px var(--el-input-focus-border-color) inset}.el-date-editor.el-input,.el-date-editor.el-input__wrapper{width:var(--el-date-editor-width);height:var(--el-input-height,var(--el-component-size))}.el-date-editor--monthrange{--el-date-editor-width:var(--el-date-editor-monthrange-width)}.el-date-editor--daterange,.el-date-editor--timerange{--el-date-editor-width:var(--el-date-editor-daterange-width)}.el-date-editor--datetimerange{--el-date-editor-width:var(--el-date-editor-datetimerange-width)}.el-date-editor--dates .el-input__wrapper{text-overflow:ellipsis;white-space:nowrap}.el-date-editor .close-icon,.el-date-editor .clear-icon{cursor:pointer}.el-date-editor .clear-icon:hover{color:var(--el-input-clear-hover-color)}.el-date-editor .el-range__icon{height:inherit;color:var(--el-text-color-placeholder);float:left;font-size:14px}.el-date-editor .el-range__icon svg{vertical-align:middle}.el-date-editor .el-range-input{-webkit-appearance:none;-moz-appearance:none;appearance:none;text-align:center;width:39%;height:30px;line-height:30px;font-size:var(--el-font-size-base);color:var(--el-text-color-regular);background-color:#0000;border:none;outline:none;margin:0;padding:0;display:inline-block}.el-date-editor .el-range-input::placeholder{color:var(--el-text-color-placeholder)}.el-date-editor .el-range-separator{overflow-wrap:break-word;height:100%;color:var(--el-text-color-primary);flex:1;justify-content:center;align-items:center;margin:0;padding:0 5px;font-size:14px;display:inline-flex}.el-date-editor .el-range__close-icon{color:var(--el-text-color-placeholder);height:inherit;width:unset;cursor:pointer;font-size:14px}.el-date-editor .el-range__close-icon:hover{color:var(--el-input-clear-hover-color)}.el-date-editor .el-range__close-icon svg{vertical-align:middle}.el-date-editor .el-range__close-icon--hidden{opacity:0;visibility:hidden}.el-range-editor.el-input__wrapper{vertical-align:middle;align-items:center;padding:0 10px;display:inline-flex}.el-range-editor.is-active,.el-range-editor.is-active:hover{box-shadow:0 0 0 1px var(--el-input-focus-border-color) inset}.el-range-editor--large{line-height:var(--el-component-size-large)}.el-range-editor--large.el-input__wrapper{height:var(--el-component-size-large)}.el-range-editor--large .el-range-separator{font-size:14px;line-height:40px}.el-range-editor--large .el-range-input{height:38px;font-size:14px;line-height:38px}.el-range-editor--small{line-height:var(--el-component-size-small)}.el-range-editor--small.el-input__wrapper{height:var(--el-component-size-small)}.el-range-editor--small .el-range-separator{font-size:12px;line-height:24px}.el-range-editor--small .el-range-input{height:22px;font-size:12px;line-height:22px}.el-range-editor.is-disabled{background-color:var(--el-disabled-bg-color);border-color:var(--el-disabled-border-color);color:var(--el-disabled-text-color);cursor:not-allowed}.el-range-editor.is-disabled:hover,.el-range-editor.is-disabled:focus{border-color:var(--el-disabled-border-color)}.el-range-editor.is-disabled input{background-color:var(--el-disabled-bg-color);color:var(--el-disabled-text-color);cursor:not-allowed}.el-range-editor.is-disabled input::placeholder{color:var(--el-text-color-placeholder)}.el-range-editor.is-disabled .el-range-separator{color:var(--el-disabled-text-color)}.el-picker-panel{color:var(--el-text-color-regular);background:var(--el-datepicker-bg-color);border-radius:var(--el-popper-border-radius,var(--el-border-radius-base));line-height:30px}.el-picker-panel .el-time-panel{border:solid 1px var(--el-datepicker-border-color);background-color:var(--el-datepicker-bg-color);box-shadow:var(--el-box-shadow-light);margin:5px 0}.el-picker-panel__body:after,.el-picker-panel__body-wrapper:after{content:"";clear:both;display:table}.el-picker-panel__content{margin:15px;position:relative}.el-picker-panel__footer{border-top:1px solid var(--el-datepicker-inner-border-color);text-align:right;background-color:var(--el-datepicker-bg-color);padding:4px 12px;font-size:0;position:relative}.el-picker-panel__shortcut{width:100%;color:var(--el-datepicker-text-color);text-align:left;cursor:pointer;background-color:#0000;border:0;outline:none;padding-left:12px;font-size:14px;line-height:28px;display:block}.el-picker-panel__shortcut:hover{color:var(--el-datepicker-hover-text-color)}.el-picker-panel__shortcut.active{color:var(--el-datepicker-active-color);background-color:#e6f1fe}.el-picker-panel__btn{border:1px solid var(--el-fill-color-darker);color:var(--el-text-color-primary);cursor:pointer;background-color:#0000;border-radius:2px;outline:none;padding:0 20px;font-size:12px;line-height:24px}.el-picker-panel__btn[disabled]{color:var(--el-text-color-disabled);cursor:not-allowed}.el-picker-panel__icon-btn{color:var(--el-datepicker-icon-color);cursor:pointer;background:0 0;border:0;outline:none;margin-top:8px;padding:1px 6px;font-size:12px;line-height:1}.el-picker-panel__icon-btn:hover{color:var(--el-datepicker-hover-text-color)}.el-picker-panel__icon-btn:focus-visible{color:var(--el-datepicker-hover-text-color)}.el-picker-panel__icon-btn.is-disabled{color:var(--el-text-color-disabled)}.el-picker-panel__icon-btn.is-disabled:hover{cursor:not-allowed}.el-picker-panel__icon-btn.is-disabled .el-icon{cursor:inherit}.el-picker-panel__icon-btn .el-icon{cursor:pointer;font-size:inherit}.el-picker-panel__link-btn{vertical-align:middle}.el-picker-panel.is-disabled .el-picker-panel__prev-btn{color:var(--el-text-color-disabled)}.el-picker-panel.is-disabled .el-picker-panel__prev-btn:hover{cursor:not-allowed}.el-picker-panel.is-disabled .el-picker-panel__prev-btn .el-icon{cursor:inherit}.el-picker-panel.is-disabled .el-picker-panel__next-btn{color:var(--el-text-color-disabled)}.el-picker-panel.is-disabled .el-picker-panel__next-btn:hover{cursor:not-allowed}.el-picker-panel.is-disabled .el-picker-panel__next-btn .el-icon{cursor:inherit}.el-picker-panel.is-disabled .el-picker-panel__icon-btn{color:var(--el-text-color-disabled)}.el-picker-panel.is-disabled .el-picker-panel__icon-btn:hover{cursor:not-allowed}.el-picker-panel.is-disabled .el-picker-panel__icon-btn .el-icon{cursor:inherit}.el-picker-panel.is-disabled .el-picker-panel__shortcut{color:var(--el-text-color-disabled)}.el-picker-panel.is-disabled .el-picker-panel__shortcut:hover{cursor:not-allowed}.el-picker-panel.is-disabled .el-picker-panel__shortcut .el-icon{cursor:inherit}.el-picker-panel [slot=sidebar],.el-picker-panel__sidebar{border-right:1px solid var(--el-datepicker-inner-border-color);box-sizing:border-box;width:110px;padding-top:6px;position:absolute;top:0;bottom:0;overflow:auto}.el-picker-panel [slot=sidebar]+.el-picker-panel__body,.el-picker-panel__sidebar+.el-picker-panel__body{margin-left:110px}.el-date-picker{--el-datepicker-text-color:var(--el-text-color-regular);--el-datepicker-off-text-color:var(--el-text-color-placeholder);--el-datepicker-header-text-color:var(--el-text-color-regular);--el-datepicker-icon-color:var(--el-text-color-primary);--el-datepicker-border-color:var(--el-disabled-border-color);--el-datepicker-inner-border-color:var(--el-border-color-light);--el-datepicker-inrange-bg-color:var(--el-border-color-extra-light);--el-datepicker-inrange-hover-bg-color:var(--el-border-color-extra-light);--el-datepicker-active-color:var(--el-color-primary);--el-datepicker-hover-text-color:var(--el-color-primary);--el-datepicker-bg-color:var(--el-bg-color-overlay);--el-fill-color-blank:var(--el-datepicker-bg-color);width:322px}.el-date-picker.has-sidebar.has-time{width:434px}.el-date-picker.has-sidebar{width:438px}.el-date-picker.has-time .el-picker-panel__body-wrapper{position:relative}.el-date-picker .el-picker-panel__content{width:292px}.el-date-picker table{table-layout:fixed;width:100%}.el-date-picker__editor-wrap{padding:0 5px;display:table-cell;position:relative}.el-date-picker__time-header{border-bottom:1px solid var(--el-datepicker-inner-border-color);box-sizing:border-box;width:100%;padding:8px 5px 5px;font-size:12px;display:table;position:relative}.el-date-picker__header{text-align:center;padding:12px 12px 0}.el-date-picker__header--bordered{border-bottom:solid 1px var(--el-border-color-lighter);margin-bottom:0;padding-bottom:12px}.el-date-picker__header--bordered+.el-picker-panel__content{margin-top:0}.el-date-picker__header-label{text-align:center;cursor:pointer;color:var(--el-text-color-regular);padding:0 5px;font-size:16px;font-weight:500;line-height:22px}.el-date-picker__header-label:hover{color:var(--el-datepicker-hover-text-color)}.el-date-picker__header-label:focus-visible{color:var(--el-datepicker-hover-text-color);outline:none}.el-date-picker__header-label.active{color:var(--el-datepicker-active-color)}.el-date-picker__prev-btn{float:left}.el-date-picker__next-btn{float:right}.el-date-picker__time-wrap{text-align:center;padding:10px}.el-date-picker__time-label{float:left;cursor:pointer;margin-left:10px;line-height:30px}.el-date-picker .el-time-panel{position:absolute}.el-date-picker.is-disabled .el-date-picker__header-label{color:var(--el-text-color-disabled)}.el-date-picker.is-disabled .el-date-picker__header-label:hover{cursor:not-allowed}.el-date-picker.is-disabled .el-date-picker__header-label .el-icon{cursor:inherit}.el-date-range-picker{--el-datepicker-text-color:var(--el-text-color-regular);--el-datepicker-off-text-color:var(--el-text-color-placeholder);--el-datepicker-header-text-color:var(--el-text-color-regular);--el-datepicker-icon-color:var(--el-text-color-primary);--el-datepicker-border-color:var(--el-disabled-border-color);--el-datepicker-inner-border-color:var(--el-border-color-light);--el-datepicker-inrange-bg-color:var(--el-border-color-extra-light);--el-datepicker-inrange-hover-bg-color:var(--el-border-color-extra-light);--el-datepicker-active-color:var(--el-color-primary);--el-datepicker-hover-text-color:var(--el-color-primary);--el-datepicker-bg-color:var(--el-bg-color-overlay);width:646px}.el-date-range-picker.has-sidebar{width:756px}.el-date-range-picker.has-time .el-picker-panel__body-wrapper{position:relative}.el-date-range-picker table{table-layout:fixed;width:100%}.el-date-range-picker .el-picker-panel__body{min-width:513px}.el-date-range-picker .el-picker-panel__content{margin:0}.el-date-range-picker.single-panel{width:322px}.el-date-range-picker.single-panel .el-picker-panel__body{min-width:322px}.el-date-range-picker.single-panel.has-sidebar.has-time{width:434px}.el-date-range-picker.single-panel.has-sidebar{width:438px}.el-date-range-picker.single-panel .el-picker-panel__content{width:292px;margin:15px;padding:0;display:block;position:relative}.el-date-range-picker__header{text-align:center;height:28px;position:relative}.el-date-range-picker__header [class*=arrow-left]{float:left}.el-date-range-picker__header [class*=arrow-right]{float:right}.el-date-range-picker__header div{margin-right:50px;font-size:16px;font-weight:500}.el-date-range-picker__header-label{text-align:center;cursor:pointer;color:var(--el-text-color-regular);padding:0 5px;font-size:16px;font-weight:500;line-height:22px}.el-date-range-picker__header-label:hover{color:var(--el-datepicker-hover-text-color)}.el-date-range-picker__header-label:focus-visible{color:var(--el-datepicker-hover-text-color);outline:none}.el-date-range-picker__header-label.active{color:var(--el-datepicker-active-color)}.el-date-range-picker__content{box-sizing:border-box;width:50%;margin:0;padding:16px;display:table-cell}.el-date-range-picker__content.is-left{border-right:1px solid var(--el-datepicker-inner-border-color)}.el-date-range-picker__content .el-date-range-picker__header div{margin-left:50px;margin-right:50px}.el-date-range-picker__editors-wrap{box-sizing:border-box;display:table-cell}.el-date-range-picker__editors-wrap.is-right{text-align:right}.el-date-range-picker__time-header{border-bottom:1px solid var(--el-datepicker-inner-border-color);box-sizing:border-box;width:100%;padding:8px 5px 5px;font-size:12px;display:table;position:relative}.el-date-range-picker__time-header>.el-icon-arrow-right{vertical-align:middle;color:var(--el-datepicker-icon-color);font-size:20px;display:table-cell}.el-date-range-picker__time-picker-wrap{padding:0 5px;display:table-cell;position:relative}.el-date-range-picker__time-picker-wrap .el-picker-panel{z-index:1;background:#fff;position:absolute;top:13px;right:0}.el-date-range-picker__time-picker-wrap .el-time-panel{position:absolute}.el-date-range-picker.is-disabled .el-date-range-picker__header-label{color:var(--el-text-color-disabled)}.el-date-range-picker.is-disabled .el-date-range-picker__header-label:hover{cursor:not-allowed}.el-date-range-picker.is-disabled .el-date-range-picker__header-label .el-icon{cursor:inherit}.el-time-range-picker{width:354px;overflow:visible}.el-time-range-picker__content{text-align:center;z-index:1;padding:10px;position:relative}.el-time-range-picker__cell{box-sizing:border-box;width:50%;margin:0;padding:4px 7px 7px;display:inline-block}.el-time-range-picker__header{text-align:center;margin-bottom:5px;font-size:14px}.el-time-range-picker__body{border:1px solid var(--el-datepicker-border-color);border-radius:2px}.el-time-panel{width:180px;z-index:var(--el-index-top);-webkit-user-select:none;user-select:none;box-sizing:content-box;border-radius:2px;position:relative;left:0}.el-time-panel__content{font-size:0;position:relative;overflow:hidden}.el-time-panel__content:after,.el-time-panel__content:before{content:"";z-index:-1;box-sizing:border-box;text-align:left;height:32px;margin-top:-16px;padding-top:6px;position:absolute;top:50%;left:0;right:0}.el-time-panel__content:after{margin-left:12%;margin-right:12%;left:50%}.el-time-panel__content:before{border-top:1px solid var(--el-border-color-light);border-bottom:1px solid var(--el-border-color-light);margin-left:12%;margin-right:12%;padding-left:50%}.el-time-panel__content.has-seconds:after{left:66.6667%}.el-time-panel__content.has-seconds:before{padding-left:33.3333%}.el-time-panel__footer{border-top:1px solid var(--el-timepicker-inner-border-color,var(--el-border-color-light));text-align:right;box-sizing:border-box;height:36px;padding:4px;line-height:25px}.el-time-panel__btn{cursor:pointer;color:var(--el-text-color-primary);background-color:#0000;border:none;outline:none;margin:0 5px;padding:0 5px;font-size:12px;line-height:28px}.el-time-panel__btn.confirm{color:var(--el-timepicker-active-color,var(--el-color-primary));font-weight:800}.el-picker-panel.is-border{border:solid 1px var(--el-border-color-lighter)}.el-picker-panel.is-border .el-picker-panel__body-wrapper{position:relative}.el-picker-panel.is-border.el-picker-panel [slot=sidebar],.el-picker-panel.is-border.el-picker-panel__sidebar{border-right:1px solid var(--el-datepicker-inner-border-color);box-sizing:border-box;width:110px;height:100%;padding-top:6px;position:absolute;top:0;overflow:auto}}@layer element-plus;@layer element-plus{.el-switch{--el-switch-on-color:var(--el-color-primary);--el-switch-off-color:var(--el-border-color);vertical-align:middle;align-items:center;height:32px;font-size:14px;line-height:20px;display:inline-flex;position:relative}.el-switch.is-disabled .el-switch__core,.el-switch.is-disabled .el-switch__label{cursor:not-allowed}.el-switch__label{transition:var(--el-transition-duration-fast);cursor:pointer;vertical-align:middle;height:20px;color:var(--el-text-color-primary);font-size:14px;font-weight:500;display:inline-block}.el-switch__label.is-active{color:var(--el-color-primary)}.el-switch__label--left{margin-right:10px}.el-switch__label--right{margin-left:10px}.el-switch__label *{font-size:14px;line-height:1;display:inline-block}.el-switch__label .el-icon{height:inherit}.el-switch__label .el-icon svg{vertical-align:middle}.el-switch__input{opacity:0;width:0;height:0;margin:0;position:absolute}.el-switch__input:focus-visible~.el-switch__core{outline:2px solid var(--el-switch-on-color);outline-offset:1px}.el-switch__core{border:1px solid var(--el-switch-border-color,var(--el-switch-off-color));box-sizing:border-box;background:var(--el-switch-off-color);cursor:pointer;min-width:40px;height:20px;transition:border-color var(--el-transition-duration),background-color var(--el-transition-duration);border-radius:10px;outline:none;align-items:center;display:inline-flex;position:relative}.el-switch__core .el-switch__inner{width:100%;transition:all var(--el-transition-duration);justify-content:center;align-items:center;height:16px;padding:0 4px 0 18px;display:flex;overflow:hidden}.el-switch__core .el-switch__inner-wrapper{color:var(--el-color-white);-webkit-user-select:none;user-select:none;text-overflow:ellipsis;white-space:nowrap;align-items:center;font-size:12px;display:flex;overflow:hidden}.el-switch__core .el-switch__action{border-radius:var(--el-border-radius-circle);transition:all var(--el-transition-duration);background-color:var(--el-color-white);width:16px;height:16px;color:var(--el-switch-off-color);justify-content:center;align-items:center;display:flex;position:absolute;left:1px}.el-switch.is-checked .el-switch__core{border-color:var(--el-switch-border-color,var(--el-switch-on-color));background-color:var(--el-switch-on-color)}.el-switch.is-checked .el-switch__core .el-switch__action{color:var(--el-switch-on-color);left:calc(100% - 17px)}.el-switch.is-checked .el-switch__core .el-switch__inner{padding:0 18px 0 4px}.el-switch.is-disabled{opacity:.6}.el-switch--wide .el-switch__label.el-switch__label--left span{left:10px}.el-switch--wide .el-switch__label.el-switch__label--right span{right:10px}.el-switch .label-fade-enter-from,.el-switch .label-fade-leave-active{opacity:0}.el-switch--large{height:40px;font-size:14px;line-height:24px}.el-switch--large .el-switch__label{height:24px;font-size:14px}.el-switch--large .el-switch__label *{font-size:14px}.el-switch--large .el-switch__core{border-radius:12px;min-width:50px;height:24px}.el-switch--large .el-switch__core .el-switch__inner{height:20px;padding:0 6px 0 22px}.el-switch--large .el-switch__core .el-switch__action{width:20px;height:20px}.el-switch--large.is-checked .el-switch__core .el-switch__action{left:calc(100% - 21px)}.el-switch--large.is-checked .el-switch__core .el-switch__inner{padding:0 22px 0 6px}.el-switch--small{height:24px;font-size:12px;line-height:16px}.el-switch--small .el-switch__label{height:16px;font-size:12px}.el-switch--small .el-switch__label *{font-size:12px}.el-switch--small .el-switch__core{border-radius:8px;min-width:30px;height:16px}.el-switch--small .el-switch__core .el-switch__inner{height:12px;padding:0 2px 0 14px}.el-switch--small .el-switch__core .el-switch__action{width:12px;height:12px}.el-switch--small.is-checked .el-switch__core .el-switch__action{left:calc(100% - 13px)}.el-switch--small.is-checked .el-switch__core .el-switch__inner{padding:0 14px 0 2px}}@layer element-plus{.el-overlay.is-drawer{overflow:hidden}.el-drawer{--el-drawer-bg-color:var(--el-dialog-bg-color,var(--el-bg-color));--el-drawer-padding-primary:var(--el-dialog-padding-primary,20px);--el-drawer-dragger-size:8px;box-sizing:border-box;background-color:var(--el-drawer-bg-color);box-shadow:var(--el-box-shadow-dark);transition:all var(--el-transition-duration);flex-direction:column;display:flex;position:absolute}.el-drawer .rtl,.el-drawer .ltr,.el-drawer .ttb,.el-drawer .btt{transform:translate(0)}.el-drawer__sr-focus:focus{outline:none!important}.el-drawer__header{color:var(--el-text-color-primary);padding:var(--el-drawer-padding-primary);align-items:center;margin-bottom:32px;padding-bottom:0;display:flex;overflow:hidden}.el-drawer__header>:first-child{flex:1}.el-drawer__title{line-height:inherit;flex:1;margin:0;font-size:16px}.el-drawer__footer{padding:var(--el-drawer-padding-primary);text-align:right;padding-top:10px;overflow:hidden}.el-drawer__close-btn{cursor:pointer;font-size:var(--el-font-size-extra-large);color:inherit;background-color:#0000;border:none;outline:none;display:inline-flex}.el-drawer__close-btn:focus i,.el-drawer__close-btn:hover i{color:var(--el-color-primary)}.el-drawer__body{padding:var(--el-drawer-padding-primary);flex:1;overflow:auto}.el-drawer__body>*{box-sizing:border-box}.el-drawer.is-dragging{transition:none}.el-drawer__dragger{-webkit-user-select:none;user-select:none;background-color:#0000;transition:all .2s;position:absolute}.el-drawer__dragger:before{content:"";background-color:#0000;transition:all .2s;position:absolute}.el-drawer__dragger:hover:before{background-color:var(--el-color-primary)}.el-drawer.ltr,.el-drawer.rtl{height:100%;top:0;bottom:0}.el-drawer.ltr>.el-drawer__dragger,.el-drawer.rtl>.el-drawer__dragger{height:100%;width:var(--el-drawer-dragger-size);cursor:ew-resize;top:0;bottom:0}.el-drawer.ltr>.el-drawer__dragger:before,.el-drawer.rtl>.el-drawer__dragger:before{width:3px;top:0;bottom:0}.el-drawer.ttb,.el-drawer.btt{width:100%;left:0;right:0}.el-drawer.ttb>.el-drawer__dragger,.el-drawer.btt>.el-drawer__dragger{width:100%;height:var(--el-drawer-dragger-size);cursor:ns-resize;left:0;right:0}.el-drawer.ttb>.el-drawer__dragger:before,.el-drawer.btt>.el-drawer__dragger:before{height:3px;left:0;right:0}.el-drawer.ltr{left:0}.el-drawer.ltr>.el-drawer__dragger{right:0}.el-drawer.ltr>.el-drawer__dragger:before{right:-2px}.el-drawer.rtl{right:0}.el-drawer.rtl>.el-drawer__dragger{left:0}.el-drawer.rtl>.el-drawer__dragger:before{left:-2px}.el-drawer.ttb{top:0}.el-drawer.ttb>.el-drawer__dragger{bottom:0}.el-drawer.ttb>.el-drawer__dragger:before{bottom:-2px}.el-drawer.btt{bottom:0}.el-drawer.btt>.el-drawer__dragger{top:0}.el-drawer.btt>.el-drawer__dragger:before{top:-2px}.el-modal-drawer.is-penetrable{pointer-events:none}.el-modal-drawer.is-penetrable .el-drawer{pointer-events:auto}.el-drawer-fade-enter-active,.el-drawer-fade-leave-active{transition:all var(--el-transition-duration)}.el-drawer-fade-enter-from,.el-drawer-fade-enter-active,.el-drawer-fade-enter-to,.el-drawer-fade-leave-from,.el-drawer-fade-leave-active,.el-drawer-fade-leave-to{overflow:hidden!important}.el-drawer-fade-enter-from,.el-drawer-fade-leave-to{background-color:#0000!important}.el-drawer-fade-enter-from .rtl,.el-drawer-fade-leave-to .rtl{transform:translate(100%)}.el-drawer-fade-enter-from .ltr,.el-drawer-fade-leave-to .ltr{transform:translate(-100%)}.el-drawer-fade-enter-from .ttb,.el-drawer-fade-leave-to .ttb{transform:translateY(-100%)}.el-drawer-fade-enter-from .btt,.el-drawer-fade-leave-to .btt{transform:translateY(100%)}}@layer element-plus{.el-tag{--el-tag-font-size:12px;--el-tag-border-radius:4px;--el-tag-border-radius-rounded:9999px;background-color:var(--el-tag-bg-color);border-color:var(--el-tag-border-color);color:var(--el-tag-text-color);vertical-align:middle;height:24px;font-size:var(--el-tag-font-size);border-radius:var(--el-tag-border-radius);box-sizing:border-box;white-space:nowrap;--el-icon-size:14px;--el-tag-bg-color:var(--el-color-primary-light-9);--el-tag-border-color:var(--el-color-primary-light-8);--el-tag-hover-color:var(--el-color-primary);border-style:solid;border-width:1px;justify-content:center;align-items:center;padding:0 9px;line-height:1;display:inline-flex}.el-tag.el-tag--primary{--el-tag-bg-color:var(--el-color-primary-light-9);--el-tag-border-color:var(--el-color-primary-light-8);--el-tag-hover-color:var(--el-color-primary)}.el-tag.el-tag--success{--el-tag-bg-color:var(--el-color-success-light-9);--el-tag-border-color:var(--el-color-success-light-8);--el-tag-hover-color:var(--el-color-success)}.el-tag.el-tag--warning{--el-tag-bg-color:var(--el-color-warning-light-9);--el-tag-border-color:var(--el-color-warning-light-8);--el-tag-hover-color:var(--el-color-warning)}.el-tag.el-tag--danger{--el-tag-bg-color:var(--el-color-danger-light-9);--el-tag-border-color:var(--el-color-danger-light-8);--el-tag-hover-color:var(--el-color-danger)}.el-tag.el-tag--error{--el-tag-bg-color:var(--el-color-error-light-9);--el-tag-border-color:var(--el-color-error-light-8);--el-tag-hover-color:var(--el-color-error)}.el-tag.el-tag--info{--el-tag-bg-color:var(--el-color-info-light-9);--el-tag-border-color:var(--el-color-info-light-8);--el-tag-hover-color:var(--el-color-info)}.el-tag.is-hit{border-color:var(--el-color-primary)}.el-tag.is-round{border-radius:var(--el-tag-border-radius-rounded)}.el-tag .el-tag__close{color:var(--el-tag-text-color);flex-shrink:0}.el-tag .el-tag__close:hover{color:var(--el-color-white);background-color:var(--el-tag-hover-color)}.el-tag.el-tag--primary{--el-tag-text-color:var(--el-color-primary)}.el-tag.el-tag--success{--el-tag-text-color:var(--el-color-success)}.el-tag.el-tag--warning{--el-tag-text-color:var(--el-color-warning)}.el-tag.el-tag--danger{--el-tag-text-color:var(--el-color-danger)}.el-tag.el-tag--error{--el-tag-text-color:var(--el-color-error)}.el-tag.el-tag--info{--el-tag-text-color:var(--el-color-info)}.el-tag .el-icon{cursor:pointer;font-size:calc(var(--el-icon-size) - 2px);height:var(--el-icon-size);width:var(--el-icon-size);border-radius:50%}.el-tag .el-tag__close{background-color:#0000;border:none;border-radius:50%;outline:none;margin-left:6px;padding:0;overflow:hidden}.el-tag .el-tag__close:focus-visible{outline:2px solid var(--el-color-primary);outline-offset:2px}.el-tag .el-tag__close .el-icon{display:flex}.el-tag--dark{--el-tag-text-color:var(--el-color-white);--el-tag-bg-color:var(--el-color-primary);--el-tag-border-color:var(--el-color-primary);--el-tag-hover-color:var(--el-color-primary-light-3)}.el-tag--dark.el-tag--primary{--el-tag-bg-color:var(--el-color-primary);--el-tag-border-color:var(--el-color-primary);--el-tag-hover-color:var(--el-color-primary-light-3)}.el-tag--dark.el-tag--success{--el-tag-bg-color:var(--el-color-success);--el-tag-border-color:var(--el-color-success);--el-tag-hover-color:var(--el-color-success-light-3)}.el-tag--dark.el-tag--warning{--el-tag-bg-color:var(--el-color-warning);--el-tag-border-color:var(--el-color-warning);--el-tag-hover-color:var(--el-color-warning-light-3)}.el-tag--dark.el-tag--danger{--el-tag-bg-color:var(--el-color-danger);--el-tag-border-color:var(--el-color-danger);--el-tag-hover-color:var(--el-color-danger-light-3)}.el-tag--dark.el-tag--error{--el-tag-bg-color:var(--el-color-error);--el-tag-border-color:var(--el-color-error);--el-tag-hover-color:var(--el-color-error-light-3)}.el-tag--dark.el-tag--info{--el-tag-bg-color:var(--el-color-info);--el-tag-border-color:var(--el-color-info);--el-tag-hover-color:var(--el-color-info-light-3)}.el-tag--dark.el-tag--primary,.el-tag--dark.el-tag--success,.el-tag--dark.el-tag--warning,.el-tag--dark.el-tag--danger,.el-tag--dark.el-tag--error,.el-tag--dark.el-tag--info{--el-tag-text-color:var(--el-color-white)}.el-tag--plain,.el-tag--plain.el-tag--primary{--el-tag-bg-color:var(--el-fill-color-blank);--el-tag-border-color:var(--el-color-primary-light-5);--el-tag-hover-color:var(--el-color-primary)}.el-tag--plain.el-tag--success{--el-tag-bg-color:var(--el-fill-color-blank);--el-tag-border-color:var(--el-color-success-light-5);--el-tag-hover-color:var(--el-color-success)}.el-tag--plain.el-tag--warning{--el-tag-bg-color:var(--el-fill-color-blank);--el-tag-border-color:var(--el-color-warning-light-5);--el-tag-hover-color:var(--el-color-warning)}.el-tag--plain.el-tag--danger{--el-tag-bg-color:var(--el-fill-color-blank);--el-tag-border-color:var(--el-color-danger-light-5);--el-tag-hover-color:var(--el-color-danger)}.el-tag--plain.el-tag--error{--el-tag-bg-color:var(--el-fill-color-blank);--el-tag-border-color:var(--el-color-error-light-5);--el-tag-hover-color:var(--el-color-error)}.el-tag--plain.el-tag--info{--el-tag-bg-color:var(--el-fill-color-blank);--el-tag-border-color:var(--el-color-info-light-5);--el-tag-hover-color:var(--el-color-info)}.el-tag.is-closable{padding-right:5px}.el-tag--large{--el-icon-size:16px;height:32px;padding:0 11px}.el-tag--large .el-tag__close{margin-left:8px}.el-tag--large.is-closable{padding-right:7px}.el-tag--small{--el-icon-size:12px;height:20px;padding:0 7px}.el-tag--small .el-tag__close{margin-left:4px}.el-tag--small.is-closable{padding-right:3px}.el-tag--small .el-icon-close{transform:scale(.8)}.el-tag.el-tag--primary.is-hit{border-color:var(--el-color-primary)}.el-tag.el-tag--success.is-hit{border-color:var(--el-color-success)}.el-tag.el-tag--warning.is-hit{border-color:var(--el-color-warning)}.el-tag.el-tag--danger.is-hit{border-color:var(--el-color-danger)}.el-tag.el-tag--error.is-hit{border-color:var(--el-color-error)}.el-tag.el-tag--info.is-hit{border-color:var(--el-color-info)}}@layer element-plus{.el-select-dropdown__item{font-size:var(--el-font-size-base);white-space:nowrap;text-overflow:ellipsis;color:var(--el-text-color-regular);box-sizing:border-box;cursor:pointer;height:34px;padding:0 32px 0 20px;line-height:34px;position:relative;overflow:hidden}.el-select-dropdown__item.is-hovering{background-color:var(--el-fill-color-light)}.el-select-dropdown__item.is-selected{color:var(--el-color-primary);font-weight:700}.el-select-dropdown__item.is-disabled{color:var(--el-text-color-placeholder);cursor:not-allowed;background-color:unset}.el-select-dropdown.is-multiple .el-select-dropdown__item.is-selected:after{content:"";background-position:50%;background-repeat:no-repeat;background-color:var(--el-color-primary);-webkit-mask:url("data:image/svg+xml;utf8,%3Csvg class='icon' width='200' height='200' viewBox='0 0 1024 1024' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath fill='currentColor' d='M406.656 706.944L195.84 496.256a32 32 0 10-45.248 45.248l256 256 512-512a32 32 0 00-45.248-45.248L406.592 706.944z'%3E%3C/path%3E%3C/svg%3E") 0 0/100% 100% no-repeat;mask:url("data:image/svg+xml;utf8,%3Csvg class='icon' width='200' height='200' viewBox='0 0 1024 1024' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath fill='currentColor' d='M406.656 706.944L195.84 496.256a32 32 0 10-45.248 45.248l256 256 512-512a32 32 0 00-45.248-45.248L406.592 706.944z'%3E%3C/path%3E%3C/svg%3E") 0 0/100% 100% no-repeat;border-top:none;border-right:none;width:12px;height:12px;position:absolute;top:50%;right:20px;transform:translateY(-50%);-webkit-mask:url("data:image/svg+xml;utf8,%3Csvg class='icon' width='200' height='200' viewBox='0 0 1024 1024' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath fill='currentColor' d='M406.656 706.944L195.84 496.256a32 32 0 10-45.248 45.248l256 256 512-512a32 32 0 00-45.248-45.248L406.592 706.944z'%3E%3C/path%3E%3C/svg%3E") 0 0/100% 100% no-repeat}.el-select-dropdown.is-multiple .el-select-dropdown__item.is-disabled:after{background-color:var(--el-text-color-placeholder)}}@layer element-plus{.el-select-group{margin:0;padding:0}.el-select-group__wrap{margin:0;padding:0;list-style:none;position:relative}.el-select-group__title{box-sizing:border-box;color:var(--el-color-info);text-overflow:ellipsis;white-space:nowrap;padding:0 20px;font-size:12px;line-height:34px;overflow:hidden}.el-select-group .el-select-dropdown__item{padding-left:20px}}@layer element-plus{.el-select-dropdown{z-index:calc(var(--el-index-top) + 1);border-radius:var(--el-border-radius-base);box-sizing:border-box}.el-select-dropdown .el-scrollbar.is-empty .el-select-dropdown__list{padding:0}.el-select-dropdown__loading,.el-select-dropdown__empty{text-align:center;color:var(--el-text-color-secondary);font-size:var(--el-select-font-size);margin:0;padding:10px 0}.el-select-dropdown__wrap{max-height:274px}.el-select-dropdown__list{box-sizing:border-box;margin:0;padding:6px 0;list-style:none}.el-select-dropdown__list.el-vl__window{margin:6px 0;padding:0}.el-select-dropdown__header{border-bottom:1px solid var(--el-border-color-light);padding:10px}.el-select-dropdown__footer{border-top:1px solid var(--el-border-color-light);padding:10px}.el-select-dropdown__item{font-size:var(--el-font-size-base);white-space:nowrap;text-overflow:ellipsis;color:var(--el-text-color-regular);box-sizing:border-box;cursor:pointer;height:34px;padding:0 32px 0 20px;line-height:34px;position:relative;overflow:hidden}.el-select-dropdown__item.is-hovering{background-color:var(--el-fill-color-light)}.el-select-dropdown__item.is-selected{color:var(--el-color-primary);font-weight:700}.el-select-dropdown__item.is-disabled{color:var(--el-text-color-placeholder);cursor:not-allowed;background-color:unset}.el-select-dropdown.is-multiple .el-select-dropdown__item.is-selected:after{content:"";background-position:50%;background-repeat:no-repeat;background-color:var(--el-color-primary);-webkit-mask:url("data:image/svg+xml;utf8,%3Csvg class='icon' width='200' height='200' viewBox='0 0 1024 1024' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath fill='currentColor' d='M406.656 706.944L195.84 496.256a32 32 0 10-45.248 45.248l256 256 512-512a32 32 0 00-45.248-45.248L406.592 706.944z'%3E%3C/path%3E%3C/svg%3E") 0 0/100% 100% no-repeat;mask:url("data:image/svg+xml;utf8,%3Csvg class='icon' width='200' height='200' viewBox='0 0 1024 1024' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath fill='currentColor' d='M406.656 706.944L195.84 496.256a32 32 0 10-45.248 45.248l256 256 512-512a32 32 0 00-45.248-45.248L406.592 706.944z'%3E%3C/path%3E%3C/svg%3E") 0 0/100% 100% no-repeat;border-top:none;border-right:none;width:12px;height:12px;position:absolute;top:50%;right:20px;transform:translateY(-50%);-webkit-mask:url("data:image/svg+xml;utf8,%3Csvg class='icon' width='200' height='200' viewBox='0 0 1024 1024' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath fill='currentColor' d='M406.656 706.944L195.84 496.256a32 32 0 10-45.248 45.248l256 256 512-512a32 32 0 00-45.248-45.248L406.592 706.944z'%3E%3C/path%3E%3C/svg%3E") 0 0/100% 100% no-repeat}.el-select-dropdown.is-multiple .el-select-dropdown__item.is-disabled:after{background-color:var(--el-text-color-placeholder)}.el-select-group{margin:0;padding:0}.el-select-group__wrap{margin:0;padding:0;list-style:none;position:relative}.el-select-group__title{box-sizing:border-box;color:var(--el-color-info);text-overflow:ellipsis;white-space:nowrap;padding:0 20px;font-size:12px;line-height:34px;overflow:hidden}.el-select-group .el-select-dropdown__item{padding-left:20px}.el-select{--el-select-border-color-hover:var(--el-border-color-hover);--el-select-disabled-color:var(--el-disabled-text-color);--el-select-disabled-border:var(--el-disabled-border-color);--el-select-font-size:var(--el-font-size-base);--el-select-close-hover-color:var(--el-text-color-secondary);--el-select-input-color:var(--el-text-color-placeholder);--el-select-multiple-input-color:var(--el-text-color-regular);--el-select-input-focus-border-color:var(--el-color-primary);--el-select-input-font-size:14px;--el-select-width:100%;vertical-align:middle;width:var(--el-select-width);display:inline-block;position:relative}.el-select__wrapper{box-sizing:border-box;cursor:pointer;text-align:left;border-radius:var(--el-border-radius-base);background-color:var(--el-fill-color-blank);min-height:32px;transition:var(--el-transition-duration);box-shadow:0 0 0 1px var(--el-border-color) inset;align-items:center;gap:6px;padding:4px 12px;font-size:14px;line-height:24px;display:flex;position:relative;transform:translate(0)}.el-select__wrapper.is-filterable{cursor:text}.el-select__wrapper.is-focused{box-shadow:0 0 0 1px var(--el-color-primary) inset}.el-select__wrapper.is-hovering:not(.is-focused){box-shadow:0 0 0 1px var(--el-border-color-hover) inset}.el-select__wrapper.is-disabled{cursor:not-allowed;background-color:var(--el-fill-color-light);color:var(--el-text-color-placeholder);box-shadow:0 0 0 1px var(--el-select-disabled-border) inset}.el-select__wrapper.is-disabled:hover{box-shadow:0 0 0 1px var(--el-select-disabled-border) inset}.el-select__wrapper.is-disabled.is-focus{box-shadow:0 0 0 1px var(--el-input-focus-border-color) inset}.el-select__wrapper.is-disabled .el-select__selected-item{color:var(--el-select-disabled-color)}.el-select__wrapper.is-disabled .el-select__caret,.el-select__wrapper.is-disabled .el-tag,.el-select__wrapper.is-disabled input{cursor:not-allowed}.el-select__wrapper.is-disabled .el-select__prefix,.el-select__wrapper.is-disabled .el-select__suffix{pointer-events:none}.el-select__prefix,.el-select__suffix{color:var(--el-input-icon-color,var(--el-text-color-placeholder));flex-shrink:0;align-items:center;gap:6px;display:flex}.el-select__caret{color:var(--el-select-input-color);font-size:var(--el-select-input-font-size);transition:var(--el-transition-duration);cursor:pointer;transform:rotate(0)}.el-select__caret.is-reverse{transform:rotate(180deg)}.el-select__clear{cursor:pointer}.el-select__clear:hover{color:var(--el-select-close-hover-color)}.el-select__selection{flex-wrap:wrap;flex:1;align-items:center;gap:6px;min-width:0;display:flex;position:relative}.el-select__selection.is-near{margin-left:-8px}.el-select__selection .el-tag{cursor:pointer;border-color:#0000}.el-select__selection .el-tag.el-tag--plain{border-color:var(--el-tag-border-color)}.el-select__selection .el-tag .el-tag__content{min-width:0}.el-select__selected-item{-webkit-user-select:none;user-select:none;flex-wrap:wrap;display:flex}.el-select__tags-text{text-overflow:ellipsis;white-space:nowrap;line-height:normal;display:block;overflow:hidden}.el-select__placeholder{z-index:-1;text-overflow:ellipsis;white-space:nowrap;width:100%;color:var(--el-input-text-color,var(--el-text-color-regular));display:block;position:absolute;top:50%;overflow:hidden;transform:translateY(-50%)}.el-select__placeholder.is-transparent{-webkit-user-select:none;user-select:none;color:var(--el-text-color-placeholder)}.el-select__popper.el-popper{background:var(--el-bg-color-overlay);border:1px solid var(--el-border-color-light);box-shadow:var(--el-box-shadow-light)}.el-select__popper.el-popper .el-popper__arrow:before{border:1px solid var(--el-border-color-light)}.el-select__popper.el-popper[data-popper-placement^=top] .el-popper__arrow:before{border-top-color:#0000;border-left-color:#0000}.el-select__popper.el-popper[data-popper-placement^=bottom] .el-popper__arrow:before{border-bottom-color:#0000;border-right-color:#0000}.el-select__popper.el-popper[data-popper-placement^=left] .el-popper__arrow:before{border-bottom-color:#0000;border-left-color:#0000}.el-select__popper.el-popper[data-popper-placement^=right] .el-popper__arrow:before{border-top-color:#0000;border-right-color:#0000}.el-select__input-wrapper{flex:1}.el-select__input-wrapper.is-hidden{opacity:0;z-index:-1;position:absolute}.el-select__input{color:var(--el-select-multiple-input-color);font-size:inherit;-webkit-appearance:none;-moz-appearance:none;appearance:none;background-color:#0000;border:none;outline:none;width:100%;height:24px;padding:0;font-family:inherit}.el-select__input-calculator{visibility:hidden;white-space:pre;max-width:100%;position:absolute;top:0;left:0;overflow:hidden}.el-select--large .el-select__wrapper{gap:6px;min-height:40px;padding:8px 16px;font-size:14px;line-height:24px}.el-select--large .el-select__selection{gap:6px}.el-select--large .el-select__selection.is-near{margin-left:-8px}.el-select--large .el-select__prefix,.el-select--large .el-select__suffix{gap:6px}.el-select--large .el-select__input{height:24px}.el-select--small .el-select__wrapper{gap:4px;min-height:24px;padding:2px 8px;font-size:12px;line-height:20px}.el-select--small .el-select__selection{gap:4px}.el-select--small .el-select__selection.is-near{margin-left:-6px}.el-select--small .el-select__prefix,.el-select--small .el-select__suffix{gap:4px}.el-select--small .el-select__input{height:20px}}@layer element-plus{.el-upload{--el-upload-dragger-padding-horizontal:10px;--el-upload-dragger-padding-vertical:40px;--el-upload-list-picture-card-size:var(--el-upload-picture-card-size);--el-upload-picture-card-size:148px;cursor:pointer;outline:none;justify-content:center;align-items:center;display:inline-flex}.el-upload.is-disabled{cursor:not-allowed}.el-upload.is-disabled:focus{border-color:var(--el-border-color-darker);color:inherit}.el-upload.is-disabled:focus .el-upload-dragger{border-color:var(--el-border-color-darker)}.el-upload.is-disabled .el-upload-dragger{cursor:not-allowed;background-color:var(--el-disabled-bg-color)}.el-upload.is-disabled .el-upload-dragger .el-upload__text{color:var(--el-text-color-placeholder)}.el-upload.is-disabled .el-upload-dragger .el-upload__text em{color:var(--el-disabled-text-color)}.el-upload.is-disabled .el-upload-dragger:hover{border-color:var(--el-border-color-darker)}.el-upload__input{display:none}.el-upload__tip{color:var(--el-text-color-regular);margin-top:7px;font-size:12px}.el-upload iframe{z-index:-1;opacity:0;filter:alpha(opacity=0);position:absolute;top:0;left:0}.el-upload--picture-card{background-color:var(--el-fill-color-lighter);border:1px dashed var(--el-border-color-darker);box-sizing:border-box;width:var(--el-upload-picture-card-size);height:var(--el-upload-picture-card-size);cursor:pointer;vertical-align:top;border-radius:6px;justify-content:center;align-items:center;display:inline-flex}.el-upload--picture-card>i{color:var(--el-text-color-secondary);font-size:28px}.el-upload--picture-card:hover{border-color:var(--el-color-primary);color:var(--el-color-primary)}.el-upload.is-drag{display:block}.el-upload:focus{border-color:var(--el-color-primary);color:var(--el-color-primary)}.el-upload:focus .el-upload-dragger{border-color:var(--el-color-primary)}.el-upload-dragger{padding:var(--el-upload-dragger-padding-vertical) var(--el-upload-dragger-padding-horizontal);background-color:var(--el-fill-color-blank);border:1px dashed var(--el-border-color);box-sizing:border-box;text-align:center;cursor:pointer;border-radius:6px;position:relative;overflow:hidden}.el-upload-dragger .el-icon--upload{color:var(--el-text-color-placeholder);margin-bottom:16px;font-size:67px;line-height:50px}.el-upload-dragger+.el-upload__tip{text-align:center}.el-upload-dragger~.el-upload__files{border-top:var(--el-border);margin-top:7px;padding-top:5px}.el-upload-dragger .el-upload__text{color:var(--el-text-color-regular);text-align:center;font-size:14px}.el-upload-dragger .el-upload__text em{color:var(--el-color-primary);font-style:normal}.el-upload-dragger:hover{border-color:var(--el-color-primary)}.el-upload-dragger.is-dragover{padding:calc(var(--el-upload-dragger-padding-vertical) - 1px) calc(var(--el-upload-dragger-padding-horizontal) - 1px);background-color:var(--el-color-primary-light-9);border:2px dashed var(--el-color-primary)}.el-upload-list{--el-upload-dragger-padding-horizontal:10px;--el-upload-dragger-padding-vertical:40px;--el-upload-list-picture-card-size:var(--el-upload-picture-card-size);--el-upload-picture-card-size:148px;margin:10px 0 0;padding:0;list-style:none;position:relative}.el-upload-list__item{color:var(--el-text-color-regular);box-sizing:border-box;border-radius:4px;width:100%;margin-bottom:5px;font-size:14px;transition:all .5s cubic-bezier(.55,0,.1,1);position:relative}.el-upload-list__item .el-progress{width:100%;position:absolute;top:20px}.el-upload-list__item .el-progress__text{position:absolute;top:-13px;right:0}.el-upload-list__item .el-progress-bar{margin-right:0;padding-right:0}.el-upload-list__item .el-icon--upload-success{color:var(--el-color-success)}.el-upload-list__item .el-icon--close{cursor:pointer;opacity:.75;color:var(--el-text-color-regular);transition:opacity var(--el-transition-duration);display:none;position:absolute;top:50%;right:5px;transform:translateY(-50%)}.el-upload-list__item .el-icon--close:hover{opacity:1;color:var(--el-color-primary)}.el-upload-list__item .el-icon--close-tip{cursor:pointer;opacity:1;color:var(--el-color-primary);font-size:12px;font-style:normal;display:none;position:absolute;top:1px;right:5px}.el-upload-list__item:hover,.el-upload-list__item:focus-within{background-color:var(--el-fill-color-light)}.el-upload-list__item:hover .el-icon--close,.el-upload-list__item:focus-within .el-icon--close{display:inline-flex}.el-upload-list__item:hover .el-icon--close-tip,.el-upload-list__item:focus-within .el-icon--close-tip{right:24px}.el-upload-list__item:hover .el-progress__text,.el-upload-list__item:focus-within .el-progress__text{display:none}.el-upload-list__item .el-upload-list__item-info{flex-direction:column;justify-content:center;width:calc(100% - 30px);margin-left:4px;display:inline-flex}.el-upload-list__item.is-success .el-upload-list__item-status-label{display:inline-flex}.el-upload-list__item.is-success .el-upload-list__item-name:hover,.el-upload-list__item.is-success .el-upload-list__item-name:focus{color:var(--el-color-primary);cursor:pointer}.el-upload-list__item.is-success:focus:not(:hover) .el-icon--close-tip{display:inline-block}.el-upload-list__item.is-success:not(.focusing):focus,.el-upload-list__item.is-success:active{outline-width:0}.el-upload-list__item.is-success:not(.focusing):focus .el-icon--close-tip,.el-upload-list__item.is-success:active .el-icon--close-tip{display:none}.el-upload-list__item.is-success:hover .el-upload-list__item-status-label,.el-upload-list__item.is-success:focus .el-upload-list__item-status-label,.el-upload-list__item.is-success:focus-within .el-upload-list__item-status-label{opacity:0;display:none}.el-upload-list__item-name{color:var(--el-text-color-regular);text-align:center;transition:color var(--el-transition-duration);font-size:var(--el-font-size-base);align-items:center;padding:0 4px;display:inline-flex}.el-upload-list__item-name .el-icon{color:var(--el-text-color-secondary);margin-right:6px}.el-upload-list__item-file-name{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.el-upload-list__item-status-label{line-height:inherit;height:100%;transition:opacity var(--el-transition-duration);justify-content:center;align-items:center;display:none;position:absolute;top:0;right:5px}.el-upload-list__item-delete{color:var(--el-text-color-regular);font-size:12px;display:none;position:absolute;top:0;right:10px}.el-upload-list__item-delete:hover{color:var(--el-color-primary)}.el-upload-list--picture-card{flex-wrap:wrap;margin:0;display:inline-flex}.el-upload-list--picture-card .el-upload-list__item{background-color:var(--el-fill-color-blank);border:1px solid var(--el-border-color);box-sizing:border-box;width:var(--el-upload-list-picture-card-size);height:var(--el-upload-list-picture-card-size);border-radius:6px;margin:0 8px 8px 0;padding:0;display:inline-flex;overflow:hidden}.el-upload-list--picture-card .el-upload-list__item .el-icon--check,.el-upload-list--picture-card .el-upload-list__item .el-icon--circle-check{color:#fff}.el-upload-list--picture-card .el-upload-list__item .el-icon--close{display:none}.el-upload-list--picture-card .el-upload-list__item:hover .el-upload-list__item-status-label{opacity:0;display:block}.el-upload-list--picture-card .el-upload-list__item:hover .el-progress__text{display:block}.el-upload-list--picture-card .el-upload-list__item .el-upload-list__item-name{display:none}.el-upload-list--picture-card .el-upload-list__item-thumbnail{object-fit:contain;width:100%;height:100%}.el-upload-list--picture-card .el-upload-list__item-status-label{background:var(--el-color-success);text-align:center;width:40px;height:24px;top:-6px;right:-15px;transform:rotate(45deg)}.el-upload-list--picture-card .el-upload-list__item-status-label i{margin-top:11px;font-size:12px;transform:rotate(-45deg)}.el-upload-list--picture-card .el-upload-list__item-actions{cursor:default;color:#fff;opacity:0;background-color:var(--el-overlay-color-lighter);width:100%;height:100%;transition:opacity var(--el-transition-duration);justify-content:center;align-items:center;font-size:20px;display:inline-flex;position:absolute;top:0;left:0}.el-upload-list--picture-card .el-upload-list__item-actions span{cursor:pointer;display:none}.el-upload-list--picture-card .el-upload-list__item-actions span+span{margin-left:16px}.el-upload-list--picture-card .el-upload-list__item-actions .el-upload-list__item-delete{font-size:inherit;color:inherit;position:static}.el-upload-list--picture-card .el-upload-list__item-actions:hover{opacity:1}.el-upload-list--picture-card .el-upload-list__item-actions:hover span{display:inline-flex}.el-upload-list--picture-card .el-progress{width:126px;top:50%;bottom:auto;left:50%;transform:translate(-50%,-50%)}.el-upload-list--picture-card .el-progress .el-progress__text{top:50%}.el-upload-list--picture .el-upload-list__item{z-index:0;background-color:var(--el-fill-color-blank);border:1px solid var(--el-border-color);box-sizing:border-box;border-radius:6px;align-items:center;margin-top:10px;padding:10px;display:flex;overflow:hidden}.el-upload-list--picture .el-upload-list__item .el-icon--check,.el-upload-list--picture .el-upload-list__item .el-icon--circle-check{color:#fff}.el-upload-list--picture .el-upload-list__item:hover .el-upload-list__item-status-label{opacity:0;display:inline-flex}.el-upload-list--picture .el-upload-list__item:hover .el-progress__text{display:block}.el-upload-list--picture .el-upload-list__item.is-success .el-upload-list__item-name i{display:none}.el-upload-list--picture .el-upload-list__item .el-icon--close{top:5px;transform:translateY(0)}.el-upload-list--picture .el-upload-list__item-thumbnail{object-fit:contain;z-index:1;background-color:var(--el-color-white);justify-content:center;align-items:center;width:70px;height:70px;display:inline-flex;position:relative}.el-upload-list--picture .el-upload-list__item-status-label{background:var(--el-color-success);text-align:center;width:46px;height:26px;position:absolute;top:-7px;right:-17px;transform:rotate(45deg)}.el-upload-list--picture .el-upload-list__item-status-label i{margin-top:12px;font-size:12px;transform:rotate(-45deg)}.el-upload-list--picture .el-progress{position:relative;top:-7px}.el-upload-cover{z-index:10;cursor:default;width:100%;height:100%;position:absolute;top:0;left:0;overflow:hidden}.el-upload-cover:after{content:"";vertical-align:middle;height:100%;display:inline-block}.el-upload-cover img{width:100%;height:100%;display:block}.el-upload-cover__label{background:var(--el-color-success);text-align:center;width:40px;height:24px;top:-6px;right:-15px;transform:rotate(45deg)}.el-upload-cover__label i{color:#fff;margin-top:11px;font-size:12px;transform:rotate(-45deg)}.el-upload-cover__progress{vertical-align:middle;width:243px;display:inline-block;position:static}.el-upload-cover__progress+.el-upload__inner{opacity:0}.el-upload-cover__content{width:100%;height:100%;position:absolute;top:0;left:0}.el-upload-cover__interact{background-color:var(--el-overlay-color-light);text-align:center;width:100%;height:100%;position:absolute;bottom:0;left:0}.el-upload-cover__interact .btn{color:#fff;cursor:pointer;vertical-align:middle;transition:var(--el-transition-md-fade);margin-top:60px;font-size:14px;display:inline-block}.el-upload-cover__interact .btn i{margin-top:0}.el-upload-cover__interact .btn span{opacity:0;transition:opacity .15s linear}.el-upload-cover__interact .btn:not(:first-child){margin-left:35px}.el-upload-cover__interact .btn:hover{transform:translateY(-13px)}.el-upload-cover__interact .btn:hover span{opacity:1}.el-upload-cover__interact .btn i{color:#fff;font-size:24px;line-height:inherit;margin:0 auto 5px;display:block}.el-upload-cover__title{text-overflow:ellipsis;white-space:nowrap;text-align:left;width:100%;height:36px;color:var(--el-text-color-primary);background-color:#fff;margin:0;padding:0 10px;font-size:14px;font-weight:400;line-height:36px;position:absolute;bottom:0;left:0;overflow:hidden}.el-upload-cover+.el-upload__inner{opacity:0;z-index:1;position:relative}}@layer element-plus{.el-input-number{vertical-align:middle;width:150px;line-height:30px;display:inline-flex;position:relative}.el-input-number .el-input__wrapper{padding-left:42px;padding-right:42px}.el-input-number .el-input__inner{-webkit-appearance:none;-moz-appearance:textfield;text-align:center;line-height:1}.el-input-number .el-input__inner::-webkit-inner-spin-button{-webkit-appearance:none;margin:0}.el-input-number .el-input__inner::-webkit-outer-spin-button{-webkit-appearance:none;margin:0}.el-input-number.is-left .el-input__inner{text-align:left}.el-input-number.is-right .el-input__inner{text-align:right}.el-input-number.is-center .el-input__inner{text-align:center}.el-input-number__increase,.el-input-number__decrease{z-index:1;background:var(--el-fill-color-light);width:32px;height:auto;color:var(--el-text-color-regular);cursor:pointer;-webkit-user-select:none;user-select:none;justify-content:center;align-items:center;font-size:13px;display:flex;position:absolute;top:1px;bottom:1px}.el-input-number__increase:hover,.el-input-number__decrease:hover{color:var(--el-color-primary)}.el-input-number__increase:hover~.el-input:not(.is-disabled) .el-input__wrapper,.el-input-number__decrease:hover~.el-input:not(.is-disabled) .el-input__wrapper{box-shadow:0 0 0 1px var(--el-input-focus-border-color,var(--el-color-primary)) inset}.el-input-number__increase.is-disabled,.el-input-number__decrease.is-disabled{color:var(--el-disabled-text-color);cursor:not-allowed}.el-input-number__increase{border-radius:0 var(--el-border-radius-base) var(--el-border-radius-base) 0;border-left:var(--el-border);right:1px}.el-input-number__decrease{border-radius:var(--el-border-radius-base) 0 0 var(--el-border-radius-base);border-right:var(--el-border);left:1px}.el-input-number.is-disabled .el-input-number__increase,.el-input-number.is-disabled .el-input-number__decrease{border-color:var(--el-disabled-border-color);color:var(--el-disabled-border-color)}.el-input-number.is-disabled .el-input-number__increase:hover,.el-input-number.is-disabled .el-input-number__decrease:hover{color:var(--el-disabled-border-color);cursor:not-allowed}.el-input-number--large{width:180px;line-height:38px}.el-input-number--large .el-input-number__increase,.el-input-number--large .el-input-number__decrease{width:40px;font-size:14px}.el-input-number--large.is-controls-right .el-input--large .el-input__wrapper{padding-right:47px}.el-input-number--large .el-input--large .el-input__wrapper{padding-left:47px;padding-right:47px}.el-input-number--small{width:120px;line-height:22px}.el-input-number--small .el-input-number__increase,.el-input-number--small .el-input-number__decrease{width:24px;font-size:12px}.el-input-number--small.is-controls-right .el-input--small .el-input__wrapper{padding-right:31px}.el-input-number--small .el-input--small .el-input__wrapper{padding-left:31px;padding-right:31px}.el-input-number--small .el-input-number__increase [class*=el-icon],.el-input-number--small .el-input-number__decrease [class*=el-icon]{transform:scale(.9)}.el-input-number.is-without-controls .el-input__wrapper{padding-left:15px;padding-right:15px}.el-input-number.is-controls-right .el-input__wrapper{padding-left:15px;padding-right:42px}.el-input-number.is-controls-right .el-input-number__increase,.el-input-number.is-controls-right .el-input-number__decrease{--el-input-number-controls-height:15px;height:var(--el-input-number-controls-height);line-height:var(--el-input-number-controls-height)}.el-input-number.is-controls-right .el-input-number__increase [class*=el-icon],.el-input-number.is-controls-right .el-input-number__decrease [class*=el-icon]{transform:scale(.8)}.el-input-number.is-controls-right .el-input-number__increase{border-radius:0 var(--el-border-radius-base) 0 0;border-bottom:var(--el-border);bottom:auto;left:auto}.el-input-number.is-controls-right .el-input-number__decrease{border-right:none;border-left:var(--el-border);border-radius:0 0 var(--el-border-radius-base) 0;top:auto;left:auto;right:1px}.el-input-number.is-controls-right[class*=large] [class*=increase],.el-input-number.is-controls-right[class*=large] [class*=decrease]{--el-input-number-controls-height:19px}.el-input-number.is-controls-right[class*=small] [class*=increase],.el-input-number.is-controls-right[class*=small] [class*=decrease]{--el-input-number-controls-height:11px}}@layer element-plus{.el-slider{--el-slider-main-bg-color:var(--el-color-primary);--el-slider-runway-bg-color:var(--el-border-color-light);--el-slider-stop-bg-color:var(--el-color-white);--el-slider-disabled-color:var(--el-text-color-placeholder);--el-slider-border-radius:3px;--el-slider-height:6px;--el-slider-button-size:20px;--el-slider-button-wrapper-size:36px;--el-slider-button-wrapper-offset:-15px;align-items:center;width:100%;height:32px;display:flex}.el-slider__runway{height:var(--el-slider-height);background-color:var(--el-slider-runway-bg-color);border-radius:var(--el-slider-border-radius);cursor:pointer;flex:1;position:relative}.el-slider__runway.show-input{width:auto;margin-right:30px}.el-slider__runway.is-disabled{cursor:default}.el-slider__runway.is-disabled .el-slider__bar{background-color:var(--el-slider-disabled-color)}.el-slider__runway.is-disabled .el-slider__button{border-color:var(--el-slider-disabled-color)}.el-slider__runway.is-disabled .el-slider__button-wrapper:hover,.el-slider__runway.is-disabled .el-slider__button-wrapper.hover,.el-slider__runway.is-disabled .el-slider__button-wrapper.dragging{cursor:not-allowed}.el-slider__runway.is-disabled .el-slider__button:hover,.el-slider__runway.is-disabled .el-slider__button.hover,.el-slider__runway.is-disabled .el-slider__button.dragging{cursor:not-allowed;transform:scale(1)}.el-slider__input{flex-shrink:0;width:130px}.el-slider__bar{height:var(--el-slider-height);background-color:var(--el-slider-main-bg-color);border-top-left-radius:var(--el-slider-border-radius);border-bottom-left-radius:var(--el-slider-border-radius);position:absolute}.el-slider__button-wrapper{height:var(--el-slider-button-wrapper-size);width:var(--el-slider-button-wrapper-size);z-index:1;top:var(--el-slider-button-wrapper-offset);text-align:center;-webkit-user-select:none;user-select:none;background-color:#0000;outline:none;line-height:normal;position:absolute;transform:translate(-50%)}.el-slider__button-wrapper:after{content:"";vertical-align:middle;height:100%;display:inline-block}.el-slider__button-wrapper:hover,.el-slider__button-wrapper.hover{cursor:grab}.el-slider__button-wrapper.dragging{cursor:grabbing}.el-slider__button{width:var(--el-slider-button-size);height:var(--el-slider-button-size);vertical-align:middle;border:solid 2px var(--el-slider-main-bg-color);background-color:var(--el-color-white);box-sizing:border-box;transition:var(--el-transition-duration-fast);-webkit-user-select:none;user-select:none;border-radius:50%;display:inline-block}.el-slider__button:hover,.el-slider__button.hover,.el-slider__button.dragging{transform:scale(1.2)}.el-slider__button:hover,.el-slider__button.hover{cursor:grab}.el-slider__button.dragging{cursor:grabbing}.el-slider__stop{height:var(--el-slider-height);width:var(--el-slider-height);border-radius:var(--el-border-radius-circle);background-color:var(--el-slider-stop-bg-color);position:absolute;transform:translate(-50%)}.el-slider__marks{width:18px;height:100%;top:0;left:12px}.el-slider__marks-text{color:var(--el-color-info);white-space:pre;margin-top:15px;font-size:14px;position:absolute;transform:translate(-50%)}.el-slider.is-vertical{flex:0;width:auto;height:100%;display:inline-flex;position:relative}.el-slider.is-vertical .el-slider__runway{width:var(--el-slider-height);height:100%;margin:0 16px}.el-slider.is-vertical .el-slider__bar{width:var(--el-slider-height);border-radius:0 0 3px 3px;height:auto}.el-slider.is-vertical .el-slider__button-wrapper{top:auto;left:var(--el-slider-button-wrapper-offset);transform:translateY(50%)}.el-slider.is-vertical .el-slider__stop{transform:translateY(50%)}.el-slider.is-vertical .el-slider__marks-text{margin-top:0;left:15px;transform:translateY(50%)}.el-slider--large{height:40px}.el-slider--small{height:24px}}@layer element-plus{.el-color-picker-panel{--el-colorpicker-bg-color:var(--el-bg-color-overlay);--el-fill-color-blank:var(--el-colorpicker-bg-color);box-sizing:content-box;background:var(--el-colorpicker-bg-color);width:300px;padding:12px}.el-color-picker-panel.is-border{border:solid 1px var(--el-border-color-lighter);border-radius:4px}.el-color-picker-panel__wrapper{margin-bottom:6px}.el-color-picker-panel__footer{text-align:right;justify-content:space-between;margin-top:12px;display:flex}.el-color-picker-panel__footer .el-input{color:#000;width:160px;font-size:12px;line-height:26px}.el-color-picker-panel.is-disabled .el-color-svpanel,.el-color-picker-panel.is-disabled .el-color-hue-slider{cursor:not-allowed;opacity:.3}.el-color-picker-panel.is-disabled .el-color-hue-slider__thumb{cursor:not-allowed}.el-color-picker-panel.is-disabled .el-color-alpha-slider,.el-color-picker-panel.is-disabled .el-color-predefine .el-color-predefine__color-selector{cursor:not-allowed;opacity:.3}.el-color-predefine{width:280px;margin-top:8px;font-size:12px;display:flex}.el-color-predefine__colors{flex-wrap:wrap;flex:1;gap:8px;display:flex}.el-color-predefine__color-selector{border-radius:var(--el-border-radius-base);cursor:pointer;border:none;outline:none;width:20px;height:20px;padding:0;overflow:hidden}.el-color-predefine__color-selector.selected{box-shadow:0 0 3px 2px var(--el-color-primary)}.el-color-predefine__color-selector:focus-visible{outline:2px solid var(--el-color-primary);outline-offset:2px}.el-color-predefine__color-selector>div{height:100%;display:flex}.el-color-predefine__color-selector.is-alpha{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMCAIAAADZF8uwAAAAGUlEQVQYV2M4gwH+YwCGIasIUwhT25BVBADtzYNYrHvv4gAAAABJRU5ErkJggg==)}.el-color-hue-slider{box-sizing:border-box;float:right;background-color:red;width:280px;height:12px;padding:0 2px;position:relative}.el-color-hue-slider__bar{background:linear-gradient(90deg,red,#ff0 17%,#0f0 33%,#0ff,#00f 67%,#f0f 83%,red);height:100%;position:relative}.el-color-hue-slider__thumb{cursor:pointer;box-sizing:border-box;border:1px solid var(--el-border-color-lighter);z-index:1;background:#fff;border-radius:1px;width:4px;height:100%;position:absolute;top:0;left:0;box-shadow:0 0 2px #0009}.el-color-hue-slider__thumb:focus-visible{outline:2px solid var(--el-color-primary);outline-offset:1px}.el-color-hue-slider.is-vertical{width:12px;height:180px;padding:2px 0}.el-color-hue-slider.is-vertical .el-color-hue-slider__bar{background:linear-gradient(red,#ff0 17%,#0f0 33%,#0ff,#00f 67%,#f0f 83%,red)}.el-color-hue-slider.is-vertical .el-color-hue-slider__thumb{width:100%;height:4px;top:0;left:0}.el-color-svpanel{background-image:linear-gradient(#0000,#000),linear-gradient(90deg,#fff,#fff0);width:280px;height:180px;position:relative}.el-color-svpanel__cursor{cursor:pointer;border-radius:50%;width:4px;height:4px;position:absolute;transform:translate(-2px,-2px);box-shadow:0 0 0 1.5px #fff,inset 0 0 1px 1px #0000004d,0 0 1px 2px #0006}.el-color-svpanel__cursor:focus-visible{outline:2px solid var(--el-color-primary);outline-offset:2px}.el-color-alpha-slider{box-sizing:border-box;background-image:linear-gradient(45deg,var(--el-color-picker-alpha-bg-a) 25%,var(--el-color-picker-alpha-bg-b) 25%),linear-gradient(135deg,var(--el-color-picker-alpha-bg-a) 25%,var(--el-color-picker-alpha-bg-b) 25%),linear-gradient(45deg,var(--el-color-picker-alpha-bg-b) 75%,var(--el-color-picker-alpha-bg-a) 75%),linear-gradient(135deg,var(--el-color-picker-alpha-bg-b) 75%,var(--el-color-picker-alpha-bg-a) 75%);background-position:0 0,6px 0,6px -6px,0 6px;background-size:12px 12px;width:280px;height:12px;position:relative}.el-color-alpha-slider.is-disabled .el-color-alpha-slider__thumb{cursor:not-allowed}.el-color-alpha-slider__bar{background:linear-gradient(to right,#fff0 0%,var(--el-bg-color) 100%);height:100%;position:relative}.el-color-alpha-slider__thumb{cursor:pointer;box-sizing:border-box;border:1px solid var(--el-border-color-lighter);z-index:1;background:#fff;border-radius:1px;width:4px;height:100%;position:absolute;top:0;left:0;box-shadow:0 0 2px #0009}.el-color-alpha-slider__thumb:focus-visible{outline:2px solid var(--el-color-primary);outline-offset:1px}.el-color-alpha-slider.is-vertical{width:20px;height:180px}.el-color-alpha-slider.is-vertical .el-color-alpha-slider__bar{background:linear-gradient(#fff0,#fff)}.el-color-alpha-slider.is-vertical .el-color-alpha-slider__thumb{width:100%;height:4px;top:0;left:0}.el-color-picker-panel{--el-color-picker-alpha-bg-a:#ccc;--el-color-picker-alpha-bg-b:transparent}.dark .el-color-picker-panel{--el-color-picker-alpha-bg-a:#333}}@layer element-plus{.el-color-picker{outline:none;width:32px;height:32px;line-height:normal;display:inline-block;position:relative}.el-color-picker:hover:not(:-webkit-any(.is-disabled,.is-focused)) .el-color-picker__trigger{border-color:var(--el-border-color-hover)}.el-color-picker:hover:not(:is(.is-disabled,.is-focused)) .el-color-picker__trigger{border-color:var(--el-border-color-hover)}.el-color-picker:focus-visible:not(.is-disabled) .el-color-picker__trigger{outline:2px solid var(--el-color-primary);outline-offset:1px}.el-color-picker.is-focused .el-color-picker__trigger{border-color:var(--el-color-primary)}.el-color-picker.is-disabled .el-color-picker__trigger{cursor:not-allowed;background-color:var(--el-fill-color-light)}.el-color-picker.is-disabled .el-color-picker__color{opacity:.3}.el-color-picker--large{width:40px;height:40px}.el-color-picker--small{width:24px;height:24px}.el-color-picker--small .el-color-picker__icon,.el-color-picker--small .el-color-picker__empty{transform:scale(.8)}.el-color-picker__trigger{box-sizing:border-box;border:1px solid var(--el-border-color);cursor:pointer;border-radius:4px;justify-content:center;align-items:center;width:100%;height:100%;padding:4px;font-size:0;display:inline-flex;position:relative}.el-color-picker__color{box-sizing:border-box;border:1px solid var(--el-text-color-secondary);border-radius:var(--el-border-radius-small);text-align:center;width:100%;height:100%;display:block;position:relative}.el-color-picker__color.is-alpha{background-image:linear-gradient(45deg,var(--el-color-picker-alpha-bg-a) 25%,var(--el-color-picker-alpha-bg-b) 25%),linear-gradient(135deg,var(--el-color-picker-alpha-bg-a) 25%,var(--el-color-picker-alpha-bg-b) 25%),linear-gradient(45deg,var(--el-color-picker-alpha-bg-b) 75%,var(--el-color-picker-alpha-bg-a) 75%),linear-gradient(135deg,var(--el-color-picker-alpha-bg-b) 75%,var(--el-color-picker-alpha-bg-a) 75%);background-position:0 0,6px 0,6px -6px,0 6px;background-size:12px 12px}.el-color-picker__color-inner{justify-content:center;align-items:center;width:100%;height:100%;display:inline-flex}.el-color-picker .el-color-picker__empty{color:var(--el-text-color-secondary);font-size:12px}.el-color-picker .el-color-picker__icon{color:#fff;justify-content:center;align-items:center;font-size:12px;display:inline-flex}.el-color-picker__panel{border-radius:var(--el-border-radius-base);box-shadow:var(--el-box-shadow-light);background-color:#fff}.el-color-picker__panel.el-popper{border:1px solid var(--el-border-color-lighter)}.el-color-picker,.el-color-picker__panel{--el-color-picker-alpha-bg-a:#ccc;--el-color-picker-alpha-bg-b:transparent}.dark .el-color-picker,.dark .el-color-picker__panel{--el-color-picker-alpha-bg-a:#333}}@layer element-plus{.el-table{--el-table-border-color:var(--el-border-color-lighter);--el-table-border:1px solid var(--el-table-border-color);--el-table-text-color:var(--el-text-color-regular);--el-table-header-text-color:var(--el-text-color-secondary);--el-table-row-hover-bg-color:var(--el-fill-color-light);--el-table-current-row-bg-color:var(--el-color-primary-light-9);--el-table-header-bg-color:var(--el-fill-color-blank);--el-table-fixed-box-shadow:var(--el-box-shadow-light);--el-table-bg-color:var(--el-fill-color-blank);--el-table-tr-bg-color:var(--el-fill-color-blank);--el-table-expanded-cell-bg-color:var(--el-fill-color-blank);--el-table-fixed-left-column:inset 10px 0 10px -10px #00000026;--el-table-fixed-right-column:inset -10px 0 10px -10px #00000026;--el-table-index:var(--el-index-normal);box-sizing:border-box;background-color:var(--el-table-bg-color);width:100%;max-width:100%;height:-moz-fit-content;height:fit-content;font-size:var(--el-font-size-base);color:var(--el-table-text-color);position:relative;overflow:hidden}.el-table__inner-wrapper{flex-direction:column;height:100%;display:flex;position:relative}.el-table__inner-wrapper:before{height:1px;bottom:0;left:0}.el-table tbody:focus-visible{outline:none}.el-table.has-footer.el-table--scrollable-y tr:last-child td.el-table__cell,.el-table.has-footer.el-table--fluid-height tr:last-child td.el-table__cell{border-bottom-color:#0000}.el-table__empty-block{text-align:center;justify-content:center;align-items:center;width:100%;min-height:60px;display:flex;position:sticky;left:0}.el-table__empty-text{width:50%;color:var(--el-text-color-secondary);line-height:60px}.el-table__expand-column .cell{text-align:center;-webkit-user-select:none;user-select:none;padding:0}.el-table__expand-icon{cursor:pointer;-webkit-appearance:none;-moz-appearance:none;appearance:none;border-radius:var(--el-border-radius-base);transition:transform var(--el-transition-duration-fast) ease-in-out;background-color:#0000;border:none;outline:none;margin:0;padding:0}.el-table__expand-icon:focus-visible{outline:2px solid var(--el-color-primary);outline-offset:-2px}.el-table__expand-icon{color:var(--el-text-color-regular);width:min(23px,100%);height:23px;font-size:12px;line-height:12px}.el-table__expand-icon.is-disabled{color:var(--el-disabled-text-color);cursor:not-allowed}.el-table__expand-icon--expanded{transform:rotate(90deg)}.el-table__expand-icon>.el-icon{font-size:12px}.el-table__expanded-cell{background-color:var(--el-table-expanded-cell-bg-color)}.el-table__expanded-cell[class*=cell]{padding:20px 50px}.el-table__expanded-cell:hover{background-color:#0000!important}.el-table__placeholder{width:20px;display:inline-block}.el-table__append-wrapper{overflow:hidden}.el-table--fit{border-bottom:0;border-right:0}.el-table--fit .el-table__cell.gutter{border-right-width:1px}.el-table--fit .el-table__inner-wrapper:before{width:100%}.el-table thead{color:var(--el-table-header-text-color)}.el-table thead th{font-weight:600}.el-table thead.is-group th.el-table__cell{background:var(--el-fill-color-light)}.el-table .el-table__cell{box-sizing:border-box;text-overflow:ellipsis;vertical-align:middle;text-align:left;min-width:0;z-index:var(--el-table-index);padding:8px 0;position:relative}.el-table .el-table__cell.is-center{text-align:center}.el-table .el-table__cell.is-right{text-align:right}.el-table .el-table__cell.gutter{border-bottom-width:0;border-right-width:0;width:15px;padding:0}.el-table .el-table__cell.is-hidden>*{visibility:hidden}.el-table .cell{box-sizing:border-box;text-overflow:ellipsis;white-space:normal;overflow-wrap:break-word;padding:0 12px;line-height:23px;overflow:hidden}.el-table .cell.el-tooltip{white-space:nowrap;min-width:50px}.el-table--large{font-size:var(--el-font-size-base)}.el-table--large .el-table__cell{padding:12px 0}.el-table--large .cell{padding:0 16px}.el-table--default{font-size:var(--el-font-size-base)}.el-table--default .el-table__cell{padding:8px 0}.el-table--default .cell{padding:0 12px}.el-table--small{font-size:var(--el-font-size-extra-small)}.el-table--small .el-table__cell{padding:4px 0}.el-table--small .cell{padding:0 8px}.el-table tr{background-color:var(--el-table-tr-bg-color)}.el-table tr input[type=checkbox]{margin:0}.el-table th.el-table__cell.is-leaf,.el-table td.el-table__cell{border-bottom:var(--el-table-border)}.el-table th.el-table__cell.is-sortable{cursor:pointer}.el-table th.el-table__cell{background-color:var(--el-table-header-bg-color)}.el-table th.el-table__cell>.cell.highlight{color:var(--el-color-primary)}.el-table th.el-table__cell.required>div:before{content:"";vertical-align:middle;background:#ff4d51;border-radius:50%;width:8px;height:8px;margin-right:5px;display:inline-block}.el-table td.el-table__cell div{box-sizing:border-box}.el-table td.el-table__cell.gutter{width:0}.el-table--border:after,.el-table--border:before,.el-table--border .el-table__inner-wrapper:after,.el-table__inner-wrapper:before{content:"";background-color:var(--el-table-border-color);z-index:calc(var(--el-table-index) + 2);position:absolute}.el-table--border .el-table__inner-wrapper:after{width:100%;height:1px;z-index:calc(var(--el-table-index) + 2);top:0;left:0}.el-table--border:before{width:1px;height:100%;top:-1px;left:0}.el-table--border:after{width:1px;height:100%;top:-1px;right:0}.el-table--border .el-table__inner-wrapper{border-bottom:none;border-right:none}.el-table--border .el-table__footer-wrapper{flex-shrink:0;position:relative}.el-table--border .el-table__cell{border-right:var(--el-table-border)}.el-table--border th.el-table__cell.gutter:last-of-type{border-bottom:var(--el-table-border);border-bottom-width:1px}.el-table--border th.el-table__cell{border-bottom:var(--el-table-border)}.el-table--hidden{visibility:hidden}.el-table__header-wrapper,.el-table__body-wrapper,.el-table__footer-wrapper{width:100%}.el-table__header-wrapper tr td.el-table-fixed-column--left,.el-table__header-wrapper tr td.el-table-fixed-column--right,.el-table__header-wrapper tr th.el-table-fixed-column--left,.el-table__header-wrapper tr th.el-table-fixed-column--right,.el-table__body-wrapper tr td.el-table-fixed-column--left,.el-table__body-wrapper tr td.el-table-fixed-column--right,.el-table__body-wrapper tr th.el-table-fixed-column--left,.el-table__body-wrapper tr th.el-table-fixed-column--right,.el-table__footer-wrapper tr td.el-table-fixed-column--left,.el-table__footer-wrapper tr td.el-table-fixed-column--right,.el-table__footer-wrapper tr th.el-table-fixed-column--left,.el-table__footer-wrapper tr th.el-table-fixed-column--right{background:inherit;z-index:calc(var(--el-table-index) + 1);position:sticky!important}.el-table__header-wrapper tr td.el-table-fixed-column--left.is-last-column:before,.el-table__header-wrapper tr td.el-table-fixed-column--left.is-first-column:before,.el-table__header-wrapper tr td.el-table-fixed-column--right.is-last-column:before,.el-table__header-wrapper tr td.el-table-fixed-column--right.is-first-column:before,.el-table__header-wrapper tr th.el-table-fixed-column--left.is-last-column:before,.el-table__header-wrapper tr th.el-table-fixed-column--left.is-first-column:before,.el-table__header-wrapper tr th.el-table-fixed-column--right.is-last-column:before,.el-table__header-wrapper tr th.el-table-fixed-column--right.is-first-column:before,.el-table__body-wrapper tr td.el-table-fixed-column--left.is-last-column:before,.el-table__body-wrapper tr td.el-table-fixed-column--left.is-first-column:before,.el-table__body-wrapper tr td.el-table-fixed-column--right.is-last-column:before,.el-table__body-wrapper tr td.el-table-fixed-column--right.is-first-column:before,.el-table__body-wrapper tr th.el-table-fixed-column--left.is-last-column:before,.el-table__body-wrapper tr th.el-table-fixed-column--left.is-first-column:before,.el-table__body-wrapper tr th.el-table-fixed-column--right.is-last-column:before,.el-table__body-wrapper tr th.el-table-fixed-column--right.is-first-column:before,.el-table__footer-wrapper tr td.el-table-fixed-column--left.is-last-column:before,.el-table__footer-wrapper tr td.el-table-fixed-column--left.is-first-column:before,.el-table__footer-wrapper tr td.el-table-fixed-column--right.is-last-column:before,.el-table__footer-wrapper tr td.el-table-fixed-column--right.is-first-column:before,.el-table__footer-wrapper tr th.el-table-fixed-column--left.is-last-column:before,.el-table__footer-wrapper tr th.el-table-fixed-column--left.is-first-column:before,.el-table__footer-wrapper tr th.el-table-fixed-column--right.is-last-column:before,.el-table__footer-wrapper tr th.el-table-fixed-column--right.is-first-column:before{content:"";width:10px;box-shadow:none;touch-action:none;pointer-events:none;position:absolute;top:0;bottom:0;overflow:hidden}.el-table__header-wrapper tr td.el-table-fixed-column--left.is-first-column:before,.el-table__header-wrapper tr td.el-table-fixed-column--right.is-first-column:before,.el-table__header-wrapper tr th.el-table-fixed-column--left.is-first-column:before,.el-table__header-wrapper tr th.el-table-fixed-column--right.is-first-column:before,.el-table__body-wrapper tr td.el-table-fixed-column--left.is-first-column:before,.el-table__body-wrapper tr td.el-table-fixed-column--right.is-first-column:before,.el-table__body-wrapper tr th.el-table-fixed-column--left.is-first-column:before,.el-table__body-wrapper tr th.el-table-fixed-column--right.is-first-column:before,.el-table__footer-wrapper tr td.el-table-fixed-column--left.is-first-column:before,.el-table__footer-wrapper tr td.el-table-fixed-column--right.is-first-column:before,.el-table__footer-wrapper tr th.el-table-fixed-column--left.is-first-column:before,.el-table__footer-wrapper tr th.el-table-fixed-column--right.is-first-column:before{left:-10px}.el-table__header-wrapper tr td.el-table-fixed-column--left.is-last-column:before,.el-table__header-wrapper tr td.el-table-fixed-column--right.is-last-column:before,.el-table__header-wrapper tr th.el-table-fixed-column--left.is-last-column:before,.el-table__header-wrapper tr th.el-table-fixed-column--right.is-last-column:before,.el-table__body-wrapper tr td.el-table-fixed-column--left.is-last-column:before,.el-table__body-wrapper tr td.el-table-fixed-column--right.is-last-column:before,.el-table__body-wrapper tr th.el-table-fixed-column--left.is-last-column:before,.el-table__body-wrapper tr th.el-table-fixed-column--right.is-last-column:before,.el-table__footer-wrapper tr td.el-table-fixed-column--left.is-last-column:before,.el-table__footer-wrapper tr td.el-table-fixed-column--right.is-last-column:before,.el-table__footer-wrapper tr th.el-table-fixed-column--left.is-last-column:before,.el-table__footer-wrapper tr th.el-table-fixed-column--right.is-last-column:before{right:-10px}.el-table__header-wrapper tr td.el-table__fixed-right-patch,.el-table__header-wrapper tr th.el-table__fixed-right-patch,.el-table__body-wrapper tr td.el-table__fixed-right-patch,.el-table__body-wrapper tr th.el-table__fixed-right-patch,.el-table__footer-wrapper tr td.el-table__fixed-right-patch,.el-table__footer-wrapper tr th.el-table__fixed-right-patch{z-index:calc(var(--el-table-index) + 1);background:#fff;right:0;position:sticky!important}.el-table__header-wrapper{flex-shrink:0}.el-table__header-wrapper tr th.el-table-fixed-column--left,.el-table__header-wrapper tr th.el-table-fixed-column--right{background-color:var(--el-table-header-bg-color)}.el-table__header,.el-table__body,.el-table__footer{table-layout:fixed;border-collapse:separate}.el-table__header-wrapper{overflow:hidden}.el-table__header-wrapper tbody td.el-table__cell{background-color:var(--el-table-row-hover-bg-color);color:var(--el-table-text-color)}.el-table__footer-wrapper{flex-shrink:0;overflow:hidden}.el-table__footer-wrapper tfoot td.el-table__cell{background-color:var(--el-table-row-hover-bg-color);color:var(--el-table-text-color)}.el-table__header-wrapper .el-table-column--selection>.cell,.el-table__body-wrapper .el-table-column--selection>.cell{align-items:center;height:23px;display:inline-flex}.el-table__header-wrapper .el-table-column--selection .el-checkbox,.el-table__body-wrapper .el-table-column--selection .el-checkbox{height:unset}.el-table.is-scrolling-left .el-table-fixed-column--right.is-first-column:before{box-shadow:var(--el-table-fixed-right-column)}.el-table.is-scrolling-left.el-table--border .el-table-fixed-column--left.is-last-column.el-table__cell{border-right:var(--el-table-border)}.el-table.is-scrolling-left th.el-table-fixed-column--left{background-color:var(--el-table-header-bg-color)}.el-table.is-scrolling-right .el-table-fixed-column--left.is-last-column:before{box-shadow:var(--el-table-fixed-left-column)}.el-table.is-scrolling-right .el-table-fixed-column--left.is-last-column.el-table__cell{border-right:none}.el-table.is-scrolling-right th.el-table-fixed-column--right{background-color:var(--el-table-header-bg-color)}.el-table.is-scrolling-middle .el-table-fixed-column--left.is-last-column.el-table__cell{border-right:none}.el-table.is-scrolling-middle .el-table-fixed-column--right.is-first-column:before{box-shadow:var(--el-table-fixed-right-column)}.el-table.is-scrolling-middle .el-table-fixed-column--left.is-last-column:before{box-shadow:var(--el-table-fixed-left-column)}.el-table.is-scrolling-none .el-table-fixed-column--left.is-first-column:before,.el-table.is-scrolling-none .el-table-fixed-column--left.is-last-column:before,.el-table.is-scrolling-none .el-table-fixed-column--right.is-first-column:before,.el-table.is-scrolling-none .el-table-fixed-column--right.is-last-column:before{box-shadow:none}.el-table.is-scrolling-none th.el-table-fixed-column--left,.el-table.is-scrolling-none th.el-table-fixed-column--right{background-color:var(--el-table-header-bg-color)}.el-table__body-wrapper{flex:1;position:relative;overflow:hidden}.el-table__body-wrapper .el-scrollbar__bar{z-index:calc(var(--el-table-index) + 2)}.el-table .caret-wrapper{cursor:pointer;-webkit-appearance:none;-moz-appearance:none;appearance:none;border-radius:var(--el-border-radius-base);transition:transform var(--el-transition-duration-fast) ease-in-out;background-color:#0000;border:none;outline:none;margin:0;padding:0}.el-table .caret-wrapper:focus-visible{outline:2px solid var(--el-color-primary);outline-offset:2px}.el-table .caret-wrapper{vertical-align:middle;width:24px;height:14px;overflow:initial;flex-direction:column;align-items:center;display:inline-flex;position:relative}.el-table .sort-caret{border:5px solid #0000;width:0;height:0;position:absolute;left:7px}.el-table .sort-caret.ascending{border-bottom-color:var(--el-text-color-placeholder);top:-5px}.el-table .sort-caret.descending{border-top-color:var(--el-text-color-placeholder);bottom:-3px}.el-table .ascending .sort-caret.ascending{border-bottom-color:var(--el-color-primary)}.el-table .descending .sort-caret.descending{border-top-color:var(--el-color-primary)}.el-table .hidden-columns{visibility:hidden;z-index:-1;position:absolute}.el-table--striped .el-table__body tr.el-table__row--striped td.el-table__cell{background:var(--el-fill-color-lighter)}.el-table--striped .el-table__body tr.el-table__row--striped.current-row td.el-table__cell{background-color:var(--el-table-current-row-bg-color)}.el-table__body tr.hover-row>td.el-table__cell,.el-table__body tr.hover-row.current-row>td.el-table__cell,.el-table__body tr.hover-row.el-table__row--striped>td.el-table__cell,.el-table__body tr.hover-row.el-table__row--striped.current-row>td.el-table__cell,.el-table__body tr>td.hover-cell{background-color:var(--el-table-row-hover-bg-color)}.el-table__body tr.current-row>td.el-table__cell{background-color:var(--el-table-current-row-bg-color)}.el-table.el-table--scrollable-y .el-table__body-header{z-index:calc(var(--el-table-index) + 2);position:sticky;top:0}.el-table.el-table--scrollable-y .el-table__body-footer{z-index:calc(var(--el-table-index) + 2);position:sticky;bottom:0}.el-table__column-resize-proxy{border-left:var(--el-table-border);width:0;z-index:calc(var(--el-table-index) + 9);position:absolute;top:0;bottom:0;left:200px}.el-table__column-filter-trigger{cursor:pointer;-webkit-appearance:none;-moz-appearance:none;appearance:none;border-radius:var(--el-border-radius-base);transition:transform var(--el-transition-duration-fast) ease-in-out;background-color:#0000;border:none;outline:none;margin:0;padding:0}.el-table__column-filter-trigger:focus-visible{outline:2px solid var(--el-color-primary);outline-offset:2px}.el-table__column-filter-trigger{display:inline-block}.el-table__column-filter-trigger i{color:var(--el-color-info);vertical-align:middle;font-size:14px}.el-table__border-left-patch{width:1px;height:100%;z-index:calc(var(--el-table-index) + 2);background-color:var(--el-table-border-color);position:absolute;top:0;left:0}.el-table__border-bottom-patch{height:1px;z-index:calc(var(--el-table-index) + 2);background-color:var(--el-table-border-color);position:absolute;left:0}.el-table__border-right-patch{width:1px;height:100%;z-index:calc(var(--el-table-index) + 2);background-color:var(--el-table-border-color);position:absolute;top:0}.el-table--enable-row-transition .el-table__body td.el-table__cell{transition:background-color .25s 1ms}.el-table--enable-row-hover .el-table__body tr:hover>td.el-table__cell{background-color:var(--el-table-row-hover-bg-color)}.el-table [class*=el-table__row--level] .el-table__expand-icon{text-align:center;width:20px;display:inline-block}.el-table .el-table.el-table--border .el-table__cell{border-right:var(--el-table-border)}.el-table:not(.el-table--border) .el-table__cell{border-right:none}.el-table:not(.el-table--border)>.el-table__inner-wrapper:after{content:none}}@layer element-plus{.el-table-column--selection .cell{padding-left:14px;padding-right:14px}.el-table-filter{border:solid 1px var(--el-border-color-lighter);box-shadow:var(--el-box-shadow-light);box-sizing:border-box;background-color:#fff;border-radius:2px}.el-table-filter__list{outline:none;min-width:100px;margin:0;padding:5px 0;list-style:none}.el-table-filter__list-item{cursor:pointer;line-height:36px;font-size:var(--el-font-size-base);outline:none;padding:0 10px}.el-table-filter__list-item:hover,.el-table-filter__list-item:focus{background-color:var(--el-color-primary-light-9);color:var(--el-color-primary)}.el-table-filter__list-item.is-active{background-color:var(--el-color-primary);color:#fff}.el-table-filter__multiple{outline:none}.el-table-filter__content{min-width:100px}.el-table-filter__bottom{border-top:1px solid var(--el-border-color-lighter);padding:8px}.el-table-filter__bottom button{cursor:pointer;-webkit-appearance:none;-moz-appearance:none;appearance:none;border-radius:var(--el-border-radius-base);transition:transform var(--el-transition-duration-fast) ease-in-out;background-color:#0000;border:none;outline:none;margin:0;padding:0}.el-table-filter__bottom button:focus-visible{outline:2px solid var(--el-color-primary);outline-offset:2px}.el-table-filter__bottom button{color:var(--el-text-color-regular);font-size:var(--el-font-size-small);padding:0 3px}.el-table-filter__bottom button:hover{color:var(--el-color-primary)}.el-table-filter__bottom button.is-disabled{color:var(--el-disabled-text-color);cursor:not-allowed}.el-table-filter__wrap{max-height:280px}.el-table-filter__checkbox-group{padding:10px}.el-table-filter__checkbox-group label.el-checkbox{height:unset;align-items:center;margin-bottom:12px;margin-left:5px;margin-right:5px;display:flex}.el-table-filter__checkbox-group .el-checkbox:last-child{margin-bottom:0}}@layer element-plus{.el-pagination{--el-pagination-font-size:14px;--el-pagination-bg-color:var(--el-fill-color-blank);--el-pagination-text-color:var(--el-text-color-primary);--el-pagination-border-radius:2px;--el-pagination-button-color:var(--el-text-color-primary);--el-pagination-button-width:32px;--el-pagination-button-height:32px;--el-pagination-button-disabled-color:var(--el-text-color-placeholder);--el-pagination-button-disabled-bg-color:var(--el-fill-color-blank);--el-pagination-button-bg-color:var(--el-fill-color);--el-pagination-hover-color:var(--el-color-primary);--el-pagination-font-size-small:12px;--el-pagination-button-width-small:24px;--el-pagination-button-height-small:24px;--el-pagination-button-width-large:40px;--el-pagination-button-height-large:40px;--el-pagination-item-gap:16px;white-space:nowrap;color:var(--el-pagination-text-color);font-size:var(--el-pagination-font-size);align-items:center;font-weight:400;display:flex}.el-pagination .el-input__inner{text-align:center;-moz-appearance:textfield}.el-pagination .el-select{width:128px}.el-pagination .btn-prev,.el-pagination .btn-next{font-size:var(--el-pagination-font-size);min-width:var(--el-pagination-button-width);height:var(--el-pagination-button-height);line-height:var(--el-pagination-button-height);color:var(--el-pagination-button-color);background:var(--el-pagination-bg-color);border-radius:var(--el-pagination-border-radius);cursor:pointer;text-align:center;box-sizing:border-box;border:none;justify-content:center;align-items:center;padding:0 4px;display:flex}.el-pagination .btn-prev *,.el-pagination .btn-next *{pointer-events:none}.el-pagination .btn-prev:focus,.el-pagination .btn-next:focus{outline:none}.el-pagination .btn-prev:hover,.el-pagination .btn-next:hover{color:var(--el-pagination-hover-color)}.el-pagination .btn-prev.is-active,.el-pagination .btn-next.is-active{color:var(--el-pagination-hover-color);cursor:default;font-weight:700}.el-pagination .btn-prev.is-active.is-disabled,.el-pagination .btn-next.is-active.is-disabled{color:var(--el-text-color-secondary);font-weight:700}.el-pagination .btn-prev:disabled,.el-pagination .btn-prev.is-disabled,.el-pagination .btn-next:disabled,.el-pagination .btn-next.is-disabled{color:var(--el-pagination-button-disabled-color);background-color:var(--el-pagination-button-disabled-bg-color);cursor:not-allowed}.el-pagination .btn-prev:focus-visible{outline:1px solid var(--el-pagination-hover-color);outline-offset:-1px}.el-pagination .btn-next:focus-visible{outline:1px solid var(--el-pagination-hover-color);outline-offset:-1px}.el-pagination .btn-prev .el-icon,.el-pagination .btn-next .el-icon{width:inherit;font-size:12px;font-weight:700;display:block}.el-pagination>.is-first{margin-left:0!important}.el-pagination>.is-last{margin-right:0!important}.el-pagination .btn-prev{margin-left:var(--el-pagination-item-gap)}.el-pagination__sizes,.el-pagination__total{margin-left:var(--el-pagination-item-gap);color:var(--el-text-color-regular);font-weight:400}.el-pagination__total[disabled=true]{color:var(--el-text-color-placeholder)}.el-pagination__jump{margin-left:var(--el-pagination-item-gap);color:var(--el-text-color-regular);align-items:center;font-weight:400;display:flex}.el-pagination__jump[disabled=true]{color:var(--el-text-color-placeholder)}.el-pagination__goto{margin-right:8px}.el-pagination__editor{text-align:center;box-sizing:border-box}.el-pagination__editor.el-input{width:56px}.el-pagination__editor .el-input__inner::-webkit-inner-spin-button{-webkit-appearance:none;margin:0}.el-pagination__editor .el-input__inner::-webkit-outer-spin-button{-webkit-appearance:none;margin:0}.el-pagination__classifier{margin-left:8px}.el-pagination__rightwrapper{flex:1;justify-content:flex-end;align-items:center;display:flex}.el-pagination.is-background .btn-prev,.el-pagination.is-background .btn-next,.el-pagination.is-background .el-pager li{background-color:var(--el-pagination-button-bg-color);margin:0 4px}.el-pagination.is-background .btn-prev.is-active,.el-pagination.is-background .btn-next.is-active,.el-pagination.is-background .el-pager li.is-active{background-color:var(--el-color-primary);color:var(--el-color-white)}.el-pagination.is-background .btn-prev:disabled,.el-pagination.is-background .btn-prev.is-disabled,.el-pagination.is-background .btn-next:disabled,.el-pagination.is-background .btn-next.is-disabled,.el-pagination.is-background .el-pager li:disabled,.el-pagination.is-background .el-pager li.is-disabled{color:var(--el-text-color-placeholder);background-color:var(--el-disabled-bg-color)}.el-pagination.is-background .btn-prev:disabled.is-active,.el-pagination.is-background .btn-prev.is-disabled.is-active,.el-pagination.is-background .btn-next:disabled.is-active,.el-pagination.is-background .btn-next.is-disabled.is-active,.el-pagination.is-background .el-pager li:disabled.is-active,.el-pagination.is-background .el-pager li.is-disabled.is-active{color:var(--el-text-color-secondary);background-color:var(--el-fill-color-dark)}.el-pagination.is-background .btn-prev{margin-left:var(--el-pagination-item-gap)}.el-pagination--small .btn-prev,.el-pagination--small .btn-next,.el-pagination--small .el-pager li{height:var(--el-pagination-button-height-small);line-height:var(--el-pagination-button-height-small);font-size:var(--el-pagination-font-size-small);min-width:var(--el-pagination-button-width-small)}.el-pagination--small span:not([class*=suffix]),.el-pagination--small button{font-size:var(--el-pagination-font-size-small)}.el-pagination--small .el-select{width:100px}.el-pagination--large .btn-prev,.el-pagination--large .btn-next,.el-pagination--large .el-pager li{height:var(--el-pagination-button-height-large);line-height:var(--el-pagination-button-height-large);min-width:var(--el-pagination-button-width-large)}.el-pagination--large .el-select .el-input{width:160px}.el-pager{-webkit-user-select:none;user-select:none;align-items:center;margin:0;padding:0;font-size:0;list-style:none;display:flex}.el-pager li{font-size:var(--el-pagination-font-size);min-width:var(--el-pagination-button-width);height:var(--el-pagination-button-height);line-height:var(--el-pagination-button-height);color:var(--el-pagination-button-color);background:var(--el-pagination-bg-color);border-radius:var(--el-pagination-border-radius);cursor:pointer;text-align:center;box-sizing:border-box;border:none;justify-content:center;align-items:center;padding:0 4px;display:flex}.el-pager li *{pointer-events:none}.el-pager li:focus{outline:none}.el-pager li:hover{color:var(--el-pagination-hover-color)}.el-pager li.is-active{color:var(--el-pagination-hover-color);cursor:default;font-weight:700}.el-pager li.is-active.is-disabled{color:var(--el-text-color-secondary);font-weight:700}.el-pager li:disabled,.el-pager li.is-disabled{color:var(--el-pagination-button-disabled-color);background-color:var(--el-pagination-button-disabled-bg-color);cursor:not-allowed}.el-pager li:focus-visible{outline:1px solid var(--el-pagination-hover-color);outline-offset:-1px}}@layer element-plus{.el-vl__wrapper{position:relative}.el-vl__wrapper:hover .el-virtual-scrollbar,.el-vl__wrapper.always-on .el-virtual-scrollbar{opacity:1}.el-vl__window{scrollbar-width:none}.el-vl__window::-webkit-scrollbar{display:none}.el-virtual-scrollbar{opacity:0;transition:opacity .34s ease-out}.el-virtual-scrollbar.always-on{opacity:1}.el-vg__wrapper{position:relative}}@layer element-plus{.el-cascader-panel{--el-cascader-menu-text-color:var(--el-text-color-regular);--el-cascader-menu-selected-text-color:var(--el-color-primary);--el-cascader-menu-fill:var(--el-bg-color-overlay);--el-cascader-menu-font-size:var(--el-font-size-base);--el-cascader-menu-radius:var(--el-border-radius-base);--el-cascader-menu-border:solid 1px var(--el-border-color-light);--el-cascader-menu-shadow:var(--el-box-shadow-light);--el-cascader-node-background-hover:var(--el-fill-color-light);--el-cascader-node-color-disabled:var(--el-text-color-placeholder);--el-cascader-color-empty:var(--el-text-color-placeholder);--el-cascader-tag-background:var(--el-fill-color);border-radius:var(--el-cascader-menu-radius);width:-moz-fit-content;width:fit-content;font-size:var(--el-cascader-menu-font-size);display:flex}.el-cascader-panel.is-bordered{border:var(--el-cascader-menu-border);border-radius:var(--el-cascader-menu-radius)}.el-cascader-menu{box-sizing:border-box;min-width:180px;color:var(--el-cascader-menu-text-color);border-right:var(--el-cascader-menu-border)}.el-cascader-menu:last-child{border-right:none}.el-cascader-menu:last-child .el-cascader-node{padding-right:20px}.el-cascader-menu__wrap.el-scrollbar__wrap{height:204px}.el-cascader-menu__list{box-sizing:border-box;min-height:100%;margin:0;padding:6px 0;list-style:none;position:relative}.el-cascader-menu__list.el-vl__window{margin:6px 0;padding:0}.el-cascader-menu__list.el-vl__window ul{margin:0;padding:0}.el-cascader-menu__hover-zone{pointer-events:none;width:100%;height:100%;position:absolute;top:0;left:0}.el-cascader-menu__empty-text{color:var(--el-cascader-color-empty);align-items:center;display:flex;position:absolute;top:50%;left:50%;transform:translate(-50%,-50%)}.el-cascader-menu__empty-text .is-loading{margin-right:2px}.el-cascader-node{box-sizing:border-box;outline:none;align-items:center;height:34px;padding:0 30px 0 20px;line-height:34px;display:flex;position:relative}.el-cascader-node.is-selectable.in-active-path{color:var(--el-cascader-menu-text-color)}.el-cascader-node.in-active-path,.el-cascader-node.is-selectable.in-checked-path,.el-cascader-node.is-active{color:var(--el-cascader-menu-selected-text-color);font-weight:700}.el-cascader-node:not(.is-disabled){cursor:pointer}.el-cascader-node:not(.is-disabled):hover,.el-cascader-node:not(.is-disabled):focus{background:var(--el-cascader-node-background-hover)}.el-cascader-node.is-disabled{color:var(--el-cascader-node-color-disabled);cursor:not-allowed}.el-cascader-node__prefix{position:absolute;left:10px}.el-cascader-node__postfix{position:absolute;right:10px}.el-cascader-node__label{text-align:left;white-space:nowrap;text-overflow:ellipsis;flex:1;padding:0 8px;overflow:hidden}.el-cascader-node>.el-checkbox,.el-cascader-node>.el-radio{margin-right:0}.el-cascader-node>.el-radio .el-radio__label{padding-left:0}}@layer element-plus{.el-alert{--el-alert-padding:8px 16px;--el-alert-border-radius-base:var(--el-border-radius-base);--el-alert-title-font-size:14px;--el-alert-title-with-description-font-size:16px;--el-alert-description-font-size:14px;--el-alert-close-font-size:16px;--el-alert-close-customed-font-size:14px;--el-alert-icon-size:16px;--el-alert-icon-large-size:28px;width:100%;padding:var(--el-alert-padding);box-sizing:border-box;border-radius:var(--el-alert-border-radius-base);background-color:var(--el-color-white);opacity:1;transition:opacity var(--el-transition-duration-fast);align-items:center;margin:0;display:flex;position:relative;overflow:hidden}.el-alert.is-light .el-alert__close-btn{color:var(--el-text-color-placeholder)}.el-alert.is-dark .el-alert__close-btn,.el-alert.is-dark .el-alert__description{color:var(--el-color-white)}.el-alert.is-center{justify-content:center}.el-alert--primary{--el-alert-bg-color:var(--el-color-primary-light-9)}.el-alert--primary.is-light{background-color:var(--el-alert-bg-color);color:var(--el-color-primary)}.el-alert--primary.is-light .el-alert__description{color:var(--el-color-primary)}.el-alert--primary.is-dark{background-color:var(--el-color-primary);color:var(--el-color-white)}.el-alert--success{--el-alert-bg-color:var(--el-color-success-light-9)}.el-alert--success.is-light{background-color:var(--el-alert-bg-color);color:var(--el-color-success)}.el-alert--success.is-light .el-alert__description{color:var(--el-color-success)}.el-alert--success.is-dark{background-color:var(--el-color-success);color:var(--el-color-white)}.el-alert--info{--el-alert-bg-color:var(--el-color-info-light-9)}.el-alert--info.is-light{background-color:var(--el-alert-bg-color);color:var(--el-color-info)}.el-alert--info.is-light .el-alert__description{color:var(--el-color-info)}.el-alert--info.is-dark{background-color:var(--el-color-info);color:var(--el-color-white)}.el-alert--warning{--el-alert-bg-color:var(--el-color-warning-light-9)}.el-alert--warning.is-light{background-color:var(--el-alert-bg-color);color:var(--el-color-warning)}.el-alert--warning.is-light .el-alert__description{color:var(--el-color-warning)}.el-alert--warning.is-dark{background-color:var(--el-color-warning);color:var(--el-color-white)}.el-alert--error{--el-alert-bg-color:var(--el-color-error-light-9)}.el-alert--error.is-light{background-color:var(--el-alert-bg-color);color:var(--el-color-error)}.el-alert--error.is-light .el-alert__description{color:var(--el-color-error)}.el-alert--error.is-dark{background-color:var(--el-color-error);color:var(--el-color-white)}.el-alert__content{flex-direction:column;gap:4px;display:flex}.el-alert .el-alert__icon{font-size:var(--el-alert-icon-size);width:var(--el-alert-icon-size);margin-right:8px}.el-alert .el-alert__icon.is-big{font-size:var(--el-alert-icon-large-size);width:var(--el-alert-icon-large-size);margin-right:12px}.el-alert__title{font-size:var(--el-alert-title-font-size);line-height:24px}.el-alert__title.with-description{font-size:var(--el-alert-title-with-description-font-size)}.el-alert .el-alert__description{font-size:var(--el-alert-description-font-size);margin:0}.el-alert .el-alert__close-btn{font-size:var(--el-alert-close-font-size);opacity:1;cursor:pointer;position:absolute;top:12px;right:16px}.el-alert .el-alert__close-btn.is-customed{font-style:normal;font-size:var(--el-alert-close-customed-font-size);line-height:24px;top:8px}.el-alert-fade-enter-from,.el-alert-fade-leave-active{opacity:0}}@layer element-plus{.el-tabs{--el-tabs-header-height:40px;display:flex}.el-tabs__header{justify-content:space-between;align-items:center;margin:0 0 15px;padding:0;display:flex;position:relative}.el-tabs__header-vertical{flex-direction:column}.el-tabs__active-bar{background-color:var(--el-color-primary);z-index:1;height:2px;transition:width var(--el-transition-duration) var(--el-transition-function-ease-in-out-bezier),transform var(--el-transition-duration) var(--el-transition-function-ease-in-out-bezier);list-style:none;position:absolute;bottom:0;left:0}.el-tabs__active-bar.is-bottom{bottom:auto}.el-tabs__new-tab{border:1px solid var(--el-border-color);text-align:center;width:20px;height:20px;color:var(--el-text-color-primary);cursor:pointer;border-radius:3px;flex-shrink:0;justify-content:center;align-items:center;margin:10px 0 10px 10px;font-size:12px;line-height:20px;transition:all .15s;display:flex}.el-tabs__new-tab .is-icon-plus{height:inherit;width:inherit;transform:scale(.8)}.el-tabs__new-tab .is-icon-plus svg{vertical-align:middle}.el-tabs__new-tab:hover{color:var(--el-color-primary)}.el-tabs__new-tab-vertical{margin-left:0}.el-tabs__nav-wrap{flex:auto;margin-bottom:-1px;position:relative;overflow:hidden}.el-tabs__nav-wrap:after{content:"";background-color:var(--el-border-color-light);width:100%;height:2px;z-index:var(--el-index-normal);position:absolute;bottom:0;left:0}.el-tabs__nav-wrap.is-bottom:after{top:0;bottom:auto}.el-tabs__nav-wrap.is-scrollable{box-sizing:border-box;padding:0 20px}.el-tabs__nav-scroll{overflow:hidden}.el-tabs__nav-next,.el-tabs__nav-prev{cursor:pointer;color:var(--el-text-color-secondary);text-align:center;width:20px;font-size:12px;line-height:44px;position:absolute}.el-tabs__nav-next.is-disabled,.el-tabs__nav-prev.is-disabled{color:var(--el-text-color-disabled);cursor:not-allowed}.el-tabs__nav-next{right:0}.el-tabs__nav-prev{left:0}.el-tabs__nav{white-space:nowrap;transition:transform var(--el-transition-duration);float:left;z-index:calc(var(--el-index-normal) + 1);display:flex;position:relative}.el-tabs__nav.is-stretch{min-width:100%;display:flex}.el-tabs__nav.is-stretch>*{text-align:center;flex:1}.el-tabs__item{height:var(--el-tabs-header-height);box-sizing:border-box;font-size:var(--el-font-size-base);color:var(--el-text-color-primary);justify-content:center;align-items:center;padding:0 20px;font-weight:500;list-style:none;display:flex;position:relative}.el-tabs__item:focus,.el-tabs__item:focus:active{outline:none}.el-tabs__item:focus-visible{box-shadow:0 0 2px 2px var(--el-color-primary) inset;border-radius:3px}.el-tabs__item .is-icon-close{text-align:center;transition:all var(--el-transition-duration) var(--el-transition-function-ease-in-out-bezier);border-radius:50%;margin-left:5px}.el-tabs__item .is-icon-close:before{display:inline-block;transform:scale(.9)}.el-tabs__item .is-icon-close:hover{background-color:var(--el-text-color-placeholder);color:#fff}.el-tabs__item.is-active{color:var(--el-color-primary)}.el-tabs__item:hover{color:var(--el-color-primary);cursor:pointer}.el-tabs__item.is-disabled{color:var(--el-disabled-text-color);cursor:not-allowed}.el-tabs__content{flex-grow:1;position:relative;overflow:hidden}.el-tabs--top>.el-tabs__header .el-tabs__item:nth-child(2),.el-tabs--bottom>.el-tabs__header .el-tabs__item:nth-child(2){padding-left:0}.el-tabs--top>.el-tabs__header .el-tabs__item:last-child,.el-tabs--bottom>.el-tabs__header .el-tabs__item:last-child{padding-right:0}.el-tabs--top.el-tabs--border-card>.el-tabs__header .el-tabs__item:nth-child(2),.el-tabs--top.el-tabs--card>.el-tabs__header .el-tabs__item:nth-child(2),.el-tabs--bottom.el-tabs--border-card>.el-tabs__header .el-tabs__item:nth-child(2),.el-tabs--bottom.el-tabs--card>.el-tabs__header .el-tabs__item:nth-child(2){padding-left:20px}.el-tabs--top.el-tabs--border-card>.el-tabs__header .el-tabs__item:last-child,.el-tabs--top.el-tabs--card>.el-tabs__header .el-tabs__item:last-child,.el-tabs--bottom.el-tabs--border-card>.el-tabs__header .el-tabs__item:last-child,.el-tabs--bottom.el-tabs--card>.el-tabs__header .el-tabs__item:last-child{padding-right:20px}.el-tabs--card>.el-tabs__header{border-bottom:1px solid var(--el-border-color-light);height:var(--el-tabs-header-height);box-sizing:border-box}.el-tabs--card>.el-tabs__header .el-tabs__nav-wrap:after{content:none}.el-tabs--card>.el-tabs__header .el-tabs__nav{border:1px solid var(--el-border-color-light);box-sizing:border-box;border-bottom:none;border-radius:4px 4px 0 0}.el-tabs--card>.el-tabs__header .el-tabs__active-bar{display:none}.el-tabs--card>.el-tabs__header .el-tabs__item .is-icon-close{transform-origin:100%;width:0;height:14px;font-size:12px;position:relative;right:-2px;overflow:hidden}.el-tabs--card>.el-tabs__header .el-tabs__item{border-bottom:1px solid #0000;border-left:1px solid var(--el-border-color-light);transition:color var(--el-transition-duration) var(--el-transition-function-ease-in-out-bezier),padding var(--el-transition-duration) var(--el-transition-function-ease-in-out-bezier);margin-top:-1px}.el-tabs--card>.el-tabs__header .el-tabs__item:first-child{border-left:none}.el-tabs--card>.el-tabs__header .el-tabs__item.is-closable:hover{padding-left:13px;padding-right:13px}.el-tabs--card>.el-tabs__header .el-tabs__item.is-closable:hover .is-icon-close{width:14px}.el-tabs--card>.el-tabs__header .el-tabs__item.is-active{border-bottom-color:var(--el-bg-color)}.el-tabs--card>.el-tabs__header .el-tabs__item.is-active.is-closable{padding-left:20px;padding-right:20px}.el-tabs--card>.el-tabs__header .el-tabs__item.is-active.is-closable .is-icon-close{width:14px}.el-tabs--border-card{background:var(--el-bg-color-overlay);border:1px solid var(--el-border-color)}.el-tabs--border-card>.el-tabs__content{padding:15px}.el-tabs--border-card>.el-tabs__header{background-color:var(--el-fill-color-light);border-bottom:1px solid var(--el-border-color-light);margin:0}.el-tabs--border-card>.el-tabs__header .el-tabs__nav-wrap:after{content:none}.el-tabs--border-card>.el-tabs__header .el-tabs__item{transition:all var(--el-transition-duration) var(--el-transition-function-ease-in-out-bezier);color:var(--el-text-color-secondary);border:1px solid #0000;margin-top:-1px}.el-tabs--border-card>.el-tabs__header .el-tabs__item:first-child,.el-tabs--border-card>.el-tabs__header .el-tabs__item+.el-tabs__item{margin-left:-1px}.el-tabs--border-card>.el-tabs__header .el-tabs__item.is-active{color:var(--el-color-primary);background-color:var(--el-bg-color-overlay);border-right-color:var(--el-border-color);border-left-color:var(--el-border-color)}.el-tabs--border-card>.el-tabs__header .el-tabs__item:not(.is-disabled):hover{color:var(--el-color-primary)}.el-tabs--border-card>.el-tabs__header .el-tabs__item.is-disabled{color:var(--el-disabled-text-color)}.el-tabs--border-card>.el-tabs__header .is-scrollable .el-tabs__item:first-child{margin-left:0}.el-tabs--bottom{flex-direction:column}.el-tabs--bottom .el-tabs__header.is-bottom{margin-top:10px;margin-bottom:0}.el-tabs--bottom.el-tabs--border-card .el-tabs__header.is-bottom{border-bottom:0;border-top:1px solid var(--el-border-color)}.el-tabs--bottom.el-tabs--border-card .el-tabs__nav-wrap.is-bottom{margin-top:-1px;margin-bottom:0}.el-tabs--bottom.el-tabs--border-card .el-tabs__item.is-bottom:not(.is-active){border:1px solid #0000}.el-tabs--bottom.el-tabs--border-card .el-tabs__item.is-bottom{margin:0 -1px -1px}.el-tabs--card>.el-tabs__header.is-left,.el-tabs--card>.el-tabs__header.is-right,.el-tabs--border-card>.el-tabs__header.is-left,.el-tabs--border-card>.el-tabs__header.is-right{border-bottom:none}.el-tabs--left,.el-tabs--right{overflow:hidden}.el-tabs--left .el-tabs__header.is-left,.el-tabs--left .el-tabs__header.is-right,.el-tabs--left .el-tabs__nav-wrap.is-left,.el-tabs--left .el-tabs__nav-wrap.is-right,.el-tabs--left .el-tabs__nav-scroll,.el-tabs--right .el-tabs__header.is-left,.el-tabs--right .el-tabs__header.is-right,.el-tabs--right .el-tabs__nav-wrap.is-left,.el-tabs--right .el-tabs__nav-wrap.is-right,.el-tabs--right .el-tabs__nav-scroll{height:100%}.el-tabs--left .el-tabs__active-bar.is-left,.el-tabs--left .el-tabs__active-bar.is-right,.el-tabs--right .el-tabs__active-bar.is-left,.el-tabs--right .el-tabs__active-bar.is-right{width:2px;height:auto;top:0;bottom:auto}.el-tabs--left .el-tabs__nav-wrap.is-left,.el-tabs--left .el-tabs__nav-wrap.is-right,.el-tabs--right .el-tabs__nav-wrap.is-left,.el-tabs--right .el-tabs__nav-wrap.is-right{margin-bottom:0}.el-tabs--left .el-tabs__nav-wrap.is-left>.el-tabs__nav-prev,.el-tabs--left .el-tabs__nav-wrap.is-left>.el-tabs__nav-next,.el-tabs--left .el-tabs__nav-wrap.is-right>.el-tabs__nav-prev,.el-tabs--left .el-tabs__nav-wrap.is-right>.el-tabs__nav-next,.el-tabs--right .el-tabs__nav-wrap.is-left>.el-tabs__nav-prev,.el-tabs--right .el-tabs__nav-wrap.is-left>.el-tabs__nav-next,.el-tabs--right .el-tabs__nav-wrap.is-right>.el-tabs__nav-prev,.el-tabs--right .el-tabs__nav-wrap.is-right>.el-tabs__nav-next{text-align:center;cursor:pointer;width:100%;height:30px;line-height:30px}.el-tabs--left .el-tabs__nav-wrap.is-left>.el-tabs__nav-prev i,.el-tabs--left .el-tabs__nav-wrap.is-left>.el-tabs__nav-next i,.el-tabs--left .el-tabs__nav-wrap.is-right>.el-tabs__nav-prev i,.el-tabs--left .el-tabs__nav-wrap.is-right>.el-tabs__nav-next i,.el-tabs--right .el-tabs__nav-wrap.is-left>.el-tabs__nav-prev i,.el-tabs--right .el-tabs__nav-wrap.is-left>.el-tabs__nav-next i,.el-tabs--right .el-tabs__nav-wrap.is-right>.el-tabs__nav-prev i,.el-tabs--right .el-tabs__nav-wrap.is-right>.el-tabs__nav-next i{transform:rotate(90deg)}.el-tabs--left .el-tabs__nav-wrap.is-left>.el-tabs__nav-prev.is-disabled,.el-tabs--left .el-tabs__nav-wrap.is-left>.el-tabs__nav-next.is-disabled,.el-tabs--left .el-tabs__nav-wrap.is-right>.el-tabs__nav-prev.is-disabled,.el-tabs--left .el-tabs__nav-wrap.is-right>.el-tabs__nav-next.is-disabled,.el-tabs--right .el-tabs__nav-wrap.is-left>.el-tabs__nav-prev.is-disabled,.el-tabs--right .el-tabs__nav-wrap.is-left>.el-tabs__nav-next.is-disabled,.el-tabs--right .el-tabs__nav-wrap.is-right>.el-tabs__nav-prev.is-disabled,.el-tabs--right .el-tabs__nav-wrap.is-right>.el-tabs__nav-next.is-disabled{cursor:not-allowed}.el-tabs--left .el-tabs__nav-wrap.is-left>.el-tabs__nav-prev,.el-tabs--left .el-tabs__nav-wrap.is-right>.el-tabs__nav-prev,.el-tabs--right .el-tabs__nav-wrap.is-left>.el-tabs__nav-prev,.el-tabs--right .el-tabs__nav-wrap.is-right>.el-tabs__nav-prev{top:0;left:auto}.el-tabs--left .el-tabs__nav-wrap.is-left>.el-tabs__nav-next,.el-tabs--left .el-tabs__nav-wrap.is-right>.el-tabs__nav-next,.el-tabs--right .el-tabs__nav-wrap.is-left>.el-tabs__nav-next,.el-tabs--right .el-tabs__nav-wrap.is-right>.el-tabs__nav-next{bottom:0;right:auto}.el-tabs--left .el-tabs__nav-wrap.is-left.is-scrollable,.el-tabs--left .el-tabs__nav-wrap.is-right.is-scrollable,.el-tabs--right .el-tabs__nav-wrap.is-left.is-scrollable,.el-tabs--right .el-tabs__nav-wrap.is-right.is-scrollable{padding:30px 0}.el-tabs--left .el-tabs__nav-wrap.is-left:after,.el-tabs--left .el-tabs__nav-wrap.is-right:after,.el-tabs--right .el-tabs__nav-wrap.is-left:after,.el-tabs--right .el-tabs__nav-wrap.is-right:after{width:2px;height:100%;top:0;bottom:auto}.el-tabs--left .el-tabs__nav.is-left,.el-tabs--left .el-tabs__nav.is-right,.el-tabs--right .el-tabs__nav.is-left,.el-tabs--right .el-tabs__nav.is-right{flex-direction:column}.el-tabs--left .el-tabs__item.is-left,.el-tabs--right .el-tabs__item.is-left{justify-content:flex-end}.el-tabs--left .el-tabs__item.is-right,.el-tabs--right .el-tabs__item.is-right{justify-content:flex-start}.el-tabs--left{flex-direction:row}.el-tabs--left .el-tabs__header.is-left{margin-bottom:0;margin-right:10px}.el-tabs--left .el-tabs__nav-wrap.is-left{margin-right:-1px}.el-tabs--left .el-tabs__nav-wrap.is-left:after,.el-tabs--left .el-tabs__active-bar.is-left{left:auto;right:0}.el-tabs--left .el-tabs__item.is-left{text-align:right}.el-tabs--left.el-tabs--card .el-tabs__active-bar.is-left{display:none}.el-tabs--left.el-tabs--card .el-tabs__item.is-left{border-left:none;border-right:1px solid var(--el-border-color-light);border-bottom:none;border-top:1px solid var(--el-border-color-light);text-align:left}.el-tabs--left.el-tabs--card .el-tabs__item.is-left:first-child{border-right:1px solid var(--el-border-color-light);border-top:none}.el-tabs--left.el-tabs--card .el-tabs__item.is-left.is-active{border:1px solid var(--el-border-color-light);border-bottom:none;border-left:none;border-right-color:#fff}.el-tabs--left.el-tabs--card .el-tabs__item.is-left.is-active:first-child{border-top:none}.el-tabs--left.el-tabs--card .el-tabs__item.is-left.is-active:last-child{border-bottom:none}.el-tabs--left.el-tabs--card .el-tabs__nav{border-bottom:1px solid var(--el-border-color-light);border-right:none;border-radius:4px 0 0 4px}.el-tabs--left.el-tabs--card .el-tabs__new-tab{float:none}.el-tabs--left.el-tabs--border-card .el-tabs__header.is-left{border-right:1px solid var(--el-border-color)}.el-tabs--left.el-tabs--border-card .el-tabs__item.is-left{border:1px solid #0000;margin:-1px 0 -1px -1px}.el-tabs--left.el-tabs--border-card .el-tabs__item.is-left.is-active{border-color:#d1dbe5 #0000}.el-tabs--left>.el-tabs__content+.el-tabs__header{order:-1}.el-tabs--right .el-tabs__header.is-right{margin-bottom:0;margin-left:10px}.el-tabs--right .el-tabs__nav-wrap.is-right{margin-left:-1px}.el-tabs--right .el-tabs__nav-wrap.is-right:after{left:0;right:auto}.el-tabs--right .el-tabs__active-bar.is-right{left:0}.el-tabs--right.el-tabs--card .el-tabs__active-bar.is-right{display:none}.el-tabs--right.el-tabs--card .el-tabs__item.is-right{border-bottom:none;border-top:1px solid var(--el-border-color-light)}.el-tabs--right.el-tabs--card .el-tabs__item.is-right:first-child{border-left:1px solid var(--el-border-color-light);border-top:none}.el-tabs--right.el-tabs--card .el-tabs__item.is-right.is-active{border:1px solid var(--el-border-color-light);border-bottom:none;border-left-color:#fff;border-right:none}.el-tabs--right.el-tabs--card .el-tabs__item.is-right.is-active:first-child{border-top:none}.el-tabs--right.el-tabs--card .el-tabs__item.is-right.is-active:last-child{border-bottom:none}.el-tabs--right.el-tabs--card .el-tabs__nav{border-bottom:1px solid var(--el-border-color-light);border-left:none;border-radius:0 4px 4px 0}.el-tabs--right.el-tabs--border-card .el-tabs__header.is-right{border-left:1px solid var(--el-border-color)}.el-tabs--right.el-tabs--border-card .el-tabs__item.is-right{border:1px solid #0000;margin:-1px -1px -1px 0}.el-tabs--right.el-tabs--border-card .el-tabs__item.is-right.is-active{border-color:#d1dbe5 #0000}.el-tabs--top{flex-direction:column}.el-tabs--top>.el-tabs__content+.el-tabs__header{order:-1}.slideInRight-transition,.slideInLeft-transition{display:inline-block}.slideInRight-enter{animation:slideInRight-enter var(--el-transition-duration)}.slideInRight-leave{animation:slideInRight-leave var(--el-transition-duration);position:absolute;left:0;right:0}.slideInLeft-enter{animation:slideInLeft-enter var(--el-transition-duration)}.slideInLeft-leave{animation:slideInLeft-leave var(--el-transition-duration);position:absolute;left:0;right:0}@keyframes slideInRight-enter{0%{opacity:0;transform-origin:0 0;transform:translate(100%)}to{opacity:1;transform-origin:0 0;transform:translate(0)}}@keyframes slideInRight-leave{0%{transform-origin:0 0;opacity:1;transform:translate(0)}to{transform-origin:0 0;opacity:0;transform:translate(100%)}}@keyframes slideInLeft-enter{0%{opacity:0;transform-origin:0 0;transform:translate(-100%)}to{opacity:1;transform-origin:0 0;transform:translate(0)}}@keyframes slideInLeft-leave{0%{transform-origin:0 0;opacity:1;transform:translate(0)}to{transform-origin:0 0;opacity:0;transform:translate(-100%)}}}@layer element-plus;@layer element-plus{.el-breadcrumb{font-size:14px;line-height:1}.el-breadcrumb:before,.el-breadcrumb:after{content:"";display:table}.el-breadcrumb:after{clear:both}}@layer element-plus{.el-breadcrumb__separator{color:var(--el-text-color-placeholder);margin:0 9px;font-weight:700}.el-breadcrumb__separator.el-icon{margin:0 6px;font-weight:400}.el-breadcrumb__separator.el-icon svg{vertical-align:middle}.el-breadcrumb__item{float:left;align-items:center;display:inline-flex}.el-breadcrumb__inner{color:var(--el-text-color-regular)}.el-breadcrumb__inner.is-link,.el-breadcrumb__inner a{transition:var(--el-transition-color);color:var(--el-text-color-primary);font-weight:700;text-decoration:none}.el-breadcrumb__inner.is-link:hover,.el-breadcrumb__inner a:hover{color:var(--el-color-primary);cursor:pointer}.el-breadcrumb__item:last-child .el-breadcrumb__inner,.el-breadcrumb__item:last-child .el-breadcrumb__inner:hover,.el-breadcrumb__item:last-child .el-breadcrumb__inner a,.el-breadcrumb__item:last-child .el-breadcrumb__inner a:hover{color:var(--el-text-color-regular);cursor:text;font-weight:400}.el-breadcrumb__item:last-child .el-breadcrumb__separator{display:none}}@layer element-plus{.el-radio-button{--el-radio-button-checked-bg-color:var(--el-color-primary);--el-radio-button-checked-text-color:var(--el-color-white);--el-radio-button-checked-border-color:var(--el-color-primary);--el-radio-button-disabled-checked-fill:var(--el-border-color-extra-light);outline:none;display:inline-block;position:relative}.el-radio-button__inner{white-space:nowrap;vertical-align:middle;background:var(--el-button-bg-color,var(--el-fill-color-blank));outline:var(--el-border);line-height:1;font-weight:var(--el-button-font-weight,var(--el-font-weight-primary));color:var(--el-button-text-color,var(--el-text-color-regular));-webkit-appearance:none;text-align:center;box-sizing:border-box;cursor:pointer;transition:var(--el-transition-all);-webkit-user-select:none;user-select:none;font-size:var(--el-font-size-base);border-radius:0;margin:0;padding:8px 15px;display:inline-block;position:relative}.el-radio-button__inner.is-round{padding:8px 15px}.el-radio-button__inner:hover{color:var(--el-color-primary)}.el-radio-button__inner [class*=el-icon-]{line-height:.9}.el-radio-button__inner [class*=el-icon-]+span{margin-left:5px}.el-radio-button:first-child .el-radio-button__inner{border-radius:var(--el-border-radius-base) 0 0 var(--el-border-radius-base);box-shadow:none!important}.el-radio-button.is-active .el-radio-button__original-radio:not(:disabled)+.el-radio-button__inner{color:var(--el-radio-button-checked-text-color,var(--el-color-white));background-color:var(--el-radio-button-checked-bg-color,var(--el-color-primary));border-color:var(--el-radio-button-checked-border-color,var(--el-color-primary));box-shadow:-1px 0 0 0 var(--el-radio-button-checked-border-color,var(--el-color-primary))}.el-radio-button__original-radio{opacity:0;z-index:-1;outline:none;position:absolute}.el-radio-button__original-radio:focus-visible+.el-radio-button__inner{border-left:var(--el-border);border-left-color:var(--el-radio-button-checked-border-color,var(--el-color-primary));outline:2px solid var(--el-radio-button-checked-border-color);outline-offset:1px;z-index:2;border-radius:var(--el-border-radius-base);box-shadow:none}.el-radio-button__original-radio:disabled+.el-radio-button__inner{color:var(--el-disabled-text-color);cursor:not-allowed;background-image:none;background-color:var(--el-button-disabled-bg-color,var(--el-fill-color-blank));border-color:var(--el-button-disabled-border-color,var(--el-border-color-light));box-shadow:none}.el-radio-button__original-radio:disabled:checked+.el-radio-button__inner{background-color:var(--el-radio-button-disabled-checked-fill)}.el-radio-button:last-child .el-radio-button__inner{border-radius:0 var(--el-border-radius-base) var(--el-border-radius-base) 0}.el-radio-button:first-child:last-child .el-radio-button__inner{border-radius:var(--el-border-radius-base)}.el-radio-button--large .el-radio-button__inner{font-size:var(--el-font-size-base);border-radius:0;padding:12px 19px}.el-radio-button--large .el-radio-button__inner.is-round{padding:12px 19px}.el-radio-button--small .el-radio-button__inner{border-radius:0;padding:5px 11px;font-size:12px}.el-radio-button--small .el-radio-button__inner.is-round{padding:5px 11px}}@layer element-plus{.el-empty{--el-empty-padding:40px 0;--el-empty-image-width:160px;--el-empty-description-margin-top:20px;--el-empty-bottom-margin-top:20px;--el-empty-fill-color-0:var(--el-color-white);--el-empty-fill-color-1:#fcfcfd;--el-empty-fill-color-2:#f8f9fb;--el-empty-fill-color-3:#f7f8fc;--el-empty-fill-color-4:#eeeff3;--el-empty-fill-color-5:#edeef2;--el-empty-fill-color-6:#e9ebef;--el-empty-fill-color-7:#e5e7e9;--el-empty-fill-color-8:#e0e3e9;--el-empty-fill-color-9:#d5d7de;text-align:center;box-sizing:border-box;padding:var(--el-empty-padding);flex-direction:column;justify-content:center;align-items:center;display:flex}.el-empty__image{width:var(--el-empty-image-width)}.el-empty__image img{-webkit-user-select:none;user-select:none;vertical-align:top;object-fit:contain;width:100%;height:100%}.el-empty__image svg{color:var(--el-svg-monochrome-grey);fill:currentColor;vertical-align:top;width:100%;height:100%}.el-empty__description{margin-top:var(--el-empty-description-margin-top)}.el-empty__description p{font-size:var(--el-font-size-base);color:var(--el-text-color-secondary);margin:0}.el-empty__bottom{margin-top:var(--el-empty-bottom-margin-top)}}@layer element-plus{.fade-in-linear-enter-active,.fade-in-linear-leave-active{transition:var(--el-transition-fade-linear)}.fade-in-linear-enter-from,.fade-in-linear-leave-to{opacity:0}.el-fade-in-linear-enter-active,.el-fade-in-linear-leave-active{transition:var(--el-transition-fade-linear)}.el-fade-in-linear-enter-from,.el-fade-in-linear-leave-to{opacity:0}.el-fade-in-enter-active,.el-fade-in-leave-active{transition:all var(--el-transition-duration) cubic-bezier(.55,0,.1,1)}.el-fade-in-enter-from,.el-fade-in-leave-active{opacity:0}.el-zoom-in-center-enter-active,.el-zoom-in-center-leave-active{transition:all var(--el-transition-duration) cubic-bezier(.55,0,.1,1)}.el-zoom-in-center-enter-from,.el-zoom-in-center-leave-active{opacity:0;transform:scaleX(0)}.el-zoom-in-top-enter-active,.el-zoom-in-top-leave-active{opacity:1;transition:var(--el-transition-md-fade);transform-origin:top;transform:scaleY(1)}.el-zoom-in-top-enter-active[data-popper-placement^=top],.el-zoom-in-top-leave-active[data-popper-placement^=top]{transform-origin:bottom}.el-zoom-in-top-enter-from,.el-zoom-in-top-leave-active{opacity:0;transform:scaleY(0)}.el-zoom-in-bottom-enter-active,.el-zoom-in-bottom-leave-active{opacity:1;transition:var(--el-transition-md-fade);transform-origin:bottom;transform:scaleY(1)}.el-zoom-in-bottom-enter-from,.el-zoom-in-bottom-leave-active{opacity:0;transform:scaleY(0)}.el-zoom-in-left-enter-active,.el-zoom-in-left-leave-active{opacity:1;transition:var(--el-transition-md-fade);transform-origin:0 0;transform:scale(1)}.el-zoom-in-left-enter-from,.el-zoom-in-left-leave-active{opacity:0;transform:scale(.45)}.collapse-transition{transition:var(--el-transition-duration) height ease-in-out,var(--el-transition-duration) padding-top ease-in-out,var(--el-transition-duration) padding-bottom ease-in-out}.el-collapse-transition-leave-active,.el-collapse-transition-enter-active{transition:var(--el-transition-duration) max-height ease-in-out,var(--el-transition-duration) padding-top ease-in-out,var(--el-transition-duration) padding-bottom ease-in-out}.horizontal-collapse-transition{transition:var(--el-transition-duration) width ease-in-out,var(--el-transition-duration) padding-left ease-in-out,var(--el-transition-duration) padding-right ease-in-out}.el-list-enter-active,.el-list-leave-active{transition:all 1s}.el-list-enter-from,.el-list-leave-to{opacity:0;transform:translateY(-30px)}.el-list-leave-active{position:absolute!important}.el-opacity-transition{transition:opacity var(--el-transition-duration) cubic-bezier(.55,0,.1,1)}.el-picker__popper{--el-datepicker-border-color:var(--el-disabled-border-color)}.el-picker__popper.el-popper{background:var(--el-bg-color-overlay);border:1px solid var(--el-datepicker-border-color);box-shadow:var(--el-box-shadow-light)}.el-picker__popper.el-popper .el-popper__arrow:before{border:1px solid var(--el-datepicker-border-color)}.el-picker__popper.el-popper[data-popper-placement^=top] .el-popper__arrow:before{border-top-color:#0000;border-left-color:#0000}.el-picker__popper.el-popper[data-popper-placement^=bottom] .el-popper__arrow:before{border-bottom-color:#0000;border-right-color:#0000}.el-picker__popper.el-popper[data-popper-placement^=left] .el-popper__arrow:before{border-bottom-color:#0000;border-left-color:#0000}.el-picker__popper.el-popper[data-popper-placement^=right] .el-popper__arrow:before{border-top-color:#0000;border-right-color:#0000}.el-date-editor{--el-date-editor-width:220px;--el-date-editor-monthrange-width:300px;--el-date-editor-daterange-width:350px;--el-date-editor-datetimerange-width:400px;--el-input-text-color:var(--el-text-color-regular);--el-input-border:var(--el-border);--el-input-hover-border:var(--el-border-color-hover);--el-input-focus-border:var(--el-color-primary);--el-input-transparent-border:0 0 0 1px transparent inset;--el-input-border-color:var(--el-border-color);--el-input-border-radius:var(--el-border-radius-base);--el-input-bg-color:var(--el-fill-color-blank);--el-input-icon-color:var(--el-text-color-placeholder);--el-input-placeholder-color:var(--el-text-color-placeholder);--el-input-hover-border-color:var(--el-border-color-hover);--el-input-clear-hover-color:var(--el-text-color-secondary);--el-input-focus-border-color:var(--el-color-primary);--el-input-width:100%;text-align:left;vertical-align:middle;position:relative}.el-date-editor.el-input__wrapper{box-shadow:0 0 0 1px var(--el-input-border-color,var(--el-border-color)) inset}.el-date-editor.el-input__wrapper:hover{box-shadow:0 0 0 1px var(--el-input-hover-border-color) inset}.el-date-editor.is-focus .el-input__wrapper{box-shadow:0 0 0 1px var(--el-input-focus-border-color) inset}.el-date-editor.el-input,.el-date-editor.el-input__wrapper{width:var(--el-date-editor-width);height:var(--el-input-height,var(--el-component-size))}.el-date-editor--monthrange{--el-date-editor-width:var(--el-date-editor-monthrange-width)}.el-date-editor--daterange,.el-date-editor--timerange{--el-date-editor-width:var(--el-date-editor-daterange-width)}.el-date-editor--datetimerange{--el-date-editor-width:var(--el-date-editor-datetimerange-width)}.el-date-editor--dates .el-input__wrapper{text-overflow:ellipsis;white-space:nowrap}.el-date-editor .close-icon,.el-date-editor .clear-icon{cursor:pointer}.el-date-editor .clear-icon:hover{color:var(--el-input-clear-hover-color)}.el-date-editor .el-range__icon{height:inherit;color:var(--el-text-color-placeholder);float:left;font-size:14px}.el-date-editor .el-range__icon svg{vertical-align:middle}.el-date-editor .el-range-input{-webkit-appearance:none;-moz-appearance:none;appearance:none;text-align:center;width:39%;height:30px;line-height:30px;font-size:var(--el-font-size-base);color:var(--el-text-color-regular);background-color:#0000;border:none;outline:none;margin:0;padding:0;display:inline-block}.el-date-editor .el-range-input::placeholder{color:var(--el-text-color-placeholder)}.el-date-editor .el-range-separator{overflow-wrap:break-word;height:100%;color:var(--el-text-color-primary);flex:1;justify-content:center;align-items:center;margin:0;padding:0 5px;font-size:14px;display:inline-flex}.el-date-editor .el-range__close-icon{color:var(--el-text-color-placeholder);height:inherit;width:unset;cursor:pointer;font-size:14px}.el-date-editor .el-range__close-icon:hover{color:var(--el-input-clear-hover-color)}.el-date-editor .el-range__close-icon svg{vertical-align:middle}.el-date-editor .el-range__close-icon--hidden{opacity:0;visibility:hidden}.el-range-editor.el-input__wrapper{vertical-align:middle;align-items:center;padding:0 10px;display:inline-flex}.el-range-editor.is-active,.el-range-editor.is-active:hover{box-shadow:0 0 0 1px var(--el-input-focus-border-color) inset}.el-range-editor--large{line-height:var(--el-component-size-large)}.el-range-editor--large.el-input__wrapper{height:var(--el-component-size-large)}.el-range-editor--large .el-range-separator{font-size:14px;line-height:40px}.el-range-editor--large .el-range-input{height:38px;font-size:14px;line-height:38px}.el-range-editor--small{line-height:var(--el-component-size-small)}.el-range-editor--small.el-input__wrapper{height:var(--el-component-size-small)}.el-range-editor--small .el-range-separator{font-size:12px;line-height:24px}.el-range-editor--small .el-range-input{height:22px;font-size:12px;line-height:22px}.el-range-editor.is-disabled{background-color:var(--el-disabled-bg-color);border-color:var(--el-disabled-border-color);color:var(--el-disabled-text-color);cursor:not-allowed}.el-range-editor.is-disabled:hover,.el-range-editor.is-disabled:focus{border-color:var(--el-disabled-border-color)}.el-range-editor.is-disabled input{background-color:var(--el-disabled-bg-color);color:var(--el-disabled-text-color);cursor:not-allowed}.el-range-editor.is-disabled input::placeholder{color:var(--el-text-color-placeholder)}.el-range-editor.is-disabled .el-range-separator{color:var(--el-disabled-text-color)}.el-picker-panel{color:var(--el-text-color-regular);background:var(--el-datepicker-bg-color);border-radius:var(--el-popper-border-radius,var(--el-border-radius-base));line-height:30px}.el-picker-panel .el-time-panel{border:solid 1px var(--el-datepicker-border-color);background-color:var(--el-datepicker-bg-color);box-shadow:var(--el-box-shadow-light);margin:5px 0}.el-picker-panel__body:after,.el-picker-panel__body-wrapper:after{content:"";clear:both;display:table}.el-picker-panel__content{margin:15px;position:relative}.el-picker-panel__footer{border-top:1px solid var(--el-datepicker-inner-border-color);text-align:right;background-color:var(--el-datepicker-bg-color);padding:4px 12px;font-size:0;position:relative}.el-picker-panel__shortcut{width:100%;color:var(--el-datepicker-text-color);text-align:left;cursor:pointer;background-color:#0000;border:0;outline:none;padding-left:12px;font-size:14px;line-height:28px;display:block}.el-picker-panel__shortcut:hover{color:var(--el-datepicker-hover-text-color)}.el-picker-panel__shortcut.active{color:var(--el-datepicker-active-color);background-color:#e6f1fe}.el-picker-panel__btn{border:1px solid var(--el-fill-color-darker);color:var(--el-text-color-primary);cursor:pointer;background-color:#0000;border-radius:2px;outline:none;padding:0 20px;font-size:12px;line-height:24px}.el-picker-panel__btn[disabled]{color:var(--el-text-color-disabled);cursor:not-allowed}.el-picker-panel__icon-btn{color:var(--el-datepicker-icon-color);cursor:pointer;background:0 0;border:0;outline:none;margin-top:8px;padding:1px 6px;font-size:12px;line-height:1}.el-picker-panel__icon-btn:hover{color:var(--el-datepicker-hover-text-color)}.el-picker-panel__icon-btn:focus-visible{color:var(--el-datepicker-hover-text-color)}.el-picker-panel__icon-btn.is-disabled{color:var(--el-text-color-disabled)}.el-picker-panel__icon-btn.is-disabled:hover{cursor:not-allowed}.el-picker-panel__icon-btn.is-disabled .el-icon{cursor:inherit}.el-picker-panel__icon-btn .el-icon{cursor:pointer;font-size:inherit}.el-picker-panel__link-btn{vertical-align:middle}.el-picker-panel.is-disabled .el-picker-panel__prev-btn{color:var(--el-text-color-disabled)}.el-picker-panel.is-disabled .el-picker-panel__prev-btn:hover{cursor:not-allowed}.el-picker-panel.is-disabled .el-picker-panel__prev-btn .el-icon{cursor:inherit}.el-picker-panel.is-disabled .el-picker-panel__next-btn{color:var(--el-text-color-disabled)}.el-picker-panel.is-disabled .el-picker-panel__next-btn:hover{cursor:not-allowed}.el-picker-panel.is-disabled .el-picker-panel__next-btn .el-icon{cursor:inherit}.el-picker-panel.is-disabled .el-picker-panel__icon-btn{color:var(--el-text-color-disabled)}.el-picker-panel.is-disabled .el-picker-panel__icon-btn:hover{cursor:not-allowed}.el-picker-panel.is-disabled .el-picker-panel__icon-btn .el-icon{cursor:inherit}.el-picker-panel.is-disabled .el-picker-panel__shortcut{color:var(--el-text-color-disabled)}.el-picker-panel.is-disabled .el-picker-panel__shortcut:hover{cursor:not-allowed}.el-picker-panel.is-disabled .el-picker-panel__shortcut .el-icon{cursor:inherit}.el-picker-panel [slot=sidebar],.el-picker-panel__sidebar{border-right:1px solid var(--el-datepicker-inner-border-color);box-sizing:border-box;width:110px;padding-top:6px;position:absolute;top:0;bottom:0;overflow:auto}.el-picker-panel [slot=sidebar]+.el-picker-panel__body,.el-picker-panel__sidebar+.el-picker-panel__body{margin-left:110px}.el-time-spinner.has-seconds .el-time-spinner__wrapper{width:33.3%}.el-time-spinner__wrapper{vertical-align:top;width:50%;max-height:192px;display:inline-block;position:relative;overflow:auto}.el-time-spinner__wrapper.el-scrollbar__wrap:not(.el-scrollbar__wrap--hidden-default){padding-bottom:15px}.el-time-spinner__wrapper.is-arrow{box-sizing:border-box;text-align:center;overflow:hidden}.el-time-spinner__wrapper.is-arrow .el-time-spinner__list{transform:translateY(-32px)}.el-time-spinner__wrapper.is-arrow .el-time-spinner__item:hover:not(.is-disabled):not(.is-active){background:var(--el-fill-color-light);cursor:default}.el-time-spinner__arrow{color:var(--el-text-color-secondary);width:100%;z-index:var(--el-index-normal);text-align:center;cursor:pointer;height:30px;font-size:12px;line-height:30px;position:absolute;left:0}.el-time-spinner__arrow:hover{color:var(--el-color-primary)}.el-time-spinner__arrow.arrow-up{top:10px}.el-time-spinner__arrow.arrow-down{bottom:10px}.el-time-spinner__input.el-input{width:70%}.el-time-spinner__input.el-input .el-input__inner{text-align:center;padding:0}.el-time-spinner__list{text-align:center;margin:0;padding:0;list-style:none}.el-time-spinner__list:after,.el-time-spinner__list:before{content:"";width:100%;height:80px;display:block}.el-time-spinner__item{height:32px;color:var(--el-text-color-regular);font-size:12px;line-height:32px}.el-time-spinner__item:hover:not(.is-disabled):not(.is-active){background:var(--el-fill-color-light);cursor:pointer}.el-time-spinner__item.is-active:not(.is-disabled){color:var(--el-text-color-primary);font-weight:700}.el-time-spinner__item.is-disabled{color:var(--el-text-color-placeholder);cursor:not-allowed}.el-time-panel{width:180px;z-index:var(--el-index-top);-webkit-user-select:none;user-select:none;box-sizing:content-box;border-radius:2px;position:relative;left:0}.el-time-panel__content{font-size:0;position:relative;overflow:hidden}.el-time-panel__content:after,.el-time-panel__content:before{content:"";z-index:-1;box-sizing:border-box;text-align:left;height:32px;margin-top:-16px;padding-top:6px;position:absolute;top:50%;left:0;right:0}.el-time-panel__content:after{margin-left:12%;margin-right:12%;left:50%}.el-time-panel__content:before{border-top:1px solid var(--el-border-color-light);border-bottom:1px solid var(--el-border-color-light);margin-left:12%;margin-right:12%;padding-left:50%}.el-time-panel__content.has-seconds:after{left:66.6667%}.el-time-panel__content.has-seconds:before{padding-left:33.3333%}.el-time-panel__footer{border-top:1px solid var(--el-timepicker-inner-border-color,var(--el-border-color-light));text-align:right;box-sizing:border-box;height:36px;padding:4px;line-height:25px}.el-time-panel__btn{cursor:pointer;color:var(--el-text-color-primary);background-color:#0000;border:none;outline:none;margin:0 5px;padding:0 5px;font-size:12px;line-height:28px}.el-time-panel__btn.confirm{color:var(--el-timepicker-active-color,var(--el-color-primary));font-weight:800}.el-time-range-picker{width:354px;overflow:visible}.el-time-range-picker__content{text-align:center;z-index:1;padding:10px;position:relative}.el-time-range-picker__cell{box-sizing:border-box;width:50%;margin:0;padding:4px 7px 7px;display:inline-block}.el-time-range-picker__header{text-align:center;margin-bottom:5px;font-size:14px}.el-time-range-picker__body{border:1px solid var(--el-datepicker-border-color);border-radius:2px}}@layer element-plus{.fade-in-linear-enter-active,.fade-in-linear-leave-active{transition:var(--el-transition-fade-linear)}.fade-in-linear-enter-from,.fade-in-linear-leave-to{opacity:0}.el-fade-in-linear-enter-active,.el-fade-in-linear-leave-active{transition:var(--el-transition-fade-linear)}.el-fade-in-linear-enter-from,.el-fade-in-linear-leave-to{opacity:0}.el-fade-in-enter-active,.el-fade-in-leave-active{transition:all var(--el-transition-duration) cubic-bezier(.55,0,.1,1)}.el-fade-in-enter-from,.el-fade-in-leave-active{opacity:0}.el-zoom-in-center-enter-active,.el-zoom-in-center-leave-active{transition:all var(--el-transition-duration) cubic-bezier(.55,0,.1,1)}.el-zoom-in-center-enter-from,.el-zoom-in-center-leave-active{opacity:0;transform:scaleX(0)}.el-zoom-in-top-enter-active,.el-zoom-in-top-leave-active{opacity:1;transition:var(--el-transition-md-fade);transform-origin:top;transform:scaleY(1)}.el-zoom-in-top-enter-active[data-popper-placement^=top],.el-zoom-in-top-leave-active[data-popper-placement^=top]{transform-origin:bottom}.el-zoom-in-top-enter-from,.el-zoom-in-top-leave-active{opacity:0;transform:scaleY(0)}.el-zoom-in-bottom-enter-active,.el-zoom-in-bottom-leave-active{opacity:1;transition:var(--el-transition-md-fade);transform-origin:bottom;transform:scaleY(1)}.el-zoom-in-bottom-enter-from,.el-zoom-in-bottom-leave-active{opacity:0;transform:scaleY(0)}.el-zoom-in-left-enter-active,.el-zoom-in-left-leave-active{opacity:1;transition:var(--el-transition-md-fade);transform-origin:0 0;transform:scale(1)}.el-zoom-in-left-enter-from,.el-zoom-in-left-leave-active{opacity:0;transform:scale(.45)}.collapse-transition{transition:var(--el-transition-duration) height ease-in-out,var(--el-transition-duration) padding-top ease-in-out,var(--el-transition-duration) padding-bottom ease-in-out}.el-collapse-transition-leave-active,.el-collapse-transition-enter-active{transition:var(--el-transition-duration) max-height ease-in-out,var(--el-transition-duration) padding-top ease-in-out,var(--el-transition-duration) padding-bottom ease-in-out}.horizontal-collapse-transition{transition:var(--el-transition-duration) width ease-in-out,var(--el-transition-duration) padding-left ease-in-out,var(--el-transition-duration) padding-right ease-in-out}.el-list-enter-active,.el-list-leave-active{transition:all 1s}.el-list-enter-from,.el-list-leave-to{opacity:0;transform:translateY(-30px)}.el-list-leave-active{position:absolute!important}.el-opacity-transition{transition:opacity var(--el-transition-duration) cubic-bezier(.55,0,.1,1)}.el-picker__popper{--el-datepicker-border-color:var(--el-disabled-border-color)}.el-picker__popper.el-popper{background:var(--el-bg-color-overlay);border:1px solid var(--el-datepicker-border-color);box-shadow:var(--el-box-shadow-light)}.el-picker__popper.el-popper .el-popper__arrow:before{border:1px solid var(--el-datepicker-border-color)}.el-picker__popper.el-popper[data-popper-placement^=top] .el-popper__arrow:before{border-top-color:#0000;border-left-color:#0000}.el-picker__popper.el-popper[data-popper-placement^=bottom] .el-popper__arrow:before{border-bottom-color:#0000;border-right-color:#0000}.el-picker__popper.el-popper[data-popper-placement^=left] .el-popper__arrow:before{border-bottom-color:#0000;border-left-color:#0000}.el-picker__popper.el-popper[data-popper-placement^=right] .el-popper__arrow:before{border-top-color:#0000;border-right-color:#0000}.el-date-editor{--el-date-editor-width:220px;--el-date-editor-monthrange-width:300px;--el-date-editor-daterange-width:350px;--el-date-editor-datetimerange-width:400px;--el-input-text-color:var(--el-text-color-regular);--el-input-border:var(--el-border);--el-input-hover-border:var(--el-border-color-hover);--el-input-focus-border:var(--el-color-primary);--el-input-transparent-border:0 0 0 1px transparent inset;--el-input-border-color:var(--el-border-color);--el-input-border-radius:var(--el-border-radius-base);--el-input-bg-color:var(--el-fill-color-blank);--el-input-icon-color:var(--el-text-color-placeholder);--el-input-placeholder-color:var(--el-text-color-placeholder);--el-input-hover-border-color:var(--el-border-color-hover);--el-input-clear-hover-color:var(--el-text-color-secondary);--el-input-focus-border-color:var(--el-color-primary);--el-input-width:100%;text-align:left;vertical-align:middle;position:relative}.el-date-editor.el-input__wrapper{box-shadow:0 0 0 1px var(--el-input-border-color,var(--el-border-color)) inset}.el-date-editor.el-input__wrapper:hover{box-shadow:0 0 0 1px var(--el-input-hover-border-color) inset}.el-date-editor.is-focus .el-input__wrapper{box-shadow:0 0 0 1px var(--el-input-focus-border-color) inset}.el-date-editor.el-input,.el-date-editor.el-input__wrapper{width:var(--el-date-editor-width);height:var(--el-input-height,var(--el-component-size))}.el-date-editor--monthrange{--el-date-editor-width:var(--el-date-editor-monthrange-width)}.el-date-editor--daterange,.el-date-editor--timerange{--el-date-editor-width:var(--el-date-editor-daterange-width)}.el-date-editor--datetimerange{--el-date-editor-width:var(--el-date-editor-datetimerange-width)}.el-date-editor--dates .el-input__wrapper{text-overflow:ellipsis;white-space:nowrap}.el-date-editor .close-icon,.el-date-editor .clear-icon{cursor:pointer}.el-date-editor .clear-icon:hover{color:var(--el-input-clear-hover-color)}.el-date-editor .el-range__icon{height:inherit;color:var(--el-text-color-placeholder);float:left;font-size:14px}.el-date-editor .el-range__icon svg{vertical-align:middle}.el-date-editor .el-range-input{-webkit-appearance:none;-moz-appearance:none;appearance:none;text-align:center;width:39%;height:30px;line-height:30px;font-size:var(--el-font-size-base);color:var(--el-text-color-regular);background-color:#0000;border:none;outline:none;margin:0;padding:0;display:inline-block}.el-date-editor .el-range-input::placeholder{color:var(--el-text-color-placeholder)}.el-date-editor .el-range-separator{overflow-wrap:break-word;height:100%;color:var(--el-text-color-primary);flex:1;justify-content:center;align-items:center;margin:0;padding:0 5px;font-size:14px;display:inline-flex}.el-date-editor .el-range__close-icon{color:var(--el-text-color-placeholder);height:inherit;width:unset;cursor:pointer;font-size:14px}.el-date-editor .el-range__close-icon:hover{color:var(--el-input-clear-hover-color)}.el-date-editor .el-range__close-icon svg{vertical-align:middle}.el-date-editor .el-range__close-icon--hidden{opacity:0;visibility:hidden}.el-range-editor.el-input__wrapper{vertical-align:middle;align-items:center;padding:0 10px;display:inline-flex}.el-range-editor.is-active,.el-range-editor.is-active:hover{box-shadow:0 0 0 1px var(--el-input-focus-border-color) inset}.el-range-editor--large{line-height:var(--el-component-size-large)}.el-range-editor--large.el-input__wrapper{height:var(--el-component-size-large)}.el-range-editor--large .el-range-separator{font-size:14px;line-height:40px}.el-range-editor--large .el-range-input{height:38px;font-size:14px;line-height:38px}.el-range-editor--small{line-height:var(--el-component-size-small)}.el-range-editor--small.el-input__wrapper{height:var(--el-component-size-small)}.el-range-editor--small .el-range-separator{font-size:12px;line-height:24px}.el-range-editor--small .el-range-input{height:22px;font-size:12px;line-height:22px}.el-range-editor.is-disabled{background-color:var(--el-disabled-bg-color);border-color:var(--el-disabled-border-color);color:var(--el-disabled-text-color);cursor:not-allowed}.el-range-editor.is-disabled:hover,.el-range-editor.is-disabled:focus{border-color:var(--el-disabled-border-color)}.el-range-editor.is-disabled input{background-color:var(--el-disabled-bg-color);color:var(--el-disabled-text-color);cursor:not-allowed}.el-range-editor.is-disabled input::placeholder{color:var(--el-text-color-placeholder)}.el-range-editor.is-disabled .el-range-separator{color:var(--el-disabled-text-color)}.el-picker-panel{color:var(--el-text-color-regular);background:var(--el-datepicker-bg-color);border-radius:var(--el-popper-border-radius,var(--el-border-radius-base));line-height:30px}.el-picker-panel .el-time-panel{border:solid 1px var(--el-datepicker-border-color);background-color:var(--el-datepicker-bg-color);box-shadow:var(--el-box-shadow-light);margin:5px 0}.el-picker-panel__body:after,.el-picker-panel__body-wrapper:after{content:"";clear:both;display:table}.el-picker-panel__content{margin:15px;position:relative}.el-picker-panel__footer{border-top:1px solid var(--el-datepicker-inner-border-color);text-align:right;background-color:var(--el-datepicker-bg-color);padding:4px 12px;font-size:0;position:relative}.el-picker-panel__shortcut{width:100%;color:var(--el-datepicker-text-color);text-align:left;cursor:pointer;background-color:#0000;border:0;outline:none;padding-left:12px;font-size:14px;line-height:28px;display:block}.el-picker-panel__shortcut:hover{color:var(--el-datepicker-hover-text-color)}.el-picker-panel__shortcut.active{color:var(--el-datepicker-active-color);background-color:#e6f1fe}.el-picker-panel__btn{border:1px solid var(--el-fill-color-darker);color:var(--el-text-color-primary);cursor:pointer;background-color:#0000;border-radius:2px;outline:none;padding:0 20px;font-size:12px;line-height:24px}.el-picker-panel__btn[disabled]{color:var(--el-text-color-disabled);cursor:not-allowed}.el-picker-panel__icon-btn{color:var(--el-datepicker-icon-color);cursor:pointer;background:0 0;border:0;outline:none;margin-top:8px;padding:1px 6px;font-size:12px;line-height:1}.el-picker-panel__icon-btn:hover{color:var(--el-datepicker-hover-text-color)}.el-picker-panel__icon-btn:focus-visible{color:var(--el-datepicker-hover-text-color)}.el-picker-panel__icon-btn.is-disabled{color:var(--el-text-color-disabled)}.el-picker-panel__icon-btn.is-disabled:hover{cursor:not-allowed}.el-picker-panel__icon-btn.is-disabled .el-icon{cursor:inherit}.el-picker-panel__icon-btn .el-icon{cursor:pointer;font-size:inherit}.el-picker-panel__link-btn{vertical-align:middle}.el-picker-panel.is-disabled .el-picker-panel__prev-btn{color:var(--el-text-color-disabled)}.el-picker-panel.is-disabled .el-picker-panel__prev-btn:hover{cursor:not-allowed}.el-picker-panel.is-disabled .el-picker-panel__prev-btn .el-icon{cursor:inherit}.el-picker-panel.is-disabled .el-picker-panel__next-btn{color:var(--el-text-color-disabled)}.el-picker-panel.is-disabled .el-picker-panel__next-btn:hover{cursor:not-allowed}.el-picker-panel.is-disabled .el-picker-panel__next-btn .el-icon{cursor:inherit}.el-picker-panel.is-disabled .el-picker-panel__icon-btn{color:var(--el-text-color-disabled)}.el-picker-panel.is-disabled .el-picker-panel__icon-btn:hover{cursor:not-allowed}.el-picker-panel.is-disabled .el-picker-panel__icon-btn .el-icon{cursor:inherit}.el-picker-panel.is-disabled .el-picker-panel__shortcut{color:var(--el-text-color-disabled)}.el-picker-panel.is-disabled .el-picker-panel__shortcut:hover{cursor:not-allowed}.el-picker-panel.is-disabled .el-picker-panel__shortcut .el-icon{cursor:inherit}.el-picker-panel [slot=sidebar],.el-picker-panel__sidebar{border-right:1px solid var(--el-datepicker-inner-border-color);box-sizing:border-box;width:110px;padding-top:6px;position:absolute;top:0;bottom:0;overflow:auto}.el-picker-panel [slot=sidebar]+.el-picker-panel__body,.el-picker-panel__sidebar+.el-picker-panel__body{margin-left:110px}.el-date-picker{--el-datepicker-text-color:var(--el-text-color-regular);--el-datepicker-off-text-color:var(--el-text-color-placeholder);--el-datepicker-header-text-color:var(--el-text-color-regular);--el-datepicker-icon-color:var(--el-text-color-primary);--el-datepicker-border-color:var(--el-disabled-border-color);--el-datepicker-inner-border-color:var(--el-border-color-light);--el-datepicker-inrange-bg-color:var(--el-border-color-extra-light);--el-datepicker-inrange-hover-bg-color:var(--el-border-color-extra-light);--el-datepicker-active-color:var(--el-color-primary);--el-datepicker-hover-text-color:var(--el-color-primary);--el-datepicker-bg-color:var(--el-bg-color-overlay);--el-fill-color-blank:var(--el-datepicker-bg-color);width:322px}.el-date-picker.has-sidebar.has-time{width:434px}.el-date-picker.has-sidebar{width:438px}.el-date-picker.has-time .el-picker-panel__body-wrapper{position:relative}.el-date-picker .el-picker-panel__content{width:292px}.el-date-picker table{table-layout:fixed;width:100%}.el-date-picker__editor-wrap{padding:0 5px;display:table-cell;position:relative}.el-date-picker__time-header{border-bottom:1px solid var(--el-datepicker-inner-border-color);box-sizing:border-box;width:100%;padding:8px 5px 5px;font-size:12px;display:table;position:relative}.el-date-picker__header{text-align:center;padding:12px 12px 0}.el-date-picker__header--bordered{border-bottom:solid 1px var(--el-border-color-lighter);margin-bottom:0;padding-bottom:12px}.el-date-picker__header--bordered+.el-picker-panel__content{margin-top:0}.el-date-picker__header-label{text-align:center;cursor:pointer;color:var(--el-text-color-regular);padding:0 5px;font-size:16px;font-weight:500;line-height:22px}.el-date-picker__header-label:hover{color:var(--el-datepicker-hover-text-color)}.el-date-picker__header-label:focus-visible{color:var(--el-datepicker-hover-text-color);outline:none}.el-date-picker__header-label.active{color:var(--el-datepicker-active-color)}.el-date-picker__prev-btn{float:left}.el-date-picker__next-btn{float:right}.el-date-picker__time-wrap{text-align:center;padding:10px}.el-date-picker__time-label{float:left;cursor:pointer;margin-left:10px;line-height:30px}.el-date-picker .el-time-panel{position:absolute}.el-date-picker.is-disabled .el-date-picker__header-label{color:var(--el-text-color-disabled)}.el-date-picker.is-disabled .el-date-picker__header-label:hover{cursor:not-allowed}.el-date-picker.is-disabled .el-date-picker__header-label .el-icon{cursor:inherit}.time-select{min-width:0;margin:5px 0}.time-select .el-picker-panel__content{max-height:200px;margin:0}.time-select-item{padding:8px 10px;font-size:14px;line-height:20px}.time-select-item.disabled{color:var(--el-datepicker-border-color);cursor:not-allowed}.time-select-item:hover{background-color:var(--el-fill-color-light);cursor:pointer;font-weight:700}.time-select .time-select-item.selected:not(.disabled){color:var(--el-color-primary);font-weight:700}}@layer element-plus{.el-steps{line-height:normal;display:flex}.el-steps--simple{background:var(--el-fill-color-light);border-radius:4px;padding:13px 8%}.el-steps--horizontal{white-space:nowrap}.el-steps--vertical{flex-flow:column;height:100%}}@layer element-plus{.el-step{flex-shrink:1;position:relative}.el-step:last-of-type .el-step__line{display:none}.el-step:last-of-type.is-flex{flex-grow:0;flex-shrink:0;flex-basis:auto!important}.el-step:last-of-type .el-step__main,.el-step:last-of-type .el-step__description{padding-right:0}.el-step__head{width:100%;position:relative}.el-step__head.is-process{color:var(--el-text-color-primary);border-color:var(--el-text-color-primary)}.el-step__head.is-wait{color:var(--el-text-color-placeholder);border-color:var(--el-text-color-placeholder)}.el-step__head.is-success{color:var(--el-color-success);border-color:var(--el-color-success)}.el-step__head.is-error{color:var(--el-color-danger);border-color:var(--el-color-danger)}.el-step__head.is-finish{color:var(--el-color-primary);border-color:var(--el-color-primary)}.el-step__icon{z-index:1;box-sizing:border-box;background:var(--el-bg-color);justify-content:center;align-items:center;width:24px;height:24px;font-size:14px;transition:all .15s ease-out;display:inline-flex;position:relative}.el-step__icon.is-text{border:2px solid;border-radius:50%}.el-step__icon.is-icon{width:40px}.el-step__icon-inner{-webkit-user-select:none;user-select:none;text-align:center;color:inherit;font-weight:700;line-height:1;display:inline-block}.el-step__icon-inner[class*=el-icon]:not(.is-status){font-size:25px;font-weight:400}.el-step__icon-inner.is-status{transform:translateY(1px)}.el-step__line{background-color:var(--el-text-color-placeholder);border-color:currentColor;position:absolute}.el-step__line-inner{box-sizing:border-box;border:1px solid;width:0;height:0;transition:all .15s ease-out;display:block}.el-step__main{white-space:normal;text-align:left}.el-step__title{font-size:16px;line-height:38px}.el-step__title.is-process{color:var(--el-text-color-primary);font-weight:700}.el-step__title.is-wait{color:var(--el-text-color-placeholder)}.el-step__title.is-success{color:var(--el-color-success)}.el-step__title.is-error{color:var(--el-color-danger)}.el-step__title.is-finish{color:var(--el-color-primary)}.el-step__description{margin-top:-5px;padding-right:10%;font-size:12px;font-weight:400;line-height:20px}.el-step__description.is-process{color:var(--el-text-color-primary)}.el-step__description.is-wait{color:var(--el-text-color-placeholder)}.el-step__description.is-success{color:var(--el-color-success)}.el-step__description.is-error{color:var(--el-color-danger)}.el-step__description.is-finish{color:var(--el-color-primary)}.el-step.is-horizontal{display:inline-block}.el-step.is-horizontal .el-step__line{height:2px;top:11px;left:0;right:0}.el-step.is-vertical{display:flex}.el-step.is-vertical .el-step__head{flex-grow:0;width:24px}.el-step.is-vertical .el-step__main{flex-grow:1;padding-left:10px}.el-step.is-vertical .el-step__title{padding-bottom:8px;line-height:24px}.el-step.is-vertical .el-step__line{width:2px;top:0;bottom:0;left:11px}.el-step.is-vertical .el-step__icon.is-icon{width:24px}.el-step.is-vertical .el-step__description{padding-right:0}.el-step.is-center .el-step__head,.el-step.is-center .el-step__main{text-align:center}.el-step.is-center .el-step__description{padding-left:20%;padding-right:20%}.el-step.is-center .el-step__line{left:50%;right:-50%}.el-step.is-simple{align-items:center;display:flex}.el-step.is-simple .el-step__head{width:auto;padding-right:10px;font-size:0}.el-step.is-simple .el-step__icon{background:0 0;width:16px;height:16px;font-size:12px}.el-step.is-simple .el-step__icon-inner[class*=el-icon]:not(.is-status){font-size:18px}.el-step.is-simple .el-step__icon-inner.is-status{transform:scale(.8)translateY(1px)}.el-step.is-simple .el-step__main{flex-grow:1;align-items:stretch;display:flex;position:relative}.el-step.is-simple .el-step__title{font-size:16px;line-height:20px}.el-step.is-simple:not(:last-of-type) .el-step__title{overflow-wrap:break-word;max-width:50%}.el-step.is-simple .el-step__arrow{flex-grow:1;justify-content:center;align-items:center;display:flex}.el-step.is-simple .el-step__arrow:before,.el-step.is-simple .el-step__arrow:after{content:"";background:var(--el-text-color-placeholder);width:1px;height:15px;display:inline-block;position:absolute}.el-step.is-simple .el-step__arrow:before{transform-origin:0 0;transform:rotate(-45deg)translateY(-4px)}.el-step.is-simple .el-step__arrow:after{transform-origin:100% 100%;transform:rotate(45deg)translateY(4px)}.el-step.is-simple:last-of-type .el-step__arrow{display:none}}@layer element-plus{.fade-in-linear-enter-active,.fade-in-linear-leave-active{transition:var(--el-transition-fade-linear)}.fade-in-linear-enter-from,.fade-in-linear-leave-to{opacity:0}.el-fade-in-linear-enter-active,.el-fade-in-linear-leave-active{transition:var(--el-transition-fade-linear)}.el-fade-in-linear-enter-from,.el-fade-in-linear-leave-to{opacity:0}.el-fade-in-enter-active,.el-fade-in-leave-active{transition:all var(--el-transition-duration) cubic-bezier(.55,0,.1,1)}.el-fade-in-enter-from,.el-fade-in-leave-active{opacity:0}.el-zoom-in-center-enter-active,.el-zoom-in-center-leave-active{transition:all var(--el-transition-duration) cubic-bezier(.55,0,.1,1)}.el-zoom-in-center-enter-from,.el-zoom-in-center-leave-active{opacity:0;transform:scaleX(0)}.el-zoom-in-top-enter-active,.el-zoom-in-top-leave-active{opacity:1;transition:var(--el-transition-md-fade);transform-origin:top;transform:scaleY(1)}.el-zoom-in-top-enter-active[data-popper-placement^=top],.el-zoom-in-top-leave-active[data-popper-placement^=top]{transform-origin:bottom}.el-zoom-in-top-enter-from,.el-zoom-in-top-leave-active{opacity:0;transform:scaleY(0)}.el-zoom-in-bottom-enter-active,.el-zoom-in-bottom-leave-active{opacity:1;transition:var(--el-transition-md-fade);transform-origin:bottom;transform:scaleY(1)}.el-zoom-in-bottom-enter-from,.el-zoom-in-bottom-leave-active{opacity:0;transform:scaleY(0)}.el-zoom-in-left-enter-active,.el-zoom-in-left-leave-active{opacity:1;transition:var(--el-transition-md-fade);transform-origin:0 0;transform:scale(1)}.el-zoom-in-left-enter-from,.el-zoom-in-left-leave-active{opacity:0;transform:scale(.45)}.collapse-transition{transition:var(--el-transition-duration) height ease-in-out,var(--el-transition-duration) padding-top ease-in-out,var(--el-transition-duration) padding-bottom ease-in-out}.el-collapse-transition-leave-active,.el-collapse-transition-enter-active{transition:var(--el-transition-duration) max-height ease-in-out,var(--el-transition-duration) padding-top ease-in-out,var(--el-transition-duration) padding-bottom ease-in-out}.horizontal-collapse-transition{transition:var(--el-transition-duration) width ease-in-out,var(--el-transition-duration) padding-left ease-in-out,var(--el-transition-duration) padding-right ease-in-out}.el-list-enter-active,.el-list-leave-active{transition:all 1s}.el-list-enter-from,.el-list-leave-to{opacity:0;transform:translateY(-30px)}.el-list-leave-active{position:absolute!important}.el-opacity-transition{transition:opacity var(--el-transition-duration) cubic-bezier(.55,0,.1,1)}:root{--el-menu-active-color:var(--el-color-primary);--el-menu-text-color:var(--el-text-color-primary);--el-menu-hover-text-color:var(--el-color-primary);--el-menu-bg-color:var(--el-fill-color-blank);--el-menu-hover-bg-color:var(--el-color-primary-light-9);--el-menu-item-height:56px;--el-menu-sub-item-height:calc(var(--el-menu-item-height) - 6px);--el-menu-horizontal-height:60px;--el-menu-horizontal-sub-item-height:36px;--el-menu-item-font-size:var(--el-font-size-base);--el-menu-item-hover-fill:var(--el-color-primary-light-9);--el-menu-border-color:var(--el-border-color);--el-menu-base-level-padding:20px;--el-menu-level-padding:20px;--el-menu-icon-width:24px}.el-menu{border-right:solid 1px var(--el-menu-border-color);background-color:var(--el-menu-bg-color);box-sizing:border-box;margin:0;padding-left:0;list-style:none;position:relative}.el-menu--vertical:not(.el-menu--collapse):not(.el-menu--popup-container) .el-menu-item,.el-menu--vertical:not(.el-menu--collapse):not(.el-menu--popup-container) .el-sub-menu__title,.el-menu--vertical:not(.el-menu--collapse):not(.el-menu--popup-container) .el-menu-item-group__title{white-space:nowrap;padding-left:calc(var(--el-menu-base-level-padding) + var(--el-menu-level) * var(--el-menu-level-padding))}.el-menu:not(.el-menu--collapse) .el-sub-menu__title{padding-right:calc(var(--el-menu-base-level-padding) + var(--el-menu-icon-width))}.el-menu--horizontal{height:var(--el-menu-horizontal-height);border-right:none;flex-wrap:nowrap;display:flex}.el-menu--horizontal.el-menu--popup-container{height:unset}.el-menu--horizontal.el-menu{border-bottom:solid 1px var(--el-menu-border-color)}.el-menu--horizontal>.el-menu-item{height:100%;color:var(--el-menu-text-color);border-bottom:2px solid #0000;justify-content:center;align-items:center;margin:0;display:inline-flex}.el-menu--horizontal>.el-menu-item a,.el-menu--horizontal>.el-menu-item a:hover{color:inherit}.el-menu--horizontal>.el-sub-menu:focus,.el-menu--horizontal>.el-sub-menu:hover{outline:none}.el-menu--horizontal>.el-sub-menu:hover .el-sub-menu__title{color:var(--el-menu-hover-text-color)}.el-menu--horizontal>.el-sub-menu.is-active .el-sub-menu__title{border-bottom:2px solid var(--el-menu-active-color);color:var(--el-menu-active-color)}.el-menu--horizontal>.el-sub-menu .el-sub-menu__title{height:100%;color:var(--el-menu-text-color);border-bottom:2px solid #0000}.el-menu--horizontal>.el-sub-menu .el-sub-menu__title:hover{background-color:var(--el-menu-bg-color)}.el-menu--horizontal .el-menu .el-menu-item,.el-menu--horizontal .el-menu .el-sub-menu__title{background-color:var(--el-menu-bg-color);height:var(--el-menu-horizontal-sub-item-height);line-height:var(--el-menu-horizontal-sub-item-height);color:var(--el-menu-text-color);align-items:center;padding:0 10px;display:flex}.el-menu--horizontal .el-menu .el-sub-menu__title{padding-right:40px}.el-menu--horizontal .el-menu .el-menu-item.is-active,.el-menu--horizontal .el-menu .el-menu-item.is-active:hover,.el-menu--horizontal .el-menu .el-sub-menu.is-active>.el-sub-menu__title,.el-menu--horizontal .el-menu .el-sub-menu.is-active>.el-sub-menu__title:hover{color:var(--el-menu-active-color)}.el-menu--horizontal .el-menu-item:not(.is-disabled):hover,.el-menu--horizontal .el-menu-item:not(.is-disabled):focus{color:var(--el-menu-active-color,var(--el-menu-hover-text-color));background-color:var(--el-menu-hover-bg-color);outline:none}.el-menu--horizontal>.el-menu-item.is-active{border-bottom:2px solid var(--el-menu-active-color);color:var(--el-menu-active-color)!important}.el-menu--collapse{width:calc(var(--el-menu-icon-width) + var(--el-menu-base-level-padding) * 2)}.el-menu--collapse>.el-menu-item [class^=el-icon],.el-menu--collapse>.el-sub-menu>.el-sub-menu__title [class^=el-icon],.el-menu--collapse>.el-menu-item-group>ul>.el-sub-menu>.el-sub-menu__title [class^=el-icon]{vertical-align:middle;width:var(--el-menu-icon-width);text-align:center;margin:0}.el-menu--collapse>.el-menu-item .el-sub-menu__icon-arrow,.el-menu--collapse>.el-sub-menu>.el-sub-menu__title .el-sub-menu__icon-arrow,.el-menu--collapse>.el-menu-item-group>ul>.el-sub-menu>.el-sub-menu__title .el-sub-menu__icon-arrow{display:none}.el-menu--collapse>.el-menu-item>span,.el-menu--collapse>.el-sub-menu>.el-sub-menu__title>span,.el-menu--collapse>.el-menu-item-group>ul>.el-sub-menu>.el-sub-menu__title>span{visibility:hidden;width:0;height:0;display:inline-block;overflow:hidden}.el-menu--collapse>.el-menu-item.is-active i{color:inherit}.el-menu--collapse .el-menu .el-sub-menu{min-width:200px}.el-menu--collapse .el-sub-menu.is-active .el-sub-menu__title{color:var(--el-menu-active-color)}.el-menu--popup{z-index:100;border-radius:var(--el-border-radius-small);min-width:200px;box-shadow:var(--el-box-shadow-light);border:none;padding:5px 0}.el-menu .el-icon{flex-shrink:0}.el-menu-item{height:var(--el-menu-item-height);line-height:var(--el-menu-item-height);font-size:var(--el-menu-item-font-size);color:var(--el-menu-text-color);padding:0 var(--el-menu-base-level-padding);cursor:pointer;transition:border-color var(--el-transition-duration),background-color var(--el-transition-duration),color var(--el-transition-duration);box-sizing:border-box;white-space:nowrap;align-items:center;list-style:none;display:flex;position:relative}.el-menu-item *{vertical-align:bottom}.el-menu-item i{color:inherit}.el-menu-item:hover,.el-menu-item:focus{outline:none}.el-menu-item:hover{background-color:var(--el-menu-hover-bg-color)}.el-menu-item.is-disabled{opacity:.25;cursor:not-allowed;background:0 0!important}.el-menu-item [class^=el-icon]{width:var(--el-menu-icon-width);text-align:center;vertical-align:middle;margin-right:5px;font-size:18px}.el-menu-item.is-active{color:var(--el-menu-active-color)}.el-menu-item.is-active i{color:inherit}.el-menu-item .el-menu-tooltip__trigger{box-sizing:border-box;width:100%;height:100%;padding:0 var(--el-menu-base-level-padding);align-items:center;display:inline-flex;position:absolute;top:0;left:0}.el-sub-menu{margin:0;padding-left:0;list-style:none}.el-sub-menu__title{height:var(--el-menu-item-height);line-height:var(--el-menu-item-height);font-size:var(--el-menu-item-font-size);color:var(--el-menu-text-color);padding:0 var(--el-menu-base-level-padding);cursor:pointer;transition:border-color var(--el-transition-duration),background-color var(--el-transition-duration),color var(--el-transition-duration);box-sizing:border-box;white-space:nowrap;align-items:center;list-style:none;display:flex;position:relative}.el-sub-menu__title *{vertical-align:bottom}.el-sub-menu__title i{color:inherit}.el-sub-menu__title:hover,.el-sub-menu__title:focus{outline:none}.el-sub-menu__title.is-disabled{opacity:.25;cursor:not-allowed;background:0 0!important}.el-sub-menu__title:hover{background-color:var(--el-menu-hover-bg-color)}.el-sub-menu .el-menu{border:none}.el-sub-menu .el-menu-item{height:var(--el-menu-sub-item-height);line-height:var(--el-menu-sub-item-height)}.el-sub-menu.el-sub-menu__hide-arrow .el-sub-menu__title{padding-right:var(--el-menu-base-level-padding)}.el-sub-menu__hide-arrow .el-sub-menu__icon-arrow{display:none!important}.el-sub-menu.is-active .el-sub-menu__title{border-bottom-color:var(--el-menu-active-color)}.el-sub-menu.is-disabled .el-sub-menu__title,.el-sub-menu.is-disabled .el-menu-item{opacity:.25;cursor:not-allowed;background:0 0!important}.el-sub-menu .el-icon{vertical-align:middle;width:var(--el-menu-icon-width);text-align:center;margin-right:5px;font-size:18px}.el-sub-menu .el-icon.el-sub-menu__icon-more{margin-right:0!important}.el-sub-menu .el-sub-menu__icon-arrow{top:50%;right:var(--el-menu-base-level-padding);transition:transform var(--el-transition-duration);width:inherit;margin-top:-6px;margin-right:0;font-size:12px;position:absolute}.el-menu-item-group>ul{padding:0}.el-menu-item-group__title{padding:7px 0 7px var(--el-menu-base-level-padding);color:var(--el-text-color-secondary);font-size:12px;line-height:normal}.horizontal-collapse-transition .el-sub-menu__title .el-sub-menu__icon-arrow{transition:var(--el-transition-duration-fast);opacity:0}.el-popper,.el-menu--popup-container,.el-menu{outline:none}}@layer element-plus;@layer element-plus;@layer element-plus{.el-avatar{--el-avatar-text-color:var(--el-color-white);--el-avatar-bg-color:var(--el-text-color-disabled);--el-avatar-text-size:14px;--el-avatar-icon-size:18px;--el-avatar-border-radius:var(--el-border-radius-base);--el-avatar-size-large:56px;--el-avatar-size:40px;--el-avatar-size-small:24px;box-sizing:border-box;text-align:center;color:var(--el-avatar-text-color);background:var(--el-avatar-bg-color);width:var(--el-avatar-size);height:var(--el-avatar-size);font-size:var(--el-avatar-text-size);outline:none;justify-content:center;align-items:center;display:inline-flex;overflow:hidden}.el-avatar>img{width:100%;height:100%;display:block}.el-avatar--circle{border-radius:50%}.el-avatar--square{border-radius:var(--el-avatar-border-radius)}.el-avatar--icon{font-size:var(--el-avatar-icon-size)}.el-avatar--small{--el-avatar-size:24px}.el-avatar--large{--el-avatar-size:56px}}@layer element-plus{.el-timeline{--el-timeline-node-size-normal:12px;--el-timeline-node-size-large:14px;--el-timeline-node-color:var(--el-border-color-light);font-size:var(--el-font-size-base);margin:0;list-style:none}.el-timeline .el-timeline-item:last-child .el-timeline-item__tail{display:none}.el-timeline .el-timeline-item__center{align-items:center;display:flex}.el-timeline .el-timeline-item__center .el-timeline-item__wrapper{width:100%}.el-timeline .el-timeline-item__center .el-timeline-item__tail{top:0}.el-timeline .el-timeline-item__center:first-child .el-timeline-item__tail{height:calc(50% + 10px);top:calc(50% - 10px)}.el-timeline .el-timeline-item__center:last-child .el-timeline-item__tail{height:calc(50% - 10px);display:block}.el-timeline.is-start{padding-left:40px;padding-right:0}.el-timeline.is-end{padding-left:0;padding-right:40px}.el-timeline.is-alternate{padding-left:20px;padding-right:20px}.el-timeline.is-alternate .el-timeline-item:nth-child(odd) .el-timeline-item__wrapper{width:calc(50% - 28px);left:calc(50% - var(--el-timeline-node-size-large) / 2);padding-left:28px}.el-timeline.is-alternate .el-timeline-item:nth-child(2n) .el-timeline-item__wrapper{width:calc(50% - 28px + var(--el-timeline-node-size-large) / 2);text-align:right;padding-right:28px}.el-timeline.is-alternate-reverse{padding-left:20px;padding-right:20px}.el-timeline.is-alternate-reverse .el-timeline-item:nth-child(odd) .el-timeline-item__wrapper{width:calc(50% - 28px + var(--el-timeline-node-size-large) / 2);text-align:right;padding-right:28px}.el-timeline.is-alternate-reverse .el-timeline-item:nth-child(2n) .el-timeline-item__wrapper{width:calc(50% - 28px);left:calc(50% - var(--el-timeline-node-size-large) / 2);padding-left:28px}}@layer element-plus{.el-timeline-item{padding-bottom:20px;position:relative}.el-timeline-item__wrapper{box-sizing:content-box;position:relative;top:-3px}.el-timeline-item__tail{border-left:2px solid var(--el-timeline-node-color);height:100%;position:absolute}.el-timeline-item .el-timeline-item__icon{color:var(--el-color-white);font-size:var(--el-font-size-small)}.el-timeline-item__node{background-color:var(--el-timeline-node-color);border-color:var(--el-timeline-node-color);box-sizing:border-box;border-radius:50%;justify-content:center;align-items:center;display:flex;position:absolute}.el-timeline-item__node--normal{width:var(--el-timeline-node-size-normal);height:var(--el-timeline-node-size-normal)}.el-timeline-item__node--large{width:var(--el-timeline-node-size-large);height:var(--el-timeline-node-size-large)}.el-timeline-item__node.is-hollow{background:var(--el-color-white);border-style:solid;border-width:2px}.el-timeline-item__node--primary{background-color:var(--el-color-primary);border-color:var(--el-color-primary)}.el-timeline-item__node--success{background-color:var(--el-color-success);border-color:var(--el-color-success)}.el-timeline-item__node--warning{background-color:var(--el-color-warning);border-color:var(--el-color-warning)}.el-timeline-item__node--danger{background-color:var(--el-color-danger);border-color:var(--el-color-danger)}.el-timeline-item__node--info{background-color:var(--el-color-info);border-color:var(--el-color-info)}.el-timeline-item__dot{justify-content:center;align-items:center;display:flex;position:absolute}.el-timeline-item__content{color:var(--el-text-color-primary)}.el-timeline-item__timestamp{color:var(--el-text-color-secondary);line-height:1;font-size:var(--el-font-size-small)}.el-timeline-item__timestamp.is-top{margin-bottom:8px;padding-top:4px}.el-timeline-item__timestamp.is-bottom{margin-top:8px}.el-timeline-item.is-start .el-timeline-item__wrapper{padding-left:28px}.el-timeline-item.is-start .el-timeline-item__tail{left:4px}.el-timeline-item.is-start .el-timeline-item__node--normal{left:-1px}.el-timeline-item.is-start .el-timeline-item__node--large{left:-2px}.el-timeline-item.is-end .el-timeline-item__wrapper{text-align:right;padding-right:28px}.el-timeline-item.is-end .el-timeline-item__tail{right:4px}.el-timeline-item.is-end .el-timeline-item__node--normal{right:-1px}.el-timeline-item.is-end .el-timeline-item__node--large{right:-2px}.el-timeline-item.is-alternate .el-timeline-item__tail,.el-timeline-item.is-alternate .el-timeline-item__node,.el-timeline-item.is-alternate-reverse .el-timeline-item__tail,.el-timeline-item.is-alternate-reverse .el-timeline-item__node{left:50%;transform:translate(-50%)}}@layer element-plus{.el-badge{--el-badge-bg-color:var(--el-color-danger);--el-badge-radius:10px;--el-badge-font-size:12px;--el-badge-padding:6px;--el-badge-size:18px;vertical-align:middle;width:-moz-fit-content;width:fit-content;display:inline-block;position:relative}.el-badge__content{background-color:var(--el-badge-bg-color);border-radius:var(--el-badge-radius);color:var(--el-color-white);font-size:var(--el-badge-font-size);height:var(--el-badge-size);padding:0 var(--el-badge-padding);white-space:nowrap;border:1px solid var(--el-bg-color);justify-content:center;align-items:center;display:inline-flex}.el-badge__content.is-fixed{top:0;right:calc(1px + var(--el-badge-size) / 2);z-index:var(--el-index-normal);position:absolute;transform:translateY(-50%)translate(100%)}.el-badge__content.is-fixed.is-dot{right:5px}.el-badge__content.is-dot{border-radius:50%;width:8px;height:8px;padding:0;right:0}.el-badge__content.is-hide-zero{display:none}.el-badge__content--primary{background-color:var(--el-color-primary)}.el-badge__content--success{background-color:var(--el-color-success)}.el-badge__content--warning{background-color:var(--el-color-warning)}.el-badge__content--info{background-color:var(--el-color-info)}.el-badge__content--danger{background-color:var(--el-color-danger)}}@layer element-plus{.el-collapse{--el-collapse-border-color:var(--el-border-color-lighter);--el-collapse-header-height:48px;--el-collapse-header-bg-color:var(--el-fill-color-blank);--el-collapse-header-text-color:var(--el-text-color-primary);--el-collapse-header-font-size:13px;--el-collapse-content-bg-color:var(--el-fill-color-blank);--el-collapse-content-font-size:13px;--el-collapse-content-text-color:var(--el-text-color-primary);border-top:1px solid var(--el-collapse-border-color);border-bottom:1px solid var(--el-collapse-border-color)}.el-collapse-icon-position-left .el-collapse-item__header{gap:8px}.el-collapse-icon-position-left .el-collapse-item__title{order:1}.el-collapse-icon-position-right .el-collapse-item__header{padding-right:8px}}@layer element-plus{.el-collapse-item.is-disabled .el-collapse-item__header{color:var(--el-text-color-disabled);cursor:not-allowed}.el-collapse-item__header{width:100%;min-height:var(--el-collapse-header-height);line-height:var(--el-collapse-header-height);background-color:var(--el-collapse-header-bg-color);color:var(--el-collapse-header-text-color);cursor:pointer;border:none;border-bottom:1px solid var(--el-collapse-border-color);font-size:var(--el-collapse-header-font-size);transition:border-bottom-color var(--el-transition-duration);box-sizing:border-box;outline:none;align-items:center;padding:0;font-weight:500;display:flex}.el-collapse-item__arrow{transition:transform var(--el-transition-duration);font-weight:300}.el-collapse-item__arrow.is-active{transform:rotate(90deg)}.el-collapse-item__title{text-align:left;flex:auto}.el-collapse-item__header.focusing:focus:not(:hover){color:var(--el-color-primary)}.el-collapse-item__header.is-active{border-bottom-color:#0000}.el-collapse-item__wrap{will-change:height;background-color:var(--el-collapse-content-bg-color);box-sizing:border-box;border-bottom:1px solid var(--el-collapse-border-color);overflow:hidden}.el-collapse-item__content{font-size:var(--el-collapse-content-font-size);color:var(--el-collapse-content-text-color);padding-bottom:25px;line-height:1.76923}.el-collapse-item:last-child{margin-bottom:-1px}} + +:root{--iti-hover-color: rgba(0, 0, 0, .05);--iti-border-color: #ccc;--iti-dropdown-bg: white;--iti-icon-color: #555;--iti-spacer-horizontal: 8px;--iti-flag-height: 12px;--iti-flag-width: 16px;--iti-globe-height: 16px;--iti-search-clear-icon-height: 13px;--iti-border-width: 1px;--iti-arrow-height: 4px;--iti-arrow-width: calc((var(--iti-arrow-height) / 2) * 3);--iti-triangle-border: calc(var(--iti-arrow-width) / 2);--iti-arrow-padding: 6px;--iti-flag-sprite-width: 3904px;--iti-flag-sprite-height: 12px;--iti-mobile-popup-margin: 30px}.iti{position:relative;display:inline-block}.iti *{box-sizing:border-box}.iti__a11y-text{width:1px;height:1px;clip:rect(1px,1px,1px,1px);overflow:hidden;position:absolute}.iti input.iti__tel-input,.iti input.iti__tel-input[type=text],.iti input.iti__tel-input[type=tel]{position:relative;z-index:0;margin:0!important}.iti__country-container{position:absolute;top:0;bottom:0;left:0;padding:var(--iti-border-width)}.iti__selected-country{z-index:1;position:relative;display:flex;align-items:center;height:100%;background:none;border:0;margin:0;padding:0;font-family:inherit;font-size:inherit;color:inherit;border-radius:0;font-weight:inherit;line-height:inherit;text-decoration:none}.iti__selected-country-primary{display:flex;align-items:center;height:100%;padding:0 var(--iti-arrow-padding) 0 var(--iti-spacer-horizontal)}.iti__arrow{margin-left:var(--iti-arrow-padding);width:0;height:0;border-left:var(--iti-triangle-border) solid transparent;border-right:var(--iti-triangle-border) solid transparent;border-top:var(--iti-arrow-height) solid var(--iti-icon-color)}.iti__arrow--up{border-top:none;border-bottom:var(--iti-arrow-height) solid var(--iti-icon-color)}.iti__dropdown-content{border-radius:3px;background-color:var(--iti-dropdown-bg)}.iti--inline-dropdown .iti__dropdown-content{border:var(--iti-border-width) solid var(--iti-border-color);box-shadow:1px 1px 4px #0003}.iti--inline-dropdown:not(.iti--container) .iti__dropdown-content{position:absolute;z-index:2;left:0}.iti__search-input{width:100%;border-width:0;border-radius:3px;padding-left:30px;padding-right:28px}[dir=rtl] .iti__search-input{padding-left:inherit;padding-right:30px;background-position:right 8px center}.iti__search-input::-webkit-search-cancel-button{-webkit-appearance:none;-moz-appearance:none;appearance:none}.iti__search-input,.iti__country{padding-top:8px;padding-bottom:8px}.iti__search-input-wrapper{position:relative;display:flex;align-items:center;border-bottom:1px solid var(--iti-border-color)}.iti__search-icon{position:absolute;left:8px;display:flex;pointer-events:none}[dir=rtl] .iti__search-icon{left:auto;right:8px}.iti__search-icon-svg{width:var(--iti-globe-height);height:var(--iti-globe-height);display:block;stroke:var(--iti-icon-color);fill:none;stroke-width:3}.iti__search-clear{position:absolute;right:4px;background:transparent;border:0;border-radius:3px;cursor:pointer;padding:5px;display:flex;align-items:center;justify-content:center;transition:background-color .15s ease}.iti__search-clear .iti__search-clear-x{stroke-width:2}.iti__search-clear .iti__search-clear-bg{fill:var(--iti-icon-color)}.iti__search-clear-svg{width:var(--iti-search-clear-icon-height);height:var(--iti-search-clear-icon-height);display:block}[dir=rtl] .iti__search-clear{right:auto;left:4px}.iti__search-clear:hover,.iti__search-clear:focus-visible{background:var(--iti-hover-color);outline:none}.iti__no-results{text-align:center;padding:30px 0}.iti__country-list{list-style:none;padding:0;margin:0;cursor:pointer;overflow-y:scroll;-webkit-overflow-scrolling:touch}.iti--inline-dropdown .iti__country-list{max-height:185px}.iti--flexible-dropdown-width .iti__country-list{white-space:nowrap}@media (max-width: 500px){.iti--flexible-dropdown-width .iti__country-list{white-space:normal}}.iti__country{display:flex;align-items:center;padding-left:var(--iti-spacer-horizontal);padding-right:var(--iti-spacer-horizontal);outline:none}.iti__country-name{flex-grow:1}.iti__country-check{margin:0 1px 0 var(--iti-spacer-horizontal);display:flex;align-items:center;color:var(--iti-icon-color)}.iti__country-check-svg{width:var(--iti-search-clear-icon-height);height:var(--iti-search-clear-icon-height);display:block}.iti__country.iti__highlight{background-color:var(--iti-hover-color)}.iti__country-list .iti__flag{margin-right:var(--iti-spacer-horizontal)}[dir=rtl] .iti__country-list .iti__flag{margin-right:0;margin-left:var(--iti-spacer-horizontal)}.iti__country-list .iti__flag{flex-shrink:0}.iti--allow-dropdown .iti__country-container:has(+input[disabled]) button.iti__selected-country,.iti--allow-dropdown .iti__country-container:has(+input[readonly]) button.iti__selected-country{cursor:not-allowed}.iti--allow-dropdown .iti__country-container:has(+input[disabled]) button.iti__selected-country .iti__arrow,.iti--allow-dropdown .iti__country-container:has(+input[readonly]) button.iti__selected-country .iti__arrow{visibility:hidden}.iti--allow-dropdown .iti__country-container:not(:has(+input[disabled])):not(:has(+input[readonly])) .iti__selected-country-primary:hover,.iti--allow-dropdown .iti__country-container:not(:has(+input[disabled])):not(:has(+input[readonly])) .iti__selected-country:has(+.iti__dropdown-content:hover) .iti__selected-country-primary{background-color:var(--iti-hover-color)}.iti .iti__selected-dial-code{margin-left:4px}.iti--container{position:fixed;top:-1000px;left:-1000px;z-index:1060}.iti--container:hover{cursor:pointer}.iti__hide{display:none}.iti__v-hide{visibility:hidden}.iti--fullscreen-popup.iti--container{background-color:#00000080;top:0;bottom:0;left:0;right:0;position:fixed;padding:var(--iti-mobile-popup-margin);display:flex;flex-direction:column;justify-content:flex-start}.iti--fullscreen-popup .iti__dropdown-content{display:flex;flex-direction:column;max-height:100%;position:relative}.iti--fullscreen-popup .iti__country,.iti--fullscreen-popup .iti__search-input{padding-top:10px;padding-bottom:10px}.iti--fullscreen-popup .iti__country{padding-left:10px;padding-right:10px;line-height:1.5em}.iti__flag{--iti-flag-offset: 100px;height:var(--iti-flag-height);width:var(--iti-flag-width);border-radius:1px;box-shadow:0 0 1px #888;background-image:image-set(var(--iti-path-flags-1x) 1x,var(--iti-path-flags-2x) 2x);background-repeat:no-repeat;background-position:var(--iti-flag-offset) 0;background-size:var(--iti-flag-sprite-width) var(--iti-flag-sprite-height)}.iti__loading{position:relative;background:none;box-shadow:none}.iti__loading:after{content:"";position:absolute;top:0;right:0;bottom:0;left:0;margin:auto;width:var(--iti-flag-height);height:var(--iti-flag-height);box-sizing:border-box;border:2px solid var(--iti-icon-color);border-right-color:transparent;border-radius:50%;animation:iti-spinner 1s linear infinite}@keyframes iti-spinner{to{transform:rotate(360deg)}}.iti__ac{--iti-flag-offset: 0px}.iti__ad{--iti-flag-offset: -16px}.iti__ae{--iti-flag-offset: -32px}.iti__af{--iti-flag-offset: -48px}.iti__ag{--iti-flag-offset: -64px}.iti__ai{--iti-flag-offset: -80px}.iti__al{--iti-flag-offset: -96px}.iti__am{--iti-flag-offset: -112px}.iti__ao{--iti-flag-offset: -128px}.iti__ar{--iti-flag-offset: -144px}.iti__as{--iti-flag-offset: -160px}.iti__at{--iti-flag-offset: -176px}.iti__au{--iti-flag-offset: -192px}.iti__aw{--iti-flag-offset: -208px}.iti__ax{--iti-flag-offset: -224px}.iti__az{--iti-flag-offset: -240px}.iti__ba{--iti-flag-offset: -256px}.iti__bb{--iti-flag-offset: -272px}.iti__bd{--iti-flag-offset: -288px}.iti__be{--iti-flag-offset: -304px}.iti__bf{--iti-flag-offset: -320px}.iti__bg{--iti-flag-offset: -336px}.iti__bh{--iti-flag-offset: -352px}.iti__bi{--iti-flag-offset: -368px}.iti__bj{--iti-flag-offset: -384px}.iti__bl{--iti-flag-offset: -400px}.iti__bm{--iti-flag-offset: -416px}.iti__bn{--iti-flag-offset: -432px}.iti__bo{--iti-flag-offset: -448px}.iti__bq{--iti-flag-offset: -464px}.iti__br{--iti-flag-offset: -480px}.iti__bs{--iti-flag-offset: -496px}.iti__bt{--iti-flag-offset: -512px}.iti__bw{--iti-flag-offset: -528px}.iti__by{--iti-flag-offset: -544px}.iti__bz{--iti-flag-offset: -560px}.iti__ca{--iti-flag-offset: -576px}.iti__cc{--iti-flag-offset: -592px}.iti__cd{--iti-flag-offset: -608px}.iti__cf{--iti-flag-offset: -624px}.iti__cg{--iti-flag-offset: -640px}.iti__ch{--iti-flag-offset: -656px}.iti__ci{--iti-flag-offset: -672px}.iti__ck{--iti-flag-offset: -688px}.iti__cl{--iti-flag-offset: -704px}.iti__cm{--iti-flag-offset: -720px}.iti__cn{--iti-flag-offset: -736px}.iti__co{--iti-flag-offset: -752px}.iti__cr{--iti-flag-offset: -768px}.iti__cu{--iti-flag-offset: -784px}.iti__cv{--iti-flag-offset: -800px}.iti__cw{--iti-flag-offset: -816px}.iti__cx{--iti-flag-offset: -832px}.iti__cy{--iti-flag-offset: -848px}.iti__cz{--iti-flag-offset: -864px}.iti__de{--iti-flag-offset: -880px}.iti__dj{--iti-flag-offset: -896px}.iti__dk{--iti-flag-offset: -912px}.iti__dm{--iti-flag-offset: -928px}.iti__do{--iti-flag-offset: -944px}.iti__dz{--iti-flag-offset: -960px}.iti__ec{--iti-flag-offset: -976px}.iti__ee{--iti-flag-offset: -992px}.iti__eg{--iti-flag-offset: -1008px}.iti__eh{--iti-flag-offset: -1024px}.iti__er{--iti-flag-offset: -1040px}.iti__es{--iti-flag-offset: -1056px}.iti__et{--iti-flag-offset: -1072px}.iti__fi{--iti-flag-offset: -1088px}.iti__fj{--iti-flag-offset: -1104px}.iti__fk{--iti-flag-offset: -1120px}.iti__fm{--iti-flag-offset: -1136px}.iti__fo{--iti-flag-offset: -1152px}.iti__fr{--iti-flag-offset: -1168px}.iti__ga{--iti-flag-offset: -1184px}.iti__gb{--iti-flag-offset: -1200px}.iti__gd{--iti-flag-offset: -1216px}.iti__ge{--iti-flag-offset: -1232px}.iti__gf{--iti-flag-offset: -1248px}.iti__gg{--iti-flag-offset: -1264px}.iti__gh{--iti-flag-offset: -1280px}.iti__gi{--iti-flag-offset: -1296px}.iti__gl{--iti-flag-offset: -1312px}.iti__gm{--iti-flag-offset: -1328px}.iti__gn{--iti-flag-offset: -1344px}.iti__gp{--iti-flag-offset: -1360px}.iti__gq{--iti-flag-offset: -1376px}.iti__gr{--iti-flag-offset: -1392px}.iti__gt{--iti-flag-offset: -1408px}.iti__gu{--iti-flag-offset: -1424px}.iti__gw{--iti-flag-offset: -1440px}.iti__gy{--iti-flag-offset: -1456px}.iti__hk{--iti-flag-offset: -1472px}.iti__hn{--iti-flag-offset: -1488px}.iti__hr{--iti-flag-offset: -1504px}.iti__ht{--iti-flag-offset: -1520px}.iti__hu{--iti-flag-offset: -1536px}.iti__id{--iti-flag-offset: -1552px}.iti__ie{--iti-flag-offset: -1568px}.iti__il{--iti-flag-offset: -1584px}.iti__im{--iti-flag-offset: -1600px}.iti__in{--iti-flag-offset: -1616px}.iti__io{--iti-flag-offset: -1632px}.iti__iq{--iti-flag-offset: -1648px}.iti__ir{--iti-flag-offset: -1664px}.iti__is{--iti-flag-offset: -1680px}.iti__it{--iti-flag-offset: -1696px}.iti__je{--iti-flag-offset: -1712px}.iti__jm{--iti-flag-offset: -1728px}.iti__jo{--iti-flag-offset: -1744px}.iti__jp{--iti-flag-offset: -1760px}.iti__ke{--iti-flag-offset: -1776px}.iti__kg{--iti-flag-offset: -1792px}.iti__kh{--iti-flag-offset: -1808px}.iti__ki{--iti-flag-offset: -1824px}.iti__km{--iti-flag-offset: -1840px}.iti__kn{--iti-flag-offset: -1856px}.iti__kp{--iti-flag-offset: -1872px}.iti__kr{--iti-flag-offset: -1888px}.iti__kw{--iti-flag-offset: -1904px}.iti__ky{--iti-flag-offset: -1920px}.iti__kz{--iti-flag-offset: -1936px}.iti__la{--iti-flag-offset: -1952px}.iti__lb{--iti-flag-offset: -1968px}.iti__lc{--iti-flag-offset: -1984px}.iti__li{--iti-flag-offset: -2000px}.iti__lk{--iti-flag-offset: -2016px}.iti__lr{--iti-flag-offset: -2032px}.iti__ls{--iti-flag-offset: -2048px}.iti__lt{--iti-flag-offset: -2064px}.iti__lu{--iti-flag-offset: -2080px}.iti__lv{--iti-flag-offset: -2096px}.iti__ly{--iti-flag-offset: -2112px}.iti__ma{--iti-flag-offset: -2128px}.iti__mc{--iti-flag-offset: -2144px}.iti__md{--iti-flag-offset: -2160px}.iti__me{--iti-flag-offset: -2176px}.iti__mf{--iti-flag-offset: -2192px}.iti__mg{--iti-flag-offset: -2208px}.iti__mh{--iti-flag-offset: -2224px}.iti__mk{--iti-flag-offset: -2240px}.iti__ml{--iti-flag-offset: -2256px}.iti__mm{--iti-flag-offset: -2272px}.iti__mn{--iti-flag-offset: -2288px}.iti__mo{--iti-flag-offset: -2304px}.iti__mp{--iti-flag-offset: -2320px}.iti__mq{--iti-flag-offset: -2336px}.iti__mr{--iti-flag-offset: -2352px}.iti__ms{--iti-flag-offset: -2368px}.iti__mt{--iti-flag-offset: -2384px}.iti__mu{--iti-flag-offset: -2400px}.iti__mv{--iti-flag-offset: -2416px}.iti__mw{--iti-flag-offset: -2432px}.iti__mx{--iti-flag-offset: -2448px}.iti__my{--iti-flag-offset: -2464px}.iti__mz{--iti-flag-offset: -2480px}.iti__na{--iti-flag-offset: -2496px}.iti__nc{--iti-flag-offset: -2512px}.iti__ne{--iti-flag-offset: -2528px}.iti__nf{--iti-flag-offset: -2544px}.iti__ng{--iti-flag-offset: -2560px}.iti__ni{--iti-flag-offset: -2576px}.iti__nl{--iti-flag-offset: -2592px}.iti__no{--iti-flag-offset: -2608px}.iti__np{--iti-flag-offset: -2624px}.iti__nr{--iti-flag-offset: -2640px}.iti__nu{--iti-flag-offset: -2656px}.iti__nz{--iti-flag-offset: -2672px}.iti__om{--iti-flag-offset: -2688px}.iti__pa{--iti-flag-offset: -2704px}.iti__pe{--iti-flag-offset: -2720px}.iti__pf{--iti-flag-offset: -2736px}.iti__pg{--iti-flag-offset: -2752px}.iti__ph{--iti-flag-offset: -2768px}.iti__pk{--iti-flag-offset: -2784px}.iti__pl{--iti-flag-offset: -2800px}.iti__pm{--iti-flag-offset: -2816px}.iti__pr{--iti-flag-offset: -2832px}.iti__ps{--iti-flag-offset: -2848px}.iti__pt{--iti-flag-offset: -2864px}.iti__pw{--iti-flag-offset: -2880px}.iti__py{--iti-flag-offset: -2896px}.iti__qa{--iti-flag-offset: -2912px}.iti__re{--iti-flag-offset: -2928px}.iti__ro{--iti-flag-offset: -2944px}.iti__rs{--iti-flag-offset: -2960px}.iti__ru{--iti-flag-offset: -2976px}.iti__rw{--iti-flag-offset: -2992px}.iti__sa{--iti-flag-offset: -3008px}.iti__sb{--iti-flag-offset: -3024px}.iti__sc{--iti-flag-offset: -3040px}.iti__sd{--iti-flag-offset: -3056px}.iti__se{--iti-flag-offset: -3072px}.iti__sg{--iti-flag-offset: -3088px}.iti__sh{--iti-flag-offset: -3104px}.iti__si{--iti-flag-offset: -3120px}.iti__sj{--iti-flag-offset: -3136px}.iti__sk{--iti-flag-offset: -3152px}.iti__sl{--iti-flag-offset: -3168px}.iti__sm{--iti-flag-offset: -3184px}.iti__sn{--iti-flag-offset: -3200px}.iti__so{--iti-flag-offset: -3216px}.iti__sr{--iti-flag-offset: -3232px}.iti__ss{--iti-flag-offset: -3248px}.iti__st{--iti-flag-offset: -3264px}.iti__sv{--iti-flag-offset: -3280px}.iti__sx{--iti-flag-offset: -3296px}.iti__sy{--iti-flag-offset: -3312px}.iti__sz{--iti-flag-offset: -3328px}.iti__tc{--iti-flag-offset: -3344px}.iti__td{--iti-flag-offset: -3360px}.iti__tg{--iti-flag-offset: -3376px}.iti__th{--iti-flag-offset: -3392px}.iti__tj{--iti-flag-offset: -3408px}.iti__tk{--iti-flag-offset: -3424px}.iti__tl{--iti-flag-offset: -3440px}.iti__tm{--iti-flag-offset: -3456px}.iti__tn{--iti-flag-offset: -3472px}.iti__to{--iti-flag-offset: -3488px}.iti__tr{--iti-flag-offset: -3504px}.iti__tt{--iti-flag-offset: -3520px}.iti__tv{--iti-flag-offset: -3536px}.iti__tw{--iti-flag-offset: -3552px}.iti__tz{--iti-flag-offset: -3568px}.iti__ua{--iti-flag-offset: -3584px}.iti__ug{--iti-flag-offset: -3600px}.iti__us{--iti-flag-offset: -3616px}.iti__uy{--iti-flag-offset: -3632px}.iti__uz{--iti-flag-offset: -3648px}.iti__va{--iti-flag-offset: -3664px}.iti__vc{--iti-flag-offset: -3680px}.iti__ve{--iti-flag-offset: -3696px}.iti__vg{--iti-flag-offset: -3712px}.iti__vi{--iti-flag-offset: -3728px}.iti__vn{--iti-flag-offset: -3744px}.iti__vu{--iti-flag-offset: -3760px}.iti__wf{--iti-flag-offset: -3776px}.iti__ws{--iti-flag-offset: -3792px}.iti__xk{--iti-flag-offset: -3808px}.iti__ye{--iti-flag-offset: -3824px}.iti__yt{--iti-flag-offset: -3840px}.iti__za{--iti-flag-offset: -3856px}.iti__zm{--iti-flag-offset: -3872px}.iti__zw{--iti-flag-offset: -3888px}.iti__globe{background:none;box-shadow:none;height:var(--iti-globe-height);display:flex;align-items:center;justify-content:center;padding:0}.iti__globe .iti__globe-svg{width:100%;height:100%;fill:var(--iti-icon-color)}@supports (-webkit-appearance: none) and (not (background: -webkit-canvas(foo))){.iti__tel-input:focus{outline-offset:1px}}:root{--iti-path-flags-1x: url(../../flags.webp);--iti-path-flags-2x: url(../../flags@2x.webp)} + +.navigable-list-container[data-v-a5646a37]{width:100%}.fcrm_theme_mode{display:inline-flex;align-items:center}.fcrm_theme_mode--trigger{display:inline-flex;align-items:center;justify-content:center;background:none;border:none;cursor:pointer;padding:4px;border-radius:6px;color:var(--fc-secondary-text);transition:color .2s,background-color .2s}.fcrm_theme_mode--trigger:hover{color:var(--fc-text);background-color:var(--fc-bg-hover, rgba(128, 128, 128, .1))}.fcrm_theme_mode--trigger svg{width:20px;height:20px;display:block}.fcrm_theme_mode--popper .el-dropdown-menu{min-width:160px}.fcrm_theme_mode--popper .el-dropdown-menu__item{padding:0;border-radius:6px;line-height:normal}.fcrm_theme_mode--item{display:flex;align-items:center;gap:10px;font-size:14px;white-space:nowrap;width:100%}.fcrm_theme_mode--item>svg:first-child{width:18px;height:18px;flex-shrink:0;color:var(--fc-secondary-text)}.fcrm_theme_mode--item>span{flex:1}.fcrm_theme_mode--active .fcrm_theme_mode--item>svg:first-child{color:var(--fc-text)}.fcrm_theme_mode--check{width:16px;height:16px;flex-shrink:0;color:var(--fc-text)} + +.fcrm-loader-overlay[data-v-523b0015]{position:absolute;top:0;left:0;width:100%;min-height:100%;z-index:1}.fcrm-fade-enter-active[data-v-523b0015],.fcrm-fade-leave-active[data-v-523b0015]{transition:opacity .2s ease}.fcrm-fade-enter-from[data-v-523b0015],.fcrm-fade-leave-to[data-v-523b0015]{opacity:0} + +.promo_block--centered[data-v-832a775e]{background:#fff;padding:10px;text-align:center;display:block;overflow:hidden} + +[data-v-f370520c] .fcrm_email_preview_drawer .el-drawer__header{border-bottom:1px solid var(--fc-primary-border);margin-bottom:0;padding-bottom:16px}[data-v-f370520c] .fcrm_email_preview_drawer .el-drawer__body{padding:0;background:var(--fc-secondary-bg)}.fc_email_preview[data-v-f370520c]{display:inline-block}.fcrm_email_preview_shell[data-v-f370520c]{min-height:100%}.fcrm_preview_meta[data-v-f370520c]{padding:10px 20px;font-size:13px;color:var(--fc-secondary-text);border-bottom:1px solid var(--fc-primary-border);background:var(--fc-secondary-bg)}.fcrm_preview_loading[data-v-f370520c]{padding:20px}.contact_selector_title[data-v-f370520c]{font-weight:600;margin-bottom:8px}.contact_selector_action[data-v-f370520c]{padding-top:16px}@media (max-width: 768px){.fcrm_preview_toolbar[data-v-f370520c]{flex-direction:column;align-items:flex-start}.fcrm_preview_toolbar_actions[data-v-f370520c]{width:100%}} + +.echart-container-wrapper .chart-container[data-v-5d01edb6]{width:100%}.echart-container-wrapper .fcrm-chart-placeholder[data-v-5d01edb6]{min-height:100px;display:flex;align-items:center;justify-content:center;border:1px dashed var(--fc-light-bg);color:var(--fc-text-muted);font-size:12px} + +.promo_block--centered[data-v-822997cc]{background:#fff;padding:10px;text-align:center;display:block;overflow:hidden} + +.editor-container[data-v-0b3373b2]{display:flex;flex-direction:column;gap:10px;margin:0}.controls[data-v-0b3373b2]{display:flex;justify-content:space-between;align-items:center;margin-bottom:10px}.iframe-container[data-v-0b3373b2]{min-height:500px}body.fcrm-editor-fullscreen[data-v-2f9efcf8],html.fcrm-editor-fullscreen[data-v-2f9efcf8]{overflow:hidden!important;position:fixed!important;width:100%!important;height:100%!important}body.fcrm-editor-fullscreen .fcrm_fullscreen_active .fluentcrm_header.fc_visual_header[data-v-2f9efcf8]{box-shadow:0 2px 4px #00000014;position:relative!important;width:100%!important;max-width:100%!important;left:0!important;right:0!important}body.fcrm-editor-fullscreen .fcrm_fullscreen_active .fc_visual_body[data-v-2f9efcf8]{height:100%!important;overflow:auto!important}body.fcrm-editor-fullscreen .fcrm_fullscreen_active .iframe-container[data-v-2f9efcf8]{height:calc(100vh - 50px)!important;width:100%!important}body.fcrm-editor-fullscreen .fcrm_fullscreen_active .fc_visual_body.fcrm_has_fullscreen_bar .iframe-container[data-v-2f9efcf8]{margin-top:0!important;height:calc(100vh - 106px)!important}body.fcrm-editor-fullscreen .fcrm_fullscreen_active .iframe-container iframe[data-v-2f9efcf8]{border:none!important;height:100%!important;width:100%!important}.fc_template_sidebar[data-v-2f9efcf8]{display:flex;flex-direction:column}.fc_layout_actions[data-v-2f9efcf8]{display:flex;gap:8px;justify-content:space-between} + +.promo_block--centered[data-v-0bbb2bdb]{background:#fff;padding:20px;text-align:center;display:block;overflow:hidden} + +.fc_template_create_from_stratch.is-creating[data-v-0cc3b88c]{pointer-events:none;opacity:.7} + +.v-enter-active[data-v-5204b068],.v-leave-active[data-v-5204b068]{transition:all .3s ease-in;transform:scale(1)}.v-enter[data-v-5204b068],.v-leave-to[data-v-5204b068]{opacity:0;transition:all .3s ease-out;transform:scale(.8)} + +.fcrm-db-health-actions[data-v-e46b47fe]{display:flex;gap:8px}.fcrm-db-health-intro[data-v-e46b47fe]{margin:0 0 16px;color:var(--fc-secondary-text);font-size:13px;line-height:1.6}.fcrm-db-health-ok[data-v-e46b47fe]{display:flex;align-items:center;gap:8px;margin-bottom:16px;padding:10px 14px;border-radius:6px;background:var(--fc-success-bg);color:var(--fc-success);font-size:13px} + + +.fcrm-mcp-status-block p[data-v-77c6b92d]{margin-bottom:12px;color:var(--fc-secondary-text)}.fcrm-mcp-links[data-v-77c6b92d]{display:flex;gap:12px;flex-wrap:wrap}.fcrm-mcp-toolkit-action[data-v-77c6b92d]{width:auto!important;min-width:150px;max-width:220px}.fcrm-mcp-snippet[data-v-77c6b92d]{background:var(--fc-secondary-bg);border:1px solid var(--fc-primary-border);border-radius:6px;padding:12px;overflow-x:auto;font-size:13px;white-space:pre;margin-bottom:12px}.fcrm-divider[data-v-77c6b92d]{border-top:1px solid var(--fc-primary-border);margin:16px 0}.fcrm_help_text[data-v-77c6b92d]{color:var(--fc-secondary-text);margin-bottom:12px}.fcrm-mcp-creds[data-v-77c6b92d]{display:flex;gap:12px;margin-bottom:16px}.fcrm-mcp-creds-input[data-v-77c6b92d]{flex:1}.fcrm-mcp-creds-hint[data-v-77c6b92d]{margin-left:12px;color:var(--fc-secondary-text);font-size:13px}.fcrm-mcp-localdev[data-v-77c6b92d]{display:flex;align-items:center;gap:8px;margin:12px 0 16px;padding:8px 12px;background:var(--fc-secondary-bg);border:1px dashed var(--fc-primary-border);border-radius:6px}.fcrm-mcp-localdev .fcrm-mcp-creds-hint[data-v-77c6b92d]{margin-left:0}@media (max-width: 600px){.fcrm-mcp-creds[data-v-77c6b92d]{flex-direction:column}.fcrm-mcp-localdev[data-v-77c6b92d]{flex-direction:column;align-items:flex-start}} + +.fc_choice_menu_bar[data-v-90db346c]{position:relative;padding-right:40px}.fc_choice_menu_bar>.fc_clickable_icon[data-v-90db346c]{position:absolute;top:8px;right:8px;cursor:pointer;font-size:18px;line-height:1}.fc_choice_blocks[data-v-90db346c]{margin-top:6px}.fc_choice_row[data-v-90db346c]{align-items:stretch}.fc_choice_block[data-v-90db346c]{display:flex}.fc_choice_block .fc_choice_card[data-v-90db346c]{height:100%;display:flex;flex-direction:column;justify-content:flex-start} + +.echart-container-wrapper[data-v-62202f49]{position:relative;min-height:160px}.echart-container-wrapper .chart-container[data-v-62202f49]{width:100%}.echart-container-wrapper .fc-chart-placeholder[data-v-62202f49]{min-height:100px;display:flex;align-items:center;justify-content:center;border:1px dashed var(--fc-light-bg);color:var(--fc-text-muted);font-size:12px}.fcrm_notice .fcrm_notice_text a[data-v-527fc7c7]{text-decoration:underline}.fcrm_notice .fcrm_notice_text a.is-disabled[data-v-527fc7c7]{pointer-events:none;opacity:.6}.fcrm_notice .fcrm_notice_cta[data-v-527fc7c7]{white-space:nowrap}.fcrm_notice+.fcrm_notice[data-v-527fc7c7]{margin-top:8px} + +.items_actions[data-v-81e7743d]{display:flex;align-items:center;gap:8px}.items_inner[data-v-81e7743d]{display:flex;flex-wrap:wrap;align-items:center;gap:8px}.items_actions .el-alert[data-v-81e7743d]{width:160px;padding:4px 8px;margin:0;justify-content:center;align-items:center;gap:2px;border-radius:6px;background:var(--fc-secondary-bg)}.items_actions .el-alert[data-v-81e7743d] .el-alert__title{color:var(--fc-text-muted);font-feature-settings:"ss11" on,"liga" off,"calt" off;font-size:12px;font-style:normal;font-weight:500;line-height:16px} + diff --git a/wp-content/plugins/fluent-crm/assets/admin/global_admin.js b/wp-content/plugins/fluent-crm/assets/admin/global_admin.js new file mode 100644 index 0000000..61cd095 --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/admin/global_admin.js @@ -0,0 +1 @@ +!function(e){function t(t){if(t){e(".fcrm_menu_item, .fcrm_icon_menu").removeClass("fcrm_active");var c=[".fcrm_item_"+t,'.fcrm_menu_item[data-key="'+t+'"]','.fcrm_icon_menu[data-key="'+t+'"]'].join(", "),m=e(c);m.length&&m.addClass("fcrm_active")}}e(document).ready(function(){function c(){var t=e("#fc_server_timestamp");if(t.length){var c=parseInt(t.data("timestamp"));if(c){var m=new Date(1e3*c),n=m.getUTCFullYear()+"-"+("0"+(m.getUTCMonth()+1)).slice(-2)+"-"+("0"+m.getUTCDate()).slice(-2)+" "+("0"+(m.getUTCHours()%12||12)).slice(-2)+":"+("0"+m.getUTCMinutes()).slice(-2)+(m.getUTCHours()<12?" am":" pm");t.text("Server Time: "+n),t.data("timestamp",c+60)}}}if(e(".fcrm_submenu_items").on("click","a",function(){window.innerWidth>768&&e(this).closest(".fcrm_submenu_items").addClass("fcrm_force_hide")}),e(".fcrm_has_sub_items").on("mouseenter",function(){e(this).find(".fcrm_submenu_items").removeClass("fcrm_force_hide")}),e(".fcrm_menu").on("click",".fcrm_menu_item > .fcrm_menu_primary",function(c){t(e(this).closest(".fcrm_menu_item").data("key")),"SPAN"!==c.target.nodeName&&e(".fcrm_menu").removeClass("fcrm_menu_open")}),e(".fcrm_topbar_right").on("click",".fcrm_icon_menu",function(){t(e(this).data("key"))}),e("body").on("click",".components-color-palette__custom-color",function(e){e.preventDefault()}),c(),setInterval(c,6e4),!e(".fcrm_menu_item.fcrm_active, .fcrm_icon_menu.fcrm_active").length){var m=(window.location.hash||"").match(/^#\/([^\/?#]+)/),n=m?m[1]:"";"subscribers"===n&&(n="contacts"),t(n)}}),e(document).on("fluentcrm_route_change",function(e,c){t(c)})}(jQuery); diff --git a/wp-content/plugins/fluent-crm/assets/admin/index.php b/wp-content/plugins/fluent-crm/assets/admin/index.php new file mode 100644 index 0000000..6220032 --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/admin/index.php @@ -0,0 +1,2 @@ +({loading:!1,onboardingLogo:I,onboardingModalLogo:"data:image/svg+xml,%3c?xml%20version='1.0'%20encoding='UTF-8'%20standalone='no'?%3e%3c!DOCTYPE%20svg%20PUBLIC%20'-//W3C//DTD%20SVG%201.1//EN'%20'http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd'%3e%3csvg%20width='100%25'%20height='100%25'%20viewBox='0%200%20300%20300'%20version='1.1'%20xmlns='http://www.w3.org/2000/svg'%20xmlns:xlink='http://www.w3.org/1999/xlink'%20xml:space='preserve'%20xmlns:serif='http://www.serif.com/'%20style='fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;'%3e%3cpath%20d='M300,30c0,-16.557%20-13.443,-30%20-30,-30l-240,0c-16.557,0%20-30,13.443%20-30,30l0,240c0,16.557%2013.443,30%2030,30l240,0c16.557,0%2030,-13.443%2030,-30l0,-240Z'/%3e%3cg%3e%3cpath%20d='M250.955,71.122c0,-0%20-129.408,34.674%20-181.023,48.505c-12.32,3.301%20-20.887,14.465%20-20.887,27.22c-0,9.696%20-0,18.989%20-0,18.989c-0,0%20103.954,-27.854%20162.681,-43.59c23.139,-6.2%2039.229,-27.169%2039.229,-51.124c0,-0%200,-0%200,-0Z'%20style='fill:%23fff;'/%3e%3cpath%20d='M173.46,154.928c-0,0%20-68.092,18.246%20-103.528,27.741c-12.32,3.301%20-20.887,14.465%20-20.887,27.22c-0,9.696%20-0,18.989%20-0,18.989c-0,0%2048.721,-13.054%2085.185,-22.825c23.14,-6.2%2039.23,-27.169%2039.23,-51.124c-0,-0.001%20-0,-0.001%20-0,-0.001Z'%20style='fill:%23fff;'/%3e%3c/g%3e%3c/svg%3e",active_step:1,total_steps:4,config:window.fcAdmin,business_settings:window.fcAdmin.business_settings||{},list_segments:[{title:"",slug:""},{title:"",slug:""}],tag_segments:[{title:"",slug:""},{title:"",slug:""}],share_essentials:"no",install_fluentforms:"yes",install_fluentcart:"yes",show_essential:!1,email_address:"",rest_statuses:{put_request_status:"checking",delete_request_status:"checking"},show_essential_modal:!1}),computed:{onboardingProgress(){return Math.min(this.active_step,this.total_steps)/this.total_steps*100}},methods:{deleteLogo(){this.business_settings.logo=""},saveBusinessSettings(){this.loading=!0,this.$put("setting",{settings:{business_settings:this.business_settings}}).then(()=>{this.$notify.success({title:this.$t("Great!"),message:this.$t("Business Settings has been saved"),offset:19}),this.active_step=2}).catch(e=>{this.handleError(e)}).finally(()=>{this.loading=!1})},saveLists(){const e=this.list_segments.filter(e=>e.title&&e.slug);e.length?(this.loading=!0,this.$post("lists/bulk",{lists:e}).then(e=>{this.$notify.success({title:this.$t("Great!"),message:e.message,offset:19}),this.active_step=3}).catch(e=>{this.handleError(e)}).finally(()=>{this.loading=!1})):this.$notify.error({title:this.$t("Error"),message:this.$t("Please add at least one list"),offset:19})},slugifyList(e){this.list_segments[e].title&&(this.list_segments[e].slug=this.slugify(this.list_segments[e].title))},addListItem(){this.list_segments.push({title:"",slug:""})},deleteListItem(e){this.list_segments.splice(e,1)},saveTags(){const e=this.tag_segments.filter(e=>e.title&&e.slug);e.length?(this.loading=!0,this.$post("tags/bulk",{tags:e}).then(e=>{this.$notify.success({title:this.$t("Great!"),message:e.message,offset:19}),this.active_step=4}).catch(e=>{this.handleError(e)}).finally(()=>{this.loading=!1})):this.$notify.error({title:this.$t("Error"),message:this.$t("Please add at least one Tag"),offset:19})},slugifyTag(e){this.tag_segments[e].title&&(this.tag_segments[e].slug=this.slugify(this.tag_segments[e].title))},addTagItem(){this.tag_segments.push({title:"",slug:""})},deleteTagItem(e){this.tag_segments.splice(e,1)},handleDataShare(e){this.share_essentials=e,this.show_essential_modal=!1,this.complete(!0)},complete(e=!1){"yes"===this.share_essentials||e?(this.loading=!0,this.$post("setting/complete-installation",{install_fluentform:this.install_fluentforms,install_fluentcart:this.install_fluentcart,share_essentials:this.share_essentials,optin_email:this.email_address}).then(e=>{this.$notify.success({title:this.$t("Great!"),message:e.message,offset:19});try{"yes"!==window.localStorage.getItem("fcrm_onboarding_congratulations_shown")&&window.localStorage.setItem("fcrm_onboarding_congratulations_pending","yes")}catch(s){}setTimeout(()=>{window.location.href=this.config.dashboard_url},100)}).catch(e=>{console.log(e),this.handleError(e)}).finally(()=>{this.loading=!1})):this.show_essential_modal=!0},checkRestRequest(e,s){this[s]("setting/test").then(s=>{s.message?this.rest_statuses[e]=!0:this.rest_statuses[e]=!1}).catch(()=>{this.rest_statuses[e]=!1})}},mounted(){this.checkRestRequest("put_request_status","$put"),this.checkRestRequest("delete_request_status","$del"),jQuery(".update-nag,.notice, #wpbody-content > .updated, #wpbody-content > .error").remove()}},[["render",function(u,g,f,p,h,b){const v=e("Icons"),T=y,I=w,M=$,U=k,P=e("photo-widget"),L=C,E=x,F=S,ks=V;return s(),t("div",R,[h.rest_statuses.put_request_status&&h.rest_statuses.delete_request_status?n("",!0):(s(),t("div",G,[l("span",A,[a(v,{"icon-name":"el-icon-info"})]),l("h3",null,i(u.$t("Server Issue detected")),1),l("p",null,i(u.$t("server_does_not_support"))+" "+i(u.$t("using_gridpane")),1),l("a",B,i(u.$t("View GridPane Article")),1)])),l("div",z,[l("div",D,[l("div",N,[l("img",{src:h.onboardingLogo,alt:"FluentCRM"},null,8,W),l("div",j,[l("div",q,i(u.$t("Step %1$s of %2$s",h.active_step,h.total_steps)),1)])]),a(T,{percentage:b.onboardingProgress,"show-text":!1,"stroke-width":8,class:"fcrm_onboarding_header_progress",color:"var(--fc-deep-bg)"},null,8,["percentage"])]),l("div",Y,[1===h.active_step?(s(),t("div",Z,[l("div",H,[l("div",O,i(u.$t("Welcome to FluentCRM!"))+" 👋 ",1),l("div",X,i(u.$t("thankyou_for_using_fluentcrm")),1)]),l("div",J,[a(L,{"label-position":"top"},{default:o(()=>[a(M,{label:u.$t("Business Name")},{default:o(()=>[a(I,{modelValue:h.business_settings.business_name,"onUpdate:modelValue":g[0]||(g[0]=e=>h.business_settings.business_name=e),placeholder:u.$t("e.g., MyBusiness Inc."),class:"fcrm_setup-item_input"},null,8,["modelValue","placeholder"])],void 0,!0),_:1},8,["label"]),a(M,{label:u.$t("Logo")},{default:o(()=>[a(P,{modelValue:h.business_settings.logo,"onUpdate:modelValue":g[1]||(g[1]=e=>h.business_settings.logo=e)},{after:o(()=>[h.business_settings.logo?(s(),r(U,{key:0,class:"only-icon-btn small",plain:"",type:"danger",onClick:b.deleteLogo},{default:o(()=>[l("span",Q,[a(v,{"icon-name":"delete"})])],void 0,!0),_:1},8,["onClick"])):n("",!0)]),_:1},8,["modelValue"])],void 0,!0),_:1},8,["label"]),a(M,{label:u.$t("Business Full Address")},{default:o(()=>[a(I,{modelValue:h.business_settings.business_address,"onUpdate:modelValue":g[2]||(g[2]=e=>h.business_settings.business_address=e),class:"fcrm_setup-address_control",rows:3,type:"textarea",placeholder:u.$t("street, city, state, zip, country")},null,8,["modelValue","placeholder"])],void 0,!0),_:1},8,["label"])],void 0),_:1}),l("div",K,[l("p",null,[l("b",null,i(u.$t("Note:")),1),d(" "+i(u.$t("This setup is completely optional. It only takes two minutes, but you can skip and come back to customize these options in the Settings menu at any time.")),1)])])]),l("div",ee,[l("div",se,[l("a",{class:"el-button only-text",href:h.config.dashboard_url},i(u.$t("Skip All")),9,te)]),c((s(),r(U,{disabled:h.loading,onClick:g[3]||(g[3]=e=>b.saveBusinessSettings()),type:"primary"},{default:o(()=>[d(i(u.$t("Next")),1)],void 0),_:1},8,["disabled"])),[[ks,h.loading]])])])):2===h.active_step?(s(),t("div",le,[l("div",ae,[l("div",ie,i(u.$t("Contact Segment Lists")),1),l("div",ne,i(u.$t("setup_lists_to_segment_your_contacts")),1)]),l("div",oe,[l("table",null,[l("thead",null,[l("tr",null,[l("th",null,i(u.$t("Segment Name")),1),l("th",null,i(u.$t("Slug")),1),g[21]||(g[21]=l("th",null,null,-1))])]),l("tbody",null,[(s(!0),t(_,null,m(h.list_segments,(e,i)=>(s(),t("tr",{key:i},[l("td",null,[a(I,{onChange:e=>b.slugifyList(i),placeholder:u.$t("EG: User Type")+" "+(i+1),modelValue:e.title,"onUpdate:modelValue":s=>e.title=s},null,8,["onChange","placeholder","modelValue","onUpdate:modelValue"])]),l("td",null,[a(I,{modelValue:e.slug,"onUpdate:modelValue":s=>e.slug=s},null,8,["modelValue","onUpdate:modelValue"])]),l("td",re,[a(U,{onClick:e=>b.deleteListItem(i),size:"small",type:"danger",disabled:1==h.list_segments.length},{default:o(()=>[l("span",de,[a(v,{"icon-name":"delete"})])],void 0),_:1},8,["onClick","disabled"])])]))),128))])]),a(U,{size:"small",onClick:g[4]||(g[4]=e=>b.addListItem())},{default:o(()=>[l("span",ce,[a(v,{"icon-name":"plus"})]),d(" "+i(u.$t("Add More")),1)],void 0),_:1})]),l("div",_e,[l("div",me,[l("a",{class:"el-button only-text",href:h.config.dashboard_url},i(u.$t("Skip All")),9,ue)]),l("div",ge,[a(U,{onClick:g[5]||(g[5]=e=>h.active_step=1)},{default:o(()=>[d(i(u.$t("Go Back")),1)],void 0),_:1}),c((s(),r(U,{disabled:h.loading,onClick:g[6]||(g[6]=e=>b.saveLists()),type:"primary"},{default:o(()=>[d(i(u.$t("Next")),1)],void 0),_:1},8,["disabled"])),[[ks,h.loading]])])])])):3===h.active_step?(s(),t("div",fe,[l("div",pe,[l("div",he,i(u.$t("Contact Tags")),1),l("div",be,[d(i(u.$t("create_some_tags"))+" ",1),l("b",null,[d(i(u.$t("Example:"))+" ",1),l("em",null,i(u.$t("Product-X User, Product-Y User, Influencer etc")),1)])])]),l("div",ve,[l("table",null,[l("thead",null,[l("tr",null,[l("th",null,i(u.$t("Tag Name")),1),l("th",null,i(u.$t("Slug")),1),g[22]||(g[22]=l("th",null,null,-1))])]),l("tbody",null,[(s(!0),t(_,null,m(h.tag_segments,(e,i)=>(s(),t("tr",{key:i},[l("td",null,[a(I,{onChange:e=>b.slugifyTag(i),placeholder:u.$t("EG: Tag")+" "+(i+1),modelValue:e.title,"onUpdate:modelValue":s=>e.title=s},null,8,["onChange","placeholder","modelValue","onUpdate:modelValue"])]),l("td",null,[a(I,{modelValue:e.slug,"onUpdate:modelValue":s=>e.slug=s},null,8,["modelValue","onUpdate:modelValue"])]),l("td",ye,[a(U,{onClick:e=>b.deleteTagItem(i),size:"small",type:"danger",disabled:1==h.tag_segments.length},{default:o(()=>[l("span",$e,[a(v,{"icon-name":"delete"})])],void 0),_:1},8,["onClick","disabled"])])]))),128))])]),a(U,{size:"small",onClick:g[7]||(g[7]=e=>b.addTagItem())},{default:o(()=>[l("span",we,[a(v,{"icon-name":"plus"})]),d(" "+i(u.$t("Add More")),1)],void 0),_:1})]),l("div",ke,[l("div",Ce,[l("a",{class:"el-button only-text",href:h.config.dashboard_url},i(u.$t("Skip All")),9,Ve)]),l("div",xe,[a(U,{onClick:g[8]||(g[8]=e=>h.active_step=2)},{default:o(()=>[d(i(u.$t("Go Back")),1)],void 0),_:1}),c((s(),r(U,{disabled:h.loading,onClick:g[9]||(g[9]=e=>b.saveTags()),type:"primary"},{default:o(()=>[d(i(u.$t("Next")),1)],void 0),_:1},8,["disabled"])),[[ks,h.loading]])])])])):4===h.active_step?(s(),t("div",Se,[l("div",Te,[l("div",Ie,i(u.$t("Almost Done!"))+" 👍",1),h.config.has_fluentform?(s(),t("div",Ue,[d(i(u.$t("Thank you again for configuring your own CRM in WordPress.")),1),g[24]||(g[24]=l("br",null,null,-1)),d(" "+i(u.$t("Setup.Subscribe_Newsletter")),1)])):(s(),t("div",Me,[d(i(u.$t("install_fluentform"))+" ",1),g[23]||(g[23]=l("b",null,"Fluent Forms",-1)),d(" plugin. "+i(u.$t("fluentform_info")),1)]))]),l("div",Pe,[l("div",Le,[l("div",Ee,[l("div",Fe,[l("h3",Re,i(u.$t("All In One CRM Features")),1),l("span",Ge,i(u.$t("Already Installed")),1)]),l("p",Ae,i(u.$t("Get all the essential CRM features to manage your business")),1),l("div",Be,[l("span",ze,[a(v,{"icon-name":"star"})]),d(" "+i(u.$t("Essentials to run your business")),1)])]),h.config.has_fluentcart?n("",!0):(s(),t("div",De,[l("div",Ne,[l("h4",We,i(u.$t("Sell directly from your CRM")),1),a(E,{"true-value":"yes","false-value":"no",modelValue:h.install_fluentcart,"onUpdate:modelValue":g[10]||(g[10]=e=>h.install_fluentcart=e),"aria-label":u.$t("Setup.Install_FluentCart")},null,8,["modelValue","aria-label"])]),l("p",je,i(u.$t("Install FluentCart to sell products, subscriptions, and digital downloads without leaving your CRM.")),1),l("div",qe,[l("span",Ye,[a(v,{"icon-name":"rocket"})]),d(" "+i(u.$t("Start generating revenue instantly")),1)])])),h.config.has_fluentform?n("",!0):(s(),t("div",Ze,[l("div",He,[l("h4",Oe,i(u.$t("Capture leads effortlessly")),1),a(E,{"true-value":"yes","false-value":"no",modelValue:h.install_fluentforms,"onUpdate:modelValue":g[11]||(g[11]=e=>h.install_fluentforms=e),"aria-label":u.$t("Setup.Install_FluentForm")},null,8,["modelValue","aria-label"])]),l("p",Xe,i(u.$t("Install Fluent Forms Plugin for lead collection forms and automatically sync contacts to your CRM.")),1),l("div",Je,[l("span",Qe,[a(v,{"icon-name":"user"})]),d(" "+i(u.$t("Perfect for growing your audience")),1)])]))]),l("div",Ke,[l("div",es,[l("div",ss,i(u.$t("Help us to make FluentCRM better")),1),l("div",ts,[l("p",ls,[d(i(u.$t("Setup.FluentCrm.Share_Essentials.desc"))+" ",1),l("button",{type:"button",onClick:g[12]||(g[12]=e=>h.show_essential=!h.show_essential),class:"fcrm_what_we_collect_toggle is-link small","aria-expanded":h.show_essential?"true":"false","aria-controls":"fcrm-what-we-collect-details"},i(u.$t("What We Collect")),9,as)]),h.show_essential?(s(),t("div",is,[l("p",ns,i(u.$t("what_we_collect_infos")),1)])):n("",!0)])]),a(E,{"true-value":"yes","false-value":"no",modelValue:h.share_essentials,"onUpdate:modelValue":g[13]||(g[13]=e=>h.share_essentials=e)},{default:o(()=>[d(i(u.$t("Share Essentials")),1)],void 0),_:1},8,["modelValue"])]),l("div",os,[l("label",null,i(u.$t("Your Email Address")),1),l("div",rs,i(u.$t("Setup.Send_Marketing_tips")),1),a(I,{class:"fcrm_mt_12",placeholder:u.$t("Email Address for bi-monthly newsletter"),type:"email",modelValue:h.email_address,"onUpdate:modelValue":g[14]||(g[14]=e=>h.email_address=e)},null,8,["placeholder","modelValue"])])]),l("div",ds,[g[25]||(g[25]=l("div",{class:"actions-left"},null,-1)),l("div",cs,[a(U,{onClick:g[15]||(g[15]=e=>h.active_step=3)},{default:o(()=>[d(i(u.$t("Go Back")),1)],void 0),_:1}),c((s(),r(U,{disabled:h.loading,onClick:g[16]||(g[16]=e=>b.complete()),type:"primary"},{default:o(()=>[d(i(u.$t("Complete Installation")),1)],void 0),_:1},8,["disabled"])),[[ks,h.loading]])])])])):n("",!0)]),a(F,{"close-on-click-modal":!1,"show-close":!1,"append-to-body":!0,"modal-class":"fcrm_essential_modal",modelValue:h.show_essential_modal,"onUpdate:modelValue":g[20]||(g[20]=e=>h.show_essential_modal=e),width:"460px"},{footer:o(()=>[l("div",ws,[a(U,{onClick:g[18]||(g[18]=e=>b.handleDataShare("no"))},{default:o(()=>[d(i(u.$t("No thanks")),1)],void 0,!0),_:1}),a(U,{type:"primary",onClick:g[19]||(g[19]=e=>b.handleDataShare("yes"))},{default:o(()=>[d(i(u.$t("Yes, Count me in")),1)],void 0,!0),_:1})])]),default:o(()=>[l("div",_s,[l("button",{type:"button",class:"fcrm_essential_modal__close","aria-label":u.$t("Close"),onClick:g[17]||(g[17]=e=>h.show_essential_modal=!1)},[l("span",us,[a(v,{"icon-name":"close"})])],8,ms),l("img",{src:h.onboardingModalLogo,alt:"FluentCRM",class:"fcrm_essential_modal__logo"},null,8,gs),l("div",fs,i(u.$t("Let's build a better CRM")),1),l("div",ps,i(u.$t("Get_Improved_Help")),1),l("div",hs,[l("span",bs,[a(v,{"icon-name":"shield"})]),l("div",vs,[l("div",ys,i(u.$t("Zero personal data collected")),1),l("p",$s,i(u.$t("Your contacts, campaigns, and private details are completely secure. We only track basic software interactions.")),1)])])])],void 0),_:1},8,["modelValue"])])])}]]));ks.directive("loading",T.directive),ks.config.globalProperties.$notify=L,ks.config.globalProperties.$get=E.get,ks.config.globalProperties.$post=E.post,ks.config.globalProperties.$put=E.put,ks.config.globalProperties.$del=E.del,ks.mixin({methods:{$t:F.$t,slugify:F.slugify,convertToText:F.convertToText,handleError(e){if(!e)return;let s="string"==typeof e?e:e.message||F.convertToText(e)||"Something is wrong!";L({type:"error",title:"Error",message:s,dangerouslyUseHTMLString:!1})}}}),ks.mount("#fluentcrm_setup_wizard"); diff --git a/wp-content/plugins/fluent-crm/assets/admin/visual-editor/visual-editor.js b/wp-content/plugins/fluent-crm/assets/admin/visual-editor/visual-editor.js new file mode 100644 index 0000000..d2f4b1c --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/admin/visual-editor/visual-editor.js @@ -0,0 +1 @@ +import{aA as e,aB as t,aC as i,aD as a,ay as s,aw as n,aE as l,aF as o,aG as d,az as r,ax as c,k as u,U as _,E as m,g as h}from"../../vendor-element-plus.js?ver=3.1.8";import{W as p,X as g,a6 as f,ac as v,Z as y,$ as w,ab as V,a5 as b,a9 as k,Y as x,aa as C,J as T,az as B,aQ as $,a8 as E,aK as F}from"../../vendor.js?ver=3.1.8";import{_ as S}from"../../fc-bits-ui.js?ver=3.1.8";import{M as D}from"../../_MergeCodes.js?ver=3.1.8";import"../../input-popover-dropdown.js?ver=3.1.8";const I={class:"fc_iframe_wrap"};const R={style:{padding:"20px"}},M={style:{padding:"14px","text-align":"left"}},z={style:{"text-align":"center"}},L={style:{"text-align":"left"}};const P={key:0,class:"fc_visual_builder_wrap"},U={key:0,class:"fc_visual_wrap"},j={class:"fc_visual_preview_inline"},W={class:"fc_visual_intro"},N={key:0,class:"fc_visual_starter"},A={class:"fc_visual_blocks"},O=["onClick"],J=["src","alt"],Q={key:2,style:{position:"absolute",right:"2px"}},Y={class:"fc_builder_modal_wrap"},H={class:"fc_visual_modal"},q={key:0},G={class:"fc_editor_header"},K={class:"fc_head_left"},X=["src"],Z={class:"fc_head_right"},ee={class:"fc_visual_parent"},te=["src"],ie={key:1},ae={key:0,class:"fc_visual_starter"},se={class:"fc_visual_blocks"},ne=["onClick"],le=["src","alt"],oe={key:1,style:{background:"white"}},de=["src"];const re=S({name:"VisualEditor",props:["modelValue","campaign","extra_tags"],emits:["save","update:modelValue"],components:{IframeBuilder:S({name:"IframeBuilder",props:{preview_html:{type:String,default:()=>""},frame_height:{type:String,default:()=>"500px"}},data:()=>({loading_preview:!0}),methods:{loadFrame(){const e=this.$refs.fc_ifr.attachShadow({mode:"closed"}),t=document.createElement("div");t.innerHTML=this.preview_html,e.appendChild(t),this.loading_preview=!1,this.loading_preview=!1}},mounted(){this.loadFrame()}},[["render",function(e,t,i,a,s,n){return p(),g("div",I,[f(y("div",{ref:"fc_ifr",style:w([{width:"100%",height:"500px",overflow:"auto"},{height:i.frame_height}])},null,4),[[v,!s.loading_preview]])])}]]),MergeCodes:D,LoaderSkeleton:S({name:"LoaderSkeleton"},[["render",function(s,n,l,o,d,r){const c=e,u=i,_=a,m=t;return p(),g("div",R,[V(_,{gutter:30},{default:b(()=>[V(c,{span:5},{default:b(()=>[...n[0]||(n[0]=[k(".",-1)])],void 0,!0),_:1}),V(c,{span:9},{default:b(()=>[V(m,{animated:""},{template:b(()=>[V(u,{variant:"image",style:{height:"240px"}}),V(u,{variant:"h3",style:{width:"50%",margin:"20px auto"}}),y("div",M,[y("div",z,[V(u,{variant:"text",style:{width:"30%"}}),V(u,{variant:"text"}),V(u,{variant:"text"})]),V(_,{gutter:30},{default:b(()=>[V(c,{span:12},{default:b(()=>[V(u,{variant:"image",style:{height:"140px",margin:"20px 0"}})],void 0,!0),_:1}),V(c,{span:12},{default:b(()=>[V(u,{variant:"image",style:{height:"140px",margin:"20px 0"}})],void 0,!0),_:1})],void 0,!0),_:1}),y("div",L,[V(u,{variant:"text"}),V(u,{variant:"text"}),V(u,{variant:"text",style:{width:"30%"}})])])]),_:1})],void 0,!0),_:1}),V(c,{span:5},{default:b(()=>[...n[1]||(n[1]=[k(".",-1)])],void 0,!0),_:1}),V(c,{span:4},{default:b(()=>[V(m,{rows:12,animated:""}),V(m,{animated:"",rows:3})],void 0,!0),_:1}),V(c,{span:1},{default:b(()=>[V(m,{rows:6})],void 0,!0),_:1})],void 0),_:1})])}]]),DisplayCondition:S({name:"DisplayCondition",props:["existing_tag","editing_condition"],emits:["insertTag"],data:()=>({form:{selected_tags:[],display_type:"show_if_tag_exist"},loading:!1}),computed:{tags(){const e={};return this.each(this.appVars.available_tags,t=>{e[t.id]=t}),e}},methods:{fireCondition(){const e=[];if(this.each(this.form.selected_tags,t=>{this.tags[t]?e.push(this.tags[t].title):delete this.form.selected_tags[t]}),!this.form.selected_tags||!this.form.selected_tags.length)return void this.$notify.error(this.$t("Please select at least one tag"));let t=this.$t("Show if in tags:");"show_if_tag_not_exist"==this.form.display_type&&(t=this.$t("Show if not in tags:"));const i={type:"check_contact_tag",label:t,description:e.join(", "),before:'

[fc_vis_cond type=\''+this.form.display_type+"' values='"+this.form.selected_tags.join("|")+"']

",after:'

[/fc_vis_cond]

'};this.$emit("insertTag",i)},parseExitingCondition(){if(!this.editing_condition||!this.editing_condition.before)return;this.loading=!0;let e=this.editing_condition.before.match(/\[fc_vis_cond ([^\]]*)\]/)[1];e=e.split(" ");var t={};for(const i in e){const a=e[i].split("='");t[a[0]]=a[1].replace(/'/g,"")}t.type&&(this.form.display_type=t.type),t.values&&(this.form.selected_tags=t.values.split("|")),this.loading=!1}},mounted(){this.parseExitingCondition()}},[["render",function(e,t,i,a,_,m){const h=o,v=l,y=n,w=r,$=d,E=c,F=u,S=s;return p(),g("div",null,[f((p(),x(E,{"label-position":"top",model:_.form},{default:b(()=>[V(y,{label:e.$t("CONDITION TYPE")},{default:b(()=>[V(v,{modelValue:_.form.display_type,"onUpdate:modelValue":t[0]||(t[0]=e=>_.form.display_type=e)},{default:b(()=>[V(h,{value:"show_if_tag_exist"},{default:b(()=>[k(C(e.$t("Show IF in Selected Tag")),1)],void 0,!0),_:1}),V(h,{value:"show_if_tag_not_exist"},{default:b(()=>[k(C(e.$t("Show IF not in selected tag")),1)],void 0,!0),_:1})],void 0,!0),_:1},8,["modelValue"])],void 0,!0),_:1},8,["label"]),V(y,{label:e.$t("Select Targeted Tags")},{default:b(()=>[V($,{modelValue:_.form.selected_tags,"onUpdate:modelValue":t[1]||(t[1]=e=>_.form.selected_tags=e)},{default:b(()=>[(p(!0),g(T,null,B(m.tags,e=>(p(),x(w,{key:e.slug,value:e.id},{default:b(()=>[k(C(e.title),1)],void 0,!0),_:2},1032,["value"]))),128))],void 0,!0),_:1},8,["modelValue"])],void 0,!0),_:1},8,["label"])],void 0),_:1},8,["model"])),[[S,_.loading]]),V(F,{onClick:t[2]||(t[2]=e=>m.fireCondition()),type:"primary"},{default:b(()=>[k(C(e.$t("Apply Condition")),1)],void 0),_:1})])}]]),Loading:_},data(){var e;return{is_active:!1,is_loading:!1,editor_loaded:!1,visualFrameReady:!1,pendingVisualBuilderDesign:null,pendingVisualBuilderRefresh:!1,visualBuilderRefreshTimers:[],editor_type:"old",frame_url:"",isVerified:!1,target_origin:window.fcVisualVars.editor_domain,context:(null==(e=this.$route)?void 0:e.name)||"contact_email",inlineCallBack:null,predefinedTemplates:[{id:243318,name:"Blank",image:this.appVars.images_url+"/templates/blank.jpg"},{id:228634,name:"Standard",image:this.appVars.images_url+"/templates/standard.jpg"},{id:249301,name:"Sales",image:this.appVars.images_url+"/templates/sales.jpg"}],loadedtemplateId:243318,showDisplayCondition:!1,editing_condition:{}}},computed:{mergeTags(){const e={};let t=[...window.fcAdmin.globalSmartCodes,...window.fcAdmin.extendedSmartCodes];return this.extra_tags&&this.extra_tags.length&&(t=[...t,...this.extra_tags]),this.each(t,t=>{e[t.key]||(e[t.key]={name:t.title,mergeTags:{}});let i=1;this.each(t.shortcodes,(a,s)=>{e[t.key].mergeTags[i+"_"+s]={name:a,value:s},i++})}),e},is_inline(){return"edit_template"==this.context||"campaign"==this.context||"edit-sequence-email"==this.context}},methods:{cloneForPostMessage(e){const t=F(e);if(null==t||"object"!=typeof t)return t;if("function"==typeof structuredClone)try{return structuredClone(t)}catch(i){}try{return JSON.parse(JSON.stringify(t))}catch(i){return t}},loadingFrame(){document.body.classList.add("fc_locked"),this.is_loading=!0,this.is_active=!0;const e=document.getElementById("fc_visual_frame");this.isVerified||(e.contentWindow.postMessage({from:"fc_parent",action:"verify"},this.target_origin),this.isVerified=!0)},iframeEvent(e){const t=e.data;if("fc_editor"==t.type)if("editor_loaded"==t.action){if(document.body.classList.add("fc_locked_loaded"),this.visualFrameReady=!0,!this.hydrateVisualDesign(this.pendingVisualBuilderDesign||this.getInitialContent())){document.getElementById("fc_visual_frame").contentWindow.postMessage({from:"fc_parent",action:"load_template",template_id:this.loadedtemplateId,mergeTags:this.cloneForPostMessage(this.mergeTags)},this.target_origin)}setTimeout(()=>{this.is_loading=!1,this.editor_loaded=!0},1e3)}else if("save_design"==t.action)this.saveContent(t),this.$notify({message:this.$t("Saved"),position:"bottom-right",customClass:"fc_notify_z bottom_right",type:"success",duration:500});else if("save_close"==t.action)this.saveContent(t),document.body.classList.remove("fc_locked","fc_locked_loaded"),this.is_active=!1,"edit_funnel"==this.context&&(this.frame_url="",this.$nextTick(()=>{this.setFrameUrl()}));else if("image_selector"==t.action)this.initUploader();else if("open_merge_codes"==t.action)jQuery("#fc_merge_code_wrap button").trigger("click");else if("updated_design"==t.action)this.saveContent(t),this.inlineCallBack&&this.inlineCallBack(t);else if("display_condition"==t.action){if(!window.fcVisualVars.has_conditions)return void this.$notify.error(this.$t("Please update FluentCRM Pro first"));this.initDisplayCondition(t.items)}else console.log(t)},initDisplayCondition(e){this.editing_condition=e,this.showDisplayCondition=!0},loadTemplate(e,t){const i=document.getElementById("fc_visual_frame");this.loadedtemplateId=e,i.contentWindow.postMessage({from:"fc_parent",action:"load_template",template_id:e,mergeTags:this.cloneForPostMessage(this.mergeTags)},this.target_origin),this.editor_type="old",t?this.loadingFrame():setTimeout(()=>{this.is_loading=!1,this.editor_loaded=!0},1e3)},loadDesign(e){return!!e&&(this.campaign._visual_builder_design=e,this.pendingVisualBuilderDesign=e,this.pendingVisualBuilderRefresh=!0,this.editor_type="old",this.hydrateVisualDesign(e))},hydrateVisualDesign(e){if(!e)return!1;const t=document.getElementById("fc_visual_frame");return!!(t&&t.contentWindow&&this.visualFrameReady)&&(t.contentWindow.postMessage({from:"fc_parent",action:"load_design",data:this.cloneForPostMessage(e),mergeTags:this.cloneForPostMessage(this.mergeTags)},this.target_origin),this.pendingVisualBuilderDesign=null,this.pendingVisualBuilderRefresh&&this.scheduleVisualContentRefresh("template_import_preview"),!0)},scheduleVisualContentRefresh(e="update_only"){const t=document.getElementById("fc_visual_frame");return!!(t&&t.contentWindow&&this.visualFrameReady)&&(this.pendingVisualBuilderRefresh=!1,this.clearVisualBuilderRefreshTimers(),[1200,3e3,6e3].forEach(t=>{const i=setTimeout(()=>{if(!this.visualFrameReady)return;const t=document.getElementById("fc_visual_frame");t&&t.contentWindow&&t.contentWindow.postMessage({from:"fc_parent",action:"fire_save_data",reference:e},this.target_origin)},t);this.visualBuilderRefreshTimers.push(i)}),!0)},clearVisualBuilderRefreshTimers(){this.visualBuilderRefreshTimers.forEach(e=>{clearTimeout(e)}),this.visualBuilderRefreshTimers=[]},saveContent(e){"template_import_preview"!==e.reference&&(this.campaign._visual_builder_design=e.design),this.$emit("update:modelValue",e.html),["update_only","template_import_preview"].includes(e.reference)||this.$nextTick(()=>{this.$emit("save")}),this.editor_type="old"},getInitialContent(){return this.campaign._visual_builder_design||null},initUploader(){wp.media.editor.remove("fc_launch_editor_button");const e=wp.media.editor.send.attachment,t=this;return wp.media.editor.send.attachment=function(i,a){const s=document.getElementById("fc_visual_frame");let n={url:a.url,width:a.width,height:a.height,altText:a.alt,alternateText:a.alt};"full"!=i.size&&a.sizes&&a.sizes[i.size]&&(a.sizes[i.size].width<1e3&&(i.size="large"),a.sizes[i.size]&&a.sizes[i.size].width>1e3&&(n=a.sizes[i.size])),s.contentWindow.postMessage({from:"fc_parent",action:"add_media",media:n},t.target_origin),wp.media.editor.send.attachment=e},wp.media.editor.open("fc_launch_editor_button",{frame:"post",state:"insert",title:this.$t("Select Image for Your Email Body"),multiple:!1}),!1},setFrameUrl(){if(!window.fcVisualVars)return"";const e=new URL(window.fcVisualVars.url);this.each(window.fcVisualVars.params,(t,i)=>{e.searchParams.set(i,t)}),e.searchParams.set("context",this.context),e.searchParams.set("version",this.appVars.app_version),this.appVars.disable_ai&&e.searchParams.set("disable_ai","yes"),this.frame_url=e.href},listenBus(e){if(!this.editor_loaded)return void this.$notify.error(this.$t("Editor is loading. Please wait"));e.callback?this.inlineCallBack=e.callback:this.inlineCallBack=null;document.getElementById("fc_visual_frame").contentWindow.postMessage({from:"fc_parent",action:"fire_save_data",reference:e.reference},this.target_origin)},fireConditionTag(e){document.getElementById("fc_visual_frame").contentWindow.postMessage({from:"fc_parent",action:"apply_condition",item:this.cloneForPostMessage(e)},this.target_origin),this.showDisplayCondition=!1}},mounted(){this.setFrameUrl(),this.campaign._visual_builder_design?this.editor_type="old":!this.modelValue||-1!==this.modelValue.indexOf(this.$t("Start Writing Here"))||jQuery(this.modelValue).text().trim().length<20?this.editor_type="new":this.editor_type="existing_content",window.addEventListener("message",this.iframeEvent,!1),window.wpActiveEditor||(window.wpActiveEditor=null),this.$bus.on("getVisualData",this.listenBus)},beforeUnmount(){window.removeEventListener("message",this.iframeEvent,!1),document.body.classList.remove("fc_locked","fc_locked_loaded"),this.$bus.off("getVisualData",this.listenBus),this.clearVisualBuilderRefreshTimers()}},[["render",function(e,t,i,a,s,n){const l=u,o=$("Loading"),d=m,r=$("iframe-builder"),c=$("loader-skeleton"),_=$("merge-codes"),F=$("display-condition"),S=h;return p(),g("div",null,[n.is_inline?(p(),g("div",ie,["new"==s.editor_type?(p(),g("div",ae,[y("h1",null,C(e.$t("Select a starter design to build your email")),1),y("div",se,[(p(!0),g(T,null,B(s.predefinedTemplates,e=>(p(),g("div",{onClick:t=>n.loadTemplate(e.id),key:e.id,class:"fc_visual_block"},[y("img",{src:e.image,alt:e.name},null,8,le),y("h3",null,C(e.name),1)],8,ne))),128))])])):s.editor_loaded?E("",!0):(p(),g("div",oe,[V(c)])),s.frame_url?(p(),g("iframe",{key:2,id:"fc_visual_frame",style:w([{visibility:s.editor_loaded&&"new"!=s.editor_type?"visible":"hidden"},{width:"100%","min-height":"calc(100vh - 110px)"}]),src:s.frame_url+"&inline=yes"},null,12,de)):E("",!0)])):(p(),g("div",P,[s.is_active?E("",!0):(p(),g("div",U,[y("div",j,[y("div",W,[y("h3",null,C(e.$t("Visually design your email with Drag & Drop Builder")),1),"new"==s.editor_type?(p(),g("div",N,[y("h1",null,C(e.$t("Select a starter design to build your email")),1),y("div",A,[(p(!0),g(T,null,B(s.predefinedTemplates,e=>(p(),g("div",{onClick:t=>n.loadTemplate(e.id,!0),key:e.id,class:"fc_visual_block"},[y("img",{src:e.image,alt:e.name},null,8,J),y("h3",null,C(e.name),1)],8,O))),128))])])):(p(),x(l,{key:1,id:"fc_launch_editor_button",type:"primary",onClick:t[0]||(t[0]=e=>n.loadingFrame())},{default:b(()=>[k(C(e.$t("Launch Visual Editor")),1)],void 0),_:1})),s.editor_loaded?E("",!0):(p(),g("p",Q,[V(d,{class:"is-loading"},{default:b(()=>[V(o)],void 0),_:1})]))]),s.is_active?E("",!0):(p(),x(r,{key:0,frame_height:"800px",preview_html:i.modelValue},null,8,["preview_html"]))])])),f(y("div",Y,[y("div",H,[s.editor_loaded?E("",!0):(p(),g("div",q,[y("div",G,[y("div",K,[y("img",{src:e.appVars.images_url+"/fluentcrm-logo.svg"},null,8,X),t[2]||(t[2]=y("span",null,"Pro",-1))]),y("div",Z,[V(l,{type:"primary",size:"small",disabled:!0},{default:b(()=>[k(C(e.$t("Save")),1)],void 0),_:1}),V(l,{type:"danger",size:"small",disabled:!0},{default:b(()=>[k(C(e.$t("Save & close")),1)],void 0),_:1})])]),V(c)])),y("div",ee,[V(_,{id:"fc_merge_code_wrap",extra_tags:i.extra_tags},null,8,["extra_tags"])]),s.frame_url?(p(),g("iframe",{key:1,style:w([{visibility:s.editor_loaded?"visible":"hidden"},{width:"100%"}]),id:"fc_visual_frame",src:s.frame_url},null,12,te)):E("",!0)])],512),[[v,s.is_active]])])),s.showDisplayCondition?(p(),x(S,{key:2,class:"fc_force_modal",title:"Select your Condition",modelValue:s.showDisplayCondition,"onUpdate:modelValue":t[1]||(t[1]=e=>s.showDisplayCondition=e),"append-to-body":!0,"modal-append-to-body":!0,width:"30%"},{default:b(()=>[V(F,{editing_condition:s.editing_condition,onInsertTag:n.fireConditionTag},null,8,["editing_condition","onInsertTag"])],void 0),_:1},8,["modelValue"])):E("",!0)])}]]);!function e(){window.FLUENTCRM&&window.FLUENTCRM.app?window.FLUENTCRM.app.component("VisualEmailBuilder",re):setTimeout(e,100)}(); diff --git a/wp-content/plugins/fluent-crm/assets/clipboard.js b/wp-content/plugins/fluent-crm/assets/clipboard.js new file mode 100644 index 0000000..2a29f03 --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/clipboard.js @@ -0,0 +1 @@ +function e(e){if(!e)return!1;try{const o=document.createElement("input");return o.value=e,document.body.appendChild(o),o.select(),o.setSelectionRange(0,99999),document.execCommand("copy"),document.body.removeChild(o),!0}catch(o){return console.error("Failed to copy to clipboard:",o),!1}}export{e as c}; diff --git a/wp-content/plugins/fluent-crm/assets/crm_managers.png b/wp-content/plugins/fluent-crm/assets/crm_managers.png new file mode 100644 index 0000000..4d98f85 Binary files /dev/null and b/wp-content/plugins/fluent-crm/assets/crm_managers.png differ diff --git a/wp-content/plugins/fluent-crm/assets/data_config.js b/wp-content/plugins/fluent-crm/assets/data_config.js new file mode 100644 index 0000000..024bf86 --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/data_config.js @@ -0,0 +1 @@ +function e(e){e=window.fcAdmin.trans[e]||e;const t=Array.prototype.slice.call(arguments,1);if(0===t.length)return e;let a=0;return e=e.replace(/%(\d*)s|%d/g,(e,i)=>{if(i){const a=parseInt(i,10)-1;return a{const e=new Date,t=new Date;return t.setTime(t.getTime()-6048e5),[t,e]}},{text:e("This Month"),value:()=>{const e=new Date;return[new Date(e.getFullYear(),e.getMonth(),1),e]}},{text:e("Last month"),value:()=>{const e=new Date,t=new Date;return t.setTime(t.getTime()-2592e6),[t,e]}},{text:e("Last 3 months"),value:()=>{const e=new Date,t=new Date;return t.setTime(t.getTime()-7776e6),[t,e]}},{text:e("This quarter"),value:()=>{const e=new Date,t=Math.floor(e.getMonth()/3);return[new Date(e.getFullYear(),3*t,1),e]}},{text:e("Last quarter"),value:()=>{const e=new Date,t=Math.floor(e.getMonth()/3),a=new Date(e.getFullYear(),3*t-3,1),i=new Date(a.getFullYear(),a.getMonth()+3,0);return[a,i]}},{text:e("Year to Date"),value:()=>{const e=new Date;return[new Date((new Date).getFullYear(),0,1),e]}},{text:e("Last year"),value:()=>{const e=(new Date).getFullYear()-1;return[new Date(e,0,1),new Date(e,11,31)]}}];function o(){const e=new Date,t=new Date;t.setTime(t.getTime()-6048e5);const a=e=>`${e.getFullYear()}-${String(e.getMonth()+1).padStart(2,"0")}-${String(e.getDate()).padStart(2,"0")}`;return[a(t),a(e)]}const l=/^\d{4}-\d{2}-\d{2}$/;function u(e){if(!e||!Array.isArray(e)||e.length<2)return null;const t=e[0],a=e[1],i="string"==typeof t?t:t&&t.toISOString?t.toISOString().slice(0,10):null,n="string"==typeof a?a:a&&a.toISOString?a.toISOString().slice(0,10):null;return i&&n?{startStr:i,endStr:n}:null}function c(e,t){if(!e||"string"!=typeof e)return e;if(!l.test(e.trim()))return e;const a="undefined"!=typeof window&&window.dayjs;if(!a)return e;const i=u(t),n=a(e);if(!n.isValid())return e;if(!i)return n.format("MMM DD");const s=a(i.startStr);if(a(i.endStr).diff(s,"day")<=7)return n.format("dddd");const r=i.startStr.slice(0,4)===i.endStr.slice(0,4)&&function(e,t){const a="undefined"!=typeof window&&window.dayjs;if(!a)return!1;const i=a(e).year();return String(i)===t.startStr.slice(0,4)}(e,i);return r?n.format("MMM DD"):n.format("MMM DD, YYYY")}function d(e,t){if(!Array.isArray(e)||0===e.length)return e;const a="undefined"!=typeof window&&window.dayjs,i=u(t);if(!a||!i)return a?e.map(e=>{if(!e||!l.test(String(e).trim()))return e;const t=a(e);return t.isValid()?t.format("MMM DD"):e}):e;const n=a(i.startStr),s=a(i.endStr).diff(n,"day"),r=s<=7,o=s>7&&e.every(e=>l.test(String(e).trim())&&a(e).year().toString()===i.startStr.slice(0,4));return e.map(e=>{if(!e||"string"!=typeof e)return e;if(!l.test(e.trim()))return e;const t=a(e);return t.isValid()?r?t.format("dddd"):o?t.format("MMM DD"):t.format("MMM DD, YYYY"):e})}const p={disabledDate:e=>e.getTime()<=Date.now()-864e5,shortcuts:[{text:e("After 1 Hour"),value:()=>{const e=new Date;return e.setTime(e.getTime()+36e5),e}},{text:e("Tomorrow"),value:()=>{const e=new Date;return e.setTime(e.getTime()+864e5),e}},{text:e("After 2 Days"),value:()=>{const e=new Date;return e.setTime(e.getTime()+1728e5),e}},{text:e("After 1 Week"),value:()=>{const e=new Date;return e.setTime(e.getTime()+6048e5),e}}]},v={disabledDate:e=>e.getTime()<=Date.now()-864e5},f=function(e){if(!e)return"";let t=e;return t=t.indexOf("://")>-1?t.split("/")[2]:t.split("/")[0],t=t.split(":")[0],t},m=function(e){return[e.address_line_1,e.address_line_2,e.city,e.state,e.postal_code,e.country].filter(Boolean).join(", ")};export{e as $,i as a,d as b,v as c,r as d,n as e,c as f,o as g,p as h,m as i,f as j,s as k,a as l,t as s}; diff --git a/wp-content/plugins/fluent-crm/assets/fc-bits-ui.js b/wp-content/plugins/fluent-crm/assets/fc-bits-ui.js new file mode 100644 index 0000000..98eb284 --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/fc-bits-ui.js @@ -0,0 +1 @@ +var t=Object.defineProperty,C=(C,e,l)=>((C,e,l)=>e in C?t(C,e,{enumerable:!0,configurable:!0,writable:!0,value:l}):C[e]=l)(C,"symbol"!=typeof e?e+"":e,l);import{a as e,v as l,m as i,d as o,f as n,E as r,b as h}from"./vendor-element-plus.js?ver=3.1.8";import{aQ as s,W as a,X as w,Z as d,a0 as c,Y as g,a5 as H,ab as V,bB as v}from"./vendor.js?ver=3.1.8";const L=(t,C)=>{const e=t.__vccOpts||t;for(const[l,i]of C)e[l]=i;return e};let p=0;const M={key:21,xmlns:"http://www.w3.org/2000/svg",width:"14",height:"15",viewBox:"0 0 12 13",fill:"none"},m={key:22,xmlns:"http://www.w3.org/2000/svg",width:"14",height:"15",viewBox:"0 0 13 12",fill:"none"},f={key:23,xmlns:"http://www.w3.org/2000/svg",width:"14",height:"15",viewBox:"0 0 15 15",fill:"none"},u={key:24,xmlns:"http://www.w3.org/2000/svg",width:"11",height:"11",viewBox:"0 0 11 11",fill:"none"},Z={key:25,xmlns:"http://www.w3.org/2000/svg",width:"14",height:"15",viewBox:"0 0 14 15",fill:"none"},x={key:26,xmlns:"http://www.w3.org/2000/svg",width:"14",height:"15",viewBox:"0 0 17 12",fill:"none"},k={key:27,xmlns:"http://www.w3.org/2000/svg",width:"20",height:"20",viewBox:"0 0 20 20",fill:"none"},y={key:28,xmlns:"http://www.w3.org/2000/svg",width:"20",height:"20",viewBox:"0 0 20 20",fill:"none"},q={key:29,xmlns:"http://www.w3.org/2000/svg",width:"20",height:"20",viewBox:"0 0 20 20",fill:"none"},N={key:30,xmlns:"http://www.w3.org/2000/svg",width:"40",height:"40",viewBox:"0 0 40 40",fill:"none"},B={key:31,xmlns:"http://www.w3.org/2000/svg",width:"40",height:"40",viewBox:"0 0 40 40",fill:"none"},z={key:32,xmlns:"http://www.w3.org/2000/svg",width:"16",height:"16",viewBox:"0 0 20 20",fill:"none"},E={key:33,width:"20",height:"20",viewBox:"0 0 20 20",fill:"none",xmlns:"http://www.w3.org/2000/svg"},b={key:34,width:"20",height:"20",viewBox:"0 0 20 20",fill:"none",xmlns:"http://www.w3.org/2000/svg"},D={key:35,width:"20",height:"20",viewBox:"0 0 20 20",fill:"none",xmlns:"http://www.w3.org/2000/svg"},S={key:36,width:"20",height:"20",viewBox:"0 0 20 20",fill:"none",xmlns:"http://www.w3.org/2000/svg"},_={key:37,width:"24",height:"24",viewBox:"0 0 24 24",fill:"none",xmlns:"http://www.w3.org/2000/svg"},F={key:38,width:"20",height:"20",viewBox:"0 0 20 20",fill:"none",xmlns:"http://www.w3.org/2000/svg"},I={key:39,width:"20",height:"20",viewBox:"0 0 20 20",fill:"none",xmlns:"http://www.w3.org/2000/svg"},A={key:40,width:"20",height:"20",viewBox:"0 0 20 20",fill:"none",xmlns:"http://www.w3.org/2000/svg"},O={key:41,width:"20",height:"20",viewBox:"0 0 20 20",fill:"none",xmlns:"http://www.w3.org/2000/svg"},T={key:42,width:"20",height:"20",viewBox:"0 0 20 20",fill:"none",xmlns:"http://www.w3.org/2000/svg"},j={key:43,width:"20",height:"20",viewBox:"0 0 20 20",fill:"none",xmlns:"http://www.w3.org/2000/svg"},G={key:44,class:"fcrm_empty_icon",xmlns:"http://www.w3.org/2000/svg",width:"83",height:"67",viewBox:"0 0 83 67",fill:"none"},$=["fill"],U=["fill"],Y=["filter"],P=["fill"],R=["id"],K=["id"],W=["id"],Q=["id"],X={key:45,xmlns:"http://www.w3.org/2000/svg",width:"132",height:"116",viewBox:"0 0 132 116",fill:"none"},J={key:46,width:"18",height:"18",viewBox:"0 0 18 18",fill:"none",xmlns:"http://www.w3.org/2000/svg"},tt={key:47,xmlns:"http://www.w3.org/2000/svg",width:"125",height:"116",viewBox:"0 0 125 116",fill:"none"},Ct={key:48,xmlns:"http://www.w3.org/2000/svg",width:"125",height:"125",viewBox:"0 0 20 20",fill:"none"},et={key:49,width:"20",height:"20",viewBox:"0 0 20 20",fill:"none",xmlns:"http://www.w3.org/2000/svg"},lt={key:50,width:"20",height:"20",viewBox:"0 0 20 20",fill:"none",xmlns:"http://www.w3.org/2000/svg"},it={key:51,width:"20",height:"20",viewBox:"0 0 20 20",fill:"none",xmlns:"http://www.w3.org/2000/svg"},ot={key:52,xmlns:"http://www.w3.org/2000/svg",width:"20",height:"20",viewBox:"0 0 14 14",fill:"none"},nt={key:53,xmlns:"http://www.w3.org/2000/svg",width:"20",height:"20",viewBox:"0 0 20 20",fill:"none"},rt={key:54,xmlns:"http://www.w3.org/2000/svg",width:"20",height:"20",viewBox:"0 -4 15 20",fill:"none"},ht={key:55,width:"20",height:"20",viewBox:"0 0 20 20",fill:"none",xmlns:"http://www.w3.org/2000/svg"},st={key:56,width:"20",height:"20",viewBox:"0 0 20 20",fill:"none",xmlns:"http://www.w3.org/2000/svg"},at={key:57,width:"20",height:"20",viewBox:"0 0 20 20",fill:"none",xmlns:"http://www.w3.org/2000/svg"},wt={key:58,width:"20",height:"20",viewBox:"0 0 20 20",fill:"none",xmlns:"http://www.w3.org/2000/svg"},dt={key:59,width:"20",height:"20",viewBox:"0 0 20 20",fill:"none",xmlns:"http://www.w3.org/2000/svg"},ct={key:60,width:"24",height:"24",viewBox:"0 0 24 24",fill:"none",xmlns:"http://www.w3.org/2000/svg"},gt={key:61,width:"20",height:"20",viewBox:"0 0 20 20",fill:"none",xmlns:"http://www.w3.org/2000/svg"},Ht={key:62,xmlns:"http://www.w3.org/2000/svg",width:"40",height:"40",viewBox:"0 0 40 40",fill:"none"},Vt={key:63,xmlns:"http://www.w3.org/2000/svg",width:"20",height:"20",viewBox:"0 0 20 20",fill:"none"},vt={key:64,width:"20",height:"20",viewBox:"0 0 20 20",fill:"none",xmlns:"http://www.w3.org/2000/svg"},Lt={key:65,width:"20",height:"20",viewBox:"0 0 20 20",fill:"none",xmlns:"http://www.w3.org/2000/svg"},pt={key:66,width:"20",height:"20",viewBox:"0 0 20 20",fill:"none",xmlns:"http://www.w3.org/2000/svg"},Mt={key:67,width:"20",height:"20",viewBox:"0 0 20 20",fill:"none",xmlns:"http://www.w3.org/2000/svg"},mt={key:68,width:"20",height:"20",viewBox:"0 0 20 20",fill:"none",xmlns:"http://www.w3.org/2000/svg"},ft={key:69,width:"20",height:"20",viewBox:"0 0 20 20",fill:"none",xmlns:"http://www.w3.org/2000/svg"},ut={key:70,width:"20",height:"20",viewBox:"0 0 20 20",fill:"none",xmlns:"http://www.w3.org/2000/svg"},Zt={key:71,width:"20",height:"20",viewBox:"0 0 20 20",fill:"none",xmlns:"http://www.w3.org/2000/svg"},xt={key:72,width:"20",height:"20",viewBox:"0 0 20 20",fill:"none",xmlns:"http://www.w3.org/2000/svg"},kt={key:73,width:"20",height:"20",viewBox:"0 0 20 20",fill:"none",xmlns:"http://www.w3.org/2000/svg"},yt={key:74,width:"20",height:"20",viewBox:"0 0 20 20",fill:"none",xmlns:"http://www.w3.org/2000/svg"},qt={key:75,width:"24",height:"24",viewBox:"0 0 24 24",fill:"none",xmlns:"http://www.w3.org/2000/svg"},Nt={key:76,width:"20",height:"20",viewBox:"0 0 20 20",fill:"none",xmlns:"http://www.w3.org/2000/svg"},Bt={key:77,width:"20",height:"20",viewBox:"0 0 20 20",fill:"none",xmlns:"http://www.w3.org/2000/svg"},zt={key:78,width:"20",height:"20",viewBox:"0 0 20 20",fill:"none",xmlns:"http://www.w3.org/2000/svg"},Et={key:79,width:"20",height:"20",viewBox:"0 0 20 20",fill:"none",xmlns:"http://www.w3.org/2000/svg"},bt={key:80,width:"16",height:"16",viewBox:"0 0 16 16",fill:"none",xmlns:"http://www.w3.org/2000/svg"},Dt={key:81,width:"16",height:"16",viewBox:"0 0 16 16",fill:"none",xmlns:"http://www.w3.org/2000/svg"},St={key:82,width:"16",height:"16",viewBox:"0 0 16 16",fill:"none",xmlns:"http://www.w3.org/2000/svg"},_t={key:83,width:"16",height:"16",viewBox:"0 0 16 16",fill:"none",xmlns:"http://www.w3.org/2000/svg"},Ft={key:84,xmlns:"http://www.w3.org/2000/svg",width:"16",height:"16",viewBox:"0 0 16 16",fill:"none"},It={key:85,width:"20",height:"20",viewBox:"0 0 20 20",fill:"none",xmlns:"http://www.w3.org/2000/svg"},At={key:86,width:"20",height:"20",viewBox:"0 0 20 20",fill:"none",xmlns:"http://www.w3.org/2000/svg"},Ot={key:87,xmlns:"http://www.w3.org/2000/svg",width:"8",height:"5",viewBox:"0 0 8 5",fill:"none"},Tt={key:88,width:"16",height:"16",viewBox:"0 0 16 16",fill:"none",xmlns:"http://www.w3.org/2000/svg"},jt={key:89,xmlns:"http://www.w3.org/2000/svg",width:"8",height:"5",viewBox:"0 0 8 5",fill:"none"},Gt={key:90,width:"20",height:"20",viewBox:"0 0 20 20",fill:"none",xmlns:"http://www.w3.org/2000/svg"},$t={key:91,width:"20",height:"20",viewBox:"0 0 20 20",fill:"none",xmlns:"http://www.w3.org/2000/svg"},Ut={key:92,width:"20",height:"20",viewBox:"0 0 20 20",fill:"none",xmlns:"http://www.w3.org/2000/svg"},Yt={key:93,width:"24",height:"24",viewBox:"0 0 24 24",fill:"none",xmlns:"http://www.w3.org/2000/svg"},Pt={key:94,xmlns:"http://www.w3.org/2000/svg",width:"20",height:"20",viewBox:"0 0 20 20",fill:"none"},Rt={key:95,width:"20",height:"20",viewBox:"0 0 20 20",fill:"none",xmlns:"http://www.w3.org/2000/svg"},Kt={key:96,width:"20",height:"20",viewBox:"0 0 20 20",fill:"none",xmlns:"http://www.w3.org/2000/svg"},Wt={key:97,width:"20",height:"20",viewBox:"0 0 20 20",fill:"none",xmlns:"http://www.w3.org/2000/svg"},Qt={key:98,width:"16",height:"16",viewBox:"0 0 16 16",fill:"none",xmlns:"http://www.w3.org/2000/svg"},Xt={key:99,width:"16",height:"16",viewBox:"0 0 16 16",fill:"none",xmlns:"http://www.w3.org/2000/svg"},Jt={key:100,width:"24",height:"24",viewBox:"0 0 24 24",fill:"none",xmlns:"http://www.w3.org/2000/svg"},tC={key:101,width:"20",height:"20",viewBox:"0 0 20 20",fill:"none",xmlns:"http://www.w3.org/2000/svg"},CC={key:102,width:"20",height:"20",viewBox:"0 0 20 20",fill:"none",xmlns:"http://www.w3.org/2000/svg"},eC={key:103,xmlns:"http://www.w3.org/2000/svg",width:"20",height:"20",viewBox:"0 0 20 20",fill:"none"},lC={key:104,xmlns:"http://www.w3.org/2000/svg",width:"13",height:"13",viewBox:"0 0 13 13",fill:"none"},iC={key:105,width:"20",height:"20",viewBox:"0 0 20 20",fill:"none",xmlns:"http://www.w3.org/2000/svg"},oC={key:106,xmlns:"http://www.w3.org/2000/svg",width:"20",height:"20",viewBox:"0 0 20 20",fill:"none"},nC={key:107,xmlns:"http://www.w3.org/2000/svg",width:"20",height:"20",viewBox:"0 0 20 20",fill:"none"},rC={key:108,width:"20",height:"20",viewBox:"0 0 20 20",fill:"none",xmlns:"http://www.w3.org/2000/svg"},hC={key:109,xmlns:"http://www.w3.org/2000/svg",width:"16",height:"16",viewBox:"0 0 16 16",fill:"none"},sC={key:111,width:"16",height:"16",viewBox:"0 0 16 16",fill:"none",xmlns:"http://www.w3.org/2000/svg"},aC={key:112,width:"20",height:"20",viewBox:"0 0 20 20",fill:"none",xmlns:"http://www.w3.org/2000/svg"},wC={key:113,xmlns:"http://www.w3.org/2000/svg",width:"20",height:"20",viewBox:"0 0 20 20",fill:"none"},dC={key:114,width:"20",height:"20",viewBox:"0 0 20 20",fill:"none",xmlns:"http://www.w3.org/2000/svg"},cC={key:115,width:"20",height:"20",viewBox:"0 0 20 20",fill:"none",xmlns:"http://www.w3.org/2000/svg"},gC={key:116,width:"20",height:"20",viewBox:"0 0 20 20",fill:"none",xmlns:"http://www.w3.org/2000/svg"},HC={key:117,width:"20",height:"20",viewBox:"0 0 20 20",fill:"none",xmlns:"http://www.w3.org/2000/svg"},VC={key:118,width:"20",height:"20",viewBox:"0 0 20 20",fill:"none",xmlns:"http://www.w3.org/2000/svg"},vC={key:119,width:"20",height:"20",viewBox:"0 0 20 20",fill:"none",xmlns:"http://www.w3.org/2000/svg"},LC={key:120,width:"20",height:"20",viewBox:"0 0 20 20",fill:"none",xmlns:"http://www.w3.org/2000/svg"},pC={key:121,width:"20",height:"20",viewBox:"0 0 20 20",fill:"none",xmlns:"http://www.w3.org/2000/svg"},MC={key:122,width:"20",height:"20",viewBox:"0 0 20 20",fill:"none",xmlns:"http://www.w3.org/2000/svg"},mC={key:123,width:"20",height:"20",viewBox:"0 0 20 20",fill:"none",xmlns:"http://www.w3.org/2000/svg"},fC={key:124,xmlns:"http://www.w3.org/2000/svg",width:"20",height:"20",viewBox:"0 0 20 20",fill:"none"};const uC=L({name:"Icons",components:{Folder:n,Document:o,Message:i,VideoCamera:l,AlarmClock:e},data:()=>({iconsInstanceId:"fcrm-icons-"+ ++p}),props:{iconName:{type:String,required:!0},iconClass:{type:String,default:""}},computed:{emptyStateSvgIds(){return{filter0:`${this.iconsInstanceId}-filter0-d-empty`,paint0:`${this.iconsInstanceId}-paint0-linear-empty`,paint1:`${this.iconsInstanceId}-paint1-linear-empty`,paint2:`${this.iconsInstanceId}-paint2-linear-empty`}}}},[["render",function(t,C,e,l,i,o){const n=s("Folder"),h=r,L=s("Document"),p=s("Message"),uC=s("VideoCamera"),ZC=s("AlarmClock"),xC=s("QuestionFilled");return"el-icon-document"===e.iconName?(a(),w("svg",{key:0,class:c(e.iconClass),id:"document",xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 1024 1024",width:"18",height:"18",fill:"currentColor"},[...C[0]||(C[0]=[d("path",{d:"M832 512h-256v256h-384v-768h640v512zM806 576l-166 166v-166h166zM160 832h480l256 -256v-608q0 -14 -9 -23t-23 -9h-704q-14 0 -23 9t-9 23v832q0 14 9 23t23 9zM320 384h384v-64h-384v64zM320 576h160v-64h-160v64zM320 192h384v-64h-384v64z",transform:"scale(1,-1) translate(0,-896)"},null,-1)])],2)):"el-icon-message"===e.iconName?(a(),w("svg",{key:1,class:c(e.iconClass),id:"message",xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 1024 1024",width:"18",height:"18",fill:"currentColor"},[...C[1]||(C[1]=[d("path",{d:"M128 672v-512q1 -27 19 -45t45 -19h640q27 1 45 19t19 45v512h-768zM128 736h768q27 -1 45 -19t19 -45v-512q-1 -54 -37.5 -90.5t-90.5 -37.5h-640q-54 1 -90.5 37.5t-37.5 90.5v512q1 27 19 45t45 19zM904 672l-247 -283q-28 -31 -65.5 -48t-79.5 -17t-79.5 17t-65.5 48 l-247 283h784zM205 672l211 -241q18 -21 43 -32t53 -11t53 11t43 32l211 241h-614z",transform:"scale(1,-1) translate(0,-896)"},null,-1)])],2)):"el-icon-set-up"===e.iconName?(a(),w("svg",{key:2,class:c(e.iconClass),id:"set-up",xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 1024 1024",width:"18",height:"18",fill:"currentColor"},[...C[2]||(C[2]=[d("path",{d:"M224 736q-27 -1 -45 -19t-19 -45v-576q1 -27 19 -45t45 -19h576q27 1 45 19t19 45v576q-1 27 -19 45t-45 19h-576zM224 800h576q54 -1 90.5 -37.5t37.5 -90.5v-576q-1 -54 -37.5 -90.5t-90.5 -37.5h-576q-54 1 -90.5 37.5t-37.5 90.5v576q1 54 37.5 90.5t90.5 37.5z M384 480q27 1 45 19t18 45t-18 45t-45 18t-45 -18t-18 -45t18 -45t45 -19zM384 416q-54 1 -90.5 37.5t-37.5 90.5q1 54 37.5 90.5t90.5 37.5q54 -1 90.5 -37.5t37.5 -90.5q-1 -54 -37.5 -90.5t-90.5 -37.5zM448 576zM480 576h256q32 0 32 -32v0q0 -32 -32 -32h-256 q-32 0 -32 32v0q0 32 32 32zM640 160q27 1 45 19t18 45t-18 45t-45 18t-45 -18t-18 -45t18 -45t45 -19zM640 96q-54 1 -90.5 37.5t-37.5 90.5q1 54 37.5 90.5t90.5 37.5q54 -1 90.5 -37.5t37.5 -90.5q-1 -54 -37.5 -90.5t-90.5 -37.5zM256 256zM288 256h256q32 0 32 -32v0 q0 -32 -32 -32h-256q-32 0 -32 32v0q0 32 32 32z",transform:"scale(1,-1) translate(0,-896)"},null,-1)])],2)):"el-icon-s-custom"===e.iconName?(a(),w("svg",{key:3,class:c(e.iconClass),id:"s-custom",xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 1024 1024",width:"18",height:"18",fill:"currentColor"},[...C[3]||(C[3]=[d("path",{d:"M629 367q135 -42 216 -150t83 -249h-832q2 141 83 249t216 150l117 -175zM720 592q-2 -88 -61 -147t-147 -61q-88 2 -147 61t-61 147q2 88 61 147t147 61q88 -2 147 -61t61 -147z",transform:"scale(1,-1) translate(0,-896)"},null,-1)])],2)):"el-icon-connection"===e.iconName?(a(),w("svg",{key:4,class:c(e.iconClass),id:"connection",xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 1024 1024",width:"18",height:"18",fill:"currentColor"},[...C[4]||(C[4]=[d("path",{d:"M640 512v-64h-192q-54 -1 -90.5 -37.5t-37.5 -90.5v-128q1 -54 37.5 -90.5t90.5 -37.5h320q54 1 90.5 37.5t37.5 90.5v128q0 35 -17 64t-47 47v70q57 -21 92 -69t36 -112v-128q-2 -82 -56 -136t-136 -56h-320q-82 2 -136 56t-56 136v128q2 82 56 136t136 56h192zM384 256 v64h192q54 1 90.5 37.5t37.5 90.5v128q-1 54 -37.5 90.5t-90.5 37.5h-320q-54 -1 -90.5 -37.5t-37.5 -90.5v-128q0 -35 17 -64t47 -47v-70q-58 21 -92.5 70t-35.5 111v128q2 82 56 136t136 56h320q82 -2 136 -56t56 -136v-128q-2 -82 -56 -136t-136 -56h-192z",transform:"scale(1,-1) translate(0,-896)"},null,-1)])],2)):"el-icon-s-check"===e.iconName?(a(),w("svg",{key:5,class:c(e.iconClass),id:"s-check",xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 1024 1024",width:"18",height:"18",fill:"currentColor"},[...C[5]||(C[5]=[d("path",{d:"M624 420v-164h144q54 -1 90.5 -37.5t37.5 -90.5h-768q1 54 37.5 90.5t90.5 37.5h144v164q-51 38 -70 96t0.5 118.5t68.5 96.5t113 36t113 -36t68.5 -96.5t0.5 -118.5t-70 -96zM128 0v64h768v-64h-768z",transform:"scale(1,-1) translate(0,-896)"},null,-1)])],2)):"el-icon-setting"===e.iconName?(a(),w("svg",{key:6,class:c(e.iconClass),id:"setting",xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 1024 1024",width:"18",height:"18",fill:"currentColor"},[...C[6]||(C[6]=[d("path",{d:"M625 832l222 -128l-43 -75q22 -26 40 -57t30 -64h86v-256h-86q-24 -67 -70 -121l43 -75l-222 -128l-43 75q-70 -13 -140 0l-43 -75l-222 128l43 75q-23 27 -40.5 57.5t-29.5 63.5h-86v256h86q24 67 70 121l-43 75l222 128l43 -75q70 13 140 0zM649 745l-12 -20l-22 -39 l-45 8q-58 11 -116 0l-45 -8l-34 59l-110 -64l33 -59l-29 -34q-38 -45 -58 -101l-15 -43h-68v-128h67l16 -43q10 -29 24 -53t34 -48l29 -34l-33 -59l110 -64l34 59l45 -8q58 -11 116 0l45 8l34 -59l110 64l-33 59l29 34q38 46 58 101l15 43h68v128h-67l-16 43 q-9 27 -24 52.5t-34 48.5l-29 34l33 59zM512 604q95 -2 158.5 -65.5t65.5 -158.5q-2 -95 -65.5 -158.5t-158.5 -65.5q-95 2 -158.5 65.5t-65.5 158.5q2 95 65.5 158.5t158.5 65.5zM512 540q-68 -2 -113 -47t-47 -113q2 -68 47 -113t113 -47q68 2 113 47t47 113 q-2 68 -47 113t-113 47z",transform:"scale(1,-1) translate(0,-896)"},null,-1)])],2)):"el-icon-shopping-cart-2"===e.iconName?(a(),w("svg",{key:7,class:c(e.iconClass),id:"shopping-cart-2",xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 1024 1024",width:"18",height:"18",fill:"currentColor"},[...C[7]||(C[7]=[d("path",{d:"M432 -32q-20 1 -33.5 14.5t-13.5 33.5t13.5 33.5t33.5 13.5t33.5 -13.5t13.5 -33.5t-13.5 -33.5t-33.5 -14.5zM752 -32q-20 1 -33.5 14.5t-13.5 33.5t13.5 33.5t33.5 13.5t33.5 -13.5t13.5 -33.5t-13.5 -33.5t-33.5 -14.5zM96 768q-14 0 -23 9t-9 23t9 23t23 9h1 q12 0 20.5 -7t10.5 -19l34 -166h607q15 0 24.5 -12t6.5 -27l-96 -448q-2 -11 -10.5 -18t-20.5 -7h-448q-12 0 -20.5 7t-10.5 19l-123 614h-134zM410 192h396l82 384h-555z",transform:"scale(1,-1) translate(0,-896)"},null,-1)])],2)):"el-icon-attract"===e.iconName?(a(),w("svg",{key:8,class:c(e.iconClass),id:"attract",xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 1024 1024",width:"18",height:"18",fill:"currentColor"},[...C[8]||(C[8]=[d("path",{d:"M832 576v128h-128v-320q-2 -82 -56 -136t-136 -56q-82 2 -136 56t-56 136v320h-128v-128h128v-64h-128v-128q3 -136 93.5 -226.5t226.5 -93.5q136 3 226.5 93.5t93.5 226.5v128h-128v64h128zM640 384v384h256v-384q-4 -163 -112.5 -271.5t-271.5 -112.5 q-163 4 -271.5 112.5t-112.5 271.5v384h256v-384q1 -54 37.5 -90.5t90.5 -37.5q54 1 90.5 37.5t37.5 90.5z",transform:"scale(1,-1) translate(0,-896)"},null,-1)])],2)):"el-icon-bangzhu"===e.iconName?(a(),w("svg",{key:9,class:c(e.iconClass),id:"bangzhu",xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 1024 1024",width:"18",height:"18",fill:"currentColor"},[...C[9]||(C[9]=[d("path",{d:"M512 0q163 4 271.5 112.5t112.5 271.5q-4 163 -112.5 271.5t-271.5 112.5q-163 -4 -271.5 -112.5t-112.5 -271.5q4 -163 112.5 -271.5t271.5 -112.5zM512 -64q-190 5 -316.5 131.5t-131.5 316.5q5 190 131.5 316.5t316.5 131.5q190 -5 316.5 -131.5t131.5 -316.5 q-5 -190 -131.5 -316.5t-316.5 -131.5zM512 192q82 2 136 56t56 136q-2 82 -56 136t-136 56q-82 -2 -136 -56t-56 -136q2 -82 56 -136t136 -56zM512 128q-109 3 -181 75t-75 181q3 109 75 181t181 75q109 -3 181 -75t75 -181q-3 -109 -75 -181t-181 -75zM623 541l161 160 l45 -45l-160 -161q-19 27 -46 46zM669 273l160 -161l-45 -45l-161 160q27 19 46 46zM401 227l-161 -160l-45 45l160 161q19 -27 46 -46zM355 495l-160 161l45 45l161 -160q-27 -19 -46 -46z",transform:"scale(1,-1) translate(0,-896)"},null,-1)])],2)):"el-icon-s-claim"===e.iconName?(a(),w("svg",{key:10,class:c(e.iconClass),id:"s-claim",xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 1024 1024",width:"18",height:"18",fill:"currentColor"},[...C[10]||(C[10]=[d("path",{d:"M704 704h160v-736h-704v736h160v-64h384v64zM312 359l-46 -46l181 -181l317 317l-45 45l-272 -271zM384 704v96h256v-96h-256z",transform:"scale(1,-1) translate(0,-896)"},null,-1)])],2)):"el-icon-guide"===e.iconName?(a(),w("svg",{key:11,class:c(e.iconClass),id:"guide",xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 1024 1024",width:"18",height:"18",fill:"currentColor"},[...C[11]||(C[11]=[d("path",{d:"M640 288h-64v192h64v-192zM640 128v-160q0 -14 -9 -23t-23 -9h-192q-14 0 -23 9t-9 23v160h64v-128h128v128h64zM384 288v192h64v-192h-64zM640 640h-64v128h-128v-128h-64v160q0 14 9 23t23 9h192q14 0 23 -9t9 -23v-160zM221 640l-71 -80l71 -80h547v160h-547zM206 704 h594q14 0 23 -9t9 -23v-224q0 -14 -9 -23t-23 -9h-594q-14 0 -24 11l-99 112q-8 9 -8 21t8 21l99 112q10 11 24 11zM885 208l-71 -80h-547v160h547zM828 352h-593q-14 0 -23 -9t-9 -23v-224q0 -14 9 -23t23 -9h593q15 0 24 11l100 112q8 9 8 21t-8 21l-100 112q-9 11 -24 11 z",transform:"scale(1,-1) translate(0,-896)"},null,-1)])],2)):"el-icon-ship"===e.iconName?(a(),w("svg",{key:12,class:c(e.iconClass),id:"ship",xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 1024 1024",width:"18",height:"18",fill:"currentColor"},[...C[12]||(C[12]=[d("path",{d:"M512 509v-61h406q15 0 24.5 -13t5.5 -28l-76 -268q-19 -63 -69 -100.5t-116 -38.5h-350q-66 1 -116 38.5t-69 100.5l-76 268q-4 15 5.5 28t24.5 13h342v330q1 18 16 27t31 1l14 -7l3 1v-3l232 -126q16 -10 15.5 -29.5t-17.5 -27.5zM512 579l145 66l-145 79v-145zM512 384 h-363l18 -64h690l18 64h-363zM185 256l29 -99q12 -42 45.5 -67t77.5 -26h350q44 1 77.5 26t45.5 67l29 99h-654z",transform:"scale(1,-1) translate(0,-896)"},null,-1)])],2)):"el-icon-info"===e.iconName?(a(),w("svg",{key:13,class:c(e.iconClass),id:"info",xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 1024 1024",width:"18",height:"18",fill:"currentColor"},[...C[13]||(C[13]=[d("path",{d:"M512 832q190 -5 316.5 -131.5t131.5 -316.5q-5 -190 -131.5 -316.5t-316.5 -131.5q-190 5 -316.5 131.5t-131.5 316.5q5 190 131.5 316.5t316.5 131.5zM579 557q26 0 43 15.5t17 41.5t-17 41.5t-42.5 15.5t-42.5 -15.5t-17 -41.5t17 -41.5t42 -15.5zM591 197q0 6 1 16 t0 19l-53 -61q-8 -9 -16.5 -14t-14.5 -3q-9 4 -8 14l88 277q5 28 -9 48t-45 24q-35 -1 -76.5 -29.5t-72.5 -72.5v-15q-1 -10 0 -19l53 61q8 9 16.5 14t13.5 3q10 -5 7 -16l-87 -276q-7 -25 7 -44.5t49 -26.5q50 1 84 29t63 72z",transform:"scale(1,-1) translate(0,-896)"},null,-1)])],2)):"el-icon-lock"===e.iconName?(a(),w("svg",{key:14,class:c(e.iconClass),id:"lock",xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 1024 1024",width:"18",height:"18",fill:"currentColor"},[...C[14]||(C[14]=[d("path",{d:"M224 448a32 32 0 0 0-32 32v384a32 32 0 0 0 32 32h576a32 32 0 0 0 32-32V480a32 32 0 0 0-32-32zm0-64h576a96 96 0 0 1 96 96v384a96 96 0 0 1-96 96H224a96 96 0 0 1-96-96V480a96 96 0 0 1 96-96"},null,-1),d("path",{d:"M512 544a32 32 0 0 1 32 32v192a32 32 0 1 1-64 0V576a32 32 0 0 1 32-32m192-160v-64a192 192 0 1 0-384 0v64zM512 64a256 256 0 0 1 256 256v128H256V320A256 256 0 0 1 512 64"},null,-1)])],2)):"more_actions"===e.iconName?(a(),w("svg",{key:15,class:c(e.iconClass),xmlns:"http://www.w3.org/2000/svg",width:"20",height:"20",viewBox:"0 0 20 20",fill:"none"},[...C[15]||(C[15]=[d("path",{d:"M10 3.25C9.38125 3.25 8.875 3.75625 8.875 4.375C8.875 4.99375 9.38125 5.5 10 5.5C10.6187 5.5 11.125 4.99375 11.125 4.375C11.125 3.75625 10.6187 3.25 10 3.25ZM10 14.5C9.38125 14.5 8.875 15.0063 8.875 15.625C8.875 16.2438 9.38125 16.75 10 16.75C10.6187 16.75 11.125 16.2438 11.125 15.625C11.125 15.0063 10.6187 14.5 10 14.5ZM10 8.875C9.38125 8.875 8.875 9.38125 8.875 10C8.875 10.6188 9.38125 11.125 10 11.125C10.6187 11.125 11.125 10.6188 11.125 10C11.125 9.38125 10.6187 8.875 10 8.875Z",fill:"currentColor"},null,-1)])],2)):"Folder"===e.iconName?(a(),g(h,{key:16},{default:H(()=>[V(n)],void 0),_:1})):"Document"===e.iconName?(a(),g(h,{key:17},{default:H(()=>[V(L)],void 0),_:1})):"Message"===e.iconName?(a(),g(h,{key:18},{default:H(()=>[V(p)],void 0),_:1})):"VideoCamera"===e.iconName?(a(),g(h,{key:19},{default:H(()=>[V(uC)],void 0),_:1})):"AlarmClock"===e.iconName?(a(),g(h,{key:20},{default:H(()=>[V(ZC)],void 0),_:1})):"ContactSegments"===e.iconName?(a(),w("svg",M,[...C[16]||(C[16]=[d("path",{d:"M1.25 0C0.904825 0 0.625 0.279825 0.625 0.625V1.875H1.875V1.25H10.625V11.25H1.875V10.625H0.625V11.875C0.625 12.2202 0.904825 12.5 1.25 12.5H11.25C11.5952 12.5 11.875 12.2202 11.875 11.875V0.625C11.875 0.279825 11.5952 0 11.25 0H1.25ZM4.375 8.75C4.375 7.71444 5.21444 6.875 6.25 6.875C7.28556 6.875 8.125 7.71444 8.125 8.75H4.375ZM6.25 6.25C5.55963 6.25 5 5.69037 5 5C5 4.30964 5.55963 3.75 6.25 3.75C6.94037 3.75 7.5 4.30964 7.5 5C7.5 5.69037 6.94037 6.25 6.25 6.25ZM2.5 4.375V3.125H0V4.375H2.5ZM2.5 5.625V6.875H0V5.625H2.5ZM2.5 9.375V8.125H0V9.375H2.5Z",fill:"var(--fc-secondary-text)"},null,-1)])])):"RecurringCampaigns"===e.iconName?(a(),w("svg",m,[...C[17]||(C[17]=[d("path",{d:"M3.75 8.75C3.75 8.75 8.125 9.375 10 11.25H10.625C10.9702 11.25 11.25 10.9702 11.25 10.625V6.83563C11.7891 6.69688 12.1875 6.20744 12.1875 5.625C12.1875 5.04256 11.7891 4.55312 11.25 4.41437V0.625C11.25 0.279825 10.9702 0 10.625 0H10C8.125 1.875 3.75 2.5 3.75 2.5H1.25C0.559644 2.5 0 3.05964 0 3.75V7.5C0 8.19037 0.559644 8.75 1.25 8.75H1.875L2.5 11.875H3.75V8.75ZM5 3.53825C5.42706 3.44662 5.95469 3.31996 6.52456 3.15233C7.57344 2.84384 8.90625 2.35789 10 1.60911V9.64087C8.90625 8.89212 7.57344 8.40619 6.52456 8.09769C5.95469 7.93006 5.42706 7.80338 5 7.71175V3.53825ZM1.25 3.75H3.75V7.5H1.25V3.75Z",fill:"var(--fc-secondary-text)"},null,-1)])])):"EmailSequences"===e.iconName?(a(),w("svg",f,[...C[18]||(C[18]=[d("path",{d:"M13.5 7.5C13.4999 6.16266 13.0529 4.8637 12.2302 3.80939C11.4074 2.75508 10.2561 2.00586 8.95884 1.6807C7.66164 1.35554 6.29298 1.47308 5.07021 2.01465C3.84744 2.55623 2.84065 3.49079 2.20972 4.66994C1.57878 5.84909 1.35987 7.20523 1.58774 8.52301C1.81562 9.84079 2.47722 11.0447 3.46748 11.9435C4.45774 12.8423 5.7199 13.3845 7.05353 13.484C8.38716 13.5835 9.71581 13.2346 10.8285 12.4927L11.661 13.7407C10.4297 14.564 8.98122 15.0024 7.5 15C3.35775 15 0 11.6422 0 7.5C0 3.35775 3.35775 0 7.5 0C11.6422 0 15 3.35775 15 7.5V8.625C15.0001 9.18656 14.8201 9.73335 14.4864 10.185C14.1528 10.6367 13.683 10.9695 13.1463 11.1345C12.6095 11.2995 12.0339 11.288 11.5042 11.1017C10.9744 10.9154 10.5184 10.5642 10.203 10.0995C9.70243 10.62 9.06222 10.9849 8.35929 11.1503C7.65635 11.3158 6.9206 11.2748 6.24042 11.0322C5.56024 10.7896 4.96457 10.3558 4.52498 9.78286C4.08538 9.20993 3.82056 8.52227 3.76233 7.80248C3.7041 7.08269 3.85494 6.36139 4.19672 5.72525C4.5385 5.08911 5.05669 4.56519 5.68903 4.21643C6.32137 3.86766 7.04096 3.70889 7.76134 3.7592C8.48173 3.8095 9.17227 4.06674 9.75 4.5H11.25V8.625C11.25 8.92337 11.3685 9.20952 11.5795 9.4205C11.7905 9.63147 12.0766 9.75 12.375 9.75C12.6734 9.75 12.9595 9.63147 13.1705 9.4205C13.3815 9.20952 13.5 8.92337 13.5 8.625V7.5ZM7.5 5.25C6.90326 5.25 6.33097 5.48705 5.90901 5.90901C5.48705 6.33097 5.25 6.90326 5.25 7.5C5.25 8.09674 5.48705 8.66903 5.90901 9.09099C6.33097 9.51295 6.90326 9.75 7.5 9.75C8.09674 9.75 8.66903 9.51295 9.09099 9.09099C9.51295 8.66903 9.75 8.09674 9.75 7.5C9.75 6.90326 9.51295 6.33097 9.09099 5.90901C8.66903 5.48705 8.09674 5.25 7.5 5.25Z",fill:"var(--fc-secondary-text)"},null,-1)])])):"SetupIcon"===e.iconName?(a(),w("svg",u,[...C[19]||(C[19]=[d("path",{d:"M1.39509 0.122144C1.79531 -0.0206658 2.22944 -0.0386988 2.64013 0.0704267C3.05081 0.179552 3.41873 0.410702 3.69528 0.733339C3.97182 1.05598 4.14398 1.45492 4.18901 1.87746C4.23404 2.30001 4.14983 2.72627 3.94749 3.09994L10.3729 9.52594L9.52449 10.3743L3.09849 3.94834C2.72473 4.14989 2.29868 4.23346 1.87649 4.18804C1.45429 4.14263 1.05576 3.97037 0.733413 3.69395C0.411068 3.41753 0.180036 3.04994 0.0707638 2.63961C-0.0385088 2.22928 -0.0208941 1.79547 0.121287 1.39534L1.46349 2.73754C1.54651 2.8235 1.64582 2.89207 1.75562 2.93924C1.86543 2.9864 1.98352 3.01123 2.10303 3.01227C2.22253 3.01331 2.34104 2.99054 2.45165 2.94528C2.56225 2.90003 2.66274 2.8332 2.74724 2.7487C2.83175 2.6642 2.89857 2.56371 2.94383 2.4531C2.98908 2.3425 3.01185 2.22398 3.01081 2.10448C3.00977 1.98498 2.98495 1.86688 2.93778 1.75708C2.89061 1.64728 2.82205 1.54797 2.73609 1.46494L1.39449 0.121544L1.39509 0.122144ZM7.61529 1.25254L9.52449 0.191744L10.3729 1.04014L9.31209 2.94934L8.25129 3.16174L6.97929 4.43434L6.13029 3.58594L7.40289 2.31334L7.61529 1.25254V1.25254ZM3.58449 6.13174L4.43289 6.98014L1.25109 10.1619C1.14291 10.2704 0.997323 10.3334 0.844185 10.338C0.691046 10.3426 0.541955 10.2884 0.427476 10.1866C0.312997 10.0848 0.241805 9.94304 0.228494 9.79041C0.215183 9.63778 0.260762 9.48584 0.355887 9.36574L0.402687 9.31354L3.58449 6.13174Z",fill:"currentColor"},null,-1)])])):"Documentations"===e.iconName?(a(),w("svg",Z,[...C[20]||(C[20]=[d("path",{d:"M13.5 4.5V14.2448C13.5007 14.3432 13.482 14.4409 13.4449 14.5322C13.4079 14.6234 13.3532 14.7065 13.284 14.7766C13.2149 14.8468 13.1326 14.9026 13.0419 14.9409C12.9511 14.9792 12.8537 14.9993 12.7553 15H0.74475C0.54736 15 0.358043 14.9216 0.218397 14.7821C0.0787511 14.6426 0.000198782 14.4534 0 14.256V0.744C0 0.34125 0.33675 0 0.7515 0H8.99775L13.5 4.5ZM12 5.25H8.25V1.5H1.5V13.5H12V5.25ZM3.75 3.75H6V5.25H3.75V3.75ZM3.75 6.75H9.75V8.25H3.75V6.75ZM3.75 9.75H9.75V11.25H3.75V9.75Z",fill:"var(--fc-secondary-text)"},null,-1)])])):"VideoTutorials"===e.iconName?(a(),w("svg",x,[...C[21]||(C[21]=[d("path",{d:"M11.25 0C11.4489 0 11.6397 0.0790178 11.7803 0.21967C11.921 0.360322 12 0.551088 12 0.75V3.9L15.9097 1.1625C15.966 1.12309 16.0319 1.09987 16.1004 1.09538C16.1689 1.09088 16.2374 1.10528 16.2983 1.137C16.3591 1.16872 16.4102 1.21654 16.4457 1.27526C16.4813 1.33398 16.5001 1.40134 16.5 1.47V10.53C16.5001 10.5987 16.4813 10.666 16.4457 10.7247C16.4102 10.7835 16.3591 10.8313 16.2983 10.863C16.2374 10.8947 16.1689 10.9091 16.1004 10.9046C16.0319 10.9001 15.966 10.8769 15.9097 10.8375L12 8.1V11.25C12 11.4489 11.921 11.6397 11.7803 11.7803C11.6397 11.921 11.4489 12 11.25 12H0.75C0.551088 12 0.360322 11.921 0.21967 11.7803C0.0790176 11.6397 0 11.4489 0 11.25V0.75C0 0.551088 0.0790176 0.360322 0.21967 0.21967C0.360322 0.0790178 0.551088 0 0.75 0H11.25ZM10.5 1.5H1.5V10.5H10.5V1.5ZM4.8 3.62175C4.85708 3.62159 4.91302 3.63773 4.96125 3.66825L8.2275 5.74725C8.26993 5.77438 8.30484 5.81175 8.32903 5.85592C8.35321 5.90009 8.36589 5.94964 8.36589 6C8.36589 6.05036 8.35321 6.09991 8.32903 6.14408C8.30484 6.18825 8.26993 6.22562 8.2275 6.25275L4.96125 8.3325C4.91576 8.3615 4.86329 8.37769 4.80936 8.37938C4.75544 8.38106 4.70206 8.36818 4.65484 8.34207C4.60763 8.31597 4.56833 8.27762 4.54108 8.23106C4.51383 8.1845 4.49964 8.13145 4.5 8.0775V3.9225C4.5 3.75675 4.635 3.6225 4.8 3.6225V3.62175ZM15 3.63L12 5.73V6.2685L15 8.3685V3.63Z",fill:"currentColor"},null,-1)])])):"TagIcon"===e.iconName?(a(),w("svg",k,[...C[22]||(C[22]=[d("path",{d:"M9.17489 2.5752L16.5991 3.63645L17.6596 11.0614L10.7656 17.9554C10.625 18.096 10.4343 18.175 10.2354 18.175C10.0365 18.175 9.84578 18.096 9.70514 17.9554L2.28014 10.5304C2.13953 10.3898 2.06055 10.1991 2.06055 10.0002C2.06055 9.80132 2.13953 9.61059 2.28014 9.46995L9.17489 2.5752ZM9.70514 4.1667L3.87089 10.0002L10.2354 16.3639L16.0689 10.5304L15.2739 4.9617L9.70514 4.1667V4.1667ZM11.2951 8.9397C11.0138 8.65823 10.8557 8.27653 10.8558 7.87856C10.8559 7.6815 10.8947 7.48638 10.9701 7.30433C11.0456 7.12229 11.1561 6.95689 11.2955 6.81757C11.4349 6.67825 11.6003 6.56775 11.7824 6.49237C11.9645 6.417 12.1596 6.37822 12.3567 6.37825C12.7546 6.37832 13.1363 6.53648 13.4176 6.81795C13.699 7.09941 13.857 7.48111 13.857 7.87909C13.8569 8.27706 13.6987 8.65871 13.4173 8.94007C13.1358 9.22143 12.7541 9.37946 12.3561 9.37939C11.9581 9.37932 11.5765 9.22116 11.2951 8.9397V8.9397Z",fill:"var(--fc-secondary-text)"},null,-1)])])):"ListIcon"===e.iconName?(a(),w("svg",y,[...C[23]||(C[23]=[d("path",{d:"M7 4H16.75V5.5H7V4ZM4.375 5.875C4.07663 5.875 3.79048 5.75647 3.5795 5.5455C3.36853 5.33452 3.25 5.04837 3.25 4.75C3.25 4.45163 3.36853 4.16548 3.5795 3.9545C3.79048 3.74353 4.07663 3.625 4.375 3.625C4.67337 3.625 4.95952 3.74353 5.1705 3.9545C5.38147 4.16548 5.5 4.45163 5.5 4.75C5.5 5.04837 5.38147 5.33452 5.1705 5.5455C4.95952 5.75647 4.67337 5.875 4.375 5.875ZM4.375 11.125C4.07663 11.125 3.79048 11.0065 3.5795 10.7955C3.36853 10.5845 3.25 10.2984 3.25 10C3.25 9.70163 3.36853 9.41548 3.5795 9.2045C3.79048 8.99353 4.07663 8.875 4.375 8.875C4.67337 8.875 4.95952 8.99353 5.1705 9.2045C5.38147 9.41548 5.5 9.70163 5.5 10C5.5 10.2984 5.38147 10.5845 5.1705 10.7955C4.95952 11.0065 4.67337 11.125 4.375 11.125ZM4.375 16.3C4.07663 16.3 3.79048 16.1815 3.5795 15.9705C3.36853 15.7595 3.25 15.4734 3.25 15.175C3.25 14.8766 3.36853 14.5905 3.5795 14.3795C3.79048 14.1685 4.07663 14.05 4.375 14.05C4.67337 14.05 4.95952 14.1685 5.1705 14.3795C5.38147 14.5905 5.5 14.8766 5.5 15.175C5.5 15.4734 5.38147 15.7595 5.1705 15.9705C4.95952 16.1815 4.67337 16.3 4.375 16.3ZM7 9.25H16.75V10.75H7V9.25ZM7 14.5H16.75V16H7V14.5Z",fill:"var(--fc-secondary-text)"},null,-1)])])):"statusIcon"===e.iconName?(a(),w("svg",q,[...C[24]||(C[24]=[d("path",{d:"M9.7015 11.3201L10.7605 12.3791L17.11 6.02962L18.1705 7.09012L10.7605 14.5001L5.9875 9.72712L7.048 8.66663L8.64175 10.2604L9.7015 11.3194V11.3201ZM9.703 9.19913L13.417 5.48438L14.4745 6.54187L10.7605 10.2566L9.703 9.19913ZM7.58275 13.4404L6.523 14.5001L1.75 9.72712L2.8105 8.66663L3.87025 9.72638L3.8695 9.72712L7.58275 13.4404V13.4404Z",fill:"var(--fc-secondary-text)"},null,-1)])])):"FluentFormIcon"===e.iconName?(a(),w("svg",N,[...C[25]||(C[25]=[d("rect",{width:"40",height:"40",rx:"4",fill:"#089DFF"},null,-1),d("path",{d:"M5.42493 14.2701C5.42493 11.1227 7.97639 8.57129 11.1238 8.57129H33.6983C33.6983 11.7187 31.1468 14.2701 27.9994 14.2701H5.42493Z",fill:"white"},null,-1),d("path",{d:"M5.42499 23.1353C5.42499 19.9879 7.97645 17.4365 11.1238 17.4365H33.6983C33.6983 20.5838 31.1469 23.1353 27.9995 23.1353H5.42499Z",fill:"white"},null,-1),d("path",{d:"M9.82336 31.9995C9.82336 28.8521 12.3748 26.3007 15.5222 26.3007H28.6722C28.6722 29.448 26.1208 31.9995 22.9734 31.9995H9.82336Z",fill:"white"},null,-1)])])):"FluentSMTPIcon"===e.iconName?(a(),w("svg",B,[...C[26]||(C[26]=[v('',6)])])):"export"===e.iconName?(a(),w("svg",z,[...C[27]||(C[27]=[d("path",{d:"M7.75 3.25V4.75H4V15.25H16V8.5H17.5V16C17.5 16.1989 17.421 16.3897 17.2803 16.5303C17.1397 16.671 16.9489 16.75 16.75 16.75H3.25C3.05109 16.75 2.86032 16.671 2.71967 16.5303C2.57902 16.3897 2.5 16.1989 2.5 16V4C2.5 3.80109 2.57902 3.61032 2.71967 3.46967C2.86032 3.32902 3.05109 3.25 3.25 3.25H7.75ZM13 4.75V1.75L18.25 6.25H11.5C11.1022 6.25 10.7206 6.40804 10.4393 6.68934C10.158 6.97064 10 7.35218 10 7.75V12.25H8.5V7.75C8.5 6.95435 8.81607 6.19129 9.37868 5.62868C9.94129 5.06607 10.7044 4.75 11.5 4.75H13Z",fill:"var(--fc-secondary-text)"},null,-1)])])):"plus"===e.iconName?(a(),w("svg",E,[...C[28]||(C[28]=[d("path",{d:"M9.25 9.25V4.75H10.75V9.25H15.25V10.75H10.75V15.25H9.25V10.75H4.75V9.25H9.25Z",fill:"currentColor"},null,-1)])])):"minus"===e.iconName?(a(),w("svg",b,[...C[29]||(C[29]=[d("path",{d:"M4.75 9.25H15.25V10.75H4.75V9.25Z",fill:"currentColor"},null,-1)])])):"import"==e.iconName?(a(),w("svg",D,[...C[30]||(C[30]=[d("path",{d:"M10.75 8.5H14.5L10 13L5.5 8.5H9.25V3.25H10.75V8.5ZM4 15.25H16V10H17.5V16C17.5 16.1989 17.421 16.3897 17.2803 16.5303C17.1397 16.671 16.9489 16.75 16.75 16.75H3.25C3.05109 16.75 2.86032 16.671 2.71967 16.5303C2.57902 16.3897 2.5 16.1989 2.5 16V10H4V15.25Z",fill:"var(--fc-secondary-text)"},null,-1)])])):"search"===e.iconName?(a(),w("svg",S,[...C[31]||(C[31]=[d("path",{d:"M14.5232 13.4627L17.7355 16.6742L16.6742 17.7355L13.4627 14.5232C12.2678 15.4812 10.7815 16.0022 9.25 16C5.524 16 2.5 12.976 2.5 9.25C2.5 5.524 5.524 2.5 9.25 2.5C12.976 2.5 16 5.524 16 9.25C16.0022 10.7815 15.4812 12.2678 14.5232 13.4627ZM13.0187 12.9062C13.9706 11.9274 14.5021 10.6153 14.5 9.25C14.5 6.349 12.1502 4 9.25 4C6.349 4 4 6.349 4 9.25C4 12.1502 6.349 14.5 9.25 14.5C10.6153 14.5021 11.9274 13.9706 12.9062 13.0187L13.0187 12.9062V12.9062Z",fill:"currentColor"},null,-1)])])):"crown"===e.iconName?(a(),w("svg",_,[...C[32]||(C[32]=[d("path",{d:"M4.34299 8.4585L5.50039 18.3H18.5L19.6574 8.4585L16.0484 10.8642L12.0002 5.1969L7.95199 10.8642L4.34299 8.4585ZM3.72109 5.88L7.50019 8.4L11.2676 3.126C11.3508 3.00933 11.4608 2.91424 11.5882 2.84862C11.7156 2.78301 11.8569 2.74878 12.0002 2.74878C12.1435 2.74878 12.2848 2.78301 12.4122 2.84862C12.5396 2.91424 12.6495 3.00933 12.7328 3.126L16.5002 8.4L20.2802 5.88C20.4232 5.78482 20.5906 5.73259 20.7624 5.72946C20.9342 5.72634 21.1033 5.77246 21.2497 5.86238C21.3961 5.9523 21.5137 6.08225 21.5887 6.23688C21.6636 6.39151 21.6927 6.56436 21.6725 6.735L20.1938 19.3053C20.168 19.5242 20.0628 19.7261 19.898 19.8726C19.7333 20.019 19.5205 20.1 19.3001 20.1H4.70029C4.47985 20.1 4.26709 20.019 4.10236 19.8726C3.93762 19.7261 3.83238 19.5242 3.80659 19.3053L2.32789 6.7341C2.30791 6.56354 2.33715 6.39081 2.41215 6.23633C2.48715 6.08184 2.60479 5.95203 2.75117 5.86224C2.89755 5.77244 3.06657 5.72639 3.23828 5.72954C3.40998 5.73269 3.5772 5.7849 3.72019 5.88H3.72109ZM12.0002 14.7C11.5228 14.7 11.065 14.5104 10.7274 14.1728C10.3898 13.8352 10.2002 13.3774 10.2002 12.9C10.2002 12.4226 10.3898 11.9648 10.7274 11.6272C11.065 11.2896 11.5228 11.1 12.0002 11.1C12.4776 11.1 12.9354 11.2896 13.273 11.6272C13.6105 11.9648 13.8002 12.4226 13.8002 12.9C13.8002 13.3774 13.6105 13.8352 13.273 14.1728C12.9354 14.5104 12.4776 14.7 12.0002 14.7Z",fill:"CurrentColor"},null,-1)])])):"sidebar"===e.iconName?(a(),w("svg",F,[...C[33]||(C[33]=[d("path",{d:"M16.6789 5.19727C16.6789 4.73702 16.3172 4.36364 15.8713 4.36364H8.82557V16.6364H15.8713C16.3172 16.6364 16.6789 16.263 16.6789 15.8027V5.19727ZM5.59598 8.30273C5.96069 8.30273 6.25636 8.60813 6.25652 8.98455C6.25652 9.36111 5.96078 9.66637 5.59598 9.66637H4.86233C4.49753 9.66637 4.2018 9.36111 4.2018 8.98455C4.20195 8.60813 4.49762 8.30273 4.86233 8.30273H5.59598ZM5.59598 6.03001C5.96069 6.03001 6.25636 6.3354 6.25652 6.71183C6.25652 7.08838 5.96078 7.39364 5.59598 7.39364H4.86233C4.49753 7.39364 4.2018 7.08838 4.2018 6.71183C4.20195 6.3354 4.49762 6.03001 4.86233 6.03001H5.59598ZM3.32108 15.8027C3.32108 16.263 3.68281 16.6364 4.12869 16.6364H7.50449V4.36364H4.12869C3.68282 4.36364 3.32108 4.73703 3.32108 5.19727V15.8027ZM18 15.8027C18 17.0161 17.0468 18 15.8713 18H4.12869C2.95321 18 2 17.0161 2 15.8027V5.19727C2 3.98391 2.9532 3 4.12869 3H15.8713C17.0468 3 18 3.98392 18 5.19727V15.8027Z",fill:"var(--fc-secondary-text)"},null,-1)])])):"graduationCap"===e.iconName?(a(),w("svg",I,[...C[34]||(C[34]=[d("path",{d:"M5 9.58331L2.5 8.125L10 3.75L17.5 8.125V13.4375H16.25V8.85419L15 9.58331V13.7571L14.8609 13.929C13.7161 15.3437 11.9636 16.25 10 16.25C8.03636 16.25 6.28393 15.3437 5.13914 13.929L5 13.7571V9.58331ZM6.25 10.3125V13.3073C7.16701 14.3463 8.50695 15 10 15C11.4931 15 12.833 14.3463 13.75 13.3073V10.3125L10 12.5L6.25 10.3125ZM4.98079 8.125L10 11.0529L15.0192 8.125L10 5.19713L4.98079 8.125Z",fill:"currentColor"},null,-1)])])):"manageLabels"===e.iconName?(a(),w("svg",A,[...C[35]||(C[35]=[d("path",{d:"M5.5 14.5H14.5V13H5.5V14.5ZM3 17.5V3H17V17.5H3ZM4.5 16.0571L15.5 16V4.5H4.5V16.0571Z",fill:"var(--fc-secondary-text)"},null,-1)])])):"desktop"===e.iconName?(a(),w("svg",O,[...C[36]||(C[36]=[d("path",{d:"M4.00003 13H16V4.75H4.00003V13ZM10.75 14.5V16H13.75V17.5H6.25003V16H9.25003V14.5H3.24403C3.14554 14.4994 3.04814 14.4794 2.95741 14.4411C2.86667 14.4028 2.78439 14.347 2.71527 14.2768C2.64616 14.2066 2.59157 14.1235 2.55463 14.0322C2.5177 13.9409 2.49914 13.8432 2.50003 13.7448V4.00525C2.50003 3.58825 2.84128 3.25 3.24403 3.25H16.756C17.167 3.25 17.5 3.58675 17.5 4.00525V13.7448C17.5 14.1618 17.1588 14.5 16.756 14.5H10.75V14.5Z",fill:"CurrentColor"},null,-1)])])):"tablet"===e.iconName?(a(),w("svg",T,[...C[37]||(C[37]=[d("path",{d:"M5.5 4V16H14.5V4H5.5ZM4.75 2.5H15.25C15.4489 2.5 15.6397 2.57902 15.7803 2.71967C15.921 2.86032 16 3.05109 16 3.25V16.75C16 16.9489 15.921 17.1397 15.7803 17.2803C15.6397 17.421 15.4489 17.5 15.25 17.5H4.75C4.55109 17.5 4.36032 17.421 4.21967 17.2803C4.07902 17.1397 4 16.9489 4 16.75V3.25C4 3.05109 4.07902 2.86032 4.21967 2.71967C4.36032 2.57902 4.55109 2.5 4.75 2.5ZM10 13.75C10.1989 13.75 10.3897 13.829 10.5303 13.9697C10.671 14.1103 10.75 14.3011 10.75 14.5C10.75 14.6989 10.671 14.8897 10.5303 15.0303C10.3897 15.171 10.1989 15.25 10 15.25C9.80109 15.25 9.61032 15.171 9.46967 15.0303C9.32902 14.8897 9.25 14.6989 9.25 14.5C9.25 14.3011 9.32902 14.1103 9.46967 13.9697C9.61032 13.829 9.80109 13.75 10 13.75V13.75Z",fill:"CurrentColor"},null,-1)])])):"mobile"===e.iconName?(a(),w("svg",j,[...C[38]||(C[38]=[d("path",{d:"M6.25 4V16H13.75V4H6.25ZM5.5 2.5H14.5C14.6989 2.5 14.8897 2.57902 15.0303 2.71967C15.171 2.86032 15.25 3.05109 15.25 3.25V16.75C15.25 16.9489 15.171 17.1397 15.0303 17.2803C14.8897 17.421 14.6989 17.5 14.5 17.5H5.5C5.30109 17.5 5.11032 17.421 4.96967 17.2803C4.82902 17.1397 4.75 16.9489 4.75 16.75V3.25C4.75 3.05109 4.82902 2.86032 4.96967 2.71967C5.11032 2.57902 5.30109 2.5 5.5 2.5ZM10 13.75C10.1989 13.75 10.3897 13.829 10.5303 13.9697C10.671 14.1103 10.75 14.3011 10.75 14.5C10.75 14.6989 10.671 14.8897 10.5303 15.0303C10.3897 15.171 10.1989 15.25 10 15.25C9.80109 15.25 9.61032 15.171 9.46967 15.0303C9.32902 14.8897 9.25 14.6989 9.25 14.5C9.25 14.3011 9.32902 14.1103 9.46967 13.9697C9.61032 13.829 9.80109 13.75 10 13.75V13.75Z",fill:"CurrentColor"},null,-1)])])):"common-empty-state"===e.iconName?(a(),w("svg",G,[C[43]||(C[43]=v('',5)),d("path",{d:"M60.9548 55.7743H69.7738C70.8815 55.7743 71.8309 54.7902 71.8309 53.6421V10.9993C71.8309 9.85125 70.8815 8.86719 69.7738 8.86719H61.2286",fill:`url(#${o.emptyStateSvgIds.paint0})`},null,8,$),d("path",{d:"M25.9407 55.7743H17.3956C16.2879 55.7743 15.3385 54.7902 15.3385 53.6421V10.9993C15.3385 9.85125 16.2879 8.86719 17.3956 8.86719H25.7647",fill:`url(#${o.emptyStateSvgIds.paint1})`},null,8,U),C[44]||(C[44]=v('',10)),d("g",{filter:`url(#${o.emptyStateSvgIds.filter0})`},[d("path",{d:"M58.9278 58.5963H27.5957C26.488 58.5963 25.5385 57.6122 25.5385 56.4642V6.93292C25.5385 5.78485 26.488 4.80078 27.5957 4.80078H58.9278C60.0355 4.80078 60.9849 5.78485 60.9849 6.93292V56.4642C60.9849 57.6122 60.0355 58.5963 58.9278 58.5963Z",fill:`url(#${o.emptyStateSvgIds.paint2})`},null,8,P)],8,Y),C[45]||(C[45]=d("path",{d:"M55.3737 21.6331H33.5362C33.0615 21.6331 32.745 21.3358 32.745 20.89V18.9579C32.745 18.5121 33.0615 18.2148 33.5362 18.2148H55.3737C55.8485 18.2148 56.1649 18.5121 56.1649 18.9579V20.89C56.1649 21.1872 55.8485 21.6331 55.3737 21.6331Z",fill:"#D5DDEA"},null,-1)),C[46]||(C[46]=d("path",{d:"M55.3737 30.4925H33.5362C33.0615 30.4925 32.745 30.1952 32.745 29.7494V27.8173C32.745 27.3715 33.0615 27.0742 33.5362 27.0742H55.3737C55.8485 27.0742 56.1649 27.3715 56.1649 27.8173V29.7494C56.1649 30.1952 55.8485 30.4925 55.3737 30.4925Z",fill:"#D5DDEA"},null,-1)),C[47]||(C[47]=d("path",{d:"M55.3737 39.512H33.5362C33.0615 39.512 32.745 39.2148 32.745 38.7689V36.8368C32.745 36.391 33.0615 36.0938 33.5362 36.0938H55.3737C55.8485 36.0938 56.1649 36.391 56.1649 36.8368V38.7689C56.1649 39.2148 55.8485 39.512 55.3737 39.512Z",fill:"#D5DDEA"},null,-1)),C[48]||(C[48]=d("path",{d:"M55.374 48.6956H46.6706C46.1959 48.6956 45.8794 48.3983 45.8794 47.9525V46.0204C45.8794 45.5746 46.1959 45.2773 46.6706 45.2773H55.374C55.8487 45.2773 56.1652 45.5746 56.1652 46.0204V47.9525C56.1652 48.3983 55.8487 48.6956 55.374 48.6956Z",fill:"#D5DDEA"},null,-1)),d("defs",null,[d("filter",{id:o.emptyStateSvgIds.filter0,x:"21.5385",y:"4.80078",width:"43.4464",height:"61.7969",filterUnits:"userSpaceOnUse","color-interpolation-filters":"sRGB"},[...C[39]||(C[39]=[v('',7)])],8,R),d("linearGradient",{id:o.emptyStateSvgIds.paint0,x1:"66.3893",y1:"7.78218",x2:"66.3893",y2:"56.28",gradientUnits:"userSpaceOnUse"},[...C[40]||(C[40]=[d("stop",{"stop-color":"#FDFEFF"},null,-1),d("stop",{offset:"0.9964","stop-color":"#ECF0F5"},null,-1)])],8,K),d("linearGradient",{id:o.emptyStateSvgIds.paint1,x1:"20.6362",y1:"7.78218",x2:"20.6362",y2:"56.28",gradientUnits:"userSpaceOnUse"},[...C[41]||(C[41]=[d("stop",{"stop-color":"#FDFEFF"},null,-1),d("stop",{offset:"0.9964","stop-color":"#ECF0F5"},null,-1)])],8,W),d("linearGradient",{id:o.emptyStateSvgIds.paint2,x1:"43.2502",y1:"3.55644",x2:"43.2502",y2:"59.1763",gradientUnits:"userSpaceOnUse"},[...C[42]||(C[42]=[d("stop",{"stop-color":"#FDFEFF"},null,-1),d("stop",{offset:"0.9964","stop-color":"#ECF0F5"},null,-1)])],8,Q)])])):"campaign-empty-state"===e.iconName?(a(),w("svg",X,[...C[49]||(C[49]=[v('',15)])])):"campaigns"===e.iconName?(a(),w("svg",J,[...C[50]||(C[50]=[d("path",{d:"M7.01313 11.9842C7.01313 11.9842 11.6552 12.6473 13.6447 14.6368H14.3079C14.6741 14.6368 14.971 14.3399 14.971 13.9736V9.95291C15.5431 9.80569 15.9658 9.28637 15.9658 8.66837C15.9658 8.05038 15.5431 7.53106 14.971 7.38384V3.36311C14.971 2.99686 14.6741 2.69995 14.3079 2.69995H13.6447C11.6552 4.68942 7.01313 5.35258 7.01313 5.35258H4.3605C3.62799 5.35258 3.03418 5.94639 3.03418 6.6789V10.6578C3.03418 11.3904 3.62799 11.9842 4.3605 11.9842H5.02365L5.68681 15.3H7.01313V11.9842ZM8.33944 6.45422C8.79258 6.357 9.35242 6.2226 9.95708 6.04474C11.07 5.71741 12.4842 5.20179 13.6447 4.4073V12.9294C12.4842 12.135 11.07 11.6194 9.95708 11.292C9.35242 11.1142 8.79258 10.9797 8.33944 10.8825V6.45422ZM4.3605 6.6789H7.01313V10.6578H4.3605V6.6789Z",fill:"currentColor"},null,-1)])])):"email-template-empty-state"===e.iconName?(a(),w("svg",tt,[...C[51]||(C[51]=[v('',16)])])):"pattern-empty-state"===e.iconName?(a(),w("svg",Ct,[...C[52]||(C[52]=[d("path",{d:"M4 3.25H10C10.1989 3.25 10.3897 3.32902 10.5303 3.46967C10.671 3.61032 10.75 3.80109 10.75 4V10C10.75 10.1989 10.671 10.3897 10.5303 10.5303C10.3897 10.671 10.1989 10.75 10 10.75H4C3.80109 10.75 3.61032 10.671 3.46967 10.5303C3.32902 10.3897 3.25 10.1989 3.25 10V4C3.25 3.80109 3.32902 3.61032 3.46967 3.46967C3.61032 3.32902 3.80109 3.25 4 3.25ZM4.75 4.75V9.25H9.25V4.75H4.75ZM10 12.25H16C16.1989 12.25 16.3897 12.329 16.5303 12.4697C16.671 12.6103 16.75 12.8011 16.75 13V16C16.75 16.1989 16.671 16.3897 16.5303 16.5303C16.3897 16.671 16.1989 16.75 16 16.75H10C9.80109 16.75 9.61032 16.671 9.46967 16.5303C9.32902 16.3897 9.25 16.1989 9.25 16V13C9.25 12.8011 9.32902 12.6103 9.46967 12.4697C9.61032 12.329 9.80109 12.25 10 12.25ZM10.75 13.75V15.25H15.25V13.75H10.75ZM13 3.25H16C16.1989 3.25 16.3897 3.32902 16.5303 3.46967C16.671 3.61032 16.75 3.80109 16.75 4V7C16.75 7.19891 16.671 7.38968 16.5303 7.53033C16.3897 7.67098 16.1989 7.75 16 7.75H13C12.8011 7.75 12.6103 7.67098 12.4697 7.53033C12.329 7.38968 12.25 7.19891 12.25 7V4C12.25 3.80109 12.329 3.61032 12.4697 3.46967C12.6103 3.32902 12.8011 3.25 13 3.25ZM13.75 4.75V6.25H15.25V4.75H13.75ZM4 13.25H7C7.19891 13.25 7.38968 13.329 7.53033 13.4697C7.67098 13.6103 7.75 13.8011 7.75 14V17C7.75 17.1989 7.67098 17.3897 7.53033 17.5303C7.38968 17.671 7.19891 17.75 7 17.75H4C3.80109 17.75 3.61032 17.671 3.46967 17.5303C3.32902 17.3897 3.25 17.1989 3.25 17V14C3.25 13.8011 3.32902 13.6103 3.46967 13.4697C3.61032 13.329 3.80109 13.25 4 13.25Z",fill:"var(--fc-primary-border)"},null,-1)])])):"send-mail"===e.iconName?(a(),w("svg",et,[...C[53]||(C[53]=[d("path",{d:"M2.44226 8.02765C2.05976 7.8739 2.06426 7.64515 2.46776 7.5109L16.7823 2.7394C17.179 2.6074 17.4063 2.8294 17.2953 3.2179L13.2048 17.5324C13.0923 17.9292 12.8485 17.9472 12.667 17.5849L9.25001 10.7502L2.44226 8.02765ZM6.10976 7.87765L10.3368 9.5689L12.6168 14.1304L15.2763 4.8229L6.10901 7.87765H6.10976Z",fill:"CurrentColor"},null,-1)])])):"reload"===e.iconName?(a(),w("svg",lt,[...C[54]||(C[54]=[d("path",{d:"M10 5C8.28215 5 6.76567 5.86641 5.86527 7.1875H7.5V8.4375H3.75V4.6875H5V6.2496C6.13988 4.73229 7.95477 3.75 10 3.75C13.4517 3.75 16.25 6.54822 16.25 10H15C15 7.23857 12.7614 5 10 5ZM5 10C5 12.7614 7.23857 15 10 15C11.7179 15 13.2343 14.1336 14.1348 12.8125H12.5V11.5625H16.25V15.3125H15V13.7504C13.8601 15.2677 12.0452 16.25 10 16.25C6.54822 16.25 3.75 13.4517 3.75 10H5Z",fill:"currentColor"},null,-1)])])):"eye"===e.iconName?(a(),w("svg",it,[...C[55]||(C[55]=[d("path",{d:"M10 3.25C14.044 3.25 17.4085 6.16 18.1143 10C17.4093 13.84 14.044 16.75 10 16.75C5.956 16.75 2.5915 13.84 1.88575 10C2.59075 6.16 5.956 3.25 10 3.25ZM10 15.25C11.5296 15.2497 13.0138 14.7301 14.2096 13.7764C15.4055 12.8226 16.2422 11.4912 16.5828 10C16.2409 8.50998 15.4037 7.18 14.208 6.22752C13.0122 5.27504 11.5287 4.7564 10 4.7564C8.47127 4.7564 6.98777 5.27504 5.79203 6.22752C4.5963 7.18 3.75908 8.50998 3.41725 10C3.75782 11.4912 4.59451 12.8226 5.79036 13.7764C6.98621 14.7301 8.4704 15.2497 10 15.25V15.25ZM10 13.375C9.10489 13.375 8.24645 13.0194 7.61351 12.3865C6.98058 11.7536 6.625 10.8951 6.625 10C6.625 9.10489 6.98058 8.24645 7.61351 7.61352C8.24645 6.98058 9.10489 6.625 10 6.625C10.8951 6.625 11.7535 6.98058 12.3865 7.61352C13.0194 8.24645 13.375 9.10489 13.375 10C13.375 10.8951 13.0194 11.7536 12.3865 12.3865C11.7535 13.0194 10.8951 13.375 10 13.375ZM10 11.875C10.4973 11.875 10.9742 11.6775 11.3258 11.3258C11.6775 10.9742 11.875 10.4973 11.875 10C11.875 9.50272 11.6775 9.02581 11.3258 8.67418C10.9742 8.32254 10.4973 8.125 10 8.125C9.50272 8.125 9.02581 8.32254 8.67417 8.67418C8.32254 9.02581 8.125 9.50272 8.125 10C8.125 10.4973 8.32254 10.9742 8.67417 11.3258C9.02581 11.6775 9.50272 11.875 10 11.875Z",fill:"CurrentColor"},null,-1)])])):"sms-status"===e.iconName?(a(),w("svg",ot,[...C[56]||(C[56]=[d("path",{d:"M4.08838 10.675L1.75 12.5126V2.8001C2.8 3.04097 2.80525 2.88836 2.9038 2.77583C3.00235 2.66331 3.13576 2.6001 3.275 2.6001H11.725C11.8643 2.6001 11.9977 2.65541 12.0962 2.75396C12.1948 2.85251 12.25 2.98592 12.25 3.1251V10.1501C12.25 10.2893 12.1948 10.4227 12.0962 10.5212C11.9977 10.6198 11.8643 10.6751 11.725 10.6751H4.08838ZM3.72558 9.6251H11.2V3.6501H2.8V10.3521L3.72558 9.6251ZM6.475 5.9501H7.525V7.0001H6.475V5.9501ZM4.375 5.9501H5.425V7.0001H4.375V5.9501ZM8.575 5.9501H9.625V7.0001H8.575V5.9501Z",fill:"var(--fc-secondary-text)"},null,-1)])])):"Plus"===e.iconName?(a(),w("svg",nt,[...C[57]||(C[57]=[d("path",{d:"M9.25 9.25V4.75H10.75V9.25H15.25V10.75H10.75V15.25H9.25V10.75H4.75V9.25H9.25Z",fill:"var(--fc-secondary-text)"},null,-1)])])):"contact-status"===e.iconName?(a(),w("svg",rt,[...C[58]||(C[58]=[d("path",{d:"M0.75 0H14.25C14.4489 0 14.6397 0.0790176 14.7803 0.21967C14.921 0.360322 15 0.551088 15 0.75V11.75C15 11.9489 14.921 12.1397 14.7803 12.2803C14.6397 12.421 14.4489 12.5 14.25 12.5H0.75C0.551088 12.5 0.360322 12.421 0.21967 12.2803C0.0790176 12.1397 0 11.9489 0 11.75V0.75C0 0.551088 0.0790176 0.360322 0.21967 0.21967C0.360322 0.0790176 0.551088 0 0.75 0ZM13.5 3.1785L7.554 8.5035L1.5 3.162V11H13.5V3.1785ZM1.88325 1.5L7.54575 6.4965L13.1265 1.5H1.88325Z",fill:"var(--fc-secondary-text)"},null,-1)])])):"contacts"===e.iconName?(a(),w("svg",ht,[...C[59]||(C[59]=[d("path",{d:"M5 3.75C4.65482 3.75 4.375 4.02982 4.375 4.375V5.625H5.625V5H14.375V15H5.625V14.375H4.375V15.625C4.375 15.9702 4.65482 16.25 5 16.25H15C15.3452 16.25 15.625 15.9702 15.625 15.625V4.375C15.625 4.02982 15.3452 3.75 15 3.75H5ZM8.125 12.5C8.125 11.4644 8.96444 10.625 10 10.625C11.0356 10.625 11.875 11.4644 11.875 12.5H8.125ZM10 10C9.30963 10 8.75 9.44037 8.75 8.75C8.75 8.05964 9.30963 7.5 10 7.5C10.6904 7.5 11.25 8.05964 11.25 8.75C11.25 9.44037 10.6904 10 10 10ZM6.25 8.125V6.875H3.75V8.125H6.25ZM6.25 9.375V10.625H3.75V9.375H6.25ZM6.25 13.125V11.875H3.75V13.125H6.25Z",fill:"var(--fc-secondary-text)"},null,-1)])])):"resend-email"===e.iconName?(a(),w("svg",st,[...C[60]||(C[60]=[d("path",{d:"M3 3.53173C3 3.23821 3.23442 3 3.52326 3H17.4767C17.7656 3 18 3.23821 18 3.53173V10.6214H16.9535V4.06345H4.04651V15.0525H11.1977V16.1159H3.52326C3.23442 16.1159 3 15.8777 3 15.5842V3.53173ZM6.02372 7.16377L6.60419 6.27898L10.5 8.91847L14.3958 6.27898L14.9763 7.16377L10.5 10.196L6.02372 7.16377Z",fill:"var(--fc-secondary-text)"},null,-1),d("path",{d:"M12.9785 15.5889H14.6895V16.1895H14.0918C14.4736 16.5081 14.9642 16.7002 15.5 16.7002C16.715 16.7002 17.7002 15.715 17.7002 14.5C17.7002 14.4171 17.6954 14.3351 17.6865 14.2549L17.6533 13.957L18.25 13.8906L18.2832 14.1895C18.2945 14.2915 18.2998 14.3952 18.2998 14.5C18.2998 16.0463 17.0463 17.2998 15.5 17.2998C14.7556 17.2998 14.0794 17.0086 13.5781 16.5352V17.2998H12.9785V15.5889ZM12.7002 14.5C12.7002 12.9536 13.9536 11.7002 15.5 11.7002C16.2447 11.7002 16.9215 11.9911 17.4229 12.4648V11.7002H18.0225V13.4111H16.3115V12.8115H16.9102C16.5281 12.4921 16.0366 12.2998 15.5 12.2998C14.285 12.2998 13.2998 13.285 13.2998 14.5C13.2998 14.5828 13.3046 14.6649 13.3135 14.7451L13.3467 15.043L12.75 15.1084L12.7168 14.8105C12.7055 14.7085 12.7002 14.6048 12.7002 14.5Z",fill:"var(--fc-secondary-text)"},null,-1)])])):"pause"===e.iconName?(a(),w("svg",at,[...C[61]||(C[61]=[d("path",{d:"M5.5 4.75H7V15.25H5.5V4.75ZM13 4.75H14.5V15.25H13V4.75Z",fill:"CurrentColor"},null,-1)])])):"calendarWithTime"===e.iconName?(a(),w("svg",wt,[...C[62]||(C[62]=[d("path",{d:"M6.875 4.375V3.125H8.125V4.375H11.875V3.125H13.125V4.375H15.625C15.9702 4.375 16.25 4.65482 16.25 5V8.125H15V5.625H13.125V6.875H11.875V5.625H8.125V6.875H6.875V5.625H5V14.375H8.75V15.625H4.375C4.02982 15.625 3.75 15.3452 3.75 15V5C3.75 4.65482 4.02982 4.375 4.375 4.375H6.875ZM13.125 10C11.7443 10 10.625 11.1193 10.625 12.5C10.625 13.8807 11.7443 15 13.125 15C14.5057 15 15.625 13.8807 15.625 12.5C15.625 11.1193 14.5057 10 13.125 10ZM9.375 12.5C9.375 10.4289 11.0539 8.75 13.125 8.75C15.1961 8.75 16.875 10.4289 16.875 12.5C16.875 14.5711 15.1961 16.25 13.125 16.25C11.0539 16.25 9.375 14.5711 9.375 12.5ZM12.5 10.625V12.7589L13.9331 14.1919L14.8169 13.3081L13.75 12.2411V10.625H12.5Z",fill:"CurrentColor"},null,-1)])])):"calendar"===e.iconName?(a(),w("svg",dt,[...C[63]||(C[63]=[d("path",{d:"M13.75 3.25H16.75C16.9489 3.25 17.1397 3.32902 17.2803 3.46967C17.421 3.61032 17.5 3.80109 17.5 4V16C17.5 16.1989 17.421 16.3897 17.2803 16.5303C17.1397 16.671 16.9489 16.75 16.75 16.75H3.25C3.05109 16.75 2.86032 16.671 2.71967 16.5303C2.57902 16.3897 2.5 16.1989 2.5 16V4C2.5 3.80109 2.57902 3.61032 2.71967 3.46967C2.86032 3.32902 3.05109 3.25 3.25 3.25H6.25V1.75H7.75V3.25H12.25V1.75H13.75V3.25ZM12.25 4.75H7.75V6.25H6.25V4.75H4V7.75H16V4.75H13.75V6.25H12.25V4.75ZM16 9.25H4V15.25H16V9.25Z",fill:"currentColor"},null,-1)])])):"circleCheck"===e.iconName?(a(),w("svg",ct,[...C[64]||(C[64]=[d("path",{d:"M12 21C7.0293 21 3 16.9707 3 12C3 7.0293 7.0293 3 12 3C16.9707 3 21 7.0293 21 12C21 16.9707 16.9707 21 12 21ZM12 19.2C13.9096 19.2 15.7409 18.4414 17.0912 17.0912C18.4414 15.7409 19.2 13.9096 19.2 12C19.2 10.0904 18.4414 8.25909 17.0912 6.90883C15.7409 5.55857 13.9096 4.8 12 4.8C10.0904 4.8 8.25909 5.55857 6.90883 6.90883C5.55857 8.25909 4.8 10.0904 4.8 12C4.8 13.9096 5.55857 15.7409 6.90883 17.0912C8.25909 18.4414 10.0904 19.2 12 19.2ZM11.1027 15.6L7.284 11.7813L8.5566 10.5087L11.1027 13.0548L16.1931 7.9635L17.4666 9.2361L11.1027 15.6Z",fill:"currentColor"},null,-1)])])):"checkFill"===e.iconName?(a(),w("svg",gt,[...C[65]||(C[65]=[d("path",{d:"M10 17.5C5.85775 17.5 2.5 14.1422 2.5 10C2.5 5.85775 5.85775 2.5 10 2.5C14.1422 2.5 17.5 5.85775 17.5 10C17.5 14.1422 14.1422 17.5 10 17.5ZM9.25225 13L14.5547 7.69675L13.4943 6.63625L9.25225 10.879L7.1305 8.75725L6.07 9.81775L9.25225 13Z",fill:"currentColor"},null,-1)])])):"fluentforms"===e.iconName?(a(),w("svg",Ht,[...C[66]||(C[66]=[d("rect",{width:"40",height:"40",rx:"4",fill:"#089DFF"},null,-1),d("path",{d:"M5.42493 14.2692C5.42493 11.1218 7.97639 8.57031 11.1238 8.57031H33.6983C33.6983 11.7177 31.1468 14.2692 27.9994 14.2692H5.42493Z",fill:"white"},null,-1),d("path",{d:"M5.42505 23.1324C5.42505 19.9851 7.97651 17.4336 11.1239 17.4336H33.6984C33.6984 20.581 31.1469 23.1324 27.9995 23.1324H5.42505Z",fill:"white"},null,-1),d("path",{d:"M9.82336 31.9996C9.82336 28.8522 12.3748 26.3008 15.5222 26.3008H28.6722C28.6722 29.4482 26.1208 31.9996 22.9734 31.9996H9.82336Z",fill:"white"},null,-1)])])):"download"===e.iconName?(a(),w("svg",Vt,[...C[67]||(C[67]=[d("path",{d:"M10.75 8.5H14.5L10 13L5.5 8.5H9.25V3.25H10.75V8.5ZM4 15.25H16V10H17.5V16C17.5 16.1989 17.421 16.3897 17.2803 16.5303C17.1397 16.671 16.9489 16.75 16.75 16.75H3.25C3.05109 16.75 2.86032 16.671 2.71967 16.5303C2.57902 16.3897 2.5 16.1989 2.5 16V10H4V15.25Z",fill:"currentColor"},null,-1)])])):"envelope"===e.iconName?(a(),w("svg",vt,[...C[68]||(C[68]=[d("path",{d:"M3.25 3.75H16.75C16.9489 3.75 17.1397 3.82902 17.2803 3.96967C17.421 4.11032 17.5 4.30109 17.5 4.5V15.5C17.5 15.6989 17.421 15.8897 17.2803 16.0303C17.1397 16.171 16.9489 16.25 16.75 16.25H3.25C3.05109 16.25 2.86032 16.171 2.71967 16.0303C2.57902 15.8897 2.5 15.6989 2.5 15.5V4.5C2.5 4.30109 2.57902 4.11032 2.71967 3.96967C2.86032 3.82902 3.05109 3.75 3.25 3.75ZM16 6.9285L10.054 12.2535L4 6.912V14.75H16V6.9285ZM4.38325 5.25L10.0457 10.2465L15.6265 5.25H4.38325Z",fill:"currentColor"},null,-1)])])):"envelopeOpen"===e.iconName?(a(),w("svg",Lt,[...C[69]||(C[69]=[d("path",{d:"M2.68225 6.14037L9.6175 1.98237C9.73406 1.91243 9.86744 1.87549 10.0034 1.87549C10.1393 1.87549 10.2727 1.91243 10.3892 1.98237L17.3177 6.14112C17.3733 6.17443 17.4194 6.22158 17.4513 6.27797C17.4832 6.33436 17.5 6.39806 17.5 6.46287V15.9999C17.5 16.1988 17.421 16.3895 17.2803 16.5302C17.1397 16.6709 16.9489 16.7499 16.75 16.7499H3.25C3.05109 16.7499 2.86032 16.6709 2.71967 16.5302C2.57902 16.3895 2.5 16.1988 2.5 15.9999V6.46212C2.49999 6.39731 2.51677 6.33361 2.54871 6.27722C2.58065 6.22083 2.62666 6.17368 2.68225 6.14037ZM4 7.09962V15.2499H16V7.09887L10.003 3.49887L4 7.09887V7.09962ZM10.045 11.2734L14.017 7.92612L14.983 9.07362L10.0555 13.2264L5.023 9.07887L5.977 7.92087L10.045 11.2734Z",fill:"currentColor"},null,-1)])])):"envelopeWithClick"===e.iconName?(a(),w("svg",pt,[...C[70]||(C[70]=[d("path",{d:"M10.9055 7.65787L11.6492 9.70187L10.2801 10.2002L9.53614 8.15619L8.3999 8.86871L8.872 4.2002L12.2341 7.4734L10.9058 7.65787H10.9055ZM10.9023 9.35334L10.1111 7.17936L10.9737 7.05988L9.33011 5.45999L9.09989 7.7418L9.83717 7.27932L10.6284 9.45329L10.9023 9.35334Z",fill:"currentColor"},null,-1),d("path",{d:"M15.5283 2.375V6.28125L17.625 8.37695V8.63672L17.6064 16.9941C17.6059 17.3508 17.315 17.6249 16.9746 17.625H3.00684C2.66946 17.6249 2.37501 17.3535 2.375 16.9932V8.38965L4.47559 6.26953V2.375H15.5283ZM3.625 16.375H16.3574L16.3721 9.62012L10.3018 12.9385L10.002 13.1016L9.70215 12.9385L3.625 9.61621V16.375ZM5.72559 9.33984L10.002 11.6768L14.2783 9.33984V3.625H5.72559V9.33984ZM4.08203 8.44238L4.47559 8.65625V8.0459L4.08203 8.44238ZM15.5283 8.65625L15.9219 8.44141L15.5283 8.04785V8.65625Z",fill:"currentColor"},null,-1)])])):"unsubscribe"===e.iconName?(a(),w("svg",Mt,[...C[71]||(C[71]=[d("path",{d:"M17.5 11.5H16V6.4285L10.054 11.7535L4 6.412V15.25H12.25V16.75H3.25C3.05109 16.75 2.86032 16.671 2.71967 16.5303C2.57902 16.3897 2.5 16.1989 2.5 16V4C2.5 3.80109 2.57902 3.61032 2.71967 3.46967C2.86032 3.32902 3.05109 3.25 3.25 3.25H16.75C16.9489 3.25 17.1397 3.32902 17.2803 3.46967C17.421 3.61032 17.5 3.80109 17.5 4V11.5ZM4.38325 4.75L10.0458 9.7465L15.6265 4.75H4.38325ZM17.0605 15.25L18.652 16.8407L17.5908 17.902L16 16.3105L14.4093 17.902L13.348 16.8407L14.9395 15.25L13.348 13.6593L14.4093 12.598L16 14.1895L17.5908 12.598L18.652 13.6593L17.0605 15.25Z",fill:"CurrentColor"},null,-1)])])):"click"===e.iconName?(a(),w("svg",mt,[...C[72]||(C[72]=[d("path",{d:"M12.5408 11.1234L14.4548 16.3839L10.9313 17.6664L9.01654 12.4059L6.09229 14.2396L7.30729 2.22461L15.96 10.6486L12.5415 11.1234H12.5408ZM12.5325 15.4869L10.4963 9.89186L12.7163 9.58436L8.48629 5.46686L7.89379 11.3394L9.79129 10.1491L11.8275 15.7441L12.5325 15.4869V15.4869Z",fill:"currentColor"},null,-1)])])):"wallet"===e.iconName?(a(),w("svg",ft,[...C[73]||(C[73]=[d("path",{d:"M14.5 6.25H16.75C16.9489 6.25 17.1397 6.32902 17.2803 6.46967C17.421 6.61032 17.5 6.80109 17.5 7V16C17.5 16.1989 17.421 16.3897 17.2803 16.5303C17.1397 16.671 16.9489 16.75 16.75 16.75H3.25C3.05109 16.75 2.86032 16.671 2.71967 16.5303C2.57902 16.3897 2.5 16.1989 2.5 16V4C2.5 3.80109 2.57902 3.61032 2.71967 3.46967C2.86032 3.32902 3.05109 3.25 3.25 3.25H14.5V6.25ZM4 7.75V15.25H16V7.75H4ZM4 4.75V6.25H13V4.75H4ZM12.25 10.75H14.5V12.25H12.25V10.75Z",fill:"CurrentColor"},null,-1)])])):"externalLink"===e.iconName?(a(),w("svg",ut,[...C[74]||(C[74]=[d("path",{d:"M8.5 5.5V7H4.75V15.25H13V11.5H14.5V16C14.5 16.1989 14.421 16.3897 14.2803 16.5303C14.1397 16.671 13.9489 16.75 13.75 16.75H4C3.80109 16.75 3.61032 16.671 3.46967 16.5303C3.32902 16.3897 3.25 16.1989 3.25 16V6.25C3.25 6.05109 3.32902 5.86032 3.46967 5.71967C3.61032 5.57902 3.80109 5.5 4 5.5H8.5ZM16.75 3.25V9.25H15.25V5.80975L9.40525 11.6553L8.34475 10.5948L14.1888 4.75H10.75V3.25H16.75Z",fill:"CurrentColor"},null,-1)])])):"paperPlane"===e.iconName?(a(),w("svg",Zt,[...C[75]||(C[75]=[d("path",{d:"M13.5373 6.62961C13.9492 6.48017 14.1536 6.42639 14.2669 6.40715C14.2677 6.44336 14.2669 6.47273 14.265 6.49199C14.1035 8.18916 13.3958 12.3646 13.0324 14.3075C12.9473 14.7625 12.882 14.9791 12.4426 14.721C12.1533 14.551 11.8916 14.3371 11.6112 14.1533C10.6913 13.5502 9.37459 12.6522 9.48272 12.7062C8.5771 12.1094 8.95619 11.744 9.46174 11.2566C9.54362 11.1777 9.62876 11.0956 9.7123 11.0088C9.74959 10.9701 9.93202 10.8004 10.1892 10.5613C11.0143 9.79425 12.6087 8.31198 12.649 8.14044C12.6556 8.1123 12.6617 8.00741 12.5993 7.95201C12.537 7.89662 12.4451 7.91557 12.3787 7.93063C12.2846 7.95198 10.7861 8.94244 7.88322 10.902C7.45788 11.194 7.07263 11.3364 6.72744 11.3289L6.73054 11.3304C6.27585 11.1701 5.82244 11.032 5.36664 10.8932C5.0672 10.802 4.76674 10.7104 4.4642 10.6121C4.39021 10.5881 4.31801 10.5652 4.25017 10.5439C7.3939 9.17439 9.48779 8.27267 10.537 7.83626C12.061 7.2024 12.9649 6.83731 13.5373 6.62961ZM15.2197 5.29047C15.0437 5.14768 14.8503 5.08214 14.7159 5.05005C14.5772 5.0169 14.4453 5.00623 14.3453 5.008C14.0145 5.01382 13.6591 5.10791 13.0635 5.32402C12.456 5.54447 11.5225 5.92212 10.0036 6.55387C8.93279 6.99927 6.8009 7.91761 3.61324 9.3064C3.33324 9.41834 3.07658 9.54536 2.87379 9.69591C2.68653 9.83501 2.4244 10.0838 2.37645 10.4664C2.34023 10.7553 2.42447 11.022 2.5953 11.232C2.74402 11.4147 2.93456 11.5258 3.07965 11.5961C3.29504 11.7004 3.58484 11.7912 3.83954 11.8711C4.23754 11.9958 4.63462 12.1232 5.03347 12.2453C6.26828 12.6233 7.28477 12.9344 8.4032 13.6714C9.22355 14.212 10.0281 14.7762 10.8498 15.3148C11.1495 15.5113 11.4297 15.7367 11.7391 15.9185C12.0771 16.1171 12.5068 16.2982 13.0299 16.25C13.8204 16.1773 14.2316 15.4501 14.3976 14.5629C14.7601 12.6249 15.4794 8.39148 15.6476 6.62358C15.6696 6.39282 15.6439 6.13418 15.6225 6.0093C15.6005 5.88071 15.5332 5.54493 15.2197 5.29047Z",fill:"currentColor"},null,-1)])])):"delete"===e.iconName||"trash"===e.iconName?(a(),w("svg",xt,[...C[76]||(C[76]=[d("path",{d:"M13.75 5.5H17.5V7H16V16.75C16 16.9489 15.921 17.1397 15.7803 17.2803C15.6397 17.421 15.4489 17.5 15.25 17.5H4.75C4.55109 17.5 4.36032 17.421 4.21967 17.2803C4.07902 17.1397 4 16.9489 4 16.75V7H2.5V5.5H6.25V3.25C6.25 3.05109 6.32902 2.86032 6.46967 2.71967C6.61032 2.57902 6.80109 2.5 7 2.5H13C13.1989 2.5 13.3897 2.57902 13.5303 2.71967C13.671 2.86032 13.75 3.05109 13.75 3.25V5.5ZM14.5 7H5.5V16H14.5V7ZM7.75 9.25H9.25V13.75H7.75V9.25ZM10.75 9.25H12.25V13.75H10.75V9.25ZM7.75 4V5.5H12.25V4H7.75Z",fill:"currentColor"},null,-1)])])):"phone"===e.iconName?(a(),w("svg",kt,[...C[77]||(C[77]=[d("path",{d:"M8.0245 9.0115C8.72825 10.2479 9.75214 11.2717 10.9885 11.9755L11.6515 11.047C11.7581 10.8977 11.9158 10.7927 12.0946 10.7518C12.2734 10.7108 12.4611 10.7369 12.622 10.825C13.6827 11.4047 14.8542 11.7533 16.0593 11.848C16.2473 11.8629 16.4229 11.9482 16.5509 12.0867C16.6789 12.2253 16.75 12.4071 16.75 12.5958V15.9423C16.75 16.1279 16.6812 16.3071 16.5568 16.4449C16.4324 16.5828 16.2612 16.6696 16.0765 16.6885C15.679 16.7298 15.2785 16.75 14.875 16.75C8.455 16.75 3.25 11.545 3.25 5.125C3.25 4.7215 3.27025 4.321 3.3115 3.9235C3.33044 3.73877 3.41724 3.56764 3.55509 3.44323C3.69295 3.31881 3.87205 3.24996 4.05775 3.25H7.40425C7.59292 3.24998 7.77467 3.32106 7.91326 3.44909C8.05185 3.57711 8.1371 3.75267 8.152 3.94075C8.24667 5.14584 8.59531 6.31726 9.175 7.378C9.2631 7.53892 9.28916 7.72656 9.24825 7.9054C9.20734 8.08424 9.1023 8.24188 8.953 8.3485L8.0245 9.0115V9.0115ZM6.133 8.51875L7.558 7.501C7.15359 6.62807 6.87651 5.70163 6.73525 4.75H4.7575C4.753 4.8745 4.75075 4.99975 4.75075 5.125C4.75 10.717 9.283 15.25 14.875 15.25C15.0003 15.25 15.1255 15.2478 15.25 15.2425V13.2648C14.2984 13.1235 13.3719 12.8464 12.499 12.442L11.4813 13.867C11.0715 13.7078 10.6735 13.5198 10.2903 13.3045L10.2468 13.2798C8.77568 12.4425 7.55746 11.2243 6.72025 9.75325L6.6955 9.70975C6.48018 9.3265 6.29221 8.9285 6.133 8.51875V8.51875Z",fill:"CurrentColor"},null,-1)])])):"rightArrow"===e.iconName?(a(),w("svg",yt,[...C[78]||(C[78]=[d("path",{d:"M13.129 9.24952L9.106 5.22652L10.1665 4.16602L16 9.99952L10.1665 15.833L9.106 14.7725L13.129 10.7495H4V9.24952H13.129Z",fill:"CurrentColor"},null,-1)])])):"chevron-right"===e.iconName?(a(),w("svg",qt,[...C[79]||(C[79]=[d("path",{d:"M12.955 11.9991L8.5 7.54408L9.7726 6.27148L15.5002 11.9991L9.7726 17.7267L8.5 16.4541L12.955 11.9991Z",fill:"currentColor"},null,-1)])])):"leftArrow"===e.iconName?(a(),w("svg",Nt,[...C[80]||(C[80]=[d("path",{d:"M6.871 9.24952H16V10.7495H6.871L10.894 14.7725L9.8335 15.833L4 9.99952L9.8335 4.16602L10.894 5.22652L6.871 9.24952Z",fill:"CurrentColor"},null,-1)])])):"circleFilled"===e.iconName?(a(),w("svg",Bt,[...C[81]||(C[81]=[d("path",{d:"M10 17.5C5.85775 17.5 2.5 14.1422 2.5 10C2.5 5.85775 5.85775 2.5 10 2.5C14.1422 2.5 17.5 5.85775 17.5 10C17.5 14.1422 14.1422 17.5 10 17.5ZM9.25225 13L14.5547 7.69675L13.4943 6.63625L9.25225 10.879L7.1305 8.75725L6.07 9.81775L9.25225 13Z",fill:"CurrentColor"},null,-1)])])):"more"===e.iconName?(a(),w("svg",zt,[...C[82]||(C[82]=[d("path",{d:"M10 3.25C9.38125 3.25 8.875 3.75625 8.875 4.375C8.875 4.99375 9.38125 5.5 10 5.5C10.6187 5.5 11.125 4.99375 11.125 4.375C11.125 3.75625 10.6187 3.25 10 3.25ZM10 14.5C9.38125 14.5 8.875 15.0063 8.875 15.625C8.875 16.2438 9.38125 16.75 10 16.75C10.6187 16.75 11.125 16.2438 11.125 15.625C11.125 15.0063 10.6187 14.5 10 14.5ZM10 8.875C9.38125 8.875 8.875 9.38125 8.875 10C8.875 10.6188 9.38125 11.125 10 11.125C10.6187 11.125 11.125 10.6188 11.125 10C11.125 9.38125 10.6187 8.875 10 8.875Z",fill:"CurrentColor"},null,-1)])])):"close"===e.iconName?(a(),w("svg",Et,[...C[83]||(C[83]=[d("path",{d:"M14.0659 6.2876L10.353 10.0005L14.0649 13.7124L13.7123 14.0649L10.0004 10.353L6.28754 14.0659L5.93402 13.7124L9.2934 10.354L9.64691 10.0005L5.93402 6.2876L6.28754 5.93408L10.0004 9.64697L10.3539 9.29346L13.7123 5.93408L14.0659 6.2876Z",fill:"var(--fc-text-muted)",stroke:"currentColor"},null,-1)])])):"gutenberg"===e.iconName?(a(),w("svg",bt,[...C[84]||(C[84]=[d("path",{d:"M8 2.5L12.75 5.25V10.75L8 13.5L3.25 10.75V5.25L8 2.5ZM4.74694 5.53885L8.00005 7.4222L11.2531 5.53887L8 3.6555L4.74694 5.53885ZM4.25 6.40664V10.1735L7.50005 12.055V8.28825L4.25 6.40664ZM8.50005 12.055L11.75 10.1735V6.40668L8.50005 8.28825V12.055Z",fill:"currentColor"},null,-1)])])):"visualBuilder"===e.iconName?(a(),w("svg",Dt,[...C[85]||(C[85]=[d("path",{d:"M9.2 4.4H10.4V5.6H13.4C13.5591 5.6 13.7117 5.66321 13.8243 5.77574C13.9368 5.88826 14 6.04087 14 6.2V10.7L10.4 8.6L10.4216 13.4372L11.7554 12.1472L12.8246 14H6.2C6.04087 14 5.88826 13.9368 5.77574 13.8243C5.66321 13.7117 5.6 13.5591 5.6 13.4V10.4H4.4V9.2H5.6V6.2C5.6 6.04087 5.66321 5.88826 5.77574 5.77574C5.88826 5.66321 6.04087 5.6 6.2 5.6H9.2V4.4ZM14 11.2028V13.4C14.0001 13.4625 13.9903 13.5247 13.9712 13.5842L12.7952 11.5478L14 11.2028ZM3.2 9.2V10.4H2V9.2H3.2ZM3.2 6.8V8H2V6.8H3.2ZM3.2 4.4V5.6H2V4.4H3.2ZM3.2 2V3.2H2V2H3.2ZM5.6 2V3.2H4.4V2H5.6ZM8 2V3.2H6.8V2H8ZM10.4 2V3.2H9.2V2H10.4Z",fill:"currentColor"},null,-1)])])):"rawHTML"===e.iconName?(a(),w("svg",St,[...C[86]||(C[86]=[d("path",{d:"M12.7996 14H3.19961C3.04048 14 2.88787 13.9368 2.77535 13.8243C2.66282 13.7117 2.59961 13.5591 2.59961 13.4V2.6C2.59961 2.44087 2.66282 2.28826 2.77535 2.17574C2.88787 2.06321 3.04048 2 3.19961 2H12.7996C12.9587 2 13.1114 2.06321 13.2239 2.17574C13.3364 2.28826 13.3996 2.44087 13.3996 2.6V13.4C13.3996 13.5591 13.3364 13.7117 13.2239 13.8243C13.1114 13.9368 12.9587 14 12.7996 14ZM12.1996 12.8V3.2H3.79961V12.8H12.1996ZM5.59961 5H10.3996V6.2H5.59961V5ZM5.59961 7.4H10.3996V8.6H5.59961V7.4ZM5.59961 9.8H10.3996V11H5.59961V9.8Z",fill:"currentColor"},null,-1)])])):"classicEditor"===e.iconName?(a(),w("svg",_t,[...C[87]||(C[87]=[d("path",{d:"M15.1998 8.00156L11.8056 11.3958L10.9572 10.5474L13.503 8.00156L10.9572 5.45576L11.8056 4.60736L15.1998 8.00156ZM2.4966 8.00156L5.0424 10.5474L4.194 11.3958L0.799805 8.00156L4.194 4.60736L5.0418 5.45576L2.4966 8.00156ZM6.6726 13.4016H5.3958L9.327 2.60156H10.6038L6.6726 13.4016Z",fill:"currentColor"},null,-1)])])):"EditPen"===e.iconName?(a(),w("svg",Ft,[...C[88]||(C[88]=[d("path",{d:"M4.6484 10.4001L10.7336 4.31485L9.8852 3.46645L3.8 9.55165V10.4001H4.6484ZM5.1458 11.6001H2.6V9.05425L9.461 2.19325C9.57352 2.08077 9.7261 2.01758 9.8852 2.01758C10.0443 2.01758 10.1969 2.08077 10.3094 2.19325L12.0068 3.89065C12.1193 4.00317 12.1825 4.15575 12.1825 4.31485C12.1825 4.47395 12.1193 4.62653 12.0068 4.73905L5.1458 11.6001V11.6001ZM2.6 12.8001H13.4V14.0001H2.6V12.8001Z",fill:"currentColor"},null,-1)])])):"downicon"===e.iconName||"downIcon"===e.iconName?(a(),w("svg",It,[...C[89]||(C[89]=[d("path",{d:"M10.0001 10.8785L13.7126 7.16602L14.7731 8.22652L10.0001 12.9995L5.22705 8.22652L6.28755 7.16602L10.0001 10.8785Z",fill:"currentColor"},null,-1)])])):"star"===e.iconName?(a(),w("svg",At,[...C[90]||(C[90]=[d("path",{d:"M10 14.695L4.71025 17.656L5.8915 11.71L1.44025 7.594L7.4605 6.88L10 1.375L12.5395 6.88L18.5597 7.594L14.1085 11.71L15.2897 17.656L10 14.695ZM10 12.976L13.1852 14.7587L12.4735 11.179L15.1532 8.70025L11.5285 8.2705L10 4.95625L8.4715 8.27125L4.84675 8.70025L7.5265 11.179L6.81475 14.7587L10 12.976V12.976Z",fill:"currentColor"},null,-1)])])):"arrow-down"===e.iconName?(a(),w("svg",Ot,[...C[91]||(C[91]=[d("path",{d:"M3.8184 2.97L6.7884 0L7.6368 0.8484L3.8184 4.6668L0 0.8484L0.8484 0L3.8184 2.97Z",fill:"var(--fc-secondary-text)"},null,-1)])])):"arrow-long-down"===e.iconName?(a(),w("svg",Tt,[...C[92]||(C[92]=[d("path",{d:"M8.59981 10.5024L11.8182 7.28402L12.6666 8.13242L7.99981 12.7992L3.33301 8.13242L4.18141 7.28402L7.39981 10.5024V3.19922H8.59981V10.5024Z",fill:"currentColor"},null,-1)])])):"arrow-up"===e.iconName?(a(),w("svg",jt,[...C[93]||(C[93]=[d("path",{d:"M3.8184 1.6968L0.8484 4.6668L0 3.8184L3.8184 0L7.6368 3.8184L6.7884 4.6668L3.8184 1.6968Z",fill:"var(--fc-secondary-text)"},null,-1)])])):"bar-chart"===e.iconName?(a(),w("svg",Gt,[...C[94]||(C[94]=[d("path",{d:"M2.5 10.75H7V16.75H2.5V10.75ZM13 7H17.5V16.75H13V7ZM7.75 3.25H12.25V16.75H7.75V3.25ZM4 12.25V15.25H5.5V12.25H4ZM9.25 4.75V15.25H10.75V4.75H9.25ZM14.5 8.5V15.25H16V8.5H14.5Z",fill:"currentColor"},null,-1)])])):"line-chart"===e.iconName?(a(),w("svg",$t,[...C[95]||(C[95]=[d("path",{d:"M4.75 3.25V15.25H16.75V16.75H3.25V3.25H4.75ZM16.2197 5.71975L17.2802 6.78025L13 11.0605L10.75 8.81125L7.53025 12.0303L6.46975 10.9698L10.75 6.6895L13 8.93875L16.2197 5.71975V5.71975Z",fill:"currentColor"},null,-1)])])):"configure"===e.iconName?(a(),w("svg",Ut,[...C[96]||(C[96]=[d("path",{d:"M5.625 6.875C5.625 6.35723 6.04473 5.9375 6.5625 5.9375C7.08027 5.9375 7.5 6.35723 7.5 6.875C7.5 7.39277 7.08027 7.8125 6.5625 7.8125C6.04473 7.8125 5.625 7.39277 5.625 6.875ZM6.5625 4.6875C5.35438 4.6875 4.375 5.66688 4.375 6.875C4.375 8.08312 5.35438 9.0625 6.5625 9.0625C7.77062 9.0625 8.75 8.08312 8.75 6.875C8.75 5.66688 7.77062 4.6875 6.5625 4.6875ZM10 7.5H15V6.25H10V7.5ZM12.5 13.125C12.5 12.6072 12.9197 12.1875 13.4375 12.1875C13.9553 12.1875 14.375 12.6072 14.375 13.125C14.375 13.6428 13.9553 14.0625 13.4375 14.0625C12.9197 14.0625 12.5 13.6428 12.5 13.125ZM13.4375 10.9375C12.2294 10.9375 11.25 11.9169 11.25 13.125C11.25 14.3331 12.2294 15.3125 13.4375 15.3125C14.6456 15.3125 15.625 14.3331 15.625 13.125C15.625 11.9169 14.6456 10.9375 13.4375 10.9375ZM5 12.5V13.75H10V12.5H5Z",fill:"currentColor"},null,-1)])])):"arrow-left"===e.iconName?(a(),w("svg",Yt,[...C[97]||(C[97]=[d("path",{d:"M8.24525 11.1002H19.2V12.9002H8.24525L13.0728 17.7278L11.8002 19.0004L4.80005 12.0002L11.8002 5L13.0728 6.2726L8.24525 11.1002Z",fill:"currentColor"},null,-1)])])):"settings"===e.iconName||"gear"===e.iconName?(a(),w("svg",Pt,[...C[98]||(C[98]=[d("path",{d:"M2.5 9.99998C2.5 9.35123 2.5825 8.72273 2.737 8.12198C3.15135 8.14377 3.56365 8.05055 3.92833 7.85264C4.29301 7.65472 4.59586 7.35983 4.8034 7.00054C5.01095 6.64126 5.1151 6.23158 5.10436 5.8168C5.09361 5.40202 4.96837 4.99829 4.7425 4.65023C5.64921 3.75816 6.7681 3.11161 7.99375 2.77148C8.18199 3.14159 8.46898 3.45238 8.82294 3.66947C9.1769 3.88655 9.58402 4.00145 9.99925 4.00145C10.4145 4.00145 10.8216 3.88655 11.1756 3.66947C11.5295 3.45238 11.8165 3.14159 12.0048 2.77148C13.2304 3.11161 14.3493 3.75816 15.256 4.65023C15.0299 4.99835 14.9045 5.40224 14.8936 5.81721C14.8828 6.23218 14.987 6.64206 15.1946 7.00149C15.4023 7.36093 15.7054 7.65591 16.0703 7.8538C16.4352 8.05168 16.8477 8.14476 17.2623 8.12273C17.4167 8.72273 17.4993 9.35123 17.4993 9.99998C17.4993 10.6487 17.4167 11.2772 17.2623 11.878C16.8478 11.8561 16.4354 11.9492 16.0706 12.147C15.7059 12.3449 15.4029 12.6398 15.1953 12.9991C14.9876 13.3584 14.8834 13.7681 14.8941 14.183C14.9048 14.5978 15.0301 15.0016 15.256 15.3497C14.3493 16.2418 13.2304 16.8884 12.0048 17.2285C11.8165 16.8584 11.5295 16.5476 11.1756 16.3305C10.8216 16.1134 10.4145 15.9985 9.99925 15.9985C9.58402 15.9985 9.1769 16.1134 8.82294 16.3305C8.46898 16.5476 8.18199 16.8584 7.99375 17.2285C6.7681 16.8884 5.64921 16.2418 4.7425 15.3497C4.96863 15.0016 5.09405 14.5977 5.10488 14.1828C5.11571 13.7678 5.01152 13.3579 4.80386 12.9985C4.59619 12.639 4.29314 12.3441 3.92823 12.1462C3.56332 11.9483 3.15078 11.8552 2.73625 11.8772C2.5825 11.278 2.5 10.6495 2.5 9.99998ZM6.103 12.25C6.5755 13.0682 6.7105 14.0095 6.526 14.893C6.832 15.1105 7.1575 15.2987 7.49875 15.4555C8.18625 14.8396 9.07699 14.4993 10 14.5C10.945 14.5 11.8285 14.8532 12.5013 15.4555C12.8425 15.2987 13.168 15.1105 13.474 14.893C13.2846 13.99 13.4352 13.0488 13.897 12.25C14.358 11.4508 15.0978 10.8499 15.9745 10.5625C16.0092 10.1883 16.0092 9.81168 15.9745 9.43748C15.0975 9.15028 14.3574 8.54935 13.8962 7.74998C13.4345 6.95118 13.2838 6.01001 13.4733 5.10698C13.1673 4.88943 12.8417 4.7011 12.5005 4.54448C11.8132 5.16018 10.9228 5.50044 10 5.49998C9.07699 5.50063 8.18625 5.16036 7.49875 4.54448C7.1576 4.7011 6.83192 4.88943 6.526 5.10698C6.71542 6.01001 6.56479 6.95118 6.103 7.74998C5.64203 8.5492 4.90224 9.15012 4.0255 9.43748C3.99081 9.81168 3.99081 10.1883 4.0255 10.5625C4.90252 10.8497 5.6426 11.4506 6.10375 12.25H6.103ZM10 12.25C9.40326 12.25 8.83097 12.0129 8.40901 11.591C7.98705 11.169 7.75 10.5967 7.75 9.99998C7.75 9.40325 7.98705 8.83095 8.40901 8.40899C8.83097 7.98704 9.40326 7.74998 10 7.74998C10.5967 7.74998 11.169 7.98704 11.591 8.40899C12.0129 8.83095 12.25 9.40325 12.25 9.99998C12.25 10.5967 12.0129 11.169 11.591 11.591C11.169 12.0129 10.5967 12.25 10 12.25ZM10 10.75C10.1989 10.75 10.3897 10.671 10.5303 10.5303C10.671 10.3897 10.75 10.1989 10.75 9.99998C10.75 9.80107 10.671 9.61031 10.5303 9.46965C10.3897 9.329 10.1989 9.24998 10 9.24998C9.80109 9.24998 9.61032 9.329 9.46967 9.46965C9.32902 9.61031 9.25 9.80107 9.25 9.99998C9.25 10.1989 9.32902 10.3897 9.46967 10.5303C9.61032 10.671 9.80109 10.75 10 10.75Z",fill:"currentColor"},null,-1)])])):"play"===e.iconName?(a(),w("svg",Rt,[...C[99]||(C[99]=[d("path",{d:"M10 17.5C5.85775 17.5 2.5 14.1422 2.5 10C2.5 5.85775 5.85775 2.5 10 2.5C14.1422 2.5 17.5 5.85775 17.5 10C17.5 14.1422 14.1422 17.5 10 17.5ZM10 16C11.5913 16 13.1174 15.3679 14.2426 14.2426C15.3679 13.1174 16 11.5913 16 10C16 8.4087 15.3679 6.88258 14.2426 5.75736C13.1174 4.63214 11.5913 4 10 4C8.4087 4 6.88258 4.63214 5.75736 5.75736C4.63214 6.88258 4 8.4087 4 10C4 11.5913 4.63214 13.1174 5.75736 14.2426C6.88258 15.3679 8.4087 16 10 16V16ZM8.9665 7.31125L12.6258 9.75025C12.6669 9.77764 12.7006 9.81476 12.724 9.85834C12.7473 9.90191 12.7595 9.95057 12.7595 10C12.7595 10.0494 12.7473 10.0981 12.724 10.1417C12.7006 10.1852 12.6669 10.2224 12.6258 10.2498L8.96575 12.6888C8.92062 12.7187 8.86824 12.7358 8.81415 12.7384C8.76007 12.7409 8.7063 12.7288 8.65856 12.7033C8.61081 12.6777 8.57086 12.6398 8.54294 12.5934C8.51503 12.547 8.50019 12.4939 8.5 12.4398V7.56025C8.5001 7.50599 8.51492 7.45277 8.54287 7.40626C8.57082 7.35975 8.61086 7.3217 8.65873 7.29615C8.7066 7.27059 8.76051 7.25851 8.8147 7.26117C8.8689 7.26383 8.92136 7.28113 8.9665 7.31125V7.31125Z",fill:"currentColor"},null,-1)])])):"user"===e.iconName?(a(),w("svg",Kt,[...C[100]||(C[100]=[d("path",{d:"M4 18C4 16.3834 4.63214 14.8331 5.75736 13.69C6.88258 12.5469 8.4087 11.9048 10 11.9048C11.5913 11.9048 13.1174 12.5469 14.2426 13.69C15.3679 14.8331 16 16.3834 16 18H14.5C14.5 16.7876 14.0259 15.6248 13.182 14.7675C12.3381 13.9102 11.1935 13.4286 10 13.4286C8.80653 13.4286 7.66193 13.9102 6.81802 14.7675C5.97411 15.6248 5.5 16.7876 5.5 18H4ZM10 11.1429C7.51375 11.1429 5.5 9.09714 5.5 6.57143C5.5 4.04571 7.51375 2 10 2C12.4862 2 14.5 4.04571 14.5 6.57143C14.5 9.09714 12.4862 11.1429 10 11.1429ZM10 9.61905C11.6575 9.61905 13 8.25524 13 6.57143C13 4.88762 11.6575 3.52381 10 3.52381C8.3425 3.52381 7 4.88762 7 6.57143C7 8.25524 8.3425 9.61905 10 9.61905Z",fill:"currentColor"},null,-1)])])):"users"===e.iconName?(a(),w("svg",Wt,[...C[101]||(C[101]=[d("path",{d:"M2.5 17.5C2.5 15.9087 3.13214 14.3826 4.25736 13.2574C5.38258 12.1321 6.9087 11.5 8.5 11.5C10.0913 11.5 11.6174 12.1321 12.7426 13.2574C13.8679 14.3826 14.5 15.9087 14.5 17.5H13C13 16.3065 12.5259 15.1619 11.682 14.318C10.8381 13.4741 9.69347 13 8.5 13C7.30653 13 6.16193 13.4741 5.31802 14.318C4.47411 15.1619 4 16.3065 4 17.5H2.5ZM8.5 10.75C6.01375 10.75 4 8.73625 4 6.25C4 3.76375 6.01375 1.75 8.5 1.75C10.9863 1.75 13 3.76375 13 6.25C13 8.73625 10.9863 10.75 8.5 10.75ZM8.5 9.25C10.1575 9.25 11.5 7.9075 11.5 6.25C11.5 4.5925 10.1575 3.25 8.5 3.25C6.8425 3.25 5.5 4.5925 5.5 6.25C5.5 7.9075 6.8425 9.25 8.5 9.25ZM14.713 12.0273C15.767 12.5019 16.6615 13.2709 17.2889 14.2418C17.9164 15.2126 18.2501 16.344 18.25 17.5H16.75C16.7502 16.633 16.4999 15.7844 16.0293 15.0562C15.5587 14.328 14.8878 13.7512 14.0972 13.3953L14.7123 12.0273H14.713ZM14.197 3.55975C14.9526 3.87122 15.5987 4.40015 16.0533 5.07942C16.5078 5.75869 16.7503 6.55768 16.75 7.375C16.7503 8.40425 16.3658 9.39642 15.6719 10.1566C14.978 10.9168 14.025 11.3901 13 11.4835V9.97375C13.5557 9.89416 14.0713 9.63851 14.471 9.24434C14.8707 8.85017 15.1335 8.33824 15.2209 7.7837C15.3082 7.22916 15.2155 6.66122 14.9563 6.16327C14.6971 5.66531 14.2851 5.26356 13.7808 5.017L14.197 3.55975V3.55975Z",fill:"currentColor"},null,-1)])])):"action"===e.iconName?(a(),w("svg",Qt,[...C[102]||(C[102]=[d("path",{d:"M8.60019 6.20078H13.4002L7.4002 15.2008V9.80078H3.2002L8.60019 0.800781V6.20078ZM7.4002 7.40078V5.13278L5.3194 8.60078H8.60019V11.2372L11.158 7.40078H7.4002Z",fill:"currentColor"},null,-1)])])):"conditions"===e.iconName?(a(),w("svg",Xt,[...C[103]||(C[103]=[d("path",{d:"M5.06277 6.07409C5.16685 6.45499 5.39325 6.79112 5.70712 7.03072C6.02098 7.27032 6.40491 7.40011 6.79977 7.40009H9.19977C9.90659 7.40014 10.5907 7.64971 11.1315 8.1048C11.6723 8.55989 12.0351 9.19128 12.156 9.88769C12.5632 10.0205 12.9097 10.2942 13.1333 10.6596C13.3569 11.0249 13.4429 11.458 13.3759 11.8811C13.3089 12.3042 13.0933 12.6895 12.7678 12.9679C12.4422 13.2463 12.0281 13.3995 11.5998 13.4001C11.1807 13.4004 10.7746 13.2545 10.4516 12.9875C10.1286 12.7205 9.90885 12.3492 9.83029 11.9375C9.75173 11.5259 9.81926 11.0997 10.0213 10.7325C10.2232 10.3653 10.547 10.0801 10.9368 9.92609C10.8327 9.54519 10.6063 9.20906 10.2924 8.96946C9.97856 8.72986 9.59464 8.60008 9.19977 8.60009H6.79977C6.15051 8.60105 5.51861 8.39042 4.99977 8.00009V9.90209C5.40027 10.0436 5.73783 10.3222 5.95279 10.6886C6.16775 11.0549 6.24626 11.4855 6.17446 11.9042C6.10265 12.3228 5.88515 12.7026 5.5604 12.9764C5.23564 13.2502 4.82454 13.4004 4.39977 13.4004C3.975 13.4004 3.5639 13.2502 3.23915 12.9764C2.91439 12.7026 2.69689 12.3228 2.62509 11.9042C2.55328 11.4855 2.6318 11.0549 2.84676 10.6886C3.06172 10.3222 3.39928 10.0436 3.79977 9.90209V6.09809C3.40196 5.95764 3.06609 5.68195 2.85078 5.31915C2.63548 4.95635 2.5544 4.52946 2.62169 4.11298C2.68899 3.6965 2.90039 3.31688 3.21901 3.04035C3.53762 2.76382 3.94322 2.60795 4.36503 2.59993C4.78683 2.59192 5.19806 2.73227 5.52695 2.99649C5.85584 3.26072 6.08151 3.63204 6.16458 4.04566C6.24766 4.45928 6.18285 4.88894 5.98148 5.25966C5.78011 5.63038 5.45496 5.91862 5.06277 6.07409V6.07409ZM4.39977 5.00009C4.5589 5.00009 4.71151 4.93688 4.82404 4.82436C4.93656 4.71183 4.99977 4.55922 4.99977 4.40009C4.99977 4.24096 4.93656 4.08835 4.82404 3.97583C4.71151 3.86331 4.5589 3.80009 4.39977 3.80009C4.24064 3.80009 4.08803 3.86331 3.97551 3.97583C3.86299 4.08835 3.79977 4.24096 3.79977 4.40009C3.79977 4.55922 3.86299 4.71183 3.97551 4.82436C4.08803 4.93688 4.24064 5.00009 4.39977 5.00009ZM4.39977 12.2001C4.5589 12.2001 4.71151 12.1369 4.82404 12.0244C4.93656 11.9118 4.99977 11.7592 4.99977 11.6001C4.99977 11.441 4.93656 11.2883 4.82404 11.1758C4.71151 11.0633 4.5589 11.0001 4.39977 11.0001C4.24064 11.0001 4.08803 11.0633 3.97551 11.1758C3.86299 11.2883 3.79977 11.441 3.79977 11.6001C3.79977 11.7592 3.86299 11.9118 3.97551 12.0244C4.08803 12.1369 4.24064 12.2001 4.39977 12.2001ZM11.5998 12.2001C11.7589 12.2001 11.9115 12.1369 12.024 12.0244C12.1366 11.9118 12.1998 11.7592 12.1998 11.6001C12.1998 11.441 12.1366 11.2883 12.024 11.1758C11.9115 11.0633 11.7589 11.0001 11.5998 11.0001C11.4406 11.0001 11.288 11.0633 11.1755 11.1758C11.063 11.2883 10.9998 11.441 10.9998 11.6001C10.9998 11.7592 11.063 11.9118 11.1755 12.0244C11.288 12.1369 11.4406 12.2001 11.5998 12.2001Z",fill:"currentColor"},null,-1)])])):"upload"===e.iconName?(a(),w("svg",Jt,[...C[104]||(C[104]=[d("path",{d:"M12.0001 12.5274L15.8188 16.3452L14.5452 17.6187L12.9001 15.9735V21H11.1V15.9717L9.45485 17.6187L8.18135 16.3452L12.0001 12.5274ZM12.0001 3C13.5453 3.00007 15.0367 3.568 16.1906 4.59581C17.3445 5.62361 18.0805 7.03962 18.2587 8.5746C19.3785 8.87998 20.3554 9.56919 21.0186 10.5218C21.6819 11.4744 21.9893 12.6297 21.8871 13.786C21.7849 14.9422 21.2797 16.0257 20.4597 16.8472C19.6396 17.6687 18.557 18.1759 17.401 18.2802V16.4676C17.8151 16.4085 18.2133 16.2674 18.5724 16.0527C18.9314 15.8379 19.2441 15.5539 19.4922 15.217C19.7402 14.8801 19.9187 14.4972 20.0171 14.0906C20.1156 13.6839 20.1321 13.2618 20.0656 12.8488C19.9991 12.4357 19.851 12.0401 19.63 11.6849C19.4089 11.3297 19.1194 11.0221 18.7781 10.78C18.4369 10.538 18.0509 10.3663 17.6426 10.2751C17.2343 10.1838 16.812 10.1748 16.4002 10.2486C16.5411 9.5924 16.5335 8.91297 16.3778 8.2601C16.2222 7.60722 15.9225 6.99743 15.5007 6.47538C15.0789 5.95333 14.5456 5.53225 13.94 5.24298C13.3343 4.9537 12.6717 4.80357 12.0005 4.80357C11.3293 4.80357 10.6667 4.9537 10.061 5.24298C9.45539 5.53225 8.92214 5.95333 8.50031 6.47538C8.07849 6.99743 7.77879 7.60722 7.62315 8.2601C7.46752 8.91297 7.4599 9.5924 7.60085 10.2486C6.77974 10.0944 5.93101 10.2727 5.24136 10.7443C4.55171 11.2159 4.07765 11.9421 3.92345 12.7632C3.76925 13.5843 3.94756 14.433 4.41914 15.1227C4.89072 15.8123 5.61694 16.2864 6.43805 16.4406L6.60005 16.4676V18.2802C5.44396 18.1761 4.36122 17.669 3.54107 16.8476C2.72093 16.0261 2.21555 14.9426 2.11326 13.7863C2.01097 12.6301 2.31828 11.4747 2.98148 10.522C3.64468 9.56934 4.62159 8.88005 5.74145 8.5746C5.91939 7.03954 6.65532 5.62342 7.80927 4.59558C8.96323 3.56774 10.4547 2.99988 12.0001 3Z",fill:"currentColor"},null,-1)])])):"simple-upload"===e.iconName?(a(),w("svg",tC,[...C[105]||(C[105]=[d("path",{d:"M3.5 10.5V15.75H16.5V10.5H17V16C17 16.0663 16.9736 16.1299 16.9268 16.1768C16.8799 16.2236 16.8163 16.25 16.75 16.25H3.25C3.18369 16.25 3.12012 16.2236 3.07324 16.1768C3.02636 16.1299 3 16.0663 3 16V10.5H3.5ZM13.293 7.25H10.25V12.5H9.75V7.25H6.70703L10 3.95703L13.293 7.25Z",fill:"#565865",stroke:"#565865"},null,-1)])])):"save"===e.iconName?(a(),w("svg",CC,[...C[106]||(C[106]=[d("path",{d:"M6.25 15.25V10.75H13.75V15.25H15.25V6.871L13.129 4.75H4.75V15.25H6.25ZM4 3.25H13.75L16.75 6.25V16C16.75 16.1989 16.671 16.3897 16.5303 16.5303C16.3897 16.671 16.1989 16.75 16 16.75H4C3.80109 16.75 3.61032 16.671 3.46967 16.5303C3.32902 16.3897 3.25 16.1989 3.25 16V4C3.25 3.80109 3.32902 3.61032 3.46967 3.46967C3.61032 3.32902 3.80109 3.25 4 3.25V3.25ZM7.75 12.25V15.25H12.25V12.25H7.75Z",fill:"currentColor"},null,-1)])])):"sync"===e.iconName?(a(),w("svg",eC,[...C[107]||(C[107]=[d("path",{d:"M5.5 4H16.75C16.9489 4 17.1397 4.07902 17.2803 4.21967C17.421 4.36032 17.5 4.55109 17.5 4.75V10H16V5.5H5.5V7.75L1.75 4.75L5.5 1.75V4ZM14.5 16H3.25C3.05109 16 2.86032 15.921 2.71967 15.7803C2.57902 15.6397 2.5 15.4489 2.5 15.25V10H4V14.5H14.5V12.25L18.25 15.25L14.5 18.25V16Z",fill:"currentColor"},null,-1)])])):"disable"===e.iconName?(a(),w("svg",lC,[...C[108]||(C[108]=[d("path",{d:"M3.18394 2.30006L10.1999 9.31606C10.8585 8.46913 11.25 7.40569 11.25 6.25C11.25 3.48857 9.01144 1.25 6.25 1.25C5.09431 1.25 4.03087 1.64152 3.18394 2.30006ZM9.31606 10.1999L2.30006 3.18394C1.64152 4.03087 1.25 5.09431 1.25 6.25C1.25 9.01144 3.48857 11.25 6.25 11.25C7.40569 11.25 8.46913 10.8585 9.31606 10.1999ZM1.83058 1.83058C2.96092 0.700237 4.52428 0 6.25 0C9.70175 0 12.5 2.79822 12.5 6.25C12.5 7.97569 11.7998 9.53906 10.6694 10.6694C9.53906 11.7998 7.97569 12.5 6.25 12.5C2.79822 12.5 0 9.70175 0 6.25C0 4.52428 0.700237 2.96092 1.83058 1.83058Z",fill:"currentColor"},null,-1)])])):"total_automations"==e.iconName||"automation"==e.iconName?(a(),w("svg",iC,[...C[109]||(C[109]=[d("path",{d:"M11.125 2.5C11.125 2.83319 10.9802 3.13254 10.75 3.33854V4.75H14.5C15.7427 4.75 16.75 5.75736 16.75 7V14.5C16.75 15.7427 15.7427 16.75 14.5 16.75H5.5C4.25736 16.75 3.25 15.7427 3.25 14.5V7C3.25 5.75736 4.25736 4.75 5.5 4.75H9.25V3.33854C9.01982 3.13254 8.875 2.83319 8.875 2.5C8.875 1.87868 9.3787 1.375 10 1.375C10.6213 1.375 11.125 1.87868 11.125 2.5ZM5.5 6.25C5.08579 6.25 4.75 6.58579 4.75 7V14.5C4.75 14.9142 5.08579 15.25 5.5 15.25H14.5C14.9142 15.25 15.25 14.9142 15.25 14.5V7C15.25 6.58579 14.9142 6.25 14.5 6.25H10.75H9.25H5.5ZM2.5 8.5H1V13H2.5V8.5ZM17.5 8.5H19V13H17.5V8.5ZM7.75 11.875C8.37132 11.875 8.875 11.3713 8.875 10.75C8.875 10.1287 8.37132 9.625 7.75 9.625C7.12868 9.625 6.625 10.1287 6.625 10.75C6.625 11.3713 7.12868 11.875 7.75 11.875ZM12.25 11.875C12.8713 11.875 13.375 11.3713 13.375 10.75C13.375 10.1287 12.8713 9.625 12.25 9.625C11.6287 9.625 11.125 10.1287 11.125 10.75C11.125 11.3713 11.6287 11.875 12.25 11.875Z",fill:"currentColor"},null,-1)])])):"filter"===e.iconName?(a(),w("svg",oC,[...C[110]||(C[110]=[d("path",{d:"M16.75 4V5.5H16L12.25 11.125V17.5H7.75V11.125L4 5.5H3.25V4H16.75ZM5.803 5.5L9.25 10.6705V16H10.75V10.6705L14.197 5.5H5.803Z",fill:"currentColor"},null,-1)])])):"column"===e.iconName?(a(),w("svg",nC,[...C[111]||(C[111]=[d("path",{d:"M9.25 4.75H4.75V15.25H9.25V4.75ZM10.75 4.75V15.25H15.25V4.75H10.75ZM4 3.25H16C16.1989 3.25 16.3897 3.32902 16.5303 3.46967C16.671 3.61032 16.75 3.80109 16.75 4V16C16.75 16.1989 16.671 16.3897 16.5303 16.5303C16.3897 16.671 16.1989 16.75 16 16.75H4C3.80109 16.75 3.61032 16.671 3.46967 16.5303C3.32902 16.3897 3.25 16.1989 3.25 16V4C3.25 3.80109 3.32902 3.61032 3.46967 3.46967C3.61032 3.32902 3.80109 3.25 4 3.25Z",fill:"currentColor"},null,-1)])])):"picture"===e.iconName||"image"===e.iconName?(a(),w("svg",rC,[...C[112]||(C[112]=[d("path",{d:"M16.75 12.25V14.5H19V16H16.75V18.25H15.25V16H13V14.5H15.25V12.25H16.75ZM16.756 3.25C17.167 3.25 17.5 3.58375 17.5 3.99475V10.75H16V4.75H4V15.2493L11.5 7.75L13.75 10V12.1218L11.5 9.87175L6.12025 15.25H11.5V16.75H3.244C3.04661 16.7498 2.85737 16.6712 2.71787 16.5316C2.57836 16.392 2.5 16.2026 2.5 16.0052V3.99475C2.50137 3.79778 2.58018 3.60926 2.71938 3.46991C2.85859 3.33056 3.04704 3.25157 3.244 3.25H16.756ZM7 6.25C7.39782 6.25 7.77936 6.40804 8.06066 6.68934C8.34196 6.97064 8.5 7.35218 8.5 7.75C8.5 8.14782 8.34196 8.52936 8.06066 8.81066C7.77936 9.09196 7.39782 9.25 7 9.25C6.60218 9.25 6.22064 9.09196 5.93934 8.81066C5.65804 8.52936 5.5 8.14782 5.5 7.75C5.5 7.35218 5.65804 6.97064 5.93934 6.68934C6.22064 6.40804 6.60218 6.25 7 6.25V6.25Z",fill:"currentColor"},null,-1)])])):"text"===e.iconName?(a(),w("svg",hC,[...C[113]||(C[113]=[d("path",{d:"M12.8 14H3.19998C3.04085 14 2.88823 13.9368 2.77571 13.8243C2.66319 13.7117 2.59998 13.5591 2.59998 13.4V2.6C2.59998 2.44087 2.66319 2.28826 2.77571 2.17574C2.88823 2.06321 3.04085 2 3.19998 2H12.8C12.9591 2 13.1117 2.06321 13.2242 2.17574C13.3368 2.28826 13.4 2.44087 13.4 2.6V13.4C13.4 13.5591 13.3368 13.7117 13.2242 13.8243C13.1117 13.9368 12.9591 14 12.8 14ZM12.2 12.8V3.2H3.79998V12.8H12.2ZM5.59998 5H10.4V6.2H5.59998V5ZM5.59998 7.4H10.4V8.6H5.59998V7.4ZM5.59998 9.8H10.4V11H5.59998V9.8Z",fill:"currentColor"},null,-1)])])):"info"===e.iconName?(a(),w("svg",{key:110,class:c(e.iconClass),xmlns:"http://www.w3.org/2000/svg",width:"20",height:"20",viewBox:"0 0 20 20",fill:"none"},[...C[114]||(C[114]=[d("path",{"fill-rule":"evenodd","clip-rule":"evenodd",d:"M10 16.25C13.4518 16.25 16.25 13.4518 16.25 10C16.25 6.54822 13.4518 3.75 10 3.75C6.54822 3.75 3.75 6.54822 3.75 10C3.75 13.4518 6.54822 16.25 10 16.25ZM11.1158 13.2086L11.2156 12.8006C11.164 12.8249 11.0807 12.8526 10.9665 12.8841C10.852 12.9157 10.7489 12.9318 10.6583 12.9318C10.4654 12.9318 10.3295 12.9001 10.2507 12.8366C10.1724 12.773 10.1333 12.6534 10.1333 12.4783C10.1333 12.4089 10.1451 12.3054 10.1697 12.17C10.1936 12.0337 10.2211 11.9126 10.2516 11.8067L10.6242 10.4876C10.6607 10.3665 10.6857 10.2334 10.6992 10.0882C10.7129 9.94325 10.7193 9.84185 10.7193 9.78429C10.7193 9.50614 10.6218 9.28041 10.4268 9.10629C10.2317 8.93229 9.95393 8.84529 9.59396 8.84529C9.39365 8.84529 9.18188 8.88088 8.95776 8.952C8.73363 9.02294 8.49933 9.1084 8.25421 9.2082L8.15415 9.6165C8.22719 9.58949 8.31419 9.56043 8.41598 9.53034C8.51732 9.50038 8.61674 9.48489 8.71347 9.48489C8.91096 9.48489 9.04399 9.51856 9.1137 9.58488C9.18342 9.65139 9.21844 9.7697 9.21844 9.93883C9.21844 10.0324 9.20736 10.1363 9.18438 10.2492C9.16172 10.3628 9.13342 10.483 9.10013 10.6098L8.72595 11.9342C8.69266 12.0734 8.66834 12.1979 8.65304 12.3084C8.63786 12.4189 8.63057 12.5272 8.63057 12.6326C8.63057 12.9048 8.73114 13.1292 8.93222 13.3063C9.13329 13.4826 9.41523 13.5714 9.77769 13.5714C10.0137 13.5714 10.2209 13.5406 10.3992 13.4785C10.5773 13.4167 10.8164 13.3268 11.1158 13.2086ZM11.0495 7.8502C11.2235 7.68882 11.3101 7.49254 11.3101 7.26272C11.3101 7.03341 11.2236 6.83675 11.0495 6.67331C10.8758 6.51032 10.6666 6.42857 10.4219 6.42857C10.1765 6.42857 9.96635 6.51013 9.79107 6.67331C9.61579 6.83675 9.52796 7.03334 9.52796 7.26272C9.52796 7.49254 9.61579 7.68875 9.79107 7.8502C9.96667 8.01217 10.1764 8.09321 10.4219 8.09321C10.6666 8.09321 10.8758 8.01217 11.0495 7.8502Z",fill:"currentColor"},null,-1)])],2)):"rocket"===e.iconName?(a(),w("svg",sC,[...C[115]||(C[115]=[d("path",{d:"M3.7996 8.60007C3.7996 5.54727 5.5414 2.93847 7.9996 1.89087C10.4578 2.93847 12.1996 5.54727 12.1996 8.60007C12.1996 9.09387 12.154 9.57567 12.0676 10.0419L13.2316 11.1411C13.2797 11.1866 13.3114 11.2468 13.3217 11.3123C13.332 11.3777 13.3204 11.4447 13.2886 11.5029L11.7916 14.2479C11.7693 14.2889 11.7377 14.3241 11.6994 14.3507C11.6611 14.3773 11.617 14.3947 11.5708 14.4013C11.5246 14.4079 11.4775 14.4037 11.4333 14.389C11.389 14.3742 11.3488 14.3493 11.3158 14.3163L9.97541 12.9759C9.86291 12.8633 9.71032 12.8001 9.55121 12.8001H6.448C6.28889 12.8001 6.1363 12.8633 6.0238 12.9759L4.6834 14.3163C4.65044 14.3493 4.61022 14.3742 4.56594 14.389C4.52167 14.4037 4.47456 14.4079 4.42836 14.4013C4.38217 14.3947 4.33816 14.3773 4.29983 14.3507C4.26151 14.3241 4.22993 14.2889 4.2076 14.2479L2.7106 11.5029C2.67882 11.4447 2.66717 11.3777 2.67748 11.3123C2.6878 11.2468 2.71948 11.1866 2.7676 11.1411L3.9316 10.0419C3.8458 9.57567 3.7996 9.09387 3.7996 8.60007ZM4.6852 12.6177L5.1754 12.1275C5.51289 11.7899 5.97065 11.6002 6.448 11.6001H9.55121C10.0286 11.6002 10.4863 11.7899 10.8238 12.1275L11.314 12.6177L11.9032 11.5377L11.2432 10.9137C11.0973 10.7759 10.9883 10.6038 10.9262 10.413C10.864 10.2222 10.8507 10.0189 10.8874 9.82167C10.9618 9.42327 10.9996 9.01467 10.9996 8.60007C10.9996 6.27807 9.8026 4.22007 7.9996 3.22407C6.1966 4.22007 4.9996 6.27807 4.9996 8.60007C4.9996 9.01467 5.0374 9.42327 5.1118 9.82227C5.14852 10.0195 5.13519 10.2228 5.07303 10.4136C5.01087 10.6044 4.90187 10.7765 4.756 10.9143L4.096 11.5377L4.6852 12.6177V12.6177ZM7.9996 8.60007C7.68135 8.60007 7.37612 8.47364 7.15108 8.2486C6.92603 8.02355 6.7996 7.71833 6.7996 7.40007C6.7996 7.08181 6.92603 6.77659 7.15108 6.55154C7.37612 6.3265 7.68135 6.20007 7.9996 6.20007C8.31786 6.20007 8.62309 6.3265 8.84813 6.55154C9.07318 6.77659 9.1996 7.08181 9.1996 7.40007C9.1996 7.71833 9.07318 8.02355 8.84813 8.2486C8.62309 8.47364 8.31786 8.60007 7.9996 8.60007Z",fill:"currentColor"},null,-1)])])):"shield"===e.iconName?(a(),w("svg",aC,[...C[116]||(C[116]=[d("path",{d:"M10 3L15.478 4.162C15.7827 4.22691 16 4.48464 16 4.78309V11.1385C16 12.415 15.3313 13.6075 14.2187 14.3152L10 17L5.78133 14.3152C4.668 13.6069 4 12.415 4 11.1391V4.78309C4 4.48464 4.21733 4.22691 4.522 4.162L10 3ZM10 4.30391L5.33333 5.29345V11.1385C5.33333 11.9893 5.77867 12.7841 6.52067 13.2563L10 15.4708L13.4793 13.2563C14.2213 12.7841 14.6667 11.9899 14.6667 11.1391V5.29345L10 4.30455V4.30391ZM12.968 7.59582L13.9113 8.49564L9.66867 12.5455L6.84 9.84536L7.78267 8.94555L9.668 10.7452L12.968 7.59518V7.59582Z",fill:"currentColor"},null,-1)])])):"double-optin"===e.iconName?(a(),w("svg",wC,[...C[117]||(C[117]=[d("path",{d:"M10 3C13.8661 3 17 6.13389 17 10V11.125C17.0001 11.5796 16.8541 12.022 16.584 12.3877C16.3139 12.7534 15.9336 13.0227 15.499 13.1562C15.0645 13.2898 14.5987 13.2807 14.1699 13.1299C13.7411 12.9791 13.3724 12.6945 13.1172 12.3184L12.7705 11.8086L12.3428 12.2529C11.909 12.704 11.3542 13.0206 10.7451 13.1641C10.1359 13.3075 9.49769 13.2718 8.9082 13.0615C8.31879 12.8513 7.80284 12.475 7.42188 11.9785C7.04089 11.482 6.81121 10.8855 6.76074 10.2617C6.71038 9.63812 6.84065 9.01306 7.13672 8.46191C7.43293 7.91059 7.88264 7.45656 8.43066 7.1543C8.97867 6.85207 9.60227 6.71422 10.2266 6.75781C10.8509 6.80142 11.4495 7.0249 11.9502 7.40039L12.083 7.5H13.25V11.125C13.25 11.556 13.4208 11.9697 13.7256 12.2744C14.0303 12.5792 14.444 12.75 14.875 12.75C15.306 12.75 15.7197 12.5792 16.0244 12.2744C16.3292 11.9697 16.5 11.556 16.5 11.125V10L16.4941 9.72852C16.4375 8.37609 15.9596 7.07265 15.124 6.00195C14.2327 4.85982 12.9854 4.04756 11.5801 3.69531C10.175 3.34321 8.69262 3.47109 7.36816 4.05762C6.04357 4.64429 4.95208 5.65629 4.26855 6.93359C3.58504 8.21101 3.34786 9.6808 3.59473 11.1084C3.84161 12.5359 4.55908 13.8398 5.63184 14.8135C6.70461 15.7871 8.07188 16.3746 9.5166 16.4824C10.7943 16.5777 12.0676 16.293 13.1787 15.6699L13.457 16.0879C12.4064 16.6859 11.2156 17.0019 10.001 17H10C6.13389 17 3 13.8661 3 10C3 6.13389 6.13389 3 10 3ZM10 7.25C9.27066 7.25 8.57139 7.53994 8.05566 8.05566C7.53994 8.57139 7.25 9.27066 7.25 10C7.25 10.7293 7.53994 11.4286 8.05566 11.9443C8.57139 12.4601 9.27065 12.75 10 12.75C10.7293 12.75 11.4286 12.4601 11.9443 11.9443C12.4601 11.4286 12.75 10.7293 12.75 10C12.75 9.27065 12.4601 8.57139 11.9443 8.05566C11.4286 7.53994 10.7293 7.25 10 7.25Z",stroke:"currentColor"},null,-1)])])):"duplicate"===e.iconName?(a(),w("svg",dC,[...C[118]||(C[118]=[d("path",{d:"M6.25 5.5V3.25C6.25 3.05109 6.32902 2.86032 6.46967 2.71967C6.61032 2.57902 6.80109 2.5 7 2.5H16C16.1989 2.5 16.3897 2.57902 16.5303 2.71967C16.671 2.86032 16.75 3.05109 16.75 3.25V13.75C16.75 13.9489 16.671 14.1397 16.5303 14.2803C16.3897 14.421 16.1989 14.5 16 14.5H13.75V16.75C13.75 17.164 13.4125 17.5 12.9948 17.5H4.00525C3.90635 17.5006 3.8083 17.4816 3.71674 17.4442C3.62519 17.4068 3.54192 17.3517 3.47174 17.282C3.40156 17.2123 3.34584 17.1294 3.30779 17.0381C3.26974 16.9468 3.2501 16.8489 3.25 16.75L3.25225 6.25C3.25225 5.836 3.58975 5.5 4.0075 5.5H6.25ZM4.75225 7L4.75 16H12.25V7H4.75225ZM7.75 5.5H13.75V13H15.25V4H7.75V5.5Z",fill:"currentColor"},null,-1)])])):"glob"===e.iconName?(a(),w("svg",cC,[...C[119]||(C[119]=[d("path",{d:"M10 17.5C5.85775 17.5 2.5 14.1422 2.5 10C2.5 5.85775 5.85775 2.5 10 2.5C14.1422 2.5 17.5 5.85775 17.5 10C17.5 14.1422 14.1422 17.5 10 17.5ZM8.2825 15.7502C7.54256 14.1807 7.1139 12.4827 7.02025 10.75H4.0465C4.19244 11.9042 4.67044 12.9911 5.42243 13.8788C6.17441 14.7664 7.16801 15.4166 8.2825 15.7502ZM8.5225 10.75C8.63575 12.5792 9.1585 14.2975 10 15.814C10.8642 14.2574 11.3691 12.5271 11.4775 10.75H8.5225ZM15.9535 10.75H12.9797C12.8861 12.4827 12.4574 14.1807 11.7175 15.7502C12.832 15.4166 13.8256 14.7664 14.5776 13.8788C15.3296 12.9911 15.8076 11.9042 15.9535 10.75ZM4.0465 9.25H7.02025C7.1139 7.51734 7.54256 5.81926 8.2825 4.24975C7.16801 4.58341 6.17441 5.23356 5.42243 6.12122C4.67044 7.00888 4.19244 8.09583 4.0465 9.25ZM8.52325 9.25H11.4767C11.3686 7.47295 10.864 5.74265 10 4.186C9.13576 5.74259 8.63092 7.47289 8.5225 9.25H8.52325ZM11.7175 4.24975C12.4574 5.81926 12.8861 7.51734 12.9797 9.25H15.9535C15.8076 8.09583 15.3296 7.00888 14.5776 6.12122C13.8256 5.23356 12.832 4.58341 11.7175 4.24975Z",fill:"currentColor"},null,-1)])])):"briefcase"===e.iconName?(a(),w("svg",gC,[...C[120]||(C[120]=[d("path",{d:"M6.25 4.75V2.5C6.25 2.30109 6.32902 2.11032 6.46967 1.96967C6.61032 1.82902 6.80109 1.75 7 1.75H13C13.1989 1.75 13.3897 1.82902 13.5303 1.96967C13.671 2.11032 13.75 2.30109 13.75 2.5V4.75H16.75C16.9489 4.75 17.1397 4.82902 17.2803 4.96967C17.421 5.11032 17.5 5.30109 17.5 5.5V16C17.5 16.1989 17.421 16.3897 17.2803 16.5303C17.1397 16.671 16.9489 16.75 16.75 16.75H3.25C3.05109 16.75 2.86032 16.671 2.71967 16.5303C2.57902 16.3897 2.5 16.1989 2.5 16V5.5C2.5 5.30109 2.57902 5.11032 2.71967 4.96967C2.86032 4.82902 3.05109 4.75 3.25 4.75H6.25ZM4 13V15.25H16V13H4ZM4 11.5H16V6.25H4V11.5ZM7.75 3.25V4.75H12.25V3.25H7.75ZM9.25 9.25H10.75V10.75H9.25V9.25Z",fill:"currentColor"},null,-1)])])):"building"===e.iconName?(a(),w("svg",HC,[...C[121]||(C[121]=[d("path",{d:"M16.75 15.25H18.25V16.75H1.75V15.25H3.25V4C3.25 3.80109 3.32902 3.61032 3.46967 3.46967C3.61032 3.32902 3.80109 3.25 4 3.25H11.5C11.6989 3.25 11.8897 3.32902 12.0303 3.46967C12.171 3.61032 12.25 3.80109 12.25 4V15.25H15.25V9.25H13.75V7.75H16C16.1989 7.75 16.3897 7.82902 16.5303 7.96967C16.671 8.11032 16.75 8.30109 16.75 8.5V15.25ZM4.75 4.75V15.25H10.75V4.75H4.75ZM6.25 9.25H9.25V10.75H6.25V9.25ZM6.25 6.25H9.25V7.75H6.25V6.25Z",fill:"currentColor"},null,-1)])])):"facebook"===e.iconName?(a(),w("svg",VC,[...C[122]||(C[122]=[d("path",{d:"M10 2.5C5.85775 2.5 2.5 5.85775 2.5 10C2.5 13.7433 5.24275 16.846 8.8285 17.4092V12.1675H6.9235V10H8.8285V8.34775C8.8285 6.46825 9.9475 5.43025 11.6613 5.43025C12.4818 5.43025 13.3397 5.5765 13.3397 5.5765V7.4215H12.3947C11.4625 7.4215 11.1722 7.99975 11.1722 8.593V10H13.252L12.9197 12.1675H11.1722V17.4092C14.7572 16.8467 17.5 13.7425 17.5 10C17.5 5.85775 14.1422 2.5 10 2.5Z",fill:"currentColor"},null,-1)])])):"linkedin"===e.iconName?(a(),w("svg",vC,[...C[123]||(C[123]=[d("path",{"fill-rule":"evenodd","clip-rule":"evenodd",d:"M16.8056 1.25H3.19444C2.12014 1.25 1.25 2.12014 1.25 3.19444V16.8056C1.25 17.8799 2.12014 18.75 3.19444 18.75H16.8056C17.8799 18.75 18.75 17.8799 18.75 16.8056V3.19444C18.75 2.12014 17.8799 1.25 16.8056 1.25ZM6.54132 16.3194H3.9309V7.89271H6.54132V16.3194ZM5.22396 6.78924C4.37083 6.78924 3.68056 6.0941 3.68056 5.23368C3.68056 4.37326 4.37083 3.67812 5.22396 3.67812C6.07708 3.67812 6.76736 4.37326 6.76736 5.23368C6.76736 6.0941 6.07708 6.78924 5.22396 6.78924ZM16.3194 16.3194H13.7236V11.8958C13.7236 10.683 13.2618 10.0049 12.3042 10.0049C11.259 10.0049 10.7146 10.7097 10.7146 11.8958V16.3194H8.21111V7.89271H10.7146V9.02778C10.7146 9.02778 11.4681 7.63507 13.2545 7.63507C15.041 7.63507 16.3219 8.72639 16.3219 10.9844V16.3194H16.3194Z",fill:"currentColor"},null,-1)])])):"x"===e.iconName||"twitter"===e.iconName?(a(),w("svg",LC,[...C[124]||(C[124]=[d("path",{d:"M7.5 3.75H3.125L8.28804 10.6341L3.40622 16.2499H5.06249L9.05519 11.6569L12.5 16.25H16.875L11.4948 9.07644L16.1251 3.75H14.4688L10.7277 8.05361L7.5 3.75ZM13.125 15L5.625 5H6.875L14.375 15H13.125Z",fill:"currentColor"},null,-1)])])):"spammed"===e.iconName?(a(),w("svg",pC,[...C[125]||(C[125]=[d("g",{"clip-path":"url(#clip0_14797_190301)"},[d("path",{d:"M14 3L18 10L14 17H6L2 10L6 3H14ZM13.1614 4.47368H6.83863L3.68073 10L6.83863 15.5263H13.1614L16.3193 10L13.1614 4.47368ZM9.27273 12.2105H10.7273V13.6842H9.27273V12.2105ZM9.27273 6.31579H10.7273V10.7368H9.27273V6.31579Z",fill:"currentColor"})],-1),d("defs",null,[d("clipPath",{id:"clip0_14797_190301"},[d("rect",{width:"20",height:"20",fill:"white"})])],-1)])])):"transactional"===e.iconName?(a(),w("svg",MC,[...C[126]||(C[126]=[d("g",{"clip-path":"url(#clip0_14797_190297)"},[d("path",{d:"M7.2 15.3679V16.6645C7.2 16.8498 7.0433 17 6.85 17C6.76812 17 6.68884 16.9725 6.62594 16.9222L3.74265 14.6192C3.59415 14.5005 3.57409 14.289 3.69784 14.1466C3.76434 14.0702 3.86285 14.0259 3.96672 14.0259H14.2C14.9732 14.0259 15.6 13.4251 15.6 12.684V7.31608H17V12.684C17 14.1662 15.7464 15.3679 14.2 15.3679H7.2ZM12.8 4.63215V3.33549C12.8 3.15021 12.9567 3 13.15 3C13.2319 3 13.3111 3.02752 13.3741 3.07776L16.2574 5.3809C16.4058 5.49952 16.4259 5.71107 16.3022 5.85341C16.2357 5.9299 16.1372 5.97412 16.0333 5.97412L5.8 5.97411C5.0268 5.97411 4.4 6.57493 4.4 7.31608V12.684H3V7.31608C3 5.83378 4.2536 4.63215 5.8 4.63215H12.8Z",fill:"currentColor"})],-1),d("defs",null,[d("clipPath",{id:"clip0_14797_190297"},[d("rect",{width:"20",height:"20",fill:"white"})])],-1)])])):"ai"===e.iconName?(a(),w("svg",mC,[...C[127]||(C[127]=[d("path",{d:"M9.9991 3C10.1943 3 10.3841 3.06406 10.5397 3.18199C10.6564 3.2705 10.7492 3.38584 10.8105 3.51729L10.8616 3.65356L10.8642 3.66522L11.88 7.6072C11.9117 7.73016 11.9758 7.84279 12.0656 7.93263C12.1554 8.02249 12.268 8.08733 12.391 8.11911L16.3339 9.13486L16.3429 9.13665V9.13755C16.4845 9.17662 16.6136 9.2493 16.7194 9.34913L16.8171 9.45761L16.896 9.58043C16.9642 9.70896 17 9.85305 17 10C17 10.1959 16.9358 10.3865 16.8171 10.5424C16.6984 10.6982 16.5317 10.8104 16.3429 10.8624L16.3339 10.8651L12.391 11.8809C12.268 11.9127 12.1554 11.9775 12.0656 12.0674C11.9758 12.1572 11.9117 12.2698 11.88 12.3928L10.8633 16.3348L10.8607 16.3464C10.808 16.5344 10.6952 16.7 10.5397 16.818C10.3841 16.9361 10.1935 17 9.99821 17C9.80298 16.9999 9.61315 16.936 9.45761 16.818C9.30206 16.7 9.18933 16.5344 9.13665 16.3464L9.13307 16.3348L8.11732 12.3928L8.08773 12.3023C8.05209 12.2146 7.99908 12.1347 7.93174 12.0674C7.84184 11.9775 7.72941 11.9126 7.6063 11.8809L3.66432 10.8642C3.65986 10.8631 3.6553 10.8619 3.65087 10.8607C3.46355 10.8075 3.29852 10.695 3.1811 10.5397C3.09296 10.4231 3.035 10.2872 3.01165 10.1443L3 10L3.01165 9.85566C3.035 9.71281 3.09296 9.57693 3.1811 9.4603C3.29852 9.30496 3.46354 9.19251 3.65087 9.13934L3.66432 9.13576L7.6063 8.11821C7.7293 8.0865 7.84186 8.0224 7.93174 7.93263C8.02162 7.84281 8.0855 7.73022 8.11732 7.6072L9.13397 3.66522L9.13665 3.65356H9.13755C9.19024 3.4656 9.30297 3.29997 9.4585 3.18199L9.58133 3.104C9.7095 3.0363 9.8527 3.00001 9.9991 3ZM9.229 7.89408C9.14588 8.21551 8.9779 8.50928 8.74308 8.74398C8.50825 8.97865 8.21464 9.14607 7.89319 9.229L4.90599 9.9991L7.89319 10.7701L8.01242 10.8051C8.28729 10.896 8.53756 11.0505 8.74308 11.256C8.94862 11.4616 9.10316 11.7118 9.19403 11.9867L9.229 12.1059L9.99821 15.0931L10.7692 12.1059C10.8522 11.7844 11.0195 11.4909 11.2542 11.256C11.4891 11.0211 11.7833 10.8531 12.105 10.7701L15.0931 10L12.105 9.22989C11.7833 9.14691 11.4891 8.97888 11.2542 8.74398C11.0488 8.53848 10.895 8.2881 10.8042 8.01332L10.7692 7.89408L9.9991 4.90779L9.229 7.89408ZM4.28381 14.4978V14.4297H4.21568C3.89888 14.4297 3.64205 14.1727 3.64191 13.8559C3.64191 13.539 3.89879 13.2821 4.21568 13.2821H4.28381V13.2131C4.28381 12.8964 4.54089 12.6396 4.85758 12.6393C5.17447 12.6393 5.43135 12.8962 5.43135 13.2131V13.2821H5.50038C5.81727 13.2821 6.07415 13.539 6.07415 13.8559C6.07401 14.1727 5.81718 14.4297 5.50038 14.4297H5.43135V14.4978C5.43135 14.8147 5.17447 15.0716 4.85758 15.0716C4.54089 15.0714 4.28381 14.8146 4.28381 14.4978ZM14.566 6.78689V6.07595H13.8541C13.5375 6.07575 13.2806 5.81875 13.2804 5.50218C13.2804 5.18541 13.5374 4.9286 13.8541 4.92841H14.566V4.21657C14.566 3.89969 14.8228 3.6428 15.1397 3.6428C15.4566 3.64282 15.7135 3.8997 15.7135 4.21657V4.92841H16.4253C16.742 4.92863 16.9991 5.18543 16.9991 5.50218C16.9989 5.81873 16.7419 6.07572 16.4253 6.07595H15.7135V6.78689C15.7135 7.10376 15.4566 7.36064 15.1397 7.36066C14.8228 7.36066 14.566 7.10377 14.566 6.78689Z",fill:"#8762F0"},null,-1)])])):"cart"===e.iconName?(a(),w("svg",fC,[...C[128]||(C[128]=[d("path",{d:"M5.5 15.75C5.76522 15.75 6.0195 15.8554 6.20703 16.043C6.39457 16.2305 6.5 16.4848 6.5 16.75C6.5 17.0152 6.39457 17.2695 6.20703 17.457C6.0195 17.6446 5.76522 17.75 5.5 17.75C5.23478 17.75 4.9805 17.6446 4.79297 17.457C4.60543 17.2695 4.5 17.0152 4.5 16.75C4.5 16.4848 4.60543 16.2305 4.79297 16.043C4.9805 15.8554 5.23478 15.75 5.5 15.75ZM14.5 15.75C14.7652 15.75 15.0195 15.8554 15.207 16.043C15.3946 16.2305 15.5 16.4848 15.5 16.75C15.5 17.0152 15.3946 17.2695 15.207 17.457C15.0195 17.6446 14.7652 17.75 14.5 17.75C14.2348 17.75 13.9805 17.6446 13.793 17.457C13.6054 17.2695 13.5 17.0152 13.5 16.75C13.5 16.4848 13.6054 16.2305 13.793 16.043C13.9805 15.8554 14.2348 15.75 14.5 15.75ZM4.75 3C4.8163 3 4.87987 3.02636 4.92676 3.07324C4.97364 3.12013 5 3.1837 5 3.25V12.75H15.2188L16.9688 5.75H7.5V5.25H17.29C17.328 5.25001 17.3653 5.25878 17.3994 5.27539C17.4336 5.29206 17.4639 5.31672 17.4873 5.34668C17.5105 5.37653 17.5263 5.41126 17.5342 5.44824C17.542 5.48538 17.5414 5.52373 17.5322 5.56055L15.6572 13.0605C15.6437 13.1146 15.6123 13.163 15.5684 13.1973C15.5245 13.2314 15.4706 13.25 15.415 13.25H4.75C4.68369 13.25 4.62012 13.2236 4.57324 13.1768C4.52636 13.1299 4.5 13.0663 4.5 13V3.5H3V3H4.75Z",stroke:"currentColor"},null,-1)])])):(a(),g(h,{key:125},{default:H(()=>[V(xC)],void 0),_:1}))}]]),ZC="fluent_theme_mode",xC="onFcrmThemeChange",kC=class t{static getSystemTheme(){return window.matchMedia&&window.matchMedia("(prefers-color-scheme: dark)").matches?this.MODE_DARK:this.MODE_LIGHT}static getCurrentTheme(){const t=[this.MODE_LIGHT,this.MODE_DARK,this.MODE_SYSTEM];try{const C=localStorage.getItem(ZC);if(C){if(C.startsWith("system"))return this.MODE_SYSTEM;if(t.includes(C))return C}}catch(C){console.warn("localStorage unavailable, using default theme")}return this.MODE_LIGHT}static isSystem(){return this.getCurrentTheme()===this.MODE_SYSTEM}static apply(t){const C=t,e=t===this.MODE_SYSTEM?this.getSystemTheme():t,l=e===this.MODE_DARK;document.body.classList.remove("fcrm-dark","fcrm-light"),l?document.body.classList.contains("fluent_theme_dark")||document.body.classList.add("fluent_theme_dark"):document.body.classList.remove("fluent_theme_dark");try{const l=t===this.MODE_SYSTEM?`system:${e}`:C;localStorage.setItem(ZC,l)}catch(i){console.warn("localStorage unavailable, theme preference not saved")}window.dispatchEvent(new CustomEvent(xC,{detail:{theme:C,effective:e}}))}listenForSystemChange(){window.matchMedia("(prefers-color-scheme: dark)").addEventListener("change",()=>{t.isSystem()&&t.apply(t.MODE_SYSTEM)})}init(){this.listenForSystemChange(),t.apply(t.getCurrentTheme())}};C(kC,"MODE_LIGHT","light"),C(kC,"MODE_DARK","dark"),C(kC,"MODE_SYSTEM","system");let yC=kC;(new yC).init();const qC=()=>{var t;return(null==(t=window.fcAdmin)?void 0:t.is_rtl)?"bottom-left":"bottom-right"},NC=t=>{const C="string"==typeof t?{message:t}:t||{};return h({position:qC(),...C})};["success","warning","info","error"].forEach(t=>{NC[t]=C=>{const e="string"==typeof C?{message:C}:C||{};return h[t]({position:qC(),...e})}}),NC.close=h.close,NC.closeAll=h.closeAll;export{uC as I,yC as T,L as _,xC as a,NC as n}; diff --git a/wp-content/plugins/fluent-crm/assets/fc-bits.js b/wp-content/plugins/fluent-crm/assets/fc-bits.js new file mode 100644 index 0000000..53969b2 --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/fc-bits.js @@ -0,0 +1 @@ +import{m as e}from"./vendor.js?ver=3.1.8";const t={},n=function(e,n,s){let r=Promise.resolve();if(n&&n.length>0){const e=document.getElementsByTagName("link"),o=document.querySelector("meta[property=csp-nonce]"),i=(null==o?void 0:o.nonce)||(null==o?void 0:o.getAttribute("nonce"));r=Promise.allSettled(n.map(n=>{if(n=function(e,t){return new URL(e,t).href}(n,s),n in t)return;t[n]=!0;const r=n.endsWith(".css"),o=r?'[rel="stylesheet"]':"";if(!!s)for(let t=e.length-1;t>=0;t--){const s=e[t];if(s.href===n&&(!r||"stylesheet"===s.rel))return}else if(document.querySelector(`link[href="${n}"]${o}`))return;const a=document.createElement("link");return a.rel=r?"stylesheet":"modulepreload",r||(a.as="script"),a.crossOrigin="",a.href=n,i&&a.setAttribute("nonce",i),document.head.appendChild(a),r?new Promise((e,t)=>{a.addEventListener("load",e),a.addEventListener("error",()=>t(new Error(`Unable to preload CSS for ${n}`)))}):void 0}))}function o(e){const t=new Event("vite:preloadError",{cancelable:!0});if(t.payload=e,window.dispatchEvent(t),!t.defaultPrevented)throw e}return r.then(t=>{for(const e of t||[])"rejected"===e.status&&o(e.reason);return e().catch(o)})};function s(e){e=window.fcAdmin.trans[e]||e;const t=Array.prototype.slice.call(arguments,1);if(0===t.length)return e;let n=0;return e=e.replace(/%(\d*)\$?s|%d/g,(e,s)=>{if(s){const n=parseInt(s,10)-1;return n")},$t:s,trans:function(e){return s(e)},$_n:function(e,t,n){return s(parseInt(n.toString().replace(/,/g,""),10)>1?t:e,n)},percent:function(e,t){if(!t||!e)return"--";const n=e/t*100;return Number.isInteger(n)?n+"%":n.toFixed(2)+"%"},nsDateFormat:function(e,t=null){const n=void 0===e?null:e;if(!n)return"";null===t&&(t=window.dayjs(e).isSame(dayjs(),"year")?"D MMM":"D MMM, YYYY");const s=window.dayjs(n);return s.isValid()?s.format(t):null},smartDate:function(e,t=!1){if(!e)return"";let n="D MMM, YYYY";if(window.dayjs(e).isSame(window.dayjs(),"year")&&(n="D MMM",t)){Math.abs(dayjs(e).diff(dayjs(),"days"))<=5&&(n=0==window.dayjs(e).minute()?"D MMM, ha":"D MMM, hh:mma")}const s=window.dayjs(e);return s.isValid()?s.format(n):null},humanDiffTime:function(e){const t=void 0===e?null:e;if(!t)return"";if(window.fcAdmin.disable_time_diff){return window.dayjs(t).format(window.fcAdmin.wp_date_time_format)}const n=new Date-this.appStartTime,s=window.dayjs(t),r=window.dayjs(window.fcAdmin.server_time).add(n,"milliseconds");return s.from(r)},hasPermission:function(e){return-1!==window.fcAdmin.auth.permissions.indexOf(e)},formatMoney:function(e,t=2,n=".",s=","){try{t=Math.abs(t),t=isNaN(t)?2:t,parseInt(e)==e&&(t=0);const r=e<0?"-":"",o=parseInt(e=Math.abs(Number(e)||0).toFixed(t)).toString(),i=o.length>3?o.length%3:0;return r+(i?o.substr(0,i)+s:"")+o.substr(i).replace(/(\d{3})(?=\d)/g,"$1"+s)+(t?n+Math.abs(e-o).toFixed(t).slice(2):"")}catch(r){return""}}};function o(e,t=""){let n={};return Object.keys(e).forEach(s=>{"object"==typeof e[s]?n=Object.assign(n,o(e[s],`${t}[${s}]`)):n[`${t}[${s}]`]=e[s]}),n}const i=function(e,t,n={}){const s=`${window.fcAdmin.rest.url}/${t}`,r={"X-WP-Nonce":window.fcAdmin.rest.nonce};return-1!==["PUT","PATCH","DELETE"].indexOf(e.toUpperCase())&&(r["X-HTTP-Method-Override"]=e,e="POST"),n.query_timestamp=Date.now(),new Promise((t,i)=>{const a=new XMLHttpRequest;let c=s;if("GET"===e.toUpperCase()){const e=new URLSearchParams;Object.keys(n).forEach(t=>{if(null!==n[t]&&!1!==n[t]&&void 0!==n[t])if(Array.isArray(n[t]))n[t].forEach(n=>{null!==n&&"object"==typeof n?e.append(t+"[]",JSON.stringify(n)):e.append(t+"[]",n)});else if("object"==typeof n[t]){const s=o(n[t],t);Object.keys(s).forEach(t=>{e.append(t,s[t])})}else e.append(t,n[t])});let t=e.toString();-1!==c.indexOf("?")?c+="&"+t:c+="?"+t}a.open(e,c,!0),Object.keys(r).forEach(e=>{a.setRequestHeader(e,r[e])}),a.onload=function(){let o;try{o=JSON.parse(a.responseText)}catch(l){o=null}!function(e,t,n,s,r){window.fluentApiLogger&&(n.query_timestamp&&delete n.query_timestamp,window.fluentApiLogger.logRequest({url:e,method:t,payload:JSON.parse(JSON.stringify(n)),response:s,status:r}))}(s,r["X-HTTP-Method-Override"]?r["X-HTTP-Method-Override"]:e,n,o,a.status),this.status>=200&&this.status<300&&o?t(o):o?("object"==typeof o&&(o.xhr_status=a.status),"rest_cookie_invalid_nonce"==o.code&&document.dispatchEvent(new CustomEvent("fluent_renew_rest_nonce",{detail:o})),i(o)):(window.FLUENTCRM.instance.$notify({message:'Unexpected error from server. Please check browser console. ',type:"error",dangerouslyUseHTMLString:!0,customClass:"fc_bottom-right",position:window.fcAdmin&&window.fcAdmin.is_rtl?"bottom-left":"bottom-right",onClick:()=>{window.FLUENTCRM.instance.$messageBox.alert(a.responseText+"\nURL: "+c+"\nMethod: "+e,"Error Details",{customStyle:{maxWidth:"60%"}})},duration:1e4}),console.info("Your server firewall blocked the request or it's a plugin conflict. Please check the detailed error."),console.log({status:a.status,statusText:a.statusText,responseText:a.responseText}))},a.onerror=function(){console.info("Your server firewall blocked the request or it's a plugin conflict. Please check the detailed error."),console.log({status:a.status,statusText:a.statusText,responseText:a.responseText}),i({status:a.status,statusText:a.statusText})},"GET"===e.toUpperCase()?a.send():(a.setRequestHeader("Content-Type","application/json;charset=UTF-8"),a.send(JSON.stringify(n)))})},a={get:(e,t={})=>i("GET",e,t),post:(e,t={})=>i("POST",e,t),delete:(e,t={})=>i("DELETE",e,t),del:(e,t={})=>i("DELETE",e,t),put:(e,t={})=>i("PUT",e,t),patch:(e,t={})=>i("PATCH",e,t),uploadFile(e,t={}){const n=`${window.fcAdmin.rest.url}/${e}`,s={"X-WP-Nonce":window.fcAdmin.rest.nonce};return new Promise((e,r)=>{const o=new XMLHttpRequest;o.open("POST",n,!0),Object.keys(s).forEach(e=>{o.setRequestHeader(e,s[e])}),o.onload=function(){this.status>=200&&this.status<300?e(JSON.parse(o.responseText)):r(JSON.parse(o.responseText))},o.onerror=function(){r({status:o.status,statusText:o.statusText})},o.send(t)})},ajax:(e,t,n={})=>function(e,t,n={}){const s=window.fcAdmin.ajaxurl;return n.query_timestamp=Date.now(),n.action=t,new Promise((t,r)=>{const i=new XMLHttpRequest;let a=s;if("GET"===e.toUpperCase()){let e={};Object.keys(n).forEach(t=>{null===n[t]||!1===n[t]?delete n[t]:"object"==typeof n[t]?e=Object.assign(e,o(n[t],t)):e[t]=n[t]});let t=new URLSearchParams(e).toString();-1!==a.indexOf("?")?a+="&"+t:a+="?"+t}i.open(e,a,!0),i.onload=function(){if(this.status>=200&&this.status<300)t(JSON.parse(i.responseText));else{let t;try{t=JSON.parse(i.responseText)}catch(e){t=null}r(t)}},i.onerror=function(){r({status:i.status,statusText:i.statusText})},"GET"===e.toUpperCase()?i.send():(i.setRequestHeader("Content-Type","application/json;charset=UTF-8"),i.send(JSON.stringify(n)))})}(e,t,n)},c="fcrm_pref";class l{static get(e,t=""){const n=localStorage.getItem(c);if(!n)return t;const s=JSON.parse(n);return s&&s[e]?s[e]:t}static set(e,t){let n=localStorage.getItem(c);n?(n=JSON.parse(n),"object"!=typeof n&&(n={})):n={},n[e]=t,localStorage.setItem(c,JSON.stringify(n))}static remove(e){const t=JSON.parse(localStorage.getItem(c));t&&t[e]&&(delete t[e],localStorage.setItem(c,JSON.stringify(t)))}static clear(){localStorage.removeItem(c)}}const u={install:(t,n)=>{t.config.globalProperties.$bus=e()}};class d{constructor(){this.errors={}}get(e){if(this.errors[e])return Object.values(this.errors[e])[0]}has(e){return!!this.errors[e]}record(e){this.errors=e}clear(){this.errors={}}}export{r as C,d as E,a as R,l as S,n as _,u as e}; diff --git a/wp-content/plugins/fluent-crm/assets/flags.webp b/wp-content/plugins/fluent-crm/assets/flags.webp new file mode 100644 index 0000000..e24e3b2 Binary files /dev/null and b/wp-content/plugins/fluent-crm/assets/flags.webp differ diff --git a/wp-content/plugins/fluent-crm/assets/flags@2x.webp b/wp-content/plugins/fluent-crm/assets/flags@2x.webp new file mode 100644 index 0000000..9662f02 Binary files /dev/null and b/wp-content/plugins/fluent-crm/assets/flags@2x.webp differ diff --git a/wp-content/plugins/fluent-crm/assets/fluentcrm-logo.js b/wp-content/plugins/fluent-crm/assets/fluentcrm-logo.js new file mode 100644 index 0000000..c13719f --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/fluentcrm-logo.js @@ -0,0 +1 @@ +const e=""+new URL("fluentcrm-logo.png",import.meta.url).href;export{e as f}; diff --git a/wp-content/plugins/fluent-crm/assets/fluentcrm-logo.png b/wp-content/plugins/fluent-crm/assets/fluentcrm-logo.png new file mode 100644 index 0000000..e8bee95 Binary files /dev/null and b/wp-content/plugins/fluent-crm/assets/fluentcrm-logo.png differ diff --git a/wp-content/plugins/fluent-crm/assets/guten-editor/content_styling.css b/wp-content/plugins/fluent-crm/assets/guten-editor/content_styling.css new file mode 100644 index 0000000..8d79099 --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/guten-editor/content_styling.css @@ -0,0 +1 @@ +:root{--fc-primary-bg: #FFFFFF;--fc-secondary-bg: #F5F7FA;--fc-light-bg: #E1E4EA;--fc-deep-bg: #222530;--fc-primary-text: #0E121B;--fc-secondary-text: #525866;--fc-text-muted: #99A0AE;--fc-text-inverse: #FFFFFF;--fc-primary-border: #E1E4EA;--fc-secondary-border: #CACFD8;--fc-primary-button: #222530;--fc-text-link: #335CFF;--fc-success: #1FC16B;--fc-success-bg: #E0FAEC;--fc-error: #FB3748;--fc-error-bg: #FFEBEC;--fc-warning: #F6B51E;--fc-warning-bg: #FFFAEB;--el-color-primary: var(--fc-deep-bg);--el-color-primary-light-3: var(--fc-secondary-text);--el-color-primary-light-5: var(--fc-text-muted);--el-color-primary-light-7: var(--fc-secondary-border);--el-color-primary-light-8: var(--fc-primary-border);--el-color-primary-light-9: var(--fc-secondary-bg);--el-color-primary-dark-2: var(--fc-primary-text);--el-color-success: var(--fc-success);--el-color-warning: var(--fc-warning);--el-color-danger: var(--fc-error);--el-color-error: var(--fc-error);--el-color-info: var(--fc-text-muted);--el-text-color-primary: var(--fc-primary-text);--el-text-color-regular: var(--fc-secondary-text);--el-text-color-secondary: var(--fc-text-muted);--el-text-color-placeholder: var(--fc-text-muted);--el-text-color-disabled: var(--fc-secondary-border);--el-border-color: var(--fc-primary-border);--el-border-color-light: var(--fc-primary-border);--el-border-color-lighter: var(--fc-secondary-border);--el-border-color-dark: var(--fc-secondary-border);--el-fill-color-light: var(--fc-secondary-bg);--el-fill-color-lighter: var(--fc-secondary-bg);--el-bg-color: var(--fc-primary-bg);--el-bg-color-page: var(--fc-secondary-bg);--el-button-text-color: var(--fc-text-inverse);--el-fill-color-blank: var(--fc-primary-bg);--el-bg-color-overlay: var(--fc-primary-bg);--fcrm-border-radius-8: 8px;--el-border-radius-base: var(--fcrm-border-radius-8);--theme-block-max-width: 700px;--global-calc-content-width: 700px;--theme-block-wide-max-width: 820px;--theme-font-weight: 400;--theme-text-transform: none;--theme-text-decoration: none;--theme-font-size: 16px;--theme-line-height: 1.60;--theme-letter-spacing: 0em;--theme-button-font-weight: 500;--theme-button-font-size: 16px;--theme-palette-color-1: #4F46E5;--theme-palette-color-2: #7C3AED;--theme-palette-color-3: #1F2937;--theme-palette-color-4: #374151;--theme-palette-color-5: #6B7280;--theme-palette-color-6: #9CA3AF;--theme-palette-color-7: #E5E7EB;--theme-palette-color-8: #ffffff;--theme-text-color: var(--fcom-primary-text, #19283a);--theme-link-initial-color: var(--theme-palette-color-1);--theme-link-hover-color: var(--theme-palette-color-2);--theme-selection-text-color: #ffffff;--theme-selection-background-color: var(--theme-palette-color-1);--theme-border-color: var(--theme-palette-color-5);--theme-headings-color: var(--theme-palette-color-4);--theme-content-spacing: 20px;--theme-button-min-height: 40px;--theme-button-shadow: none;--theme-button-transform: none;--theme-button-text-initial-color: #ffffff;--theme-button-text-hover-color: #ffffff;--theme-button-background-initial-color: var(--theme-palette-color-1);--theme-button-background-hover-color: var(--theme-palette-color-2);--theme-button-border: none;--theme-button-padding: 5px 20px;--theme-normal-container-max-width: 1290px;--theme-content-vertical-spacing: 60px;--theme-container-edge-spacing: 90vw;--theme-narrow-container-max-width: 750px;--theme-wide-offset: 130px;--fcom-font-size-small: 16px;--fcom-font-size-medium: 18px;--fcom-font-size-large: 22px;--fcom-font-size-larger: 26px;--fcom-font-size-xxlarge: 32px;--wp--preset--spacing--20: 7px;--wp--preset--spacing--30: 11px;--wp--preset--spacing--40: 16px;--wp--preset--spacing--50: 24px;--wp--preset--spacing--60: 36px;--wp--preset--spacing--70: 54px;--wp--preset--spacing--80: 81px}.fcom_lesson_details .fcom_lesson_content .has-theme-palette-color-1-color{color:var(--theme-palette-color-1)}.fcom_lesson_details .fcom_lesson_content .has-theme-palette-color-2-color{color:var(--theme-palette-color-2)}.fcom_lesson_details .fcom_lesson_content .has-theme-palette-color-3-color{color:var(--theme-palette-color-3)}.fcom_lesson_details .fcom_lesson_content .has-theme-palette-color-4-color{color:var(--theme-palette-color-4)}.fcom_lesson_details .fcom_lesson_content .has-theme-palette-color-5-color{color:var(--theme-palette-color-5)}.fcom_lesson_details .fcom_lesson_content .has-theme-palette-color-6-color{color:var(--theme-palette-color-6)}.fcom_lesson_details .fcom_lesson_content .has-theme-palette-color-7-color{color:var(--theme-palette-color-7)}.fcom_lesson_details .fcom_lesson_content .has-theme-palette-color-8-color{color:var(--theme-palette-color-8)}.fcom_lesson_details .fcom_lesson_content .has-theme-palette-color-1-background-color{background-color:var(--theme-palette-color-1)}.fcom_lesson_details .fcom_lesson_content .has-theme-palette-color-2-background-color{background-color:var(--theme-palette-color-2)}.fcom_lesson_details .fcom_lesson_content .has-theme-palette-color-3-background-color{background-color:var(--theme-palette-color-3)}.fcom_lesson_details .fcom_lesson_content .has-theme-palette-color-4-background-color{background-color:var(--theme-palette-color-4)}.fcom_lesson_details .fcom_lesson_content .has-theme-palette-color-5-background-color{background-color:var(--theme-palette-color-5)}.fcom_lesson_details .fcom_lesson_content .has-theme-palette-color-6-background-color{background-color:var(--theme-palette-color-6)}.fcom_lesson_details .fcom_lesson_content .has-theme-palette-color-7-background-color{background-color:var(--theme-palette-color-7)}.fcom_lesson_details .fcom_lesson_content .has-theme-palette-color-8-background-color{background-color:var(--theme-palette-color-8)}.fcom_lesson_details .fcom_lesson_content .has-small-font-size{font-size:var(--fcom-font-size-small)}.fcom_lesson_details .fcom_lesson_content .has-medium-font-size{font-size:var(--fcom-font-size-medium)}.fcom_lesson_details .fcom_lesson_content .has-large-font-size{font-size:var(--fcom-font-size-large)}.fcom_lesson_details .fcom_lesson_content .has-larger-font-size{font-size:var(--fcom-font-size-larger)}.fcom_lesson_details .fcom_lesson_content .has-xxlarge-font-size{font-size:var(--fcom-font-size-xxlarge)}.fcom_lesson_details .fcom_lesson_content .is-root-container>.alignfull{margin-inline:var(--has-wide, -20px)}.fcom_lesson_details .fcom_lesson_content .is-root-container>.wp-block.alignleft{margin-inline-start:calc((100% - min(var(--theme-block-max-width),100%))/2)}.fcom_lesson_details .fcom_lesson_content .is-root-container>.wp-block.alignright{margin-inline-end:calc((100% - min(var(--theme-block-max-width),100%))/2)}.fcom_lesson_details .fcom_lesson_content :root .wp-element-button{font-size:var(--theme-button-font-size);font-weight:var(--theme-button-font-weight);font-style:var(--theme-button-font-style);line-height:var(--theme-button-line-height);letter-spacing:var(--theme-button-letter-spacing);text-transform:var(--theme-button-text-transform);-webkit-text-decoration:var(--theme-button-text-decoration);text-decoration:var(--theme-button-text-decoration)}.fcom_lesson_details .fcom_lesson_content :root .wp-block-button[style*=font-weight] .wp-element-button{font-weight:inherit}.fcom_lesson_details .fcom_lesson_content .wp-block-columns:last-child{margin-bottom:0}.fcom_lesson_details .fcom_lesson_content .has-drop-cap:not(:focus):first-letter{font-size:5.8em;font-weight:700;margin:.1em .12em .05em 0}.fcom_lesson_details .fcom_lesson_content figcaption{text-align:center;margin-block:.5em 0}.fcom_lesson_details .fcom_lesson_content .wp-block-code,.fcom_lesson_details .fcom_lesson_content .wp-block-verse,.fcom_lesson_details .fcom_lesson_content .wp-block-preformatted{box-sizing:border-box;tab-size:4;padding:15px 20px;border-radius:3px;background:var(--theme-palette-color-7)}.fcom_lesson_details .fcom_lesson_content blockquote{margin-inline:0}.fcom_lesson_details .fcom_lesson_content blockquote:where(:not(.is-style-plain)):where(:not(.has-text-align-center):not(.has-text-align-right)){border-inline-start:4px solid var(--theme-palette-color-1)}.fcom_lesson_details .fcom_lesson_content blockquote:where(:not(.is-style-plain)).has-text-align-center{padding-block:1.5em;border-block:3px solid var(--theme-palette-color-1)}.fcom_lesson_details .fcom_lesson_content blockquote:where(:not(.is-style-plain)).has-text-align-right{border-inline-end:4px solid var(--theme-palette-color-1)}.fcom_lesson_details .fcom_lesson_content blockquote:where(:not(.is-style-plain):not(.has-text-align-center):not(.has-text-align-right)){padding-inline-start:1.5em}.fcom_lesson_details .fcom_lesson_content blockquote.has-text-align-right{padding-inline-end:1.5em}.fcom_lesson_details .fcom_lesson_content blockquote p:last-child{margin-bottom:0}.fcom_lesson_details .fcom_lesson_content blockquote cite{font-size:14px}.fcom_lesson_details .fcom_lesson_content .wp-block-list{padding-left:30px}.fcom_lesson_details .fcom_lesson_content .wp-block-pullquote{position:relative;padding:70px;text-align:initial;border-width:10px;border-style:solid;border-color:var(--theme-palette-color-1)}.fcom_lesson_details .fcom_lesson_content .wp-block-pullquote blockquote{border:0;padding:0;margin:0;position:relative;isolation:isolate}.fcom_lesson_details .fcom_lesson_content .wp-block-pullquote blockquote p{margin-top:0;margin-bottom:1em}.fcom_lesson_details .fcom_lesson_content .wp-block-pullquote blockquote p:last-child{margin-bottom:0}.fcom_lesson_details .fcom_lesson_content .wp-block-pullquote blockquote cite{font-size:16px;font-weight:500}.fcom_lesson_details .fcom_lesson_content [data-align=left] .wp-block-pullquote,.fcom_lesson_details .fcom_lesson_content [data-align=right] .wp-block-pullquote{max-width:50%;margin-top:.3em;margin-bottom:.3em}.fcom_lesson_details .fcom_lesson_content .wp-block-table table{border-width:1px}.fcom_lesson_details .fcom_lesson_content .wp-block-table table:not(.has-border-color) thead,.fcom_lesson_details .fcom_lesson_content .wp-block-table table:not(.has-border-color) tfoot,.fcom_lesson_details .fcom_lesson_content .wp-block-table table:not(.has-border-color) td,.fcom_lesson_details .fcom_lesson_content .wp-block-table table:not(.has-border-color) th{border-color:var(--theme-table-border-color, var(--theme-border-color))}.fcom_lesson_details .fcom_lesson_content .wp-block-table th:not([class*=has-text-align]){text-align:inherit}.fcom_lesson_details .fcom_lesson_content .wp-block-table.is-style-stripes{border:0}.fcom_lesson_details .fcom_lesson_content .wp-block-separator{border:none;margin-inline:auto;color:var(--theme-form-field-border-initial-color)}.fcom_lesson_details .fcom_lesson_content .wp-block-separator:not(:where(.is-style-wide,.is-style-dots,.alignfull,.alignwide)){max-width:100px !important}.fcom_lesson_details .fcom_lesson_content .wp-block-separator:not(.is-style-dots){height:2px;background-color:currentColor}.fcom_lesson_details .fcom_lesson_content :root :where(p.has-background,.wp-block-group.has-background){padding:30px;box-sizing:border-box}.fcom_lesson_details .fcom_lesson_content h1.has-background,.fcom_lesson_details .fcom_lesson_content h2.has-background,.fcom_lesson_details .fcom_lesson_content h3.has-background,.fcom_lesson_details .fcom_lesson_content h4.has-background,.fcom_lesson_details .fcom_lesson_content h5.has-background,.fcom_lesson_details .fcom_lesson_content h6.has-background{padding:1.25em 2.375em}.fcom_lesson_details .fcom_lesson_content .wp-element-button{display:inline-flex;align-items:center;justify-content:center;min-height:var(--theme-button-min-height);padding:var(--theme-button-padding);border:none;-webkit-appearance:none;appearance:none;cursor:pointer;-webkit-user-select:none;user-select:none;text-align:center;border-radius:var(--theme-button-border-radius, 3px);transition:all .12s cubic-bezier(0.455, 0.03, 0.515, 0.955);--has-link-decoration: var(--false)}.fcom_lesson_details .fcom_lesson_content .wp-element-button:hover{opacity:.8}.fcom_lesson_details .fcom_lesson_content .wp-element-button:not(.has-background){position:relative;background:var(--fcom-primary-button, #2B2E33);border:1px solid var(--fcom-primary-button, #2B2E33);color:var(--fcom-primary-button-text, var(--fcom-primary-bg, #FFFFFF))}.fcom_lesson_details .fcom_lesson_content .is-style-outline .wp-element-button:not(.has-background){background:rgba(0,0,0,0);color:var(--fcom-primary-button, #2B2E33);border:1px solid}.fcom_lesson_details .fcom_lesson_content .is-style-outline .wp-element-button:not(.has-background):hover{background:var(--fcom-primary-button, #2B2E33);border:1px solid var(--fcom-primary-button, #2B2E33);color:var(--fcom-primary-button-text, var(--fcom-primary-bg, #FFFFFF))}.fcom_lesson_details .fcom_lesson_content .is-layout-flex{display:flex;flex-wrap:wrap;align-items:center}.fcom_lesson_details .fcom_lesson_content .wp-block-group-is-layout-grid{display:grid;justify-items:center;container-type:inline-size;grid-template-columns:repeat(auto-fill, minmax(min(12rem, 100%), 1fr));grid-gap:var(--theme-content-spacing)}.fcom_lesson_details .fcom_lesson_content .wp-block-group.has-background{padding:30px;box-sizing:border-box}:root :where(.is-layout-flex){gap:var(--theme-content-spacing)} diff --git a/wp-content/plugins/fluent-crm/assets/guten-editor/editor.css b/wp-content/plugins/fluent-crm/assets/guten-editor/editor.css new file mode 100644 index 0000000..f2d4384 --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/guten-editor/editor.css @@ -0,0 +1 @@ +:root{--fc-primary-bg: #FFFFFF;--fc-secondary-bg: #F5F7FA;--fc-light-bg: #E1E4EA;--fc-deep-bg: #222530;--fc-primary-text: #0E121B;--fc-secondary-text: #525866;--fc-text-muted: #99A0AE;--fc-text-inverse: #FFFFFF;--fc-primary-border: #E1E4EA;--fc-secondary-border: #CACFD8;--fc-primary-button: #222530;--fc-text-link: #335CFF;--fc-success: #1FC16B;--fc-success-bg: #E0FAEC;--fc-error: #FB3748;--fc-error-bg: #FFEBEC;--fc-warning: #F6B51E;--fc-warning-bg: #FFFAEB;--el-color-primary: var(--fc-deep-bg);--el-color-primary-light-3: var(--fc-secondary-text);--el-color-primary-light-5: var(--fc-text-muted);--el-color-primary-light-7: var(--fc-secondary-border);--el-color-primary-light-8: var(--fc-primary-border);--el-color-primary-light-9: var(--fc-secondary-bg);--el-color-primary-dark-2: var(--fc-primary-text);--el-color-success: var(--fc-success);--el-color-warning: var(--fc-warning);--el-color-danger: var(--fc-error);--el-color-error: var(--fc-error);--el-color-info: var(--fc-text-muted);--el-text-color-primary: var(--fc-primary-text);--el-text-color-regular: var(--fc-secondary-text);--el-text-color-secondary: var(--fc-text-muted);--el-text-color-placeholder: var(--fc-text-muted);--el-text-color-disabled: var(--fc-secondary-border);--el-border-color: var(--fc-primary-border);--el-border-color-light: var(--fc-primary-border);--el-border-color-lighter: var(--fc-secondary-border);--el-border-color-dark: var(--fc-secondary-border);--el-fill-color-light: var(--fc-secondary-bg);--el-fill-color-lighter: var(--fc-secondary-bg);--el-bg-color: var(--fc-primary-bg);--el-bg-color-page: var(--fc-secondary-bg);--el-button-text-color: var(--fc-text-inverse);--el-fill-color-blank: var(--fc-primary-bg);--el-bg-color-overlay: var(--fc-primary-bg);--fcrm-border-radius-8: 8px;--el-border-radius-base: var(--fcrm-border-radius-8);--theme-block-max-width: 700px;--global-calc-content-width: 700px;--theme-block-wide-max-width: 820px;--theme-font-weight: 400;--theme-text-transform: none;--theme-text-decoration: none;--theme-font-size: 16px;--theme-line-height: 1.60;--theme-letter-spacing: 0em;--theme-button-font-weight: 500;--theme-button-font-size: 16px;--theme-palette-color-1: #4F46E5;--theme-palette-color-2: #7C3AED;--theme-palette-color-3: #1F2937;--theme-palette-color-4: #374151;--theme-palette-color-5: #6B7280;--theme-palette-color-6: #9CA3AF;--theme-palette-color-7: #E5E7EB;--theme-palette-color-8: #ffffff;--theme-text-color: var(--fcom-primary-text, #19283a);--theme-link-initial-color: var(--theme-palette-color-1);--theme-link-hover-color: var(--theme-palette-color-2);--theme-selection-text-color: #ffffff;--theme-selection-background-color: var(--theme-palette-color-1);--theme-border-color: var(--theme-palette-color-5);--theme-headings-color: var(--theme-palette-color-4);--theme-content-spacing: 20px;--theme-button-min-height: 40px;--theme-button-shadow: none;--theme-button-transform: none;--theme-button-text-initial-color: #ffffff;--theme-button-text-hover-color: #ffffff;--theme-button-background-initial-color: var(--theme-palette-color-1);--theme-button-background-hover-color: var(--theme-palette-color-2);--theme-button-border: none;--theme-button-padding: 5px 20px;--theme-normal-container-max-width: 1290px;--theme-content-vertical-spacing: 60px;--theme-container-edge-spacing: 90vw;--theme-narrow-container-max-width: 750px;--theme-wide-offset: 130px;--fcom-font-size-small: 16px;--fcom-font-size-medium: 18px;--fcom-font-size-large: 22px;--fcom-font-size-larger: 26px;--fcom-font-size-xxlarge: 32px;--wp--preset--spacing--20: 7px;--wp--preset--spacing--30: 11px;--wp--preset--spacing--40: 16px;--wp--preset--spacing--50: 24px;--wp--preset--spacing--60: 36px;--wp--preset--spacing--70: 54px;--wp--preset--spacing--80: 81px}body .has-theme-palette-color-1-color{color:var(--theme-palette-color-1)}body .has-theme-palette-color-2-color{color:var(--theme-palette-color-2)}body .has-theme-palette-color-3-color{color:var(--theme-palette-color-3)}body .has-theme-palette-color-4-color{color:var(--theme-palette-color-4)}body .has-theme-palette-color-5-color{color:var(--theme-palette-color-5)}body .has-theme-palette-color-6-color{color:var(--theme-palette-color-6)}body .has-theme-palette-color-7-color{color:var(--theme-palette-color-7)}body .has-theme-palette-color-8-color{color:var(--theme-palette-color-8)}body .has-theme-palette-color-1-background-color{background-color:var(--theme-palette-color-1)}body .has-theme-palette-color-2-background-color{background-color:var(--theme-palette-color-2)}body .has-theme-palette-color-3-background-color{background-color:var(--theme-palette-color-3)}body .has-theme-palette-color-4-background-color{background-color:var(--theme-palette-color-4)}body .has-theme-palette-color-5-background-color{background-color:var(--theme-palette-color-5)}body .has-theme-palette-color-6-background-color{background-color:var(--theme-palette-color-6)}body .has-theme-palette-color-7-background-color{background-color:var(--theme-palette-color-7)}body .has-theme-palette-color-8-background-color{background-color:var(--theme-palette-color-8)}body .has-small-font-size{font-size:var(--fcom-font-size-small)}body .has-medium-font-size{font-size:var(--fcom-font-size-medium)}body .has-large-font-size{font-size:var(--fcom-font-size-large)}body .has-larger-font-size{font-size:var(--fcom-font-size-larger)}body .has-xxlarge-font-size{font-size:var(--fcom-font-size-xxlarge)}body .is-root-container>.alignfull{margin-inline:var(--has-wide, -20px)}body .is-root-container>.wp-block.alignleft{margin-inline-start:calc((100% - min(var(--theme-block-max-width),100%))/2)}body .is-root-container>.wp-block.alignright{margin-inline-end:calc((100% - min(var(--theme-block-max-width),100%))/2)}body :root .wp-element-button{font-size:var(--theme-button-font-size);font-weight:var(--theme-button-font-weight);font-style:var(--theme-button-font-style);line-height:var(--theme-button-line-height);letter-spacing:var(--theme-button-letter-spacing);text-transform:var(--theme-button-text-transform);-webkit-text-decoration:var(--theme-button-text-decoration);text-decoration:var(--theme-button-text-decoration)}body :root .wp-block-button[style*=font-weight] .wp-element-button{font-weight:inherit}body .wp-block-columns:last-child{margin-bottom:0}body .has-drop-cap:not(:focus):first-letter{font-size:5.8em;font-weight:700;margin:.1em .12em .05em 0}body figcaption{text-align:center;margin-block:.5em 0}body .wp-block-code,body .wp-block-verse,body .wp-block-preformatted{box-sizing:border-box;tab-size:4;padding:15px 20px;border-radius:3px;background:var(--theme-palette-color-7)}body blockquote{margin-inline:0}body blockquote:where(:not(.is-style-plain)):where(:not(.has-text-align-center):not(.has-text-align-right)){border-inline-start:4px solid var(--theme-palette-color-1)}body blockquote:where(:not(.is-style-plain)).has-text-align-center{padding-block:1.5em;border-block:3px solid var(--theme-palette-color-1)}body blockquote:where(:not(.is-style-plain)).has-text-align-right{border-inline-end:4px solid var(--theme-palette-color-1)}body blockquote:where(:not(.is-style-plain):not(.has-text-align-center):not(.has-text-align-right)){padding-inline-start:1.5em}body blockquote.has-text-align-right{padding-inline-end:1.5em}body blockquote p:last-child{margin-bottom:0}body blockquote cite{font-size:14px}body .wp-block-list{padding-left:30px}body .wp-block-pullquote{position:relative;padding:70px;text-align:initial;border-width:10px;border-style:solid;border-color:var(--theme-palette-color-1)}body .wp-block-pullquote blockquote{border:0;padding:0;margin:0;position:relative;isolation:isolate}body .wp-block-pullquote blockquote p{margin-top:0;margin-bottom:1em}body .wp-block-pullquote blockquote p:last-child{margin-bottom:0}body .wp-block-pullquote blockquote cite{font-size:16px;font-weight:500}body [data-align=left] .wp-block-pullquote,body [data-align=right] .wp-block-pullquote{max-width:50%;margin-top:.3em;margin-bottom:.3em}body .wp-block-table table{border-width:1px}body .wp-block-table table:not(.has-border-color) thead,body .wp-block-table table:not(.has-border-color) tfoot,body .wp-block-table table:not(.has-border-color) td,body .wp-block-table table:not(.has-border-color) th{border-color:var(--theme-table-border-color, var(--theme-border-color))}body .wp-block-table th:not([class*=has-text-align]){text-align:inherit}body .wp-block-table.is-style-stripes{border:0}body .wp-block-separator{border:none;margin-inline:auto;color:var(--theme-form-field-border-initial-color)}body .wp-block-separator:not(:where(.is-style-wide,.is-style-dots,.alignfull,.alignwide)){max-width:100px !important}body .wp-block-separator:not(.is-style-dots){height:2px;background-color:currentColor}body :root :where(p.has-background,.wp-block-group.has-background){padding:30px;box-sizing:border-box}body h1.has-background,body h2.has-background,body h3.has-background,body h4.has-background,body h5.has-background,body h6.has-background{padding:1.25em 2.375em}body .wp-element-button{display:inline-flex;align-items:center;justify-content:center;min-height:var(--theme-button-min-height);padding:var(--theme-button-padding);border:none;-webkit-appearance:none;appearance:none;cursor:pointer;-webkit-user-select:none;user-select:none;text-align:center;border-radius:var(--theme-button-border-radius, 3px);transition:all .12s cubic-bezier(0.455, 0.03, 0.515, 0.955);--has-link-decoration: var(--false)}body .wp-element-button:hover{opacity:.8}body .wp-element-button:not(.has-background){position:relative;background:var(--fcom-primary-button, #2B2E33);border:1px solid var(--fcom-primary-button, #2B2E33);color:var(--fcom-primary-button-text, var(--fcom-primary-bg, #FFFFFF))}body .is-style-outline .wp-element-button:not(.has-background){background:rgba(0,0,0,0);color:var(--fcom-primary-button, #2B2E33);border:1px solid}body .is-style-outline .wp-element-button:not(.has-background):hover{background:var(--fcom-primary-button, #2B2E33);border:1px solid var(--fcom-primary-button, #2B2E33);color:var(--fcom-primary-button-text, var(--fcom-primary-bg, #FFFFFF))}body .is-layout-flex{display:flex;flex-wrap:wrap;align-items:center}body{background-color:#fff;background-image:none}body .is-root-container p{font-size:var(--theme-font-size, 16px);line-height:var(--theme-line-height, 1.6)}.block-editor-iframe__html.is-zoomed-out .block-editor-iframe__body{padding:20px 30px}.editor-visual-editor__post-title-wrapper.edit-post-visual-editor__post-title-wrapper{margin-top:0px !important;padding-top:0;margin-bottom:30px;position:relative}.editor-visual-editor__post-title-wrapper.edit-post-visual-editor__post-title-wrapper h1{font-size:32px;font-weight:700}h1{--theme-font-weight: 700;--theme-font-size: 40px;--theme-line-height: 1.5}h2{--theme-font-weight: 700;--theme-font-size: 35px;--theme-line-height: 1.5}h3{--theme-font-weight: 700;--theme-font-size: 30px;--theme-line-height: 1.5}h4{--theme-font-weight: 700;--theme-font-size: 25px;--theme-line-height: 1.5}h5{--theme-font-weight: 700;--theme-font-size: 20px;--theme-line-height: 1.5}h6{--theme-font-weight: 700;--theme-font-size: 16px;--theme-line-height: 1.5}.wp-block-pullquote{--theme-font-weight: 600;--theme-font-size: 25px}pre,code,samp,kbd{--theme-font-weight: 400;--theme-font-size: 16px}figcaption{--theme-font-size: 14px}li::marker{color:#959595}.editor-styles-wrapper{--true: initial;--false: ;--wp--style--global--content-size: var(--theme-block-max-width);--wp--style--global--wide-size: var(--theme-block-wide-max-width);box-sizing:border-box;border:var(--has-boxed, var(--theme-boxed-content-border));padding:var(--has-boxed, var(--theme-boxed-content-spacing));box-shadow:var(--has-boxed, var(--theme-boxed-content-box-shadow));border-radius:var(--has-boxed, var(--theme-boxed-content-border-radius));margin-inline:auto;margin-block:var(--has-boxed, 20px);width:calc(100% - 40px);max-width:100%}:is(.is-layout-flow,.is-layout-constrained)>*:where(:not(h1,h2,h3,h4,h5,h6)){margin-block-start:0;margin-block-end:var(--theme-content-spacing)}:is(.is-layout-flow,.is-layout-constrained) :where(h1,h2,h3,h4,h5,h6){margin-block-end:calc(var(--has-theme-content-spacing, 1)*(.3em + 10px))}:root{color:#19283a}:root a{color:var(--fcom-text-link, #2271b1)}.block-editor-block-list__layout.is-root-container>.alignwide{max-width:var(--theme-block-wide-max-width);box-sizing:border-box}.is-root-container{padding:0 20px}.wp-has-aspect-ratio .wp-block-embed__wrapper:before{padding-top:56.25%;content:"";display:block}.wp-has-aspect-ratio iframe{bottom:0;height:100%;left:0;position:absolute;right:0;top:0;width:100%}.wp-embed-aspect-16-9 iframe{aspect-ratio:16/9} diff --git a/wp-content/plugins/fluent-crm/assets/guten-editor/index-rtl.css b/wp-content/plugins/fluent-crm/assets/guten-editor/index-rtl.css new file mode 100644 index 0000000..dd1d193 --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/guten-editor/index-rtl.css @@ -0,0 +1,7 @@ +:root{--fc-primary-bg:#fff;--fc-secondary-bg:#f5f7fa;--fc-light-bg:#e1e4ea;--fc-deep-bg:#222530;--fc-primary-text:#0e121b;--fc-secondary-text:#525866;--fc-text-muted:#99a0ae;--fc-text-inverse:#fff;--fc-primary-border:#e1e4ea;--fc-secondary-border:#cacfd8;--fc-primary-button:#222530;--fc-text-link:#335cff;--fc-success:#1fc16b;--fc-success-bg:#e0faec;--fc-error:#fb3748;--fc-error-bg:#ffebec;--fc-warning:#f6b51e;--fc-warning-bg:#fffaeb;--el-color-primary:var(--fc-deep-bg);--el-color-primary-light-3:var(--fc-secondary-text);--el-color-primary-light-5:var(--fc-text-muted);--el-color-primary-light-7:var(--fc-secondary-border);--el-color-primary-light-8:var(--fc-primary-border);--el-color-primary-light-9:var(--fc-secondary-bg);--el-color-primary-dark-2:var(--fc-primary-text);--el-color-success:var(--fc-success);--el-color-warning:var(--fc-warning);--el-color-danger:var(--fc-error);--el-color-error:var(--fc-error);--el-color-info:var(--fc-text-muted);--el-text-color-primary:var(--fc-primary-text);--el-text-color-regular:var(--fc-secondary-text);--el-text-color-secondary:var(--fc-text-muted);--el-text-color-placeholder:var(--fc-text-muted);--el-text-color-disabled:var(--fc-secondary-border);--el-border-color:var(--fc-primary-border);--el-border-color-light:var(--fc-primary-border);--el-border-color-lighter:var(--fc-secondary-border);--el-border-color-dark:var(--fc-secondary-border);--el-fill-color-light:var(--fc-secondary-bg);--el-fill-color-lighter:var(--fc-secondary-bg);--el-bg-color:var(--fc-primary-bg);--el-bg-color-page:var(--fc-secondary-bg);--el-button-text-color:var(--fc-text-inverse);--el-fill-color-blank:var(--fc-primary-bg);--el-bg-color-overlay:var(--fc-primary-bg);--fcrm-border-radius-8:8px;--el-border-radius-base:var(--fcrm-border-radius-8);--theme-block-max-width:700px;--global-calc-content-width:700px;--theme-block-wide-max-width:820px;--theme-font-weight:400;--theme-text-transform:none;--theme-text-decoration:none;--theme-font-size:16px;--theme-line-height:1.60;--theme-letter-spacing:0em;--theme-button-font-weight:500;--theme-button-font-size:16px;--theme-palette-color-1:#4f46e5;--theme-palette-color-2:#7c3aed;--theme-palette-color-3:#1f2937;--theme-palette-color-4:#374151;--theme-palette-color-5:#6b7280;--theme-palette-color-6:#9ca3af;--theme-palette-color-7:#e5e7eb;--theme-palette-color-8:#fff;--theme-text-color:var(--fcom-primary-text,#19283a);--theme-link-initial-color:var(--theme-palette-color-1);--theme-link-hover-color:var(--theme-palette-color-2);--theme-selection-text-color:#fff;--theme-selection-background-color:var(--theme-palette-color-1);--theme-border-color:var(--theme-palette-color-5);--theme-headings-color:var(--theme-palette-color-4);--theme-content-spacing:20px;--theme-button-min-height:40px;--theme-button-shadow:none;--theme-button-transform:none;--theme-button-text-initial-color:#fff;--theme-button-text-hover-color:#fff;--theme-button-background-initial-color:var(--theme-palette-color-1);--theme-button-background-hover-color:var(--theme-palette-color-2);--theme-button-border:none;--theme-button-padding:5px 20px;--theme-normal-container-max-width:1290px;--theme-content-vertical-spacing:60px;--theme-container-edge-spacing:90vw;--theme-narrow-container-max-width:750px;--theme-wide-offset:130px;--fcom-font-size-small:16px;--fcom-font-size-medium:18px;--fcom-font-size-large:22px;--fcom-font-size-larger:26px;--fcom-font-size-xxlarge:32px;--wp--preset--spacing--20:7px;--wp--preset--spacing--30:11px;--wp--preset--spacing--40:16px;--wp--preset--spacing--50:24px;--wp--preset--spacing--60:36px;--wp--preset--spacing--70:54px;--wp--preset--spacing--80:81px}.editor-styles-wrapper,:root{--wp-admin-theme-color:#3e58e1!important;--wp-admin-theme-color-darker-10:#3e58f2!important;--wp-admin-theme-color-darker-10--rgb:62,88,242;--wp-admin-theme-color-darker-20:#213fd4!important;--wp-admin-theme-color-darker-20--rgb:33,63,212;--has-boxed:0px}:host,:root{--rem:16}body[data-design-template=classic]{margin-right:20px!important}:root :where(.wp-element-button,.wp-block-button__link){background-color:#32373c;border-radius:3px;color:#fff;padding:8px 20px}body .wp-block-button.is-style-outline .wp-element-button:not(.has-text-color),body .wp-block-button.is-style-outline .wp-element-button:not(.has-text-color):hover{color:#32373c}body .wp-block-button.is-style-outline .wp-element-button:not(.has-border-color),body .wp-block-button.is-style-outline .wp-element-button:not(.has-border-color):hover{border-color:#32373c}body .wp-block-button.is-style-outline .wp-element-button:not(.has-background),body .wp-block-button.is-style-outline .wp-element-button:not(.has-background):hover{background:transparent}.wp-block-buttons:not(.is-content-justification-center,.is-content-justification-right){align-items:flex-start;justify-content:flex-start}.wp-block-button__link.has-fc-small-font-size{font-size:13px}.wp-block-button__link.has-fc-regular-font-size{font-size:16px}.wp-block-button__link.has-fc-medium-font-size{font-size:18px}.wp-block-button__link.has-fc-large-font-size{font-size:26px}.wp-block-button__link.has-fc-x-large-font-size{font-size:32px}body .wp-block-pullquote{border-color:#e5e7eb;border-width:4px;padding:20px}body .wp-block-pullquote blockquote cite{font-size:90%}.gutenberg__editor .fcrm_danger_btn,.gutenberg__editor .fcrm_delete_btn,.gutenberg__editor .fcrm_secondary_btn{background:#fff;border:1px solid var(--fc-primary-border);border-radius:8px;box-shadow:0 1px 2px 0 rgba(10,13,20,.031);color:var(--fc-secondary-text);font-size:14px;font-weight:500;height:auto;line-height:20px;padding:7px 10px}.gutenberg__editor .fcrm_danger_btn>span,.gutenberg__editor .fcrm_delete_btn>span,.gutenberg__editor .fcrm_secondary_btn>span{gap:4px}.gutenberg__editor .fcrm_danger_btn .el-icon,.gutenberg__editor .fcrm_danger_btn .fcrm-preview-icon,.gutenberg__editor .fcrm_danger_btn .icon,.gutenberg__editor .fcrm_delete_btn .el-icon,.gutenberg__editor .fcrm_delete_btn .fcrm-preview-icon,.gutenberg__editor .fcrm_delete_btn .icon,.gutenberg__editor .fcrm_secondary_btn .el-icon,.gutenberg__editor .fcrm_secondary_btn .fcrm-preview-icon,.gutenberg__editor .fcrm_secondary_btn .icon{color:var(--fc-primary-text)}.gutenberg__editor .fcrm_danger_btn .el-icon svg,.gutenberg__editor .fcrm_danger_btn .fcrm-preview-icon svg,.gutenberg__editor .fcrm_danger_btn .icon svg,.gutenberg__editor .fcrm_delete_btn .el-icon svg,.gutenberg__editor .fcrm_delete_btn .fcrm-preview-icon svg,.gutenberg__editor .fcrm_delete_btn .icon svg,.gutenberg__editor .fcrm_secondary_btn .el-icon svg,.gutenberg__editor .fcrm_secondary_btn .fcrm-preview-icon svg,.gutenberg__editor .fcrm_secondary_btn .icon svg{display:block}.gutenberg__editor .fcrm_danger_btn.small,.gutenberg__editor .fcrm_delete_btn.small,.gutenberg__editor .fcrm_secondary_btn.small{padding:5px 10px}.gutenberg__editor .fcrm_danger_btn:hover,.gutenberg__editor .fcrm_delete_btn:hover,.gutenberg__editor .fcrm_secondary_btn:hover{background:var(--fc-secondary-bg);border-color:var(--fc-secondary-border);color:var(--fc-deep-bg)}.gutenberg__editor .fcrm_danger_btn.is-disabled,.gutenberg__editor .fcrm_delete_btn.is-disabled,.gutenberg__editor .fcrm_secondary_btn.is-disabled{opacity:.6}.gutenberg__editor .fcrm_primary_btn{background:var(--fc-deep-bg);border-color:var(--fc-deep-bg);border-radius:8px;color:#fff;font-size:14px;height:auto;line-height:20px;padding:7px 10px}.gutenberg__editor .fcrm_primary_btn:hover{color:#fff}.gutenberg__editor .fcrm_primary_btn .cmd{background:var(--alpha-white-alpha-10,hsla(0,0%,100%,.102));border-radius:4px;color:var(--fc-text-muted);display:block;font-size:12px;font-weight:500;line-height:16px;padding:2px 6px;text-transform:uppercase}.gutenberg__editor .fcrm_primary_btn>span{gap:4px}.gutenberg__editor .fcrm_primary_btn .el-icon,.gutenberg__editor .fcrm_primary_btn .icon{color:#fff}.gutenberg__editor .fcrm_primary_btn .el-icon svg,.gutenberg__editor .fcrm_primary_btn .icon svg{display:block}.gutenberg__editor .fcrm_primary_btn:hover{background:var(--fc-deep-bg);border-color:var(--fc-deep-bg)}.gutenberg__editor .fcrm_primary_btn.small{padding:5px 10px}.gutenberg__editor .fcrm_primary_btn.is-disabled:hover{background:var(--fc-deep-bg);border-color:var(--fc-deep-bg)}.gutenberg__editor .fcrm_primary_btn.is-disabled{cursor:not-allowed;opacity:.5}.gutenberg__editor .fcrm_danger_btn,.gutenberg__editor .fcrm_delete_btn{border-color:var(--fc-error);box-shadow:none;color:var(--fc-error)}.gutenberg__editor .fcrm_danger_btn .el-icon,.gutenberg__editor .fcrm_delete_btn .el-icon{color:var(--fc-error)}.gutenberg__editor .fcrm_danger_btn:hover,.gutenberg__editor .fcrm_delete_btn:hover{border-color:var(--fc-error)}.gutenberg__editor .fcrm_setup_btn{background:rgba(34,37,48,.1);border:none;border-radius:6px;color:var(--fc-secondary-text);cursor:pointer;font-size:12px;font-weight:500;height:auto;padding:7px 10px;transition:background-color .2s ease}.fcrm_btn_small{padding:3px 8px}.fcrm_pro_btn,.fcrm_pro_btn:hover{background:var(--fc-deep-bg);border-color:var(--fc-deep-bg)}.editor-preview-dropdown__toggle.components-dropdown-menu__toggle{align-items:center;display:flex;gap:8px}.components-button.fcrm-compose-preview,.components-button.fcrm-compose-smartcodes{padding:7px}.components-button.fcrm-footer-smartcode-btn{align-items:center;background:var(--fc-secondary-bg);border:1px solid;border-color:var(--fc-secondary-border);border-radius:4px;color:var(--fc-deep-bg);display:inline-flex;gap:4px;height:auto;margin-inline-start:10px;padding:4px 8px}.editor-styles-wrapper{padding-bottom:0!important}body .is-root-container>.alignfull{margin-inline-end:calc(var(--fcrm-padding-right, -20px)*-1);margin-inline-start:calc(var(--fcrm-padding-left, 20px)*-1)}h1,h2,h3{word-break:normal}.interface-interface-skeleton{top:0}@media(max-width:782px){.admin-bar .interface-interface-skeleton{top:46px}}html.interface-interface-skeleton__html-container{margin-top:0!important;overscroll-behavior:auto!important}.interface-interface-skeleton__body{overscroll-behavior-y:auto!important}.interface-interface-skeleton__content{overscroll-behavior:auto!important}p>a{text-decoration:underline!important;text-underline-offset:.15em!important}a[href="edit.php?post_type=wp_block"]{display:none}.wporg-gutenberg-block-layout{display:block!important}html{font-size:var(--theme-font-size,16px)}.wporg-gutenberg-hide-on-mobile{display:none!important}@media(min-width:782px){.wporg-gutenberg-hide-on-mobile{display:inherit!important}}.components-notice-list{display:none!important}.editor-header__back-button{display:none}@media(min-width:782px){.editor-header:has(>.editor-header__center){grid-template:auto/0 minmax(min-content,1fr) 2fr minmax(min-content,1fr) 60px}}.wp-embed-aspect-16-9{aspect-ratio:16/9}.components-modal__screen-overlay.commands-command-menu__overlay,body:not(.fcrm-compose-ui) .editor-header.edit-post-header .editor-post-publish-button,body:not(.fcrm-compose-ui) .editor-header.edit-post-header .editor-post-save-draft{display:none!important}.editor-post-last-edited-panel,.editor-post-summary .components-flex [data-wp-component=VStack] button:not(.fcrm-email-body-settings-btn),.editor-post-summary .editor-post-panel__row,span.editor-document-bar__shortcut{display:none}.editor-header .edit-post-fullscreen-mode-close,body.fcrm-compose-ui .editor-post-summary,body:not(.fcrm-compose-ui) .editor-header .editor-post-publish-button__button{display:none!important}.interface-interface-skeleton{right:0!important;left:0!important}body:not(.fcrm-compose-ui) .editor-header button[aria-label*="Submit for Review"],body:not(.fcrm-compose-ui) .editor-header button[aria-label*="submit for review"],body:not(.fcrm-compose-ui) .editor-header__actions .editor-post-publish-button__button,body:not(.fcrm-compose-ui) .editor-header__toolbar .components-button:first-of-type,body:not(.fcrm-compose-ui) .editor-header__toolbar .components-button[aria-label*=WordPress],body:not(.fcrm-compose-ui) .editor-header__toolbar .components-button[aria-label*=wordpress],body:not(.fcrm-compose-ui) .editor-header__toolbar .wp-logo,body:not(.fcrm-compose-ui) .editor-header__toolbar button[aria-label*=WordPress],body:not(.fcrm-compose-ui) .editor-header__toolbar button[aria-label*=wordpress],body:not(.fcrm-compose-ui) .editor-header__toolbar>.components-button:first-child,body:not(.fcrm-compose-ui) .editor-header__toolbar>button:first-child,body:not(.fcrm-compose-ui) button.editor-post-publish-button__button{display:none!important}.edit-post-header,.editor-header{--fcrm-compose-btn-bg:#f3f4f6;--fcrm-compose-btn-border:#9ca3af;--fcrm-compose-btn-text:#1f2937;--fcrm-compose-btn-hover:#e5e7eb}.edit-post-header-toolbar__left .fcrm-compose-smartcodes-left,.edit-post-header__settings .fcrm-compose-fullscreen,.edit-post-header__settings .fcrm-compose-smartcodes,.edit-post-header__settings .fcrm-email-preview-btn,.editor-header__toolbar .fcrm-compose-smartcodes-left{height:32px;padding:6px;width:32px}body.fcrm-compose-ui .edit-post-header__settings button[aria-label*=Preview]:not(.fcrm-compose-preview):not([aria-label*=Email]),body.fcrm-compose-ui .editor-header .fcrm-compose-actions~* button[aria-label*=Preview]:not(.fcrm-compose-preview),body.fcrm-compose-ui .editor-header__actions button[aria-label*=Preview]:not(.fcrm-compose-preview):not([aria-label*=Email]),body.fcrm-compose-ui .editor-header__settings button[aria-label*=Preview]:not(.fcrm-compose-preview):not([aria-label*=Email]){display:none!important}.edit-post-header .fcrm-compose-actions,.editor-header .fcrm-compose-actions{align-items:center!important;display:flex!important;gap:10px!important;margin-right:auto!important}body.fcrm-compose-ui .edit-post-header-toolbar__left .block-editor-inserter__toggle,body.fcrm-compose-ui .edit-post-header-toolbar__left [aria-label*="Add block"],body.fcrm-compose-ui .edit-post-header-toolbar__left [aria-label*="add block"],body.fcrm-compose-ui .edit-post-header-toolbar__left button:not(.fcrm-compose-back),body.fcrm-compose-ui .editor-header__toolbar .block-editor-inserter__toggle,body.fcrm-compose-ui .editor-header__toolbar [aria-label*="Add block"],body.fcrm-compose-ui .editor-header__toolbar [aria-label*="add block"],body.fcrm-compose-ui .editor-header__toolbar button:not(.fcrm-compose-back){display:inline-flex!important}body.fcrm-compose-ui .editor-header__actions{display:flex!important;visibility:visible!important}body.fcrm-compose-ui .edit-post-header__settings,body.fcrm-compose-ui .editor-header__settings{align-items:center!important;display:flex!important;gap:10px!important;visibility:visible!important}body.fcrm-compose-ui .edit-post-header__settings .editor-post-publish-button,body.fcrm-compose-ui .edit-post-header__settings .editor-post-publish-button__button,body.fcrm-compose-ui .edit-post-header__settings .editor-post-save-draft,body.fcrm-compose-ui .edit-post-header__settings button[aria-label*=Publish],body.fcrm-compose-ui .edit-post-header__settings button[aria-label*=Submit],body.fcrm-compose-ui .editor-header .editor-post-publish-button,body.fcrm-compose-ui .editor-header .editor-post-publish-button__button,body.fcrm-compose-ui .editor-header__actions .editor-post-publish-button,body.fcrm-compose-ui .editor-header__actions .editor-post-publish-button__button,body.fcrm-compose-ui .editor-header__actions .editor-post-save-draft,body.fcrm-compose-ui .editor-header__actions button[aria-label*=Publish],body.fcrm-compose-ui .editor-header__actions button[aria-label*=Submit],body.fcrm-compose-ui .editor-header__settings .editor-post-publish-button,body.fcrm-compose-ui .editor-header__settings .editor-post-publish-button__button,body.fcrm-compose-ui .editor-header__settings .editor-post-save-draft,body.fcrm-compose-ui .editor-header__settings button[aria-label*=Publish],body.fcrm-compose-ui .editor-header__settings button[aria-label*=Submit]{display:none!important}body.fcrm-compose-ui .edit-post-header-toolbar__left .fcrm-compose-smartcodes,body.fcrm-compose-ui .edit-post-header__settings .fcrm-compose-fullscreen,body.fcrm-compose-ui .edit-post-header__settings .fcrm-compose-next,body.fcrm-compose-ui .edit-post-header__settings .fcrm-compose-preview,body.fcrm-compose-ui .edit-post-header__settings .fcrm-compose-save,body.fcrm-compose-ui .editor-header .fcrm-compose-actions .fcrm-compose-fullscreen,body.fcrm-compose-ui .editor-header .fcrm-compose-actions .fcrm-compose-next,body.fcrm-compose-ui .editor-header .fcrm-compose-actions .fcrm-compose-preview,body.fcrm-compose-ui .editor-header .fcrm-compose-actions .fcrm-compose-save,body.fcrm-compose-ui .editor-header__actions .fcrm-compose-fullscreen,body.fcrm-compose-ui .editor-header__actions .fcrm-compose-next,body.fcrm-compose-ui .editor-header__actions .fcrm-compose-preview,body.fcrm-compose-ui .editor-header__actions .fcrm-compose-save,body.fcrm-compose-ui .editor-header__settings .fcrm-compose-fullscreen,body.fcrm-compose-ui .editor-header__settings .fcrm-compose-next,body.fcrm-compose-ui .editor-header__settings .fcrm-compose-preview,body.fcrm-compose-ui .editor-header__settings .fcrm-compose-save,body.fcrm-compose-ui .editor-header__toolbar .fcrm-compose-smartcodes{display:inline-flex!important;opacity:1!important;visibility:visible!important}body.fcrm-compose-ui .edit-post-header .fcrm-compose-actions,body.fcrm-compose-ui .editor-header .fcrm-compose-actions{display:flex!important;visibility:visible!important}.fcrm-layout-selector .fcrm-layout-grid{border-bottom:1px solid var(--fc-primary-border);display:grid;gap:16px;grid-template-columns:repeat(auto-fill,minmax(100px,1fr));padding-bottom:16px}.fcrm-layout-selector .fcrm-layout-thumb-placeholder{background-color:#e8e8e8}.fcrm-layout-selector .fcrm-layout-thumb-svg{align-items:center;background-color:#f0f0f0;display:flex;justify-content:center;padding:4px}.fcrm-layout-selector .fcrm-layout-thumb-svg svg{fill:currentColor;height:100%;max-height:32px;max-width:36px;width:100%}.fcrm-layout-selector .fcrm-layout-option{background:none;border:none;border-radius:8px;cursor:pointer;display:block;margin:0;padding:0;position:relative}.fcrm-layout-selector .fcrm-layout-option.is-selected .icon,.fcrm-layout-selector .fcrm-layout-option:focus .icon,.fcrm-layout-selector .fcrm-layout-option:hover .icon{opacity:1;transform:scale(1)}.fcrm-layout-selector .fcrm-layout-option.is-selected .fcrm-layout-thumb,.fcrm-layout-selector .fcrm-layout-option:focus .fcrm-layout-thumb,.fcrm-layout-selector .fcrm-layout-option:hover .fcrm-layout-thumb{outline-color:var(--fc-primary-text)}.fcrm-layout-selector .fcrm-layout-option .icon{opacity:0;position:absolute;left:4px;top:4px;transform:scale(.4);transition:.3s;-webkit-transition:.3s}.fcrm-layout-selector .fcrm-layout-option .icon svg{display:block}.fcrm-layout-selector .fcrm-layout-option .fcrm-layout-thumb{aspect-ratio:4/3;border-radius:8px;display:block;margin:0;min-height:80px;outline:1px solid transparent;outline-offset:-1px;overflow:hidden;width:100%}.fcrm-layout-selector .fcrm-layout-option .fcrm-layout-thumb img,.fcrm-layout-selector .fcrm-layout-option .fcrm-layout-thumb svg{display:block;height:100%;object-fit:contain;width:100%}.fcrm-layout-selector .fcrm-layout-option .fcrm-layout-label{color:var(--fc-primary-text);display:block;font-size:13px;font-weight:500;line-height:16px;margin:8px 0 0;padding:0 4px;text-align:center}.fcrm-smartcode-examples-panel{--fcrm-sidebar-accent:#4b5563;--fcrm-sidebar-accent-soft:#f3f4f6;--fcrm-sidebar-accent-hover:#e5e7eb;--fcrm-sidebar-border:#d1d5db;--fcrm-sidebar-text:#1f2937;--fcrm-sidebar-muted:#6b7280}.fcrm-template-buttons{display:flex;flex-direction:column;gap:8px;padding:16px 16px 0}.fcrm-template-buttons .components-button{justify-content:center!important}.fcrm-smartcode-tip{color:var(--fcrm-sidebar-text);font-size:12px;line-height:1.45;margin:0 0 10px}.fcrm-smartcode-tip code{background:var(--fcrm-sidebar-accent-soft);border-radius:3px;padding:1px 4px}.fcrm-smartcode-group{box-sizing:border-box;margin-bottom:12px}.fcrm-smartcode-group *,.fcrm-smartcode-group :after,.fcrm-smartcode-group :before{box-sizing:border-box}.fcrm-smartcode-group__title{color:var(--fcrm-sidebar-text);font-size:12px;letter-spacing:.5px;margin:0 0 6px;text-transform:uppercase}.fcrm-style-panel .fcrm-sub-panel{border-top:none;margin:20px 0}.fcrm-style-panel .fcrm-sub-panel .spacing-sizes-control{margin-top:10px}.fcrm-smartcode-group__list{list-style:none;margin:0;padding:0}.fcrm-smartcode-group__search{background:#fff;border:1px solid var(--fcrm-sidebar-border);border-radius:4px;color:var(--fcrm-sidebar-text);font-size:12px;margin:0 0 8px;padding:6px 8px;width:100%}.fcrm-smartcode-group__item{margin-bottom:7px}.fcrm-smartcode-group__row{align-items:center;display:flex;flex-wrap:wrap;gap:6px}.fcrm-smartcode-group__code{flex-grow:1;font-size:12px;line-height:1.4;padding:3px 6px}.fcrm-smartcode-group__code,.fcrm-smartcode-group__copy-btn{background:var(--fcrm-sidebar-accent-soft);border:1px solid var(--fcrm-sidebar-border);border-radius:4px;color:var(--fcrm-sidebar-text);cursor:pointer}.fcrm-smartcode-group__copy-btn{font-size:11px;padding:3px 7px}.fcrm-smartcode-group__copy-btn.is-copied{background:var(--fcrm-sidebar-accent);border-color:var(--fcrm-sidebar-accent);color:#fff}.fcrm-smartcode-group__label{color:var(--fcrm-sidebar-muted);font-size:11px;margin-top:2px}.fcrm-smartcode-group__empty{color:var(--fcrm-sidebar-muted);font-size:11px;padding:4px 0}.fcrm-smartcode-toolbar-popover *{box-sizing:border-box}.fcrm-smartcode-toolbar-popover .components-popover__content{background:#fff;border:1px solid var(--fc-primary-border);border-radius:8px;box-shadow:0 16px 32px -12px rgba(14,18,27,.102);max-height:100%!important;max-width:578px;overflow:hidden!important;padding:8px;width:min(500px,100vw - 32px)}.fcrm-smartcode-toolbar-popover .fcrm-smartcode-toolbar-popover__wrap{box-sizing:border-box;display:flex;max-height:400px;min-height:0}.fcrm-smartcode-toolbar-popover .fcrm-smartcode-toolbar-popover__sidebar{background:#f9fafb;border-left:1px solid #e1e4ea;display:flex;flex:none;flex-direction:column;list-style:none;margin:0;max-height:400px;max-width:190px;min-height:0;overflow-y:auto;padding:12px;position:relative;width:190px;word-break:normal}.fcrm-smartcode-toolbar-popover .fcrm-smartcode-toolbar-popover__body{display:flex;flex:1;flex-direction:column;max-height:400px;min-height:0;overflow-x:hidden;overflow-y:auto;padding:0}.fcrm-smartcode-toolbar-popover .fcrm-smartcode-toolbar-popover__items{padding:0 8px}.fcrm-smartcode-toolbar-popover .fcrm-smartcode-toolbar-popover__search{background:#fff;border-bottom:1px solid #e1e4ea;padding:10px;position:sticky;top:0;z-index:1}.fcrm-smartcode-toolbar-popover .fcrm-smartcode-toolbar-popover__search input{border:1px solid #e1e4ea;border-radius:6px;box-shadow:none;height:36px!important;padding:6px 10px}.fcrm-smartcode-toolbar-popover .fcrm-smartcode-toolbar-popover__doc-btn{background:#f5f7fa;border:none!important;border-radius:6px;box-shadow:none!important;color:#525866;display:block;font-size:13px;font-weight:500;justify-content:center;margin-top:auto;padding:6px 8px;text-align:center;text-decoration:none;transition:all .2s ease;width:100%}.fcrm-smartcode-toolbar-popover .fcrm-smartcode-toolbar-popover__doc-btn:hover{background:#0e121b!important;color:#fff!important}.fcrm-smartcode-toolbar-popover__tabs{display:flex;flex-direction:column;gap:4px;max-height:360px;overflow:auto}.fcrm-smartcode-toolbar-popover__tab{background:transparent;border:1px solid transparent;border-radius:8px;color:#4b5563;cursor:pointer;display:block;font-size:12px;line-height:16px;padding:9px 10px;text-align:right;width:100%}.fcrm-smartcode-toolbar-popover__tab.is-active{background:#0f172a;color:#fff}.fcrm-smartcode-toolbar-popover__feedback{color:#475569;font-size:12px;line-height:16px}.fcrm-smartcode-toolbar-popover__item{background:none;border:none;border-bottom:1px solid #e1e4ea;color:#0e121b;cursor:pointer;display:block;font-size:14px;line-height:20px;margin-bottom:0;padding:8px;text-align:right;transition:background-color .2s ease;white-space:normal;width:100%;word-break:normal!important}.fcrm-smartcode-toolbar-popover__item:last-child{border-bottom:none}.fcrm-smartcode-toolbar-popover__item:hover{background:#f9fafb}.fcrm-smartcode-toolbar-popover__item .fcrm-smartcode-toolbar-popover__item-label{color:#0e121b;display:block;font-size:13px;line-height:16px}.fcrm-smartcode-toolbar-popover__item .fcrm-smartcode-toolbar-popover__item-code{color:#99a0ae;display:block;font-size:10px;line-height:14px;margin:2px 0 0;overflow-wrap:break-word;word-break:break-all}.fcrm-smartcode-toolbar-popover__empty{color:#6b7280;font-size:13px;padding:14px 8px}@keyframes onOffInterval{0%{offset-distance:0}to{offset-distance:100%}}.fcrm_ai_button_anim_wrapper{border-radius:8px;clip-path:inset(0 round 8px);overflow:hidden;padding:1px;position:relative;z-index:1}.fcrm_ai_button_anim_wrapper .fcrm_ai_button_anim{background:transparent;border:0;border-radius:8px;clip-path:inset(0 round 8px);container-type:inline-size;inset:0;position:absolute;z-index:-1}.fcrm_ai_button_anim_wrapper .fcrm_ai_button_anim_inner{animation:onOffInterval 4s linear infinite;aspect-ratio:1/1;background:radial-gradient(circle at 100% 8px,#8762f0,transparent 70%);border-radius:8px;offset-anchor:100% 60%;offset-path:border-box;position:absolute;width:50cqmin}.fcrm_ai_button_anim_wrapper .fcrm_ai_button{position:relative;z-index:2}.fcrm-layout-settings-modal__content{max-width:480px;min-width:320px}.fcrm-layout-settings-modal__extra{border-top:1px solid #dcdcde;margin-top:20px;padding-top:16px}.fcrm-layout-settings-modal__extra:empty{display:none}.fcrm-ai-writing-popover *{box-sizing:border-box}.fcrm-ai-writing-popover .components-popover__content{border:1px solid #e1e4ea;border-radius:8px;box-shadow:0 4px 16px rgba(0,0,0,.1);overflow:hidden;width:min(380px,100vw - 32px)}.fcrm-ai-writing-popover__wrap{display:flex;flex-direction:column;max-height:500px}.fcrm-ai-writing-popover__header{align-items:center;border-bottom:1px solid #e1e4ea;color:#0e121b;display:flex;font-size:14px;font-weight:600;gap:8px;justify-content:space-between;padding:10px 16px}.fcrm-ai-writing-popover__tone-badge{align-items:center;background:#f9fafb;border:1px solid #e1e4ea;border-radius:14px;color:#525866;cursor:pointer;display:inline-flex;font-size:12px;font-weight:500;gap:4px;padding:3px 8px;transition:border-color .15s ease,background .15s ease;white-space:nowrap}.fcrm-ai-writing-popover__tone-badge:hover{background:#f0f1f3;border-color:#cacfd8}.fcrm-ai-writing-popover__tone-badge-label{color:#99a0ae;font-weight:400}.fcrm-ai-writing-popover__tone-badge-value{color:#0e121b}.fcrm-ai-writing-popover__tone-badge-arrow{color:#99a0ae;font-size:10px}.fcrm-ai-writing-popover__sub-header{align-items:center;border-bottom:1px solid #f0f1f3;color:#0e121b;display:flex;font-size:13px;font-weight:600;gap:8px;padding:8px 16px}.fcrm-ai-writing-popover__back-btn{background:none;border:none;border-radius:4px;color:#525866;cursor:pointer;font-size:16px;padding:2px 6px}.fcrm-ai-writing-popover__back-btn:hover{background:#f5f7fa}.fcrm-ai-writing-popover__actions{display:flex;flex-direction:column;gap:1px;padding:6px}.fcrm-ai-writing-popover__action{align-items:flex-start;background:transparent;border:none;border-radius:6px;color:#0e121b;cursor:pointer;display:flex;flex-direction:column;gap:2px;padding:8px 12px;text-align:right;width:100%}.fcrm-ai-writing-popover__action:hover{background:#f5f7fa}.fcrm-ai-writing-popover__action-label{font-size:13px;font-weight:500}.fcrm-ai-writing-popover__action-desc{color:#99a0ae;font-size:12px}.fcrm-ai-writing-popover__tone-options{display:flex;flex-wrap:wrap;gap:6px;padding:8px 16px}.fcrm-ai-writing-popover__tone-btn{background:#fff;border:1px solid #e1e4ea;border-radius:20px;color:#525866;cursor:pointer;font-size:13px;padding:6px 14px}.fcrm-ai-writing-popover__tone-btn:hover{background:#f5f7fa;border-color:#cacfd8}.fcrm-ai-writing-popover__tone-btn.is-active{background:#0e121b;border-color:#0e121b;color:#fff}.fcrm-ai-writing-popover__custom-input{padding:8px 16px}.fcrm-ai-writing-popover__loading{color:#525866;padding:32px 16px;text-align:center}.fcrm-ai-writing-popover__loading p{font-size:13px;margin:8px 0 0}.fcrm-ai-writing-popover__preview-text{background:#f5f7fa;border-radius:6px;color:#0e121b;font-size:13px;line-height:1.6;margin:8px 16px;max-height:240px;overflow-y:auto;padding:12px}.fcrm-ai-writing-popover__footer{background:#f9fafb;border-top:1px solid var(--fc-primary-border);display:flex;gap:8px;justify-content:flex-end;padding:12px 16px}.fcrm-ai-writing-popover__error{background:#fef2f2;border-bottom:1px solid #fecaca;color:#e5484d;font-size:13px;padding:8px 16px}.fcrm-ai-writer-block *{box-sizing:border-box}.fcrm-ai-writer-block__inner{background:var(--fc-primary-bg);border:1px solid rgba(0,0,0,.078);border-radius:8px;box-shadow:0 1px 3px -1.5px rgba(51,51,51,.161),0 5px 5px -2.5px rgba(51,51,51,.078),0 12px 6px -6px rgba(51,51,51,.02),0 16px 8px -8px rgba(51,51,51,.012),0 0 0 1px rgba(51,51,51,.039),inset 0 -.5px .5px 0 rgba(51,51,51,.078);overflow:hidden}.fcrm-ai-writer-block__header{align-items:center;border-bottom:1px solid var(--fc-primary-border);display:flex;gap:12px;padding:12px 16px}.fcrm-ai-writer-block__icon{align-items:center;background:rgba(119,66,230,.161);border-radius:6px;color:#8762f0;display:flex;height:26px;justify-content:center;width:26px}.fcrm-ai-writer-block__icon svg{display:block}.fcrm-ai-writer-block__title{color:var(--fc-primary-text);flex:1;font-size:14px;font-weight:500;line-height:20px;margin:0}.fcrm-ai-writer-block__controls{align-items:center;display:flex;gap:8px}.fcrm-ai-writer-block__select{background:var(--fc-secondary-bg);border:1px solid var(--fc-primary-border);border-radius:8px;color:var(--fc-secondary-text);cursor:pointer;font-size:14px;font-weight:500;line-height:20px;outline:none;padding:4px 6px}.fcrm-ai-writer-block__select:hover{border-color:#cacfd8}.fcrm-ai-writer-block__select:focus{border-color:#3e58e1}.fcrm-ai-writer-block__prompt{background:transparent;border:none;color:#0e121b;display:block;font-family:inherit;font-size:14px;line-height:1.5;min-height:72px;outline:none;padding:12px 14px;resize:vertical;width:100%}.fcrm-ai-writer-block__prompt::placeholder{color:#99a0ae}.fcrm-ai-writer-block__prompt:disabled{opacity:.6}.fcrm-ai-writer-block__error{background:#fef2f2;color:#e5484d;font-size:13px;padding:8px 14px}.fcrm-ai-writer-block__footer{align-items:center;background:#f9fafb;border-top:1px solid #e1e4ea;display:flex;flex-wrap:wrap;gap:12px;justify-content:space-between;padding:10px 12px}.fcrm-ai-writer-block__tip{color:var(--fc-text-muted);font-size:14px;font-weight:500;line-height:20px;margin:0}.components-button.fcrm-ai-writer-block__generate-button,.fcrm-ai-writer-block__generate-button{align-items:center;background:#efebff;border:1px solid #efebff;border-radius:8px;color:#8762f0;display:inline-flex;font-size:14px;font-weight:500;gap:4px;line-height:20px;min-height:36px}.components-button.fcrm-ai-writer-block__generate-button:disabled,.fcrm-ai-writer-block__generate-button:disabled{background:var(--fc-primary-bg)!important;border-color:#8762f0;color:#8762f0!important;opacity:.6}.components-button.fcrm-ai-writer-block__generate-button:hover,.fcrm-ai-writer-block__generate-button:hover{background:var(--fc-primary-bg)!important;color:#8762f0!important}.components-button.fcrm-ai-writer-block__generate-button .icon,.fcrm-ai-writer-block__generate-button .icon{display:block;height:20px;width:20px}.components-button.fcrm-ai-writer-block__generate-button .icon svg,.fcrm-ai-writer-block__generate-button .icon svg{display:block}.fcrm-ai-writer-block__loading{align-items:center;color:#525866;display:flex;font-size:13px;gap:8px}.fcrm-ai-writer-block__loading svg{margin:0}.fcrm-email-body-panel-wrapper.components-panel__body{border-top:none!important;padding:0!important}.fcrm-email-body-panel-wrapper.components-panel__body>.components-panel__body-title{display:none!important}.edit-post-header .components-button,.editor-header .components-button{border-radius:6px}.edit-post-header .components-button.is-primary,.editor-header .components-button.is-primary{background:var(--static-static-black,#0e121b);color:#fff}.edit-post-header .components-button.fcrm-compose-back,.editor-header .components-button.fcrm-compose-back{align-items:center;background:var(--static-static-black,#0e121b);border:none;box-shadow:none;color:#fff;cursor:pointer;display:flex;height:32px;justify-content:center;margin:0 0 0 10px;width:32px}.edit-post-header .components-button.fcrm-compose-back .fcrm-back-icon svg,.editor-header .components-button.fcrm-compose-back .fcrm-back-icon svg{display:block}.edit-post-header .components-button.fcrm-compose-save,.editor-header .components-button.fcrm-compose-save{justify-content:center;min-width:72px}.edit-post-header .components-button.fcrm-compose-ai-writing .fcrm-ai-writing-icon svg,.edit-post-header .components-button.fcrm-compose-smartcodes .fcrm-smartcode-icon svg,.editor-header .components-button.fcrm-compose-ai-writing .fcrm-ai-writing-icon svg,.editor-header .components-button.fcrm-compose-smartcodes .fcrm-smartcode-icon svg{display:block}.editor-styles-wrapper .fcrm-smartcode-highlight{background:#ebf1ff;border-radius:3px;color:#335cff;font-size:.92em;padding:1px 3px}.editor-styles-wrapper{background-color:var(--fcrm-body-bg,#fafafa)!important;color:var(--theme-text-color);font-family:var(--theme-font-family)!important;max-width:var(--fcrm-content-width,700px)!important}.editor-styles-wrapper p{font-size:var(--theme-font-size,16px);line-height:var(--theme-line-height,1.6)}.is-root-container{background-color:var(--fcrm-content-bg,#fff)!important;border-radius:var(--fcrm-content-radius,0)!important;margin-bottom:var(--fcrm-margin-bottom,20px)!important;margin-top:var(--fcrm-margin-top,20px)!important;overflow:hidden;padding:var(--fcrm-padding-top,20px) var(--fcrm-padding-left,20px) var(--fcrm-padding-bottom,20px) var(--fcrm-padding-right,20px)!important}h1,h2,h3,h4,h5,h6{color:var(--fcrm-headings-color,#202020);font-family:var(--fcrm-headings-font)!important}a{color:var(--fcrm-link-color,#0693e3)}.fcrm-sidebar-tabs [role=tablist]{border-bottom:1px solid #e0e0e0;display:flex!important;margin:16px auto}.fcrm-design-presets,.fcrm-smartcodes-tab{padding:4px 16px 0}.fcrm-design-presets__grid{display:grid;gap:12px;grid-template-columns:repeat(auto-fill,minmax(100px,1fr))}.fcrm-design-presets__item{background:none;border:2px solid transparent;border-radius:8px;cursor:pointer;display:block;margin:0;padding:0;text-align:center}.fcrm-design-presets__item:focus,.fcrm-design-presets__item:hover{border-color:#d1d5db}.fcrm-design-presets__item.is-selected{border-color:var(--wp-admin-theme-color,#3e58e1)}.fcrm-design-presets__thumb{aspect-ratio:4/3;background:#f0f0f0;border-radius:6px;display:block;overflow:hidden}.fcrm-design-presets__thumb img{display:block;height:100%;object-fit:cover;width:100%}.fcrm-design-presets__placeholder{background:#e8e8e8;display:block;height:100%;width:100%}.fcrm-design-presets__label{color:var(--fcrm-sidebar-text,#1f2937);display:block;font-size:12px;font-weight:500;line-height:16px;margin-top:6px;padding:0 2px 4px}.fcrm-design-presets__empty{color:var(--fcrm-sidebar-muted,#6b7280);font-size:13px;padding:16px}.fcrm-email-style-settings .fcrm-style-panel{border:none!important;margin-bottom:4px}.fcrm-email-style-settings .fcrm-style-panel>.components-panel__body-title{background:hsla(0,0%,94%,.612);margin-bottom:0}.fcrm-email-style-settings .fcrm-style-panel>.components-panel__body-title .components-panel__body-toggle{color:var(--fcrm-sidebar-text,#1f2937);font-size:13px;font-weight:600}.fcrm-email-style-settings .fcrm-style-panel>.components-panel__body-title+div{padding:0 0 8px}.fcrm-email-style-settings .fcrm-style-panel .components-range-control .components-base-control__label{font-size:12px;font-weight:500}.fcrm-email-style-settings .fcrm-style-panel .spacing-sizes-control__wrapper{align-items:center}.fcrm-email-style-settings .fcrm-style-panel .components-select-control{margin-bottom:8px}.fcrm-email-style-settings .fcrm-style-panel .components-select-control .components-base-control__label{font-size:12px;font-weight:500}.fcrm-email-style-settings .fcrm-style-panel .fcrm-typo-section{border-top:1px solid #e0e0e0;margin-top:16px;padding-top:16px}.fcrm-email-style-settings .fcrm-style-sub-panel{border:1px solid var(--fc-primary-border);border-radius:6px;margin-bottom:8px;padding:0}.fcrm-email-style-settings .fcrm-style-sub-panel>.components-panel__body-title .components-panel__body-toggle{font-size:12px;font-weight:500;padding:8px 10px}.fcrm-email-style-settings .fcrm-style-sub-panel>.components-panel__body-title+div{padding:4px 10px 10px}.fcrm-footer-settings-modal .components-modal__content{max-width:calc(100vw - 32px);width:640px}.fcrm-footer-settings-modal .components-radio-control .components-base-control__label{display:none}.fcrm-footer-settings-modal .components-radio-control .components-radio-control__option{margin-bottom:2px}.fcrm-footer-settings-modal .components-radio-control .components-radio-control__option label{font-size:13px;font-weight:500}.fcrm-footer-settings-modal .fcrm-footer-tinymce-wrap .fcrm-footer-tinymce__header{align-items:center;display:flex;justify-content:space-between;margin-bottom:8px}.fcrm-footer-settings-modal .fcrm-footer-tinymce-wrap .fcrm-footer-tinymce__header .fcrm-footer-tinymce__label{margin:0}.fcrm-footer-settings-modal .fcrm-footer-tinymce-wrap .fcrm-footer-tinymce__header .fcrm-footer-editor-actions{align-items:center;display:flex;gap:8px}.fcrm-footer-settings-modal .fcrm-footer-tinymce-wrap .fcrm-footer-tinymce__header .fcrm-footer-editor-toggle .fcrm-footer-toggle-switch{background:#f0f0f0;border-radius:6px;display:flex;gap:2px;height:28px;padding:3px}.fcrm-footer-settings-modal .fcrm-footer-tinymce-wrap .fcrm-footer-tinymce__header .fcrm-footer-editor-toggle .fcrm-footer-toggle-switch button{background:transparent;border:none;border-radius:4px;color:#757575;cursor:pointer;flex:1;font-size:12px;font-weight:500;line-height:1;padding:2px 10px;transition:all .15s ease;white-space:nowrap}.fcrm-footer-settings-modal .fcrm-footer-tinymce-wrap .fcrm-footer-tinymce__header .fcrm-footer-editor-toggle .fcrm-footer-toggle-switch button.active{background:#fff;box-shadow:0 1px 3px rgba(0,0,0,.12),0 1px 2px rgba(0,0,0,.06);color:#1e1e1e}.fcrm-footer-settings-modal .fcrm-footer-tinymce-wrap .fcrm-footer-tinymce__header .fcrm-footer-editor-toggle .fcrm-footer-toggle-switch button:hover:not(.active){color:#3c434a}.fcrm-footer-settings-modal .fcrm-footer-tinymce-wrap .fcrm-footer-tinymce__label{color:#1e1e1e;font-size:11px;font-weight:500;margin:0 0 8px;text-transform:uppercase}.fcrm-footer-settings-modal .fcrm-footer-tinymce-wrap .mce-tinymce{border-radius:4px!important;overflow:hidden}.fcrm-footer-settings-modal .fcrm-footer-tinymce-wrap .fcrm-footer-tinymce__help{color:#6b7280;font-size:12px;line-height:1.4;margin:8px 0 0}.fcrm-footer-settings-modal .fcrm-footer-tinymce-wrap .fcrm-footer-text-mode{border:1px solid #ddd;border-radius:4px;overflow:hidden}.fcrm-footer-settings-modal .fcrm-footer-tinymce-wrap .fcrm-footer-text-mode__quicktags{background:#f9f9f9;border-bottom:1px solid #ddd;display:flex;flex-wrap:wrap;gap:4px;padding:6px 8px}.fcrm-footer-settings-modal .fcrm-footer-tinymce-wrap .fcrm-footer-text-mode__quicktags button{background:#fff;border:1px solid #c3c4c7;border-radius:3px;color:#2c3338;cursor:pointer;font-size:12px;font-weight:500;line-height:1.4;padding:3px 8px;transition:border-color .1s ease,background .1s ease}.fcrm-footer-settings-modal .fcrm-footer-tinymce-wrap .fcrm-footer-text-mode__quicktags button:hover{background:#f0f6fc;border-color:#2271b1;color:#2271b1}.fcrm-footer-settings-modal .fcrm-footer-tinymce-wrap .fcrm-footer-text-mode__textarea{background:#fff;border:none;box-sizing:border-box;color:#2c3338;display:block;font-family:Consolas,Monaco,Courier New,monospace;font-size:12px;line-height:1.6;min-height:160px;padding:8px;resize:vertical;width:100%}.fcrm-footer-settings-modal .fcrm-footer-tinymce-wrap .fcrm-footer-text-mode__textarea:focus{box-shadow:inset 0 0 0 1px #2271b1;outline:none}.fcrm-footer-settings-modal .components-input-control__label{font-size:12px;font-weight:500}.fcrm-footer-settings-modal__content{padding-bottom:4px}.fcrm-footer-settings__hint{color:#6b7280;font-size:12px;line-height:1.4;margin:-8px 0 0}.fcrm-footer-settings__divider{border-top:1px solid #e0e0e0;margin:0}.fcrm-footer-settings__done{align-self:flex-end}.mce-floatpanel,.mce-tooltip,.mce-window{z-index:200000!important}.mce-modal-block{z-index:199999!important}.fcrm-color-toggle-wrap{align-items:center;border:1px solid #e0e0e0;border-radius:6px;display:inline-flex;gap:6px;padding:6px 10px;transition:border-color .15s ease}.fcrm-color-toggle-wrap:hover{border-color:#9ca3af}.fcrm-color-toggle{border:none;border-radius:0;display:inline-flex;gap:8px;position:relative}.fcrm-color-toggle,.fcrm-color-toggle__clear{align-items:center;background:#fff;cursor:pointer;padding:0}.fcrm-color-toggle__clear{border:none;border-radius:999px;color:#9ca3af;display:flex;font-size:15px;font-weight:500;height:20px;justify-content:center;line-height:1;text-align:center;-webkit-user-select:none;user-select:none;width:20px}.fcrm-color-toggle__clear svg{display:block;height:14px;width:14px}.fcrm-color-toggle__clear:hover{background:#f1f5f9;color:#4b5563}.fcrm-color-indicator{border:1px solid rgba(0,0,0,.15);border-radius:50%;display:block;flex-shrink:0;height:20px;width:20px}.fcrm-color-hex{color:#4b5563;font-family:monospace;font-size:12px}.block-editor-block-inspector__advanced{display:none!important}.fcrm-smart-link-popover .components-popover__content{background:#fff;border:1px solid var(--fc-primary-border);border-radius:8px;box-shadow:0 16px 32px -12px rgba(14,18,27,.1);overflow:hidden!important}.fcrm-smart-link-popover .fcrm-smart-link-popover__content{display:flex;flex-direction:column;max-height:420px;width:320px}.fcrm-smart-link-popover .fcrm-smart-link-popover__content .components-search-control{border-bottom:1px solid #e1e4ea;flex-shrink:0;padding:8px}.fcrm-smart-link-popover .fcrm-smart-link-popover__content .components-search-control input{border:1px solid #e1e4ea;border-radius:6px;box-shadow:none;font-size:13px;height:36px!important;padding:6px 10px}.fcrm-smart-link-popover .fcrm-smart-link-popover__loading{display:flex;justify-content:center;padding:24px 16px}.fcrm-smart-link-popover .fcrm-smart-link-popover__empty{color:#6b7280;font-size:13px;padding:24px 16px;text-align:center}.fcrm-smart-link-popover .fcrm-smart-link-popover__list{max-height:340px;overflow-y:auto;padding:4px 0}.fcrm-smart-link-popover .fcrm-smart-link-popover__item{align-items:flex-start;background:none;border:none;cursor:pointer;display:flex;flex-direction:column;gap:2px;padding:8px 12px;text-align:right;transition:background-color .15s ease;width:100%}.fcrm-smart-link-popover .fcrm-smart-link-popover__item:hover{background-color:#f3f4f6}.fcrm-smart-link-popover .fcrm-smart-link-popover__item-title{color:#0e121b;font-size:13px;font-weight:500;line-height:1.4}.fcrm-smart-link-popover .fcrm-smart-link-popover__item-url{color:#99a0ae;font-size:11px;line-height:1.4;max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.fcrm-smartcode-popover .fcrm-smartcode-popover__content{padding:10px 12px}.fcrm-smartcode-popover .fcrm-smartcode-popover__header{margin-bottom:8px}.fcrm-smartcode-popover .fcrm-smartcode-popover__name{background:#ebf1ff;border-radius:3px;color:#335cff;font-family:monospace;font-size:11px;font-weight:500;padding:2px 6px}.fcrm-smartcode-popover .fcrm-smartcode-popover__label{color:#525866;font-size:12px;font-weight:500;margin-bottom:6px}.fcrm-smartcode-popover .fcrm-smartcode-popover__row{align-items:center;display:flex;gap:6px;min-width:260px}.fcrm-smartcode-popover .fcrm-smartcode-popover__row .components-base-control{flex:1;margin-bottom:0}.fcrm-smartcode-popover .fcrm-smartcode-popover__row .components-text-control__input{font-size:13px;height:32px;min-height:32px}.fcrm-smartcode-popover .fcrm-smartcode-popover__row .components-button{flex-shrink:0;height:32px}.block-editor-block-settings-menu__popover a.components-menu-item__button[href*=pattern],.block-editor-block-toolbar.is-synced>.block-editor-block-toolbar__slot{display:none}.fcrm-no-pattern-features #tabs-1-patterns,body.fcrm-no-pattern-features .reusable-blocks-menu-items__convert{display:none!important}.fcrm-info-panel{margin-top:12px}.fcrm-sidebar-info-content{padding:8px 16px 4px}.fcrm-sidebar-info-content h3{color:#1e1e1e;font-size:13px;font-weight:600;margin:0 0 10px}.fcrm-sidebar-info-content p{color:#757575;font-size:12.5px;line-height:1.6;margin:0 0 12px}.fcrm-sidebar-info-content p:last-child{margin-bottom:0}.is-layout-constrained>.aligncenter,.is-layout-flow>.aligncenter{margin-right:auto!important;margin-left:auto!important}.is-layout-constrained>.alignright,.is-layout-flow>.alignright{float:none;margin-inline-end:0;margin-inline-start:auto}.is-layout-flex{display:flex;flex-wrap:wrap}.is-layout-flex.is-vertical{flex-direction:column}.is-layout-flex.is-nowrap{flex-wrap:nowrap}.is-layout-flex.is-content-justification-left{justify-content:flex-start}.is-layout-flex.is-content-justification-center{justify-content:center}.is-layout-flex.is-content-justification-right{justify-content:flex-end}.is-layout-flex.is-content-justification-space-between{justify-content:space-between}.fluent-singleProduct-template-settings{width:100%}@media(max-width:900px){.fcrm-smartcode-toolbar-popover__sidebar{border-bottom:1px solid #e5e7eb;border-left:0}.fcrm-smartcode-toolbar-popover__tabs{flex-direction:row;flex-wrap:wrap;max-height:none}} +html[dir=rtl] .has-text-align-right{text-align:right}html[dir=rtl] .fcrm-smartcode-toolbar-popover__item,html[dir=rtl] .fcrm-smartcode-toolbar-popover__tab,html[dir=rtl] .has-text-align-left{text-align:left}html[dir=rtl] .fcrm-smartcode-toolbar-popover .fcrm-smartcode-toolbar-popover__sidebar{border-right:1px solid #e1e4ea;border-left:none} +.fluent-single-product-block{margin:20px 0}.fcw_p{align-items:center;border-radius:5px;display:flex;overflow:hidden;padding:15px}.fcw_p .fcw_image{flex:1;padding:0 0 0 10px}.fcw_p .fcw_image img{max-width:100%}.fcw_p .fcw_p_content{flex:1;padding:0 10px 0 0}.fcw_p .fcw_p_content h2{margin:5px 0 10px;padding:0}.fcw_p .fcw_p_content p{color:inherit!important}.fcw_p .fcw_p_content .wp-block-button{margin-top:15px;padding:0;width:100%}.fcw_p .fcw_p_content .fcb_p_button{max-width:100%}.fcw_p .fcw_p_content .fcb_p_button .wp-block-buttons{width:100%}.fcw_p .fcw_p_content .fcb_p_button .wp-block-button,.fcw_p .fcw_p_content .fcb_p_button .wp-block-button__link{box-sizing:border-box;width:100%}.fcw_p .fcw_p_content .fcb_p_button .wp-block-button__width-25{width:25%}.fcw_p .fcw_p_content .fcb_p_button .wp-block-button__width-50{width:50%}.fcw_p .fcw_p_content .fcb_p_button .wp-block-button__width-75{width:75%}.fcw_p .fcw_p_content .fcb_p_button .wp-block-button__width-100{width:100%}.fcw_p .fcw_p_content .fcw_btn{border-radius:4px;display:inline-block;font-weight:500;margin-top:14px;padding:10px 14px;text-decoration:none}.fcw_p .fcw_p_content .fcw_p_price{align-items:baseline;display:flex;flex-wrap:wrap;gap:8px;line-height:1.25;margin:8px 0 10px}.fcw_p .fcw_p_content .fcw_p_price .screen-reader-text{border:0;clip:rect(1px,1px,1px,1px);clip-path:inset(50%);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;white-space:nowrap;width:1px}.fcw_p .fcw_p_content .fcw_p_price del{color:#9aa3aa;text-decoration-thickness:1px}.fcw_p .fcw_p_content .fcw_p_price ins{font-weight:600;text-decoration:none}.fcw_p.fcw_template_none{text-align:center}.fcw_p.fcw_template_none .fcw_p_content{padding:0}.fcw_p.fcw_template_none .fcw_p_content *{text-align:center}.fcw_p.fcw_template_top{flex-direction:column;text-align:center}.fcw_p.fcw_template_top .fcw_p_content,.fcw_p.fcw_template_top .fcw_p_content .fcw_p_desc,.fcw_p.fcw_template_top .fcw_p_content .fcw_p_desc *,.fcw_p.fcw_template_top .fcw_p_content .fcw_p_title,.fcw_p.fcw_template_top .fcw_p_content .fcw_p_title *{text-align:center}.fc_product_loading{display:none}.fc_woo_loader{align-items:center;background:#fff7eb;border-radius:10px;display:flex;flex-direction:column;justify-content:center;padding:50px 40px;text-align:center;width:100%}.fcw_search_box{background:#eaeaea;border-radius:10px;padding:20px}.fluent-single-product-search-bar{align-items:center;display:grid;gap:10px;grid-template-columns:minmax(0,1fr) auto}.fluent-single-product-search-bar>div{min-width:0}.fluent-single-product-search-bar input{box-sizing:border-box;max-width:100%;width:100%}.fluent-single-product-search-bar button{min-width:120px;white-space:nowrap}.fcw_results .components-radio-control__option{margin-bottom:6px}.fcw_results .components-radio-control__option label{line-height:1.4;overflow-wrap:anywhere} +.fluent-latest-posts-settings{width:100%}.fluent-latest-posts-settings .components-base-control__help{margin-top:0}.fluent-latest-posts-settings .components-base-control .components-base-control__field{margin-bottom:0}.fc_latest_posts_items .fc_latest_post_item{border:1px solid #edeef4;text-align:center}.fc_latest_posts_items .fc_latest_post_item .fc_latest_post_thumbnail{background-position:50%;background-repeat:no-repeat}.fc_latest_posts_items .fc_latest_post_item .fc_latest_post_thumbnail img{display:block;max-height:400px;object-fit:cover;width:100%}.fc_latest_posts_items .fc_latest_post_item .fc_latest_post_content{padding:35px 40px 30px}.fc_latest_posts_items .fc_latest_post_item .fc_latest_post_content>:last-child{margin-bottom:0!important}.fc_latest_posts_items .fc_latest_post_item .fc_latest_post_content .meta{align-items:center;display:flex;gap:15px;justify-content:flex-start;line-height:1.3;margin:0 0 8px}.fc_latest_posts_items .fc_latest_post_item .fc_latest_post_content .meta .author{align-items:center;display:inline-flex;gap:7px}.fc_latest_posts_items .fc_latest_post_item .fc_latest_post_content .meta .author a{color:#000;font-weight:500}.fc_latest_posts_items .fc_latest_post_item .fc_latest_post_content .meta .author a:hover{color:#7757e6}.fc_latest_posts_items .fc_latest_post_item .fc_latest_post_content .meta .author img{border-radius:30px;display:block;height:30px;width:30px}.fc_latest_posts_items .fc_latest_post_item .fc_latest_post_content .meta .comments{color:#acacac;display:block}.fc_latest_posts_items .fc_latest_post_item .fc_latest_post_content .title{font-size:25px;line-height:1.4;margin-bottom:12px;padding:0;text-align:center}.fc_latest_posts_items .fc_latest_post_item .fc_latest_post_content .title a{color:#393d57;display:inline-block;transition:.2s}.fc_latest_posts_items .fc_latest_post_item .fc_latest_post_content .title a:hover{color:#7757e6}.fc_latest_posts_items .fc_latest_post_item .fc_latest_post_content .description{margin:0 0 15px;text-align:center}.fc_latest_posts_items .fc_latest_post_item .fc_latest_post_content .fc_latest_post_btn{color:#000;display:inline-block;text-decoration:none;transition:.3s}.fc_latest_posts_items .fc_latest_post_item .fc_latest_post_content .fc_latest_post_btn:focus{box-shadow:none;outline:none}.fc_latest_posts_items .fc_latest_post_item .fc_latest_post_content .fc_latest_post_btn:hover{color:#7757e6}.fc_latest_posts_items.template-default .fc_latest_post_item .fc_latest_post_thumbnail,.fc_latest_posts_items.template-layout-7 .fc_latest_post_item .fc_latest_post_thumbnail{height:350px}.fc_latest_posts_items.template-layout-2 .fc_latest_post_item,.fc_latest_posts_items.template-layout-3 .fc_latest_post_item,.fc_latest_posts_items.template-layout-4 .fc_latest_post_item{display:flex;text-align:start}.fc_latest_posts_items.template-layout-2 .fc_latest_post_item .fc_latest_post_thumbnail,.fc_latest_posts_items.template-layout-3 .fc_latest_post_item .fc_latest_post_thumbnail,.fc_latest_posts_items.template-layout-4 .fc_latest_post_item .fc_latest_post_thumbnail{width:220px}.fc_latest_posts_items.template-layout-2 .fc_latest_post_item .fc_latest_post_thumbnail img,.fc_latest_posts_items.template-layout-3 .fc_latest_post_item .fc_latest_post_thumbnail img,.fc_latest_posts_items.template-layout-4 .fc_latest_post_item .fc_latest_post_thumbnail img{height:100%}.fc_latest_posts_items.template-layout-2 .fc_latest_post_item .fc_latest_post_content,.fc_latest_posts_items.template-layout-3 .fc_latest_post_item .fc_latest_post_content,.fc_latest_posts_items.template-layout-4 .fc_latest_post_item .fc_latest_post_content{flex:1;text-align:start}.fc_latest_posts_items.template-layout-2 .fc_latest_post_item .fc_latest_post_content h1,.fc_latest_posts_items.template-layout-3 .fc_latest_post_item .fc_latest_post_content h1,.fc_latest_posts_items.template-layout-4 .fc_latest_post_item .fc_latest_post_content h1{font-size:22px;text-align:start}.fc_latest_posts_items.template-layout-2 .fc_latest_post_item .fc_latest_post_content .description,.fc_latest_posts_items.template-layout-3 .fc_latest_post_item .fc_latest_post_content .description,.fc_latest_posts_items.template-layout-4 .fc_latest_post_item .fc_latest_post_content .description{font-size:15px!important;text-align:start}.fc_latest_posts_items.template-layout-3 .fc_latest_post_item,.fc_latest_posts_items.template-layout-4 .fc_latest_post_item{align-items:center;gap:15px;padding:35px 40px 30px;text-align:start}.fc_latest_posts_items.template-layout-3 .fc_latest_post_item .fc_latest_post_thumbnail,.fc_latest_posts_items.template-layout-4 .fc_latest_post_item .fc_latest_post_thumbnail{height:220px;order:2}.fc_latest_posts_items.template-layout-3 .fc_latest_post_item .fc_latest_post_thumbnail img,.fc_latest_posts_items.template-layout-4 .fc_latest_post_item .fc_latest_post_thumbnail img{border-radius:4px}.fc_latest_posts_items.template-layout-3 .fc_latest_post_item .fc_latest_post_content,.fc_latest_posts_items.template-layout-4 .fc_latest_post_item .fc_latest_post_content{padding:0}.fc_latest_posts_items.template-layout-3 .fc_latest_post_item .fc_latest_post_content .description,.fc_latest_posts_items.template-layout-3 .fc_latest_post_item .fc_latest_post_content h1,.fc_latest_posts_items.template-layout-4 .fc_latest_post_item .fc_latest_post_content .description,.fc_latest_posts_items.template-layout-4 .fc_latest_post_item .fc_latest_post_content h1{text-align:start}.fc_latest_posts_items.template-layout-4 .fc_latest_post_item{align-items:normal;border:none;border-bottom:1px solid #edeef4;margin:0;padding:20px 0}.fc_latest_posts_items.template-layout-4 .fc_latest_post_item .fc_latest_post_content{display:flex;flex-direction:column}.fc_latest_posts_items.template-layout-4 .fc_latest_post_item .fc_latest_post_content .meta{margin:20px 0 0;order:3}.fc_latest_posts_items.template-layout-4 .fc_latest_post_item .fc_latest_post_content .description{margin-bottom:0}.fc_latest_posts_items.template-layout-4 .fc_latest_post_item .fc_latest_post_thumbnail{height:auto}.fc_latest_posts_items.template-layout-5 .fc_latest_post_item{border:none}.fc_latest_posts_items.template-layout-5 .fc_latest_post_item+.fc_latest_post_item{margin-top:5px}.fc_latest_posts_items.template-layout-5 .fc_latest_post_item .fc_latest_post_content{padding:0}.fc_latest_posts_items.template-layout-5 .fc_latest_post_item .fc_latest_post_content h1{align-items:flex-start;display:flex;font-size:16px;font-weight:600;gap:5px;margin:0;position:relative;text-align:start}.fc_latest_posts_items.template-layout-5 .fc_latest_post_item .fc_latest_post_content h1:before{background:#000;border-radius:20px;content:"";height:5px;margin-top:10px;width:5px}.fc_latest_posts_items.template-layout-5 .fc_latest_post_item .fc_latest_post_content h1 a{color:#7757e6;text-decoration:underline}.fc_latest_posts_items.template-layout-5 .fc_latest_post_item .fc_latest_post_content h1 a:hover{color:#000}.fc_latest_posts_items.template-layout-6 .fc_latest_post_item{align-items:center;border:none;border-bottom:1px solid #edeef4;display:flex;gap:15px;padding:25px 0;text-align:start}.fc_latest_posts_items.template-layout-6 .fc_latest_post_item:first-child{border-top:1px solid #edeef4}.fc_latest_posts_items.template-layout-6 .fc_latest_post_item+.fc_latest_post_item{margin:0}.fc_latest_posts_items.template-layout-6 .fc_latest_post_item .fc_latest_post_thumbnail{border-radius:5px;height:100px;width:100px}.fc_latest_posts_items.template-layout-6 .fc_latest_post_item .fc_latest_post_thumbnail img{border-radius:5px;height:auto;max-height:100px;width:100px}.fc_latest_posts_items.template-layout-6 .fc_latest_post_item .fc_latest_post_content{padding:0}.fc_latest_posts_items.template-layout-6 .fc_latest_post_item .fc_latest_post_content .title{font-size:20px;font-weight:600;text-align:start}.fc_latest_posts_items.template-layout-7 .fc_latest_post_item{border:none}.fc_latest_posts_items.template-layout-7 .fc_latest_post_item .fc_latest_post_thumbnail{margin-bottom:25px}.fc_latest_posts_items.template-layout-7 .fc_latest_post_item .fc_latest_post_content{padding:0;text-align:start}.fc_latest_posts_items.template-layout-7 .fc_latest_post_item .fc_latest_post_content .description{margin-bottom:17px;text-align:start}.fc_latest_posts_items.template-layout-7 .fc_latest_post_item .fc_latest_post_content .title{margin-bottom:10px;text-align:start}.fc_latest_posts_items.template-layout-7 .fc_latest_post_item .fc_latest_post_content .fc_latest_post_btn{border:2px solid #000;border-radius:4px;font-weight:500;padding:4px 14px}.fc-recent-posts-number .components-base-control__field{margin-bottom:5px!important}.fc-recent-posts-number .components-base-control__help{margin-top:0}.show-setting-control-box{position:relative}.show-setting-control-box .components-base-control:last-child{margin-bottom:24px}.show-setting-control-box .show-setting-dropdown{position:absolute;left:0;top:-4px}.show-setting-control-box .show-setting-dropdown button{background:none;color:#000;display:block;height:auto;margin:0;padding:0}.show-setting-control-box .show-setting-dropdown button:hover,.show-setting-control-box .show-setting-dropdown button:hover:not(:disabled){background:none;color:#000}.show-setting-control-box .show-setting-dropdown button svg{display:block}.show-setting-control-box.select-layout{margin-bottom:24px}.show-setting-control-box.select-layout>p{display:block;font-size:11px;font-weight:500;line-height:1.4;margin-bottom:8px;padding:0;text-transform:uppercase}.show-setting-control-box.select-layout .show-setting-dropdown{align-items:center;display:flex;position:relative;top:0}.show-setting-control-box.select-layout .show-setting-dropdown>img{border:1px solid #eff1ff;border-radius:2px;cursor:pointer;display:block;margin-top:4px;object-fit:cover;padding:5px;width:80px}.show-setting-control-box.select-layout .show-setting-dropdown p{display:inline-block;font-size:11px;font-weight:500;line-height:1.4;margin-bottom:8px;margin-top:0;padding:0;text-transform:uppercase}.show-setting-control-box.select-layout .show-setting-dropdown .components-button{position:absolute;left:0;top:-27px}.dropdown-render-content{min-width:180px;padding:4px}.dropdown-render-content .components-base-control:last-child{margin-bottom:0}.dropdown-render-content p{display:inline-block;font-size:11px;font-weight:500;line-height:1.4;margin-bottom:8px;margin-top:0;padding:0;text-transform:uppercase}.fc-layout-picker{max-width:320px;min-width:260px}.fc-layout-picker .fc-layout-picker-grid{display:grid;gap:8px;grid-template-columns:repeat(3,minmax(0,1fr));max-height:280px;overflow-y:auto;padding-left:2px}.fc-layout-picker .fc-layout-picker-option{background:#fff;border:1px solid #dfe3ea;border-radius:6px;cursor:pointer;margin:0;padding:6px;text-align:center;transition:border-color .2s ease,box-shadow .2s ease;width:100%}.fc-layout-picker .fc-layout-picker-option:hover{border-color:#3858e9}.fc-layout-picker .fc-layout-picker-option.is-active{border-color:#3858e9;box-shadow:inset 0 0 0 1px #3858e9}.fc-layout-picker .fc-layout-picker-option img{border-radius:4px;display:block;height:auto;margin-bottom:6px;width:100%}.fc-layout-picker .fc-layout-picker-option span{color:#1e1e1e;display:block;font-size:11px;line-height:1.2} +.fc_woo_products{column-gap:20px;display:grid;grid-template-columns:1fr 1fr}.fc_woo_products .fc_woo_product{margin-bottom:35px}.fc_woo_products .fc_woo_product.no-image .fc_woo_product_info{width:100%}.fc_woo_products .fc_woo_product .fc_woo_product_img{height:280px;margin-bottom:20px;position:relative;width:100%}.fc_woo_products .fc_woo_product .fc_woo_product_img img{background:#eee;display:block;height:100%;object-fit:cover;width:100%}.fc_woo_products .fc_woo_product .fc_woo_product_info .title{color:#2a363d;font-size:20px;font-weight:500;line-height:1.2;margin-bottom:8px}.fc_woo_products .fc_woo_product .fc_woo_product_info .price{align-items:baseline;color:#37454e;display:flex;flex-wrap:wrap;font-size:16px;gap:8px;line-height:1.2;margin-bottom:10px}.fc_woo_products .fc_woo_product .fc_woo_product_info .price .screen-reader-text{border:0;clip:rect(1px,1px,1px,1px);clip-path:inset(50%);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;white-space:nowrap;width:1px}.fc_woo_products .fc_woo_product .fc_woo_product_info .price del{color:#9aa3aa;text-decoration-thickness:1px}.fc_woo_products .fc_woo_product .fc_woo_product_info .price ins{font-weight:600;text-decoration:none}.fc_woo_products .fc_woo_product .fc_woo_product_info .description,.fc_woo_products .fc_woo_product .fc_woo_product_info .description p{line-height:1.5}.fc_woo_products .fc_woo_product .fc_woo_product_info .add-to-cart-btn{color:#202020;display:inline-block;font-size:14px;font-weight:600}.fc_woo_products.template-layout-2{grid-template-columns:1fr}.fc_woo_products.template-layout-2 .fc_woo_product{align-items:center;display:flex}.fc_woo_products.template-layout-2 .fc_woo_product.no-image{display:block}.fc_woo_products.template-layout-2 .fc_woo_product.no-image .fc_woo_product_info{padding:0;width:100%}.fc_woo_products.template-layout-2 .fc_woo_product .fc_woo_product_img{flex:1;height:100%;margin:0;padding-left:10px;width:45%}.fc_woo_products.template-layout-2 .fc_woo_product .fc_woo_product_info{flex:1;padding:10px;width:55%}.fc_woo_products.template-layout-2 .fc_woo_product .fc_woo_product_info .price{margin-bottom:25px}.fc_woo_products.template-layout-2 .fc_woo_product .fc_woo_product_info .add-to-cart-btn{background:#2a363d;color:#fff;padding:15px;text-align:center;width:100%}.fc_woo_products.template-layout-3 .fc_woo_product{text-align:center}.fc_woo_products.template-layout-3 .fc_woo_product .fc_woo_product_img,.fc_woo_products.template-layout-3 .fc_woo_product .fc_woo_product_img img{border-radius:6px}.fc_woo_products.template-layout-3 .fc_woo_product .fc_woo_product_info .title{text-align:center}.show-setting-control-box{position:relative}.show-setting-control-box .components-base-control:last-child{margin-bottom:24px}.show-setting-control-box .show-setting-dropdown{position:absolute;left:0;top:-4px}.show-setting-control-box .show-setting-dropdown button{background:none;color:#000;display:block;height:auto;margin:0;padding:0}.show-setting-control-box .show-setting-dropdown button:hover,.show-setting-control-box .show-setting-dropdown button:hover:not(:disabled){background:none;color:#000}.show-setting-control-box .show-setting-dropdown button svg{display:block}.show-setting-control-box.select-layout{margin-bottom:24px}.show-setting-control-box.select-layout>p{display:block;font-size:11px;font-weight:500;line-height:1.4;margin-bottom:8px;padding:0;text-transform:uppercase}.show-setting-control-box.select-layout .show-setting-dropdown{align-items:center;display:flex;position:relative;top:0}.show-setting-control-box.select-layout .show-setting-dropdown>img{border:1px solid #eff1ff;border-radius:2px;cursor:pointer;display:block;margin-top:4px;object-fit:cover;padding:5px;width:80px}.show-setting-control-box.select-layout .show-setting-dropdown p{display:inline-block;font-size:11px;font-weight:500;line-height:1.4;margin-bottom:8px;margin-top:0;padding:0;text-transform:uppercase}.show-setting-control-box.select-layout .show-setting-dropdown .components-button{position:absolute;left:0;top:-27px}.fc-latest-products-content-color-settings,.fc-latest-products-settings{box-sizing:border-box;min-width:0;overflow-x:hidden;width:100%}.fc-latest-products-content-color-settings .components-base-control,.fc-latest-products-content-color-settings .components-base-control__field,.fc-latest-products-content-color-settings .components-input-control,.fc-latest-products-content-color-settings .components-input-control__container,.fc-latest-products-content-color-settings .components-select-control__input,.fc-latest-products-content-color-settings .components-text-control__input,.fc-latest-products-content-color-settings input,.fc-latest-products-content-color-settings select,.fc-latest-products-settings .components-base-control,.fc-latest-products-settings .components-base-control__field,.fc-latest-products-settings .components-input-control,.fc-latest-products-settings .components-input-control__container,.fc-latest-products-settings .components-select-control__input,.fc-latest-products-settings .components-text-control__input,.fc-latest-products-settings input,.fc-latest-products-settings select{box-sizing:border-box;max-width:100%;min-width:0;width:100%}.fc-layout-picker{max-width:280px;min-width:250px}.fc-layout-picker .fc-layout-picker-grid{display:grid;gap:8px;grid-template-columns:repeat(3,minmax(0,1fr))}.fc-layout-picker .fc-layout-picker-option{background:#fff;border:1px solid #dfe3ea;border-radius:6px;cursor:pointer;margin:0;padding:6px;text-align:center;transition:border-color .2s ease,box-shadow .2s ease;width:100%}.fc-layout-picker .fc-layout-picker-option:hover{border-color:#3858e9}.fc-layout-picker .fc-layout-picker-option.is-active{border-color:#3858e9;box-shadow:inset 0 0 0 1px #3858e9}.fc-layout-picker .fc-layout-picker-option img{border-radius:4px;display:block;height:auto;margin-bottom:6px;width:100%}.fc-layout-picker .fc-layout-picker-option span{color:#1e1e1e;display:block;font-size:11px;line-height:1.2} +.fc-cond-section{background:#ffffd7;border:1px dashed #d3d6db;padding:10px 0;position:relative}.fc-cond-section:before{background:#ffffd7;border-radius:2px;color:#757575;content:"Conditional";font-size:10px;font-weight:600;right:12px;letter-spacing:.5px;line-height:20px;padding:0 6px;position:absolute;text-transform:uppercase;top:-10px}.components-panel__body .components-form-token-field{margin-bottom:8px} +.fcrm-has-condition:not(.is-selected){outline:1px dashed #c8a415;outline-offset:2px;position:relative}.fcrm-has-condition:not(.is-selected):after{background:#fff8d6;border-radius:2px;color:#8a7000;content:"Conditional";font-size:10px;font-weight:600;letter-spacing:.5px;line-height:20px;padding:0 6px;pointer-events:none;position:absolute;left:12px;text-transform:uppercase;top:-10px;z-index:1} diff --git a/wp-content/plugins/fluent-crm/assets/guten-editor/index.asset.php b/wp-content/plugins/fluent-crm/assets/guten-editor/index.asset.php new file mode 100644 index 0000000..06107d5 --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/guten-editor/index.asset.php @@ -0,0 +1 @@ + array('react', 'react-jsx-runtime', 'wp-api-fetch', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-compose', 'wp-data', 'wp-editor', 'wp-element', 'wp-hooks', 'wp-i18n', 'wp-plugins', 'wp-primitives', 'wp-rich-text'), 'version' => '7482ff8e00b21264d357'); diff --git a/wp-content/plugins/fluent-crm/assets/guten-editor/index.css b/wp-content/plugins/fluent-crm/assets/guten-editor/index.css new file mode 100644 index 0000000..779e11f --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/guten-editor/index.css @@ -0,0 +1,7 @@ +:root{--fc-primary-bg:#fff;--fc-secondary-bg:#f5f7fa;--fc-light-bg:#e1e4ea;--fc-deep-bg:#222530;--fc-primary-text:#0e121b;--fc-secondary-text:#525866;--fc-text-muted:#99a0ae;--fc-text-inverse:#fff;--fc-primary-border:#e1e4ea;--fc-secondary-border:#cacfd8;--fc-primary-button:#222530;--fc-text-link:#335cff;--fc-success:#1fc16b;--fc-success-bg:#e0faec;--fc-error:#fb3748;--fc-error-bg:#ffebec;--fc-warning:#f6b51e;--fc-warning-bg:#fffaeb;--el-color-primary:var(--fc-deep-bg);--el-color-primary-light-3:var(--fc-secondary-text);--el-color-primary-light-5:var(--fc-text-muted);--el-color-primary-light-7:var(--fc-secondary-border);--el-color-primary-light-8:var(--fc-primary-border);--el-color-primary-light-9:var(--fc-secondary-bg);--el-color-primary-dark-2:var(--fc-primary-text);--el-color-success:var(--fc-success);--el-color-warning:var(--fc-warning);--el-color-danger:var(--fc-error);--el-color-error:var(--fc-error);--el-color-info:var(--fc-text-muted);--el-text-color-primary:var(--fc-primary-text);--el-text-color-regular:var(--fc-secondary-text);--el-text-color-secondary:var(--fc-text-muted);--el-text-color-placeholder:var(--fc-text-muted);--el-text-color-disabled:var(--fc-secondary-border);--el-border-color:var(--fc-primary-border);--el-border-color-light:var(--fc-primary-border);--el-border-color-lighter:var(--fc-secondary-border);--el-border-color-dark:var(--fc-secondary-border);--el-fill-color-light:var(--fc-secondary-bg);--el-fill-color-lighter:var(--fc-secondary-bg);--el-bg-color:var(--fc-primary-bg);--el-bg-color-page:var(--fc-secondary-bg);--el-button-text-color:var(--fc-text-inverse);--el-fill-color-blank:var(--fc-primary-bg);--el-bg-color-overlay:var(--fc-primary-bg);--fcrm-border-radius-8:8px;--el-border-radius-base:var(--fcrm-border-radius-8);--theme-block-max-width:700px;--global-calc-content-width:700px;--theme-block-wide-max-width:820px;--theme-font-weight:400;--theme-text-transform:none;--theme-text-decoration:none;--theme-font-size:16px;--theme-line-height:1.60;--theme-letter-spacing:0em;--theme-button-font-weight:500;--theme-button-font-size:16px;--theme-palette-color-1:#4f46e5;--theme-palette-color-2:#7c3aed;--theme-palette-color-3:#1f2937;--theme-palette-color-4:#374151;--theme-palette-color-5:#6b7280;--theme-palette-color-6:#9ca3af;--theme-palette-color-7:#e5e7eb;--theme-palette-color-8:#fff;--theme-text-color:var(--fcom-primary-text,#19283a);--theme-link-initial-color:var(--theme-palette-color-1);--theme-link-hover-color:var(--theme-palette-color-2);--theme-selection-text-color:#fff;--theme-selection-background-color:var(--theme-palette-color-1);--theme-border-color:var(--theme-palette-color-5);--theme-headings-color:var(--theme-palette-color-4);--theme-content-spacing:20px;--theme-button-min-height:40px;--theme-button-shadow:none;--theme-button-transform:none;--theme-button-text-initial-color:#fff;--theme-button-text-hover-color:#fff;--theme-button-background-initial-color:var(--theme-palette-color-1);--theme-button-background-hover-color:var(--theme-palette-color-2);--theme-button-border:none;--theme-button-padding:5px 20px;--theme-normal-container-max-width:1290px;--theme-content-vertical-spacing:60px;--theme-container-edge-spacing:90vw;--theme-narrow-container-max-width:750px;--theme-wide-offset:130px;--fcom-font-size-small:16px;--fcom-font-size-medium:18px;--fcom-font-size-large:22px;--fcom-font-size-larger:26px;--fcom-font-size-xxlarge:32px;--wp--preset--spacing--20:7px;--wp--preset--spacing--30:11px;--wp--preset--spacing--40:16px;--wp--preset--spacing--50:24px;--wp--preset--spacing--60:36px;--wp--preset--spacing--70:54px;--wp--preset--spacing--80:81px}.editor-styles-wrapper,:root{--wp-admin-theme-color:#3e58e1!important;--wp-admin-theme-color-darker-10:#3e58f2!important;--wp-admin-theme-color-darker-10--rgb:62,88,242;--wp-admin-theme-color-darker-20:#213fd4!important;--wp-admin-theme-color-darker-20--rgb:33,63,212;--has-boxed:0px}:host,:root{--rem:16}body[data-design-template=classic]{margin-left:20px!important}:root :where(.wp-element-button,.wp-block-button__link){background-color:#32373c;border-radius:3px;color:#fff;padding:8px 20px}body .wp-block-button.is-style-outline .wp-element-button:not(.has-text-color),body .wp-block-button.is-style-outline .wp-element-button:not(.has-text-color):hover{color:#32373c}body .wp-block-button.is-style-outline .wp-element-button:not(.has-border-color),body .wp-block-button.is-style-outline .wp-element-button:not(.has-border-color):hover{border-color:#32373c}body .wp-block-button.is-style-outline .wp-element-button:not(.has-background),body .wp-block-button.is-style-outline .wp-element-button:not(.has-background):hover{background:transparent}.wp-block-buttons:not(.is-content-justification-center,.is-content-justification-right){align-items:flex-start;justify-content:flex-start}.wp-block-button__link.has-fc-small-font-size{font-size:13px}.wp-block-button__link.has-fc-regular-font-size{font-size:16px}.wp-block-button__link.has-fc-medium-font-size{font-size:18px}.wp-block-button__link.has-fc-large-font-size{font-size:26px}.wp-block-button__link.has-fc-x-large-font-size{font-size:32px}body .wp-block-pullquote{border-color:#e5e7eb;border-width:4px;padding:20px}body .wp-block-pullquote blockquote cite{font-size:90%}.gutenberg__editor .fcrm_danger_btn,.gutenberg__editor .fcrm_delete_btn,.gutenberg__editor .fcrm_secondary_btn{background:#fff;border:1px solid var(--fc-primary-border);border-radius:8px;box-shadow:0 1px 2px 0 rgba(10,13,20,.031);color:var(--fc-secondary-text);font-size:14px;font-weight:500;height:auto;line-height:20px;padding:7px 10px}.gutenberg__editor .fcrm_danger_btn>span,.gutenberg__editor .fcrm_delete_btn>span,.gutenberg__editor .fcrm_secondary_btn>span{gap:4px}.gutenberg__editor .fcrm_danger_btn .el-icon,.gutenberg__editor .fcrm_danger_btn .fcrm-preview-icon,.gutenberg__editor .fcrm_danger_btn .icon,.gutenberg__editor .fcrm_delete_btn .el-icon,.gutenberg__editor .fcrm_delete_btn .fcrm-preview-icon,.gutenberg__editor .fcrm_delete_btn .icon,.gutenberg__editor .fcrm_secondary_btn .el-icon,.gutenberg__editor .fcrm_secondary_btn .fcrm-preview-icon,.gutenberg__editor .fcrm_secondary_btn .icon{color:var(--fc-primary-text)}.gutenberg__editor .fcrm_danger_btn .el-icon svg,.gutenberg__editor .fcrm_danger_btn .fcrm-preview-icon svg,.gutenberg__editor .fcrm_danger_btn .icon svg,.gutenberg__editor .fcrm_delete_btn .el-icon svg,.gutenberg__editor .fcrm_delete_btn .fcrm-preview-icon svg,.gutenberg__editor .fcrm_delete_btn .icon svg,.gutenberg__editor .fcrm_secondary_btn .el-icon svg,.gutenberg__editor .fcrm_secondary_btn .fcrm-preview-icon svg,.gutenberg__editor .fcrm_secondary_btn .icon svg{display:block}.gutenberg__editor .fcrm_danger_btn.small,.gutenberg__editor .fcrm_delete_btn.small,.gutenberg__editor .fcrm_secondary_btn.small{padding:5px 10px}.gutenberg__editor .fcrm_danger_btn:hover,.gutenberg__editor .fcrm_delete_btn:hover,.gutenberg__editor .fcrm_secondary_btn:hover{background:var(--fc-secondary-bg);border-color:var(--fc-secondary-border);color:var(--fc-deep-bg)}.gutenberg__editor .fcrm_danger_btn.is-disabled,.gutenberg__editor .fcrm_delete_btn.is-disabled,.gutenberg__editor .fcrm_secondary_btn.is-disabled{opacity:.6}.gutenberg__editor .fcrm_primary_btn{background:var(--fc-deep-bg);border-color:var(--fc-deep-bg);border-radius:8px;color:#fff;font-size:14px;height:auto;line-height:20px;padding:7px 10px}.gutenberg__editor .fcrm_primary_btn:hover{color:#fff}.gutenberg__editor .fcrm_primary_btn .cmd{background:var(--alpha-white-alpha-10,hsla(0,0%,100%,.102));border-radius:4px;color:var(--fc-text-muted);display:block;font-size:12px;font-weight:500;line-height:16px;padding:2px 6px;text-transform:uppercase}.gutenberg__editor .fcrm_primary_btn>span{gap:4px}.gutenberg__editor .fcrm_primary_btn .el-icon,.gutenberg__editor .fcrm_primary_btn .icon{color:#fff}.gutenberg__editor .fcrm_primary_btn .el-icon svg,.gutenberg__editor .fcrm_primary_btn .icon svg{display:block}.gutenberg__editor .fcrm_primary_btn:hover{background:var(--fc-deep-bg);border-color:var(--fc-deep-bg)}.gutenberg__editor .fcrm_primary_btn.small{padding:5px 10px}.gutenberg__editor .fcrm_primary_btn.is-disabled:hover{background:var(--fc-deep-bg);border-color:var(--fc-deep-bg)}.gutenberg__editor .fcrm_primary_btn.is-disabled{cursor:not-allowed;opacity:.5}.gutenberg__editor .fcrm_danger_btn,.gutenberg__editor .fcrm_delete_btn{border-color:var(--fc-error);box-shadow:none;color:var(--fc-error)}.gutenberg__editor .fcrm_danger_btn .el-icon,.gutenberg__editor .fcrm_delete_btn .el-icon{color:var(--fc-error)}.gutenberg__editor .fcrm_danger_btn:hover,.gutenberg__editor .fcrm_delete_btn:hover{border-color:var(--fc-error)}.gutenberg__editor .fcrm_setup_btn{background:rgba(34,37,48,.1);border:none;border-radius:6px;color:var(--fc-secondary-text);cursor:pointer;font-size:12px;font-weight:500;height:auto;padding:7px 10px;transition:background-color .2s ease}.fcrm_btn_small{padding:3px 8px}.fcrm_pro_btn,.fcrm_pro_btn:hover{background:var(--fc-deep-bg);border-color:var(--fc-deep-bg)}.editor-preview-dropdown__toggle.components-dropdown-menu__toggle{align-items:center;display:flex;gap:8px}.components-button.fcrm-compose-preview,.components-button.fcrm-compose-smartcodes{padding:7px}.components-button.fcrm-footer-smartcode-btn{align-items:center;background:var(--fc-secondary-bg);border:1px solid;border-color:var(--fc-secondary-border);border-radius:4px;color:var(--fc-deep-bg);display:inline-flex;gap:4px;height:auto;margin-inline-start:10px;padding:4px 8px}.editor-styles-wrapper{padding-bottom:0!important}body .is-root-container>.alignfull{margin-inline-end:calc(var(--fcrm-padding-right, -20px)*-1);margin-inline-start:calc(var(--fcrm-padding-left, 20px)*-1)}h1,h2,h3{word-break:normal}.interface-interface-skeleton{top:0}@media(max-width:782px){.admin-bar .interface-interface-skeleton{top:46px}}html.interface-interface-skeleton__html-container{margin-top:0!important;overscroll-behavior:auto!important}.interface-interface-skeleton__body{overscroll-behavior-y:auto!important}.interface-interface-skeleton__content{overscroll-behavior:auto!important}p>a{text-decoration:underline!important;text-underline-offset:.15em!important}a[href="edit.php?post_type=wp_block"]{display:none}.wporg-gutenberg-block-layout{display:block!important}html{font-size:var(--theme-font-size,16px)}.wporg-gutenberg-hide-on-mobile{display:none!important}@media(min-width:782px){.wporg-gutenberg-hide-on-mobile{display:inherit!important}}.components-notice-list{display:none!important}.editor-header__back-button{display:none}@media(min-width:782px){.editor-header:has(>.editor-header__center){grid-template:auto/0 minmax(min-content,1fr) 2fr minmax(min-content,1fr) 60px}}.wp-embed-aspect-16-9{aspect-ratio:16/9}.components-modal__screen-overlay.commands-command-menu__overlay,body:not(.fcrm-compose-ui) .editor-header.edit-post-header .editor-post-publish-button,body:not(.fcrm-compose-ui) .editor-header.edit-post-header .editor-post-save-draft{display:none!important}.editor-post-last-edited-panel,.editor-post-summary .components-flex [data-wp-component=VStack] button:not(.fcrm-email-body-settings-btn),.editor-post-summary .editor-post-panel__row,span.editor-document-bar__shortcut{display:none}.editor-header .edit-post-fullscreen-mode-close,body.fcrm-compose-ui .editor-post-summary,body:not(.fcrm-compose-ui) .editor-header .editor-post-publish-button__button{display:none!important}.interface-interface-skeleton{left:0!important;right:0!important}body:not(.fcrm-compose-ui) .editor-header button[aria-label*="Submit for Review"],body:not(.fcrm-compose-ui) .editor-header button[aria-label*="submit for review"],body:not(.fcrm-compose-ui) .editor-header__actions .editor-post-publish-button__button,body:not(.fcrm-compose-ui) .editor-header__toolbar .components-button:first-of-type,body:not(.fcrm-compose-ui) .editor-header__toolbar .components-button[aria-label*=WordPress],body:not(.fcrm-compose-ui) .editor-header__toolbar .components-button[aria-label*=wordpress],body:not(.fcrm-compose-ui) .editor-header__toolbar .wp-logo,body:not(.fcrm-compose-ui) .editor-header__toolbar button[aria-label*=WordPress],body:not(.fcrm-compose-ui) .editor-header__toolbar button[aria-label*=wordpress],body:not(.fcrm-compose-ui) .editor-header__toolbar>.components-button:first-child,body:not(.fcrm-compose-ui) .editor-header__toolbar>button:first-child,body:not(.fcrm-compose-ui) button.editor-post-publish-button__button{display:none!important}.edit-post-header,.editor-header{--fcrm-compose-btn-bg:#f3f4f6;--fcrm-compose-btn-border:#9ca3af;--fcrm-compose-btn-text:#1f2937;--fcrm-compose-btn-hover:#e5e7eb}.edit-post-header-toolbar__left .fcrm-compose-smartcodes-left,.edit-post-header__settings .fcrm-compose-fullscreen,.edit-post-header__settings .fcrm-compose-smartcodes,.edit-post-header__settings .fcrm-email-preview-btn,.editor-header__toolbar .fcrm-compose-smartcodes-left{height:32px;padding:6px;width:32px}body.fcrm-compose-ui .edit-post-header__settings button[aria-label*=Preview]:not(.fcrm-compose-preview):not([aria-label*=Email]),body.fcrm-compose-ui .editor-header .fcrm-compose-actions~* button[aria-label*=Preview]:not(.fcrm-compose-preview),body.fcrm-compose-ui .editor-header__actions button[aria-label*=Preview]:not(.fcrm-compose-preview):not([aria-label*=Email]),body.fcrm-compose-ui .editor-header__settings button[aria-label*=Preview]:not(.fcrm-compose-preview):not([aria-label*=Email]){display:none!important}.edit-post-header .fcrm-compose-actions,.editor-header .fcrm-compose-actions{align-items:center!important;display:flex!important;gap:10px!important;margin-left:auto!important}body.fcrm-compose-ui .edit-post-header-toolbar__left .block-editor-inserter__toggle,body.fcrm-compose-ui .edit-post-header-toolbar__left [aria-label*="Add block"],body.fcrm-compose-ui .edit-post-header-toolbar__left [aria-label*="add block"],body.fcrm-compose-ui .edit-post-header-toolbar__left button:not(.fcrm-compose-back),body.fcrm-compose-ui .editor-header__toolbar .block-editor-inserter__toggle,body.fcrm-compose-ui .editor-header__toolbar [aria-label*="Add block"],body.fcrm-compose-ui .editor-header__toolbar [aria-label*="add block"],body.fcrm-compose-ui .editor-header__toolbar button:not(.fcrm-compose-back){display:inline-flex!important}body.fcrm-compose-ui .editor-header__actions{display:flex!important;visibility:visible!important}body.fcrm-compose-ui .edit-post-header__settings,body.fcrm-compose-ui .editor-header__settings{align-items:center!important;display:flex!important;gap:10px!important;visibility:visible!important}body.fcrm-compose-ui .edit-post-header__settings .editor-post-publish-button,body.fcrm-compose-ui .edit-post-header__settings .editor-post-publish-button__button,body.fcrm-compose-ui .edit-post-header__settings .editor-post-save-draft,body.fcrm-compose-ui .edit-post-header__settings button[aria-label*=Publish],body.fcrm-compose-ui .edit-post-header__settings button[aria-label*=Submit],body.fcrm-compose-ui .editor-header .editor-post-publish-button,body.fcrm-compose-ui .editor-header .editor-post-publish-button__button,body.fcrm-compose-ui .editor-header__actions .editor-post-publish-button,body.fcrm-compose-ui .editor-header__actions .editor-post-publish-button__button,body.fcrm-compose-ui .editor-header__actions .editor-post-save-draft,body.fcrm-compose-ui .editor-header__actions button[aria-label*=Publish],body.fcrm-compose-ui .editor-header__actions button[aria-label*=Submit],body.fcrm-compose-ui .editor-header__settings .editor-post-publish-button,body.fcrm-compose-ui .editor-header__settings .editor-post-publish-button__button,body.fcrm-compose-ui .editor-header__settings .editor-post-save-draft,body.fcrm-compose-ui .editor-header__settings button[aria-label*=Publish],body.fcrm-compose-ui .editor-header__settings button[aria-label*=Submit]{display:none!important}body.fcrm-compose-ui .edit-post-header-toolbar__left .fcrm-compose-smartcodes,body.fcrm-compose-ui .edit-post-header__settings .fcrm-compose-fullscreen,body.fcrm-compose-ui .edit-post-header__settings .fcrm-compose-next,body.fcrm-compose-ui .edit-post-header__settings .fcrm-compose-preview,body.fcrm-compose-ui .edit-post-header__settings .fcrm-compose-save,body.fcrm-compose-ui .editor-header .fcrm-compose-actions .fcrm-compose-fullscreen,body.fcrm-compose-ui .editor-header .fcrm-compose-actions .fcrm-compose-next,body.fcrm-compose-ui .editor-header .fcrm-compose-actions .fcrm-compose-preview,body.fcrm-compose-ui .editor-header .fcrm-compose-actions .fcrm-compose-save,body.fcrm-compose-ui .editor-header__actions .fcrm-compose-fullscreen,body.fcrm-compose-ui .editor-header__actions .fcrm-compose-next,body.fcrm-compose-ui .editor-header__actions .fcrm-compose-preview,body.fcrm-compose-ui .editor-header__actions .fcrm-compose-save,body.fcrm-compose-ui .editor-header__settings .fcrm-compose-fullscreen,body.fcrm-compose-ui .editor-header__settings .fcrm-compose-next,body.fcrm-compose-ui .editor-header__settings .fcrm-compose-preview,body.fcrm-compose-ui .editor-header__settings .fcrm-compose-save,body.fcrm-compose-ui .editor-header__toolbar .fcrm-compose-smartcodes{display:inline-flex!important;opacity:1!important;visibility:visible!important}body.fcrm-compose-ui .edit-post-header .fcrm-compose-actions,body.fcrm-compose-ui .editor-header .fcrm-compose-actions{display:flex!important;visibility:visible!important}.fcrm-layout-selector .fcrm-layout-grid{border-bottom:1px solid var(--fc-primary-border);display:grid;gap:16px;grid-template-columns:repeat(auto-fill,minmax(100px,1fr));padding-bottom:16px}.fcrm-layout-selector .fcrm-layout-thumb-placeholder{background-color:#e8e8e8}.fcrm-layout-selector .fcrm-layout-thumb-svg{align-items:center;background-color:#f0f0f0;display:flex;justify-content:center;padding:4px}.fcrm-layout-selector .fcrm-layout-thumb-svg svg{fill:currentColor;height:100%;max-height:32px;max-width:36px;width:100%}.fcrm-layout-selector .fcrm-layout-option{background:none;border:none;border-radius:8px;cursor:pointer;display:block;margin:0;padding:0;position:relative}.fcrm-layout-selector .fcrm-layout-option.is-selected .icon,.fcrm-layout-selector .fcrm-layout-option:focus .icon,.fcrm-layout-selector .fcrm-layout-option:hover .icon{opacity:1;transform:scale(1)}.fcrm-layout-selector .fcrm-layout-option.is-selected .fcrm-layout-thumb,.fcrm-layout-selector .fcrm-layout-option:focus .fcrm-layout-thumb,.fcrm-layout-selector .fcrm-layout-option:hover .fcrm-layout-thumb{outline-color:var(--fc-primary-text)}.fcrm-layout-selector .fcrm-layout-option .icon{opacity:0;position:absolute;right:4px;top:4px;transform:scale(.4);transition:.3s;-webkit-transition:.3s}.fcrm-layout-selector .fcrm-layout-option .icon svg{display:block}.fcrm-layout-selector .fcrm-layout-option .fcrm-layout-thumb{aspect-ratio:4/3;border-radius:8px;display:block;margin:0;min-height:80px;outline:1px solid transparent;outline-offset:-1px;overflow:hidden;width:100%}.fcrm-layout-selector .fcrm-layout-option .fcrm-layout-thumb img,.fcrm-layout-selector .fcrm-layout-option .fcrm-layout-thumb svg{display:block;height:100%;object-fit:contain;width:100%}.fcrm-layout-selector .fcrm-layout-option .fcrm-layout-label{color:var(--fc-primary-text);display:block;font-size:13px;font-weight:500;line-height:16px;margin:8px 0 0;padding:0 4px;text-align:center}.fcrm-smartcode-examples-panel{--fcrm-sidebar-accent:#4b5563;--fcrm-sidebar-accent-soft:#f3f4f6;--fcrm-sidebar-accent-hover:#e5e7eb;--fcrm-sidebar-border:#d1d5db;--fcrm-sidebar-text:#1f2937;--fcrm-sidebar-muted:#6b7280}.fcrm-template-buttons{display:flex;flex-direction:column;gap:8px;padding:16px 16px 0}.fcrm-template-buttons .components-button{justify-content:center!important}.fcrm-smartcode-tip{color:var(--fcrm-sidebar-text);font-size:12px;line-height:1.45;margin:0 0 10px}.fcrm-smartcode-tip code{background:var(--fcrm-sidebar-accent-soft);border-radius:3px;padding:1px 4px}.fcrm-smartcode-group{box-sizing:border-box;margin-bottom:12px}.fcrm-smartcode-group *,.fcrm-smartcode-group :after,.fcrm-smartcode-group :before{box-sizing:border-box}.fcrm-smartcode-group__title{color:var(--fcrm-sidebar-text);font-size:12px;letter-spacing:.5px;margin:0 0 6px;text-transform:uppercase}.fcrm-style-panel .fcrm-sub-panel{border-top:none;margin:20px 0}.fcrm-style-panel .fcrm-sub-panel .spacing-sizes-control{margin-top:10px}.fcrm-smartcode-group__list{list-style:none;margin:0;padding:0}.fcrm-smartcode-group__search{background:#fff;border:1px solid var(--fcrm-sidebar-border);border-radius:4px;color:var(--fcrm-sidebar-text);font-size:12px;margin:0 0 8px;padding:6px 8px;width:100%}.fcrm-smartcode-group__item{margin-bottom:7px}.fcrm-smartcode-group__row{align-items:center;display:flex;flex-wrap:wrap;gap:6px}.fcrm-smartcode-group__code{flex-grow:1;font-size:12px;line-height:1.4;padding:3px 6px}.fcrm-smartcode-group__code,.fcrm-smartcode-group__copy-btn{background:var(--fcrm-sidebar-accent-soft);border:1px solid var(--fcrm-sidebar-border);border-radius:4px;color:var(--fcrm-sidebar-text);cursor:pointer}.fcrm-smartcode-group__copy-btn{font-size:11px;padding:3px 7px}.fcrm-smartcode-group__copy-btn.is-copied{background:var(--fcrm-sidebar-accent);border-color:var(--fcrm-sidebar-accent);color:#fff}.fcrm-smartcode-group__label{color:var(--fcrm-sidebar-muted);font-size:11px;margin-top:2px}.fcrm-smartcode-group__empty{color:var(--fcrm-sidebar-muted);font-size:11px;padding:4px 0}.fcrm-smartcode-toolbar-popover *{box-sizing:border-box}.fcrm-smartcode-toolbar-popover .components-popover__content{background:#fff;border:1px solid var(--fc-primary-border);border-radius:8px;box-shadow:0 16px 32px -12px rgba(14,18,27,.102);max-height:100%!important;max-width:578px;overflow:hidden!important;padding:8px;width:min(500px,100vw - 32px)}.fcrm-smartcode-toolbar-popover .fcrm-smartcode-toolbar-popover__wrap{box-sizing:border-box;display:flex;max-height:400px;min-height:0}.fcrm-smartcode-toolbar-popover .fcrm-smartcode-toolbar-popover__sidebar{background:#f9fafb;border-right:1px solid #e1e4ea;display:flex;flex:none;flex-direction:column;list-style:none;margin:0;max-height:400px;max-width:190px;min-height:0;overflow-y:auto;padding:12px;position:relative;width:190px;word-break:normal}.fcrm-smartcode-toolbar-popover .fcrm-smartcode-toolbar-popover__body{display:flex;flex:1;flex-direction:column;max-height:400px;min-height:0;overflow-x:hidden;overflow-y:auto;padding:0}.fcrm-smartcode-toolbar-popover .fcrm-smartcode-toolbar-popover__items{padding:0 8px}.fcrm-smartcode-toolbar-popover .fcrm-smartcode-toolbar-popover__search{background:#fff;border-bottom:1px solid #e1e4ea;padding:10px;position:sticky;top:0;z-index:1}.fcrm-smartcode-toolbar-popover .fcrm-smartcode-toolbar-popover__search input{border:1px solid #e1e4ea;border-radius:6px;box-shadow:none;height:36px!important;padding:6px 10px}.fcrm-smartcode-toolbar-popover .fcrm-smartcode-toolbar-popover__doc-btn{background:#f5f7fa;border:none!important;border-radius:6px;box-shadow:none!important;color:#525866;display:block;font-size:13px;font-weight:500;justify-content:center;margin-top:auto;padding:6px 8px;text-align:center;text-decoration:none;transition:all .2s ease;width:100%}.fcrm-smartcode-toolbar-popover .fcrm-smartcode-toolbar-popover__doc-btn:hover{background:#0e121b!important;color:#fff!important}.fcrm-smartcode-toolbar-popover__tabs{display:flex;flex-direction:column;gap:4px;max-height:360px;overflow:auto}.fcrm-smartcode-toolbar-popover__tab{background:transparent;border:1px solid transparent;border-radius:8px;color:#4b5563;cursor:pointer;display:block;font-size:12px;line-height:16px;padding:9px 10px;text-align:left;width:100%}.fcrm-smartcode-toolbar-popover__tab.is-active{background:#0f172a;color:#fff}.fcrm-smartcode-toolbar-popover__feedback{color:#475569;font-size:12px;line-height:16px}.fcrm-smartcode-toolbar-popover__item{background:none;border:none;border-bottom:1px solid #e1e4ea;color:#0e121b;cursor:pointer;display:block;font-size:14px;line-height:20px;margin-bottom:0;padding:8px;text-align:left;transition:background-color .2s ease;white-space:normal;width:100%;word-break:normal!important}.fcrm-smartcode-toolbar-popover__item:last-child{border-bottom:none}.fcrm-smartcode-toolbar-popover__item:hover{background:#f9fafb}.fcrm-smartcode-toolbar-popover__item .fcrm-smartcode-toolbar-popover__item-label{color:#0e121b;display:block;font-size:13px;line-height:16px}.fcrm-smartcode-toolbar-popover__item .fcrm-smartcode-toolbar-popover__item-code{color:#99a0ae;display:block;font-size:10px;line-height:14px;margin:2px 0 0;overflow-wrap:break-word;word-break:break-all}.fcrm-smartcode-toolbar-popover__empty{color:#6b7280;font-size:13px;padding:14px 8px}@keyframes onOffInterval{0%{offset-distance:0}to{offset-distance:100%}}.fcrm_ai_button_anim_wrapper{border-radius:8px;clip-path:inset(0 round 8px);overflow:hidden;padding:1px;position:relative;z-index:1}.fcrm_ai_button_anim_wrapper .fcrm_ai_button_anim{background:transparent;border:0;border-radius:8px;clip-path:inset(0 round 8px);container-type:inline-size;inset:0;position:absolute;z-index:-1}.fcrm_ai_button_anim_wrapper .fcrm_ai_button_anim_inner{animation:onOffInterval 4s linear infinite;aspect-ratio:1/1;background:radial-gradient(circle at 100% 8px,#8762f0,transparent 70%);border-radius:8px;offset-anchor:100% 60%;offset-path:border-box;position:absolute;width:50cqmin}.fcrm_ai_button_anim_wrapper .fcrm_ai_button{position:relative;z-index:2}.fcrm-layout-settings-modal__content{max-width:480px;min-width:320px}.fcrm-layout-settings-modal__extra{border-top:1px solid #dcdcde;margin-top:20px;padding-top:16px}.fcrm-layout-settings-modal__extra:empty{display:none}.fcrm-ai-writing-popover *{box-sizing:border-box}.fcrm-ai-writing-popover .components-popover__content{border:1px solid #e1e4ea;border-radius:8px;box-shadow:0 4px 16px rgba(0,0,0,.1);overflow:hidden;width:min(380px,100vw - 32px)}.fcrm-ai-writing-popover__wrap{display:flex;flex-direction:column;max-height:500px}.fcrm-ai-writing-popover__header{align-items:center;border-bottom:1px solid #e1e4ea;color:#0e121b;display:flex;font-size:14px;font-weight:600;gap:8px;justify-content:space-between;padding:10px 16px}.fcrm-ai-writing-popover__tone-badge{align-items:center;background:#f9fafb;border:1px solid #e1e4ea;border-radius:14px;color:#525866;cursor:pointer;display:inline-flex;font-size:12px;font-weight:500;gap:4px;padding:3px 8px;transition:border-color .15s ease,background .15s ease;white-space:nowrap}.fcrm-ai-writing-popover__tone-badge:hover{background:#f0f1f3;border-color:#cacfd8}.fcrm-ai-writing-popover__tone-badge-label{color:#99a0ae;font-weight:400}.fcrm-ai-writing-popover__tone-badge-value{color:#0e121b}.fcrm-ai-writing-popover__tone-badge-arrow{color:#99a0ae;font-size:10px}.fcrm-ai-writing-popover__sub-header{align-items:center;border-bottom:1px solid #f0f1f3;color:#0e121b;display:flex;font-size:13px;font-weight:600;gap:8px;padding:8px 16px}.fcrm-ai-writing-popover__back-btn{background:none;border:none;border-radius:4px;color:#525866;cursor:pointer;font-size:16px;padding:2px 6px}.fcrm-ai-writing-popover__back-btn:hover{background:#f5f7fa}.fcrm-ai-writing-popover__actions{display:flex;flex-direction:column;gap:1px;padding:6px}.fcrm-ai-writing-popover__action{align-items:flex-start;background:transparent;border:none;border-radius:6px;color:#0e121b;cursor:pointer;display:flex;flex-direction:column;gap:2px;padding:8px 12px;text-align:left;width:100%}.fcrm-ai-writing-popover__action:hover{background:#f5f7fa}.fcrm-ai-writing-popover__action-label{font-size:13px;font-weight:500}.fcrm-ai-writing-popover__action-desc{color:#99a0ae;font-size:12px}.fcrm-ai-writing-popover__tone-options{display:flex;flex-wrap:wrap;gap:6px;padding:8px 16px}.fcrm-ai-writing-popover__tone-btn{background:#fff;border:1px solid #e1e4ea;border-radius:20px;color:#525866;cursor:pointer;font-size:13px;padding:6px 14px}.fcrm-ai-writing-popover__tone-btn:hover{background:#f5f7fa;border-color:#cacfd8}.fcrm-ai-writing-popover__tone-btn.is-active{background:#0e121b;border-color:#0e121b;color:#fff}.fcrm-ai-writing-popover__custom-input{padding:8px 16px}.fcrm-ai-writing-popover__loading{color:#525866;padding:32px 16px;text-align:center}.fcrm-ai-writing-popover__loading p{font-size:13px;margin:8px 0 0}.fcrm-ai-writing-popover__preview-text{background:#f5f7fa;border-radius:6px;color:#0e121b;font-size:13px;line-height:1.6;margin:8px 16px;max-height:240px;overflow-y:auto;padding:12px}.fcrm-ai-writing-popover__footer{background:#f9fafb;border-top:1px solid var(--fc-primary-border);display:flex;gap:8px;justify-content:flex-end;padding:12px 16px}.fcrm-ai-writing-popover__error{background:#fef2f2;border-bottom:1px solid #fecaca;color:#e5484d;font-size:13px;padding:8px 16px}.fcrm-ai-writer-block *{box-sizing:border-box}.fcrm-ai-writer-block__inner{background:var(--fc-primary-bg);border:1px solid rgba(0,0,0,.078);border-radius:8px;box-shadow:0 1px 3px -1.5px rgba(51,51,51,.161),0 5px 5px -2.5px rgba(51,51,51,.078),0 12px 6px -6px rgba(51,51,51,.02),0 16px 8px -8px rgba(51,51,51,.012),0 0 0 1px rgba(51,51,51,.039),inset 0 -.5px .5px 0 rgba(51,51,51,.078);overflow:hidden}.fcrm-ai-writer-block__header{align-items:center;border-bottom:1px solid var(--fc-primary-border);display:flex;gap:12px;padding:12px 16px}.fcrm-ai-writer-block__icon{align-items:center;background:rgba(119,66,230,.161);border-radius:6px;color:#8762f0;display:flex;height:26px;justify-content:center;width:26px}.fcrm-ai-writer-block__icon svg{display:block}.fcrm-ai-writer-block__title{color:var(--fc-primary-text);flex:1;font-size:14px;font-weight:500;line-height:20px;margin:0}.fcrm-ai-writer-block__controls{align-items:center;display:flex;gap:8px}.fcrm-ai-writer-block__select{background:var(--fc-secondary-bg);border:1px solid var(--fc-primary-border);border-radius:8px;color:var(--fc-secondary-text);cursor:pointer;font-size:14px;font-weight:500;line-height:20px;outline:none;padding:4px 6px}.fcrm-ai-writer-block__select:hover{border-color:#cacfd8}.fcrm-ai-writer-block__select:focus{border-color:#3e58e1}.fcrm-ai-writer-block__prompt{background:transparent;border:none;color:#0e121b;display:block;font-family:inherit;font-size:14px;line-height:1.5;min-height:72px;outline:none;padding:12px 14px;resize:vertical;width:100%}.fcrm-ai-writer-block__prompt::placeholder{color:#99a0ae}.fcrm-ai-writer-block__prompt:disabled{opacity:.6}.fcrm-ai-writer-block__error{background:#fef2f2;color:#e5484d;font-size:13px;padding:8px 14px}.fcrm-ai-writer-block__footer{align-items:center;background:#f9fafb;border-top:1px solid #e1e4ea;display:flex;flex-wrap:wrap;gap:12px;justify-content:space-between;padding:10px 12px}.fcrm-ai-writer-block__tip{color:var(--fc-text-muted);font-size:14px;font-weight:500;line-height:20px;margin:0}.components-button.fcrm-ai-writer-block__generate-button,.fcrm-ai-writer-block__generate-button{align-items:center;background:#efebff;border:1px solid #efebff;border-radius:8px;color:#8762f0;display:inline-flex;font-size:14px;font-weight:500;gap:4px;line-height:20px;min-height:36px}.components-button.fcrm-ai-writer-block__generate-button:disabled,.fcrm-ai-writer-block__generate-button:disabled{background:var(--fc-primary-bg)!important;border-color:#8762f0;color:#8762f0!important;opacity:.6}.components-button.fcrm-ai-writer-block__generate-button:hover,.fcrm-ai-writer-block__generate-button:hover{background:var(--fc-primary-bg)!important;color:#8762f0!important}.components-button.fcrm-ai-writer-block__generate-button .icon,.fcrm-ai-writer-block__generate-button .icon{display:block;height:20px;width:20px}.components-button.fcrm-ai-writer-block__generate-button .icon svg,.fcrm-ai-writer-block__generate-button .icon svg{display:block}.fcrm-ai-writer-block__loading{align-items:center;color:#525866;display:flex;font-size:13px;gap:8px}.fcrm-ai-writer-block__loading svg{margin:0}.fcrm-email-body-panel-wrapper.components-panel__body{border-top:none!important;padding:0!important}.fcrm-email-body-panel-wrapper.components-panel__body>.components-panel__body-title{display:none!important}.edit-post-header .components-button,.editor-header .components-button{border-radius:6px}.edit-post-header .components-button.is-primary,.editor-header .components-button.is-primary{background:var(--static-static-black,#0e121b);color:#fff}.edit-post-header .components-button.fcrm-compose-back,.editor-header .components-button.fcrm-compose-back{align-items:center;background:var(--static-static-black,#0e121b);border:none;box-shadow:none;color:#fff;cursor:pointer;display:flex;height:32px;justify-content:center;margin:0 10px 0 0;width:32px}.edit-post-header .components-button.fcrm-compose-back .fcrm-back-icon svg,.editor-header .components-button.fcrm-compose-back .fcrm-back-icon svg{display:block}.edit-post-header .components-button.fcrm-compose-save,.editor-header .components-button.fcrm-compose-save{justify-content:center;min-width:72px}.edit-post-header .components-button.fcrm-compose-ai-writing .fcrm-ai-writing-icon svg,.edit-post-header .components-button.fcrm-compose-smartcodes .fcrm-smartcode-icon svg,.editor-header .components-button.fcrm-compose-ai-writing .fcrm-ai-writing-icon svg,.editor-header .components-button.fcrm-compose-smartcodes .fcrm-smartcode-icon svg{display:block}.editor-styles-wrapper .fcrm-smartcode-highlight{background:#ebf1ff;border-radius:3px;color:#335cff;font-size:.92em;padding:1px 3px}.editor-styles-wrapper{background-color:var(--fcrm-body-bg,#fafafa)!important;color:var(--theme-text-color);font-family:var(--theme-font-family)!important;max-width:var(--fcrm-content-width,700px)!important}.editor-styles-wrapper p{font-size:var(--theme-font-size,16px);line-height:var(--theme-line-height,1.6)}.is-root-container{background-color:var(--fcrm-content-bg,#fff)!important;border-radius:var(--fcrm-content-radius,0)!important;margin-bottom:var(--fcrm-margin-bottom,20px)!important;margin-top:var(--fcrm-margin-top,20px)!important;overflow:hidden;padding:var(--fcrm-padding-top,20px) var(--fcrm-padding-right,20px) var(--fcrm-padding-bottom,20px) var(--fcrm-padding-left,20px)!important}h1,h2,h3,h4,h5,h6{color:var(--fcrm-headings-color,#202020);font-family:var(--fcrm-headings-font)!important}a{color:var(--fcrm-link-color,#0693e3)}.fcrm-sidebar-tabs [role=tablist]{border-bottom:1px solid #e0e0e0;display:flex!important;margin:16px auto}.fcrm-design-presets,.fcrm-smartcodes-tab{padding:4px 16px 0}.fcrm-design-presets__grid{display:grid;gap:12px;grid-template-columns:repeat(auto-fill,minmax(100px,1fr))}.fcrm-design-presets__item{background:none;border:2px solid transparent;border-radius:8px;cursor:pointer;display:block;margin:0;padding:0;text-align:center}.fcrm-design-presets__item:focus,.fcrm-design-presets__item:hover{border-color:#d1d5db}.fcrm-design-presets__item.is-selected{border-color:var(--wp-admin-theme-color,#3e58e1)}.fcrm-design-presets__thumb{aspect-ratio:4/3;background:#f0f0f0;border-radius:6px;display:block;overflow:hidden}.fcrm-design-presets__thumb img{display:block;height:100%;object-fit:cover;width:100%}.fcrm-design-presets__placeholder{background:#e8e8e8;display:block;height:100%;width:100%}.fcrm-design-presets__label{color:var(--fcrm-sidebar-text,#1f2937);display:block;font-size:12px;font-weight:500;line-height:16px;margin-top:6px;padding:0 2px 4px}.fcrm-design-presets__empty{color:var(--fcrm-sidebar-muted,#6b7280);font-size:13px;padding:16px}.fcrm-email-style-settings .fcrm-style-panel{border:none!important;margin-bottom:4px}.fcrm-email-style-settings .fcrm-style-panel>.components-panel__body-title{background:hsla(0,0%,94%,.612);margin-bottom:0}.fcrm-email-style-settings .fcrm-style-panel>.components-panel__body-title .components-panel__body-toggle{color:var(--fcrm-sidebar-text,#1f2937);font-size:13px;font-weight:600}.fcrm-email-style-settings .fcrm-style-panel>.components-panel__body-title+div{padding:0 0 8px}.fcrm-email-style-settings .fcrm-style-panel .components-range-control .components-base-control__label{font-size:12px;font-weight:500}.fcrm-email-style-settings .fcrm-style-panel .spacing-sizes-control__wrapper{align-items:center}.fcrm-email-style-settings .fcrm-style-panel .components-select-control{margin-bottom:8px}.fcrm-email-style-settings .fcrm-style-panel .components-select-control .components-base-control__label{font-size:12px;font-weight:500}.fcrm-email-style-settings .fcrm-style-panel .fcrm-typo-section{border-top:1px solid #e0e0e0;margin-top:16px;padding-top:16px}.fcrm-email-style-settings .fcrm-style-sub-panel{border:1px solid var(--fc-primary-border);border-radius:6px;margin-bottom:8px;padding:0}.fcrm-email-style-settings .fcrm-style-sub-panel>.components-panel__body-title .components-panel__body-toggle{font-size:12px;font-weight:500;padding:8px 10px}.fcrm-email-style-settings .fcrm-style-sub-panel>.components-panel__body-title+div{padding:4px 10px 10px}.fcrm-footer-settings-modal .components-modal__content{max-width:calc(100vw - 32px);width:640px}.fcrm-footer-settings-modal .components-radio-control .components-base-control__label{display:none}.fcrm-footer-settings-modal .components-radio-control .components-radio-control__option{margin-bottom:2px}.fcrm-footer-settings-modal .components-radio-control .components-radio-control__option label{font-size:13px;font-weight:500}.fcrm-footer-settings-modal .fcrm-footer-tinymce-wrap .fcrm-footer-tinymce__header{align-items:center;display:flex;justify-content:space-between;margin-bottom:8px}.fcrm-footer-settings-modal .fcrm-footer-tinymce-wrap .fcrm-footer-tinymce__header .fcrm-footer-tinymce__label{margin:0}.fcrm-footer-settings-modal .fcrm-footer-tinymce-wrap .fcrm-footer-tinymce__header .fcrm-footer-editor-actions{align-items:center;display:flex;gap:8px}.fcrm-footer-settings-modal .fcrm-footer-tinymce-wrap .fcrm-footer-tinymce__header .fcrm-footer-editor-toggle .fcrm-footer-toggle-switch{background:#f0f0f0;border-radius:6px;display:flex;gap:2px;height:28px;padding:3px}.fcrm-footer-settings-modal .fcrm-footer-tinymce-wrap .fcrm-footer-tinymce__header .fcrm-footer-editor-toggle .fcrm-footer-toggle-switch button{background:transparent;border:none;border-radius:4px;color:#757575;cursor:pointer;flex:1;font-size:12px;font-weight:500;line-height:1;padding:2px 10px;transition:all .15s ease;white-space:nowrap}.fcrm-footer-settings-modal .fcrm-footer-tinymce-wrap .fcrm-footer-tinymce__header .fcrm-footer-editor-toggle .fcrm-footer-toggle-switch button.active{background:#fff;box-shadow:0 1px 3px rgba(0,0,0,.12),0 1px 2px rgba(0,0,0,.06);color:#1e1e1e}.fcrm-footer-settings-modal .fcrm-footer-tinymce-wrap .fcrm-footer-tinymce__header .fcrm-footer-editor-toggle .fcrm-footer-toggle-switch button:hover:not(.active){color:#3c434a}.fcrm-footer-settings-modal .fcrm-footer-tinymce-wrap .fcrm-footer-tinymce__label{color:#1e1e1e;font-size:11px;font-weight:500;margin:0 0 8px;text-transform:uppercase}.fcrm-footer-settings-modal .fcrm-footer-tinymce-wrap .mce-tinymce{border-radius:4px!important;overflow:hidden}.fcrm-footer-settings-modal .fcrm-footer-tinymce-wrap .fcrm-footer-tinymce__help{color:#6b7280;font-size:12px;line-height:1.4;margin:8px 0 0}.fcrm-footer-settings-modal .fcrm-footer-tinymce-wrap .fcrm-footer-text-mode{border:1px solid #ddd;border-radius:4px;overflow:hidden}.fcrm-footer-settings-modal .fcrm-footer-tinymce-wrap .fcrm-footer-text-mode__quicktags{background:#f9f9f9;border-bottom:1px solid #ddd;display:flex;flex-wrap:wrap;gap:4px;padding:6px 8px}.fcrm-footer-settings-modal .fcrm-footer-tinymce-wrap .fcrm-footer-text-mode__quicktags button{background:#fff;border:1px solid #c3c4c7;border-radius:3px;color:#2c3338;cursor:pointer;font-size:12px;font-weight:500;line-height:1.4;padding:3px 8px;transition:border-color .1s ease,background .1s ease}.fcrm-footer-settings-modal .fcrm-footer-tinymce-wrap .fcrm-footer-text-mode__quicktags button:hover{background:#f0f6fc;border-color:#2271b1;color:#2271b1}.fcrm-footer-settings-modal .fcrm-footer-tinymce-wrap .fcrm-footer-text-mode__textarea{background:#fff;border:none;box-sizing:border-box;color:#2c3338;display:block;font-family:Consolas,Monaco,Courier New,monospace;font-size:12px;line-height:1.6;min-height:160px;padding:8px;resize:vertical;width:100%}.fcrm-footer-settings-modal .fcrm-footer-tinymce-wrap .fcrm-footer-text-mode__textarea:focus{box-shadow:inset 0 0 0 1px #2271b1;outline:none}.fcrm-footer-settings-modal .components-input-control__label{font-size:12px;font-weight:500}.fcrm-footer-settings-modal__content{padding-bottom:4px}.fcrm-footer-settings__hint{color:#6b7280;font-size:12px;line-height:1.4;margin:-8px 0 0}.fcrm-footer-settings__divider{border-top:1px solid #e0e0e0;margin:0}.fcrm-footer-settings__done{align-self:flex-end}.mce-floatpanel,.mce-tooltip,.mce-window{z-index:200000!important}.mce-modal-block{z-index:199999!important}.fcrm-color-toggle-wrap{align-items:center;border:1px solid #e0e0e0;border-radius:6px;display:inline-flex;gap:6px;padding:6px 10px;transition:border-color .15s ease}.fcrm-color-toggle-wrap:hover{border-color:#9ca3af}.fcrm-color-toggle{border:none;border-radius:0;display:inline-flex;gap:8px;position:relative}.fcrm-color-toggle,.fcrm-color-toggle__clear{align-items:center;background:#fff;cursor:pointer;padding:0}.fcrm-color-toggle__clear{border:none;border-radius:999px;color:#9ca3af;display:flex;font-size:15px;font-weight:500;height:20px;justify-content:center;line-height:1;text-align:center;-webkit-user-select:none;user-select:none;width:20px}.fcrm-color-toggle__clear svg{display:block;height:14px;width:14px}.fcrm-color-toggle__clear:hover{background:#f1f5f9;color:#4b5563}.fcrm-color-indicator{border:1px solid rgba(0,0,0,.15);border-radius:50%;display:block;flex-shrink:0;height:20px;width:20px}.fcrm-color-hex{color:#4b5563;font-family:monospace;font-size:12px}.block-editor-block-inspector__advanced{display:none!important}.fcrm-smart-link-popover .components-popover__content{background:#fff;border:1px solid var(--fc-primary-border);border-radius:8px;box-shadow:0 16px 32px -12px rgba(14,18,27,.1);overflow:hidden!important}.fcrm-smart-link-popover .fcrm-smart-link-popover__content{display:flex;flex-direction:column;max-height:420px;width:320px}.fcrm-smart-link-popover .fcrm-smart-link-popover__content .components-search-control{border-bottom:1px solid #e1e4ea;flex-shrink:0;padding:8px}.fcrm-smart-link-popover .fcrm-smart-link-popover__content .components-search-control input{border:1px solid #e1e4ea;border-radius:6px;box-shadow:none;font-size:13px;height:36px!important;padding:6px 10px}.fcrm-smart-link-popover .fcrm-smart-link-popover__loading{display:flex;justify-content:center;padding:24px 16px}.fcrm-smart-link-popover .fcrm-smart-link-popover__empty{color:#6b7280;font-size:13px;padding:24px 16px;text-align:center}.fcrm-smart-link-popover .fcrm-smart-link-popover__list{max-height:340px;overflow-y:auto;padding:4px 0}.fcrm-smart-link-popover .fcrm-smart-link-popover__item{align-items:flex-start;background:none;border:none;cursor:pointer;display:flex;flex-direction:column;gap:2px;padding:8px 12px;text-align:left;transition:background-color .15s ease;width:100%}.fcrm-smart-link-popover .fcrm-smart-link-popover__item:hover{background-color:#f3f4f6}.fcrm-smart-link-popover .fcrm-smart-link-popover__item-title{color:#0e121b;font-size:13px;font-weight:500;line-height:1.4}.fcrm-smart-link-popover .fcrm-smart-link-popover__item-url{color:#99a0ae;font-size:11px;line-height:1.4;max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.fcrm-smartcode-popover .fcrm-smartcode-popover__content{padding:10px 12px}.fcrm-smartcode-popover .fcrm-smartcode-popover__header{margin-bottom:8px}.fcrm-smartcode-popover .fcrm-smartcode-popover__name{background:#ebf1ff;border-radius:3px;color:#335cff;font-family:monospace;font-size:11px;font-weight:500;padding:2px 6px}.fcrm-smartcode-popover .fcrm-smartcode-popover__label{color:#525866;font-size:12px;font-weight:500;margin-bottom:6px}.fcrm-smartcode-popover .fcrm-smartcode-popover__row{align-items:center;display:flex;gap:6px;min-width:260px}.fcrm-smartcode-popover .fcrm-smartcode-popover__row .components-base-control{flex:1;margin-bottom:0}.fcrm-smartcode-popover .fcrm-smartcode-popover__row .components-text-control__input{font-size:13px;height:32px;min-height:32px}.fcrm-smartcode-popover .fcrm-smartcode-popover__row .components-button{flex-shrink:0;height:32px}.block-editor-block-settings-menu__popover a.components-menu-item__button[href*=pattern],.block-editor-block-toolbar.is-synced>.block-editor-block-toolbar__slot{display:none}.fcrm-no-pattern-features #tabs-1-patterns,body.fcrm-no-pattern-features .reusable-blocks-menu-items__convert{display:none!important}.fcrm-info-panel{margin-top:12px}.fcrm-sidebar-info-content{padding:8px 16px 4px}.fcrm-sidebar-info-content h3{color:#1e1e1e;font-size:13px;font-weight:600;margin:0 0 10px}.fcrm-sidebar-info-content p{color:#757575;font-size:12.5px;line-height:1.6;margin:0 0 12px}.fcrm-sidebar-info-content p:last-child{margin-bottom:0}.is-layout-constrained>.aligncenter,.is-layout-flow>.aligncenter{margin-left:auto!important;margin-right:auto!important}.is-layout-constrained>.alignright,.is-layout-flow>.alignright{float:none;margin-inline-end:0;margin-inline-start:auto}.is-layout-flex{display:flex;flex-wrap:wrap}.is-layout-flex.is-vertical{flex-direction:column}.is-layout-flex.is-nowrap{flex-wrap:nowrap}.is-layout-flex.is-content-justification-left{justify-content:flex-start}.is-layout-flex.is-content-justification-center{justify-content:center}.is-layout-flex.is-content-justification-right{justify-content:flex-end}.is-layout-flex.is-content-justification-space-between{justify-content:space-between}.fluent-singleProduct-template-settings{width:100%}@media(max-width:900px){.fcrm-smartcode-toolbar-popover__sidebar{border-bottom:1px solid #e5e7eb;border-right:0}.fcrm-smartcode-toolbar-popover__tabs{flex-direction:row;flex-wrap:wrap;max-height:none}} +html[dir=rtl] .has-text-align-right{text-align:left}html[dir=rtl] .fcrm-smartcode-toolbar-popover__item,html[dir=rtl] .fcrm-smartcode-toolbar-popover__tab,html[dir=rtl] .has-text-align-left{text-align:right}html[dir=rtl] .fcrm-smartcode-toolbar-popover .fcrm-smartcode-toolbar-popover__sidebar{border-left:1px solid #e1e4ea;border-right:none} +.fluent-single-product-block{margin:20px 0}.fcw_p{align-items:center;border-radius:5px;display:flex;overflow:hidden;padding:15px}.fcw_p .fcw_image{flex:1;padding:0 10px 0 0}.fcw_p .fcw_image img{max-width:100%}.fcw_p .fcw_p_content{flex:1;padding:0 0 0 10px}.fcw_p .fcw_p_content h2{margin:5px 0 10px;padding:0}.fcw_p .fcw_p_content p{color:inherit!important}.fcw_p .fcw_p_content .wp-block-button{margin-top:15px;padding:0;width:100%}.fcw_p .fcw_p_content .fcb_p_button{max-width:100%}.fcw_p .fcw_p_content .fcb_p_button .wp-block-buttons{width:100%}.fcw_p .fcw_p_content .fcb_p_button .wp-block-button,.fcw_p .fcw_p_content .fcb_p_button .wp-block-button__link{box-sizing:border-box;width:100%}.fcw_p .fcw_p_content .fcb_p_button .wp-block-button__width-25{width:25%}.fcw_p .fcw_p_content .fcb_p_button .wp-block-button__width-50{width:50%}.fcw_p .fcw_p_content .fcb_p_button .wp-block-button__width-75{width:75%}.fcw_p .fcw_p_content .fcb_p_button .wp-block-button__width-100{width:100%}.fcw_p .fcw_p_content .fcw_btn{border-radius:4px;display:inline-block;font-weight:500;margin-top:14px;padding:10px 14px;text-decoration:none}.fcw_p .fcw_p_content .fcw_p_price{align-items:baseline;display:flex;flex-wrap:wrap;gap:8px;line-height:1.25;margin:8px 0 10px}.fcw_p .fcw_p_content .fcw_p_price .screen-reader-text{border:0;clip:rect(1px,1px,1px,1px);clip-path:inset(50%);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;white-space:nowrap;width:1px}.fcw_p .fcw_p_content .fcw_p_price del{color:#9aa3aa;text-decoration-thickness:1px}.fcw_p .fcw_p_content .fcw_p_price ins{font-weight:600;text-decoration:none}.fcw_p.fcw_template_none{text-align:center}.fcw_p.fcw_template_none .fcw_p_content{padding:0}.fcw_p.fcw_template_none .fcw_p_content *{text-align:center}.fcw_p.fcw_template_top{flex-direction:column;text-align:center}.fcw_p.fcw_template_top .fcw_p_content,.fcw_p.fcw_template_top .fcw_p_content .fcw_p_desc,.fcw_p.fcw_template_top .fcw_p_content .fcw_p_desc *,.fcw_p.fcw_template_top .fcw_p_content .fcw_p_title,.fcw_p.fcw_template_top .fcw_p_content .fcw_p_title *{text-align:center}.fc_product_loading{display:none}.fc_woo_loader{align-items:center;background:#fff7eb;border-radius:10px;display:flex;flex-direction:column;justify-content:center;padding:50px 40px;text-align:center;width:100%}.fcw_search_box{background:#eaeaea;border-radius:10px;padding:20px}.fluent-single-product-search-bar{align-items:center;display:grid;gap:10px;grid-template-columns:minmax(0,1fr) auto}.fluent-single-product-search-bar>div{min-width:0}.fluent-single-product-search-bar input{box-sizing:border-box;max-width:100%;width:100%}.fluent-single-product-search-bar button{min-width:120px;white-space:nowrap}.fcw_results .components-radio-control__option{margin-bottom:6px}.fcw_results .components-radio-control__option label{line-height:1.4;overflow-wrap:anywhere} +.fluent-latest-posts-settings{width:100%}.fluent-latest-posts-settings .components-base-control__help{margin-top:0}.fluent-latest-posts-settings .components-base-control .components-base-control__field{margin-bottom:0}.fc_latest_posts_items .fc_latest_post_item{border:1px solid #edeef4;text-align:center}.fc_latest_posts_items .fc_latest_post_item .fc_latest_post_thumbnail{background-position:50%;background-repeat:no-repeat}.fc_latest_posts_items .fc_latest_post_item .fc_latest_post_thumbnail img{display:block;max-height:400px;object-fit:cover;width:100%}.fc_latest_posts_items .fc_latest_post_item .fc_latest_post_content{padding:35px 40px 30px}.fc_latest_posts_items .fc_latest_post_item .fc_latest_post_content>:last-child{margin-bottom:0!important}.fc_latest_posts_items .fc_latest_post_item .fc_latest_post_content .meta{align-items:center;display:flex;gap:15px;justify-content:flex-start;line-height:1.3;margin:0 0 8px}.fc_latest_posts_items .fc_latest_post_item .fc_latest_post_content .meta .author{align-items:center;display:inline-flex;gap:7px}.fc_latest_posts_items .fc_latest_post_item .fc_latest_post_content .meta .author a{color:#000;font-weight:500}.fc_latest_posts_items .fc_latest_post_item .fc_latest_post_content .meta .author a:hover{color:#7757e6}.fc_latest_posts_items .fc_latest_post_item .fc_latest_post_content .meta .author img{border-radius:30px;display:block;height:30px;width:30px}.fc_latest_posts_items .fc_latest_post_item .fc_latest_post_content .meta .comments{color:#acacac;display:block}.fc_latest_posts_items .fc_latest_post_item .fc_latest_post_content .title{font-size:25px;line-height:1.4;margin-bottom:12px;padding:0;text-align:center}.fc_latest_posts_items .fc_latest_post_item .fc_latest_post_content .title a{color:#393d57;display:inline-block;transition:.2s}.fc_latest_posts_items .fc_latest_post_item .fc_latest_post_content .title a:hover{color:#7757e6}.fc_latest_posts_items .fc_latest_post_item .fc_latest_post_content .description{margin:0 0 15px;text-align:center}.fc_latest_posts_items .fc_latest_post_item .fc_latest_post_content .fc_latest_post_btn{color:#000;display:inline-block;text-decoration:none;transition:.3s}.fc_latest_posts_items .fc_latest_post_item .fc_latest_post_content .fc_latest_post_btn:focus{box-shadow:none;outline:none}.fc_latest_posts_items .fc_latest_post_item .fc_latest_post_content .fc_latest_post_btn:hover{color:#7757e6}.fc_latest_posts_items.template-default .fc_latest_post_item .fc_latest_post_thumbnail,.fc_latest_posts_items.template-layout-7 .fc_latest_post_item .fc_latest_post_thumbnail{height:350px}.fc_latest_posts_items.template-layout-2 .fc_latest_post_item,.fc_latest_posts_items.template-layout-3 .fc_latest_post_item,.fc_latest_posts_items.template-layout-4 .fc_latest_post_item{display:flex;text-align:start}.fc_latest_posts_items.template-layout-2 .fc_latest_post_item .fc_latest_post_thumbnail,.fc_latest_posts_items.template-layout-3 .fc_latest_post_item .fc_latest_post_thumbnail,.fc_latest_posts_items.template-layout-4 .fc_latest_post_item .fc_latest_post_thumbnail{width:220px}.fc_latest_posts_items.template-layout-2 .fc_latest_post_item .fc_latest_post_thumbnail img,.fc_latest_posts_items.template-layout-3 .fc_latest_post_item .fc_latest_post_thumbnail img,.fc_latest_posts_items.template-layout-4 .fc_latest_post_item .fc_latest_post_thumbnail img{height:100%}.fc_latest_posts_items.template-layout-2 .fc_latest_post_item .fc_latest_post_content,.fc_latest_posts_items.template-layout-3 .fc_latest_post_item .fc_latest_post_content,.fc_latest_posts_items.template-layout-4 .fc_latest_post_item .fc_latest_post_content{flex:1;text-align:start}.fc_latest_posts_items.template-layout-2 .fc_latest_post_item .fc_latest_post_content h1,.fc_latest_posts_items.template-layout-3 .fc_latest_post_item .fc_latest_post_content h1,.fc_latest_posts_items.template-layout-4 .fc_latest_post_item .fc_latest_post_content h1{font-size:22px;text-align:start}.fc_latest_posts_items.template-layout-2 .fc_latest_post_item .fc_latest_post_content .description,.fc_latest_posts_items.template-layout-3 .fc_latest_post_item .fc_latest_post_content .description,.fc_latest_posts_items.template-layout-4 .fc_latest_post_item .fc_latest_post_content .description{font-size:15px!important;text-align:start}.fc_latest_posts_items.template-layout-3 .fc_latest_post_item,.fc_latest_posts_items.template-layout-4 .fc_latest_post_item{align-items:center;gap:15px;padding:35px 40px 30px;text-align:start}.fc_latest_posts_items.template-layout-3 .fc_latest_post_item .fc_latest_post_thumbnail,.fc_latest_posts_items.template-layout-4 .fc_latest_post_item .fc_latest_post_thumbnail{height:220px;order:2}.fc_latest_posts_items.template-layout-3 .fc_latest_post_item .fc_latest_post_thumbnail img,.fc_latest_posts_items.template-layout-4 .fc_latest_post_item .fc_latest_post_thumbnail img{border-radius:4px}.fc_latest_posts_items.template-layout-3 .fc_latest_post_item .fc_latest_post_content,.fc_latest_posts_items.template-layout-4 .fc_latest_post_item .fc_latest_post_content{padding:0}.fc_latest_posts_items.template-layout-3 .fc_latest_post_item .fc_latest_post_content .description,.fc_latest_posts_items.template-layout-3 .fc_latest_post_item .fc_latest_post_content h1,.fc_latest_posts_items.template-layout-4 .fc_latest_post_item .fc_latest_post_content .description,.fc_latest_posts_items.template-layout-4 .fc_latest_post_item .fc_latest_post_content h1{text-align:start}.fc_latest_posts_items.template-layout-4 .fc_latest_post_item{align-items:normal;border:none;border-bottom:1px solid #edeef4;margin:0;padding:20px 0}.fc_latest_posts_items.template-layout-4 .fc_latest_post_item .fc_latest_post_content{display:flex;flex-direction:column}.fc_latest_posts_items.template-layout-4 .fc_latest_post_item .fc_latest_post_content .meta{margin:20px 0 0;order:3}.fc_latest_posts_items.template-layout-4 .fc_latest_post_item .fc_latest_post_content .description{margin-bottom:0}.fc_latest_posts_items.template-layout-4 .fc_latest_post_item .fc_latest_post_thumbnail{height:auto}.fc_latest_posts_items.template-layout-5 .fc_latest_post_item{border:none}.fc_latest_posts_items.template-layout-5 .fc_latest_post_item+.fc_latest_post_item{margin-top:5px}.fc_latest_posts_items.template-layout-5 .fc_latest_post_item .fc_latest_post_content{padding:0}.fc_latest_posts_items.template-layout-5 .fc_latest_post_item .fc_latest_post_content h1{align-items:flex-start;display:flex;font-size:16px;font-weight:600;gap:5px;margin:0;position:relative;text-align:start}.fc_latest_posts_items.template-layout-5 .fc_latest_post_item .fc_latest_post_content h1:before{background:#000;border-radius:20px;content:"";height:5px;margin-top:10px;width:5px}.fc_latest_posts_items.template-layout-5 .fc_latest_post_item .fc_latest_post_content h1 a{color:#7757e6;text-decoration:underline}.fc_latest_posts_items.template-layout-5 .fc_latest_post_item .fc_latest_post_content h1 a:hover{color:#000}.fc_latest_posts_items.template-layout-6 .fc_latest_post_item{align-items:center;border:none;border-bottom:1px solid #edeef4;display:flex;gap:15px;padding:25px 0;text-align:start}.fc_latest_posts_items.template-layout-6 .fc_latest_post_item:first-child{border-top:1px solid #edeef4}.fc_latest_posts_items.template-layout-6 .fc_latest_post_item+.fc_latest_post_item{margin:0}.fc_latest_posts_items.template-layout-6 .fc_latest_post_item .fc_latest_post_thumbnail{border-radius:5px;height:100px;width:100px}.fc_latest_posts_items.template-layout-6 .fc_latest_post_item .fc_latest_post_thumbnail img{border-radius:5px;height:auto;max-height:100px;width:100px}.fc_latest_posts_items.template-layout-6 .fc_latest_post_item .fc_latest_post_content{padding:0}.fc_latest_posts_items.template-layout-6 .fc_latest_post_item .fc_latest_post_content .title{font-size:20px;font-weight:600;text-align:start}.fc_latest_posts_items.template-layout-7 .fc_latest_post_item{border:none}.fc_latest_posts_items.template-layout-7 .fc_latest_post_item .fc_latest_post_thumbnail{margin-bottom:25px}.fc_latest_posts_items.template-layout-7 .fc_latest_post_item .fc_latest_post_content{padding:0;text-align:start}.fc_latest_posts_items.template-layout-7 .fc_latest_post_item .fc_latest_post_content .description{margin-bottom:17px;text-align:start}.fc_latest_posts_items.template-layout-7 .fc_latest_post_item .fc_latest_post_content .title{margin-bottom:10px;text-align:start}.fc_latest_posts_items.template-layout-7 .fc_latest_post_item .fc_latest_post_content .fc_latest_post_btn{border:2px solid #000;border-radius:4px;font-weight:500;padding:4px 14px}.fc-recent-posts-number .components-base-control__field{margin-bottom:5px!important}.fc-recent-posts-number .components-base-control__help{margin-top:0}.show-setting-control-box{position:relative}.show-setting-control-box .components-base-control:last-child{margin-bottom:24px}.show-setting-control-box .show-setting-dropdown{position:absolute;right:0;top:-4px}.show-setting-control-box .show-setting-dropdown button{background:none;color:#000;display:block;height:auto;margin:0;padding:0}.show-setting-control-box .show-setting-dropdown button:hover,.show-setting-control-box .show-setting-dropdown button:hover:not(:disabled){background:none;color:#000}.show-setting-control-box .show-setting-dropdown button svg{display:block}.show-setting-control-box.select-layout{margin-bottom:24px}.show-setting-control-box.select-layout>p{display:block;font-size:11px;font-weight:500;line-height:1.4;margin-bottom:8px;padding:0;text-transform:uppercase}.show-setting-control-box.select-layout .show-setting-dropdown{align-items:center;display:flex;position:relative;top:0}.show-setting-control-box.select-layout .show-setting-dropdown>img{border:1px solid #eff1ff;border-radius:2px;cursor:pointer;display:block;margin-top:4px;object-fit:cover;padding:5px;width:80px}.show-setting-control-box.select-layout .show-setting-dropdown p{display:inline-block;font-size:11px;font-weight:500;line-height:1.4;margin-bottom:8px;margin-top:0;padding:0;text-transform:uppercase}.show-setting-control-box.select-layout .show-setting-dropdown .components-button{position:absolute;right:0;top:-27px}.dropdown-render-content{min-width:180px;padding:4px}.dropdown-render-content .components-base-control:last-child{margin-bottom:0}.dropdown-render-content p{display:inline-block;font-size:11px;font-weight:500;line-height:1.4;margin-bottom:8px;margin-top:0;padding:0;text-transform:uppercase}.fc-layout-picker{max-width:320px;min-width:260px}.fc-layout-picker .fc-layout-picker-grid{display:grid;gap:8px;grid-template-columns:repeat(3,minmax(0,1fr));max-height:280px;overflow-y:auto;padding-right:2px}.fc-layout-picker .fc-layout-picker-option{background:#fff;border:1px solid #dfe3ea;border-radius:6px;cursor:pointer;margin:0;padding:6px;text-align:center;transition:border-color .2s ease,box-shadow .2s ease;width:100%}.fc-layout-picker .fc-layout-picker-option:hover{border-color:#3858e9}.fc-layout-picker .fc-layout-picker-option.is-active{border-color:#3858e9;box-shadow:inset 0 0 0 1px #3858e9}.fc-layout-picker .fc-layout-picker-option img{border-radius:4px;display:block;height:auto;margin-bottom:6px;width:100%}.fc-layout-picker .fc-layout-picker-option span{color:#1e1e1e;display:block;font-size:11px;line-height:1.2} +.fc_woo_products{column-gap:20px;display:grid;grid-template-columns:1fr 1fr}.fc_woo_products .fc_woo_product{margin-bottom:35px}.fc_woo_products .fc_woo_product.no-image .fc_woo_product_info{width:100%}.fc_woo_products .fc_woo_product .fc_woo_product_img{height:280px;margin-bottom:20px;position:relative;width:100%}.fc_woo_products .fc_woo_product .fc_woo_product_img img{background:#eee;display:block;height:100%;object-fit:cover;width:100%}.fc_woo_products .fc_woo_product .fc_woo_product_info .title{color:#2a363d;font-size:20px;font-weight:500;line-height:1.2;margin-bottom:8px}.fc_woo_products .fc_woo_product .fc_woo_product_info .price{align-items:baseline;color:#37454e;display:flex;flex-wrap:wrap;font-size:16px;gap:8px;line-height:1.2;margin-bottom:10px}.fc_woo_products .fc_woo_product .fc_woo_product_info .price .screen-reader-text{border:0;clip:rect(1px,1px,1px,1px);clip-path:inset(50%);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;white-space:nowrap;width:1px}.fc_woo_products .fc_woo_product .fc_woo_product_info .price del{color:#9aa3aa;text-decoration-thickness:1px}.fc_woo_products .fc_woo_product .fc_woo_product_info .price ins{font-weight:600;text-decoration:none}.fc_woo_products .fc_woo_product .fc_woo_product_info .description,.fc_woo_products .fc_woo_product .fc_woo_product_info .description p{line-height:1.5}.fc_woo_products .fc_woo_product .fc_woo_product_info .add-to-cart-btn{color:#202020;display:inline-block;font-size:14px;font-weight:600}.fc_woo_products.template-layout-2{grid-template-columns:1fr}.fc_woo_products.template-layout-2 .fc_woo_product{align-items:center;display:flex}.fc_woo_products.template-layout-2 .fc_woo_product.no-image{display:block}.fc_woo_products.template-layout-2 .fc_woo_product.no-image .fc_woo_product_info{padding:0;width:100%}.fc_woo_products.template-layout-2 .fc_woo_product .fc_woo_product_img{flex:1;height:100%;margin:0;padding-right:10px;width:45%}.fc_woo_products.template-layout-2 .fc_woo_product .fc_woo_product_info{flex:1;padding:10px;width:55%}.fc_woo_products.template-layout-2 .fc_woo_product .fc_woo_product_info .price{margin-bottom:25px}.fc_woo_products.template-layout-2 .fc_woo_product .fc_woo_product_info .add-to-cart-btn{background:#2a363d;color:#fff;padding:15px;text-align:center;width:100%}.fc_woo_products.template-layout-3 .fc_woo_product{text-align:center}.fc_woo_products.template-layout-3 .fc_woo_product .fc_woo_product_img,.fc_woo_products.template-layout-3 .fc_woo_product .fc_woo_product_img img{border-radius:6px}.fc_woo_products.template-layout-3 .fc_woo_product .fc_woo_product_info .title{text-align:center}.show-setting-control-box{position:relative}.show-setting-control-box .components-base-control:last-child{margin-bottom:24px}.show-setting-control-box .show-setting-dropdown{position:absolute;right:0;top:-4px}.show-setting-control-box .show-setting-dropdown button{background:none;color:#000;display:block;height:auto;margin:0;padding:0}.show-setting-control-box .show-setting-dropdown button:hover,.show-setting-control-box .show-setting-dropdown button:hover:not(:disabled){background:none;color:#000}.show-setting-control-box .show-setting-dropdown button svg{display:block}.show-setting-control-box.select-layout{margin-bottom:24px}.show-setting-control-box.select-layout>p{display:block;font-size:11px;font-weight:500;line-height:1.4;margin-bottom:8px;padding:0;text-transform:uppercase}.show-setting-control-box.select-layout .show-setting-dropdown{align-items:center;display:flex;position:relative;top:0}.show-setting-control-box.select-layout .show-setting-dropdown>img{border:1px solid #eff1ff;border-radius:2px;cursor:pointer;display:block;margin-top:4px;object-fit:cover;padding:5px;width:80px}.show-setting-control-box.select-layout .show-setting-dropdown p{display:inline-block;font-size:11px;font-weight:500;line-height:1.4;margin-bottom:8px;margin-top:0;padding:0;text-transform:uppercase}.show-setting-control-box.select-layout .show-setting-dropdown .components-button{position:absolute;right:0;top:-27px}.fc-latest-products-content-color-settings,.fc-latest-products-settings{box-sizing:border-box;min-width:0;overflow-x:hidden;width:100%}.fc-latest-products-content-color-settings .components-base-control,.fc-latest-products-content-color-settings .components-base-control__field,.fc-latest-products-content-color-settings .components-input-control,.fc-latest-products-content-color-settings .components-input-control__container,.fc-latest-products-content-color-settings .components-select-control__input,.fc-latest-products-content-color-settings .components-text-control__input,.fc-latest-products-content-color-settings input,.fc-latest-products-content-color-settings select,.fc-latest-products-settings .components-base-control,.fc-latest-products-settings .components-base-control__field,.fc-latest-products-settings .components-input-control,.fc-latest-products-settings .components-input-control__container,.fc-latest-products-settings .components-select-control__input,.fc-latest-products-settings .components-text-control__input,.fc-latest-products-settings input,.fc-latest-products-settings select{box-sizing:border-box;max-width:100%;min-width:0;width:100%}.fc-layout-picker{max-width:280px;min-width:250px}.fc-layout-picker .fc-layout-picker-grid{display:grid;gap:8px;grid-template-columns:repeat(3,minmax(0,1fr))}.fc-layout-picker .fc-layout-picker-option{background:#fff;border:1px solid #dfe3ea;border-radius:6px;cursor:pointer;margin:0;padding:6px;text-align:center;transition:border-color .2s ease,box-shadow .2s ease;width:100%}.fc-layout-picker .fc-layout-picker-option:hover{border-color:#3858e9}.fc-layout-picker .fc-layout-picker-option.is-active{border-color:#3858e9;box-shadow:inset 0 0 0 1px #3858e9}.fc-layout-picker .fc-layout-picker-option img{border-radius:4px;display:block;height:auto;margin-bottom:6px;width:100%}.fc-layout-picker .fc-layout-picker-option span{color:#1e1e1e;display:block;font-size:11px;line-height:1.2} +.fc-cond-section{background:#ffffd7;border:1px dashed #d3d6db;padding:10px 0;position:relative}.fc-cond-section:before{background:#ffffd7;border-radius:2px;color:#757575;content:"Conditional";font-size:10px;font-weight:600;left:12px;letter-spacing:.5px;line-height:20px;padding:0 6px;position:absolute;text-transform:uppercase;top:-10px}.components-panel__body .components-form-token-field{margin-bottom:8px} +.fcrm-has-condition:not(.is-selected){outline:1px dashed #c8a415;outline-offset:2px;position:relative}.fcrm-has-condition:not(.is-selected):after{background:#fff8d6;border-radius:2px;color:#8a7000;content:"Conditional";font-size:10px;font-weight:600;letter-spacing:.5px;line-height:20px;padding:0 6px;pointer-events:none;position:absolute;right:12px;text-transform:uppercase;top:-10px;z-index:1} diff --git a/wp-content/plugins/fluent-crm/assets/guten-editor/index.js b/wp-content/plugins/fluent-crm/assets/guten-editor/index.js new file mode 100644 index 0000000..7868182 --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/guten-editor/index.js @@ -0,0 +1 @@ +(()=>{"use strict";var e={n:t=>{var n=t&&t.__esModule?()=>t.default:()=>t;return e.d(n,{a:n}),n},d:(t,n)=>{for(var o in n)e.o(n,o)&&!e.o(t,o)&&Object.defineProperty(t,o,{enumerable:!0,get:n[o]})},o:(e,t)=>Object.prototype.hasOwnProperty.call(e,t)};const t=window.wp.plugins,n=window.wp.data,o=window.wp.hooks,r=window.wp.blocks,i=window.wp.element,a=window.ReactJSXRuntime,{InspectorControls:s,PanelColorSettings:l,MediaUpload:c,MediaUploadCheck:d}=wp.blockEditor,{__}=wp.i18n,{useState:u,useEffect:m,useRef:p}=wp.element,{PanelBody:f,PanelRow:g,ToggleControl:h,SelectControl:y,ComboboxControl:_,Spinner:I}=wp.components,b=_||wp.components.__experimentalComboboxControl,w=e=>{const{attributes:{productId:t,showDescription:n,showPrice:o,template:r,backgroundColor:i,contentColor:c,pricingColor:d},setAttributes:_}=e,[w,x]=u([]),[v,C]=u(t?t.toString():null),[S,N]=u(""),[j,M]=u(!1),[k,T]=u(!1),E=p(null),D=wp.apiFetch,{addQueryArgs:A}=wp.url;m(()=>{t&&(C(t.toString()),P(t))},[t]),m(()=>()=>{E.current&&clearTimeout(E.current)},[]);const L=e=>{M(!0),D({path:A("wc/store/products",{per_page:20,...e})}).then(e=>{x(e)}).catch(e=>{T(!0)}).finally(()=>{M(!1)})},P=e=>{e&&D({path:A("wc/store/products/"+e)}).then(e=>{N(e.name||"")}).catch(()=>{N(""),T(!0)})},B=e=>{if(!e)return;C(e);const t=w.find(t=>t.id.toString()===e);t&&N(t.name||""),_({productId:e})},R=w.map(e=>({value:e.id.toString(),label:e.name}));return v&&S&&!R.find(e=>e.value===v)&&R.unshift({value:v,label:S}),t?(0,a.jsxs)(s,{children:[(0,a.jsx)(f,{title:__("Template Settings"),initialOpen:!0,children:(0,a.jsx)(g,{children:(0,a.jsxs)("div",{className:"fluent-singleProduct-template-settings",children:[(0,a.jsxs)("div",{style:{marginBottom:"12px"},children:[b?(0,a.jsx)(b,{__next40pxDefaultSize:!0,__nextHasNoMarginBottom:!0,label:__("Select Product"),value:v,options:R,onChange:B,onFilterValueChange:e=>{E.current&&clearTimeout(E.current),E.current=setTimeout(()=>{L(e?{search:e}:{})},1200)},onFocus:()=>{w.length||L()},placeholder:__("Search product"),expandOnFocus:!0}):(0,a.jsx)(y,{__next40pxDefaultSize:!0,__nextHasNoMarginBottom:!0,label:__("Select Product"),value:v,options:R,onChange:B}),j&&(0,a.jsx)("div",{style:{marginTop:"8px"},children:(0,a.jsx)(I,{})})]}),(0,a.jsx)(y,{__next40pxDefaultSize:!0,__nextHasNoMarginBottom:!0,label:__("Design Template"),value:r,options:[{value:"left",name:"Image Left"},{value:"top",name:"Image Top"},{value:"none",name:"No Image"}].map(e=>({value:e.value,label:e.name})),onChange:e=>_({template:e})}),(0,a.jsx)(h,{__nextHasNoMarginBottom:!0,label:__("Show Description"),checked:n,onChange:()=>_({showDescription:!n})}),(0,a.jsx)(h,{__nextHasNoMarginBottom:!0,label:__("Show Price"),checked:o,onChange:()=>_({showPrice:!o})})]})})}),(0,a.jsx)("div",{className:"fluent-singleProduct-titleAndSubtitle-settings",children:(0,a.jsx)(l,{title:__("Customization"),colorSettings:[{value:c,onChange:e=>{_({contentColor:e})},label:__("Content Color")},{value:i,onChange:e=>{_({backgroundColor:e})},label:__("Background Color")},{value:d,onChange:e=>{_({pricingColor:e})},label:__("Pricing Color")}]})})]}):null},x=window.wp.i18n,v=window.wp.blockEditor;function C(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,o=Array(t);n2?n-2:0),r=2;r1?t-1:0),o=1;o1?n-1:0),r=1;r2&&void 0!==arguments[2]?arguments[2]:G;if(j&&j(e,null),!U(t))return e;let o=t.length;for(;o--;){let r=t[o];if("string"==typeof r){const e=n(r);e!==r&&(M(t)||(t[o]=e),r=e)}e[r]=!0}return e}function ae(e){for(let t=0;t/g),xe=D(/\${[\w\W]*/g),ve=D(/^data-[\-\w.\u00B7-\uFFFF]+$/),Ce=D(/^aria-[\-\w]+$/),Se=D(/^(?:(?:(?:f|ht)tps?|mailto|tel|callto|sms|cid|xmpp|matrix):|[^a-z]|[a-z+.\-]+(?:[^a-z+.\-:]|$))/i),Ne=D(/^(?:\w+script|data):/i),je=D(/[\u0000-\u0020\u00A0\u1680\u180E\u2000-\u2029\u205F\u3000]/g),Me=D(/^html$/i),ke=D(/^[a-z][.\w]*(-[.\w]+)+$/i),Te=D(/<[/\w!]/g),Ee=D(/<[/\w]/g),De=D(/<\/no(script|embed|frames)/i),Ae=D(/\/>/i),Le=function(){return"undefined"==typeof window?null:window},Pe=function(e,t,n,o){return X(e,t)&&U(e[t])?ie(o.base?se(o.base):{},e[t],o.transform):n};var Be=function e(){let t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:Le();const n=t=>e(t);if(n.version="3.4.11",n.removed=[],!t||!t.document||9!==t.document.nodeType||!t.Element)return n.isSupported=!1,n;let o=t.document;const r=o,i=r.currentScript;t.DocumentFragment;const a=t.HTMLTemplateElement,s=t.Node,l=t.Element,c=t.NodeFilter;void 0===t.NamedNodeMap&&(t.NamedNodeMap||t.MozNamedAttrMap),t.HTMLFormElement;const d=t.DOMParser,u=t.trustedTypes,m=l.prototype,p=le(m,"cloneNode"),f=le(m,"remove"),g=le(m,"nextSibling"),h=le(m,"childNodes"),y=le(m,"parentNode"),_=le(m,"shadowRoot"),I=le(m,"attributes"),b=s&&s.prototype?le(s.prototype,"nodeType"):null,w=s&&s.prototype?le(s.prototype,"nodeName"):null;if("function"==typeof a){const e=o.createElement("template");e.content&&e.content.ownerDocument&&(o=e.content.ownerDocument)}let x,v,C="",S=!1,j=0;const M=function(){if(j>0)throw ne('A configured TRUSTED_TYPES_POLICY callback (createHTML or createScriptURL) must not call DOMPurify.sanitize, as that causes infinite recursion. Do not pass a policy whose callbacks wrap DOMPurify as TRUSTED_TYPES_POLICY; see the "DOMPurify and Trusted Types" section of the README.')},k=function(e){M(),j++;try{return x.createHTML(e)}finally{j--}},T=o,L=T.implementation,P=T.createNodeIterator,B=T.createDocumentFragment,oe=T.getElementsByTagName,re=r.importNode;let ae={afterSanitizeAttributes:[],afterSanitizeElements:[],afterSanitizeShadowDOM:[],beforeSanitizeAttributes:[],beforeSanitizeElements:[],beforeSanitizeShadowDOM:[],uponSanitizeAttribute:[],uponSanitizeElement:[],uponSanitizeShadowNode:[]};n.isSupported="function"==typeof N&&"function"==typeof y&&L&&void 0!==L.createHTMLDocument;const Be=be,Re=we,ze=xe,He=ve,Oe=Ce,Fe=Ne,Ue=je,Ge=ke;let Ze=Se,Ve=null;const We=ie({},[...ce,...de,...ue,...pe,...ge]);let Qe=null;const Ye=ie({},[...he,...ye,..._e,...Ie]);let Je=Object.seal(A(null,{tagNameCheck:{writable:!0,configurable:!1,enumerable:!0,value:null},attributeNameCheck:{writable:!0,configurable:!1,enumerable:!0,value:null},allowCustomizedBuiltInElements:{writable:!0,configurable:!1,enumerable:!0,value:!1}})),$e=null,qe=null;const Ke=Object.seal(A(null,{tagCheck:{writable:!0,configurable:!1,enumerable:!0,value:null},attributeCheck:{writable:!0,configurable:!1,enumerable:!0,value:null}}));let Xe=!0,et=!0,tt=!1,nt=!0,ot=!1,rt=!0,it=!1,at=!1,st=null,lt=null,ct=!1,dt=!1,ut=!1,mt=!1,pt=!0,ft=!1;const gt="user-content-";let ht=!0,yt=!1,_t={},It=null;const bt=ie({},["annotation-xml","audio","colgroup","desc","foreignobject","head","iframe","math","mi","mn","mo","ms","mtext","noembed","noframes","noscript","plaintext","script","selectedcontent","style","svg","template","thead","title","video","xmp"]);let wt=null;const xt=ie({},["audio","video","img","source","image","track"]);let vt=null;const Ct=ie({},["alt","class","for","id","label","name","pattern","placeholder","role","summary","title","value","style","xmlns"]),St="http://www.w3.org/1998/Math/MathML",Nt="http://www.w3.org/2000/svg",jt="http://www.w3.org/1999/xhtml";let Mt=jt,kt=!1,Tt=null;const Et=ie({},[St,Nt,jt],Z),Dt=E(["mi","mo","mn","ms","mtext"]);let At=ie({},Dt);const Lt=E(["annotation-xml"]);let Pt=ie({},Lt);const Bt=ie({},["title","style","font","a","script"]);let Rt=null;const zt=["application/xhtml+xml","text/html"];let Ht=null,Ot=null;const Ft=o.createElement("form"),Ut=function(e){return e instanceof RegExp||e instanceof Function},Gt=function(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};if(Ot&&Ot===e)return;e&&"object"==typeof e||(e={}),e=se(e),Rt=-1===zt.indexOf(e.PARSER_MEDIA_TYPE)?"text/html":e.PARSER_MEDIA_TYPE,Ht="application/xhtml+xml"===Rt?Z:G,Ve=Pe(e,"ALLOWED_TAGS",We,{transform:Ht}),Qe=Pe(e,"ALLOWED_ATTR",Ye,{transform:Ht}),Tt=Pe(e,"ALLOWED_NAMESPACES",Et,{transform:Z}),vt=Pe(e,"ADD_URI_SAFE_ATTR",Ct,{transform:Ht,base:Ct}),wt=Pe(e,"ADD_DATA_URI_TAGS",xt,{transform:Ht,base:xt}),It=Pe(e,"FORBID_CONTENTS",bt,{transform:Ht}),$e=Pe(e,"FORBID_TAGS",se({}),{transform:Ht}),qe=Pe(e,"FORBID_ATTR",se({}),{transform:Ht}),_t=!!X(e,"USE_PROFILES")&&(e.USE_PROFILES&&"object"==typeof e.USE_PROFILES?se(e.USE_PROFILES):e.USE_PROFILES),Xe=!1!==e.ALLOW_ARIA_ATTR,et=!1!==e.ALLOW_DATA_ATTR,tt=e.ALLOW_UNKNOWN_PROTOCOLS||!1,nt=!1!==e.ALLOW_SELF_CLOSE_IN_ATTR,ot=e.SAFE_FOR_TEMPLATES||!1,rt=!1!==e.SAFE_FOR_XML,it=e.WHOLE_DOCUMENT||!1,dt=e.RETURN_DOM||!1,ut=e.RETURN_DOM_FRAGMENT||!1,mt=e.RETURN_TRUSTED_TYPE||!1,ct=e.FORCE_BODY||!1,pt=!1!==e.SANITIZE_DOM,ft=e.SANITIZE_NAMED_PROPS||!1,ht=!1!==e.KEEP_CONTENT,yt=e.IN_PLACE||!1,Ze=function(e){try{return te(e,""),!0}catch(e){return!1}}(e.ALLOWED_URI_REGEXP)?e.ALLOWED_URI_REGEXP:Se,Mt="string"==typeof e.NAMESPACE?e.NAMESPACE:jt,At=X(e,"MATHML_TEXT_INTEGRATION_POINTS")&&e.MATHML_TEXT_INTEGRATION_POINTS&&"object"==typeof e.MATHML_TEXT_INTEGRATION_POINTS?se(e.MATHML_TEXT_INTEGRATION_POINTS):ie({},Dt),Pt=X(e,"HTML_INTEGRATION_POINTS")&&e.HTML_INTEGRATION_POINTS&&"object"==typeof e.HTML_INTEGRATION_POINTS?se(e.HTML_INTEGRATION_POINTS):ie({},Lt);const t=X(e,"CUSTOM_ELEMENT_HANDLING")&&e.CUSTOM_ELEMENT_HANDLING&&"object"==typeof e.CUSTOM_ELEMENT_HANDLING?se(e.CUSTOM_ELEMENT_HANDLING):A(null);if(Je=A(null),X(t,"tagNameCheck")&&Ut(t.tagNameCheck)&&(Je.tagNameCheck=t.tagNameCheck),X(t,"attributeNameCheck")&&Ut(t.attributeNameCheck)&&(Je.attributeNameCheck=t.attributeNameCheck),X(t,"allowCustomizedBuiltInElements")&&"boolean"==typeof t.allowCustomizedBuiltInElements&&(Je.allowCustomizedBuiltInElements=t.allowCustomizedBuiltInElements),D(Je),ot&&(et=!1),ut&&(dt=!0),_t&&(Ve=ie({},ge),Qe=A(null),!0===_t.html&&(ie(Ve,ce),ie(Qe,he)),!0===_t.svg&&(ie(Ve,de),ie(Qe,ye),ie(Qe,Ie)),!0===_t.svgFilters&&(ie(Ve,ue),ie(Qe,ye),ie(Qe,Ie)),!0===_t.mathMl&&(ie(Ve,pe),ie(Qe,_e),ie(Qe,Ie))),Ke.tagCheck=null,Ke.attributeCheck=null,X(e,"ADD_TAGS")&&("function"==typeof e.ADD_TAGS?Ke.tagCheck=e.ADD_TAGS:U(e.ADD_TAGS)&&(Ve===We&&(Ve=se(Ve)),ie(Ve,e.ADD_TAGS,Ht))),X(e,"ADD_ATTR")&&("function"==typeof e.ADD_ATTR?Ke.attributeCheck=e.ADD_ATTR:U(e.ADD_ATTR)&&(Qe===Ye&&(Qe=se(Qe)),ie(Qe,e.ADD_ATTR,Ht))),X(e,"ADD_URI_SAFE_ATTR")&&U(e.ADD_URI_SAFE_ATTR)&&ie(vt,e.ADD_URI_SAFE_ATTR,Ht),X(e,"FORBID_CONTENTS")&&U(e.FORBID_CONTENTS)&&(It===bt&&(It=se(It)),ie(It,e.FORBID_CONTENTS,Ht)),X(e,"ADD_FORBID_CONTENTS")&&U(e.ADD_FORBID_CONTENTS)&&(It===bt&&(It=se(It)),ie(It,e.ADD_FORBID_CONTENTS,Ht)),ht&&(Ve["#text"]=!0),it&&ie(Ve,["html","head","body"]),Ve.table&&(ie(Ve,["tbody"]),delete $e.tbody),e.TRUSTED_TYPES_POLICY){if("function"!=typeof e.TRUSTED_TYPES_POLICY.createHTML)throw ne('TRUSTED_TYPES_POLICY configuration option must provide a "createHTML" hook.');if("function"!=typeof e.TRUSTED_TYPES_POLICY.createScriptURL)throw ne('TRUSTED_TYPES_POLICY configuration option must provide a "createScriptURL" hook.');const t=x;x=e.TRUSTED_TYPES_POLICY;try{C=k("")}catch(e){throw x=t,e}}else null===e.TRUSTED_TYPES_POLICY?(x=void 0,C=""):(void 0===x&&(S||(v=function(e,t){if("object"!=typeof e||"function"!=typeof e.createPolicy)return null;let n=null;const o="data-tt-policy-suffix";t&&t.hasAttribute(o)&&(n=t.getAttribute(o));const r="dompurify"+(n?"#"+n:"");try{return e.createPolicy(r,{createHTML:e=>e,createScriptURL:e=>e})}catch(e){return console.warn("TrustedTypes policy "+r+" could not be created."),null}}(u,i),S=!0),x=v),x&&"string"==typeof C&&(C=k("")));E&&E(e),Ot=e},Zt=ie({},[...de,...ue,...me]),Vt=ie({},[...pe,...fe]),Wt=function(e){O(n.removed,{element:e});try{y(e).removeChild(e)}catch(t){if(f(e),!y(e))throw ne("a node selected for removal could not be detached from its tree and cannot be safely returned; refusing to sanitize in place")}},Qt=function(e){const t=h(e);if(t){const e=[];R(t,t=>{O(e,t)}),R(e,e=>{try{f(e)}catch(e){}})}const n=I(e);if(n)for(let t=n.length-1;t>=0;--t){const o=n[t],r=o&&o.name;if("string"==typeof r)try{e.removeAttribute(r)}catch(e){}}},Yt=function(e,t){try{O(n.removed,{attribute:t.getAttributeNode(e),from:t})}catch(e){O(n.removed,{attribute:null,from:t})}if(t.removeAttribute(e),"is"===e)if(dt||ut)try{Wt(t)}catch(e){}else try{t.setAttribute(e,"")}catch(e){}},Jt=function(e){const t=I(e);if(t)for(let n=t.length-1;n>=0;--n){const o=t[n],r=o&&o.name;if("string"==typeof r&&!Qe[Ht(r)])try{e.removeAttribute(r)}catch(e){}}},$t=function(e){let t=null,n=null;if(ct)e=""+e;else{const t=V(e,/^[\r\n\t ]+/);n=t&&t[0]}"application/xhtml+xml"===Rt&&Mt===jt&&(e=''+e+"");const r=x?k(e):e;if(Mt===jt)try{t=(new d).parseFromString(r,Rt)}catch(e){}if(!t||!t.documentElement){t=L.createDocument(Mt,"template",null);try{t.documentElement.innerHTML=kt?C:r}catch(e){}}const i=t.body||t.documentElement;return e&&n&&i.insertBefore(o.createTextNode(n),i.childNodes[0]||null),Mt===jt?oe.call(t,it?"html":"body")[0]:it?t.documentElement:i},qt=function(e){return P.call(e.ownerDocument||e,e,c.SHOW_ELEMENT|c.SHOW_COMMENT|c.SHOW_TEXT|c.SHOW_PROCESSING_INSTRUCTION|c.SHOW_CDATA_SECTION,null)},Kt=function(e){return e=W(e,Be," "),e=W(e,Re," "),W(e,ze," ")},Xt=function(e){var t;e.normalize();const n=P.call(e.ownerDocument||e,e,c.SHOW_TEXT|c.SHOW_COMMENT|c.SHOW_CDATA_SECTION|c.SHOW_PROCESSING_INSTRUCTION,null);let o=n.nextNode();for(;o;)o.data=Kt(o.data),o=n.nextNode();const r=null===(t=e.querySelectorAll)||void 0===t?void 0:t.call(e,"template");r&&R(r,e=>{tn(e.content)&&Xt(e.content)})},en=function(e){const t=w?w(e):null;return"string"==typeof t&&"form"===Ht(t)&&("string"!=typeof e.nodeName||"string"!=typeof e.textContent||"function"!=typeof e.removeChild||e.attributes!==I(e)||"function"!=typeof e.removeAttribute||"function"!=typeof e.setAttribute||"string"!=typeof e.namespaceURI||"function"!=typeof e.insertBefore||"function"!=typeof e.hasChildNodes||e.nodeType!==b(e)||e.childNodes!==h(e))},tn=function(e){if(!b||"object"!=typeof e||null===e)return!1;try{return 11===b(e)}catch(e){return!1}},nn=function(e){if(!b||"object"!=typeof e||null===e)return!1;try{return"number"==typeof b(e)}catch(e){return!1}};function on(e,t,o){0!==e.length&&R(e,e=>{e.call(n,t,o,Ot)})}const rn=function(e){if(on(ae.beforeSanitizeElements,e,null),en(e))return Wt(e),!0;const t=Ht(w?w(e):e.nodeName);if(on(ae.uponSanitizeElement,e,{tagName:t,allowedTags:Ve}),function(e,t){return!!(rt&&e.hasChildNodes()&&!nn(e.firstElementChild)&&te(Te,e.textContent)&&te(Te,e.innerHTML))||!(!rt||e.namespaceURI!==jt||"style"!==t||!nn(e.firstElementChild))||7===e.nodeType||!(!rt||8!==e.nodeType||!te(Ee,e.data))}(e,t))return Wt(e),!0;if($e[t]||!(Ke.tagCheck instanceof Function&&Ke.tagCheck(t))&&!Ve[t])return function(e,t){if(!$e[t]&&ln(t)){if(Je.tagNameCheck instanceof RegExp&&te(Je.tagNameCheck,t))return!1;if(Je.tagNameCheck instanceof Function&&Je.tagNameCheck(t))return!1}if(ht&&!It[t]){const t=y(e),n=h(e);if(n&&t)for(let o=n.length-1;o>=0;--o){const r=yt?n[o]:p(n[o],!0);t.insertBefore(r,g(e))}}return Wt(e),!0}(e,t);if(1===(b?b(e):e.nodeType)&&!function(e){let t=y(e);t&&t.tagName||(t={namespaceURI:Mt,tagName:"template"});const n=G(e.tagName),o=G(t.tagName);return!!Tt[e.namespaceURI]&&(e.namespaceURI===Nt?function(e,t,n){return t.namespaceURI===jt?"svg"===e:t.namespaceURI===St?"svg"===e&&("annotation-xml"===n||At[n]):Boolean(Zt[e])}(n,t,o):e.namespaceURI===St?function(e,t,n){return t.namespaceURI===jt?"math"===e:t.namespaceURI===Nt?"math"===e&&Pt[n]:Boolean(Vt[e])}(n,t,o):e.namespaceURI===jt?function(e,t,n){return!(t.namespaceURI===Nt&&!Pt[n])&&!(t.namespaceURI===St&&!At[n])&&!Vt[e]&&(Bt[e]||!Zt[e])}(n,t,o):!("application/xhtml+xml"!==Rt||!Tt[e.namespaceURI]))}(e))return Wt(e),!0;if(("noscript"===t||"noembed"===t||"noframes"===t)&&te(De,e.innerHTML))return Wt(e),!0;if(ot&&3===e.nodeType){const t=Kt(e.textContent);e.textContent!==t&&(O(n.removed,{element:e.cloneNode()}),e.textContent=t)}return on(ae.afterSanitizeElements,e,null),!1},an=function(e,t,n){if(qe[t])return!1;if(pt&&("id"===t||"name"===t)&&(n in o||n in Ft))return!1;const r=Qe[t]||Ke.attributeCheck instanceof Function&&Ke.attributeCheck(t,e);if(et&&te(He,t));else if(Xe&&te(Oe,t));else if(r){if(vt[t]);else if(te(Ze,W(n,Ue,"")));else if("src"!==t&&"xlink:href"!==t&&"href"!==t||"script"===e||0!==Q(n,"data:")||!wt[e])if(tt&&!te(Fe,W(n,Ue,"")));else if(n)return!1}else if(!(ln(e)&&(Je.tagNameCheck instanceof RegExp&&te(Je.tagNameCheck,e)||Je.tagNameCheck instanceof Function&&Je.tagNameCheck(e))&&(Je.attributeNameCheck instanceof RegExp&&te(Je.attributeNameCheck,t)||Je.attributeNameCheck instanceof Function&&Je.attributeNameCheck(t,e))||"is"===t&&Je.allowCustomizedBuiltInElements&&(Je.tagNameCheck instanceof RegExp&&te(Je.tagNameCheck,n)||Je.tagNameCheck instanceof Function&&Je.tagNameCheck(n))))return!1;return!0},sn=ie({},["annotation-xml","color-profile","font-face","font-face-format","font-face-name","font-face-src","font-face-uri","missing-glyph"]),ln=function(e){return!sn[G(e)]&&te(Ge,e)},cn=function(e,t,n,o){if(x&&"object"==typeof u&&"function"==typeof u.getAttributeType&&!n)switch(u.getAttributeType(e,t)){case"TrustedHTML":return k(o);case"TrustedScriptURL":return function(e){M(),j++;try{return x.createScriptURL(e)}finally{j--}}(o)}return o},dn=function(e,t,o,r){try{o?e.setAttributeNS(o,t,r):e.setAttribute(t,r),en(e)?Wt(e):H(n.removed)}catch(n){Yt(t,e)}},un=function(e){on(ae.beforeSanitizeAttributes,e,null);const t=e.attributes;if(!t||en(e))return;const n={attrName:"",attrValue:"",keepAttr:!0,allowedAttributes:Qe,forceKeepAttr:void 0};let o=t.length;const r=Ht(e.nodeName);for(;o--;){const i=t[o],a=i.name,s=i.namespaceURI,l=i.value,c=Ht(a),d=l;let u="value"===a?d:Y(d);n.attrName=c,n.attrValue=u,n.keepAttr=!0,n.forceKeepAttr=void 0,on(ae.uponSanitizeAttribute,e,n),u=n.attrValue,!ft||"id"!==c&&"name"!==c||0===Q(u,gt)||(Yt(a,e),u=gt+u),rt&&te(/((--!?|])>)|<\/(style|script|title|xmp|textarea|noscript|iframe|noembed|noframes)/i,u)||"attributename"===c&&V(u,"href")?Yt(a,e):n.forceKeepAttr||(!n.keepAttr||!nt&&te(Ae,u)?Yt(a,e):(ot&&(u=Kt(u)),an(r,c,u)?(u=cn(r,c,s,u),u!==d&&dn(e,a,s,u)):Yt(a,e)))}on(ae.afterSanitizeAttributes,e,null)},mn=function(e){let t=null;const n=qt(e);for(on(ae.beforeSanitizeShadowDOM,e,null);t=n.nextNode();)if(on(ae.uponSanitizeShadowNode,t,null),rn(t),un(t),tn(t.content)&&mn(t.content),1===(b?b(t):t.nodeType)){const e=_(t);tn(e)&&(pn(e),mn(e))}on(ae.afterSanitizeShadowDOM,e,null)},pn=function(e){const t=[{node:e,shadow:null}];for(;t.length>0;){const e=t.pop();if(e.shadow){mn(e.shadow);continue}const n=e.node,o=1===(b?b(n):n.nodeType),r=h(n);if(r)for(let e=r.length-1;e>=0;--e)t.push({node:r[e],shadow:null});if(o){const e=w?w(n):null;if("string"==typeof e&&"template"===Ht(e)){const e=n.content;tn(e)&&t.push({node:e,shadow:null})}}if(o){const e=_(n);tn(e)&&t.push({node:null,shadow:e},{node:e,shadow:null})}}};return n.sanitize=function(e){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},o=null,i=null,a=null,s=null;if(kt=!e,kt&&(e="\x3c!--\x3e"),"string"!=typeof e&&!nn(e)&&"string"!=typeof(e=function(e){switch(typeof e){case"string":return e;case"number":return J(e);case"boolean":return $(e);case"bigint":return q?q(e):"0";case"symbol":return K?K(e):"Symbol()";case"undefined":default:return ee(e);case"function":case"object":{if(null===e)return ee(e);const t=e,n=le(t,"toString");if("function"==typeof n){const e=n(t);return"string"==typeof e?e:ee(e)}return ee(e)}}}(e)))throw ne("dirty is not a string, aborting");if(!n.isSupported)return e;at?(Ve=st,Qe=lt):Gt(t),(ae.uponSanitizeElement.length>0||ae.uponSanitizeAttribute.length>0)&&(Ve=se(Ve)),ae.uponSanitizeAttribute.length>0&&(Qe=se(Qe)),n.removed=[];const l=yt&&"string"!=typeof e&&nn(e);if(l){const t=w?w(e):e.nodeName;if("string"==typeof t){const e=Ht(t);if(!Ve[e]||$e[e])throw ne("root node is forbidden and cannot be sanitized in-place")}if(en(e))throw ne("root node is clobbered and cannot be sanitized in-place");try{pn(e)}catch(t){throw Qt(e),t}}else if(nn(e))o=$t("\x3c!----\x3e"),i=o.ownerDocument.importNode(e,!0),1===i.nodeType&&"BODY"===i.nodeName||"HTML"===i.nodeName?o=i:o.appendChild(i),pn(i);else{if(!dt&&!ot&&!it&&-1===e.indexOf("<"))return x&&mt?k(e):e;if(o=$t(e),!o)return dt?null:mt?C:""}o&&ct&&Wt(o.firstChild);const c=qt(l?e:o);try{for(;a=c.nextNode();)rn(a),un(a),tn(a.content)&&mn(a.content)}catch(t){throw l&&Qt(e),t}if(l)return R(n.removed,e=>{e.element&&function(e){const t=[e];for(;t.length>0;){const e=t.pop();1===(b?b(e):e.nodeType)&&Jt(e);const n=h(e);if(n)for(let e=n.length-1;e>=0;--e)t.push(n[e])}}(e.element)}),ot&&Xt(e),e;if(dt){if(ot&&Xt(o),ut)for(s=B.call(o.ownerDocument);o.firstChild;)s.appendChild(o.firstChild);else s=o;return(Qe.shadowroot||Qe.shadowrootmode)&&(s=re.call(r,s,!0)),s}let d=it?o.outerHTML:o.innerHTML;return it&&Ve["!doctype"]&&o.ownerDocument&&o.ownerDocument.doctype&&o.ownerDocument.doctype.name&&te(Me,o.ownerDocument.doctype.name)&&(d="\n"+d),ot&&(d=Kt(d)),x&&mt?k(d):d},n.setConfig=function(){Gt(arguments.length>0&&void 0!==arguments[0]?arguments[0]:{}),at=!0,st=Ve,lt=Qe},n.clearConfig=function(){Ot=null,at=!1,st=null,lt=null,x=v,C=""},n.isValidAttribute=function(e,t,n){Ot||Gt({});const o=Ht(e),r=Ht(t);return an(o,r,n)},n.addHook=function(e,t){"function"==typeof t&&X(ae,e)&&O(ae[e],t)},n.removeHook=function(e,t){if(X(ae,e)){if(void 0!==t){const n=z(ae[e],t);return-1===n?void 0:F(ae[e],n,1)[0]}return H(ae[e])}},n.removeHooks=function(e){X(ae,e)&&(ae[e]=[])},n.removeAllHooks=function(){ae={afterSanitizeAttributes:[],afterSanitizeElements:[],afterSanitizeShadowDOM:[],beforeSanitizeAttributes:[],beforeSanitizeElements:[],beforeSanitizeShadowDOM:[],uponSanitizeAttribute:[],uponSanitizeElement:[],uponSanitizeShadowNode:[]}},n}();const Re={ALLOWED_TAGS:["span","bdi","bdo","del","ins","small","strong","em","sup","sub"],ALLOWED_ATTR:["class","dir"],ALLOW_DATA_ATTR:!1},ze=(e,t="Free")=>{const n=Be.sanitize(e?String(e):t,Re);if("undefined"!=typeof document){const e=document.createElement("div");return e.innerHTML=n,e.querySelectorAll(".screen-reader-text").forEach(e=>e.remove()),e.querySelectorAll("del[aria-hidden], ins[aria-hidden]").forEach(e=>e.removeAttribute("aria-hidden")),e.innerHTML}return n.replace(/]*\bclass=(["'])[^"']*\bscreen-reader-text\b[^"']*\1)[^>]*>[\s\S]*?<\/span>/gi,"").replace(/<(del|ins)\b([^>]*)>/gi,(e,t,n)=>`<${t}${n.replace(/\saria-hidden=(["']).*?\1|\saria-hidden=[^\s>]*/gi,"")}>`)},{RadioControl:He,Spinner:Oe}=wp.components,{useState:Fe,useEffect:Ue}=wp.element,{__:Ge}=wp.i18n,Ze=e=>{const{attributes:{productId:t,showDescription:n,showPrice:o,buttonText:r,customImage:i,template:s,backgroundColor:l,contentColor:c,pricingColor:d},setAttributes:u}=e,{InnerBlocks:m}=wp.blockEditor,[p,f]=Fe([]),[g,h]=Fe(""),[y,_]=Fe(""),[I,b]=Fe({}),[w,x]=Fe(!1),[v,C]=Fe(!1),[S,N]=Fe(!1);Ue(()=>{t?T(t):k()},[t]);const j=wp.apiFetch,{addQueryArgs:M}=wp.url,k=e=>{C(!0),j({path:M("wc/store/products",{per_page:6,...e})}).then(e=>{f(e)}).catch(e=>{N(!0)}).finally(()=>{C(!1)})},T=e=>{x(!0),j({path:M("wc/store/products/"+e)}).then(e=>{b(e),u({productId:e.id}),x(!1)}).catch(e=>{N(!0)})},E={backgroundColor:l,color:c},D=["fcw_p","fcw_template_"+s,w?"fc_product_loading":""].filter(Boolean).join(" "),A=["fcw_search_box",w?"fc_product_loading":""].filter(Boolean).join(" ");return(0,a.jsxs)("div",{children:[w&&(0,a.jsxs)("div",{style:E,className:"fc_woo_loader",children:[(0,a.jsx)(Oe,{}),(0,a.jsx)("h3",{children:"Loading product"})]}),I.id&&t?(0,a.jsxs)("div",{style:E,className:D,children:["none"!=s&&(0,a.jsx)("div",{className:"fcw_image",children:(0,a.jsx)("img",{src:i||I&&I.images&&I.images.length&&I.images[0].src||""})}),(0,a.jsxs)("div",{className:"fcw_p_content",children:[(0,a.jsx)("h2",{style:{color:c},className:"fcw_p_title",dangerouslySetInnerHTML:{__html:I.name}}),n&&(0,a.jsx)("div",{style:{color:c},className:"fcw_p_desc",dangerouslySetInnerHTML:{__html:I.short_description}}),o&&(0,a.jsx)("div",{style:{color:d},className:"fcw_p_price",dangerouslySetInnerHTML:{__html:ze(I.price_html,Ge("Free"))}}),(0,a.jsx)("div",{className:"fcb_p_button",children:(0,a.jsx)(m,{template:[["core/buttons",{},[["core/button",{text:r,url:I.permalink,align:"left"}]]]],templateLock:"all"})})]})]}):(0,a.jsxs)("div",{className:A,children:[(0,a.jsx)("h4",{children:"Search and Select a Product"}),(0,a.jsx)("hr",{}),(0,a.jsxs)("div",{style:{marginBottom:"25px",display:"flex"},className:"fluent-single-product-search-bar",children:[(0,a.jsx)("div",{style:{width:"80%"},children:(0,a.jsx)("input",{placeholder:"product",style:{width:"100%",height:"30px"},value:g,onChange:e=>{h(e.target.value)},onKeyDown:e=>{"Enter"!==e.key&&""!==e.target.value||k({search:g})}})}),(0,a.jsx)("button",{style:{width:"20%",height:"30px"},onClick:()=>{k({search:g})},children:"Search"})]}),v?(0,a.jsx)("h2",{children:(0,a.jsx)(Oe,{})}):(0,a.jsx)("div",{className:"fcw_results",children:p&&p.length?(0,a.jsx)(He,{selected:y,options:p.map(e=>({value:e.id.toString(),label:e.name})),onChange:e=>{_(e)}}):(0,a.jsx)("div",{className:"fcw_products_not_found",children:(0,a.jsx)("h2",{children:"No products found!"})})}),(0,a.jsx)("div",{style:{marginTop:"20px"},className:"components-button is-primary",onClick:()=>{T(y)},children:"Done"})]})]})},{Fragment:Ve}=wp.element,We=e=>{const t=(0,v.useBlockProps)({className:"fluent-single-product-block"});return(0,a.jsxs)(Ve,{children:[(0,a.jsx)("div",{...t,children:(0,a.jsx)(Ze,{attributes:e.attributes,setAttributes:e.setAttributes})}),(0,a.jsx)(w,{attributes:e.attributes,setAttributes:e.setAttributes})]})},Qe=()=>{const e=v.useBlockProps.save();return(0,a.jsx)("div",{...e,children:(0,a.jsx)(v.InnerBlocks.Content,{})})},{__:Ye}=wp.i18n,Je={productId:{type:"number",default:null},showDescription:{type:"boolean",default:!0},showPrice:{type:"boolean",default:!0},buttonText:{type:"string",default:Ye("Buy Now")},customImage:{type:"string",default:""},backgroundColor:{type:"string",default:"#fffeeb"},contentColor:{type:"string",default:""},pricingColor:{type:"string",default:""},template:{type:"string",default:"left"}},$e=wp.element.createElement,{__:qe}=wp.i18n,{registerBlockType:Ke}=wp.blocks,Xe=window.fcrmBlockEditorConfig?.modules||{},et=[{attributes:Je,save:()=>(0,a.jsx)("div",{children:(0,a.jsx)(v.InnerBlocks.Content,{})})}],tt=$e("svg",{width:20,height:20,viewBox:"0 0 24 24"},$e("path",{fill:"#7F54B3",d:"M5 4h14a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2h-8.2L7 20v-4H5a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2z"}),$e("text",{x:12,y:12.8,textAnchor:"middle",fontSize:6.5,fontFamily:"Arial, sans-serif",fontWeight:700,fill:"#fff"},"woo"));if(!1!==Xe.hasWooCommerce){const e={apiVersion:3,title:qe("Woo Product (Single)"),description:qe("Product Block For your Email"),category:"layout",icon:tt,keywords:[qe("product"),qe("woocommerce"),qe("card")],supports:{align:["wide","full"],html:!0},attributes:Je,deprecated:et,edit:We,save:Qe};Ke("fluentcrm/woo-product",e),Ke("fluent-crm/woo-product",{...e,supports:{...e.supports,inserter:!1}})}window.React;const nt="data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNDAzIiBoZWlnaHQ9IjM1NCIgdmlld0JveD0iMCAwIDQwMyAzNTQiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxyZWN0IHg9IjEiIHk9IjEiIHdpZHRoPSI0MDEiIGhlaWdodD0iMzUyIiByeD0iOSIgc3Ryb2tlPSIjRURFRUY0IiBzdHJva2Utd2lkdGg9IjIiLz4KPHJlY3QgeD0iNDQiIHk9IjIyNSIgd2lkdGg9IjMxNCIgaGVpZ2h0PSIxNiIgcng9IjgiIGZpbGw9IiNFREVFRjQiLz4KPHJlY3QgeD0iMjciIHk9IjI1NyIgd2lkdGg9IjM0OSIgaGVpZ2h0PSIxMCIgcng9IjUiIGZpbGw9IiNFREVFRjQiLz4KPHJlY3QgeD0iNzIiIHk9IjI4MCIgd2lkdGg9IjI1OSIgaGVpZ2h0PSIxMCIgcng9IjUiIGZpbGw9IiNFREVFRjQiLz4KPHJlY3QgeD0iMTczIiB5PSIzMTMiIHdpZHRoPSI1NyIgaGVpZ2h0PSIxNCIgcng9IjciIGZpbGw9IiNFREVFRjQiLz4KPHBhdGggZD0iTTAgMTBDMCA0LjQ3NzE2IDQuNDc3MTUgMCAxMCAwSDM5MUMzOTYuNTIzIDAgNDAxIDQuNDc3MTUgNDAxIDEwVjIwMEgwVjEwWiIgZmlsbD0iI0VERUVGNCIvPgo8L3N2Zz4K",ot="data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNDE5IiBoZWlnaHQ9IjIwMSIgdmlld0JveD0iMCAwIDQxOSAyMDEiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxyZWN0IHg9IjEiIHk9IjEiIHdpZHRoPSI0MTciIGhlaWdodD0iMTk5IiByeD0iOSIgc3Ryb2tlPSIjRURFRUY0IiBzdHJva2Utd2lkdGg9IjIiLz4KPHBhdGggZD0iTTAgMTBDMCA0LjQ3NzE1IDQuNDc3MTUgMCAxMCAwSDEzN1YyMDFIMTBDNC40NzcxNSAyMDEgMCAxOTYuNTIzIDAgMTkxVjEwWiIgZmlsbD0iI0VERUVGNCIvPgo8cmVjdCB4PSIxNjIiIHk9IjcyIiB3aWR0aD0iMjMwIiBoZWlnaHQ9IjE2IiByeD0iOCIgZmlsbD0iI0VERUVGNCIvPgo8cGF0aCBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGNsaXAtcnVsZT0iZXZlbm9kZCIgZD0iTTE4Mi45MjMgNTcuNDM0MkMxODAuOTI1IDYwLjIwMDEgMTc3LjY3MyA2MiAxNzQgNjJDMTcwLjMyNyA2MiAxNjcuMDc1IDYwLjIwMDEgMTY1LjA3NyA1Ny40MzQyQzE2Ni43MjcgNTQuMjEyOSAxNzAuMTAzIDUyLjAwNDMgMTc0IDUyQzE3Ny44OTcgNTIuMDA0MyAxODEuMjczIDU0LjIxMjkgMTgyLjkyMyA1Ny40MzQyWk0xODMuMzg0IDU4LjQ4MDJDMTg1LjAyMSA1Ni40Mjg4IDE4NiA1My44Mjg3IDE4NiA1MUMxODYgNDQuMzcyNiAxODAuNjI3IDM5IDE3NCAzOUMxNjcuMzczIDM5IDE2MiA0NC4zNzI2IDE2MiA1MUMxNjIgNTMuNDg1MyAxNjIuNzU2IDU1Ljc5NDEgMTY0LjA0OSA1Ny43MDkzQzE2NC4yMjggNTcuOTc0IDE2NC40MTcgNTguMjMxMSAxNjQuNjE2IDU4LjQ4MDFDMTY2LjgxNSA2MS4yMzQ5IDE3MC4yMDEgNjMgMTc0IDYzQzE3Ny43OTkgNjMgMTgxLjE4NSA2MS4yMzQ5IDE4My4zODQgNTguNDgwMlpNMTc0IDUxQzE3MS43OTEgNTEgMTcwIDQ5LjIwOTIgMTcwIDQ3QzE3MCA0NC43OTA5IDE3MS43OTEgNDMgMTc0IDQzQzE3Ni4yMDkgNDMgMTc4IDQ0Ljc5MDkgMTc4IDQ3QzE3OCA0OS4yMDkyIDE3Ni4yMDkgNTEgMTc0IDUxWiIgZmlsbD0iI0VERUVGNCIvPgo8cmVjdCB4PSIxOTAiIHk9IjQ5IiB3aWR0aD0iMzQiIGhlaWdodD0iNiIgcng9IjMiIGZpbGw9IiNFREVFRjQiLz4KPHJlY3QgeD0iMjU1IiB5PSI0OSIgd2lkdGg9IjU0IiBoZWlnaHQ9IjYiIHJ4PSIzIiBmaWxsPSIjRURFRUY0Ii8+CjxyZWN0IHg9IjIzNCIgeT0iNTEiIHdpZHRoPSIxMSIgaGVpZ2h0PSIyIiByeD0iMSIgZmlsbD0iI0VERUVGNCIvPgo8cmVjdCB4PSIxNjIiIHk9IjEwMyIgd2lkdGg9IjIzMCIgaGVpZ2h0PSIxMCIgcng9IjUiIGZpbGw9IiNFREVFRjQiLz4KPHJlY3QgeD0iMTYyIiB5PSIxMjEiIHdpZHRoPSIxNzAiIGhlaWdodD0iMTAiIHJ4PSI1IiBmaWxsPSIjRURFRUY0Ii8+CjxyZWN0IHg9IjE2MiIgeT0iMTQ4IiB3aWR0aD0iNTciIGhlaWdodD0iMTQiIHJ4PSI3IiBmaWxsPSIjRURFRUY0Ii8+Cjwvc3ZnPgo=",rt="data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNDU2IiBoZWlnaHQ9IjIwMSIgdmlld0JveD0iMCAwIDQ1NiAyMDEiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxyZWN0IHg9IjEiIHk9IjEiIHdpZHRoPSI0NTQiIGhlaWdodD0iMTk5IiByeD0iOSIgc3Ryb2tlPSIjRURFRUY0IiBzdHJva2Utd2lkdGg9IjIiLz4KPHJlY3QgeD0iMjgyIiB5PSIyNyIgd2lkdGg9IjE0NyIgaGVpZ2h0PSIxNDciIHJ4PSIxMCIgZmlsbD0iI0VERUVGNCIvPgo8cmVjdCB4PSIyNyIgeT0iNzIiIHdpZHRoPSIyMzAiIGhlaWdodD0iMTYiIHJ4PSI4IiBmaWxsPSIjRURFRUY0Ii8+CjxwYXRoIGZpbGwtcnVsZT0iZXZlbm9kZCIgY2xpcC1ydWxlPSJldmVub2RkIiBkPSJNNDcuOTIyOSA1Ny40MzQyQzQ1LjkyNSA2MC4yMDAxIDQyLjY3MjcgNjIgMzkgNjJDMzUuMzI3MyA2MiAzMi4wNzUgNjAuMjAwMSAzMC4wNzcxIDU3LjQzNDJDMzEuNzI3MyA1NC4yMTI5IDM1LjEwMjggNTIuMDA0MyAzOSA1MkM0Mi44OTcyIDUyLjAwNDMgNDYuMjcyNyA1NC4yMTI5IDQ3LjkyMjkgNTcuNDM0MlpNNDguMzgzOSA1OC40ODAyQzUwLjAyMTIgNTYuNDI4OCA1MSA1My44Mjg3IDUxIDUxQzUxIDQ0LjM3MjYgNDUuNjI3NCAzOSAzOSAzOUMzMi4zNzI2IDM5IDI3IDQ0LjM3MjYgMjcgNTFDMjcgNTMuNDg1MyAyNy43NTU1IDU1Ljc5NDEgMjkuMDQ5NCA1Ny43MDkzQzI5LjIyODIgNTcuOTc0IDI5LjQxNzMgNTguMjMxMSAyOS42MTYxIDU4LjQ4MDFDMzEuODE0OCA2MS4yMzQ5IDM1LjIwMTMgNjMgMzkgNjNDNDIuNzk4NyA2MyA0Ni4xODUxIDYxLjIzNDkgNDguMzgzOSA1OC40ODAyWk0zOSA1MUMzNi43OTA4IDUxIDM0Ljk5OTkgNDkuMjA5MiAzNC45OTk5IDQ3QzM0Ljk5OTkgNDQuNzkwOSAzNi43OTA4IDQzIDM5IDQzQzQxLjIwOTEgNDMgNDMgNDQuNzkwOSA0MyA0N0M0MyA0OS4yMDkyIDQxLjIwOTEgNTEgMzkgNTFaIiBmaWxsPSIjRURFRUY0Ii8+CjxyZWN0IHg9IjU1IiB5PSI0OSIgd2lkdGg9IjM0IiBoZWlnaHQ9IjYiIHJ4PSIzIiBmaWxsPSIjRURFRUY0Ii8+CjxyZWN0IHg9IjEyMCIgeT0iNDkiIHdpZHRoPSI1NCIgaGVpZ2h0PSI2IiByeD0iMyIgZmlsbD0iI0VERUVGNCIvPgo8cmVjdCB4PSI5OSIgeT0iNTEiIHdpZHRoPSIxMSIgaGVpZ2h0PSIyIiByeD0iMSIgZmlsbD0iI0VERUVGNCIvPgo8cmVjdCB4PSIyNyIgeT0iMTAzIiB3aWR0aD0iMjMwIiBoZWlnaHQ9IjEwIiByeD0iNSIgZmlsbD0iI0VERUVGNCIvPgo8cmVjdCB4PSIyNyIgeT0iMTIxIiB3aWR0aD0iMTcwIiBoZWlnaHQ9IjEwIiByeD0iNSIgZmlsbD0iI0VERUVGNCIvPgo8cmVjdCB4PSIyNyIgeT0iMTQ4IiB3aWR0aD0iNTciIGhlaWdodD0iMTQiIHJ4PSI3IiBmaWxsPSIjRURFRUY0Ii8+Cjwvc3ZnPgo=",it="data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNDE5IiBoZWlnaHQ9IjIwMSIgdmlld0JveD0iMCAwIDQxOSAyMDEiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxyZWN0IHg9IjEiIHk9IjEiIHdpZHRoPSI0MTciIGhlaWdodD0iMTk5IiByeD0iOSIgc3Ryb2tlPSIjRURFRUY0IiBzdHJva2Utd2lkdGg9IjIiLz4KPHBhdGggZD0iTTQxOSAxOTFDNDE5IDE5Ni41MjMgNDE0LjUyMyAyMDEgNDA5IDIwMUwyODIgMjAxTDI4MiAzLjI4MTg3ZS0wNkw0MDkgMS40Mzg0NmUtMDVDNDE0LjUyMyAxLjQ4Njc0ZS0wNSA0MTkgNC40NzcxNyA0MTkgMTBMNDE5IDE5MVoiIGZpbGw9IiNFREVFRjQiLz4KPHJlY3QgeD0iMjciIHk9IjUzIiB3aWR0aD0iMjMwIiBoZWlnaHQ9IjE2IiByeD0iOCIgZmlsbD0iI0VERUVGNCIvPgo8cmVjdCB4PSIyNyIgeT0iODQiIHdpZHRoPSIyMzAiIGhlaWdodD0iMTAiIHJ4PSI1IiBmaWxsPSIjRURFRUY0Ii8+CjxyZWN0IHg9IjI3IiB5PSIxMDIiIHdpZHRoPSIxNzAiIGhlaWdodD0iMTAiIHJ4PSI1IiBmaWxsPSIjRURFRUY0Ii8+CjxwYXRoIGZpbGwtcnVsZT0iZXZlbm9kZCIgY2xpcC1ydWxlPSJldmVub2RkIiBkPSJNNDcuOTIyOSAxNDIuNDM0QzQ1LjkyNSAxNDUuMiA0Mi42NzI3IDE0NyAzOSAxNDdDMzUuMzI3MyAxNDcgMzIuMDc1IDE0NS4yIDMwLjA3NzEgMTQyLjQzNEMzMS43MjczIDEzOS4yMTMgMzUuMTAyOCAxMzcuMDA0IDM5IDEzN0M0Mi44OTcyIDEzNy4wMDQgNDYuMjcyNyAxMzkuMjEzIDQ3LjkyMjkgMTQyLjQzNFpNNDguMzgzOSAxNDMuNDhDNTAuMDIxMiAxNDEuNDI5IDUxIDEzOC44MjkgNTEgMTM2QzUxIDEyOS4zNzMgNDUuNjI3NCAxMjQgMzkgMTI0QzMyLjM3MjYgMTI0IDI3IDEyOS4zNzMgMjcgMTM2QzI3IDEzOC40ODUgMjcuNzU1NSAxNDAuNzk0IDI5LjA0OTQgMTQyLjcwOUMyOS4yMjgyIDE0Mi45NzQgMjkuNDE3MyAxNDMuMjMxIDI5LjYxNjEgMTQzLjQ4QzMxLjgxNDggMTQ2LjIzNSAzNS4yMDEzIDE0OCAzOSAxNDhDNDIuNzk4NyAxNDggNDYuMTg1MSAxNDYuMjM1IDQ4LjM4MzkgMTQzLjQ4Wk0zOSAxMzZDMzYuNzkwOCAxMzYgMzQuOTk5OSAxMzQuMjA5IDM0Ljk5OTkgMTMyQzM0Ljk5OTkgMTI5Ljc5MSAzNi43OTA4IDEyOCAzOSAxMjhDNDEuMjA5MSAxMjggNDMgMTI5Ljc5MSA0MyAxMzJDNDMgMTM0LjIwOSA0MS4yMDkxIDEzNiAzOSAxMzZaIiBmaWxsPSIjRURFRUY0Ii8+CjxyZWN0IHg9IjU1IiB5PSIxMzQiIHdpZHRoPSIzNCIgaGVpZ2h0PSI2IiByeD0iMyIgZmlsbD0iI0VERUVGNCIvPgo8cmVjdCB4PSIxMjAiIHk9IjEzNCIgd2lkdGg9IjU0IiBoZWlnaHQ9IjYiIHJ4PSIzIiBmaWxsPSIjRURFRUY0Ii8+CjxyZWN0IHg9Ijk5IiB5PSIxMzYiIHdpZHRoPSIxMSIgaGVpZ2h0PSIyIiByeD0iMSIgZmlsbD0iI0VERUVGNCIvPgo8L3N2Zz4K",at="data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMzg4IiBoZWlnaHQ9IjE1NCIgdmlld0JveD0iMCAwIDM4OCAxNTQiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxyZWN0IHg9IjEiIHk9IjEiIHdpZHRoPSIzODYiIGhlaWdodD0iMTUyIiByeD0iOSIgc3Ryb2tlPSIjRURFRUY0IiBzdHJva2Utd2lkdGg9IjIiLz4KPHJlY3QgeD0iNDgiIHk9IjI3IiB3aWR0aD0iMzEzIiBoZWlnaHQ9IjE2IiByeD0iOCIgZmlsbD0iI0VERUVGNCIvPgo8cmVjdCB4PSI0OCIgeT0iMTExIiB3aWR0aD0iMzEzIiBoZWlnaHQ9IjE2IiByeD0iOCIgZmlsbD0iI0VERUVGNCIvPgo8cmVjdCB4PSI0OCIgeT0iNTUiIHdpZHRoPSIzMTMiIGhlaWdodD0iMTYiIHJ4PSI4IiBmaWxsPSIjRURFRUY0Ii8+CjxyZWN0IHg9IjQ4IiB5PSI4MyIgd2lkdGg9IjI4MS4xNjkiIGhlaWdodD0iMTYiIHJ4PSI4IiBmaWxsPSIjRURFRUY0Ii8+CjxjaXJjbGUgY3g9IjMzIiBjeT0iMzUiIHI9IjYiIGZpbGw9IiNFREVFRjQiLz4KPGNpcmNsZSBjeD0iMzMiIGN5PSI2MyIgcj0iNiIgZmlsbD0iI0VERUVGNCIvPgo8Y2lyY2xlIGN4PSIzMyIgY3k9IjkxIiByPSI2IiBmaWxsPSIjRURFRUY0Ii8+CjxjaXJjbGUgY3g9IjMzIiBjeT0iMTE5IiByPSI2IiBmaWxsPSIjRURFRUY0Ii8+Cjwvc3ZnPgo=",st="data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMzg2IiBoZWlnaHQ9IjIxOCIgdmlld0JveD0iMCAwIDM4NiAyMTgiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxyZWN0IHg9IjEiIHk9IjEiIHdpZHRoPSIzODQiIGhlaWdodD0iMjE2IiByeD0iOSIgc3Ryb2tlPSIjRURFRUY0IiBzdHJva2Utd2lkdGg9IjIiLz4KPHJlY3QgeD0iMjciIHk9IjI3IiB3aWR0aD0iNjciIGhlaWdodD0iNjciIHJ4PSI1IiBmaWxsPSIjRURFRUY0Ii8+CjxyZWN0IHg9IjEyOSIgeT0iMzkiIHdpZHRoPSIyMzAiIGhlaWdodD0iMTYiIHJ4PSI4IiBmaWxsPSIjRURFRUY0Ii8+CjxyZWN0IHg9IjEyOSIgeT0iNzYiIHdpZHRoPSIzNCIgaGVpZ2h0PSI2IiByeD0iMyIgZmlsbD0iI0VERUVGNCIvPgo8cmVjdCB4PSIxOTQiIHk9Ijc2IiB3aWR0aD0iNTQiIGhlaWdodD0iNiIgcng9IjMiIGZpbGw9IiNFREVFRjQiLz4KPHJlY3QgeD0iMTczIiB5PSI3OCIgd2lkdGg9IjExIiBoZWlnaHQ9IjIiIHJ4PSIxIiBmaWxsPSIjRURFRUY0Ii8+CjxyZWN0IHg9IjI3IiB5PSIxMjQiIHdpZHRoPSI2NyIgaGVpZ2h0PSI2NyIgcng9IjUiIGZpbGw9IiNFREVFRjQiLz4KPHJlY3QgeD0iMTI5IiB5PSIxMzYiIHdpZHRoPSIyMzAiIGhlaWdodD0iMTYiIHJ4PSI4IiBmaWxsPSIjRURFRUY0Ii8+CjxyZWN0IHg9IjEyOSIgeT0iMTczIiB3aWR0aD0iMzQiIGhlaWdodD0iNiIgcng9IjMiIGZpbGw9IiNFREVFRjQiLz4KPHJlY3QgeD0iMTk0IiB5PSIxNzMiIHdpZHRoPSI1NCIgaGVpZ2h0PSI2IiByeD0iMyIgZmlsbD0iI0VERUVGNCIvPgo8cmVjdCB4PSIxNzMiIHk9IjE3NSIgd2lkdGg9IjExIiBoZWlnaHQ9IjIiIHJ4PSIxIiBmaWxsPSIjRURFRUY0Ii8+CjxsaW5lIHgxPSIyNyIgeTE9IjEwOC41IiB4Mj0iMzU5IiB5Mj0iMTA4LjUiIHN0cm9rZT0iI0VERUVGNCIvPgo8L3N2Zz4K",lt="data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNDAzIiBoZWlnaHQ9IjM3MSIgdmlld0JveD0iMCAwIDQwMyAzNzEiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxyZWN0IHg9IjEiIHk9IjEiIHdpZHRoPSI0MDEiIGhlaWdodD0iMzY5IiByeD0iOSIgc3Ryb2tlPSIjRURFRUY0IiBzdHJva2Utd2lkdGg9IjIiLz4KPHJlY3QgeD0iMjciIHk9IjIyNSIgd2lkdGg9IjMxNCIgaGVpZ2h0PSIxNiIgcng9IjgiIGZpbGw9IiNFREVFRjQiLz4KPHJlY3QgeD0iMjciIHk9IjI1NyIgd2lkdGg9IjM0OSIgaGVpZ2h0PSIxMCIgcng9IjUiIGZpbGw9IiNFREVFRjQiLz4KPHJlY3QgeD0iMjciIHk9IjI4MCIgd2lkdGg9IjI1OSIgaGVpZ2h0PSIxMCIgcng9IjUiIGZpbGw9IiNFREVFRjQiLz4KPHBhdGggZD0iTTEgMTBDMSA0LjQ3NzE2IDUuNDc3MTUgMCAxMSAwSDM5MkMzOTcuNTIzIDAgNDAyIDQuNDc3MTUgNDAyIDEwVjIwMEgxVjEwWiIgZmlsbD0iI0VERUVGNCIvPgo8cmVjdCB4PSIyOCIgeT0iMzEyIiB3aWR0aD0iODAiIGhlaWdodD0iMzEiIHJ4PSI0IiBzdHJva2U9IiNFREVFRjQiIHN0cm9rZS13aWR0aD0iMiIvPgo8cmVjdCB4PSI0NCIgeT0iMzI0IiB3aWR0aD0iNDgiIGhlaWdodD0iNyIgcng9IjMuNSIgZmlsbD0iI0VERUVGNCIvPgo8L3N2Zz4K",{InspectorControls:ct,PanelColorSettings:dt}=wp.blockEditor,{__:ut}=wp.i18n,{PanelBody:mt,PanelRow:pt,SelectControl:ft,ToggleControl:gt,TextControl:ht,Dropdown:yt,Button:_t,FormTokenField:It}=wp.components,{useState:bt,useEffect:wt}=wp.element,xt=e=>{const{attributes:{selectedPostType:t,postTypes:n,selectedPostsPerPage:o,selectedLayout:r,contentColor:i,titleColor:s,backgroundColor:l,authorColor:c,commentColor:d,buttonColor:u,showImage:m,showMeta:p,showMetaAuthor:f,showMetaAuthorImg:g,showMetaComments:h,showButton:y,buttonText:_,showDescription:I,orderBy:b,order:w,taxTypes:x,catType:v,recentPostDays:C,selectedExcerptLength:S,backgroundType:N,selectedOperatorType:j,postsGap:M},setAttributes:k}=e,[T,E]=bt([]);wt(()=>{L()},[]);const D=wp.apiFetch,{addQueryArgs:A}=wp.url,L=e=>{D({path:A("fluent-crm/v2/campaigns-pro/posts/taxonomies",{...e})}).then(e=>{E(e.taxonomies)}).catch(()=>{}).finally(()=>{})},P=e=>{const t=T[e]?.terms;if(!t)return"";const n=Object.keys(t);return n.length?n[0]:""};wt(()=>{const e=P(t);e&&v!==e&&k({catType:e})},[t,T]);const B=[{value:s,onChange:e=>{k({titleColor:e})},label:ut("Title Color","fluent-crm")}];"layout-5"!==r&&B.push({value:l,onChange:e=>{k({backgroundColor:e})},label:ut("Box Background Color","fluent-crm")}),"default"!==r&&"layout-5"!==r&&"layout-7"!==r&&!0===p&&(!0===f&&B.push({value:c,onChange:e=>{k({authorColor:e})},label:ut("Author Color","fluent-crm")}),!0===h&&B.push({value:d,onChange:e=>{k({commentColor:e})},label:ut("Meta Comment Color","fluent-crm")})),"layout-6"!==r&&"layout-5"!==r&&B.push({value:i,onChange:e=>{k({contentColor:e})},label:ut("Content Color","fluent-crm")}),"layout-6"!==r&&"layout-4"!==r&&"layout-5"!==r&&B.push({value:u,onChange:e=>{k({buttonColor:e})},label:ut("Button Color","fluent-crm")});const R=[{value:"all",label:"All"}];T[t]?.terms&&T[t]?.terms[v]&&T[t]?.terms[v].map(e=>R.push(e));const z=e=>{if(!x||x.length<1)return[];const[t]=x;return t.hasOwnProperty(e)?t[e]:[]};let H=nt;"layout-2"===r?H=ot:"layout-3"===r?H=rt:"layout-4"===r?H=it:"layout-5"===r?H=at:"layout-6"===r?H=st:"layout-7"===r&&(H=lt);const O=[{value:"default",label:ut("Default","fluent-crm"),image:nt},{value:"layout-2",label:ut("Layout 2","fluent-crm"),image:ot},{value:"layout-3",label:ut("Layout 3","fluent-crm"),image:rt},{value:"layout-4",label:ut("Layout 4","fluent-crm"),image:it},{value:"layout-5",label:ut("Layout 5","fluent-crm"),image:at},{value:"layout-6",label:ut("Layout 6","fluent-crm"),image:st},{value:"layout-7",label:ut("Layout 7","fluent-crm"),image:lt}];return(0,a.jsxs)(ct,{children:[(0,a.jsx)(mt,{title:ut("General Settings","fluent-crm"),initialOpen:!0,children:(0,a.jsx)(pt,{children:(0,a.jsxs)("div",{className:"fluent-latest-posts-settings",children:[n&&n.length?(0,a.jsx)(ft,{__next40pxDefaultSize:!0,__nextHasNoMarginBottom:!0,label:ut("Select Post Type","fluent-crm"),value:t,options:n.map(e=>({value:e.value,label:e.label})),onChange:e=>{const t={selectedPostType:e},n=P(e);n&&(t.catType=n),t.taxTypes=[],k(t)}}):null,T[t]?.terms?(0,a.jsx)("div",{children:Object.entries(T[t]?.terms).map(([e,t],n)=>{return(0,a.jsx)(It,{value:z(e),suggestions:t?.map(e=>e.label),label:(o=e,ut("Taxonomy","fluent-crm")+" "+o.replaceAll("_"," ")),onChange:t=>((e,t)=>{const n=[...x];n.length<1&&n.push({}),n[0][e]=t,k({taxTypes:[...n]})})(e,t)},e+n);var o})}):null,T[t]?.terms?(0,a.jsx)(ft,{__next40pxDefaultSize:!0,__nextHasNoMarginBottom:!0,className:"fc-operator-type",label:ut("Select Operator Type","fluent-crm"),value:j,options:[{label:"AND",value:"AND"},{label:"OR",value:"OR"}],help:ut("Select the operator type for the taxonomy filter","fluent-crm"),onChange:e=>{k({selectedOperatorType:e})}}):"",(0,a.jsx)(ft,{__next40pxDefaultSize:!0,__nextHasNoMarginBottom:!0,label:ut("Order by","fluent-crm"),options:[{label:ut("Newest to Oldest","fluent-crm"),value:"date/desc"},{label:ut("Oldest to Newest","fluent-crm"),value:"date/asc"},{label:ut("A → Z","fluent-crm"),value:"title/asc"},{label:ut("Z → A","fluent-crm"),value:"title/desc"}],value:`${b}/${w}`,onChange:e=>{const[t,n]=e.split("/");n!==w&&k({order:n}),t!==b&&k({orderBy:t})}}),(0,a.jsx)(ht,{__next40pxDefaultSize:!0,__nextHasNoMarginBottom:!0,type:"number",className:"fc-recent-posts-number",value:C,label:ut("Posts from last (days)","fluent-crm"),help:ut("e.g get posts in the last (7) days","fluent-crm"),onChange:e=>k({recentPostDays:e})}),(0,a.jsx)(ht,{__next40pxDefaultSize:!0,__nextHasNoMarginBottom:!0,type:"number",className:"fce-dimension-box",value:o,label:ut("Show Posts","fluent-crm"),help:ut("e.g how many posts you want to show","fluent-crm"),onChange:e=>k({selectedPostsPerPage:e})}),(0,a.jsx)(ht,{__next40pxDefaultSize:!0,__nextHasNoMarginBottom:!0,type:"number",min:0,max:500,className:"fce-dimension-box",value:M,label:ut("Posts Gap (px)","fluent-crm"),help:ut("Space between each post item","fluent-crm"),onChange:e=>k({postsGap:Math.min(500,Math.max(0,parseInt(e,10)||0))})}),(0,a.jsxs)("div",{className:"show-setting-control-box select-layout",children:[(0,a.jsx)("p",{children:ut("Select Layout","fluent-crm")}),(0,a.jsx)(yt,{className:"show-setting-dropdown",popoverProps:{placement:"bottom-end"},renderToggle:({isOpen:e,onToggle:t})=>(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)("img",{onClick:t,"aria-expanded":e,src:H,alt:""}),(0,a.jsx)(_t,{variant:"primary",onClick:t,"aria-expanded":e,children:(0,a.jsx)("svg",{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 24 24",width:"24",height:"24","aria-hidden":"true",focusable:"false",children:(0,a.jsx)("path",{d:"M13 19h-2v-2h2v2zm0-6h-2v-2h2v2zm0-6h-2V5h2v2z"})})})]}),renderContent:({onToggle:e})=>(0,a.jsxs)("div",{className:"dropdown-render-content dropdown-render-selected-layout fc-layout-picker",children:[(0,a.jsx)("p",{children:ut("Select Layout","fluent-crm")}),(0,a.jsx)("div",{className:"fc-layout-picker-grid",role:"radiogroup","aria-label":ut("Layout","fluent-crm"),children:O.map(t=>(0,a.jsxs)("button",{type:"button",className:"fc-layout-picker-option "+(r===t.value?"is-active":""),role:"radio","aria-checked":r===t.value,onClick:()=>{k({selectedLayout:t.value}),e()},children:[(0,a.jsx)("img",{src:t.image,alt:t.label}),(0,a.jsx)("span",{children:t.label})]},t.value))})]})})]}),"layout-5"!==r?(0,a.jsx)(gt,{__nextHasNoMarginBottom:!0,label:ut("Show Image","fluent-crm"),checked:m,onChange:()=>k({showImage:!m})}):null,"layout-5"!==r&&"layout-6"!==r?(0,a.jsxs)("div",{className:"show-setting-control-box",children:[(0,a.jsx)(gt,{__nextHasNoMarginBottom:!0,label:ut("Show Excerpt","fluent-crm"),checked:I,onChange:()=>k({showDescription:!I})}),!0===I?(0,a.jsx)(yt,{className:"show-setting-dropdown",contentClassName:"my-popover-content-classname",popoverProps:{placement:"bottom-end"},renderToggle:({isOpen:e,onToggle:t})=>(0,a.jsx)(_t,{variant:"primary",onClick:t,"aria-expanded":e,children:(0,a.jsx)("svg",{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 24 24",width:"24",height:"24","aria-hidden":"true",focusable:"false",children:(0,a.jsx)("path",{d:"M13 19h-2v-2h2v2zm0-6h-2v-2h2v2zm0-6h-2V5h2v2z"})})}),renderContent:()=>(0,a.jsx)("div",{className:"dropdown-render-content",children:(0,a.jsx)(ht,{__next40pxDefaultSize:!0,__nextHasNoMarginBottom:!0,type:"number",max:100,className:"fce-dimension-box",value:S,label:ut("Excerpt Length","fluent-crm"),onChange:e=>k({selectedExcerptLength:e})})})}):null]}):null,"default"!==r&&"layout-5"!==r&&"layout-7"!==r?(0,a.jsxs)("div",{className:"show-setting-control-box",children:[(0,a.jsx)(gt,{__nextHasNoMarginBottom:!0,label:ut("Show Meta","fluent-crm"),checked:p,onChange:()=>k({showMeta:!p})}),!0===p?(0,a.jsx)(yt,{className:"show-setting-dropdown",contentClassName:"my-popover-content-classname",popoverProps:{placement:"bottom-end"},renderToggle:({isOpen:e,onToggle:t})=>(0,a.jsx)(_t,{variant:"primary",onClick:t,"aria-expanded":e,children:(0,a.jsx)("svg",{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 24 24",width:"24",height:"24","aria-hidden":"true",focusable:"false",children:(0,a.jsx)("path",{d:"M13 19h-2v-2h2v2zm0-6h-2v-2h2v2zm0-6h-2V5h2v2z"})})}),renderContent:()=>(0,a.jsxs)("div",{className:"dropdown-render-content setting-dropdown-meta",children:[(0,a.jsx)(gt,{__nextHasNoMarginBottom:!0,label:ut("Show Comments","fluent-crm"),checked:h,onChange:()=>k({showMetaComments:!h})}),(0,a.jsx)(gt,{__nextHasNoMarginBottom:!0,label:ut("Show Author","fluent-crm"),checked:f,onChange:()=>k({showMetaAuthor:!f})}),!0===f&&"layout-6"!==r?(0,a.jsx)(gt,{__nextHasNoMarginBottom:!0,label:ut("Show Author Avatar","fluent-crm"),checked:g,onChange:()=>k({showMetaAuthorImg:!g})}):null]})}):null]}):null,"layout-4"!==r&&"layout-5"!==r&&"layout-6"!==r?(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)(gt,{__nextHasNoMarginBottom:!0,label:ut("Show Button","fluent-crm"),checked:y,onChange:()=>k({showButton:!y})}),!0===y?(0,a.jsx)(ht,{__next40pxDefaultSize:!0,__nextHasNoMarginBottom:!0,type:"text",value:_,label:ut("Button Text","fluent-crm"),onChange:e=>k({buttonText:e})}):null]}):null,"layout-5"!==r?(0,a.jsx)(ft,{__next40pxDefaultSize:!0,__nextHasNoMarginBottom:!0,label:ut("Post Image Type","fluent-crm"),options:[{label:ut("Cover","fluent-crm"),value:"cover"},{label:ut("Contain","fluent-crm"),value:"contain"},{label:ut("None","fluent-crm"),value:"inherit"}],value:N,onChange:e=>k({backgroundType:e})}):null]})})}),(0,a.jsx)("div",{className:"fluent-latest-posts-content-color-settings",children:(0,a.jsx)(dt,{title:ut("Customization","fluent-crm"),colorSettings:B})})]})},{Spinner:vt}=wp.components,{useState:Ct,useEffect:St}=wp.element,{__:Nt,_n}=wp.i18n,jt=e=>{const{attributes:{selectedPostType:t,selectedPostsPerPage:n,selectedLayout:o,contentColor:r,titleColor:i,backgroundColor:s,authorColor:l,commentColor:c,buttonColor:d,showImage:u,showMeta:m,showMetaAuthor:p,showMetaAuthorImg:f,showMetaComments:g,showButton:h,showDescription:y,selectedExcerptLength:_,orderBy:I,order:b,taxTypes:w,catType:x,recentPostDays:v,buttonText:C,backgroundType:S,selectedOperatorType:N,postsGap:j},setAttributes:M}=e,[k,T]=Ct([]),[E,D]=Ct(!1),[A,L]=Ct(!1);St(()=>{R()},[t,n,I,b,w,x,v,N]);const P=wp.apiFetch,{addQueryArgs:B}=wp.url,R=e=>{D(!0),P({path:B("fluent-crm/v2/campaigns-pro/posts",{per_page:n,post_type:t,orderBy:I,order:b,taxTypes:w,catType:x,days:v,excerptLength:_,operator:N,...e})}).then(e=>{T(e.posts),M({postTypes:e.post_types})}).catch(e=>{L(!0)}).finally(()=>{D(!1)})},z={color:r},H={color:i},O={background:s,marginBottom:(j??20)+"px"},F={color:l},U={color:c};let G="";G="layout-7"===o?{color:d,border:"2px solid "+d}:{color:d};const Z=e=>{const t=_,n=e.split(/\s+/);return n.length<=t?e:n.slice(0,t).join(" ")+"..."};return(0,a.jsx)("div",{children:(0,a.jsx)("div",{className:"fc_latest_blog_posts",children:E?(0,a.jsx)("h2",{children:(0,a.jsx)(vt,{})}):(0,a.jsx)("div",{className:"fc_latest_posts_items template-"+o,children:k&&k.length?k.map((e,t)=>(0,a.jsx)("div",{className:"fc_latest_post_item",style:O,children:"layout-6"===o?(0,a.jsxs)(a.Fragment,{children:[e.thumbnail&&!0===u?(0,a.jsx)("div",{className:"fc_latest_post_thumbnail",style:{backgroundImage:`url(${e.thumbnail})`,backgroundSize:S}}):null,(0,a.jsxs)("div",{className:"fc_latest_post_content",children:[(0,a.jsx)("h1",{className:"title",style:H,children:e.post_title?e.post_title:"(no title)"}),!0===m?(0,a.jsxs)("p",{className:"meta",style:F,children:[!0===p?(0,a.jsx)("span",{className:"author",children:(0,a.jsx)("span",{style:F,children:e.author})}):null,!0===p&&!0===g&&e.comment_count?"-":null,!0===g&&e.comment_count?(0,a.jsx)("span",{className:"comments",style:U,children:_n(e.comment_count+" comment",e.comment_count+" comments",e.comment_count)}):null]}):null]})]}):(0,a.jsxs)(a.Fragment,{children:[e.thumbnail&&!0===u&&"layout-5"!==o?(0,a.jsx)("div",{className:"fc_latest_post_thumbnail",style:{backgroundImage:`url(${e.thumbnail})`,backgroundSize:S}}):null,(0,a.jsxs)("div",{className:"fc_latest_post_content",children:[!0===m&&"default"!==o&&"layout-5"!==o&&"layout-7"!==o?(0,a.jsxs)("p",{className:"meta",style:F,children:[!0===p?(0,a.jsxs)("span",{className:"author",children:[!0===f?(0,a.jsx)("img",{src:e.author_avatar,alt:e.author}):null,(0,a.jsx)("span",{style:F,children:e.author})]}):null,!0===p&&!0===g&&e.comment_count?"-":null,!0===g&&e.comment_count?(0,a.jsx)("span",{className:"comments",style:U,children:_n(e.comment_count+" comment",e.comment_count+" comments",e.comment_count)}):null]}):null,(0,a.jsx)("h1",{className:"title",style:H,children:e.post_title?e.post_title:"(no title)"}),!0===y&&"layout-5"!==o&&e.post_excerpt?(0,a.jsx)("p",{className:"description",style:z,children:Z(e.post_excerpt)}):null,!0===h&&"layout-4"!==o&&"layout-5"!==o?(0,a.jsx)("span",{style:G,className:"fc_latest_post_btn",children:C}):null]})]})},t)):(0,a.jsx)("div",{className:"fcw_products_not_found",children:(0,a.jsx)("h2",{children:"No Posts found!"})})})})})},{Fragment:Mt}=((0,n.withSelect)((e,t)=>{const{selectedPostType:n}=t.attributes;return{selectedPostType:n}})(jt),wp.element),{__:kt}=wp.i18n,Tt={selectedPostType:{type:"string",default:"post"},selectedPostsPerPage:{type:"string",default:"3"},selectedLayout:{type:"string",default:"default"},showImage:{type:"boolean",default:!0},showMeta:{type:"boolean",default:!0},showMetaAuthor:{type:"boolean",default:!0},showMetaAuthorImg:{type:"boolean",default:!0},showMetaComments:{type:"boolean",default:!0},showButton:{type:"boolean",default:!0},showDescription:{type:"boolean",default:!0},selectedExcerptLength:{type:"string",default:"55"},contentColor:{type:"string",default:"#6b6d7c"},titleColor:{type:"string",default:"#393d57"},backgroundColor:{type:"string",default:"#ffffff"},authorColor:{type:"string",default:"#393d57"},commentColor:{type:"string",default:"#acacac"},buttonColor:{type:"string",default:"#000000"},taxTypes:{type:"array",default:[]},catType:{type:"string",default:"category"},order:{type:"string",default:"desc"},orderBy:{type:"string",default:"date"},recentPostDays:{type:"string",default:""},buttonText:{type:"string",default:"Read More"},backgroundType:{type:"string",default:"cover"},selectedOperatorType:{type:"string",default:"OR"},postsGap:{type:"number",default:20}},Et=wp.element.createElement,{__:Dt}=wp.i18n,{registerBlockType:At}=wp.blocks,Lt=window.fcrmBlockEditorConfig?.modules||{},Pt=[{attributes:Tt,save:()=>(0,a.jsx)("div",{children:(0,a.jsx)(v.InnerBlocks.Content,{})})}],Bt=Et("svg",{width:20,height:20},Et("path",{d:"M0 0h24v24H0V0z",fill:"none"}),Et("path",{fill:"#96588a",d:"M22 9.24l-7.19-.62L12 2 9.19 8.63 2 9.24l5.46 4.73L5.82 21 12 17.27 18.18 21l-1.63-7.03L22 9.24zM12 15.4l-3.76 2.27 1-4.28-3.32-2.88 4.38-.38L12 6.1l1.71 4.04 4.38.38-3.32 2.88 1 4.28L12 15.4z"}));!1!==Lt.hasFluentCampaign&&At("fluent-crm/latest-posts",{apiVersion:3,title:Dt("Latest Posts Block"),description:Dt("Latest Posts Block For your Email"),category:"layout",icon:Bt,keywords:[Dt("card"),Dt("latest blog"),Dt("latest posts")],supports:{align:["wide","full"],html:!0},attributes:Tt,deprecated:Pt,edit:e=>{const t=(0,v.useBlockProps)({className:"fluent-latest-posts-block"});return(0,a.jsxs)(Mt,{children:[(0,a.jsx)("div",{...t,children:(0,a.jsx)(jt,{attributes:e.attributes,setAttributes:e.setAttributes})}),(0,a.jsx)(xt,{attributes:e.attributes,setAttributes:e.setAttributes})]})},save:Qe});const Rt={selectedLayout:{type:"string",default:"default"},selectedPostsPerPage:{type:"string",default:"3"},showDescription:{type:"boolean",default:!0},showImage:{type:"boolean",default:!0},showPrice:{type:"boolean",default:!0},showButton:{type:"boolean",default:!0},taxonomies:{type:"array",default:[]},taxType:{type:"string",default:"all"},order:{type:"string",default:"desc"},orderBy:{type:"string",default:"date"},buttonText:{type:"string",default:"Buy Now"},titleColor:{type:"string",default:"#2a363d"},descriptionColor:{type:"string",default:""},priceColor:{type:"string",default:"#37454e"},buttonColor:{type:"string",default:""},buttonBG:{type:"string",default:"#2a363d"}},{Fragment:zt}=wp.element,Ht=({attributes:e,setAttributes:t,LandingPage:n,InspectorSettings:o})=>{const r=(0,v.useBlockProps)({className:"fluent-latest-posts-block"});return(0,a.jsxs)(zt,{children:[(0,a.jsx)("div",{...r,children:(0,a.jsx)(n,{attributes:e,setAttributes:t})}),(0,a.jsx)(o,{attributes:e,setAttributes:t})]})},{Spinner:Ot}=wp.components,{useState:Ft,useEffect:Ut}=wp.element,{__:Gt,_n:Zt}=wp.i18n,Vt=e=>{const{attributes:{selectedLayout:t,order:n,orderBy:o,taxonomies:r,taxType:i,selectedPostsPerPage:s,showDescription:l,showImage:c,showPrice:d,showButton:u,buttonText:m,titleColor:p,priceColor:f,buttonColor:g,buttonBG:h,descriptionColor:y},setAttributes:_}=e,[I,b]=Ft([]),[w,x]=Ft(!1),v=wp.apiFetch,{addQueryArgs:C}=wp.url,[S,N]=Ft(!1),j=window.fcrmBlockEditorConfig||{},M=j.endpoints?.products||"fluent-crm/v2/campaigns-pro/products";Ut(()=>{k()},[s,n,o,i]);const k=e=>{x(!0),N(!1),v({path:C(M,{per_page:s,order:n,orderby:o,taxType:i,...e})}).then(e=>{const t=(e=>{const t={product:{terms:{product_cat:[]}}};return Array.isArray(e)?{products:e,taxonomies:t}:{products:Array.isArray(e?.products)?e.products:Array.isArray(e?.data?.products)?e.data.products:[],taxonomies:e?.taxonomies||e?.data?.taxonomies||t}})(e);b(t.products),_({taxonomies:t.taxonomies})}).catch(()=>(e=>v({path:C("wc/store/products",{per_page:s,order:n,orderby:o,...e})}).then(e=>({products:Array.isArray(e)?e:[],taxonomies:{product:{terms:{product_cat:[]}}}})))(e).then(e=>{b(e.products),_({taxonomies:e.taxonomies})})).catch(()=>{b([]),N(!0)}).finally(()=>{x(!1)})},T=e=>e?e.image?e.image:e.images&&e.images.length&&e.images[0].src||"":"",E={color:p},D={color:y},A={color:f};let L="";return"layout-3"!==t&&!0===u&&(L={color:g}),"layout-2"===t&&(L={color:g,background:h}),(0,a.jsx)("div",{children:w?(0,a.jsx)("h2",{children:(0,a.jsx)(Ot,{})}):(0,a.jsx)("div",{className:"fc_woo_products template-"+t,children:I&&I.length?I.map((e,n)=>(0,a.jsxs)("div",{className:"fc_woo_product"+(c?"":" no-image"),children:[!0===c?(0,a.jsx)("div",{className:"fc_woo_product_img",children:(0,a.jsx)("img",{src:T(e),alt:""})}):null,(0,a.jsxs)("div",{className:"fc_woo_product_info",children:[(0,a.jsxs)("div",{children:[(0,a.jsx)("h3",{className:"title",style:E,dangerouslySetInnerHTML:{__html:e.name}}),"layout-2"===t&&!0===l?(0,a.jsx)("p",{className:"description",style:D,dangerouslySetInnerHTML:{__html:e.short_description}}):null,!0===d?(0,a.jsx)("span",{className:"price",style:A,dangerouslySetInnerHTML:{__html:ze(e.price_html,Gt("Free"))}}):null]}),"layout-3"!==t&&!0===u?(0,a.jsx)("span",{className:"add-to-cart-btn",style:L,children:m}):null]})]},n)):(0,a.jsx)("div",{className:"fcw_products_not_found",children:(0,a.jsx)("h2",{children:"No Products found!"})})})})},Wt="data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMzM2IiBoZWlnaHQ9IjM2OSIgdmlld0JveD0iMCAwIDMzNiAzNjkiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxyZWN0IHg9IjI0IiB5PSIyNCIgd2lkdGg9IjI4OCIgaGVpZ2h0PSIyMTMiIGZpbGw9IiNFREVFRjQiLz4KPHJlY3QgeD0iMjQiIHk9IjI2MyIgd2lkdGg9IjIwMyIgaGVpZ2h0PSIxNiIgcng9IjgiIGZpbGw9IiNFREVFRjQiLz4KPHJlY3QgeD0iMjQiIHk9IjI5NCIgd2lkdGg9IjUxIiBoZWlnaHQ9IjEyIiByeD0iNiIgZmlsbD0iI0VERUVGNCIvPgo8cmVjdCB4PSIyNCIgeT0iMzI5IiB3aWR0aD0iNjIiIGhlaWdodD0iMTQiIHJ4PSI3IiBmaWxsPSIjRURFRUY0Ii8+CjxyZWN0IHg9IjEiIHk9IjEiIHdpZHRoPSIzMzQiIGhlaWdodD0iMzY3IiByeD0iOSIgc3Ryb2tlPSIjRURFRUY0IiBzdHJva2Utd2lkdGg9IjIiLz4KPC9zdmc+Cg==",Qt="data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNzExIiBoZWlnaHQ9IjUwOCIgdmlld0JveD0iMCAwIDcxMSA1MDgiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxyZWN0IHg9IjMwIiB5PSIzMCIgd2lkdGg9IjI5OCIgaGVpZ2h0PSIyMTIiIGZpbGw9IiNFREVFRjQiLz4KPHJlY3QgeD0iMzY1IiB5PSI0MiIgd2lkdGg9IjIxNiIgaGVpZ2h0PSIxNiIgcng9IjgiIGZpbGw9IiNFREVFRjQiLz4KPHJlY3QgeD0iMzY1IiB5PSI3OCIgd2lkdGg9IjI5NiIgaGVpZ2h0PSIxMiIgcng9IjYiIGZpbGw9IiNFREVFRjQiLz4KPHJlY3QgeD0iMzY1IiB5PSIxMDEiIHdpZHRoPSIxNDkiIGhlaWdodD0iMTIiIHJ4PSI2IiBmaWxsPSIjRURFRUY0Ii8+CjxyZWN0IHg9IjM2NSIgeT0iMTMzIiB3aWR0aD0iNjIiIGhlaWdodD0iMTQiIHJ4PSI3IiBmaWxsPSIjRURFRUY0Ii8+CjxyZWN0IHg9IjM2NSIgeT0iMTczIiB3aWR0aD0iMzE2IiBoZWlnaHQ9IjU2IiBmaWxsPSIjRURFRUY0Ii8+CjxyZWN0IHg9IjMwIiB5PSIyNjYiIHdpZHRoPSIyOTgiIGhlaWdodD0iMjEyIiBmaWxsPSIjRURFRUY0Ii8+CjxyZWN0IHg9IjM2NSIgeT0iMjc4IiB3aWR0aD0iMjE2IiBoZWlnaHQ9IjE2IiByeD0iOCIgZmlsbD0iI0VERUVGNCIvPgo8cmVjdCB4PSIzNjUiIHk9IjMxNCIgd2lkdGg9IjI5NiIgaGVpZ2h0PSIxMiIgcng9IjYiIGZpbGw9IiNFREVFRjQiLz4KPHJlY3QgeD0iMzY1IiB5PSIzMzciIHdpZHRoPSIxNDkiIGhlaWdodD0iMTIiIHJ4PSI2IiBmaWxsPSIjRURFRUY0Ii8+CjxyZWN0IHg9IjM2NSIgeT0iMzY5IiB3aWR0aD0iNjIiIGhlaWdodD0iMTQiIHJ4PSI3IiBmaWxsPSIjRURFRUY0Ii8+CjxyZWN0IHg9IjM2NSIgeT0iNDA5IiB3aWR0aD0iMzE2IiBoZWlnaHQ9IjU2IiBmaWxsPSIjRURFRUY0Ii8+CjxyZWN0IHg9IjEiIHk9IjEiIHdpZHRoPSI3MDkiIGhlaWdodD0iNTA2IiByeD0iOSIgc3Ryb2tlPSIjRURFRUY0IiBzdHJva2Utd2lkdGg9IjIiLz4KPC9zdmc+Cg==",Yt="data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMzM2IiBoZWlnaHQ9IjMzMiIgdmlld0JveD0iMCAwIDMzNiAzMzIiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxyZWN0IHg9IjI0IiB5PSIyNCIgd2lkdGg9IjI4OCIgaGVpZ2h0PSIyMTMiIHJ4PSIxMCIgZmlsbD0iI0VERUVGNCIvPgo8cmVjdCB4PSI2NiIgeT0iMjYzIiB3aWR0aD0iMjAzIiBoZWlnaHQ9IjE2IiByeD0iOCIgZmlsbD0iI0VERUVGNCIvPgo8cmVjdCB4PSIxNDIiIHk9IjI5NCIgd2lkdGg9IjUxIiBoZWlnaHQ9IjEyIiByeD0iNiIgZmlsbD0iI0VERUVGNCIvPgo8cmVjdCB4PSIxIiB5PSIxIiB3aWR0aD0iMzM0IiBoZWlnaHQ9IjMzMCIgcng9IjkiIHN0cm9rZT0iI0VERUVGNCIgc3Ryb2tlLXdpZHRoPSIyIi8+Cjwvc3ZnPgo=",{InspectorControls:Jt,PanelColorSettings:$t}=wp.blockEditor,{__:qt}=wp.i18n,{PanelBody:Kt,PanelRow:Xt,SelectControl:en,ToggleControl:tn,TextControl:nn,Dropdown:on,Button:rn}=wp.components,an=e=>{const{attributes:{selectedLayout:t,order:n,orderBy:o,taxonomies:r,taxType:i,selectedPostsPerPage:s,showDescription:l,showImage:c,showPrice:d,showButton:u,buttonText:m,titleColor:p,priceColor:f,buttonColor:g,buttonBG:h,descriptionColor:y},setAttributes:_}=e;let I=Wt;"layout-2"===t?I=Qt:"layout-3"===t&&(I=Yt);const b=[{value:"default",label:qt("Default","fluent-crm"),image:Wt},{value:"layout-2",label:qt("Layout 2","fluent-crm"),image:Qt},{value:"layout-3",label:qt("Layout 3","fluent-crm"),image:Yt}],w=[{value:p,onChange:e=>{_({titleColor:e})},label:qt("Title Color")}];"layout-2"===t&&!0===l&&w.push({value:y,onChange:e=>{_({descriptionColor:e})},label:qt("Description Color")}),!0===d&&w.push({value:f,onChange:e=>{_({priceColor:e})},label:qt("Price Color")}),"layout-3"!==t&&!0===u&&w.push({value:g,onChange:e=>{_({buttonColor:e})},label:qt("Button Color")}),"layout-2"===t&&!0===u&&w.push({value:h,onChange:e=>{_({buttonBG:e})},label:qt("Button Background")});const x=[{value:"all",label:"All"}];return r.product?.terms&&r.product?.terms.product_cat&&r.product?.terms.product_cat.map(e=>x.push(e)),(0,a.jsxs)(Jt,{children:[(0,a.jsx)(Kt,{title:"General Settings",initialOpen:!0,children:(0,a.jsx)(Xt,{children:(0,a.jsxs)("div",{className:"fc-latest-products-settings",children:[r.product?.terms?(0,a.jsx)(en,{__next40pxDefaultSize:!0,__nextHasNoMarginBottom:!0,value:i,options:x,placeholder:qt("Select Taxonomy","fluent-crm"),label:qt("Select Taxonomy","fluent-crm"),clearable:!0,onChange:e=>{_({taxType:e})}}):null,(0,a.jsx)(en,{__next40pxDefaultSize:!0,__nextHasNoMarginBottom:!0,label:qt("Order by","fluent-crm"),options:[{label:qt("Newest to Oldest","fluent-crm"),value:"date/desc"},{label:qt("Oldest to Newest","fluent-crm"),value:"date/asc"},{label:qt("A → Z","fluent-crm"),value:"title/asc"},{label:qt("Z → A","fluent-crm"),value:"title/desc"}],value:`${o}/${n}`,onChange:e=>{const[t,r]=e.split("/");r!==n&&_({order:r}),t!==o&&_({orderBy:t})}}),(0,a.jsx)(nn,{__next40pxDefaultSize:!0,__nextHasNoMarginBottom:!0,type:"number",className:"fce-dimension-box",value:s,label:qt("Show Products"),help:"e.g how many products you want to show",onChange:e=>_({selectedPostsPerPage:e})}),(0,a.jsxs)("div",{className:"show-setting-control-box select-layout",children:[(0,a.jsx)("p",{children:"Select Layout"}),(0,a.jsx)(on,{className:"show-setting-dropdown",popoverProps:{placement:"bottom-end"},renderToggle:({isOpen:e,onToggle:t})=>(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)("img",{onClick:t,"aria-expanded":e,src:I,alt:""}),(0,a.jsx)(rn,{variant:"primary",onClick:t,"aria-expanded":e,children:(0,a.jsx)("svg",{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 24 24",width:"24",height:"24","aria-hidden":"true",focusable:"false",children:(0,a.jsx)("path",{d:"M13 19h-2v-2h2v2zm0-6h-2v-2h2v2zm0-6h-2V5h2v2z"})})})]}),renderContent:({onToggle:e})=>(0,a.jsxs)("div",{className:"dropdown-render-content dropdown-render-selected-layout fc-layout-picker",children:[(0,a.jsx)("p",{children:"Select Layout"}),(0,a.jsx)("div",{className:"fc-layout-picker-grid",role:"radiogroup","aria-label":qt("Layout","fluent-crm"),children:b.map(n=>(0,a.jsxs)("button",{type:"button",className:"fc-layout-picker-option "+(t===n.value?"is-active":""),role:"radio","aria-checked":t===n.value,onClick:()=>{_({selectedLayout:n.value}),e()},children:[(0,a.jsx)("img",{src:n.image,alt:n.label}),(0,a.jsx)("span",{children:n.label})]},n.value))})]})})]}),(0,a.jsx)(tn,{__nextHasNoMarginBottom:!0,label:qt("Show Image"),checked:c,onChange:()=>_({showImage:!c})}),"layout-2"===t?(0,a.jsx)(tn,{__nextHasNoMarginBottom:!0,label:qt("Show Description"),checked:l,onChange:()=>_({showDescription:!l})}):null,(0,a.jsx)(tn,{__nextHasNoMarginBottom:!0,label:qt("Show Price"),checked:d,onChange:()=>_({showPrice:!d})}),"layout-3"!==t?(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)(tn,{__nextHasNoMarginBottom:!0,label:qt("Show Button"),checked:u,onChange:()=>_({showButton:!u})}),!0===u?(0,a.jsx)(nn,{__next40pxDefaultSize:!0,__nextHasNoMarginBottom:!0,type:"text",value:m,label:qt("Button Text"),onChange:e=>_({buttonText:e})}):null]}):null]})})}),(0,a.jsx)("div",{className:"fc-latest-products-content-color-settings",children:(0,a.jsx)($t,{title:qt("Customization"),colorSettings:w})})]})},sn=wp.element.createElement,{__:ln}=wp.i18n,{registerBlockType:cn}=wp.blocks,dn=window.fcrmBlockEditorConfig?.modules||{},un=[{attributes:Rt,save:()=>(0,a.jsx)("div",{children:(0,a.jsx)(v.InnerBlocks.Content,{})})}],mn=sn("svg",{width:20,height:20,viewBox:"0 0 24 24"},sn("path",{fill:"#7F54B3",d:"M5 4h14a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2h-8.2L7 20v-4H5a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2z"}),sn("text",{x:12,y:12.8,textAnchor:"middle",fontSize:6.5,fontFamily:"Arial, sans-serif",fontWeight:700,fill:"#fff"},"woo"));!1!==dn.hasFluentCampaign&&!1!==dn.hasWooCommerce&&cn("fluent-crm/woo-products",{apiVersion:3,title:ln("WooCommerce Products"),description:ln("WooCommerce Products For your Email"),category:"layout",icon:mn,keywords:[ln("card"),ln("latest product"),ln("latest products"),ln("products")],supports:{align:["wide","full"],html:!0},attributes:Rt,deprecated:un,edit:e=>(0,a.jsx)(Ht,{attributes:e.attributes,setAttributes:e.setAttributes,LandingPage:Vt,InspectorSettings:an}),save:Qe});const pn=(e="")=>((e="")=>{const t=document.createElement("textarea");return t.innerHTML=String(e),t.value})(e).replace(/\s+/g," ").trim(),fn=()=>{const e=window.fcrmBlockEditorConfig||{};return e.endpoints?.cartProducts||"fluent-crm/v2/editor/cart-products"},gn=e=>e?e.image?e.image:e.thumbnail?e.thumbnail:e.images&&e.images.length&&e.images[0].src||"":"",hn=(e={})=>{if(e?.label)return e.label;const t=e?.id?`#${e.id}`:"",n=e?.name||e?.post_title||"";return[t?`${t} - ${n}`:n,pn(e?.price_html?ze(e.price_html).replace(/<[^>]*>/g,""):e?.price_text),Array.isArray(e?.categories)?e.categories.filter(Boolean).join(", "):""].filter(Boolean).join(" | ")},yn=(e={})=>({id:e?.id,name:e?.name||e?.post_title||`#${e?.id}`,label:hn(e),short_description:e?.short_description||e?.post_excerpt||"",price_html:ze(e?.price_html||e?.price||""),image:gn(e),permalink:e?.permalink||"#"}),In=e=>{const t=Array.isArray(e?.products)?e.products:null,n=Array.isArray(e?.data?.products)?e.data.products:null;return{products:(t||n||[]).map(yn).filter(e=>!!e.id),taxonomies:e?.taxonomies||e?.data?.taxonomies||{}}},bn=e=>{const t=e?.product||e?.data?.product;return t&&t.id?yn(t):In(e).products[0]||null},{Spinner:wn}=wp.components,{useState:xn,useEffect:vn}=wp.element,{__:Cn}=wp.i18n,Sn=e=>{const{attributes:{selectedLayout:t,order:n,orderBy:o,taxonomies:r,taxType:i,selectedPostsPerPage:s,showDescription:l,showImage:c,showPrice:d,showButton:u,buttonText:m,titleColor:p,priceColor:f,buttonColor:g,buttonBG:h,descriptionColor:y},setAttributes:_}=e,[I,b]=xn([]),[w,x]=xn(!1),v=wp.apiFetch,{addQueryArgs:C}=wp.url,[S,N]=xn(!1),j=fn();vn(()=>{M()},[s,n,o,i]);const M=e=>{x(!0),v({path:C(j,{per_page:s,order:n,orderby:o,taxType:i,...e})}).then(e=>{const t=In(e);b(t.products),_({taxonomies:t.taxonomies})}).catch(e=>{N(!0)}).finally(()=>{x(!1)})},k=e=>e?.name||e?.post_title||"",T=e=>e?.short_description||e?.post_excerpt||"",E=e=>ze(e?.price_html||e?.price||e?.detail?.formatted_min_price,Cn("Free")),D={color:p},A={color:y},L={color:f};let P="";return"layout-3"!==t&&!0===u&&(P={color:g}),"layout-2"===t&&(P={color:g,background:h}),(0,a.jsx)("div",{children:w?(0,a.jsx)("h2",{children:(0,a.jsx)(wn,{})}):(0,a.jsx)("div",{className:"fc_woo_products template-"+t,children:I&&I.length?I.map((e,n)=>(0,a.jsxs)("div",{className:"fc_woo_product"+(c?"":" no-image"),children:[!0===c?(0,a.jsx)("div",{className:"fc_woo_product_img",children:(0,a.jsx)("img",{src:gn(e),alt:""})}):null,(0,a.jsxs)("div",{className:"fc_woo_product_info",children:[(0,a.jsxs)("div",{children:[(0,a.jsx)("h3",{className:"title",style:D,dangerouslySetInnerHTML:{__html:k(e)}}),"layout-2"===t&&!0===l?(0,a.jsx)("p",{className:"description",style:A,dangerouslySetInnerHTML:{__html:T(e)}}):null,!0===d?(0,a.jsx)("span",{className:"price",style:L,dangerouslySetInnerHTML:{__html:E(e)}}):null]}),"layout-3"!==t&&!0===u?(0,a.jsx)("span",{className:"add-to-cart-btn",style:P,children:m}):null]})]},n)):(0,a.jsx)("div",{className:"fcw_products_not_found",children:(0,a.jsx)("h2",{children:"No Products found!"})})})})},Nn=wp.element.createElement,{__:jn}=wp.i18n,{registerBlockType:Mn}=wp.blocks,kn=window.fcrmBlockEditorConfig?.modules||{},Tn=[{attributes:Rt,save:()=>(0,a.jsx)("div",{children:(0,a.jsx)(v.InnerBlocks.Content,{})})}],En=Nn("svg",{width:20,height:20,viewBox:"0 0 24 24"},Nn("rect",{x:0,y:0,width:24,height:24,rx:4,fill:"#0b4dbb"}),Nn("path",{fill:"#fff",d:"M11.2 16.4H4.2l1.1-2.5c.3-.7 1-1.2 1.8-1.2h8.3l-.6 1.3c-.6 1.5-2 2.4-3.6 2.4z"}),Nn("path",{fill:"#fff",d:"M17 11.2H7.2l.6-1.3c.6-1.5 2-2.4 3.6-2.4h8.5l-1.1 2.5c-.3.7-1 1.2-1.8 1.2z"}));!1!==kn.hasFluentCart&&Mn("fluent-crm/cart-products",{apiVersion:3,title:jn("FluentCart Products"),description:jn("FluentCart Products For your Email"),category:"layout",icon:En,keywords:[jn("card"),jn("fluentcart"),jn("products")],supports:{align:["wide","full"],html:!0},attributes:Rt,deprecated:Tn,edit:e=>(0,a.jsx)(Ht,{attributes:e.attributes,setAttributes:e.setAttributes,LandingPage:Sn,InspectorSettings:an}),save:Qe});const{InspectorControls:Dn,PanelColorSettings:An}=wp.blockEditor,{__:Ln}=wp.i18n,{useState:Pn,useEffect:Bn,useRef:Rn}=wp.element,{PanelBody:zn,PanelRow:Hn,SelectControl:On,ToggleControl:Fn,ComboboxControl:Un,Spinner:Gn}=wp.components,Zn=Un||wp.components.__experimentalComboboxControl,Vn=e=>{const{attributes:{productId:t,showImage:n,showDescription:o,showPrice:r,showButton:i,template:s,backgroundColor:l,contentColor:c,pricingColor:d},setAttributes:u}=e,[m,p]=Pn([]),[f,g]=Pn(""),[h,y]=Pn(!1),_=Rn(null),I=wp.apiFetch,{addQueryArgs:b}=wp.url,w=fn(),x=[{value:"left",label:Ln("Image Left")},{value:"top",label:Ln("Image Top")},{value:"none",label:Ln("No Image")}];Bn(()=>(t&&C(t),()=>{_.current&&clearTimeout(_.current)}),[]);const v=(e={})=>{y(!0),I({path:b(w,{per_page:25,...e})}).then(e=>{p(In(e).products)}).catch(()=>{p([])}).finally(()=>{y(!1)})},C=e=>{e&&I({path:b(w,{product_id:e})}).then(e=>{const t=bn(e);g(t?.label||t?.name||"")}).catch(()=>{g("")})},S=e=>{if(!e)return;const t=m.find(t=>String(t.id)===String(e));t&&g(t.label||t.name||""),u({productId:parseInt(e,10)})},N=m.map(e=>({value:String(e.id),label:e.label||e.name})),j=t?String(t):null;j&&f&&!N.find(e=>e.value===j)&&N.unshift({value:j,label:f});const M=[{value:c,onChange:e=>u({contentColor:e}),label:Ln("Content Color")},{value:l,onChange:e=>u({backgroundColor:e}),label:Ln("Background Color")},{value:d,onChange:e=>u({pricingColor:e}),label:Ln("Price Color")}];return t?(0,a.jsxs)(Dn,{children:[(0,a.jsx)(zn,{title:Ln("Template Settings"),initialOpen:!0,children:(0,a.jsx)(Hn,{children:(0,a.jsxs)("div",{className:"fc-latest-products-settings",children:[(0,a.jsxs)("div",{style:{marginBottom:"12px"},children:[Zn?(0,a.jsx)(Zn,{__next40pxDefaultSize:!0,__nextHasNoMarginBottom:!0,label:Ln("Select Product"),value:j,options:N,onChange:S,onFilterValueChange:e=>{_.current&&clearTimeout(_.current),_.current=setTimeout(()=>{v(e?{search:e}:{})},1200)},onFocus:()=>{m.length||v()},placeholder:Ln("Search product"),expandOnFocus:!0}):(0,a.jsx)(On,{__next40pxDefaultSize:!0,__nextHasNoMarginBottom:!0,label:Ln("Select Product"),value:j,options:[{value:"",label:Ln("Select a Product")},...N],onChange:S}),h&&(0,a.jsx)("div",{style:{marginTop:"8px"},children:(0,a.jsx)(Gn,{})})]}),(0,a.jsx)(On,{__next40pxDefaultSize:!0,__nextHasNoMarginBottom:!0,label:Ln("Design Template"),value:s,options:x,onChange:e=>{const t={template:e};"none"===e&&(t.showImage=!1),u(t)}}),(0,a.jsx)(Fn,{__nextHasNoMarginBottom:!0,label:Ln("Show Image"),checked:n,onChange:e=>u({showImage:e})}),(0,a.jsx)(Fn,{__nextHasNoMarginBottom:!0,label:Ln("Show Description"),checked:o,onChange:e=>u({showDescription:e})}),(0,a.jsx)(Fn,{__nextHasNoMarginBottom:!0,label:Ln("Show Price"),checked:r,onChange:e=>u({showPrice:e})}),(0,a.jsx)(Fn,{__nextHasNoMarginBottom:!0,label:Ln("Show Button"),checked:i,onChange:e=>u({showButton:e})})]})})}),(0,a.jsx)("div",{className:"fc-latest-products-content-color-settings",children:(0,a.jsx)(An,{title:Ln("Customization"),colorSettings:M})})]}):null},{RadioControl:Wn,Spinner:Qn}=wp.components,{InnerBlocks:Yn}=wp.blockEditor,{useState:Jn,useEffect:$n}=wp.element,{__:qn}=wp.i18n,Kn=e=>{const{clientId:t,attributes:{productId:n,showImage:o,showDescription:r,showPrice:i,showButton:s,buttonText:l,customImage:c,template:d,backgroundColor:u,contentColor:m,pricingColor:p},setAttributes:f}=e,[g,h]=Jn([]),[y,_]=Jn(""),[I,b]=Jn(""),[w,x]=Jn({}),[v,C]=Jn(!1),[S,N]=Jn(!1),j=wp.apiFetch,{addQueryArgs:M}=wp.url,k=fn();$n(()=>{n?P(n):L()},[n]);const{useDispatch:T,useSelect:E}=wp.data,{updateBlockAttributes:D}=T("core/block-editor"),A=E(e=>e("core/block-editor").getBlocks(t),[t]);$n(()=>{w.permalink&&A.forEach(e=>{"core/buttons"===e.name&&e.innerBlocks.forEach(e=>{"core/button"===e.name&&D(e.clientId,{url:w.permalink})})})},[w.permalink]);const L=(e={})=>{N(!0),j({path:M(k,{per_page:10,...e})}).then(e=>{const t=In(e).products;h(t)}).catch(()=>{h([])}).finally(()=>{N(!1)})},P=e=>{const t=parseInt(e,10);t&&(C(!0),j({path:M(k,{product_id:t})}).then(e=>{const t=bn(e);t&&(x(t),b(String(t.id)),f({productId:t.id}))}).catch(()=>{x({})}).finally(()=>{C(!1)}))},B=c||(w&&w.image?w.image:""),R=o&&"none"!==d&&B?d:"none",z={backgroundColor:u,color:m},H=["fcw_p",`fcw_template_${R}`,v?"fc_product_loading":""].filter(Boolean).join(" "),O=["fcw_search_box",v?"fc_product_loading":""].filter(Boolean).join(" ");return(0,a.jsxs)("div",{children:[v?(0,a.jsxs)("div",{style:z,className:"fc_woo_loader",children:[(0,a.jsx)(Qn,{}),(0,a.jsx)("h3",{children:qn("Loading product")})]}):null,w.id&&n?(0,a.jsxs)("div",{style:z,className:H,children:["none"!==R?(0,a.jsx)("div",{className:"fcw_image",children:(0,a.jsx)("img",{src:B,alt:""})}):null,(0,a.jsxs)("div",{className:"fcw_p_content",children:[(0,a.jsx)("h2",{style:{color:m||void 0},className:"fcw_p_title",dangerouslySetInnerHTML:{__html:w.name||""}}),r?(0,a.jsx)("div",{style:{color:m||void 0},className:"fcw_p_desc",dangerouslySetInnerHTML:{__html:w.short_description||""}}):null,i?(0,a.jsx)("div",{style:{color:p||void 0},className:"fcw_p_price",dangerouslySetInnerHTML:{__html:ze(w.price_html,qn("Free"))}}):null,s?(0,a.jsx)("div",{className:"fcb_p_button",children:(0,a.jsx)(Yn,{template:[["core/buttons",{},[["core/button",{text:l,url:w.permalink||"#",align:"left"}]]]],templateLock:"all"})}):null]})]}):(0,a.jsxs)("div",{className:O,children:[(0,a.jsx)("h4",{children:qn("Search and Select a Product")}),(0,a.jsx)("hr",{}),(0,a.jsxs)("div",{style:{marginBottom:"25px"},className:"fluent-single-product-search-bar",children:[(0,a.jsx)("div",{children:(0,a.jsx)("input",{placeholder:qn("Product name or id:1122"),style:{height:"36px"},value:y,onChange:e=>_(e.target.value),onKeyDown:e=>{"Enter"!==e.key&&""!==e.target.value||L({search:y})}})}),(0,a.jsx)("button",{style:{height:"36px"},onClick:()=>L({search:y}),children:qn("Search")})]}),S?(0,a.jsx)("h2",{children:(0,a.jsx)(Qn,{})}):(0,a.jsx)("div",{className:"fcw_results",children:g.length?(0,a.jsx)(Wn,{selected:I,options:g.map(e=>({value:String(e.id),label:e.label||e.name})),onChange:e=>b(e)}):(0,a.jsx)("div",{className:"fcw_products_not_found",children:(0,a.jsx)("h2",{children:qn("No products found!")})})}),(0,a.jsx)("button",{type:"button",style:{marginTop:"20px"},className:"components-button is-primary",disabled:!I,onClick:()=>P(I),children:qn("Done")})]})]})},{Fragment:Xn}=wp.element,{__:eo}=wp.i18n,to={productId:{type:"number",default:null},showImage:{type:"boolean",default:!0},showDescription:{type:"boolean",default:!0},showPrice:{type:"boolean",default:!0},showButton:{type:"boolean",default:!0},buttonText:{type:"string",default:eo("Buy Now")},customImage:{type:"string",default:""},backgroundColor:{type:"string",default:"#fffeeb"},contentColor:{type:"string",default:""},pricingColor:{type:"string",default:""},template:{type:"string",default:"left"}},no=wp.element.createElement,{__:oo}=wp.i18n,{registerBlockType:ro}=wp.blocks,io=window.fcrmBlockEditorConfig?.modules||{},ao=[{attributes:{...to,buttonColor:{type:"string",default:"#ffffff"},buttonBG:{type:"string",default:"#2a363d"}},save:Qe},{attributes:to,save:()=>(0,a.jsx)("div",{children:(0,a.jsx)(v.InnerBlocks.Content,{})})}],so=no("svg",{width:20,height:20,viewBox:"0 0 24 24"},no("rect",{x:0,y:0,width:24,height:24,rx:4,fill:"#0b4dbb"}),no("path",{fill:"#fff",d:"M11.2 16.4H4.2l1.1-2.5c.3-.7 1-1.2 1.8-1.2h8.3l-.6 1.3c-.6 1.5-2 2.4-3.6 2.4z"}),no("path",{fill:"#fff",d:"M17 11.2H7.2l.6-1.3c.6-1.5 2-2.4 3.6-2.4h8.5l-1.1 2.5c-.3.7-1 1.2-1.8 1.2z"}));!1!==io.hasFluentCart&&ro("fluent-crm/cart-product",{apiVersion:3,title:oo("FluentCart Product (Single)"),description:oo("Single FluentCart product block for your email"),category:"layout",icon:so,keywords:[oo("fluentcart"),oo("product"),oo("single")],supports:{align:["wide","full"],html:!0},attributes:to,deprecated:ao,edit:e=>{const t=(0,v.useBlockProps)({className:"fluent-single-product-block"});return(0,a.jsxs)(Xn,{children:[(0,a.jsx)("div",{...t,children:(0,a.jsx)(Kn,{clientId:e.clientId,attributes:e.attributes,setAttributes:e.setAttributes})}),(0,a.jsx)(Vn,{attributes:e.attributes,setAttributes:e.setAttributes})]})},save:Qe});const lo=function({children:e,...t}){return(0,a.jsx)("div",{className:"fc-cond-section",...t,children:(0,a.jsx)("div",{className:"fc-cond-blocks",children:e})})},co=window.fcrmBlockEditorConfig?.endpoints?.tags||"fluent-crm/v2/reports/options?fields=tags",uo=e=>Array.isArray(e)?e.map(e=>{const t=String(e.id??e.value??"");return{id:t,title:e.title||e.label||t}}).filter(e=>!!e.id):[],mo=()=>{const e=window.fcrmEditorBoot?.available_tags||window.fcAdmin?.available_tags||window._fcrm_available_tags||[];return uo(e)},po=(e,t)=>{window.wp&&window.wp.apiFetch&&(t(!0),window.wp.apiFetch({path:co}).then(t=>{const n=uo(t?.options?.tags||[]);window._fcrm_available_tags=n,e(n)}).catch(()=>{}).finally(()=>t(!1)))},fo=()=>!1!==(window.fcrmBlockEditorConfig?.modules||{}).hasFluentCampaign,go=window.wp.components,{__:ho,_x}=wp.i18n,{registerBlockType:yo}=wp.blocks,_o=[{attributes:{condition_type:{type:"string",default:"show_if_tag_exist"},tag_ids:{type:"array",default:[]}},save:()=>(0,a.jsx)(lo,{children:(0,a.jsx)(v.InnerBlocks.Content,{})})}];yo("fluentcrm/conditional-group",{apiVersion:3,title:ho("Conditional Section"),description:ho("Add a section that shows content conditionally by subscriber tags."),category:"layout",icon:"welcome-widgets-menus",keywords:[_x("conditional"),_x("section")],supports:{align:["wide","full"],anchor:!0,html:!1},attributes:{condition_type:{type:"string",default:"show_if_tag_exist"},tag_ids:{type:"array",default:[]}},deprecated:_o,edit:e=>{const{attributes:t,setAttributes:n}=e,{condition_type:o,tag_ids:r}=t,[s,l]=(0,i.useState)(mo()),[c,d]=(0,i.useState)(!1),u=fo(),m=(0,i.useMemo)(()=>Array.isArray(r)?r.map(e=>String(e)):[],[r]);(0,i.useEffect)(()=>{s.length||po(l,d)},[]);const p=(0,i.useMemo)(()=>{const e={};return s.forEach(t=>{e[t.id]=t.title}),e},[s]),f=(0,i.useMemo)(()=>{const e={};return s.forEach(t=>{e[t.title]=t.id}),e},[s]),g=(0,i.useMemo)(()=>m.map(e=>p[e]||e),[m,p]),h=(0,i.useMemo)(()=>s.map(e=>e.title),[s]),y=ho("show_if_tag_exist"===o?"Shown to subscribers with any selected tag.":"Hidden from subscribers with any selected tag.","fluent-crm"),_=(0,v.useBlockProps)({className:"fc-cond-section"});return(0,a.jsxs)(i.Fragment,{children:[(0,a.jsx)(v.InspectorControls,{children:(0,a.jsxs)(go.PanelBody,{title:ho("Conditional Settings"),children:[(0,a.jsx)(go.SelectControl,{__next40pxDefaultSize:!0,__nextHasNoMarginBottom:!0,label:ho("Condition Type"),value:o,onChange:e=>n({condition_type:e||"show_if_tag_exist"}),options:[{value:"show_if_tag_exist",label:ho("Show if in selected tags","fluent-crm")},{value:"show_if_tag_not_exist",label:ho("Show if not in selected tags","fluent-crm")}]}),c?(0,a.jsx)(go.Spinner,{}):(0,a.jsx)(go.FormTokenField,{label:ho("Tags","fluent-crm"),value:g,suggestions:h,onChange:e=>{if(!u)return;const t=e.map(e=>f[e]).filter(Boolean);n({tag_ids:t})},disabled:!u,__experimentalExpandOnFocus:!0,__experimentalAutoSelectFirstMatch:!0,__experimentalShowHowTo:!1,__next40pxDefaultSize:!0}),u?(0,a.jsx)("p",{className:"components-base-control__help",children:y}):(0,a.jsx)(go.Notice,{status:"warning",isDismissible:!1,children:ho("Conditional Section requires FluentCRM Pro.","fluent-crm")})]})}),(0,a.jsx)("div",{..._,children:(0,a.jsx)("div",{className:"fc-cond-blocks",children:(0,a.jsx)(v.InnerBlocks,{})})})]})},save:()=>{const e=v.useBlockProps.save({className:"fc-cond-section"});return(0,a.jsx)("div",{...e,children:(0,a.jsx)("div",{className:"fc-cond-blocks",children:(0,a.jsx)(v.InnerBlocks.Content,{})})})}});const Io=window.wp.apiFetch;var bo=e.n(Io);const{__:wo,_x:xo}=wp.i18n,{registerBlockType:vo,createBlock:Co}=wp.blocks,So=wp.element.createElement,No="fcrm_ai_writing_tone",jo="fcrm_ai_writing_length",Mo=["Professional","Casual","Friendly","Urgent","Persuasive","Formal"],ko=["Short","Medium","Long"];function To(e,t,n){try{const n=localStorage.getItem(e);if(n&&t.includes(n))return n}catch(e){}return n}const Eo=So("svg",{width:20,height:20,viewBox:"0 0 20 20",fill:"none",xmlns:"http://www.w3.org/2000/svg"},So("path",{d:"M9.9991 3C10.1943 3 10.3841 3.06406 10.5397 3.18199C10.6564 3.2705 10.7492 3.38584 10.8105 3.51729L10.8616 3.65356L10.8642 3.66522L11.88 7.6072C11.9117 7.73016 11.9758 7.84279 12.0656 7.93263C12.1554 8.02249 12.268 8.08733 12.391 8.11911L16.3339 9.13486L16.3429 9.13665V9.13755C16.4845 9.17662 16.6136 9.2493 16.7194 9.34913L16.8171 9.45761L16.896 9.58043C16.9642 9.70896 17 9.85305 17 10C17 10.1959 16.9358 10.3865 16.8171 10.5424C16.6984 10.6982 16.5317 10.8104 16.3429 10.8624L16.3339 10.8651L12.391 11.8809C12.268 11.9127 12.1554 11.9775 12.0656 12.0674C11.9758 12.1572 11.9117 12.2698 11.88 12.3928L10.8633 16.3348L10.8607 16.3464C10.808 16.5344 10.6952 16.7 10.5397 16.818C10.3841 16.9361 10.1935 17 9.99821 17C9.80298 16.9999 9.61315 16.936 9.45761 16.818C9.30206 16.7 9.18933 16.5344 9.13665 16.3464L9.13307 16.3348L8.11732 12.3928L8.08773 12.3023C8.05209 12.2146 7.99908 12.1347 7.93174 12.0674C7.84184 11.9775 7.72941 11.9126 7.6063 11.8809L3.66432 10.8642C3.65986 10.8631 3.6553 10.8619 3.65087 10.8607C3.46355 10.8075 3.29852 10.695 3.1811 10.5397C3.09296 10.4231 3.035 10.2872 3.01165 10.1443L3 10L3.01165 9.85566C3.035 9.71281 3.09296 9.57693 3.1811 9.4603C3.29852 9.30496 3.46354 9.19251 3.65087 9.13934L3.66432 9.13576L7.6063 8.11821C7.7293 8.0865 7.84186 8.0224 7.93174 7.93263C8.02162 7.84281 8.0855 7.73022 8.11732 7.6072L9.13397 3.66522L9.13665 3.65356H9.13755C9.19024 3.4656 9.30297 3.29997 9.4585 3.18199L9.58133 3.104C9.7095 3.0363 9.8527 3.00001 9.9991 3ZM9.229 7.89408C9.14588 8.21551 8.9779 8.50928 8.74308 8.74398C8.50825 8.97865 8.21464 9.14607 7.89319 9.229L4.90599 9.9991L7.89319 10.7701L8.01242 10.8051C8.28729 10.896 8.53756 11.0505 8.74308 11.256C8.94862 11.4616 9.10316 11.7118 9.19403 11.9867L9.229 12.1059L9.99821 15.0931L10.7692 12.1059C10.8522 11.7844 11.0195 11.4909 11.2542 11.256C11.4891 11.0211 11.7833 10.8531 12.105 10.7701L15.0931 10L12.105 9.22989C11.7833 9.14691 11.4891 8.97888 11.2542 8.74398C11.0488 8.53848 10.895 8.2881 10.8042 8.01332L10.7692 7.89408L9.9991 4.90779L9.229 7.89408ZM4.28381 14.4978V14.4297H4.21568C3.89888 14.4297 3.64205 14.1727 3.64191 13.8559C3.64191 13.539 3.89879 13.2821 4.21568 13.2821H4.28381V13.2131C4.28381 12.8964 4.54089 12.6396 4.85758 12.6393C5.17447 12.6393 5.43135 12.8962 5.43135 13.2131V13.2821H5.50038C5.81727 13.2821 6.07415 13.539 6.07415 13.8559C6.07401 14.1727 5.81718 14.4297 5.50038 14.4297H5.43135V14.4978C5.43135 14.8147 5.17447 15.0716 4.85758 15.0716C4.54089 15.0714 4.28381 14.8146 4.28381 14.4978ZM14.566 6.78689V6.07595H13.8541C13.5375 6.07575 13.2806 5.81875 13.2804 5.50218C13.2804 5.18541 13.5374 4.9286 13.8541 4.92841H14.566V4.21657C14.566 3.89969 14.8228 3.6428 15.1397 3.6428C15.4566 3.64282 15.7135 3.8997 15.7135 4.21657V4.92841H16.4253C16.742 4.92863 16.9991 5.18543 16.9991 5.50218C16.9989 5.81873 16.7419 6.07572 16.4253 6.07595H15.7135V6.78689C15.7135 7.10376 15.4566 7.36064 15.1397 7.36066C14.8228 7.36066 14.566 7.10377 14.566 6.78689Z",fill:"currentColor"})),Do=window.fcrmEditorBoot||{};Do.ai_writing&&Do.ai_writing.enabled&&vo("fluent-crm/ai-writer",{apiVersion:3,title:wo("Write with AI","fluent-crm"),description:wo("Generate a complete email section with AI. Type a prompt and the block will be replaced with generated content.","fluent-crm"),category:"text",icon:Eo,keywords:[xo("ai"),xo("writer"),xo("generate"),xo("prompt")],supports:{html:!1,reusable:!1,multiple:!0},attributes:{prompt:{type:"string",default:""}},edit:({attributes:e,setAttributes:t,clientId:o})=>{const{prompt:s}=e,[l,c]=(0,i.useState)(!1),[d,u]=(0,i.useState)(""),[m,p]=(0,i.useState)(()=>To(No,Mo,"Professional")),[f,g]=(0,i.useState)(()=>To(jo,ko,"Medium")),h=(0,v.useBlockProps)({className:"fcrm-ai-writer-block"}),y=(0,i.useCallback)(()=>{s&&s.trim()&&(u(""),c(!0),bo()({path:"fluent-crm/v2/ai/generate-email-body",method:"POST",data:{prompt:s.trim(),tone:m.toLowerCase(),length:f.toLowerCase(),audience:"",cta:"",context:{design_template:(window.fcrmEditorBoot||{}).current_design_template||"",editor_type:"block_editor",output_format:"gutenberg_blocks",campaign_type:((window.fcrmEditorBoot||{}).entity||{}).block_type||"",has_existing_body:"yes"}}}).then(e=>{if(e&&e.email_body){const i=(t=e.email_body)?t.includes("\x3c!-- wp:")?(0,r.parse)(t):(0,r.rawHandler)({HTML:t})||(0,r.pasteHandler)({HTML:t,mode:"BLOCKS"})||(0,r.pasteHandler)({plainText:t,mode:"BLOCKS"}):[];i&&i.length?(0,n.dispatch)("core/block-editor").replaceBlocks([o],i):u(wo("Could not generate content. Try a different prompt.","fluent-crm"))}else u(wo("No content was generated. Please try again.","fluent-crm"));var t}).catch(e=>{u(e&&e.message||wo("An error occurred. Please try again.","fluent-crm"))}).finally(()=>{c(!1)}))},[s,m,f,o]),_=(0,i.useCallback)(e=>{"Enter"===e.key&&(e.metaKey||e.ctrlKey)&&(e.preventDefault(),y())},[y]);return(0,i.useCallback)(()=>{(0,n.dispatch)("core/block-editor").replaceBlocks([o],[Co("core/paragraph")])},[o]),(0,a.jsx)("div",{...h,children:(0,a.jsxs)("div",{className:"fcrm-ai-writer-block__inner",children:[(0,a.jsxs)("div",{className:"fcrm-ai-writer-block__header",children:[(0,a.jsx)("span",{className:"fcrm-ai-writer-block__icon",children:Eo}),(0,a.jsx)("span",{className:"fcrm-ai-writer-block__title",children:wo("Write with AI","fluent-crm")}),(0,a.jsxs)("div",{className:"fcrm-ai-writer-block__controls",children:[(0,a.jsx)("select",{className:"fcrm-ai-writer-block__select","aria-label":wo("Tone","fluent-crm"),value:m,onChange:e=>{p(e.target.value);try{localStorage.setItem(No,e.target.value)}catch(e){}},children:Mo.map(e=>(0,a.jsx)("option",{value:e,children:e},e))}),(0,a.jsx)("select",{className:"fcrm-ai-writer-block__select","aria-label":wo("Length","fluent-crm"),value:f,onChange:e=>{g(e.target.value);try{localStorage.setItem(jo,e.target.value)}catch(e){}},children:ko.map(e=>(0,a.jsx)("option",{value:e,children:e},e))})]})]}),(0,a.jsx)("textarea",{className:"fcrm-ai-writer-block__prompt",value:s||"",onChange:e=>t({prompt:e.target.value}),onKeyDown:_,placeholder:wo("Describe the email you want to write... (Ctrl+Enter to generate)","fluent-crm"),rows:3,disabled:l}),d?(0,a.jsx)("div",{className:"fcrm-ai-writer-block__error",children:d}):null,(0,a.jsxs)("div",{className:"fcrm-ai-writer-block__footer",children:[(0,a.jsx)("span",{className:"fcrm-ai-writer-block__tip",children:wo("Tip: Be specific about your audience and goal","fluent-crm")}),l?(0,a.jsxs)("div",{className:"components-button fcrm-ai-writer-block__generate-button fcrm-ai-writer-block__loading",children:[(0,a.jsx)(go.Spinner,{}),wo("Generating...","fluent-crm")]}):(0,a.jsxs)("div",{className:"fcrm_ai_button_anim_wrapper",children:[s||s&&s.trim()?(0,a.jsx)("div",{className:"fcrm_ai_button_anim",children:(0,a.jsx)("div",{className:"fcrm_ai_button_anim_inner"})}):null,(0,a.jsxs)(go.Button,{variant:"primary",disabled:!s||!s.trim(),onClick:y,className:"fcrm_ai_button fcrm-ai-writer-block__generate-button",children:[(0,a.jsx)("span",{className:"icon",children:(0,a.jsx)("svg",{width:"20",height:"20",viewBox:"0 0 20 20",fill:"none",xmlns:"http://www.w3.org/2000/svg",children:(0,a.jsx)("path",{d:"M9.9991 3C10.1943 3 10.3841 3.06406 10.5397 3.18199C10.6564 3.2705 10.7492 3.38584 10.8105 3.51729L10.8616 3.65356L10.8642 3.66522L11.88 7.6072C11.9117 7.73016 11.9758 7.84279 12.0656 7.93263C12.1554 8.02249 12.268 8.08733 12.391 8.11911L16.3339 9.13486L16.3429 9.13665V9.13755C16.4845 9.17662 16.6136 9.2493 16.7194 9.34913L16.8171 9.45761L16.896 9.58043C16.9642 9.70896 17 9.85305 17 10C17 10.1959 16.9358 10.3865 16.8171 10.5424C16.6984 10.6982 16.5317 10.8104 16.3429 10.8624L16.3339 10.8651L12.391 11.8809C12.268 11.9127 12.1554 11.9775 12.0656 12.0674C11.9758 12.1572 11.9117 12.2698 11.88 12.3928L10.8633 16.3348L10.8607 16.3464C10.808 16.5344 10.6952 16.7 10.5397 16.818C10.3841 16.9361 10.1935 17 9.99821 17C9.80298 16.9999 9.61315 16.936 9.45761 16.818C9.30206 16.7 9.18933 16.5344 9.13665 16.3464L9.13307 16.3348L8.11732 12.3928L8.08773 12.3023C8.05209 12.2146 7.99908 12.1347 7.93174 12.0674C7.84184 11.9775 7.72941 11.9126 7.6063 11.8809L3.66432 10.8642C3.65986 10.8631 3.6553 10.8619 3.65087 10.8607C3.46355 10.8075 3.29852 10.695 3.1811 10.5397C3.09296 10.4231 3.035 10.2872 3.01165 10.1443L3 10L3.01165 9.85566C3.035 9.71281 3.09296 9.57693 3.1811 9.4603C3.29852 9.30496 3.46354 9.19251 3.65087 9.13934L3.66432 9.13576L7.6063 8.11821C7.7293 8.0865 7.84186 8.0224 7.93174 7.93263C8.02162 7.84281 8.0855 7.73022 8.11732 7.6072L9.13397 3.66522L9.13665 3.65356H9.13755C9.19024 3.4656 9.30297 3.29997 9.4585 3.18199L9.58133 3.104C9.7095 3.0363 9.8527 3.00001 9.9991 3ZM9.229 7.89408C9.14588 8.21551 8.9779 8.50928 8.74308 8.74398C8.50825 8.97865 8.21464 9.14607 7.89319 9.229L4.90599 9.9991L7.89319 10.7701L8.01242 10.8051C8.28729 10.896 8.53756 11.0505 8.74308 11.256C8.94862 11.4616 9.10316 11.7118 9.19403 11.9867L9.229 12.1059L9.99821 15.0931L10.7692 12.1059C10.8522 11.7844 11.0195 11.4909 11.2542 11.256C11.4891 11.0211 11.7833 10.8531 12.105 10.7701L15.0931 10L12.105 9.22989C11.7833 9.14691 11.4891 8.97888 11.2542 8.74398C11.0488 8.53848 10.895 8.2881 10.8042 8.01332L10.7692 7.89408L9.9991 4.90779L9.229 7.89408ZM4.28381 14.4978V14.4297H4.21568C3.89888 14.4297 3.64205 14.1727 3.64191 13.8559C3.64191 13.539 3.89879 13.2821 4.21568 13.2821H4.28381V13.2131C4.28381 12.8964 4.54089 12.6396 4.85758 12.6393C5.17447 12.6393 5.43135 12.8962 5.43135 13.2131V13.2821H5.50038C5.81727 13.2821 6.07415 13.539 6.07415 13.8559C6.07401 14.1727 5.81718 14.4297 5.50038 14.4297H5.43135V14.4978C5.43135 14.8147 5.17447 15.0716 4.85758 15.0716C4.54089 15.0714 4.28381 14.8146 4.28381 14.4978ZM14.566 6.78689V6.07595H13.8541C13.5375 6.07575 13.2806 5.81875 13.2804 5.50218C13.2804 5.18541 13.5374 4.9286 13.8541 4.92841H14.566V4.21657C14.566 3.89969 14.8228 3.6428 15.1397 3.6428C15.4566 3.64282 15.7135 3.8997 15.7135 4.21657V4.92841H16.4253C16.742 4.92863 16.9991 5.18543 16.9991 5.50218C16.9989 5.81873 16.7419 6.07572 16.4253 6.07595H15.7135V6.78689C15.7135 7.10376 15.4566 7.36064 15.1397 7.36066C14.8228 7.36066 14.566 7.10377 14.566 6.78689Z",fill:"currentColor"})})}),wo("Generate with AI","fluent-crm")]})]})]})]})})},save:()=>null});const Ao=window.wp.editor,Lo=Object.freeze({LAYOUT_CHANGE:"EDITOR_LAYOUT_CHANGE",FULLSCREEN_TOGGLE:"EDITOR_FULLSCREEN_TOGGLE",OPEN_EMAIL_PREVIEW:"EDITOR_OPEN_EMAIL_PREVIEW",OPEN_TEMPLATES:"EDITOR_OPEN_TEMPLATES",OPEN_SAVE_TEMPLATE:"EDITOR_OPEN_SAVE_TEMPLATE",SAVE_DRAFT:"EDITOR_SAVE_DRAFT",BACK:"EDITOR_BACK",NEXT:"EDITOR_NEXT",RECOVERY_NOTICE:"EDITOR_RECOVERY_NOTICE",STYLE_CONFIG_CHANGE:"EDITOR_STYLE_CONFIG_CHANGE",FOOTER_SETTINGS_CHANGE:"EDITOR_FOOTER_SETTINGS_CHANGE"}),Po=Object.freeze({LAYOUT_SYNC:"LAYOUT_SYNC",UPDATE_EDITOR:"UPDATE_EDITOR",STYLE_CONFIG_SYNC:"STYLE_CONFIG_SYNC",FOOTER_SETTINGS_SYNC:"FOOTER_SETTINGS_SYNC",INSERT_SMARTCODE:"INSERT_SMARTCODE"}),Bo=(e,t={})=>{window.parent&&window.parent.postMessage({action:e,...t},"*")},Ro=e=>!(!e||e.action!==Po.LAYOUT_SYNC||"string"!=typeof e.design_template),zo=e=>!(!e||e.action!==Po.STYLE_CONFIG_SYNC||!e.template_config||"object"!=typeof e.template_config),Ho=(e,t)=>{if("undefined"==typeof window)return()=>{};const n=(()=>{const e=window.fcrmEditorBoot||{},t=e.parent_origin||e.parentOrigin||"";if(t)try{return new URL(String(t),window.location.origin).origin}catch(e){}try{if(document.referrer)return new URL(document.referrer).origin}catch(e){}return window.location.origin})(),o=o=>{if(!window.parent||o.source!==window.parent)return;if(!o.origin||o.origin!==n)return;const r=(e=>e&&"object"==typeof e&&e.data?e.data:null)(o);r&&("function"!=typeof e||e(r,o))&&t(r,o)};return window.addEventListener("message",o),()=>window.removeEventListener("message",o)},Oo=/%(\d*)s|%d/g;function Fo(e,...t){return function(e,t=[]){if(!t.length)return e;let n=0;return String(e).replace(Oo,(e,o)=>{if(o){const n=parseInt(o,10)-1;return n
',""),children:(0,x.__)("Link","fluent-crm")}),(0,a.jsx)("button",{type:"button",onClick:()=>b("
    \n
  • ","
  • \n
"),children:(0,x.__)("UL","fluent-crm")}),(0,a.jsx)("button",{type:"button",onClick:()=>b("
    \n
  1. ","
  2. \n
"),children:(0,x.__)("OL","fluent-crm")}),(0,a.jsx)("button",{type:"button",onClick:()=>b("
  • ","
  • "),children:(0,x.__)("LI","fluent-crm")}),(0,a.jsx)("button",{type:"button",onClick:()=>b("",""),children:(0,x.__)("Code","fluent-crm")})]}),(0,a.jsx)("textarea",{ref:f,value:u,onChange:e=>{m(e.target.value),o.current(e.target.value)},className:"fcrm-footer-text-mode__textarea",rows:8})]})]})},Ri=()=>{const[e,t]=(0,i.useState)(!1),[n,o]=(0,i.useState)(()=>Ti());(0,i.useEffect)(()=>{const e=e=>{const t=e.data;if("FOOTER_SETTINGS_SYNC"===t?.action){const e=t.footer_settings||null;window._fcrmFooterSettings=e,o(e?{...e}:null)}};window.addEventListener("message",e);const t=window._fcrmFooterSettings;return t&&o({...t}),()=>window.removeEventListener("message",e)},[]),(0,i.useEffect)(()=>{if(n)return window._fcrmOpenFooterSettings=()=>t(!0),()=>{delete window._fcrmOpenFooterSettings};delete window._fcrmOpenFooterSettings},[!!n]),(0,i.useEffect)(()=>{let e=null,t=null,n=null;const o=setTimeout(()=>{Li(document);const e=Pi();e&&Li(e)},1e3);return e=new MutationObserver(()=>{n||(n=setTimeout(()=>{if(n=null,!Ti())return;const e=Pi();e&&!e.getElementById(Mi)&&Li(e),document.getElementById(Mi)||Li(document)},500))}),e.observe(document.body,{childList:!0,subtree:!0}),t=setInterval(()=>{const e=Pi();e&&(Li(e),clearInterval(t),t=null)},2e3),()=>{clearTimeout(o),n&&clearTimeout(n),e&&e.disconnect(),t&&clearInterval(t)}},[]),(0,i.useEffect)(()=>{n&&(window._fcrmFooterSettings=n),(()=>{Li(document);const e=Pi();e&&Li(e)})()},[n]);const r=(0,i.useCallback)((e,t)=>{o(n=>{if(!n)return n;const o={...n,[e]:t};return"custom_footer"!==e||"yes"!==t||n.footer_content||(o.footer_content=Ei()),Bo(Lo.FOOTER_SETTINGS_CHANGE,{footer_settings:o}),o})},[]);if(!n)return null;const s="yes"===n.disable_footer,l="yes"===n.custom_footer;return e?(0,a.jsx)(go.Modal,{title:"Email Footer",onRequestClose:()=>t(!1),className:"fcrm-footer-settings-modal",children:(0,a.jsxs)(go.__experimentalVStack,{spacing:4,className:"fcrm-footer-settings-modal__content",children:[(0,a.jsx)(go.ToggleControl,{label:"Show Email Footer",checked:!s,onChange:e=>r("disable_footer",e?"no":"yes"),__nextHasNoMarginBottom:!0}),!s&&(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)("div",{className:"fcrm-footer-settings__divider"}),(0,a.jsx)(go.RadioControl,{selected:n.custom_footer,options:[{label:"Global Footer",value:"no"},{label:"Custom Footer",value:"yes"}],onChange:e=>r("custom_footer",e)}),!l&&(0,a.jsx)("p",{className:"fcrm-footer-settings__hint",children:"Uses your default email footer from global settings."}),l&&(0,a.jsx)(Bi,{value:n.footer_content,onChange:e=>r("footer_content",e)}),(0,a.jsx)("div",{className:"fcrm-footer-settings__divider"}),(0,a.jsxs)(go.__experimentalHStack,{alignment:"top",spacing:6,children:[(0,a.jsx)("div",{style:{flex:1},children:(0,a.jsx)(go.__experimentalNumberControl,{label:"Font Size (px)",value:parseInt(n.font_size,10)||14,onChange:e=>{const t=parseInt(e,10);r("font_size",t&&t>0?String(t):"")},min:8,max:24,__nextHasNoMarginBottom:!0})}),(0,a.jsx)("div",{style:{flex:1},children:(0,a.jsx)(go.BaseControl,{label:"Text Color",__nextHasNoMarginBottom:!0,children:(0,a.jsx)(go.Dropdown,{popoverProps:{placement:"bottom-start"},renderToggle:({isOpen:e,onToggle:t})=>(0,a.jsxs)("button",{onClick:t,"aria-expanded":e,className:"fcrm-color-toggle",type:"button",children:[(0,a.jsx)("span",{className:"fcrm-color-indicator",style:{background:n.font_color||"#9ca3af"}}),(0,a.jsx)("span",{className:"fcrm-color-hex",children:n.font_color||"#9ca3af"})]}),renderContent:()=>(0,a.jsx)(go.ColorPicker,{color:n.font_color||"#9ca3af",onChange:e=>r("font_color",e),enableAlpha:!1})})})})]}),(0,a.jsxs)(go.__experimentalHStack,{alignment:"top",spacing:6,children:[(0,a.jsx)("div",{style:{flex:1},children:(0,a.jsx)(go.BaseControl,{label:"Background Color",__nextHasNoMarginBottom:!0,children:(0,a.jsx)(go.Dropdown,{popoverProps:{placement:"bottom-start"},renderToggle:({isOpen:e,onToggle:t})=>{const o="transparent"!==(n.background_color||"transparent");return(0,a.jsxs)("div",{className:"fcrm-color-toggle-wrap",children:[(0,a.jsxs)("button",{onClick:t,"aria-expanded":e,className:"fcrm-color-toggle",type:"button",children:[(0,a.jsx)("span",{className:"fcrm-color-indicator",style:{background:n.background_color||"transparent"}}),(0,a.jsx)("span",{className:"fcrm-color-hex",children:n.background_color||"transparent"})]}),o&&(0,a.jsx)("button",{type:"button",className:"fcrm-color-toggle__clear","aria-label":"Clear background color",title:"Clear background color",onClick:()=>r("background_color","transparent"),children:(0,a.jsxs)("svg",{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 512 512",fill:"none",stroke:"currentColor",children:[(0,a.jsx)("line",{x1:"368",y1:"368",x2:"144",y2:"144",strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:"32"}),(0,a.jsx)("line",{x1:"368",y1:"144",x2:"144",y2:"368",strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:"32"})]})})]})},renderContent:()=>(0,a.jsx)(go.ColorPicker,{color:n.background_color&&"transparent"!==n.background_color?n.background_color:"#ffffff",onChange:e=>r("background_color",e),enableAlpha:!1})})})}),(0,a.jsx)("div",{style:{flex:1},children:(0,a.jsx)(go.__experimentalNumberControl,{label:"Footer Padding (px)",value:Di(n),onChange:e=>{const t=parseInt(e,10);r("footer_padding",!Number.isNaN(t)&&t>=0?String(t):"")},min:0,max:80,__nextHasNoMarginBottom:!0})})]})]}),s&&(0,a.jsx)("p",{className:"fcrm-footer-settings__hint",children:"Email footer is hidden for this email."}),(0,a.jsx)(go.Button,{variant:"primary",onClick:()=>t(!1),className:"fcrm-footer-settings__done",children:"Done"})]})}):null},zi=["content","text","value","caption"],Hi="fcrm_ai_writing_tone";function Oi(e){if(!e)return"";let t=e.replace(/<[^>]*>/g,"");return t=t.replace(/^### (.+)$/gm,"

    $1

    ").replace(/^## (.+)$/gm,"

    $1

    ").replace(/^# (.+)$/gm,"

    $1

    ").replace(/\*\*(.+?)\*\*/g,"$1").replace(/(?$1"),t.split(/\n{2,}/).map(e=>(e=e.trim())?/^/.test(e)?e:/^[-*] /m.test(e)?"
      "+e.split("\n").map(e=>e.replace(/^[-*]\s+/,"").trim()).filter(Boolean).map(e=>"
    • "+e+"
    • ").join("")+"
    ":/^\d+\.\s/m.test(e)?"
      "+e.split("\n").map(e=>e.replace(/^\d+\.\s+/,"").trim()).filter(Boolean).map(e=>"
    1. "+e+"
    2. ").join("")+"
    ":"

    "+e.replace(/\n/g,"
    ")+"

    ":"").join("")}function Fi(e){if("string"==typeof e)return e;if(e&&"object"==typeof e){if("function"==typeof e.toHTMLString)return e.toHTMLString();if("function"==typeof e.toString&&"[object Object]"!==e.toString())return e.toString()}return null}const Ui=[{key:"rewrite",label:(0,x.__)("Rewrite","fluent-crm"),description:(0,x.__)("Rewrite while keeping the same meaning","fluent-crm")},{key:"shorten",label:(0,x.__)("Shorten","fluent-crm"),description:(0,x.__)("Make it shorter and more concise","fluent-crm")},{key:"expand",label:(0,x.__)("Expand","fluent-crm"),description:(0,x.__)("Add more detail and engagement","fluent-crm")},{key:"fix_grammar",label:(0,x.__)("Fix Grammar","fluent-crm"),description:(0,x.__)("Fix grammar, spelling, and punctuation","fluent-crm")},{key:"custom",label:(0,x.__)("Custom Prompt","fluent-crm"),description:(0,x.__)("Describe what you want","fluent-crm")}],Gi=["Professional","Casual","Friendly","Urgent","Persuasive","Formal"];function Zi(e){if(!e)return null;if("core/list"===e.name){const t=function(e){if(!e.innerBlocks||!e.innerBlocks.length)return"";const t=!(!e.attributes||!e.attributes.ordered);return e.innerBlocks.map((e,n)=>{const o=e.attributes&&Fi(e.attributes.content)||"";return t?n+1+". "+o:"- "+o}).join("\n")}(e);return t?{textKey:"__list",content:t}:null}if(!e.attributes)return null;const t=zi.find(t=>{const n=e.attributes[t];return null!=n&&null!==Fi(n)});return t?{textKey:t,content:Fi(e.attributes[t])||""}:null}(0,t.registerPlugin)("fcrm-custom-document-info",{render:()=>{const e=window.fcrmEditorBoot||{},t="fcrm-custom-document-info/fcrm-email-body-panel";if((0,i.useEffect)(()=>{const e=(0,n.select)("core/editor"),o=(0,n.dispatch)("core/editor");e&&o&&"function"==typeof e.isEditorPanelOpened&&"function"==typeof o.toggleEditorPanelOpened&&(e.isEditorPanelOpened(t)||o.toggleEditorPanelOpened(t))},[]),e.fcrm_ui&&"compose"!==e.fcrm_ui)return null;const o=e.features||{};return!1===o.email_style_settings?o.sidebar_content?(0,a.jsx)(Ao.PluginDocumentSettingPanel,{name:"fcrm-email-body-panel",title:o.sidebar_panel_title||"",icon:"info-outline",className:"fcrm-email-body-panel-wrapper fcrm-info-panel",children:(0,a.jsx)("div",{className:"fcrm-sidebar-info-content",dangerouslySetInnerHTML:{__html:o.sidebar_content}})}):null:(0,a.jsx)(Ao.PluginDocumentSettingPanel,{name:"fcrm-email-body-panel",title:"",icon:"email-alt",className:"fcrm-email-body-panel-wrapper",children:(0,a.jsx)(oi,{})})}}),(0,t.registerPlugin)("fcrm-smartcode-toolbar-popover",{render:()=>{const[e,t]=(0,i.useState)(!1),[n,o]=(0,i.useState)(null),[r,s]=(0,i.useState)(""),l=()=>{t(!1),s("")};return(0,i.useEffect)(()=>{const e=()=>document.querySelector(".fcrm-compose-smartcodes"),n=()=>{const n=e();n&&(o(n),t(!0),s(""))},r=()=>{const n=e();n&&(o(n),t(e=>(e||s(""),!e)))};return window.addEventListener("fcrm-open-smartcodes",n),window.addEventListener("fcrm-toggle-smartcodes",r),()=>{window.removeEventListener("fcrm-open-smartcodes",n),window.removeEventListener("fcrm-toggle-smartcodes",r)}},[]),(0,i.useEffect)(()=>{if(!e)return;const t=e=>{const t=e.target;if(!(t instanceof Element))return;const o=!(!n||n!==t&&!n.contains(t)),r=!!t.closest(".fcrm-smartcode-toolbar-popover");o||r||l()},o=e=>{"Escape"===e.key&&l()};return document.addEventListener("mousedown",t,!0),document.addEventListener("touchstart",t,!0),document.addEventListener("keydown",o,!0),()=>{document.removeEventListener("mousedown",t,!0),document.removeEventListener("touchstart",t,!0),document.removeEventListener("keydown",o,!0)}},[e,n]),e&&n?(0,a.jsx)(ai,{anchor:n,onClose:l,onSelect:e=>{const t=li(e);(function(e){if(navigator.clipboard&&"function"==typeof navigator.clipboard.writeText)return navigator.clipboard.writeText(e).then(()=>!0).catch(()=>!1);if(document.queryCommandSupported&&document.queryCommandSupported("copy")){const t=document.createElement("textarea");t.value=e,t.style.position="fixed",document.body.appendChild(t),t.select(),t.setSelectionRange(0,t.value.length);let n=!1;try{n=document.execCommand("copy")}catch(e){n=!1}finally{document.body.removeChild(t)}return Promise.resolve(n)}return Promise.resolve(!1)})(e).then(e=>{e||t?l():s((0,x.__)("Could not insert or copy shortcode","fluent-crm"))})},feedbackMessage:r}):null}}),(0,t.registerPlugin)("fcrm-email-footer-preview",{render:()=>!1===((window.fcrmEditorBoot||{}).features||{}).email_footer?null:(0,a.jsx)(Ri,{})}),(0,t.registerPlugin)("fcrm-ai-writing-popover",{render:()=>{const[e,t]=(0,i.useState)(!1),[o,s]=(0,i.useState)(null),[l,c]=(0,i.useState)(!1),[d,u]=(0,i.useState)(""),[m,p]=(0,i.useState)("actions"),[f,g]=(0,i.useState)(""),[h,y]=(0,i.useState)(""),[_,I]=(0,i.useState)(()=>function(){try{const e=localStorage.getItem(Hi);if(e&&Gi.includes(e))return e}catch(e){}return"Professional"}()),[b,w]=(0,i.useState)(""),[v,C]=(0,i.useState)(null),S=(0,i.useRef)(null),N=(0,i.useCallback)(e=>{I(e),function(e){try{localStorage.setItem(Hi,e)}catch(e){}}(e)},[]),j=(0,i.useCallback)(()=>{u(""),p("actions"),g(""),y(""),w(""),C(null),c(!1)},[]),M=(0,i.useCallback)(()=>{t(!1),j()},[j]),k=(0,i.useCallback)((e,t)=>{const o=function(){const e=(0,n.select)("core/block-editor");if(!e)return null;const t="function"==typeof e.getSelectedBlockClientId?e.getSelectedBlockClientId():null;if(t){const n=Zi(e.getBlock(t));return n?{clientIds:[t],textKey:n.textKey,content:n.content}:null}const o="function"==typeof e.getMultiSelectedBlockClientIds?e.getMultiSelectedBlockClientIds():[];if(!o||!o.length)return null;const r=[];let i=null;for(const t of o){const n=Zi(e.getBlock(t));n&&(i||(i=n.textKey),n.content&&r.push(n.content))}return i?{clientIds:o,textKey:i,content:r.join("\n\n")}:null}();o?o.textKey?o.content||"custom"===e?(C(o),u(""),c(!0),p("preview"),bo()({path:"fluent-crm/v2/ai/generate",method:"POST",data:{action:e,content:o.content,tone:_,custom_prompt:t||""}}).then(e=>{e&&e.content?w(e.content):(u((0,x.__)("No content was generated. Please try again.","fluent-crm")),p("actions"))}).catch(e=>{const t=e&&e.message?e.message:(0,x.__)("An error occurred. Please try again.","fluent-crm");u(t),p("actions")}).finally(()=>{c(!1)})):u((0,x.__)("The selected block is empty. Add some text first or use Custom Prompt.","fluent-crm")):u((0,x.__)("This block type is not supported for AI writing.","fluent-crm")):u((0,x.__)("Please select a text block first.","fluent-crm"))},[_]),T=(0,i.useCallback)(()=>{v&&v.clientIds&&v.clientIds.length&&v.textKey&&b&&function(e,t,o){const i=(0,n.dispatch)("core/block-editor"),a=(0,r.pasteHandler)({plainText:o,mode:"BLOCKS"});if(e.length>1||"__list"===t)return!(!a||!a.length||(i.replaceBlocks(e,a),0));if(a&&a.length>1)return i.replaceBlocks([e[0]],a),!0;let s=o;if(a&&1===a.length&&a[0].attributes)for(const e of zi){const t=a[0].attributes[e];if(null!=t){const e=Fi(t);if(null!==e){s=e;break}}}i.updateBlockAttributes(e[0],{[t]:s})}(v.clientIds,v.textKey,b),M()},[v,b,M]),E=(0,i.useCallback)(()=>{w(""),u(""),k(f,h)},[f,h,k]),D=(0,i.useCallback)(e=>{g(e),u(""),"custom"!==e?k(e,""):p("custom")},[k]);return(0,i.useEffect)(()=>{const e=window.fcrmEditorBoot||{};if(!e.ai_writing||!e.ai_writing.enabled)return;const n=e=>{const n=e.detail&&e.detail.anchor,o=document.querySelector(".fcrm-compose-ai-writing"),r=n||o;r&&s(r),t(e=>{const t=!e;return t&&j(),t})};return window.addEventListener("fcrm-toggle-ai-writing",n),()=>{window.removeEventListener("fcrm-toggle-ai-writing",n)}},[j]),(0,i.useEffect)(()=>{if(!e)return()=>{};const t=e=>{const t=e.target;if(!t)return;const n=o,r=S.current,i=!(!n||n!==t&&!n.contains(t)),a=!(!r||r!==t&&!r.contains(t));i||a||M()},n=e=>{if("Escape"===e.key){if("tone"===m)return void p("actions");M()}};return document.addEventListener("mousedown",t,!0),document.addEventListener("touchstart",t,!0),document.addEventListener("keydown",n,!0),()=>{document.removeEventListener("mousedown",t,!0),document.removeEventListener("touchstart",t,!0),document.removeEventListener("keydown",n,!0)}},[e,o,M,m]),e&&o?(0,a.jsx)(go.Popover,{className:"fcrm-ai-writing-popover",anchor:o,placement:"bottom-end",onClose:M,focusOnMount:!1,children:(0,a.jsxs)("div",{className:"fcrm-ai-writing-popover__wrap",ref:S,children:[(0,a.jsxs)("div",{className:"fcrm-ai-writing-popover__header",children:[(0,a.jsx)("span",{children:(0,x.__)("AI Writing","fluent-crm")}),(0,a.jsxs)("button",{type:"button",className:"fcrm-ai-writing-popover__tone-badge",onClick:()=>p("tone"===m?"actions":"tone"),title:(0,x.__)("Change writing tone","fluent-crm"),children:[(0,a.jsx)("span",{className:"fcrm-ai-writing-popover__tone-badge-label",children:(0,x.__)("Tone:","fluent-crm")}),(0,a.jsx)("span",{className:"fcrm-ai-writing-popover__tone-badge-value",children:_}),(0,a.jsx)("span",{className:"fcrm-ai-writing-popover__tone-badge-arrow",children:"tone"===m?"▴":"▾"})]})]}),d?(0,a.jsx)("div",{className:"fcrm-ai-writing-popover__error",children:d}):null,"actions"===m?(0,a.jsx)("div",{className:"fcrm-ai-writing-popover__actions",children:Ui.map(e=>(0,a.jsxs)("button",{type:"button",className:"fcrm-ai-writing-popover__action",onClick:()=>D(e.key),children:[(0,a.jsx)("span",{className:"fcrm-ai-writing-popover__action-label",children:e.label}),(0,a.jsx)("span",{className:"fcrm-ai-writing-popover__action-desc",children:e.description})]},e.key))}):null,"tone"===m?(0,a.jsx)("div",{className:"fcrm-ai-writing-popover__tone",children:(0,a.jsx)("div",{className:"fcrm-ai-writing-popover__tone-options",children:Gi.map(e=>(0,a.jsx)("button",{type:"button",className:"fcrm-ai-writing-popover__tone-btn"+(_===e?" is-active":""),onClick:()=>{N(e),p("actions")},children:e},e))})}):null,"custom"===m?(0,a.jsxs)("div",{className:"fcrm-ai-writing-popover__custom",children:[(0,a.jsxs)("div",{className:"fcrm-ai-writing-popover__sub-header",children:[(0,a.jsx)("button",{type:"button",className:"fcrm-ai-writing-popover__back-btn",onClick:()=>{p("actions"),u("")},children:"←"}),(0,a.jsx)("span",{children:(0,x.__)("Custom Prompt","fluent-crm")})]}),(0,a.jsx)("div",{className:"fcrm-ai-writing-popover__custom-input",children:(0,a.jsx)(go.TextareaControl,{value:h,onChange:y,placeholder:(0,x.__)("Describe what you want the AI to do with the selected text...","fluent-crm"),rows:3,__nextHasNoMarginBottom:!0})}),(0,a.jsx)("div",{className:"fcrm-ai-writing-popover__footer",children:(0,a.jsx)(go.Button,{variant:"primary",disabled:!h.trim(),onClick:()=>k("custom",h),children:(0,x.__)("Generate","fluent-crm")})})]}):null,"preview"===m?(0,a.jsx)("div",{className:"fcrm-ai-writing-popover__preview",children:l?(0,a.jsxs)("div",{className:"fcrm-ai-writing-popover__loading",children:[(0,a.jsx)(go.Spinner,{}),(0,a.jsx)("p",{children:(0,x.__)("Generating...","fluent-crm")})]}):(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)("div",{className:"fcrm-ai-writing-popover__sub-header",children:(0,a.jsx)("span",{children:(0,x.__)("Generated Content","fluent-crm")})}),(0,a.jsx)("div",{className:"fcrm-ai-writing-popover__preview-text",dangerouslySetInnerHTML:{__html:Oi(b)}}),(0,a.jsxs)("div",{className:"fcrm-ai-writing-popover__footer",children:[(0,a.jsx)(go.Button,{variant:"secondary",onClick:()=>{j()},children:(0,x.__)("Discard","fluent-crm")}),(0,a.jsx)(go.Button,{variant:"secondary",onClick:E,children:(0,x.__)("Try Again","fluent-crm")}),(0,a.jsx)(go.Button,{variant:"primary",onClick:T,children:(0,x.__)("Accept","fluent-crm")})]})]})}):null]})}):null}}),(0,t.registerPlugin)("fcrm-reusable-block-controls",{render:function(){return null}}),window.fcrmRegisterCustomPatterns=(e=[],t={})=>function(e=[],t={}){return mi(e.map((e,t)=>di(e,t)).filter(Boolean),t)}(e,t),function(){if(fi)return;if(!window.wp||!window.wp.apiFetch||"function"!=typeof window.wp.apiFetch.use)return;fi=!0;const e="/fluent-crm/v2/editor-patterns",t="/fluent-crm/v2/editor-pattern-categories";window.wp.apiFetch.use((n,o)=>{const r=function(e={}){if("string"==typeof e.path&&e.path)return e.path;if("string"==typeof e.url&&e.url)try{const t=new URL(e.url,window.location.origin);return`${t.pathname}${t.search||""}`}catch(t){return e.url}return""}(n),i=function(e={}){return String(e.method||"GET").toUpperCase()}(n);if(function(e=""){return/\/wp\/v2\/blocks(?:\/(\d+))?(?:\?|$)/.test(String(e))}(r)){const t=function(e=""){const t=String(e).match(/\/wp\/v2\/blocks\/(\d+)/);return t?t[1]:null}(r);return o("GET"!==i||t?"POST"!==i||t?!t||"PUT"!==i&&"PATCH"!==i&&"POST"!==i?t&&"DELETE"===i?{...n,path:e+"/"+t,method:"DELETE"}:t&&"GET"===i?{...n,path:e+"/"+t}:n:{...n,path:e+"/"+t,method:"POST"}:{...n,path:e,method:"POST"}:{...n,path:e})}if(function(e=""){return String(e).includes("/wp/v2/wp_pattern_category")}(r))return o("GET"===i?{...n,path:t}:"POST"===i?{...n,path:t,method:"POST"}:n);const a=function(e=""){return String(e).includes("/wp/v2/block-patterns/patterns")}(r),s=function(e=""){return String(e).includes("/wp/v2/block-patterns/categories")}(r);if(!a&&!s)return o(n);const{unregisterAll:l,normalizedPatterns:c,normalizedCategories:d}=ui();return l?a?Promise.resolve(c.map(pi)):Promise.resolve(d):o(n)})}(),function(){if(xi(),Si)return;Si=!0,Ni(),vi=[300,1e3].map(e=>setTimeout(()=>{xi()},e));let e=0;Ci=setInterval(()=>{e+=1,xi(),e>=15&&(Ni(),Si=!1)},1e3)}(),!1===(window.fcrmEditorBoot||{}).features?.create_pattern&&document.body.classList.add("fcrm-no-pattern-features");let Vi=!1;"undefined"!=typeof document&&document.body&&Uo()&&document.body.classList.add("fcrm-compose-ui"),(0,t.registerPlugin)("fluent-com-disable-gutenberg-features",{render:()=>((0,i.useEffect)(()=>{if(Vi)return;(0,n.select)("core/edit-post").isFeatureActive("welcomeGuide")&&(0,n.dispatch)("core/edit-post").toggleFeature("welcomeGuide"),(0,n.select)("core/edit-post").isFeatureActive("fullscreenMode")&&(0,n.dispatch)("core/edit-post").toggleFeature("fullscreenMode"),"text"===(0,n.select)("core/edit-post").getEditorMode()&&(0,n.dispatch)("core/edit-post").switchEditorMode("visual"),(0,n.select)("core/edit-post").getActiveGeneralSidebarName()||(0,n.dispatch)("core/edit-post").openGeneralSidebar("edit-post/document"),Uo()||(0,n.dispatch)("core/editor").lockPostSaving(),(0,n.dispatch)("core/editor").lockPostAutosaving();let e=!1;function t(){e||(e=!0,window.requestAnimationFrame(()=>{e=!1,cr(),lr()}))}const o=[100,300,600,1e3,2e3,3500,5e3,8e3].map(e=>setTimeout(t,e)),r=window.setInterval(t,1e3),i=window.setTimeout(()=>window.clearInterval(r),3e4),a=(0,n.subscribe)(t);window.addEventListener("load",t);const s=new MutationObserver(function(){t()}),l=document.body||document.documentElement;return l&&s.observe(l,{childList:!0,subtree:!0,attributes:!0,attributeFilter:["class","style"]}),Vi=!0,()=>{o.forEach(e=>clearTimeout(e)),clearInterval(r),clearTimeout(i),"function"==typeof a&&a(),window.removeEventListener("load",t),s.disconnect()}},[]),null)}),(0,o.addFilter)("editor.DocumentBar","fluentcom-custom-document-bar",()=>null),(0,o.addFilter)("blocks.registerBlockType","fluentcrm/group-customizations",(e,t)=>"core/group"!==t?e:{...e,attributes:{...e.attributes,fcrmDisableBottomSpacing:{type:"boolean",default:!1}},supports:{...e.supports,layout:{...e.supports?.layout||{},allowedLayouts:["constrained","flow","flex"]}}}),(0,o.addFilter)("editor.Autocomplete.completers","fluent_com/add_autocmplete",function(e){return e=e.filter(e=>"users"!==e.name),[...e,pr]}),window.addEventListener("beforeunload",function(e){e.stopImmediatePropagation()}),new Promise(e=>{let t=!1;const o=()=>{if(!t){t=!0;try{r()}catch(e){}clearTimeout(i),e()}};if((0,n.select)("core/editor").isCleanNewPost()||(0,n.select)("core/block-editor").getBlockCount()>0)return void e();const r=(0,n.subscribe)(()=>{((0,n.select)("core/editor").isCleanNewPost()||(0,n.select)("core/block-editor").getBlockCount()>0)&&o()}),i=setTimeout(o,3e3)}).then(()=>{console.log("[FCRM Autosave] Editor initial stores ready");try{(0,r.unregisterBlockVariation)("core/paragraph","stretchy-paragraph"),(0,r.unregisterBlockVariation)("core/heading","stretchy-heading"),(0,r.unregisterBlockVariation)("core/group","group-grid"),(0,r.unregisterBlockVariation)("core/group","group-stack")}catch(e){}}).catch(()=>{}),console.log("[FCRM Autosave] Boot start");let Wi=!1,Qi=!1,Yi=null;const Ji=window.fcrmEditorBoot||null,$i=!!Ji?.can_save;console.log("[FCRM Autosave] Boot data:",Ji);const qi=Ji?.entity||null,Ki=Ji?.autosave?.endpoint||window.location.origin+"/wp-json/fluent-crm/v1/editor-autosave",Xi=Ji?.autosave?.nonce||"";let ea=!1,ta=!1,na=null,oa=null,ra=null,ia=0;try{qi&&qi.id&&(qi.content||""===qi.content)&&(ra=aa(qi.content||""))}catch(e){}function aa(e){let t,n=0,o=0;if(!e)return"0";for(o=0;oua(!1),5e3))}function da(){oa||(oa=setTimeout(()=>{ua(!0),oa=null,da()},6e4))}async function ua(e){if(!$i)return{saved:!1,status:"disabled"};if(ta)return{saved:!1,status:"in_flight"};if(!ea&&!e)return{saved:!1,status:"not_dirty"};if("template"===qi?.block_type&&!qi?.id)return console.log("[FCRM Autosave] Skip autosave: template has no ID yet"),sa(),{saved:!1,status:"missing_id"};const t=(0,n.select)("core/block-editor").getBlocks(),o=function(e){const t=aa(e||"");return{block_type:qi?.block_type||"",id:qi?.id||null,content:e||"",hash:t,prev_updated_at:qi?.updated_at||""}}((0,r.serialize)(t));if(o.hash===ra&&!e)return ea=!1,sa(),{saved:!0,status:"already_synced"};ta=!0,ea=!1,sa(),la("Saving…");try{const e=await fetch(Ki,{method:"POST",headers:{"Content-Type":"application/json","X-WP-Nonce":Xi},body:JSON.stringify(o),credentials:"same-origin"}),t=await e.json();if("conflict"===t.status)return la("Conflict – not saved"),{saved:!1,status:t.status};if("skipped"===t.status)return la("Autosave skipped"),{saved:!1,status:t.status};if(ra=o.hash,t.id&&(!qi?.id||t.created)){qi.id=t.id;try{!function(e){if(!e)return;const t=new URL(window.location.href);t.searchParams.set("bid",e),!t.searchParams.get("block_type")&&qi?.block_type&&t.searchParams.set("block_type",qi.block_type),window.history.replaceState({},document.title,t.toString())}(t.id)}catch(e){}}return t.updated_at&&(qi.updated_at=t.updated_at),la("noop"===t.status?"Up to date":"Saved"),ia=0,{saved:!0,status:t.status||"saved"}}catch(e){ia=Math.min(ia+1,5);const t=[3e3,8e3,15e3,3e4,45e3,6e4][ia];return la("Save failed – retry "+Math.round(t/1e3)+"s"),ea=!0,sa(),setTimeout(()=>ua(!1),t),{saved:!1,status:"failed"}}finally{ta=!1,sa()}}function ma(e){var t=!1,n=[];return e.forEach(function(e){var o=ma(e.innerBlocks||[]);if(o.anyRecovered&&(t=!0),!1===e.isValid){var i=!1;try{var a=e.originalContent||"";if(a){var s=(0,r.rawHandler)({HTML:a});s&&s.length>0&&(t=!0,i=!0,n.push.apply(n,s))}}catch(t){console.warn('[FCRM Editor] rawHandler recovery failed for "'+e.name+'":',t.message)}if(!i)try{var l=(0,r.createBlock)(e.name,e.attributes,o.blocks);t=!0,n.push(l)}catch(t){console.warn('[FCRM Editor] Could not recover block "'+e.name+'":',t.message),n.push(Object.assign({},e,{innerBlocks:o.blocks}))}}else o.anyRecovered?n.push(Object.assign({},e,{innerBlocks:o.blocks})):n.push(e)}),{blocks:n,anyRecovered:t}}function pa(e){for(var t=0;t{$i&&ea&&!ta&&ua(!0)}),Ho(e=>!(!e||e.type!==Po.UPDATE_EDITOR||!e.data||"object"!=typeof e.data),e=>{Qi=!0;const{content:t,templateConfig:o,extra_tags:r}=e.data;r&&(window.fcrm_funnel_context_codes=r,window.dispatchEvent(new CustomEvent("fcrm-context-smartcodes-updated"))),"string"==typeof t&&(setTimeout(()=>{fa(t,1)},50),Yi&&clearTimeout(Yi),Yi=setTimeout(function(){try{var e=(0,n.select)("core/block-editor").getBlocks();if(!pa(e))return;var t=ma(e);t.anyRecovered&&((0,n.dispatch)("core/block-editor").resetBlocks(t.blocks),ua(!0))}catch(e){console.warn("[FCRM Editor] Late block recovery failed:",e)}finally{Yi=null}},2500)),o&&"object"==typeof o&&(window._fcrmStyleConfig=JSON.parse(JSON.stringify(o)),window.dispatchEvent(new CustomEvent("fcrm:style-config-sync",{detail:{template_config:o}})));const i=e.data.footerSettings;void 0!==i&&(window._fcrmFooterSettings=i||null,window.postMessage({action:"FOOTER_SETTINGS_SYNC",footer_settings:i},"*")),setTimeout(()=>{Wi=!0},1e3)}),Ho(e=>!(!e||e.type!==Po.INSERT_SMARTCODE||"string"!=typeof e.shortcode),e=>{const t=String(e.shortcode||"").trim();t&&li(t)});let ga=!1;!function e(t,n=0){if(window._wpLoadBlockEditor&&"function"==typeof window._wpLoadBlockEditor.then){let e=!1;const n=()=>{e||(e=!0,t())};return window._wpLoadBlockEditor.then(n).catch(t=>{e=!0,console.error("[FCRM Editor] initializeEditor failed:",t);try{window.parent.postMessage({action:"EDITOR_INIT_FAILED",error:String(t)},"*")}catch(e){}}),void setTimeout(()=>{e||(console.warn("[FCRM Autosave] _wpLoadBlockEditor timed out, proceeding"),n())},5e3)}if(n>50)return console.warn("[FCRM Autosave] _wpLoadBlockEditor not found, proceeding anyway"),void t();setTimeout(()=>e(t,n+1),100)}(()=>{(function(){if(!ga){ga=!0;try{window.parent.postMessage({content:"ping",action:"EDITOR_READY"},"*")}catch(e){}console.log("[FCRM Editor] EDITOR_READY sent (editor booted)")}})(),setTimeout(()=>{cr()},500),Wi||(window.parent&&window.parent!==window?Qi||setTimeout(function(){Wi||(Wi=!0,console.log("[FCRM Autosave] Standalone readiness fallback (iframe, no UPDATE_EDITOR after timeout)"))},5e3):(Wi=!0,console.log("[FCRM Autosave] Standalone readiness enabled (not in iframe)")));let e="",t="",o=null;(0,n.subscribe)(()=>{if(!Wi)return;const i=(0,n.select)("core/editor").getEditedPostAttribute("title");if(i!==t){t=i;try{window.parent.postMessage({content:i,action:"TITLE_UPDATED"},"*")}catch(e){}}const a=(0,n.select)("core/editor").getEditedPostAttribute("featured_media");if(a!==o){o=a;try{window.parent.postMessage({content:a,action:"FEATURED_MEDIA_UPDATED"},"*")}catch(e){}}const s=(0,n.select)("core/block-editor").getBlocks(),l=(0,r.serialize)(s);if(e!==l){e=l;try{window.parent.postMessage({content:l,action:"EDITOR_UPDATED"},"*")}catch(e){}ca()}});let i=null;(0,n.subscribe)(()=>{if(!Wi)return;const e=(0,n.select)("core/block-editor").getBlocks(),t=e.map(e=>e.clientId);if(null===i)return void(i=t);const o=t.filter(e=>!i.includes(e));i=t,o.length&&o.forEach(t=>{const o=e.find(e=>e.clientId===t);if(!o||"core/image"!==o.name)return;const i=e.findIndex(e=>e.clientId===t);i<0||e[i+1]||(0,n.dispatch)("core/block-editor").insertBlock((0,r.createBlock)("core/paragraph"),i+1)})}),$i?(console.log("[FCRM Autosave] Enabled. Entity ID:",qi?.id,"Block Type:",qi?.block_type),da(),ca()):(la("Autosave disabled"),console.log("[FCRM Autosave] Disabled (canSave false)"))}),window.fcrmManualAutosave=()=>ua(!0),sa({canSave:$i}),console.log("[FCRM Autosave] Manual trigger available: window.fcrmManualAutosave()")})(); \ No newline at end of file diff --git a/wp-content/plugins/fluent-crm/assets/guten-editor/index.php b/wp-content/plugins/fluent-crm/assets/guten-editor/index.php new file mode 100644 index 0000000..6220032 --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/guten-editor/index.php @@ -0,0 +1,2 @@ + + + + + + + + + + + + + + + + + diff --git a/wp-content/plugins/fluent-crm/assets/images/avatar.png b/wp-content/plugins/fluent-crm/assets/images/avatar.png new file mode 100644 index 0000000..e361b26 Binary files /dev/null and b/wp-content/plugins/fluent-crm/assets/images/avatar.png differ diff --git a/wp-content/plugins/fluent-crm/assets/images/buddyboss.svg b/wp-content/plugins/fluent-crm/assets/images/buddyboss.svg new file mode 100644 index 0000000..b84ebfc --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/images/buddyboss.svg @@ -0,0 +1 @@ +BB_Logos \ No newline at end of file diff --git a/wp-content/plugins/fluent-crm/assets/images/buddypress.png b/wp-content/plugins/fluent-crm/assets/images/buddypress.png new file mode 100644 index 0000000..21824e3 Binary files /dev/null and b/wp-content/plugins/fluent-crm/assets/images/buddypress.png differ diff --git a/wp-content/plugins/fluent-crm/assets/images/classic-editor.svg b/wp-content/plugins/fluent-crm/assets/images/classic-editor.svg new file mode 100644 index 0000000..43f811f --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/images/classic-editor.svg @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/wp-content/plugins/fluent-crm/assets/images/classic.png b/wp-content/plugins/fluent-crm/assets/images/classic.png new file mode 100644 index 0000000..dd1f45c Binary files /dev/null and b/wp-content/plugins/fluent-crm/assets/images/classic.png differ diff --git a/wp-content/plugins/fluent-crm/assets/images/classic_raw.png b/wp-content/plugins/fluent-crm/assets/images/classic_raw.png new file mode 100644 index 0000000..370410a Binary files /dev/null and b/wp-content/plugins/fluent-crm/assets/images/classic_raw.png differ diff --git a/wp-content/plugins/fluent-crm/assets/images/csv.svg b/wp-content/plugins/fluent-crm/assets/images/csv.svg new file mode 100644 index 0000000..610d340 --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/images/csv.svg @@ -0,0 +1,56 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/wp-content/plugins/fluent-crm/assets/images/default-profile.png b/wp-content/plugins/fluent-crm/assets/images/default-profile.png new file mode 100644 index 0000000..21ac6db Binary files /dev/null and b/wp-content/plugins/fluent-crm/assets/images/default-profile.png differ diff --git a/wp-content/plugins/fluent-crm/assets/images/drag-drop.png b/wp-content/plugins/fluent-crm/assets/images/drag-drop.png new file mode 100644 index 0000000..7b50a6e Binary files /dev/null and b/wp-content/plugins/fluent-crm/assets/images/drag-drop.png differ diff --git a/wp-content/plugins/fluent-crm/assets/images/edd.svg b/wp-content/plugins/fluent-crm/assets/images/edd.svg new file mode 100644 index 0000000..5b295c4 --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/images/edd.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/wp-content/plugins/fluent-crm/assets/images/email_icon.png b/wp-content/plugins/fluent-crm/assets/images/email_icon.png new file mode 100644 index 0000000..2896a24 Binary files /dev/null and b/wp-content/plugins/fluent-crm/assets/images/email_icon.png differ diff --git a/wp-content/plugins/fluent-crm/assets/images/fluent-boards.svg b/wp-content/plugins/fluent-crm/assets/images/fluent-boards.svg new file mode 100644 index 0000000..75f14ea --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/images/fluent-boards.svg @@ -0,0 +1,3 @@ + + + diff --git a/wp-content/plugins/fluent-crm/assets/images/fluent-booking.svg b/wp-content/plugins/fluent-crm/assets/images/fluent-booking.svg new file mode 100644 index 0000000..b8ba53b --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/images/fluent-booking.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/wp-content/plugins/fluent-crm/assets/images/fluent-cart-dark.svg b/wp-content/plugins/fluent-crm/assets/images/fluent-cart-dark.svg new file mode 100644 index 0000000..7731250 --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/images/fluent-cart-dark.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/wp-content/plugins/fluent-crm/assets/images/fluent-community.svg b/wp-content/plugins/fluent-crm/assets/images/fluent-community.svg new file mode 100644 index 0000000..fd6ee88 --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/images/fluent-community.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/wp-content/plugins/fluent-crm/assets/images/fluent-connect.svg b/wp-content/plugins/fluent-crm/assets/images/fluent-connect.svg new file mode 100644 index 0000000..573db79 --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/images/fluent-connect.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/wp-content/plugins/fluent-crm/assets/images/fluent-forms.svg b/wp-content/plugins/fluent-crm/assets/images/fluent-forms.svg new file mode 100644 index 0000000..a141b62 --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/images/fluent-forms.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/wp-content/plugins/fluent-crm/assets/images/fluent-smtp.svg b/wp-content/plugins/fluent-crm/assets/images/fluent-smtp.svg new file mode 100644 index 0000000..d419e97 --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/images/fluent-smtp.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/wp-content/plugins/fluent-crm/assets/images/fluent-support.svg b/wp-content/plugins/fluent-crm/assets/images/fluent-support.svg new file mode 100644 index 0000000..3e19cd8 --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/images/fluent-support.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/wp-content/plugins/fluent-crm/assets/images/fluent-toolkit.svg b/wp-content/plugins/fluent-crm/assets/images/fluent-toolkit.svg new file mode 100644 index 0000000..31b73c3 --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/images/fluent-toolkit.svg @@ -0,0 +1,3 @@ + + + diff --git a/wp-content/plugins/fluent-crm/assets/images/fluentcrm-logo.png b/wp-content/plugins/fluent-crm/assets/images/fluentcrm-logo.png new file mode 100644 index 0000000..e8bee95 Binary files /dev/null and b/wp-content/plugins/fluent-crm/assets/images/fluentcrm-logo.png differ diff --git a/wp-content/plugins/fluent-crm/assets/images/fluentcrm-logo.svg b/wp-content/plugins/fluent-crm/assets/images/fluentcrm-logo.svg new file mode 100644 index 0000000..f691148 --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/images/fluentcrm-logo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/wp-content/plugins/fluent-crm/assets/images/fluentform.png b/wp-content/plugins/fluent-crm/assets/images/fluentform.png new file mode 100644 index 0000000..8822067 Binary files /dev/null and b/wp-content/plugins/fluent-crm/assets/images/fluentform.png differ diff --git a/wp-content/plugins/fluent-crm/assets/images/forms/form_1.svg b/wp-content/plugins/fluent-crm/assets/images/forms/form_1.svg new file mode 100644 index 0000000..6190bd5 --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/images/forms/form_1.svg @@ -0,0 +1 @@ +news_1 \ No newline at end of file diff --git a/wp-content/plugins/fluent-crm/assets/images/forms/form_2.svg b/wp-content/plugins/fluent-crm/assets/images/forms/form_2.svg new file mode 100644 index 0000000..5a294e0 --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/images/forms/form_2.svg @@ -0,0 +1 @@ +news_2 \ No newline at end of file diff --git a/wp-content/plugins/fluent-crm/assets/images/forms/form_3.svg b/wp-content/plugins/fluent-crm/assets/images/forms/form_3.svg new file mode 100644 index 0000000..4dc2f2e --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/images/forms/form_3.svg @@ -0,0 +1 @@ +news_3 \ No newline at end of file diff --git a/wp-content/plugins/fluent-crm/assets/images/funnel_icons/actions.svg b/wp-content/plugins/fluent-crm/assets/images/funnel_icons/actions.svg new file mode 100644 index 0000000..cbe8cae --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/images/funnel_icons/actions.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/wp-content/plugins/fluent-crm/assets/images/funnel_icons/apply_list.svg b/wp-content/plugins/fluent-crm/assets/images/funnel_icons/apply_list.svg new file mode 100644 index 0000000..e7a7af2 --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/images/funnel_icons/apply_list.svg @@ -0,0 +1 @@ +Apply List \ No newline at end of file diff --git a/wp-content/plugins/fluent-crm/assets/images/funnel_icons/apply_list.svg.svg b/wp-content/plugins/fluent-crm/assets/images/funnel_icons/apply_list.svg.svg new file mode 100644 index 0000000..508dc0d --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/images/funnel_icons/apply_list.svg.svg @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/wp-content/plugins/fluent-crm/assets/images/funnel_icons/apply_tag.svg b/wp-content/plugins/fluent-crm/assets/images/funnel_icons/apply_tag.svg new file mode 100644 index 0000000..f004948 --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/images/funnel_icons/apply_tag.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/wp-content/plugins/fluent-crm/assets/images/funnel_icons/benchmarks.svg b/wp-content/plugins/fluent-crm/assets/images/funnel_icons/benchmarks.svg new file mode 100644 index 0000000..4547d0a --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/images/funnel_icons/benchmarks.svg @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/wp-content/plugins/fluent-crm/assets/images/funnel_icons/cancel_automation.svg b/wp-content/plugins/fluent-crm/assets/images/funnel_icons/cancel_automation.svg new file mode 100644 index 0000000..c0983cc --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/images/funnel_icons/cancel_automation.svg @@ -0,0 +1 @@ +Cancel Automations \ No newline at end of file diff --git a/wp-content/plugins/fluent-crm/assets/images/funnel_icons/cancel_sequence.svg b/wp-content/plugins/fluent-crm/assets/images/funnel_icons/cancel_sequence.svg new file mode 100644 index 0000000..0ba718b --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/images/funnel_icons/cancel_sequence.svg @@ -0,0 +1 @@ +Cancel Sequence Emails \ No newline at end of file diff --git a/wp-content/plugins/fluent-crm/assets/images/funnel_icons/change_woo_status.svg b/wp-content/plugins/fluent-crm/assets/images/funnel_icons/change_woo_status.svg new file mode 100644 index 0000000..5771fe6 --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/images/funnel_icons/change_woo_status.svg @@ -0,0 +1,17 @@ + + + + + + + + + + + + + diff --git a/wp-content/plugins/fluent-crm/assets/images/funnel_icons/conditions.svg b/wp-content/plugins/fluent-crm/assets/images/funnel_icons/conditions.svg new file mode 100644 index 0000000..30b13d9 --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/images/funnel_icons/conditions.svg @@ -0,0 +1,3 @@ + + + diff --git a/wp-content/plugins/fluent-crm/assets/images/funnel_icons/contact_update.svg b/wp-content/plugins/fluent-crm/assets/images/funnel_icons/contact_update.svg new file mode 100644 index 0000000..cda2c4c --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/images/funnel_icons/contact_update.svg @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/wp-content/plugins/fluent-crm/assets/images/funnel_icons/create_wp_user.svg b/wp-content/plugins/fluent-crm/assets/images/funnel_icons/create_wp_user.svg new file mode 100644 index 0000000..a062358 --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/images/funnel_icons/create_wp_user.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/wp-content/plugins/fluent-crm/assets/images/funnel_icons/custom_email.svg b/wp-content/plugins/fluent-crm/assets/images/funnel_icons/custom_email.svg new file mode 100644 index 0000000..6ba7838 --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/images/funnel_icons/custom_email.svg @@ -0,0 +1 @@ +Send Custom Email \ No newline at end of file diff --git a/wp-content/plugins/fluent-crm/assets/images/funnel_icons/easydigitaldownloads.svg b/wp-content/plugins/fluent-crm/assets/images/funnel_icons/easydigitaldownloads.svg new file mode 100644 index 0000000..3ff5bdb --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/images/funnel_icons/easydigitaldownloads.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/wp-content/plugins/fluent-crm/assets/images/funnel_icons/edd_purchased.svg b/wp-content/plugins/fluent-crm/assets/images/funnel_icons/edd_purchased.svg new file mode 100644 index 0000000..09fc123 --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/images/funnel_icons/edd_purchased.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/wp-content/plugins/fluent-crm/assets/images/funnel_icons/end_funnel.svg b/wp-content/plugins/fluent-crm/assets/images/funnel_icons/end_funnel.svg new file mode 100644 index 0000000..cf6b554 --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/images/funnel_icons/end_funnel.svg @@ -0,0 +1,3 @@ + + + diff --git a/wp-content/plugins/fluent-crm/assets/images/funnel_icons/fontello.eot b/wp-content/plugins/fluent-crm/assets/images/funnel_icons/fontello.eot new file mode 100644 index 0000000..5fda43d Binary files /dev/null and b/wp-content/plugins/fluent-crm/assets/images/funnel_icons/fontello.eot differ diff --git a/wp-content/plugins/fluent-crm/assets/images/funnel_icons/fontello.svg b/wp-content/plugins/fluent-crm/assets/images/funnel_icons/fontello.svg new file mode 100644 index 0000000..92e02be --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/images/funnel_icons/fontello.svg @@ -0,0 +1,124 @@ + + + +Copyright (C) 2021 by original authors @ fontello.com + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/wp-content/plugins/fluent-crm/assets/images/funnel_icons/fontello.ttf b/wp-content/plugins/fluent-crm/assets/images/funnel_icons/fontello.ttf new file mode 100644 index 0000000..0b17e22 Binary files /dev/null and b/wp-content/plugins/fluent-crm/assets/images/funnel_icons/fontello.ttf differ diff --git a/wp-content/plugins/fluent-crm/assets/images/funnel_icons/fontello.woff b/wp-content/plugins/fluent-crm/assets/images/funnel_icons/fontello.woff new file mode 100644 index 0000000..c4d5c4a Binary files /dev/null and b/wp-content/plugins/fluent-crm/assets/images/funnel_icons/fontello.woff differ diff --git a/wp-content/plugins/fluent-crm/assets/images/funnel_icons/fontello.woff2 b/wp-content/plugins/fluent-crm/assets/images/funnel_icons/fontello.woff2 new file mode 100644 index 0000000..c33a44a Binary files /dev/null and b/wp-content/plugins/fluent-crm/assets/images/funnel_icons/fontello.woff2 differ diff --git a/wp-content/plugins/fluent-crm/assets/images/funnel_icons/has_contact_property.svg b/wp-content/plugins/fluent-crm/assets/images/funnel_icons/has_contact_property.svg new file mode 100644 index 0000000..52c3061 --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/images/funnel_icons/has_contact_property.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/wp-content/plugins/fluent-crm/assets/images/funnel_icons/has_list.svg b/wp-content/plugins/fluent-crm/assets/images/funnel_icons/has_list.svg new file mode 100644 index 0000000..17f0e14 --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/images/funnel_icons/has_list.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/wp-content/plugins/fluent-crm/assets/images/funnel_icons/has_tag.svg b/wp-content/plugins/fluent-crm/assets/images/funnel_icons/has_tag.svg new file mode 100644 index 0000000..7b03737 --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/images/funnel_icons/has_tag.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/wp-content/plugins/fluent-crm/assets/images/funnel_icons/has_wp_role.svg b/wp-content/plugins/fluent-crm/assets/images/funnel_icons/has_wp_role.svg new file mode 100644 index 0000000..0c27fc0 --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/images/funnel_icons/has_wp_role.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/wp-content/plugins/fluent-crm/assets/images/funnel_icons/has_wp_role.svg.svg b/wp-content/plugins/fluent-crm/assets/images/funnel_icons/has_wp_role.svg.svg new file mode 100644 index 0000000..8534ba0 --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/images/funnel_icons/has_wp_role.svg.svg @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + diff --git a/wp-content/plugins/fluent-crm/assets/images/funnel_icons/ld_in_course.svg b/wp-content/plugins/fluent-crm/assets/images/funnel_icons/ld_in_course.svg new file mode 100644 index 0000000..9dd2a80 --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/images/funnel_icons/ld_in_course.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/wp-content/plugins/fluent-crm/assets/images/funnel_icons/ld_in_group.svg b/wp-content/plugins/fluent-crm/assets/images/funnel_icons/ld_in_group.svg new file mode 100644 index 0000000..afb851c --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/images/funnel_icons/ld_in_group.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/wp-content/plugins/fluent-crm/assets/images/funnel_icons/learndash.svg b/wp-content/plugins/fluent-crm/assets/images/funnel_icons/learndash.svg new file mode 100644 index 0000000..302fe25 --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/images/funnel_icons/learndash.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/wp-content/plugins/fluent-crm/assets/images/funnel_icons/lifter_has_membership.svg b/wp-content/plugins/fluent-crm/assets/images/funnel_icons/lifter_has_membership.svg new file mode 100644 index 0000000..8f56694 --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/images/funnel_icons/lifter_has_membership.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/wp-content/plugins/fluent-crm/assets/images/funnel_icons/lifter_in_course.svg b/wp-content/plugins/fluent-crm/assets/images/funnel_icons/lifter_in_course.svg new file mode 100644 index 0000000..7cdb5d9 --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/images/funnel_icons/lifter_in_course.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/wp-content/plugins/fluent-crm/assets/images/funnel_icons/lifterlms.svg b/wp-content/plugins/fluent-crm/assets/images/funnel_icons/lifterlms.svg new file mode 100644 index 0000000..6d891b5 --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/images/funnel_icons/lifterlms.svg @@ -0,0 +1,17 @@ + + + + + + + + + + + + + diff --git a/wp-content/plugins/fluent-crm/assets/images/funnel_icons/link_clicked.svg b/wp-content/plugins/fluent-crm/assets/images/funnel_icons/link_clicked.svg new file mode 100644 index 0000000..c6d0ad0 --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/images/funnel_icons/link_clicked.svg @@ -0,0 +1 @@ +Link Clicked \ No newline at end of file diff --git a/wp-content/plugins/fluent-crm/assets/images/funnel_icons/list_applied.svg b/wp-content/plugins/fluent-crm/assets/images/funnel_icons/list_applied.svg new file mode 100644 index 0000000..218b615 --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/images/funnel_icons/list_applied.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/wp-content/plugins/fluent-crm/assets/images/funnel_icons/list_remove.svg b/wp-content/plugins/fluent-crm/assets/images/funnel_icons/list_remove.svg new file mode 100644 index 0000000..2793544 --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/images/funnel_icons/list_remove.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/wp-content/plugins/fluent-crm/assets/images/funnel_icons/list_removed.svg b/wp-content/plugins/fluent-crm/assets/images/funnel_icons/list_removed.svg new file mode 100644 index 0000000..41d7e66 --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/images/funnel_icons/list_removed.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/wp-content/plugins/fluent-crm/assets/images/funnel_icons/memberpress.svg b/wp-content/plugins/fluent-crm/assets/images/funnel_icons/memberpress.svg new file mode 100644 index 0000000..f7486ae --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/images/funnel_icons/memberpress.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/wp-content/plugins/fluent-crm/assets/images/funnel_icons/new_order_edd.svg b/wp-content/plugins/fluent-crm/assets/images/funnel_icons/new_order_edd.svg new file mode 100644 index 0000000..ccee502 --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/images/funnel_icons/new_order_edd.svg @@ -0,0 +1 @@ +New Order Success in EDD \ No newline at end of file diff --git a/wp-content/plugins/fluent-crm/assets/images/funnel_icons/new_order_woo.svg b/wp-content/plugins/fluent-crm/assets/images/funnel_icons/new_order_woo.svg new file mode 100644 index 0000000..c7a7ff1 --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/images/funnel_icons/new_order_woo.svg @@ -0,0 +1 @@ +Order Received In WooCommerce \ No newline at end of file diff --git a/wp-content/plugins/fluent-crm/assets/images/funnel_icons/paidmembershippro.svg b/wp-content/plugins/fluent-crm/assets/images/funnel_icons/paidmembershippro.svg new file mode 100644 index 0000000..ac8f6e8 --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/images/funnel_icons/paidmembershippro.svg @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/wp-content/plugins/fluent-crm/assets/images/funnel_icons/pmpro_in_membership.svg b/wp-content/plugins/fluent-crm/assets/images/funnel_icons/pmpro_in_membership.svg new file mode 100644 index 0000000..95b0747 --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/images/funnel_icons/pmpro_in_membership.svg @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/wp-content/plugins/fluent-crm/assets/images/funnel_icons/rcp_in_membership.svg b/wp-content/plugins/fluent-crm/assets/images/funnel_icons/rcp_in_membership.svg new file mode 100644 index 0000000..00a2227 --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/images/funnel_icons/rcp_in_membership.svg @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + diff --git a/wp-content/plugins/fluent-crm/assets/images/funnel_icons/restrictcontentpro.svg b/wp-content/plugins/fluent-crm/assets/images/funnel_icons/restrictcontentpro.svg new file mode 100644 index 0000000..20d868d --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/images/funnel_icons/restrictcontentpro.svg @@ -0,0 +1,11 @@ + + + + + + + diff --git a/wp-content/plugins/fluent-crm/assets/images/funnel_icons/send_campaign.svg b/wp-content/plugins/fluent-crm/assets/images/funnel_icons/send_campaign.svg new file mode 100644 index 0000000..e1f5683 --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/images/funnel_icons/send_campaign.svg @@ -0,0 +1 @@ +Send Campaign Email \ No newline at end of file diff --git a/wp-content/plugins/fluent-crm/assets/images/funnel_icons/set_sequence.svg b/wp-content/plugins/fluent-crm/assets/images/funnel_icons/set_sequence.svg new file mode 100644 index 0000000..becda1f --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/images/funnel_icons/set_sequence.svg @@ -0,0 +1 @@ +Set Sequence Emails \ No newline at end of file diff --git a/wp-content/plugins/fluent-crm/assets/images/funnel_icons/tag-applied.svg b/wp-content/plugins/fluent-crm/assets/images/funnel_icons/tag-applied.svg new file mode 100644 index 0000000..e0e320b --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/images/funnel_icons/tag-applied.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/wp-content/plugins/fluent-crm/assets/images/funnel_icons/tag_remove.svg b/wp-content/plugins/fluent-crm/assets/images/funnel_icons/tag_remove.svg new file mode 100644 index 0000000..b545f64 --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/images/funnel_icons/tag_remove.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/wp-content/plugins/fluent-crm/assets/images/funnel_icons/tag_removed.svg b/wp-content/plugins/fluent-crm/assets/images/funnel_icons/tag_removed.svg new file mode 100644 index 0000000..e7eaee2 --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/images/funnel_icons/tag_removed.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/wp-content/plugins/fluent-crm/assets/images/funnel_icons/trigger.svg b/wp-content/plugins/fluent-crm/assets/images/funnel_icons/trigger.svg new file mode 100644 index 0000000..f6e7569 --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/images/funnel_icons/trigger.svg @@ -0,0 +1,3 @@ + + + diff --git a/wp-content/plugins/fluent-crm/assets/images/funnel_icons/tutor_in_course.svg b/wp-content/plugins/fluent-crm/assets/images/funnel_icons/tutor_in_course.svg new file mode 100644 index 0000000..679689d --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/images/funnel_icons/tutor_in_course.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/wp-content/plugins/fluent-crm/assets/images/funnel_icons/tutorlms.svg b/wp-content/plugins/fluent-crm/assets/images/funnel_icons/tutorlms.svg new file mode 100644 index 0000000..15b1cbb --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/images/funnel_icons/tutorlms.svg @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + diff --git a/wp-content/plugins/fluent-crm/assets/images/funnel_icons/user_register.svg b/wp-content/plugins/fluent-crm/assets/images/funnel_icons/user_register.svg new file mode 100644 index 0000000..a062358 --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/images/funnel_icons/user_register.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/wp-content/plugins/fluent-crm/assets/images/funnel_icons/wait_time.svg b/wp-content/plugins/fluent-crm/assets/images/funnel_icons/wait_time.svg new file mode 100644 index 0000000..1635b51 --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/images/funnel_icons/wait_time.svg @@ -0,0 +1 @@ +Wait X Days \ No newline at end of file diff --git a/wp-content/plugins/fluent-crm/assets/images/funnel_icons/webhooks.svg b/wp-content/plugins/fluent-crm/assets/images/funnel_icons/webhooks.svg new file mode 100644 index 0000000..1d1c205 --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/images/funnel_icons/webhooks.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/wp-content/plugins/fluent-crm/assets/images/funnel_icons/wishlist_in_level.svg b/wp-content/plugins/fluent-crm/assets/images/funnel_icons/wishlist_in_level.svg new file mode 100644 index 0000000..4e64339 --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/images/funnel_icons/wishlist_in_level.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/wp-content/plugins/fluent-crm/assets/images/funnel_icons/wishlistmember.svg b/wp-content/plugins/fluent-crm/assets/images/funnel_icons/wishlistmember.svg new file mode 100644 index 0000000..35d837a --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/images/funnel_icons/wishlistmember.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/wp-content/plugins/fluent-crm/assets/images/funnel_icons/woo_purchased.svg b/wp-content/plugins/fluent-crm/assets/images/funnel_icons/woo_purchased.svg new file mode 100644 index 0000000..48757b8 --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/images/funnel_icons/woo_purchased.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/wp-content/plugins/fluent-crm/assets/images/funnel_icons/woocommerce.svg b/wp-content/plugins/fluent-crm/assets/images/funnel_icons/woocommerce.svg new file mode 100644 index 0000000..7d7e8a5 --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/images/funnel_icons/woocommerce.svg @@ -0,0 +1,13 @@ + + + + + + + + + diff --git a/wp-content/plugins/fluent-crm/assets/images/funnel_icons/wordpress.svg b/wp-content/plugins/fluent-crm/assets/images/funnel_icons/wordpress.svg new file mode 100644 index 0000000..ace1232 --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/images/funnel_icons/wordpress.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/wp-content/plugins/fluent-crm/assets/images/funnel_icons/wordpress_role.svg b/wp-content/plugins/fluent-crm/assets/images/funnel_icons/wordpress_role.svg new file mode 100644 index 0000000..5a38a80 --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/images/funnel_icons/wordpress_role.svg @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + diff --git a/wp-content/plugins/fluent-crm/assets/images/funnel_icons/wp_user_meta.svg b/wp-content/plugins/fluent-crm/assets/images/funnel_icons/wp_user_meta.svg new file mode 100644 index 0000000..709c3b4 --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/images/funnel_icons/wp_user_meta.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/wp-content/plugins/fluent-crm/assets/images/funnel_icons/wp_user_role.svg b/wp-content/plugins/fluent-crm/assets/images/funnel_icons/wp_user_role.svg new file mode 100644 index 0000000..7365527 --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/images/funnel_icons/wp_user_role.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/wp-content/plugins/fluent-crm/assets/images/funnel_icons/writing.svg b/wp-content/plugins/fluent-crm/assets/images/funnel_icons/writing.svg new file mode 100644 index 0000000..a865d5c --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/images/funnel_icons/writing.svg @@ -0,0 +1,54 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/wp-content/plugins/fluent-crm/assets/images/grabme.svg b/wp-content/plugins/fluent-crm/assets/images/grabme.svg new file mode 100644 index 0000000..7299d03 --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/images/grabme.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/wp-content/plugins/fluent-crm/assets/images/gutenberg-builder.svg b/wp-content/plugins/fluent-crm/assets/images/gutenberg-builder.svg new file mode 100644 index 0000000..df944a7 --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/images/gutenberg-builder.svg @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/wp-content/plugins/fluent-crm/assets/images/html-editor.svg b/wp-content/plugins/fluent-crm/assets/images/html-editor.svg new file mode 100644 index 0000000..c6015a2 --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/images/html-editor.svg @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/wp-content/plugins/fluent-crm/assets/images/index.php b/wp-content/plugins/fluent-crm/assets/images/index.php new file mode 100644 index 0000000..6220032 --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/images/index.php @@ -0,0 +1,2 @@ + + + diff --git a/wp-content/plugins/fluent-crm/assets/images/learndash.png b/wp-content/plugins/fluent-crm/assets/images/learndash.png new file mode 100644 index 0000000..fb46696 Binary files /dev/null and b/wp-content/plugins/fluent-crm/assets/images/learndash.png differ diff --git a/wp-content/plugins/fluent-crm/assets/images/learnpress.png b/wp-content/plugins/fluent-crm/assets/images/learnpress.png new file mode 100644 index 0000000..7cabfd5 Binary files /dev/null and b/wp-content/plugins/fluent-crm/assets/images/learnpress.png differ diff --git a/wp-content/plugins/fluent-crm/assets/images/lifterlms.png b/wp-content/plugins/fluent-crm/assets/images/lifterlms.png new file mode 100644 index 0000000..66abd51 Binary files /dev/null and b/wp-content/plugins/fluent-crm/assets/images/lifterlms.png differ diff --git a/wp-content/plugins/fluent-crm/assets/images/memberpress.jpg b/wp-content/plugins/fluent-crm/assets/images/memberpress.jpg new file mode 100644 index 0000000..c44b2bb Binary files /dev/null and b/wp-content/plugins/fluent-crm/assets/images/memberpress.jpg differ diff --git a/wp-content/plugins/fluent-crm/assets/images/memberpress.png b/wp-content/plugins/fluent-crm/assets/images/memberpress.png new file mode 100644 index 0000000..fdda46b Binary files /dev/null and b/wp-content/plugins/fluent-crm/assets/images/memberpress.png differ diff --git a/wp-content/plugins/fluent-crm/assets/images/migrators/active_campaign.png b/wp-content/plugins/fluent-crm/assets/images/migrators/active_campaign.png new file mode 100644 index 0000000..6de5a3a Binary files /dev/null and b/wp-content/plugins/fluent-crm/assets/images/migrators/active_campaign.png differ diff --git a/wp-content/plugins/fluent-crm/assets/images/migrators/convertkit.png b/wp-content/plugins/fluent-crm/assets/images/migrators/convertkit.png new file mode 100644 index 0000000..c9b716b Binary files /dev/null and b/wp-content/plugins/fluent-crm/assets/images/migrators/convertkit.png differ diff --git a/wp-content/plugins/fluent-crm/assets/images/migrators/crm_importers.png b/wp-content/plugins/fluent-crm/assets/images/migrators/crm_importers.png new file mode 100644 index 0000000..f76757b Binary files /dev/null and b/wp-content/plugins/fluent-crm/assets/images/migrators/crm_importers.png differ diff --git a/wp-content/plugins/fluent-crm/assets/images/migrators/drip.png b/wp-content/plugins/fluent-crm/assets/images/migrators/drip.png new file mode 100644 index 0000000..c1d11ea Binary files /dev/null and b/wp-content/plugins/fluent-crm/assets/images/migrators/drip.png differ diff --git a/wp-content/plugins/fluent-crm/assets/images/migrators/mailchimp.png b/wp-content/plugins/fluent-crm/assets/images/migrators/mailchimp.png new file mode 100644 index 0000000..d70f4e8 Binary files /dev/null and b/wp-content/plugins/fluent-crm/assets/images/migrators/mailchimp.png differ diff --git a/wp-content/plugins/fluent-crm/assets/images/migrators/mailerlite.png b/wp-content/plugins/fluent-crm/assets/images/migrators/mailerlite.png new file mode 100644 index 0000000..0c47cb5 Binary files /dev/null and b/wp-content/plugins/fluent-crm/assets/images/migrators/mailerlite.png differ diff --git a/wp-content/plugins/fluent-crm/assets/images/plain-centered.png b/wp-content/plugins/fluent-crm/assets/images/plain-centered.png new file mode 100644 index 0000000..bb93be5 Binary files /dev/null and b/wp-content/plugins/fluent-crm/assets/images/plain-centered.png differ diff --git a/wp-content/plugins/fluent-crm/assets/images/plain_centered.svg b/wp-content/plugins/fluent-crm/assets/images/plain_centered.svg new file mode 100644 index 0000000..b8e281d --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/images/plain_centered.svg @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/wp-content/plugins/fluent-crm/assets/images/plain_left.svg b/wp-content/plugins/fluent-crm/assets/images/plain_left.svg new file mode 100644 index 0000000..c111789 --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/images/plain_left.svg @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/wp-content/plugins/fluent-crm/assets/images/pmpro.png b/wp-content/plugins/fluent-crm/assets/images/pmpro.png new file mode 100644 index 0000000..33f05ae Binary files /dev/null and b/wp-content/plugins/fluent-crm/assets/images/pmpro.png differ diff --git a/wp-content/plugins/fluent-crm/assets/images/promo/advanced_report_demo.png b/wp-content/plugins/fluent-crm/assets/images/promo/advanced_report_demo.png new file mode 100644 index 0000000..2a95ca1 Binary files /dev/null and b/wp-content/plugins/fluent-crm/assets/images/promo/advanced_report_demo.png differ diff --git a/wp-content/plugins/fluent-crm/assets/images/promo/dynamic_segment_all.png b/wp-content/plugins/fluent-crm/assets/images/promo/dynamic_segment_all.png new file mode 100644 index 0000000..87fca37 Binary files /dev/null and b/wp-content/plugins/fluent-crm/assets/images/promo/dynamic_segment_all.png differ diff --git a/wp-content/plugins/fluent-crm/assets/images/promo/segment_campaign.png b/wp-content/plugins/fluent-crm/assets/images/promo/segment_campaign.png new file mode 100644 index 0000000..8c7dca6 Binary files /dev/null and b/wp-content/plugins/fluent-crm/assets/images/promo/segment_campaign.png differ diff --git a/wp-content/plugins/fluent-crm/assets/images/promo/segment_create.png b/wp-content/plugins/fluent-crm/assets/images/promo/segment_create.png new file mode 100644 index 0000000..b5f78e6 Binary files /dev/null and b/wp-content/plugins/fluent-crm/assets/images/promo/segment_create.png differ diff --git a/wp-content/plugins/fluent-crm/assets/images/raw-html.png b/wp-content/plugins/fluent-crm/assets/images/raw-html.png new file mode 100644 index 0000000..800767e Binary files /dev/null and b/wp-content/plugins/fluent-crm/assets/images/raw-html.png differ diff --git a/wp-content/plugins/fluent-crm/assets/images/rcp.png b/wp-content/plugins/fluent-crm/assets/images/rcp.png new file mode 100644 index 0000000..79e3e9b Binary files /dev/null and b/wp-content/plugins/fluent-crm/assets/images/rcp.png differ diff --git a/wp-content/plugins/fluent-crm/assets/images/sample-companies.csv b/wp-content/plugins/fluent-crm/assets/images/sample-companies.csv new file mode 100644 index 0000000..bd1677e --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/images/sample-companies.csv @@ -0,0 +1,3 @@ +name,owner_email,owner_name,industry,description,logo,type,email,phone,address_line_1,address_line_2,postal_code,city,state,country,employees_number,linkedin_url,facebook_url,twitter_url,website +,,,,,,,,,,,,,,,,,,, +,,,,,,,,,,,,,,,,,,, diff --git a/wp-content/plugins/fluent-crm/assets/images/simple.png b/wp-content/plugins/fluent-crm/assets/images/simple.png new file mode 100644 index 0000000..99c2a58 Binary files /dev/null and b/wp-content/plugins/fluent-crm/assets/images/simple.png differ diff --git a/wp-content/plugins/fluent-crm/assets/images/templates/blank.jpg b/wp-content/plugins/fluent-crm/assets/images/templates/blank.jpg new file mode 100644 index 0000000..4a9eb36 Binary files /dev/null and b/wp-content/plugins/fluent-crm/assets/images/templates/blank.jpg differ diff --git a/wp-content/plugins/fluent-crm/assets/images/templates/sales.jpg b/wp-content/plugins/fluent-crm/assets/images/templates/sales.jpg new file mode 100644 index 0000000..905e26d Binary files /dev/null and b/wp-content/plugins/fluent-crm/assets/images/templates/sales.jpg differ diff --git a/wp-content/plugins/fluent-crm/assets/images/templates/standard.jpg b/wp-content/plugins/fluent-crm/assets/images/templates/standard.jpg new file mode 100644 index 0000000..05d112a Binary files /dev/null and b/wp-content/plugins/fluent-crm/assets/images/templates/standard.jpg differ diff --git a/wp-content/plugins/fluent-crm/assets/images/tile.png b/wp-content/plugins/fluent-crm/assets/images/tile.png new file mode 100644 index 0000000..aab827d Binary files /dev/null and b/wp-content/plugins/fluent-crm/assets/images/tile.png differ diff --git a/wp-content/plugins/fluent-crm/assets/images/tutorlms.jpg b/wp-content/plugins/fluent-crm/assets/images/tutorlms.jpg new file mode 100644 index 0000000..0f113c9 Binary files /dev/null and b/wp-content/plugins/fluent-crm/assets/images/tutorlms.jpg differ diff --git a/wp-content/plugins/fluent-crm/assets/images/visual-builder.svg b/wp-content/plugins/fluent-crm/assets/images/visual-builder.svg new file mode 100644 index 0000000..d25a599 --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/images/visual-builder.svg @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/wp-content/plugins/fluent-crm/assets/images/wishlist_member.png b/wp-content/plugins/fluent-crm/assets/images/wishlist_member.png new file mode 100644 index 0000000..6c49c5c Binary files /dev/null and b/wp-content/plugins/fluent-crm/assets/images/wishlist_member.png differ diff --git a/wp-content/plugins/fluent-crm/assets/images/woo.svg b/wp-content/plugins/fluent-crm/assets/images/woo.svg new file mode 100644 index 0000000..fb4a420 --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/images/woo.svg @@ -0,0 +1,15 @@ + + +WooCommerce Logo + + + +image/svg+xml + + + + + + + + diff --git a/wp-content/plugins/fluent-crm/assets/images/wordpress.svg b/wp-content/plugins/fluent-crm/assets/images/wordpress.svg new file mode 100644 index 0000000..46586fd --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/images/wordpress.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/wp-content/plugins/fluent-crm/assets/incoming_webhook.png b/wp-content/plugins/fluent-crm/assets/incoming_webhook.png new file mode 100644 index 0000000..a5f4965 Binary files /dev/null and b/wp-content/plugins/fluent-crm/assets/incoming_webhook.png differ diff --git a/wp-content/plugins/fluent-crm/assets/index.php b/wp-content/plugins/fluent-crm/assets/index.php new file mode 100644 index 0000000..6220032 --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/index.php @@ -0,0 +1,2 @@ +[]},close_on_insert:{type:Boolean,default:()=>!0},buttonText:{type:String,default:()=>"Add SmartCodes"},btnType:{type:String,default:()=>"success"},doc_url:{type:String,default:()=>""}},data:()=>({activeIndex:0,visible:!1,searchQuery:""}),methods:{selectEmoji(e){this.insertShortcode(e.data)},insertShortcode(e){this.$emit("command",e),this.close_on_insert&&(this.visible=!1)},filteredShortcodes(e={}){if(!this.searchQuery)return e||{};const a=this.searchQuery.toLowerCase(),t={};return Object.entries(e||{}).forEach(([e,s])=>{(e.toLowerCase().includes(a)||s.toLowerCase().includes(a))&&(t[e]=s)}),t}},mounted(){}},x={class:"el_pop_data_group"},g={class:"el_pop_data_headings"},C=["data-item_index","onClick"],L={key:0,class:"pop_doc"},T=["href"],w={class:"el_pop_data_body"},$={class:"el_pop_search"},j=["onClick"];const Q=k(S,[["render",function(k,S,Q,I,M,E){const U=e,V=t,z=a,A=s;return o(),r(A,{placement:"right-end",offset:50,"popper-class":"fcrm-smartcodes-popover el-dropdown-list-wrapper",visible:M.visible,"onUpdate:visible":S[1]||(S[1]=e=>M.visible=e),trigger:"click"},{reference:l(()=>[d("div",y(b(k.$attrs)),[_(z,null,{default:l(()=>[_(V,{class:"editor-add-shortcode",size:"small",type:Q.btnType,innerHTML:Q.buttonText},null,8,["type","innerHTML"])],void 0,!0),_:1})],16)]),default:l(()=>[d("div",x,[d("div",g,[d("ul",null,[(o(!0),i(n,null,c(Q.data,(e,a)=>(o(),i("li",{"data-item_index":a,key:a,class:p(M.activeIndex==a?"active_item_selected":""),onClick:e=>M.activeIndex=a},u(e.title),11,C))),128))]),Q.doc_url?(o(),i("div",L,[d("a",{href:Q.doc_url,target:"_blank",rel:"noopener"},u(k.$t?k.$t("Learn More"):"Learn More"),9,T)])):h("",!0)]),d("div",w,[d("div",$,[_(U,{modelValue:M.searchQuery,"onUpdate:modelValue":S[0]||(S[0]=e=>M.searchQuery=e),placeholder:k.$t?k.$t("Search shortcodes..."):"Search shortcodes...",clearable:""},null,8,["modelValue","placeholder"])]),(o(!0),i(n,null,c(Q.data,(e,a)=>(o(),i("div",{key:a},[m(d("ul",{class:p("el_pop_body_item_"+a)},[(o(!0),i(n,null,c(E.filteredShortcodes(e.shortcodes),(e,a)=>(o(),i("li",{onClick:e=>E.insertShortcode(a),key:a},[v(u(e),1),d("span",null,u(a),1)],8,j))),128))],2),[[f,M.activeIndex==a]])]))),128))])])],void 0),_:1},8,["visible"])}]]);export{Q as p}; diff --git a/wp-content/plugins/fluent-crm/assets/input-popover-dropdown2.js b/wp-content/plugins/fluent-crm/assets/input-popover-dropdown2.js new file mode 100644 index 0000000..023ca50 --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/input-popover-dropdown2.js @@ -0,0 +1 @@ +import{e,k as t,aO as a}from"./vendor-element-plus.js?ver=3.1.8";import{aQ as s,W as o,X as r,ab as l,a5 as n,Z as i,J as d,az as c,a0 as u,aa as p,a8 as m,a6 as _,a9 as b,ac as v,Y as f}from"./vendor.js?ver=3.1.8";import{C as h}from"./CustomIcon.js?ver=3.1.8";import{_ as y}from"./fc-bits-ui.js?ver=3.1.8";const S={name:"inputPopoverDropdownExtended",components:{CustomIcon:h},emits:["command"],props:{data:Array,close_on_insert:{type:Boolean,default:()=>!0},buttonText:{type:String,default:()=>"Add SmartCodes"},buttonIcon:{type:String,default:()=>""},buttonClass:{type:String,default:()=>""},buttonAriaLabel:{type:String,default:()=>""},btnType:{type:String,default:()=>"success"},btn_ref:{type:String,default:()=>"input-popover1"},doc_url:{type:String,default:()=>""}},data:()=>({activeIndex:0,visible:!1,searchQuery:""}),methods:{selectEmoji(e){this.insertShortcode(e.data)},insertShortcode(e){this.$emit("command",e),this.close_on_insert&&(this.visible=!1)},filteredShortcodes(e){if(!this.searchQuery)return e;const t=this.searchQuery.toLowerCase(),a={};return Object.entries(e).forEach(([e,s])=>{(e.toLowerCase().includes(t)||s.toLowerCase().includes(t))&&(a[e]=s)}),a}},mounted(){}},g={class:"el_pop_data_group"},k={class:"el_pop_data_headings"},C=["data-item_index","onClick"],x={key:0,class:"pop_doc"},I=["href"],L={class:"el_pop_data_body"},w={class:"el_pop_search"},T=["onClick"],j=["innerHTML"];const Q=y(S,[["render",function(h,y,S,Q,A,E){const M=e,V=s("custom-icon"),$=t,z=a;return o(),r("div",null,[l(z,{ref:S.btn_ref,placement:"right-end",offset:50,width:"auto","popper-class":"fcrm-smartcodes-popover el-dropdown-list-wrapper",visible:A.visible,"onUpdate:visible":y[1]||(y[1]=e=>A.visible=e),trigger:"click"},{reference:n(()=>[l($,{size:"small",class:u(S.buttonClass),type:S.btnType,"aria-label":S.buttonAriaLabel||S.buttonText},{default:n(()=>[S.buttonIcon?(o(),f(V,{key:0,type:S.buttonIcon},null,8,["type"])):(o(),r("span",{key:1,innerHTML:S.buttonText},null,8,j))],void 0,!0),_:1},8,["class","type","aria-label"])]),default:n(()=>[i("div",g,[i("div",k,[i("ul",null,[(o(!0),r(d,null,c(S.data,(e,t)=>(o(),r("li",{"data-item_index":t,key:t,class:u(A.activeIndex==t?"active_item_selected":""),onClick:e=>A.activeIndex=t},p(e.title),11,C))),128))]),S.doc_url?(o(),r("div",x,[i("a",{href:S.doc_url,target:"_blank",rel:"noopener"},p(h.$t("Learn More")),9,I)])):m("",!0)]),i("div",L,[i("div",w,[l(M,{modelValue:A.searchQuery,"onUpdate:modelValue":y[0]||(y[0]=e=>A.searchQuery=e),placeholder:h.$t("Search shortcodes..."),clearable:""},null,8,["modelValue","placeholder"])]),(o(!0),r(d,null,c(S.data,(e,t)=>(o(),r("div",{key:t},[_(i("ul",{class:u("el_pop_body_item_"+t)},[(o(!0),r(d,null,c(E.filteredShortcodes(e.shortcodes),(e,t)=>(o(),r("li",{onClick:e=>E.insertShortcode(t),key:t},[b(p(e),1),i("span",null,p(t),1)],8,T))),128))],2),[[v,A.activeIndex==t]])]))),128))])])],void 0),_:1},8,["visible"])])}]]);export{Q as I}; diff --git a/wp-content/plugins/fluent-crm/assets/libs/choices/choices.min.css b/wp-content/plugins/fluent-crm/assets/libs/choices/choices.min.css new file mode 100644 index 0000000..9260536 --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/libs/choices/choices.min.css @@ -0,0 +1 @@ +.choices{position:relative;overflow:hidden;margin-bottom:24px;font-size:16px}.choices:focus{outline:0}.choices:last-child{margin-bottom:0}.choices.is-open{overflow:visible}.choices.is-disabled .choices__inner,.choices.is-disabled .choices__input{background-color:#eaeaea;cursor:not-allowed;-webkit-user-select:none;user-select:none}.choices.is-disabled .choices__item{cursor:not-allowed}.choices [hidden]{display:none!important}.choices[data-type*=select-one]{cursor:pointer}.choices[data-type*=select-one] .choices__inner{padding-bottom:7.5px}.choices[data-type*=select-one] .choices__input{display:block;width:100%;padding:10px;border-bottom:1px solid #ddd;background-color:#fff;margin:0}.choices[data-type*=select-one] .choices__button{background-image:url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjEiIGhlaWdodD0iMjEiIHZpZXdCb3g9IjAgMCAyMSAyMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48ZyBmaWxsPSIjMDAwIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiPjxwYXRoIGQ9Ik0yLjU5Mi4wNDRsMTguMzY0IDE4LjM2NC0yLjU0OCAyLjU0OEwuMDQ0IDIuNTkyeiIvPjxwYXRoIGQ9Ik0wIDE4LjM2NEwxOC4zNjQgMGwyLjU0OCAyLjU0OEwyLjU0OCAyMC45MTJ6Ii8+PC9nPjwvc3ZnPg==);padding:0;background-size:8px;position:absolute;top:50%;right:0;margin-top:-10px;margin-right:25px;height:20px;width:20px;border-radius:10em;opacity:.25}.choices[data-type*=select-one] .choices__button:focus,.choices[data-type*=select-one] .choices__button:hover{opacity:1}.choices[data-type*=select-one] .choices__button:focus{box-shadow:0 0 0 2px #00bcd4}.choices[data-type*=select-one] .choices__item[data-value=""] .choices__button{display:none}.choices[data-type*=select-one]::after{content:"";height:0;width:0;border-style:solid;border-color:#333 transparent transparent;border-width:5px;position:absolute;right:11.5px;top:50%;margin-top:-2.5px;pointer-events:none}.choices[data-type*=select-one].is-open::after{border-color:transparent transparent #333;margin-top:-7.5px}.choices[data-type*=select-one][dir=rtl]::after{left:11.5px;right:auto}.choices[data-type*=select-one][dir=rtl] .choices__button{right:auto;left:0;margin-left:25px;margin-right:0}.choices[data-type*=select-multiple] .choices__inner,.choices[data-type*=text] .choices__inner{cursor:text}.choices[data-type*=select-multiple] .choices__button,.choices[data-type*=text] .choices__button{position:relative;display:inline-block;margin:0-4px 0 8px;padding-left:16px;border-left:1px solid #008fa1;background-image:url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjEiIGhlaWdodD0iMjEiIHZpZXdCb3g9IjAgMCAyMSAyMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48ZyBmaWxsPSIjRkZGIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiPjxwYXRoIGQ9Ik0yLjU5Mi4wNDRsMTguMzY0IDE4LjM2NC0yLjU0OCAyLjU0OEwuMDQ0IDIuNTkyeiIvPjxwYXRoIGQ9Ik0wIDE4LjM2NEwxOC4zNjQgMGwyLjU0OCAyLjU0OEwyLjU0OCAyMC45MTJ6Ii8+PC9nPjwvc3ZnPg==);background-size:8px;width:8px;line-height:1;opacity:.75;border-radius:0}.choices[data-type*=select-multiple] .choices__button:focus,.choices[data-type*=select-multiple] .choices__button:hover,.choices[data-type*=text] .choices__button:focus,.choices[data-type*=text] .choices__button:hover{opacity:1}.choices__inner{display:inline-block;vertical-align:top;width:100%;background-color:#f9f9f9;padding:7.5px 7.5px 3.75px;border:1px solid #ddd;border-radius:2.5px;font-size:14px;min-height:44px;overflow:hidden}.is-focused .choices__inner,.is-open .choices__inner{border-color:#b7b7b7}.is-open .choices__inner{border-radius:2.5px 2.5px 0 0}.is-flipped.is-open .choices__inner{border-radius:0 0 2.5px 2.5px}.choices__list{margin:0;padding-left:0;list-style:none}.choices__list--single{display:inline-block;padding:4px 16px 4px 4px;width:100%}[dir=rtl] .choices__list--single{padding-right:4px;padding-left:16px}.choices__list--single .choices__item{width:100%}.choices__list--multiple{display:inline}.choices__list--multiple .choices__item{display:inline-block;vertical-align:middle;border-radius:20px;padding:4px 10px;font-size:12px;font-weight:500;margin-right:3.75px;margin-bottom:3.75px;background-color:#00bcd4;border:1px solid #00a5bb;color:#fff;word-break:break-all;box-sizing:border-box}.choices__list--multiple .choices__item[data-deletable]{padding-right:5px}[dir=rtl] .choices__list--multiple .choices__item{margin-right:0;margin-left:3.75px}.choices__list--multiple .choices__item.is-highlighted{background-color:#00a5bb;border:1px solid #008fa1}.is-disabled .choices__list--multiple .choices__item{background-color:#aaa;border:1px solid #919191}.choices__list--dropdown,.choices__list[aria-expanded]{visibility:hidden;z-index:1;position:absolute;width:100%;background-color:#fff;border:1px solid #ddd;top:100%;margin-top:-1px;border-bottom-left-radius:2.5px;border-bottom-right-radius:2.5px;overflow:hidden;word-break:break-all;will-change:visibility}.is-active.choices__list--dropdown,.is-active.choices__list[aria-expanded]{visibility:visible}.is-open .choices__list--dropdown,.is-open .choices__list[aria-expanded]{border-color:#b7b7b7}.is-flipped .choices__list--dropdown,.is-flipped .choices__list[aria-expanded]{top:auto;bottom:100%;margin-top:0;margin-bottom:-1px;border-radius:.25rem .25rem 0 0}.choices__list--dropdown .choices__list,.choices__list[aria-expanded] .choices__list{position:relative;max-height:300px;overflow:auto;-webkit-overflow-scrolling:touch;will-change:scroll-position}.choices__list--dropdown .choices__item,.choices__list[aria-expanded] .choices__item{position:relative;padding:10px;font-size:14px}[dir=rtl] .choices__list--dropdown .choices__item,[dir=rtl] .choices__list[aria-expanded] .choices__item{text-align:right}@media (min-width:640px){.choices__list--dropdown .choices__item--selectable,.choices__list[aria-expanded] .choices__item--selectable{padding-right:100px}.choices__list--dropdown .choices__item--selectable::after,.choices__list[aria-expanded] .choices__item--selectable::after{content:attr(data-select-text);font-size:12px;opacity:0;position:absolute;right:10px;top:50%;transform:translateY(-50%)}[dir=rtl] .choices__list--dropdown .choices__item--selectable,[dir=rtl] .choices__list[aria-expanded] .choices__item--selectable{text-align:right;padding-left:100px;padding-right:10px}[dir=rtl] .choices__list--dropdown .choices__item--selectable::after,[dir=rtl] .choices__list[aria-expanded] .choices__item--selectable::after{right:auto;left:10px}}.choices__list--dropdown .choices__item--selectable.is-highlighted,.choices__list[aria-expanded] .choices__item--selectable.is-highlighted{background-color:#f2f2f2}.choices__list--dropdown .choices__item--selectable.is-highlighted::after,.choices__list[aria-expanded] .choices__item--selectable.is-highlighted::after{opacity:.5}.choices__item{cursor:default}.choices__item--selectable{cursor:pointer}.choices__item--disabled{cursor:not-allowed;-webkit-user-select:none;user-select:none;opacity:.5}.choices__heading{font-weight:600;font-size:12px;padding:10px;border-bottom:1px solid #f7f7f7;color:gray}.choices__button{text-indent:-9999px;-webkit-appearance:none;appearance:none;border:0;background-color:transparent;background-repeat:no-repeat;background-position:center;cursor:pointer}.choices__button:focus,.choices__input:focus{outline:0}.choices__input{display:inline-block;vertical-align:baseline;background-color:#f9f9f9;font-size:14px;margin-bottom:5px;border:0;border-radius:0;max-width:100%;padding:4px 0 4px 2px}.choices__input::-webkit-search-cancel-button,.choices__input::-webkit-search-decoration,.choices__input::-webkit-search-results-button,.choices__input::-webkit-search-results-decoration{display:none}.choices__input::-ms-clear,.choices__input::-ms-reveal{display:none;width:0;height:0}[dir=rtl] .choices__input{padding-right:2px;padding-left:0}.choices__placeholder{opacity:.5} \ No newline at end of file diff --git a/wp-content/plugins/fluent-crm/assets/libs/choices/choices.min.js b/wp-content/plugins/fluent-crm/assets/libs/choices/choices.min.js new file mode 100644 index 0000000..af28094 --- /dev/null +++ b/wp-content/plugins/fluent-crm/assets/libs/choices/choices.min.js @@ -0,0 +1,2 @@ +/*! For license information please see choices.min.js.LICENSE.txt */ +!function(){"use strict";var e={282:function(e,t,i){Object.defineProperty(t,"__esModule",{value:!0}),t.clearChoices=t.activateChoices=t.filterChoices=t.addChoice=void 0;var n=i(883);t.addChoice=function(e){var t=e.value,i=e.label,r=e.id,s=e.groupId,o=e.disabled,a=e.elementId,c=e.customProperties,l=e.placeholder,h=e.keyCode;return{type:n.ACTION_TYPES.ADD_CHOICE,value:t,label:i,id:r,groupId:s,disabled:o,elementId:a,customProperties:c,placeholder:l,keyCode:h}},t.filterChoices=function(e){return{type:n.ACTION_TYPES.FILTER_CHOICES,results:e}},t.activateChoices=function(e){return void 0===e&&(e=!0),{type:n.ACTION_TYPES.ACTIVATE_CHOICES,active:e}},t.clearChoices=function(){return{type:n.ACTION_TYPES.CLEAR_CHOICES}}},783:function(e,t,i){Object.defineProperty(t,"__esModule",{value:!0}),t.addGroup=void 0;var n=i(883);t.addGroup=function(e){var t=e.value,i=e.id,r=e.active,s=e.disabled;return{type:n.ACTION_TYPES.ADD_GROUP,value:t,id:i,active:r,disabled:s}}},464:function(e,t,i){Object.defineProperty(t,"__esModule",{value:!0}),t.highlightItem=t.removeItem=t.addItem=void 0;var n=i(883);t.addItem=function(e){var t=e.value,i=e.label,r=e.id,s=e.choiceId,o=e.groupId,a=e.customProperties,c=e.placeholder,l=e.keyCode;return{type:n.ACTION_TYPES.ADD_ITEM,value:t,label:i,id:r,choiceId:s,groupId:o,customProperties:a,placeholder:c,keyCode:l}},t.removeItem=function(e,t){return{type:n.ACTION_TYPES.REMOVE_ITEM,id:e,choiceId:t}},t.highlightItem=function(e,t){return{type:n.ACTION_TYPES.HIGHLIGHT_ITEM,id:e,highlighted:t}}},137:function(e,t,i){Object.defineProperty(t,"__esModule",{value:!0}),t.setIsLoading=t.resetTo=t.clearAll=void 0;var n=i(883);t.clearAll=function(){return{type:n.ACTION_TYPES.CLEAR_ALL}},t.resetTo=function(e){return{type:n.ACTION_TYPES.RESET_TO,state:e}},t.setIsLoading=function(e){return{type:n.ACTION_TYPES.SET_IS_LOADING,isLoading:e}}},373:function(e,t,i){var n=this&&this.__spreadArray||function(e,t,i){if(i||2===arguments.length)for(var n,r=0,s=t.length;r=0?this._store.getGroupById(r):null;return this._store.dispatch((0,l.highlightItem)(i,!0)),t&&this.passedElement.triggerEvent(d.EVENTS.highlightItem,{id:i,value:o,label:c,groupValue:h&&h.value?h.value:null}),this},e.prototype.unhighlightItem=function(e){if(!e||!e.id)return this;var t=e.id,i=e.groupId,n=void 0===i?-1:i,r=e.value,s=void 0===r?"":r,o=e.label,a=void 0===o?"":o,c=n>=0?this._store.getGroupById(n):null;return this._store.dispatch((0,l.highlightItem)(t,!1)),this.passedElement.triggerEvent(d.EVENTS.highlightItem,{id:t,value:s,label:a,groupValue:c&&c.value?c.value:null}),this},e.prototype.highlightAll=function(){var e=this;return this._store.items.forEach((function(t){return e.highlightItem(t)})),this},e.prototype.unhighlightAll=function(){var e=this;return this._store.items.forEach((function(t){return e.unhighlightItem(t)})),this},e.prototype.removeActiveItemsByValue=function(e){var t=this;return this._store.activeItems.filter((function(t){return t.value===e})).forEach((function(e){return t._removeItem(e)})),this},e.prototype.removeActiveItems=function(e){var t=this;return this._store.activeItems.filter((function(t){return t.id!==e})).forEach((function(e){return t._removeItem(e)})),this},e.prototype.removeHighlightedItems=function(e){var t=this;return void 0===e&&(e=!1),this._store.highlightedActiveItems.forEach((function(i){t._removeItem(i),e&&t._triggerChange(i.value)})),this},e.prototype.showDropdown=function(e){var t=this;return this.dropdown.isActive||requestAnimationFrame((function(){t.dropdown.show(),t.containerOuter.open(t.dropdown.distanceFromTopWindow),!e&&t._canSearch&&t.input.focus(),t.passedElement.triggerEvent(d.EVENTS.showDropdown,{})})),this},e.prototype.hideDropdown=function(e){var t=this;return this.dropdown.isActive?(requestAnimationFrame((function(){t.dropdown.hide(),t.containerOuter.close(),!e&&t._canSearch&&(t.input.removeActiveDescendant(),t.input.blur()),t.passedElement.triggerEvent(d.EVENTS.hideDropdown,{})})),this):this},e.prototype.getValue=function(e){void 0===e&&(e=!1);var t=this._store.activeItems.reduce((function(t,i){var n=e?i.value:i;return t.push(n),t}),[]);return this._isSelectOneElement?t[0]:t},e.prototype.setValue=function(e){var t=this;return this.initialised?(e.forEach((function(e){return t._setChoiceOrItem(e)})),this):this},e.prototype.setChoiceByValue=function(e){var t=this;return!this.initialised||this._isTextElement||(Array.isArray(e)?e:[e]).forEach((function(e){return t._findAndSelectChoiceByValue(e)})),this},e.prototype.setChoices=function(e,t,i,n){var r=this;if(void 0===e&&(e=[]),void 0===t&&(t="value"),void 0===i&&(i="label"),void 0===n&&(n=!1),!this.initialised)throw new ReferenceError("setChoices was called on a non-initialized instance of Choices");if(!this._isSelectElement)throw new TypeError("setChoices can't be used with INPUT based Choices");if("string"!=typeof t||!t)throw new TypeError("value parameter must be a name of 'value' field in passed objects");if(n&&this.clearChoices(),"function"==typeof e){var s=e(this);if("function"==typeof Promise&&s instanceof Promise)return new Promise((function(e){return requestAnimationFrame(e)})).then((function(){return r._handleLoadingState(!0)})).then((function(){return s})).then((function(e){return r.setChoices(e,t,i,n)})).catch((function(e){r.config.silent||console.error(e)})).then((function(){return r._handleLoadingState(!1)})).then((function(){return r}));if(!Array.isArray(s))throw new TypeError(".setChoices first argument function must return either array of choices or Promise, got: ".concat(typeof s));return this.setChoices(s,t,i,!1)}if(!Array.isArray(e))throw new TypeError(".setChoices must be called either with array of choices with a function resulting into Promise of array of choices");return this.containerOuter.removeLoadingState(),this._startLoading(),e.forEach((function(e){if(e.choices)r._addGroup({id:e.id?parseInt("".concat(e.id),10):null,group:e,valueKey:t,labelKey:i});else{var n=e;r._addChoice({value:n[t],label:n[i],isSelected:!!n.selected,isDisabled:!!n.disabled,placeholder:!!n.placeholder,customProperties:n.customProperties})}})),this._stopLoading(),this},e.prototype.clearChoices=function(){return this._store.dispatch((0,a.clearChoices)()),this},e.prototype.clearStore=function(){return this._store.dispatch((0,h.clearAll)()),this},e.prototype.clearInput=function(){var e=!this._isSelectOneElement;return this.input.clear(e),!this._isTextElement&&this._canSearch&&(this._isSearching=!1,this._store.dispatch((0,a.activateChoices)(!0))),this},e.prototype._render=function(){if(!this._store.isLoading()){this._currentState=this._store.state;var e=this._currentState.choices!==this._prevState.choices||this._currentState.groups!==this._prevState.groups||this._currentState.items!==this._prevState.items,t=this._isSelectElement,i=this._currentState.items!==this._prevState.items;e&&(t&&this._renderChoices(),i&&this._renderItems(),this._prevState=this._currentState)}},e.prototype._renderChoices=function(){var e=this,t=this._store,i=t.activeGroups,n=t.activeChoices,r=document.createDocumentFragment();if(this.choiceList.clear(),this.config.resetScrollPosition&&requestAnimationFrame((function(){return e.choiceList.scrollToTop()})),i.length>=1&&!this._isSearching){var s=n.filter((function(e){return!0===e.placeholder&&-1===e.groupId}));s.length>=1&&(r=this._createChoicesFragment(s,r)),r=this._createGroupsFragment(i,n,r)}else n.length>=1&&(r=this._createChoicesFragment(n,r));if(r.childNodes&&r.childNodes.length>0){var o=this._store.activeItems,a=this._canAddItem(o,this.input.value);if(a.response)this.choiceList.append(r),this._highlightChoice();else{var c=this._getTemplate("notice",a.notice);this.choiceList.append(c)}}else{var l=void 0;c=void 0,this._isSearching?(c="function"==typeof this.config.noResultsText?this.config.noResultsText():this.config.noResultsText,l=this._getTemplate("notice",c,"no-results")):(c="function"==typeof this.config.noChoicesText?this.config.noChoicesText():this.config.noChoicesText,l=this._getTemplate("notice",c,"no-choices")),this.choiceList.append(l)}},e.prototype._renderItems=function(){var e=this._store.activeItems||[];this.itemList.clear();var t=this._createItemsFragment(e);t.childNodes&&this.itemList.append(t)},e.prototype._createGroupsFragment=function(e,t,i){var n=this;return void 0===i&&(i=document.createDocumentFragment()),this.config.shouldSort&&e.sort(this.config.sorter),e.forEach((function(e){var r=function(e){return t.filter((function(t){return n._isSelectOneElement?t.groupId===e.id:t.groupId===e.id&&("always"===n.config.renderSelectedChoices||!t.selected)}))}(e);if(r.length>=1){var s=n._getTemplate("choiceGroup",e);i.appendChild(s),n._createChoicesFragment(r,i,!0)}})),i},e.prototype._createChoicesFragment=function(e,t,i){var r=this;void 0===t&&(t=document.createDocumentFragment()),void 0===i&&(i=!1);var s=this.config,o=s.renderSelectedChoices,a=s.searchResultLimit,c=s.renderChoiceLimit,l=this._isSearching?f.sortByScore:this.config.sorter,h=function(e){if("auto"!==o||r._isSelectOneElement||!e.selected){var i=r._getTemplate("choice",e,r.config.itemSelectText);t.appendChild(i)}},u=e;"auto"!==o||this._isSelectOneElement||(u=e.filter((function(e){return!e.selected})));var d=u.reduce((function(e,t){return t.placeholder?e.placeholderChoices.push(t):e.normalChoices.push(t),e}),{placeholderChoices:[],normalChoices:[]}),p=d.placeholderChoices,m=d.normalChoices;(this.config.shouldSort||this._isSearching)&&m.sort(l);var v=u.length,g=this._isSelectOneElement?n(n([],p,!0),m,!0):m;this._isSearching?v=a:c&&c>0&&!i&&(v=c);for(var _=0;_=n){var o=r?this._searchChoices(e):0;this.passedElement.triggerEvent(d.EVENTS.search,{value:e,resultCount:o})}else s&&(this._isSearching=!1,this._store.dispatch((0,a.activateChoices)(!0)))}},e.prototype._canAddItem=function(e,t){var i=!0,n="function"==typeof this.config.addItemText?this.config.addItemText(t):this.config.addItemText;if(!this._isSelectOneElement){var r=(0,f.existsInArray)(e,t);this.config.maxItemCount>0&&this.config.maxItemCount<=e.length&&(i=!1,n="function"==typeof this.config.maxItemText?this.config.maxItemText(this.config.maxItemCount):this.config.maxItemText),!this.config.duplicateItemsAllowed&&r&&i&&(i=!1,n="function"==typeof this.config.uniqueItemText?this.config.uniqueItemText(t):this.config.uniqueItemText),this._isTextElement&&this.config.addItems&&i&&"function"==typeof this.config.addItemFilter&&!this.config.addItemFilter(t)&&(i=!1,n="function"==typeof this.config.customAddItemText?this.config.customAddItemText(t):this.config.customAddItemText)}return{response:i,notice:n}},e.prototype._searchChoices=function(e){var t="string"==typeof e?e.trim():e,i="string"==typeof this._currentValue?this._currentValue.trim():this._currentValue;if(t.length<1&&t==="".concat(i," "))return 0;var r=this._store.searchableChoices,s=t,c=Object.assign(this.config.fuseOptions,{keys:n([],this.config.searchFields,!0),includeMatches:!0}),l=new o.default(r,c).search(s);return this._currentValue=t,this._highlightPosition=0,this._isSearching=!0,this._store.dispatch((0,a.filterChoices)(l)),l.length},e.prototype._addEventListeners=function(){var e=document.documentElement;e.addEventListener("touchend",this._onTouchEnd,!0),this.containerOuter.element.addEventListener("keydown",this._onKeyDown,!0),this.containerOuter.element.addEventListener("mousedown",this._onMouseDown,!0),e.addEventListener("click",this._onClick,{passive:!0}),e.addEventListener("touchmove",this._onTouchMove,{passive:!0}),this.dropdown.element.addEventListener("mouseover",this._onMouseOver,{passive:!0}),this._isSelectOneElement&&(this.containerOuter.element.addEventListener("focus",this._onFocus,{passive:!0}),this.containerOuter.element.addEventListener("blur",this._onBlur,{passive:!0})),this.input.element.addEventListener("keyup",this._onKeyUp,{passive:!0}),this.input.element.addEventListener("focus",this._onFocus,{passive:!0}),this.input.element.addEventListener("blur",this._onBlur,{passive:!0}),this.input.element.form&&this.input.element.form.addEventListener("reset",this._onFormReset,{passive:!0}),this.input.addEventListeners()},e.prototype._removeEventListeners=function(){var e=document.documentElement;e.removeEventListener("touchend",this._onTouchEnd,!0),this.containerOuter.element.removeEventListener("keydown",this._onKeyDown,!0),this.containerOuter.element.removeEventListener("mousedown",this._onMouseDown,!0),e.removeEventListener("click",this._onClick),e.removeEventListener("touchmove",this._onTouchMove),this.dropdown.element.removeEventListener("mouseover",this._onMouseOver),this._isSelectOneElement&&(this.containerOuter.element.removeEventListener("focus",this._onFocus),this.containerOuter.element.removeEventListener("blur",this._onBlur)),this.input.element.removeEventListener("keyup",this._onKeyUp),this.input.element.removeEventListener("focus",this._onFocus),this.input.element.removeEventListener("blur",this._onBlur),this.input.element.form&&this.input.element.form.removeEventListener("reset",this._onFormReset),this.input.removeEventListeners()},e.prototype._onKeyDown=function(e){var t=e.keyCode,i=this._store.activeItems,n=this.input.isFocussed,r=this.dropdown.isActive,s=this.itemList.hasChildren(),o=String.fromCharCode(t),a=/[^\x00-\x1F]/.test(o),c=d.KEY_CODES.BACK_KEY,l=d.KEY_CODES.DELETE_KEY,h=d.KEY_CODES.ENTER_KEY,u=d.KEY_CODES.A_KEY,p=d.KEY_CODES.ESC_KEY,f=d.KEY_CODES.UP_KEY,m=d.KEY_CODES.DOWN_KEY,v=d.KEY_CODES.PAGE_UP_KEY,g=d.KEY_CODES.PAGE_DOWN_KEY;switch(this._isTextElement||r||!a||(this.showDropdown(),this.input.isFocussed||(this.input.value+=e.key.toLowerCase())),t){case u:return this._onSelectKey(e,s);case h:return this._onEnterKey(e,i,r);case p:return this._onEscapeKey(r);case f:case v:case m:case g:return this._onDirectionKey(e,r);case l:case c:return this._onDeleteKey(e,i,n)}},e.prototype._onKeyUp=function(e){var t=e.target,i=e.keyCode,n=this.input.value,r=this._store.activeItems,s=this._canAddItem(r,n),o=d.KEY_CODES.BACK_KEY,c=d.KEY_CODES.DELETE_KEY;if(this._isTextElement)if(s.notice&&n){var l=this._getTemplate("notice",s.notice);this.dropdown.element.innerHTML=l.outerHTML,this.showDropdown(!0)}else this.hideDropdown(!0);else{var h=(i===o||i===c)&&t&&!t.value,u=!this._isTextElement&&this._isSearching,p=this._canSearch&&s.response;h&&u?(this._isSearching=!1,this._store.dispatch((0,a.activateChoices)(!0))):p&&this._handleSearch(this.input.rawValue)}this._canSearch=this.config.searchEnabled},e.prototype._onSelectKey=function(e,t){var i=e.ctrlKey,n=e.metaKey;(i||n)&&t&&(this._canSearch=!1,this.config.removeItems&&!this.input.value&&this.input.element===document.activeElement&&this.highlightAll())},e.prototype._onEnterKey=function(e,t,i){var n=e.target,r=d.KEY_CODES.ENTER_KEY,s=n&&n.hasAttribute("data-button");if(this._isTextElement&&n&&n.value){var o=this.input.value;this._canAddItem(t,o).response&&(this.hideDropdown(!0),this._addItem({value:o}),this._triggerChange(o),this.clearInput())}if(s&&(this._handleButtonAction(t,n),e.preventDefault()),i){var a=this.dropdown.getChild(".".concat(this.config.classNames.highlightedState));a&&(t[0]&&(t[0].keyCode=r),this._handleChoiceAction(t,a)),e.preventDefault()}else this._isSelectOneElement&&(this.showDropdown(),e.preventDefault())},e.prototype._onEscapeKey=function(e){e&&(this.hideDropdown(!0),this.containerOuter.focus())},e.prototype._onDirectionKey=function(e,t){var i=e.keyCode,n=e.metaKey,r=d.KEY_CODES.DOWN_KEY,s=d.KEY_CODES.PAGE_UP_KEY,o=d.KEY_CODES.PAGE_DOWN_KEY;if(t||this._isSelectOneElement){this.showDropdown(),this._canSearch=!1;var a=i===r||i===o?1:-1,c="[data-choice-selectable]",l=void 0;if(n||i===o||i===s)l=a>0?this.dropdown.element.querySelector("".concat(c,":last-of-type")):this.dropdown.element.querySelector(c);else{var h=this.dropdown.element.querySelector(".".concat(this.config.classNames.highlightedState));l=h?(0,f.getAdjacentEl)(h,c,a):this.dropdown.element.querySelector(c)}l&&((0,f.isScrolledIntoView)(l,this.choiceList.element,a)||this.choiceList.scrollToChildElement(l,a),this._highlightChoice(l)),e.preventDefault()}},e.prototype._onDeleteKey=function(e,t,i){var n=e.target;this._isSelectOneElement||n.value||!i||(this._handleBackspace(t),e.preventDefault())},e.prototype._onTouchMove=function(){this._wasTap&&(this._wasTap=!1)},e.prototype._onTouchEnd=function(e){var t=(e||e.touches[0]).target;this._wasTap&&this.containerOuter.element.contains(t)&&((t===this.containerOuter.element||t===this.containerInner.element)&&(this._isTextElement?this.input.focus():this._isSelectMultipleElement&&this.showDropdown()),e.stopPropagation()),this._wasTap=!0},e.prototype._onMouseDown=function(e){var t=e.target;if(t instanceof HTMLElement){if(_&&this.choiceList.element.contains(t)){var i=this.choiceList.element.firstElementChild,n="ltr"===this._direction?e.offsetX>=i.offsetWidth:e.offsetX0&&this.unhighlightAll(),this.containerOuter.removeFocusState(),this.hideDropdown(!0))},e.prototype._onFocus=function(e){var t,i=this,n=e.target;n&&this.containerOuter.element.contains(n)&&((t={})[d.TEXT_TYPE]=function(){n===i.input.element&&i.containerOuter.addFocusState()},t[d.SELECT_ONE_TYPE]=function(){i.containerOuter.addFocusState(),n===i.input.element&&i.showDropdown(!0)},t[d.SELECT_MULTIPLE_TYPE]=function(){n===i.input.element&&(i.showDropdown(!0),i.containerOuter.addFocusState())},t)[this.passedElement.element.type]()},e.prototype._onBlur=function(e){var t,i=this,n=e.target;if(n&&this.containerOuter.element.contains(n)&&!this._isScrollingOnIe){var r=this._store.activeItems.some((function(e){return e.highlighted}));((t={})[d.TEXT_TYPE]=function(){n===i.input.element&&(i.containerOuter.removeFocusState(),r&&i.unhighlightAll(),i.hideDropdown(!0))},t[d.SELECT_ONE_TYPE]=function(){i.containerOuter.removeFocusState(),(n===i.input.element||n===i.containerOuter.element&&!i._canSearch)&&i.hideDropdown(!0)},t[d.SELECT_MULTIPLE_TYPE]=function(){n===i.input.element&&(i.containerOuter.removeFocusState(),i.hideDropdown(!0),r&&i.unhighlightAll())},t)[this.passedElement.element.type]()}else this._isScrollingOnIe=!1,this.input.element.focus()},e.prototype._onFormReset=function(){this._store.dispatch((0,h.resetTo)(this._initialState))},e.prototype._highlightChoice=function(e){var t=this;void 0===e&&(e=null);var i=Array.from(this.dropdown.element.querySelectorAll("[data-choice-selectable]"));if(i.length){var n=e;Array.from(this.dropdown.element.querySelectorAll(".".concat(this.config.classNames.highlightedState))).forEach((function(e){e.classList.remove(t.config.classNames.highlightedState),e.setAttribute("aria-selected","false")})),n?this._highlightPosition=i.indexOf(n):(n=i.length>this._highlightPosition?i[this._highlightPosition]:i[i.length-1])||(n=i[0]),n.classList.add(this.config.classNames.highlightedState),n.setAttribute("aria-selected","true"),this.passedElement.triggerEvent(d.EVENTS.highlightChoice,{el:n}),this.dropdown.isActive&&(this.input.setActiveDescendant(n.id),this.containerOuter.setActiveDescendant(n.id))}},e.prototype._addItem=function(e){var t=e.value,i=e.label,n=void 0===i?null:i,r=e.choiceId,s=void 0===r?-1:r,o=e.groupId,a=void 0===o?-1:o,c=e.customProperties,h=void 0===c?{}:c,u=e.placeholder,p=void 0!==u&&u,f=e.keyCode,m=void 0===f?-1:f,v="string"==typeof t?t.trim():t,g=this._store.items,_=n||v,y=s||-1,E=a>=0?this._store.getGroupById(a):null,b=g?g.length+1:1;this.config.prependValue&&(v=this.config.prependValue+v.toString()),this.config.appendValue&&(v+=this.config.appendValue.toString()),this._store.dispatch((0,l.addItem)({value:v,label:_,id:b,choiceId:y,groupId:a,customProperties:h,placeholder:p,keyCode:m})),this._isSelectOneElement&&this.removeActiveItems(b),this.passedElement.triggerEvent(d.EVENTS.addItem,{id:b,value:v,label:_,customProperties:h,groupValue:E&&E.value?E.value:null,keyCode:m})},e.prototype._removeItem=function(e){var t=e.id,i=e.value,n=e.label,r=e.customProperties,s=e.choiceId,o=e.groupId,a=o&&o>=0?this._store.getGroupById(o):null;t&&s&&(this._store.dispatch((0,l.removeItem)(t,s)),this.passedElement.triggerEvent(d.EVENTS.removeItem,{id:t,value:i,label:n,customProperties:r,groupValue:a&&a.value?a.value:null}))},e.prototype._addChoice=function(e){var t=e.value,i=e.label,n=void 0===i?null:i,r=e.isSelected,s=void 0!==r&&r,o=e.isDisabled,c=void 0!==o&&o,l=e.groupId,h=void 0===l?-1:l,u=e.customProperties,d=void 0===u?{}:u,p=e.placeholder,f=void 0!==p&&p,m=e.keyCode,v=void 0===m?-1:m;if(null!=t){var g=this._store.choices,_=n||t,y=g?g.length+1:1,E="".concat(this._baseId,"-").concat(this._idNames.itemChoice,"-").concat(y);this._store.dispatch((0,a.addChoice)({id:y,groupId:h,elementId:E,value:t,label:_,disabled:c,customProperties:d,placeholder:f,keyCode:v})),s&&this._addItem({value:t,label:_,choiceId:y,customProperties:d,placeholder:f,keyCode:v})}},e.prototype._addGroup=function(e){var t=this,i=e.group,n=e.id,r=e.valueKey,s=void 0===r?"value":r,o=e.labelKey,a=void 0===o?"label":o,l=(0,f.isType)("Object",i)?i.choices:Array.from(i.getElementsByTagName("OPTION")),h=n||Math.floor((new Date).valueOf()*Math.random()),u=!!i.disabled&&i.disabled;l?(this._store.dispatch((0,c.addGroup)({value:i.label,id:h,active:!0,disabled:u})),l.forEach((function(e){var i=e.disabled||e.parentNode&&e.parentNode.disabled;t._addChoice({value:e[s],label:(0,f.isType)("Object",e)?e[a]:e.innerHTML,isSelected:e.selected,isDisabled:i,groupId:h,customProperties:e.customProperties,placeholder:e.placeholder})}))):this._store.dispatch((0,c.addGroup)({value:i.label,id:i.id,active:!1,disabled:i.disabled}))},e.prototype._getTemplate=function(e){for(var t,i=[],r=1;r0?this.element.scrollTop+o-r:e.offsetTop;requestAnimationFrame((function(){i._animateScroll(a,t)}))}},e.prototype._scrollDown=function(e,t,i){var n=(i-e)/t,r=n>1?n:1;this.element.scrollTop=e+r},e.prototype._scrollUp=function(e,t,i){var n=(e-i)/t,r=n>1?n:1;this.element.scrollTop=e-r},e.prototype._animateScroll=function(e,t){var i=this,r=n.SCROLLING_SPEED,s=this.element.scrollTop,o=!1;t>0?(this._scrollDown(s,r,e),se&&(o=!0)),o&&requestAnimationFrame((function(){i._animateScroll(e,t)}))},e}();t.default=r},730:function(e,t,i){Object.defineProperty(t,"__esModule",{value:!0});var n=i(799),r=function(){function e(e){var t=e.element,i=e.classNames;if(this.element=t,this.classNames=i,!(t instanceof HTMLInputElement||t instanceof HTMLSelectElement))throw new TypeError("Invalid element passed");this.isDisabled=!1}return Object.defineProperty(e.prototype,"isActive",{get:function(){return"active"===this.element.dataset.choice},enumerable:!1,configurable:!0}),Object.defineProperty(e.prototype,"dir",{get:function(){return this.element.dir},enumerable:!1,configurable:!0}),Object.defineProperty(e.prototype,"value",{get:function(){return this.element.value},set:function(e){this.element.value=e},enumerable:!1,configurable:!0}),e.prototype.conceal=function(){this.element.classList.add(this.classNames.input),this.element.hidden=!0,this.element.tabIndex=-1;var e=this.element.getAttribute("style");e&&this.element.setAttribute("data-choice-orig-style",e),this.element.setAttribute("data-choice","active")},e.prototype.reveal=function(){this.element.classList.remove(this.classNames.input),this.element.hidden=!1,this.element.removeAttribute("tabindex");var e=this.element.getAttribute("data-choice-orig-style");e?(this.element.removeAttribute("data-choice-orig-style"),this.element.setAttribute("style",e)):this.element.removeAttribute("style"),this.element.removeAttribute("data-choice"),this.element.value=this.element.value},e.prototype.enable=function(){this.element.removeAttribute("disabled"),this.element.disabled=!1,this.isDisabled=!1},e.prototype.disable=function(){this.element.setAttribute("disabled",""),this.element.disabled=!0,this.isDisabled=!0},e.prototype.triggerEvent=function(e,t){(0,n.dispatchEvent)(this.element,e,t)},e}();t.default=r},541:function(e,t,i){var n,r=this&&this.__extends||(n=function(e,t){return n=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var i in t)Object.prototype.hasOwnProperty.call(t,i)&&(e[i]=t[i])},n(e,t)},function(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Class extends value "+String(t)+" is not a constructor or null");function i(){this.constructor=e}n(e,t),e.prototype=null===t?Object.create(t):(i.prototype=t.prototype,new i)}),s=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var o=function(e){function t(t){var i=t.element,n=t.classNames,r=t.delimiter,s=e.call(this,{element:i,classNames:n})||this;return s.delimiter=r,s}return r(t,e),Object.defineProperty(t.prototype,"value",{get:function(){return this.element.value},set:function(e){this.element.setAttribute("value",e),this.element.value=e},enumerable:!1,configurable:!0}),t}(s(i(730)).default);t.default=o},982:function(e,t,i){var n,r=this&&this.__extends||(n=function(e,t){return n=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var i in t)Object.prototype.hasOwnProperty.call(t,i)&&(e[i]=t[i])},n(e,t)},function(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Class extends value "+String(t)+" is not a constructor or null");function i(){this.constructor=e}n(e,t),e.prototype=null===t?Object.create(t):(i.prototype=t.prototype,new i)}),s=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var o=function(e){function t(t){var i=t.element,n=t.classNames,r=t.template,s=e.call(this,{element:i,classNames:n})||this;return s.template=r,s}return r(t,e),Object.defineProperty(t.prototype,"placeholderOption",{get:function(){return this.element.querySelector('option[value=""]')||this.element.querySelector("option[placeholder]")},enumerable:!1,configurable:!0}),Object.defineProperty(t.prototype,"optionGroups",{get:function(){return Array.from(this.element.getElementsByTagName("OPTGROUP"))},enumerable:!1,configurable:!0}),Object.defineProperty(t.prototype,"options",{get:function(){return Array.from(this.element.options)},set:function(e){var t=this,i=document.createDocumentFragment();e.forEach((function(e){return n=e,r=t.template(n),void i.appendChild(r);var n,r})),this.appendDocFragment(i)},enumerable:!1,configurable:!0}),t.prototype.appendDocFragment=function(e){this.element.innerHTML="",this.element.appendChild(e)},t}(s(i(730)).default);t.default=o},883:function(e,t){Object.defineProperty(t,"__esModule",{value:!0}),t.SCROLLING_SPEED=t.SELECT_MULTIPLE_TYPE=t.SELECT_ONE_TYPE=t.TEXT_TYPE=t.KEY_CODES=t.ACTION_TYPES=t.EVENTS=void 0,t.EVENTS={showDropdown:"showDropdown",hideDropdown:"hideDropdown",change:"change",choice:"choice",search:"search",addItem:"addItem",removeItem:"removeItem",highlightItem:"highlightItem",highlightChoice:"highlightChoice",unhighlightItem:"unhighlightItem"},t.ACTION_TYPES={ADD_CHOICE:"ADD_CHOICE",FILTER_CHOICES:"FILTER_CHOICES",ACTIVATE_CHOICES:"ACTIVATE_CHOICES",CLEAR_CHOICES:"CLEAR_CHOICES",ADD_GROUP:"ADD_GROUP",ADD_ITEM:"ADD_ITEM",REMOVE_ITEM:"REMOVE_ITEM",HIGHLIGHT_ITEM:"HIGHLIGHT_ITEM",CLEAR_ALL:"CLEAR_ALL",RESET_TO:"RESET_TO",SET_IS_LOADING:"SET_IS_LOADING"},t.KEY_CODES={BACK_KEY:46,DELETE_KEY:8,ENTER_KEY:13,A_KEY:65,ESC_KEY:27,UP_KEY:38,DOWN_KEY:40,PAGE_UP_KEY:33,PAGE_DOWN_KEY:34},t.TEXT_TYPE="text",t.SELECT_ONE_TYPE="select-one",t.SELECT_MULTIPLE_TYPE="select-multiple",t.SCROLLING_SPEED=4},789:function(e,t,i){Object.defineProperty(t,"__esModule",{value:!0}),t.DEFAULT_CONFIG=t.DEFAULT_CLASSNAMES=void 0;var n=i(799);t.DEFAULT_CLASSNAMES={containerOuter:"choices",containerInner:"choices__inner",input:"choices__input",inputCloned:"choices__input--cloned",list:"choices__list",listItems:"choices__list--multiple",listSingle:"choices__list--single",listDropdown:"choices__list--dropdown",item:"choices__item",itemSelectable:"choices__item--selectable",itemDisabled:"choices__item--disabled",itemChoice:"choices__item--choice",placeholder:"choices__placeholder",group:"choices__group",groupHeading:"choices__heading",button:"choices__button",activeState:"is-active",focusState:"is-focused",openState:"is-open",disabledState:"is-disabled",highlightedState:"is-highlighted",selectedState:"is-selected",flippedState:"is-flipped",loadingState:"is-loading",noResults:"has-no-results",noChoices:"has-no-choices"},t.DEFAULT_CONFIG={items:[],choices:[],silent:!1,renderChoiceLimit:-1,maxItemCount:-1,addItems:!0,addItemFilter:null,removeItems:!0,removeItemButton:!1,editItems:!1,allowHTML:!0,duplicateItemsAllowed:!0,delimiter:",",paste:!0,searchEnabled:!0,searchChoices:!0,searchFloor:1,searchResultLimit:4,searchFields:["label","value"],position:"auto",resetScrollPosition:!0,shouldSort:!0,shouldSortItems:!1,sorter:n.sortByAlpha,placeholder:!0,placeholderValue:null,searchPlaceholderValue:null,prependValue:null,appendValue:null,renderSelectedChoices:"auto",loadingText:"Loading...",noResultsText:"No results found",noChoicesText:"No choices to choose from",itemSelectText:"Press to select",uniqueItemText:"Only unique values can be added",customAddItemText:"Only values matching specific conditions can be added",addItemText:function(e){return'Press Enter to add "'.concat((0,n.sanitise)(e),'"')},maxItemText:function(e){return"Only ".concat(e," values can be added")},valueComparer:function(e,t){return e===t},fuseOptions:{includeScore:!0},labelId:"",callbackOnInit:null,callbackOnCreateTemplates:null,classNames:t.DEFAULT_CLASSNAMES}},18:function(e,t){Object.defineProperty(t,"__esModule",{value:!0})},978:function(e,t){Object.defineProperty(t,"__esModule",{value:!0})},948:function(e,t){Object.defineProperty(t,"__esModule",{value:!0})},359:function(e,t){Object.defineProperty(t,"__esModule",{value:!0})},285:function(e,t){Object.defineProperty(t,"__esModule",{value:!0})},533:function(e,t){Object.defineProperty(t,"__esModule",{value:!0})},187:function(e,t,i){var n=this&&this.__createBinding||(Object.create?function(e,t,i,n){void 0===n&&(n=i);var r=Object.getOwnPropertyDescriptor(t,i);r&&!("get"in r?!t.__esModule:r.writable||r.configurable)||(r={enumerable:!0,get:function(){return t[i]}}),Object.defineProperty(e,n,r)}:function(e,t,i,n){void 0===n&&(n=i),e[n]=t[i]}),r=this&&this.__exportStar||function(e,t){for(var i in e)"default"===i||Object.prototype.hasOwnProperty.call(t,i)||n(t,e,i)};Object.defineProperty(t,"__esModule",{value:!0}),r(i(18),t),r(i(978),t),r(i(948),t),r(i(359),t),r(i(285),t),r(i(533),t),r(i(287),t),r(i(132),t),r(i(837),t),r(i(598),t),r(i(369),t),r(i(37),t),r(i(47),t),r(i(923),t),r(i(876),t)},287:function(e,t){Object.defineProperty(t,"__esModule",{value:!0})},132:function(e,t){Object.defineProperty(t,"__esModule",{value:!0})},837:function(e,t){Object.defineProperty(t,"__esModule",{value:!0})},598:function(e,t){Object.defineProperty(t,"__esModule",{value:!0})},37:function(e,t){Object.defineProperty(t,"__esModule",{value:!0})},369:function(e,t){Object.defineProperty(t,"__esModule",{value:!0})},47:function(e,t){Object.defineProperty(t,"__esModule",{value:!0})},923:function(e,t){Object.defineProperty(t,"__esModule",{value:!0})},876:function(e,t){Object.defineProperty(t,"__esModule",{value:!0})},799:function(e,t){var i;Object.defineProperty(t,"__esModule",{value:!0}),t.parseCustomProperties=t.diff=t.cloneObject=t.existsInArray=t.dispatchEvent=t.sortByScore=t.sortByAlpha=t.strToEl=t.sanitise=t.isScrolledIntoView=t.getAdjacentEl=t.wrap=t.isType=t.getType=t.generateId=t.generateChars=t.getRandomNumber=void 0,t.getRandomNumber=function(e,t){return Math.floor(Math.random()*(t-e)+e)},t.generateChars=function(e){return Array.from({length:e},(function(){return(0,t.getRandomNumber)(0,36).toString(36)})).join("")},t.generateId=function(e,i){var n=e.id||e.name&&"".concat(e.name,"-").concat((0,t.generateChars)(2))||(0,t.generateChars)(4);return n=n.replace(/(:|\.|\[|\]|,)/g,""),"".concat(i,"-").concat(n)},t.getType=function(e){return Object.prototype.toString.call(e).slice(8,-1)},t.isType=function(e,i){return null!=i&&(0,t.getType)(i)===e},t.wrap=function(e,t){return void 0===t&&(t=document.createElement("div")),e.parentNode&&(e.nextSibling?e.parentNode.insertBefore(t,e.nextSibling):e.parentNode.appendChild(t)),t.appendChild(e)},t.getAdjacentEl=function(e,t,i){void 0===i&&(i=1);for(var n="".concat(i>0?"next":"previous","ElementSibling"),r=e[n];r;){if(r.matches(t))return r;r=r[n]}return r},t.isScrolledIntoView=function(e,t,i){return void 0===i&&(i=1),!!e&&(i>0?t.scrollTop+t.offsetHeight>=e.offsetTop+e.offsetHeight:e.offsetTop>=t.scrollTop)},t.sanitise=function(e){return"string"!=typeof e?e:e.replace(/&/g,"&").replace(/>/g,">").replace(/-1?e.map((function(e){var t=e;return t.id===parseInt("".concat(o.choiceId),10)&&(t.selected=!0),t})):e;case"REMOVE_ITEM":var a=n;return a.choiceId&&a.choiceId>-1?e.map((function(e){var t=e;return t.id===parseInt("".concat(a.choiceId),10)&&(t.selected=!1),t})):e;case"FILTER_CHOICES":var c=n;return e.map((function(e){var t=e;return t.active=c.results.some((function(e){var i=e.item,n=e.score;return i.id===t.id&&(t.score=n,!0)})),t}));case"ACTIVATE_CHOICES":var l=n;return e.map((function(e){var t=e;return t.active=l.active,t}));case"CLEAR_CHOICES":return t.defaultState;default:return e}}},871:function(e,t){var i=this&&this.__spreadArray||function(e,t,i){if(i||2===arguments.length)for(var n,r=0,s=t.length;r0?"treeitem":"option"),Object.assign(E.dataset,{choice:"",id:d,value:p,selectText:i}),g?(E.classList.add(h),E.dataset.choiceDisabled="",E.setAttribute("aria-disabled","true")):(E.classList.add(c),E.dataset.choiceSelectable=""),E},input:function(e,t){var i=e.classNames,n=i.input,r=i.inputCloned,s=Object.assign(document.createElement("input"),{type:"search",name:"search_terms",className:"".concat(n," ").concat(r),autocomplete:"off",autocapitalize:"off",spellcheck:!1});return s.setAttribute("role","textbox"),s.setAttribute("aria-autocomplete","list"),s.setAttribute("aria-label",t),s},dropdown:function(e){var t=e.classNames,i=t.list,n=t.listDropdown,r=document.createElement("div");return r.classList.add(i,n),r.setAttribute("aria-expanded","false"),r},notice:function(e,t,i){var n,r=e.allowHTML,s=e.classNames,o=s.item,a=s.itemChoice,c=s.noResults,l=s.noChoices;void 0===i&&(i="");var h=[o,a];return"no-choices"===i?h.push(l):"no-results"===i&&h.push(c),Object.assign(document.createElement("div"),((n={})[r?"innerHTML":"innerText"]=t,n.className=h.join(" "),n))},option:function(e){var t=e.label,i=e.value,n=e.customProperties,r=e.active,s=e.disabled,o=new Option(t,i,!1,r);return n&&(o.dataset.customProperties="".concat(n)),o.disabled=!!s,o}};t.default=i},996:function(e){var t=function(e){return function(e){return!!e&&"object"==typeof e}(e)&&!function(e){var t=Object.prototype.toString.call(e);return"[object RegExp]"===t||"[object Date]"===t||function(e){return e.$$typeof===i}(e)}(e)},i="function"==typeof Symbol&&Symbol.for?Symbol.for("react.element"):60103;function n(e,t){return!1!==t.clone&&t.isMergeableObject(e)?a((i=e,Array.isArray(i)?[]:{}),e,t):e;var i}function r(e,t,i){return e.concat(t).map((function(e){return n(e,i)}))}function s(e){return Object.keys(e).concat(function(e){return Object.getOwnPropertySymbols?Object.getOwnPropertySymbols(e).filter((function(t){return e.propertyIsEnumerable(t)})):[]}(e))}function o(e,t){try{return t in e}catch(e){return!1}}function a(e,i,c){(c=c||{}).arrayMerge=c.arrayMerge||r,c.isMergeableObject=c.isMergeableObject||t,c.cloneUnlessOtherwiseSpecified=n;var l=Array.isArray(i);return l===Array.isArray(e)?l?c.arrayMerge(e,i,c):function(e,t,i){var r={};return i.isMergeableObject(e)&&s(e).forEach((function(t){r[t]=n(e[t],i)})),s(t).forEach((function(s){(function(e,t){return o(e,t)&&!(Object.hasOwnProperty.call(e,t)&&Object.propertyIsEnumerable.call(e,t))})(e,s)||(o(e,s)&&i.isMergeableObject(t[s])?r[s]=function(e,t){if(!t.customMerge)return a;var i=t.customMerge(e);return"function"==typeof i?i:a}(s,i)(e[s],t[s],i):r[s]=n(t[s],i))})),r}(e,i,c):n(i,c)}a.all=function(e,t){if(!Array.isArray(e))throw new Error("first argument should be an array");return e.reduce((function(e,i){return a(e,i,t)}),{})};var c=a;e.exports=c},221:function(e,t,i){function n(e){return Array.isArray?Array.isArray(e):"[object Array]"===l(e)}function r(e){return"string"==typeof e}function s(e){return"number"==typeof e}function o(e){return"object"==typeof e}function a(e){return null!=e}function c(e){return!e.trim().length}function l(e){return null==e?void 0===e?"[object Undefined]":"[object Null]":Object.prototype.toString.call(e)}i.r(t),i.d(t,{default:function(){return R}});const h=Object.prototype.hasOwnProperty;class u{constructor(e){this._keys=[],this._keyMap={};let t=0;e.forEach((e=>{let i=d(e);t+=i.weight,this._keys.push(i),this._keyMap[i.id]=i,t+=i.weight})),this._keys.forEach((e=>{e.weight/=t}))}get(e){return this._keyMap[e]}keys(){return this._keys}toJSON(){return JSON.stringify(this._keys)}}function d(e){let t=null,i=null,s=null,o=1,a=null;if(r(e)||n(e))s=e,t=p(e),i=f(e);else{if(!h.call(e,"name"))throw new Error("Missing name property in key");const n=e.name;if(s=n,h.call(e,"weight")&&(o=e.weight,o<=0))throw new Error((e=>`Property 'weight' in key '${e}' must be a positive integer`)(n));t=p(n),i=f(n),a=e.getFn}return{path:t,id:i,weight:o,src:s,getFn:a}}function p(e){return n(e)?e:e.split(".")}function f(e){return n(e)?e.join("."):e}var m={isCaseSensitive:!1,includeScore:!1,keys:[],shouldSort:!0,sortFn:(e,t)=>e.score===t.score?e.idx{if(a(e))if(t[u]){const d=e[t[u]];if(!a(d))return;if(u===t.length-1&&(r(d)||s(d)||function(e){return!0===e||!1===e||function(e){return o(e)&&null!==e}(e)&&"[object Boolean]"==l(e)}(d)))i.push(function(e){return null==e?"":function(e){if("string"==typeof e)return e;let t=e+"";return"0"==t&&1/e==-1/0?"-0":t}(e)}(d));else if(n(d)){c=!0;for(let e=0,i=d.length;e{this._keysMap[e.id]=t}))}create(){!this.isCreated&&this.docs.length&&(this.isCreated=!0,r(this.docs[0])?this.docs.forEach(((e,t)=>{this._addString(e,t)})):this.docs.forEach(((e,t)=>{this._addObject(e,t)})),this.norm.clear())}add(e){const t=this.size();r(e)?this._addString(e,t):this._addObject(e,t)}removeAt(e){this.records.splice(e,1);for(let t=e,i=this.size();t{let o=t.getFn?t.getFn(e):this.getFn(e,t.path);if(a(o))if(n(o)){let e=[];const t=[{nestedArrIndex:-1,value:o}];for(;t.length;){const{nestedArrIndex:i,value:s}=t.pop();if(a(s))if(r(s)&&!c(s)){let t={v:s,i:i,n:this.norm.get(s)};e.push(t)}else n(s)&&s.forEach(((e,i)=>{t.push({nestedArrIndex:i,value:e})}))}i.$[s]=e}else if(r(o)&&!c(o)){let e={v:o,n:this.norm.get(o)};i.$[s]=e}})),this.records.push(i)}toJSON(){return{keys:this.keys,records:this.records}}}function _(e,t,{getFn:i=m.getFn,fieldNormWeight:n=m.fieldNormWeight}={}){const r=new g({getFn:i,fieldNormWeight:n});return r.setKeys(e.map(d)),r.setSources(t),r.create(),r}function y(e,{errors:t=0,currentLocation:i=0,expectedLocation:n=0,distance:r=m.distance,ignoreLocation:s=m.ignoreLocation}={}){const o=t/e.length;if(s)return o;const a=Math.abs(n-i);return r?o+a/r:a?1:o}const E=32;function b(e){let t={};for(let i=0,n=e.length;i{this.chunks.push({pattern:e,alphabet:b(e),startIndex:t})},h=this.pattern.length;if(h>E){let e=0;const t=h%E,i=h-t;for(;e{const{isMatch:f,score:v,indices:g}=function(e,t,i,{location:n=m.location,distance:r=m.distance,threshold:s=m.threshold,findAllMatches:o=m.findAllMatches,minMatchCharLength:a=m.minMatchCharLength,includeMatches:c=m.includeMatches,ignoreLocation:l=m.ignoreLocation}={}){if(t.length>E)throw new Error("Pattern length exceeds max of 32.");const h=t.length,u=e.length,d=Math.max(0,Math.min(n,u));let p=s,f=d;const v=a>1||c,g=v?Array(u):[];let _;for(;(_=e.indexOf(t,f))>-1;){let e=y(t,{currentLocation:_,expectedLocation:d,distance:r,ignoreLocation:l});if(p=Math.min(e,p),f=_+h,v){let e=0;for(;e=c;s-=1){let o=s-1,a=i[e.charAt(o)];if(v&&(g[o]=+!!a),_[s]=(_[s+1]<<1|1)&a,n&&(_[s]|=(b[s+1]|b[s])<<1|1|b[s+1]),_[s]&I&&(S=y(t,{errors:n,currentLocation:o,expectedLocation:d,distance:r,ignoreLocation:l}),S<=p)){if(p=S,f=o,f<=d)break;c=Math.max(1,2*d-f)}}if(y(t,{errors:n+1,currentLocation:d,expectedLocation:d,distance:r,ignoreLocation:l})>p)break;b=_}const C={isMatch:f>=0,score:Math.max(.001,S)};if(v){const e=function(e=[],t=m.minMatchCharLength){let i=[],n=-1,r=-1,s=0;for(let o=e.length;s=t&&i.push([n,r]),n=-1)}return e[s-1]&&s-n>=t&&i.push([n,s-1]),i}(g,a);e.length?c&&(C.indices=e):C.isMatch=!1}return C}(e,t,d,{location:n+p,distance:r,threshold:s,findAllMatches:o,minMatchCharLength:a,includeMatches:i,ignoreLocation:c});f&&(u=!0),h+=v,f&&g&&(l=[...l,...g])}));let d={isMatch:u,score:u?h/this.chunks.length:1};return u&&i&&(d.indices=l),d}}class O{constructor(e){this.pattern=e}static isMultiMatch(e){return I(e,this.multiRegex)}static isSingleMatch(e){return I(e,this.singleRegex)}search(){}}function I(e,t){const i=e.match(t);return i?i[1]:null}class C extends O{constructor(e,{location:t=m.location,threshold:i=m.threshold,distance:n=m.distance,includeMatches:r=m.includeMatches,findAllMatches:s=m.findAllMatches,minMatchCharLength:o=m.minMatchCharLength,isCaseSensitive:a=m.isCaseSensitive,ignoreLocation:c=m.ignoreLocation}={}){super(e),this._bitapSearch=new S(e,{location:t,threshold:i,distance:n,includeMatches:r,findAllMatches:s,minMatchCharLength:o,isCaseSensitive:a,ignoreLocation:c})}static get type(){return"fuzzy"}static get multiRegex(){return/^"(.*)"$/}static get singleRegex(){return/^(.*)$/}search(e){return this._bitapSearch.searchIn(e)}}class T extends O{constructor(e){super(e)}static get type(){return"include"}static get multiRegex(){return/^'"(.*)"$/}static get singleRegex(){return/^'(.*)$/}search(e){let t,i=0;const n=[],r=this.pattern.length;for(;(t=e.indexOf(this.pattern,i))>-1;)i=t+r,n.push([t,i-1]);const s=!!n.length;return{isMatch:s,score:s?0:1,indices:n}}}const L=[class extends O{constructor(e){super(e)}static get type(){return"exact"}static get multiRegex(){return/^="(.*)"$/}static get singleRegex(){return/^=(.*)$/}search(e){const t=e===this.pattern;return{isMatch:t,score:t?0:1,indices:[0,this.pattern.length-1]}}},T,class extends O{constructor(e){super(e)}static get type(){return"prefix-exact"}static get multiRegex(){return/^\^"(.*)"$/}static get singleRegex(){return/^\^(.*)$/}search(e){const t=e.startsWith(this.pattern);return{isMatch:t,score:t?0:1,indices:[0,this.pattern.length-1]}}},class extends O{constructor(e){super(e)}static get type(){return"inverse-prefix-exact"}static get multiRegex(){return/^!\^"(.*)"$/}static get singleRegex(){return/^!\^(.*)$/}search(e){const t=!e.startsWith(this.pattern);return{isMatch:t,score:t?0:1,indices:[0,e.length-1]}}},class extends O{constructor(e){super(e)}static get type(){return"inverse-suffix-exact"}static get multiRegex(){return/^!"(.*)"\$$/}static get singleRegex(){return/^!(.*)\$$/}search(e){const t=!e.endsWith(this.pattern);return{isMatch:t,score:t?0:1,indices:[0,e.length-1]}}},class extends O{constructor(e){super(e)}static get type(){return"suffix-exact"}static get multiRegex(){return/^"(.*)"\$$/}static get singleRegex(){return/^(.*)\$$/}search(e){const t=e.endsWith(this.pattern);return{isMatch:t,score:t?0:1,indices:[e.length-this.pattern.length,e.length-1]}}},class extends O{constructor(e){super(e)}static get type(){return"inverse-exact"}static get multiRegex(){return/^!"(.*)"$/}static get singleRegex(){return/^!(.*)$/}search(e){const t=-1===e.indexOf(this.pattern);return{isMatch:t,score:t?0:1,indices:[0,e.length-1]}}},C],w=L.length,A=/ +(?=(?:[^\"]*\"[^\"]*\")*[^\"]*$)/,M=new Set([C.type,T.type]);const P=[];function x(e,t){for(let i=0,n=P.length;i!(!e.$and&&!e.$or),j=e=>({[N]:Object.keys(e).map((t=>({[t]:e[t]})))});function F(e,t,{auto:i=!0}={}){const s=e=>{let a=Object.keys(e);const c=(e=>!!e.$path)(e);if(!c&&a.length>1&&!D(e))return s(j(e));if((e=>!n(e)&&o(e)&&!D(e))(e)){const n=c?e.$path:a[0],s=c?e.$val:e[n];if(!r(s))throw new Error((e=>`Invalid value for key ${e}`)(n));const o={keyId:f(n),pattern:s};return i&&(o.searcher=x(s,t)),o}let l={children:[],operator:a[0]};return a.forEach((t=>{const i=e[t];n(i)&&i.forEach((e=>{l.children.push(s(e))}))})),l};return D(e)||(e=j(e)),s(e)}function k(e,t){const i=e.matches;t.matches=[],a(i)&&i.forEach((e=>{if(!a(e.indices)||!e.indices.length)return;const{indices:i,value:n}=e;let r={indices:i,value:n};e.key&&(r.key=e.key.src),e.idx>-1&&(r.refIndex=e.idx),t.matches.push(r)}))}function K(e,t){t.score=e.score}class R{constructor(e,t={},i){this.options={...m,...t},this.options.useExtendedSearch,this._keyStore=new u(this.options.keys),this.setCollection(e,i)}setCollection(e,t){if(this._docs=e,t&&!(t instanceof g))throw new Error("Incorrect 'index' type");this._myIndex=t||_(this.options.keys,this._docs,{getFn:this.options.getFn,fieldNormWeight:this.options.fieldNormWeight})}add(e){a(e)&&(this._docs.push(e),this._myIndex.add(e))}remove(e=(()=>!1)){const t=[];for(let i=0,n=this._docs.length;i{let i=1;e.matches.forEach((({key:e,norm:n,score:r})=>{const s=e?e.weight:null;i*=Math.pow(0===r&&s?Number.EPSILON:r,(s||1)*(t?1:n))})),e.score=i}))}(l,{ignoreFieldNorm:c}),o&&l.sort(a),s(t)&&t>-1&&(l=l.slice(0,t)),function(e,t,{includeMatches:i=m.includeMatches,includeScore:n=m.includeScore}={}){const r=[];return i&&r.push(k),n&&r.push(K),e.map((e=>{const{idx:i}=e,n={item:t[i],refIndex:i};return r.length&&r.forEach((t=>{t(e,n)})),n}))}(l,this._docs,{includeMatches:i,includeScore:n})}_searchStringList(e){const t=x(e,this.options),{records:i}=this._myIndex,n=[];return i.forEach((({v:e,i:i,n:r})=>{if(!a(e))return;const{isMatch:s,score:o,indices:c}=t.searchIn(e);s&&n.push({item:e,idx:i,matches:[{score:o,value:e,norm:r,indices:c}]})})),n}_searchLogical(e){const t=F(e,this.options),i=(e,t,n)=>{if(!e.children){const{keyId:i,searcher:r}=e,s=this._findMatches({key:this._keyStore.get(i),value:this._myIndex.getValueForItemAtKeyId(t,i),searcher:r});return s&&s.length?[{idx:n,item:t,matches:s}]:[]}const r=[];for(let s=0,o=e.children.length;s{if(a(e)){let o=i(t,e,n);o.length&&(r[n]||(r[n]={idx:n,item:e,matches:[]},s.push(r[n])),o.forEach((({matches:e})=>{r[n].matches.push(...e)})))}})),s}_searchObjectList(e){const t=x(e,this.options),{keys:i,records:n}=this._myIndex,r=[];return n.forEach((({$:e,i:n})=>{if(!a(e))return;let s=[];i.forEach(((i,n)=>{s.push(...this._findMatches({key:i,value:e[n],searcher:t}))})),s.length&&r.push({idx:n,item:e,matches:s})})),r}_findMatches({key:e,value:t,searcher:i}){if(!a(t))return[];let r=[];if(n(t))t.forEach((({v:t,i:n,n:s})=>{if(!a(t))return;const{isMatch:o,score:c,indices:l}=i.searchIn(t);o&&r.push({score:c,key:e,value:t,idx:n,norm:s,indices:l})}));else{const{v:n,n:s}=t,{isMatch:o,score:a,indices:c}=i.searchIn(n);o&&r.push({score:a,key:e,value:n,norm:s,indices:c})}return r}}R.version="6.6.2",R.createIndex=_,R.parseIndex=function(e,{getFn:t=m.getFn,fieldNormWeight:i=m.fieldNormWeight}={}){const{keys:n,records:r}=e,s=new g({getFn:t,fieldNormWeight:i});return s.setKeys(n),s.setIndexRecords(r),s},R.config=m,R.parseQuery=F,function(...e){P.push(...e)}(class{constructor(e,{isCaseSensitive:t=m.isCaseSensitive,includeMatches:i=m.includeMatches,minMatchCharLength:n=m.minMatchCharLength,ignoreLocation:r=m.ignoreLocation,findAllMatches:s=m.findAllMatches,location:o=m.location,threshold:a=m.threshold,distance:c=m.distance}={}){this.query=null,this.options={isCaseSensitive:t,includeMatches:i,minMatchCharLength:n,findAllMatches:s,ignoreLocation:r,location:o,threshold:a,distance:c},this.pattern=t?e:e.toLowerCase(),this.query=function(e,t={}){return e.split("|").map((e=>{let i=e.trim().split(A).filter((e=>e&&!!e.trim())),n=[];for(let e=0,r=i.length;e 12:00 (24-h format, midday) +* 12:00 am --> 00:00 (24-h format, midnight, start of day) +* +* Differs from momentjs parse rules: +* 00:00 pm, 12:00 pm --> 12:00 (24-h format, day not change) +* 00:00 am, 12:00 am --> 00:00 (24-h format, day not change) +* +* +* Author: Vitaliy Potapov +* Project page: http://github.com/vitalets/combodate +* Copyright (c) 2012 Vitaliy Potapov. Released under MIT License. +**/ +!function($){var a=function(a,b){if(this.$element=$(a),!this.$element.is("input")){$.error("Combodate should be applied to INPUT element");return}this.options=$.extend({},$.fn.combodate.defaults,b,this.$element.data()),this.init()};a.prototype={constructor:a,init:function(){this.map={day:["D","date"],month:["M","month"],year:["Y","year"],hour:["[Hh]","hours"],minute:["m","minutes"],second:["s","seconds"],ampm:["[Aa]",""]},this.$widget=$('').html(this.getTemplate()),this.initCombos(),this.$widget.on("change","select",$.proxy(function(a){this.$element.val(this.getValue()).change(),this.options.smartDays&&($(a.target).is(".month")||$(a.target).is(".year"))&&this.fillCombo("day")},this)),this.$widget.find("select").css("width","auto"),this.$element.hide().after(this.$widget),this.setValue(this.$element.val()||this.options.value)},getTemplate:function(){var a=this.options.template,b=this.options.customClass;return $.each(this.map,function(e,b){b=b[0];var c=new RegExp(b+"+"),d=b.length>1?b.substring(1,2):b;a=a.replace(c,"{"+d+"}")}),a=a.replace(/ /g," "),$.each(this.map,function(d,c){var e=(c=c[0]).length>1?c.substring(1,2):c;a=a.replace("{"+e+"}",'')}),a},initCombos:function(){for(var a in this.map){var b=this.$widget.find("."+a);this["$"+a]=b.length?b:null,this.fillCombo(a)}},fillCombo:function(c){var a=this["$"+c];if(a){var d=this["fill"+c.charAt(0).toUpperCase()+c.slice(1)](),e=a.val();a.empty();for(var b=0;b'+d[b][1]+"");a.val(e)}},fillCommon:function(a){var b,c=[];if("name"===this.options.firstItem){var d="function"==typeof(b=moment.relativeTime||moment.langData()._relativeTime)[a]?b[a](1,!0,a,!1):b[a];d=d.split(" ").reverse()[0],c.push(["",d])}else"empty"===this.options.firstItem&&c.push(["",""]);return c},fillDay:function(){var b,a,c=this.fillCommon("d"),g=-1!==this.options.template.indexOf("DD"),d=31;if(this.options.smartDays&&this.$month&&this.$year){var e=parseInt(this.$month.val(),10),f=parseInt(this.$year.val(),10);isNaN(e)||isNaN(f)||(d=moment([f,e]).daysInMonth())}for(a=1;a<=d;a++)b=g?this.leadZero(a):a,c.push([a,b]);return c},fillMonth:function(){var b,a,c=this.fillCommon("M"),d=-1!==this.options.template.indexOf("MMMM"),e=-1!==this.options.template.indexOf("MMM"),f=-1!==this.options.template.indexOf("MM");for(a=0;a<=11;a++)b=d?moment().date(1).month(a).format("MMMM"):e?moment().date(1).month(a).format("MMM"):f?this.leadZero(a+1):a+1,c.push([a,b]);return c},fillYear:function(){var b,a,c=[],d=-1!==this.options.template.indexOf("YYYY");for(a=this.options.maxYear;a>=this.options.minYear;a--)b=d?a:(a+"").substring(2),c[this.options.yearDescending?"push":"unshift"]([a,b]);return this.fillCommon("y").concat(c)},fillHour:function(){var b,a,c=this.fillCommon("h"),d=-1!==this.options.template.indexOf("h"),e=(this.options.template.indexOf("H"),-1!==this.options.template.toLowerCase().indexOf("hh")),f=d?12:23;for(a=d?1:0;a<=f;a++)b=e?this.leadZero(a):a,c.push([a,b]);return c},fillMinute:function(){var b,a,c=this.fillCommon("m"),d=-1!==this.options.template.indexOf("mm");for(a=0;a<=59;a+=this.options.minuteStep)b=d?this.leadZero(a):a,c.push([a,b]);return c},fillSecond:function(){var b,a,c=this.fillCommon("s"),d=-1!==this.options.template.indexOf("ss");for(a=0;a<=59;a+=this.options.secondStep)b=d?this.leadZero(a):a,c.push([a,b]);return c},fillAmpm:function(){var a=-1!==this.options.template.indexOf("a");return this.options.template.indexOf("A"),[["am",a?"am":"AM"],["pm",a?"pm":"PM"]]},getValue:function(c){var b,a={},e=this,d=!1;return($.each(this.map,function(b,c){if("ampm"!==b&&(a[b]=e["$"+b]?parseInt(e["$"+b].val(),10):"day"===b?1:0,isNaN(a[b])))return d=!0,!1}),d)?"":(this.$ampm&&(12===a.hour?a.hour="am"===this.$ampm.val()?0:12:a.hour="am"===this.$ampm.val()?a.hour:a.hour+12),b=moment([a.year,a.month,a.day,a.hour,a.minute,a.second]),this.highlight(b),null===(c=void 0===c?this.options.format:c))?b.isValid()?b:null:b.isValid()?b.format(c):""},setValue:function(b){if(b){var c="string"==typeof b?moment(b,this.options.format,!0):moment(b),d=this,a={};c.isValid()&&($.each(this.map,function(b,d){"ampm"!==b&&(a[b]=c[d[1]]())}),this.$ampm&&(a.hour>=12?(a.ampm="pm",a.hour>12&&(a.hour-=12)):(a.ampm="am",0===a.hour&&(a.hour=12))),$.each(a,function(a,b){d["$"+a]&&("minute"===a&&d.options.minuteStep>1&&d.options.roundTime&&(b=e(d["$"+a],b)),"second"===a&&d.options.secondStep>1&&d.options.roundTime&&(b=e(d["$"+a],b)),d["$"+a].val(b))}),this.options.smartDays&&this.fillCombo("day"),this.$element.val(c.format(this.options.format)).change())}function e(a,c){var b={};return a.children("option").each(function(f,e){var a,d=$(e).attr("value");""!==d&&(a=Math.abs(d-c),(void 0===b.distance||a",noCalendar:!1,now:new Date,onChange:[],onClose:[],onDayCreate:[],onDestroy:[],onKeyDown:[],onMonthChange:[],onOpen:[],onParseConfig:[],onReady:[],onValueUpdate:[],onYearChange:[],onPreCalendarPosition:[],plugins:[],position:"auto",positionElement:void 0,prevArrow:"",shorthandCurrentMonth:!1,showMonths:1,static:!1,time_24hr:!1,weekNumbers:!1,wrap:!1},i={weekdays:{shorthand:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],longhand:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"]},months:{shorthand:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],longhand:["January","February","March","April","May","June","July","August","September","October","November","December"]},daysInMonth:[31,28,31,30,31,30,31,31,30,31,30,31],firstDayOfWeek:0,ordinal:function(e){var n=e%100;if(n>3&&n<21)return"th";switch(n%10){case 1:return"st";case 2:return"nd";case 3:return"rd";default:return"th"}},rangeSeparator:" to ",weekAbbreviation:"Wk",scrollTitle:"Scroll to increment",toggleTitle:"Click to toggle",amPM:["AM","PM"],yearAriaLabel:"Year",monthAriaLabel:"Month",hourAriaLabel:"Hour",minuteAriaLabel:"Minute",time_24hr:!1},o=function(e,n){return void 0===n&&(n=2),("000"+e).slice(-1*n)},r=function(e){return!0===e?1:0};function l(e,n){var t;return function(){var a=this,i=arguments;clearTimeout(t),t=setTimeout((function(){return e.apply(a,i)}),n)}}var c=function(e){return e instanceof Array?e:[e]};function s(e,n,t){if(!0===t)return e.classList.add(n);e.classList.remove(n)}function d(e,n,t){var a=window.document.createElement(e);return n=n||"",t=t||"",a.className=n,void 0!==t&&(a.textContent=t),a}function u(e){for(;e.firstChild;)e.removeChild(e.firstChild)}function f(e,n){return n(e)?e:e.parentNode?f(e.parentNode,n):void 0}function m(e,n){var t=d("div","numInputWrapper"),a=d("input","numInput "+e),i=d("span","arrowUp"),o=d("span","arrowDown");if(-1===navigator.userAgent.indexOf("MSIE 9.0")?a.type="number":(a.type="text",a.pattern="\\d*"),void 0!==n)for(var r in n)a.setAttribute(r,n[r]);return t.appendChild(a),t.appendChild(i),t.appendChild(o),t}function g(e){try{return"function"==typeof e.composedPath?e.composedPath()[0]:e.target}catch(n){return e.target}}var p=function(){},h=function(e,n,t){return t.months[n?"shorthand":"longhand"][e]},v={D:p,F:function(e,n,t){e.setMonth(t.months.longhand.indexOf(n))},G:function(e,n){e.setHours((e.getHours()>=12?12:0)+parseFloat(n))},H:function(e,n){e.setHours(parseFloat(n))},J:function(e,n){e.setDate(parseFloat(n))},K:function(e,n,t){e.setHours(e.getHours()%12+12*r(new RegExp(t.amPM[1],"i").test(n)))},M:function(e,n,t){e.setMonth(t.months.shorthand.indexOf(n))},S:function(e,n){e.setSeconds(parseFloat(n))},U:function(e,n){return new Date(1e3*parseFloat(n))},W:function(e,n,t){var a=parseInt(n),i=new Date(e.getFullYear(),0,2+7*(a-1),0,0,0,0);return i.setDate(i.getDate()-i.getDay()+t.firstDayOfWeek),i},Y:function(e,n){e.setFullYear(parseFloat(n))},Z:function(e,n){return new Date(n)},d:function(e,n){e.setDate(parseFloat(n))},h:function(e,n){e.setHours((e.getHours()>=12?12:0)+parseFloat(n))},i:function(e,n){e.setMinutes(parseFloat(n))},j:function(e,n){e.setDate(parseFloat(n))},l:p,m:function(e,n){e.setMonth(parseFloat(n)-1)},n:function(e,n){e.setMonth(parseFloat(n)-1)},s:function(e,n){e.setSeconds(parseFloat(n))},u:function(e,n){return new Date(parseFloat(n))},w:p,y:function(e,n){e.setFullYear(2e3+parseFloat(n))}},D={D:"",F:"",G:"(\\d\\d|\\d)",H:"(\\d\\d|\\d)",J:"(\\d\\d|\\d)\\w+",K:"",M:"",S:"(\\d\\d|\\d)",U:"(.+)",W:"(\\d\\d|\\d)",Y:"(\\d{4})",Z:"(.+)",d:"(\\d\\d|\\d)",h:"(\\d\\d|\\d)",i:"(\\d\\d|\\d)",j:"(\\d\\d|\\d)",l:"",m:"(\\d\\d|\\d)",n:"(\\d\\d|\\d)",s:"(\\d\\d|\\d)",u:"(.+)",w:"(\\d\\d|\\d)",y:"(\\d{2})"},w={Z:function(e){return e.toISOString()},D:function(e,n,t){return n.weekdays.shorthand[w.w(e,n,t)]},F:function(e,n,t){return h(w.n(e,n,t)-1,!1,n)},G:function(e,n,t){return o(w.h(e,n,t))},H:function(e){return o(e.getHours())},J:function(e,n){return void 0!==n.ordinal?e.getDate()+n.ordinal(e.getDate()):e.getDate()},K:function(e,n){return n.amPM[r(e.getHours()>11)]},M:function(e,n){return h(e.getMonth(),!0,n)},S:function(e){return o(e.getSeconds())},U:function(e){return e.getTime()/1e3},W:function(e,n,t){return t.getWeek(e)},Y:function(e){return o(e.getFullYear(),4)},d:function(e){return o(e.getDate())},h:function(e){return e.getHours()%12?e.getHours()%12:12},i:function(e){return o(e.getMinutes())},j:function(e){return e.getDate()},l:function(e,n){return n.weekdays.longhand[e.getDay()]},m:function(e){return o(e.getMonth()+1)},n:function(e){return e.getMonth()+1},s:function(e){return e.getSeconds()},u:function(e){return e.getTime()},w:function(e){return e.getDay()},y:function(e){return String(e.getFullYear()).substring(2)}},b=function(e){var n=e.config,t=void 0===n?a:n,o=e.l10n,r=void 0===o?i:o,l=e.isMobile,c=void 0!==l&&l;return function(e,n,a){var i=a||r;return void 0===t.formatDate||c?n.split("").map((function(n,a,o){return w[n]&&"\\"!==o[a-1]?w[n](e,i,t):"\\"!==n?n:""})).join(""):t.formatDate(e,n,i)}},C=function(e){var n=e.config,t=void 0===n?a:n,o=e.l10n,r=void 0===o?i:o;return function(e,n,i,o){if(0===e||e){var l,c=o||r,s=e;if(e instanceof Date)l=new Date(e.getTime());else if("string"!=typeof e&&void 0!==e.toFixed)l=new Date(e);else if("string"==typeof e){var d=n||(t||a).dateFormat,u=String(e).trim();if("today"===u)l=new Date,i=!0;else if(t&&t.parseDate)l=t.parseDate(e,d);else if(/Z$/.test(u)||/GMT$/.test(u))l=new Date(e);else{for(var f=void 0,m=[],g=0,p=0,h="";g=0?new Date:new Date(w.config.minDate.getTime()),t=E(w.config);n.setHours(t.hours,t.minutes,t.seconds,n.getMilliseconds()),w.selectedDates=[n],w.latestSelectedDateObj=n}void 0!==e&&"blur"!==e.type&&function(e){e.preventDefault();var n="keydown"===e.type,t=g(e),a=t;void 0!==w.amPM&&t===w.amPM&&(w.amPM.textContent=w.l10n.amPM[r(w.amPM.textContent===w.l10n.amPM[0])]);var i=parseFloat(a.getAttribute("min")),l=parseFloat(a.getAttribute("max")),c=parseFloat(a.getAttribute("step")),s=parseInt(a.value,10),d=e.delta||(n?38===e.which?1:-1:0),u=s+c*d;if(void 0!==a.value&&2===a.value.length){var f=a===w.hourElement,m=a===w.minuteElement;ul&&(u=a===w.hourElement?u-l-r(!w.amPM):i,m&&L(void 0,1,w.hourElement)),w.amPM&&f&&(1===c?u+s===23:Math.abs(u-s)>c)&&(w.amPM.textContent=w.l10n.amPM[r(w.amPM.textContent===w.l10n.amPM[0])]),a.value=o(u)}}(e);var a=w._input.value;O(),ye(),w._input.value!==a&&w._debouncedChange()}function O(){if(void 0!==w.hourElement&&void 0!==w.minuteElement){var e,n,t=(parseInt(w.hourElement.value.slice(-2),10)||0)%24,a=(parseInt(w.minuteElement.value,10)||0)%60,i=void 0!==w.secondElement?(parseInt(w.secondElement.value,10)||0)%60:0;void 0!==w.amPM&&(e=t,n=w.amPM.textContent,t=e%12+12*r(n===w.l10n.amPM[1]));var o=void 0!==w.config.minTime||w.config.minDate&&w.minDateHasTime&&w.latestSelectedDateObj&&0===M(w.latestSelectedDateObj,w.config.minDate,!0),l=void 0!==w.config.maxTime||w.config.maxDate&&w.maxDateHasTime&&w.latestSelectedDateObj&&0===M(w.latestSelectedDateObj,w.config.maxDate,!0);if(void 0!==w.config.maxTime&&void 0!==w.config.minTime&&w.config.minTime>w.config.maxTime){var c=y(w.config.minTime.getHours(),w.config.minTime.getMinutes(),w.config.minTime.getSeconds()),s=y(w.config.maxTime.getHours(),w.config.maxTime.getMinutes(),w.config.maxTime.getSeconds()),d=y(t,a,i);if(d>s&&d=12)]),void 0!==w.secondElement&&(w.secondElement.value=o(t)))}function N(e){var n=g(e),t=parseInt(n.value)+(e.delta||0);(t/1e3>1||"Enter"===e.key&&!/[^\d]/.test(t.toString()))&&ee(t)}function P(e,n,t,a){return n instanceof Array?n.forEach((function(n){return P(e,n,t,a)})):e instanceof Array?e.forEach((function(e){return P(e,n,t,a)})):(e.addEventListener(n,t,a),void w._handlers.push({remove:function(){return e.removeEventListener(n,t,a)}}))}function Y(){De("onChange")}function j(e,n){var t=void 0!==e?w.parseDate(e):w.latestSelectedDateObj||(w.config.minDate&&w.config.minDate>w.now?w.config.minDate:w.config.maxDate&&w.config.maxDate=0&&M(e,w.selectedDates[1])<=0)}(n)&&!be(n)&&o.classList.add("inRange"),w.weekNumbers&&1===w.config.showMonths&&"prevMonthDay"!==e&&a%7==6&&w.weekNumbers.insertAdjacentHTML("beforeend",""+w.config.getWeek(n)+""),De("onDayCreate",o),o}function W(e){e.focus(),"range"===w.config.mode&&oe(e)}function B(e){for(var n=e>0?0:w.config.showMonths-1,t=e>0?w.config.showMonths:-1,a=n;a!=t;a+=e)for(var i=w.daysContainer.children[a],o=e>0?0:i.children.length-1,r=e>0?i.children.length:-1,l=o;l!=r;l+=e){var c=i.children[l];if(-1===c.className.indexOf("hidden")&&ne(c.dateObj))return c}}function J(e,n){var t=k(),a=te(t||document.body),i=void 0!==e?e:a?t:void 0!==w.selectedDateElem&&te(w.selectedDateElem)?w.selectedDateElem:void 0!==w.todayDateElem&&te(w.todayDateElem)?w.todayDateElem:B(n>0?1:-1);void 0===i?w._input.focus():a?function(e,n){for(var t=-1===e.className.indexOf("Month")?e.dateObj.getMonth():w.currentMonth,a=n>0?w.config.showMonths:-1,i=n>0?1:-1,o=t-w.currentMonth;o!=a;o+=i)for(var r=w.daysContainer.children[o],l=t-w.currentMonth===o?e.$i+n:n<0?r.children.length-1:0,c=r.children.length,s=l;s>=0&&s0?c:-1);s+=i){var d=r.children[s];if(-1===d.className.indexOf("hidden")&&ne(d.dateObj)&&Math.abs(e.$i-s)>=Math.abs(n))return W(d)}w.changeMonth(i),J(B(i),0)}(i,n):W(i)}function K(e,n){for(var t=(new Date(e,n,1).getDay()-w.l10n.firstDayOfWeek+7)%7,a=w.utils.getDaysInMonth((n-1+12)%12,e),i=w.utils.getDaysInMonth(n,e),o=window.document.createDocumentFragment(),r=w.config.showMonths>1,l=r?"prevMonthDay hidden":"prevMonthDay",c=r?"nextMonthDay hidden":"nextMonthDay",s=a+1-t,u=0;s<=a;s++,u++)o.appendChild(R("flatpickr-day "+l,new Date(e,n-1,s),0,u));for(s=1;s<=i;s++,u++)o.appendChild(R("flatpickr-day",new Date(e,n,s),0,u));for(var f=i+1;f<=42-t&&(1===w.config.showMonths||u%7!=0);f++,u++)o.appendChild(R("flatpickr-day "+c,new Date(e,n+1,f%i),0,u));var m=d("div","dayContainer");return m.appendChild(o),m}function U(){if(void 0!==w.daysContainer){u(w.daysContainer),w.weekNumbers&&u(w.weekNumbers);for(var e=document.createDocumentFragment(),n=0;n1||"dropdown"!==w.config.monthSelectorType)){var e=function(e){return!(void 0!==w.config.minDate&&w.currentYear===w.config.minDate.getFullYear()&&ew.config.maxDate.getMonth())};w.monthsDropdownContainer.tabIndex=-1,w.monthsDropdownContainer.innerHTML="";for(var n=0;n<12;n++)if(e(n)){var t=d("option","flatpickr-monthDropdown-month");t.value=new Date(w.currentYear,n).getMonth().toString(),t.textContent=h(n,w.config.shorthandCurrentMonth,w.l10n),t.tabIndex=-1,w.currentMonth===n&&(t.selected=!0),w.monthsDropdownContainer.appendChild(t)}}}function $(){var e,n=d("div","flatpickr-month"),t=window.document.createDocumentFragment();w.config.showMonths>1||"static"===w.config.monthSelectorType?e=d("span","cur-month"):(w.monthsDropdownContainer=d("select","flatpickr-monthDropdown-months"),w.monthsDropdownContainer.setAttribute("aria-label",w.l10n.monthAriaLabel),P(w.monthsDropdownContainer,"change",(function(e){var n=g(e),t=parseInt(n.value,10);w.changeMonth(t-w.currentMonth),De("onMonthChange")})),q(),e=w.monthsDropdownContainer);var a=m("cur-year",{tabindex:"-1"}),i=a.getElementsByTagName("input")[0];i.setAttribute("aria-label",w.l10n.yearAriaLabel),w.config.minDate&&i.setAttribute("min",w.config.minDate.getFullYear().toString()),w.config.maxDate&&(i.setAttribute("max",w.config.maxDate.getFullYear().toString()),i.disabled=!!w.config.minDate&&w.config.minDate.getFullYear()===w.config.maxDate.getFullYear());var o=d("div","flatpickr-current-month");return o.appendChild(e),o.appendChild(a),t.appendChild(o),n.appendChild(t),{container:n,yearElement:i,monthElement:e}}function V(){u(w.monthNav),w.monthNav.appendChild(w.prevMonthNav),w.config.showMonths&&(w.yearElements=[],w.monthElements=[]);for(var e=w.config.showMonths;e--;){var n=$();w.yearElements.push(n.yearElement),w.monthElements.push(n.monthElement),w.monthNav.appendChild(n.container)}w.monthNav.appendChild(w.nextMonthNav)}function z(){w.weekdayContainer?u(w.weekdayContainer):w.weekdayContainer=d("div","flatpickr-weekdays");for(var e=w.config.showMonths;e--;){var n=d("div","flatpickr-weekdaycontainer");w.weekdayContainer.appendChild(n)}return G(),w.weekdayContainer}function G(){if(w.weekdayContainer){var e=w.l10n.firstDayOfWeek,t=n(w.l10n.weekdays.shorthand);e>0&&e\n "+t.join("")+"\n \n "}}function Z(e,n){void 0===n&&(n=!0);var t=n?e:e-w.currentMonth;t<0&&!0===w._hidePrevMonthArrow||t>0&&!0===w._hideNextMonthArrow||(w.currentMonth+=t,(w.currentMonth<0||w.currentMonth>11)&&(w.currentYear+=w.currentMonth>11?1:-1,w.currentMonth=(w.currentMonth+12)%12,De("onYearChange"),q()),U(),De("onMonthChange"),Ce())}function Q(e){return w.calendarContainer.contains(e)}function X(e){if(w.isOpen&&!w.config.inline){var n=g(e),t=Q(n),a=!(n===w.input||n===w.altInput||w.element.contains(n)||e.path&&e.path.indexOf&&(~e.path.indexOf(w.input)||~e.path.indexOf(w.altInput)))&&!t&&!Q(e.relatedTarget),i=!w.config.ignoredFocusElements.some((function(e){return e.contains(n)}));a&&i&&(w.config.allowInput&&w.setDate(w._input.value,!1,w.config.altInput?w.config.altFormat:w.config.dateFormat),void 0!==w.timeContainer&&void 0!==w.minuteElement&&void 0!==w.hourElement&&""!==w.input.value&&void 0!==w.input.value&&_(),w.close(),w.config&&"range"===w.config.mode&&1===w.selectedDates.length&&w.clear(!1))}}function ee(e){if(!(!e||w.config.minDate&&ew.config.maxDate.getFullYear())){var n=e,t=w.currentYear!==n;w.currentYear=n||w.currentYear,w.config.maxDate&&w.currentYear===w.config.maxDate.getFullYear()?w.currentMonth=Math.min(w.config.maxDate.getMonth(),w.currentMonth):w.config.minDate&&w.currentYear===w.config.minDate.getFullYear()&&(w.currentMonth=Math.max(w.config.minDate.getMonth(),w.currentMonth)),t&&(w.redraw(),De("onYearChange"),q())}}function ne(e,n){var t;void 0===n&&(n=!0);var a=w.parseDate(e,void 0,n);if(w.config.minDate&&a&&M(a,w.config.minDate,void 0!==n?n:!w.minDateHasTime)<0||w.config.maxDate&&a&&M(a,w.config.maxDate,void 0!==n?n:!w.maxDateHasTime)>0)return!1;if(!w.config.enable&&0===w.config.disable.length)return!0;if(void 0===a)return!1;for(var i=!!w.config.enable,o=null!==(t=w.config.enable)&&void 0!==t?t:w.config.disable,r=0,l=void 0;r=l.from.getTime()&&a.getTime()<=l.to.getTime())return i}return!i}function te(e){return void 0!==w.daysContainer&&(-1===e.className.indexOf("hidden")&&-1===e.className.indexOf("flatpickr-disabled")&&w.daysContainer.contains(e))}function ae(e){var n=e.target===w._input,t=w._input.value.trimEnd()!==Me();!n||!t||e.relatedTarget&&Q(e.relatedTarget)||w.setDate(w._input.value,!0,e.target===w.altInput?w.config.altFormat:w.config.dateFormat)}function ie(e){var n=g(e),t=w.config.wrap?p.contains(n):n===w._input,a=w.config.allowInput,i=w.isOpen&&(!a||!t),o=w.config.inline&&t&&!a;if(13===e.keyCode&&t){if(a)return w.setDate(w._input.value,!0,n===w.altInput?w.config.altFormat:w.config.dateFormat),w.close(),n.blur();w.open()}else if(Q(n)||i||o){var r=!!w.timeContainer&&w.timeContainer.contains(n);switch(e.keyCode){case 13:r?(e.preventDefault(),_(),fe()):me(e);break;case 27:e.preventDefault(),fe();break;case 8:case 46:t&&!w.config.allowInput&&(e.preventDefault(),w.clear());break;case 37:case 39:if(r||t)w.hourElement&&w.hourElement.focus();else{e.preventDefault();var l=k();if(void 0!==w.daysContainer&&(!1===a||l&&te(l))){var c=39===e.keyCode?1:-1;e.ctrlKey?(e.stopPropagation(),Z(c),J(B(1),0)):J(void 0,c)}}break;case 38:case 40:e.preventDefault();var s=40===e.keyCode?1:-1;w.daysContainer&&void 0!==n.$i||n===w.input||n===w.altInput?e.ctrlKey?(e.stopPropagation(),ee(w.currentYear-s),J(B(1),0)):r||J(void 0,7*s):n===w.currentYearElement?ee(w.currentYear-s):w.config.enableTime&&(!r&&w.hourElement&&w.hourElement.focus(),_(e),w._debouncedChange());break;case 9:if(r){var d=[w.hourElement,w.minuteElement,w.secondElement,w.amPM].concat(w.pluginElements).filter((function(e){return e})),u=d.indexOf(n);if(-1!==u){var f=d[u+(e.shiftKey?-1:1)];e.preventDefault(),(f||w._input).focus()}}else!w.config.noCalendar&&w.daysContainer&&w.daysContainer.contains(n)&&e.shiftKey&&(e.preventDefault(),w._input.focus())}}if(void 0!==w.amPM&&n===w.amPM)switch(e.key){case w.l10n.amPM[0].charAt(0):case w.l10n.amPM[0].charAt(0).toLowerCase():w.amPM.textContent=w.l10n.amPM[0],O(),ye();break;case w.l10n.amPM[1].charAt(0):case w.l10n.amPM[1].charAt(0).toLowerCase():w.amPM.textContent=w.l10n.amPM[1],O(),ye()}(t||Q(n))&&De("onKeyDown",e)}function oe(e,n){if(void 0===n&&(n="flatpickr-day"),1===w.selectedDates.length&&(!e||e.classList.contains(n)&&!e.classList.contains("flatpickr-disabled"))){for(var t=e?e.dateObj.getTime():w.days.firstElementChild.dateObj.getTime(),a=w.parseDate(w.selectedDates[0],void 0,!0).getTime(),i=Math.min(t,w.selectedDates[0].getTime()),o=Math.max(t,w.selectedDates[0].getTime()),r=!1,l=0,c=0,s=i;si&&sl)?l=s:s>a&&(!c||s ."+n)).forEach((function(n){var i,o,s,d=n.dateObj.getTime(),u=l>0&&d0&&d>c;if(u)return n.classList.add("notAllowed"),void["inRange","startRange","endRange"].forEach((function(e){n.classList.remove(e)}));r&&!u||(["startRange","inRange","endRange","notAllowed"].forEach((function(e){n.classList.remove(e)})),void 0!==e&&(e.classList.add(t<=w.selectedDates[0].getTime()?"startRange":"endRange"),at&&d===a&&n.classList.add("endRange"),d>=l&&(0===c||d<=c)&&(o=a,s=t,(i=d)>Math.min(o,s)&&i0||t.getMinutes()>0||t.getSeconds()>0),w.selectedDates&&(w.selectedDates=w.selectedDates.filter((function(e){return ne(e)})),w.selectedDates.length||"min"!==e||F(t),ye()),w.daysContainer&&(ue(),void 0!==t?w.currentYearElement[e]=t.getFullYear().toString():w.currentYearElement.removeAttribute(e),w.currentYearElement.disabled=!!a&&void 0!==t&&a.getFullYear()===t.getFullYear())}}function ce(){return w.config.wrap?p.querySelector("[data-input]"):p}function se(){"object"!=typeof w.config.locale&&void 0===I.l10ns[w.config.locale]&&w.config.errorHandler(new Error("flatpickr: invalid locale "+w.config.locale)),w.l10n=e(e({},I.l10ns.default),"object"==typeof w.config.locale?w.config.locale:"default"!==w.config.locale?I.l10ns[w.config.locale]:void 0),D.D="("+w.l10n.weekdays.shorthand.join("|")+")",D.l="("+w.l10n.weekdays.longhand.join("|")+")",D.M="("+w.l10n.months.shorthand.join("|")+")",D.F="("+w.l10n.months.longhand.join("|")+")",D.K="("+w.l10n.amPM[0]+"|"+w.l10n.amPM[1]+"|"+w.l10n.amPM[0].toLowerCase()+"|"+w.l10n.amPM[1].toLowerCase()+")",void 0===e(e({},v),JSON.parse(JSON.stringify(p.dataset||{}))).time_24hr&&void 0===I.defaultConfig.time_24hr&&(w.config.time_24hr=w.l10n.time_24hr),w.formatDate=b(w),w.parseDate=C({config:w.config,l10n:w.l10n})}function de(e){if("function"!=typeof w.config.position){if(void 0!==w.calendarContainer){De("onPreCalendarPosition");var n=e||w._positionElement,t=Array.prototype.reduce.call(w.calendarContainer.children,(function(e,n){return e+n.offsetHeight}),0),a=w.calendarContainer.offsetWidth,i=w.config.position.split(" "),o=i[0],r=i.length>1?i[1]:null,l=n.getBoundingClientRect(),c=window.innerHeight-l.bottom,d="above"===o||"below"!==o&&ct,u=window.pageYOffset+l.top+(d?-t-2:n.offsetHeight+2);if(s(w.calendarContainer,"arrowTop",!d),s(w.calendarContainer,"arrowBottom",d),!w.config.inline){var f=window.pageXOffset+l.left,m=!1,g=!1;"center"===r?(f-=(a-l.width)/2,m=!0):"right"===r&&(f-=a-l.width,g=!0),s(w.calendarContainer,"arrowLeft",!m&&!g),s(w.calendarContainer,"arrowCenter",m),s(w.calendarContainer,"arrowRight",g);var p=window.document.body.offsetWidth-(window.pageXOffset+l.right),h=f+a>window.document.body.offsetWidth,v=p+a>window.document.body.offsetWidth;if(s(w.calendarContainer,"rightMost",h),!w.config.static)if(w.calendarContainer.style.top=u+"px",h)if(v){var D=function(){for(var e=null,n=0;nw.currentMonth+w.config.showMonths-1)&&"range"!==w.config.mode;if(w.selectedDateElem=t,"single"===w.config.mode)w.selectedDates=[a];else if("multiple"===w.config.mode){var o=be(a);o?w.selectedDates.splice(parseInt(o),1):w.selectedDates.push(a)}else"range"===w.config.mode&&(2===w.selectedDates.length&&w.clear(!1,!1),w.latestSelectedDateObj=a,w.selectedDates.push(a),0!==M(a,w.selectedDates[0],!0)&&w.selectedDates.sort((function(e,n){return e.getTime()-n.getTime()})));if(O(),i){var r=w.currentYear!==a.getFullYear();w.currentYear=a.getFullYear(),w.currentMonth=a.getMonth(),r&&(De("onYearChange"),q()),De("onMonthChange")}if(Ce(),U(),ye(),i||"range"===w.config.mode||1!==w.config.showMonths?void 0!==w.selectedDateElem&&void 0===w.hourElement&&w.selectedDateElem&&w.selectedDateElem.focus():W(t),void 0!==w.hourElement&&void 0!==w.hourElement&&w.hourElement.focus(),w.config.closeOnSelect){var l="single"===w.config.mode&&!w.config.enableTime,c="range"===w.config.mode&&2===w.selectedDates.length&&!w.config.enableTime;(l||c)&&fe()}Y()}}w.parseDate=C({config:w.config,l10n:w.l10n}),w._handlers=[],w.pluginElements=[],w.loadedPlugins=[],w._bind=P,w._setHoursFromDate=F,w._positionCalendar=de,w.changeMonth=Z,w.changeYear=ee,w.clear=function(e,n){void 0===e&&(e=!0);void 0===n&&(n=!0);w.input.value="",void 0!==w.altInput&&(w.altInput.value="");void 0!==w.mobileInput&&(w.mobileInput.value="");w.selectedDates=[],w.latestSelectedDateObj=void 0,!0===n&&(w.currentYear=w._initialDate.getFullYear(),w.currentMonth=w._initialDate.getMonth());if(!0===w.config.enableTime){var t=E(w.config),a=t.hours,i=t.minutes,o=t.seconds;A(a,i,o)}w.redraw(),e&&De("onChange")},w.close=function(){w.isOpen=!1,w.isMobile||(void 0!==w.calendarContainer&&w.calendarContainer.classList.remove("open"),void 0!==w._input&&w._input.classList.remove("active"));De("onClose")},w.onMouseOver=oe,w._createElement=d,w.createDay=R,w.destroy=function(){void 0!==w.config&&De("onDestroy");for(var e=w._handlers.length;e--;)w._handlers[e].remove();if(w._handlers=[],w.mobileInput)w.mobileInput.parentNode&&w.mobileInput.parentNode.removeChild(w.mobileInput),w.mobileInput=void 0;else if(w.calendarContainer&&w.calendarContainer.parentNode)if(w.config.static&&w.calendarContainer.parentNode){var n=w.calendarContainer.parentNode;if(n.lastChild&&n.removeChild(n.lastChild),n.parentNode){for(;n.firstChild;)n.parentNode.insertBefore(n.firstChild,n);n.parentNode.removeChild(n)}}else w.calendarContainer.parentNode.removeChild(w.calendarContainer);w.altInput&&(w.input.type="text",w.altInput.parentNode&&w.altInput.parentNode.removeChild(w.altInput),delete w.altInput);w.input&&(w.input.type=w.input._type,w.input.classList.remove("flatpickr-input"),w.input.removeAttribute("readonly"));["_showTimeInput","latestSelectedDateObj","_hideNextMonthArrow","_hidePrevMonthArrow","__hideNextMonthArrow","__hidePrevMonthArrow","isMobile","isOpen","selectedDateElem","minDateHasTime","maxDateHasTime","days","daysContainer","_input","_positionElement","innerContainer","rContainer","monthNav","todayDateElem","calendarContainer","weekdayContainer","prevMonthNav","nextMonthNav","monthsDropdownContainer","currentMonthElement","currentYearElement","navigationCurrentMonth","selectedDateElem","config"].forEach((function(e){try{delete w[e]}catch(e){}}))},w.isEnabled=ne,w.jumpToDate=j,w.updateValue=ye,w.open=function(e,n){void 0===n&&(n=w._positionElement);if(!0===w.isMobile){if(e){e.preventDefault();var t=g(e);t&&t.blur()}return void 0!==w.mobileInput&&(w.mobileInput.focus(),w.mobileInput.click()),void De("onOpen")}if(w._input.disabled||w.config.inline)return;var a=w.isOpen;w.isOpen=!0,a||(w.calendarContainer.classList.add("open"),w._input.classList.add("active"),De("onOpen"),de(n));!0===w.config.enableTime&&!0===w.config.noCalendar&&(!1!==w.config.allowInput||void 0!==e&&w.timeContainer.contains(e.relatedTarget)||setTimeout((function(){return w.hourElement.select()}),50))},w.redraw=ue,w.set=function(e,n){if(null!==e&&"object"==typeof e)for(var a in Object.assign(w.config,e),e)void 0!==ge[a]&&ge[a].forEach((function(e){return e()}));else w.config[e]=n,void 0!==ge[e]?ge[e].forEach((function(e){return e()})):t.indexOf(e)>-1&&(w.config[e]=c(n));w.redraw(),ye(!0)},w.setDate=function(e,n,t){void 0===n&&(n=!1);void 0===t&&(t=w.config.dateFormat);if(0!==e&&!e||e instanceof Array&&0===e.length)return w.clear(n);pe(e,t),w.latestSelectedDateObj=w.selectedDates[w.selectedDates.length-1],w.redraw(),j(void 0,n),F(),0===w.selectedDates.length&&w.clear(!1);ye(n),n&&De("onChange")},w.toggle=function(e){if(!0===w.isOpen)return w.close();w.open(e)};var ge={locale:[se,G],showMonths:[V,S,z],minDate:[j],maxDate:[j],positionElement:[ve],clickOpens:[function(){!0===w.config.clickOpens?(P(w._input,"focus",w.open),P(w._input,"click",w.open)):(w._input.removeEventListener("focus",w.open),w._input.removeEventListener("click",w.open))}]};function pe(e,n){var t=[];if(e instanceof Array)t=e.map((function(e){return w.parseDate(e,n)}));else if(e instanceof Date||"number"==typeof e)t=[w.parseDate(e,n)];else if("string"==typeof e)switch(w.config.mode){case"single":case"time":t=[w.parseDate(e,n)];break;case"multiple":t=e.split(w.config.conjunction).map((function(e){return w.parseDate(e,n)}));break;case"range":t=e.split(w.l10n.rangeSeparator).map((function(e){return w.parseDate(e,n)}))}else w.config.errorHandler(new Error("Invalid date supplied: "+JSON.stringify(e)));w.selectedDates=w.config.allowInvalidPreload?t:t.filter((function(e){return e instanceof Date&&ne(e,!1)})),"range"===w.config.mode&&w.selectedDates.sort((function(e,n){return e.getTime()-n.getTime()}))}function he(e){return e.slice().map((function(e){return"string"==typeof e||"number"==typeof e||e instanceof Date?w.parseDate(e,void 0,!0):e&&"object"==typeof e&&e.from&&e.to?{from:w.parseDate(e.from,void 0),to:w.parseDate(e.to,void 0)}:e})).filter((function(e){return e}))}function ve(){w._positionElement=w.config.positionElement||w._input}function De(e,n){if(void 0!==w.config){var t=w.config[e];if(void 0!==t&&t.length>0)for(var a=0;t[a]&&a1||"static"===w.config.monthSelectorType?w.monthElements[n].textContent=h(t.getMonth(),w.config.shorthandCurrentMonth,w.l10n)+" ":w.monthsDropdownContainer.value=t.getMonth().toString(),e.value=t.getFullYear().toString()})),w._hidePrevMonthArrow=void 0!==w.config.minDate&&(w.currentYear===w.config.minDate.getFullYear()?w.currentMonth<=w.config.minDate.getMonth():w.currentYearw.config.maxDate.getMonth():w.currentYear>w.config.maxDate.getFullYear()))}function Me(e){var n=e||(w.config.altInput?w.config.altFormat:w.config.dateFormat);return w.selectedDates.map((function(e){return w.formatDate(e,n)})).filter((function(e,n,t){return"range"!==w.config.mode||w.config.enableTime||t.indexOf(e)===n})).join("range"!==w.config.mode?w.config.conjunction:w.l10n.rangeSeparator)}function ye(e){void 0===e&&(e=!0),void 0!==w.mobileInput&&w.mobileFormatStr&&(w.mobileInput.value=void 0!==w.latestSelectedDateObj?w.formatDate(w.latestSelectedDateObj,w.mobileFormatStr):""),w.input.value=Me(w.config.dateFormat),void 0!==w.altInput&&(w.altInput.value=Me(w.config.altFormat)),!1!==e&&De("onValueUpdate")}function xe(e){var n=g(e),t=w.prevMonthNav.contains(n),a=w.nextMonthNav.contains(n);t||a?Z(t?-1:1):w.yearElements.indexOf(n)>=0?n.select():n.classList.contains("arrowUp")?w.changeYear(w.currentYear+1):n.classList.contains("arrowDown")&&w.changeYear(w.currentYear-1)}return function(){w.element=w.input=p,w.isOpen=!1,function(){var n=["wrap","weekNumbers","allowInput","allowInvalidPreload","clickOpens","time_24hr","enableTime","noCalendar","altInput","shorthandCurrentMonth","inline","static","enableSeconds","disableMobile"],i=e(e({},JSON.parse(JSON.stringify(p.dataset||{}))),v),o={};w.config.parseDate=i.parseDate,w.config.formatDate=i.formatDate,Object.defineProperty(w.config,"enable",{get:function(){return w.config._enable},set:function(e){w.config._enable=he(e)}}),Object.defineProperty(w.config,"disable",{get:function(){return w.config._disable},set:function(e){w.config._disable=he(e)}});var r="time"===i.mode;if(!i.dateFormat&&(i.enableTime||r)){var l=I.defaultConfig.dateFormat||a.dateFormat;o.dateFormat=i.noCalendar||r?"H:i"+(i.enableSeconds?":S":""):l+" H:i"+(i.enableSeconds?":S":"")}if(i.altInput&&(i.enableTime||r)&&!i.altFormat){var s=I.defaultConfig.altFormat||a.altFormat;o.altFormat=i.noCalendar||r?"h:i"+(i.enableSeconds?":S K":" K"):s+" h:i"+(i.enableSeconds?":S":"")+" K"}Object.defineProperty(w.config,"minDate",{get:function(){return w.config._minDate},set:le("min")}),Object.defineProperty(w.config,"maxDate",{get:function(){return w.config._maxDate},set:le("max")});var d=function(e){return function(n){w.config["min"===e?"_minTime":"_maxTime"]=w.parseDate(n,"H:i:S")}};Object.defineProperty(w.config,"minTime",{get:function(){return w.config._minTime},set:d("min")}),Object.defineProperty(w.config,"maxTime",{get:function(){return w.config._maxTime},set:d("max")}),"time"===i.mode&&(w.config.noCalendar=!0,w.config.enableTime=!0);Object.assign(w.config,o,i);for(var u=0;u-1?w.config[m]=c(f[m]).map(T).concat(w.config[m]):void 0===i[m]&&(w.config[m]=f[m])}i.altInputClass||(w.config.altInputClass=ce().className+" "+w.config.altInputClass);De("onParseConfig")}(),se(),function(){if(w.input=ce(),!w.input)return void w.config.errorHandler(new Error("Invalid input element specified"));w.input._type=w.input.type,w.input.type="text",w.input.classList.add("flatpickr-input"),w._input=w.input,w.config.altInput&&(w.altInput=d(w.input.nodeName,w.config.altInputClass),w._input=w.altInput,w.altInput.placeholder=w.input.placeholder,w.altInput.disabled=w.input.disabled,w.altInput.required=w.input.required,w.altInput.tabIndex=w.input.tabIndex,w.altInput.type="text",w.input.setAttribute("type","hidden"),!w.config.static&&w.input.parentNode&&w.input.parentNode.insertBefore(w.altInput,w.input.nextSibling));w.config.allowInput||w._input.setAttribute("readonly","readonly");ve()}(),function(){w.selectedDates=[],w.now=w.parseDate(w.config.now)||new Date;var e=w.config.defaultDate||("INPUT"!==w.input.nodeName&&"TEXTAREA"!==w.input.nodeName||!w.input.placeholder||w.input.value!==w.input.placeholder?w.input.value:null);e&&pe(e,w.config.dateFormat);w._initialDate=w.selectedDates.length>0?w.selectedDates[0]:w.config.minDate&&w.config.minDate.getTime()>w.now.getTime()?w.config.minDate:w.config.maxDate&&w.config.maxDate.getTime()0&&(w.latestSelectedDateObj=w.selectedDates[0]);void 0!==w.config.minTime&&(w.config.minTime=w.parseDate(w.config.minTime,"H:i"));void 0!==w.config.maxTime&&(w.config.maxTime=w.parseDate(w.config.maxTime,"H:i"));w.minDateHasTime=!!w.config.minDate&&(w.config.minDate.getHours()>0||w.config.minDate.getMinutes()>0||w.config.minDate.getSeconds()>0),w.maxDateHasTime=!!w.config.maxDate&&(w.config.maxDate.getHours()>0||w.config.maxDate.getMinutes()>0||w.config.maxDate.getSeconds()>0)}(),w.utils={getDaysInMonth:function(e,n){return void 0===e&&(e=w.currentMonth),void 0===n&&(n=w.currentYear),1===e&&(n%4==0&&n%100!=0||n%400==0)?29:w.l10n.daysInMonth[e]}},w.isMobile||function(){var e=window.document.createDocumentFragment();if(w.calendarContainer=d("div","flatpickr-calendar"),w.calendarContainer.tabIndex=-1,!w.config.noCalendar){if(e.appendChild((w.monthNav=d("div","flatpickr-months"),w.yearElements=[],w.monthElements=[],w.prevMonthNav=d("span","flatpickr-prev-month"),w.prevMonthNav.innerHTML=w.config.prevArrow,w.nextMonthNav=d("span","flatpickr-next-month"),w.nextMonthNav.innerHTML=w.config.nextArrow,V(),Object.defineProperty(w,"_hidePrevMonthArrow",{get:function(){return w.__hidePrevMonthArrow},set:function(e){w.__hidePrevMonthArrow!==e&&(s(w.prevMonthNav,"flatpickr-disabled",e),w.__hidePrevMonthArrow=e)}}),Object.defineProperty(w,"_hideNextMonthArrow",{get:function(){return w.__hideNextMonthArrow},set:function(e){w.__hideNextMonthArrow!==e&&(s(w.nextMonthNav,"flatpickr-disabled",e),w.__hideNextMonthArrow=e)}}),w.currentYearElement=w.yearElements[0],Ce(),w.monthNav)),w.innerContainer=d("div","flatpickr-innerContainer"),w.config.weekNumbers){var n=function(){w.calendarContainer.classList.add("hasWeeks");var e=d("div","flatpickr-weekwrapper");e.appendChild(d("span","flatpickr-weekday",w.l10n.weekAbbreviation));var n=d("div","flatpickr-weeks");return e.appendChild(n),{weekWrapper:e,weekNumbers:n}}(),t=n.weekWrapper,a=n.weekNumbers;w.innerContainer.appendChild(t),w.weekNumbers=a,w.weekWrapper=t}w.rContainer=d("div","flatpickr-rContainer"),w.rContainer.appendChild(z()),w.daysContainer||(w.daysContainer=d("div","flatpickr-days"),w.daysContainer.tabIndex=-1),U(),w.rContainer.appendChild(w.daysContainer),w.innerContainer.appendChild(w.rContainer),e.appendChild(w.innerContainer)}w.config.enableTime&&e.appendChild(function(){w.calendarContainer.classList.add("hasTime"),w.config.noCalendar&&w.calendarContainer.classList.add("noCalendar");var e=E(w.config);w.timeContainer=d("div","flatpickr-time"),w.timeContainer.tabIndex=-1;var n=d("span","flatpickr-time-separator",":"),t=m("flatpickr-hour",{"aria-label":w.l10n.hourAriaLabel});w.hourElement=t.getElementsByTagName("input")[0];var a=m("flatpickr-minute",{"aria-label":w.l10n.minuteAriaLabel});w.minuteElement=a.getElementsByTagName("input")[0],w.hourElement.tabIndex=w.minuteElement.tabIndex=-1,w.hourElement.value=o(w.latestSelectedDateObj?w.latestSelectedDateObj.getHours():w.config.time_24hr?e.hours:function(e){switch(e%24){case 0:case 12:return 12;default:return e%12}}(e.hours)),w.minuteElement.value=o(w.latestSelectedDateObj?w.latestSelectedDateObj.getMinutes():e.minutes),w.hourElement.setAttribute("step",w.config.hourIncrement.toString()),w.minuteElement.setAttribute("step",w.config.minuteIncrement.toString()),w.hourElement.setAttribute("min",w.config.time_24hr?"0":"1"),w.hourElement.setAttribute("max",w.config.time_24hr?"23":"12"),w.hourElement.setAttribute("maxlength","2"),w.minuteElement.setAttribute("min","0"),w.minuteElement.setAttribute("max","59"),w.minuteElement.setAttribute("maxlength","2"),w.timeContainer.appendChild(t),w.timeContainer.appendChild(n),w.timeContainer.appendChild(a),w.config.time_24hr&&w.timeContainer.classList.add("time24hr");if(w.config.enableSeconds){w.timeContainer.classList.add("hasSeconds");var i=m("flatpickr-second");w.secondElement=i.getElementsByTagName("input")[0],w.secondElement.value=o(w.latestSelectedDateObj?w.latestSelectedDateObj.getSeconds():e.seconds),w.secondElement.setAttribute("step",w.minuteElement.getAttribute("step")),w.secondElement.setAttribute("min","0"),w.secondElement.setAttribute("max","59"),w.secondElement.setAttribute("maxlength","2"),w.timeContainer.appendChild(d("span","flatpickr-time-separator",":")),w.timeContainer.appendChild(i)}w.config.time_24hr||(w.amPM=d("span","flatpickr-am-pm",w.l10n.amPM[r((w.latestSelectedDateObj?w.hourElement.value:w.config.defaultHour)>11)]),w.amPM.title=w.l10n.toggleTitle,w.amPM.tabIndex=-1,w.timeContainer.appendChild(w.amPM));return w.timeContainer}());s(w.calendarContainer,"rangeMode","range"===w.config.mode),s(w.calendarContainer,"animate",!0===w.config.animate),s(w.calendarContainer,"multiMonth",w.config.showMonths>1),w.calendarContainer.appendChild(e);var i=void 0!==w.config.appendTo&&void 0!==w.config.appendTo.nodeType;if((w.config.inline||w.config.static)&&(w.calendarContainer.classList.add(w.config.inline?"inline":"static"),w.config.inline&&(!i&&w.element.parentNode?w.element.parentNode.insertBefore(w.calendarContainer,w._input.nextSibling):void 0!==w.config.appendTo&&w.config.appendTo.appendChild(w.calendarContainer)),w.config.static)){var l=d("div","flatpickr-wrapper");w.element.parentNode&&w.element.parentNode.insertBefore(l,w.element),l.appendChild(w.element),w.altInput&&l.appendChild(w.altInput),l.appendChild(w.calendarContainer)}w.config.static||w.config.inline||(void 0!==w.config.appendTo?w.config.appendTo:window.document.body).appendChild(w.calendarContainer)}(),function(){w.config.wrap&&["open","close","toggle","clear"].forEach((function(e){Array.prototype.forEach.call(w.element.querySelectorAll("[data-"+e+"]"),(function(n){return P(n,"click",w[e])}))}));if(w.isMobile)return void function(){var e=w.config.enableTime?w.config.noCalendar?"time":"datetime-local":"date";w.mobileInput=d("input",w.input.className+" flatpickr-mobile"),w.mobileInput.tabIndex=1,w.mobileInput.type=e,w.mobileInput.disabled=w.input.disabled,w.mobileInput.required=w.input.required,w.mobileInput.placeholder=w.input.placeholder,w.mobileFormatStr="datetime-local"===e?"Y-m-d\\TH:i:S":"date"===e?"Y-m-d":"H:i:S",w.selectedDates.length>0&&(w.mobileInput.defaultValue=w.mobileInput.value=w.formatDate(w.selectedDates[0],w.mobileFormatStr));w.config.minDate&&(w.mobileInput.min=w.formatDate(w.config.minDate,"Y-m-d"));w.config.maxDate&&(w.mobileInput.max=w.formatDate(w.config.maxDate,"Y-m-d"));w.input.getAttribute("step")&&(w.mobileInput.step=String(w.input.getAttribute("step")));w.input.type="hidden",void 0!==w.altInput&&(w.altInput.type="hidden");try{w.input.parentNode&&w.input.parentNode.insertBefore(w.mobileInput,w.input.nextSibling)}catch(e){}P(w.mobileInput,"change",(function(e){w.setDate(g(e).value,!1,w.mobileFormatStr),De("onChange"),De("onClose")}))}();var e=l(re,50);w._debouncedChange=l(Y,300),w.daysContainer&&!/iPhone|iPad|iPod/i.test(navigator.userAgent)&&P(w.daysContainer,"mouseover",(function(e){"range"===w.config.mode&&oe(g(e))}));P(w._input,"keydown",ie),void 0!==w.calendarContainer&&P(w.calendarContainer,"keydown",ie);w.config.inline||w.config.static||P(window,"resize",e);void 0!==window.ontouchstart?P(window.document,"touchstart",X):P(window.document,"mousedown",X);P(window.document,"focus",X,{capture:!0}),!0===w.config.clickOpens&&(P(w._input,"focus",w.open),P(w._input,"click",w.open));void 0!==w.daysContainer&&(P(w.monthNav,"click",xe),P(w.monthNav,["keyup","increment"],N),P(w.daysContainer,"click",me));if(void 0!==w.timeContainer&&void 0!==w.minuteElement&&void 0!==w.hourElement){var n=function(e){return g(e).select()};P(w.timeContainer,["increment"],_),P(w.timeContainer,"blur",_,{capture:!0}),P(w.timeContainer,"click",H),P([w.hourElement,w.minuteElement],["focus","click"],n),void 0!==w.secondElement&&P(w.secondElement,"focus",(function(){return w.secondElement&&w.secondElement.select()})),void 0!==w.amPM&&P(w.amPM,"click",(function(e){_(e)}))}w.config.allowInput&&P(w._input,"blur",ae)}(),(w.selectedDates.length||w.config.noCalendar)&&(w.config.enableTime&&F(w.config.noCalendar?w.latestSelectedDateObj:void 0),ye(!1)),S();var n=/^((?!chrome|android).)*safari/i.test(navigator.userAgent);!w.isMobile&&n&&de(),De("onReady")}(),w}function T(e,n){for(var t=Array.prototype.slice.call(e).filter((function(e){return e instanceof HTMLElement})),a=[],i=0;i assets/libs (viteStaticCopy), + * so this ships as assets/libs/fluentcrm/form-fields.js and is enqueued by + * FormElementBuilder via wp_enqueue_script — no hardcoded inline + getStats(); + + $stats['today'] = [ + 'title' => __('Today', 'fluent-smtp'), + 'sent' => ($allTime['sent']) ? $logModel->getTotalCountStat('sent', $startToday) : 0, + 'failed' => ($allTime['failed']) ? $logModel->getTotalCountStat('failed', $startToday) : 0 + ]; + + $lastWeek = gmdate('Y-m-d 00:00:01', strtotime('-7 days')); + $stats['week'] = [ + 'title' => __('Last 7 days', 'fluent-smtp'), + 'sent' => ($allTime['sent']) ? $logModel->getTotalCountStat('sent', $lastWeek) : 0, + 'failed' => ($allTime['failed']) ? $logModel->getTotalCountStat('failed', $lastWeek) : 0, + ]; + + $stats['all_time'] = [ + 'title' => __('All', 'fluent-smtp'), + 'sent' => $allTime['sent'], + 'failed' => $allTime['failed'], + ]; + ob_start(); + ?> + + + + + + + + + + + + + + + + + +
    + + 'handleForbiddenException', + 'FluentMail\Includes\Support\ValidationException' => 'handleValidationException' + ]; + + public function handle($e) + { + foreach ($this->handlers as $key => $value) { + if ($e instanceof $key) { + return $this->{$value}($e); + } + } + } + + public function handleForbiddenException($e) + { + wp_send_json_error([ + 'message' => $e->getMessage() + ], $e->getCode() ?: 403); + } + + public function handleValidationException($e) + { + wp_send_json_error([ + 'message' => $e->getMessage(), + 'errors' => $e->errors() + ], $e->getCode() ?: 422); + } +} diff --git a/wp-content/plugins/fluent-smtp/app/Hooks/Handlers/InitializeSiteHandler.php b/wp-content/plugins/fluent-smtp/app/Hooks/Handlers/InitializeSiteHandler.php new file mode 100644 index 0000000..3b4bd14 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/app/Hooks/Handlers/InitializeSiteHandler.php @@ -0,0 +1,21 @@ +blog_id; + switch_to_blog((int)$blog_id); + \FluentMailMigrations\EmailLogs::migrate(); + restore_current_blog(); + } +} diff --git a/wp-content/plugins/fluent-smtp/app/Hooks/Handlers/ProviderValidator.php b/wp-content/plugins/fluent-smtp/app/Hooks/Handlers/ProviderValidator.php new file mode 100644 index 0000000..6b3eb27 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/app/Hooks/Handlers/ProviderValidator.php @@ -0,0 +1,35 @@ +getProviderValidator($provider, $errors)) { + return $validator->validate(); + } + + return $errors; + } + + protected function getProviderValidator($provider, $errors) + { + $key = $provider['provider']; + + $path = FluentMail('path.app') . 'Services/Mailer/Providers/' . $key; + + $file = $path . '/' . 'Validator.php'; + + + if (file_exists($file)) { + $ns = 'FluentMail\App\Services\Mailer\Providers\\' . $key; + + $class = $ns . '\Validator'; + + if (class_exists($class)) { + return new $class($provider, $errors); + } + } + } +} \ No newline at end of file diff --git a/wp-content/plugins/fluent-smtp/app/Hooks/Handlers/SchedulerHandler.php b/wp-content/plugins/fluent-smtp/app/Hooks/Handlers/SchedulerHandler.php new file mode 100644 index 0000000..b942bd1 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/app/Hooks/Handlers/SchedulerHandler.php @@ -0,0 +1,356 @@ +dailyActionName, array($this, 'handleScheduledJobs')); + add_filter('fluentmail_email_sending_failed', array($this, 'maybeHandleFallbackConnection'), 10, 4); + + add_action('fluentsmtp_renew_gmail_token', array($this, 'renewGmailToken')); + + add_action('fluentmail_email_sending_failed_no_fallback', array($this, 'maybeSendNotification'), 10, 3); + } + + public function handleScheduledJobs() + { + $this->deleteOldEmails(); + $this->sendDailyDigest(); + } + + private function deleteOldEmails() + { + $settings = fluentMailGetSettings(); + $logSaveDays = intval(Arr::get($settings, 'misc.log_saved_interval_days')); + if ($logSaveDays) { + (new \FluentMail\App\Models\Logger())->deleteLogsOlderThan($logSaveDays); + } + } + + public function sendDailyDigest() + { + $settings = (new Settings())->notificationSettings(); + + if ($settings['enabled'] != 'yes' || empty($settings['notify_days']) || empty($settings['notify_email'])) { + return; + } + + $currentDay = gmdate('D'); + if (!in_array($currentDay, $settings['notify_days'])) { + return; + } + + $sendTo = $settings['notify_email']; + $sendTo = str_replace(['{site_admin}', '{admin_email}'], get_option('admin_email'), $sendTo); + + $sendToArray = explode(',', $sendTo); + + $sendToArray = array_filter($sendToArray, function ($email) { + return is_email($email); + }); + + if (!$sendToArray) { + return false; + } + + // we can send a summary email + $lastDigestSent = get_option('_fluentmail_last_email_digest'); + if ($lastDigestSent) { + if ((time() - strtotime($lastDigestSent)) < 72000) { + return false; // we don't want to send another email if sent time within 20 hours + } + } else { + $lastDigestSent = gmdate('Y-m-d', strtotime('-7 days')); + } + + // Let's create the stats + $startDate = gmdate('Y-m-d 00:00:01', (strtotime($lastDigestSent) - 86400)); + $endDate = gmdate('Y-m-d 23:59:59', strtotime('-1 days')); + + $reportingDays = floor((strtotime($endDate) - strtotime($startDate)) / 86400); + + $loggerModel = new Logger(); + $sentCount = $loggerModel->getTotalCountStat('sent', $startDate, $endDate); + + $sentStats = [ + 'total' => $sentCount, + 'subjects' => [], + 'unique_subjects' => 0 + ]; + if ($sentCount) { + $sentStats['unique_subjects'] = $loggerModel->getSubjectCountStat('sent', $startDate, $endDate); + $sentStats['subjects'] = $loggerModel->getSubjectStat('sent', $startDate, $endDate, 10); + } + + $failedCount = $loggerModel->getTotalCountStat('failed', $startDate, $endDate); + $failedStats = [ + 'total' => $sentCount, + 'subjects' => [], + 'unique_subjects' => 0 + ]; + if ($failedCount) { + $failedStats['unique_subjects'] = $loggerModel->getSubjectCountStat('failed', $startDate, $endDate); + $failedStats['subjects'] = $loggerModel->getSubjectStat('failed', $startDate, $endDate); + } + + $sentSubTitle = sprintf( + __('Showing %1$s of %2$s different subject lines sent in the past %3$s', 'fluent-smtp'), + number_format_i18n(count($sentStats['subjects'])), + number_format_i18n($sentStats['unique_subjects']), + ($reportingDays < 2) ? 'day' : $reportingDays . ' days' + ); + + $failedSubTitle = sprintf( + __('Showing %1$s of %2$s different subject lines failed in the past %3$s', 'fluent-smtp'), + number_format_i18n(count($failedStats['subjects'])), + number_format_i18n($failedStats['unique_subjects']), + ($reportingDays < 2) ? 'day' : $reportingDays . ' days' + ); + + $sentTitle = __('Emails Sent', 'fluent-smtp'); + if ($sentCount) { + $sentTitle .= ' (' . number_format_i18n($sentCount) . ')'; + } + $failedTitle = __('Email Failures', 'fluent-smtp'); + if ($failedCount) { + $failedTitle .= ' (' . number_format_i18n($failedCount) . ')'; + } + + $reportingDate = gmdate(get_option('date_format'), strtotime($startDate)); + + $data = [ + 'sent' => [ + 'total' => $sentCount, + 'title' => $sentTitle, + 'subtitle' => $sentSubTitle, + 'subject_items' => $sentStats['subjects'] + ], + 'fail' => [ + 'total' => $failedCount, + 'title' => $failedTitle, + 'subtitle' => $failedSubTitle, + 'subject_items' => $failedStats['subjects'] + ], + 'date_range' => $reportingDate, + 'domain_name' => $this->getDomainName() + ]; + + $emailBody = (string)fluentMail('view')->make('admin.digest_email', $data); + $emailSubject = $reportingDate . ' email sending stats for ' . $this->getDomainName(); + + $headers = array('Content-Type: text/html; charset=UTF-8'); + + update_option('_fluentmail_last_email_digest', gmdate('Y-m-d H:i:s')); + + return wp_mail($sendToArray, $emailSubject, $emailBody, $headers); + } + + private function getDomainName() + { + $parts = parse_url(site_url()); + $url = $parts['host'] . (isset($parts['path']) ? $parts['path'] : ''); + return untrailingslashit($url); + } + + public function maybeHandleFallbackConnection($status, $logId, $handler, $data = []) + { + if (defined('FLUENTMAIL_EMAIL_TESTING')) { + return false; + } + + $settings = (new \FluentMail\App\Models\Settings())->getSettings(); + + $fallbackConnectionId = \FluentMail\Includes\Support\Arr::get($settings, 'misc.fallback_connection'); + + if (!$fallbackConnectionId) { + do_action('fluentmail_email_sending_failed_no_fallback', $logId, $handler, $data); + return false; + } + + $fallbackConnection = \FluentMail\Includes\Support\Arr::get($settings, 'connections.' . $fallbackConnectionId); + + if (!$fallbackConnection) { + do_action('fluentmail_email_sending_failed_no_fallback', $logId, $handler, $data); + return false; + } + + $phpMailer = $handler->getPhpMailer(); + + $fallbackSettings = $fallbackConnection['provider_settings']; + $phpMailer->setFrom($fallbackSettings['sender_email'], $phpMailer->FromName); + + // Trap the fluentSMTPMail mailer here + $phpMailer = new \FluentMail\App\Services\Mailer\FluentPHPMailer($phpMailer); + return $phpMailer->sendViaFallback($logId); + } + + public function renewGmailToken() + { + $settings = fluentMailGetSettings(); + + if (!$settings) { + return; + } + + $connections = Arr::get($settings, 'connections', []); + + foreach ($connections as $connection) { + if (Arr::get($connection, 'provider_settings.provider') != 'gmail') { + continue; + } + $providerSettings = $connection['provider_settings']; + if (($providerSettings['expire_stamp'] - 480) < time() && !empty($providerSettings['refresh_token'])) { + $this->callGmailApiForNewToken($connection['provider_settings']); + } + } + } + + public function callGmailApiForNewToken($settings) + { + if (Arr::get($settings, 'key_store') == 'wp_config') { + $settings['client_id'] = defined('FLUENTMAIL_GMAIL_CLIENT_ID') ? FLUENTMAIL_GMAIL_CLIENT_ID : ''; + $settings['client_secret'] = defined('FLUENTMAIL_GMAIL_CLIENT_SECRET') ? FLUENTMAIL_GMAIL_CLIENT_SECRET : ''; + } + + if (!class_exists('\FluentSmtpLib\Google\Client')) { + require_once FLUENTMAIL_PLUGIN_PATH . 'includes/libs/google-api-client/build/vendor/autoload.php'; + } + + try { + $client = new \FluentSmtpLib\Google\Client(); + $client->setClientId($settings['client_id']); + $client->setClientSecret($settings['client_secret']); + $client->addScope("https://www.googleapis.com/auth/gmail.compose"); + $client->setAccessType('offline'); + $client->setApprovalPrompt('force'); + + $tokens = [ + 'access_token' => $settings['access_token'], + 'refresh_token' => $settings['refresh_token'], + 'expires_in' => $settings['expire_stamp'] - time() + ]; + + $client->setAccessToken($tokens); + + $newTokens = $client->refreshToken($tokens['refresh_token']); + $result = $this->saveNewGmailTokens($settings, $newTokens); + + if (!$result) { + return new \WP_Error('api_error', __('Failed to renew the token', 'fluent-smtp')); + } + + return true; + } catch (\Exception $exception) { + return new \WP_Error('api_error', $exception->getMessage()); + } + } + + + public function maybeSendNotification($rowId, $handler, $logData = []) + { + $lastNotificationSent = get_option('_fsmtp_last_notification_sent'); + if ($lastNotificationSent && (time() - $lastNotificationSent) < 60) { + return false; + } + + update_option('_fsmtp_last_notification_sent', time()); + + $notificationManager = new NotificationManager(); + $channels = $notificationManager->getActiveChannels(); + + if (!$channels) { + return false; + } + + foreach ($channels as $channel) { + $driver = $channel['driver']; + $channelSettings = Arr::get($channel, 'settings', []); + + if ($driver == 'telegram') { + $data = [ + 'token_id' => Arr::get($channelSettings, 'token'), + 'provider' => $handler->getSetting('provider'), + 'error_message' => $this->getErrorMessageFromResponse(maybe_unserialize(Arr::get($logData, 'response'))) + ]; + + NotificationHelper::sendFailedNotificationTele($data); + continue; + } + + if ($driver == 'slack') { + NotificationHelper::sendSlackMessage(NotificationHelper::formatSlackMessageBlock($handler, $logData), Arr::get($channelSettings, 'webhook_url'), false); + continue; + } + + if ($driver == 'discord') { + NotificationHelper::sendDiscordMessage(NotificationHelper::formatDiscordMessageBlock($handler, $logData), Arr::get($channelSettings, 'webhook_url'), false); + continue; + } + + if ($driver == 'pushover') { + NotificationHelper::sendPushoverMessage( + NotificationHelper::formatPushoverMessage($handler, $logData), + Arr::get($channelSettings, 'api_token'), + Arr::get($channelSettings, 'user_key'), + false, + 1 // High priority for failed emails + ); + continue; + } + } + + return true; + } + + private function saveNewGmailTokens($existingData, $tokens) + { + if (empty($tokens['access_token']) || empty($tokens['refresh_token'])) { + return false; + } + + $senderEmail = $existingData['sender_email']; + + $existingData['access_token'] = $tokens['access_token']; + $existingData['refresh_token'] = $tokens['refresh_token']; + $existingData['expire_stamp'] = $tokens['expires_in'] + time(); + $existingData['expires_in'] = $tokens['expires_in']; + + (new Settings())->updateConnection($senderEmail, $existingData); + fluentMailGetProvider($senderEmail, true); // we are clearing the static cache here + wp_schedule_single_event($existingData['expire_stamp'] - 360, 'fluentsmtp_renew_gmail_token'); + return true; + } + + private function getErrorMessageFromResponse($response) + { + if (!$response || !is_array($response)) { + return ''; + } + + if (!empty($response['fallback_response']['message'])) { + $message = $response['fallback_response']['message']; + } else { + $message = Arr::get($response, 'message'); + } + + if (!$message) { + return ''; + } + + if (!is_string($message)) { + $message = json_encode($message); + } + + return $message; + } +} diff --git a/wp-content/plugins/fluent-smtp/app/Hooks/actions.php b/wp-content/plugins/fluent-smtp/app/Hooks/actions.php new file mode 100644 index 0000000..74cfc88 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/app/Hooks/actions.php @@ -0,0 +1,8 @@ + __('SMTP/Mail Settings', 'fluent-smtp'), + 'url' => admin_url('options-general.php?page=fluent-mail#/') + ]; + + return $links; +}); + +add_filter( 'plugin_action_links_' . plugin_basename( FLUENTMAIL_PLUGIN_FILE ), function ($links) { + $links['settings'] = sprintf( + '%s', + admin_url('options-general.php?page=fluent-mail#/connections'), + esc_attr__( 'Go to Fluent SMTP Settings page', 'fluent-smtp' ), + esc_html__( 'Settings', 'fluent-smtp' ) + ); + return $links; +}, 10, 1 ); diff --git a/wp-content/plugins/fluent-smtp/app/Hooks/index.php b/wp-content/plugins/fluent-smtp/app/Hooks/index.php new file mode 100644 index 0000000..f0f663c --- /dev/null +++ b/wp-content/plugins/fluent-smtp/app/Hooks/index.php @@ -0,0 +1 @@ +app = App::getInstance(); + $this->request = $this->app['request']; + $this->response = $this->app['response']; + } + + public function send($data = null, $code = 200) + { + return $this->response->send($data, $code); + } + + public function sendSuccess($data = null, $code = 200) + { + return $this->response->sendSuccess($data, $code); + } + + public function sendError($data = null, $code = 422) + { + return $this->response->sendError($data, $code); + } + + public function verify() + { + $permission = 'manage_options'; + if(!current_user_can($permission)) { + wp_send_json_error([ + 'message' => __('You do not have permission to do this action', 'fluent-smtp') + ]); + die(); + } + + $nonce = $this->request->get('nonce'); + if(!wp_verify_nonce($nonce, FLUENTMAIL)) { + wp_send_json_error([ + 'message' => __('Security Failed. Please reload the page', 'fluent-smtp') + ]); + die(); + } + + return true; + } +} diff --git a/wp-content/plugins/fluent-smtp/app/Http/Controllers/DashboardController.php b/wp-content/plugins/fluent-smtp/app/Http/Controllers/DashboardController.php new file mode 100644 index 0000000..cb63769 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/app/Http/Controllers/DashboardController.php @@ -0,0 +1,133 @@ +verify(); + + $connections = $manager->getSettings('connections', []); + + return $this->send([ + 'stats' => $logger->getStats(), + 'settings_stat' => [ + 'connection_counts' => count($connections), + 'active_senders' => count($manager->getSettings('mappings', [])), + 'auto_delete_days' => $manager->getSettings('misc.log_saved_interval_days'), + 'log_enabled' => $manager->getSettings('misc.log_emails') + ] + ]); + } + + public function getDayTimeStats() + { + $this->verify(); + + $lastDay = 0; + if (isset($_REQUEST['last_day'])) { + $lastDay = (int)$_REQUEST['last_day']; + } + + global $wpdb; + if ($lastDay > 6) { + $results = $wpdb->get_results("SELECT + DAYNAME(created_at) AS day_of_week, + HOUR(created_at) AS hour_of_day, + COUNT(*) AS count +FROM + {$wpdb->prefix}fsmpt_email_logs +WHERE + created_at >= NOW() - INTERVAL {$lastDay} DAY +GROUP BY + DAYNAME(created_at), + HOUR(created_at) +ORDER BY + FIELD(DAYNAME(created_at), 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'), + HOUR(created_at)"); + } else { + $results = $wpdb->get_results("SELECT + DAYNAME(created_at) AS day_of_week, + HOUR(created_at) AS hour_of_day, + COUNT(*) AS count +FROM + {$wpdb->prefix}fsmpt_email_logs +GROUP BY + DAYNAME(created_at), + HOUR(created_at) +ORDER BY + FIELD(DAYNAME(created_at), 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'), + HOUR(created_at)"); + } + + // Assuming $results is the array of records fetched from the database. + $dataItems = [ + 'Mon' => [], 'Tue' => [], 'Wed' => [], 'Thu' => [], 'Fri' => [], 'Sat' => [], 'Sun' => [] + ]; + + $hours = ['0:00', '1:00', '2:00', '3:00', '4:00', '5:00', '6:00', '7:00', '8:00', '9:00', '10:00', '11:00', '12:00', '13:00', '14:00', '15:00', '16:00', '17:00', '18:00', '19:00', '20:00', '21:00', '22:00', '23:00']; + + foreach ($dataItems as $day => $data) { + foreach ($hours as $hour) { + $dataItems[$day][$hour] = 0; + } + } + + foreach ($results as $row) { + $day = substr($row->day_of_week, 0, 3); // Shorten 'Monday' to 'Mon', etc. + $hour = $row->hour_of_day . ":00"; // Format hour as '0:00', '1:00', etc. + $dataItems[$day][$hour] = (int)$row->count; + } + + return $this->send([ + 'stats' => $dataItems + ]); + + } + + public function getSendingStats(Request $request, Reporting $reporting) + { + $this->verify(); + + list($from, $to) = $request->get('date_range'); + + return $this->send([ + 'stats' => $reporting->getSendingStats($from, $to) + ]); + + } + + public function getDocs() + { + $this->verify(); + + $request = wp_remote_get('https://fluentsmtp.com/wp-json/wp/v2/docs?per_page=100'); + + $docs = json_decode(wp_remote_retrieve_body($request), true); + + + $formattedDocs = []; + + foreach ($docs as $doc) { + $primaryCategory = Arr::get($doc, 'taxonomy_info.doc_category.0', ['value' => 'none', 'label' => 'Other']); + $formattedDocs[] = [ + 'title' => $doc['title']['rendered'], + 'content' => $doc['content']['rendered'], + 'link' => $doc['link'], + 'category' => $primaryCategory + ]; + } + + return $this->send([ + 'docs' => $formattedDocs + ]); + } + +} diff --git a/wp-content/plugins/fluent-smtp/app/Http/Controllers/DiscordController.php b/wp-content/plugins/fluent-smtp/app/Http/Controllers/DiscordController.php new file mode 100644 index 0000000..ffb2b72 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/app/Http/Controllers/DiscordController.php @@ -0,0 +1,90 @@ +verify(); + + $formData = $request->get('settings', []); + + if (empty($formData['webhook_url'])) { + return $this->sendError([ + 'message' => __('Webhook URL is required', 'fluent-smtp') + ], 422); + } + + // validate the webhook URL + $webhookUrl = Arr::get($formData, 'webhook_url'); + if (!filter_var($webhookUrl, FILTER_VALIDATE_URL)) { + return $this->sendError([ + 'message' => __('Please provide a valid Webhook URL', 'fluent-smtp') + ], 422); + } + + if (empty($formData['channel_name'])) { + return $this->sendError([ + 'message' => __('Channel Name required', 'fluent-smtp') + ], 422); + } + + NotificationHelper::updateChannelSettings('discord', [ + 'status' => 'yes', + 'channel_name' => sanitize_text_field(Arr::get($formData, 'channel_name')), + 'webhook_url' => sanitize_url(Arr::get($formData, 'webhook_url')), + ]); + + return $this->sendSuccess([ + 'message' => __('Your settings has been saved', 'fluent-smtp'), + ]); + } + + public function sendTestMessage(Request $request) + { + // Let's update the notification status + $settings = (new Settings())->notificationSettings(); + + if (Arr::get($settings, 'discord.status') != 'yes') { + return $this->sendError([ + 'message' => __('Slack notification is not enabled', 'fluent-smtp') + ], 422); + } + + $message = 'This is a test message for ' . site_url() . '. If you get this message, then your site is connected successfully.'; + + $result = NotificationHelper::sendDiscordMessage($message, Arr::get($settings, 'discord.webhook_url')); + + if (is_wp_error($result)) { + return $this->sendError([ + 'message' => $result->get_error_message(), + 'errors' => $result->get_error_data(), + ], 422); + } + + return $this->sendSuccess([ + 'message' => __('Test message sent successfully', 'fluent-smtp'), + 'server_response' => $result + ]); + } + + public function disconnect() + { + NotificationHelper::updateChannelSettings('discord', [ + 'status' => 'no', + 'webhook_url' => '', + 'channel_name' => '' + ]); + + return $this->sendSuccess([ + 'message' => __('Discord connection has been disconnected successfully', 'fluent-smtp') + ]); + } +} diff --git a/wp-content/plugins/fluent-smtp/app/Http/Controllers/LoggerController.php b/wp-content/plugins/fluent-smtp/app/Http/Controllers/LoggerController.php new file mode 100644 index 0000000..cfc41c5 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/app/Http/Controllers/LoggerController.php @@ -0,0 +1,117 @@ +verify(); + + return $this->send( + $logger->get( + $request->except(['nonce', 'action']) + ) + ); + } + + public function show(Request $request, Logger $logger) + { + $this->verify(); + + $result = $logger->navigate($request->all()); + + return $this->sendSuccess($result); + } + + public function delete(Request $request, Logger $logger) + { + $this->verify(); + + $id = (array) $request->get('id'); + + $logger->delete($id); + + if ($id && $id[0] == 'all') { + $subject = 'All logs'; + } else { + $count = count($id); + $subject = $count > 1 ? "{$count} Logs" : 'Log'; + } + + return $this->sendSuccess([ + 'message' => sprintf(__('%s deleted successfully.', 'fluent-smtp'), $subject) + ]); + } + + public function retry(Request $request, Logger $logger) + { + $this->verify(); + + try { + $this->app->addAction('wp_mail_failed', function($response) use ($logger, $request) { + $log = $logger->find($id = $request->get('id')); + $log['retries'] = $log['retries'] + 1; + $logger->updateLog($log, ['id' => $id]); + + return $this->sendError([ + 'message' => $response->get_error_message(), + 'errors' => $response->get_error_data() + ], $response->get_error_code()); + }); + + if ($email = $logger->resendEmailFromLog($request->get('id'), $request->get('type'))) { + return $this->sendSuccess([ + 'email' => $email, + 'message' => __('Email sent successfully.', 'fluent-smtp') + ]); + } + + throw new \Exception(esc_html__('Something went wrong', 'fluent-smtp'), 400); + + } catch (\Exception $e) { + return $this->sendError([ + 'message' => $e->getMessage() + ], $e->getCode()); + } + } + + public function retryBulk(Request $request, Logger $logger) + { + $this->verify(); + $logIds = $request->get('log_ids', []); + + $failedCount = 0; + $this->app->addAction('wp_mail_failed', function($response) use (&$failedCount) { + $failedCount++; + }); + + $failedInitiated = 0; + $successCount = 0; + foreach ($logIds as $logId) { + try { + $email = $logger->resendEmailFromLog($logId, 'check_realtime'); + $successCount++; + } catch (\Exception $exception) { + $failedInitiated++; + } + } + $message = __('Selected Emails have been proceed to send.', 'fluent-smtp'); + + if ($failedCount) { + $message .= sprintf(__(' But %d emails are reported to failed to send.', 'fluent-smtp'), $failedCount); + } + + if ($failedInitiated) { + $message .= sprintf(__(' And %d emails are failed to init the emails', 'fluent-smtp'), $failedInitiated); + } + + return $this->sendSuccess([ + 'message' => $message + ]); + + } +} diff --git a/wp-content/plugins/fluent-smtp/app/Http/Controllers/PushoverController.php b/wp-content/plugins/fluent-smtp/app/Http/Controllers/PushoverController.php new file mode 100644 index 0000000..455c403 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/app/Http/Controllers/PushoverController.php @@ -0,0 +1,81 @@ +verify(); + + $formData = $request->get('settings', []); + + if (empty($formData['api_token'])) { + return $this->sendError([ + 'message' => __('API Token is required', 'fluent-smtp') + ], 422); + } + + if (empty($formData['user_key'])) { + return $this->sendError([ + 'message' => __('User Key is required', 'fluent-smtp') + ], 422); + } + + NotificationHelper::updateChannelSettings('pushover', [ + 'status' => 'yes', + 'api_token' => sanitize_text_field(Arr::get($formData, 'api_token')), + 'user_key' => sanitize_text_field(Arr::get($formData, 'user_key')), + ]); + + return $this->sendSuccess([ + 'message' => __('Your settings has been saved', 'fluent-smtp'), + ]); + } + + public function sendTestMessage(Request $request) + { + $settings = (new Settings())->notificationSettings(); + + if (Arr::get($settings, 'pushover.status') != 'yes') { + return $this->sendError([ + 'message' => __('Pushover notification is not enabled', 'fluent-smtp') + ], 422); + } + + $result = NotificationHelper::sendTestPushoverMessage( + Arr::get($settings, 'pushover.api_token'), + Arr::get($settings, 'pushover.user_key') + ); + + if (is_wp_error($result)) { + return $this->sendError([ + 'message' => $result->get_error_message(), + 'errors' => $result->get_error_data(), + ], 422); + } + + return $this->sendSuccess([ + 'message' => __('Test message sent successfully', 'fluent-smtp'), + 'server_response' => $result + ]); + } + + public function disconnect() + { + NotificationHelper::updateChannelSettings('pushover', [ + 'status' => 'no', + 'api_token' => '', + 'user_key' => '' + ]); + + return $this->sendSuccess([ + 'message' => __('Pushover connection has been disconnected successfully', 'fluent-smtp') + ]); + } +} diff --git a/wp-content/plugins/fluent-smtp/app/Http/Controllers/SettingsController.php b/wp-content/plugins/fluent-smtp/app/Http/Controllers/SettingsController.php new file mode 100644 index 0000000..bcb0eff --- /dev/null +++ b/wp-content/plugins/fluent-smtp/app/Http/Controllers/SettingsController.php @@ -0,0 +1,728 @@ +verify(); + + try { + $setting = $settings->get(); + + return $this->sendSuccess([ + 'settings' => $setting + ]); + } catch (Exception $e) { + return $this->sendError([ + 'message' => $e->getMessage() + ], $e->getCode()); + } + } + + public function validate(Request $request, Settings $settings, Factory $factory) + { + $this->verify(); + + try { + $data = $request->except(['action', 'nonce']); + + $provider = $factory->make($data['provider']['key']); + + $provider->validateBasicInformation($data); + + $this->sendSuccess(); + } catch (ValidationException $e) { + $this->sendError($e->errors(), $e->getCode()); + } + } + + public function store(Request $request, Settings $settings, Factory $factory) + { + $this->verify(); + + $passWordKeys = ['password', 'access_key', 'secret_key', 'api_key', 'client_id', 'client_secret', 'auth_token', 'access_token', 'refresh_token']; + + try { + $data = $request->except(['action', 'nonce']); + + $data = wp_unslash($data); + + $provider = $factory->make($data['connection']['provider']); + + $connection = $data['connection']; + + foreach ($connection as $index => $value) { + if ($index == 'sender_email') { + $connection['sender_email'] = sanitize_email($connection['sender_email']); + } + + if (in_array($index, $passWordKeys)) { + if ($value) { + $connection[$index] = trim($value); + } + continue; + } + + if (is_string($value) && $value) { + $connection[$index] = sanitize_text_field($value); + } + } + + $data['connection'] = $connection; + + $this->validateConnection($provider, $connection); + + $provider->checkConnection($connection); + + $data['valid_senders'] = $provider->getValidSenders($connection); + + $data = apply_filters('fluentmail_saving_connection_data', $data, $data['connection']['provider']); + + $settings->store($data); + + return $this->sendSuccess([ + 'message' => __('Settings saved successfully.', 'fluent-smtp'), + 'connections' => $settings->getConnections(), + 'mappings' => $settings->getMappings(), + 'misc' => $settings->getMisc() + ]); + } catch (ValidationException $e) { + return $this->sendError($e->errors(), 422); + } catch (Exception $e) { + return $this->sendError([ + 'message' => $e->getMessage() + ], 422); + } + } + + public function storeMiscSettings(Request $request, Settings $settings) + { + $this->verify(); + + $misc = $request->get('settings'); + $settings->updateMiscSettings($misc); + $this->sendSuccess([ + 'message' => __('General Settings has been updated', 'fluent-smtp') + ]); + } + + public function delete(Request $request, Settings $settings) + { + $this->verify(); + + $settings = $settings->delete($request->get('key')); + + return $this->sendSuccess($settings); + } + + public function storeGlobals(Request $request, Settings $settings) + { + $this->verify(); + + $settings->saveGlobalSettings( + $data = $request->except(['action', 'nonce']) + ); + + return $this->sendSuccess([ + 'form' => $data, + 'message' => __('Settings saved successfully.', 'fluent-smtp') + ]); + } + + public function sendTestEmil(Request $request, Settings $settings) + { + $this->verify(); + + try { + $this->app->addAction('wp_mail_failed', [$this, 'onFail']); + + $data = $request->except(['action', 'nonce']); + + if (!isset($data['email'])) { + return $this->sendError([ + 'email_error' => __('The email field is required.', 'fluent-smtp') + ], 422); + } + + if (!defined('FLUENTMAIL_EMAIL_TESTING')) { + define('FLUENTMAIL_EMAIL_TESTING', true); + } + + $settings->sendTestEmail($data, $settings->get()); + + return $this->sendSuccess([ + 'message' => __('Email delivered successfully.', 'fluent-smtp') + ]); + } catch (Exception $e) { + return $this->sendError([ + 'message' => $e->getMessage() + ], $e->getCode()); + } + } + + public function onFail($response) + { + return $this->sendError([ + 'message' => $response->get_error_message(), + 'errors' => $response->get_error_data() + ], 422); + } + + public function validateConnection($provider, $connection) + { + $errors = []; + + try { + $provider->validateBasicInformation($connection); + } catch (ValidationException $e) { + $errors = $e->errors(); + } + + try { + $provider->validateProviderInformation($connection); + } catch (ValidationException $e) { + $errors = array_merge($errors, $e->errors()); + } + + if ($errors) { + throw new ValidationException(esc_html__('Unprocessable Entity', 'fluent-smtp'), 422, null, $errors); // phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped + } + } + + public function getConnectionInfo(Request $request, Settings $settings, Factory $factory) + { + $this->verify(); + + $connectionId = $request->get('connection_id'); + $connections = $settings->getConnections(); + + if (!isset($connections[$connectionId]['provider_settings'])) { + return $this->sendSuccess([ + 'info' => __('Sorry no connection found. Please reload the page and try again', 'fluent-smtp') + ]); + } + + $connection = $connections[$connectionId]['provider_settings']; + + $provider = $factory->make($connection['provider']); + + return $this->sendSuccess($provider->getConnectionInfo($connection)); + } + + public function addNewSenderEmail(Request $request, Settings $settings, Factory $factory) + { + $this->verify(); + + $connectionId = $request->get('connection_id'); + $connections = $settings->getConnections(); + + if (!isset($connections[$connectionId]['provider_settings'])) { + return $this->sendSuccess([ + 'info' => __('Sorry no connection found. Please reload the page and try again', 'fluent-smtp') + ]); + } + + $connection = $connections[$connectionId]['provider_settings']; + + $provider = $factory->make($connection['provider']); + $email = sanitize_email($request->get('new_sender')); + + if (!is_email($email)) { + return $this->sendError([ + 'message' => __('Please provide a valid email address', 'fluent-smtp') + ]); + } + + $result = $provider->addNewSenderEmail($connection, $email); + + if (is_wp_error($result)) { + return $this->sendError([ + 'message' => $result->get_error_message() + ]); + } + + return $this->sendSuccess([ + 'message' => __('Email has been added successfully', 'fluent-smtp') + ]); + } + + public function removeSenderEmail(Request $request, Settings $settings, Factory $factory) + { + $this->verify(); + + $connectionId = $request->get('connection_id'); + $connections = $settings->getConnections(); + + if (!isset($connections[$connectionId]['provider_settings'])) { + return $this->sendSuccess([ + 'info' => __('Sorry no connection found. Please reload the page and try again', 'fluent-smtp') + ]); + } + + $connection = $connections[$connectionId]['provider_settings']; + + $provider = $factory->make($connection['provider']); + $email = sanitize_email($request->get('email')); + + if (!is_email($email)) { + return $this->sendError([ + 'message' => __('Please provide a valid email address', 'fluent-smtp') + ]); + } + + $result = $provider->removeSenderEmail($connection, $email); + + if (is_wp_error($result)) { + return $this->sendError([ + 'message' => $result->get_error_message() + ]); + } + + return $this->sendSuccess([ + 'message' => __('Email has been removed successfully', 'fluent-smtp') + ]); + } + + public function installPlugin(Request $request) + { + $this->verify(); + $pluginSlug = $request->get('plugin_slug'); + $plugin = [ + 'name' => $pluginSlug, + 'repo-slug' => $pluginSlug, + 'file' => $pluginSlug . '.php' + ]; + + $UrlMaps = [ + 'fluentform' => [ + 'admin_url' => admin_url('admin.php?page=fluent_forms'), + 'title' => __('Go to Fluent Forms Dashboard', 'fluent-smtp') + ], + 'fluent-crm' => [ + 'admin_url' => admin_url('admin.php?page=fluentcrm-admin'), + 'title' => __('Go to FluentCRM Dashboard', 'fluent-smtp') + ], + 'ninja-tables' => [ + 'admin_url' => admin_url('admin.php?page=ninja_tables#/'), + 'title' => __('Go to Ninja Tables Dashboard', 'fluent-smtp') + ] + ]; + + if (!isset($UrlMaps[$pluginSlug]) || !wp_is_file_mod_allowed('install_plugins')) { + $this->sendError([ + 'message' => __('Sorry, You can not install this plugin', 'fluent-smtp') + ]); + } + + try { + $this->backgroundInstaller($plugin); + $this->send([ + 'message' => __('Plugin has been successfully installed.', 'fluent-smtp'), + 'info' => $UrlMaps[$pluginSlug] + ]); + } catch (\Exception $exception) { + $this->sendError([ + 'message' => $exception->getMessage() + ]); + } + } + + private function backgroundInstaller($plugin_to_install) + { + if (!empty($plugin_to_install['repo-slug'])) { + require_once ABSPATH . 'wp-admin/includes/file.php'; + require_once ABSPATH . 'wp-admin/includes/plugin-install.php'; + require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php'; + require_once ABSPATH . 'wp-admin/includes/plugin.php'; + + WP_Filesystem(); + + $skin = new \Automatic_Upgrader_Skin(); + $upgrader = new \WP_Upgrader($skin); + $installed_plugins = array_keys(\get_plugins()); + $plugin_slug = $plugin_to_install['repo-slug']; + $plugin_file = isset($plugin_to_install['file']) ? $plugin_to_install['file'] : $plugin_slug . '.php'; + $installed = false; + $activate = false; + + // See if the plugin is installed already. + if (isset($installed_plugins[$plugin_file])) { + $installed = true; + $activate = !is_plugin_active($installed_plugins[$plugin_file]); + } + + // Install this thing! + if (!$installed) { + // Suppress feedback. + ob_start(); + + try { + $plugin_information = plugins_api( + 'plugin_information', + array( + 'slug' => $plugin_slug, + 'fields' => array( + 'short_description' => false, + 'sections' => false, + 'requires' => false, + 'rating' => false, + 'ratings' => false, + 'downloaded' => false, + 'last_updated' => false, + 'added' => false, + 'tags' => false, + 'homepage' => false, + 'donate_link' => false, + 'author_profile' => false, + 'author' => false, + ), + ) + ); + + if (is_wp_error($plugin_information)) { + throw new \Exception(wp_kses_post($plugin_information->get_error_message())); + } + + $package = $plugin_information->download_link; + $download = $upgrader->download_package($package); + + if (is_wp_error($download)) { + throw new \Exception(wp_kses_post($download->get_error_message())); + } + + $working_dir = $upgrader->unpack_package($download, true); + + if (is_wp_error($working_dir)) { + throw new \Exception(wp_kses_post($working_dir->get_error_message())); + } + + $result = $upgrader->install_package( + array( + 'source' => $working_dir, + 'destination' => WP_PLUGIN_DIR, + 'clear_destination' => false, + 'abort_if_destination_exists' => false, + 'clear_working' => true, + 'hook_extra' => array( + 'type' => 'plugin', + 'action' => 'install', + ), + ) + ); + + if (is_wp_error($result)) { + throw new \Exception(wp_kses_post($result->get_error_message())); + } + + $activate = true; + } catch (\Exception $e) { + throw new \Exception(esc_html($e->getMessage())); + } + + // Discard feedback. + ob_end_clean(); + } + + wp_clean_plugins_cache(); + + // Activate this thing. + if ($activate) { + try { + $result = activate_plugin($installed ? $installed_plugins[$plugin_file] : $plugin_slug . '/' . $plugin_file); + + if (is_wp_error($result)) { + throw new \Exception(esc_html($result->get_error_message())); + } + } catch (\Exception $e) { + throw new \Exception(esc_html($e->getMessage())); + } + } + } + } + + public function subscribe() + { + $this->verify(); + $email = sanitize_text_field($_REQUEST['email']); // phpcs:ignore WordPress.Security.NonceVerification.Recommended + + $displayName = ''; + + if (isset($_REQUEST['display_name'])) { + $displayName = sanitize_text_field($_REQUEST['display_name']); + } + + if (!is_email($email)) { + return $this->sendError([ + 'message' => __('Sorry! The provider email is not valid', 'fluent-smtp') + ], 422); + } + + $shareEssentials = 'no'; + + if ($_REQUEST['share_essentials'] == 'yes') { + update_option('_fluentsmtp_sub_update', 'shared', 'no'); + $shareEssentials = 'yes'; + } else { + update_option('_fluentsmtp_sub_update', 'yes', 'no'); + } + + $this->pushData($email, $shareEssentials, $displayName); + + return $this->sendSuccess([ + 'message' => __('You are subscribed to plugin update and monthly tips', 'fluent-smtp') + ]); + } + + public function subscribeDismiss() + { + $this->verify(); + update_option('_fluentsmtp_dismissed_timestamp', time(), 'no'); + + return $this->sendSuccess([ + 'message' => 'success' + ]); + } + + private function pushData($optinEmail, $shareEssentials, $displayName = '') + { + $user = get_user_by('ID', get_current_user_id()); + + $url = 'https://fluentsmtp.com/wp-admin/?fluentcrm=1&route=contact&hash=6012116c-90d8-42a5-a65b-3649aa34b356'; + + + if (!$displayName) { + $displayName = trim($user->first_name . ' ' . $user->last_name); + if (!$displayName) { + $displayName = $user->display_name; + } + } + + wp_remote_post($url, [ + 'body' => json_encode([ // phpcs:ignore WordPress.WP.AlternativeFunctions.json_encode_json_encode + 'full_name' => $displayName, + 'email' => $optinEmail, + 'source' => 'smtp', + 'optin_website' => site_url(), + 'share_essential' => $shareEssentials + ]) + ]); + } + + public function getGmailAuthUrl(Request $request) + { + $this->verify(); + $connection = wp_unslash($request->get('connection')); + + $clientId = Arr::get($connection, 'client_id'); + $clientSecret = Arr::get($connection, 'client_secret'); + + if (Arr::get($connection, 'key_store') == 'wp_config') { + if (defined('FLUENTMAIL_GMAIL_CLIENT_ID')) { + $clientId = FLUENTMAIL_GMAIL_CLIENT_ID; + } else { + return $this->sendError([ + 'client_id' => [ + 'required' => __('Please define FLUENTMAIL_GMAIL_CLIENT_ID in your wp-config.php file', 'fluent-smtp') + ] + ]); + } + if (defined('FLUENTMAIL_GMAIL_CLIENT_SECRET')) { + $clientSecret = FLUENTMAIL_GMAIL_CLIENT_SECRET; + } else { + return $this->sendError([ + 'client_secret' => [ + 'required' => __('Please define FLUENTMAIL_GMAIL_CLIENT_SECRET in your wp-config.php file', 'fluent-smtp') + ] + ]); + } + } + + if (!$clientId) { + return $this->sendError([ + 'client_id' => [ + 'required' => __('Please provide application client id', 'fluent-smtp') + ] + ]); + } + + if (!$clientSecret) { + return $this->sendError([ + 'client_secret' => [ + 'required' => __('Please provide application client secret', 'fluent-smtp') + ] + ]); + } + + $authUrl = add_query_arg([ + 'response_type' => 'code', + 'access_type' => 'offline', + 'client_id' => $clientId, + 'redirect_uri' => apply_filters('fluentsmtp_gapi_callback', 'https://fluentsmtp.com/gapi/'), + 'state' => admin_url('options-general.php?page=fluent-mail&gapi=1'), + 'scope' => 'https://mail.google.com/', + 'approval_prompt' => 'force', + 'include_granted_scopes' => 'true' + ], 'https://accounts.google.com/o/oauth2/auth'); + + return $this->sendSuccess([ + 'auth_url' => filter_var($authUrl, FILTER_SANITIZE_URL) + ]); + } + + public function getOutlookAuthUrl(Request $request) + { + $this->verify(); + $connection = wp_unslash($request->get('connection')); + + $clientId = Arr::get($connection, 'client_id'); + $clientSecret = Arr::get($connection, 'client_secret'); + + delete_option('_fluentsmtp_intended_outlook_info'); + + if (Arr::get($connection, 'key_store') == 'wp_config') { + if (defined('FLUENTMAIL_OUTLOOK_CLIENT_ID')) { + $clientId = FLUENTMAIL_OUTLOOK_CLIENT_ID; + } else { + return $this->sendError([ + 'client_id' => [ + 'required' => __('Please define FLUENTMAIL_OUTLOOK_CLIENT_ID in your wp-config.php file', 'fluent-smtp') + ] + ]); + } + if (defined('FLUENTMAIL_OUTLOOK_CLIENT_SECRET')) { + $clientSecret = FLUENTMAIL_OUTLOOK_CLIENT_SECRET; + } else { + return $this->sendError([ + 'client_secret' => [ + 'required' => __('Please define FLUENTMAIL_OUTLOOK_CLIENT_SECRET in your wp-config.php file', 'fluent-smtp') + ] + ]); + } + } else { + update_option('_fluentsmtp_intended_outlook_info', [ + 'client_id' => $clientId, + 'client_secret' => $clientSecret + ]); + } + + if (!$clientId) { + return $this->sendError([ + 'client_id' => [ + 'required' => __('Please provide application client id', 'fluent-smtp') + ] + ]); + } + + if (!$clientSecret) { + return $this->sendError([ + 'client_secret' => [ + 'required' => __('Please provide application client secret', 'fluent-smtp') + ] + ]); + } + + return $this->sendSuccess([ + 'auth_url' => (new \FluentMail\App\Services\Mailer\Providers\Outlook\API($clientId, $clientSecret))->getAuthUrl() + ]); + } + + public function getNotificationSettings() + { + $settings = (new Settings())->notificationSettings(); + $this->verify(); + + $settings['telegram_notify_token'] = ''; + + return $this->sendSuccess([ + 'settings' => $settings + ]); + } + + public function saveNotificationSettings(Request $request) + { + $this->verify(); + + $settings = $request->get('settings', []); + + $settings = Arr::only($settings, ['enabled', 'notify_email', 'notify_days']); + + $settings['notify_email'] = sanitize_text_field($settings['notify_email']); + $settings['enabled'] = sanitize_text_field($settings['enabled']); + + $defaults = [ + 'enabled' => 'no', + 'notify_email' => '{site_admin}', + 'notify_days' => ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'] + ]; + + $oldSettings = (new Settings())->notificationSettings(); + $defaults = wp_parse_args($defaults, $oldSettings); + + $settings = wp_parse_args($settings, $defaults); + + update_option('_fluent_smtp_notify_settings', $settings, false); + + return $this->sendSuccess([ + 'message' => __('Settings has been updated successfully', 'fluent-smtp') + ]); + } + + public function getNotificationChannels() + { + $this->verify(); + + $notificationManager = new NotificationManager(); + $channels = $notificationManager->getAllChannels(); + $settings = (new Settings())->notificationSettings(); + $activeChannel = Arr::get($settings, 'active_channel', []); + + // Add status and active state to each channel + $channelsWithStatus = []; + foreach ($channels as $key => $channel) { + $channelSettings = Arr::get($settings, $key, []); + $channelsWithStatus[$key] = array_merge($channel, [ + 'status' => Arr::get($channelSettings, 'status', 'no'), + 'is_active' => in_array($key, $activeChannel), + 'settings' => $channelSettings + ]); + } + + return $this->sendSuccess([ + 'channels' => $channelsWithStatus, + 'active_channel' => $activeChannel + ]); + } + + public function toggleNotificationChannel(Request $request) + { + $this->verify(); + + $channelKeys = $request->get('channel_keys', []); + $channelKeys = array_map('sanitize_text_field', $channelKeys); + $allChanelKeys = (new NotificationManager())->getAllChannelKeys(); + $channelKeys = array_filter($channelKeys, function ($key) use ($allChanelKeys) { + return in_array($key, $allChanelKeys); + }); + + $settings = (new Settings())->notificationSettings(); + + $settings['active_channel'] = $channelKeys; + + update_option('_fluent_smtp_notify_settings', $settings, false); + + return $this->sendSuccess([ + 'message' => __('Notification channel updated successfully', 'fluent-smtp'), + 'active_channels' => $channelKeys + ]); + } +} diff --git a/wp-content/plugins/fluent-smtp/app/Http/Controllers/SlackController.php b/wp-content/plugins/fluent-smtp/app/Http/Controllers/SlackController.php new file mode 100644 index 0000000..85b780c --- /dev/null +++ b/wp-content/plugins/fluent-smtp/app/Http/Controllers/SlackController.php @@ -0,0 +1,98 @@ +verify(); + + $formData = $request->get('settings', []); + + $userEmail = sanitize_email(Arr::get($formData, 'user_email')); + + if (!is_email($userEmail)) { + return $this->sendError([ + 'message' => __('Please provide a valid email address', 'fluent-smtp') + ], 422); + } + + $nonce = wp_create_nonce('fluent_smtp_slack_register_site'); + + $payload = [ + 'admin_email' => $userEmail, + 'smtp_url' => admin_url('options-general.php?_slacK_nonce=' . $nonce . '&page=fluent-mail#/'), + 'site_url' => site_url(), + 'site_title' => get_bloginfo('name'), + 'site_lang' => get_bloginfo('language'), + ]; + + + $activationData = NotificationHelper::registerSlackSite($payload); + + if (is_wp_error($activationData)) { + return $this->sendError([ + 'message' => $activationData->get_error_message(), + 'errors' => $activationData->get_error_data(), + ], 422); + } + + NotificationHelper::updateChannelSettings('slack', [ + 'status' => 'pending', + 'token' => Arr::get($activationData, 'site_token'), + 'redirect_url' => '' + ]); + + return $this->sendSuccess([ + 'message' => __('Awesome! You are redirecting to slack', 'fluent-smtp'), + 'redirect_url' => Arr::get($activationData, 'redirect_url') + ]); + } + + public function sendTestMessage(Request $request) + { + // Let's update the notification status + $settings = (new Settings())->notificationSettings(); + + if (Arr::get($settings, 'slack.status') != 'yes') { + return $this->sendError([ + 'message' => __('Slack notification is not enabled', 'fluent-smtp') + ], 422); + } + + $message = 'This is a test message for ' . site_url() . '. If you get this message, then your site is connected successfully.'; + + $result = NotificationHelper::sendSlackMessage($message, Arr::get($settings, 'slack.webhook_url')); + + if (is_wp_error($result)) { + return $this->sendError([ + 'message' => $result->get_error_message(), + 'errors' => $result->get_error_data(), + ], 422); + } + + return $this->sendSuccess([ + 'message' => __('Test message sent successfully', 'fluent-smtp') + ]); + } + + public function disconnect() + { + NotificationHelper::updateChannelSettings('slack', [ + 'status' => 'no', + 'webhook_url' => '', + 'token' => '' + ]); + + return $this->sendSuccess([ + 'message' => __('Slack connection has been disconnected successfully', 'fluent-smtp') + ]); + } + +} diff --git a/wp-content/plugins/fluent-smtp/app/Http/Controllers/TelegramController.php b/wp-content/plugins/fluent-smtp/app/Http/Controllers/TelegramController.php new file mode 100644 index 0000000..efbd74c --- /dev/null +++ b/wp-content/plugins/fluent-smtp/app/Http/Controllers/TelegramController.php @@ -0,0 +1,161 @@ +verify(); + + $formData = $request->get('settings', []); + + $userEmail = sanitize_email(Arr::get($formData, 'user_email')); + + if (!is_email($userEmail)) { + return $this->sendError([ + 'message' => __('Please provide a valid email address', 'fluent-smtp') + ], 422); + } + + $payload = [ + 'admin_email' => $userEmail, + 'smtp_url' => admin_url('options-general.php?page=fluent-mail#/'), + 'site_url' => site_url(), + 'site_title' => get_bloginfo('name'), + 'site_lang' => get_bloginfo('language'), + ]; + + + $activationData = NotificationHelper::issueTelegramPinCode($payload); + + if (is_wp_error($activationData)) { + return $this->sendError([ + 'message' => $activationData->get_error_message(), + 'errors' => $activationData->get_error_data(), + ], 422); + } + + return $this->sendSuccess([ + 'message' => __('Awesome! Please activate the connection from your telegram account.', 'fluent-smtp'), + 'site_token' => Arr::get($activationData, 'site_token'), + 'site_pin' => Arr::get($activationData, 'site_pin'), + ]); + } + + public function confirmConnection(Request $request) + { + $this->verify(); + + $siteToken = $request->get('site_token', ''); + + if (empty($siteToken)) { + return $this->sendError([ + 'message' => __('Please provide site token', 'fluent-smtp') + ], 422); + } + + + $connectionInfo = NotificationHelper::getTelegramConnectionInfo($siteToken); + + if (is_wp_error($connectionInfo)) { + return $this->sendError([ + 'message' => $connectionInfo->get_error_message(), + 'errors' => $connectionInfo->get_error_data(), + ], 422); + } + + NotificationHelper::updateChannelSettings('telegram', [ + 'status' => 'yes', + 'token' => $siteToken + ]); + + return $this->sendSuccess([ + 'success' => true, + 'message' => __('Connection successful', 'fluent-smtp'), + ]); + } + + public function getTelegramConnectionInfo(Request $request) + { + $this->verify(); + + $settings = (new Settings())->notificationSettings(); + + if (Arr::get($settings, 'telegram.status') != 'yes') { + return $this->sendSuccess([ + 'message' => __('Telegram notification is not enabled', 'fluent-smtp'), + 'telegram_notify_status' => 'no' + ], 200); + } + + $siteToken = Arr::get($settings, 'telegram.token'); + + $connectionInfo = NotificationHelper::getTelegramConnectionInfo($siteToken); + + if (is_wp_error($connectionInfo)) { + return $this->sendSuccess([ + 'telegram_notify_status' => 'failed', + 'message' => $connectionInfo->get_error_message(), + 'errors' => $connectionInfo->get_error_data(), + ]); + } + + return $this->sendSuccess([ + 'telegram_notify_status' => 'yes', + 'telegram_receiver' => Arr::get($connectionInfo, 'telegram_receiver', []), + ]); + } + + public function sendTestMessage(Request $request) + { + // Let's update the notification status + $settings = (new Settings())->notificationSettings(); + + if (Arr::get($settings, 'telegram.status') != 'yes') { + return $this->sendError([ + 'message' => __('Telegram notification is not enabled', 'fluent-smtp') + ], 422); + } + + $result = NotificationHelper::sendTestTelegramMessage(Arr::get($settings, 'telegram.token')); + + if (is_wp_error($result)) { + return $this->sendError([ + 'message' => $result->get_error_message(), + 'errors' => $result->get_error_data(), + ], 422); + } + + return $this->sendSuccess([ + 'message' => __('Test message sent successfully', 'fluent-smtp') + ]); + } + + public function disconnect() + { + $settings = (new Settings())->notificationSettings(); + + $token = Arr::get($settings, 'telegram.token'); + + // Only call disconnect API if we have a token + if ($token) { + NotificationHelper::disconnectTelegram($token); + } + + NotificationHelper::updateChannelSettings('telegram', [ + 'status' => 'no', + 'token' => '' + ]); + + return $this->sendSuccess([ + 'message' => __('Telegram connection has been disconnected successfully', 'fluent-smtp') + ]); + } +} diff --git a/wp-content/plugins/fluent-smtp/app/Http/index.php b/wp-content/plugins/fluent-smtp/app/Http/index.php new file mode 100644 index 0000000..f0f663c --- /dev/null +++ b/wp-content/plugins/fluent-smtp/app/Http/index.php @@ -0,0 +1 @@ +get('/', 'DashboardController@index'); +$app->get('/day-time-stats', 'DashboardController@getDayTimeStats'); +$app->get('sending_stats', 'DashboardController@getSendingStats'); + +$app->get('/settings', 'SettingsController@index'); +$app->post('/settings/validate', 'SettingsController@validate'); +$app->post('/settings', 'SettingsController@store'); +$app->post('/misc-settings', 'SettingsController@storeMiscSettings'); +$app->post('/settings/delete', 'SettingsController@delete'); +$app->post('/settings/misc', 'SettingsController@storeGlobals'); +$app->post('/settings/test', 'SettingsController@sendTestEmil'); +$app->post('/settings/subscribe', 'SettingsController@subscribe'); +$app->post('/settings/subscribe-dismiss', 'SettingsController@subscribeDismiss'); +$app->get('settings/connection_info', 'SettingsController@getConnectionInfo'); +$app->post('settings/add_new_sender_email', 'SettingsController@addNewSenderEmail'); +$app->post('settings/remove_sender_email', 'SettingsController@removeSenderEmail'); + + +$app->get('settings/notification-settings', 'SettingsController@getNotificationSettings'); +$app->post('settings/notification-settings', 'SettingsController@saveNotificationSettings'); +$app->get('settings/notification-channels', 'SettingsController@getNotificationChannels'); +$app->post('settings/notification-channels/toggle', 'SettingsController@toggleNotificationChannel'); +$app->post('settings/gmail_auth_url', 'SettingsController@getGmailAuthUrl'); +$app->post('settings/outlook_auth_url', 'SettingsController@getOutlookAuthUrl'); + +/* + * Telegram Routes + */ +$app->post('settings/telegram/issue-pin-code', 'TelegramController@issuePinCode'); +$app->post('settings/telegram/confirm', 'TelegramController@confirmConnection'); +$app->get('settings/telegram/info', 'TelegramController@getTelegramConnectionInfo'); +$app->post('settings/telegram/send-test', 'TelegramController@sendTestMessage'); +$app->post('settings/telegram/disconnect', 'TelegramController@disconnect'); + +/* + * Slack Routes + */ +$app->post('settings/slack/register', 'SlackController@registerSite'); +$app->post('settings/slack/send-test', 'SlackController@sendTestMessage'); +$app->post('settings/slack/disconnect', 'SlackController@disconnect'); + +/* + * Discord Routes + */ +$app->post('settings/discord/register', 'DiscordController@registerSite'); +$app->post('settings/discord/send-test', 'DiscordController@sendTestMessage'); +$app->post('settings/discord/disconnect', 'DiscordController@disconnect'); + +/* + * Pushover Routes + */ +$app->post('settings/pushover/register', 'PushoverController@registerSite'); +$app->post('settings/pushover/send-test', 'PushoverController@sendTestMessage'); +$app->post('settings/pushover/disconnect', 'PushoverController@disconnect'); + + + +$app->get('/logs', 'LoggerController@get'); +$app->get('/logs/show', 'LoggerController@show'); +$app->post('/logs/retry', 'LoggerController@retry'); +$app->post('/logs/retry-bulk', 'LoggerController@retryBulk'); +$app->post('/logs/delete', 'LoggerController@delete'); + + +$app->post('install_plugin', 'SettingsController@installPlugin'); +$app->get('docs', 'DashboardController@getDocs'); diff --git a/wp-content/plugins/fluent-smtp/app/Models/Logger.php b/wp-content/plugins/fluent-smtp/app/Models/Logger.php new file mode 100644 index 0000000..4bef97d --- /dev/null +++ b/wp-content/plugins/fluent-smtp/app/Models/Logger.php @@ -0,0 +1,534 @@ +table = $this->db->prefix . FLUENT_MAIL_DB_PREFIX . 'email_logs'; + } + + public function get($data) + { + $db = $this->getDb(); + $page = isset($data['page']) ? (int)$data['page'] : 1; + $perPage = isset($data['per_page']) ? (int)$data['per_page'] : 15; + $offset = ($page - 1) * $perPage; + + $query = $db->table(FLUENT_MAIL_DB_PREFIX . 'email_logs') + ->limit($perPage) + ->offset($offset) + ->orderBy('id', 'DESC'); + + if (!empty($data['status'])) { + $query->where('status', sanitize_text_field($data['status'])); + } + + if (!empty($data['date_range']) && is_array($data['date_range']) && count($data['date_range']) == 2) { + $dateRange = $data['date_range']; + $from = $dateRange[0] . ' 00:00:01'; + $to = $dateRange[1] . ' 23:59:59'; + $query->whereBetween('created_at', $from, $to); + } + + if (!empty($data['search'])) { + $search = trim(sanitize_text_field($data['search'])); + $query->where(function ($q) use ($search) { + $searchColumns = $this->searchables; + + $columnSearch = false; + if (strpos($search, ':')) { + $searchArray = explode(':', $search); + $column = array_shift($searchArray); + if (in_array($column, $this->fillables)) { + $columnSearch = true; + $q->where($column, 'LIKE', '%' . trim(implode(':', $searchArray)) . '%'); + } + } + + if (!$columnSearch) { + $firstColumn = array_shift($searchColumns); + $q->where($firstColumn, 'LIKE', '%' . $search . '%'); + foreach ($searchColumns as $column) { + $q->orWhere($column, 'LIKE', '%' . $search . '%'); + } + } + + }); + } + + $result = $query->paginate(); + $result['data'] = $this->formatResult($result['data']); + + return $result; + } + + protected function buildWhere($data) + { + $where = []; + + if (isset($data['filter_by_value'])) { + $where[$data['filter_by']] = $data['filter_by_value']; + } + + if (isset($data['query'])) { + foreach ($this->searchables as $column) { + if (isset($where[$column])) { + $where[$column] .= '|' . $data['query']; + } else { + $where[$column] = $data['query']; + } + } + } + + $args = [1]; + $andWhere = $orWhere = ''; + $whereClause = "WHERE 1 = '%d'"; + + foreach ($where as $key => $value) { + if (in_array($key, ['status', 'created_at'])) { + if ($key == 'created_at') { + if (is_array($value)) { + $args[] = $value[0]; + $args[] = $value[1]; + } else { + $args[] = $value; + $args[] = $value; + } + $andWhere .= " AND `{$key}` >= '%s' AND `{$key}` < '%s' + INTERVAL 1 DAY"; + } else { + $args[] = $value; + $andWhere .= " AND `{$key}` = '%s'"; + } + } else { + if (strpos($value, '|')) { + $nestedOr = ''; + $values = explode('|', $value); + foreach ($values as $itemValue) { + $args[] = '%' . $this->db->esc_like($itemValue) . '%'; + $nestedOr .= " OR `{$key}` LIKE '%s'"; + } + $orWhere .= ' OR (' . trim($nestedOr, 'OR ') . ')'; + } else { + $args[] = '%' . $this->db->esc_like($value) . '%'; + $orWhere .= " OR `{$key}` LIKE '%s'"; + } + } + } + + if ($orWhere) { + $orWhere = 'AND (' . trim($orWhere, 'OR ') . ')'; + } + + $whereClause = implode(' ', [$whereClause, trim($andWhere), $orWhere]); + + return [$whereClause, $args]; + } + + protected function formatResult($result) + { + $result = is_array($result) ? $result : func_get_args(); + foreach ($result as $key => $row) { + $result[$key] = $this->maybeUnserialize((array)$row); + $result[$key]['id'] = (int)$result[$key]['id']; + $result[$key]['retries'] = (int)$result[$key]['retries']; + $result[$key]['from'] = htmlspecialchars($result[$key]['from']); + $result[$key]['subject'] = wp_kses_post( + wp_unslash($result[$key]['subject']) + ); + } + + return $result; + } + + protected function maybeUnserialize(array $data) + { + foreach ($data as $key => $value) { + if ($this->isUnserializable($key)) { + $data[$key] = $this->unserialize($value); + } + } + + return $data; + } + + protected function isUnserializable($key) + { + $allowedFields = [ + 'to', + 'headers', + 'attachments', + 'response', + 'extra' + ]; + + return in_array($key, $allowedFields); + } + + protected function unserialize($data) + { + if (is_serialized($data)) { + if (preg_match('/(^|;)O:[0-9]+:/', $data)) { + return $data; + } + return unserialize(trim($data), ['allowed_classes' => false]); + } + + return $data; + } + + protected function formatHeaders($headers) + { + foreach ((array)$headers as $key => $header) { + if (is_array($header)) { + $header = $this->formatHeaders($header); + } else { + $header = htmlspecialchars($header); + } + + $headers[$key] = $header; + } + + return $headers; + } + + public function add($data) + { + try { + $data = array_merge($data, [ + 'created_at' => current_time('mysql') + ]); + + return $this->getDb()->table(FLUENT_MAIL_DB_PREFIX . 'email_logs') + ->insert($data); + + } catch (Exception $e) { + return $e; + } + } + + public function delete(array $id) + { + if ($id && $id[0] == 'all') { + return $this->db->query("TRUNCATE TABLE {$this->table}"); + } + + $ids = array_filter($id, 'intval'); + + if ($ids) { + return $this->getDb()->table(FLUENT_MAIL_DB_PREFIX . 'email_logs') + ->whereIn('id', $ids) + ->delete(); + } + + return false; + } + + public function navigate($data) + { + $filterBy = Arr::get($data, 'filter_by'); + foreach (['date', 'daterange', 'datetime', 'datetimerange'] as $field) { + if ($filterBy == $field) { + $data['filter_by'] = 'created_at'; + } + } + + $id = $data['id']; + + $dir = isset($data['dir']) ? $data['dir'] : null; + + list($where, $args) = $this->buildWhere($data); + + $args = array_merge($args, [$id]); + + $sqlNext = "SELECT * FROM {$this->table} {$where} AND `id` > '%d' ORDER BY id LIMIT 2"; + $sqlPrev = "SELECT * FROM {$this->table} {$where} AND `id` < '%d' ORDER BY id DESC LIMIT 2"; + + if ($dir == 'next') { + $query = $this->db->prepare($sqlNext, $args); + } else if ($dir == 'prev') { + $query = $this->db->prepare($sqlPrev, $args); + } else { + foreach (['next' => $sqlNext, 'prev' => $sqlPrev] as $key => $sql) { + + $keyResult = $this->db->get_results( + $this->db->prepare($sql, $args) + ); + + $result[$key] = $this->formatResult($keyResult); + } + + return $result; + } + + $result = $this->db->get_results($query); + + if (count($result) > 1) { + $next = true; + $prev = true; + } else { + if ($dir == 'next') { + $next = false; + $prev = true; + } else { + $next = true; + $prev = false; + } + } + + return [ + 'log' => $result ? $this->formatResult($result[0])[0] : null, + 'next' => $next, + 'prev' => $prev + ]; + } + + public function find($id) + { + + $row = $this->getDb()->table(FLUENT_MAIL_DB_PREFIX . 'email_logs') + ->where('id', $id) + ->first(); + + $row->extra = $this->unserialize($row->extra); + + $row->response = $this->unserialize($row->response); + + return (array)$row; + } + + public function resendEmailFromLog($id, $type = 'retry') + { + $email = $this->find($id); + + $email['to'] = $this->unserialize($email['to']); + $email['headers'] = $this->unserialize($email['headers']); + $email['attachments'] = $this->unserialize($email['attachments']); + $email['extra'] = $this->unserialize($email['extra']); + + // Convert PHPMailer attachment format to wp_mail format + $wpMailAttachments = []; + if (!empty($email['attachments']) && is_array($email['attachments'])) { + foreach ($email['attachments'] as $attachment) { + if (is_array($attachment)) { + // PHPMailer format: [path, filename, name, encoding, type, isString, disposition, cid] + if (isset($attachment[0]) && is_string($attachment[0])) { + $filePath = $attachment[0]; + if (file_exists($filePath) && is_readable($filePath)) { + $wpMailAttachments[] = $filePath; + } + } + } elseif (is_string($attachment)) { + if (file_exists($attachment) && is_readable($attachment)) { + $wpMailAttachments[] = $attachment; + } + } + } + } + + $headers = []; + + foreach ($email['headers'] as $key => $value) { + + if($key == 'content-type' && $value == 'multipart/alternative') { + $value = 'text/html'; + } + + if (is_array($value)) { + $values = []; + $value = array_filter($value); + foreach ($value as $v) { + if (is_array($v) && isset($v['email'])) { + $v = $v['email']; + } + $values[] = $v; + } + if ($values) { + $headers[] = "{$key}: " . implode(';', $values); + } + } else { + if ($value) { + $headers[] = "{$key}: $value"; + } + } + } + + $headers = array_merge($headers, [ + 'From: ' . $email['from'] + ]); + + $to = []; + foreach ($email['to'] as $recipient) { + if (isset($recipient['name'])) { + $to[] = $recipient['name'] . ' <' . $recipient['email'] . '>'; + } else { + $to[] = $recipient['email']; + } + } + + try { + if (!defined('FLUENTMAIL_LOG_OFF')) { + define('FLUENTMAIL_LOG_OFF', true); + } + + $result = wp_mail( + $to, + $email['subject'], + $email['body'], + $headers, + $wpMailAttachments // Use the converted attachment format + ); + + $updateData = [ + 'status' => 'sent', + 'updated_at' => current_time('mysql'), + ]; + + if (!$result && $type == 'check_realtime' && $email['status'] == 'failed') { + $updateData['status'] = 'failed'; + } + + if ($type == 'resend') { + $updateData['resent_count'] = intval($email['resent_count']) + 1; + } else { + $updateData['retries'] = intval($email['retries']) + 1; + } + + if ($this->updateLog($updateData, ['id' => $id])) { + $email = $this->find($id); + $email['to'] = $this->unserialize($email['to']); + $email['headers'] = $this->unserialize($email['headers']); + $email['attachments'] = $this->unserialize($email['attachments']); + $email['extra'] = $this->unserialize($email['extra']); + return $email; + } + } catch (\PHPMailer\PHPMailer\Exception $e) { + throw $e; + } + } + + public function updateLog($data, $where) + { + return $this->db->update($this->table, $data, $where); + } + + public function getStats() + { + $succeeded = $this->db->get_var("select COUNT(id) from {$this->table} where status='sent'"); + $failed = $this->db->get_var("select COUNT(id) from {$this->table} where status='failed'"); + + return [ + 'sent' => $succeeded, + 'failed' => $failed + ]; + } + + public function deleteLogsOlderThan($days) + { + try { + + $date = gmdate('Y-m-d H:i:s', current_time('timestamp') - $days * DAY_IN_SECONDS); + $query = $this->db->prepare("DELETE FROM {$this->table} WHERE `created_at` < %s", $date); + return $this->db->query($query); + + } catch (Exception $e) { + if (wp_get_environment_type() != 'production') { + error_log('Message: ' . $e->getMessage()); + } + } + } + + public function getTotalCountStat($status, $startDate, $endDate = false) + { + if ($endDate) { + $query = $this->db->prepare( + "SELECT COUNT(*) + FROM {$this->table} + WHERE status = %s + AND created_at >= %s + AND created_at <= %s", + $status, + $startDate, + $endDate + ); + } else { + $query = $this->db->prepare( + "SELECT COUNT(*) + FROM {$this->table} + WHERE status = %s + AND created_at >= %s", + $status, + $startDate + ); + } + + return (int)$this->db->get_var($query); + } + + public function getSubjectCountStat($status, $startDate, $endDate) + { + $query = $this->db->prepare( + "SELECT COUNT(DISTINCT(subject)) + FROM {$this->table} + WHERE status = %s + AND created_at >= %s + AND created_at <= %s", + $status, + $startDate, + $endDate + ); + + return (int)$this->db->get_var($query); + } + + public function getSubjectStat($status, $statDate, $endDate, $limit = 5) + { + $query = $this->db->prepare( + "SELECT subject, + COUNT(DISTINCT id) AS emails_sent + FROM {$this->table} + WHERE created_at >= %s + AND created_at <= %s + AND status = %s + GROUP BY subject + ORDER BY emails_sent DESC + LIMIT {$limit}", + $statDate, + $endDate, + $status + ); + + return $this->db->get_results($query, ARRAY_A); + } + +} diff --git a/wp-content/plugins/fluent-smtp/app/Models/Model.php b/wp-content/plugins/fluent-smtp/app/Models/Model.php new file mode 100644 index 0000000..2e4e511 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/app/Models/Model.php @@ -0,0 +1,32 @@ +app = fluentMail(); + $this->db = $GLOBALS['wpdb']; + } + + public function getTable() + { + return $this->table; + } + + public function __call($method, $params) + { + return call_user_func_array([$this->db, $method], $params); + } + + public function getDb() + { + return fluentMailDb(); + } +} diff --git a/wp-content/plugins/fluent-smtp/app/Models/Settings.php b/wp-content/plugins/fluent-smtp/app/Models/Settings.php new file mode 100644 index 0000000..07d852f --- /dev/null +++ b/wp-content/plugins/fluent-smtp/app/Models/Settings.php @@ -0,0 +1,268 @@ +get(); + } + + public function store($inputs) + { + $settings = $this->getSettings(); + $mappings = $this->getMappings($settings); + $connections = $this->getConnections($settings); + $email = Arr::get($inputs, 'connection.sender_email'); + + $key = $inputs['connection_key']; + + if (isset($connections[$key])) { + $mappings = array_filter($mappings, function ($mappingKey) use ($key) { + return $mappingKey != $key; + }); + unset($connections[$key]); + } + + $primaryEmails = []; + foreach ($connections as $connection) { + $primaryEmails[] = $connection['provider_settings']['sender_email']; + } + + $uniqueKey = $this->generateUniqueKey($email); + + $extraMappings = $inputs['valid_senders']; + + foreach ($extraMappings as $emailIndex => $email) { + if (in_array($email, $primaryEmails)) { + unset($extraMappings[$emailIndex]); + } + } + + $extraMappings[] = $email; + $extraMappings = array_unique($extraMappings); + $extraMappings = array_fill_keys($extraMappings, $uniqueKey); + + $mappings = array_merge($mappings, $extraMappings); + + $providers = fluentMail(Manager::class)->getConfig('providers'); + + $title = $providers[$inputs['connection']['provider']]['title']; + + $connections[$uniqueKey] = [ + 'title' => $title, + 'provider_settings' => $inputs['connection'] + ]; + + $settings['mappings'] = $mappings; + + $settings['connections'] = $connections; + + if ($settings['mappings'] && $settings['connections']) { + $validMappings = array_keys(Arr::get($settings, 'connections', [])); + + $settings['mappings'] = array_filter($settings['mappings'], function ($key) use ($validMappings) { + return in_array($key, $validMappings); + }); + } + + $misc = $this->getMisc(); + + if (!$misc) { + $misc = [ + 'log_emails' => 'yes', + 'log_saved_interval_days' => '14', + 'disable_fluentcrm_logs' => 'no', + 'default_connection' => '' + ]; + } + + if (empty($misc['default_connection']) || $misc['default_connection'] == $key) { + $misc['default_connection'] = $uniqueKey; + $settings['misc'] = $misc; + } + + fluentMailSetSettings($settings); + + return $settings; + } + + public function generateUniqueKey($email) + { + return md5($email); + } + + public function saveGlobalSettings($data) + { + return fluentMailSetSettings($data); + } + + public function delete($key) + { + $settings = $this->getSettings(); + + + $mappings = $settings['mappings']; + $connections = $settings['connections']; + + unset($connections[$key]); + + foreach ($mappings as $mapKey => $mapValue) { + if ($mapValue == $key) { + unset($mappings[$mapKey]); + } + } + + $settings['mappings'] = $mappings; + $settings['connections'] = $connections; + + if (Arr::get($settings, 'misc.default_connection') == $key) { + $default = Arr::get($settings, 'mappings', []); + $default = reset($default); + Arr::set($settings, 'misc.default_connection', $default ?: ''); + } + + if (Arr::get($settings, 'misc.fallback_connection') == $key) { + Arr::set($settings, 'misc.fallback_connection', ''); + } + + fluentMailSetSettings($settings); + + return $settings; + } + + public function getDefaults() + { + $url = str_replace( + ['http://', 'http://www.', 'www.'], + '', + get_bloginfo('wpurl') + ); + + return [ + 'sender_name' => $url, + 'sender_email' => get_option('admin_email') + ]; + } + + public function getVerifiedEmails() + { + $optionName = FLUENTMAIL . '-ses-verified-emails'; + + return get_option($optionName, []); + } + + public function saveVerifiedEmails($verifiedEmails) + { + $optionName = FLUENTMAIL . '-ses-verified-emails'; + $emails = get_option($optionName, []); + update_option($optionName, array_unique(array_merge( + $emails, $verifiedEmails + ))); + } + + public function getConnections($settings = null) + { + $settings = $settings ?: $this->getSettings(); + + return Arr::get($settings, 'connections', []); + } + + public function getMappings($settings = null) + { + $settings = $settings ?: $this->getSettings(); + + return Arr::get($settings, 'mappings', []); + } + + public function getMisc($settings = null) + { + $settings = $settings ?: $this->getSettings(); + + return Arr::get($settings, 'misc', []); + } + + public function getConnection($email) + { + $settings = $this->getSettings(); + $mappings = $this->getMappings($settings); + $connections = $this->getConnections($settings); + + if (isset($mappings[$email])) { + if (isset($connections[$mappings[$email]])) { + return $connections[$mappings[$email]]; + } + } + + return []; + } + + public function updateMiscSettings($misc) + { + $settings = $this->get(); + $settings['misc'] = $misc; + $this->saveGlobalSettings($settings); + } + + public function updateConnection($fromEmail, $connection) + { + $key = $this->generateUniqueKey($fromEmail); + $settings = $this->getSettings(); + $settings['connections'][$key]['provider_settings'] = $connection; + $this->saveGlobalSettings($settings); + } + + public function notificationSettings() + { + $defaults = [ + 'enabled' => 'no', + 'notify_email' => '{site_admin}', + 'notify_days' => ['Mon'], + 'active_channel' => [], + 'telegram' => [ + 'status' => 'no', + 'token' => '' + ], + 'slack' => [ + 'status' => 'no', + 'token' => '', + 'webhook_url' => '' + ], + 'discord' => [ + 'status' => 'no', + 'channel_name' => '', + 'webhook_url' => '' + ], + ]; + + $settings = get_option('_fluent_smtp_notify_settings', []); + + $settings = wp_parse_args($settings, $defaults); + + if (!is_array($settings['active_channel'])) { + $settings['active_channel'] = array_filter([$settings['active_channel']]); + } + + return $settings; + } + + public function getAvailableNotificationChannels() + { + $manager = new \FluentMail\App\Services\Notification\Manager(); + return $manager->getAllChannels(); + } +} diff --git a/wp-content/plugins/fluent-smtp/app/Models/Traits/SendTestEmailTrait.php b/wp-content/plugins/fluent-smtp/app/Models/Traits/SendTestEmailTrait.php new file mode 100644 index 0000000..0d33077 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/app/Models/Traits/SendTestEmailTrait.php @@ -0,0 +1,35 @@ +make('admin.email_html'); + $subject .= ' - HTML Version'; + } else { + $headers[] = 'Content-Type: text/plain; charset=UTF-8'; + $body = (string)fluentMail('view')->make('admin.email_text'); + $subject .= ' - Text Version'; + } + + if (!empty($data['from'])) { + $headers[] = 'From: ' . $data['from']; + } + + if (!defined('FLUENTMAIL_TEST_EMAIL')) { + define('FLUENTMAIL_TEST_EMAIL', true); + } + + return wp_mail($to, $subject, $body, $headers); + } +} diff --git a/wp-content/plugins/fluent-smtp/app/Services/Converter.php b/wp-content/plugins/fluent-smtp/app/Services/Converter.php new file mode 100644 index 0000000..3b457ee --- /dev/null +++ b/wp-content/plugins/fluent-smtp/app/Services/Converter.php @@ -0,0 +1,484 @@ +maybeWPMailSmtp(); + if($wpMailSmtp) { + return $wpMailSmtp; + } + + $easySMTP = $this->maybeEasySmtp(); + if($easySMTP) { + return $easySMTP; + } + + return false; + } + + private function maybeWPMailSmtp() + { + $wpMailSettings = get_option('wp_mail_smtp'); + if (!$wpMailSettings) { + return false; + } + + $mailSettings = Arr::get($wpMailSettings, 'mail', []); + + $commonSettings = [ + 'sender_name' => $this->maybeFromWPMailDefined('mail', 'from_name', Arr::get($mailSettings, 'from_name')), + 'sender_email' => $this->maybeFromWPMailDefined('mail', 'from_email', Arr::get($mailSettings, 'from_email')), + 'force_from_name' => Arr::get($mailSettings, 'from_name_force') == 1 ? 'yes' : 'no', + 'force_from_email' => Arr::get($mailSettings, 'from_email_force') == 1 ? 'yes' : 'no', + 'return_path' => Arr::get($mailSettings, 'return_path') == 1 ? 'yes' : 'no' + ]; + + // Let's try the SMTP First + $mailer = Arr::get($mailSettings, 'mailer'); + + if ($mailer == 'smtp') { + $smtp = Arr::get($wpMailSettings, 'smtp', []); + $auth = $this->maybeFromWPMailDefined('smtp', 'auth', Arr::get($smtp, 'auth')) == 1 ? 'yes' : 'no'; + + $userName = $this->maybeFromWPMailDefined('smtp', 'user', Arr::get($smtp, 'user')); + $password = $this->maybeFromWPMailDefined('smtp', 'pass', ''); + + if ($auth == 'yes') { + if (!$password) { + $password = $this->wpMailPassDecode(Arr::get($smtp, 'pass')); + } + } + + $localSettings = [ + 'host' => $this->maybeFromWPMailDefined('smtp', 'host', Arr::get($smtp, 'host')), + 'port' => $this->maybeFromWPMailDefined('smtp', 'port', Arr::get($smtp, 'port')), + 'auth' => $auth, + 'username' => $userName, + 'password' => $password, + 'auto_tls' => $this->maybeFromWPMailDefined('smtp', 'auto_tls', Arr::get($smtp, 'auto_tls')) == 1 ? 'yes' : 'no', + 'encryption' => $this->maybeFromWPMailDefined('smtp', 'encryption', Arr::get($smtp, 'encryption', 'none')), + 'key_store' => 'db', + 'provider' => 'smtp' + ]; + + $commonSettings = wp_parse_args($commonSettings, $localSettings); + } else if ($mailer == 'mailgun') { + $mailgun = Arr::get($wpMailSettings, 'mailgun', []); + $localSettings = [ + 'api_key' => $this->maybeFromWPMailDefined('mailgun', 'api_key', Arr::get($mailgun, 'api_key')), + 'domain_name' => $this->maybeFromWPMailDefined('mailgun', 'domain', Arr::get($mailgun, 'domain')), + 'key_store' => 'db', + 'region' => strtolower($this->maybeFromWPMailDefined('mailgun', 'region', Arr::get($mailgun, 'region'))), + 'provider' => 'mailgun' + ]; + $commonSettings = wp_parse_args($commonSettings, $localSettings); + unset($commonSettings['force_from_email']); + } else if ($mailer == 'sendinblue' || $mailer == 'sendgrid' || $mailer == 'pepipostapi' || $mailer == 'smtp2go') { + $local = Arr::get($wpMailSettings, $mailer, []); + $localSettings = [ + 'api_key' => $this->maybeFromWPMailDefined($mailer, 'api_key', Arr::get($local, 'api_key')), + 'key_store' => 'db', + 'provider' => ($mailer == 'pepipostapi') ? 'pepipost' : $mailer + ]; + $commonSettings = wp_parse_args($commonSettings, $localSettings); + unset($commonSettings['force_from_email']); + } else if ($mailer == 'amazonses') { + $local = Arr::get($wpMailSettings, $mailer, []); + $localSettings = [ + 'access_key' => $this->maybeFromWPMailDefined($mailer, 'client_id', Arr::get($local, 'client_id')), + 'secret_key' => $this->maybeFromWPMailDefined($mailer, 'client_secret', Arr::get($local, 'client_secret')), + 'region' => $this->maybeFromWPMailDefined($mailer, 'region', Arr::get($local, 'region')), + 'key_store' => 'db', + 'provider' => 'ses' + ]; + + $commonSettings = wp_parse_args($commonSettings, $localSettings); + } else if ($mailer == 'mail') { + $commonSettings['provider'] = 'default'; + } else { + return false; + } + + return [ + 'title' => __('Import data from your current plugin (WP Mail SMTP)', 'fluent-smtp'), + 'subtitle' => __('We have detected other SMTP plugin\'s settings available on your site. Click bellow to pre-populate the values', 'fluent-smtp'), + 'settings' => $commonSettings, + 'button_text' => __('Import From WP Mail SMTP', 'fluent-smtp') + ]; + } + + private function wpMailPassDecode($encrypted) + { + if (apply_filters('wp_mail_smtp_helpers_crypto_stop', false)) { + return $encrypted; + } + + if (!function_exists('\mb_strlen') || !function_exists('\mb_substr') || !function_exists('\sodium_crypto_secretbox_open')) { + return $encrypted; + } + + // Unpack base64 message. + $decoded = base64_decode($encrypted); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_decode + + if (false === $decoded) { + return $encrypted; + } + + if (mb_strlen($decoded, '8bit') < (SODIUM_CRYPTO_SECRETBOX_NONCEBYTES + SODIUM_CRYPTO_SECRETBOX_MACBYTES)) { // phpcs:ignore + return $encrypted; + } + + // Pull nonce and ciphertext out of unpacked message. + $nonce = mb_substr($decoded, 0, SODIUM_CRYPTO_SECRETBOX_NONCEBYTES, '8bit'); // phpcs:ignore + $ciphertext = mb_substr($decoded, SODIUM_CRYPTO_SECRETBOX_NONCEBYTES, null, '8bit'); // phpcs:ignore + + $secret_key = $this->getWPMailSecretKey(); + + if (empty($secret_key)) { + return $encrypted; + } + + // Decrypt it. + $message = sodium_crypto_secretbox_open( // phpcs:ignore + $ciphertext, + $nonce, + $secret_key + ); + + // Check for decryption failures. + if (false === $message) { + return $encrypted; + } + + return $message; + } + + private function getWPMailSecretKey() + { + if (defined('WPMS_CRYPTO_KEY')) { + return WPMS_CRYPTO_KEY; + } + + $secret_key = get_option('wp_mail_smtp_mail_key'); + $secret_key = apply_filters('wp_mail_smtp_helpers_crypto_get_secret_key', $secret_key); + + // If we already have the secret, send it back. + if (false !== $secret_key) { + $secret_key = base64_decode($secret_key); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_decode + } + + return $secret_key; + } + + private function maybeFromWPMailDefined($group, $key, $value) + { + + if (!defined('WPMS_ON') || !WPMS_ON) { + return $value; + } + + // Just to feel safe. + $group = sanitize_key($group); + $key = sanitize_key($key); + $return = false; + + switch ($group) { + case 'mail': + switch ($key) { + case 'from_name': + if (defined('WPMS_MAIL_FROM_NAME') && WPMS_MAIL_FROM_NAME) { + $value = WPMS_MAIL_FROM_NAME; + } + break; + case 'from_email': + if (defined('WPMS_MAIL_FROM') && WPMS_MAIL_FROM) { + $value = WPMS_MAIL_FROM; + } + break; + case 'mailer': + if (defined('WPMS_MAILER') && WPMS_MAILER) { + $value = WPMS_MAILER; + } + break; + case 'return_path': + if (defined('WPMS_SET_RETURN_PATH') && WPMS_SET_RETURN_PATH) { + $value = WPMS_SET_RETURN_PATH; + } + break; + case 'from_name_force': + if (defined('WPMS_MAIL_FROM_NAME_FORCE') && WPMS_MAIL_FROM_NAME_FORCE) { + $value = WPMS_MAIL_FROM_NAME_FORCE; + } + break; + case 'from_email_force': + if (defined('WPMS_MAIL_FROM_FORCE') && WPMS_MAIL_FROM_FORCE) { + $value = WPMS_MAIL_FROM_FORCE; + } + break; + } + + break; + + case 'smtp': + switch ($key) { + case 'host': + if (defined('WPMS_SMTP_HOST') && WPMS_SMTP_HOST) { + $value = WPMS_SMTP_HOST; + } + break; + case 'port': + if (defined('WPMS_SMTP_PORT') && WPMS_SMTP_PORT) { + $value = WPMS_SMTP_PORT; + } + break; + case 'encryption': + if (defined('WPMS_SSL') && WPMS_SSL) { + $value = WPMS_SSL; + } + break; + case 'auth': + if (defined('WPMS_SMTP_AUTH') && WPMS_SMTP_AUTH) { + $value = WPMS_SMTP_AUTH; + } + break; + case 'autotls': + if (defined('WPMS_SMTP_AUTOTLS') && WPMS_SMTP_AUTOTLS) { + $value = WPMS_SMTP_AUTOTLS; + } + break; + case 'user': + if (defined('WPMS_SMTP_USER') && WPMS_SMTP_USER) { + $value = WPMS_SMTP_USER; + } + break; + case 'pass': + if (defined('WPMS_SMTP_PASS') && WPMS_SMTP_PASS) { + $value = WPMS_SMTP_PASS; + } + break; + } + + break; + + case 'amazonses': + switch ($key) { + case 'client_id': + if (defined('WPMS_AMAZONSES_CLIENT_ID') && WPMS_AMAZONSES_CLIENT_ID) { + $value = WPMS_AMAZONSES_CLIENT_ID; + } + break; + case 'client_secret': + if (defined('WPMS_AMAZONSES_CLIENT_SECRET') && WPMS_AMAZONSES_CLIENT_SECRET) { + $value = WPMS_AMAZONSES_CLIENT_SECRET; + } + break; + case 'region': + if (defined('WPMS_AMAZONSES_REGION') && WPMS_AMAZONSES_REGION) { + $value = WPMS_AMAZONSES_REGION; + } + break; + } + + break; + + case 'mailgun': + switch ($key) { + case 'api_key': + if (defined('WPMS_MAILGUN_API_KEY') && WPMS_MAILGUN_API_KEY) { + $value = WPMS_MAILGUN_API_KEY; + } + break; + case 'domain': + if (defined('WPMS_MAILGUN_DOMAIN') && WPMS_MAILGUN_DOMAIN) { + $value = WPMS_MAILGUN_DOMAIN; + } + break; + case 'region': + if (defined('WPMS_MAILGUN_REGION') && WPMS_MAILGUN_REGION) { + $value = WPMS_MAILGUN_REGION; + } + break; + } + + break; + + case 'sendgrid': + switch ($key) { + case 'api_key': + if (defined('WPMS_SENDGRID_API_KEY') && WPMS_SENDGRID_API_KEY) { + $value = WPMS_SENDGRID_API_KEY; + } + break; + case 'domain': + if (defined('WPMS_SENDGRID_DOMAIN') && WPMS_SENDGRID_DOMAIN) { + $value = WPMS_SENDGRID_DOMAIN; + } + break; + } + + break; + + case 'sendinblue': + switch ($key) { + case 'api_key': + if (defined('WPMS_SENDINBLUE_API_KEY') && WPMS_SENDINBLUE_API_KEY) { + $value = WPMS_SENDINBLUE_API_KEY; + } + break; + case 'domain': + if (defined('WPMS_SENDINBLUE_DOMAIN') && WPMS_SENDINBLUE_DOMAIN) { + $value = WPMS_SENDINBLUE_DOMAIN; + } + break; + } + break; + + case 'pepipostapi': + switch ($key) { + case 'api_key': + if (defined('WPMS_PEPIPOST_API_KEY') && WPMS_PEPIPOST_API_KEY) { + $value = WPMS_PEPIPOST_API_KEY; + } + break; + } + break; + + case 'elasticmail': + switch ($key) { + case 'api_key': + if (defined('FLUENTMAIL_ELASTICMAIL_API_KEY') && FLUENTMAIL_ELASTICMAIL_API_KEY) { + $value = FLUENTMAIL_ELASTICMAIL_API_KEY; + } + break; + } + + break; + } + + return $value; + } + + /* + * For EasySMTP + */ + private function maybeEasySmtp() + { + $settings = get_option('swpsmtp_options'); + + if (!$settings || !is_array($settings)) { + return false; + } + + $auth = 'no'; + if (Arr::get($settings, 'smtp_settings.autentication')) { + $auth = 'yes'; + } + + $commonSettings = [ + 'sender_name' => Arr::get($settings, 'from_name_field'), + 'sender_email' => Arr::get($settings, 'from_email_field'), + 'force_from_name' => Arr::get($settings, 'force_from_name_replace') == 1 ? 'yes' : 'no', + 'force_from_email' => 'yes', + 'return_path' => 'yes', + 'host' => Arr::get($settings, 'smtp_settings.host'), + 'port' => Arr::get($settings, 'smtp_settings.port'), + 'auth' => $auth, + 'username' => Arr::get($settings, 'smtp_settings.username'), + 'password' => $this->decryptEasySMTPPass(Arr::get($settings, 'smtp_settings.password')), + 'auto_tls' => Arr::get($settings, 'smtp_settings.password') == 1 ? 'yes' : 'no', + 'encryption' => Arr::get($settings, 'smtp_settings.type_encryption'), + 'key_store' => 'db', + 'provider' => 'smtp' + ]; + + return [ + 'title' => __('Import data from your current plugin (Easy WP SMTP)', 'fluent-smtp'), + 'subtitle' => __('We have detected other SMTP plugin\'s settings available on your site. Click bellow to pre-populate the values', 'fluent-smtp'), + 'driver' => 'smtp', + 'settings' => $commonSettings, + 'button_text' => __('Import From Easy WP SMTP', 'fluent-smtp') + ]; + + } + + private function decryptEasySMTPPass($temp_password) + { + if (!$temp_password) { + return $temp_password; + } + + try { + if (get_option('swpsmtp_pass_encrypted')) { + $key = get_option('swpsmtp_enc_key', false); + if (empty($key)) { + $key = wp_salt(); + } + return $this->decryptEasypassword($temp_password, $key); + } + } catch (\Exception $e) { + return $temp_password; + } + + $password = ''; + $decoded_pass = base64_decode($temp_password); //phpcs:ignore + /* no additional checks for servers that aren't configured with mbstring enabled */ + if (!function_exists('mb_detect_encoding')) { + return $decoded_pass; + } + /* end of mbstring check */ + if (base64_encode($decoded_pass) === $temp_password) { //phpcs:ignore + //it might be encoded + if (false === mb_detect_encoding($decoded_pass)) { //could not find character encoding. + $password = $temp_password; + } else { + $password = base64_decode($temp_password); //phpcs:ignore + } + } else { //not encoded + $password = $temp_password; + } + return stripslashes($password); + + } + + private function decryptEasyPassword($in, $key, $fmt = 1) + { + + if (!function_exists('\openssl_cipher_iv_length') || !function_exists('\openssl_decrypt') || !function_exists('\openssl_digest')) { + return $in; + } + + $raw = base64_decode($in); + + $iv_num_bytes = openssl_cipher_iv_length('aes-256-ctr'); + + // and do an integrity check on the size. + if (strlen($raw) < $iv_num_bytes) { + return $in; + } + + // Extract the initialisation vector and encrypted data + $iv = substr($raw, 0, $iv_num_bytes); + $raw = substr($raw, $iv_num_bytes); + + $hasAlgo = 'sha256'; + // Hash the key + $keyhash = openssl_digest($key, $hasAlgo, true); + + // and decrypt. + $opts = 1; + $res = openssl_decrypt($raw, 'aes-256-ctr', $keyhash, $opts, $iv); + + if ($res === false) { + return $in; + } + + return $res; + + } + +} diff --git a/wp-content/plugins/fluent-smtp/app/Services/DB/AliasFacade.php b/wp-content/plugins/fluent-smtp/app/Services/DB/AliasFacade.php new file mode 100644 index 0000000..1f709a8 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/app/Services/DB/AliasFacade.php @@ -0,0 +1,43 @@ +container = $container; + + $this->wpdb = $wpdb; + + $this->setAdapter()->setAdapterConfig($config)->connect(); + + // Create event dependency + $this->eventHandler = $this->container->build('\\FluentMail\\App\\Services\\DB\\EventHandler'); + + if ($alias) { + $this->createAlias($alias); + } + } + + /** + * Create an easily accessible query builder alias + * + * @param $alias + */ + public function createAlias($alias) + { + class_alias('FluentMail\\App\\Services\\DB\\AliasFacade', $alias); + + $builder = $this->container->build('\\FluentMail\\App\\Services\\DB\\QueryBuilder\\QueryBuilderHandler', array($this)); + + AliasFacade::setQueryBuilderInstance($builder); + } + + /** + * Returns an instance of Query Builder + */ + public function getQueryBuilder() + { + return $this->container->build('\\FluentMail\\App\\Services\\DB\\QueryBuilder\\QueryBuilderHandler', array($this)); + } + + + /** + * Create the connection adapter + */ + protected function connect() + { + $this->setDbInstance($this->wpdb); + + // Preserve the first database connection with a static property + if (! static::$storedConnection) { + static::$storedConnection = $this; + } + } + + /** + * @param $db + * + * @return $this + */ + public function setDbInstance($db) + { + $this->dbInstance = $db; + + return $this; + } + + /** + * @return \wpdb + */ + public function getDbInstance() + { + return $this->dbInstance; + } + + /** + * @param $adapter + * + * @return $this + */ + public function setAdapter($adapter = 'mysql') + { + $this->adapter = $adapter; + + return $this; + } + + /** + * @return string + */ + public function getAdapter() + { + return $this->adapter; + } + + /** + * @param array $adapterConfig + * + * @return $this + */ + public function setAdapterConfig(array $adapterConfig) + { + $this->adapterConfig = $adapterConfig; + + return $this; + } + + /** + * @return array + */ + public function getAdapterConfig() + { + return $this->adapterConfig; + } + + /** + * @return \FluentMail\App\Services\DB\Viocon\Container + */ + public function getContainer() + { + return $this->container; + } + + /** + * @return EventHandler + */ + public function getEventHandler() + { + return $this->eventHandler; + } + + /** + * @return Connection + */ + public static function getStoredConnection() + { + return static::$storedConnection; + } +} diff --git a/wp-content/plugins/fluent-smtp/app/Services/DB/EventHandler.php b/wp-content/plugins/fluent-smtp/app/Services/DB/EventHandler.php new file mode 100644 index 0000000..8536ffe --- /dev/null +++ b/wp-content/plugins/fluent-smtp/app/Services/DB/EventHandler.php @@ -0,0 +1,99 @@ +events; + } + + /** + * @param $event + * @param $table + * + * @return callable|null + */ + public function getEvent($event, $table = ':any') + { + if ($table instanceof Raw) { + return null; + } + return isset($this->events[$table][$event]) ? $this->events[$table][$event] : null; + } + + /** + * @param $event + * @param string $table + * @param callable $action + * + * @return void + */ + public function registerEvent($event, $table, \Closure $action) + { + $table = $table ?: ':any'; + + $this->events[$table][$event] = $action; + } + + /** + * @param $event + * @param string $table + * + * @return void + */ + public function removeEvent($event, $table = ':any') + { + unset($this->events[$table][$event]); + } + + /** + * @param \FluentMail\App\Services\DB\src\QueryBuilder\QueryBuilderHandler $queryBuilder + * @param $event + * @return mixed + */ + public function fireEvents($queryBuilder, $event) + { + $originalArgs = func_get_args(); + $statements = $queryBuilder->getStatements(); + $tables = isset($statements['tables']) ? $statements['tables'] : array(); + + // Events added with :any will be fired in case of any table, + // we are adding :any as a fake table at the beginning. + array_unshift($tables, ':any'); + + // Fire all events + foreach ($tables as $table) { + // Fire before events for :any table + if ($action = $this->getEvent($event, $table)) { + // Make an event id, with event type and table + $eventId = $event . $table; + + // Fire event + $handlerParams = $originalArgs; + unset($handlerParams[1]); // we do not need $event + // Add to fired list + $this->firedEvents[] = $eventId; + $result = call_user_func_array($action, $handlerParams); + if (!is_null($result)) { + return $result; + }; + } + } + } +} diff --git a/wp-content/plugins/fluent-smtp/app/Services/DB/Exception.php b/wp-content/plugins/fluent-smtp/app/Services/DB/Exception.php new file mode 100644 index 0000000..80afdf4 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/app/Services/DB/Exception.php @@ -0,0 +1,6 @@ +connection = $connection; + $this->container = $this->connection->getContainer(); + } + + /** + * Build select query string and bindings + * + * @param $statements + * + * @return array + * @throws \FluentMail\App\Services\DB\Exception + */ + public function select($statements) + { + if (! array_key_exists('tables', $statements)) { + throw new Exception('No table specified.', 3); + } elseif (! array_key_exists('selects', $statements)) { + $statements['selects'][] = '*'; + } + + // From + $tables = $this->arrayStr($statements['tables'], ', '); + // Select + $selects = $this->arrayStr($statements['selects'], ', '); + + + // Wheres + list($whereCriteria, $whereBindings) = $this->buildCriteriaWithType($statements, 'wheres', 'WHERE'); + // Group bys + $groupBys = ''; + if (isset($statements['groupBys']) && $groupBys = $this->arrayStr($statements['groupBys'], ', ')) { + $groupBys = 'GROUP BY ' . $groupBys; + } + + // Order bys + $orderBys = ''; + if (isset($statements['orderBys']) && is_array($statements['orderBys'])) { + foreach ($statements['orderBys'] as $orderBy) { + $orderBys .= $this->wrapSanitizer($orderBy['field']) . ' ' . $orderBy['type'] . ', '; + } + + if ($orderBys = trim($orderBys, ', ')) { + $orderBys = 'ORDER BY ' . $orderBys; + } + } + + // Limit and offset + $limit = isset($statements['limit']) ? 'LIMIT ' . $statements['limit'] : ''; + $offset = isset($statements['offset']) ? 'OFFSET ' . $statements['offset'] : ''; + + // Having + list($havingCriteria, $havingBindings) = $this->buildCriteriaWithType($statements, 'havings', 'HAVING'); + + // Joins + $joinString = $this->buildJoin($statements); + + $sqlArray = array( + 'SELECT' . (isset($statements['distinct']) ? ' DISTINCT' : ''), + $selects, + 'FROM', + $tables, + $joinString, + $whereCriteria, + $groupBys, + $havingCriteria, + $orderBys, + $limit, + $offset + ); + + $sql = $this->concatenateQuery($sqlArray); + + $bindings = array_merge( + $whereBindings, + $havingBindings + ); + + return compact('sql', 'bindings'); + } + + /** + * Build just criteria part of the query + * + * @param $statements + * @param bool $bindValues + * + * @return array + */ + public function criteriaOnly($statements, $bindValues = true) + { + $sql = $bindings = array(); + + if (! isset($statements['criteria'])) { + return compact('sql', 'bindings'); + } + + list($sql, $bindings) = $this->buildCriteria($statements['criteria'], $bindValues); + + return compact('sql', 'bindings'); + } + + /** + * Build a generic insert/ignore/replace query + * + * @param $statements + * @param array $data + * + * @return array + * @throws \FluentMail\App\Services\DB\Exception + */ + private function doInsert($statements, array $data, $type) + { + if (! isset($statements['tables'])) { + throw new Exception('No table specified', 3); + } + + $table = end($statements['tables']); + + $bindings = $keys = $values = array(); + + foreach ($data as $key => $value) { + $keys[] = $key; + if ($value instanceof Raw) { + $values[] = (string) $value; + } else { + $values[] = '?'; + $bindings[] = $value; + } + } + + $sqlArray = array( + $type . ' INTO', + $this->wrapSanitizer($table), + '(' . $this->arrayStr($keys, ',') . ')', + 'VALUES', + '(' . $this->arrayStr($values, ',', false) . ')', + ); + + if (isset($statements['onduplicate'])) { + if (count($statements['onduplicate']) < 1) { + throw new Exception('No data given.', 4); + } + list($updateStatement, $updateBindings) = $this->getUpdateStatement($statements['onduplicate']); + $sqlArray[] = 'ON DUPLICATE KEY UPDATE ' . $updateStatement; + $bindings = array_merge($bindings, $updateBindings); + } + + $sql = $this->concatenateQuery($sqlArray); + + return compact('sql', 'bindings'); + } + + /** + * Build Insert query + * + * @param $statements + * @param array $data + * + * @return array + * @throws \FluentMail\App\Services\DB\Exception + */ + public function insert($statements, array $data) + { + return $this->doInsert($statements, $data, 'INSERT'); + } + + /** + * Build Insert Ignore query + * + * @param $statements + * @param array $data + * + * @return array + * @throws \FluentMail\App\Services\DB\Exception + */ + public function insertIgnore($statements, array $data) + { + return $this->doInsert($statements, $data, 'INSERT IGNORE'); + } + + /** + * Build Insert Ignore query + * + * @param $statements + * @param array $data + * + * @return array + * @throws \FluentMail\App\Services\DB\Exception + */ + public function replace($statements, array $data) + { + return $this->doInsert($statements, $data, 'REPLACE'); + } + + /** + * Build fields assignment part of SET ... or ON DUBLICATE KEY UPDATE ... statements + * + * @param array $data + * + * @return array + */ + private function getUpdateStatement($data) + { + $bindings = array(); + $statement = ''; + + foreach ($data as $key => $value) { + if ($value instanceof Raw) { + $statement .= $this->wrapSanitizer($key) . '=' . $value . ','; + } else { + $statement .= $this->wrapSanitizer($key) . '=?,'; + $bindings[] = $value; + } + } + + $statement = trim($statement, ','); + + return array($statement, $bindings); + } + + /** + * Build update query + * + * @param $statements + * @param array $data + * + * @return array + * @throws \FluentMail\App\Services\DB\Exception + */ + public function update($statements, array $data) + { + if (! isset($statements['tables'])) { + throw new Exception('No table specified', 3); + } elseif (count($data) < 1) { + throw new Exception('No data given.', 4); + } + + $table = end($statements['tables']); + + // Update statement + list($updateStatement, $bindings) = $this->getUpdateStatement($data); + + // Wheres + list($whereCriteria, $whereBindings) = $this->buildCriteriaWithType($statements, 'wheres', 'WHERE'); + + // Limit + $limit = isset($statements['limit']) ? 'LIMIT ' . $statements['limit'] : ''; + + $sqlArray = array( + 'UPDATE', + $this->wrapSanitizer($table), + 'SET ' . $updateStatement, + $whereCriteria, + $limit + ); + + $sql = $this->concatenateQuery($sqlArray); + + $bindings = array_merge($bindings, $whereBindings); + + return compact('sql', 'bindings'); + } + + /** + * Build delete query + * + * @param $statements + * + * @return array + * @throws \FluentMail\App\Services\DB\src\Exception + */ + public function delete($statements) + { + if (! isset($statements['tables'])) { + throw new Exception('No table specified', 3); + } + + $table = end($statements['tables']); + + // Wheres + list($whereCriteria, $whereBindings) = $this->buildCriteriaWithType($statements, 'wheres', 'WHERE'); + + // Limit + $limit = isset($statements['limit']) ? 'LIMIT ' . $statements['limit'] : ''; + + $sqlArray = array('DELETE FROM', $this->wrapSanitizer($table), $whereCriteria); + $sql = $this->concatenateQuery($sqlArray); + $bindings = $whereBindings; + + return compact('sql', 'bindings'); + } + + /** + * Array concatenating method, like implode. + * But it does wrap sanitizer and trims last glue + * + * @param array $pieces + * @param $glue + * @param bool $wrapSanitizer + * + * @return string + */ + protected function arrayStr(array $pieces, $glue, $wrapSanitizer = true) + { + $str = ''; + + foreach ($pieces as $key => $piece) { + if ($wrapSanitizer) { + $piece = $this->wrapSanitizer($piece); + } + + if (! is_int($key)) { + $piece = ($wrapSanitizer ? $this->wrapSanitizer($key) : $key) . ' AS ' . $piece; + } + + $str .= $piece . $glue; + } + + return trim($str, $glue); + } + + /** + * Join different part of queries with a space. + * + * @param array $pieces + * + * @return string + */ + protected function concatenateQuery(array $pieces) + { + $str = ''; + + foreach ($pieces as $piece) { + $str = trim($str) . ' ' . trim($piece); + } + + return trim($str); + } + + /** + * Build generic criteria string and bindings from statements, like "a = b and c = ?" + * + * @param $statements + * @param bool $bindValues + * + * @return array + */ + protected function buildCriteria($statements, $bindValues = true) + { + $criteria = ''; + $bindings = array(); + + foreach ($statements as $statement) { + $key = $this->wrapSanitizer($statement['key']); + $value = $statement['value']; + + if (is_null($value) && $key instanceof \Closure) { + // We have a closure, a nested criteria + + // Build a new NestedCriteria class, keep it by reference so any changes made + // in the closure should reflect here + $nestedCriteria = $this->container->build( + '\\FluentMail\\App\\Services\\DB\\QueryBuilder\\NestedCriteria', + array($this->connection) + ); + + $nestedCriteria = & $nestedCriteria; + // Call the closure with our new nestedCriteria object + $key($nestedCriteria); + // Get the criteria only query from the nestedCriteria object + $queryObject = $nestedCriteria->getQuery('criteriaOnly', true); + // Merge the bindings we get from nestedCriteria object + $bindings = array_merge($bindings, $queryObject->getBindings()); + // Append the sql we get from the nestedCriteria object + $criteria .= $statement['joiner'] . ' (' . $queryObject->getSql() . ') '; + } elseif (is_array($value)) { + // where_in or between like query + $criteria .= $statement['joiner'] . ' ' . $key . ' ' . $statement['operator']; + + switch ($statement['operator']) { + case 'BETWEEN': + $bindings = array_merge($bindings, $statement['value']); + $criteria .= ' ? AND ? '; + break; + default: + $valuePlaceholder = ''; + foreach ($statement['value'] as $subValue) { + $valuePlaceholder .= '?, '; + $bindings[] = $subValue; + } + + $valuePlaceholder = trim($valuePlaceholder, ', '); + $criteria .= ' (' . $valuePlaceholder . ') '; + break; + } + } elseif ($value instanceof Raw) { + $criteria .= "{$statement['joiner']} {$key} {$statement['operator']} $value "; + } else { + // Usual where like criteria + + if (! $bindValues) { + // Specially for joins + + // We are not binding values, lets sanitize then + $value = $this->wrapSanitizer($value); + $criteria .= $statement['joiner'] . ' ' . $key . ' ' . $statement['operator'] . ' ' . $value . ' '; + } elseif ($statement['key'] instanceof Raw) { + $criteria .= $statement['joiner'] . ' ' . $key . ' '; + $bindings = array_merge($bindings, $statement['key']->getBindings()); + } else { + // For wheres + + $valuePlaceholder = '?'; + $bindings[] = $value; + $criteria .= $statement['joiner'] . ' ' . $key . ' ' . $statement['operator'] . ' ' + . $valuePlaceholder . ' '; + } + } + } + + // Clear all white spaces, and, or from beginning and white spaces from ending + $criteria = preg_replace('/^(\s?AND ?|\s?OR ?)|\s$/i', '', $criteria); + + return array($criteria, $bindings); + } + + /** + * Wrap values with adapter's sanitizer like, '`' + * + * @param $value + * + * @return string + */ + public function wrapSanitizer($value) + { + // Its a raw query, just cast as string, object has __toString() + if ($value instanceof Raw) { + return (string) $value; + } elseif ($value instanceof \Closure) { + return $value; + } + + // Separate our table and fields which are joined with a ".", + // like my_table.id + $valueArr = explode('.', $value, 2); + + foreach ($valueArr as $key => $subValue) { + // Don't wrap if we have *, which is not a usual field + $valueArr[$key] = trim($subValue) == '*' ? $subValue : $this->sanitizer . $subValue . $this->sanitizer; + } + + // Join these back with "." and return + return implode('.', $valueArr); + } + + /** + * Build criteria string and binding with various types added, like WHERE and Having + * + * @param $statements + * @param $key + * @param $type + * @param bool $bindValues + * + * @return array + */ + protected function buildCriteriaWithType($statements, $key, $type, $bindValues = true) + { + $criteria = ''; + $bindings = array(); + + if (isset($statements[$key])) { + // Get the generic/adapter agnostic criteria string from parent + list($criteria, $bindings) = $this->buildCriteria($statements[$key], $bindValues); + + if ($criteria) { + $criteria = $type . ' ' . $criteria; + } + } + + return array($criteria, $bindings); + } + + /** + * Build join string + * + * @param $statements + * + * @return array|string + */ + protected function buildJoin($statements) + { + $sql = ''; + + if (! array_key_exists('joins', $statements) || ! is_array($statements['joins'])) { + return $sql; + } + + foreach ($statements['joins'] as $joinArr) { + if (is_array($joinArr['table'])) { + $mainTable = $joinArr['table'][0]; + $aliasTable = $joinArr['table'][1]; + $table = $this->wrapSanitizer($mainTable) . ' AS ' . $this->wrapSanitizer($aliasTable); + } else { + $table = $joinArr['table'] instanceof Raw ? + (string) $joinArr['table'] : + $this->wrapSanitizer($joinArr['table']); + } + + $joinBuilder = $joinArr['joinBuilder']; + + $sqlArr = array( + $sql, + strtoupper($joinArr['type']), + 'JOIN', + $table, + 'ON', + $joinBuilder->getQuery('criteriaOnly', false)->getSql() + ); + + $sql = $this->concatenateQuery($sqlArr); + } + + return $sql; + } +} diff --git a/wp-content/plugins/fluent-smtp/app/Services/DB/QueryBuilder/Adapters/Mysql.php b/wp-content/plugins/fluent-smtp/app/Services/DB/QueryBuilder/Adapters/Mysql.php new file mode 100644 index 0000000..35229e6 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/app/Services/DB/QueryBuilder/Adapters/Mysql.php @@ -0,0 +1,10 @@ +joinHandler($key, $operator, $value, 'AND'); + } + + /** + * @param $key + * @param $operator + * @param $value + * + * @return $this + */ + public function orOn($key, $operator, $value) + { + return $this->joinHandler($key, $operator, $value, 'OR'); + } + + /** + * @param $key + * @param null $operator + * @param null $value + * @param string $joiner + * + * @return $this + */ + protected function joinHandler($key, $operator = null, $value = null, $joiner = 'AND') + { + $key = $this->addTablePrefix($key); + $value = $this->addTablePrefix($value); + $this->statements['criteria'][] = compact('key', 'operator', 'value', 'joiner'); + + return $this; + } +} diff --git a/wp-content/plugins/fluent-smtp/app/Services/DB/QueryBuilder/NestedCriteria.php b/wp-content/plugins/fluent-smtp/app/Services/DB/QueryBuilder/NestedCriteria.php new file mode 100644 index 0000000..8e3ea7e --- /dev/null +++ b/wp-content/plugins/fluent-smtp/app/Services/DB/QueryBuilder/NestedCriteria.php @@ -0,0 +1,20 @@ +addTablePrefix($key); + $this->statements['criteria'][] = compact('key', 'operator', 'value', 'joiner'); + + return $this; + } +} diff --git a/wp-content/plugins/fluent-smtp/app/Services/DB/QueryBuilder/QueryBuilderHandler.php b/wp-content/plugins/fluent-smtp/app/Services/DB/QueryBuilder/QueryBuilderHandler.php new file mode 100644 index 0000000..5595c3c --- /dev/null +++ b/wp-content/plugins/fluent-smtp/app/Services/DB/QueryBuilder/QueryBuilderHandler.php @@ -0,0 +1,1161 @@ +connection = $connection; + $this->container = $this->connection->getContainer(); + $this->db = $this->connection->getDbInstance(); + $this->adapter = $this->connection->getAdapter(); + $this->adapterConfig = $this->connection->getAdapterConfig(); + + if (isset($this->adapterConfig['prefix'])) { + $this->tablePrefix = $this->adapterConfig['prefix']; + } + // Query builder adapter instance + $this->adapterInstance = $this->container->build( + '\\FluentMail\\App\\Services\\DB\\QueryBuilder\\Adapters\\' . ucfirst($this->adapter), + array($this->connection) + ); + } + + /** + * Set the fetch mode + * + * @param $mode + * @return $this + */ + public function setFetchMode($mode) + { + $this->fetchParameters = func_get_args(); + + return $this; + } + + /** + * Fetch query results as object of specified type + * + * @param $className + * @param array $constructorArgs + * @return QueryBuilderHandler + */ + public function asObject($className, $constructorArgs = array()) + { + var_dump('need to implement this'); die(); + + return $this->setFetchMode(\PDO::FETCH_CLASS, $className, $constructorArgs); + } + + /** + * @param null|\FluentMail\App\Services\DB\Connection $connection + * + * @return static + */ + public function newQuery(?Connection $connection = null) + { + if (is_null($connection)) { + $connection = $this->connection; + } + + return new static($connection); + } + + /** + * @param $sql + * @param array $bindings + * + * @return $this + */ + public function query($sql, $bindings = array()) + { + $this->dbStatement = $this->container->build( + '\\FluentMail\\App\\Services\\DB\\QueryBuilder\\QueryObject', + array($sql, $bindings) + )->getRawSql(); + + return $this; + } + + /** + * @param $rawSql + * + * @return float execution time + */ + public function statement($rawSql) + { + $start = microtime(true); + + $this->db->query($rawSql); + + return microtime(true) - $start; + } + + /** + * Get all rows + * + * @return array|object|null + * @throws \FluentMail\App\Services\DB\Exception + */ + public function get() + { + $eventResult = $this->fireEvents('before-select'); + + if (! is_null($eventResult)) { + return $eventResult; + }; + + if (is_null($this->dbStatement)) { + $queryObject = $this->getQuery('select'); + + $this->dbStatement = $queryObject->getRawSql(); + } + + $start = microtime(true); + $result = $this->db->get_results($this->dbStatement); + $executionTime = microtime(true) - $start; + $this->dbStatement = null; + $this->fireEvents('after-select', $result, $executionTime); + + return $result; + } + + /** + * Get first row + * + * @return \stdClass|null + */ + public function first() + { + $this->limit(1); + $result = $this->get(); + + return empty($result) ? null : $result[0]; + } + + /** + * @param $value + * @param string $fieldName + * + * @return null|\stdClass + */ + public function findAll($fieldName, $value) + { + $this->where($fieldName, '=', $value); + + return $this->get(); + } + + /** + * @param $value + * @param string $fieldName + * + * @return null|\stdClass + */ + public function find($value, $fieldName = 'id') + { + $this->where($fieldName, '=', $value); + + return $this->first(); + } + + /** + * Get count of rows + * + * @return int + */ + public function count() + { + // Get the current statements + $originalStatements = $this->statements; + + unset($this->statements['orderBys']); + unset($this->statements['limit']); + unset($this->statements['offset']); + + $count = $this->aggregate('count'); + $this->statements = $originalStatements; + + return $count; + } + + /** + * @param $type + * + * @return int + */ + protected function aggregate($type) + { + // Get the current selects + $mainSelects = isset($this->statements['selects']) ? $this->statements['selects'] : null; + // Replace select with a scalar value like `count` + $this->statements['selects'] = array($this->raw($type . '(*) as field')); + $row = $this->get(); + + // Set the select as it was + if ($mainSelects) { + $this->statements['selects'] = $mainSelects; + } else { + unset($this->statements['selects']); + } + + if (($count = count($row)) > 1) { + return $count; + } else { + $item = (array) $row[0]; + + return (int) $item['field']; + } + } + + /** + * @param string $type + * @param array $dataToBePassed + * + * @return mixed + * @throws Exception + */ + public function getQuery($type = 'select', $dataToBePassed = array()) + { + $allowedTypes = array('select', 'insert', 'insertignore', 'replace', 'delete', 'update', 'criteriaonly'); + + if (! in_array(strtolower($type), $allowedTypes)) { + throw new Exception(wp_kses_post($type . ' is not a known type.'), 2); + } + + $queryArr = $this->adapterInstance->$type($this->statements, $dataToBePassed); + + return $this->container->build( + '\\FluentMail\\App\\Services\\DB\\QueryBuilder\\QueryObject', + array($queryArr['sql'], $queryArr['bindings']) + ); + } + + /** + * @param QueryBuilderHandler $queryBuilder + * @param null $alias + * + * @return Raw + */ + public function subQuery(QueryBuilderHandler $queryBuilder, $alias = null) + { + $sql = '(' . $queryBuilder->getQuery()->getRawSql() . ')'; + + if ($alias) { + $sql = $sql . ' as ' . $alias; + } + + return $queryBuilder->raw($sql); + } + + /** + * @param $data + * + * @return array|string + * @throws \FluentMail\App\Services\DB\Exception + */ + private function doInsert($data, $type) + { + $eventResult = $this->fireEvents('before-insert'); + + if (! is_null($eventResult)) { + return $eventResult; + } + + // If first value is not an array + // Its not a batch insert + if (! is_array(current($data))) { + $start = microtime(true); + + $queryObject = $this->getQuery($type, $data); + + $executionTime = $this->statement($queryObject->getRawSql()); + + $return = $this->db->insert_id; + } else { + // Its a batch insert + $executionTime = 0; + $return = array(); + foreach ($data as $subData) { + $start = microtime(true); + + $queryObject = $this->getQuery($type, $subData); + + $executionTime = $this->statement($queryObject->getRawSql()); + + $return[] = $this->db->insert_id; + } + } + + $this->fireEvents('after-insert', $return, $executionTime); + + return $return; + } + + /** + * @param $data + * + * @return array|string + */ + public function insert($data) + { + return $this->doInsert($data, 'insert'); + } + + /** + * @param $data + * + * @return array|string + */ + public function insertIgnore($data) + { + return $this->doInsert($data, 'insertignore'); + } + + /** + * @param $data + * + * @return array|string + */ + public function replace($data) + { + return $this->doInsert($data, 'replace'); + } + + /** + * @param $data + * + * @throws \FluentMail\App\Services\DB\Exception + */ + public function update($data) + { + $eventResult = $this->fireEvents('before-update'); + + if (! is_null($eventResult)) { + return $eventResult; + } + + $queryObject = $this->getQuery('update', $data); + + $executionTime = $this->statement($queryObject->getRawSql()); + + $this->fireEvents('after-update', $queryObject, $executionTime); + } + + /** + * @param $data + * + * @return array|string + */ + public function updateOrInsert($data) + { + if ($this->first()) { + return $this->update($data); + } else { + return $this->insert($data); + } + } + + /** + * @param $data + * + * @return $this + */ + public function onDuplicateKeyUpdate($data) + { + $this->addStatement('onduplicate', $data); + + return $this; + } + + /** + * @return mixed + * @throws \FluentMail\App\Services\DB\Exception + */ + public function delete() + { + $eventResult = $this->fireEvents('before-delete'); + + if (! is_null($eventResult)) { + return $eventResult; + } + + $queryObject = $this->getQuery('delete'); + + $executionTime = $this->statement($queryObject->getRawSql()); + + $this->fireEvents('after-delete', $queryObject, $executionTime); + } + + /** + * @param string|array $tables Single table or multiple tables + * as an array or as multiple parameters + * + * @return static + */ + public function table($tables) + { + if (! is_array($tables)) { + // because a single table is converted to an array anyways, + // this makes sense. + $tables = array($tables); + } + + $instance = new static($this->connection); + $tables = $this->addTablePrefix($tables, false); + $instance->addStatement('tables', $tables); + + return $instance; + } + + /** + * @param $tables + * + * @return $this + */ + public function from($tables) + { + if (! is_array($tables)) { + $tables = array($tables); + } + + $tables = $this->addTablePrefix($tables, false); + $this->addStatement('tables', $tables); + + return $this; + } + + /** + * @param $fields + * + * @return $this + */ + public function select($fields) + { + if (! is_array($fields)) { + $fields = array($fields); + } + + $fields = $this->addTablePrefix($fields); + $this->addStatement('selects', $fields); + + return $this; + } + + /** + * @param $fields + * + * @return $this + */ + public function selectDistinct($fields) + { + $this->select($fields); + $this->addStatement('distinct', true); + + return $this; + } + + /** + * @param $field + * + * @return $this + */ + public function groupBy($field) + { + $field = $this->addTablePrefix($field); + $this->addStatement('groupBys', $field); + + return $this; + } + + /** + * @param $fields + * @param string $defaultDirection + * + * @return $this + */ + public function orderBy($fields, $defaultDirection = 'ASC') + { + if (! is_array($fields)) { + $fields = array($fields); + } + + foreach ($fields as $key => $value) { + $field = $key; + $type = $value; + + if (is_int($key)) { + $field = $value; + $type = $defaultDirection; + } + + if (!$field instanceof Raw) { + $field = $this->addTablePrefix($field); + } + + $this->statements['orderBys'][] = compact('field', 'type'); + } + + return $this; + } + + /** + * @param $limit + * + * @return $this + */ + public function limit($limit) + { + $this->statements['limit'] = $limit; + + return $this; + } + + /** + * @param $offset + * + * @return $this + */ + public function offset($offset) + { + $this->statements['offset'] = $offset; + + return $this; + } + + /** + * @param $key + * @param $operator + * @param $value + * @param string $joiner + * + * @return $this + */ + public function having($key, $operator = null, $value = null, $joiner = 'AND') + { + $key = $this->addTablePrefix($key); + $this->statements['havings'][] = compact('key', 'operator', 'value', 'joiner'); + + return $this; + } + + /** + * @param $key + * @param $operator + * @param $value + * + * @return $this + */ + public function orHaving($key, $operator, $value) + { + return $this->having($key, $operator, $value, 'OR'); + } + + /** + * @param $key + * @param $operator + * @param $value + * + * @return $this + */ + public function where($key, $operator = null, $value = null) + { + // If two params are given then assume operator is = + if (func_num_args() == 2) { + $value = $operator; + $operator = '='; + } + + return $this->whereHandler($key, $operator, $value); + } + + /** + * @param $key + * @param $operator + * @param $value + * + * @return $this + */ + public function orWhere($key, $operator = null, $value = null) + { + // If two params are given then assume operator is = + if (func_num_args() == 2) { + $value = $operator; + $operator = '='; + } + + return $this->whereHandler($key, $operator, $value, 'OR'); + } + + /** + * @param $key + * @param $operator + * @param $value + * + * @return $this + */ + public function whereNot($key, $operator = null, $value = null) + { + // If two params are given then assume operator is = + if (func_num_args() == 2) { + $value = $operator; + $operator = '='; + } + + return $this->whereHandler($key, $operator, $value, 'AND NOT'); + } + + /** + * @param $key + * @param $operator + * @param $value + * + * @return $this + */ + public function orWhereNot($key, $operator = null, $value = null) + { + // If two params are given then assume operator is = + if (func_num_args() == 2) { + $value = $operator; + $operator = '='; + } + + return $this->whereHandler($key, $operator, $value, 'OR NOT'); + } + + /** + * @param $key + * @param array $values + * + * @return $this + */ + public function whereIn($key, $values) + { + return $this->whereHandler($key, 'IN', $values, 'AND'); + } + + /** + * @param $key + * @param array $values + * + * @return $this + */ + public function whereNotIn($key, $values) + { + return $this->whereHandler($key, 'NOT IN', $values, 'AND'); + } + + /** + * @param $key + * @param array $values + * + * @return $this + */ + public function orWhereIn($key, $values) + { + return $this->whereHandler($key, 'IN', $values, 'OR'); + } + + /** + * @param $key + * @param array $values + * + * @return $this + */ + public function orWhereNotIn($key, $values) + { + return $this->whereHandler($key, 'NOT IN', $values, 'OR'); + } + + /** + * @param $key + * @param $valueFrom + * @param $valueTo + * + * @return $this + */ + public function whereBetween($key, $valueFrom, $valueTo) + { + return $this->whereHandler($key, 'BETWEEN', array($valueFrom, $valueTo), 'AND'); + } + + /** + * @param $key + * @param $valueFrom + * @param $valueTo + * + * @return $this + */ + public function orWhereBetween($key, $valueFrom, $valueTo) + { + return $this->whereHandler($key, 'BETWEEN', array($valueFrom, $valueTo), 'OR'); + } + + /** + * @param $key + * @return QueryBuilderHandler + */ + public function whereNull($key) + { + return $this->whereNullHandler($key); + } + + /** + * @param $key + * @return QueryBuilderHandler + */ + public function whereNotNull($key) + { + return $this->whereNullHandler($key, 'NOT'); + } + + /** + * @param $key + * @return QueryBuilderHandler + */ + public function orWhereNull($key) + { + return $this->whereNullHandler($key, '', 'or'); + } + + /** + * @param $key + * @return QueryBuilderHandler + */ + public function orWhereNotNull($key) + { + return $this->whereNullHandler($key, 'NOT', 'or'); + } + + protected function whereNullHandler($key, $prefix = '', $operator = '') + { + $key = $this->adapterInstance->wrapSanitizer($this->addTablePrefix($key)); + + return $this->{$operator . 'Where'}($this->raw("{$key} IS {$prefix} NULL")); + } + + /** + * @param $table + * @param $key + * @param $operator + * @param $value + * @param string $type + * + * @return $this + */ + public function join($table, $key, $operator = null, $value = null, $type = 'inner') + { + if (! $key instanceof \Closure) { + $key = function ($joinBuilder) use ($key, $operator, $value) { + $joinBuilder->on($key, $operator, $value); + }; + } + + // Build a new JoinBuilder class, keep it by reference so any changes made + // in the closure should reflect here + $joinBuilder = $this->container->build('\\FluentMail\\App\\Services\\DB\\QueryBuilder\\JoinBuilder', array($this->connection)); + $joinBuilder = & $joinBuilder; + // Call the closure with our new joinBuilder object + $key($joinBuilder); + $table = $this->addTablePrefix($table, false); + // Get the criteria only query from the joinBuilder object + $this->statements['joins'][] = compact('type', 'table', 'joinBuilder'); + + return $this; + } + + /** + * Runs a transaction + * + * @param $callback + * + * @return $this + */ + public function transaction(\Closure $callback) + { + try { + // Begin the PDO transaction + $this->db->query('START TRANSACTION'); + + // Get the Transaction class + $transaction = $this->container->build( + '\\FluentMail\\App\\Services\\DB\\QueryBuilder\\Transaction', + array($this->connection) + ); + + // Call closure + $callback($transaction); + + // If no errors have been thrown or the transaction wasn't completed within + // the closure, commit the changes + $this->db->query('COMMIT'); + + return $this; + } catch (TransactionHaltException $e) { + // Commit or rollback behavior has been handled in the closure, so exit + return $this; + } catch (\Exception $e) { + // something happened, rollback changes + $this->db->query('ROLLBACK'); + + return $this; + } + } + + /** + * @param $table + * @param $key + * @param null $operator + * @param null $value + * + * @return $this + */ + public function leftJoin($table, $key, $operator = null, $value = null) + { + return $this->join($table, $key, $operator, $value, 'left'); + } + + /** + * @param $table + * @param $key + * @param null $operator + * @param null $value + * + * @return $this + */ + public function rightJoin($table, $key, $operator = null, $value = null) + { + return $this->join($table, $key, $operator, $value, 'right'); + } + + /** + * @param $table + * @param $key + * @param null $operator + * @param null $value + * + * @return $this + */ + public function innerJoin($table, $key, $operator = null, $value = null) + { + return $this->join($table, $key, $operator, $value, 'inner'); + } + + /** + * Add a raw query + * + * @param $value + * @param $bindings + * + * @return mixed + */ + public function raw($value, $bindings = array()) + { + return $this->container->build('\\FluentMail\\App\\Services\\DB\\QueryBuilder\\Raw', array($value, $bindings)); + } + + /** + * Return db instance + * + * @return \wpdb + */ + public function db() + { + return $this->db; + } + + /** + * @param \FluentMail\App\Services\DB\Connection $connection + * + * @return $this + */ + public function setConnection(Connection $connection) + { + $this->connection = $connection; + + return $this; + } + + /** + * @return \FluentMail\App\Services\DB\Connection + */ + public function getConnection() + { + return $this->connection; + } + + /** + * @param $key + * @param $operator + * @param $value + * @param string $joiner + * + * @return $this + */ + protected function whereHandler($key, $operator = null, $value = null, $joiner = 'AND') + { + $key = $this->addTablePrefix($key); + $this->statements['wheres'][] = compact('key', 'operator', 'value', 'joiner'); + + return $this; + } + + /** + * Add table prefix (if given) on given string. + * + * @param $values + * @param bool $tableFieldMix If we have mixes of field and table names with a "." + * + * @return array|mixed + */ + public function addTablePrefix($values, $tableFieldMix = true) + { + if (is_null($this->tablePrefix)) { + return $values; + } + + // $value will be an array and we will add prefix to all table names + + // If supplied value is not an array then make it one + $single = false; + + if (! is_array($values)) { + $values = array($values); + // We had single value, so should return a single value + $single = true; + } + + $return = array(); + + foreach ($values as $key => $value) { + // It's a raw query, just add it to our return array and continue next + if ($value instanceof Raw || $value instanceof \Closure) { + $return[$key] = $value; + continue; + } + + // If key is not integer, it is likely a alias mapping, + // so we need to change prefix target + $target = &$value; + if (! is_int($key)) { + $target = &$key; + } + + if (! $tableFieldMix || ($tableFieldMix && strpos($target, '.') !== false)) { + $target = $this->tablePrefix . $target; + } + + $return[$key] = $value; + } + + // If we had single value then we should return a single value (end value of the array) + return $single ? end($return) : $return; + } + + /** + * @param $key + * @param $value + */ + protected function addStatement($key, $value) + { + if (! is_array($value)) { + $value = array($value); + } + + if (! array_key_exists($key, $this->statements)) { + $this->statements[$key] = $value; + } else { + $this->statements[$key] = array_merge($this->statements[$key], $value); + } + } + + /** + * @param $event + * @param $table + * + * @return callable|null + */ + public function getEvent($event, $table = ':any') + { + return $this->connection->getEventHandler()->getEvent($event, $table); + } + + /** + * @param $event + * @param string $table + * @param callable $action + * + * @return void + */ + public function registerEvent($event, $table, \Closure $action) + { + $table = $table ?: ':any'; + + if ($table != ':any') { + $table = $this->addTablePrefix($table, false); + } + + $this->connection->getEventHandler()->registerEvent($event, $table, $action); + } + + /** + * @param $event + * @param string $table + * + * @return void + */ + public function removeEvent($event, $table = ':any') + { + if ($table != ':any') { + $table = $this->addTablePrefix($table, false); + } + + $this->connection->getEventHandler()->removeEvent($event, $table); + } + + /** + * @param $event + * @return mixed + */ + public function fireEvents($event) + { + $params = func_get_args(); + array_unshift($params, $this); + + return call_user_func_array( + array($this->connection->getEventHandler(), 'fireEvents'), + $params + ); + } + + /** + * @return array + */ + public function getStatements() + { + return $this->statements; + } + + /** + * Get the paginated rows. + * + * @param null $perPage + * @param array $columns + * + * @return array + */ + public function paginate($perPage = null, $columns = array('*')) + { + $currentPage = intval($_GET['page']) ?: 1; + + $perPage = $perPage ?: intval($_REQUEST['per_page']) ?: 15; + + $skip = $perPage * ($currentPage - 1); + + $data = (array) $this->select($columns)->limit($perPage)->offset($skip)->get(); + + $dataCount = count($data); + + $from = $dataCount > 0 ? ($currentPage - 1) * $perPage + 1 : null; + + $to = $dataCount > 0 ? $from + $dataCount - 1 : null; + + $total = $this->count(); + + $lastPage = (int) ceil($total / $perPage); + + return array( + 'current_page' => $currentPage, + 'per_page' => $perPage, + 'from' => $from, + 'to' => $to, + 'last_page' => $lastPage, + 'total' => $total, + 'data' => $data, + ); + } + + /** + * Apply the callback's query changes if the given "value" is true. + * + * @param mixed $value + * @param callable $callback + * @param callable $default + * @return mixed + */ + public function when($value, $callback, $default = null) + { + if ($value) { + return $callback($this, $value) ?: $this; + } elseif ($default) { + return $default($this, $value) ?: $this; + } + + return $this; + } +} diff --git a/wp-content/plugins/fluent-smtp/app/Services/DB/QueryBuilder/QueryObject.php b/wp-content/plugins/fluent-smtp/app/Services/DB/QueryBuilder/QueryObject.php new file mode 100644 index 0000000..f1b809c --- /dev/null +++ b/wp-content/plugins/fluent-smtp/app/Services/DB/QueryBuilder/QueryObject.php @@ -0,0 +1,102 @@ +sql = (string) $sql; + + $this->bindings = $bindings; + + global $wpdb; + + $this->db = $wpdb; + } + + /** + * @return string + */ + public function getSql() + { + return $this->sql; + } + + /** + * @return array + */ + public function getBindings() + { + return $this->bindings; + } + + /** + * Get the raw/bound sql + * + * @return string + */ + public function getRawSql() + { + return $this->interpolateQuery($this->sql, $this->bindings); + } + + /** + * Replaces any parameter placeholders in a query with the value of that + * parameter. Useful for debugging. Assumes anonymous parameters from + * $params are are in the same order as specified in $query + * + * Reference: http://stackoverflow.com/a/1376838/656489 + * + * @param string $query The sql query with parameter placeholders + * @param array $params The array of substitution parameters + * + * @return string The interpolated query + */ + protected function interpolateQuery($query, $params) + { + $keys = $placeHolders = []; + + foreach ($params as $key => $value) { + if (is_string($key)) { + $keys[] = '/:' . $key . '/'; + } else { + $keys[] = '/[?]/'; + } + + $placeHolders[] = $this->getPlaceHolder($value); + } + + $query = preg_replace($keys, $placeHolders, $query, 1, $count); + + return $params ? $this->db->prepare($query, $params) : $query; + } + + private function getPlaceHolder($value) + { + $placeHolder = '%s'; + + if (is_int($value)) { + $placeHolder = '%d'; + } elseif (is_float($value)) { + $placeHolder = '%f'; + } + + return $placeHolder; + } +} diff --git a/wp-content/plugins/fluent-smtp/app/Services/DB/QueryBuilder/Raw.php b/wp-content/plugins/fluent-smtp/app/Services/DB/QueryBuilder/Raw.php new file mode 100644 index 0000000..5696bb0 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/app/Services/DB/QueryBuilder/Raw.php @@ -0,0 +1,34 @@ +value = (string)$value; + $this->bindings = (array)$bindings; + } + + public function getBindings() + { + return $this->bindings; + } + + /** + * @return string + */ + public function __toString() + { + return (string) $this->value; + } +} diff --git a/wp-content/plugins/fluent-smtp/app/Services/DB/QueryBuilder/Transaction.php b/wp-content/plugins/fluent-smtp/app/Services/DB/QueryBuilder/Transaction.php new file mode 100644 index 0000000..7420dd3 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/app/Services/DB/QueryBuilder/Transaction.php @@ -0,0 +1,27 @@ +db->query('COMMIT'); + + throw new TransactionHaltException(); + } + + /** + * Rollback the database changes + */ + public function rollback() + { + $this->db->query('ROLLBACK'); + + throw new TransactionHaltException(); + } +} diff --git a/wp-content/plugins/fluent-smtp/app/Services/DB/QueryBuilder/TransactionHaltException.php b/wp-content/plugins/fluent-smtp/app/Services/DB/QueryBuilder/TransactionHaltException.php new file mode 100644 index 0000000..9ce3d7a --- /dev/null +++ b/wp-content/plugins/fluent-smtp/app/Services/DB/QueryBuilder/TransactionHaltException.php @@ -0,0 +1,7 @@ +registry[$key] = compact('object', 'singleton'); + } + + /** + * If we have a registry for the given key + * + * @param string $key + * + * @return bool + */ + public function has($key) + { + return array_key_exists($key, $this->registry); + } + + /** + * Register as singleton. + * + * @param string $key + * @param mixed $object + * + * @return void + */ + public function singleton($key, $object) + { + $this->set($key, $object, true); + } + + /** + * Register or replace an instance as a singleton. + * Useful for replacing with Mocked instance + * + * @param string $key + * @param mixed $instance + * + * @return void + */ + public function setInstance($key, $instance) + { + $this->singletons[$key] = $instance; + } + + /** + * Build from the given key. + * If there is a class registered with Container::set() then it's instance + * will be returned. If a closure is registered, a closure's return value + * will be returned. If nothing is registered then it will try to build an + * instance with new $key(...). + * + * $parameters will be passed to closure or class constructor. + * + * + * @param string $key + * @param array $parameters + * + * @return mixed + */ + public function build($key, $parameters = array()) + { + // If we have a singleton instance registered the just return it + if (array_key_exists($key, $this->singletons)) { + return $this->singletons[$key]; + } + + // If we don't have a registered object with the key then assume user + // is trying to build a class with the given key/name + + if (!array_key_exists($key, $this->registry)) { + $object = $key; + } else { + $object = $this->registry[$key]['object']; + } + + + $instance = $this->instanciate($object, $parameters); + + // If the key is registered as a singleton, we can save the instance as singleton + // for later use + if (isset($this->registry[$key]['singleton']) && $this->registry[$key]['singleton'] === true) { + $this->singletons[$key] = $instance; + } + + return $instance; + } + + /** + * Instantiate an instance of the given type. + * + * @param string $key + * @param array $parameters + * + * @throws \Exception + * @return mixed + */ + protected function instanciate($key, $parameters = null) + { + + if ($key instanceof \Closure) { + return call_user_func_array($key, $parameters); + } + + $reflection = new \ReflectionClass($key); + return $reflection->newInstanceArgs($parameters); + } +} diff --git a/wp-content/plugins/fluent-smtp/app/Services/DB/Viocon/VioconException.php b/wp-content/plugins/fluent-smtp/app/Services/DB/Viocon/VioconException.php new file mode 100644 index 0000000..a673b7c --- /dev/null +++ b/wp-content/plugins/fluent-smtp/app/Services/DB/Viocon/VioconException.php @@ -0,0 +1,7 @@ + $wpdb->prefix]); + + $FluentSmtpDb = new \FluentMail\App\Services\DB\QueryBuilder\QueryBuilderHandler($connection); + } + + return $FluentSmtpDb; + } +} diff --git a/wp-content/plugins/fluent-smtp/app/Services/Html2Text.php b/wp-content/plugins/fluent-smtp/app/Services/Html2Text.php new file mode 100644 index 0000000..f57e418 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/app/Services/Html2Text.php @@ -0,0 +1,662 @@ + + * + * This script is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * The GNU General Public License can be found at + * http://www.gnu.org/copyleft/gpl.html. + * + * This script is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +namespace FluentMail\App\Services; + +class Html2Text +{ + const ENCODING = 'UTF-8'; + + protected $htmlFuncFlags; + + /** + * Contains the HTML content to convert. + * + * @var string $html + */ + protected $html; + + /** + * Contains the converted, formatted text. + * + * @var string $text + */ + protected $text; + + /** + * List of preg* regular expression patterns to search for, + * used in conjunction with $replace. + * + * @var array $search + * @see $replace + */ + protected $search = array( + "/\r/", // Non-legal carriage return + "/[\n\t]+/", // Newlines and tabs + '/]*>.*?<\/head>/i', // + '/]*>.*?<\/script>/i', // '); + } elseif ($format === self::FORMAT_JS) { + static::writeOutput(static::generateScript()); + } + static::resetStatic(); + } + } + public function close() : void + { + self::resetStatic(); + } + public function reset() + { + parent::reset(); + self::resetStatic(); + } + /** + * Forget all logged records + */ + public static function resetStatic() : void + { + static::$records = []; + } + /** + * Wrapper for register_shutdown_function to allow overriding + */ + protected function registerShutdownFunction() : void + { + if (\PHP_SAPI !== 'cli') { + \register_shutdown_function(['Monolog\\Handler\\BrowserConsoleHandler', 'send']); + } + } + /** + * Wrapper for echo to allow overriding + */ + protected static function writeOutput(string $str) : void + { + echo $str; + } + /** + * Checks the format of the response + * + * If Content-Type is set to application/javascript or text/javascript -> js + * If Content-Type is set to text/html, or is unset -> html + * If Content-Type is anything else -> unknown + * + * @return string One of 'js', 'html' or 'unknown' + * @phpstan-return self::FORMAT_* + */ + protected static function getResponseFormat() : string + { + // Check content type + foreach (\headers_list() as $header) { + if (\stripos($header, 'content-type:') === 0) { + return static::getResponseFormatFromContentType($header); + } + } + return self::FORMAT_HTML; + } + /** + * @return string One of 'js', 'html' or 'unknown' + * @phpstan-return self::FORMAT_* + */ + protected static function getResponseFormatFromContentType(string $contentType) : string + { + // This handler only works with HTML and javascript outputs + // text/javascript is obsolete in favour of application/javascript, but still used + if (\stripos($contentType, 'application/javascript') !== \false || \stripos($contentType, 'text/javascript') !== \false) { + return self::FORMAT_JS; + } + if (\stripos($contentType, 'text/html') !== \false) { + return self::FORMAT_HTML; + } + return self::FORMAT_UNKNOWN; + } + private static function generateScript() : string + { + $script = []; + foreach (static::$records as $record) { + $context = static::dump('Context', $record['context']); + $extra = static::dump('Extra', $record['extra']); + if (empty($context) && empty($extra)) { + $script[] = static::call_array(static::getConsoleMethodForLevel($record['level']), static::handleStyles($record['formatted'])); + } else { + $script = \array_merge($script, [static::call_array('groupCollapsed', static::handleStyles($record['formatted']))], $context, $extra, [static::call('groupEnd')]); + } + } + return "(function (c) {if (c && c.groupCollapsed) {\n" . \implode("\n", $script) . "\n}})(console);"; + } + private static function getConsoleMethodForLevel(int $level) : string + { + return [\FluentSmtpLib\Monolog\Logger::DEBUG => 'debug', \FluentSmtpLib\Monolog\Logger::INFO => 'info', \FluentSmtpLib\Monolog\Logger::NOTICE => 'info', \FluentSmtpLib\Monolog\Logger::WARNING => 'warn', \FluentSmtpLib\Monolog\Logger::ERROR => 'error', \FluentSmtpLib\Monolog\Logger::CRITICAL => 'error', \FluentSmtpLib\Monolog\Logger::ALERT => 'error', \FluentSmtpLib\Monolog\Logger::EMERGENCY => 'error'][$level] ?? 'log'; + } + /** + * @return string[] + */ + private static function handleStyles(string $formatted) : array + { + $args = []; + $format = '%c' . $formatted; + \preg_match_all('/\\[\\[(.*?)\\]\\]\\{([^}]*)\\}/s', $format, $matches, \PREG_OFFSET_CAPTURE | \PREG_SET_ORDER); + foreach (\array_reverse($matches) as $match) { + $args[] = '"font-weight: normal"'; + $args[] = static::quote(static::handleCustomStyles($match[2][0], $match[1][0])); + $pos = $match[0][1]; + $format = \FluentSmtpLib\Monolog\Utils::substr($format, 0, $pos) . '%c' . $match[1][0] . '%c' . \FluentSmtpLib\Monolog\Utils::substr($format, $pos + \strlen($match[0][0])); + } + $args[] = static::quote('font-weight: normal'); + $args[] = static::quote($format); + return \array_reverse($args); + } + private static function handleCustomStyles(string $style, string $string) : string + { + static $colors = ['blue', 'green', 'red', 'magenta', 'orange', 'black', 'grey']; + static $labels = []; + $style = \preg_replace_callback('/macro\\s*:(.*?)(?:;|$)/', function (array $m) use($string, &$colors, &$labels) { + if (\trim($m[1]) === 'autolabel') { + // Format the string as a label with consistent auto assigned background color + if (!isset($labels[$string])) { + $labels[$string] = $colors[\count($labels) % \count($colors)]; + } + $color = $labels[$string]; + return "background-color: {$color}; color: white; border-radius: 3px; padding: 0 2px 0 2px"; + } + return $m[1]; + }, $style); + if (null === $style) { + $pcreErrorCode = \preg_last_error(); + throw new \RuntimeException('Failed to run preg_replace_callback: ' . $pcreErrorCode . ' / ' . \FluentSmtpLib\Monolog\Utils::pcreLastErrorMessage($pcreErrorCode)); + } + return $style; + } + /** + * @param mixed[] $dict + * @return mixed[] + */ + private static function dump(string $title, array $dict) : array + { + $script = []; + $dict = \array_filter($dict); + if (empty($dict)) { + return $script; + } + $script[] = static::call('log', static::quote('%c%s'), static::quote('font-weight: bold'), static::quote($title)); + foreach ($dict as $key => $value) { + $value = \json_encode($value); + if (empty($value)) { + $value = static::quote(''); + } + $script[] = static::call('log', static::quote('%s: %o'), static::quote((string) $key), $value); + } + return $script; + } + private static function quote(string $arg) : string + { + return '"' . \addcslashes($arg, "\"\n\\") . '"'; + } + /** + * @param mixed $args + */ + private static function call(...$args) : string + { + $method = \array_shift($args); + if (!\is_string($method)) { + throw new \UnexpectedValueException('Expected the first arg to be a string, got: ' . \var_export($method, \true)); + } + return static::call_array($method, $args); + } + /** + * @param mixed[] $args + */ + private static function call_array(string $method, array $args) : string + { + return 'c.' . $method . '(' . \implode(', ', $args) . ');'; + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/BufferHandler.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/BufferHandler.php new file mode 100644 index 0000000..58d6ca5 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/BufferHandler.php @@ -0,0 +1,143 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace FluentSmtpLib\Monolog\Handler; + +use FluentSmtpLib\Monolog\Logger; +use FluentSmtpLib\Monolog\ResettableInterface; +use FluentSmtpLib\Monolog\Formatter\FormatterInterface; +/** + * Buffers all records until closing the handler and then pass them as batch. + * + * This is useful for a MailHandler to send only one mail per request instead of + * sending one per log message. + * + * @author Christophe Coevoet + * + * @phpstan-import-type Record from \Monolog\Logger + */ +class BufferHandler extends \FluentSmtpLib\Monolog\Handler\AbstractHandler implements \FluentSmtpLib\Monolog\Handler\ProcessableHandlerInterface, \FluentSmtpLib\Monolog\Handler\FormattableHandlerInterface +{ + use ProcessableHandlerTrait; + /** @var HandlerInterface */ + protected $handler; + /** @var int */ + protected $bufferSize = 0; + /** @var int */ + protected $bufferLimit; + /** @var bool */ + protected $flushOnOverflow; + /** @var Record[] */ + protected $buffer = []; + /** @var bool */ + protected $initialized = \false; + /** + * @param HandlerInterface $handler Handler. + * @param int $bufferLimit How many entries should be buffered at most, beyond that the oldest items are removed from the buffer. + * @param bool $flushOnOverflow If true, the buffer is flushed when the max size has been reached, by default oldest entries are discarded + */ + public function __construct(\FluentSmtpLib\Monolog\Handler\HandlerInterface $handler, int $bufferLimit = 0, $level = \FluentSmtpLib\Monolog\Logger::DEBUG, bool $bubble = \true, bool $flushOnOverflow = \false) + { + parent::__construct($level, $bubble); + $this->handler = $handler; + $this->bufferLimit = $bufferLimit; + $this->flushOnOverflow = $flushOnOverflow; + } + /** + * {@inheritDoc} + */ + public function handle(array $record) : bool + { + if ($record['level'] < $this->level) { + return \false; + } + if (!$this->initialized) { + // __destructor() doesn't get called on Fatal errors + \register_shutdown_function([$this, 'close']); + $this->initialized = \true; + } + if ($this->bufferLimit > 0 && $this->bufferSize === $this->bufferLimit) { + if ($this->flushOnOverflow) { + $this->flush(); + } else { + \array_shift($this->buffer); + $this->bufferSize--; + } + } + if ($this->processors) { + /** @var Record $record */ + $record = $this->processRecord($record); + } + $this->buffer[] = $record; + $this->bufferSize++; + return \false === $this->bubble; + } + public function flush() : void + { + if ($this->bufferSize === 0) { + return; + } + $this->handler->handleBatch($this->buffer); + $this->clear(); + } + public function __destruct() + { + // suppress the parent behavior since we already have register_shutdown_function() + // to call close(), and the reference contained there will prevent this from being + // GC'd until the end of the request + } + /** + * {@inheritDoc} + */ + public function close() : void + { + $this->flush(); + $this->handler->close(); + } + /** + * Clears the buffer without flushing any messages down to the wrapped handler. + */ + public function clear() : void + { + $this->bufferSize = 0; + $this->buffer = []; + } + public function reset() + { + $this->flush(); + parent::reset(); + $this->resetProcessors(); + if ($this->handler instanceof \FluentSmtpLib\Monolog\ResettableInterface) { + $this->handler->reset(); + } + } + /** + * {@inheritDoc} + */ + public function setFormatter(\FluentSmtpLib\Monolog\Formatter\FormatterInterface $formatter) : \FluentSmtpLib\Monolog\Handler\HandlerInterface + { + if ($this->handler instanceof \FluentSmtpLib\Monolog\Handler\FormattableHandlerInterface) { + $this->handler->setFormatter($formatter); + return $this; + } + throw new \UnexpectedValueException('The nested handler of type ' . \get_class($this->handler) . ' does not support formatters.'); + } + /** + * {@inheritDoc} + */ + public function getFormatter() : \FluentSmtpLib\Monolog\Formatter\FormatterInterface + { + if ($this->handler instanceof \FluentSmtpLib\Monolog\Handler\FormattableHandlerInterface) { + return $this->handler->getFormatter(); + } + throw new \UnexpectedValueException('The nested handler of type ' . \get_class($this->handler) . ' does not support formatters.'); + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/ChromePHPHandler.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/ChromePHPHandler.php new file mode 100644 index 0000000..fcb8b7d --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/ChromePHPHandler.php @@ -0,0 +1,157 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace FluentSmtpLib\Monolog\Handler; + +use FluentSmtpLib\Monolog\Formatter\ChromePHPFormatter; +use FluentSmtpLib\Monolog\Formatter\FormatterInterface; +use FluentSmtpLib\Monolog\Logger; +use FluentSmtpLib\Monolog\Utils; +/** + * Handler sending logs to the ChromePHP extension (http://www.chromephp.com/) + * + * This also works out of the box with Firefox 43+ + * + * @author Christophe Coevoet + * + * @phpstan-import-type Record from \Monolog\Logger + */ +class ChromePHPHandler extends \FluentSmtpLib\Monolog\Handler\AbstractProcessingHandler +{ + use WebRequestRecognizerTrait; + /** + * Version of the extension + */ + protected const VERSION = '4.0'; + /** + * Header name + */ + protected const HEADER_NAME = 'X-ChromeLogger-Data'; + /** + * Regular expression to detect supported browsers (matches any Chrome, or Firefox 43+) + */ + protected const USER_AGENT_REGEX = '{\\b(?:Chrome/\\d+(?:\\.\\d+)*|HeadlessChrome|Firefox/(?:4[3-9]|[5-9]\\d|\\d{3,})(?:\\.\\d)*)\\b}'; + /** @var bool */ + protected static $initialized = \false; + /** + * Tracks whether we sent too much data + * + * Chrome limits the headers to 4KB, so when we sent 3KB we stop sending + * + * @var bool + */ + protected static $overflowed = \false; + /** @var mixed[] */ + protected static $json = ['version' => self::VERSION, 'columns' => ['label', 'log', 'backtrace', 'type'], 'rows' => []]; + /** @var bool */ + protected static $sendHeaders = \true; + public function __construct($level = \FluentSmtpLib\Monolog\Logger::DEBUG, bool $bubble = \true) + { + parent::__construct($level, $bubble); + if (!\function_exists('json_encode')) { + throw new \RuntimeException('PHP\'s json extension is required to use Monolog\'s ChromePHPHandler'); + } + } + /** + * {@inheritDoc} + */ + public function handleBatch(array $records) : void + { + if (!$this->isWebRequest()) { + return; + } + $messages = []; + foreach ($records as $record) { + if ($record['level'] < $this->level) { + continue; + } + /** @var Record $message */ + $message = $this->processRecord($record); + $messages[] = $message; + } + if (!empty($messages)) { + $messages = $this->getFormatter()->formatBatch($messages); + self::$json['rows'] = \array_merge(self::$json['rows'], $messages); + $this->send(); + } + } + /** + * {@inheritDoc} + */ + protected function getDefaultFormatter() : \FluentSmtpLib\Monolog\Formatter\FormatterInterface + { + return new \FluentSmtpLib\Monolog\Formatter\ChromePHPFormatter(); + } + /** + * Creates & sends header for a record + * + * @see sendHeader() + * @see send() + */ + protected function write(array $record) : void + { + if (!$this->isWebRequest()) { + return; + } + self::$json['rows'][] = $record['formatted']; + $this->send(); + } + /** + * Sends the log header + * + * @see sendHeader() + */ + protected function send() : void + { + if (self::$overflowed || !self::$sendHeaders) { + return; + } + if (!self::$initialized) { + self::$initialized = \true; + self::$sendHeaders = $this->headersAccepted(); + if (!self::$sendHeaders) { + return; + } + self::$json['request_uri'] = $_SERVER['REQUEST_URI'] ?? ''; + } + $json = \FluentSmtpLib\Monolog\Utils::jsonEncode(self::$json, \FluentSmtpLib\Monolog\Utils::DEFAULT_JSON_FLAGS & ~\JSON_UNESCAPED_UNICODE, \true); + $data = \base64_encode($json); + if (\strlen($data) > 3 * 1024) { + self::$overflowed = \true; + $record = ['message' => 'Incomplete logs, chrome header size limit reached', 'context' => [], 'level' => \FluentSmtpLib\Monolog\Logger::WARNING, 'level_name' => \FluentSmtpLib\Monolog\Logger::getLevelName(\FluentSmtpLib\Monolog\Logger::WARNING), 'channel' => 'monolog', 'datetime' => new \DateTimeImmutable(), 'extra' => []]; + self::$json['rows'][\count(self::$json['rows']) - 1] = $this->getFormatter()->format($record); + $json = \FluentSmtpLib\Monolog\Utils::jsonEncode(self::$json, \FluentSmtpLib\Monolog\Utils::DEFAULT_JSON_FLAGS & ~\JSON_UNESCAPED_UNICODE, \true); + $data = \base64_encode($json); + } + if (\trim($data) !== '') { + $this->sendHeader(static::HEADER_NAME, $data); + } + } + /** + * Send header string to the client + */ + protected function sendHeader(string $header, string $content) : void + { + if (!\headers_sent() && self::$sendHeaders) { + \header(\sprintf('%s: %s', $header, $content)); + } + } + /** + * Verifies if the headers are accepted by the current user agent + */ + protected function headersAccepted() : bool + { + if (empty($_SERVER['HTTP_USER_AGENT'])) { + return \false; + } + return \preg_match(static::USER_AGENT_REGEX, $_SERVER['HTTP_USER_AGENT']) === 1; + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/CouchDBHandler.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/CouchDBHandler.php new file mode 100644 index 0000000..994d3b5 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/CouchDBHandler.php @@ -0,0 +1,56 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace FluentSmtpLib\Monolog\Handler; + +use FluentSmtpLib\Monolog\Formatter\FormatterInterface; +use FluentSmtpLib\Monolog\Formatter\JsonFormatter; +use FluentSmtpLib\Monolog\Logger; +/** + * CouchDB handler + * + * @author Markus Bachmann + */ +class CouchDBHandler extends \FluentSmtpLib\Monolog\Handler\AbstractProcessingHandler +{ + /** @var mixed[] */ + private $options; + /** + * @param mixed[] $options + */ + public function __construct(array $options = [], $level = \FluentSmtpLib\Monolog\Logger::DEBUG, bool $bubble = \true) + { + $this->options = \array_merge(['host' => 'localhost', 'port' => 5984, 'dbname' => 'logger', 'username' => null, 'password' => null], $options); + parent::__construct($level, $bubble); + } + /** + * {@inheritDoc} + */ + protected function write(array $record) : void + { + $basicAuth = null; + if ($this->options['username']) { + $basicAuth = \sprintf('%s:%s@', $this->options['username'], $this->options['password']); + } + $url = 'http://' . $basicAuth . $this->options['host'] . ':' . $this->options['port'] . '/' . $this->options['dbname']; + $context = \stream_context_create(['http' => ['method' => 'POST', 'content' => $record['formatted'], 'ignore_errors' => \true, 'max_redirects' => 0, 'header' => 'Content-type: application/json']]); + if (\false === @\file_get_contents($url, \false, $context)) { + throw new \RuntimeException(\sprintf('Could not connect to %s', $url)); + } + } + /** + * {@inheritDoc} + */ + protected function getDefaultFormatter() : \FluentSmtpLib\Monolog\Formatter\FormatterInterface + { + return new \FluentSmtpLib\Monolog\Formatter\JsonFormatter(\FluentSmtpLib\Monolog\Formatter\JsonFormatter::BATCH_MODE_JSON, \false); + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/CubeHandler.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/CubeHandler.php new file mode 100644 index 0000000..8953304 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/CubeHandler.php @@ -0,0 +1,138 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace FluentSmtpLib\Monolog\Handler; + +use FluentSmtpLib\Monolog\Logger; +use FluentSmtpLib\Monolog\Utils; +/** + * Logs to Cube. + * + * @link https://github.com/square/cube/wiki + * @author Wan Chen + * @deprecated Since 2.8.0 and 3.2.0, Cube appears abandoned and thus we will drop this handler in Monolog 4 + */ +class CubeHandler extends \FluentSmtpLib\Monolog\Handler\AbstractProcessingHandler +{ + /** @var resource|\Socket|null */ + private $udpConnection = null; + /** @var resource|\CurlHandle|null */ + private $httpConnection = null; + /** @var string */ + private $scheme; + /** @var string */ + private $host; + /** @var int */ + private $port; + /** @var string[] */ + private $acceptedSchemes = ['http', 'udp']; + /** + * Create a Cube handler + * + * @throws \UnexpectedValueException when given url is not a valid url. + * A valid url must consist of three parts : protocol://host:port + * Only valid protocols used by Cube are http and udp + */ + public function __construct(string $url, $level = \FluentSmtpLib\Monolog\Logger::DEBUG, bool $bubble = \true) + { + $urlInfo = \parse_url($url); + if ($urlInfo === \false || !isset($urlInfo['scheme'], $urlInfo['host'], $urlInfo['port'])) { + throw new \UnexpectedValueException('URL "' . $url . '" is not valid'); + } + if (!\in_array($urlInfo['scheme'], $this->acceptedSchemes)) { + throw new \UnexpectedValueException('Invalid protocol (' . $urlInfo['scheme'] . ').' . ' Valid options are ' . \implode(', ', $this->acceptedSchemes)); + } + $this->scheme = $urlInfo['scheme']; + $this->host = $urlInfo['host']; + $this->port = (int) $urlInfo['port']; + parent::__construct($level, $bubble); + } + /** + * Establish a connection to an UDP socket + * + * @throws \LogicException when unable to connect to the socket + * @throws MissingExtensionException when there is no socket extension + */ + protected function connectUdp() : void + { + if (!\extension_loaded('sockets')) { + throw new \FluentSmtpLib\Monolog\Handler\MissingExtensionException('The sockets extension is required to use udp URLs with the CubeHandler'); + } + $udpConnection = \socket_create(\AF_INET, \SOCK_DGRAM, 0); + if (\false === $udpConnection) { + throw new \LogicException('Unable to create a socket'); + } + $this->udpConnection = $udpConnection; + if (!\socket_connect($this->udpConnection, $this->host, $this->port)) { + throw new \LogicException('Unable to connect to the socket at ' . $this->host . ':' . $this->port); + } + } + /** + * Establish a connection to an http server + * + * @throws \LogicException when unable to connect to the socket + * @throws MissingExtensionException when no curl extension + */ + protected function connectHttp() : void + { + if (!\extension_loaded('curl')) { + throw new \FluentSmtpLib\Monolog\Handler\MissingExtensionException('The curl extension is required to use http URLs with the CubeHandler'); + } + $httpConnection = \curl_init('http://' . $this->host . ':' . $this->port . '/1.0/event/put'); + if (\false === $httpConnection) { + throw new \LogicException('Unable to connect to ' . $this->host . ':' . $this->port); + } + $this->httpConnection = $httpConnection; + \curl_setopt($this->httpConnection, \CURLOPT_CUSTOMREQUEST, "POST"); + \curl_setopt($this->httpConnection, \CURLOPT_RETURNTRANSFER, \true); + } + /** + * {@inheritDoc} + */ + protected function write(array $record) : void + { + $date = $record['datetime']; + $data = ['time' => $date->format('Y-m-d\\TH:i:s.uO')]; + unset($record['datetime']); + if (isset($record['context']['type'])) { + $data['type'] = $record['context']['type']; + unset($record['context']['type']); + } else { + $data['type'] = $record['channel']; + } + $data['data'] = $record['context']; + $data['data']['level'] = $record['level']; + if ($this->scheme === 'http') { + $this->writeHttp(\FluentSmtpLib\Monolog\Utils::jsonEncode($data)); + } else { + $this->writeUdp(\FluentSmtpLib\Monolog\Utils::jsonEncode($data)); + } + } + private function writeUdp(string $data) : void + { + if (!$this->udpConnection) { + $this->connectUdp(); + } + \socket_send($this->udpConnection, $data, \strlen($data), 0); + } + private function writeHttp(string $data) : void + { + if (!$this->httpConnection) { + $this->connectHttp(); + } + if (null === $this->httpConnection) { + throw new \LogicException('No connection could be established'); + } + \curl_setopt($this->httpConnection, \CURLOPT_POSTFIELDS, '[' . $data . ']'); + \curl_setopt($this->httpConnection, \CURLOPT_HTTPHEADER, ['Content-Type: application/json', 'Content-Length: ' . \strlen('[' . $data . ']')]); + \FluentSmtpLib\Monolog\Handler\Curl\Util::execute($this->httpConnection, 5, \false); + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/Curl/Util.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/Curl/Util.php new file mode 100644 index 0000000..5de3b3b --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/Curl/Util.php @@ -0,0 +1,54 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace FluentSmtpLib\Monolog\Handler\Curl; + +use CurlHandle; +/** + * This class is marked as internal and it is not under the BC promise of the package. + * + * @internal + */ +final class Util +{ + /** @var array */ + private static $retriableErrorCodes = [\CURLE_COULDNT_RESOLVE_HOST, \CURLE_COULDNT_CONNECT, \CURLE_HTTP_NOT_FOUND, \CURLE_READ_ERROR, \CURLE_OPERATION_TIMEOUTED, \CURLE_HTTP_POST_ERROR, \CURLE_SSL_CONNECT_ERROR]; + /** + * Executes a CURL request with optional retries and exception on failure + * + * @param resource|CurlHandle $ch curl handler + * @param int $retries + * @param bool $closeAfterDone + * @return bool|string @see curl_exec + */ + public static function execute($ch, int $retries = 5, bool $closeAfterDone = \true) + { + while ($retries--) { + $curlResponse = \curl_exec($ch); + if ($curlResponse === \false) { + $curlErrno = \curl_errno($ch); + if (\false === \in_array($curlErrno, self::$retriableErrorCodes, \true) || !$retries) { + $curlError = \curl_error($ch); + if ($closeAfterDone) { + \curl_close($ch); + } + throw new \RuntimeException(\sprintf('Curl error (code %d): %s', $curlErrno, $curlError)); + } + continue; + } + if ($closeAfterDone) { + \curl_close($ch); + } + return $curlResponse; + } + return \false; + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/DeduplicationHandler.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/DeduplicationHandler.php new file mode 100644 index 0000000..53f8ec5 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/DeduplicationHandler.php @@ -0,0 +1,157 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace FluentSmtpLib\Monolog\Handler; + +use FluentSmtpLib\Monolog\Logger; +use FluentSmtpLib\Psr\Log\LogLevel; +/** + * Simple handler wrapper that deduplicates log records across multiple requests + * + * It also includes the BufferHandler functionality and will buffer + * all messages until the end of the request or flush() is called. + * + * This works by storing all log records' messages above $deduplicationLevel + * to the file specified by $deduplicationStore. When further logs come in at the end of the + * request (or when flush() is called), all those above $deduplicationLevel are checked + * against the existing stored logs. If they match and the timestamps in the stored log is + * not older than $time seconds, the new log record is discarded. If no log record is new, the + * whole data set is discarded. + * + * This is mainly useful in combination with Mail handlers or things like Slack or HipChat handlers + * that send messages to people, to avoid spamming with the same message over and over in case of + * a major component failure like a database server being down which makes all requests fail in the + * same way. + * + * @author Jordi Boggiano + * + * @phpstan-import-type Record from \Monolog\Logger + * @phpstan-import-type LevelName from \Monolog\Logger + * @phpstan-import-type Level from \Monolog\Logger + */ +class DeduplicationHandler extends \FluentSmtpLib\Monolog\Handler\BufferHandler +{ + /** + * @var string + */ + protected $deduplicationStore; + /** + * @var Level + */ + protected $deduplicationLevel; + /** + * @var int + */ + protected $time; + /** + * @var bool + */ + private $gc = \false; + /** + * @param HandlerInterface $handler Handler. + * @param string $deduplicationStore The file/path where the deduplication log should be kept + * @param string|int $deduplicationLevel The minimum logging level for log records to be looked at for deduplication purposes + * @param int $time The period (in seconds) during which duplicate entries should be suppressed after a given log is sent through + * @param bool $bubble Whether the messages that are handled can bubble up the stack or not + * + * @phpstan-param Level|LevelName|LogLevel::* $deduplicationLevel + */ + public function __construct(\FluentSmtpLib\Monolog\Handler\HandlerInterface $handler, ?string $deduplicationStore = null, $deduplicationLevel = \FluentSmtpLib\Monolog\Logger::ERROR, int $time = 60, bool $bubble = \true) + { + parent::__construct($handler, 0, \FluentSmtpLib\Monolog\Logger::DEBUG, $bubble, \false); + $this->deduplicationStore = $deduplicationStore === null ? \sys_get_temp_dir() . '/monolog-dedup-' . \substr(\md5(__FILE__), 0, 20) . '.log' : $deduplicationStore; + $this->deduplicationLevel = \FluentSmtpLib\Monolog\Logger::toMonologLevel($deduplicationLevel); + $this->time = $time; + } + public function flush() : void + { + if ($this->bufferSize === 0) { + return; + } + $passthru = null; + foreach ($this->buffer as $record) { + if ($record['level'] >= $this->deduplicationLevel) { + $passthru = $passthru || !$this->isDuplicate($record); + if ($passthru) { + $this->appendRecord($record); + } + } + } + // default of null is valid as well as if no record matches duplicationLevel we just pass through + if ($passthru === \true || $passthru === null) { + $this->handler->handleBatch($this->buffer); + } + $this->clear(); + if ($this->gc) { + $this->collectLogs(); + } + } + /** + * @phpstan-param Record $record + */ + private function isDuplicate(array $record) : bool + { + if (!\file_exists($this->deduplicationStore)) { + return \false; + } + $store = \file($this->deduplicationStore, \FILE_IGNORE_NEW_LINES | \FILE_SKIP_EMPTY_LINES); + if (!\is_array($store)) { + return \false; + } + $yesterday = \time() - 86400; + $timestampValidity = $record['datetime']->getTimestamp() - $this->time; + $expectedMessage = \preg_replace('{[\\r\\n].*}', '', $record['message']); + for ($i = \count($store) - 1; $i >= 0; $i--) { + list($timestamp, $level, $message) = \explode(':', $store[$i], 3); + if ($level === $record['level_name'] && $message === $expectedMessage && $timestamp > $timestampValidity) { + return \true; + } + if ($timestamp < $yesterday) { + $this->gc = \true; + } + } + return \false; + } + private function collectLogs() : void + { + if (!\file_exists($this->deduplicationStore)) { + return; + } + $handle = \fopen($this->deduplicationStore, 'rw+'); + if (!$handle) { + throw new \RuntimeException('Failed to open file for reading and writing: ' . $this->deduplicationStore); + } + \flock($handle, \LOCK_EX); + $validLogs = []; + $timestampValidity = \time() - $this->time; + while (!\feof($handle)) { + $log = \fgets($handle); + if ($log && \substr($log, 0, 10) >= $timestampValidity) { + $validLogs[] = $log; + } + } + \ftruncate($handle, 0); + \rewind($handle); + foreach ($validLogs as $log) { + \fwrite($handle, $log); + } + \flock($handle, \LOCK_UN); + \fclose($handle); + $this->gc = \false; + } + /** + * @phpstan-param Record $record + */ + private function appendRecord(array $record) : void + { + \file_put_contents($this->deduplicationStore, $record['datetime']->getTimestamp() . ':' . $record['level_name'] . ':' . \preg_replace('{[\\r\\n].*}', '', $record['message']) . "\n", \FILE_APPEND); + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/DoctrineCouchDBHandler.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/DoctrineCouchDBHandler.php new file mode 100644 index 0000000..cef6a41 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/DoctrineCouchDBHandler.php @@ -0,0 +1,43 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace FluentSmtpLib\Monolog\Handler; + +use FluentSmtpLib\Monolog\Logger; +use FluentSmtpLib\Monolog\Formatter\NormalizerFormatter; +use FluentSmtpLib\Monolog\Formatter\FormatterInterface; +use FluentSmtpLib\Doctrine\CouchDB\CouchDBClient; +/** + * CouchDB handler for Doctrine CouchDB ODM + * + * @author Markus Bachmann + */ +class DoctrineCouchDBHandler extends \FluentSmtpLib\Monolog\Handler\AbstractProcessingHandler +{ + /** @var CouchDBClient */ + private $client; + public function __construct(\FluentSmtpLib\Doctrine\CouchDB\CouchDBClient $client, $level = \FluentSmtpLib\Monolog\Logger::DEBUG, bool $bubble = \true) + { + $this->client = $client; + parent::__construct($level, $bubble); + } + /** + * {@inheritDoc} + */ + protected function write(array $record) : void + { + $this->client->postDocument($record['formatted']); + } + protected function getDefaultFormatter() : \FluentSmtpLib\Monolog\Formatter\FormatterInterface + { + return new \FluentSmtpLib\Monolog\Formatter\NormalizerFormatter(); + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/DynamoDbHandler.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/DynamoDbHandler.php new file mode 100644 index 0000000..5740bcb --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/DynamoDbHandler.php @@ -0,0 +1,89 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace FluentSmtpLib\Monolog\Handler; + +use FluentSmtpLib\Aws\Sdk; +use FluentSmtpLib\Aws\DynamoDb\DynamoDbClient; +use FluentSmtpLib\Monolog\Formatter\FormatterInterface; +use FluentSmtpLib\Aws\DynamoDb\Marshaler; +use FluentSmtpLib\Monolog\Formatter\ScalarFormatter; +use FluentSmtpLib\Monolog\Logger; +/** + * Amazon DynamoDB handler (http://aws.amazon.com/dynamodb/) + * + * @link https://github.com/aws/aws-sdk-php/ + * @author Andrew Lawson + */ +class DynamoDbHandler extends \FluentSmtpLib\Monolog\Handler\AbstractProcessingHandler +{ + public const DATE_FORMAT = 'Y-m-d\\TH:i:s.uO'; + /** + * @var DynamoDbClient + */ + protected $client; + /** + * @var string + */ + protected $table; + /** + * @var int + */ + protected $version; + /** + * @var Marshaler + */ + protected $marshaler; + public function __construct(\FluentSmtpLib\Aws\DynamoDb\DynamoDbClient $client, string $table, $level = \FluentSmtpLib\Monolog\Logger::DEBUG, bool $bubble = \true) + { + /** @phpstan-ignore-next-line */ + if (\defined('Aws\\Sdk::VERSION') && \version_compare(\FluentSmtpLib\Aws\Sdk::VERSION, '3.0', '>=')) { + $this->version = 3; + $this->marshaler = new \FluentSmtpLib\Aws\DynamoDb\Marshaler(); + } else { + $this->version = 2; + } + $this->client = $client; + $this->table = $table; + parent::__construct($level, $bubble); + } + /** + * {@inheritDoc} + */ + protected function write(array $record) : void + { + $filtered = $this->filterEmptyFields($record['formatted']); + if ($this->version === 3) { + $formatted = $this->marshaler->marshalItem($filtered); + } else { + /** @phpstan-ignore-next-line */ + $formatted = $this->client->formatAttributes($filtered); + } + $this->client->putItem(['TableName' => $this->table, 'Item' => $formatted]); + } + /** + * @param mixed[] $record + * @return mixed[] + */ + protected function filterEmptyFields(array $record) : array + { + return \array_filter($record, function ($value) { + return !empty($value) || \false === $value || 0 === $value; + }); + } + /** + * {@inheritDoc} + */ + protected function getDefaultFormatter() : \FluentSmtpLib\Monolog\Formatter\FormatterInterface + { + return new \FluentSmtpLib\Monolog\Formatter\ScalarFormatter(self::DATE_FORMAT); + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/ElasticaHandler.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/ElasticaHandler.php new file mode 100644 index 0000000..3e839d6 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/ElasticaHandler.php @@ -0,0 +1,118 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace FluentSmtpLib\Monolog\Handler; + +use FluentSmtpLib\Elastica\Document; +use FluentSmtpLib\Monolog\Formatter\FormatterInterface; +use FluentSmtpLib\Monolog\Formatter\ElasticaFormatter; +use FluentSmtpLib\Monolog\Logger; +use FluentSmtpLib\Elastica\Client; +use FluentSmtpLib\Elastica\Exception\ExceptionInterface; +/** + * Elastic Search handler + * + * Usage example: + * + * $client = new \Elastica\Client(); + * $options = array( + * 'index' => 'elastic_index_name', + * 'type' => 'elastic_doc_type', Types have been removed in Elastica 7 + * ); + * $handler = new ElasticaHandler($client, $options); + * $log = new Logger('application'); + * $log->pushHandler($handler); + * + * @author Jelle Vink + */ +class ElasticaHandler extends \FluentSmtpLib\Monolog\Handler\AbstractProcessingHandler +{ + /** + * @var Client + */ + protected $client; + /** + * @var mixed[] Handler config options + */ + protected $options = []; + /** + * @param Client $client Elastica Client object + * @param mixed[] $options Handler configuration + */ + public function __construct(\FluentSmtpLib\Elastica\Client $client, array $options = [], $level = \FluentSmtpLib\Monolog\Logger::DEBUG, bool $bubble = \true) + { + parent::__construct($level, $bubble); + $this->client = $client; + $this->options = \array_merge([ + 'index' => 'monolog', + // Elastic index name + 'type' => 'record', + // Elastic document type + 'ignore_error' => \false, + ], $options); + } + /** + * {@inheritDoc} + */ + protected function write(array $record) : void + { + $this->bulkSend([$record['formatted']]); + } + /** + * {@inheritDoc} + */ + public function setFormatter(\FluentSmtpLib\Monolog\Formatter\FormatterInterface $formatter) : \FluentSmtpLib\Monolog\Handler\HandlerInterface + { + if ($formatter instanceof \FluentSmtpLib\Monolog\Formatter\ElasticaFormatter) { + return parent::setFormatter($formatter); + } + throw new \InvalidArgumentException('ElasticaHandler is only compatible with ElasticaFormatter'); + } + /** + * @return mixed[] + */ + public function getOptions() : array + { + return $this->options; + } + /** + * {@inheritDoc} + */ + protected function getDefaultFormatter() : \FluentSmtpLib\Monolog\Formatter\FormatterInterface + { + return new \FluentSmtpLib\Monolog\Formatter\ElasticaFormatter($this->options['index'], $this->options['type']); + } + /** + * {@inheritDoc} + */ + public function handleBatch(array $records) : void + { + $documents = $this->getFormatter()->formatBatch($records); + $this->bulkSend($documents); + } + /** + * Use Elasticsearch bulk API to send list of documents + * + * @param Document[] $documents + * + * @throws \RuntimeException + */ + protected function bulkSend(array $documents) : void + { + try { + $this->client->addDocuments($documents); + } catch (\FluentSmtpLib\Elastica\Exception\ExceptionInterface $e) { + if (!$this->options['ignore_error']) { + throw new \RuntimeException("Error sending messages to Elasticsearch", 0, $e); + } + } + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/ElasticsearchHandler.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/ElasticsearchHandler.php new file mode 100644 index 0000000..ad5aad7 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/ElasticsearchHandler.php @@ -0,0 +1,186 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace FluentSmtpLib\Monolog\Handler; + +use FluentSmtpLib\Elastic\Elasticsearch\Response\Elasticsearch; +use Throwable; +use RuntimeException; +use FluentSmtpLib\Monolog\Logger; +use FluentSmtpLib\Monolog\Formatter\FormatterInterface; +use FluentSmtpLib\Monolog\Formatter\ElasticsearchFormatter; +use InvalidArgumentException; +use FluentSmtpLib\Elasticsearch\Common\Exceptions\RuntimeException as ElasticsearchRuntimeException; +use FluentSmtpLib\Elasticsearch\Client; +use FluentSmtpLib\Elastic\Elasticsearch\Exception\InvalidArgumentException as ElasticInvalidArgumentException; +use FluentSmtpLib\Elastic\Elasticsearch\Client as Client8; +/** + * Elasticsearch handler + * + * @link https://www.elastic.co/guide/en/elasticsearch/client/php-api/current/index.html + * + * Simple usage example: + * + * $client = \Elasticsearch\ClientBuilder::create() + * ->setHosts($hosts) + * ->build(); + * + * $options = array( + * 'index' => 'elastic_index_name', + * 'type' => 'elastic_doc_type', + * ); + * $handler = new ElasticsearchHandler($client, $options); + * $log = new Logger('application'); + * $log->pushHandler($handler); + * + * @author Avtandil Kikabidze + */ +class ElasticsearchHandler extends \FluentSmtpLib\Monolog\Handler\AbstractProcessingHandler +{ + /** + * @var Client|Client8 + */ + protected $client; + /** + * @var mixed[] Handler config options + */ + protected $options = []; + /** + * @var bool + */ + private $needsType; + /** + * @param Client|Client8 $client Elasticsearch Client object + * @param mixed[] $options Handler configuration + */ + public function __construct($client, array $options = [], $level = \FluentSmtpLib\Monolog\Logger::DEBUG, bool $bubble = \true) + { + if (!$client instanceof \FluentSmtpLib\Elasticsearch\Client && !$client instanceof \FluentSmtpLib\Elastic\Elasticsearch\Client) { + throw new \TypeError('Elasticsearch\\Client or Elastic\\Elasticsearch\\Client instance required'); + } + parent::__construct($level, $bubble); + $this->client = $client; + $this->options = \array_merge([ + 'index' => 'monolog', + // Elastic index name + 'type' => '_doc', + // Elastic document type + 'ignore_error' => \false, + ], $options); + if ($client instanceof \FluentSmtpLib\Elastic\Elasticsearch\Client || $client::VERSION[0] === '7') { + $this->needsType = \false; + // force the type to _doc for ES8/ES7 + $this->options['type'] = '_doc'; + } else { + $this->needsType = \true; + } + } + /** + * {@inheritDoc} + */ + protected function write(array $record) : void + { + $this->bulkSend([$record['formatted']]); + } + /** + * {@inheritDoc} + */ + public function setFormatter(\FluentSmtpLib\Monolog\Formatter\FormatterInterface $formatter) : \FluentSmtpLib\Monolog\Handler\HandlerInterface + { + if ($formatter instanceof \FluentSmtpLib\Monolog\Formatter\ElasticsearchFormatter) { + return parent::setFormatter($formatter); + } + throw new \InvalidArgumentException('ElasticsearchHandler is only compatible with ElasticsearchFormatter'); + } + /** + * Getter options + * + * @return mixed[] + */ + public function getOptions() : array + { + return $this->options; + } + /** + * {@inheritDoc} + */ + protected function getDefaultFormatter() : \FluentSmtpLib\Monolog\Formatter\FormatterInterface + { + return new \FluentSmtpLib\Monolog\Formatter\ElasticsearchFormatter($this->options['index'], $this->options['type']); + } + /** + * {@inheritDoc} + */ + public function handleBatch(array $records) : void + { + $documents = $this->getFormatter()->formatBatch($records); + $this->bulkSend($documents); + } + /** + * Use Elasticsearch bulk API to send list of documents + * + * @param array[] $records Records + _index/_type keys + * @throws \RuntimeException + */ + protected function bulkSend(array $records) : void + { + try { + $params = ['body' => []]; + foreach ($records as $record) { + $params['body'][] = ['index' => $this->needsType ? ['_index' => $record['_index'], '_type' => $record['_type']] : ['_index' => $record['_index']]]; + unset($record['_index'], $record['_type']); + $params['body'][] = $record; + } + /** @var Elasticsearch */ + $responses = $this->client->bulk($params); + if ($responses['errors'] === \true) { + throw $this->createExceptionFromResponses($responses); + } + } catch (\Throwable $e) { + if (!$this->options['ignore_error']) { + throw new \RuntimeException('Error sending messages to Elasticsearch', 0, $e); + } + } + } + /** + * Creates elasticsearch exception from responses array + * + * Only the first error is converted into an exception. + * + * @param mixed[]|Elasticsearch $responses returned by $this->client->bulk() + */ + protected function createExceptionFromResponses($responses) : \Throwable + { + // @phpstan-ignore offsetAccess.nonOffsetAccessible + foreach ($responses['items'] ?? [] as $item) { + if (isset($item['index']['error'])) { + return $this->createExceptionFromError($item['index']['error']); + } + } + if (\class_exists(\FluentSmtpLib\Elastic\Elasticsearch\Exception\InvalidArgumentException::class)) { + return new \FluentSmtpLib\Elastic\Elasticsearch\Exception\InvalidArgumentException('Elasticsearch failed to index one or more records.'); + } + return new \FluentSmtpLib\Elasticsearch\Common\Exceptions\RuntimeException('Elasticsearch failed to index one or more records.'); + } + /** + * Creates elasticsearch exception from error array + * + * @param mixed[] $error + */ + protected function createExceptionFromError(array $error) : \Throwable + { + $previous = isset($error['caused_by']) ? $this->createExceptionFromError($error['caused_by']) : null; + if (\class_exists(\FluentSmtpLib\Elastic\Elasticsearch\Exception\InvalidArgumentException::class)) { + return new \FluentSmtpLib\Elastic\Elasticsearch\Exception\InvalidArgumentException($error['type'] . ': ' . $error['reason'], 0, $previous); + } + return new \FluentSmtpLib\Elasticsearch\Common\Exceptions\RuntimeException($error['type'] . ': ' . $error['reason'], 0, $previous); + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/ErrorLogHandler.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/ErrorLogHandler.php new file mode 100644 index 0000000..273519c --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/ErrorLogHandler.php @@ -0,0 +1,77 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace FluentSmtpLib\Monolog\Handler; + +use FluentSmtpLib\Monolog\Formatter\LineFormatter; +use FluentSmtpLib\Monolog\Formatter\FormatterInterface; +use FluentSmtpLib\Monolog\Logger; +use FluentSmtpLib\Monolog\Utils; +/** + * Stores to PHP error_log() handler. + * + * @author Elan Ruusamäe + */ +class ErrorLogHandler extends \FluentSmtpLib\Monolog\Handler\AbstractProcessingHandler +{ + public const OPERATING_SYSTEM = 0; + public const SAPI = 4; + /** @var int */ + protected $messageType; + /** @var bool */ + protected $expandNewlines; + /** + * @param int $messageType Says where the error should go. + * @param bool $expandNewlines If set to true, newlines in the message will be expanded to be take multiple log entries + */ + public function __construct(int $messageType = self::OPERATING_SYSTEM, $level = \FluentSmtpLib\Monolog\Logger::DEBUG, bool $bubble = \true, bool $expandNewlines = \false) + { + parent::__construct($level, $bubble); + if (\false === \in_array($messageType, self::getAvailableTypes(), \true)) { + $message = \sprintf('The given message type "%s" is not supported', \print_r($messageType, \true)); + throw new \InvalidArgumentException($message); + } + $this->messageType = $messageType; + $this->expandNewlines = $expandNewlines; + } + /** + * @return int[] With all available types + */ + public static function getAvailableTypes() : array + { + return [self::OPERATING_SYSTEM, self::SAPI]; + } + /** + * {@inheritDoc} + */ + protected function getDefaultFormatter() : \FluentSmtpLib\Monolog\Formatter\FormatterInterface + { + return new \FluentSmtpLib\Monolog\Formatter\LineFormatter('[%datetime%] %channel%.%level_name%: %message% %context% %extra%'); + } + /** + * {@inheritDoc} + */ + protected function write(array $record) : void + { + if (!$this->expandNewlines) { + \error_log((string) $record['formatted'], $this->messageType); + return; + } + $lines = \preg_split('{[\\r\\n]+}', (string) $record['formatted']); + if ($lines === \false) { + $pcreErrorCode = \preg_last_error(); + throw new \RuntimeException('Failed to preg_split formatted string: ' . $pcreErrorCode . ' / ' . \FluentSmtpLib\Monolog\Utils::pcreLastErrorMessage($pcreErrorCode)); + } + foreach ($lines as $line) { + \error_log($line, $this->messageType); + } + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/FallbackGroupHandler.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/FallbackGroupHandler.php new file mode 100644 index 0000000..a651c6a --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/FallbackGroupHandler.php @@ -0,0 +1,67 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace FluentSmtpLib\Monolog\Handler; + +use Throwable; +/** + * Forwards records to at most one handler + * + * If a handler fails, the exception is suppressed and the record is forwarded to the next handler. + * + * As soon as one handler handles a record successfully, the handling stops there. + * + * @phpstan-import-type Record from \Monolog\Logger + */ +class FallbackGroupHandler extends \FluentSmtpLib\Monolog\Handler\GroupHandler +{ + /** + * {@inheritDoc} + */ + public function handle(array $record) : bool + { + if ($this->processors) { + /** @var Record $record */ + $record = $this->processRecord($record); + } + foreach ($this->handlers as $handler) { + try { + $handler->handle($record); + break; + } catch (\Throwable $e) { + // What throwable? + } + } + return \false === $this->bubble; + } + /** + * {@inheritDoc} + */ + public function handleBatch(array $records) : void + { + if ($this->processors) { + $processed = []; + foreach ($records as $record) { + $processed[] = $this->processRecord($record); + } + /** @var Record[] $records */ + $records = $processed; + } + foreach ($this->handlers as $handler) { + try { + $handler->handleBatch($records); + break; + } catch (\Throwable $e) { + // What throwable? + } + } + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/FilterHandler.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/FilterHandler.php new file mode 100644 index 0000000..7470f56 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/FilterHandler.php @@ -0,0 +1,187 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace FluentSmtpLib\Monolog\Handler; + +use FluentSmtpLib\Monolog\Logger; +use FluentSmtpLib\Monolog\ResettableInterface; +use FluentSmtpLib\Monolog\Formatter\FormatterInterface; +use FluentSmtpLib\Psr\Log\LogLevel; +/** + * Simple handler wrapper that filters records based on a list of levels + * + * It can be configured with an exact list of levels to allow, or a min/max level. + * + * @author Hennadiy Verkh + * @author Jordi Boggiano + * + * @phpstan-import-type Record from \Monolog\Logger + * @phpstan-import-type Level from \Monolog\Logger + * @phpstan-import-type LevelName from \Monolog\Logger + */ +class FilterHandler extends \FluentSmtpLib\Monolog\Handler\Handler implements \FluentSmtpLib\Monolog\Handler\ProcessableHandlerInterface, \FluentSmtpLib\Monolog\ResettableInterface, \FluentSmtpLib\Monolog\Handler\FormattableHandlerInterface +{ + use ProcessableHandlerTrait; + /** + * Handler or factory callable($record, $this) + * + * @var callable|HandlerInterface + * @phpstan-var callable(?Record, HandlerInterface): HandlerInterface|HandlerInterface + */ + protected $handler; + /** + * Minimum level for logs that are passed to handler + * + * @var int[] + * @phpstan-var array + */ + protected $acceptedLevels; + /** + * Whether the messages that are handled can bubble up the stack or not + * + * @var bool + */ + protected $bubble; + /** + * @psalm-param HandlerInterface|callable(?Record, HandlerInterface): HandlerInterface $handler + * + * @param callable|HandlerInterface $handler Handler or factory callable($record|null, $filterHandler). + * @param int|array $minLevelOrList A list of levels to accept or a minimum level if maxLevel is provided + * @param int|string $maxLevel Maximum level to accept, only used if $minLevelOrList is not an array + * @param bool $bubble Whether the messages that are handled can bubble up the stack or not + * + * @phpstan-param Level|LevelName|LogLevel::*|array $minLevelOrList + * @phpstan-param Level|LevelName|LogLevel::* $maxLevel + */ + public function __construct($handler, $minLevelOrList = \FluentSmtpLib\Monolog\Logger::DEBUG, $maxLevel = \FluentSmtpLib\Monolog\Logger::EMERGENCY, bool $bubble = \true) + { + $this->handler = $handler; + $this->bubble = $bubble; + $this->setAcceptedLevels($minLevelOrList, $maxLevel); + if (!$this->handler instanceof \FluentSmtpLib\Monolog\Handler\HandlerInterface && !\is_callable($this->handler)) { + throw new \RuntimeException("The given handler (" . \json_encode($this->handler) . ") is not a callable nor a Monolog\\Handler\\HandlerInterface object"); + } + } + /** + * @phpstan-return array + */ + public function getAcceptedLevels() : array + { + return \array_flip($this->acceptedLevels); + } + /** + * @param int|string|array $minLevelOrList A list of levels to accept or a minimum level or level name if maxLevel is provided + * @param int|string $maxLevel Maximum level or level name to accept, only used if $minLevelOrList is not an array + * + * @phpstan-param Level|LevelName|LogLevel::*|array $minLevelOrList + * @phpstan-param Level|LevelName|LogLevel::* $maxLevel + */ + public function setAcceptedLevels($minLevelOrList = \FluentSmtpLib\Monolog\Logger::DEBUG, $maxLevel = \FluentSmtpLib\Monolog\Logger::EMERGENCY) : self + { + if (\is_array($minLevelOrList)) { + $acceptedLevels = \array_map('Monolog\\Logger::toMonologLevel', $minLevelOrList); + } else { + $minLevelOrList = \FluentSmtpLib\Monolog\Logger::toMonologLevel($minLevelOrList); + $maxLevel = \FluentSmtpLib\Monolog\Logger::toMonologLevel($maxLevel); + $acceptedLevels = \array_values(\array_filter(\FluentSmtpLib\Monolog\Logger::getLevels(), function ($level) use($minLevelOrList, $maxLevel) { + return $level >= $minLevelOrList && $level <= $maxLevel; + })); + } + $this->acceptedLevels = \array_flip($acceptedLevels); + return $this; + } + /** + * {@inheritDoc} + */ + public function isHandling(array $record) : bool + { + return isset($this->acceptedLevels[$record['level']]); + } + /** + * {@inheritDoc} + */ + public function handle(array $record) : bool + { + if (!$this->isHandling($record)) { + return \false; + } + if ($this->processors) { + /** @var Record $record */ + $record = $this->processRecord($record); + } + $this->getHandler($record)->handle($record); + return \false === $this->bubble; + } + /** + * {@inheritDoc} + */ + public function handleBatch(array $records) : void + { + $filtered = []; + foreach ($records as $record) { + if ($this->isHandling($record)) { + $filtered[] = $record; + } + } + if (\count($filtered) > 0) { + $this->getHandler($filtered[\count($filtered) - 1])->handleBatch($filtered); + } + } + /** + * Return the nested handler + * + * If the handler was provided as a factory callable, this will trigger the handler's instantiation. + * + * @return HandlerInterface + * + * @phpstan-param Record $record + */ + public function getHandler(?array $record = null) + { + if (!$this->handler instanceof \FluentSmtpLib\Monolog\Handler\HandlerInterface) { + $this->handler = ($this->handler)($record, $this); + if (!$this->handler instanceof \FluentSmtpLib\Monolog\Handler\HandlerInterface) { + throw new \RuntimeException("The factory callable should return a HandlerInterface"); + } + } + return $this->handler; + } + /** + * {@inheritDoc} + */ + public function setFormatter(\FluentSmtpLib\Monolog\Formatter\FormatterInterface $formatter) : \FluentSmtpLib\Monolog\Handler\HandlerInterface + { + $handler = $this->getHandler(); + if ($handler instanceof \FluentSmtpLib\Monolog\Handler\FormattableHandlerInterface) { + $handler->setFormatter($formatter); + return $this; + } + throw new \UnexpectedValueException('The nested handler of type ' . \get_class($handler) . ' does not support formatters.'); + } + /** + * {@inheritDoc} + */ + public function getFormatter() : \FluentSmtpLib\Monolog\Formatter\FormatterInterface + { + $handler = $this->getHandler(); + if ($handler instanceof \FluentSmtpLib\Monolog\Handler\FormattableHandlerInterface) { + return $handler->getFormatter(); + } + throw new \UnexpectedValueException('The nested handler of type ' . \get_class($handler) . ' does not support formatters.'); + } + public function reset() + { + $this->resetProcessors(); + if ($this->getHandler() instanceof \FluentSmtpLib\Monolog\ResettableInterface) { + $this->getHandler()->reset(); + } + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/FingersCrossed/ActivationStrategyInterface.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/FingersCrossed/ActivationStrategyInterface.php new file mode 100644 index 0000000..3b2bc3c --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/FingersCrossed/ActivationStrategyInterface.php @@ -0,0 +1,29 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace FluentSmtpLib\Monolog\Handler\FingersCrossed; + +/** + * Interface for activation strategies for the FingersCrossedHandler. + * + * @author Johannes M. Schmitt + * + * @phpstan-import-type Record from \Monolog\Logger + */ +interface ActivationStrategyInterface +{ + /** + * Returns whether the given record activates the handler. + * + * @phpstan-param Record $record + */ + public function isHandlerActivated(array $record) : bool; +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/FingersCrossed/ChannelLevelActivationStrategy.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/FingersCrossed/ChannelLevelActivationStrategy.php new file mode 100644 index 0000000..bb65e6a --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/FingersCrossed/ChannelLevelActivationStrategy.php @@ -0,0 +1,72 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace FluentSmtpLib\Monolog\Handler\FingersCrossed; + +use FluentSmtpLib\Monolog\Logger; +use FluentSmtpLib\Psr\Log\LogLevel; +/** + * Channel and Error level based monolog activation strategy. Allows to trigger activation + * based on level per channel. e.g. trigger activation on level 'ERROR' by default, except + * for records of the 'sql' channel; those should trigger activation on level 'WARN'. + * + * Example: + * + * + * $activationStrategy = new ChannelLevelActivationStrategy( + * Logger::CRITICAL, + * array( + * 'request' => Logger::ALERT, + * 'sensitive' => Logger::ERROR, + * ) + * ); + * $handler = new FingersCrossedHandler(new StreamHandler('php://stderr'), $activationStrategy); + * + * + * @author Mike Meessen + * + * @phpstan-import-type Record from \Monolog\Logger + * @phpstan-import-type Level from \Monolog\Logger + * @phpstan-import-type LevelName from \Monolog\Logger + */ +class ChannelLevelActivationStrategy implements \FluentSmtpLib\Monolog\Handler\FingersCrossed\ActivationStrategyInterface +{ + /** + * @var Level + */ + private $defaultActionLevel; + /** + * @var array + */ + private $channelToActionLevel; + /** + * @param int|string $defaultActionLevel The default action level to be used if the record's category doesn't match any + * @param array $channelToActionLevel An array that maps channel names to action levels. + * + * @phpstan-param array $channelToActionLevel + * @phpstan-param Level|LevelName|LogLevel::* $defaultActionLevel + */ + public function __construct($defaultActionLevel, array $channelToActionLevel = []) + { + $this->defaultActionLevel = \FluentSmtpLib\Monolog\Logger::toMonologLevel($defaultActionLevel); + $this->channelToActionLevel = \array_map('Monolog\\Logger::toMonologLevel', $channelToActionLevel); + } + /** + * @phpstan-param Record $record + */ + public function isHandlerActivated(array $record) : bool + { + if (isset($this->channelToActionLevel[$record['channel']])) { + return $record['level'] >= $this->channelToActionLevel[$record['channel']]; + } + return $record['level'] >= $this->defaultActionLevel; + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/FingersCrossed/ErrorLevelActivationStrategy.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/FingersCrossed/ErrorLevelActivationStrategy.php new file mode 100644 index 0000000..e3e87c7 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/FingersCrossed/ErrorLevelActivationStrategy.php @@ -0,0 +1,43 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace FluentSmtpLib\Monolog\Handler\FingersCrossed; + +use FluentSmtpLib\Monolog\Logger; +use FluentSmtpLib\Psr\Log\LogLevel; +/** + * Error level based activation strategy. + * + * @author Johannes M. Schmitt + * + * @phpstan-import-type Level from \Monolog\Logger + * @phpstan-import-type LevelName from \Monolog\Logger + */ +class ErrorLevelActivationStrategy implements \FluentSmtpLib\Monolog\Handler\FingersCrossed\ActivationStrategyInterface +{ + /** + * @var Level + */ + private $actionLevel; + /** + * @param int|string $actionLevel Level or name or value + * + * @phpstan-param Level|LevelName|LogLevel::* $actionLevel + */ + public function __construct($actionLevel) + { + $this->actionLevel = \FluentSmtpLib\Monolog\Logger::toMonologLevel($actionLevel); + } + public function isHandlerActivated(array $record) : bool + { + return $record['level'] >= $this->actionLevel; + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/FingersCrossedHandler.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/FingersCrossedHandler.php new file mode 100644 index 0000000..2649c19 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/FingersCrossedHandler.php @@ -0,0 +1,224 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace FluentSmtpLib\Monolog\Handler; + +use FluentSmtpLib\Monolog\Handler\FingersCrossed\ErrorLevelActivationStrategy; +use FluentSmtpLib\Monolog\Handler\FingersCrossed\ActivationStrategyInterface; +use FluentSmtpLib\Monolog\Logger; +use FluentSmtpLib\Monolog\ResettableInterface; +use FluentSmtpLib\Monolog\Formatter\FormatterInterface; +use FluentSmtpLib\Psr\Log\LogLevel; +/** + * Buffers all records until a certain level is reached + * + * The advantage of this approach is that you don't get any clutter in your log files. + * Only requests which actually trigger an error (or whatever your actionLevel is) will be + * in the logs, but they will contain all records, not only those above the level threshold. + * + * You can then have a passthruLevel as well which means that at the end of the request, + * even if it did not get activated, it will still send through log records of e.g. at least a + * warning level. + * + * You can find the various activation strategies in the + * Monolog\Handler\FingersCrossed\ namespace. + * + * @author Jordi Boggiano + * + * @phpstan-import-type Record from \Monolog\Logger + * @phpstan-import-type Level from \Monolog\Logger + * @phpstan-import-type LevelName from \Monolog\Logger + */ +class FingersCrossedHandler extends \FluentSmtpLib\Monolog\Handler\Handler implements \FluentSmtpLib\Monolog\Handler\ProcessableHandlerInterface, \FluentSmtpLib\Monolog\ResettableInterface, \FluentSmtpLib\Monolog\Handler\FormattableHandlerInterface +{ + use ProcessableHandlerTrait; + /** + * @var callable|HandlerInterface + * @phpstan-var callable(?Record, HandlerInterface): HandlerInterface|HandlerInterface + */ + protected $handler; + /** @var ActivationStrategyInterface */ + protected $activationStrategy; + /** @var bool */ + protected $buffering = \true; + /** @var int */ + protected $bufferSize; + /** @var Record[] */ + protected $buffer = []; + /** @var bool */ + protected $stopBuffering; + /** + * @var ?int + * @phpstan-var ?Level + */ + protected $passthruLevel; + /** @var bool */ + protected $bubble; + /** + * @psalm-param HandlerInterface|callable(?Record, HandlerInterface): HandlerInterface $handler + * + * @param callable|HandlerInterface $handler Handler or factory callable($record|null, $fingersCrossedHandler). + * @param int|string|ActivationStrategyInterface $activationStrategy Strategy which determines when this handler takes action, or a level name/value at which the handler is activated + * @param int $bufferSize How many entries should be buffered at most, beyond that the oldest items are removed from the buffer. + * @param bool $bubble Whether the messages that are handled can bubble up the stack or not + * @param bool $stopBuffering Whether the handler should stop buffering after being triggered (default true) + * @param int|string $passthruLevel Minimum level to always flush to handler on close, even if strategy not triggered + * + * @phpstan-param Level|LevelName|LogLevel::* $passthruLevel + * @phpstan-param Level|LevelName|LogLevel::*|ActivationStrategyInterface $activationStrategy + */ + public function __construct($handler, $activationStrategy = null, int $bufferSize = 0, bool $bubble = \true, bool $stopBuffering = \true, $passthruLevel = null) + { + if (null === $activationStrategy) { + $activationStrategy = new \FluentSmtpLib\Monolog\Handler\FingersCrossed\ErrorLevelActivationStrategy(\FluentSmtpLib\Monolog\Logger::WARNING); + } + // convert simple int activationStrategy to an object + if (!$activationStrategy instanceof \FluentSmtpLib\Monolog\Handler\FingersCrossed\ActivationStrategyInterface) { + $activationStrategy = new \FluentSmtpLib\Monolog\Handler\FingersCrossed\ErrorLevelActivationStrategy($activationStrategy); + } + $this->handler = $handler; + $this->activationStrategy = $activationStrategy; + $this->bufferSize = $bufferSize; + $this->bubble = $bubble; + $this->stopBuffering = $stopBuffering; + if ($passthruLevel !== null) { + $this->passthruLevel = \FluentSmtpLib\Monolog\Logger::toMonologLevel($passthruLevel); + } + if (!$this->handler instanceof \FluentSmtpLib\Monolog\Handler\HandlerInterface && !\is_callable($this->handler)) { + throw new \RuntimeException("The given handler (" . \json_encode($this->handler) . ") is not a callable nor a Monolog\\Handler\\HandlerInterface object"); + } + } + /** + * {@inheritDoc} + */ + public function isHandling(array $record) : bool + { + return \true; + } + /** + * Manually activate this logger regardless of the activation strategy + */ + public function activate() : void + { + if ($this->stopBuffering) { + $this->buffering = \false; + } + $this->getHandler(\end($this->buffer) ?: null)->handleBatch($this->buffer); + $this->buffer = []; + } + /** + * {@inheritDoc} + */ + public function handle(array $record) : bool + { + if ($this->processors) { + /** @var Record $record */ + $record = $this->processRecord($record); + } + if ($this->buffering) { + $this->buffer[] = $record; + if ($this->bufferSize > 0 && \count($this->buffer) > $this->bufferSize) { + \array_shift($this->buffer); + } + if ($this->activationStrategy->isHandlerActivated($record)) { + $this->activate(); + } + } else { + $this->getHandler($record)->handle($record); + } + return \false === $this->bubble; + } + /** + * {@inheritDoc} + */ + public function close() : void + { + $this->flushBuffer(); + $this->getHandler()->close(); + } + public function reset() + { + $this->flushBuffer(); + $this->resetProcessors(); + if ($this->getHandler() instanceof \FluentSmtpLib\Monolog\ResettableInterface) { + $this->getHandler()->reset(); + } + } + /** + * Clears the buffer without flushing any messages down to the wrapped handler. + * + * It also resets the handler to its initial buffering state. + */ + public function clear() : void + { + $this->buffer = []; + $this->reset(); + } + /** + * Resets the state of the handler. Stops forwarding records to the wrapped handler. + */ + private function flushBuffer() : void + { + if (null !== $this->passthruLevel) { + $level = $this->passthruLevel; + $this->buffer = \array_filter($this->buffer, function ($record) use($level) { + return $record['level'] >= $level; + }); + if (\count($this->buffer) > 0) { + $this->getHandler(\end($this->buffer))->handleBatch($this->buffer); + } + } + $this->buffer = []; + $this->buffering = \true; + } + /** + * Return the nested handler + * + * If the handler was provided as a factory callable, this will trigger the handler's instantiation. + * + * @return HandlerInterface + * + * @phpstan-param Record $record + */ + public function getHandler(?array $record = null) + { + if (!$this->handler instanceof \FluentSmtpLib\Monolog\Handler\HandlerInterface) { + $this->handler = ($this->handler)($record, $this); + if (!$this->handler instanceof \FluentSmtpLib\Monolog\Handler\HandlerInterface) { + throw new \RuntimeException("The factory callable should return a HandlerInterface"); + } + } + return $this->handler; + } + /** + * {@inheritDoc} + */ + public function setFormatter(\FluentSmtpLib\Monolog\Formatter\FormatterInterface $formatter) : \FluentSmtpLib\Monolog\Handler\HandlerInterface + { + $handler = $this->getHandler(); + if ($handler instanceof \FluentSmtpLib\Monolog\Handler\FormattableHandlerInterface) { + $handler->setFormatter($formatter); + return $this; + } + throw new \UnexpectedValueException('The nested handler of type ' . \get_class($handler) . ' does not support formatters.'); + } + /** + * {@inheritDoc} + */ + public function getFormatter() : \FluentSmtpLib\Monolog\Formatter\FormatterInterface + { + $handler = $this->getHandler(); + if ($handler instanceof \FluentSmtpLib\Monolog\Handler\FormattableHandlerInterface) { + return $handler->getFormatter(); + } + throw new \UnexpectedValueException('The nested handler of type ' . \get_class($handler) . ' does not support formatters.'); + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/FirePHPHandler.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/FirePHPHandler.php new file mode 100644 index 0000000..eec8db9 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/FirePHPHandler.php @@ -0,0 +1,152 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace FluentSmtpLib\Monolog\Handler; + +use FluentSmtpLib\Monolog\Formatter\WildfireFormatter; +use FluentSmtpLib\Monolog\Formatter\FormatterInterface; +/** + * Simple FirePHP Handler (http://www.firephp.org/), which uses the Wildfire protocol. + * + * @author Eric Clemmons (@ericclemmons) + * + * @phpstan-import-type FormattedRecord from AbstractProcessingHandler + */ +class FirePHPHandler extends \FluentSmtpLib\Monolog\Handler\AbstractProcessingHandler +{ + use WebRequestRecognizerTrait; + /** + * WildFire JSON header message format + */ + protected const PROTOCOL_URI = 'http://meta.wildfirehq.org/Protocol/JsonStream/0.2'; + /** + * FirePHP structure for parsing messages & their presentation + */ + protected const STRUCTURE_URI = 'http://meta.firephp.org/Wildfire/Structure/FirePHP/FirebugConsole/0.1'; + /** + * Must reference a "known" plugin, otherwise headers won't display in FirePHP + */ + protected const PLUGIN_URI = 'http://meta.firephp.org/Wildfire/Plugin/FirePHP/Library-FirePHPCore/0.3'; + /** + * Header prefix for Wildfire to recognize & parse headers + */ + protected const HEADER_PREFIX = 'X-Wf'; + /** + * Whether or not Wildfire vendor-specific headers have been generated & sent yet + * @var bool + */ + protected static $initialized = \false; + /** + * Shared static message index between potentially multiple handlers + * @var int + */ + protected static $messageIndex = 1; + /** @var bool */ + protected static $sendHeaders = \true; + /** + * Base header creation function used by init headers & record headers + * + * @param array $meta Wildfire Plugin, Protocol & Structure Indexes + * @param string $message Log message + * + * @return array Complete header string ready for the client as key and message as value + * + * @phpstan-return non-empty-array + */ + protected function createHeader(array $meta, string $message) : array + { + $header = \sprintf('%s-%s', static::HEADER_PREFIX, \join('-', $meta)); + return [$header => $message]; + } + /** + * Creates message header from record + * + * @return array + * + * @phpstan-return non-empty-array + * + * @see createHeader() + * + * @phpstan-param FormattedRecord $record + */ + protected function createRecordHeader(array $record) : array + { + // Wildfire is extensible to support multiple protocols & plugins in a single request, + // but we're not taking advantage of that (yet), so we're using "1" for simplicity's sake. + return $this->createHeader([1, 1, 1, self::$messageIndex++], $record['formatted']); + } + /** + * {@inheritDoc} + */ + protected function getDefaultFormatter() : \FluentSmtpLib\Monolog\Formatter\FormatterInterface + { + return new \FluentSmtpLib\Monolog\Formatter\WildfireFormatter(); + } + /** + * Wildfire initialization headers to enable message parsing + * + * @see createHeader() + * @see sendHeader() + * + * @return array + */ + protected function getInitHeaders() : array + { + // Initial payload consists of required headers for Wildfire + return \array_merge($this->createHeader(['Protocol', 1], static::PROTOCOL_URI), $this->createHeader([1, 'Structure', 1], static::STRUCTURE_URI), $this->createHeader([1, 'Plugin', 1], static::PLUGIN_URI)); + } + /** + * Send header string to the client + */ + protected function sendHeader(string $header, string $content) : void + { + if (!\headers_sent() && self::$sendHeaders) { + \header(\sprintf('%s: %s', $header, $content)); + } + } + /** + * Creates & sends header for a record, ensuring init headers have been sent prior + * + * @see sendHeader() + * @see sendInitHeaders() + */ + protected function write(array $record) : void + { + if (!self::$sendHeaders || !$this->isWebRequest()) { + return; + } + // WildFire-specific headers must be sent prior to any messages + if (!self::$initialized) { + self::$initialized = \true; + self::$sendHeaders = $this->headersAccepted(); + if (!self::$sendHeaders) { + return; + } + foreach ($this->getInitHeaders() as $header => $content) { + $this->sendHeader($header, $content); + } + } + $header = $this->createRecordHeader($record); + if (\trim(\current($header)) !== '') { + $this->sendHeader(\key($header), \current($header)); + } + } + /** + * Verifies if the headers are accepted by the current user agent + */ + protected function headersAccepted() : bool + { + if (!empty($_SERVER['HTTP_USER_AGENT']) && \preg_match('{\\bFirePHP/\\d+\\.\\d+\\b}', $_SERVER['HTTP_USER_AGENT'])) { + return \true; + } + return isset($_SERVER['HTTP_X_FIREPHP_VERSION']); + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/FleepHookHandler.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/FleepHookHandler.php new file mode 100644 index 0000000..57ecbad --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/FleepHookHandler.php @@ -0,0 +1,102 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace FluentSmtpLib\Monolog\Handler; + +use FluentSmtpLib\Monolog\Formatter\FormatterInterface; +use FluentSmtpLib\Monolog\Formatter\LineFormatter; +use FluentSmtpLib\Monolog\Logger; +/** + * Sends logs to Fleep.io using Webhook integrations + * + * You'll need a Fleep.io account to use this handler. + * + * @see https://fleep.io/integrations/webhooks/ Fleep Webhooks Documentation + * @author Ando Roots + * + * @phpstan-import-type FormattedRecord from AbstractProcessingHandler + */ +class FleepHookHandler extends \FluentSmtpLib\Monolog\Handler\SocketHandler +{ + protected const FLEEP_HOST = 'fleep.io'; + protected const FLEEP_HOOK_URI = '/hook/'; + /** + * @var string Webhook token (specifies the conversation where logs are sent) + */ + protected $token; + /** + * Construct a new Fleep.io Handler. + * + * For instructions on how to create a new web hook in your conversations + * see https://fleep.io/integrations/webhooks/ + * + * @param string $token Webhook token + * @throws MissingExtensionException + */ + public function __construct(string $token, $level = \FluentSmtpLib\Monolog\Logger::DEBUG, bool $bubble = \true, bool $persistent = \false, float $timeout = 0.0, float $writingTimeout = 10.0, ?float $connectionTimeout = null, ?int $chunkSize = null) + { + if (!\extension_loaded('openssl')) { + throw new \FluentSmtpLib\Monolog\Handler\MissingExtensionException('The OpenSSL PHP extension is required to use the FleepHookHandler'); + } + $this->token = $token; + $connectionString = 'ssl://' . static::FLEEP_HOST . ':443'; + parent::__construct($connectionString, $level, $bubble, $persistent, $timeout, $writingTimeout, $connectionTimeout, $chunkSize); + } + /** + * Returns the default formatter to use with this handler + * + * Overloaded to remove empty context and extra arrays from the end of the log message. + * + * @return LineFormatter + */ + protected function getDefaultFormatter() : \FluentSmtpLib\Monolog\Formatter\FormatterInterface + { + return new \FluentSmtpLib\Monolog\Formatter\LineFormatter(null, null, \true, \true); + } + /** + * Handles a log record + */ + public function write(array $record) : void + { + parent::write($record); + $this->closeSocket(); + } + /** + * {@inheritDoc} + */ + protected function generateDataStream(array $record) : string + { + $content = $this->buildContent($record); + return $this->buildHeader($content) . $content; + } + /** + * Builds the header of the API Call + */ + private function buildHeader(string $content) : string + { + $header = "POST " . static::FLEEP_HOOK_URI . $this->token . " HTTP/1.1\r\n"; + $header .= "Host: " . static::FLEEP_HOST . "\r\n"; + $header .= "Content-Type: application/x-www-form-urlencoded\r\n"; + $header .= "Content-Length: " . \strlen($content) . "\r\n"; + $header .= "\r\n"; + return $header; + } + /** + * Builds the body of API call + * + * @phpstan-param FormattedRecord $record + */ + private function buildContent(array $record) : string + { + $dataArray = ['message' => $record['formatted']]; + return \http_build_query($dataArray); + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/FlowdockHandler.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/FlowdockHandler.php new file mode 100644 index 0000000..5483c8f --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/FlowdockHandler.php @@ -0,0 +1,103 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace FluentSmtpLib\Monolog\Handler; + +use FluentSmtpLib\Monolog\Logger; +use FluentSmtpLib\Monolog\Utils; +use FluentSmtpLib\Monolog\Formatter\FlowdockFormatter; +use FluentSmtpLib\Monolog\Formatter\FormatterInterface; +/** + * Sends notifications through the Flowdock push API + * + * This must be configured with a FlowdockFormatter instance via setFormatter() + * + * Notes: + * API token - Flowdock API token + * + * @author Dominik Liebler + * @see https://www.flowdock.com/api/push + * + * @phpstan-import-type FormattedRecord from AbstractProcessingHandler + * @deprecated Since 2.9.0 and 3.3.0, Flowdock was shutdown we will thus drop this handler in Monolog 4 + */ +class FlowdockHandler extends \FluentSmtpLib\Monolog\Handler\SocketHandler +{ + /** + * @var string + */ + protected $apiToken; + /** + * @throws MissingExtensionException if OpenSSL is missing + */ + public function __construct(string $apiToken, $level = \FluentSmtpLib\Monolog\Logger::DEBUG, bool $bubble = \true, bool $persistent = \false, float $timeout = 0.0, float $writingTimeout = 10.0, ?float $connectionTimeout = null, ?int $chunkSize = null) + { + if (!\extension_loaded('openssl')) { + throw new \FluentSmtpLib\Monolog\Handler\MissingExtensionException('The OpenSSL PHP extension is required to use the FlowdockHandler'); + } + parent::__construct('ssl://api.flowdock.com:443', $level, $bubble, $persistent, $timeout, $writingTimeout, $connectionTimeout, $chunkSize); + $this->apiToken = $apiToken; + } + /** + * {@inheritDoc} + */ + public function setFormatter(\FluentSmtpLib\Monolog\Formatter\FormatterInterface $formatter) : \FluentSmtpLib\Monolog\Handler\HandlerInterface + { + if (!$formatter instanceof \FluentSmtpLib\Monolog\Formatter\FlowdockFormatter) { + throw new \InvalidArgumentException('The FlowdockHandler requires an instance of Monolog\\Formatter\\FlowdockFormatter to function correctly'); + } + return parent::setFormatter($formatter); + } + /** + * Gets the default formatter. + */ + protected function getDefaultFormatter() : \FluentSmtpLib\Monolog\Formatter\FormatterInterface + { + throw new \InvalidArgumentException('The FlowdockHandler must be configured (via setFormatter) with an instance of Monolog\\Formatter\\FlowdockFormatter to function correctly'); + } + /** + * {@inheritDoc} + */ + protected function write(array $record) : void + { + parent::write($record); + $this->closeSocket(); + } + /** + * {@inheritDoc} + */ + protected function generateDataStream(array $record) : string + { + $content = $this->buildContent($record); + return $this->buildHeader($content) . $content; + } + /** + * Builds the body of API call + * + * @phpstan-param FormattedRecord $record + */ + private function buildContent(array $record) : string + { + return \FluentSmtpLib\Monolog\Utils::jsonEncode($record['formatted']['flowdock']); + } + /** + * Builds the header of the API Call + */ + private function buildHeader(string $content) : string + { + $header = "POST /v1/messages/team_inbox/" . $this->apiToken . " HTTP/1.1\r\n"; + $header .= "Host: api.flowdock.com\r\n"; + $header .= "Content-Type: application/json\r\n"; + $header .= "Content-Length: " . \strlen($content) . "\r\n"; + $header .= "\r\n"; + return $header; + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/FormattableHandlerInterface.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/FormattableHandlerInterface.php new file mode 100644 index 0000000..43d20c9 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/FormattableHandlerInterface.php @@ -0,0 +1,35 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace FluentSmtpLib\Monolog\Handler; + +use FluentSmtpLib\Monolog\Formatter\FormatterInterface; +/** + * Interface to describe loggers that have a formatter + * + * @author Jordi Boggiano + */ +interface FormattableHandlerInterface +{ + /** + * Sets the formatter. + * + * @param FormatterInterface $formatter + * @return HandlerInterface self + */ + public function setFormatter(\FluentSmtpLib\Monolog\Formatter\FormatterInterface $formatter) : \FluentSmtpLib\Monolog\Handler\HandlerInterface; + /** + * Gets the formatter. + * + * @return FormatterInterface + */ + public function getFormatter() : \FluentSmtpLib\Monolog\Formatter\FormatterInterface; +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/FormattableHandlerTrait.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/FormattableHandlerTrait.php new file mode 100644 index 0000000..29955ee --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/FormattableHandlerTrait.php @@ -0,0 +1,54 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace FluentSmtpLib\Monolog\Handler; + +use FluentSmtpLib\Monolog\Formatter\FormatterInterface; +use FluentSmtpLib\Monolog\Formatter\LineFormatter; +/** + * Helper trait for implementing FormattableInterface + * + * @author Jordi Boggiano + */ +trait FormattableHandlerTrait +{ + /** + * @var ?FormatterInterface + */ + protected $formatter; + /** + * {@inheritDoc} + */ + public function setFormatter(\FluentSmtpLib\Monolog\Formatter\FormatterInterface $formatter) : \FluentSmtpLib\Monolog\Handler\HandlerInterface + { + $this->formatter = $formatter; + return $this; + } + /** + * {@inheritDoc} + */ + public function getFormatter() : \FluentSmtpLib\Monolog\Formatter\FormatterInterface + { + if (!$this->formatter) { + $this->formatter = $this->getDefaultFormatter(); + } + return $this->formatter; + } + /** + * Gets the default formatter. + * + * Overwrite this if the LineFormatter is not a good default for your handler. + */ + protected function getDefaultFormatter() : \FluentSmtpLib\Monolog\Formatter\FormatterInterface + { + return new \FluentSmtpLib\Monolog\Formatter\LineFormatter(); + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/GelfHandler.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/GelfHandler.php new file mode 100644 index 0000000..1d1d994 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/GelfHandler.php @@ -0,0 +1,52 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace FluentSmtpLib\Monolog\Handler; + +use FluentSmtpLib\Gelf\PublisherInterface; +use FluentSmtpLib\Monolog\Logger; +use FluentSmtpLib\Monolog\Formatter\GelfMessageFormatter; +use FluentSmtpLib\Monolog\Formatter\FormatterInterface; +/** + * Handler to send messages to a Graylog2 (http://www.graylog2.org) server + * + * @author Matt Lehner + * @author Benjamin Zikarsky + */ +class GelfHandler extends \FluentSmtpLib\Monolog\Handler\AbstractProcessingHandler +{ + /** + * @var PublisherInterface the publisher object that sends the message to the server + */ + protected $publisher; + /** + * @param PublisherInterface $publisher a gelf publisher object + */ + public function __construct(\FluentSmtpLib\Gelf\PublisherInterface $publisher, $level = \FluentSmtpLib\Monolog\Logger::DEBUG, bool $bubble = \true) + { + parent::__construct($level, $bubble); + $this->publisher = $publisher; + } + /** + * {@inheritDoc} + */ + protected function write(array $record) : void + { + $this->publisher->publish($record['formatted']); + } + /** + * {@inheritDoc} + */ + protected function getDefaultFormatter() : \FluentSmtpLib\Monolog\Formatter\FormatterInterface + { + return new \FluentSmtpLib\Monolog\Formatter\GelfMessageFormatter(); + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/GroupHandler.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/GroupHandler.php new file mode 100644 index 0000000..6ab8dd8 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/GroupHandler.php @@ -0,0 +1,115 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace FluentSmtpLib\Monolog\Handler; + +use FluentSmtpLib\Monolog\Formatter\FormatterInterface; +use FluentSmtpLib\Monolog\ResettableInterface; +/** + * Forwards records to multiple handlers + * + * @author Lenar Lõhmus + * + * @phpstan-import-type Record from \Monolog\Logger + */ +class GroupHandler extends \FluentSmtpLib\Monolog\Handler\Handler implements \FluentSmtpLib\Monolog\Handler\ProcessableHandlerInterface, \FluentSmtpLib\Monolog\ResettableInterface +{ + use ProcessableHandlerTrait; + /** @var HandlerInterface[] */ + protected $handlers; + /** @var bool */ + protected $bubble; + /** + * @param HandlerInterface[] $handlers Array of Handlers. + * @param bool $bubble Whether the messages that are handled can bubble up the stack or not + */ + public function __construct(array $handlers, bool $bubble = \true) + { + foreach ($handlers as $handler) { + if (!$handler instanceof \FluentSmtpLib\Monolog\Handler\HandlerInterface) { + throw new \InvalidArgumentException('The first argument of the GroupHandler must be an array of HandlerInterface instances.'); + } + } + $this->handlers = $handlers; + $this->bubble = $bubble; + } + /** + * {@inheritDoc} + */ + public function isHandling(array $record) : bool + { + foreach ($this->handlers as $handler) { + if ($handler->isHandling($record)) { + return \true; + } + } + return \false; + } + /** + * {@inheritDoc} + */ + public function handle(array $record) : bool + { + if ($this->processors) { + /** @var Record $record */ + $record = $this->processRecord($record); + } + foreach ($this->handlers as $handler) { + $handler->handle($record); + } + return \false === $this->bubble; + } + /** + * {@inheritDoc} + */ + public function handleBatch(array $records) : void + { + if ($this->processors) { + $processed = []; + foreach ($records as $record) { + $processed[] = $this->processRecord($record); + } + /** @var Record[] $records */ + $records = $processed; + } + foreach ($this->handlers as $handler) { + $handler->handleBatch($records); + } + } + public function reset() + { + $this->resetProcessors(); + foreach ($this->handlers as $handler) { + if ($handler instanceof \FluentSmtpLib\Monolog\ResettableInterface) { + $handler->reset(); + } + } + } + public function close() : void + { + parent::close(); + foreach ($this->handlers as $handler) { + $handler->close(); + } + } + /** + * {@inheritDoc} + */ + public function setFormatter(\FluentSmtpLib\Monolog\Formatter\FormatterInterface $formatter) : \FluentSmtpLib\Monolog\Handler\HandlerInterface + { + foreach ($this->handlers as $handler) { + if ($handler instanceof \FluentSmtpLib\Monolog\Handler\FormattableHandlerInterface) { + $handler->setFormatter($formatter); + } + } + return $this; + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/Handler.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/Handler.php new file mode 100644 index 0000000..238bcf0 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/Handler.php @@ -0,0 +1,56 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace FluentSmtpLib\Monolog\Handler; + +/** + * Base Handler class providing basic close() support as well as handleBatch + * + * @author Jordi Boggiano + */ +abstract class Handler implements \FluentSmtpLib\Monolog\Handler\HandlerInterface +{ + /** + * {@inheritDoc} + */ + public function handleBatch(array $records) : void + { + foreach ($records as $record) { + $this->handle($record); + } + } + /** + * {@inheritDoc} + */ + public function close() : void + { + } + public function __destruct() + { + try { + $this->close(); + } catch (\Throwable $e) { + // do nothing + } + } + public function __sleep() + { + $this->close(); + $reflClass = new \ReflectionClass($this); + $keys = []; + foreach ($reflClass->getProperties() as $reflProp) { + if (!$reflProp->isStatic()) { + $keys[] = $reflProp->getName(); + } + } + return $keys; + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/HandlerInterface.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/HandlerInterface.php new file mode 100644 index 0000000..8de0986 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/HandlerInterface.php @@ -0,0 +1,82 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace FluentSmtpLib\Monolog\Handler; + +/** + * Interface that all Monolog Handlers must implement + * + * @author Jordi Boggiano + * + * @phpstan-import-type Record from \Monolog\Logger + * @phpstan-import-type Level from \Monolog\Logger + */ +interface HandlerInterface +{ + /** + * Checks whether the given record will be handled by this handler. + * + * This is mostly done for performance reasons, to avoid calling processors for nothing. + * + * Handlers should still check the record levels within handle(), returning false in isHandling() + * is no guarantee that handle() will not be called, and isHandling() might not be called + * for a given record. + * + * @param array $record Partial log record containing only a level key + * + * @return bool + * + * @phpstan-param array{level: Level} $record + */ + public function isHandling(array $record) : bool; + /** + * Handles a record. + * + * All records may be passed to this method, and the handler should discard + * those that it does not want to handle. + * + * The return value of this function controls the bubbling process of the handler stack. + * Unless the bubbling is interrupted (by returning true), the Logger class will keep on + * calling further handlers in the stack with a given log record. + * + * @param array $record The record to handle + * @return bool true means that this handler handled the record, and that bubbling is not permitted. + * false means the record was either not processed or that this handler allows bubbling. + * + * @phpstan-param Record $record + */ + public function handle(array $record) : bool; + /** + * Handles a set of records at once. + * + * @param array $records The records to handle (an array of record arrays) + * + * @phpstan-param Record[] $records + */ + public function handleBatch(array $records) : void; + /** + * Closes the handler. + * + * Ends a log cycle and frees all resources used by the handler. + * + * Closing a Handler means flushing all buffers and freeing any open resources/handles. + * + * Implementations have to be idempotent (i.e. it should be possible to call close several times without breakage) + * and ideally handlers should be able to reopen themselves on handle() after they have been closed. + * + * This is useful at the end of a request and will be called automatically when the object + * is destroyed if you extend Monolog\Handler\Handler. + * + * If you are thinking of calling this method yourself, most likely you should be + * calling ResettableInterface::reset instead. Have a look. + */ + public function close() : void; +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/HandlerWrapper.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/HandlerWrapper.php new file mode 100644 index 0000000..70f617e --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/HandlerWrapper.php @@ -0,0 +1,119 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace FluentSmtpLib\Monolog\Handler; + +use FluentSmtpLib\Monolog\ResettableInterface; +use FluentSmtpLib\Monolog\Formatter\FormatterInterface; +/** + * This simple wrapper class can be used to extend handlers functionality. + * + * Example: A custom filtering that can be applied to any handler. + * + * Inherit from this class and override handle() like this: + * + * public function handle(array $record) + * { + * if ($record meets certain conditions) { + * return false; + * } + * return $this->handler->handle($record); + * } + * + * @author Alexey Karapetov + */ +class HandlerWrapper implements \FluentSmtpLib\Monolog\Handler\HandlerInterface, \FluentSmtpLib\Monolog\Handler\ProcessableHandlerInterface, \FluentSmtpLib\Monolog\Handler\FormattableHandlerInterface, \FluentSmtpLib\Monolog\ResettableInterface +{ + /** + * @var HandlerInterface + */ + protected $handler; + public function __construct(\FluentSmtpLib\Monolog\Handler\HandlerInterface $handler) + { + $this->handler = $handler; + } + /** + * {@inheritDoc} + */ + public function isHandling(array $record) : bool + { + return $this->handler->isHandling($record); + } + /** + * {@inheritDoc} + */ + public function handle(array $record) : bool + { + return $this->handler->handle($record); + } + /** + * {@inheritDoc} + */ + public function handleBatch(array $records) : void + { + $this->handler->handleBatch($records); + } + /** + * {@inheritDoc} + */ + public function close() : void + { + $this->handler->close(); + } + /** + * {@inheritDoc} + */ + public function pushProcessor(callable $callback) : \FluentSmtpLib\Monolog\Handler\HandlerInterface + { + if ($this->handler instanceof \FluentSmtpLib\Monolog\Handler\ProcessableHandlerInterface) { + $this->handler->pushProcessor($callback); + return $this; + } + throw new \LogicException('The wrapped handler does not implement ' . \FluentSmtpLib\Monolog\Handler\ProcessableHandlerInterface::class); + } + /** + * {@inheritDoc} + */ + public function popProcessor() : callable + { + if ($this->handler instanceof \FluentSmtpLib\Monolog\Handler\ProcessableHandlerInterface) { + return $this->handler->popProcessor(); + } + throw new \LogicException('The wrapped handler does not implement ' . \FluentSmtpLib\Monolog\Handler\ProcessableHandlerInterface::class); + } + /** + * {@inheritDoc} + */ + public function setFormatter(\FluentSmtpLib\Monolog\Formatter\FormatterInterface $formatter) : \FluentSmtpLib\Monolog\Handler\HandlerInterface + { + if ($this->handler instanceof \FluentSmtpLib\Monolog\Handler\FormattableHandlerInterface) { + $this->handler->setFormatter($formatter); + return $this; + } + throw new \LogicException('The wrapped handler does not implement ' . \FluentSmtpLib\Monolog\Handler\FormattableHandlerInterface::class); + } + /** + * {@inheritDoc} + */ + public function getFormatter() : \FluentSmtpLib\Monolog\Formatter\FormatterInterface + { + if ($this->handler instanceof \FluentSmtpLib\Monolog\Handler\FormattableHandlerInterface) { + return $this->handler->getFormatter(); + } + throw new \LogicException('The wrapped handler does not implement ' . \FluentSmtpLib\Monolog\Handler\FormattableHandlerInterface::class); + } + public function reset() + { + if ($this->handler instanceof \FluentSmtpLib\Monolog\ResettableInterface) { + $this->handler->reset(); + } + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/IFTTTHandler.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/IFTTTHandler.php new file mode 100644 index 0000000..1df43ea --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/IFTTTHandler.php @@ -0,0 +1,61 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace FluentSmtpLib\Monolog\Handler; + +use FluentSmtpLib\Monolog\Logger; +use FluentSmtpLib\Monolog\Utils; +/** + * IFTTTHandler uses cURL to trigger IFTTT Maker actions + * + * Register a secret key and trigger/event name at https://ifttt.com/maker + * + * value1 will be the channel from monolog's Logger constructor, + * value2 will be the level name (ERROR, WARNING, ..) + * value3 will be the log record's message + * + * @author Nehal Patel + */ +class IFTTTHandler extends \FluentSmtpLib\Monolog\Handler\AbstractProcessingHandler +{ + /** @var string */ + private $eventName; + /** @var string */ + private $secretKey; + /** + * @param string $eventName The name of the IFTTT Maker event that should be triggered + * @param string $secretKey A valid IFTTT secret key + */ + public function __construct(string $eventName, string $secretKey, $level = \FluentSmtpLib\Monolog\Logger::ERROR, bool $bubble = \true) + { + if (!\extension_loaded('curl')) { + throw new \FluentSmtpLib\Monolog\Handler\MissingExtensionException('The curl extension is needed to use the IFTTTHandler'); + } + $this->eventName = $eventName; + $this->secretKey = $secretKey; + parent::__construct($level, $bubble); + } + /** + * {@inheritDoc} + */ + public function write(array $record) : void + { + $postData = ["value1" => $record["channel"], "value2" => $record["level_name"], "value3" => $record["message"]]; + $postString = \FluentSmtpLib\Monolog\Utils::jsonEncode($postData); + $ch = \curl_init(); + \curl_setopt($ch, \CURLOPT_URL, "https://maker.ifttt.com/trigger/" . $this->eventName . "/with/key/" . $this->secretKey); + \curl_setopt($ch, \CURLOPT_POST, \true); + \curl_setopt($ch, \CURLOPT_RETURNTRANSFER, \true); + \curl_setopt($ch, \CURLOPT_POSTFIELDS, $postString); + \curl_setopt($ch, \CURLOPT_HTTPHEADER, ["Content-Type: application/json"]); + \FluentSmtpLib\Monolog\Handler\Curl\Util::execute($ch); + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/InsightOpsHandler.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/InsightOpsHandler.php new file mode 100644 index 0000000..3f34e7b --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/InsightOpsHandler.php @@ -0,0 +1,50 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace FluentSmtpLib\Monolog\Handler; + +use FluentSmtpLib\Monolog\Logger; +/** + * Inspired on LogEntriesHandler. + * + * @author Robert Kaufmann III + * @author Gabriel Machado + */ +class InsightOpsHandler extends \FluentSmtpLib\Monolog\Handler\SocketHandler +{ + /** + * @var string + */ + protected $logToken; + /** + * @param string $token Log token supplied by InsightOps + * @param string $region Region where InsightOps account is hosted. Could be 'us' or 'eu'. + * @param bool $useSSL Whether or not SSL encryption should be used + * + * @throws MissingExtensionException If SSL encryption is set to true and OpenSSL is missing + */ + public function __construct(string $token, string $region = 'us', bool $useSSL = \true, $level = \FluentSmtpLib\Monolog\Logger::DEBUG, bool $bubble = \true, bool $persistent = \false, float $timeout = 0.0, float $writingTimeout = 10.0, ?float $connectionTimeout = null, ?int $chunkSize = null) + { + if ($useSSL && !\extension_loaded('openssl')) { + throw new \FluentSmtpLib\Monolog\Handler\MissingExtensionException('The OpenSSL PHP plugin is required to use SSL encrypted connection for InsightOpsHandler'); + } + $endpoint = $useSSL ? 'ssl://' . $region . '.data.logs.insight.rapid7.com:443' : $region . '.data.logs.insight.rapid7.com:80'; + parent::__construct($endpoint, $level, $bubble, $persistent, $timeout, $writingTimeout, $connectionTimeout, $chunkSize); + $this->logToken = $token; + } + /** + * {@inheritDoc} + */ + protected function generateDataStream(array $record) : string + { + return $this->logToken . ' ' . $record['formatted']; + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/LogEntriesHandler.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/LogEntriesHandler.php new file mode 100644 index 0000000..ed9cc8e --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/LogEntriesHandler.php @@ -0,0 +1,47 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace FluentSmtpLib\Monolog\Handler; + +use FluentSmtpLib\Monolog\Logger; +/** + * @author Robert Kaufmann III + */ +class LogEntriesHandler extends \FluentSmtpLib\Monolog\Handler\SocketHandler +{ + /** + * @var string + */ + protected $logToken; + /** + * @param string $token Log token supplied by LogEntries + * @param bool $useSSL Whether or not SSL encryption should be used. + * @param string $host Custom hostname to send the data to if needed + * + * @throws MissingExtensionException If SSL encryption is set to true and OpenSSL is missing + */ + public function __construct(string $token, bool $useSSL = \true, $level = \FluentSmtpLib\Monolog\Logger::DEBUG, bool $bubble = \true, string $host = 'data.logentries.com', bool $persistent = \false, float $timeout = 0.0, float $writingTimeout = 10.0, ?float $connectionTimeout = null, ?int $chunkSize = null) + { + if ($useSSL && !\extension_loaded('openssl')) { + throw new \FluentSmtpLib\Monolog\Handler\MissingExtensionException('The OpenSSL PHP plugin is required to use SSL encrypted connection for LogEntriesHandler'); + } + $endpoint = $useSSL ? 'ssl://' . $host . ':443' : $host . ':80'; + parent::__construct($endpoint, $level, $bubble, $persistent, $timeout, $writingTimeout, $connectionTimeout, $chunkSize); + $this->logToken = $token; + } + /** + * {@inheritDoc} + */ + protected function generateDataStream(array $record) : string + { + return $this->logToken . ' ' . $record['formatted']; + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/LogglyHandler.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/LogglyHandler.php new file mode 100644 index 0000000..d622cc9 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/LogglyHandler.php @@ -0,0 +1,133 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace FluentSmtpLib\Monolog\Handler; + +use FluentSmtpLib\Monolog\Logger; +use FluentSmtpLib\Monolog\Formatter\FormatterInterface; +use FluentSmtpLib\Monolog\Formatter\LogglyFormatter; +use function array_key_exists; +use CurlHandle; +/** + * Sends errors to Loggly. + * + * @author Przemek Sobstel + * @author Adam Pancutt + * @author Gregory Barchard + */ +class LogglyHandler extends \FluentSmtpLib\Monolog\Handler\AbstractProcessingHandler +{ + protected const HOST = 'logs-01.loggly.com'; + protected const ENDPOINT_SINGLE = 'inputs'; + protected const ENDPOINT_BATCH = 'bulk'; + /** + * Caches the curl handlers for every given endpoint. + * + * @var resource[]|CurlHandle[] + */ + protected $curlHandlers = []; + /** @var string */ + protected $token; + /** @var string[] */ + protected $tag = []; + /** + * @param string $token API token supplied by Loggly + * + * @throws MissingExtensionException If the curl extension is missing + */ + public function __construct(string $token, $level = \FluentSmtpLib\Monolog\Logger::DEBUG, bool $bubble = \true) + { + if (!\extension_loaded('curl')) { + throw new \FluentSmtpLib\Monolog\Handler\MissingExtensionException('The curl extension is needed to use the LogglyHandler'); + } + $this->token = $token; + parent::__construct($level, $bubble); + } + /** + * Loads and returns the shared curl handler for the given endpoint. + * + * @param string $endpoint + * + * @return resource|CurlHandle + */ + protected function getCurlHandler(string $endpoint) + { + if (!\array_key_exists($endpoint, $this->curlHandlers)) { + $this->curlHandlers[$endpoint] = $this->loadCurlHandle($endpoint); + } + return $this->curlHandlers[$endpoint]; + } + /** + * Starts a fresh curl session for the given endpoint and returns its handler. + * + * @param string $endpoint + * + * @return resource|CurlHandle + */ + private function loadCurlHandle(string $endpoint) + { + $url = \sprintf("https://%s/%s/%s/", static::HOST, $endpoint, $this->token); + $ch = \curl_init(); + \curl_setopt($ch, \CURLOPT_URL, $url); + \curl_setopt($ch, \CURLOPT_POST, \true); + \curl_setopt($ch, \CURLOPT_RETURNTRANSFER, \true); + return $ch; + } + /** + * @param string[]|string $tag + */ + public function setTag($tag) : self + { + $tag = !empty($tag) ? $tag : []; + $this->tag = \is_array($tag) ? $tag : [$tag]; + return $this; + } + /** + * @param string[]|string $tag + */ + public function addTag($tag) : self + { + if (!empty($tag)) { + $tag = \is_array($tag) ? $tag : [$tag]; + $this->tag = \array_unique(\array_merge($this->tag, $tag)); + } + return $this; + } + protected function write(array $record) : void + { + $this->send($record["formatted"], static::ENDPOINT_SINGLE); + } + public function handleBatch(array $records) : void + { + $level = $this->level; + $records = \array_filter($records, function ($record) use($level) { + return $record['level'] >= $level; + }); + if ($records) { + $this->send($this->getFormatter()->formatBatch($records), static::ENDPOINT_BATCH); + } + } + protected function send(string $data, string $endpoint) : void + { + $ch = $this->getCurlHandler($endpoint); + $headers = ['Content-Type: application/json']; + if (!empty($this->tag)) { + $headers[] = 'X-LOGGLY-TAG: ' . \implode(',', $this->tag); + } + \curl_setopt($ch, \CURLOPT_POSTFIELDS, $data); + \curl_setopt($ch, \CURLOPT_HTTPHEADER, $headers); + \FluentSmtpLib\Monolog\Handler\Curl\Util::execute($ch, 5, \false); + } + protected function getDefaultFormatter() : \FluentSmtpLib\Monolog\Formatter\FormatterInterface + { + return new \FluentSmtpLib\Monolog\Formatter\LogglyFormatter(); + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/LogmaticHandler.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/LogmaticHandler.php new file mode 100644 index 0000000..bb3cc52 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/LogmaticHandler.php @@ -0,0 +1,75 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace FluentSmtpLib\Monolog\Handler; + +use FluentSmtpLib\Monolog\Logger; +use FluentSmtpLib\Monolog\Formatter\FormatterInterface; +use FluentSmtpLib\Monolog\Formatter\LogmaticFormatter; +/** + * @author Julien Breux + */ +class LogmaticHandler extends \FluentSmtpLib\Monolog\Handler\SocketHandler +{ + /** + * @var string + */ + private $logToken; + /** + * @var string + */ + private $hostname; + /** + * @var string + */ + private $appname; + /** + * @param string $token Log token supplied by Logmatic. + * @param string $hostname Host name supplied by Logmatic. + * @param string $appname Application name supplied by Logmatic. + * @param bool $useSSL Whether or not SSL encryption should be used. + * + * @throws MissingExtensionException If SSL encryption is set to true and OpenSSL is missing + */ + public function __construct(string $token, string $hostname = '', string $appname = '', bool $useSSL = \true, $level = \FluentSmtpLib\Monolog\Logger::DEBUG, bool $bubble = \true, bool $persistent = \false, float $timeout = 0.0, float $writingTimeout = 10.0, ?float $connectionTimeout = null, ?int $chunkSize = null) + { + if ($useSSL && !\extension_loaded('openssl')) { + throw new \FluentSmtpLib\Monolog\Handler\MissingExtensionException('The OpenSSL PHP extension is required to use SSL encrypted connection for LogmaticHandler'); + } + $endpoint = $useSSL ? 'ssl://api.logmatic.io:10515' : 'api.logmatic.io:10514'; + $endpoint .= '/v1/'; + parent::__construct($endpoint, $level, $bubble, $persistent, $timeout, $writingTimeout, $connectionTimeout, $chunkSize); + $this->logToken = $token; + $this->hostname = $hostname; + $this->appname = $appname; + } + /** + * {@inheritDoc} + */ + protected function generateDataStream(array $record) : string + { + return $this->logToken . ' ' . $record['formatted']; + } + /** + * {@inheritDoc} + */ + protected function getDefaultFormatter() : \FluentSmtpLib\Monolog\Formatter\FormatterInterface + { + $formatter = new \FluentSmtpLib\Monolog\Formatter\LogmaticFormatter(); + if (!empty($this->hostname)) { + $formatter->setHostname($this->hostname); + } + if (!empty($this->appname)) { + $formatter->setAppname($this->appname); + } + return $formatter; + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/MailHandler.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/MailHandler.php new file mode 100644 index 0000000..948016f --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/MailHandler.php @@ -0,0 +1,86 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace FluentSmtpLib\Monolog\Handler; + +use FluentSmtpLib\Monolog\Formatter\FormatterInterface; +use FluentSmtpLib\Monolog\Formatter\HtmlFormatter; +/** + * Base class for all mail handlers + * + * @author Gyula Sallai + * + * @phpstan-import-type Record from \Monolog\Logger + */ +abstract class MailHandler extends \FluentSmtpLib\Monolog\Handler\AbstractProcessingHandler +{ + /** + * {@inheritDoc} + */ + public function handleBatch(array $records) : void + { + $messages = []; + foreach ($records as $record) { + if ($record['level'] < $this->level) { + continue; + } + /** @var Record $message */ + $message = $this->processRecord($record); + $messages[] = $message; + } + if (!empty($messages)) { + $this->send((string) $this->getFormatter()->formatBatch($messages), $messages); + } + } + /** + * Send a mail with the given content + * + * @param string $content formatted email body to be sent + * @param array $records the array of log records that formed this content + * + * @phpstan-param Record[] $records + */ + protected abstract function send(string $content, array $records) : void; + /** + * {@inheritDoc} + */ + protected function write(array $record) : void + { + $this->send((string) $record['formatted'], [$record]); + } + /** + * @phpstan-param non-empty-array $records + * @phpstan-return Record + */ + protected function getHighestRecord(array $records) : array + { + $highestRecord = null; + foreach ($records as $record) { + if ($highestRecord === null || $highestRecord['level'] < $record['level']) { + $highestRecord = $record; + } + } + return $highestRecord; + } + protected function isHtmlBody(string $body) : bool + { + return ($body[0] ?? null) === '<'; + } + /** + * Gets the default formatter. + * + * @return FormatterInterface + */ + protected function getDefaultFormatter() : \FluentSmtpLib\Monolog\Formatter\FormatterInterface + { + return new \FluentSmtpLib\Monolog\Formatter\HtmlFormatter(); + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/MandrillHandler.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/MandrillHandler.php new file mode 100644 index 0000000..5322547 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/MandrillHandler.php @@ -0,0 +1,71 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace FluentSmtpLib\Monolog\Handler; + +use FluentSmtpLib\Monolog\Logger; +use FluentSmtpLib\Swift; +use FluentSmtpLib\Swift_Message; +/** + * MandrillHandler uses cURL to send the emails to the Mandrill API + * + * @author Adam Nicholson + */ +class MandrillHandler extends \FluentSmtpLib\Monolog\Handler\MailHandler +{ + /** @var Swift_Message */ + protected $message; + /** @var string */ + protected $apiKey; + /** + * @psalm-param Swift_Message|callable(): Swift_Message $message + * + * @param string $apiKey A valid Mandrill API key + * @param callable|Swift_Message $message An example message for real messages, only the body will be replaced + */ + public function __construct(string $apiKey, $message, $level = \FluentSmtpLib\Monolog\Logger::ERROR, bool $bubble = \true) + { + parent::__construct($level, $bubble); + if (!$message instanceof \FluentSmtpLib\Swift_Message && \is_callable($message)) { + $message = $message(); + } + if (!$message instanceof \FluentSmtpLib\Swift_Message) { + throw new \InvalidArgumentException('You must provide either a Swift_Message instance or a callable returning it'); + } + $this->message = $message; + $this->apiKey = $apiKey; + } + /** + * {@inheritDoc} + */ + protected function send(string $content, array $records) : void + { + $mime = 'text/plain'; + if ($this->isHtmlBody($content)) { + $mime = 'text/html'; + } + $message = clone $this->message; + $message->setBody($content, $mime); + /** @phpstan-ignore-next-line */ + if (\version_compare(\FluentSmtpLib\Swift::VERSION, '6.0.0', '>=')) { + $message->setDate(new \DateTimeImmutable()); + } else { + /** @phpstan-ignore-next-line */ + $message->setDate(\time()); + } + $ch = \curl_init(); + \curl_setopt($ch, \CURLOPT_URL, 'https://mandrillapp.com/api/1.0/messages/send-raw.json'); + \curl_setopt($ch, \CURLOPT_POST, 1); + \curl_setopt($ch, \CURLOPT_RETURNTRANSFER, 1); + \curl_setopt($ch, \CURLOPT_POSTFIELDS, \http_build_query(['key' => $this->apiKey, 'raw_message' => (string) $message, 'async' => \false])); + \FluentSmtpLib\Monolog\Handler\Curl\Util::execute($ch); + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/MissingExtensionException.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/MissingExtensionException.php new file mode 100644 index 0000000..945def4 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/MissingExtensionException.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace FluentSmtpLib\Monolog\Handler; + +/** + * Exception can be thrown if an extension for a handler is missing + * + * @author Christian Bergau + */ +class MissingExtensionException extends \Exception +{ +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/MongoDBHandler.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/MongoDBHandler.php new file mode 100644 index 0000000..86c8fc0 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/MongoDBHandler.php @@ -0,0 +1,79 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace FluentSmtpLib\Monolog\Handler; + +use MongoDB\Driver\BulkWrite; +use MongoDB\Driver\Manager; +use FluentSmtpLib\MongoDB\Client; +use FluentSmtpLib\Monolog\Logger; +use FluentSmtpLib\Monolog\Formatter\FormatterInterface; +use FluentSmtpLib\Monolog\Formatter\MongoDBFormatter; +/** + * Logs to a MongoDB database. + * + * Usage example: + * + * $log = new \Monolog\Logger('application'); + * $client = new \MongoDB\Client('mongodb://localhost:27017'); + * $mongodb = new \Monolog\Handler\MongoDBHandler($client, 'logs', 'prod'); + * $log->pushHandler($mongodb); + * + * The above examples uses the MongoDB PHP library's client class; however, the + * MongoDB\Driver\Manager class from ext-mongodb is also supported. + */ +class MongoDBHandler extends \FluentSmtpLib\Monolog\Handler\AbstractProcessingHandler +{ + /** @var \MongoDB\Collection */ + private $collection; + /** @var Client|Manager */ + private $manager; + /** @var string */ + private $namespace; + /** + * Constructor. + * + * @param Client|Manager $mongodb MongoDB library or driver client + * @param string $database Database name + * @param string $collection Collection name + */ + public function __construct($mongodb, string $database, string $collection, $level = \FluentSmtpLib\Monolog\Logger::DEBUG, bool $bubble = \true) + { + if (!($mongodb instanceof \FluentSmtpLib\MongoDB\Client || $mongodb instanceof \MongoDB\Driver\Manager)) { + throw new \InvalidArgumentException('MongoDB\\Client or MongoDB\\Driver\\Manager instance required'); + } + if ($mongodb instanceof \FluentSmtpLib\MongoDB\Client) { + $this->collection = $mongodb->selectCollection($database, $collection); + } else { + $this->manager = $mongodb; + $this->namespace = $database . '.' . $collection; + } + parent::__construct($level, $bubble); + } + protected function write(array $record) : void + { + if (isset($this->collection)) { + $this->collection->insertOne($record['formatted']); + } + if (isset($this->manager, $this->namespace)) { + $bulk = new \MongoDB\Driver\BulkWrite(); + $bulk->insert($record["formatted"]); + $this->manager->executeBulkWrite($this->namespace, $bulk); + } + } + /** + * {@inheritDoc} + */ + protected function getDefaultFormatter() : \FluentSmtpLib\Monolog\Formatter\FormatterInterface + { + return new \FluentSmtpLib\Monolog\Formatter\MongoDBFormatter(); + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/NativeMailerHandler.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/NativeMailerHandler.php new file mode 100644 index 0000000..18db8f9 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/NativeMailerHandler.php @@ -0,0 +1,149 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace FluentSmtpLib\Monolog\Handler; + +use FluentSmtpLib\Monolog\Logger; +use FluentSmtpLib\Monolog\Formatter\LineFormatter; +/** + * NativeMailerHandler uses the mail() function to send the emails + * + * @author Christophe Coevoet + * @author Mark Garrett + */ +class NativeMailerHandler extends \FluentSmtpLib\Monolog\Handler\MailHandler +{ + /** + * The email addresses to which the message will be sent + * @var string[] + */ + protected $to; + /** + * The subject of the email + * @var string + */ + protected $subject; + /** + * Optional headers for the message + * @var string[] + */ + protected $headers = []; + /** + * Optional parameters for the message + * @var string[] + */ + protected $parameters = []; + /** + * The wordwrap length for the message + * @var int + */ + protected $maxColumnWidth; + /** + * The Content-type for the message + * @var string|null + */ + protected $contentType; + /** + * The encoding for the message + * @var string + */ + protected $encoding = 'utf-8'; + /** + * @param string|string[] $to The receiver of the mail + * @param string $subject The subject of the mail + * @param string $from The sender of the mail + * @param int $maxColumnWidth The maximum column width that the message lines will have + */ + public function __construct($to, string $subject, string $from, $level = \FluentSmtpLib\Monolog\Logger::ERROR, bool $bubble = \true, int $maxColumnWidth = 70) + { + parent::__construct($level, $bubble); + $this->to = (array) $to; + $this->subject = $subject; + $this->addHeader(\sprintf('From: %s', $from)); + $this->maxColumnWidth = $maxColumnWidth; + } + /** + * Add headers to the message + * + * @param string|string[] $headers Custom added headers + */ + public function addHeader($headers) : self + { + foreach ((array) $headers as $header) { + if (\strpos($header, "\n") !== \false || \strpos($header, "\r") !== \false) { + throw new \InvalidArgumentException('Headers can not contain newline characters for security reasons'); + } + $this->headers[] = $header; + } + return $this; + } + /** + * Add parameters to the message + * + * @param string|string[] $parameters Custom added parameters + */ + public function addParameter($parameters) : self + { + $this->parameters = \array_merge($this->parameters, (array) $parameters); + return $this; + } + /** + * {@inheritDoc} + */ + protected function send(string $content, array $records) : void + { + $contentType = $this->getContentType() ?: ($this->isHtmlBody($content) ? 'text/html' : 'text/plain'); + if ($contentType !== 'text/html') { + $content = \wordwrap($content, $this->maxColumnWidth); + } + $headers = \ltrim(\implode("\r\n", $this->headers) . "\r\n", "\r\n"); + $headers .= 'Content-type: ' . $contentType . '; charset=' . $this->getEncoding() . "\r\n"; + if ($contentType === 'text/html' && \false === \strpos($headers, 'MIME-Version:')) { + $headers .= 'MIME-Version: 1.0' . "\r\n"; + } + $subject = $this->subject; + if ($records) { + $subjectFormatter = new \FluentSmtpLib\Monolog\Formatter\LineFormatter($this->subject); + $subject = $subjectFormatter->format($this->getHighestRecord($records)); + } + $parameters = \implode(' ', $this->parameters); + foreach ($this->to as $to) { + \mail($to, $subject, $content, $headers, $parameters); + } + } + public function getContentType() : ?string + { + return $this->contentType; + } + public function getEncoding() : string + { + return $this->encoding; + } + /** + * @param string $contentType The content type of the email - Defaults to text/plain. Use text/html for HTML messages. + */ + public function setContentType(string $contentType) : self + { + if (\strpos($contentType, "\n") !== \false || \strpos($contentType, "\r") !== \false) { + throw new \InvalidArgumentException('The content type can not contain newline characters to prevent email header injection'); + } + $this->contentType = $contentType; + return $this; + } + public function setEncoding(string $encoding) : self + { + if (\strpos($encoding, "\n") !== \false || \strpos($encoding, "\r") !== \false) { + throw new \InvalidArgumentException('The encoding can not contain newline characters to prevent email header injection'); + } + $this->encoding = $encoding; + return $this; + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/NewRelicHandler.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/NewRelicHandler.php new file mode 100644 index 0000000..60670cb --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/NewRelicHandler.php @@ -0,0 +1,174 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace FluentSmtpLib\Monolog\Handler; + +use FluentSmtpLib\Monolog\Logger; +use FluentSmtpLib\Monolog\Utils; +use FluentSmtpLib\Monolog\Formatter\NormalizerFormatter; +use FluentSmtpLib\Monolog\Formatter\FormatterInterface; +/** + * Class to record a log on a NewRelic application. + * Enabling New Relic High Security mode may prevent capture of useful information. + * + * This handler requires a NormalizerFormatter to function and expects an array in $record['formatted'] + * + * @see https://docs.newrelic.com/docs/agents/php-agent + * @see https://docs.newrelic.com/docs/accounts-partnerships/accounts/security/high-security + */ +class NewRelicHandler extends \FluentSmtpLib\Monolog\Handler\AbstractProcessingHandler +{ + /** + * Name of the New Relic application that will receive logs from this handler. + * + * @var ?string + */ + protected $appName; + /** + * Name of the current transaction + * + * @var ?string + */ + protected $transactionName; + /** + * Some context and extra data is passed into the handler as arrays of values. Do we send them as is + * (useful if we are using the API), or explode them for display on the NewRelic RPM website? + * + * @var bool + */ + protected $explodeArrays; + /** + * {@inheritDoc} + * + * @param string|null $appName + * @param bool $explodeArrays + * @param string|null $transactionName + */ + public function __construct($level = \FluentSmtpLib\Monolog\Logger::ERROR, bool $bubble = \true, ?string $appName = null, bool $explodeArrays = \false, ?string $transactionName = null) + { + parent::__construct($level, $bubble); + $this->appName = $appName; + $this->explodeArrays = $explodeArrays; + $this->transactionName = $transactionName; + } + /** + * {@inheritDoc} + */ + protected function write(array $record) : void + { + if (!$this->isNewRelicEnabled()) { + throw new \FluentSmtpLib\Monolog\Handler\MissingExtensionException('The newrelic PHP extension is required to use the NewRelicHandler'); + } + if ($appName = $this->getAppName($record['context'])) { + $this->setNewRelicAppName($appName); + } + if ($transactionName = $this->getTransactionName($record['context'])) { + $this->setNewRelicTransactionName($transactionName); + unset($record['formatted']['context']['transaction_name']); + } + if (isset($record['context']['exception']) && $record['context']['exception'] instanceof \Throwable) { + \newrelic_notice_error($record['message'], $record['context']['exception']); + unset($record['formatted']['context']['exception']); + } else { + \newrelic_notice_error($record['message']); + } + if (isset($record['formatted']['context']) && \is_array($record['formatted']['context'])) { + foreach ($record['formatted']['context'] as $key => $parameter) { + if (\is_array($parameter) && $this->explodeArrays) { + foreach ($parameter as $paramKey => $paramValue) { + $this->setNewRelicParameter('context_' . $key . '_' . $paramKey, $paramValue); + } + } else { + $this->setNewRelicParameter('context_' . $key, $parameter); + } + } + } + if (isset($record['formatted']['extra']) && \is_array($record['formatted']['extra'])) { + foreach ($record['formatted']['extra'] as $key => $parameter) { + if (\is_array($parameter) && $this->explodeArrays) { + foreach ($parameter as $paramKey => $paramValue) { + $this->setNewRelicParameter('extra_' . $key . '_' . $paramKey, $paramValue); + } + } else { + $this->setNewRelicParameter('extra_' . $key, $parameter); + } + } + } + } + /** + * Checks whether the NewRelic extension is enabled in the system. + * + * @return bool + */ + protected function isNewRelicEnabled() : bool + { + return \extension_loaded('newrelic'); + } + /** + * Returns the appname where this log should be sent. Each log can override the default appname, set in this + * handler's constructor, by providing the appname in it's context. + * + * @param mixed[] $context + */ + protected function getAppName(array $context) : ?string + { + if (isset($context['appname'])) { + return $context['appname']; + } + return $this->appName; + } + /** + * Returns the name of the current transaction. Each log can override the default transaction name, set in this + * handler's constructor, by providing the transaction_name in it's context + * + * @param mixed[] $context + */ + protected function getTransactionName(array $context) : ?string + { + if (isset($context['transaction_name'])) { + return $context['transaction_name']; + } + return $this->transactionName; + } + /** + * Sets the NewRelic application that should receive this log. + */ + protected function setNewRelicAppName(string $appName) : void + { + \newrelic_set_appname($appName); + } + /** + * Overwrites the name of the current transaction + */ + protected function setNewRelicTransactionName(string $transactionName) : void + { + \newrelic_name_transaction($transactionName); + } + /** + * @param string $key + * @param mixed $value + */ + protected function setNewRelicParameter(string $key, $value) : void + { + if (null === $value || \is_scalar($value)) { + \newrelic_add_custom_parameter($key, $value); + } else { + \newrelic_add_custom_parameter($key, \FluentSmtpLib\Monolog\Utils::jsonEncode($value, null, \true)); + } + } + /** + * {@inheritDoc} + */ + protected function getDefaultFormatter() : \FluentSmtpLib\Monolog\Formatter\FormatterInterface + { + return new \FluentSmtpLib\Monolog\Formatter\NormalizerFormatter(); + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/NoopHandler.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/NoopHandler.php new file mode 100644 index 0000000..12bd4be --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/NoopHandler.php @@ -0,0 +1,39 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace FluentSmtpLib\Monolog\Handler; + +/** + * No-op + * + * This handler handles anything, but does nothing, and does not stop bubbling to the rest of the stack. + * This can be used for testing, or to disable a handler when overriding a configuration without + * influencing the rest of the stack. + * + * @author Roel Harbers + */ +class NoopHandler extends \FluentSmtpLib\Monolog\Handler\Handler +{ + /** + * {@inheritDoc} + */ + public function isHandling(array $record) : bool + { + return \true; + } + /** + * {@inheritDoc} + */ + public function handle(array $record) : bool + { + return \false; + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/NullHandler.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/NullHandler.php new file mode 100644 index 0000000..a0baccd --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/NullHandler.php @@ -0,0 +1,56 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace FluentSmtpLib\Monolog\Handler; + +use FluentSmtpLib\Monolog\Logger; +use FluentSmtpLib\Psr\Log\LogLevel; +/** + * Blackhole + * + * Any record it can handle will be thrown away. This can be used + * to put on top of an existing stack to override it temporarily. + * + * @author Jordi Boggiano + * + * @phpstan-import-type Level from \Monolog\Logger + * @phpstan-import-type LevelName from \Monolog\Logger + */ +class NullHandler extends \FluentSmtpLib\Monolog\Handler\Handler +{ + /** + * @var int + */ + private $level; + /** + * @param string|int $level The minimum logging level at which this handler will be triggered + * + * @phpstan-param Level|LevelName|LogLevel::* $level + */ + public function __construct($level = \FluentSmtpLib\Monolog\Logger::DEBUG) + { + $this->level = \FluentSmtpLib\Monolog\Logger::toMonologLevel($level); + } + /** + * {@inheritDoc} + */ + public function isHandling(array $record) : bool + { + return $record['level'] >= $this->level; + } + /** + * {@inheritDoc} + */ + public function handle(array $record) : bool + { + return $record['level'] >= $this->level; + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/OverflowHandler.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/OverflowHandler.php new file mode 100644 index 0000000..1735f28 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/OverflowHandler.php @@ -0,0 +1,119 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace FluentSmtpLib\Monolog\Handler; + +use FluentSmtpLib\Monolog\Logger; +use FluentSmtpLib\Monolog\Formatter\FormatterInterface; +/** + * Handler to only pass log messages when a certain threshold of number of messages is reached. + * + * This can be useful in cases of processing a batch of data, but you're for example only interested + * in case it fails catastrophically instead of a warning for 1 or 2 events. Worse things can happen, right? + * + * Usage example: + * + * ``` + * $log = new Logger('application'); + * $handler = new SomeHandler(...) + * + * // Pass all warnings to the handler when more than 10 & all error messages when more then 5 + * $overflow = new OverflowHandler($handler, [Logger::WARNING => 10, Logger::ERROR => 5]); + * + * $log->pushHandler($overflow); + *``` + * + * @author Kris Buist + */ +class OverflowHandler extends \FluentSmtpLib\Monolog\Handler\AbstractHandler implements \FluentSmtpLib\Monolog\Handler\FormattableHandlerInterface +{ + /** @var HandlerInterface */ + private $handler; + /** @var int[] */ + private $thresholdMap = [\FluentSmtpLib\Monolog\Logger::DEBUG => 0, \FluentSmtpLib\Monolog\Logger::INFO => 0, \FluentSmtpLib\Monolog\Logger::NOTICE => 0, \FluentSmtpLib\Monolog\Logger::WARNING => 0, \FluentSmtpLib\Monolog\Logger::ERROR => 0, \FluentSmtpLib\Monolog\Logger::CRITICAL => 0, \FluentSmtpLib\Monolog\Logger::ALERT => 0, \FluentSmtpLib\Monolog\Logger::EMERGENCY => 0]; + /** + * Buffer of all messages passed to the handler before the threshold was reached + * + * @var mixed[][] + */ + private $buffer = []; + /** + * @param HandlerInterface $handler + * @param int[] $thresholdMap Dictionary of logger level => threshold + */ + public function __construct(\FluentSmtpLib\Monolog\Handler\HandlerInterface $handler, array $thresholdMap = [], $level = \FluentSmtpLib\Monolog\Logger::DEBUG, bool $bubble = \true) + { + $this->handler = $handler; + foreach ($thresholdMap as $thresholdLevel => $threshold) { + $this->thresholdMap[$thresholdLevel] = $threshold; + } + parent::__construct($level, $bubble); + } + /** + * Handles a record. + * + * All records may be passed to this method, and the handler should discard + * those that it does not want to handle. + * + * The return value of this function controls the bubbling process of the handler stack. + * Unless the bubbling is interrupted (by returning true), the Logger class will keep on + * calling further handlers in the stack with a given log record. + * + * {@inheritDoc} + */ + public function handle(array $record) : bool + { + if ($record['level'] < $this->level) { + return \false; + } + $level = $record['level']; + if (!isset($this->thresholdMap[$level])) { + $this->thresholdMap[$level] = 0; + } + if ($this->thresholdMap[$level] > 0) { + // The overflow threshold is not yet reached, so we're buffering the record and lowering the threshold by 1 + $this->thresholdMap[$level]--; + $this->buffer[$level][] = $record; + return \false === $this->bubble; + } + if ($this->thresholdMap[$level] == 0) { + // This current message is breaking the threshold. Flush the buffer and continue handling the current record + foreach ($this->buffer[$level] ?? [] as $buffered) { + $this->handler->handle($buffered); + } + $this->thresholdMap[$level]--; + unset($this->buffer[$level]); + } + $this->handler->handle($record); + return \false === $this->bubble; + } + /** + * {@inheritDoc} + */ + public function setFormatter(\FluentSmtpLib\Monolog\Formatter\FormatterInterface $formatter) : \FluentSmtpLib\Monolog\Handler\HandlerInterface + { + if ($this->handler instanceof \FluentSmtpLib\Monolog\Handler\FormattableHandlerInterface) { + $this->handler->setFormatter($formatter); + return $this; + } + throw new \UnexpectedValueException('The nested handler of type ' . \get_class($this->handler) . ' does not support formatters.'); + } + /** + * {@inheritDoc} + */ + public function getFormatter() : \FluentSmtpLib\Monolog\Formatter\FormatterInterface + { + if ($this->handler instanceof \FluentSmtpLib\Monolog\Handler\FormattableHandlerInterface) { + return $this->handler->getFormatter(); + } + throw new \UnexpectedValueException('The nested handler of type ' . \get_class($this->handler) . ' does not support formatters.'); + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/PHPConsoleHandler.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/PHPConsoleHandler.php new file mode 100644 index 0000000..5bed6e0 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/PHPConsoleHandler.php @@ -0,0 +1,255 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace FluentSmtpLib\Monolog\Handler; + +use FluentSmtpLib\Monolog\Formatter\LineFormatter; +use FluentSmtpLib\Monolog\Formatter\FormatterInterface; +use FluentSmtpLib\Monolog\Logger; +use FluentSmtpLib\Monolog\Utils; +use FluentSmtpLib\PhpConsole\Connector; +use FluentSmtpLib\PhpConsole\Handler as VendorPhpConsoleHandler; +use FluentSmtpLib\PhpConsole\Helper; +/** + * Monolog handler for Google Chrome extension "PHP Console" + * + * Display PHP error/debug log messages in Google Chrome console and notification popups, executes PHP code remotely + * + * Usage: + * 1. Install Google Chrome extension [now dead and removed from the chrome store] + * 2. See overview https://github.com/barbushin/php-console#overview + * 3. Install PHP Console library https://github.com/barbushin/php-console#installation + * 4. Example (result will looks like http://i.hizliresim.com/vg3Pz4.png) + * + * $logger = new \Monolog\Logger('all', array(new \Monolog\Handler\PHPConsoleHandler())); + * \Monolog\ErrorHandler::register($logger); + * echo $undefinedVar; + * $logger->debug('SELECT * FROM users', array('db', 'time' => 0.012)); + * PC::debug($_SERVER); // PHP Console debugger for any type of vars + * + * @author Sergey Barbushin https://www.linkedin.com/in/barbushin + * + * @phpstan-import-type Record from \Monolog\Logger + * @deprecated Since 2.8.0 and 3.2.0, PHPConsole is abandoned and thus we will drop this handler in Monolog 4 + */ +class PHPConsoleHandler extends \FluentSmtpLib\Monolog\Handler\AbstractProcessingHandler +{ + /** @var array */ + private $options = [ + 'enabled' => \true, + // bool Is PHP Console server enabled + 'classesPartialsTraceIgnore' => ['Monolog\\'], + // array Hide calls of classes started with... + 'debugTagsKeysInContext' => [0, 'tag'], + // bool Is PHP Console server enabled + 'useOwnErrorsHandler' => \false, + // bool Enable errors handling + 'useOwnExceptionsHandler' => \false, + // bool Enable exceptions handling + 'sourcesBasePath' => null, + // string Base path of all project sources to strip in errors source paths + 'registerHelper' => \true, + // bool Register PhpConsole\Helper that allows short debug calls like PC::debug($var, 'ta.g.s') + 'serverEncoding' => null, + // string|null Server internal encoding + 'headersLimit' => null, + // int|null Set headers size limit for your web-server + 'password' => null, + // string|null Protect PHP Console connection by password + 'enableSslOnlyMode' => \false, + // bool Force connection by SSL for clients with PHP Console installed + 'ipMasks' => [], + // array Set IP masks of clients that will be allowed to connect to PHP Console: array('192.168.*.*', '127.0.0.1') + 'enableEvalListener' => \false, + // bool Enable eval request to be handled by eval dispatcher(if enabled, 'password' option is also required) + 'dumperDetectCallbacks' => \false, + // bool Convert callback items in dumper vars to (callback SomeClass::someMethod) strings + 'dumperLevelLimit' => 5, + // int Maximum dumped vars array or object nested dump level + 'dumperItemsCountLimit' => 100, + // int Maximum dumped var same level array items or object properties number + 'dumperItemSizeLimit' => 5000, + // int Maximum length of any string or dumped array item + 'dumperDumpSizeLimit' => 500000, + // int Maximum approximate size of dumped vars result formatted in JSON + 'detectDumpTraceAndSource' => \false, + // bool Autodetect and append trace data to debug + 'dataStorage' => null, + ]; + /** @var Connector */ + private $connector; + /** + * @param array $options See \Monolog\Handler\PHPConsoleHandler::$options for more details + * @param Connector|null $connector Instance of \PhpConsole\Connector class (optional) + * @throws \RuntimeException + */ + public function __construct(array $options = [], ?\FluentSmtpLib\PhpConsole\Connector $connector = null, $level = \FluentSmtpLib\Monolog\Logger::DEBUG, bool $bubble = \true) + { + if (!\class_exists('FluentSmtpLib\\PhpConsole\\Connector')) { + throw new \RuntimeException('PHP Console library not found. See https://github.com/barbushin/php-console#installation'); + } + parent::__construct($level, $bubble); + $this->options = $this->initOptions($options); + $this->connector = $this->initConnector($connector); + } + /** + * @param array $options + * + * @return array + */ + private function initOptions(array $options) : array + { + $wrongOptions = \array_diff(\array_keys($options), \array_keys($this->options)); + if ($wrongOptions) { + throw new \RuntimeException('Unknown options: ' . \implode(', ', $wrongOptions)); + } + return \array_replace($this->options, $options); + } + private function initConnector(?\FluentSmtpLib\PhpConsole\Connector $connector = null) : \FluentSmtpLib\PhpConsole\Connector + { + if (!$connector) { + if ($this->options['dataStorage']) { + \FluentSmtpLib\PhpConsole\Connector::setPostponeStorage($this->options['dataStorage']); + } + $connector = \FluentSmtpLib\PhpConsole\Connector::getInstance(); + } + if ($this->options['registerHelper'] && !\FluentSmtpLib\PhpConsole\Helper::isRegistered()) { + \FluentSmtpLib\PhpConsole\Helper::register(); + } + if ($this->options['enabled'] && $connector->isActiveClient()) { + if ($this->options['useOwnErrorsHandler'] || $this->options['useOwnExceptionsHandler']) { + $handler = \FluentSmtpLib\PhpConsole\Handler::getInstance(); + $handler->setHandleErrors($this->options['useOwnErrorsHandler']); + $handler->setHandleExceptions($this->options['useOwnExceptionsHandler']); + $handler->start(); + } + if ($this->options['sourcesBasePath']) { + $connector->setSourcesBasePath($this->options['sourcesBasePath']); + } + if ($this->options['serverEncoding']) { + $connector->setServerEncoding($this->options['serverEncoding']); + } + if ($this->options['password']) { + $connector->setPassword($this->options['password']); + } + if ($this->options['enableSslOnlyMode']) { + $connector->enableSslOnlyMode(); + } + if ($this->options['ipMasks']) { + $connector->setAllowedIpMasks($this->options['ipMasks']); + } + if ($this->options['headersLimit']) { + $connector->setHeadersLimit($this->options['headersLimit']); + } + if ($this->options['detectDumpTraceAndSource']) { + $connector->getDebugDispatcher()->detectTraceAndSource = \true; + } + $dumper = $connector->getDumper(); + $dumper->levelLimit = $this->options['dumperLevelLimit']; + $dumper->itemsCountLimit = $this->options['dumperItemsCountLimit']; + $dumper->itemSizeLimit = $this->options['dumperItemSizeLimit']; + $dumper->dumpSizeLimit = $this->options['dumperDumpSizeLimit']; + $dumper->detectCallbacks = $this->options['dumperDetectCallbacks']; + if ($this->options['enableEvalListener']) { + $connector->startEvalRequestsListener(); + } + } + return $connector; + } + public function getConnector() : \FluentSmtpLib\PhpConsole\Connector + { + return $this->connector; + } + /** + * @return array + */ + public function getOptions() : array + { + return $this->options; + } + public function handle(array $record) : bool + { + if ($this->options['enabled'] && $this->connector->isActiveClient()) { + return parent::handle($record); + } + return !$this->bubble; + } + /** + * Writes the record down to the log of the implementing handler + */ + protected function write(array $record) : void + { + if ($record['level'] < \FluentSmtpLib\Monolog\Logger::NOTICE) { + $this->handleDebugRecord($record); + } elseif (isset($record['context']['exception']) && $record['context']['exception'] instanceof \Throwable) { + $this->handleExceptionRecord($record); + } else { + $this->handleErrorRecord($record); + } + } + /** + * @phpstan-param Record $record + */ + private function handleDebugRecord(array $record) : void + { + $tags = $this->getRecordTags($record); + $message = $record['message']; + if ($record['context']) { + $message .= ' ' . \FluentSmtpLib\Monolog\Utils::jsonEncode($this->connector->getDumper()->dump(\array_filter($record['context'])), null, \true); + } + $this->connector->getDebugDispatcher()->dispatchDebug($message, $tags, $this->options['classesPartialsTraceIgnore']); + } + /** + * @phpstan-param Record $record + */ + private function handleExceptionRecord(array $record) : void + { + $this->connector->getErrorsDispatcher()->dispatchException($record['context']['exception']); + } + /** + * @phpstan-param Record $record + */ + private function handleErrorRecord(array $record) : void + { + $context = $record['context']; + $this->connector->getErrorsDispatcher()->dispatchError($context['code'] ?? null, $context['message'] ?? $record['message'], $context['file'] ?? null, $context['line'] ?? null, $this->options['classesPartialsTraceIgnore']); + } + /** + * @phpstan-param Record $record + * @return string + */ + private function getRecordTags(array &$record) + { + $tags = null; + if (!empty($record['context'])) { + $context =& $record['context']; + foreach ($this->options['debugTagsKeysInContext'] as $key) { + if (!empty($context[$key])) { + $tags = $context[$key]; + if ($key === 0) { + \array_shift($context); + } else { + unset($context[$key]); + } + break; + } + } + } + return $tags ?: \strtolower($record['level_name']); + } + /** + * {@inheritDoc} + */ + protected function getDefaultFormatter() : \FluentSmtpLib\Monolog\Formatter\FormatterInterface + { + return new \FluentSmtpLib\Monolog\Formatter\LineFormatter('%message%'); + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/ProcessHandler.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/ProcessHandler.php new file mode 100644 index 0000000..873d6f9 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/ProcessHandler.php @@ -0,0 +1,168 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace FluentSmtpLib\Monolog\Handler; + +use FluentSmtpLib\Monolog\Logger; +/** + * Stores to STDIN of any process, specified by a command. + * + * Usage example: + *
    + * $log = new Logger('myLogger');
    + * $log->pushHandler(new ProcessHandler('/usr/bin/php /var/www/monolog/someScript.php'));
    + * 
    + * + * @author Kolja Zuelsdorf + */ +class ProcessHandler extends \FluentSmtpLib\Monolog\Handler\AbstractProcessingHandler +{ + /** + * Holds the process to receive data on its STDIN. + * + * @var resource|bool|null + */ + private $process; + /** + * @var string + */ + private $command; + /** + * @var string|null + */ + private $cwd; + /** + * @var resource[] + */ + private $pipes = []; + /** + * @var array + */ + protected const DESCRIPTOR_SPEC = [ + 0 => ['pipe', 'r'], + // STDIN is a pipe that the child will read from + 1 => ['pipe', 'w'], + // STDOUT is a pipe that the child will write to + 2 => ['pipe', 'w'], + ]; + /** + * @param string $command Command for the process to start. Absolute paths are recommended, + * especially if you do not use the $cwd parameter. + * @param string|null $cwd "Current working directory" (CWD) for the process to be executed in. + * @throws \InvalidArgumentException + */ + public function __construct(string $command, $level = \FluentSmtpLib\Monolog\Logger::DEBUG, bool $bubble = \true, ?string $cwd = null) + { + if ($command === '') { + throw new \InvalidArgumentException('The command argument must be a non-empty string.'); + } + if ($cwd === '') { + throw new \InvalidArgumentException('The optional CWD argument must be a non-empty string or null.'); + } + parent::__construct($level, $bubble); + $this->command = $command; + $this->cwd = $cwd; + } + /** + * Writes the record down to the log of the implementing handler + * + * @throws \UnexpectedValueException + */ + protected function write(array $record) : void + { + $this->ensureProcessIsStarted(); + $this->writeProcessInput($record['formatted']); + $errors = $this->readProcessErrors(); + if (empty($errors) === \false) { + throw new \UnexpectedValueException(\sprintf('Errors while writing to process: %s', $errors)); + } + } + /** + * Makes sure that the process is actually started, and if not, starts it, + * assigns the stream pipes, and handles startup errors, if any. + */ + private function ensureProcessIsStarted() : void + { + if (\is_resource($this->process) === \false) { + $this->startProcess(); + $this->handleStartupErrors(); + } + } + /** + * Starts the actual process and sets all streams to non-blocking. + */ + private function startProcess() : void + { + $this->process = \proc_open($this->command, static::DESCRIPTOR_SPEC, $this->pipes, $this->cwd); + foreach ($this->pipes as $pipe) { + \stream_set_blocking($pipe, \false); + } + } + /** + * Selects the STDERR stream, handles upcoming startup errors, and throws an exception, if any. + * + * @throws \UnexpectedValueException + */ + private function handleStartupErrors() : void + { + $selected = $this->selectErrorStream(); + if (\false === $selected) { + throw new \UnexpectedValueException('Something went wrong while selecting a stream.'); + } + $errors = $this->readProcessErrors(); + if (\is_resource($this->process) === \false || empty($errors) === \false) { + throw new \UnexpectedValueException(\sprintf('The process "%s" could not be opened: ' . $errors, $this->command)); + } + } + /** + * Selects the STDERR stream. + * + * @return int|bool + */ + protected function selectErrorStream() + { + $empty = []; + $errorPipes = [$this->pipes[2]]; + return \stream_select($errorPipes, $empty, $empty, 1); + } + /** + * Reads the errors of the process, if there are any. + * + * @codeCoverageIgnore + * @return string Empty string if there are no errors. + */ + protected function readProcessErrors() : string + { + return (string) \stream_get_contents($this->pipes[2]); + } + /** + * Writes to the input stream of the opened process. + * + * @codeCoverageIgnore + */ + protected function writeProcessInput(string $string) : void + { + \fwrite($this->pipes[0], $string); + } + /** + * {@inheritDoc} + */ + public function close() : void + { + if (\is_resource($this->process)) { + foreach ($this->pipes as $pipe) { + \fclose($pipe); + } + \proc_close($this->process); + $this->process = null; + } + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/ProcessableHandlerInterface.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/ProcessableHandlerInterface.php new file mode 100644 index 0000000..d880d28 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/ProcessableHandlerInterface.php @@ -0,0 +1,42 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace FluentSmtpLib\Monolog\Handler; + +use FluentSmtpLib\Monolog\Processor\ProcessorInterface; +/** + * Interface to describe loggers that have processors + * + * @author Jordi Boggiano + * + * @phpstan-import-type Record from \Monolog\Logger + */ +interface ProcessableHandlerInterface +{ + /** + * Adds a processor in the stack. + * + * @psalm-param ProcessorInterface|callable(Record): Record $callback + * + * @param ProcessorInterface|callable $callback + * @return HandlerInterface self + */ + public function pushProcessor(callable $callback) : \FluentSmtpLib\Monolog\Handler\HandlerInterface; + /** + * Removes the processor on top of the stack and returns it. + * + * @psalm-return ProcessorInterface|callable(Record): Record $callback + * + * @throws \LogicException In case the processor stack is empty + * @return callable|ProcessorInterface + */ + public function popProcessor() : callable; +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/ProcessableHandlerTrait.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/ProcessableHandlerTrait.php new file mode 100644 index 0000000..50e9ef7 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/ProcessableHandlerTrait.php @@ -0,0 +1,69 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace FluentSmtpLib\Monolog\Handler; + +use FluentSmtpLib\Monolog\ResettableInterface; +use FluentSmtpLib\Monolog\Processor\ProcessorInterface; +/** + * Helper trait for implementing ProcessableInterface + * + * @author Jordi Boggiano + * + * @phpstan-import-type Record from \Monolog\Logger + */ +trait ProcessableHandlerTrait +{ + /** + * @var callable[] + * @phpstan-var array + */ + protected $processors = []; + /** + * {@inheritDoc} + */ + public function pushProcessor(callable $callback) : \FluentSmtpLib\Monolog\Handler\HandlerInterface + { + \array_unshift($this->processors, $callback); + return $this; + } + /** + * {@inheritDoc} + */ + public function popProcessor() : callable + { + if (!$this->processors) { + throw new \LogicException('You tried to pop from an empty processor stack.'); + } + return \array_shift($this->processors); + } + /** + * Processes a record. + * + * @phpstan-param Record $record + * @phpstan-return Record + */ + protected function processRecord(array $record) : array + { + foreach ($this->processors as $processor) { + $record = $processor($record); + } + return $record; + } + protected function resetProcessors() : void + { + foreach ($this->processors as $processor) { + if ($processor instanceof \FluentSmtpLib\Monolog\ResettableInterface) { + $processor->reset(); + } + } + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/PsrHandler.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/PsrHandler.php new file mode 100644 index 0000000..83a6b8f --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/PsrHandler.php @@ -0,0 +1,84 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace FluentSmtpLib\Monolog\Handler; + +use FluentSmtpLib\Monolog\Logger; +use FluentSmtpLib\Psr\Log\LoggerInterface; +use FluentSmtpLib\Monolog\Formatter\FormatterInterface; +/** + * Proxies log messages to an existing PSR-3 compliant logger. + * + * If a formatter is configured, the formatter's output MUST be a string and the + * formatted message will be fed to the wrapped PSR logger instead of the original + * log record's message. + * + * @author Michael Moussa + */ +class PsrHandler extends \FluentSmtpLib\Monolog\Handler\AbstractHandler implements \FluentSmtpLib\Monolog\Handler\FormattableHandlerInterface +{ + /** + * PSR-3 compliant logger + * + * @var LoggerInterface + */ + protected $logger; + /** + * @var FormatterInterface|null + */ + protected $formatter; + /** + * @param LoggerInterface $logger The underlying PSR-3 compliant logger to which messages will be proxied + */ + public function __construct(\FluentSmtpLib\Psr\Log\LoggerInterface $logger, $level = \FluentSmtpLib\Monolog\Logger::DEBUG, bool $bubble = \true) + { + parent::__construct($level, $bubble); + $this->logger = $logger; + } + /** + * {@inheritDoc} + */ + public function handle(array $record) : bool + { + if (!$this->isHandling($record)) { + return \false; + } + if ($this->formatter) { + $formatted = $this->formatter->format($record); + $this->logger->log(\strtolower($record['level_name']), (string) $formatted, $record['context']); + } else { + $this->logger->log(\strtolower($record['level_name']), $record['message'], $record['context']); + } + return \false === $this->bubble; + } + /** + * Sets the formatter. + * + * @param FormatterInterface $formatter + */ + public function setFormatter(\FluentSmtpLib\Monolog\Formatter\FormatterInterface $formatter) : \FluentSmtpLib\Monolog\Handler\HandlerInterface + { + $this->formatter = $formatter; + return $this; + } + /** + * Gets the formatter. + * + * @return FormatterInterface + */ + public function getFormatter() : \FluentSmtpLib\Monolog\Formatter\FormatterInterface + { + if (!$this->formatter) { + throw new \LogicException('No formatter has been set and this handler does not have a default formatter'); + } + return $this->formatter; + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/PushoverHandler.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/PushoverHandler.php new file mode 100644 index 0000000..695fb20 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/PushoverHandler.php @@ -0,0 +1,170 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace FluentSmtpLib\Monolog\Handler; + +use FluentSmtpLib\Monolog\Logger; +use FluentSmtpLib\Monolog\Utils; +use FluentSmtpLib\Psr\Log\LogLevel; +/** + * Sends notifications through the pushover api to mobile phones + * + * @author Sebastian Göttschkes + * @see https://www.pushover.net/api + * + * @phpstan-import-type FormattedRecord from AbstractProcessingHandler + * @phpstan-import-type Level from \Monolog\Logger + * @phpstan-import-type LevelName from \Monolog\Logger + */ +class PushoverHandler extends \FluentSmtpLib\Monolog\Handler\SocketHandler +{ + /** @var string */ + private $token; + /** @var array */ + private $users; + /** @var string */ + private $title; + /** @var string|int|null */ + private $user = null; + /** @var int */ + private $retry; + /** @var int */ + private $expire; + /** @var int */ + private $highPriorityLevel; + /** @var int */ + private $emergencyLevel; + /** @var bool */ + private $useFormattedMessage = \false; + /** + * All parameters that can be sent to Pushover + * @see https://pushover.net/api + * @var array + */ + private $parameterNames = ['token' => \true, 'user' => \true, 'message' => \true, 'device' => \true, 'title' => \true, 'url' => \true, 'url_title' => \true, 'priority' => \true, 'timestamp' => \true, 'sound' => \true, 'retry' => \true, 'expire' => \true, 'callback' => \true]; + /** + * Sounds the api supports by default + * @see https://pushover.net/api#sounds + * @var string[] + */ + private $sounds = ['pushover', 'bike', 'bugle', 'cashregister', 'classical', 'cosmic', 'falling', 'gamelan', 'incoming', 'intermission', 'magic', 'mechanical', 'pianobar', 'siren', 'spacealarm', 'tugboat', 'alien', 'climb', 'persistent', 'echo', 'updown', 'none']; + /** + * @param string $token Pushover api token + * @param string|array $users Pushover user id or array of ids the message will be sent to + * @param string|null $title Title sent to the Pushover API + * @param bool $useSSL Whether to connect via SSL. Required when pushing messages to users that are not + * the pushover.net app owner. OpenSSL is required for this option. + * @param string|int $highPriorityLevel The minimum logging level at which this handler will start + * sending "high priority" requests to the Pushover API + * @param string|int $emergencyLevel The minimum logging level at which this handler will start + * sending "emergency" requests to the Pushover API + * @param int $retry The retry parameter specifies how often (in seconds) the Pushover servers will + * send the same notification to the user. + * @param int $expire The expire parameter specifies how many seconds your notification will continue + * to be retried for (every retry seconds). + * + * @phpstan-param string|array $users + * @phpstan-param Level|LevelName|LogLevel::* $highPriorityLevel + * @phpstan-param Level|LevelName|LogLevel::* $emergencyLevel + */ + public function __construct(string $token, $users, ?string $title = null, $level = \FluentSmtpLib\Monolog\Logger::CRITICAL, bool $bubble = \true, bool $useSSL = \true, $highPriorityLevel = \FluentSmtpLib\Monolog\Logger::CRITICAL, $emergencyLevel = \FluentSmtpLib\Monolog\Logger::EMERGENCY, int $retry = 30, int $expire = 25200, bool $persistent = \false, float $timeout = 0.0, float $writingTimeout = 10.0, ?float $connectionTimeout = null, ?int $chunkSize = null) + { + $connectionString = $useSSL ? 'ssl://api.pushover.net:443' : 'api.pushover.net:80'; + parent::__construct($connectionString, $level, $bubble, $persistent, $timeout, $writingTimeout, $connectionTimeout, $chunkSize); + $this->token = $token; + $this->users = (array) $users; + $this->title = $title ?: (string) \gethostname(); + $this->highPriorityLevel = \FluentSmtpLib\Monolog\Logger::toMonologLevel($highPriorityLevel); + $this->emergencyLevel = \FluentSmtpLib\Monolog\Logger::toMonologLevel($emergencyLevel); + $this->retry = $retry; + $this->expire = $expire; + } + protected function generateDataStream(array $record) : string + { + $content = $this->buildContent($record); + return $this->buildHeader($content) . $content; + } + /** + * @phpstan-param FormattedRecord $record + */ + private function buildContent(array $record) : string + { + // Pushover has a limit of 512 characters on title and message combined. + $maxMessageLength = 512 - \strlen($this->title); + $message = $this->useFormattedMessage ? $record['formatted'] : $record['message']; + $message = \FluentSmtpLib\Monolog\Utils::substr($message, 0, $maxMessageLength); + $timestamp = $record['datetime']->getTimestamp(); + $dataArray = ['token' => $this->token, 'user' => $this->user, 'message' => $message, 'title' => $this->title, 'timestamp' => $timestamp]; + if (isset($record['level']) && $record['level'] >= $this->emergencyLevel) { + $dataArray['priority'] = 2; + $dataArray['retry'] = $this->retry; + $dataArray['expire'] = $this->expire; + } elseif (isset($record['level']) && $record['level'] >= $this->highPriorityLevel) { + $dataArray['priority'] = 1; + } + // First determine the available parameters + $context = \array_intersect_key($record['context'], $this->parameterNames); + $extra = \array_intersect_key($record['extra'], $this->parameterNames); + // Least important info should be merged with subsequent info + $dataArray = \array_merge($extra, $context, $dataArray); + // Only pass sounds that are supported by the API + if (isset($dataArray['sound']) && !\in_array($dataArray['sound'], $this->sounds)) { + unset($dataArray['sound']); + } + return \http_build_query($dataArray); + } + private function buildHeader(string $content) : string + { + $header = "POST /1/messages.json HTTP/1.1\r\n"; + $header .= "Host: api.pushover.net\r\n"; + $header .= "Content-Type: application/x-www-form-urlencoded\r\n"; + $header .= "Content-Length: " . \strlen($content) . "\r\n"; + $header .= "\r\n"; + return $header; + } + protected function write(array $record) : void + { + foreach ($this->users as $user) { + $this->user = $user; + parent::write($record); + $this->closeSocket(); + } + $this->user = null; + } + /** + * @param int|string $value + * + * @phpstan-param Level|LevelName|LogLevel::* $value + */ + public function setHighPriorityLevel($value) : self + { + $this->highPriorityLevel = \FluentSmtpLib\Monolog\Logger::toMonologLevel($value); + return $this; + } + /** + * @param int|string $value + * + * @phpstan-param Level|LevelName|LogLevel::* $value + */ + public function setEmergencyLevel($value) : self + { + $this->emergencyLevel = \FluentSmtpLib\Monolog\Logger::toMonologLevel($value); + return $this; + } + /** + * Use the formatted message? + */ + public function useFormattedMessage(bool $value) : self + { + $this->useFormattedMessage = $value; + return $this; + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/RedisHandler.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/RedisHandler.php new file mode 100644 index 0000000..6c5b031 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/RedisHandler.php @@ -0,0 +1,91 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace FluentSmtpLib\Monolog\Handler; + +use FluentSmtpLib\Monolog\Formatter\LineFormatter; +use FluentSmtpLib\Monolog\Formatter\FormatterInterface; +use FluentSmtpLib\Monolog\Logger; +/** + * Logs to a Redis key using rpush + * + * usage example: + * + * $log = new Logger('application'); + * $redis = new RedisHandler(new Predis\Client("tcp://localhost:6379"), "logs", "prod"); + * $log->pushHandler($redis); + * + * @author Thomas Tourlourat + * + * @phpstan-import-type FormattedRecord from AbstractProcessingHandler + */ +class RedisHandler extends \FluentSmtpLib\Monolog\Handler\AbstractProcessingHandler +{ + /** @var \Predis\Client<\Predis\Client>|\Redis */ + private $redisClient; + /** @var string */ + private $redisKey; + /** @var int */ + protected $capSize; + /** + * @param \Predis\Client<\Predis\Client>|\Redis $redis The redis instance + * @param string $key The key name to push records to + * @param int $capSize Number of entries to limit list size to, 0 = unlimited + */ + public function __construct($redis, string $key, $level = \FluentSmtpLib\Monolog\Logger::DEBUG, bool $bubble = \true, int $capSize = 0) + { + if (!($redis instanceof \FluentSmtpLib\Predis\Client || $redis instanceof \Redis)) { + throw new \InvalidArgumentException('Predis\\Client or Redis instance required'); + } + $this->redisClient = $redis; + $this->redisKey = $key; + $this->capSize = $capSize; + parent::__construct($level, $bubble); + } + /** + * {@inheritDoc} + */ + protected function write(array $record) : void + { + if ($this->capSize) { + $this->writeCapped($record); + } else { + $this->redisClient->rpush($this->redisKey, $record["formatted"]); + } + } + /** + * Write and cap the collection + * Writes the record to the redis list and caps its + * + * @phpstan-param FormattedRecord $record + */ + protected function writeCapped(array $record) : void + { + if ($this->redisClient instanceof \Redis) { + $mode = \defined('\\Redis::MULTI') ? \Redis::MULTI : 1; + $this->redisClient->multi($mode)->rpush($this->redisKey, $record["formatted"])->ltrim($this->redisKey, -$this->capSize, -1)->exec(); + } else { + $redisKey = $this->redisKey; + $capSize = $this->capSize; + $this->redisClient->transaction(function ($tx) use($record, $redisKey, $capSize) { + $tx->rpush($redisKey, $record["formatted"]); + $tx->ltrim($redisKey, -$capSize, -1); + }); + } + } + /** + * {@inheritDoc} + */ + protected function getDefaultFormatter() : \FluentSmtpLib\Monolog\Formatter\FormatterInterface + { + return new \FluentSmtpLib\Monolog\Formatter\LineFormatter(); + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/RedisPubSubHandler.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/RedisPubSubHandler.php new file mode 100644 index 0000000..b4f8a45 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/RedisPubSubHandler.php @@ -0,0 +1,61 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace FluentSmtpLib\Monolog\Handler; + +use FluentSmtpLib\Monolog\Formatter\LineFormatter; +use FluentSmtpLib\Monolog\Formatter\FormatterInterface; +use FluentSmtpLib\Monolog\Logger; +/** + * Sends the message to a Redis Pub/Sub channel using PUBLISH + * + * usage example: + * + * $log = new Logger('application'); + * $redis = new RedisPubSubHandler(new Predis\Client("tcp://localhost:6379"), "logs", Logger::WARNING); + * $log->pushHandler($redis); + * + * @author Gaëtan Faugère + */ +class RedisPubSubHandler extends \FluentSmtpLib\Monolog\Handler\AbstractProcessingHandler +{ + /** @var \Predis\Client<\Predis\Client>|\Redis */ + private $redisClient; + /** @var string */ + private $channelKey; + /** + * @param \Predis\Client<\Predis\Client>|\Redis $redis The redis instance + * @param string $key The channel key to publish records to + */ + public function __construct($redis, string $key, $level = \FluentSmtpLib\Monolog\Logger::DEBUG, bool $bubble = \true) + { + if (!($redis instanceof \FluentSmtpLib\Predis\Client || $redis instanceof \Redis)) { + throw new \InvalidArgumentException('Predis\\Client or Redis instance required'); + } + $this->redisClient = $redis; + $this->channelKey = $key; + parent::__construct($level, $bubble); + } + /** + * {@inheritDoc} + */ + protected function write(array $record) : void + { + $this->redisClient->publish($this->channelKey, $record["formatted"]); + } + /** + * {@inheritDoc} + */ + protected function getDefaultFormatter() : \FluentSmtpLib\Monolog\Formatter\FormatterInterface + { + return new \FluentSmtpLib\Monolog\Formatter\LineFormatter(); + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/RollbarHandler.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/RollbarHandler.php new file mode 100644 index 0000000..f8f4550 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/RollbarHandler.php @@ -0,0 +1,102 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace FluentSmtpLib\Monolog\Handler; + +use FluentSmtpLib\Rollbar\RollbarLogger; +use Throwable; +use FluentSmtpLib\Monolog\Logger; +/** + * Sends errors to Rollbar + * + * If the context data contains a `payload` key, that is used as an array + * of payload options to RollbarLogger's log method. + * + * Rollbar's context info will contain the context + extra keys from the log record + * merged, and then on top of that a few keys: + * + * - level (rollbar level name) + * - monolog_level (monolog level name, raw level, as rollbar only has 5 but monolog 8) + * - channel + * - datetime (unix timestamp) + * + * @author Paul Statezny + */ +class RollbarHandler extends \FluentSmtpLib\Monolog\Handler\AbstractProcessingHandler +{ + /** + * @var RollbarLogger + */ + protected $rollbarLogger; + /** @var string[] */ + protected $levelMap = [\FluentSmtpLib\Monolog\Logger::DEBUG => 'debug', \FluentSmtpLib\Monolog\Logger::INFO => 'info', \FluentSmtpLib\Monolog\Logger::NOTICE => 'info', \FluentSmtpLib\Monolog\Logger::WARNING => 'warning', \FluentSmtpLib\Monolog\Logger::ERROR => 'error', \FluentSmtpLib\Monolog\Logger::CRITICAL => 'critical', \FluentSmtpLib\Monolog\Logger::ALERT => 'critical', \FluentSmtpLib\Monolog\Logger::EMERGENCY => 'critical']; + /** + * Records whether any log records have been added since the last flush of the rollbar notifier + * + * @var bool + */ + private $hasRecords = \false; + /** @var bool */ + protected $initialized = \false; + /** + * @param RollbarLogger $rollbarLogger RollbarLogger object constructed with valid token + */ + public function __construct(\FluentSmtpLib\Rollbar\RollbarLogger $rollbarLogger, $level = \FluentSmtpLib\Monolog\Logger::ERROR, bool $bubble = \true) + { + $this->rollbarLogger = $rollbarLogger; + parent::__construct($level, $bubble); + } + /** + * {@inheritDoc} + */ + protected function write(array $record) : void + { + if (!$this->initialized) { + // __destructor() doesn't get called on Fatal errors + \register_shutdown_function(array($this, 'close')); + $this->initialized = \true; + } + $context = $record['context']; + $context = \array_merge($context, $record['extra'], ['level' => $this->levelMap[$record['level']], 'monolog_level' => $record['level_name'], 'channel' => $record['channel'], 'datetime' => $record['datetime']->format('U')]); + if (isset($context['exception']) && $context['exception'] instanceof \Throwable) { + $exception = $context['exception']; + unset($context['exception']); + $toLog = $exception; + } else { + $toLog = $record['message']; + } + // @phpstan-ignore-next-line + $this->rollbarLogger->log($context['level'], $toLog, $context); + $this->hasRecords = \true; + } + public function flush() : void + { + if ($this->hasRecords) { + $this->rollbarLogger->flush(); + $this->hasRecords = \false; + } + } + /** + * {@inheritDoc} + */ + public function close() : void + { + $this->flush(); + } + /** + * {@inheritDoc} + */ + public function reset() + { + $this->flush(); + parent::reset(); + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/RotatingFileHandler.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/RotatingFileHandler.php new file mode 100644 index 0000000..884e274 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/RotatingFileHandler.php @@ -0,0 +1,169 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace FluentSmtpLib\Monolog\Handler; + +use InvalidArgumentException; +use FluentSmtpLib\Monolog\Logger; +use FluentSmtpLib\Monolog\Utils; +/** + * Stores logs to files that are rotated every day and a limited number of files are kept. + * + * This rotation is only intended to be used as a workaround. Using logrotate to + * handle the rotation is strongly encouraged when you can use it. + * + * @author Christophe Coevoet + * @author Jordi Boggiano + */ +class RotatingFileHandler extends \FluentSmtpLib\Monolog\Handler\StreamHandler +{ + public const FILE_PER_DAY = 'Y-m-d'; + public const FILE_PER_MONTH = 'Y-m'; + public const FILE_PER_YEAR = 'Y'; + /** @var string */ + protected $filename; + /** @var int */ + protected $maxFiles; + /** @var bool */ + protected $mustRotate; + /** @var \DateTimeImmutable */ + protected $nextRotation; + /** @var string */ + protected $filenameFormat; + /** @var string */ + protected $dateFormat; + /** + * @param string $filename + * @param int $maxFiles The maximal amount of files to keep (0 means unlimited) + * @param int|null $filePermission Optional file permissions (default (0644) are only for owner read/write) + * @param bool $useLocking Try to lock log file before doing any writes + */ + public function __construct(string $filename, int $maxFiles = 0, $level = \FluentSmtpLib\Monolog\Logger::DEBUG, bool $bubble = \true, ?int $filePermission = null, bool $useLocking = \false) + { + $this->filename = \FluentSmtpLib\Monolog\Utils::canonicalizePath($filename); + $this->maxFiles = $maxFiles; + $this->nextRotation = new \DateTimeImmutable('tomorrow'); + $this->filenameFormat = '{filename}-{date}'; + $this->dateFormat = static::FILE_PER_DAY; + parent::__construct($this->getTimedFilename(), $level, $bubble, $filePermission, $useLocking); + } + /** + * {@inheritDoc} + */ + public function close() : void + { + parent::close(); + if (\true === $this->mustRotate) { + $this->rotate(); + } + } + /** + * {@inheritDoc} + */ + public function reset() + { + parent::reset(); + if (\true === $this->mustRotate) { + $this->rotate(); + } + } + public function setFilenameFormat(string $filenameFormat, string $dateFormat) : self + { + if (!\preg_match('{^[Yy](([/_.-]?m)([/_.-]?d)?)?$}', $dateFormat)) { + throw new \InvalidArgumentException('Invalid date format - format must be one of ' . 'RotatingFileHandler::FILE_PER_DAY ("Y-m-d"), RotatingFileHandler::FILE_PER_MONTH ("Y-m") ' . 'or RotatingFileHandler::FILE_PER_YEAR ("Y"), or you can set one of the ' . 'date formats using slashes, underscores and/or dots instead of dashes.'); + } + if (\substr_count($filenameFormat, '{date}') === 0) { + throw new \InvalidArgumentException('Invalid filename format - format must contain at least `{date}`, because otherwise rotating is impossible.'); + } + $this->filenameFormat = $filenameFormat; + $this->dateFormat = $dateFormat; + $this->url = $this->getTimedFilename(); + $this->close(); + return $this; + } + /** + * {@inheritDoc} + */ + protected function write(array $record) : void + { + // on the first record written, if the log is new, we rotate (once per day) after the log has been written so that the new file exists + if (null === $this->mustRotate) { + $this->mustRotate = null === $this->url || !\file_exists($this->url); + } + // if the next rotation is expired, then we rotate immediately + if ($this->nextRotation <= $record['datetime']) { + $this->mustRotate = \true; + $this->close(); + // triggers rotation + } + parent::write($record); + if ($this->mustRotate) { + $this->close(); + // triggers rotation + } + } + /** + * Rotates the files. + */ + protected function rotate() : void + { + // update filename + $this->url = $this->getTimedFilename(); + $this->nextRotation = new \DateTimeImmutable('tomorrow'); + $this->mustRotate = \false; + // skip GC of old logs if files are unlimited + if (0 === $this->maxFiles) { + return; + } + $logFiles = \glob($this->getGlobPattern()); + if (\false === $logFiles) { + // failed to glob + return; + } + if ($this->maxFiles >= \count($logFiles)) { + // no files to remove + return; + } + // Sorting the files by name to remove the older ones + \usort($logFiles, function ($a, $b) { + return \strcmp($b, $a); + }); + foreach (\array_slice($logFiles, $this->maxFiles) as $file) { + if (\is_writable($file)) { + // suppress errors here as unlink() might fail if two processes + // are cleaning up/rotating at the same time + \set_error_handler(function (int $errno, string $errstr, string $errfile, int $errline) : bool { + return \false; + }); + \unlink($file); + \restore_error_handler(); + } + } + } + protected function getTimedFilename() : string + { + $fileInfo = \pathinfo($this->filename); + $timedFilename = \str_replace(['{filename}', '{date}'], [$fileInfo['filename'], \date($this->dateFormat)], $fileInfo['dirname'] . '/' . $this->filenameFormat); + if (isset($fileInfo['extension'])) { + $timedFilename .= '.' . $fileInfo['extension']; + } + return $timedFilename; + } + protected function getGlobPattern() : string + { + $fileInfo = \pathinfo($this->filename); + $glob = \str_replace(['{filename}', '{date}'], [$fileInfo['filename'], \str_replace(['Y', 'y', 'm', 'd'], ['[0-9][0-9][0-9][0-9]', '[0-9][0-9]', '[0-9][0-9]', '[0-9][0-9]'], $this->dateFormat)], $fileInfo['dirname'] . '/' . $this->filenameFormat); + if (isset($fileInfo['extension'])) { + $glob .= '.' . $fileInfo['extension']; + } + return $glob; + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/SamplingHandler.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/SamplingHandler.php new file mode 100644 index 0000000..e39a864 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/SamplingHandler.php @@ -0,0 +1,116 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace FluentSmtpLib\Monolog\Handler; + +use FluentSmtpLib\Monolog\Formatter\FormatterInterface; +/** + * Sampling handler + * + * A sampled event stream can be useful for logging high frequency events in + * a production environment where you only need an idea of what is happening + * and are not concerned with capturing every occurrence. Since the decision to + * handle or not handle a particular event is determined randomly, the + * resulting sampled log is not guaranteed to contain 1/N of the events that + * occurred in the application, but based on the Law of large numbers, it will + * tend to be close to this ratio with a large number of attempts. + * + * @author Bryan Davis + * @author Kunal Mehta + * + * @phpstan-import-type Record from \Monolog\Logger + * @phpstan-import-type Level from \Monolog\Logger + */ +class SamplingHandler extends \FluentSmtpLib\Monolog\Handler\AbstractHandler implements \FluentSmtpLib\Monolog\Handler\ProcessableHandlerInterface, \FluentSmtpLib\Monolog\Handler\FormattableHandlerInterface +{ + use ProcessableHandlerTrait; + /** + * @var HandlerInterface|callable + * @phpstan-var HandlerInterface|callable(Record|array{level: Level}|null, HandlerInterface): HandlerInterface + */ + protected $handler; + /** + * @var int $factor + */ + protected $factor; + /** + * @psalm-param HandlerInterface|callable(Record|array{level: Level}|null, HandlerInterface): HandlerInterface $handler + * + * @param callable|HandlerInterface $handler Handler or factory callable($record|null, $samplingHandler). + * @param int $factor Sample factor (e.g. 10 means every ~10th record is sampled) + */ + public function __construct($handler, int $factor) + { + parent::__construct(); + $this->handler = $handler; + $this->factor = $factor; + if (!$this->handler instanceof \FluentSmtpLib\Monolog\Handler\HandlerInterface && !\is_callable($this->handler)) { + throw new \RuntimeException("The given handler (" . \json_encode($this->handler) . ") is not a callable nor a Monolog\\Handler\\HandlerInterface object"); + } + } + public function isHandling(array $record) : bool + { + return $this->getHandler($record)->isHandling($record); + } + public function handle(array $record) : bool + { + if ($this->isHandling($record) && \mt_rand(1, $this->factor) === 1) { + if ($this->processors) { + /** @var Record $record */ + $record = $this->processRecord($record); + } + $this->getHandler($record)->handle($record); + } + return \false === $this->bubble; + } + /** + * Return the nested handler + * + * If the handler was provided as a factory callable, this will trigger the handler's instantiation. + * + * @phpstan-param Record|array{level: Level}|null $record + * + * @return HandlerInterface + */ + public function getHandler(?array $record = null) + { + if (!$this->handler instanceof \FluentSmtpLib\Monolog\Handler\HandlerInterface) { + $this->handler = ($this->handler)($record, $this); + if (!$this->handler instanceof \FluentSmtpLib\Monolog\Handler\HandlerInterface) { + throw new \RuntimeException("The factory callable should return a HandlerInterface"); + } + } + return $this->handler; + } + /** + * {@inheritDoc} + */ + public function setFormatter(\FluentSmtpLib\Monolog\Formatter\FormatterInterface $formatter) : \FluentSmtpLib\Monolog\Handler\HandlerInterface + { + $handler = $this->getHandler(); + if ($handler instanceof \FluentSmtpLib\Monolog\Handler\FormattableHandlerInterface) { + $handler->setFormatter($formatter); + return $this; + } + throw new \UnexpectedValueException('The nested handler of type ' . \get_class($handler) . ' does not support formatters.'); + } + /** + * {@inheritDoc} + */ + public function getFormatter() : \FluentSmtpLib\Monolog\Formatter\FormatterInterface + { + $handler = $this->getHandler(); + if ($handler instanceof \FluentSmtpLib\Monolog\Handler\FormattableHandlerInterface) { + return $handler->getFormatter(); + } + throw new \UnexpectedValueException('The nested handler of type ' . \get_class($handler) . ' does not support formatters.'); + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/SendGridHandler.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/SendGridHandler.php new file mode 100644 index 0000000..1b48150 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/SendGridHandler.php @@ -0,0 +1,92 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace FluentSmtpLib\Monolog\Handler; + +use FluentSmtpLib\Monolog\Logger; +/** + * SendGridrHandler uses the SendGrid API v2 function to send Log emails, more information in https://sendgrid.com/docs/API_Reference/Web_API/mail.html + * + * @author Ricardo Fontanelli + */ +class SendGridHandler extends \FluentSmtpLib\Monolog\Handler\MailHandler +{ + /** + * The SendGrid API User + * @var string + */ + protected $apiUser; + /** + * The SendGrid API Key + * @var string + */ + protected $apiKey; + /** + * The email addresses to which the message will be sent + * @var string + */ + protected $from; + /** + * The email addresses to which the message will be sent + * @var string[] + */ + protected $to; + /** + * The subject of the email + * @var string + */ + protected $subject; + /** + * @param string $apiUser The SendGrid API User + * @param string $apiKey The SendGrid API Key + * @param string $from The sender of the email + * @param string|string[] $to The recipients of the email + * @param string $subject The subject of the mail + */ + public function __construct(string $apiUser, string $apiKey, string $from, $to, string $subject, $level = \FluentSmtpLib\Monolog\Logger::ERROR, bool $bubble = \true) + { + if (!\extension_loaded('curl')) { + throw new \FluentSmtpLib\Monolog\Handler\MissingExtensionException('The curl extension is needed to use the SendGridHandler'); + } + parent::__construct($level, $bubble); + $this->apiUser = $apiUser; + $this->apiKey = $apiKey; + $this->from = $from; + $this->to = (array) $to; + $this->subject = $subject; + } + /** + * {@inheritDoc} + */ + protected function send(string $content, array $records) : void + { + $message = []; + $message['api_user'] = $this->apiUser; + $message['api_key'] = $this->apiKey; + $message['from'] = $this->from; + foreach ($this->to as $recipient) { + $message['to[]'] = $recipient; + } + $message['subject'] = $this->subject; + $message['date'] = \date('r'); + if ($this->isHtmlBody($content)) { + $message['html'] = $content; + } else { + $message['text'] = $content; + } + $ch = \curl_init(); + \curl_setopt($ch, \CURLOPT_URL, 'https://api.sendgrid.com/api/mail.send.json'); + \curl_setopt($ch, \CURLOPT_POST, 1); + \curl_setopt($ch, \CURLOPT_RETURNTRANSFER, 1); + \curl_setopt($ch, \CURLOPT_POSTFIELDS, \http_build_query($message)); + \FluentSmtpLib\Monolog\Handler\Curl\Util::execute($ch, 2); + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/Slack/SlackRecord.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/Slack/SlackRecord.php new file mode 100644 index 0000000..120d239 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/Slack/SlackRecord.php @@ -0,0 +1,293 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace FluentSmtpLib\Monolog\Handler\Slack; + +use FluentSmtpLib\Monolog\Logger; +use FluentSmtpLib\Monolog\Utils; +use FluentSmtpLib\Monolog\Formatter\NormalizerFormatter; +use FluentSmtpLib\Monolog\Formatter\FormatterInterface; +/** + * Slack record utility helping to log to Slack webhooks or API. + * + * @author Greg Kedzierski + * @author Haralan Dobrev + * @see https://api.slack.com/incoming-webhooks + * @see https://api.slack.com/docs/message-attachments + * + * @phpstan-import-type FormattedRecord from \Monolog\Handler\AbstractProcessingHandler + * @phpstan-import-type Record from \Monolog\Logger + */ +class SlackRecord +{ + public const COLOR_DANGER = 'danger'; + public const COLOR_WARNING = 'warning'; + public const COLOR_GOOD = 'good'; + public const COLOR_DEFAULT = '#e3e4e6'; + /** + * Slack channel (encoded ID or name) + * @var string|null + */ + private $channel; + /** + * Name of a bot + * @var string|null + */ + private $username; + /** + * User icon e.g. 'ghost', 'http://example.com/user.png' + * @var string|null + */ + private $userIcon; + /** + * Whether the message should be added to Slack as attachment (plain text otherwise) + * @var bool + */ + private $useAttachment; + /** + * Whether the the context/extra messages added to Slack as attachments are in a short style + * @var bool + */ + private $useShortAttachment; + /** + * Whether the attachment should include context and extra data + * @var bool + */ + private $includeContextAndExtra; + /** + * Dot separated list of fields to exclude from slack message. E.g. ['context.field1', 'extra.field2'] + * @var string[] + */ + private $excludeFields; + /** + * @var ?FormatterInterface + */ + private $formatter; + /** + * @var NormalizerFormatter + */ + private $normalizerFormatter; + /** + * @param string[] $excludeFields + */ + public function __construct(?string $channel = null, ?string $username = null, bool $useAttachment = \true, ?string $userIcon = null, bool $useShortAttachment = \false, bool $includeContextAndExtra = \false, array $excludeFields = array(), ?\FluentSmtpLib\Monolog\Formatter\FormatterInterface $formatter = null) + { + $this->setChannel($channel)->setUsername($username)->useAttachment($useAttachment)->setUserIcon($userIcon)->useShortAttachment($useShortAttachment)->includeContextAndExtra($includeContextAndExtra)->excludeFields($excludeFields)->setFormatter($formatter); + if ($this->includeContextAndExtra) { + $this->normalizerFormatter = new \FluentSmtpLib\Monolog\Formatter\NormalizerFormatter(); + } + } + /** + * Returns required data in format that Slack + * is expecting. + * + * @phpstan-param FormattedRecord $record + * @phpstan-return mixed[] + */ + public function getSlackData(array $record) : array + { + $dataArray = array(); + $record = $this->removeExcludedFields($record); + if ($this->username) { + $dataArray['username'] = $this->username; + } + if ($this->channel) { + $dataArray['channel'] = $this->channel; + } + if ($this->formatter && !$this->useAttachment) { + /** @phpstan-ignore-next-line */ + $message = $this->formatter->format($record); + } else { + $message = $record['message']; + } + if ($this->useAttachment) { + $attachment = array('fallback' => $message, 'text' => $message, 'color' => $this->getAttachmentColor($record['level']), 'fields' => array(), 'mrkdwn_in' => array('fields'), 'ts' => $record['datetime']->getTimestamp(), 'footer' => $this->username, 'footer_icon' => $this->userIcon); + if ($this->useShortAttachment) { + $attachment['title'] = $record['level_name']; + } else { + $attachment['title'] = 'Message'; + $attachment['fields'][] = $this->generateAttachmentField('Level', $record['level_name']); + } + if ($this->includeContextAndExtra) { + foreach (array('extra', 'context') as $key) { + if (empty($record[$key])) { + continue; + } + if ($this->useShortAttachment) { + $attachment['fields'][] = $this->generateAttachmentField((string) $key, $record[$key]); + } else { + // Add all extra fields as individual fields in attachment + $attachment['fields'] = \array_merge($attachment['fields'], $this->generateAttachmentFields($record[$key])); + } + } + } + $dataArray['attachments'] = array($attachment); + } else { + $dataArray['text'] = $message; + } + if ($this->userIcon) { + if (\filter_var($this->userIcon, \FILTER_VALIDATE_URL)) { + $dataArray['icon_url'] = $this->userIcon; + } else { + $dataArray['icon_emoji'] = ":{$this->userIcon}:"; + } + } + return $dataArray; + } + /** + * Returns a Slack message attachment color associated with + * provided level. + */ + public function getAttachmentColor(int $level) : string + { + switch (\true) { + case $level >= \FluentSmtpLib\Monolog\Logger::ERROR: + return static::COLOR_DANGER; + case $level >= \FluentSmtpLib\Monolog\Logger::WARNING: + return static::COLOR_WARNING; + case $level >= \FluentSmtpLib\Monolog\Logger::INFO: + return static::COLOR_GOOD; + default: + return static::COLOR_DEFAULT; + } + } + /** + * Stringifies an array of key/value pairs to be used in attachment fields + * + * @param mixed[] $fields + */ + public function stringify(array $fields) : string + { + /** @var Record $fields */ + $normalized = $this->normalizerFormatter->format($fields); + $hasSecondDimension = \count(\array_filter($normalized, 'is_array')); + $hasNonNumericKeys = !\count(\array_filter(\array_keys($normalized), 'is_numeric')); + return $hasSecondDimension || $hasNonNumericKeys ? \FluentSmtpLib\Monolog\Utils::jsonEncode($normalized, \JSON_PRETTY_PRINT | \FluentSmtpLib\Monolog\Utils::DEFAULT_JSON_FLAGS) : \FluentSmtpLib\Monolog\Utils::jsonEncode($normalized, \FluentSmtpLib\Monolog\Utils::DEFAULT_JSON_FLAGS); + } + /** + * Channel used by the bot when posting + * + * @param ?string $channel + * + * @return static + */ + public function setChannel(?string $channel = null) : self + { + $this->channel = $channel; + return $this; + } + /** + * Username used by the bot when posting + * + * @param ?string $username + * + * @return static + */ + public function setUsername(?string $username = null) : self + { + $this->username = $username; + return $this; + } + public function useAttachment(bool $useAttachment = \true) : self + { + $this->useAttachment = $useAttachment; + return $this; + } + public function setUserIcon(?string $userIcon = null) : self + { + $this->userIcon = $userIcon; + if (\is_string($userIcon)) { + $this->userIcon = \trim($userIcon, ':'); + } + return $this; + } + public function useShortAttachment(bool $useShortAttachment = \false) : self + { + $this->useShortAttachment = $useShortAttachment; + return $this; + } + public function includeContextAndExtra(bool $includeContextAndExtra = \false) : self + { + $this->includeContextAndExtra = $includeContextAndExtra; + if ($this->includeContextAndExtra) { + $this->normalizerFormatter = new \FluentSmtpLib\Monolog\Formatter\NormalizerFormatter(); + } + return $this; + } + /** + * @param string[] $excludeFields + */ + public function excludeFields(array $excludeFields = []) : self + { + $this->excludeFields = $excludeFields; + return $this; + } + public function setFormatter(?\FluentSmtpLib\Monolog\Formatter\FormatterInterface $formatter = null) : self + { + $this->formatter = $formatter; + return $this; + } + /** + * Generates attachment field + * + * @param string|mixed[] $value + * + * @return array{title: string, value: string, short: false} + */ + private function generateAttachmentField(string $title, $value) : array + { + $value = \is_array($value) ? \sprintf('```%s```', \substr($this->stringify($value), 0, 1990)) : $value; + return array('title' => \ucfirst($title), 'value' => $value, 'short' => \false); + } + /** + * Generates a collection of attachment fields from array + * + * @param mixed[] $data + * + * @return array + */ + private function generateAttachmentFields(array $data) : array + { + /** @var Record $data */ + $normalized = $this->normalizerFormatter->format($data); + $fields = array(); + foreach ($normalized as $key => $value) { + $fields[] = $this->generateAttachmentField((string) $key, $value); + } + return $fields; + } + /** + * Get a copy of record with fields excluded according to $this->excludeFields + * + * @phpstan-param FormattedRecord $record + * + * @return mixed[] + */ + private function removeExcludedFields(array $record) : array + { + foreach ($this->excludeFields as $field) { + $keys = \explode('.', $field); + $node =& $record; + $lastKey = \end($keys); + foreach ($keys as $key) { + if (!isset($node[$key])) { + break; + } + if ($lastKey === $key) { + unset($node[$key]); + break; + } + $node =& $node[$key]; + } + } + return $record; + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/SlackHandler.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/SlackHandler.php new file mode 100644 index 0000000..c401c01 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/SlackHandler.php @@ -0,0 +1,187 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace FluentSmtpLib\Monolog\Handler; + +use FluentSmtpLib\Monolog\Formatter\FormatterInterface; +use FluentSmtpLib\Monolog\Logger; +use FluentSmtpLib\Monolog\Utils; +use FluentSmtpLib\Monolog\Handler\Slack\SlackRecord; +/** + * Sends notifications through Slack API + * + * @author Greg Kedzierski + * @see https://api.slack.com/ + * + * @phpstan-import-type FormattedRecord from AbstractProcessingHandler + */ +class SlackHandler extends \FluentSmtpLib\Monolog\Handler\SocketHandler +{ + /** + * Slack API token + * @var string + */ + private $token; + /** + * Instance of the SlackRecord util class preparing data for Slack API. + * @var SlackRecord + */ + private $slackRecord; + /** + * @param string $token Slack API token + * @param string $channel Slack channel (encoded ID or name) + * @param string|null $username Name of a bot + * @param bool $useAttachment Whether the message should be added to Slack as attachment (plain text otherwise) + * @param string|null $iconEmoji The emoji name to use (or null) + * @param bool $useShortAttachment Whether the context/extra messages added to Slack as attachments are in a short style + * @param bool $includeContextAndExtra Whether the attachment should include context and extra data + * @param string[] $excludeFields Dot separated list of fields to exclude from slack message. E.g. ['context.field1', 'extra.field2'] + * @throws MissingExtensionException If no OpenSSL PHP extension configured + */ + public function __construct(string $token, string $channel, ?string $username = null, bool $useAttachment = \true, ?string $iconEmoji = null, $level = \FluentSmtpLib\Monolog\Logger::CRITICAL, bool $bubble = \true, bool $useShortAttachment = \false, bool $includeContextAndExtra = \false, array $excludeFields = array(), bool $persistent = \false, float $timeout = 0.0, float $writingTimeout = 10.0, ?float $connectionTimeout = null, ?int $chunkSize = null) + { + if (!\extension_loaded('openssl')) { + throw new \FluentSmtpLib\Monolog\Handler\MissingExtensionException('The OpenSSL PHP extension is required to use the SlackHandler'); + } + parent::__construct('ssl://slack.com:443', $level, $bubble, $persistent, $timeout, $writingTimeout, $connectionTimeout, $chunkSize); + $this->slackRecord = new \FluentSmtpLib\Monolog\Handler\Slack\SlackRecord($channel, $username, $useAttachment, $iconEmoji, $useShortAttachment, $includeContextAndExtra, $excludeFields); + $this->token = $token; + } + public function getSlackRecord() : \FluentSmtpLib\Monolog\Handler\Slack\SlackRecord + { + return $this->slackRecord; + } + public function getToken() : string + { + return $this->token; + } + /** + * {@inheritDoc} + */ + protected function generateDataStream(array $record) : string + { + $content = $this->buildContent($record); + return $this->buildHeader($content) . $content; + } + /** + * Builds the body of API call + * + * @phpstan-param FormattedRecord $record + */ + private function buildContent(array $record) : string + { + $dataArray = $this->prepareContentData($record); + return \http_build_query($dataArray); + } + /** + * @phpstan-param FormattedRecord $record + * @return string[] + */ + protected function prepareContentData(array $record) : array + { + $dataArray = $this->slackRecord->getSlackData($record); + $dataArray['token'] = $this->token; + if (!empty($dataArray['attachments'])) { + $dataArray['attachments'] = \FluentSmtpLib\Monolog\Utils::jsonEncode($dataArray['attachments']); + } + return $dataArray; + } + /** + * Builds the header of the API Call + */ + private function buildHeader(string $content) : string + { + $header = "POST /api/chat.postMessage HTTP/1.1\r\n"; + $header .= "Host: slack.com\r\n"; + $header .= "Content-Type: application/x-www-form-urlencoded\r\n"; + $header .= "Content-Length: " . \strlen($content) . "\r\n"; + $header .= "\r\n"; + return $header; + } + /** + * {@inheritDoc} + */ + protected function write(array $record) : void + { + parent::write($record); + $this->finalizeWrite(); + } + /** + * Finalizes the request by reading some bytes and then closing the socket + * + * If we do not read some but close the socket too early, slack sometimes + * drops the request entirely. + */ + protected function finalizeWrite() : void + { + $res = $this->getResource(); + if (\is_resource($res)) { + @\fread($res, 2048); + } + $this->closeSocket(); + } + public function setFormatter(\FluentSmtpLib\Monolog\Formatter\FormatterInterface $formatter) : \FluentSmtpLib\Monolog\Handler\HandlerInterface + { + parent::setFormatter($formatter); + $this->slackRecord->setFormatter($formatter); + return $this; + } + public function getFormatter() : \FluentSmtpLib\Monolog\Formatter\FormatterInterface + { + $formatter = parent::getFormatter(); + $this->slackRecord->setFormatter($formatter); + return $formatter; + } + /** + * Channel used by the bot when posting + */ + public function setChannel(string $channel) : self + { + $this->slackRecord->setChannel($channel); + return $this; + } + /** + * Username used by the bot when posting + */ + public function setUsername(string $username) : self + { + $this->slackRecord->setUsername($username); + return $this; + } + public function useAttachment(bool $useAttachment) : self + { + $this->slackRecord->useAttachment($useAttachment); + return $this; + } + public function setIconEmoji(string $iconEmoji) : self + { + $this->slackRecord->setUserIcon($iconEmoji); + return $this; + } + public function useShortAttachment(bool $useShortAttachment) : self + { + $this->slackRecord->useShortAttachment($useShortAttachment); + return $this; + } + public function includeContextAndExtra(bool $includeContextAndExtra) : self + { + $this->slackRecord->includeContextAndExtra($includeContextAndExtra); + return $this; + } + /** + * @param string[] $excludeFields + */ + public function excludeFields(array $excludeFields) : self + { + $this->slackRecord->excludeFields($excludeFields); + return $this; + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/SlackWebhookHandler.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/SlackWebhookHandler.php new file mode 100644 index 0000000..ed2aa7f --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/SlackWebhookHandler.php @@ -0,0 +1,90 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace FluentSmtpLib\Monolog\Handler; + +use FluentSmtpLib\Monolog\Formatter\FormatterInterface; +use FluentSmtpLib\Monolog\Logger; +use FluentSmtpLib\Monolog\Utils; +use FluentSmtpLib\Monolog\Handler\Slack\SlackRecord; +/** + * Sends notifications through Slack Webhooks + * + * @author Haralan Dobrev + * @see https://api.slack.com/incoming-webhooks + */ +class SlackWebhookHandler extends \FluentSmtpLib\Monolog\Handler\AbstractProcessingHandler +{ + /** + * Slack Webhook token + * @var string + */ + private $webhookUrl; + /** + * Instance of the SlackRecord util class preparing data for Slack API. + * @var SlackRecord + */ + private $slackRecord; + /** + * @param string $webhookUrl Slack Webhook URL + * @param string|null $channel Slack channel (encoded ID or name) + * @param string|null $username Name of a bot + * @param bool $useAttachment Whether the message should be added to Slack as attachment (plain text otherwise) + * @param string|null $iconEmoji The emoji name to use (or null) + * @param bool $useShortAttachment Whether the the context/extra messages added to Slack as attachments are in a short style + * @param bool $includeContextAndExtra Whether the attachment should include context and extra data + * @param string[] $excludeFields Dot separated list of fields to exclude from slack message. E.g. ['context.field1', 'extra.field2'] + */ + public function __construct(string $webhookUrl, ?string $channel = null, ?string $username = null, bool $useAttachment = \true, ?string $iconEmoji = null, bool $useShortAttachment = \false, bool $includeContextAndExtra = \false, $level = \FluentSmtpLib\Monolog\Logger::CRITICAL, bool $bubble = \true, array $excludeFields = array()) + { + if (!\extension_loaded('curl')) { + throw new \FluentSmtpLib\Monolog\Handler\MissingExtensionException('The curl extension is needed to use the SlackWebhookHandler'); + } + parent::__construct($level, $bubble); + $this->webhookUrl = $webhookUrl; + $this->slackRecord = new \FluentSmtpLib\Monolog\Handler\Slack\SlackRecord($channel, $username, $useAttachment, $iconEmoji, $useShortAttachment, $includeContextAndExtra, $excludeFields); + } + public function getSlackRecord() : \FluentSmtpLib\Monolog\Handler\Slack\SlackRecord + { + return $this->slackRecord; + } + public function getWebhookUrl() : string + { + return $this->webhookUrl; + } + /** + * {@inheritDoc} + */ + protected function write(array $record) : void + { + $postData = $this->slackRecord->getSlackData($record); + $postString = \FluentSmtpLib\Monolog\Utils::jsonEncode($postData); + $ch = \curl_init(); + $options = array(\CURLOPT_URL => $this->webhookUrl, \CURLOPT_POST => \true, \CURLOPT_RETURNTRANSFER => \true, \CURLOPT_HTTPHEADER => array('Content-type: application/json'), \CURLOPT_POSTFIELDS => $postString); + if (\defined('CURLOPT_SAFE_UPLOAD')) { + $options[\CURLOPT_SAFE_UPLOAD] = \true; + } + \curl_setopt_array($ch, $options); + \FluentSmtpLib\Monolog\Handler\Curl\Util::execute($ch); + } + public function setFormatter(\FluentSmtpLib\Monolog\Formatter\FormatterInterface $formatter) : \FluentSmtpLib\Monolog\Handler\HandlerInterface + { + parent::setFormatter($formatter); + $this->slackRecord->setFormatter($formatter); + return $this; + } + public function getFormatter() : \FluentSmtpLib\Monolog\Formatter\FormatterInterface + { + $formatter = parent::getFormatter(); + $this->slackRecord->setFormatter($formatter); + return $formatter; + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/SocketHandler.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/SocketHandler.php new file mode 100644 index 0000000..f5503b9 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/SocketHandler.php @@ -0,0 +1,388 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace FluentSmtpLib\Monolog\Handler; + +use FluentSmtpLib\Monolog\Logger; +/** + * Stores to any socket - uses fsockopen() or pfsockopen(). + * + * @author Pablo de Leon Belloc + * @see http://php.net/manual/en/function.fsockopen.php + * + * @phpstan-import-type Record from \Monolog\Logger + * @phpstan-import-type FormattedRecord from AbstractProcessingHandler + */ +class SocketHandler extends \FluentSmtpLib\Monolog\Handler\AbstractProcessingHandler +{ + /** @var string */ + private $connectionString; + /** @var float */ + private $connectionTimeout; + /** @var resource|null */ + private $resource; + /** @var float */ + private $timeout; + /** @var float */ + private $writingTimeout; + /** @var ?int */ + private $lastSentBytes = null; + /** @var ?int */ + private $chunkSize; + /** @var bool */ + private $persistent; + /** @var ?int */ + private $errno = null; + /** @var ?string */ + private $errstr = null; + /** @var ?float */ + private $lastWritingAt = null; + /** + * @param string $connectionString Socket connection string + * @param bool $persistent Flag to enable/disable persistent connections + * @param float $timeout Socket timeout to wait until the request is being aborted + * @param float $writingTimeout Socket timeout to wait until the request should've been sent/written + * @param float|null $connectionTimeout Socket connect timeout to wait until the connection should've been + * established + * @param int|null $chunkSize Sets the chunk size. Only has effect during connection in the writing cycle + * + * @throws \InvalidArgumentException If an invalid timeout value (less than 0) is passed. + */ + public function __construct(string $connectionString, $level = \FluentSmtpLib\Monolog\Logger::DEBUG, bool $bubble = \true, bool $persistent = \false, float $timeout = 0.0, float $writingTimeout = 10.0, ?float $connectionTimeout = null, ?int $chunkSize = null) + { + parent::__construct($level, $bubble); + $this->connectionString = $connectionString; + if ($connectionTimeout !== null) { + $this->validateTimeout($connectionTimeout); + } + $this->connectionTimeout = $connectionTimeout ?? (float) \ini_get('default_socket_timeout'); + $this->persistent = $persistent; + $this->validateTimeout($timeout); + $this->timeout = $timeout; + $this->validateTimeout($writingTimeout); + $this->writingTimeout = $writingTimeout; + $this->chunkSize = $chunkSize; + } + /** + * Connect (if necessary) and write to the socket + * + * {@inheritDoc} + * + * @throws \UnexpectedValueException + * @throws \RuntimeException + */ + protected function write(array $record) : void + { + $this->connectIfNotConnected(); + $data = $this->generateDataStream($record); + $this->writeToSocket($data); + } + /** + * We will not close a PersistentSocket instance so it can be reused in other requests. + */ + public function close() : void + { + if (!$this->isPersistent()) { + $this->closeSocket(); + } + } + /** + * Close socket, if open + */ + public function closeSocket() : void + { + if (\is_resource($this->resource)) { + \fclose($this->resource); + $this->resource = null; + } + } + /** + * Set socket connection to be persistent. It only has effect before the connection is initiated. + */ + public function setPersistent(bool $persistent) : self + { + $this->persistent = $persistent; + return $this; + } + /** + * Set connection timeout. Only has effect before we connect. + * + * @see http://php.net/manual/en/function.fsockopen.php + */ + public function setConnectionTimeout(float $seconds) : self + { + $this->validateTimeout($seconds); + $this->connectionTimeout = $seconds; + return $this; + } + /** + * Set write timeout. Only has effect before we connect. + * + * @see http://php.net/manual/en/function.stream-set-timeout.php + */ + public function setTimeout(float $seconds) : self + { + $this->validateTimeout($seconds); + $this->timeout = $seconds; + return $this; + } + /** + * Set writing timeout. Only has effect during connection in the writing cycle. + * + * @param float $seconds 0 for no timeout + */ + public function setWritingTimeout(float $seconds) : self + { + $this->validateTimeout($seconds); + $this->writingTimeout = $seconds; + return $this; + } + /** + * Set chunk size. Only has effect during connection in the writing cycle. + */ + public function setChunkSize(int $bytes) : self + { + $this->chunkSize = $bytes; + return $this; + } + /** + * Get current connection string + */ + public function getConnectionString() : string + { + return $this->connectionString; + } + /** + * Get persistent setting + */ + public function isPersistent() : bool + { + return $this->persistent; + } + /** + * Get current connection timeout setting + */ + public function getConnectionTimeout() : float + { + return $this->connectionTimeout; + } + /** + * Get current in-transfer timeout + */ + public function getTimeout() : float + { + return $this->timeout; + } + /** + * Get current local writing timeout + * + * @return float + */ + public function getWritingTimeout() : float + { + return $this->writingTimeout; + } + /** + * Get current chunk size + */ + public function getChunkSize() : ?int + { + return $this->chunkSize; + } + /** + * Check to see if the socket is currently available. + * + * UDP might appear to be connected but might fail when writing. See http://php.net/fsockopen for details. + */ + public function isConnected() : bool + { + return \is_resource($this->resource) && !\feof($this->resource); + // on TCP - other party can close connection. + } + /** + * Wrapper to allow mocking + * + * @return resource|false + */ + protected function pfsockopen() + { + return @\pfsockopen($this->connectionString, -1, $this->errno, $this->errstr, $this->connectionTimeout); + } + /** + * Wrapper to allow mocking + * + * @return resource|false + */ + protected function fsockopen() + { + return @\fsockopen($this->connectionString, -1, $this->errno, $this->errstr, $this->connectionTimeout); + } + /** + * Wrapper to allow mocking + * + * @see http://php.net/manual/en/function.stream-set-timeout.php + * + * @return bool + */ + protected function streamSetTimeout() + { + $seconds = \floor($this->timeout); + $microseconds = \round(($this->timeout - $seconds) * 1000000.0); + if (!\is_resource($this->resource)) { + throw new \LogicException('streamSetTimeout called but $this->resource is not a resource'); + } + return \stream_set_timeout($this->resource, (int) $seconds, (int) $microseconds); + } + /** + * Wrapper to allow mocking + * + * @see http://php.net/manual/en/function.stream-set-chunk-size.php + * + * @return int|bool + */ + protected function streamSetChunkSize() + { + if (!\is_resource($this->resource)) { + throw new \LogicException('streamSetChunkSize called but $this->resource is not a resource'); + } + if (null === $this->chunkSize) { + throw new \LogicException('streamSetChunkSize called but $this->chunkSize is not set'); + } + return \stream_set_chunk_size($this->resource, $this->chunkSize); + } + /** + * Wrapper to allow mocking + * + * @return int|bool + */ + protected function fwrite(string $data) + { + if (!\is_resource($this->resource)) { + throw new \LogicException('fwrite called but $this->resource is not a resource'); + } + return @\fwrite($this->resource, $data); + } + /** + * Wrapper to allow mocking + * + * @return mixed[]|bool + */ + protected function streamGetMetadata() + { + if (!\is_resource($this->resource)) { + throw new \LogicException('streamGetMetadata called but $this->resource is not a resource'); + } + return \stream_get_meta_data($this->resource); + } + private function validateTimeout(float $value) : void + { + if ($value < 0) { + throw new \InvalidArgumentException("Timeout must be 0 or a positive float (got {$value})"); + } + } + private function connectIfNotConnected() : void + { + if ($this->isConnected()) { + return; + } + $this->connect(); + } + /** + * @phpstan-param FormattedRecord $record + */ + protected function generateDataStream(array $record) : string + { + return (string) $record['formatted']; + } + /** + * @return resource|null + */ + protected function getResource() + { + return $this->resource; + } + private function connect() : void + { + $this->createSocketResource(); + $this->setSocketTimeout(); + $this->setStreamChunkSize(); + } + private function createSocketResource() : void + { + if ($this->isPersistent()) { + $resource = $this->pfsockopen(); + } else { + $resource = $this->fsockopen(); + } + if (\is_bool($resource)) { + throw new \UnexpectedValueException("Failed connecting to {$this->connectionString} ({$this->errno}: {$this->errstr})"); + } + $this->resource = $resource; + } + private function setSocketTimeout() : void + { + if (!$this->streamSetTimeout()) { + throw new \UnexpectedValueException("Failed setting timeout with stream_set_timeout()"); + } + } + private function setStreamChunkSize() : void + { + if ($this->chunkSize && !$this->streamSetChunkSize()) { + throw new \UnexpectedValueException("Failed setting chunk size with stream_set_chunk_size()"); + } + } + private function writeToSocket(string $data) : void + { + $length = \strlen($data); + $sent = 0; + $this->lastSentBytes = $sent; + while ($this->isConnected() && $sent < $length) { + if (0 == $sent) { + $chunk = $this->fwrite($data); + } else { + $chunk = $this->fwrite(\substr($data, $sent)); + } + if ($chunk === \false) { + throw new \RuntimeException("Could not write to socket"); + } + $sent += $chunk; + $socketInfo = $this->streamGetMetadata(); + if (\is_array($socketInfo) && $socketInfo['timed_out']) { + throw new \RuntimeException("Write timed-out"); + } + if ($this->writingIsTimedOut($sent)) { + throw new \RuntimeException("Write timed-out, no data sent for `{$this->writingTimeout}` seconds, probably we got disconnected (sent {$sent} of {$length})"); + } + } + if (!$this->isConnected() && $sent < $length) { + throw new \RuntimeException("End-of-file reached, probably we got disconnected (sent {$sent} of {$length})"); + } + } + private function writingIsTimedOut(int $sent) : bool + { + // convert to ms + if (0.0 == $this->writingTimeout) { + return \false; + } + if ($sent !== $this->lastSentBytes) { + $this->lastWritingAt = \microtime(\true); + $this->lastSentBytes = $sent; + return \false; + } else { + \usleep(100); + } + if (\microtime(\true) - $this->lastWritingAt >= $this->writingTimeout) { + $this->closeSocket(); + return \true; + } + return \false; + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/SqsHandler.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/SqsHandler.php new file mode 100644 index 0000000..a494a0d --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/SqsHandler.php @@ -0,0 +1,52 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace FluentSmtpLib\Monolog\Handler; + +use FluentSmtpLib\Aws\Sqs\SqsClient; +use FluentSmtpLib\Monolog\Logger; +use FluentSmtpLib\Monolog\Utils; +/** + * Writes to any sqs queue. + * + * @author Martijn van Calker + */ +class SqsHandler extends \FluentSmtpLib\Monolog\Handler\AbstractProcessingHandler +{ + /** 256 KB in bytes - maximum message size in SQS */ + protected const MAX_MESSAGE_SIZE = 262144; + /** 100 KB in bytes - head message size for new error log */ + protected const HEAD_MESSAGE_SIZE = 102400; + /** @var SqsClient */ + private $client; + /** @var string */ + private $queueUrl; + public function __construct(\FluentSmtpLib\Aws\Sqs\SqsClient $sqsClient, string $queueUrl, $level = \FluentSmtpLib\Monolog\Logger::DEBUG, bool $bubble = \true) + { + parent::__construct($level, $bubble); + $this->client = $sqsClient; + $this->queueUrl = $queueUrl; + } + /** + * {@inheritDoc} + */ + protected function write(array $record) : void + { + if (!isset($record['formatted']) || 'string' !== \gettype($record['formatted'])) { + throw new \InvalidArgumentException('SqsHandler accepts only formatted records as a string' . \FluentSmtpLib\Monolog\Utils::getRecordMessageForException($record)); + } + $messageBody = $record['formatted']; + if (\strlen($messageBody) >= static::MAX_MESSAGE_SIZE) { + $messageBody = \FluentSmtpLib\Monolog\Utils::substr($messageBody, 0, static::HEAD_MESSAGE_SIZE); + } + $this->client->sendMessage(['QueueUrl' => $this->queueUrl, 'MessageBody' => $messageBody]); + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/StreamHandler.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/StreamHandler.php new file mode 100644 index 0000000..df85c66 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/StreamHandler.php @@ -0,0 +1,230 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace FluentSmtpLib\Monolog\Handler; + +use FluentSmtpLib\Monolog\Logger; +use FluentSmtpLib\Monolog\Utils; +/** + * Stores to any stream resource + * + * Can be used to store into php://stderr, remote and local files, etc. + * + * @author Jordi Boggiano + * + * @phpstan-import-type FormattedRecord from AbstractProcessingHandler + */ +class StreamHandler extends \FluentSmtpLib\Monolog\Handler\AbstractProcessingHandler +{ + /** @const int */ + protected const MAX_CHUNK_SIZE = 2147483647; + /** @const int 10MB */ + protected const DEFAULT_CHUNK_SIZE = 10 * 1024 * 1024; + /** @var int */ + protected $streamChunkSize; + /** @var resource|null */ + protected $stream; + /** @var ?string */ + protected $url = null; + /** @var ?string */ + private $errorMessage = null; + /** @var ?int */ + protected $filePermission; + /** @var bool */ + protected $useLocking; + /** @var string */ + protected $fileOpenMode; + /** @var true|null */ + private $dirCreated = null; + /** @var bool */ + private $retrying = \false; + /** + * @param resource|string $stream If a missing path can't be created, an UnexpectedValueException will be thrown on first write + * @param int|null $filePermission Optional file permissions (default (0644) are only for owner read/write) + * @param bool $useLocking Try to lock log file before doing any writes + * @param string $fileOpenMode The fopen() mode used when opening a file, if $stream is a file path + * + * @throws \InvalidArgumentException If stream is not a resource or string + */ + public function __construct($stream, $level = \FluentSmtpLib\Monolog\Logger::DEBUG, bool $bubble = \true, ?int $filePermission = null, bool $useLocking = \false, $fileOpenMode = 'a') + { + parent::__construct($level, $bubble); + if (($phpMemoryLimit = \FluentSmtpLib\Monolog\Utils::expandIniShorthandBytes(\ini_get('memory_limit'))) !== \false) { + if ($phpMemoryLimit > 0) { + // use max 10% of allowed memory for the chunk size, and at least 100KB + $this->streamChunkSize = \min(static::MAX_CHUNK_SIZE, \max((int) ($phpMemoryLimit / 10), 100 * 1024)); + } else { + // memory is unlimited, set to the default 10MB + $this->streamChunkSize = static::DEFAULT_CHUNK_SIZE; + } + } else { + // no memory limit information, set to the default 10MB + $this->streamChunkSize = static::DEFAULT_CHUNK_SIZE; + } + if (\is_resource($stream)) { + $this->stream = $stream; + \stream_set_chunk_size($this->stream, $this->streamChunkSize); + } elseif (\is_string($stream)) { + $this->url = \FluentSmtpLib\Monolog\Utils::canonicalizePath($stream); + } else { + throw new \InvalidArgumentException('A stream must either be a resource or a string.'); + } + $this->fileOpenMode = $fileOpenMode; + $this->filePermission = $filePermission; + $this->useLocking = $useLocking; + } + /** + * {@inheritDoc} + */ + public function close() : void + { + if ($this->url && \is_resource($this->stream)) { + \fclose($this->stream); + } + $this->stream = null; + $this->dirCreated = null; + } + /** + * Return the currently active stream if it is open + * + * @return resource|null + */ + public function getStream() + { + return $this->stream; + } + /** + * Return the stream URL if it was configured with a URL and not an active resource + * + * @return string|null + */ + public function getUrl() : ?string + { + return $this->url; + } + /** + * @return int + */ + public function getStreamChunkSize() : int + { + return $this->streamChunkSize; + } + /** + * {@inheritDoc} + */ + protected function write(array $record) : void + { + if (!\is_resource($this->stream)) { + $url = $this->url; + if (null === $url || '' === $url) { + throw new \LogicException('Missing stream url, the stream can not be opened. This may be caused by a premature call to close().' . \FluentSmtpLib\Monolog\Utils::getRecordMessageForException($record)); + } + $this->createDir($url); + $this->errorMessage = null; + \set_error_handler(function (...$args) { + return $this->customErrorHandler(...$args); + }); + try { + $stream = \fopen($url, $this->fileOpenMode); + if ($this->filePermission !== null) { + @\chmod($url, $this->filePermission); + } + } finally { + \restore_error_handler(); + } + if (!\is_resource($stream)) { + $this->stream = null; + throw new \UnexpectedValueException(\sprintf('The stream or file "%s" could not be opened in append mode: ' . $this->errorMessage, $url) . \FluentSmtpLib\Monolog\Utils::getRecordMessageForException($record)); + } + \stream_set_chunk_size($stream, $this->streamChunkSize); + $this->stream = $stream; + } + $stream = $this->stream; + if (!\is_resource($stream)) { + throw new \LogicException('No stream was opened yet' . \FluentSmtpLib\Monolog\Utils::getRecordMessageForException($record)); + } + if ($this->useLocking) { + // ignoring errors here, there's not much we can do about them + \flock($stream, \LOCK_EX); + } + $this->errorMessage = null; + \set_error_handler(function (...$args) { + return $this->customErrorHandler(...$args); + }); + try { + $this->streamWrite($stream, $record); + } finally { + \restore_error_handler(); + } + if ($this->errorMessage !== null) { + $error = $this->errorMessage; + // close the resource if possible to reopen it, and retry the failed write + if (!$this->retrying && $this->url !== null && $this->url !== 'php://memory') { + $this->retrying = \true; + $this->close(); + $this->write($record); + return; + } + throw new \UnexpectedValueException('Writing to the log file failed: ' . $error . \FluentSmtpLib\Monolog\Utils::getRecordMessageForException($record)); + } + $this->retrying = \false; + if ($this->useLocking) { + \flock($stream, \LOCK_UN); + } + } + /** + * Write to stream + * @param resource $stream + * @param array $record + * + * @phpstan-param FormattedRecord $record + */ + protected function streamWrite($stream, array $record) : void + { + \fwrite($stream, (string) $record['formatted']); + } + private function customErrorHandler(int $code, string $msg) : bool + { + $this->errorMessage = \preg_replace('{^(fopen|mkdir|fwrite)\\(.*?\\): }', '', $msg); + return \true; + } + private function getDirFromStream(string $stream) : ?string + { + $pos = \strpos($stream, '://'); + if ($pos === \false) { + return \dirname($stream); + } + if ('file://' === \substr($stream, 0, 7)) { + return \dirname(\substr($stream, 7)); + } + return null; + } + private function createDir(string $url) : void + { + // Do not try to create dir if it has already been tried. + if ($this->dirCreated) { + return; + } + $dir = $this->getDirFromStream($url); + if (null !== $dir && !\is_dir($dir)) { + $this->errorMessage = null; + \set_error_handler(function (...$args) { + return $this->customErrorHandler(...$args); + }); + $status = \mkdir($dir, 0777, \true); + \restore_error_handler(); + if (\false === $status && !\is_dir($dir) && \strpos((string) $this->errorMessage, 'File exists') === \false) { + throw new \UnexpectedValueException(\sprintf('There is no existing directory at "%s" and it could not be created: ' . $this->errorMessage, $dir)); + } + } + $this->dirCreated = \true; + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/SwiftMailerHandler.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/SwiftMailerHandler.php new file mode 100644 index 0000000..54014fe --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/SwiftMailerHandler.php @@ -0,0 +1,103 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace FluentSmtpLib\Monolog\Handler; + +use FluentSmtpLib\Monolog\Logger; +use FluentSmtpLib\Monolog\Utils; +use FluentSmtpLib\Monolog\Formatter\FormatterInterface; +use FluentSmtpLib\Monolog\Formatter\LineFormatter; +use FluentSmtpLib\Swift_Message; +use FluentSmtpLib\Swift; +/** + * SwiftMailerHandler uses Swift_Mailer to send the emails + * + * @author Gyula Sallai + * + * @phpstan-import-type Record from \Monolog\Logger + * @deprecated Since Monolog 2.6. Use SymfonyMailerHandler instead. + */ +class SwiftMailerHandler extends \FluentSmtpLib\Monolog\Handler\MailHandler +{ + /** @var \Swift_Mailer */ + protected $mailer; + /** @var Swift_Message|callable(string, Record[]): Swift_Message */ + private $messageTemplate; + /** + * @psalm-param Swift_Message|callable(string, Record[]): Swift_Message $message + * + * @param \Swift_Mailer $mailer The mailer to use + * @param callable|Swift_Message $message An example message for real messages, only the body will be replaced + */ + public function __construct(\FluentSmtpLib\Swift_Mailer $mailer, $message, $level = \FluentSmtpLib\Monolog\Logger::ERROR, bool $bubble = \true) + { + parent::__construct($level, $bubble); + @\trigger_error('The SwiftMailerHandler is deprecated since Monolog 2.6. Use SymfonyMailerHandler instead.', \E_USER_DEPRECATED); + $this->mailer = $mailer; + $this->messageTemplate = $message; + } + /** + * {@inheritDoc} + */ + protected function send(string $content, array $records) : void + { + $this->mailer->send($this->buildMessage($content, $records)); + } + /** + * Gets the formatter for the Swift_Message subject. + * + * @param string|null $format The format of the subject + */ + protected function getSubjectFormatter(?string $format) : \FluentSmtpLib\Monolog\Formatter\FormatterInterface + { + return new \FluentSmtpLib\Monolog\Formatter\LineFormatter($format); + } + /** + * Creates instance of Swift_Message to be sent + * + * @param string $content formatted email body to be sent + * @param array $records Log records that formed the content + * @return Swift_Message + * + * @phpstan-param Record[] $records + */ + protected function buildMessage(string $content, array $records) : \FluentSmtpLib\Swift_Message + { + $message = null; + if ($this->messageTemplate instanceof \FluentSmtpLib\Swift_Message) { + $message = clone $this->messageTemplate; + $message->generateId(); + } elseif (\is_callable($this->messageTemplate)) { + $message = ($this->messageTemplate)($content, $records); + } + if (!$message instanceof \FluentSmtpLib\Swift_Message) { + $record = \reset($records); + throw new \InvalidArgumentException('Could not resolve message as instance of Swift_Message or a callable returning it' . ($record ? \FluentSmtpLib\Monolog\Utils::getRecordMessageForException($record) : '')); + } + if ($records) { + $subjectFormatter = $this->getSubjectFormatter($message->getSubject()); + $message->setSubject($subjectFormatter->format($this->getHighestRecord($records))); + } + $mime = 'text/plain'; + if ($this->isHtmlBody($content)) { + $mime = 'text/html'; + } + $message->setBody($content, $mime); + /** @phpstan-ignore-next-line */ + if (\version_compare(\FluentSmtpLib\Swift::VERSION, '6.0.0', '>=')) { + $message->setDate(new \DateTimeImmutable()); + } else { + /** @phpstan-ignore-next-line */ + $message->setDate(\time()); + } + return $message; + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/SymfonyMailerHandler.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/SymfonyMailerHandler.php new file mode 100644 index 0000000..7b9ffd9 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/SymfonyMailerHandler.php @@ -0,0 +1,101 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace FluentSmtpLib\Monolog\Handler; + +use FluentSmtpLib\Monolog\Logger; +use FluentSmtpLib\Monolog\Utils; +use FluentSmtpLib\Monolog\Formatter\FormatterInterface; +use FluentSmtpLib\Monolog\Formatter\LineFormatter; +use FluentSmtpLib\Symfony\Component\Mailer\MailerInterface; +use FluentSmtpLib\Symfony\Component\Mailer\Transport\TransportInterface; +use FluentSmtpLib\Symfony\Component\Mime\Email; +/** + * SymfonyMailerHandler uses Symfony's Mailer component to send the emails + * + * @author Jordi Boggiano + * + * @phpstan-import-type Record from \Monolog\Logger + */ +class SymfonyMailerHandler extends \FluentSmtpLib\Monolog\Handler\MailHandler +{ + /** @var MailerInterface|TransportInterface */ + protected $mailer; + /** @var Email|callable(string, Record[]): Email */ + private $emailTemplate; + /** + * @psalm-param Email|callable(string, Record[]): Email $email + * + * @param MailerInterface|TransportInterface $mailer The mailer to use + * @param callable|Email $email An email template, the subject/body will be replaced + */ + public function __construct($mailer, $email, $level = \FluentSmtpLib\Monolog\Logger::ERROR, bool $bubble = \true) + { + parent::__construct($level, $bubble); + $this->mailer = $mailer; + $this->emailTemplate = $email; + } + /** + * {@inheritDoc} + */ + protected function send(string $content, array $records) : void + { + $this->mailer->send($this->buildMessage($content, $records)); + } + /** + * Gets the formatter for the Swift_Message subject. + * + * @param string|null $format The format of the subject + */ + protected function getSubjectFormatter(?string $format) : \FluentSmtpLib\Monolog\Formatter\FormatterInterface + { + return new \FluentSmtpLib\Monolog\Formatter\LineFormatter($format); + } + /** + * Creates instance of Email to be sent + * + * @param string $content formatted email body to be sent + * @param array $records Log records that formed the content + * + * @phpstan-param Record[] $records + */ + protected function buildMessage(string $content, array $records) : \FluentSmtpLib\Symfony\Component\Mime\Email + { + $message = null; + if ($this->emailTemplate instanceof \FluentSmtpLib\Symfony\Component\Mime\Email) { + $message = clone $this->emailTemplate; + } elseif (\is_callable($this->emailTemplate)) { + $message = ($this->emailTemplate)($content, $records); + } + if (!$message instanceof \FluentSmtpLib\Symfony\Component\Mime\Email) { + $record = \reset($records); + throw new \InvalidArgumentException('Could not resolve message as instance of Email or a callable returning it' . ($record ? \FluentSmtpLib\Monolog\Utils::getRecordMessageForException($record) : '')); + } + if ($records) { + $subjectFormatter = $this->getSubjectFormatter($message->getSubject()); + $message->subject($subjectFormatter->format($this->getHighestRecord($records))); + } + if ($this->isHtmlBody($content)) { + if (null !== ($charset = $message->getHtmlCharset())) { + $message->html($content, $charset); + } else { + $message->html($content); + } + } else { + if (null !== ($charset = $message->getTextCharset())) { + $message->text($content, $charset); + } else { + $message->text($content); + } + } + return $message->date(new \DateTimeImmutable()); + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/SyslogHandler.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/SyslogHandler.php new file mode 100644 index 0000000..0aa8707 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/SyslogHandler.php @@ -0,0 +1,63 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace FluentSmtpLib\Monolog\Handler; + +use FluentSmtpLib\Monolog\Logger; +use FluentSmtpLib\Monolog\Utils; +/** + * Logs to syslog service. + * + * usage example: + * + * $log = new Logger('application'); + * $syslog = new SyslogHandler('myfacility', 'local6'); + * $formatter = new LineFormatter("%channel%.%level_name%: %message% %extra%"); + * $syslog->setFormatter($formatter); + * $log->pushHandler($syslog); + * + * @author Sven Paulus + */ +class SyslogHandler extends \FluentSmtpLib\Monolog\Handler\AbstractSyslogHandler +{ + /** @var string */ + protected $ident; + /** @var int */ + protected $logopts; + /** + * @param string $ident + * @param string|int $facility Either one of the names of the keys in $this->facilities, or a LOG_* facility constant + * @param int $logopts Option flags for the openlog() call, defaults to LOG_PID + */ + public function __construct(string $ident, $facility = \LOG_USER, $level = \FluentSmtpLib\Monolog\Logger::DEBUG, bool $bubble = \true, int $logopts = \LOG_PID) + { + parent::__construct($facility, $level, $bubble); + $this->ident = $ident; + $this->logopts = $logopts; + } + /** + * {@inheritDoc} + */ + public function close() : void + { + \closelog(); + } + /** + * {@inheritDoc} + */ + protected function write(array $record) : void + { + if (!\openlog($this->ident, $this->logopts, $this->facility)) { + throw new \LogicException('Can\'t open syslog for ident "' . $this->ident . '" and facility "' . $this->facility . '"' . \FluentSmtpLib\Monolog\Utils::getRecordMessageForException($record)); + } + \syslog($this->logLevels[$record['level']], (string) $record['formatted']); + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/SyslogUdp/UdpSocket.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/SyslogUdp/UdpSocket.php new file mode 100644 index 0000000..851c5a1 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/SyslogUdp/UdpSocket.php @@ -0,0 +1,76 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace FluentSmtpLib\Monolog\Handler\SyslogUdp; + +use FluentSmtpLib\Monolog\Utils; +use Socket; +class UdpSocket +{ + protected const DATAGRAM_MAX_LENGTH = 65023; + /** @var string */ + protected $ip; + /** @var int */ + protected $port; + /** @var resource|Socket|null */ + protected $socket = null; + public function __construct(string $ip, int $port = 514) + { + $this->ip = $ip; + $this->port = $port; + } + /** + * @param string $line + * @param string $header + * @return void + */ + public function write($line, $header = "") + { + $this->send($this->assembleMessage($line, $header)); + } + public function close() : void + { + if (\is_resource($this->socket) || $this->socket instanceof \Socket) { + \socket_close($this->socket); + $this->socket = null; + } + } + /** + * @return resource|Socket + */ + protected function getSocket() + { + if (null !== $this->socket) { + return $this->socket; + } + $domain = \AF_INET; + $protocol = \SOL_UDP; + // Check if we are using unix sockets. + if ($this->port === 0) { + $domain = \AF_UNIX; + $protocol = \IPPROTO_IP; + } + $this->socket = \socket_create($domain, \SOCK_DGRAM, $protocol) ?: null; + if (null === $this->socket) { + throw new \RuntimeException('The UdpSocket to ' . $this->ip . ':' . $this->port . ' could not be opened via socket_create'); + } + return $this->socket; + } + protected function send(string $chunk) : void + { + \socket_sendto($this->getSocket(), $chunk, \strlen($chunk), $flags = 0, $this->ip, $this->port); + } + protected function assembleMessage(string $line, string $header) : string + { + $chunkSize = static::DATAGRAM_MAX_LENGTH - \strlen($header); + return $header . \FluentSmtpLib\Monolog\Utils::substr($line, 0, $chunkSize); + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/SyslogUdpHandler.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/SyslogUdpHandler.php new file mode 100644 index 0000000..89f56ba --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/SyslogUdpHandler.php @@ -0,0 +1,116 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace FluentSmtpLib\Monolog\Handler; + +use DateTimeInterface; +use FluentSmtpLib\Monolog\Logger; +use FluentSmtpLib\Monolog\Handler\SyslogUdp\UdpSocket; +use FluentSmtpLib\Monolog\Utils; +/** + * A Handler for logging to a remote syslogd server. + * + * @author Jesper Skovgaard Nielsen + * @author Dominik Kukacka + */ +class SyslogUdpHandler extends \FluentSmtpLib\Monolog\Handler\AbstractSyslogHandler +{ + const RFC3164 = 0; + const RFC5424 = 1; + const RFC5424e = 2; + /** @var array */ + private $dateFormats = array(self::RFC3164 => 'M d H:i:s', self::RFC5424 => \DateTime::RFC3339, self::RFC5424e => \DateTime::RFC3339_EXTENDED); + /** @var UdpSocket */ + protected $socket; + /** @var string */ + protected $ident; + /** @var self::RFC* */ + protected $rfc; + /** + * @param string $host Either IP/hostname or a path to a unix socket (port must be 0 then) + * @param int $port Port number, or 0 if $host is a unix socket + * @param string|int $facility Either one of the names of the keys in $this->facilities, or a LOG_* facility constant + * @param bool $bubble Whether the messages that are handled can bubble up the stack or not + * @param string $ident Program name or tag for each log message. + * @param int $rfc RFC to format the message for. + * @throws MissingExtensionException + * + * @phpstan-param self::RFC* $rfc + */ + public function __construct(string $host, int $port = 514, $facility = \LOG_USER, $level = \FluentSmtpLib\Monolog\Logger::DEBUG, bool $bubble = \true, string $ident = 'php', int $rfc = self::RFC5424) + { + if (!\extension_loaded('sockets')) { + throw new \FluentSmtpLib\Monolog\Handler\MissingExtensionException('The sockets extension is required to use the SyslogUdpHandler'); + } + parent::__construct($facility, $level, $bubble); + $this->ident = $ident; + $this->rfc = $rfc; + $this->socket = new \FluentSmtpLib\Monolog\Handler\SyslogUdp\UdpSocket($host, $port); + } + protected function write(array $record) : void + { + $lines = $this->splitMessageIntoLines($record['formatted']); + $header = $this->makeCommonSyslogHeader($this->logLevels[$record['level']], $record['datetime']); + foreach ($lines as $line) { + $this->socket->write($line, $header); + } + } + public function close() : void + { + $this->socket->close(); + } + /** + * @param string|string[] $message + * @return string[] + */ + private function splitMessageIntoLines($message) : array + { + if (\is_array($message)) { + $message = \implode("\n", $message); + } + $lines = \preg_split('/$\\R?^/m', (string) $message, -1, \PREG_SPLIT_NO_EMPTY); + if (\false === $lines) { + $pcreErrorCode = \preg_last_error(); + throw new \RuntimeException('Could not preg_split: ' . $pcreErrorCode . ' / ' . \FluentSmtpLib\Monolog\Utils::pcreLastErrorMessage($pcreErrorCode)); + } + return $lines; + } + /** + * Make common syslog header (see rfc5424 or rfc3164) + */ + protected function makeCommonSyslogHeader(int $severity, \DateTimeInterface $datetime) : string + { + $priority = $severity + $this->facility; + if (!($pid = \getmypid())) { + $pid = '-'; + } + if (!($hostname = \gethostname())) { + $hostname = '-'; + } + if ($this->rfc === self::RFC3164) { + // see https://github.com/phpstan/phpstan/issues/5348 + // @phpstan-ignore-next-line + $dateNew = $datetime->setTimezone(new \DateTimeZone('UTC')); + $date = $dateNew->format($this->dateFormats[$this->rfc]); + return "<{$priority}>" . $date . " " . $hostname . " " . $this->ident . "[" . $pid . "]: "; + } + $date = $datetime->format($this->dateFormats[$this->rfc]); + return "<{$priority}>1 " . $date . " " . $hostname . " " . $this->ident . " " . $pid . " - - "; + } + /** + * Inject your own socket, mainly used for testing + */ + public function setSocket(\FluentSmtpLib\Monolog\Handler\SyslogUdp\UdpSocket $socket) : self + { + $this->socket = $socket; + return $this; + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/TelegramBotHandler.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/TelegramBotHandler.php new file mode 100644 index 0000000..47de442 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/TelegramBotHandler.php @@ -0,0 +1,216 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace FluentSmtpLib\Monolog\Handler; + +use RuntimeException; +use FluentSmtpLib\Monolog\Logger; +use FluentSmtpLib\Monolog\Utils; +/** + * Handler send logs to Telegram using Telegram Bot API. + * + * How to use: + * 1) Create telegram bot with https://telegram.me/BotFather + * 2) Create a telegram channel where logs will be recorded. + * 3) Add created bot from step 1 to the created channel from step 2. + * + * Use telegram bot API key from step 1 and channel name with '@' prefix from step 2 to create instance of TelegramBotHandler + * + * @link https://core.telegram.org/bots/api + * + * @author Mazur Alexandr + * + * @phpstan-import-type Record from \Monolog\Logger + */ +class TelegramBotHandler extends \FluentSmtpLib\Monolog\Handler\AbstractProcessingHandler +{ + private const BOT_API = 'https://api.telegram.org/bot'; + /** + * The available values of parseMode according to the Telegram api documentation + */ + private const AVAILABLE_PARSE_MODES = ['HTML', 'MarkdownV2', 'Markdown']; + /** + * The maximum number of characters allowed in a message according to the Telegram api documentation + */ + private const MAX_MESSAGE_LENGTH = 4096; + /** + * Telegram bot access token provided by BotFather. + * Create telegram bot with https://telegram.me/BotFather and use access token from it. + * @var string + */ + private $apiKey; + /** + * Telegram channel name. + * Since to start with '@' symbol as prefix. + * @var string + */ + private $channel; + /** + * The kind of formatting that is used for the message. + * See available options at https://core.telegram.org/bots/api#formatting-options + * or in AVAILABLE_PARSE_MODES + * @var ?string + */ + private $parseMode; + /** + * Disables link previews for links in the message. + * @var ?bool + */ + private $disableWebPagePreview; + /** + * Sends the message silently. Users will receive a notification with no sound. + * @var ?bool + */ + private $disableNotification; + /** + * True - split a message longer than MAX_MESSAGE_LENGTH into parts and send in multiple messages. + * False - truncates a message that is too long. + * @var bool + */ + private $splitLongMessages; + /** + * Adds 1-second delay between sending a split message (according to Telegram API to avoid 429 Too Many Requests). + * @var bool + */ + private $delayBetweenMessages; + /** + * @param string $apiKey Telegram bot access token provided by BotFather + * @param string $channel Telegram channel name + * @param bool $splitLongMessages Split a message longer than MAX_MESSAGE_LENGTH into parts and send in multiple messages + * @param bool $delayBetweenMessages Adds delay between sending a split message according to Telegram API + * @throws MissingExtensionException + */ + public function __construct(string $apiKey, string $channel, $level = \FluentSmtpLib\Monolog\Logger::DEBUG, bool $bubble = \true, ?string $parseMode = null, ?bool $disableWebPagePreview = null, ?bool $disableNotification = null, bool $splitLongMessages = \false, bool $delayBetweenMessages = \false) + { + if (!\extension_loaded('curl')) { + throw new \FluentSmtpLib\Monolog\Handler\MissingExtensionException('The curl extension is needed to use the TelegramBotHandler'); + } + parent::__construct($level, $bubble); + $this->apiKey = $apiKey; + $this->channel = $channel; + $this->setParseMode($parseMode); + $this->disableWebPagePreview($disableWebPagePreview); + $this->disableNotification($disableNotification); + $this->splitLongMessages($splitLongMessages); + $this->delayBetweenMessages($delayBetweenMessages); + } + public function setParseMode(?string $parseMode = null) : self + { + if ($parseMode !== null && !\in_array($parseMode, self::AVAILABLE_PARSE_MODES)) { + throw new \InvalidArgumentException('Unknown parseMode, use one of these: ' . \implode(', ', self::AVAILABLE_PARSE_MODES) . '.'); + } + $this->parseMode = $parseMode; + return $this; + } + public function disableWebPagePreview(?bool $disableWebPagePreview = null) : self + { + $this->disableWebPagePreview = $disableWebPagePreview; + return $this; + } + public function disableNotification(?bool $disableNotification = null) : self + { + $this->disableNotification = $disableNotification; + return $this; + } + /** + * True - split a message longer than MAX_MESSAGE_LENGTH into parts and send in multiple messages. + * False - truncates a message that is too long. + * @param bool $splitLongMessages + * @return $this + */ + public function splitLongMessages(bool $splitLongMessages = \false) : self + { + $this->splitLongMessages = $splitLongMessages; + return $this; + } + /** + * Adds 1-second delay between sending a split message (according to Telegram API to avoid 429 Too Many Requests). + * @param bool $delayBetweenMessages + * @return $this + */ + public function delayBetweenMessages(bool $delayBetweenMessages = \false) : self + { + $this->delayBetweenMessages = $delayBetweenMessages; + return $this; + } + /** + * {@inheritDoc} + */ + public function handleBatch(array $records) : void + { + /** @var Record[] $messages */ + $messages = []; + foreach ($records as $record) { + if (!$this->isHandling($record)) { + continue; + } + if ($this->processors) { + /** @var Record $record */ + $record = $this->processRecord($record); + } + $messages[] = $record; + } + if (!empty($messages)) { + $this->send((string) $this->getFormatter()->formatBatch($messages)); + } + } + /** + * @inheritDoc + */ + protected function write(array $record) : void + { + $this->send($record['formatted']); + } + /** + * Send request to @link https://api.telegram.org/bot on SendMessage action. + * @param string $message + */ + protected function send(string $message) : void + { + $messages = $this->handleMessageLength($message); + foreach ($messages as $key => $msg) { + if ($this->delayBetweenMessages && $key > 0) { + \sleep(1); + } + $this->sendCurl($msg); + } + } + protected function sendCurl(string $message) : void + { + $ch = \curl_init(); + $url = self::BOT_API . $this->apiKey . '/SendMessage'; + \curl_setopt($ch, \CURLOPT_URL, $url); + \curl_setopt($ch, \CURLOPT_RETURNTRANSFER, \true); + \curl_setopt($ch, \CURLOPT_SSL_VERIFYPEER, \true); + \curl_setopt($ch, \CURLOPT_POSTFIELDS, \http_build_query(['text' => $message, 'chat_id' => $this->channel, 'parse_mode' => $this->parseMode, 'disable_web_page_preview' => $this->disableWebPagePreview, 'disable_notification' => $this->disableNotification])); + $result = \FluentSmtpLib\Monolog\Handler\Curl\Util::execute($ch); + if (!\is_string($result)) { + throw new \RuntimeException('Telegram API error. Description: No response'); + } + $result = \json_decode($result, \true); + if ($result['ok'] === \false) { + throw new \RuntimeException('Telegram API error. Description: ' . $result['description']); + } + } + /** + * Handle a message that is too long: truncates or splits into several + * @param string $message + * @return string[] + */ + private function handleMessageLength(string $message) : array + { + $truncatedMarker = ' (...truncated)'; + if (!$this->splitLongMessages && \strlen($message) > self::MAX_MESSAGE_LENGTH) { + return [\FluentSmtpLib\Monolog\Utils::substr($message, 0, self::MAX_MESSAGE_LENGTH - \strlen($truncatedMarker)) . $truncatedMarker]; + } + return \str_split($message, self::MAX_MESSAGE_LENGTH); + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/TestHandler.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/TestHandler.php new file mode 100644 index 0000000..409b3f1 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/TestHandler.php @@ -0,0 +1,212 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace FluentSmtpLib\Monolog\Handler; + +use FluentSmtpLib\Monolog\Logger; +use FluentSmtpLib\Psr\Log\LogLevel; +/** + * Used for testing purposes. + * + * It records all records and gives you access to them for verification. + * + * @author Jordi Boggiano + * + * @method bool hasEmergency($record) + * @method bool hasAlert($record) + * @method bool hasCritical($record) + * @method bool hasError($record) + * @method bool hasWarning($record) + * @method bool hasNotice($record) + * @method bool hasInfo($record) + * @method bool hasDebug($record) + * + * @method bool hasEmergencyRecords() + * @method bool hasAlertRecords() + * @method bool hasCriticalRecords() + * @method bool hasErrorRecords() + * @method bool hasWarningRecords() + * @method bool hasNoticeRecords() + * @method bool hasInfoRecords() + * @method bool hasDebugRecords() + * + * @method bool hasEmergencyThatContains($message) + * @method bool hasAlertThatContains($message) + * @method bool hasCriticalThatContains($message) + * @method bool hasErrorThatContains($message) + * @method bool hasWarningThatContains($message) + * @method bool hasNoticeThatContains($message) + * @method bool hasInfoThatContains($message) + * @method bool hasDebugThatContains($message) + * + * @method bool hasEmergencyThatMatches($message) + * @method bool hasAlertThatMatches($message) + * @method bool hasCriticalThatMatches($message) + * @method bool hasErrorThatMatches($message) + * @method bool hasWarningThatMatches($message) + * @method bool hasNoticeThatMatches($message) + * @method bool hasInfoThatMatches($message) + * @method bool hasDebugThatMatches($message) + * + * @method bool hasEmergencyThatPasses($message) + * @method bool hasAlertThatPasses($message) + * @method bool hasCriticalThatPasses($message) + * @method bool hasErrorThatPasses($message) + * @method bool hasWarningThatPasses($message) + * @method bool hasNoticeThatPasses($message) + * @method bool hasInfoThatPasses($message) + * @method bool hasDebugThatPasses($message) + * + * @phpstan-import-type Record from \Monolog\Logger + * @phpstan-import-type Level from \Monolog\Logger + * @phpstan-import-type LevelName from \Monolog\Logger + */ +class TestHandler extends \FluentSmtpLib\Monolog\Handler\AbstractProcessingHandler +{ + /** @var Record[] */ + protected $records = []; + /** @var array */ + protected $recordsByLevel = []; + /** @var bool */ + private $skipReset = \false; + /** + * @return array + * + * @phpstan-return Record[] + */ + public function getRecords() + { + return $this->records; + } + /** + * @return void + */ + public function clear() + { + $this->records = []; + $this->recordsByLevel = []; + } + /** + * @return void + */ + public function reset() + { + if (!$this->skipReset) { + $this->clear(); + } + } + /** + * @return void + */ + public function setSkipReset(bool $skipReset) + { + $this->skipReset = $skipReset; + } + /** + * @param string|int $level Logging level value or name + * + * @phpstan-param Level|LevelName|LogLevel::* $level + */ + public function hasRecords($level) : bool + { + return isset($this->recordsByLevel[\FluentSmtpLib\Monolog\Logger::toMonologLevel($level)]); + } + /** + * @param string|array $record Either a message string or an array containing message and optionally context keys that will be checked against all records + * @param string|int $level Logging level value or name + * + * @phpstan-param array{message: string, context?: mixed[]}|string $record + * @phpstan-param Level|LevelName|LogLevel::* $level + */ + public function hasRecord($record, $level) : bool + { + if (\is_string($record)) { + $record = array('message' => $record); + } + return $this->hasRecordThatPasses(function ($rec) use($record) { + if ($rec['message'] !== $record['message']) { + return \false; + } + if (isset($record['context']) && $rec['context'] !== $record['context']) { + return \false; + } + return \true; + }, $level); + } + /** + * @param string|int $level Logging level value or name + * + * @phpstan-param Level|LevelName|LogLevel::* $level + */ + public function hasRecordThatContains(string $message, $level) : bool + { + return $this->hasRecordThatPasses(function ($rec) use($message) { + return \strpos($rec['message'], $message) !== \false; + }, $level); + } + /** + * @param string|int $level Logging level value or name + * + * @phpstan-param Level|LevelName|LogLevel::* $level + */ + public function hasRecordThatMatches(string $regex, $level) : bool + { + return $this->hasRecordThatPasses(function (array $rec) use($regex) : bool { + return \preg_match($regex, $rec['message']) > 0; + }, $level); + } + /** + * @param string|int $level Logging level value or name + * @return bool + * + * @psalm-param callable(Record, int): mixed $predicate + * @phpstan-param Level|LevelName|LogLevel::* $level + */ + public function hasRecordThatPasses(callable $predicate, $level) + { + $level = \FluentSmtpLib\Monolog\Logger::toMonologLevel($level); + if (!isset($this->recordsByLevel[$level])) { + return \false; + } + foreach ($this->recordsByLevel[$level] as $i => $rec) { + if ($predicate($rec, $i)) { + return \true; + } + } + return \false; + } + /** + * {@inheritDoc} + */ + protected function write(array $record) : void + { + $this->recordsByLevel[$record['level']][] = $record; + $this->records[] = $record; + } + /** + * @param string $method + * @param mixed[] $args + * @return bool + */ + public function __call($method, $args) + { + if (\preg_match('/(.*)(Debug|Info|Notice|Warning|Error|Critical|Alert|Emergency)(.*)/', $method, $matches) > 0) { + $genericMethod = $matches[1] . ('Records' !== $matches[3] ? 'Record' : '') . $matches[3]; + $level = \constant('Monolog\\Logger::' . \strtoupper($matches[2])); + $callback = [$this, $genericMethod]; + if (\is_callable($callback)) { + $args[] = $level; + return \call_user_func_array($callback, $args); + } + } + throw new \BadMethodCallException('Call to undefined method ' . \get_class($this) . '::' . $method . '()'); + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/WebRequestRecognizerTrait.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/WebRequestRecognizerTrait.php new file mode 100644 index 0000000..8b4abf7 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/WebRequestRecognizerTrait.php @@ -0,0 +1,24 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace FluentSmtpLib\Monolog\Handler; + +trait WebRequestRecognizerTrait +{ + /** + * Checks if PHP's serving a web request + * @return bool + */ + protected function isWebRequest() : bool + { + return 'cli' !== \PHP_SAPI && 'phpdbg' !== \PHP_SAPI; + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/WhatFailureGroupHandler.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/WhatFailureGroupHandler.php new file mode 100644 index 0000000..debd10a --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/WhatFailureGroupHandler.php @@ -0,0 +1,76 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace FluentSmtpLib\Monolog\Handler; + +/** + * Forwards records to multiple handlers suppressing failures of each handler + * and continuing through to give every handler a chance to succeed. + * + * @author Craig D'Amelio + * + * @phpstan-import-type Record from \Monolog\Logger + */ +class WhatFailureGroupHandler extends \FluentSmtpLib\Monolog\Handler\GroupHandler +{ + /** + * {@inheritDoc} + */ + public function handle(array $record) : bool + { + if ($this->processors) { + /** @var Record $record */ + $record = $this->processRecord($record); + } + foreach ($this->handlers as $handler) { + try { + $handler->handle($record); + } catch (\Throwable $e) { + // What failure? + } + } + return \false === $this->bubble; + } + /** + * {@inheritDoc} + */ + public function handleBatch(array $records) : void + { + if ($this->processors) { + $processed = array(); + foreach ($records as $record) { + $processed[] = $this->processRecord($record); + } + /** @var Record[] $records */ + $records = $processed; + } + foreach ($this->handlers as $handler) { + try { + $handler->handleBatch($records); + } catch (\Throwable $e) { + // What failure? + } + } + } + /** + * {@inheritDoc} + */ + public function close() : void + { + foreach ($this->handlers as $handler) { + try { + $handler->close(); + } catch (\Throwable $e) { + // What failure? + } + } + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/ZendMonitorHandler.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/ZendMonitorHandler.php new file mode 100644 index 0000000..8d09cf0 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Handler/ZendMonitorHandler.php @@ -0,0 +1,79 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace FluentSmtpLib\Monolog\Handler; + +use FluentSmtpLib\Monolog\Formatter\FormatterInterface; +use FluentSmtpLib\Monolog\Formatter\NormalizerFormatter; +use FluentSmtpLib\Monolog\Logger; +/** + * Handler sending logs to Zend Monitor + * + * @author Christian Bergau + * @author Jason Davis + * + * @phpstan-import-type FormattedRecord from AbstractProcessingHandler + */ +class ZendMonitorHandler extends \FluentSmtpLib\Monolog\Handler\AbstractProcessingHandler +{ + /** + * Monolog level / ZendMonitor Custom Event priority map + * + * @var array + */ + protected $levelMap = []; + /** + * @throws MissingExtensionException + */ + public function __construct($level = \FluentSmtpLib\Monolog\Logger::DEBUG, bool $bubble = \true) + { + if (!\function_exists('FluentSmtpLib\\zend_monitor_custom_event')) { + throw new \FluentSmtpLib\Monolog\Handler\MissingExtensionException('You must have Zend Server installed with Zend Monitor enabled in order to use this handler'); + } + //zend monitor constants are not defined if zend monitor is not enabled. + $this->levelMap = [\FluentSmtpLib\Monolog\Logger::DEBUG => \FluentSmtpLib\ZEND_MONITOR_EVENT_SEVERITY_INFO, \FluentSmtpLib\Monolog\Logger::INFO => \FluentSmtpLib\ZEND_MONITOR_EVENT_SEVERITY_INFO, \FluentSmtpLib\Monolog\Logger::NOTICE => \FluentSmtpLib\ZEND_MONITOR_EVENT_SEVERITY_INFO, \FluentSmtpLib\Monolog\Logger::WARNING => \FluentSmtpLib\ZEND_MONITOR_EVENT_SEVERITY_WARNING, \FluentSmtpLib\Monolog\Logger::ERROR => \FluentSmtpLib\ZEND_MONITOR_EVENT_SEVERITY_ERROR, \FluentSmtpLib\Monolog\Logger::CRITICAL => \FluentSmtpLib\ZEND_MONITOR_EVENT_SEVERITY_ERROR, \FluentSmtpLib\Monolog\Logger::ALERT => \FluentSmtpLib\ZEND_MONITOR_EVENT_SEVERITY_ERROR, \FluentSmtpLib\Monolog\Logger::EMERGENCY => \FluentSmtpLib\ZEND_MONITOR_EVENT_SEVERITY_ERROR]; + parent::__construct($level, $bubble); + } + /** + * {@inheritDoc} + */ + protected function write(array $record) : void + { + $this->writeZendMonitorCustomEvent(\FluentSmtpLib\Monolog\Logger::getLevelName($record['level']), $record['message'], $record['formatted'], $this->levelMap[$record['level']]); + } + /** + * Write to Zend Monitor Events + * @param string $type Text displayed in "Class Name (custom)" field + * @param string $message Text displayed in "Error String" + * @param array $formatted Displayed in Custom Variables tab + * @param int $severity Set the event severity level (-1,0,1) + * + * @phpstan-param FormattedRecord $formatted + */ + protected function writeZendMonitorCustomEvent(string $type, string $message, array $formatted, int $severity) : void + { + zend_monitor_custom_event($type, $message, $formatted, $severity); + } + /** + * {@inheritDoc} + */ + public function getDefaultFormatter() : \FluentSmtpLib\Monolog\Formatter\FormatterInterface + { + return new \FluentSmtpLib\Monolog\Formatter\NormalizerFormatter(); + } + /** + * @return array + */ + public function getLevelMap() : array + { + return $this->levelMap; + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/LogRecord.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/LogRecord.php new file mode 100644 index 0000000..0ab0840 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/LogRecord.php @@ -0,0 +1,33 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace FluentSmtpLib\Monolog; + +use ArrayAccess; +/** + * Monolog log record interface for forward compatibility with Monolog 3.0 + * + * This is just present in Monolog 2.4+ to allow interoperable code to be written against + * both versions by type-hinting arguments as `array|\Monolog\LogRecord $record` + * + * Do not rely on this interface for other purposes, and do not implement it. + * + * @author Jordi Boggiano + * @template-extends \ArrayAccess<'message'|'level'|'context'|'level_name'|'channel'|'datetime'|'extra'|'formatted', mixed> + * @phpstan-import-type Record from Logger + */ +interface LogRecord extends \ArrayAccess +{ + /** + * @phpstan-return Record + */ + public function toArray() : array; +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Logger.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Logger.php new file mode 100644 index 0000000..48d6834 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Logger.php @@ -0,0 +1,636 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace FluentSmtpLib\Monolog; + +use DateTimeZone; +use FluentSmtpLib\Monolog\Handler\HandlerInterface; +use FluentSmtpLib\Psr\Log\LoggerInterface; +use FluentSmtpLib\Psr\Log\InvalidArgumentException; +use FluentSmtpLib\Psr\Log\LogLevel; +use Throwable; +use Stringable; +/** + * Monolog log channel + * + * It contains a stack of Handlers and a stack of Processors, + * and uses them to store records that are added to it. + * + * @author Jordi Boggiano + * + * @phpstan-type Level Logger::DEBUG|Logger::INFO|Logger::NOTICE|Logger::WARNING|Logger::ERROR|Logger::CRITICAL|Logger::ALERT|Logger::EMERGENCY + * @phpstan-type LevelName 'DEBUG'|'INFO'|'NOTICE'|'WARNING'|'ERROR'|'CRITICAL'|'ALERT'|'EMERGENCY' + * @phpstan-type Record array{message: string, context: mixed[], level: Level, level_name: LevelName, channel: string, datetime: \DateTimeImmutable, extra: mixed[]} + */ +class Logger implements \FluentSmtpLib\Psr\Log\LoggerInterface, \FluentSmtpLib\Monolog\ResettableInterface +{ + /** + * Detailed debug information + */ + public const DEBUG = 100; + /** + * Interesting events + * + * Examples: User logs in, SQL logs. + */ + public const INFO = 200; + /** + * Uncommon events + */ + public const NOTICE = 250; + /** + * Exceptional occurrences that are not errors + * + * Examples: Use of deprecated APIs, poor use of an API, + * undesirable things that are not necessarily wrong. + */ + public const WARNING = 300; + /** + * Runtime errors + */ + public const ERROR = 400; + /** + * Critical conditions + * + * Example: Application component unavailable, unexpected exception. + */ + public const CRITICAL = 500; + /** + * Action must be taken immediately + * + * Example: Entire website down, database unavailable, etc. + * This should trigger the SMS alerts and wake you up. + */ + public const ALERT = 550; + /** + * Urgent alert. + */ + public const EMERGENCY = 600; + /** + * Monolog API version + * + * This is only bumped when API breaks are done and should + * follow the major version of the library + * + * @var int + */ + public const API = 2; + /** + * This is a static variable and not a constant to serve as an extension point for custom levels + * + * @var array $levels Logging levels with the levels as key + * + * @phpstan-var array $levels Logging levels with the levels as key + */ + protected static $levels = [self::DEBUG => 'DEBUG', self::INFO => 'INFO', self::NOTICE => 'NOTICE', self::WARNING => 'WARNING', self::ERROR => 'ERROR', self::CRITICAL => 'CRITICAL', self::ALERT => 'ALERT', self::EMERGENCY => 'EMERGENCY']; + /** + * Mapping between levels numbers defined in RFC 5424 and Monolog ones + * + * @phpstan-var array $rfc_5424_levels + */ + private const RFC_5424_LEVELS = [7 => self::DEBUG, 6 => self::INFO, 5 => self::NOTICE, 4 => self::WARNING, 3 => self::ERROR, 2 => self::CRITICAL, 1 => self::ALERT, 0 => self::EMERGENCY]; + /** + * @var string + */ + protected $name; + /** + * The handler stack + * + * @var HandlerInterface[] + */ + protected $handlers; + /** + * Processors that will process all log records + * + * To process records of a single handler instead, add the processor on that specific handler + * + * @var callable[] + */ + protected $processors; + /** + * @var bool + */ + protected $microsecondTimestamps = \true; + /** + * @var DateTimeZone + */ + protected $timezone; + /** + * @var callable|null + */ + protected $exceptionHandler; + /** + * @var int Keeps track of depth to prevent infinite logging loops + */ + private $logDepth = 0; + /** + * @var \WeakMap<\Fiber, int> Keeps track of depth inside fibers to prevent infinite logging loops + */ + private $fiberLogDepth; + /** + * @var bool Whether to detect infinite logging loops + * + * This can be disabled via {@see useLoggingLoopDetection} if you have async handlers that do not play well with this + */ + private $detectCycles = \true; + /** + * @psalm-param array $processors + * + * @param string $name The logging channel, a simple descriptive name that is attached to all log records + * @param HandlerInterface[] $handlers Optional stack of handlers, the first one in the array is called first, etc. + * @param callable[] $processors Optional array of processors + * @param DateTimeZone|null $timezone Optional timezone, if not provided date_default_timezone_get() will be used + */ + public function __construct(string $name, array $handlers = [], array $processors = [], ?\DateTimeZone $timezone = null) + { + $this->name = $name; + $this->setHandlers($handlers); + $this->processors = $processors; + $this->timezone = $timezone ?: new \DateTimeZone(\date_default_timezone_get() ?: 'UTC'); + if (\PHP_VERSION_ID >= 80100) { + // Local variable for phpstan, see https://github.com/phpstan/phpstan/issues/6732#issuecomment-1111118412 + /** @var \WeakMap<\Fiber, int> $fiberLogDepth */ + $fiberLogDepth = new \WeakMap(); + $this->fiberLogDepth = $fiberLogDepth; + } + } + public function getName() : string + { + return $this->name; + } + /** + * Return a new cloned instance with the name changed + */ + public function withName(string $name) : self + { + $new = clone $this; + $new->name = $name; + return $new; + } + /** + * Pushes a handler on to the stack. + */ + public function pushHandler(\FluentSmtpLib\Monolog\Handler\HandlerInterface $handler) : self + { + \array_unshift($this->handlers, $handler); + return $this; + } + /** + * Pops a handler from the stack + * + * @throws \LogicException If empty handler stack + */ + public function popHandler() : \FluentSmtpLib\Monolog\Handler\HandlerInterface + { + if (!$this->handlers) { + throw new \LogicException('You tried to pop from an empty handler stack.'); + } + return \array_shift($this->handlers); + } + /** + * Set handlers, replacing all existing ones. + * + * If a map is passed, keys will be ignored. + * + * @param HandlerInterface[] $handlers + */ + public function setHandlers(array $handlers) : self + { + $this->handlers = []; + foreach (\array_reverse($handlers) as $handler) { + $this->pushHandler($handler); + } + return $this; + } + /** + * @return HandlerInterface[] + */ + public function getHandlers() : array + { + return $this->handlers; + } + /** + * Adds a processor on to the stack. + */ + public function pushProcessor(callable $callback) : self + { + \array_unshift($this->processors, $callback); + return $this; + } + /** + * Removes the processor on top of the stack and returns it. + * + * @throws \LogicException If empty processor stack + * @return callable + */ + public function popProcessor() : callable + { + if (!$this->processors) { + throw new \LogicException('You tried to pop from an empty processor stack.'); + } + return \array_shift($this->processors); + } + /** + * @return callable[] + */ + public function getProcessors() : array + { + return $this->processors; + } + /** + * Control the use of microsecond resolution timestamps in the 'datetime' + * member of new records. + * + * As of PHP7.1 microseconds are always included by the engine, so + * there is no performance penalty and Monolog 2 enabled microseconds + * by default. This function lets you disable them though in case you want + * to suppress microseconds from the output. + * + * @param bool $micro True to use microtime() to create timestamps + */ + public function useMicrosecondTimestamps(bool $micro) : self + { + $this->microsecondTimestamps = $micro; + return $this; + } + public function useLoggingLoopDetection(bool $detectCycles) : self + { + $this->detectCycles = $detectCycles; + return $this; + } + /** + * Adds a log record. + * + * @param int $level The logging level (a Monolog or RFC 5424 level) + * @param string $message The log message + * @param mixed[] $context The log context + * @param DateTimeImmutable $datetime Optional log date to log into the past or future + * @return bool Whether the record has been processed + * + * @phpstan-param Level $level + */ + public function addRecord(int $level, string $message, array $context = [], ?\FluentSmtpLib\Monolog\DateTimeImmutable $datetime = null) : bool + { + if (isset(self::RFC_5424_LEVELS[$level])) { + $level = self::RFC_5424_LEVELS[$level]; + } + if ($this->detectCycles) { + if (\PHP_VERSION_ID >= 80100 && ($fiber = \Fiber::getCurrent())) { + // @phpstan-ignore offsetAssign.dimType + $this->fiberLogDepth[$fiber] = $this->fiberLogDepth[$fiber] ?? 0; + $logDepth = ++$this->fiberLogDepth[$fiber]; + } else { + $logDepth = ++$this->logDepth; + } + } else { + $logDepth = 0; + } + if ($logDepth === 3) { + $this->warning('A possible infinite logging loop was detected and aborted. It appears some of your handler code is triggering logging, see the previous log record for a hint as to what may be the cause.'); + return \false; + } elseif ($logDepth >= 5) { + // log depth 4 is let through, so we can log the warning above + return \false; + } + try { + $record = null; + foreach ($this->handlers as $handler) { + if (null === $record) { + // skip creating the record as long as no handler is going to handle it + if (!$handler->isHandling(['level' => $level])) { + continue; + } + $levelName = static::getLevelName($level); + $record = ['message' => $message, 'context' => $context, 'level' => $level, 'level_name' => $levelName, 'channel' => $this->name, 'datetime' => $datetime ?? new \FluentSmtpLib\Monolog\DateTimeImmutable($this->microsecondTimestamps, $this->timezone), 'extra' => []]; + try { + foreach ($this->processors as $processor) { + $record = $processor($record); + } + } catch (\Throwable $e) { + $this->handleException($e, $record); + return \true; + } + } + // once the record exists, send it to all handlers as long as the bubbling chain is not interrupted + try { + if (\true === $handler->handle($record)) { + break; + } + } catch (\Throwable $e) { + $this->handleException($e, $record); + return \true; + } + } + } finally { + if ($this->detectCycles) { + if (isset($fiber)) { + $this->fiberLogDepth[$fiber]--; + } else { + $this->logDepth--; + } + } + } + return null !== $record; + } + /** + * Ends a log cycle and frees all resources used by handlers. + * + * Closing a Handler means flushing all buffers and freeing any open resources/handles. + * Handlers that have been closed should be able to accept log records again and re-open + * themselves on demand, but this may not always be possible depending on implementation. + * + * This is useful at the end of a request and will be called automatically on every handler + * when they get destructed. + */ + public function close() : void + { + foreach ($this->handlers as $handler) { + $handler->close(); + } + } + /** + * Ends a log cycle and resets all handlers and processors to their initial state. + * + * Resetting a Handler or a Processor means flushing/cleaning all buffers, resetting internal + * state, and getting it back to a state in which it can receive log records again. + * + * This is useful in case you want to avoid logs leaking between two requests or jobs when you + * have a long running process like a worker or an application server serving multiple requests + * in one process. + */ + public function reset() : void + { + foreach ($this->handlers as $handler) { + if ($handler instanceof \FluentSmtpLib\Monolog\ResettableInterface) { + $handler->reset(); + } + } + foreach ($this->processors as $processor) { + if ($processor instanceof \FluentSmtpLib\Monolog\ResettableInterface) { + $processor->reset(); + } + } + } + /** + * Gets all supported logging levels. + * + * @return array Assoc array with human-readable level names => level codes. + * @phpstan-return array + */ + public static function getLevels() : array + { + return \array_flip(static::$levels); + } + /** + * Gets the name of the logging level. + * + * @throws \Psr\Log\InvalidArgumentException If level is not defined + * + * @phpstan-param Level $level + * @phpstan-return LevelName + */ + public static function getLevelName(int $level) : string + { + if (!isset(static::$levels[$level])) { + throw new \FluentSmtpLib\Psr\Log\InvalidArgumentException('Level "' . $level . '" is not defined, use one of: ' . \implode(', ', \array_keys(static::$levels))); + } + return static::$levels[$level]; + } + /** + * Converts PSR-3 levels to Monolog ones if necessary + * + * @param string|int $level Level number (monolog) or name (PSR-3) + * @throws \Psr\Log\InvalidArgumentException If level is not defined + * + * @phpstan-param Level|LevelName|LogLevel::* $level + * @phpstan-return Level + */ + public static function toMonologLevel($level) : int + { + if (\is_string($level)) { + if (\is_numeric($level)) { + /** @phpstan-ignore-next-line */ + return \intval($level); + } + // Contains chars of all log levels and avoids using strtoupper() which may have + // strange results depending on locale (for example, "i" will become "İ" in Turkish locale) + $upper = \strtr($level, 'abcdefgilmnortuwy', 'ABCDEFGILMNORTUWY'); + if (\defined(__CLASS__ . '::' . $upper)) { + return \constant(__CLASS__ . '::' . $upper); + } + throw new \FluentSmtpLib\Psr\Log\InvalidArgumentException('Level "' . $level . '" is not defined, use one of: ' . \implode(', ', \array_keys(static::$levels) + static::$levels)); + } + if (!\is_int($level)) { + throw new \FluentSmtpLib\Psr\Log\InvalidArgumentException('Level "' . \var_export($level, \true) . '" is not defined, use one of: ' . \implode(', ', \array_keys(static::$levels) + static::$levels)); + } + return $level; + } + /** + * Checks whether the Logger has a handler that listens on the given level + * + * @phpstan-param Level $level + */ + public function isHandling(int $level) : bool + { + $record = ['level' => $level]; + foreach ($this->handlers as $handler) { + if ($handler->isHandling($record)) { + return \true; + } + } + return \false; + } + /** + * Set a custom exception handler that will be called if adding a new record fails + * + * The callable will receive an exception object and the record that failed to be logged + */ + public function setExceptionHandler(?callable $callback) : self + { + $this->exceptionHandler = $callback; + return $this; + } + public function getExceptionHandler() : ?callable + { + return $this->exceptionHandler; + } + /** + * Adds a log record at an arbitrary level. + * + * This method allows for compatibility with common interfaces. + * + * @param mixed $level The log level (a Monolog, PSR-3 or RFC 5424 level) + * @param string|Stringable $message The log message + * @param mixed[] $context The log context + * + * @phpstan-param Level|LevelName|LogLevel::* $level + */ + public function log($level, $message, array $context = []) : void + { + if (!\is_int($level) && !\is_string($level)) { + throw new \InvalidArgumentException('$level is expected to be a string or int'); + } + if (isset(self::RFC_5424_LEVELS[$level])) { + $level = self::RFC_5424_LEVELS[$level]; + } + $level = static::toMonologLevel($level); + $this->addRecord($level, (string) $message, $context); + } + /** + * Adds a log record at the DEBUG level. + * + * This method allows for compatibility with common interfaces. + * + * @param string|Stringable $message The log message + * @param mixed[] $context The log context + */ + public function debug($message, array $context = []) : void + { + $this->addRecord(static::DEBUG, (string) $message, $context); + } + /** + * Adds a log record at the INFO level. + * + * This method allows for compatibility with common interfaces. + * + * @param string|Stringable $message The log message + * @param mixed[] $context The log context + */ + public function info($message, array $context = []) : void + { + $this->addRecord(static::INFO, (string) $message, $context); + } + /** + * Adds a log record at the NOTICE level. + * + * This method allows for compatibility with common interfaces. + * + * @param string|Stringable $message The log message + * @param mixed[] $context The log context + */ + public function notice($message, array $context = []) : void + { + $this->addRecord(static::NOTICE, (string) $message, $context); + } + /** + * Adds a log record at the WARNING level. + * + * This method allows for compatibility with common interfaces. + * + * @param string|Stringable $message The log message + * @param mixed[] $context The log context + */ + public function warning($message, array $context = []) : void + { + $this->addRecord(static::WARNING, (string) $message, $context); + } + /** + * Adds a log record at the ERROR level. + * + * This method allows for compatibility with common interfaces. + * + * @param string|Stringable $message The log message + * @param mixed[] $context The log context + */ + public function error($message, array $context = []) : void + { + $this->addRecord(static::ERROR, (string) $message, $context); + } + /** + * Adds a log record at the CRITICAL level. + * + * This method allows for compatibility with common interfaces. + * + * @param string|Stringable $message The log message + * @param mixed[] $context The log context + */ + public function critical($message, array $context = []) : void + { + $this->addRecord(static::CRITICAL, (string) $message, $context); + } + /** + * Adds a log record at the ALERT level. + * + * This method allows for compatibility with common interfaces. + * + * @param string|Stringable $message The log message + * @param mixed[] $context The log context + */ + public function alert($message, array $context = []) : void + { + $this->addRecord(static::ALERT, (string) $message, $context); + } + /** + * Adds a log record at the EMERGENCY level. + * + * This method allows for compatibility with common interfaces. + * + * @param string|Stringable $message The log message + * @param mixed[] $context The log context + */ + public function emergency($message, array $context = []) : void + { + $this->addRecord(static::EMERGENCY, (string) $message, $context); + } + /** + * Sets the timezone to be used for the timestamp of log records. + */ + public function setTimezone(\DateTimeZone $tz) : self + { + $this->timezone = $tz; + return $this; + } + /** + * Returns the timezone to be used for the timestamp of log records. + */ + public function getTimezone() : \DateTimeZone + { + return $this->timezone; + } + /** + * Delegates exception management to the custom exception handler, + * or throws the exception if no custom handler is set. + * + * @param array $record + * @phpstan-param Record $record + */ + protected function handleException(\Throwable $e, array $record) : void + { + if (!$this->exceptionHandler) { + throw $e; + } + ($this->exceptionHandler)($e, $record); + } + /** + * @return array + */ + public function __serialize() : array + { + return ['name' => $this->name, 'handlers' => $this->handlers, 'processors' => $this->processors, 'microsecondTimestamps' => $this->microsecondTimestamps, 'timezone' => $this->timezone, 'exceptionHandler' => $this->exceptionHandler, 'logDepth' => $this->logDepth, 'detectCycles' => $this->detectCycles]; + } + /** + * @param array $data + */ + public function __unserialize(array $data) : void + { + foreach (['name', 'handlers', 'processors', 'microsecondTimestamps', 'timezone', 'exceptionHandler', 'logDepth', 'detectCycles'] as $property) { + if (isset($data[$property])) { + $this->{$property} = $data[$property]; + } + } + if (\PHP_VERSION_ID >= 80100) { + // Local variable for phpstan, see https://github.com/phpstan/phpstan/issues/6732#issuecomment-1111118412 + /** @var \WeakMap<\Fiber, int> $fiberLogDepth */ + $fiberLogDepth = new \WeakMap(); + $this->fiberLogDepth = $fiberLogDepth; + } + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Processor/GitProcessor.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Processor/GitProcessor.php new file mode 100644 index 0000000..547ac26 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Processor/GitProcessor.php @@ -0,0 +1,66 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace FluentSmtpLib\Monolog\Processor; + +use FluentSmtpLib\Monolog\Logger; +use FluentSmtpLib\Psr\Log\LogLevel; +/** + * Injects Git branch and Git commit SHA in all records + * + * @author Nick Otter + * @author Jordi Boggiano + * + * @phpstan-import-type Level from \Monolog\Logger + * @phpstan-import-type LevelName from \Monolog\Logger + */ +class GitProcessor implements \FluentSmtpLib\Monolog\Processor\ProcessorInterface +{ + /** @var int */ + private $level; + /** @var array{branch: string, commit: string}|array|null */ + private static $cache = null; + /** + * @param string|int $level The minimum logging level at which this Processor will be triggered + * + * @phpstan-param Level|LevelName|LogLevel::* $level + */ + public function __construct($level = \FluentSmtpLib\Monolog\Logger::DEBUG) + { + $this->level = \FluentSmtpLib\Monolog\Logger::toMonologLevel($level); + } + /** + * {@inheritDoc} + */ + public function __invoke(array $record) : array + { + // return if the level is not high enough + if ($record['level'] < $this->level) { + return $record; + } + $record['extra']['git'] = self::getGitInfo(); + return $record; + } + /** + * @return array{branch: string, commit: string}|array + */ + private static function getGitInfo() : array + { + if (self::$cache) { + return self::$cache; + } + $branches = `git branch -v --no-abbrev`; + if ($branches && \preg_match('{^\\* (.+?)\\s+([a-f0-9]{40})(?:\\s|$)}m', $branches, $matches)) { + return self::$cache = ['branch' => $matches[1], 'commit' => $matches[2]]; + } + return self::$cache = []; + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Processor/HostnameProcessor.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Processor/HostnameProcessor.php new file mode 100644 index 0000000..7a30300 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Processor/HostnameProcessor.php @@ -0,0 +1,33 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace FluentSmtpLib\Monolog\Processor; + +/** + * Injects value of gethostname in all records + */ +class HostnameProcessor implements \FluentSmtpLib\Monolog\Processor\ProcessorInterface +{ + /** @var string */ + private static $host; + public function __construct() + { + self::$host = (string) \gethostname(); + } + /** + * {@inheritDoc} + */ + public function __invoke(array $record) : array + { + $record['extra']['hostname'] = self::$host; + return $record; + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Processor/IntrospectionProcessor.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Processor/IntrospectionProcessor.php new file mode 100644 index 0000000..b6973c3 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Processor/IntrospectionProcessor.php @@ -0,0 +1,96 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace FluentSmtpLib\Monolog\Processor; + +use FluentSmtpLib\Monolog\Logger; +use FluentSmtpLib\Psr\Log\LogLevel; +/** + * Injects line/file:class/function where the log message came from + * + * Warning: This only works if the handler processes the logs directly. + * If you put the processor on a handler that is behind a FingersCrossedHandler + * for example, the processor will only be called once the trigger level is reached, + * and all the log records will have the same file/line/.. data from the call that + * triggered the FingersCrossedHandler. + * + * @author Jordi Boggiano + * + * @phpstan-import-type Level from \Monolog\Logger + * @phpstan-import-type LevelName from \Monolog\Logger + */ +class IntrospectionProcessor implements \FluentSmtpLib\Monolog\Processor\ProcessorInterface +{ + /** @var int */ + private $level; + /** @var string[] */ + private $skipClassesPartials; + /** @var int */ + private $skipStackFramesCount; + /** @var string[] */ + private $skipFunctions = ['call_user_func', 'call_user_func_array']; + /** + * @param string|int $level The minimum logging level at which this Processor will be triggered + * @param string[] $skipClassesPartials + * + * @phpstan-param Level|LevelName|LogLevel::* $level + */ + public function __construct($level = \FluentSmtpLib\Monolog\Logger::DEBUG, array $skipClassesPartials = [], int $skipStackFramesCount = 0) + { + $this->level = \FluentSmtpLib\Monolog\Logger::toMonologLevel($level); + $this->skipClassesPartials = \array_merge(['Monolog\\'], $skipClassesPartials); + $this->skipStackFramesCount = $skipStackFramesCount; + } + /** + * {@inheritDoc} + */ + public function __invoke(array $record) : array + { + // return if the level is not high enough + if ($record['level'] < $this->level) { + return $record; + } + $trace = \debug_backtrace(\DEBUG_BACKTRACE_IGNORE_ARGS); + // skip first since it's always the current method + \array_shift($trace); + // the call_user_func call is also skipped + \array_shift($trace); + $i = 0; + while ($this->isTraceClassOrSkippedFunction($trace, $i)) { + if (isset($trace[$i]['class'])) { + foreach ($this->skipClassesPartials as $part) { + if (\strpos($trace[$i]['class'], $part) !== \false) { + $i++; + continue 2; + } + } + } elseif (\in_array($trace[$i]['function'], $this->skipFunctions)) { + $i++; + continue; + } + break; + } + $i += $this->skipStackFramesCount; + // we should have the call source now + $record['extra'] = \array_merge($record['extra'], ['file' => isset($trace[$i - 1]['file']) ? $trace[$i - 1]['file'] : null, 'line' => isset($trace[$i - 1]['line']) ? $trace[$i - 1]['line'] : null, 'class' => isset($trace[$i]['class']) ? $trace[$i]['class'] : null, 'callType' => isset($trace[$i]['type']) ? $trace[$i]['type'] : null, 'function' => isset($trace[$i]['function']) ? $trace[$i]['function'] : null]); + return $record; + } + /** + * @param array[] $trace + */ + private function isTraceClassOrSkippedFunction(array $trace, int $index) : bool + { + if (!isset($trace[$index])) { + return \false; + } + return isset($trace[$index]['class']) || \in_array($trace[$index]['function'], $this->skipFunctions); + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Processor/MemoryPeakUsageProcessor.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Processor/MemoryPeakUsageProcessor.php new file mode 100644 index 0000000..4494704 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Processor/MemoryPeakUsageProcessor.php @@ -0,0 +1,34 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace FluentSmtpLib\Monolog\Processor; + +/** + * Injects memory_get_peak_usage in all records + * + * @see Monolog\Processor\MemoryProcessor::__construct() for options + * @author Rob Jensen + */ +class MemoryPeakUsageProcessor extends \FluentSmtpLib\Monolog\Processor\MemoryProcessor +{ + /** + * {@inheritDoc} + */ + public function __invoke(array $record) : array + { + $usage = \memory_get_peak_usage($this->realUsage); + if ($this->useFormatting) { + $usage = $this->formatBytes($usage); + } + $record['extra']['memory_peak_usage'] = $usage; + return $record; + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Processor/MemoryProcessor.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Processor/MemoryProcessor.php new file mode 100644 index 0000000..f0b955e --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Processor/MemoryProcessor.php @@ -0,0 +1,56 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace FluentSmtpLib\Monolog\Processor; + +/** + * Some methods that are common for all memory processors + * + * @author Rob Jensen + */ +abstract class MemoryProcessor implements \FluentSmtpLib\Monolog\Processor\ProcessorInterface +{ + /** + * @var bool If true, get the real size of memory allocated from system. Else, only the memory used by emalloc() is reported. + */ + protected $realUsage; + /** + * @var bool If true, then format memory size to human readable string (MB, KB, B depending on size) + */ + protected $useFormatting; + /** + * @param bool $realUsage Set this to true to get the real size of memory allocated from system. + * @param bool $useFormatting If true, then format memory size to human readable string (MB, KB, B depending on size) + */ + public function __construct(bool $realUsage = \true, bool $useFormatting = \true) + { + $this->realUsage = $realUsage; + $this->useFormatting = $useFormatting; + } + /** + * Formats bytes into a human readable string if $this->useFormatting is true, otherwise return $bytes as is + * + * @param int $bytes + * @return string|int Formatted string if $this->useFormatting is true, otherwise return $bytes as int + */ + protected function formatBytes(int $bytes) + { + if (!$this->useFormatting) { + return $bytes; + } + if ($bytes > 1024 * 1024) { + return \round($bytes / 1024 / 1024, 2) . ' MB'; + } elseif ($bytes > 1024) { + return \round($bytes / 1024, 2) . ' KB'; + } + return $bytes . ' B'; + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Processor/MemoryUsageProcessor.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Processor/MemoryUsageProcessor.php new file mode 100644 index 0000000..ecc2a01 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Processor/MemoryUsageProcessor.php @@ -0,0 +1,34 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace FluentSmtpLib\Monolog\Processor; + +/** + * Injects memory_get_usage in all records + * + * @see Monolog\Processor\MemoryProcessor::__construct() for options + * @author Rob Jensen + */ +class MemoryUsageProcessor extends \FluentSmtpLib\Monolog\Processor\MemoryProcessor +{ + /** + * {@inheritDoc} + */ + public function __invoke(array $record) : array + { + $usage = \memory_get_usage($this->realUsage); + if ($this->useFormatting) { + $usage = $this->formatBytes($usage); + } + $record['extra']['memory_usage'] = $usage; + return $record; + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Processor/MercurialProcessor.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Processor/MercurialProcessor.php new file mode 100644 index 0000000..919d1a1 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Processor/MercurialProcessor.php @@ -0,0 +1,65 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace FluentSmtpLib\Monolog\Processor; + +use FluentSmtpLib\Monolog\Logger; +use FluentSmtpLib\Psr\Log\LogLevel; +/** + * Injects Hg branch and Hg revision number in all records + * + * @author Jonathan A. Schweder + * + * @phpstan-import-type LevelName from \Monolog\Logger + * @phpstan-import-type Level from \Monolog\Logger + */ +class MercurialProcessor implements \FluentSmtpLib\Monolog\Processor\ProcessorInterface +{ + /** @var Level */ + private $level; + /** @var array{branch: string, revision: string}|array|null */ + private static $cache = null; + /** + * @param int|string $level The minimum logging level at which this Processor will be triggered + * + * @phpstan-param Level|LevelName|LogLevel::* $level + */ + public function __construct($level = \FluentSmtpLib\Monolog\Logger::DEBUG) + { + $this->level = \FluentSmtpLib\Monolog\Logger::toMonologLevel($level); + } + /** + * {@inheritDoc} + */ + public function __invoke(array $record) : array + { + // return if the level is not high enough + if ($record['level'] < $this->level) { + return $record; + } + $record['extra']['hg'] = self::getMercurialInfo(); + return $record; + } + /** + * @return array{branch: string, revision: string}|array + */ + private static function getMercurialInfo() : array + { + if (self::$cache) { + return self::$cache; + } + $result = \explode(' ', \trim(`hg id -nb`)); + if (\count($result) >= 3) { + return self::$cache = ['branch' => $result[1], 'revision' => $result[2]]; + } + return self::$cache = []; + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Processor/ProcessIdProcessor.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Processor/ProcessIdProcessor.php new file mode 100644 index 0000000..91d8c35 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Processor/ProcessIdProcessor.php @@ -0,0 +1,29 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace FluentSmtpLib\Monolog\Processor; + +/** + * Adds value of getmypid into records + * + * @author Andreas Hörnicke + */ +class ProcessIdProcessor implements \FluentSmtpLib\Monolog\Processor\ProcessorInterface +{ + /** + * {@inheritDoc} + */ + public function __invoke(array $record) : array + { + $record['extra']['process_id'] = \getmypid(); + return $record; + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Processor/ProcessorInterface.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Processor/ProcessorInterface.php new file mode 100644 index 0000000..d1bfc31 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Processor/ProcessorInterface.php @@ -0,0 +1,30 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace FluentSmtpLib\Monolog\Processor; + +/** + * An optional interface to allow labelling Monolog processors. + * + * @author Nicolas Grekas + * + * @phpstan-import-type Record from \Monolog\Logger + */ +interface ProcessorInterface +{ + /** + * @return array The processed record + * + * @phpstan-param Record $record + * @phpstan-return Record + */ + public function __invoke(array $record); +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Processor/PsrLogMessageProcessor.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Processor/PsrLogMessageProcessor.php new file mode 100644 index 0000000..79fae0f --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Processor/PsrLogMessageProcessor.php @@ -0,0 +1,78 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace FluentSmtpLib\Monolog\Processor; + +use FluentSmtpLib\Monolog\Utils; +/** + * Processes a record's message according to PSR-3 rules + * + * It replaces {foo} with the value from $context['foo'] + * + * @author Jordi Boggiano + */ +class PsrLogMessageProcessor implements \FluentSmtpLib\Monolog\Processor\ProcessorInterface +{ + public const SIMPLE_DATE = "Y-m-d\\TH:i:s.uP"; + /** @var string|null */ + private $dateFormat; + /** @var bool */ + private $removeUsedContextFields; + /** + * @param string|null $dateFormat The format of the timestamp: one supported by DateTime::format + * @param bool $removeUsedContextFields If set to true the fields interpolated into message gets unset + */ + public function __construct(?string $dateFormat = null, bool $removeUsedContextFields = \false) + { + $this->dateFormat = $dateFormat; + $this->removeUsedContextFields = $removeUsedContextFields; + } + /** + * {@inheritDoc} + */ + public function __invoke(array $record) : array + { + if (\false === \strpos($record['message'], '{')) { + return $record; + } + $replacements = []; + foreach ($record['context'] as $key => $val) { + $placeholder = '{' . $key . '}'; + if (\strpos($record['message'], $placeholder) === \false) { + continue; + } + if (\is_null($val) || \is_scalar($val) || \is_object($val) && \method_exists($val, "__toString")) { + $replacements[$placeholder] = $val; + } elseif ($val instanceof \DateTimeInterface) { + if (!$this->dateFormat && $val instanceof \FluentSmtpLib\Monolog\DateTimeImmutable) { + // handle monolog dates using __toString if no specific dateFormat was asked for + // so that it follows the useMicroseconds flag + $replacements[$placeholder] = (string) $val; + } else { + $replacements[$placeholder] = $val->format($this->dateFormat ?: static::SIMPLE_DATE); + } + } elseif ($val instanceof \UnitEnum) { + $replacements[$placeholder] = $val instanceof \BackedEnum ? $val->value : $val->name; + } elseif (\is_object($val)) { + $replacements[$placeholder] = '[object ' . \FluentSmtpLib\Monolog\Utils::getClass($val) . ']'; + } elseif (\is_array($val)) { + $replacements[$placeholder] = 'array' . \FluentSmtpLib\Monolog\Utils::jsonEncode($val, null, \true); + } else { + $replacements[$placeholder] = '[' . \gettype($val) . ']'; + } + if ($this->removeUsedContextFields) { + unset($record['context'][$key]); + } + } + $record['message'] = \strtr($record['message'], $replacements); + return $record; + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Processor/TagProcessor.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Processor/TagProcessor.php new file mode 100644 index 0000000..5f18708 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Processor/TagProcessor.php @@ -0,0 +1,54 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace FluentSmtpLib\Monolog\Processor; + +/** + * Adds a tags array into record + * + * @author Martijn Riemers + */ +class TagProcessor implements \FluentSmtpLib\Monolog\Processor\ProcessorInterface +{ + /** @var string[] */ + private $tags; + /** + * @param string[] $tags + */ + public function __construct(array $tags = []) + { + $this->setTags($tags); + } + /** + * @param string[] $tags + */ + public function addTags(array $tags = []) : self + { + $this->tags = \array_merge($this->tags, $tags); + return $this; + } + /** + * @param string[] $tags + */ + public function setTags(array $tags = []) : self + { + $this->tags = $tags; + return $this; + } + /** + * {@inheritDoc} + */ + public function __invoke(array $record) : array + { + $record['extra']['tags'] = $this->tags; + return $record; + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Processor/UidProcessor.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Processor/UidProcessor.php new file mode 100644 index 0000000..b8c8781 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Processor/UidProcessor.php @@ -0,0 +1,51 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace FluentSmtpLib\Monolog\Processor; + +use FluentSmtpLib\Monolog\ResettableInterface; +/** + * Adds a unique identifier into records + * + * @author Simon Mönch + */ +class UidProcessor implements \FluentSmtpLib\Monolog\Processor\ProcessorInterface, \FluentSmtpLib\Monolog\ResettableInterface +{ + /** @var string */ + private $uid; + public function __construct(int $length = 7) + { + if ($length > 32 || $length < 1) { + throw new \InvalidArgumentException('The uid length must be an integer between 1 and 32'); + } + $this->uid = $this->generateUid($length); + } + /** + * {@inheritDoc} + */ + public function __invoke(array $record) : array + { + $record['extra']['uid'] = $this->uid; + return $record; + } + public function getUid() : string + { + return $this->uid; + } + public function reset() + { + $this->uid = $this->generateUid(\strlen($this->uid)); + } + private function generateUid(int $length) : string + { + return \substr(\bin2hex(\random_bytes((int) \ceil($length / 2))), 0, $length); + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Processor/WebProcessor.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Processor/WebProcessor.php new file mode 100644 index 0000000..25da558 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Processor/WebProcessor.php @@ -0,0 +1,93 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace FluentSmtpLib\Monolog\Processor; + +/** + * Injects url/method and remote IP of the current web request in all records + * + * @author Jordi Boggiano + */ +class WebProcessor implements \FluentSmtpLib\Monolog\Processor\ProcessorInterface +{ + /** + * @var array|\ArrayAccess + */ + protected $serverData; + /** + * Default fields + * + * Array is structured as [key in record.extra => key in $serverData] + * + * @var array + */ + protected $extraFields = ['url' => 'REQUEST_URI', 'ip' => 'REMOTE_ADDR', 'http_method' => 'REQUEST_METHOD', 'server' => 'SERVER_NAME', 'referrer' => 'HTTP_REFERER', 'user_agent' => 'HTTP_USER_AGENT']; + /** + * @param array|\ArrayAccess|null $serverData Array or object w/ ArrayAccess that provides access to the $_SERVER data + * @param array|array|null $extraFields Field names and the related key inside $serverData to be added (or just a list of field names to use the default configured $serverData mapping). If not provided it defaults to: [url, ip, http_method, server, referrer] + unique_id if present in server data + */ + public function __construct($serverData = null, ?array $extraFields = null) + { + if (null === $serverData) { + $this->serverData =& $_SERVER; + } elseif (\is_array($serverData) || $serverData instanceof \ArrayAccess) { + $this->serverData = $serverData; + } else { + throw new \UnexpectedValueException('$serverData must be an array or object implementing ArrayAccess.'); + } + $defaultEnabled = ['url', 'ip', 'http_method', 'server', 'referrer']; + if (isset($this->serverData['UNIQUE_ID'])) { + $this->extraFields['unique_id'] = 'UNIQUE_ID'; + $defaultEnabled[] = 'unique_id'; + } + if (null === $extraFields) { + $extraFields = $defaultEnabled; + } + if (isset($extraFields[0])) { + foreach (\array_keys($this->extraFields) as $fieldName) { + if (!\in_array($fieldName, $extraFields)) { + unset($this->extraFields[$fieldName]); + } + } + } else { + $this->extraFields = $extraFields; + } + } + /** + * {@inheritDoc} + */ + public function __invoke(array $record) : array + { + // skip processing if for some reason request data + // is not present (CLI or wonky SAPIs) + if (!isset($this->serverData['REQUEST_URI'])) { + return $record; + } + $record['extra'] = $this->appendExtraFields($record['extra']); + return $record; + } + public function addExtraField(string $extraName, string $serverName) : self + { + $this->extraFields[$extraName] = $serverName; + return $this; + } + /** + * @param mixed[] $extra + * @return mixed[] + */ + private function appendExtraFields(array $extra) : array + { + foreach ($this->extraFields as $extraName => $serverName) { + $extra[$extraName] = $this->serverData[$serverName] ?? null; + } + return $extra; + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Registry.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Registry.php new file mode 100644 index 0000000..bf4d6c9 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Registry.php @@ -0,0 +1,122 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace FluentSmtpLib\Monolog; + +use InvalidArgumentException; +/** + * Monolog log registry + * + * Allows to get `Logger` instances in the global scope + * via static method calls on this class. + * + * + * $application = new Monolog\Logger('application'); + * $api = new Monolog\Logger('api'); + * + * Monolog\Registry::addLogger($application); + * Monolog\Registry::addLogger($api); + * + * function testLogger() + * { + * Monolog\Registry::api()->error('Sent to $api Logger instance'); + * Monolog\Registry::application()->error('Sent to $application Logger instance'); + * } + * + * + * @author Tomas Tatarko + */ +class Registry +{ + /** + * List of all loggers in the registry (by named indexes) + * + * @var Logger[] + */ + private static $loggers = []; + /** + * Adds new logging channel to the registry + * + * @param Logger $logger Instance of the logging channel + * @param string|null $name Name of the logging channel ($logger->getName() by default) + * @param bool $overwrite Overwrite instance in the registry if the given name already exists? + * @throws \InvalidArgumentException If $overwrite set to false and named Logger instance already exists + * @return void + */ + public static function addLogger(\FluentSmtpLib\Monolog\Logger $logger, ?string $name = null, bool $overwrite = \false) + { + $name = $name ?: $logger->getName(); + if (isset(self::$loggers[$name]) && !$overwrite) { + throw new \InvalidArgumentException('Logger with the given name already exists'); + } + self::$loggers[$name] = $logger; + } + /** + * Checks if such logging channel exists by name or instance + * + * @param string|Logger $logger Name or logger instance + */ + public static function hasLogger($logger) : bool + { + if ($logger instanceof \FluentSmtpLib\Monolog\Logger) { + $index = \array_search($logger, self::$loggers, \true); + return \false !== $index; + } + return isset(self::$loggers[$logger]); + } + /** + * Removes instance from registry by name or instance + * + * @param string|Logger $logger Name or logger instance + */ + public static function removeLogger($logger) : void + { + if ($logger instanceof \FluentSmtpLib\Monolog\Logger) { + if (\false !== ($idx = \array_search($logger, self::$loggers, \true))) { + unset(self::$loggers[$idx]); + } + } else { + unset(self::$loggers[$logger]); + } + } + /** + * Clears the registry + */ + public static function clear() : void + { + self::$loggers = []; + } + /** + * Gets Logger instance from the registry + * + * @param string $name Name of the requested Logger instance + * @throws \InvalidArgumentException If named Logger instance is not in the registry + */ + public static function getInstance($name) : \FluentSmtpLib\Monolog\Logger + { + if (!isset(self::$loggers[$name])) { + throw new \InvalidArgumentException(\sprintf('Requested "%s" logger instance is not in the registry', $name)); + } + return self::$loggers[$name]; + } + /** + * Gets Logger instance from the registry via static method call + * + * @param string $name Name of the requested Logger instance + * @param mixed[] $arguments Arguments passed to static method call + * @throws \InvalidArgumentException If named Logger instance is not in the registry + * @return Logger Requested instance of Logger + */ + public static function __callStatic($name, $arguments) + { + return self::getInstance($name); + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/ResettableInterface.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/ResettableInterface.php new file mode 100644 index 0000000..ba4f8c4 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/ResettableInterface.php @@ -0,0 +1,34 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace FluentSmtpLib\Monolog; + +/** + * Handler or Processor implementing this interface will be reset when Logger::reset() is called. + * + * Resetting ends a log cycle gets them back to their initial state. + * + * Resetting a Handler or a Processor means flushing/cleaning all buffers, resetting internal + * state, and getting it back to a state in which it can receive log records again. + * + * This is useful in case you want to avoid logs leaking between two requests or jobs when you + * have a long running process like a worker or an application server serving multiple requests + * in one process. + * + * @author Grégoire Pineau + */ +interface ResettableInterface +{ + /** + * @return void + */ + public function reset(); +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/SignalHandler.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/SignalHandler.php new file mode 100644 index 0000000..24b0286 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/SignalHandler.php @@ -0,0 +1,104 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace FluentSmtpLib\Monolog; + +use FluentSmtpLib\Psr\Log\LoggerInterface; +use FluentSmtpLib\Psr\Log\LogLevel; +use ReflectionExtension; +/** + * Monolog POSIX signal handler + * + * @author Robert Gust-Bardon + * + * @phpstan-import-type Level from \Monolog\Logger + * @phpstan-import-type LevelName from \Monolog\Logger + */ +class SignalHandler +{ + /** @var LoggerInterface */ + private $logger; + /** @var array SIG_DFL, SIG_IGN or previous callable */ + private $previousSignalHandler = []; + /** @var array */ + private $signalLevelMap = []; + /** @var array */ + private $signalRestartSyscalls = []; + public function __construct(\FluentSmtpLib\Psr\Log\LoggerInterface $logger) + { + $this->logger = $logger; + } + /** + * @param int|string $level Level or level name + * @param bool $callPrevious + * @param bool $restartSyscalls + * @param bool|null $async + * @return $this + * + * @phpstan-param Level|LevelName|LogLevel::* $level + */ + public function registerSignalHandler(int $signo, $level = \FluentSmtpLib\Psr\Log\LogLevel::CRITICAL, bool $callPrevious = \true, bool $restartSyscalls = \true, ?bool $async = \true) : self + { + if (!\extension_loaded('pcntl') || !\function_exists('pcntl_signal')) { + return $this; + } + $level = \FluentSmtpLib\Monolog\Logger::toMonologLevel($level); + if ($callPrevious) { + $handler = \pcntl_signal_get_handler($signo); + $this->previousSignalHandler[$signo] = $handler; + } else { + unset($this->previousSignalHandler[$signo]); + } + $this->signalLevelMap[$signo] = $level; + $this->signalRestartSyscalls[$signo] = $restartSyscalls; + if ($async !== null) { + \pcntl_async_signals($async); + } + \pcntl_signal($signo, [$this, 'handleSignal'], $restartSyscalls); + return $this; + } + /** + * @param mixed $siginfo + */ + public function handleSignal(int $signo, $siginfo = null) : void + { + static $signals = []; + if (!$signals && \extension_loaded('pcntl')) { + $pcntl = new \ReflectionExtension('pcntl'); + // HHVM 3.24.2 returns an empty array. + foreach ($pcntl->getConstants() ?: \get_defined_constants(\true)['Core'] as $name => $value) { + if (\substr($name, 0, 3) === 'SIG' && $name[3] !== '_' && \is_int($value)) { + $signals[$value] = $name; + } + } + } + $level = $this->signalLevelMap[$signo] ?? \FluentSmtpLib\Psr\Log\LogLevel::CRITICAL; + $signal = $signals[$signo] ?? $signo; + $context = $siginfo ?? []; + $this->logger->log($level, \sprintf('Program received signal %s', $signal), $context); + if (!isset($this->previousSignalHandler[$signo])) { + return; + } + if ($this->previousSignalHandler[$signo] === \SIG_DFL) { + if (\extension_loaded('pcntl') && \function_exists('pcntl_signal') && \function_exists('pcntl_sigprocmask') && \function_exists('pcntl_signal_dispatch') && \extension_loaded('posix') && \function_exists('posix_getpid') && \function_exists('posix_kill')) { + $restartSyscalls = $this->signalRestartSyscalls[$signo] ?? \true; + \pcntl_signal($signo, \SIG_DFL, $restartSyscalls); + \pcntl_sigprocmask(\SIG_UNBLOCK, [$signo], $oldset); + \posix_kill(\posix_getpid(), $signo); + \pcntl_signal_dispatch(); + \pcntl_sigprocmask(\SIG_SETMASK, $oldset); + \pcntl_signal($signo, [$this, 'handleSignal'], $restartSyscalls); + } + } elseif (\is_callable($this->previousSignalHandler[$signo])) { + $this->previousSignalHandler[$signo]($signo, $siginfo); + } + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Test/TestCase.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Test/TestCase.php new file mode 100644 index 0000000..b04b3df --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Test/TestCase.php @@ -0,0 +1,63 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace FluentSmtpLib\Monolog\Test; + +use FluentSmtpLib\Monolog\Logger; +use FluentSmtpLib\Monolog\DateTimeImmutable; +use FluentSmtpLib\Monolog\Formatter\FormatterInterface; +/** + * Lets you easily generate log records and a dummy formatter for testing purposes + * + * @author Jordi Boggiano + * + * @phpstan-import-type Record from \Monolog\Logger + * @phpstan-import-type Level from \Monolog\Logger + * + * @internal feel free to reuse this to test your own handlers, this is marked internal to avoid issues with PHPStorm https://github.com/Seldaek/monolog/issues/1677 + */ +class TestCase extends \FluentSmtpLib\PHPUnit\Framework\TestCase +{ + public function tearDown() : void + { + parent::tearDown(); + if (isset($this->handler)) { + unset($this->handler); + } + } + /** + * @param mixed[] $context + * + * @return array Record + * + * @phpstan-param Level $level + * @phpstan-return Record + */ + protected function getRecord(int $level = \FluentSmtpLib\Monolog\Logger::WARNING, string $message = 'test', array $context = []) : array + { + return ['message' => (string) $message, 'context' => $context, 'level' => $level, 'level_name' => \FluentSmtpLib\Monolog\Logger::getLevelName($level), 'channel' => 'test', 'datetime' => new \FluentSmtpLib\Monolog\DateTimeImmutable(\true), 'extra' => []]; + } + /** + * @phpstan-return Record[] + */ + protected function getMultipleRecords() : array + { + return [$this->getRecord(\FluentSmtpLib\Monolog\Logger::DEBUG, 'debug message 1'), $this->getRecord(\FluentSmtpLib\Monolog\Logger::DEBUG, 'debug message 2'), $this->getRecord(\FluentSmtpLib\Monolog\Logger::INFO, 'information'), $this->getRecord(\FluentSmtpLib\Monolog\Logger::WARNING, 'warning'), $this->getRecord(\FluentSmtpLib\Monolog\Logger::ERROR, 'error')]; + } + protected function getIdentityFormatter() : \FluentSmtpLib\Monolog\Formatter\FormatterInterface + { + $formatter = $this->createMock(\FluentSmtpLib\Monolog\Formatter\FormatterInterface::class); + $formatter->expects($this->any())->method('format')->will($this->returnCallback(function ($record) { + return $record['message']; + })); + return $formatter; + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Utils.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Utils.php new file mode 100644 index 0000000..102499d --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/monolog/monolog/src/Monolog/Utils.php @@ -0,0 +1,240 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace FluentSmtpLib\Monolog; + +final class Utils +{ + const DEFAULT_JSON_FLAGS = \JSON_UNESCAPED_SLASHES | \JSON_UNESCAPED_UNICODE | \JSON_PRESERVE_ZERO_FRACTION | \JSON_INVALID_UTF8_SUBSTITUTE | \JSON_PARTIAL_OUTPUT_ON_ERROR; + public static function getClass(object $object) : string + { + $class = \get_class($object); + if (\false === ($pos = \strpos($class, "@anonymous\x00"))) { + return $class; + } + if (\false === ($parent = \get_parent_class($class))) { + return \substr($class, 0, $pos + 10); + } + return $parent . '@anonymous'; + } + public static function substr(string $string, int $start, ?int $length = null) : string + { + if (\extension_loaded('mbstring')) { + return \mb_strcut($string, $start, $length); + } + return \substr($string, $start, null === $length ? \strlen($string) : $length); + } + /** + * Makes sure if a relative path is passed in it is turned into an absolute path + * + * @param string $streamUrl stream URL or path without protocol + */ + public static function canonicalizePath(string $streamUrl) : string + { + $prefix = ''; + if ('file://' === \substr($streamUrl, 0, 7)) { + $streamUrl = \substr($streamUrl, 7); + $prefix = 'file://'; + } + // other type of stream, not supported + if (\false !== \strpos($streamUrl, '://')) { + return $streamUrl; + } + // already absolute + if (\substr($streamUrl, 0, 1) === '/' || \substr($streamUrl, 1, 1) === ':' || \substr($streamUrl, 0, 2) === '\\\\') { + return $prefix . $streamUrl; + } + $streamUrl = \getcwd() . '/' . $streamUrl; + return $prefix . $streamUrl; + } + /** + * Return the JSON representation of a value + * + * @param mixed $data + * @param int $encodeFlags flags to pass to json encode, defaults to DEFAULT_JSON_FLAGS + * @param bool $ignoreErrors whether to ignore encoding errors or to throw on error, when ignored and the encoding fails, "null" is returned which is valid json for null + * @throws \RuntimeException if encoding fails and errors are not ignored + * @return string when errors are ignored and the encoding fails, "null" is returned which is valid json for null + */ + public static function jsonEncode($data, ?int $encodeFlags = null, bool $ignoreErrors = \false) : string + { + if (null === $encodeFlags) { + $encodeFlags = self::DEFAULT_JSON_FLAGS; + } + if ($ignoreErrors) { + $json = @\json_encode($data, $encodeFlags); + if (\false === $json) { + return 'null'; + } + return $json; + } + $json = \json_encode($data, $encodeFlags); + if (\false === $json) { + $json = self::handleJsonError(\json_last_error(), $data); + } + return $json; + } + /** + * Handle a json_encode failure. + * + * If the failure is due to invalid string encoding, try to clean the + * input and encode again. If the second encoding attempt fails, the + * initial error is not encoding related or the input can't be cleaned then + * raise a descriptive exception. + * + * @param int $code return code of json_last_error function + * @param mixed $data data that was meant to be encoded + * @param int $encodeFlags flags to pass to json encode, defaults to JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE | JSON_PRESERVE_ZERO_FRACTION + * @throws \RuntimeException if failure can't be corrected + * @return string JSON encoded data after error correction + */ + public static function handleJsonError(int $code, $data, ?int $encodeFlags = null) : string + { + if ($code !== \JSON_ERROR_UTF8) { + self::throwEncodeError($code, $data); + } + if (\is_string($data)) { + self::detectAndCleanUtf8($data); + } elseif (\is_array($data)) { + \array_walk_recursive($data, array('Monolog\\Utils', 'detectAndCleanUtf8')); + } else { + self::throwEncodeError($code, $data); + } + if (null === $encodeFlags) { + $encodeFlags = self::DEFAULT_JSON_FLAGS; + } + $json = \json_encode($data, $encodeFlags); + if ($json === \false) { + self::throwEncodeError(\json_last_error(), $data); + } + return $json; + } + /** + * @internal + */ + public static function pcreLastErrorMessage(int $code) : string + { + if (\PHP_VERSION_ID >= 80000) { + return \preg_last_error_msg(); + } + $constants = \get_defined_constants(\true)['pcre']; + $constants = \array_filter($constants, function ($key) { + return \substr($key, -6) == '_ERROR'; + }, \ARRAY_FILTER_USE_KEY); + $constants = \array_flip($constants); + return $constants[$code] ?? 'UNDEFINED_ERROR'; + } + /** + * Throws an exception according to a given code with a customized message + * + * @param int $code return code of json_last_error function + * @param mixed $data data that was meant to be encoded + * @throws \RuntimeException + * + * @return never + */ + private static function throwEncodeError(int $code, $data) : void + { + switch ($code) { + case \JSON_ERROR_DEPTH: + $msg = 'Maximum stack depth exceeded'; + break; + case \JSON_ERROR_STATE_MISMATCH: + $msg = 'Underflow or the modes mismatch'; + break; + case \JSON_ERROR_CTRL_CHAR: + $msg = 'Unexpected control character found'; + break; + case \JSON_ERROR_UTF8: + $msg = 'Malformed UTF-8 characters, possibly incorrectly encoded'; + break; + default: + $msg = 'Unknown error'; + } + throw new \RuntimeException('JSON encoding failed: ' . $msg . '. Encoding: ' . \var_export($data, \true)); + } + /** + * Detect invalid UTF-8 string characters and convert to valid UTF-8. + * + * Valid UTF-8 input will be left unmodified, but strings containing + * invalid UTF-8 codepoints will be reencoded as UTF-8 with an assumed + * original encoding of ISO-8859-15. This conversion may result in + * incorrect output if the actual encoding was not ISO-8859-15, but it + * will be clean UTF-8 output and will not rely on expensive and fragile + * detection algorithms. + * + * Function converts the input in place in the passed variable so that it + * can be used as a callback for array_walk_recursive. + * + * @param mixed $data Input to check and convert if needed, passed by ref + */ + private static function detectAndCleanUtf8(&$data) : void + { + if (\is_string($data) && !\preg_match('//u', $data)) { + $data = \preg_replace_callback('/[\\x80-\\xFF]+/', function ($m) { + return \function_exists('mb_convert_encoding') ? \mb_convert_encoding($m[0], 'UTF-8', 'ISO-8859-1') : \utf8_encode($m[0]); + }, $data); + if (!\is_string($data)) { + $pcreErrorCode = \preg_last_error(); + throw new \RuntimeException('Failed to preg_replace_callback: ' . $pcreErrorCode . ' / ' . self::pcreLastErrorMessage($pcreErrorCode)); + } + $data = \str_replace(['¤', '¦', '¨', '´', '¸', '¼', '½', '¾'], ['€', 'Š', 'š', 'Ž', 'ž', 'Œ', 'œ', 'Ÿ'], $data); + } + } + /** + * Converts a string with a valid 'memory_limit' format, to bytes. + * + * @param string|false $val + * @return int|false Returns an integer representing bytes. Returns FALSE in case of error. + */ + public static function expandIniShorthandBytes($val) + { + if (!\is_string($val)) { + return \false; + } + // support -1 + if ((int) $val < 0) { + return (int) $val; + } + if (!\preg_match('/^\\s*(?\\d+)(?:\\.\\d+)?\\s*(?[gmk]?)\\s*$/i', $val, $match)) { + return \false; + } + $val = (int) $match['val']; + switch (\strtolower($match['unit'])) { + case 'g': + $val *= 1024; + case 'm': + $val *= 1024; + case 'k': + $val *= 1024; + } + return $val; + } + /** + * @param array $record + */ + public static function getRecordMessageForException(array $record) : string + { + $context = ''; + $extra = ''; + try { + if ($record['context']) { + $context = "\nContext: " . \json_encode($record['context']); + } + if ($record['extra']) { + $extra = "\nExtra: " . \json_encode($record['extra']); + } + } catch (\Throwable $e) { + // noop + } + return "\nThe exception occurred while attempting to log: " . $record['message'] . $context . $extra; + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Common/Functions/Strings.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Common/Functions/Strings.php new file mode 100644 index 0000000..6881e49 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Common/Functions/Strings.php @@ -0,0 +1,454 @@ + + * @copyright 2016 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\Common\Functions; + +use FluentSmtpLib\ParagonIE\ConstantTime\Base64; +use FluentSmtpLib\ParagonIE\ConstantTime\Base64UrlSafe; +use FluentSmtpLib\ParagonIE\ConstantTime\Hex; +use FluentSmtpLib\phpseclib3\Math\BigInteger; +use FluentSmtpLib\phpseclib3\Math\Common\FiniteField; +/** + * Common String Functions + * + * @author Jim Wigginton + */ +abstract class Strings +{ + /** + * String Shift + * + * Inspired by array_shift + * + * @param string $string + * @param int $index + * @return string + */ + public static function shift(&$string, $index = 1) + { + $substr = \substr($string, 0, $index); + $string = \substr($string, $index); + return $substr; + } + /** + * String Pop + * + * Inspired by array_pop + * + * @param string $string + * @param int $index + * @return string + */ + public static function pop(&$string, $index = 1) + { + $substr = \substr($string, -$index); + $string = \substr($string, 0, -$index); + return $substr; + } + /** + * Parse SSH2-style string + * + * Returns either an array or a boolean if $data is malformed. + * + * Valid characters for $format are as follows: + * + * C = byte + * b = boolean (true/false) + * N = uint32 + * Q = uint64 + * s = string + * i = mpint + * L = name-list + * + * uint64 is not supported. + * + * @param string $format + * @param string $data + * @return mixed + */ + public static function unpackSSH2($format, &$data) + { + $format = self::formatPack($format); + $result = []; + for ($i = 0; $i < \strlen($format); $i++) { + switch ($format[$i]) { + case 'C': + case 'b': + if (!\strlen($data)) { + throw new \LengthException('At least one byte needs to be present for successful C / b decodes'); + } + break; + case 'N': + case 'i': + case 's': + case 'L': + if (\strlen($data) < 4) { + throw new \LengthException('At least four byte needs to be present for successful N / i / s / L decodes'); + } + break; + case 'Q': + if (\strlen($data) < 8) { + throw new \LengthException('At least eight byte needs to be present for successful N / i / s / L decodes'); + } + break; + default: + throw new \InvalidArgumentException('$format contains an invalid character'); + } + switch ($format[$i]) { + case 'C': + $result[] = \ord(self::shift($data)); + continue 2; + case 'b': + $result[] = \ord(self::shift($data)) != 0; + continue 2; + case 'N': + list(, $temp) = \unpack('N', self::shift($data, 4)); + $result[] = $temp; + continue 2; + case 'Q': + // pack() added support for Q in PHP 5.6.3 and PHP 5.6 is phpseclib 3's minimum version + // so in theory we could support this BUT, "64-bit format codes are not available for + // 32-bit versions" and phpseclib works on 32-bit installs. on 32-bit installs + // 64-bit floats can be used to get larger numbers then 32-bit signed ints would allow + // for. sure, you're not gonna get the full precision of 64-bit numbers but just because + // you need > 32-bit precision doesn't mean you need the full 64-bit precision + \extract(\unpack('Nupper/Nlower', self::shift($data, 8))); + $temp = $upper ? 4294967296 * $upper : 0; + $temp += $lower < 0 ? ($lower & 0x7ffffffff) + 0x80000000 : $lower; + // $temp = hexdec(bin2hex(self::shift($data, 8))); + $result[] = $temp; + continue 2; + } + list(, $length) = \unpack('N', self::shift($data, 4)); + if (\strlen($data) < $length) { + throw new \LengthException("{$length} bytes needed; " . \strlen($data) . ' bytes available'); + } + $temp = self::shift($data, $length); + switch ($format[$i]) { + case 'i': + $result[] = new \FluentSmtpLib\phpseclib3\Math\BigInteger($temp, -256); + break; + case 's': + $result[] = $temp; + break; + case 'L': + $result[] = \explode(',', $temp); + } + } + return $result; + } + /** + * Create SSH2-style string + * + * @param string $format + * @param string|int|float|array|bool ...$elements + * @return string + */ + public static function packSSH2($format, ...$elements) + { + $format = self::formatPack($format); + if (\strlen($format) != \count($elements)) { + throw new \InvalidArgumentException('There must be as many arguments as there are characters in the $format string'); + } + $result = ''; + for ($i = 0; $i < \strlen($format); $i++) { + $element = $elements[$i]; + switch ($format[$i]) { + case 'C': + if (!\is_int($element)) { + throw new \InvalidArgumentException('Bytes must be represented as an integer between 0 and 255, inclusive.'); + } + $result .= \pack('C', $element); + break; + case 'b': + if (!\is_bool($element)) { + throw new \InvalidArgumentException('A boolean parameter was expected.'); + } + $result .= $element ? "\x01" : "\x00"; + break; + case 'Q': + if (!\is_int($element) && !\is_float($element)) { + throw new \InvalidArgumentException('An integer was expected.'); + } + // 4294967296 == 1 << 32 + $result .= \pack('NN', $element / 4294967296, $element); + break; + case 'N': + if (\is_float($element)) { + $element = (int) $element; + } + if (!\is_int($element)) { + throw new \InvalidArgumentException('An integer was expected.'); + } + $result .= \pack('N', $element); + break; + case 's': + if (!self::is_stringable($element)) { + throw new \InvalidArgumentException('A string was expected.'); + } + $result .= \pack('Na*', \strlen($element), $element); + break; + case 'i': + if (!$element instanceof \FluentSmtpLib\phpseclib3\Math\BigInteger && !$element instanceof \FluentSmtpLib\phpseclib3\Math\Common\FiniteField\Integer) { + throw new \InvalidArgumentException('A phpseclib3\\Math\\BigInteger or phpseclib3\\Math\\Common\\FiniteField\\Integer object was expected.'); + } + $element = $element->toBytes(\true); + $result .= \pack('Na*', \strlen($element), $element); + break; + case 'L': + if (!\is_array($element)) { + throw new \InvalidArgumentException('An array was expected.'); + } + $element = \implode(',', $element); + $result .= \pack('Na*', \strlen($element), $element); + break; + default: + throw new \InvalidArgumentException('$format contains an invalid character'); + } + } + return $result; + } + /** + * Expand a pack string + * + * Converts C5 to CCCCC, for example. + * + * @param string $format + * @return string + */ + private static function formatPack($format) + { + $parts = \preg_split('#(\\d+)#', $format, -1, \PREG_SPLIT_DELIM_CAPTURE); + $format = ''; + for ($i = 1; $i < \count($parts); $i += 2) { + $format .= \substr($parts[$i - 1], 0, -1) . \str_repeat(\substr($parts[$i - 1], -1), $parts[$i]); + } + $format .= $parts[$i - 1]; + return $format; + } + /** + * Convert binary data into bits + * + * bin2hex / hex2bin refer to base-256 encoded data as binary, whilst + * decbin / bindec refer to base-2 encoded data as binary. For the purposes + * of this function, bin refers to base-256 encoded data whilst bits refers + * to base-2 encoded data + * + * @param string $x + * @return string + */ + public static function bits2bin($x) + { + /* + // the pure-PHP approach is faster than the GMP approach + if (function_exists('gmp_export')) { + return strlen($x) ? gmp_export(gmp_init($x, 2)) : gmp_init(0); + } + */ + if (\preg_match('#[^01]#', $x)) { + throw new \RuntimeException('The only valid characters are 0 and 1'); + } + if (!\defined('PHP_INT_MIN')) { + \define('PHP_INT_MIN', ~\PHP_INT_MAX); + } + $length = \strlen($x); + if (!$length) { + return ''; + } + $block_size = \PHP_INT_SIZE << 3; + $pad = $block_size - $length % $block_size; + if ($pad != $block_size) { + $x = \str_repeat('0', $pad) . $x; + } + $parts = \str_split($x, $block_size); + $str = ''; + foreach ($parts as $part) { + $xor = $part[0] == '1' ? \PHP_INT_MIN : 0; + $part[0] = '0'; + $str .= \pack(\PHP_INT_SIZE == 4 ? 'N' : 'J', $xor ^ eval('return 0b' . $part . ';')); + } + return \ltrim($str, "\x00"); + } + /** + * Convert bits to binary data + * + * @param string $x + * @return string + */ + public static function bin2bits($x, $trim = \true) + { + /* + // the pure-PHP approach is slower than the GMP approach BUT + // i want to the pure-PHP version to be easily unit tested as well + if (function_exists('gmp_import')) { + return gmp_strval(gmp_import($x), 2); + } + */ + $len = \strlen($x); + $mod = $len % \PHP_INT_SIZE; + if ($mod) { + $x = \str_pad($x, $len + \PHP_INT_SIZE - $mod, "\x00", \STR_PAD_LEFT); + } + $bits = ''; + if (\PHP_INT_SIZE == 4) { + $digits = \unpack('N*', $x); + foreach ($digits as $digit) { + $bits .= \sprintf('%032b', $digit); + } + } else { + $digits = \unpack('J*', $x); + foreach ($digits as $digit) { + $bits .= \sprintf('%064b', $digit); + } + } + return $trim ? \ltrim($bits, '0') : $bits; + } + /** + * Switch Endianness Bit Order + * + * @param string $x + * @return string + */ + public static function switchEndianness($x) + { + $r = ''; + for ($i = \strlen($x) - 1; $i >= 0; $i--) { + $b = \ord($x[$i]); + if (\PHP_INT_SIZE === 8) { + // 3 operations + // from http://graphics.stanford.edu/~seander/bithacks.html#ReverseByteWith64BitsDiv + $r .= \chr(($b * 0x202020202 & 0x10884422010) % 1023); + } else { + // 7 operations + // from http://graphics.stanford.edu/~seander/bithacks.html#ReverseByteWith32Bits + $p1 = $b * 0x802 & 0x22110; + $p2 = $b * 0x8020 & 0x88440; + $r .= \chr(($p1 | $p2) * 0x10101 >> 16); + } + } + return $r; + } + /** + * Increment the current string + * + * @param string $var + * @return string + */ + public static function increment_str(&$var) + { + if (\function_exists('sodium_increment')) { + $var = \strrev($var); + \sodium_increment($var); + $var = \strrev($var); + return $var; + } + for ($i = 4; $i <= \strlen($var); $i += 4) { + $temp = \substr($var, -$i, 4); + switch ($temp) { + case "\xff\xff\xff\xff": + $var = \substr_replace($var, "\x00\x00\x00\x00", -$i, 4); + break; + case "\xff\xff\xff": + $var = \substr_replace($var, "\x80\x00\x00\x00", -$i, 4); + return $var; + default: + $temp = \unpack('Nnum', $temp); + $var = \substr_replace($var, \pack('N', $temp['num'] + 1), -$i, 4); + return $var; + } + } + $remainder = \strlen($var) % 4; + if ($remainder == 0) { + return $var; + } + $temp = \unpack('Nnum', \str_pad(\substr($var, 0, $remainder), 4, "\x00", \STR_PAD_LEFT)); + $temp = \substr(\pack('N', $temp['num'] + 1), -$remainder); + $var = \substr_replace($var, $temp, 0, $remainder); + return $var; + } + /** + * Find whether the type of a variable is string (or could be converted to one) + * + * @param mixed $var + * @return bool + * @psalm-assert-if-true string|\Stringable $var + */ + public static function is_stringable($var) + { + return \is_string($var) || \is_object($var) && \method_exists($var, '__toString'); + } + /** + * Constant Time Base64-decoding + * + * ParagoneIE\ConstantTime doesn't use libsodium if it's available so we'll do so + * ourselves. see https://github.com/paragonie/constant_time_encoding/issues/39 + * + * @param string $data + * @return string + */ + public static function base64_decode($data) + { + return \function_exists('sodium_base642bin') ? \sodium_base642bin($data, \SODIUM_BASE64_VARIANT_ORIGINAL_NO_PADDING, '=') : \FluentSmtpLib\ParagonIE\ConstantTime\Base64::decode($data); + } + /** + * Constant Time Base64-decoding (URL safe) + * + * @param string $data + * @return string + */ + public static function base64url_decode($data) + { + // return self::base64_decode(str_replace(['-', '_'], ['+', '/'], $data)); + return \function_exists('sodium_base642bin') ? \sodium_base642bin($data, \SODIUM_BASE64_VARIANT_URLSAFE_NO_PADDING, '=') : \FluentSmtpLib\ParagonIE\ConstantTime\Base64UrlSafe::decode($data); + } + /** + * Constant Time Base64-encoding + * + * @param string $data + * @return string + */ + public static function base64_encode($data) + { + return \function_exists('sodium_bin2base64') ? \sodium_bin2base64($data, \SODIUM_BASE64_VARIANT_ORIGINAL) : \FluentSmtpLib\ParagonIE\ConstantTime\Base64::encode($data); + } + /** + * Constant Time Base64-encoding (URL safe) + * + * @param string $data + * @return string + */ + public static function base64url_encode($data) + { + // return str_replace(['+', '/'], ['-', '_'], self::base64_encode($data)); + return \function_exists('sodium_bin2base64') ? \sodium_bin2base64($data, \SODIUM_BASE64_VARIANT_URLSAFE_NO_PADDING) : \FluentSmtpLib\ParagonIE\ConstantTime\Base64UrlSafe::encode($data); + } + /** + * Constant Time Hex Decoder + * + * @param string $data + * @return string + */ + public static function hex2bin($data) + { + return \function_exists('sodium_hex2bin') ? \sodium_hex2bin($data) : \FluentSmtpLib\ParagonIE\ConstantTime\Hex::decode($data); + } + /** + * Constant Time Hex Encoder + * + * @param string $data + * @return string + */ + public static function bin2hex($data) + { + return \function_exists('sodium_bin2hex') ? \sodium_bin2hex($data) : \FluentSmtpLib\ParagonIE\ConstantTime\Hex::encode($data); + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/AES.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/AES.php new file mode 100644 index 0000000..7250a3c --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/AES.php @@ -0,0 +1,112 @@ + + * setKey('abcdefghijklmnop'); + * + * $size = 10 * 1024; + * $plaintext = ''; + * for ($i = 0; $i < $size; $i++) { + * $plaintext.= 'a'; + * } + * + * echo $aes->decrypt($aes->encrypt($plaintext)); + * ?> + * + * + * @author Jim Wigginton + * @copyright 2008 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\Crypt; + +/** + * Pure-PHP implementation of AES. + * + * @author Jim Wigginton + */ +class AES extends \FluentSmtpLib\phpseclib3\Crypt\Rijndael +{ + /** + * Dummy function + * + * Since \phpseclib3\Crypt\AES extends \phpseclib3\Crypt\Rijndael, this function is, technically, available, but it doesn't do anything. + * + * @see \phpseclib3\Crypt\Rijndael::setBlockLength() + * @param int $length + * @throws \BadMethodCallException anytime it's called + */ + public function setBlockLength($length) + { + throw new \BadMethodCallException('The block length cannot be set for AES.'); + } + /** + * Sets the key length + * + * Valid key lengths are 128, 192, and 256. Set the link to bool(false) to disable a fixed key length + * + * @see \phpseclib3\Crypt\Rijndael:setKeyLength() + * @param int $length + * @throws \LengthException if the key length isn't supported + */ + public function setKeyLength($length) + { + switch ($length) { + case 128: + case 192: + case 256: + break; + default: + throw new \LengthException('Key of size ' . $length . ' not supported by this algorithm. Only keys of sizes 128, 192 or 256 supported'); + } + parent::setKeyLength($length); + } + /** + * Sets the key. + * + * Rijndael supports five different key lengths, AES only supports three. + * + * @see \phpseclib3\Crypt\Rijndael:setKey() + * @see setKeyLength() + * @param string $key + * @throws \LengthException if the key length isn't supported + */ + public function setKey($key) + { + switch (\strlen($key)) { + case 16: + case 24: + case 32: + break; + default: + throw new \LengthException('Key of size ' . \strlen($key) . ' not supported by this algorithm. Only keys of sizes 16, 24 or 32 supported'); + } + parent::setKey($key); + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/Blowfish.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/Blowfish.php new file mode 100644 index 0000000..770ddb2 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/Blowfish.php @@ -0,0 +1,591 @@ + unpack('N*', $x), $blocks); it jumps up by an additional + * ~90MB, yielding a 106x increase in memory usage. Consequently, it bcrypt calls a different + * _encryptBlock() then the regular Blowfish does. That said, the Blowfish _encryptBlock() is + * basically just a thin wrapper around the bcrypt _encryptBlock(), so there's that. + * + * This explains 3 of the 4 _encryptBlock() implementations. the last _encryptBlock() + * implementation can best be understood by doing Ctrl + F and searching for where + * self::$use_reg_intval is defined. + * + * # phpseclib's three different _setupKey() implementations + * + * Every bcrypt round is the equivalent of encrypting 512KB of data. Since OpenSSH uses 16 + * rounds by default that's ~8MB of data that's essentially being encrypted whenever + * you use bcrypt. That's a lot of data, however, bcrypt operates within tighter constraints + * than regular Blowfish, so we can use that to our advantage. In particular, whereas Blowfish + * supports variable length keys, in bcrypt, the initial "key" is the sha512 hash of the + * password. sha512 hashes are 512 bits or 64 bytes long and thus the bcrypt keys are of a + * fixed length whereas Blowfish keys are not of a fixed length. + * + * bcrypt actually has two different key expansion steps. The first one (expandstate) is + * constantly XOR'ing every _encryptBlock() parameter against the salt prior _encryptBlock()'s + * being called. The second one (expand0state) is more similar to Blowfish's _setupKey() + * but it can still use the fixed length key optimization discussed above and can do away with + * the pack() / unpack() calls. + * + * I suppose _setupKey() could be made to be a thin wrapper around expandstate() but idk it's + * just a lot of work for very marginal benefits as _setupKey() is only called once for + * regular Blowfish vs the 128 times it's called --per round-- with bcrypt. + * + * # blowfish + bcrypt in the same class + * + * Altho there's a lot of Blowfish code that bcrypt doesn't re-use, bcrypt does re-use the + * initial S-boxes, the initial P-array and the int-only _encryptBlock() implementation. + * + * # Credit + * + * phpseclib's bcrypt implementation is based losely off of OpenSSH's implementation: + * + * https://github.com/openssh/openssh-portable/blob/master/openbsd-compat/bcrypt_pbkdf.c + * + * Here's a short example of how to use this library: + * + * setKey('12345678901234567890123456789012'); + * + * $plaintext = str_repeat('a', 1024); + * + * echo $blowfish->decrypt($blowfish->encrypt($plaintext)); + * ?> + * + * + * @author Jim Wigginton + * @author Hans-Juergen Petrich + * @copyright 2007 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\Crypt; + +use FluentSmtpLib\phpseclib3\Crypt\Common\BlockCipher; +/** + * Pure-PHP implementation of Blowfish. + * + * @author Jim Wigginton + * @author Hans-Juergen Petrich + */ +class Blowfish extends \FluentSmtpLib\phpseclib3\Crypt\Common\BlockCipher +{ + /** + * Block Length of the cipher + * + * @see \phpseclib3\Crypt\Common\SymmetricKey::block_size + * @var int + */ + protected $block_size = 8; + /** + * The mcrypt specific name of the cipher + * + * @see \phpseclib3\Crypt\Common\SymmetricKey::cipher_name_mcrypt + * @var string + */ + protected $cipher_name_mcrypt = 'blowfish'; + /** + * Optimizing value while CFB-encrypting + * + * @see \phpseclib3\Crypt\Common\SymmetricKey::cfb_init_len + * @var int + */ + protected $cfb_init_len = 500; + /** + * The fixed subkeys boxes + * + * S-Box + * + * @var array + */ + private static $sbox = [0xd1310ba6, 0x98dfb5ac, 0x2ffd72db, 0xd01adfb7, 0xb8e1afed, 0x6a267e96, 0xba7c9045, 0xf12c7f99, 0x24a19947, 0xb3916cf7, 0x801f2e2, 0x858efc16, 0x636920d8, 0x71574e69, 0xa458fea3, 0xf4933d7e, 0xd95748f, 0x728eb658, 0x718bcd58, 0x82154aee, 0x7b54a41d, 0xc25a59b5, 0x9c30d539, 0x2af26013, 0xc5d1b023, 0x286085f0, 0xca417918, 0xb8db38ef, 0x8e79dcb0, 0x603a180e, 0x6c9e0e8b, 0xb01e8a3e, 0xd71577c1, 0xbd314b27, 0x78af2fda, 0x55605c60, 0xe65525f3, 0xaa55ab94, 0x57489862, 0x63e81440, 0x55ca396a, 0x2aab10b6, 0xb4cc5c34, 0x1141e8ce, 0xa15486af, 0x7c72e993, 0xb3ee1411, 0x636fbc2a, 0x2ba9c55d, 0x741831f6, 0xce5c3e16, 0x9b87931e, 0xafd6ba33, 0x6c24cf5c, 0x7a325381, 0x28958677, 0x3b8f4898, 0x6b4bb9af, 0xc4bfe81b, 0x66282193, 0x61d809cc, 0xfb21a991, 0x487cac60, 0x5dec8032, 0xef845d5d, 0xe98575b1, 0xdc262302, 0xeb651b88, 0x23893e81, 0xd396acc5, 0xf6d6ff3, 0x83f44239, 0x2e0b4482, 0xa4842004, 0x69c8f04a, 0x9e1f9b5e, 0x21c66842, 0xf6e96c9a, 0x670c9c61, 0xabd388f0, 0x6a51a0d2, 0xd8542f68, 0x960fa728, 0xab5133a3, 0x6eef0b6c, 0x137a3be4, 0xba3bf050, 0x7efb2a98, 0xa1f1651d, 0x39af0176, 0x66ca593e, 0x82430e88, 0x8cee8619, 0x456f9fb4, 0x7d84a5c3, 0x3b8b5ebe, 0xe06f75d8, 0x85c12073, 0x401a449f, 0x56c16aa6, 0x4ed3aa62, 0x363f7706, 0x1bfedf72, 0x429b023d, 0x37d0d724, 0xd00a1248, 0xdb0fead3, 0x49f1c09b, 0x75372c9, 0x80991b7b, 0x25d479d8, 0xf6e8def7, 0xe3fe501a, 0xb6794c3b, 0x976ce0bd, 0x4c006ba, 0xc1a94fb6, 0x409f60c4, 0x5e5c9ec2, 0x196a2463, 0x68fb6faf, 0x3e6c53b5, 0x1339b2eb, 0x3b52ec6f, 0x6dfc511f, 0x9b30952c, 0xcc814544, 0xaf5ebd09, 0xbee3d004, 0xde334afd, 0x660f2807, 0x192e4bb3, 0xc0cba857, 0x45c8740f, 0xd20b5f39, 0xb9d3fbdb, 0x5579c0bd, 0x1a60320a, 0xd6a100c6, 0x402c7279, 0x679f25fe, 0xfb1fa3cc, 0x8ea5e9f8, 0xdb3222f8, 0x3c7516df, 0xfd616b15, 0x2f501ec8, 0xad0552ab, 0x323db5fa, 0xfd238760, 0x53317b48, 0x3e00df82, 0x9e5c57bb, 0xca6f8ca0, 0x1a87562e, 0xdf1769db, 0xd542a8f6, 0x287effc3, 0xac6732c6, 0x8c4f5573, 0x695b27b0, 0xbbca58c8, 0xe1ffa35d, 0xb8f011a0, 0x10fa3d98, 0xfd2183b8, 0x4afcb56c, 0x2dd1d35b, 0x9a53e479, 0xb6f84565, 0xd28e49bc, 0x4bfb9790, 0xe1ddf2da, 0xa4cb7e33, 0x62fb1341, 0xcee4c6e8, 0xef20cada, 0x36774c01, 0xd07e9efe, 0x2bf11fb4, 0x95dbda4d, 0xae909198, 0xeaad8e71, 0x6b93d5a0, 0xd08ed1d0, 0xafc725e0, 0x8e3c5b2f, 0x8e7594b7, 0x8ff6e2fb, 0xf2122b64, 0x8888b812, 0x900df01c, 0x4fad5ea0, 0x688fc31c, 0xd1cff191, 0xb3a8c1ad, 0x2f2f2218, 0xbe0e1777, 0xea752dfe, 0x8b021fa1, 0xe5a0cc0f, 0xb56f74e8, 0x18acf3d6, 0xce89e299, 0xb4a84fe0, 0xfd13e0b7, 0x7cc43b81, 0xd2ada8d9, 0x165fa266, 0x80957705, 0x93cc7314, 0x211a1477, 0xe6ad2065, 0x77b5fa86, 0xc75442f5, 0xfb9d35cf, 0xebcdaf0c, 0x7b3e89a0, 0xd6411bd3, 0xae1e7e49, 0x250e2d, 0x2071b35e, 0x226800bb, 0x57b8e0af, 0x2464369b, 0xf009b91e, 0x5563911d, 0x59dfa6aa, 0x78c14389, 0xd95a537f, 0x207d5ba2, 0x2e5b9c5, 0x83260376, 0x6295cfa9, 0x11c81968, 0x4e734a41, 0xb3472dca, 0x7b14a94a, 0x1b510052, 0x9a532915, 0xd60f573f, 0xbc9bc6e4, 0x2b60a476, 0x81e67400, 0x8ba6fb5, 0x571be91f, 0xf296ec6b, 0x2a0dd915, 0xb6636521, 0xe7b9f9b6, 0xff34052e, 0xc5855664, 0x53b02d5d, 0xa99f8fa1, 0x8ba4799, 0x6e85076a, 0x4b7a70e9, 0xb5b32944, 0xdb75092e, 0xc4192623, 0xad6ea6b0, 0x49a7df7d, 0x9cee60b8, 0x8fedb266, 0xecaa8c71, 0x699a17ff, 0x5664526c, 0xc2b19ee1, 0x193602a5, 0x75094c29, 0xa0591340, 0xe4183a3e, 0x3f54989a, 0x5b429d65, 0x6b8fe4d6, 0x99f73fd6, 0xa1d29c07, 0xefe830f5, 0x4d2d38e6, 0xf0255dc1, 0x4cdd2086, 0x8470eb26, 0x6382e9c6, 0x21ecc5e, 0x9686b3f, 0x3ebaefc9, 0x3c971814, 0x6b6a70a1, 0x687f3584, 0x52a0e286, 0xb79c5305, 0xaa500737, 0x3e07841c, 0x7fdeae5c, 0x8e7d44ec, 0x5716f2b8, 0xb03ada37, 0xf0500c0d, 0xf01c1f04, 0x200b3ff, 0xae0cf51a, 0x3cb574b2, 0x25837a58, 0xdc0921bd, 0xd19113f9, 0x7ca92ff6, 0x94324773, 0x22f54701, 0x3ae5e581, 0x37c2dadc, 0xc8b57634, 0x9af3dda7, 0xa9446146, 0xfd0030e, 0xecc8c73e, 0xa4751e41, 0xe238cd99, 0x3bea0e2f, 0x3280bba1, 0x183eb331, 0x4e548b38, 0x4f6db908, 0x6f420d03, 0xf60a04bf, 0x2cb81290, 0x24977c79, 0x5679b072, 0xbcaf89af, 0xde9a771f, 0xd9930810, 0xb38bae12, 0xdccf3f2e, 0x5512721f, 0x2e6b7124, 0x501adde6, 0x9f84cd87, 0x7a584718, 0x7408da17, 0xbc9f9abc, 0xe94b7d8c, 0xec7aec3a, 0xdb851dfa, 0x63094366, 0xc464c3d2, 0xef1c1847, 0x3215d908, 0xdd433b37, 0x24c2ba16, 0x12a14d43, 0x2a65c451, 0x50940002, 0x133ae4dd, 0x71dff89e, 0x10314e55, 0x81ac77d6, 0x5f11199b, 0x43556f1, 0xd7a3c76b, 0x3c11183b, 0x5924a509, 0xf28fe6ed, 0x97f1fbfa, 0x9ebabf2c, 0x1e153c6e, 0x86e34570, 0xeae96fb1, 0x860e5e0a, 0x5a3e2ab3, 0x771fe71c, 0x4e3d06fa, 0x2965dcb9, 0x99e71d0f, 0x803e89d6, 0x5266c825, 0x2e4cc978, 0x9c10b36a, 0xc6150eba, 0x94e2ea78, 0xa5fc3c53, 0x1e0a2df4, 0xf2f74ea7, 0x361d2b3d, 0x1939260f, 0x19c27960, 0x5223a708, 0xf71312b6, 0xebadfe6e, 0xeac31f66, 0xe3bc4595, 0xa67bc883, 0xb17f37d1, 0x18cff28, 0xc332ddef, 0xbe6c5aa5, 0x65582185, 0x68ab9802, 0xeecea50f, 0xdb2f953b, 0x2aef7dad, 0x5b6e2f84, 0x1521b628, 0x29076170, 0xecdd4775, 0x619f1510, 0x13cca830, 0xeb61bd96, 0x334fe1e, 0xaa0363cf, 0xb5735c90, 0x4c70a239, 0xd59e9e0b, 0xcbaade14, 0xeecc86bc, 0x60622ca7, 0x9cab5cab, 0xb2f3846e, 0x648b1eaf, 0x19bdf0ca, 0xa02369b9, 0x655abb50, 0x40685a32, 0x3c2ab4b3, 0x319ee9d5, 0xc021b8f7, 0x9b540b19, 0x875fa099, 0x95f7997e, 0x623d7da8, 0xf837889a, 0x97e32d77, 0x11ed935f, 0x16681281, 0xe358829, 0xc7e61fd6, 0x96dedfa1, 0x7858ba99, 0x57f584a5, 0x1b227263, 0x9b83c3ff, 0x1ac24696, 0xcdb30aeb, 0x532e3054, 0x8fd948e4, 0x6dbc3128, 0x58ebf2ef, 0x34c6ffea, 0xfe28ed61, 0xee7c3c73, 0x5d4a14d9, 0xe864b7e3, 0x42105d14, 0x203e13e0, 0x45eee2b6, 0xa3aaabea, 0xdb6c4f15, 0xfacb4fd0, 0xc742f442, 0xef6abbb5, 0x654f3b1d, 0x41cd2105, 0xd81e799e, 0x86854dc7, 0xe44b476a, 0x3d816250, 0xcf62a1f2, 0x5b8d2646, 0xfc8883a0, 0xc1c7b6a3, 0x7f1524c3, 0x69cb7492, 0x47848a0b, 0x5692b285, 0x95bbf00, 0xad19489d, 0x1462b174, 0x23820e00, 0x58428d2a, 0xc55f5ea, 0x1dadf43e, 0x233f7061, 0x3372f092, 0x8d937e41, 0xd65fecf1, 0x6c223bdb, 0x7cde3759, 0xcbee7460, 0x4085f2a7, 0xce77326e, 0xa6078084, 0x19f8509e, 0xe8efd855, 0x61d99735, 0xa969a7aa, 0xc50c06c2, 0x5a04abfc, 0x800bcadc, 0x9e447a2e, 0xc3453484, 0xfdd56705, 0xe1e9ec9, 0xdb73dbd3, 0x105588cd, 0x675fda79, 0xe3674340, 0xc5c43465, 0x713e38d8, 0x3d28f89e, 0xf16dff20, 0x153e21e7, 0x8fb03d4a, 0xe6e39f2b, 0xdb83adf7, 0xe93d5a68, 0x948140f7, 0xf64c261c, 0x94692934, 0x411520f7, 0x7602d4f7, 0xbcf46b2e, 0xd4a20068, 0xd4082471, 0x3320f46a, 0x43b7d4b7, 0x500061af, 0x1e39f62e, 0x97244546, 0x14214f74, 0xbf8b8840, 0x4d95fc1d, 0x96b591af, 0x70f4ddd3, 0x66a02f45, 0xbfbc09ec, 0x3bd9785, 0x7fac6dd0, 0x31cb8504, 0x96eb27b3, 0x55fd3941, 0xda2547e6, 0xabca0a9a, 0x28507825, 0x530429f4, 0xa2c86da, 0xe9b66dfb, 0x68dc1462, 0xd7486900, 0x680ec0a4, 0x27a18dee, 0x4f3ffea2, 0xe887ad8c, 0xb58ce006, 0x7af4d6b6, 0xaace1e7c, 0xd3375fec, 0xce78a399, 0x406b2a42, 0x20fe9e35, 0xd9f385b9, 0xee39d7ab, 0x3b124e8b, 0x1dc9faf7, 0x4b6d1856, 0x26a36631, 0xeae397b2, 0x3a6efa74, 0xdd5b4332, 0x6841e7f7, 0xca7820fb, 0xfb0af54e, 0xd8feb397, 0x454056ac, 0xba489527, 0x55533a3a, 0x20838d87, 0xfe6ba9b7, 0xd096954b, 0x55a867bc, 0xa1159a58, 0xcca92963, 0x99e1db33, 0xa62a4a56, 0x3f3125f9, 0x5ef47e1c, 0x9029317c, 0xfdf8e802, 0x4272f70, 0x80bb155c, 0x5282ce3, 0x95c11548, 0xe4c66d22, 0x48c1133f, 0xc70f86dc, 0x7f9c9ee, 0x41041f0f, 0x404779a4, 0x5d886e17, 0x325f51eb, 0xd59bc0d1, 0xf2bcc18f, 0x41113564, 0x257b7834, 0x602a9c60, 0xdff8e8a3, 0x1f636c1b, 0xe12b4c2, 0x2e1329e, 0xaf664fd1, 0xcad18115, 0x6b2395e0, 0x333e92e1, 0x3b240b62, 0xeebeb922, 0x85b2a20e, 0xe6ba0d99, 0xde720c8c, 0x2da2f728, 0xd0127845, 0x95b794fd, 0x647d0862, 0xe7ccf5f0, 0x5449a36f, 0x877d48fa, 0xc39dfd27, 0xf33e8d1e, 0xa476341, 0x992eff74, 0x3a6f6eab, 0xf4f8fd37, 0xa812dc60, 0xa1ebddf8, 0x991be14c, 0xdb6e6b0d, 0xc67b5510, 0x6d672c37, 0x2765d43b, 0xdcd0e804, 0xf1290dc7, 0xcc00ffa3, 0xb5390f92, 0x690fed0b, 0x667b9ffb, 0xcedb7d9c, 0xa091cf0b, 0xd9155ea3, 0xbb132f88, 0x515bad24, 0x7b9479bf, 0x763bd6eb, 0x37392eb3, 0xcc115979, 0x8026e297, 0xf42e312d, 0x6842ada7, 0xc66a2b3b, 0x12754ccc, 0x782ef11c, 0x6a124237, 0xb79251e7, 0x6a1bbe6, 0x4bfb6350, 0x1a6b1018, 0x11caedfa, 0x3d25bdd8, 0xe2e1c3c9, 0x44421659, 0xa121386, 0xd90cec6e, 0xd5abea2a, 0x64af674e, 0xda86a85f, 0xbebfe988, 0x64e4c3fe, 0x9dbc8057, 0xf0f7c086, 0x60787bf8, 0x6003604d, 0xd1fd8346, 0xf6381fb0, 0x7745ae04, 0xd736fccc, 0x83426b33, 0xf01eab71, 0xb0804187, 0x3c005e5f, 0x77a057be, 0xbde8ae24, 0x55464299, 0xbf582e61, 0x4e58f48f, 0xf2ddfda2, 0xf474ef38, 0x8789bdc2, 0x5366f9c3, 0xc8b38e74, 0xb475f255, 0x46fcd9b9, 0x7aeb2661, 0x8b1ddf84, 0x846a0e79, 0x915f95e2, 0x466e598e, 0x20b45770, 0x8cd55591, 0xc902de4c, 0xb90bace1, 0xbb8205d0, 0x11a86248, 0x7574a99e, 0xb77f19b6, 0xe0a9dc09, 0x662d09a1, 0xc4324633, 0xe85a1f02, 0x9f0be8c, 0x4a99a025, 0x1d6efe10, 0x1ab93d1d, 0xba5a4df, 0xa186f20f, 0x2868f169, 0xdcb7da83, 0x573906fe, 0xa1e2ce9b, 0x4fcd7f52, 0x50115e01, 0xa70683fa, 0xa002b5c4, 0xde6d027, 0x9af88c27, 0x773f8641, 0xc3604c06, 0x61a806b5, 0xf0177a28, 0xc0f586e0, 0x6058aa, 0x30dc7d62, 0x11e69ed7, 0x2338ea63, 0x53c2dd94, 0xc2c21634, 0xbbcbee56, 0x90bcb6de, 0xebfc7da1, 0xce591d76, 0x6f05e409, 0x4b7c0188, 0x39720a3d, 0x7c927c24, 0x86e3725f, 0x724d9db9, 0x1ac15bb4, 0xd39eb8fc, 0xed545578, 0x8fca5b5, 0xd83d7cd3, 0x4dad0fc4, 0x1e50ef5e, 0xb161e6f8, 0xa28514d9, 0x6c51133c, 0x6fd5c7e7, 0x56e14ec4, 0x362abfce, 0xddc6c837, 0xd79a3234, 0x92638212, 0x670efa8e, 0x406000e0, 0x3a39ce37, 0xd3faf5cf, 0xabc27737, 0x5ac52d1b, 0x5cb0679e, 0x4fa33742, 0xd3822740, 0x99bc9bbe, 0xd5118e9d, 0xbf0f7315, 0xd62d1c7e, 0xc700c47b, 0xb78c1b6b, 0x21a19045, 0xb26eb1be, 0x6a366eb4, 0x5748ab2f, 0xbc946e79, 0xc6a376d2, 0x6549c2c8, 0x530ff8ee, 0x468dde7d, 0xd5730a1d, 0x4cd04dc6, 0x2939bbdb, 0xa9ba4650, 0xac9526e8, 0xbe5ee304, 0xa1fad5f0, 0x6a2d519a, 0x63ef8ce2, 0x9a86ee22, 0xc089c2b8, 0x43242ef6, 0xa51e03aa, 0x9cf2d0a4, 0x83c061ba, 0x9be96a4d, 0x8fe51550, 0xba645bd6, 0x2826a2f9, 0xa73a3ae1, 0x4ba99586, 0xef5562e9, 0xc72fefd3, 0xf752f7da, 0x3f046f69, 0x77fa0a59, 0x80e4a915, 0x87b08601, 0x9b09e6ad, 0x3b3ee593, 0xe990fd5a, 0x9e34d797, 0x2cf0b7d9, 0x22b8b51, 0x96d5ac3a, 0x17da67d, 0xd1cf3ed6, 0x7c7d2d28, 0x1f9f25cf, 0xadf2b89b, 0x5ad6b472, 0x5a88f54c, 0xe029ac71, 0xe019a5e6, 0x47b0acfd, 0xed93fa9b, 0xe8d3c48d, 0x283b57cc, 0xf8d56629, 0x79132e28, 0x785f0191, 0xed756055, 0xf7960e44, 0xe3d35e8c, 0x15056dd4, 0x88f46dba, 0x3a16125, 0x564f0bd, 0xc3eb9e15, 0x3c9057a2, 0x97271aec, 0xa93a072a, 0x1b3f6d9b, 0x1e6321f5, 0xf59c66fb, 0x26dcf319, 0x7533d928, 0xb155fdf5, 0x3563482, 0x8aba3cbb, 0x28517711, 0xc20ad9f8, 0xabcc5167, 0xccad925f, 0x4de81751, 0x3830dc8e, 0x379d5862, 0x9320f991, 0xea7a90c2, 0xfb3e7bce, 0x5121ce64, 0x774fbe32, 0xa8b6e37e, 0xc3293d46, 0x48de5369, 0x6413e680, 0xa2ae0810, 0xdd6db224, 0x69852dfd, 0x9072166, 0xb39a460a, 0x6445c0dd, 0x586cdecf, 0x1c20c8ae, 0x5bbef7dd, 0x1b588d40, 0xccd2017f, 0x6bb4e3bb, 0xdda26a7e, 0x3a59ff45, 0x3e350a44, 0xbcb4cdd5, 0x72eacea8, 0xfa6484bb, 0x8d6612ae, 0xbf3c6f47, 0xd29be463, 0x542f5d9e, 0xaec2771b, 0xf64e6370, 0x740e0d8d, 0xe75b1357, 0xf8721671, 0xaf537d5d, 0x4040cb08, 0x4eb4e2cc, 0x34d2466a, 0x115af84, 0xe1b00428, 0x95983a1d, 0x6b89fb4, 0xce6ea048, 0x6f3f3b82, 0x3520ab82, 0x11a1d4b, 0x277227f8, 0x611560b1, 0xe7933fdc, 0xbb3a792b, 0x344525bd, 0xa08839e1, 0x51ce794b, 0x2f32c9b7, 0xa01fbac9, 0xe01cc87e, 0xbcc7d1f6, 0xcf0111c3, 0xa1e8aac7, 0x1a908749, 0xd44fbd9a, 0xd0dadecb, 0xd50ada38, 0x339c32a, 0xc6913667, 0x8df9317c, 0xe0b12b4f, 0xf79e59b7, 0x43f5bb3a, 0xf2d519ff, 0x27d9459c, 0xbf97222c, 0x15e6fc2a, 0xf91fc71, 0x9b941525, 0xfae59361, 0xceb69ceb, 0xc2a86459, 0x12baa8d1, 0xb6c1075e, 0xe3056a0c, 0x10d25065, 0xcb03a442, 0xe0ec6e0e, 0x1698db3b, 0x4c98a0be, 0x3278e964, 0x9f1f9532, 0xe0d392df, 0xd3a0342b, 0x8971f21e, 0x1b0a7441, 0x4ba3348c, 0xc5be7120, 0xc37632d8, 0xdf359f8d, 0x9b992f2e, 0xe60b6f47, 0xfe3f11d, 0xe54cda54, 0x1edad891, 0xce6279cf, 0xcd3e7e6f, 0x1618b166, 0xfd2c1d05, 0x848fd2c5, 0xf6fb2299, 0xf523f357, 0xa6327623, 0x93a83531, 0x56cccd02, 0xacf08162, 0x5a75ebb5, 0x6e163697, 0x88d273cc, 0xde966292, 0x81b949d0, 0x4c50901b, 0x71c65614, 0xe6c6c7bd, 0x327a140a, 0x45e1d006, 0xc3f27b9a, 0xc9aa53fd, 0x62a80f00, 0xbb25bfe2, 0x35bdd2f6, 0x71126905, 0xb2040222, 0xb6cbcf7c, 0xcd769c2b, 0x53113ec0, 0x1640e3d3, 0x38abbd60, 0x2547adf0, 0xba38209c, 0xf746ce76, 0x77afa1c5, 0x20756060, 0x85cbfe4e, 0x8ae88dd8, 0x7aaaf9b0, 0x4cf9aa7e, 0x1948c25c, 0x2fb8a8c, 0x1c36ae4, 0xd6ebe1f9, 0x90d4f869, 0xa65cdea0, 0x3f09252d, 0xc208e69f, 0xb74e6132, 0xce77e25b, 0x578fdfe3, 0x3ac372e6]; + /** + * P-Array consists of 18 32-bit subkeys + * + * @var array + */ + private static $parray = [0x243f6a88, 0x85a308d3, 0x13198a2e, 0x3707344, 0xa4093822, 0x299f31d0, 0x82efa98, 0xec4e6c89, 0x452821e6, 0x38d01377, 0xbe5466cf, 0x34e90c6c, 0xc0ac29b7, 0xc97c50dd, 0x3f84d5b5, 0xb5470917, 0x9216d5d9, 0x8979fb1b]; + /** + * The BCTX-working Array + * + * Holds the expanded key [p] and the key-depended s-boxes [sb] + * + * @var array + */ + private $bctx; + /** + * Holds the last used key + * + * @var array + */ + private $kl; + /** + * The Key Length (in bytes) + * {@internal The max value is 256 / 8 = 32, the min value is 128 / 8 = 16. Exists in conjunction with $Nk + * because the encryption / decryption / key schedule creation requires this number and not $key_length. We could + * derive this from $key_length or vice versa, but that'd mean we'd have to do multiple shift operations, so in lieu + * of that, we'll just precompute it once.} + * + * @see \phpseclib3\Crypt\Common\SymmetricKey::setKeyLength() + * @var int + */ + protected $key_length = 16; + /** + * Default Constructor. + * + * @param string $mode + * @throws \InvalidArgumentException if an invalid / unsupported mode is provided + */ + public function __construct($mode) + { + parent::__construct($mode); + if ($this->mode == self::MODE_STREAM) { + throw new \InvalidArgumentException('Block ciphers cannot be ran in stream mode'); + } + } + /** + * Sets the key length. + * + * Key lengths can be between 32 and 448 bits. + * + * @param int $length + */ + public function setKeyLength($length) + { + if ($length < 32 || $length > 448) { + throw new \LengthException('Key size of ' . $length . ' bits is not supported by this algorithm. Only keys of sizes between 32 and 448 bits are supported'); + } + $this->key_length = $length >> 3; + parent::setKeyLength($length); + } + /** + * Test for engine validity + * + * This is mainly just a wrapper to set things up for \phpseclib3\Crypt\Common\SymmetricKey::isValidEngine() + * + * @see \phpseclib3\Crypt\Common\SymmetricKey::isValidEngine() + * @param int $engine + * @return bool + */ + protected function isValidEngineHelper($engine) + { + if ($engine == self::ENGINE_OPENSSL) { + if ($this->key_length < 16) { + return \false; + } + // quoting https://www.openssl.org/news/openssl-3.0-notes.html, OpenSSL 3.0.1 + // "Moved all variations of the EVP ciphers CAST5, BF, IDEA, SEED, RC2, RC4, RC5, and DES to the legacy provider" + // in theory openssl_get_cipher_methods() should catch this but, on GitHub Actions, at least, it does not + if (\defined('OPENSSL_VERSION_TEXT') && \version_compare(\preg_replace('#OpenSSL (\\d+\\.\\d+\\.\\d+) .*#', '$1', \OPENSSL_VERSION_TEXT), '3.0.1', '>=')) { + return \false; + } + $this->cipher_name_openssl_ecb = 'bf-ecb'; + $this->cipher_name_openssl = 'bf-' . $this->openssl_translate_mode(); + } + return parent::isValidEngineHelper($engine); + } + /** + * Setup the key (expansion) + * + * @see \phpseclib3\Crypt\Common\SymmetricKey::_setupKey() + */ + protected function setupKey() + { + if (isset($this->kl['key']) && $this->key === $this->kl['key']) { + // already expanded + return; + } + $this->kl = ['key' => $this->key]; + /* key-expanding p[] and S-Box building sb[] */ + $this->bctx = ['p' => [], 'sb' => self::$sbox]; + // unpack binary string in unsigned chars + $key = \array_values(\unpack('C*', $this->key)); + $keyl = \count($key); + // with bcrypt $keyl will always be 16 (because the key is the sha512 of the key you provide) + for ($j = 0, $i = 0; $i < 18; ++$i) { + // xor P1 with the first 32-bits of the key, xor P2 with the second 32-bits ... + for ($data = 0, $k = 0; $k < 4; ++$k) { + $data = $data << 8 | $key[$j]; + if (++$j >= $keyl) { + $j = 0; + } + } + $this->bctx['p'][] = self::$parray[$i] ^ \intval($data); + } + // encrypt the zero-string, replace P1 and P2 with the encrypted data, + // encrypt P3 and P4 with the new P1 and P2, do it with all P-array and subkeys + $data = "\x00\x00\x00\x00\x00\x00\x00\x00"; + for ($i = 0; $i < 18; $i += 2) { + list($l, $r) = \array_values(\unpack('N*', $data = $this->encryptBlock($data))); + $this->bctx['p'][$i] = $l; + $this->bctx['p'][$i + 1] = $r; + } + for ($i = 0; $i < 0x400; $i += 0x100) { + for ($j = 0; $j < 256; $j += 2) { + list($l, $r) = \array_values(\unpack('N*', $data = $this->encryptBlock($data))); + $this->bctx['sb'][$i | $j] = $l; + $this->bctx['sb'][$i | $j + 1] = $r; + } + } + } + /** + * Initialize Static Variables + */ + protected static function initialize_static_variables() + { + if (\is_float(self::$sbox[0x200])) { + self::$sbox = \array_map('intval', self::$sbox); + self::$parray = \array_map('intval', self::$parray); + } + parent::initialize_static_variables(); + } + /** + * bcrypt + * + * @param string $sha2pass + * @param string $sha2salt + * @access private + * @return string + */ + private static function bcrypt_hash($sha2pass, $sha2salt) + { + $p = self::$parray; + $sbox = self::$sbox; + $cdata = \array_values(\unpack('N*', 'OxychromaticBlowfishSwatDynamite')); + $sha2pass = \array_values(\unpack('N*', $sha2pass)); + $sha2salt = \array_values(\unpack('N*', $sha2salt)); + self::expandstate($sha2salt, $sha2pass, $sbox, $p); + for ($i = 0; $i < 64; $i++) { + self::expand0state($sha2salt, $sbox, $p); + self::expand0state($sha2pass, $sbox, $p); + } + for ($i = 0; $i < 64; $i++) { + for ($j = 0; $j < 8; $j += 2) { + // count($cdata) == 8 + list($cdata[$j], $cdata[$j + 1]) = self::encryptBlockHelperFast($cdata[$j], $cdata[$j + 1], $sbox, $p); + } + } + return \pack('V*', ...$cdata); + } + /** + * Performs OpenSSH-style bcrypt + * + * @param string $pass + * @param string $salt + * @param int $keylen + * @param int $rounds + * @access public + * @return string + */ + public static function bcrypt_pbkdf($pass, $salt, $keylen, $rounds) + { + self::initialize_static_variables(); + if (\PHP_INT_SIZE == 4) { + throw new \RuntimeException('bcrypt is far too slow to be practical on 32-bit versions of PHP'); + } + $sha2pass = \hash('sha512', $pass, \true); + $results = []; + $count = 1; + while (32 * \count($results) < $keylen) { + $countsalt = $salt . \pack('N', $count++); + $sha2salt = \hash('sha512', $countsalt, \true); + $out = $tmpout = self::bcrypt_hash($sha2pass, $sha2salt); + for ($i = 1; $i < $rounds; $i++) { + $sha2salt = \hash('sha512', $tmpout, \true); + $tmpout = self::bcrypt_hash($sha2pass, $sha2salt); + $out ^= $tmpout; + } + $results[] = $out; + } + $output = ''; + for ($i = 0; $i < 32; $i++) { + foreach ($results as $result) { + $output .= $result[$i]; + } + } + return \substr($output, 0, $keylen); + } + /** + * Key expansion without salt + * + * @access private + * @param int[] $key + * @param int[] $sbox + * @param int[] $p + * @see self::_bcrypt_hash() + */ + private static function expand0state(array $key, array &$sbox, array &$p) + { + // expand0state is basically the same thing as this: + //return self::expandstate(array_fill(0, 16, 0), $key); + // but this separate function eliminates a bunch of XORs and array lookups + $p = [$p[0] ^ $key[0], $p[1] ^ $key[1], $p[2] ^ $key[2], $p[3] ^ $key[3], $p[4] ^ $key[4], $p[5] ^ $key[5], $p[6] ^ $key[6], $p[7] ^ $key[7], $p[8] ^ $key[8], $p[9] ^ $key[9], $p[10] ^ $key[10], $p[11] ^ $key[11], $p[12] ^ $key[12], $p[13] ^ $key[13], $p[14] ^ $key[14], $p[15] ^ $key[15], $p[16] ^ $key[0], $p[17] ^ $key[1]]; + // @codingStandardsIgnoreStart + list($p[0], $p[1]) = self::encryptBlockHelperFast(0, 0, $sbox, $p); + list($p[2], $p[3]) = self::encryptBlockHelperFast($p[0], $p[1], $sbox, $p); + list($p[4], $p[5]) = self::encryptBlockHelperFast($p[2], $p[3], $sbox, $p); + list($p[6], $p[7]) = self::encryptBlockHelperFast($p[4], $p[5], $sbox, $p); + list($p[8], $p[9]) = self::encryptBlockHelperFast($p[6], $p[7], $sbox, $p); + list($p[10], $p[11]) = self::encryptBlockHelperFast($p[8], $p[9], $sbox, $p); + list($p[12], $p[13]) = self::encryptBlockHelperFast($p[10], $p[11], $sbox, $p); + list($p[14], $p[15]) = self::encryptBlockHelperFast($p[12], $p[13], $sbox, $p); + list($p[16], $p[17]) = self::encryptBlockHelperFast($p[14], $p[15], $sbox, $p); + // @codingStandardsIgnoreEnd + list($sbox[0], $sbox[1]) = self::encryptBlockHelperFast($p[16], $p[17], $sbox, $p); + for ($i = 2; $i < 1024; $i += 2) { + list($sbox[$i], $sbox[$i + 1]) = self::encryptBlockHelperFast($sbox[$i - 2], $sbox[$i - 1], $sbox, $p); + } + } + /** + * Key expansion with salt + * + * @access private + * @param int[] $data + * @param int[] $key + * @param int[] $sbox + * @param int[] $p + * @see self::_bcrypt_hash() + */ + private static function expandstate(array $data, array $key, array &$sbox, array &$p) + { + $p = [$p[0] ^ $key[0], $p[1] ^ $key[1], $p[2] ^ $key[2], $p[3] ^ $key[3], $p[4] ^ $key[4], $p[5] ^ $key[5], $p[6] ^ $key[6], $p[7] ^ $key[7], $p[8] ^ $key[8], $p[9] ^ $key[9], $p[10] ^ $key[10], $p[11] ^ $key[11], $p[12] ^ $key[12], $p[13] ^ $key[13], $p[14] ^ $key[14], $p[15] ^ $key[15], $p[16] ^ $key[0], $p[17] ^ $key[1]]; + // @codingStandardsIgnoreStart + list($p[0], $p[1]) = self::encryptBlockHelperFast($data[0], $data[1], $sbox, $p); + list($p[2], $p[3]) = self::encryptBlockHelperFast($data[2] ^ $p[0], $data[3] ^ $p[1], $sbox, $p); + list($p[4], $p[5]) = self::encryptBlockHelperFast($data[4] ^ $p[2], $data[5] ^ $p[3], $sbox, $p); + list($p[6], $p[7]) = self::encryptBlockHelperFast($data[6] ^ $p[4], $data[7] ^ $p[5], $sbox, $p); + list($p[8], $p[9]) = self::encryptBlockHelperFast($data[8] ^ $p[6], $data[9] ^ $p[7], $sbox, $p); + list($p[10], $p[11]) = self::encryptBlockHelperFast($data[10] ^ $p[8], $data[11] ^ $p[9], $sbox, $p); + list($p[12], $p[13]) = self::encryptBlockHelperFast($data[12] ^ $p[10], $data[13] ^ $p[11], $sbox, $p); + list($p[14], $p[15]) = self::encryptBlockHelperFast($data[14] ^ $p[12], $data[15] ^ $p[13], $sbox, $p); + list($p[16], $p[17]) = self::encryptBlockHelperFast($data[0] ^ $p[14], $data[1] ^ $p[15], $sbox, $p); + // @codingStandardsIgnoreEnd + list($sbox[0], $sbox[1]) = self::encryptBlockHelperFast($data[2] ^ $p[16], $data[3] ^ $p[17], $sbox, $p); + for ($i = 2, $j = 4; $i < 1024; $i += 2, $j = ($j + 2) % 16) { + // instead of 16 maybe count($data) would be better? + list($sbox[$i], $sbox[$i + 1]) = self::encryptBlockHelperFast($data[$j] ^ $sbox[$i - 2], $data[$j + 1] ^ $sbox[$i - 1], $sbox, $p); + } + } + /** + * Encrypts a block + * + * @param string $in + * @return string + */ + protected function encryptBlock($in) + { + $p = $this->bctx['p']; + // extract($this->bctx['sb'], EXTR_PREFIX_ALL, 'sb'); // slower + $sb = $this->bctx['sb']; + $in = \unpack('N*', $in); + $l = $in[1]; + $r = $in[2]; + list($r, $l) = \PHP_INT_SIZE == 4 ? self::encryptBlockHelperSlow($l, $r, $sb, $p) : self::encryptBlockHelperFast($l, $r, $sb, $p); + return \pack("N*", $r, $l); + } + /** + * Fast helper function for block encryption + * + * @access private + * @param int $x0 + * @param int $x1 + * @param int[] $sbox + * @param int[] $p + * @return int[] + */ + private static function encryptBlockHelperFast($x0, $x1, array $sbox, array $p) + { + $x0 ^= $p[0]; + $x1 ^= ($sbox[($x0 & 0xff000000) >> 24] + $sbox[0x100 | ($x0 & 0xff0000) >> 16] ^ $sbox[0x200 | ($x0 & 0xff00) >> 8]) + $sbox[0x300 | $x0 & 0xff] ^ $p[1]; + $x0 ^= ($sbox[($x1 & 0xff000000) >> 24] + $sbox[0x100 | ($x1 & 0xff0000) >> 16] ^ $sbox[0x200 | ($x1 & 0xff00) >> 8]) + $sbox[0x300 | $x1 & 0xff] ^ $p[2]; + $x1 ^= ($sbox[($x0 & 0xff000000) >> 24] + $sbox[0x100 | ($x0 & 0xff0000) >> 16] ^ $sbox[0x200 | ($x0 & 0xff00) >> 8]) + $sbox[0x300 | $x0 & 0xff] ^ $p[3]; + $x0 ^= ($sbox[($x1 & 0xff000000) >> 24] + $sbox[0x100 | ($x1 & 0xff0000) >> 16] ^ $sbox[0x200 | ($x1 & 0xff00) >> 8]) + $sbox[0x300 | $x1 & 0xff] ^ $p[4]; + $x1 ^= ($sbox[($x0 & 0xff000000) >> 24] + $sbox[0x100 | ($x0 & 0xff0000) >> 16] ^ $sbox[0x200 | ($x0 & 0xff00) >> 8]) + $sbox[0x300 | $x0 & 0xff] ^ $p[5]; + $x0 ^= ($sbox[($x1 & 0xff000000) >> 24] + $sbox[0x100 | ($x1 & 0xff0000) >> 16] ^ $sbox[0x200 | ($x1 & 0xff00) >> 8]) + $sbox[0x300 | $x1 & 0xff] ^ $p[6]; + $x1 ^= ($sbox[($x0 & 0xff000000) >> 24] + $sbox[0x100 | ($x0 & 0xff0000) >> 16] ^ $sbox[0x200 | ($x0 & 0xff00) >> 8]) + $sbox[0x300 | $x0 & 0xff] ^ $p[7]; + $x0 ^= ($sbox[($x1 & 0xff000000) >> 24] + $sbox[0x100 | ($x1 & 0xff0000) >> 16] ^ $sbox[0x200 | ($x1 & 0xff00) >> 8]) + $sbox[0x300 | $x1 & 0xff] ^ $p[8]; + $x1 ^= ($sbox[($x0 & 0xff000000) >> 24] + $sbox[0x100 | ($x0 & 0xff0000) >> 16] ^ $sbox[0x200 | ($x0 & 0xff00) >> 8]) + $sbox[0x300 | $x0 & 0xff] ^ $p[9]; + $x0 ^= ($sbox[($x1 & 0xff000000) >> 24] + $sbox[0x100 | ($x1 & 0xff0000) >> 16] ^ $sbox[0x200 | ($x1 & 0xff00) >> 8]) + $sbox[0x300 | $x1 & 0xff] ^ $p[10]; + $x1 ^= ($sbox[($x0 & 0xff000000) >> 24] + $sbox[0x100 | ($x0 & 0xff0000) >> 16] ^ $sbox[0x200 | ($x0 & 0xff00) >> 8]) + $sbox[0x300 | $x0 & 0xff] ^ $p[11]; + $x0 ^= ($sbox[($x1 & 0xff000000) >> 24] + $sbox[0x100 | ($x1 & 0xff0000) >> 16] ^ $sbox[0x200 | ($x1 & 0xff00) >> 8]) + $sbox[0x300 | $x1 & 0xff] ^ $p[12]; + $x1 ^= ($sbox[($x0 & 0xff000000) >> 24] + $sbox[0x100 | ($x0 & 0xff0000) >> 16] ^ $sbox[0x200 | ($x0 & 0xff00) >> 8]) + $sbox[0x300 | $x0 & 0xff] ^ $p[13]; + $x0 ^= ($sbox[($x1 & 0xff000000) >> 24] + $sbox[0x100 | ($x1 & 0xff0000) >> 16] ^ $sbox[0x200 | ($x1 & 0xff00) >> 8]) + $sbox[0x300 | $x1 & 0xff] ^ $p[14]; + $x1 ^= ($sbox[($x0 & 0xff000000) >> 24] + $sbox[0x100 | ($x0 & 0xff0000) >> 16] ^ $sbox[0x200 | ($x0 & 0xff00) >> 8]) + $sbox[0x300 | $x0 & 0xff] ^ $p[15]; + $x0 ^= ($sbox[($x1 & 0xff000000) >> 24] + $sbox[0x100 | ($x1 & 0xff0000) >> 16] ^ $sbox[0x200 | ($x1 & 0xff00) >> 8]) + $sbox[0x300 | $x1 & 0xff] ^ $p[16]; + return [$x1 & 0xffffffff ^ $p[17], $x0 & 0xffffffff]; + } + /** + * Slow helper function for block encryption + * + * @access private + * @param int $x0 + * @param int $x1 + * @param int[] $sbox + * @param int[] $p + * @return int[] + */ + private static function encryptBlockHelperSlow($x0, $x1, array $sbox, array $p) + { + // -16777216 == intval(0xFF000000) on 32-bit PHP installs + $x0 ^= $p[0]; + $x1 ^= self::safe_intval((self::safe_intval($sbox[($x0 & -16777216) >> 24 & 0xff] + $sbox[0x100 | ($x0 & 0xff0000) >> 16]) ^ $sbox[0x200 | ($x0 & 0xff00) >> 8]) + $sbox[0x300 | $x0 & 0xff]) ^ $p[1]; + $x0 ^= self::safe_intval((self::safe_intval($sbox[($x1 & -16777216) >> 24 & 0xff] + $sbox[0x100 | ($x1 & 0xff0000) >> 16]) ^ $sbox[0x200 | ($x1 & 0xff00) >> 8]) + $sbox[0x300 | $x1 & 0xff]) ^ $p[2]; + $x1 ^= self::safe_intval((self::safe_intval($sbox[($x0 & -16777216) >> 24 & 0xff] + $sbox[0x100 | ($x0 & 0xff0000) >> 16]) ^ $sbox[0x200 | ($x0 & 0xff00) >> 8]) + $sbox[0x300 | $x0 & 0xff]) ^ $p[3]; + $x0 ^= self::safe_intval((self::safe_intval($sbox[($x1 & -16777216) >> 24 & 0xff] + $sbox[0x100 | ($x1 & 0xff0000) >> 16]) ^ $sbox[0x200 | ($x1 & 0xff00) >> 8]) + $sbox[0x300 | $x1 & 0xff]) ^ $p[4]; + $x1 ^= self::safe_intval((self::safe_intval($sbox[($x0 & -16777216) >> 24 & 0xff] + $sbox[0x100 | ($x0 & 0xff0000) >> 16]) ^ $sbox[0x200 | ($x0 & 0xff00) >> 8]) + $sbox[0x300 | $x0 & 0xff]) ^ $p[5]; + $x0 ^= self::safe_intval((self::safe_intval($sbox[($x1 & -16777216) >> 24 & 0xff] + $sbox[0x100 | ($x1 & 0xff0000) >> 16]) ^ $sbox[0x200 | ($x1 & 0xff00) >> 8]) + $sbox[0x300 | $x1 & 0xff]) ^ $p[6]; + $x1 ^= self::safe_intval((self::safe_intval($sbox[($x0 & -16777216) >> 24 & 0xff] + $sbox[0x100 | ($x0 & 0xff0000) >> 16]) ^ $sbox[0x200 | ($x0 & 0xff00) >> 8]) + $sbox[0x300 | $x0 & 0xff]) ^ $p[7]; + $x0 ^= self::safe_intval((self::safe_intval($sbox[($x1 & -16777216) >> 24 & 0xff] + $sbox[0x100 | ($x1 & 0xff0000) >> 16]) ^ $sbox[0x200 | ($x1 & 0xff00) >> 8]) + $sbox[0x300 | $x1 & 0xff]) ^ $p[8]; + $x1 ^= self::safe_intval((self::safe_intval($sbox[($x0 & -16777216) >> 24 & 0xff] + $sbox[0x100 | ($x0 & 0xff0000) >> 16]) ^ $sbox[0x200 | ($x0 & 0xff00) >> 8]) + $sbox[0x300 | $x0 & 0xff]) ^ $p[9]; + $x0 ^= self::safe_intval((self::safe_intval($sbox[($x1 & -16777216) >> 24 & 0xff] + $sbox[0x100 | ($x1 & 0xff0000) >> 16]) ^ $sbox[0x200 | ($x1 & 0xff00) >> 8]) + $sbox[0x300 | $x1 & 0xff]) ^ $p[10]; + $x1 ^= self::safe_intval((self::safe_intval($sbox[($x0 & -16777216) >> 24 & 0xff] + $sbox[0x100 | ($x0 & 0xff0000) >> 16]) ^ $sbox[0x200 | ($x0 & 0xff00) >> 8]) + $sbox[0x300 | $x0 & 0xff]) ^ $p[11]; + $x0 ^= self::safe_intval((self::safe_intval($sbox[($x1 & -16777216) >> 24 & 0xff] + $sbox[0x100 | ($x1 & 0xff0000) >> 16]) ^ $sbox[0x200 | ($x1 & 0xff00) >> 8]) + $sbox[0x300 | $x1 & 0xff]) ^ $p[12]; + $x1 ^= self::safe_intval((self::safe_intval($sbox[($x0 & -16777216) >> 24 & 0xff] + $sbox[0x100 | ($x0 & 0xff0000) >> 16]) ^ $sbox[0x200 | ($x0 & 0xff00) >> 8]) + $sbox[0x300 | $x0 & 0xff]) ^ $p[13]; + $x0 ^= self::safe_intval((self::safe_intval($sbox[($x1 & -16777216) >> 24 & 0xff] + $sbox[0x100 | ($x1 & 0xff0000) >> 16]) ^ $sbox[0x200 | ($x1 & 0xff00) >> 8]) + $sbox[0x300 | $x1 & 0xff]) ^ $p[14]; + $x1 ^= self::safe_intval((self::safe_intval($sbox[($x0 & -16777216) >> 24 & 0xff] + $sbox[0x100 | ($x0 & 0xff0000) >> 16]) ^ $sbox[0x200 | ($x0 & 0xff00) >> 8]) + $sbox[0x300 | $x0 & 0xff]) ^ $p[15]; + $x0 ^= self::safe_intval((self::safe_intval($sbox[($x1 & -16777216) >> 24 & 0xff] + $sbox[0x100 | ($x1 & 0xff0000) >> 16]) ^ $sbox[0x200 | ($x1 & 0xff00) >> 8]) + $sbox[0x300 | $x1 & 0xff]) ^ $p[16]; + return [$x1 ^ $p[17], $x0]; + } + /** + * Decrypts a block + * + * @param string $in + * @return string + */ + protected function decryptBlock($in) + { + $p = $this->bctx['p']; + $sb = $this->bctx['sb']; + $in = \unpack('N*', $in); + $l = $in[1]; + $r = $in[2]; + for ($i = 17; $i > 2; $i -= 2) { + $l ^= $p[$i]; + $r ^= self::safe_intval((self::safe_intval($sb[$l >> 24 & 0xff] + $sb[0x100 + ($l >> 16 & 0xff)]) ^ $sb[0x200 + ($l >> 8 & 0xff)]) + $sb[0x300 + ($l & 0xff)]); + $r ^= $p[$i - 1]; + $l ^= self::safe_intval((self::safe_intval($sb[$r >> 24 & 0xff] + $sb[0x100 + ($r >> 16 & 0xff)]) ^ $sb[0x200 + ($r >> 8 & 0xff)]) + $sb[0x300 + ($r & 0xff)]); + } + return \pack('N*', $r ^ $p[0], $l ^ $p[1]); + } + /** + * Setup the performance-optimized function for de/encrypt() + * + * @see \phpseclib3\Crypt\Common\SymmetricKey::_setupInlineCrypt() + */ + protected function setupInlineCrypt() + { + $p = $this->bctx['p']; + $init_crypt = ' + static $sb; + if (!$sb) { + $sb = $this->bctx["sb"]; + } + '; + $safeint = self::safe_intval_inline(); + // Generating encrypt code: + $encrypt_block = ' + $in = unpack("N*", $in); + $l = $in[1]; + $r = $in[2]; + '; + for ($i = 0; $i < 16; $i += 2) { + $encrypt_block .= ' + $l^= ' . $p[$i] . '; + $r^= ' . \sprintf($safeint, '(' . \sprintf($safeint, '$sb[$l >> 24 & 0xff] + $sb[0x100 + ($l >> 16 & 0xff)]') . ' ^ + $sb[0x200 + ($l >> 8 & 0xff)]) + + $sb[0x300 + ($l & 0xff)]') . '; + + $r^= ' . $p[$i + 1] . '; + $l^= ' . \sprintf($safeint, '(' . \sprintf($safeint, '$sb[$r >> 24 & 0xff] + $sb[0x100 + ($r >> 16 & 0xff)]') . ' ^ + $sb[0x200 + ($r >> 8 & 0xff)]) + + $sb[0x300 + ($r & 0xff)]') . '; + '; + } + $encrypt_block .= ' + $in = pack("N*", + $r ^ ' . $p[17] . ', + $l ^ ' . $p[16] . ' + ); + '; + // Generating decrypt code: + $decrypt_block = ' + $in = unpack("N*", $in); + $l = $in[1]; + $r = $in[2]; + '; + for ($i = 17; $i > 2; $i -= 2) { + $decrypt_block .= ' + $l^= ' . $p[$i] . '; + $r^= ' . \sprintf($safeint, '(' . \sprintf($safeint, '$sb[$l >> 24 & 0xff] + $sb[0x100 + ($l >> 16 & 0xff)]') . ' ^ + $sb[0x200 + ($l >> 8 & 0xff)]) + + $sb[0x300 + ($l & 0xff)]') . '; + + $r^= ' . $p[$i - 1] . '; + $l^= ' . \sprintf($safeint, '(' . \sprintf($safeint, '$sb[$r >> 24 & 0xff] + $sb[0x100 + ($r >> 16 & 0xff)]') . ' ^ + $sb[0x200 + ($r >> 8 & 0xff)]) + + $sb[0x300 + ($r & 0xff)]') . '; + '; + } + $decrypt_block .= ' + $in = pack("N*", + $r ^ ' . $p[0] . ', + $l ^ ' . $p[1] . ' + ); + '; + $this->inline_crypt = $this->createInlineCryptFunction(['init_crypt' => $init_crypt, 'init_encrypt' => '', 'init_decrypt' => '', 'encrypt_block' => $encrypt_block, 'decrypt_block' => $decrypt_block]); + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/ChaCha20.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/ChaCha20.php new file mode 100644 index 0000000..c74b35e --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/ChaCha20.php @@ -0,0 +1,999 @@ + + * @copyright 2019 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\Crypt; + +use FluentSmtpLib\phpseclib3\Exception\BadDecryptionException; +use FluentSmtpLib\phpseclib3\Exception\InsufficientSetupException; +/** + * Pure-PHP implementation of ChaCha20. + * + * @author Jim Wigginton + */ +class ChaCha20 extends \FluentSmtpLib\phpseclib3\Crypt\Salsa20 +{ + /** + * The OpenSSL specific name of the cipher + * + * @var string + */ + protected $cipher_name_openssl = 'chacha20'; + /** + * Test for engine validity + * + * This is mainly just a wrapper to set things up for \phpseclib3\Crypt\Common\SymmetricKey::isValidEngine() + * + * @see \phpseclib3\Crypt\Common\SymmetricKey::__construct() + * @param int $engine + * @return bool + */ + protected function isValidEngineHelper($engine) + { + switch ($engine) { + case self::ENGINE_LIBSODIUM: + // PHP 7.2.0 (30 Nov 2017) added support for libsodium + // we could probably make it so that if $this->counter == 0 then the first block would be done with either OpenSSL + // or PHP and then subsequent blocks would then be done with libsodium but idk - it's not a high priority atm + // we could also make it so that if $this->counter == 0 and $this->continuousBuffer then do the first string + // with libsodium and subsequent strings with openssl or pure-PHP but again not a high priority + return \function_exists('sodium_crypto_aead_chacha20poly1305_ietf_encrypt') && $this->key_length == 32 && ($this->usePoly1305 && !isset($this->poly1305Key) && $this->counter == 0 || $this->counter == 1) && !$this->continuousBuffer; + case self::ENGINE_OPENSSL: + // OpenSSL 1.1.0 (released 25 Aug 2016) added support for chacha20. + // PHP didn't support OpenSSL 1.1.0 until 7.0.19 (11 May 2017) + // if you attempt to provide openssl with a 128 bit key (as opposed to a 256 bit key) openssl will null + // pad the key to 256 bits and still use the expansion constant for 256-bit keys. the fact that + // openssl treats the IV as both the counter and nonce, however, let's us use openssl in continuous mode + // whereas libsodium does not + if ($this->key_length != 32) { + return \false; + } + } + return parent::isValidEngineHelper($engine); + } + /** + * Encrypts a message. + * + * @see \phpseclib3\Crypt\Common\SymmetricKey::decrypt() + * @see self::crypt() + * @param string $plaintext + * @return string $ciphertext + */ + public function encrypt($plaintext) + { + $this->setup(); + if ($this->engine == self::ENGINE_LIBSODIUM) { + return $this->encrypt_with_libsodium($plaintext); + } + return parent::encrypt($plaintext); + } + /** + * Decrypts a message. + * + * $this->decrypt($this->encrypt($plaintext)) == $this->encrypt($this->encrypt($plaintext)). + * At least if the continuous buffer is disabled. + * + * @see \phpseclib3\Crypt\Common\SymmetricKey::encrypt() + * @see self::crypt() + * @param string $ciphertext + * @return string $plaintext + */ + public function decrypt($ciphertext) + { + $this->setup(); + if ($this->engine == self::ENGINE_LIBSODIUM) { + return $this->decrypt_with_libsodium($ciphertext); + } + return parent::decrypt($ciphertext); + } + /** + * Encrypts a message with libsodium + * + * @see self::encrypt() + * @param string $plaintext + * @return string $text + */ + private function encrypt_with_libsodium($plaintext) + { + $params = [$plaintext, $this->aad, $this->nonce, $this->key]; + $ciphertext = \strlen($this->nonce) == 8 ? \sodium_crypto_aead_chacha20poly1305_encrypt(...$params) : \sodium_crypto_aead_chacha20poly1305_ietf_encrypt(...$params); + if (!$this->usePoly1305) { + return \substr($ciphertext, 0, \strlen($plaintext)); + } + $newciphertext = \substr($ciphertext, 0, \strlen($plaintext)); + $this->newtag = $this->usingGeneratedPoly1305Key && \strlen($this->nonce) == 12 ? \substr($ciphertext, \strlen($plaintext)) : $this->poly1305($newciphertext); + return $newciphertext; + } + /** + * Decrypts a message with libsodium + * + * @see self::decrypt() + * @param string $ciphertext + * @return string $text + */ + private function decrypt_with_libsodium($ciphertext) + { + $params = [$ciphertext, $this->aad, $this->nonce, $this->key]; + if (isset($this->poly1305Key)) { + if ($this->oldtag === \false) { + throw new \FluentSmtpLib\phpseclib3\Exception\InsufficientSetupException('Authentication Tag has not been set'); + } + if ($this->usingGeneratedPoly1305Key && \strlen($this->nonce) == 12) { + $plaintext = \sodium_crypto_aead_chacha20poly1305_ietf_decrypt(...$params); + $this->oldtag = \false; + if ($plaintext === \false) { + throw new \FluentSmtpLib\phpseclib3\Exception\BadDecryptionException('Derived authentication tag and supplied authentication tag do not match'); + } + return $plaintext; + } + $newtag = $this->poly1305($ciphertext); + if ($this->oldtag != \substr($newtag, 0, \strlen($this->oldtag))) { + $this->oldtag = \false; + throw new \FluentSmtpLib\phpseclib3\Exception\BadDecryptionException('Derived authentication tag and supplied authentication tag do not match'); + } + $this->oldtag = \false; + } + $plaintext = \strlen($this->nonce) == 8 ? \sodium_crypto_aead_chacha20poly1305_encrypt(...$params) : \sodium_crypto_aead_chacha20poly1305_ietf_encrypt(...$params); + return \substr($plaintext, 0, \strlen($ciphertext)); + } + /** + * Sets the nonce. + * + * @param string $nonce + */ + public function setNonce($nonce) + { + if (!\is_string($nonce)) { + throw new \UnexpectedValueException('The nonce should be a string'); + } + /* + from https://tools.ietf.org/html/rfc7539#page-7 + + "Note also that the original ChaCha had a 64-bit nonce and 64-bit + block count. We have modified this here to be more consistent with + recommendations in Section 3.2 of [RFC5116]." + */ + switch (\strlen($nonce)) { + case 8: + // 64 bits + case 12: + // 96 bits + break; + default: + throw new \LengthException('Nonce of size ' . \strlen($nonce) . ' not supported by this algorithm. Only 64-bit nonces or 96-bit nonces are supported'); + } + $this->nonce = $nonce; + $this->changed = \true; + $this->setEngine(); + } + /** + * Setup the self::ENGINE_INTERNAL $engine + * + * (re)init, if necessary, the internal cipher $engine + * + * _setup() will be called each time if $changed === true + * typically this happens when using one or more of following public methods: + * + * - setKey() + * + * - setNonce() + * + * - First run of encrypt() / decrypt() with no init-settings + * + * @see self::setKey() + * @see self::setNonce() + * @see self::disableContinuousBuffer() + */ + protected function setup() + { + if (!$this->changed) { + return; + } + $this->enbuffer = $this->debuffer = ['ciphertext' => '', 'counter' => $this->counter]; + $this->changed = $this->nonIVChanged = \false; + if ($this->nonce === \false) { + throw new \FluentSmtpLib\phpseclib3\Exception\InsufficientSetupException('No nonce has been defined'); + } + if ($this->key === \false) { + throw new \FluentSmtpLib\phpseclib3\Exception\InsufficientSetupException('No key has been defined'); + } + if ($this->usePoly1305 && !isset($this->poly1305Key)) { + $this->usingGeneratedPoly1305Key = \true; + if ($this->engine == self::ENGINE_LIBSODIUM) { + return; + } + $this->createPoly1305Key(); + } + $key = $this->key; + if (\strlen($key) == 16) { + $constant = 'expand 16-byte k'; + $key .= $key; + } else { + $constant = 'expand 32-byte k'; + } + $this->p1 = $constant . $key; + $this->p2 = $this->nonce; + if (\strlen($this->nonce) == 8) { + $this->p2 = "\x00\x00\x00\x00" . $this->p2; + } + } + /** + * The quarterround function + * + * @param int $a + * @param int $b + * @param int $c + * @param int $d + */ + protected static function quarterRound(&$a, &$b, &$c, &$d) + { + // in https://datatracker.ietf.org/doc/html/rfc7539#section-2.1 the addition, + // xor'ing and rotation are all on the same line so i'm keeping it on the same + // line here as well + // @codingStandardsIgnoreStart + $a += $b; + $d = self::leftRotate(\intval($d) ^ \intval($a), 16); + $c += $d; + $b = self::leftRotate(\intval($b) ^ \intval($c), 12); + $a += $b; + $d = self::leftRotate(\intval($d) ^ \intval($a), 8); + $c += $d; + $b = self::leftRotate(\intval($b) ^ \intval($c), 7); + // @codingStandardsIgnoreEnd + } + /** + * The doubleround function + * + * @param int $x0 (by reference) + * @param int $x1 (by reference) + * @param int $x2 (by reference) + * @param int $x3 (by reference) + * @param int $x4 (by reference) + * @param int $x5 (by reference) + * @param int $x6 (by reference) + * @param int $x7 (by reference) + * @param int $x8 (by reference) + * @param int $x9 (by reference) + * @param int $x10 (by reference) + * @param int $x11 (by reference) + * @param int $x12 (by reference) + * @param int $x13 (by reference) + * @param int $x14 (by reference) + * @param int $x15 (by reference) + */ + protected static function doubleRound(&$x0, &$x1, &$x2, &$x3, &$x4, &$x5, &$x6, &$x7, &$x8, &$x9, &$x10, &$x11, &$x12, &$x13, &$x14, &$x15) + { + // columnRound + static::quarterRound($x0, $x4, $x8, $x12); + static::quarterRound($x1, $x5, $x9, $x13); + static::quarterRound($x2, $x6, $x10, $x14); + static::quarterRound($x3, $x7, $x11, $x15); + // rowRound + static::quarterRound($x0, $x5, $x10, $x15); + static::quarterRound($x1, $x6, $x11, $x12); + static::quarterRound($x2, $x7, $x8, $x13); + static::quarterRound($x3, $x4, $x9, $x14); + } + /** + * The Salsa20 hash function function + * + * On my laptop this loop unrolled / function dereferenced version of parent::salsa20 encrypts 1mb of text in + * 0.65s vs the 0.85s that it takes with the parent method. + * + * If we were free to assume that the host OS would always be 64-bits then the if condition in leftRotate could + * be eliminated and we could knock this done to 0.60s. + * + * For comparison purposes, RC4 takes 0.16s and AES in CTR mode with the Eval engine takes 0.48s. + * AES in CTR mode with the PHP engine takes 1.19s. Salsa20 / ChaCha20 do not benefit as much from the Eval + * approach due to the fact that there are a lot less variables to de-reference, fewer loops to unroll, etc + * + * @param string $x + */ + protected static function salsa20($x) + { + list(, $x0, $x1, $x2, $x3, $x4, $x5, $x6, $x7, $x8, $x9, $x10, $x11, $x12, $x13, $x14, $x15) = \unpack('V*', $x); + $z0 = $x0; + $z1 = $x1; + $z2 = $x2; + $z3 = $x3; + $z4 = $x4; + $z5 = $x5; + $z6 = $x6; + $z7 = $x7; + $z8 = $x8; + $z9 = $x9; + $z10 = $x10; + $z11 = $x11; + $z12 = $x12; + $z13 = $x13; + $z14 = $x14; + $z15 = $x15; + // @codingStandardsIgnoreStart + // columnRound + $x0 += $x4; + $x12 = self::leftRotate(\intval($x12) ^ \intval($x0), 16); + $x8 += $x12; + $x4 = self::leftRotate(\intval($x4) ^ \intval($x8), 12); + $x0 += $x4; + $x12 = self::leftRotate(\intval($x12) ^ \intval($x0), 8); + $x8 += $x12; + $x4 = self::leftRotate(\intval($x4) ^ \intval($x8), 7); + $x1 += $x5; + $x13 = self::leftRotate(\intval($x13) ^ \intval($x1), 16); + $x9 += $x13; + $x5 = self::leftRotate(\intval($x5) ^ \intval($x9), 12); + $x1 += $x5; + $x13 = self::leftRotate(\intval($x13) ^ \intval($x1), 8); + $x9 += $x13; + $x5 = self::leftRotate(\intval($x5) ^ \intval($x9), 7); + $x2 += $x6; + $x14 = self::leftRotate(\intval($x14) ^ \intval($x2), 16); + $x10 += $x14; + $x6 = self::leftRotate(\intval($x6) ^ \intval($x10), 12); + $x2 += $x6; + $x14 = self::leftRotate(\intval($x14) ^ \intval($x2), 8); + $x10 += $x14; + $x6 = self::leftRotate(\intval($x6) ^ \intval($x10), 7); + $x3 += $x7; + $x15 = self::leftRotate(\intval($x15) ^ \intval($x3), 16); + $x11 += $x15; + $x7 = self::leftRotate(\intval($x7) ^ \intval($x11), 12); + $x3 += $x7; + $x15 = self::leftRotate(\intval($x15) ^ \intval($x3), 8); + $x11 += $x15; + $x7 = self::leftRotate(\intval($x7) ^ \intval($x11), 7); + // rowRound + $x0 += $x5; + $x15 = self::leftRotate(\intval($x15) ^ \intval($x0), 16); + $x10 += $x15; + $x5 = self::leftRotate(\intval($x5) ^ \intval($x10), 12); + $x0 += $x5; + $x15 = self::leftRotate(\intval($x15) ^ \intval($x0), 8); + $x10 += $x15; + $x5 = self::leftRotate(\intval($x5) ^ \intval($x10), 7); + $x1 += $x6; + $x12 = self::leftRotate(\intval($x12) ^ \intval($x1), 16); + $x11 += $x12; + $x6 = self::leftRotate(\intval($x6) ^ \intval($x11), 12); + $x1 += $x6; + $x12 = self::leftRotate(\intval($x12) ^ \intval($x1), 8); + $x11 += $x12; + $x6 = self::leftRotate(\intval($x6) ^ \intval($x11), 7); + $x2 += $x7; + $x13 = self::leftRotate(\intval($x13) ^ \intval($x2), 16); + $x8 += $x13; + $x7 = self::leftRotate(\intval($x7) ^ \intval($x8), 12); + $x2 += $x7; + $x13 = self::leftRotate(\intval($x13) ^ \intval($x2), 8); + $x8 += $x13; + $x7 = self::leftRotate(\intval($x7) ^ \intval($x8), 7); + $x3 += $x4; + $x14 = self::leftRotate(\intval($x14) ^ \intval($x3), 16); + $x9 += $x14; + $x4 = self::leftRotate(\intval($x4) ^ \intval($x9), 12); + $x3 += $x4; + $x14 = self::leftRotate(\intval($x14) ^ \intval($x3), 8); + $x9 += $x14; + $x4 = self::leftRotate(\intval($x4) ^ \intval($x9), 7); + // columnRound + $x0 += $x4; + $x12 = self::leftRotate(\intval($x12) ^ \intval($x0), 16); + $x8 += $x12; + $x4 = self::leftRotate(\intval($x4) ^ \intval($x8), 12); + $x0 += $x4; + $x12 = self::leftRotate(\intval($x12) ^ \intval($x0), 8); + $x8 += $x12; + $x4 = self::leftRotate(\intval($x4) ^ \intval($x8), 7); + $x1 += $x5; + $x13 = self::leftRotate(\intval($x13) ^ \intval($x1), 16); + $x9 += $x13; + $x5 = self::leftRotate(\intval($x5) ^ \intval($x9), 12); + $x1 += $x5; + $x13 = self::leftRotate(\intval($x13) ^ \intval($x1), 8); + $x9 += $x13; + $x5 = self::leftRotate(\intval($x5) ^ \intval($x9), 7); + $x2 += $x6; + $x14 = self::leftRotate(\intval($x14) ^ \intval($x2), 16); + $x10 += $x14; + $x6 = self::leftRotate(\intval($x6) ^ \intval($x10), 12); + $x2 += $x6; + $x14 = self::leftRotate(\intval($x14) ^ \intval($x2), 8); + $x10 += $x14; + $x6 = self::leftRotate(\intval($x6) ^ \intval($x10), 7); + $x3 += $x7; + $x15 = self::leftRotate(\intval($x15) ^ \intval($x3), 16); + $x11 += $x15; + $x7 = self::leftRotate(\intval($x7) ^ \intval($x11), 12); + $x3 += $x7; + $x15 = self::leftRotate(\intval($x15) ^ \intval($x3), 8); + $x11 += $x15; + $x7 = self::leftRotate(\intval($x7) ^ \intval($x11), 7); + // rowRound + $x0 += $x5; + $x15 = self::leftRotate(\intval($x15) ^ \intval($x0), 16); + $x10 += $x15; + $x5 = self::leftRotate(\intval($x5) ^ \intval($x10), 12); + $x0 += $x5; + $x15 = self::leftRotate(\intval($x15) ^ \intval($x0), 8); + $x10 += $x15; + $x5 = self::leftRotate(\intval($x5) ^ \intval($x10), 7); + $x1 += $x6; + $x12 = self::leftRotate(\intval($x12) ^ \intval($x1), 16); + $x11 += $x12; + $x6 = self::leftRotate(\intval($x6) ^ \intval($x11), 12); + $x1 += $x6; + $x12 = self::leftRotate(\intval($x12) ^ \intval($x1), 8); + $x11 += $x12; + $x6 = self::leftRotate(\intval($x6) ^ \intval($x11), 7); + $x2 += $x7; + $x13 = self::leftRotate(\intval($x13) ^ \intval($x2), 16); + $x8 += $x13; + $x7 = self::leftRotate(\intval($x7) ^ \intval($x8), 12); + $x2 += $x7; + $x13 = self::leftRotate(\intval($x13) ^ \intval($x2), 8); + $x8 += $x13; + $x7 = self::leftRotate(\intval($x7) ^ \intval($x8), 7); + $x3 += $x4; + $x14 = self::leftRotate(\intval($x14) ^ \intval($x3), 16); + $x9 += $x14; + $x4 = self::leftRotate(\intval($x4) ^ \intval($x9), 12); + $x3 += $x4; + $x14 = self::leftRotate(\intval($x14) ^ \intval($x3), 8); + $x9 += $x14; + $x4 = self::leftRotate(\intval($x4) ^ \intval($x9), 7); + // columnRound + $x0 += $x4; + $x12 = self::leftRotate(\intval($x12) ^ \intval($x0), 16); + $x8 += $x12; + $x4 = self::leftRotate(\intval($x4) ^ \intval($x8), 12); + $x0 += $x4; + $x12 = self::leftRotate(\intval($x12) ^ \intval($x0), 8); + $x8 += $x12; + $x4 = self::leftRotate(\intval($x4) ^ \intval($x8), 7); + $x1 += $x5; + $x13 = self::leftRotate(\intval($x13) ^ \intval($x1), 16); + $x9 += $x13; + $x5 = self::leftRotate(\intval($x5) ^ \intval($x9), 12); + $x1 += $x5; + $x13 = self::leftRotate(\intval($x13) ^ \intval($x1), 8); + $x9 += $x13; + $x5 = self::leftRotate(\intval($x5) ^ \intval($x9), 7); + $x2 += $x6; + $x14 = self::leftRotate(\intval($x14) ^ \intval($x2), 16); + $x10 += $x14; + $x6 = self::leftRotate(\intval($x6) ^ \intval($x10), 12); + $x2 += $x6; + $x14 = self::leftRotate(\intval($x14) ^ \intval($x2), 8); + $x10 += $x14; + $x6 = self::leftRotate(\intval($x6) ^ \intval($x10), 7); + $x3 += $x7; + $x15 = self::leftRotate(\intval($x15) ^ \intval($x3), 16); + $x11 += $x15; + $x7 = self::leftRotate(\intval($x7) ^ \intval($x11), 12); + $x3 += $x7; + $x15 = self::leftRotate(\intval($x15) ^ \intval($x3), 8); + $x11 += $x15; + $x7 = self::leftRotate(\intval($x7) ^ \intval($x11), 7); + // rowRound + $x0 += $x5; + $x15 = self::leftRotate(\intval($x15) ^ \intval($x0), 16); + $x10 += $x15; + $x5 = self::leftRotate(\intval($x5) ^ \intval($x10), 12); + $x0 += $x5; + $x15 = self::leftRotate(\intval($x15) ^ \intval($x0), 8); + $x10 += $x15; + $x5 = self::leftRotate(\intval($x5) ^ \intval($x10), 7); + $x1 += $x6; + $x12 = self::leftRotate(\intval($x12) ^ \intval($x1), 16); + $x11 += $x12; + $x6 = self::leftRotate(\intval($x6) ^ \intval($x11), 12); + $x1 += $x6; + $x12 = self::leftRotate(\intval($x12) ^ \intval($x1), 8); + $x11 += $x12; + $x6 = self::leftRotate(\intval($x6) ^ \intval($x11), 7); + $x2 += $x7; + $x13 = self::leftRotate(\intval($x13) ^ \intval($x2), 16); + $x8 += $x13; + $x7 = self::leftRotate(\intval($x7) ^ \intval($x8), 12); + $x2 += $x7; + $x13 = self::leftRotate(\intval($x13) ^ \intval($x2), 8); + $x8 += $x13; + $x7 = self::leftRotate(\intval($x7) ^ \intval($x8), 7); + $x3 += $x4; + $x14 = self::leftRotate(\intval($x14) ^ \intval($x3), 16); + $x9 += $x14; + $x4 = self::leftRotate(\intval($x4) ^ \intval($x9), 12); + $x3 += $x4; + $x14 = self::leftRotate(\intval($x14) ^ \intval($x3), 8); + $x9 += $x14; + $x4 = self::leftRotate(\intval($x4) ^ \intval($x9), 7); + // columnRound + $x0 += $x4; + $x12 = self::leftRotate(\intval($x12) ^ \intval($x0), 16); + $x8 += $x12; + $x4 = self::leftRotate(\intval($x4) ^ \intval($x8), 12); + $x0 += $x4; + $x12 = self::leftRotate(\intval($x12) ^ \intval($x0), 8); + $x8 += $x12; + $x4 = self::leftRotate(\intval($x4) ^ \intval($x8), 7); + $x1 += $x5; + $x13 = self::leftRotate(\intval($x13) ^ \intval($x1), 16); + $x9 += $x13; + $x5 = self::leftRotate(\intval($x5) ^ \intval($x9), 12); + $x1 += $x5; + $x13 = self::leftRotate(\intval($x13) ^ \intval($x1), 8); + $x9 += $x13; + $x5 = self::leftRotate(\intval($x5) ^ \intval($x9), 7); + $x2 += $x6; + $x14 = self::leftRotate(\intval($x14) ^ \intval($x2), 16); + $x10 += $x14; + $x6 = self::leftRotate(\intval($x6) ^ \intval($x10), 12); + $x2 += $x6; + $x14 = self::leftRotate(\intval($x14) ^ \intval($x2), 8); + $x10 += $x14; + $x6 = self::leftRotate(\intval($x6) ^ \intval($x10), 7); + $x3 += $x7; + $x15 = self::leftRotate(\intval($x15) ^ \intval($x3), 16); + $x11 += $x15; + $x7 = self::leftRotate(\intval($x7) ^ \intval($x11), 12); + $x3 += $x7; + $x15 = self::leftRotate(\intval($x15) ^ \intval($x3), 8); + $x11 += $x15; + $x7 = self::leftRotate(\intval($x7) ^ \intval($x11), 7); + // rowRound + $x0 += $x5; + $x15 = self::leftRotate(\intval($x15) ^ \intval($x0), 16); + $x10 += $x15; + $x5 = self::leftRotate(\intval($x5) ^ \intval($x10), 12); + $x0 += $x5; + $x15 = self::leftRotate(\intval($x15) ^ \intval($x0), 8); + $x10 += $x15; + $x5 = self::leftRotate(\intval($x5) ^ \intval($x10), 7); + $x1 += $x6; + $x12 = self::leftRotate(\intval($x12) ^ \intval($x1), 16); + $x11 += $x12; + $x6 = self::leftRotate(\intval($x6) ^ \intval($x11), 12); + $x1 += $x6; + $x12 = self::leftRotate(\intval($x12) ^ \intval($x1), 8); + $x11 += $x12; + $x6 = self::leftRotate(\intval($x6) ^ \intval($x11), 7); + $x2 += $x7; + $x13 = self::leftRotate(\intval($x13) ^ \intval($x2), 16); + $x8 += $x13; + $x7 = self::leftRotate(\intval($x7) ^ \intval($x8), 12); + $x2 += $x7; + $x13 = self::leftRotate(\intval($x13) ^ \intval($x2), 8); + $x8 += $x13; + $x7 = self::leftRotate(\intval($x7) ^ \intval($x8), 7); + $x3 += $x4; + $x14 = self::leftRotate(\intval($x14) ^ \intval($x3), 16); + $x9 += $x14; + $x4 = self::leftRotate(\intval($x4) ^ \intval($x9), 12); + $x3 += $x4; + $x14 = self::leftRotate(\intval($x14) ^ \intval($x3), 8); + $x9 += $x14; + $x4 = self::leftRotate(\intval($x4) ^ \intval($x9), 7); + // columnRound + $x0 += $x4; + $x12 = self::leftRotate(\intval($x12) ^ \intval($x0), 16); + $x8 += $x12; + $x4 = self::leftRotate(\intval($x4) ^ \intval($x8), 12); + $x0 += $x4; + $x12 = self::leftRotate(\intval($x12) ^ \intval($x0), 8); + $x8 += $x12; + $x4 = self::leftRotate(\intval($x4) ^ \intval($x8), 7); + $x1 += $x5; + $x13 = self::leftRotate(\intval($x13) ^ \intval($x1), 16); + $x9 += $x13; + $x5 = self::leftRotate(\intval($x5) ^ \intval($x9), 12); + $x1 += $x5; + $x13 = self::leftRotate(\intval($x13) ^ \intval($x1), 8); + $x9 += $x13; + $x5 = self::leftRotate(\intval($x5) ^ \intval($x9), 7); + $x2 += $x6; + $x14 = self::leftRotate(\intval($x14) ^ \intval($x2), 16); + $x10 += $x14; + $x6 = self::leftRotate(\intval($x6) ^ \intval($x10), 12); + $x2 += $x6; + $x14 = self::leftRotate(\intval($x14) ^ \intval($x2), 8); + $x10 += $x14; + $x6 = self::leftRotate(\intval($x6) ^ \intval($x10), 7); + $x3 += $x7; + $x15 = self::leftRotate(\intval($x15) ^ \intval($x3), 16); + $x11 += $x15; + $x7 = self::leftRotate(\intval($x7) ^ \intval($x11), 12); + $x3 += $x7; + $x15 = self::leftRotate(\intval($x15) ^ \intval($x3), 8); + $x11 += $x15; + $x7 = self::leftRotate(\intval($x7) ^ \intval($x11), 7); + // rowRound + $x0 += $x5; + $x15 = self::leftRotate(\intval($x15) ^ \intval($x0), 16); + $x10 += $x15; + $x5 = self::leftRotate(\intval($x5) ^ \intval($x10), 12); + $x0 += $x5; + $x15 = self::leftRotate(\intval($x15) ^ \intval($x0), 8); + $x10 += $x15; + $x5 = self::leftRotate(\intval($x5) ^ \intval($x10), 7); + $x1 += $x6; + $x12 = self::leftRotate(\intval($x12) ^ \intval($x1), 16); + $x11 += $x12; + $x6 = self::leftRotate(\intval($x6) ^ \intval($x11), 12); + $x1 += $x6; + $x12 = self::leftRotate(\intval($x12) ^ \intval($x1), 8); + $x11 += $x12; + $x6 = self::leftRotate(\intval($x6) ^ \intval($x11), 7); + $x2 += $x7; + $x13 = self::leftRotate(\intval($x13) ^ \intval($x2), 16); + $x8 += $x13; + $x7 = self::leftRotate(\intval($x7) ^ \intval($x8), 12); + $x2 += $x7; + $x13 = self::leftRotate(\intval($x13) ^ \intval($x2), 8); + $x8 += $x13; + $x7 = self::leftRotate(\intval($x7) ^ \intval($x8), 7); + $x3 += $x4; + $x14 = self::leftRotate(\intval($x14) ^ \intval($x3), 16); + $x9 += $x14; + $x4 = self::leftRotate(\intval($x4) ^ \intval($x9), 12); + $x3 += $x4; + $x14 = self::leftRotate(\intval($x14) ^ \intval($x3), 8); + $x9 += $x14; + $x4 = self::leftRotate(\intval($x4) ^ \intval($x9), 7); + // columnRound + $x0 += $x4; + $x12 = self::leftRotate(\intval($x12) ^ \intval($x0), 16); + $x8 += $x12; + $x4 = self::leftRotate(\intval($x4) ^ \intval($x8), 12); + $x0 += $x4; + $x12 = self::leftRotate(\intval($x12) ^ \intval($x0), 8); + $x8 += $x12; + $x4 = self::leftRotate(\intval($x4) ^ \intval($x8), 7); + $x1 += $x5; + $x13 = self::leftRotate(\intval($x13) ^ \intval($x1), 16); + $x9 += $x13; + $x5 = self::leftRotate(\intval($x5) ^ \intval($x9), 12); + $x1 += $x5; + $x13 = self::leftRotate(\intval($x13) ^ \intval($x1), 8); + $x9 += $x13; + $x5 = self::leftRotate(\intval($x5) ^ \intval($x9), 7); + $x2 += $x6; + $x14 = self::leftRotate(\intval($x14) ^ \intval($x2), 16); + $x10 += $x14; + $x6 = self::leftRotate(\intval($x6) ^ \intval($x10), 12); + $x2 += $x6; + $x14 = self::leftRotate(\intval($x14) ^ \intval($x2), 8); + $x10 += $x14; + $x6 = self::leftRotate(\intval($x6) ^ \intval($x10), 7); + $x3 += $x7; + $x15 = self::leftRotate(\intval($x15) ^ \intval($x3), 16); + $x11 += $x15; + $x7 = self::leftRotate(\intval($x7) ^ \intval($x11), 12); + $x3 += $x7; + $x15 = self::leftRotate(\intval($x15) ^ \intval($x3), 8); + $x11 += $x15; + $x7 = self::leftRotate(\intval($x7) ^ \intval($x11), 7); + // rowRound + $x0 += $x5; + $x15 = self::leftRotate(\intval($x15) ^ \intval($x0), 16); + $x10 += $x15; + $x5 = self::leftRotate(\intval($x5) ^ \intval($x10), 12); + $x0 += $x5; + $x15 = self::leftRotate(\intval($x15) ^ \intval($x0), 8); + $x10 += $x15; + $x5 = self::leftRotate(\intval($x5) ^ \intval($x10), 7); + $x1 += $x6; + $x12 = self::leftRotate(\intval($x12) ^ \intval($x1), 16); + $x11 += $x12; + $x6 = self::leftRotate(\intval($x6) ^ \intval($x11), 12); + $x1 += $x6; + $x12 = self::leftRotate(\intval($x12) ^ \intval($x1), 8); + $x11 += $x12; + $x6 = self::leftRotate(\intval($x6) ^ \intval($x11), 7); + $x2 += $x7; + $x13 = self::leftRotate(\intval($x13) ^ \intval($x2), 16); + $x8 += $x13; + $x7 = self::leftRotate(\intval($x7) ^ \intval($x8), 12); + $x2 += $x7; + $x13 = self::leftRotate(\intval($x13) ^ \intval($x2), 8); + $x8 += $x13; + $x7 = self::leftRotate(\intval($x7) ^ \intval($x8), 7); + $x3 += $x4; + $x14 = self::leftRotate(\intval($x14) ^ \intval($x3), 16); + $x9 += $x14; + $x4 = self::leftRotate(\intval($x4) ^ \intval($x9), 12); + $x3 += $x4; + $x14 = self::leftRotate(\intval($x14) ^ \intval($x3), 8); + $x9 += $x14; + $x4 = self::leftRotate(\intval($x4) ^ \intval($x9), 7); + // columnRound + $x0 += $x4; + $x12 = self::leftRotate(\intval($x12) ^ \intval($x0), 16); + $x8 += $x12; + $x4 = self::leftRotate(\intval($x4) ^ \intval($x8), 12); + $x0 += $x4; + $x12 = self::leftRotate(\intval($x12) ^ \intval($x0), 8); + $x8 += $x12; + $x4 = self::leftRotate(\intval($x4) ^ \intval($x8), 7); + $x1 += $x5; + $x13 = self::leftRotate(\intval($x13) ^ \intval($x1), 16); + $x9 += $x13; + $x5 = self::leftRotate(\intval($x5) ^ \intval($x9), 12); + $x1 += $x5; + $x13 = self::leftRotate(\intval($x13) ^ \intval($x1), 8); + $x9 += $x13; + $x5 = self::leftRotate(\intval($x5) ^ \intval($x9), 7); + $x2 += $x6; + $x14 = self::leftRotate(\intval($x14) ^ \intval($x2), 16); + $x10 += $x14; + $x6 = self::leftRotate(\intval($x6) ^ \intval($x10), 12); + $x2 += $x6; + $x14 = self::leftRotate(\intval($x14) ^ \intval($x2), 8); + $x10 += $x14; + $x6 = self::leftRotate(\intval($x6) ^ \intval($x10), 7); + $x3 += $x7; + $x15 = self::leftRotate(\intval($x15) ^ \intval($x3), 16); + $x11 += $x15; + $x7 = self::leftRotate(\intval($x7) ^ \intval($x11), 12); + $x3 += $x7; + $x15 = self::leftRotate(\intval($x15) ^ \intval($x3), 8); + $x11 += $x15; + $x7 = self::leftRotate(\intval($x7) ^ \intval($x11), 7); + // rowRound + $x0 += $x5; + $x15 = self::leftRotate(\intval($x15) ^ \intval($x0), 16); + $x10 += $x15; + $x5 = self::leftRotate(\intval($x5) ^ \intval($x10), 12); + $x0 += $x5; + $x15 = self::leftRotate(\intval($x15) ^ \intval($x0), 8); + $x10 += $x15; + $x5 = self::leftRotate(\intval($x5) ^ \intval($x10), 7); + $x1 += $x6; + $x12 = self::leftRotate(\intval($x12) ^ \intval($x1), 16); + $x11 += $x12; + $x6 = self::leftRotate(\intval($x6) ^ \intval($x11), 12); + $x1 += $x6; + $x12 = self::leftRotate(\intval($x12) ^ \intval($x1), 8); + $x11 += $x12; + $x6 = self::leftRotate(\intval($x6) ^ \intval($x11), 7); + $x2 += $x7; + $x13 = self::leftRotate(\intval($x13) ^ \intval($x2), 16); + $x8 += $x13; + $x7 = self::leftRotate(\intval($x7) ^ \intval($x8), 12); + $x2 += $x7; + $x13 = self::leftRotate(\intval($x13) ^ \intval($x2), 8); + $x8 += $x13; + $x7 = self::leftRotate(\intval($x7) ^ \intval($x8), 7); + $x3 += $x4; + $x14 = self::leftRotate(\intval($x14) ^ \intval($x3), 16); + $x9 += $x14; + $x4 = self::leftRotate(\intval($x4) ^ \intval($x9), 12); + $x3 += $x4; + $x14 = self::leftRotate(\intval($x14) ^ \intval($x3), 8); + $x9 += $x14; + $x4 = self::leftRotate(\intval($x4) ^ \intval($x9), 7); + // columnRound + $x0 += $x4; + $x12 = self::leftRotate(\intval($x12) ^ \intval($x0), 16); + $x8 += $x12; + $x4 = self::leftRotate(\intval($x4) ^ \intval($x8), 12); + $x0 += $x4; + $x12 = self::leftRotate(\intval($x12) ^ \intval($x0), 8); + $x8 += $x12; + $x4 = self::leftRotate(\intval($x4) ^ \intval($x8), 7); + $x1 += $x5; + $x13 = self::leftRotate(\intval($x13) ^ \intval($x1), 16); + $x9 += $x13; + $x5 = self::leftRotate(\intval($x5) ^ \intval($x9), 12); + $x1 += $x5; + $x13 = self::leftRotate(\intval($x13) ^ \intval($x1), 8); + $x9 += $x13; + $x5 = self::leftRotate(\intval($x5) ^ \intval($x9), 7); + $x2 += $x6; + $x14 = self::leftRotate(\intval($x14) ^ \intval($x2), 16); + $x10 += $x14; + $x6 = self::leftRotate(\intval($x6) ^ \intval($x10), 12); + $x2 += $x6; + $x14 = self::leftRotate(\intval($x14) ^ \intval($x2), 8); + $x10 += $x14; + $x6 = self::leftRotate(\intval($x6) ^ \intval($x10), 7); + $x3 += $x7; + $x15 = self::leftRotate(\intval($x15) ^ \intval($x3), 16); + $x11 += $x15; + $x7 = self::leftRotate(\intval($x7) ^ \intval($x11), 12); + $x3 += $x7; + $x15 = self::leftRotate(\intval($x15) ^ \intval($x3), 8); + $x11 += $x15; + $x7 = self::leftRotate(\intval($x7) ^ \intval($x11), 7); + // rowRound + $x0 += $x5; + $x15 = self::leftRotate(\intval($x15) ^ \intval($x0), 16); + $x10 += $x15; + $x5 = self::leftRotate(\intval($x5) ^ \intval($x10), 12); + $x0 += $x5; + $x15 = self::leftRotate(\intval($x15) ^ \intval($x0), 8); + $x10 += $x15; + $x5 = self::leftRotate(\intval($x5) ^ \intval($x10), 7); + $x1 += $x6; + $x12 = self::leftRotate(\intval($x12) ^ \intval($x1), 16); + $x11 += $x12; + $x6 = self::leftRotate(\intval($x6) ^ \intval($x11), 12); + $x1 += $x6; + $x12 = self::leftRotate(\intval($x12) ^ \intval($x1), 8); + $x11 += $x12; + $x6 = self::leftRotate(\intval($x6) ^ \intval($x11), 7); + $x2 += $x7; + $x13 = self::leftRotate(\intval($x13) ^ \intval($x2), 16); + $x8 += $x13; + $x7 = self::leftRotate(\intval($x7) ^ \intval($x8), 12); + $x2 += $x7; + $x13 = self::leftRotate(\intval($x13) ^ \intval($x2), 8); + $x8 += $x13; + $x7 = self::leftRotate(\intval($x7) ^ \intval($x8), 7); + $x3 += $x4; + $x14 = self::leftRotate(\intval($x14) ^ \intval($x3), 16); + $x9 += $x14; + $x4 = self::leftRotate(\intval($x4) ^ \intval($x9), 12); + $x3 += $x4; + $x14 = self::leftRotate(\intval($x14) ^ \intval($x3), 8); + $x9 += $x14; + $x4 = self::leftRotate(\intval($x4) ^ \intval($x9), 7); + // columnRound + $x0 += $x4; + $x12 = self::leftRotate(\intval($x12) ^ \intval($x0), 16); + $x8 += $x12; + $x4 = self::leftRotate(\intval($x4) ^ \intval($x8), 12); + $x0 += $x4; + $x12 = self::leftRotate(\intval($x12) ^ \intval($x0), 8); + $x8 += $x12; + $x4 = self::leftRotate(\intval($x4) ^ \intval($x8), 7); + $x1 += $x5; + $x13 = self::leftRotate(\intval($x13) ^ \intval($x1), 16); + $x9 += $x13; + $x5 = self::leftRotate(\intval($x5) ^ \intval($x9), 12); + $x1 += $x5; + $x13 = self::leftRotate(\intval($x13) ^ \intval($x1), 8); + $x9 += $x13; + $x5 = self::leftRotate(\intval($x5) ^ \intval($x9), 7); + $x2 += $x6; + $x14 = self::leftRotate(\intval($x14) ^ \intval($x2), 16); + $x10 += $x14; + $x6 = self::leftRotate(\intval($x6) ^ \intval($x10), 12); + $x2 += $x6; + $x14 = self::leftRotate(\intval($x14) ^ \intval($x2), 8); + $x10 += $x14; + $x6 = self::leftRotate(\intval($x6) ^ \intval($x10), 7); + $x3 += $x7; + $x15 = self::leftRotate(\intval($x15) ^ \intval($x3), 16); + $x11 += $x15; + $x7 = self::leftRotate(\intval($x7) ^ \intval($x11), 12); + $x3 += $x7; + $x15 = self::leftRotate(\intval($x15) ^ \intval($x3), 8); + $x11 += $x15; + $x7 = self::leftRotate(\intval($x7) ^ \intval($x11), 7); + // rowRound + $x0 += $x5; + $x15 = self::leftRotate(\intval($x15) ^ \intval($x0), 16); + $x10 += $x15; + $x5 = self::leftRotate(\intval($x5) ^ \intval($x10), 12); + $x0 += $x5; + $x15 = self::leftRotate(\intval($x15) ^ \intval($x0), 8); + $x10 += $x15; + $x5 = self::leftRotate(\intval($x5) ^ \intval($x10), 7); + $x1 += $x6; + $x12 = self::leftRotate(\intval($x12) ^ \intval($x1), 16); + $x11 += $x12; + $x6 = self::leftRotate(\intval($x6) ^ \intval($x11), 12); + $x1 += $x6; + $x12 = self::leftRotate(\intval($x12) ^ \intval($x1), 8); + $x11 += $x12; + $x6 = self::leftRotate(\intval($x6) ^ \intval($x11), 7); + $x2 += $x7; + $x13 = self::leftRotate(\intval($x13) ^ \intval($x2), 16); + $x8 += $x13; + $x7 = self::leftRotate(\intval($x7) ^ \intval($x8), 12); + $x2 += $x7; + $x13 = self::leftRotate(\intval($x13) ^ \intval($x2), 8); + $x8 += $x13; + $x7 = self::leftRotate(\intval($x7) ^ \intval($x8), 7); + $x3 += $x4; + $x14 = self::leftRotate(\intval($x14) ^ \intval($x3), 16); + $x9 += $x14; + $x4 = self::leftRotate(\intval($x4) ^ \intval($x9), 12); + $x3 += $x4; + $x14 = self::leftRotate(\intval($x14) ^ \intval($x3), 8); + $x9 += $x14; + $x4 = self::leftRotate(\intval($x4) ^ \intval($x9), 7); + // columnRound + $x0 += $x4; + $x12 = self::leftRotate(\intval($x12) ^ \intval($x0), 16); + $x8 += $x12; + $x4 = self::leftRotate(\intval($x4) ^ \intval($x8), 12); + $x0 += $x4; + $x12 = self::leftRotate(\intval($x12) ^ \intval($x0), 8); + $x8 += $x12; + $x4 = self::leftRotate(\intval($x4) ^ \intval($x8), 7); + $x1 += $x5; + $x13 = self::leftRotate(\intval($x13) ^ \intval($x1), 16); + $x9 += $x13; + $x5 = self::leftRotate(\intval($x5) ^ \intval($x9), 12); + $x1 += $x5; + $x13 = self::leftRotate(\intval($x13) ^ \intval($x1), 8); + $x9 += $x13; + $x5 = self::leftRotate(\intval($x5) ^ \intval($x9), 7); + $x2 += $x6; + $x14 = self::leftRotate(\intval($x14) ^ \intval($x2), 16); + $x10 += $x14; + $x6 = self::leftRotate(\intval($x6) ^ \intval($x10), 12); + $x2 += $x6; + $x14 = self::leftRotate(\intval($x14) ^ \intval($x2), 8); + $x10 += $x14; + $x6 = self::leftRotate(\intval($x6) ^ \intval($x10), 7); + $x3 += $x7; + $x15 = self::leftRotate(\intval($x15) ^ \intval($x3), 16); + $x11 += $x15; + $x7 = self::leftRotate(\intval($x7) ^ \intval($x11), 12); + $x3 += $x7; + $x15 = self::leftRotate(\intval($x15) ^ \intval($x3), 8); + $x11 += $x15; + $x7 = self::leftRotate(\intval($x7) ^ \intval($x11), 7); + // rowRound + $x0 += $x5; + $x15 = self::leftRotate(\intval($x15) ^ \intval($x0), 16); + $x10 += $x15; + $x5 = self::leftRotate(\intval($x5) ^ \intval($x10), 12); + $x0 += $x5; + $x15 = self::leftRotate(\intval($x15) ^ \intval($x0), 8); + $x10 += $x15; + $x5 = self::leftRotate(\intval($x5) ^ \intval($x10), 7); + $x1 += $x6; + $x12 = self::leftRotate(\intval($x12) ^ \intval($x1), 16); + $x11 += $x12; + $x6 = self::leftRotate(\intval($x6) ^ \intval($x11), 12); + $x1 += $x6; + $x12 = self::leftRotate(\intval($x12) ^ \intval($x1), 8); + $x11 += $x12; + $x6 = self::leftRotate(\intval($x6) ^ \intval($x11), 7); + $x2 += $x7; + $x13 = self::leftRotate(\intval($x13) ^ \intval($x2), 16); + $x8 += $x13; + $x7 = self::leftRotate(\intval($x7) ^ \intval($x8), 12); + $x2 += $x7; + $x13 = self::leftRotate(\intval($x13) ^ \intval($x2), 8); + $x8 += $x13; + $x7 = self::leftRotate(\intval($x7) ^ \intval($x8), 7); + $x3 += $x4; + $x14 = self::leftRotate(\intval($x14) ^ \intval($x3), 16); + $x9 += $x14; + $x4 = self::leftRotate(\intval($x4) ^ \intval($x9), 12); + $x3 += $x4; + $x14 = self::leftRotate(\intval($x14) ^ \intval($x3), 8); + $x9 += $x14; + $x4 = self::leftRotate(\intval($x4) ^ \intval($x9), 7); + // @codingStandardsIgnoreEnd + $x0 += $z0; + $x1 += $z1; + $x2 += $z2; + $x3 += $z3; + $x4 += $z4; + $x5 += $z5; + $x6 += $z6; + $x7 += $z7; + $x8 += $z8; + $x9 += $z9; + $x10 += $z10; + $x11 += $z11; + $x12 += $z12; + $x13 += $z13; + $x14 += $z14; + $x15 += $z15; + return \pack('V*', $x0, $x1, $x2, $x3, $x4, $x5, $x6, $x7, $x8, $x9, $x10, $x11, $x12, $x13, $x14, $x15); + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/Common/AsymmetricKey.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/Common/AsymmetricKey.php new file mode 100644 index 0000000..b5b6f6c --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/Common/AsymmetricKey.php @@ -0,0 +1,511 @@ + + * @copyright 2016 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\Crypt\Common; + +use FluentSmtpLib\phpseclib3\Crypt\DSA; +use FluentSmtpLib\phpseclib3\Crypt\Hash; +use FluentSmtpLib\phpseclib3\Crypt\RSA; +use FluentSmtpLib\phpseclib3\Exception\NoKeyLoadedException; +use FluentSmtpLib\phpseclib3\Exception\UnsupportedFormatException; +use FluentSmtpLib\phpseclib3\Math\BigInteger; +/** + * Base Class for all asymmetric cipher classes + * + * @author Jim Wigginton + */ +abstract class AsymmetricKey +{ + /** + * Precomputed Zero + * + * @var BigInteger + */ + protected static $zero; + /** + * Precomputed One + * + * @var BigInteger + */ + protected static $one; + /** + * Format of the loaded key + * + * @var string + */ + protected $format; + /** + * Hash function + * + * @var Hash + */ + protected $hash; + /** + * HMAC function + * + * @var Hash + */ + private $hmac; + /** + * Supported plugins (lower case) + * + * @see self::initialize_static_variables() + * @var array + */ + private static $plugins = []; + /** + * Invisible plugins + * + * @see self::initialize_static_variables() + * @var array + */ + private static $invisiblePlugins = []; + /** + * Available Engines + * + * @var boolean[] + */ + protected static $engines = []; + /** + * Key Comment + * + * @var null|string + */ + private $comment; + /** + * @param string $type + * @return array|string + */ + public abstract function toString($type, array $options = []); + /** + * The constructor + */ + protected function __construct() + { + self::initialize_static_variables(); + $this->hash = new \FluentSmtpLib\phpseclib3\Crypt\Hash('sha256'); + $this->hmac = new \FluentSmtpLib\phpseclib3\Crypt\Hash('sha256'); + } + /** + * Initialize static variables + */ + protected static function initialize_static_variables() + { + if (!isset(self::$zero)) { + self::$zero = new \FluentSmtpLib\phpseclib3\Math\BigInteger(0); + self::$one = new \FluentSmtpLib\phpseclib3\Math\BigInteger(1); + } + self::loadPlugins('Keys'); + if (static::ALGORITHM != 'RSA' && static::ALGORITHM != 'DH') { + self::loadPlugins('Signature'); + } + } + /** + * Load the key + * + * @param string $key + * @param string $password optional + * @return PublicKey|PrivateKey + */ + public static function load($key, $password = \false) + { + self::initialize_static_variables(); + $class = new \ReflectionClass(static::class); + if ($class->isFinal()) { + throw new \RuntimeException('load() should not be called from final classes (' . static::class . ')'); + } + $components = \false; + foreach (self::$plugins[static::ALGORITHM]['Keys'] as $format) { + if (isset(self::$invisiblePlugins[static::ALGORITHM]) && \in_array($format, self::$invisiblePlugins[static::ALGORITHM])) { + continue; + } + try { + $components = $format::load($key, $password); + } catch (\Exception $e) { + $components = \false; + } + if ($components !== \false) { + break; + } + } + if ($components === \false) { + throw new \FluentSmtpLib\phpseclib3\Exception\NoKeyLoadedException('Unable to read key'); + } + $components['format'] = $format; + $components['secret'] = isset($components['secret']) ? $components['secret'] : ''; + $comment = isset($components['comment']) ? $components['comment'] : null; + $new = static::onLoad($components); + $new->format = $format; + $new->comment = $comment; + return $new instanceof \FluentSmtpLib\phpseclib3\Crypt\Common\PrivateKey ? $new->withPassword($password) : $new; + } + /** + * Loads a private key + * + * @return PrivateKey + * @param string|array $key + * @param string $password optional + */ + public static function loadPrivateKey($key, $password = '') + { + $key = self::load($key, $password); + if (!$key instanceof \FluentSmtpLib\phpseclib3\Crypt\Common\PrivateKey) { + throw new \FluentSmtpLib\phpseclib3\Exception\NoKeyLoadedException('The key that was loaded was not a private key'); + } + return $key; + } + /** + * Loads a public key + * + * @return PublicKey + * @param string|array $key + */ + public static function loadPublicKey($key) + { + $key = self::load($key); + if (!$key instanceof \FluentSmtpLib\phpseclib3\Crypt\Common\PublicKey) { + throw new \FluentSmtpLib\phpseclib3\Exception\NoKeyLoadedException('The key that was loaded was not a public key'); + } + return $key; + } + /** + * Loads parameters + * + * @return AsymmetricKey + * @param string|array $key + */ + public static function loadParameters($key) + { + $key = self::load($key); + if (!$key instanceof \FluentSmtpLib\phpseclib3\Crypt\Common\PrivateKey && !$key instanceof \FluentSmtpLib\phpseclib3\Crypt\Common\PublicKey) { + throw new \FluentSmtpLib\phpseclib3\Exception\NoKeyLoadedException('The key that was loaded was not a parameter'); + } + return $key; + } + /** + * Load the key, assuming a specific format + * + * @param string $type + * @param string $key + * @param string $password optional + * @return static + */ + public static function loadFormat($type, $key, $password = \false) + { + self::initialize_static_variables(); + $components = \false; + $format = \strtolower($type); + if (isset(self::$plugins[static::ALGORITHM]['Keys'][$format])) { + $format = self::$plugins[static::ALGORITHM]['Keys'][$format]; + $components = $format::load($key, $password); + } + if ($components === \false) { + throw new \FluentSmtpLib\phpseclib3\Exception\NoKeyLoadedException('Unable to read key'); + } + $components['format'] = $format; + $components['secret'] = isset($components['secret']) ? $components['secret'] : ''; + $new = static::onLoad($components); + $new->format = $format; + return $new instanceof \FluentSmtpLib\phpseclib3\Crypt\Common\PrivateKey ? $new->withPassword($password) : $new; + } + /** + * Loads a private key + * + * @return PrivateKey + * @param string $type + * @param string $key + * @param string $password optional + */ + public static function loadPrivateKeyFormat($type, $key, $password = \false) + { + $key = self::loadFormat($type, $key, $password); + if (!$key instanceof \FluentSmtpLib\phpseclib3\Crypt\Common\PrivateKey) { + throw new \FluentSmtpLib\phpseclib3\Exception\NoKeyLoadedException('The key that was loaded was not a private key'); + } + return $key; + } + /** + * Loads a public key + * + * @return PublicKey + * @param string $type + * @param string $key + */ + public static function loadPublicKeyFormat($type, $key) + { + $key = self::loadFormat($type, $key); + if (!$key instanceof \FluentSmtpLib\phpseclib3\Crypt\Common\PublicKey) { + throw new \FluentSmtpLib\phpseclib3\Exception\NoKeyLoadedException('The key that was loaded was not a public key'); + } + return $key; + } + /** + * Loads parameters + * + * @return AsymmetricKey + * @param string $type + * @param string|array $key + */ + public static function loadParametersFormat($type, $key) + { + $key = self::loadFormat($type, $key); + if (!$key instanceof \FluentSmtpLib\phpseclib3\Crypt\Common\PrivateKey && !$key instanceof \FluentSmtpLib\phpseclib3\Crypt\Common\PublicKey) { + throw new \FluentSmtpLib\phpseclib3\Exception\NoKeyLoadedException('The key that was loaded was not a parameter'); + } + return $key; + } + /** + * Validate Plugin + * + * @param string $format + * @param string $type + * @param string $method optional + * @return mixed + */ + protected static function validatePlugin($format, $type, $method = null) + { + $type = \strtolower($type); + if (!isset(self::$plugins[static::ALGORITHM][$format][$type])) { + throw new \FluentSmtpLib\phpseclib3\Exception\UnsupportedFormatException("{$type} is not a supported format"); + } + $type = self::$plugins[static::ALGORITHM][$format][$type]; + if (isset($method) && !\method_exists($type, $method)) { + throw new \FluentSmtpLib\phpseclib3\Exception\UnsupportedFormatException("{$type} does not implement {$method}"); + } + return $type; + } + /** + * Load Plugins + * + * @param string $format + */ + private static function loadPlugins($format) + { + if (!isset(self::$plugins[static::ALGORITHM][$format])) { + self::$plugins[static::ALGORITHM][$format] = []; + foreach (new \DirectoryIterator(__DIR__ . '/../' . static::ALGORITHM . '/Formats/' . $format . '/') as $file) { + if ($file->getExtension() != 'php') { + continue; + } + $name = $file->getBasename('.php'); + if ($name[0] == '.') { + continue; + } + $type = '\\FluentSmtpLib\\phpseclib3\\Crypt\\' . static::ALGORITHM . '\\Formats\\' . $format . '\\' . $name; + $reflect = new \ReflectionClass($type); + if ($reflect->isTrait()) { + continue; + } + self::$plugins[static::ALGORITHM][$format][\strtolower($name)] = $type; + if ($reflect->hasConstant('IS_INVISIBLE')) { + self::$invisiblePlugins[static::ALGORITHM][] = $type; + } + } + } + } + /** + * Returns a list of supported formats. + * + * @return array + */ + public static function getSupportedKeyFormats() + { + self::initialize_static_variables(); + return self::$plugins[static::ALGORITHM]['Keys']; + } + /** + * Add a fileformat plugin + * + * The plugin needs to either already be loaded or be auto-loadable. + * Loading a plugin whose shortname overwrite an existing shortname will overwrite the old plugin. + * + * @see self::load() + * @param string $fullname + * @return bool + */ + public static function addFileFormat($fullname) + { + self::initialize_static_variables(); + if (\class_exists($fullname)) { + $meta = new \ReflectionClass($fullname); + $shortname = $meta->getShortName(); + self::$plugins[static::ALGORITHM]['Keys'][\strtolower($shortname)] = $fullname; + if ($meta->hasConstant('IS_INVISIBLE')) { + self::$invisiblePlugins[static::ALGORITHM][] = \strtolower($shortname); + } + } + } + /** + * Returns the format of the loaded key. + * + * If the key that was loaded wasn't in a valid or if the key was auto-generated + * with RSA::createKey() then this will throw an exception. + * + * @see self::load() + * @return mixed + */ + public function getLoadedFormat() + { + if (empty($this->format)) { + throw new \FluentSmtpLib\phpseclib3\Exception\NoKeyLoadedException('This key was created with createKey - it was not loaded with load. Therefore there is no "loaded format"'); + } + $meta = new \ReflectionClass($this->format); + return $meta->getShortName(); + } + /** + * Returns the key's comment + * + * Not all key formats support comments. If you want to set a comment use toString() + * + * @return null|string + */ + public function getComment() + { + return $this->comment; + } + /** + * Tests engine validity + * + */ + public static function useBestEngine() + { + static::$engines = [ + 'PHP' => \true, + 'OpenSSL' => \extension_loaded('openssl'), + // this test can be satisfied by either of the following: + // http://php.net/manual/en/book.sodium.php + // https://github.com/paragonie/sodium_compat + 'libsodium' => \function_exists('sodium_crypto_sign_keypair'), + ]; + return static::$engines; + } + /** + * Flag to use internal engine only (useful for unit testing) + * + */ + public static function useInternalEngine() + { + static::$engines = ['PHP' => \true, 'OpenSSL' => \false, 'libsodium' => \false]; + } + /** + * __toString() magic method + * + * @return string + */ + public function __toString() + { + return $this->toString('PKCS8'); + } + /** + * Determines which hashing function should be used + * + * @param string $hash + */ + public function withHash($hash) + { + $new = clone $this; + $new->hash = new \FluentSmtpLib\phpseclib3\Crypt\Hash($hash); + $new->hmac = new \FluentSmtpLib\phpseclib3\Crypt\Hash($hash); + return $new; + } + /** + * Returns the hash algorithm currently being used + * + */ + public function getHash() + { + return clone $this->hash; + } + /** + * Compute the pseudorandom k for signature generation, + * using the process specified for deterministic DSA. + * + * @param string $h1 + * @return string + */ + protected function computek($h1) + { + $v = \str_repeat("\x01", \strlen($h1)); + $k = \str_repeat("\x00", \strlen($h1)); + $x = $this->int2octets($this->x); + $h1 = $this->bits2octets($h1); + $this->hmac->setKey($k); + $k = $this->hmac->hash($v . "\x00" . $x . $h1); + $this->hmac->setKey($k); + $v = $this->hmac->hash($v); + $k = $this->hmac->hash($v . "\x01" . $x . $h1); + $this->hmac->setKey($k); + $v = $this->hmac->hash($v); + $qlen = $this->q->getLengthInBytes(); + while (\true) { + $t = ''; + while (\strlen($t) < $qlen) { + $v = $this->hmac->hash($v); + $t = $t . $v; + } + $k = $this->bits2int($t); + if (!$k->equals(self::$zero) && $k->compare($this->q) < 0) { + break; + } + $k = $this->hmac->hash($v . "\x00"); + $this->hmac->setKey($k); + $v = $this->hmac->hash($v); + } + return $k; + } + /** + * Integer to Octet String + * + * @param BigInteger $v + * @return string + */ + private function int2octets($v) + { + $out = $v->toBytes(); + $rolen = $this->q->getLengthInBytes(); + if (\strlen($out) < $rolen) { + return \str_pad($out, $rolen, "\x00", \STR_PAD_LEFT); + } elseif (\strlen($out) > $rolen) { + return \substr($out, -$rolen); + } else { + return $out; + } + } + /** + * Bit String to Integer + * + * @param string $in + * @return BigInteger + */ + protected function bits2int($in) + { + $v = new \FluentSmtpLib\phpseclib3\Math\BigInteger($in, 256); + $vlen = \strlen($in) << 3; + $qlen = $this->q->getLength(); + if ($vlen > $qlen) { + return $v->bitwise_rightShift($vlen - $qlen); + } + return $v; + } + /** + * Bit String to Octet String + * + * @param string $in + * @return string + */ + private function bits2octets($in) + { + $z1 = $this->bits2int($in); + $z2 = $z1->subtract($this->q); + return $z2->compare(self::$zero) < 0 ? $this->int2octets($z1) : $this->int2octets($z2); + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/Common/BlockCipher.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/Common/BlockCipher.php new file mode 100644 index 0000000..9cce9a2 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/Common/BlockCipher.php @@ -0,0 +1,23 @@ + + * @author Hans-Juergen Petrich + * @copyright 2007 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\Crypt\Common; + +/** + * Base Class for all block cipher classes + * + * @author Jim Wigginton + */ +abstract class BlockCipher extends \FluentSmtpLib\phpseclib3\Crypt\Common\SymmetricKey +{ +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/Common/Formats/Keys/JWK.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/Common/Formats/Keys/JWK.php new file mode 100644 index 0000000..a8a5b80 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/Common/Formats/Keys/JWK.php @@ -0,0 +1,62 @@ + + * @copyright 2015 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\Crypt\Common\Formats\Keys; + +use FluentSmtpLib\phpseclib3\Common\Functions\Strings; +/** + * JSON Web Key Formatted Key Handler + * + * @author Jim Wigginton + */ +abstract class JWK +{ + /** + * Break a public or private key down into its constituent components + * + * @param string $key + * @param string $password + * @return array + */ + public static function load($key, $password = '') + { + if (!\FluentSmtpLib\phpseclib3\Common\Functions\Strings::is_stringable($key)) { + throw new \UnexpectedValueException('Key should be a string - not a ' . \gettype($key)); + } + $key = \preg_replace('#\\s#', '', $key); + // remove whitespace + if (\PHP_VERSION_ID >= 73000) { + $key = \json_decode($key, null, 512, \JSON_THROW_ON_ERROR); + } else { + $key = \json_decode($key); + if (!$key) { + throw new \RuntimeException('Unable to decode JSON'); + } + } + if (isset($key->kty)) { + return $key; + } + if (\count($key->keys) != 1) { + throw new \RuntimeException('Although the JWK key format supports multiple keys phpseclib does not'); + } + return $key->keys[0]; + } + /** + * Wrap a key appropriately + * + * @return string + */ + protected static function wrapKey(array $key, array $options) + { + return \json_encode(['keys' => [$key + $options]]); + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/Common/Formats/Keys/OpenSSH.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/Common/Formats/Keys/OpenSSH.php new file mode 100644 index 0000000..4efd1f6 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/Common/Formats/Keys/OpenSSH.php @@ -0,0 +1,199 @@ + + * @copyright 2015 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\Crypt\Common\Formats\Keys; + +use FluentSmtpLib\phpseclib3\Common\Functions\Strings; +use FluentSmtpLib\phpseclib3\Crypt\AES; +use FluentSmtpLib\phpseclib3\Crypt\Random; +use FluentSmtpLib\phpseclib3\Exception\BadDecryptionException; +/** + * OpenSSH Formatted RSA Key Handler + * + * @author Jim Wigginton + */ +abstract class OpenSSH +{ + /** + * Default comment + * + * @var string + */ + protected static $comment = 'phpseclib-generated-key'; + /** + * Binary key flag + * + * @var bool + */ + protected static $binary = \false; + /** + * Sets the default comment + * + * @param string $comment + */ + public static function setComment($comment) + { + self::$comment = \str_replace(["\r", "\n"], '', $comment); + } + /** + * Break a public or private key down into its constituent components + * + * $type can be either ssh-dss or ssh-rsa + * + * @param string $key + * @param string $password + * @return array + */ + public static function load($key, $password = '') + { + if (!\FluentSmtpLib\phpseclib3\Common\Functions\Strings::is_stringable($key)) { + throw new \UnexpectedValueException('Key should be a string - not a ' . \gettype($key)); + } + // key format is described here: + // https://cvsweb.openbsd.org/cgi-bin/cvsweb/src/usr.bin/ssh/PROTOCOL.key?annotate=HEAD + if (\strpos($key, 'BEGIN OPENSSH PRIVATE KEY') !== \false) { + $key = \preg_replace('#(?:^-.*?-[\\r\\n]*$)|\\s#ms', '', $key); + $key = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::base64_decode($key); + $magic = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::shift($key, 15); + if ($magic != "openssh-key-v1\x00") { + throw new \RuntimeException('Expected openssh-key-v1'); + } + list($ciphername, $kdfname, $kdfoptions, $numKeys) = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::unpackSSH2('sssN', $key); + if ($numKeys != 1) { + // if we wanted to support multiple keys we could update PublicKeyLoader to preview what the # of keys + // would be; it'd then call Common\Keys\OpenSSH.php::load() and get the paddedKey. it'd then pass + // that to the appropriate key loading parser $numKey times or something + throw new \RuntimeException('Although the OpenSSH private key format supports multiple keys phpseclib does not'); + } + switch ($ciphername) { + case 'none': + break; + case 'aes256-ctr': + if ($kdfname != 'bcrypt') { + throw new \RuntimeException('Only the bcrypt kdf is supported (' . $kdfname . ' encountered)'); + } + list($salt, $rounds) = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::unpackSSH2('sN', $kdfoptions); + $crypto = new \FluentSmtpLib\phpseclib3\Crypt\AES('ctr'); + //$crypto->setKeyLength(256); + //$crypto->disablePadding(); + $crypto->setPassword($password, 'bcrypt', $salt, $rounds, 32); + break; + default: + throw new \RuntimeException('The only supported ciphers are: none, aes256-ctr (' . $ciphername . ' is being used)'); + } + list($publicKey, $paddedKey) = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::unpackSSH2('ss', $key); + list($type) = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::unpackSSH2('s', $publicKey); + if (isset($crypto)) { + $paddedKey = $crypto->decrypt($paddedKey); + } + list($checkint1, $checkint2) = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::unpackSSH2('NN', $paddedKey); + // any leftover bytes in $paddedKey are for padding? but they should be sequential bytes. eg. 1, 2, 3, etc. + if ($checkint1 != $checkint2) { + if (isset($crypto)) { + throw new \FluentSmtpLib\phpseclib3\Exception\BadDecryptionException('Unable to decrypt key - please verify the password you are using'); + } + throw new \RuntimeException("The two checkints do not match ({$checkint1} vs. {$checkint2})"); + } + self::checkType($type); + return \compact('type', 'publicKey', 'paddedKey'); + } + $parts = \explode(' ', $key, 3); + if (!isset($parts[1])) { + $key = \base64_decode($parts[0]); + $comment = \false; + } else { + $asciiType = $parts[0]; + self::checkType($parts[0]); + $key = \base64_decode($parts[1]); + $comment = isset($parts[2]) ? $parts[2] : \false; + } + if ($key === \false) { + throw new \UnexpectedValueException('Key should be a string - not a ' . \gettype($key)); + } + list($type) = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::unpackSSH2('s', $key); + self::checkType($type); + if (isset($asciiType) && $asciiType != $type) { + throw new \RuntimeException('Two different types of keys are claimed: ' . $asciiType . ' and ' . $type); + } + if (\strlen($key) <= 4) { + throw new \UnexpectedValueException('Key appears to be malformed'); + } + $publicKey = $key; + return \compact('type', 'publicKey', 'comment'); + } + /** + * Toggle between binary and printable keys + * + * Printable keys are what are generated by default. These are the ones that go in + * $HOME/.ssh/authorized_key. + * + * @param bool $enabled + */ + public static function setBinaryOutput($enabled) + { + self::$binary = $enabled; + } + /** + * Checks to see if the type is valid + * + * @param string $candidate + */ + private static function checkType($candidate) + { + if (!\in_array($candidate, static::$types)) { + throw new \RuntimeException("The key type ({$candidate}) is not equal to: " . \implode(',', static::$types)); + } + } + /** + * Wrap a private key appropriately + * + * @param string $publicKey + * @param string $privateKey + * @param string $password + * @param array $options + * @return string + */ + protected static function wrapPrivateKey($publicKey, $privateKey, $password, $options) + { + list(, $checkint) = \unpack('N', \FluentSmtpLib\phpseclib3\Crypt\Random::string(4)); + $comment = isset($options['comment']) ? $options['comment'] : self::$comment; + $paddedKey = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::packSSH2('NN', $checkint, $checkint) . $privateKey . \FluentSmtpLib\phpseclib3\Common\Functions\Strings::packSSH2('s', $comment); + $usesEncryption = !empty($password) && \is_string($password); + /* + from http://tools.ietf.org/html/rfc4253#section-6 : + + Note that the length of the concatenation of 'packet_length', + 'padding_length', 'payload', and 'random padding' MUST be a multiple + of the cipher block size or 8, whichever is larger. + */ + $blockSize = $usesEncryption ? 16 : 8; + $paddingLength = ($blockSize - 1) * \strlen($paddedKey) % $blockSize; + for ($i = 1; $i <= $paddingLength; $i++) { + $paddedKey .= \chr($i); + } + if (!$usesEncryption) { + $key = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::packSSH2('sssNss', 'none', 'none', '', 1, $publicKey, $paddedKey); + } else { + $rounds = isset($options['rounds']) ? $options['rounds'] : 16; + $salt = \FluentSmtpLib\phpseclib3\Crypt\Random::string(16); + $kdfoptions = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::packSSH2('sN', $salt, $rounds); + $crypto = new \FluentSmtpLib\phpseclib3\Crypt\AES('ctr'); + $crypto->setPassword($password, 'bcrypt', $salt, $rounds, 32); + $paddedKey = $crypto->encrypt($paddedKey); + $key = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::packSSH2('sssNss', 'aes256-ctr', 'bcrypt', $kdfoptions, 1, $publicKey, $paddedKey); + } + $key = "openssh-key-v1\x00{$key}"; + return "-----BEGIN OPENSSH PRIVATE KEY-----\n" . \chunk_split(\FluentSmtpLib\phpseclib3\Common\Functions\Strings::base64_encode($key), 70, "\n") . "-----END OPENSSH PRIVATE KEY-----\n"; + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/Common/Formats/Keys/PKCS.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/Common/Formats/Keys/PKCS.php new file mode 100644 index 0000000..fcd084f --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/Common/Formats/Keys/PKCS.php @@ -0,0 +1,67 @@ + + * @copyright 2015 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\Crypt\Common\Formats\Keys; + +/** + * PKCS1 Formatted Key Handler + * + * @author Jim Wigginton + */ +abstract class PKCS +{ + /** + * Auto-detect the format + */ + const MODE_ANY = 0; + /** + * Require base64-encoded PEM's be supplied + */ + const MODE_PEM = 1; + /** + * Require raw DER's be supplied + */ + const MODE_DER = 2; + /**#@-*/ + /** + * Is the key a base-64 encoded PEM, DER or should it be auto-detected? + * + * @var int + */ + protected static $format = self::MODE_ANY; + /** + * Require base64-encoded PEM's be supplied + * + */ + public static function requirePEM() + { + self::$format = self::MODE_PEM; + } + /** + * Require raw DER's be supplied + * + */ + public static function requireDER() + { + self::$format = self::MODE_DER; + } + /** + * Accept any format and auto detect the format + * + * This is the default setting + * + */ + public static function requireAny() + { + self::$format = self::MODE_ANY; + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/Common/Formats/Keys/PKCS1.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/Common/Formats/Keys/PKCS1.php new file mode 100644 index 0000000..d68ca8c --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/Common/Formats/Keys/PKCS1.php @@ -0,0 +1,187 @@ + + * @copyright 2015 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\Crypt\Common\Formats\Keys; + +use FluentSmtpLib\phpseclib3\Common\Functions\Strings; +use FluentSmtpLib\phpseclib3\Crypt\AES; +use FluentSmtpLib\phpseclib3\Crypt\DES; +use FluentSmtpLib\phpseclib3\Crypt\Random; +use FluentSmtpLib\phpseclib3\Crypt\TripleDES; +use FluentSmtpLib\phpseclib3\Exception\UnsupportedAlgorithmException; +use FluentSmtpLib\phpseclib3\File\ASN1; +/** + * PKCS1 Formatted Key Handler + * + * @author Jim Wigginton + */ +abstract class PKCS1 extends \FluentSmtpLib\phpseclib3\Crypt\Common\Formats\Keys\PKCS +{ + /** + * Default encryption algorithm + * + * @var string + */ + private static $defaultEncryptionAlgorithm = 'AES-128-CBC'; + /** + * Sets the default encryption algorithm + * + * @param string $algo + */ + public static function setEncryptionAlgorithm($algo) + { + self::$defaultEncryptionAlgorithm = $algo; + } + /** + * Returns the mode constant corresponding to the mode string + * + * @param string $mode + * @return int + * @throws \UnexpectedValueException if the block cipher mode is unsupported + */ + private static function getEncryptionMode($mode) + { + switch ($mode) { + case 'CBC': + case 'ECB': + case 'CFB': + case 'OFB': + case 'CTR': + return $mode; + } + throw new \UnexpectedValueException('Unsupported block cipher mode of operation'); + } + /** + * Returns a cipher object corresponding to a string + * + * @param string $algo + * @return string + * @throws \UnexpectedValueException if the encryption algorithm is unsupported + */ + private static function getEncryptionObject($algo) + { + $modes = '(CBC|ECB|CFB|OFB|CTR)'; + switch (\true) { + case \preg_match("#^AES-(128|192|256)-{$modes}\$#", $algo, $matches): + $cipher = new \FluentSmtpLib\phpseclib3\Crypt\AES(self::getEncryptionMode($matches[2])); + $cipher->setKeyLength($matches[1]); + return $cipher; + case \preg_match("#^DES-EDE3-{$modes}\$#", $algo, $matches): + return new \FluentSmtpLib\phpseclib3\Crypt\TripleDES(self::getEncryptionMode($matches[1])); + case \preg_match("#^DES-{$modes}\$#", $algo, $matches): + return new \FluentSmtpLib\phpseclib3\Crypt\DES(self::getEncryptionMode($matches[1])); + default: + throw new \FluentSmtpLib\phpseclib3\Exception\UnsupportedAlgorithmException($algo . ' is not a supported algorithm'); + } + } + /** + * Generate a symmetric key for PKCS#1 keys + * + * @param string $password + * @param string $iv + * @param int $length + * @return string + */ + private static function generateSymmetricKey($password, $iv, $length) + { + $symkey = ''; + $iv = \substr($iv, 0, 8); + while (\strlen($symkey) < $length) { + $symkey .= \md5($symkey . $password . $iv, \true); + } + return \substr($symkey, 0, $length); + } + /** + * Break a public or private key down into its constituent components + * + * @param string $key + * @param string $password optional + * @return array + */ + protected static function load($key, $password) + { + if (!\FluentSmtpLib\phpseclib3\Common\Functions\Strings::is_stringable($key)) { + throw new \UnexpectedValueException('Key should be a string - not a ' . \gettype($key)); + } + /* Although PKCS#1 proposes a format that public and private keys can use, encrypting them is + "outside the scope" of PKCS#1. PKCS#1 then refers you to PKCS#12 and PKCS#15 if you're wanting to + protect private keys, however, that's not what OpenSSL* does. OpenSSL protects private keys by adding + two new "fields" to the key - DEK-Info and Proc-Type. These fields are discussed here: + + http://tools.ietf.org/html/rfc1421#section-4.6.1.1 + http://tools.ietf.org/html/rfc1421#section-4.6.1.3 + + DES-EDE3-CBC as an algorithm, however, is not discussed anywhere, near as I can tell. + DES-CBC and DES-EDE are discussed in RFC1423, however, DES-EDE3-CBC isn't, nor is its key derivation + function. As is, the definitive authority on this encoding scheme isn't the IETF but rather OpenSSL's + own implementation. ie. the implementation *is* the standard and any bugs that may exist in that + implementation are part of the standard, as well. + + * OpenSSL is the de facto standard. It's utilized by OpenSSH and other projects */ + if (\preg_match('#DEK-Info: (.+),(.+)#', $key, $matches)) { + $iv = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::hex2bin(\trim($matches[2])); + // remove the Proc-Type / DEK-Info sections as they're no longer needed + $key = \preg_replace('#^(?:Proc-Type|DEK-Info): .*#m', '', $key); + $ciphertext = \FluentSmtpLib\phpseclib3\File\ASN1::extractBER($key); + if ($ciphertext === \false) { + $ciphertext = $key; + } + $crypto = self::getEncryptionObject($matches[1]); + $crypto->setKey(self::generateSymmetricKey($password, $iv, $crypto->getKeyLength() >> 3)); + $crypto->setIV($iv); + $key = $crypto->decrypt($ciphertext); + } else { + if (self::$format != self::MODE_DER) { + $decoded = \FluentSmtpLib\phpseclib3\File\ASN1::extractBER($key); + if ($decoded !== \false) { + $key = $decoded; + } elseif (self::$format == self::MODE_PEM) { + throw new \UnexpectedValueException('Expected base64-encoded PEM format but was unable to decode base64 text'); + } + } + } + return $key; + } + /** + * Wrap a private key appropriately + * + * @param string $key + * @param string $type + * @param string $password + * @param array $options optional + * @return string + */ + protected static function wrapPrivateKey($key, $type, $password, array $options = []) + { + if (empty($password) || !\is_string($password)) { + return "-----BEGIN {$type} PRIVATE KEY-----\r\n" . \chunk_split(\FluentSmtpLib\phpseclib3\Common\Functions\Strings::base64_encode($key), 64) . "-----END {$type} PRIVATE KEY-----"; + } + $encryptionAlgorithm = isset($options['encryptionAlgorithm']) ? $options['encryptionAlgorithm'] : self::$defaultEncryptionAlgorithm; + $cipher = self::getEncryptionObject($encryptionAlgorithm); + $iv = \FluentSmtpLib\phpseclib3\Crypt\Random::string($cipher->getBlockLength() >> 3); + $cipher->setKey(self::generateSymmetricKey($password, $iv, $cipher->getKeyLength() >> 3)); + $cipher->setIV($iv); + $iv = \strtoupper(\FluentSmtpLib\phpseclib3\Common\Functions\Strings::bin2hex($iv)); + return "-----BEGIN {$type} PRIVATE KEY-----\r\n" . "Proc-Type: 4,ENCRYPTED\r\n" . "DEK-Info: " . $encryptionAlgorithm . ",{$iv}\r\n" . "\r\n" . \chunk_split(\FluentSmtpLib\phpseclib3\Common\Functions\Strings::base64_encode($cipher->encrypt($key)), 64) . "-----END {$type} PRIVATE KEY-----"; + } + /** + * Wrap a public key appropriately + * + * @param string $key + * @param string $type + * @return string + */ + protected static function wrapPublicKey($key, $type) + { + return "-----BEGIN {$type} PUBLIC KEY-----\r\n" . \chunk_split(\FluentSmtpLib\phpseclib3\Common\Functions\Strings::base64_encode($key), 64) . "-----END {$type} PUBLIC KEY-----"; + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/Common/Formats/Keys/PKCS8.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/Common/Formats/Keys/PKCS8.php new file mode 100644 index 0000000..ac461e6 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/Common/Formats/Keys/PKCS8.php @@ -0,0 +1,625 @@ + + * @copyright 2015 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\Crypt\Common\Formats\Keys; + +use FluentSmtpLib\phpseclib3\Common\Functions\Strings; +use FluentSmtpLib\phpseclib3\Crypt\AES; +use FluentSmtpLib\phpseclib3\Crypt\DES; +use FluentSmtpLib\phpseclib3\Crypt\Random; +use FluentSmtpLib\phpseclib3\Crypt\RC2; +use FluentSmtpLib\phpseclib3\Crypt\RC4; +use FluentSmtpLib\phpseclib3\Crypt\TripleDES; +use FluentSmtpLib\phpseclib3\Exception\InsufficientSetupException; +use FluentSmtpLib\phpseclib3\Exception\UnsupportedAlgorithmException; +use FluentSmtpLib\phpseclib3\File\ASN1; +use FluentSmtpLib\phpseclib3\File\ASN1\Maps; +/** + * PKCS#8 Formatted Key Handler + * + * @author Jim Wigginton + */ +abstract class PKCS8 extends \FluentSmtpLib\phpseclib3\Crypt\Common\Formats\Keys\PKCS +{ + /** + * Default encryption algorithm + * + * @var string + */ + private static $defaultEncryptionAlgorithm = 'id-PBES2'; + /** + * Default encryption scheme + * + * Only used when defaultEncryptionAlgorithm is id-PBES2 + * + * @var string + */ + private static $defaultEncryptionScheme = 'aes128-CBC-PAD'; + /** + * Default PRF + * + * Only used when defaultEncryptionAlgorithm is id-PBES2 + * + * @var string + */ + private static $defaultPRF = 'id-hmacWithSHA256'; + /** + * Default Iteration Count + * + * @var int + */ + private static $defaultIterationCount = 2048; + /** + * OIDs loaded + * + * @var bool + */ + private static $oidsLoaded = \false; + /** + * Binary key flag + * + * @var bool + */ + private static $binary = \false; + /** + * Sets the default encryption algorithm + * + * @param string $algo + */ + public static function setEncryptionAlgorithm($algo) + { + self::$defaultEncryptionAlgorithm = $algo; + } + /** + * Sets the default encryption algorithm for PBES2 + * + * @param string $algo + */ + public static function setEncryptionScheme($algo) + { + self::$defaultEncryptionScheme = $algo; + } + /** + * Sets the iteration count + * + * @param int $count + */ + public static function setIterationCount($count) + { + self::$defaultIterationCount = $count; + } + /** + * Sets the PRF for PBES2 + * + * @param string $algo + */ + public static function setPRF($algo) + { + self::$defaultPRF = $algo; + } + /** + * Returns a SymmetricKey object based on a PBES1 $algo + * + * @return \phpseclib3\Crypt\Common\SymmetricKey + * @param string $algo + */ + private static function getPBES1EncryptionObject($algo) + { + $algo = \preg_match('#^pbeWith(?:MD2|MD5|SHA1|SHA)And(.*?)-CBC$#', $algo, $matches) ? $matches[1] : \substr($algo, 13); + // strlen('pbeWithSHAAnd') == 13 + switch ($algo) { + case 'DES': + $cipher = new \FluentSmtpLib\phpseclib3\Crypt\DES('cbc'); + break; + case 'RC2': + $cipher = new \FluentSmtpLib\phpseclib3\Crypt\RC2('cbc'); + $cipher->setKeyLength(64); + break; + case '3-KeyTripleDES': + $cipher = new \FluentSmtpLib\phpseclib3\Crypt\TripleDES('cbc'); + break; + case '2-KeyTripleDES': + $cipher = new \FluentSmtpLib\phpseclib3\Crypt\TripleDES('cbc'); + $cipher->setKeyLength(128); + break; + case '128BitRC2': + $cipher = new \FluentSmtpLib\phpseclib3\Crypt\RC2('cbc'); + $cipher->setKeyLength(128); + break; + case '40BitRC2': + $cipher = new \FluentSmtpLib\phpseclib3\Crypt\RC2('cbc'); + $cipher->setKeyLength(40); + break; + case '128BitRC4': + $cipher = new \FluentSmtpLib\phpseclib3\Crypt\RC4(); + $cipher->setKeyLength(128); + break; + case '40BitRC4': + $cipher = new \FluentSmtpLib\phpseclib3\Crypt\RC4(); + $cipher->setKeyLength(40); + break; + default: + throw new \FluentSmtpLib\phpseclib3\Exception\UnsupportedAlgorithmException("{$algo} is not a supported algorithm"); + } + return $cipher; + } + /** + * Returns a hash based on a PBES1 $algo + * + * @return string + * @param string $algo + */ + private static function getPBES1Hash($algo) + { + if (\preg_match('#^pbeWith(MD2|MD5|SHA1|SHA)And.*?-CBC$#', $algo, $matches)) { + return $matches[1] == 'SHA' ? 'sha1' : $matches[1]; + } + return 'sha1'; + } + /** + * Returns a KDF baesd on a PBES1 $algo + * + * @return string + * @param string $algo + */ + private static function getPBES1KDF($algo) + { + switch ($algo) { + case 'pbeWithMD2AndDES-CBC': + case 'pbeWithMD2AndRC2-CBC': + case 'pbeWithMD5AndDES-CBC': + case 'pbeWithMD5AndRC2-CBC': + case 'pbeWithSHA1AndDES-CBC': + case 'pbeWithSHA1AndRC2-CBC': + return 'pbkdf1'; + } + return 'pkcs12'; + } + /** + * Returns a SymmetricKey object baesd on a PBES2 $algo + * + * @return SymmetricKey + * @param string $algo + */ + private static function getPBES2EncryptionObject($algo) + { + switch ($algo) { + case 'desCBC': + $cipher = new \FluentSmtpLib\phpseclib3\Crypt\DES('cbc'); + break; + case 'des-EDE3-CBC': + $cipher = new \FluentSmtpLib\phpseclib3\Crypt\TripleDES('cbc'); + break; + case 'rc2CBC': + $cipher = new \FluentSmtpLib\phpseclib3\Crypt\RC2('cbc'); + // in theory this can be changed + $cipher->setKeyLength(128); + break; + case 'rc5-CBC-PAD': + throw new \FluentSmtpLib\phpseclib3\Exception\UnsupportedAlgorithmException('rc5-CBC-PAD is not supported for PBES2 PKCS#8 keys'); + case 'aes128-CBC-PAD': + case 'aes192-CBC-PAD': + case 'aes256-CBC-PAD': + $cipher = new \FluentSmtpLib\phpseclib3\Crypt\AES('cbc'); + $cipher->setKeyLength(\substr($algo, 3, 3)); + break; + default: + throw new \FluentSmtpLib\phpseclib3\Exception\UnsupportedAlgorithmException("{$algo} is not supported"); + } + return $cipher; + } + /** + * Initialize static variables + * + */ + private static function initialize_static_variables() + { + if (!isset(static::$childOIDsLoaded)) { + throw new \FluentSmtpLib\phpseclib3\Exception\InsufficientSetupException('This class should not be called directly'); + } + if (!static::$childOIDsLoaded) { + \FluentSmtpLib\phpseclib3\File\ASN1::loadOIDs(\is_array(static::OID_NAME) ? \array_combine(static::OID_NAME, static::OID_VALUE) : [static::OID_NAME => static::OID_VALUE]); + static::$childOIDsLoaded = \true; + } + if (!self::$oidsLoaded) { + // from https://tools.ietf.org/html/rfc2898 + \FluentSmtpLib\phpseclib3\File\ASN1::loadOIDs([ + // PBES1 encryption schemes + 'pbeWithMD2AndDES-CBC' => '1.2.840.113549.1.5.1', + 'pbeWithMD2AndRC2-CBC' => '1.2.840.113549.1.5.4', + 'pbeWithMD5AndDES-CBC' => '1.2.840.113549.1.5.3', + 'pbeWithMD5AndRC2-CBC' => '1.2.840.113549.1.5.6', + 'pbeWithSHA1AndDES-CBC' => '1.2.840.113549.1.5.10', + 'pbeWithSHA1AndRC2-CBC' => '1.2.840.113549.1.5.11', + // from PKCS#12: + // https://tools.ietf.org/html/rfc7292 + 'pbeWithSHAAnd128BitRC4' => '1.2.840.113549.1.12.1.1', + 'pbeWithSHAAnd40BitRC4' => '1.2.840.113549.1.12.1.2', + 'pbeWithSHAAnd3-KeyTripleDES-CBC' => '1.2.840.113549.1.12.1.3', + 'pbeWithSHAAnd2-KeyTripleDES-CBC' => '1.2.840.113549.1.12.1.4', + 'pbeWithSHAAnd128BitRC2-CBC' => '1.2.840.113549.1.12.1.5', + 'pbeWithSHAAnd40BitRC2-CBC' => '1.2.840.113549.1.12.1.6', + 'id-PBKDF2' => '1.2.840.113549.1.5.12', + 'id-PBES2' => '1.2.840.113549.1.5.13', + 'id-PBMAC1' => '1.2.840.113549.1.5.14', + // from PKCS#5 v2.1: + // http://www.rsa.com/rsalabs/pkcs/files/h11302-wp-pkcs5v2-1-password-based-cryptography-standard.pdf + 'id-hmacWithSHA1' => '1.2.840.113549.2.7', + 'id-hmacWithSHA224' => '1.2.840.113549.2.8', + 'id-hmacWithSHA256' => '1.2.840.113549.2.9', + 'id-hmacWithSHA384' => '1.2.840.113549.2.10', + 'id-hmacWithSHA512' => '1.2.840.113549.2.11', + 'id-hmacWithSHA512-224' => '1.2.840.113549.2.12', + 'id-hmacWithSHA512-256' => '1.2.840.113549.2.13', + 'desCBC' => '1.3.14.3.2.7', + 'des-EDE3-CBC' => '1.2.840.113549.3.7', + 'rc2CBC' => '1.2.840.113549.3.2', + 'rc5-CBC-PAD' => '1.2.840.113549.3.9', + 'aes128-CBC-PAD' => '2.16.840.1.101.3.4.1.2', + 'aes192-CBC-PAD' => '2.16.840.1.101.3.4.1.22', + 'aes256-CBC-PAD' => '2.16.840.1.101.3.4.1.42', + ]); + self::$oidsLoaded = \true; + } + } + /** + * Break a public or private key down into its constituent components + * + * @param string $key + * @param string $password optional + * @return array + */ + protected static function load($key, $password = '') + { + if (!\FluentSmtpLib\phpseclib3\Common\Functions\Strings::is_stringable($key)) { + throw new \UnexpectedValueException('Key should be a string - not a ' . \gettype($key)); + } + $isPublic = \strpos($key, 'PUBLIC') !== \false; + $isPrivate = \strpos($key, 'PRIVATE') !== \false; + $decoded = self::preParse($key); + $meta = []; + $decrypted = \FluentSmtpLib\phpseclib3\File\ASN1::asn1map($decoded[0], \FluentSmtpLib\phpseclib3\File\ASN1\Maps\EncryptedPrivateKeyInfo::MAP); + if (\strlen($password) && \is_array($decrypted)) { + $algorithm = $decrypted['encryptionAlgorithm']['algorithm']; + switch ($algorithm) { + // PBES1 + case 'pbeWithMD2AndDES-CBC': + case 'pbeWithMD2AndRC2-CBC': + case 'pbeWithMD5AndDES-CBC': + case 'pbeWithMD5AndRC2-CBC': + case 'pbeWithSHA1AndDES-CBC': + case 'pbeWithSHA1AndRC2-CBC': + case 'pbeWithSHAAnd3-KeyTripleDES-CBC': + case 'pbeWithSHAAnd2-KeyTripleDES-CBC': + case 'pbeWithSHAAnd128BitRC2-CBC': + case 'pbeWithSHAAnd40BitRC2-CBC': + case 'pbeWithSHAAnd128BitRC4': + case 'pbeWithSHAAnd40BitRC4': + $cipher = self::getPBES1EncryptionObject($algorithm); + $hash = self::getPBES1Hash($algorithm); + $kdf = self::getPBES1KDF($algorithm); + $meta['meta']['algorithm'] = $algorithm; + $temp = \FluentSmtpLib\phpseclib3\File\ASN1::decodeBER($decrypted['encryptionAlgorithm']['parameters']); + if (!$temp) { + throw new \RuntimeException('Unable to decode BER'); + } + \extract(\FluentSmtpLib\phpseclib3\File\ASN1::asn1map($temp[0], \FluentSmtpLib\phpseclib3\File\ASN1\Maps\PBEParameter::MAP)); + $iterationCount = (int) $iterationCount->toString(); + $cipher->setPassword($password, $kdf, $hash, $salt, $iterationCount); + $key = $cipher->decrypt($decrypted['encryptedData']); + $decoded = \FluentSmtpLib\phpseclib3\File\ASN1::decodeBER($key); + if (!$decoded) { + throw new \RuntimeException('Unable to decode BER 2'); + } + break; + case 'id-PBES2': + $meta['meta']['algorithm'] = $algorithm; + $temp = \FluentSmtpLib\phpseclib3\File\ASN1::decodeBER($decrypted['encryptionAlgorithm']['parameters']); + if (!$temp) { + throw new \RuntimeException('Unable to decode BER'); + } + $temp = \FluentSmtpLib\phpseclib3\File\ASN1::asn1map($temp[0], \FluentSmtpLib\phpseclib3\File\ASN1\Maps\PBES2params::MAP); + \extract($temp); + $cipher = self::getPBES2EncryptionObject($encryptionScheme['algorithm']); + $meta['meta']['cipher'] = $encryptionScheme['algorithm']; + $temp = \FluentSmtpLib\phpseclib3\File\ASN1::decodeBER($decrypted['encryptionAlgorithm']['parameters']); + if (!$temp) { + throw new \RuntimeException('Unable to decode BER'); + } + $temp = \FluentSmtpLib\phpseclib3\File\ASN1::asn1map($temp[0], \FluentSmtpLib\phpseclib3\File\ASN1\Maps\PBES2params::MAP); + \extract($temp); + if (!$cipher instanceof \FluentSmtpLib\phpseclib3\Crypt\RC2) { + $cipher->setIV($encryptionScheme['parameters']['octetString']); + } else { + $temp = \FluentSmtpLib\phpseclib3\File\ASN1::decodeBER($encryptionScheme['parameters']); + if (!$temp) { + throw new \RuntimeException('Unable to decode BER'); + } + \extract(\FluentSmtpLib\phpseclib3\File\ASN1::asn1map($temp[0], \FluentSmtpLib\phpseclib3\File\ASN1\Maps\RC2CBCParameter::MAP)); + $effectiveKeyLength = (int) $rc2ParametersVersion->toString(); + switch ($effectiveKeyLength) { + case 160: + $effectiveKeyLength = 40; + break; + case 120: + $effectiveKeyLength = 64; + break; + case 58: + $effectiveKeyLength = 128; + break; + } + $cipher->setIV($iv); + $cipher->setKeyLength($effectiveKeyLength); + } + $meta['meta']['keyDerivationFunc'] = $keyDerivationFunc['algorithm']; + switch ($keyDerivationFunc['algorithm']) { + case 'id-PBKDF2': + $temp = \FluentSmtpLib\phpseclib3\File\ASN1::decodeBER($keyDerivationFunc['parameters']); + if (!$temp) { + throw new \RuntimeException('Unable to decode BER'); + } + $prf = ['algorithm' => 'id-hmacWithSHA1']; + $params = \FluentSmtpLib\phpseclib3\File\ASN1::asn1map($temp[0], \FluentSmtpLib\phpseclib3\File\ASN1\Maps\PBKDF2params::MAP); + \extract($params); + $meta['meta']['prf'] = $prf['algorithm']; + $hash = \str_replace('-', '/', \substr($prf['algorithm'], 11)); + $params = [$password, 'pbkdf2', $hash, $salt, (int) $iterationCount->toString()]; + if (isset($keyLength)) { + $params[] = (int) $keyLength->toString(); + } + $cipher->setPassword(...$params); + $key = $cipher->decrypt($decrypted['encryptedData']); + $decoded = \FluentSmtpLib\phpseclib3\File\ASN1::decodeBER($key); + if (!$decoded) { + throw new \RuntimeException('Unable to decode BER 3'); + } + break; + default: + throw new \FluentSmtpLib\phpseclib3\Exception\UnsupportedAlgorithmException('Only PBKDF2 is supported for PBES2 PKCS#8 keys'); + } + break; + case 'id-PBMAC1': + //$temp = ASN1::decodeBER($decrypted['encryptionAlgorithm']['parameters']); + //$value = ASN1::asn1map($temp[0], Maps\PBMAC1params::MAP); + // since i can't find any implementation that does PBMAC1 it is unsupported + throw new \FluentSmtpLib\phpseclib3\Exception\UnsupportedAlgorithmException('Only PBES1 and PBES2 PKCS#8 keys are supported.'); + } + } + $private = \FluentSmtpLib\phpseclib3\File\ASN1::asn1map($decoded[0], \FluentSmtpLib\phpseclib3\File\ASN1\Maps\OneAsymmetricKey::MAP); + if (\is_array($private)) { + if ($isPublic) { + throw new \UnexpectedValueException('Human readable string claims public key but DER encoded string claims private key'); + } + if (isset($private['privateKeyAlgorithm']['parameters']) && !$private['privateKeyAlgorithm']['parameters'] instanceof \FluentSmtpLib\phpseclib3\File\ASN1\Element && isset($decoded[0]['content'][1]['content'][1])) { + $temp = $decoded[0]['content'][1]['content'][1]; + $private['privateKeyAlgorithm']['parameters'] = new \FluentSmtpLib\phpseclib3\File\ASN1\Element(\substr($key, $temp['start'], $temp['length'])); + } + if (\is_array(static::OID_NAME)) { + if (!\in_array($private['privateKeyAlgorithm']['algorithm'], static::OID_NAME)) { + throw new \FluentSmtpLib\phpseclib3\Exception\UnsupportedAlgorithmException($private['privateKeyAlgorithm']['algorithm'] . ' is not a supported key type'); + } + } else { + if ($private['privateKeyAlgorithm']['algorithm'] != static::OID_NAME) { + throw new \FluentSmtpLib\phpseclib3\Exception\UnsupportedAlgorithmException('Only ' . static::OID_NAME . ' keys are supported; this is a ' . $private['privateKeyAlgorithm']['algorithm'] . ' key'); + } + } + if (isset($private['publicKey'])) { + if ($private['publicKey'][0] != "\x00") { + throw new \UnexpectedValueException('The first byte of the public key should be null - not ' . \bin2hex($private['publicKey'][0])); + } + $private['publicKey'] = \substr($private['publicKey'], 1); + } + return $private + $meta; + } + // EncryptedPrivateKeyInfo and PublicKeyInfo have largely identical "signatures". the only difference + // is that the former has an octet string and the later has a bit string. the first byte of a bit + // string represents the number of bits in the last byte that are to be ignored but, currently, + // bit strings wanting a non-zero amount of bits trimmed are not supported + $public = \FluentSmtpLib\phpseclib3\File\ASN1::asn1map($decoded[0], \FluentSmtpLib\phpseclib3\File\ASN1\Maps\PublicKeyInfo::MAP); + if (\is_array($public)) { + if ($isPrivate) { + throw new \UnexpectedValueException('Human readable string claims private key but DER encoded string claims public key'); + } + if ($public['publicKey'][0] != "\x00") { + throw new \UnexpectedValueException('The first byte of the public key should be null - not ' . \bin2hex($public['publicKey'][0])); + } + if (\is_array(static::OID_NAME)) { + if (!\in_array($public['publicKeyAlgorithm']['algorithm'], static::OID_NAME)) { + throw new \FluentSmtpLib\phpseclib3\Exception\UnsupportedAlgorithmException($public['publicKeyAlgorithm']['algorithm'] . ' is not a supported key type'); + } + } else { + if ($public['publicKeyAlgorithm']['algorithm'] != static::OID_NAME) { + throw new \FluentSmtpLib\phpseclib3\Exception\UnsupportedAlgorithmException('Only ' . static::OID_NAME . ' keys are supported; this is a ' . $public['publicKeyAlgorithm']['algorithm'] . ' key'); + } + } + if (isset($public['publicKeyAlgorithm']['parameters']) && !$public['publicKeyAlgorithm']['parameters'] instanceof \FluentSmtpLib\phpseclib3\File\ASN1\Element && isset($decoded[0]['content'][0]['content'][1])) { + $temp = $decoded[0]['content'][0]['content'][1]; + $public['publicKeyAlgorithm']['parameters'] = new \FluentSmtpLib\phpseclib3\File\ASN1\Element(\substr($key, $temp['start'], $temp['length'])); + } + $public['publicKey'] = \substr($public['publicKey'], 1); + return $public; + } + throw new \RuntimeException('Unable to parse using either OneAsymmetricKey or PublicKeyInfo ASN1 maps'); + } + /** + * Toggle between binary (DER) and printable (PEM) keys + * + * Printable keys are what are generated by default. + * + * @param bool $enabled + */ + public static function setBinaryOutput($enabled) + { + self::$binary = $enabled; + } + /** + * Wrap a private key appropriately + * + * @param string $key + * @param string $attr + * @param mixed $params + * @param string $password + * @param string $oid optional + * @param string $publicKey optional + * @param array $options optional + * @return string + */ + protected static function wrapPrivateKey($key, $attr, $params, $password, $oid = null, $publicKey = '', array $options = []) + { + self::initialize_static_variables(); + $key = ['version' => 'v1', 'privateKeyAlgorithm' => ['algorithm' => \is_string(static::OID_NAME) ? static::OID_NAME : $oid], 'privateKey' => $key]; + if ($oid != 'id-Ed25519' && $oid != 'id-Ed448') { + $key['privateKeyAlgorithm']['parameters'] = $params; + } + if (!empty($attr)) { + $key['attributes'] = $attr; + } + if (!empty($publicKey)) { + $key['version'] = 'v2'; + $key['publicKey'] = $publicKey; + } + $key = \FluentSmtpLib\phpseclib3\File\ASN1::encodeDER($key, \FluentSmtpLib\phpseclib3\File\ASN1\Maps\OneAsymmetricKey::MAP); + if (!empty($password) && \is_string($password)) { + $salt = \FluentSmtpLib\phpseclib3\Crypt\Random::string(8); + $iterationCount = isset($options['iterationCount']) ? $options['iterationCount'] : self::$defaultIterationCount; + $encryptionAlgorithm = isset($options['encryptionAlgorithm']) ? $options['encryptionAlgorithm'] : self::$defaultEncryptionAlgorithm; + $encryptionScheme = isset($options['encryptionScheme']) ? $options['encryptionScheme'] : self::$defaultEncryptionScheme; + $prf = isset($options['PRF']) ? $options['PRF'] : self::$defaultPRF; + if ($encryptionAlgorithm == 'id-PBES2') { + $crypto = self::getPBES2EncryptionObject($encryptionScheme); + $hash = \str_replace('-', '/', \substr($prf, 11)); + $kdf = 'pbkdf2'; + $iv = \FluentSmtpLib\phpseclib3\Crypt\Random::string($crypto->getBlockLength() >> 3); + $PBKDF2params = ['salt' => $salt, 'iterationCount' => $iterationCount, 'prf' => ['algorithm' => $prf, 'parameters' => null]]; + $PBKDF2params = \FluentSmtpLib\phpseclib3\File\ASN1::encodeDER($PBKDF2params, \FluentSmtpLib\phpseclib3\File\ASN1\Maps\PBKDF2params::MAP); + if (!$crypto instanceof \FluentSmtpLib\phpseclib3\Crypt\RC2) { + $params = ['octetString' => $iv]; + } else { + $params = ['rc2ParametersVersion' => 58, 'iv' => $iv]; + $params = \FluentSmtpLib\phpseclib3\File\ASN1::encodeDER($params, \FluentSmtpLib\phpseclib3\File\ASN1\Maps\RC2CBCParameter::MAP); + $params = new \FluentSmtpLib\phpseclib3\File\ASN1\Element($params); + } + $params = ['keyDerivationFunc' => ['algorithm' => 'id-PBKDF2', 'parameters' => new \FluentSmtpLib\phpseclib3\File\ASN1\Element($PBKDF2params)], 'encryptionScheme' => ['algorithm' => $encryptionScheme, 'parameters' => $params]]; + $params = \FluentSmtpLib\phpseclib3\File\ASN1::encodeDER($params, \FluentSmtpLib\phpseclib3\File\ASN1\Maps\PBES2params::MAP); + $crypto->setIV($iv); + } else { + $crypto = self::getPBES1EncryptionObject($encryptionAlgorithm); + $hash = self::getPBES1Hash($encryptionAlgorithm); + $kdf = self::getPBES1KDF($encryptionAlgorithm); + $params = ['salt' => $salt, 'iterationCount' => $iterationCount]; + $params = \FluentSmtpLib\phpseclib3\File\ASN1::encodeDER($params, \FluentSmtpLib\phpseclib3\File\ASN1\Maps\PBEParameter::MAP); + } + $crypto->setPassword($password, $kdf, $hash, $salt, $iterationCount); + $key = $crypto->encrypt($key); + $key = ['encryptionAlgorithm' => ['algorithm' => $encryptionAlgorithm, 'parameters' => new \FluentSmtpLib\phpseclib3\File\ASN1\Element($params)], 'encryptedData' => $key]; + $key = \FluentSmtpLib\phpseclib3\File\ASN1::encodeDER($key, \FluentSmtpLib\phpseclib3\File\ASN1\Maps\EncryptedPrivateKeyInfo::MAP); + if (isset($options['binary']) ? $options['binary'] : self::$binary) { + return $key; + } + return "-----BEGIN ENCRYPTED PRIVATE KEY-----\r\n" . \chunk_split(\FluentSmtpLib\phpseclib3\Common\Functions\Strings::base64_encode($key), 64) . "-----END ENCRYPTED PRIVATE KEY-----"; + } + if (isset($options['binary']) ? $options['binary'] : self::$binary) { + return $key; + } + return "-----BEGIN PRIVATE KEY-----\r\n" . \chunk_split(\FluentSmtpLib\phpseclib3\Common\Functions\Strings::base64_encode($key), 64) . "-----END PRIVATE KEY-----"; + } + /** + * Wrap a public key appropriately + * + * @param string $key + * @param mixed $params + * @param string $oid + * @return string + */ + protected static function wrapPublicKey($key, $params, $oid = null, array $options = []) + { + self::initialize_static_variables(); + $key = ['publicKeyAlgorithm' => ['algorithm' => \is_string(static::OID_NAME) ? static::OID_NAME : $oid], 'publicKey' => "\x00" . $key]; + if ($oid != 'id-Ed25519' && $oid != 'id-Ed448') { + $key['publicKeyAlgorithm']['parameters'] = $params; + } + $key = \FluentSmtpLib\phpseclib3\File\ASN1::encodeDER($key, \FluentSmtpLib\phpseclib3\File\ASN1\Maps\PublicKeyInfo::MAP); + if (isset($options['binary']) ? $options['binary'] : self::$binary) { + return $key; + } + return "-----BEGIN PUBLIC KEY-----\r\n" . \chunk_split(\FluentSmtpLib\phpseclib3\Common\Functions\Strings::base64_encode($key), 64) . "-----END PUBLIC KEY-----"; + } + /** + * Perform some preliminary parsing of the key + * + * @param string $key + * @return array + */ + private static function preParse(&$key) + { + self::initialize_static_variables(); + if (self::$format != self::MODE_DER) { + $decoded = \FluentSmtpLib\phpseclib3\File\ASN1::extractBER($key); + if ($decoded !== \false) { + $key = $decoded; + } elseif (self::$format == self::MODE_PEM) { + throw new \UnexpectedValueException('Expected base64-encoded PEM format but was unable to decode base64 text'); + } + } + $decoded = \FluentSmtpLib\phpseclib3\File\ASN1::decodeBER($key); + if (!$decoded) { + throw new \RuntimeException('Unable to decode BER'); + } + return $decoded; + } + /** + * Returns the encryption parameters used by the key + * + * @param string $key + * @return array + */ + public static function extractEncryptionAlgorithm($key) + { + if (!\FluentSmtpLib\phpseclib3\Common\Functions\Strings::is_stringable($key)) { + throw new \UnexpectedValueException('Key should be a string - not a ' . \gettype($key)); + } + $decoded = self::preParse($key); + $r = \FluentSmtpLib\phpseclib3\File\ASN1::asn1map($decoded[0], \FluentSmtpLib\phpseclib3\File\ASN1\Maps\EncryptedPrivateKeyInfo::MAP); + if (!\is_array($r)) { + throw new \RuntimeException('Unable to parse using EncryptedPrivateKeyInfo map'); + } + if ($r['encryptionAlgorithm']['algorithm'] == 'id-PBES2') { + $decoded = \FluentSmtpLib\phpseclib3\File\ASN1::decodeBER($r['encryptionAlgorithm']['parameters']->element); + if (!$decoded) { + throw new \RuntimeException('Unable to decode BER'); + } + $r['encryptionAlgorithm']['parameters'] = \FluentSmtpLib\phpseclib3\File\ASN1::asn1map($decoded[0], \FluentSmtpLib\phpseclib3\File\ASN1\Maps\PBES2params::MAP); + $kdf =& $r['encryptionAlgorithm']['parameters']['keyDerivationFunc']; + switch ($kdf['algorithm']) { + case 'id-PBKDF2': + $decoded = \FluentSmtpLib\phpseclib3\File\ASN1::decodeBER($kdf['parameters']->element); + if (!$decoded) { + throw new \RuntimeException('Unable to decode BER'); + } + $kdf['parameters'] = \FluentSmtpLib\phpseclib3\File\ASN1::asn1map($decoded[0], \FluentSmtpLib\phpseclib3\File\ASN1\Maps\PBKDF2params::MAP); + } + } + return $r['encryptionAlgorithm']; + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/Common/Formats/Keys/PuTTY.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/Common/Formats/Keys/PuTTY.php new file mode 100644 index 0000000..82b7162 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/Common/Formats/Keys/PuTTY.php @@ -0,0 +1,324 @@ + + * @copyright 2016 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\Crypt\Common\Formats\Keys; + +use FluentSmtpLib\phpseclib3\Common\Functions\Strings; +use FluentSmtpLib\phpseclib3\Crypt\AES; +use FluentSmtpLib\phpseclib3\Crypt\Hash; +use FluentSmtpLib\phpseclib3\Crypt\Random; +use FluentSmtpLib\phpseclib3\Exception\UnsupportedAlgorithmException; +/** + * PuTTY Formatted Key Handler + * + * @author Jim Wigginton + */ +abstract class PuTTY +{ + /** + * Default comment + * + * @var string + */ + private static $comment = 'phpseclib-generated-key'; + /** + * Default version + * + * @var int + */ + private static $version = 2; + /** + * Sets the default comment + * + * @param string $comment + */ + public static function setComment($comment) + { + self::$comment = \str_replace(["\r", "\n"], '', $comment); + } + /** + * Sets the default version + * + * @param int $version + */ + public static function setVersion($version) + { + if ($version != 2 && $version != 3) { + throw new \RuntimeException('Only supported versions are 2 and 3'); + } + self::$version = $version; + } + /** + * Generate a symmetric key for PuTTY v2 keys + * + * @param string $password + * @param int $length + * @return string + */ + private static function generateV2Key($password, $length) + { + $symkey = ''; + $sequence = 0; + while (\strlen($symkey) < $length) { + $temp = \pack('Na*', $sequence++, $password); + $symkey .= \FluentSmtpLib\phpseclib3\Common\Functions\Strings::hex2bin(\sha1($temp)); + } + return \substr($symkey, 0, $length); + } + /** + * Generate a symmetric key for PuTTY v3 keys + * + * @param string $password + * @param string $flavour + * @param int $memory + * @param int $passes + * @param string $salt + * @return array + */ + private static function generateV3Key($password, $flavour, $memory, $passes, $salt) + { + if (!\function_exists('sodium_crypto_pwhash')) { + throw new \RuntimeException('sodium_crypto_pwhash needs to exist for Argon2 password hasing'); + } + switch ($flavour) { + case 'Argon2i': + $flavour = \SODIUM_CRYPTO_PWHASH_ALG_ARGON2I13; + break; + case 'Argon2id': + $flavour = \SODIUM_CRYPTO_PWHASH_ALG_ARGON2ID13; + break; + default: + throw new \FluentSmtpLib\phpseclib3\Exception\UnsupportedAlgorithmException('Only Argon2i and Argon2id are supported'); + } + $length = 80; + // keylen + ivlen + mac_keylen + $temp = \sodium_crypto_pwhash($length, $password, $salt, $passes, $memory << 10, $flavour); + $symkey = \substr($temp, 0, 32); + $symiv = \substr($temp, 32, 16); + $hashkey = \substr($temp, -32); + return \compact('symkey', 'symiv', 'hashkey'); + } + /** + * Break a public or private key down into its constituent components + * + * @param string $key + * @param string $password + * @return array + */ + public static function load($key, $password) + { + if (!\FluentSmtpLib\phpseclib3\Common\Functions\Strings::is_stringable($key)) { + throw new \UnexpectedValueException('Key should be a string - not a ' . \gettype($key)); + } + if (\strpos($key, 'BEGIN SSH2 PUBLIC KEY') !== \false) { + $lines = \preg_split('#[\\r\\n]+#', $key); + switch (\true) { + case $lines[0] != '---- BEGIN SSH2 PUBLIC KEY ----': + throw new \UnexpectedValueException('Key doesn\'t start with ---- BEGIN SSH2 PUBLIC KEY ----'); + case $lines[\count($lines) - 1] != '---- END SSH2 PUBLIC KEY ----': + throw new \UnexpectedValueException('Key doesn\'t end with ---- END SSH2 PUBLIC KEY ----'); + } + $lines = \array_splice($lines, 1, -1); + $lines = \array_map(function ($line) { + return \rtrim($line, "\r\n"); + }, $lines); + $data = $current = ''; + $values = []; + $in_value = \false; + foreach ($lines as $line) { + switch (\true) { + case \preg_match('#^(.*?): (.*)#', $line, $match): + $in_value = $line[\strlen($line) - 1] == '\\'; + $current = \strtolower($match[1]); + $values[$current] = $in_value ? \substr($match[2], 0, -1) : $match[2]; + break; + case $in_value: + $in_value = $line[\strlen($line) - 1] == '\\'; + $values[$current] .= $in_value ? \substr($line, 0, -1) : $line; + break; + default: + $data .= $line; + } + } + $components = \call_user_func([static::PUBLIC_HANDLER, 'load'], $data); + if ($components === \false) { + throw new \UnexpectedValueException('Unable to decode public key'); + } + $components += $values; + $components['comment'] = \str_replace(['\\\\', '\\"'], ['\\', '"'], $values['comment']); + return $components; + } + $components = []; + $key = \preg_split('#\\r\\n|\\r|\\n#', \trim($key)); + if (\FluentSmtpLib\phpseclib3\Common\Functions\Strings::shift($key[0], \strlen('PuTTY-User-Key-File-')) != 'PuTTY-User-Key-File-') { + return \false; + } + $version = (int) \FluentSmtpLib\phpseclib3\Common\Functions\Strings::shift($key[0], 3); + // should be either "2: " or "3: 0" prior to int casting + if ($version != 2 && $version != 3) { + throw new \RuntimeException('Only v2 and v3 PuTTY private keys are supported'); + } + $components['type'] = $type = \rtrim($key[0]); + if (!\in_array($type, static::$types)) { + $error = \count(static::$types) == 1 ? 'Only ' . static::$types[0] . ' keys are supported. ' : ''; + throw new \FluentSmtpLib\phpseclib3\Exception\UnsupportedAlgorithmException($error . 'This is an unsupported ' . $type . ' key'); + } + $encryption = \trim(\preg_replace('#Encryption: (.+)#', '$1', $key[1])); + $components['comment'] = \trim(\preg_replace('#Comment: (.+)#', '$1', $key[2])); + $publicLength = \trim(\preg_replace('#Public-Lines: (\\d+)#', '$1', $key[3])); + $public = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::base64_decode(\implode('', \array_map('trim', \array_slice($key, 4, $publicLength)))); + $source = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::packSSH2('ssss', $type, $encryption, $components['comment'], $public); + \extract(\unpack('Nlength', \FluentSmtpLib\phpseclib3\Common\Functions\Strings::shift($public, 4))); + $newtype = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::shift($public, $length); + if ($newtype != $type) { + throw new \RuntimeException('The binary type does not match the human readable type field'); + } + $components['public'] = $public; + switch ($version) { + case 3: + $hashkey = ''; + break; + case 2: + $hashkey = 'putty-private-key-file-mac-key'; + } + $offset = $publicLength + 4; + switch ($encryption) { + case 'aes256-cbc': + $crypto = new \FluentSmtpLib\phpseclib3\Crypt\AES('cbc'); + switch ($version) { + case 3: + $flavour = \trim(\preg_replace('#Key-Derivation: (.*)#', '$1', $key[$offset++])); + $memory = \trim(\preg_replace('#Argon2-Memory: (\\d+)#', '$1', $key[$offset++])); + $passes = \trim(\preg_replace('#Argon2-Passes: (\\d+)#', '$1', $key[$offset++])); + $parallelism = \trim(\preg_replace('#Argon2-Parallelism: (\\d+)#', '$1', $key[$offset++])); + $salt = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::hex2bin(\trim(\preg_replace('#Argon2-Salt: ([0-9a-f]+)#', '$1', $key[$offset++]))); + \extract(self::generateV3Key($password, $flavour, $memory, $passes, $salt)); + break; + case 2: + $symkey = self::generateV2Key($password, 32); + $symiv = \str_repeat("\x00", $crypto->getBlockLength() >> 3); + $hashkey .= $password; + } + } + switch ($version) { + case 3: + $hash = new \FluentSmtpLib\phpseclib3\Crypt\Hash('sha256'); + $hash->setKey($hashkey); + break; + case 2: + $hash = new \FluentSmtpLib\phpseclib3\Crypt\Hash('sha1'); + $hash->setKey(\sha1($hashkey, \true)); + } + $privateLength = \trim(\preg_replace('#Private-Lines: (\\d+)#', '$1', $key[$offset++])); + $private = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::base64_decode(\implode('', \array_map('trim', \array_slice($key, $offset, $privateLength)))); + if ($encryption != 'none') { + $crypto->setKey($symkey); + $crypto->setIV($symiv); + $crypto->disablePadding(); + $private = $crypto->decrypt($private); + } + $source .= \FluentSmtpLib\phpseclib3\Common\Functions\Strings::packSSH2('s', $private); + $hmac = \trim(\preg_replace('#Private-MAC: (.+)#', '$1', $key[$offset + $privateLength])); + $hmac = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::hex2bin($hmac); + if (!\hash_equals($hash->hash($source), $hmac)) { + throw new \UnexpectedValueException('MAC validation error'); + } + $components['private'] = $private; + return $components; + } + /** + * Wrap a private key appropriately + * + * @param string $public + * @param string $private + * @param string $type + * @param string $password + * @param array $options optional + * @return string + */ + protected static function wrapPrivateKey($public, $private, $type, $password, array $options = []) + { + $encryption = !empty($password) || \is_string($password) ? 'aes256-cbc' : 'none'; + $comment = isset($options['comment']) ? $options['comment'] : self::$comment; + $version = isset($options['version']) ? $options['version'] : self::$version; + $key = "PuTTY-User-Key-File-{$version}: {$type}\r\n"; + $key .= "Encryption: {$encryption}\r\n"; + $key .= "Comment: {$comment}\r\n"; + $public = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::packSSH2('s', $type) . $public; + $source = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::packSSH2('ssss', $type, $encryption, $comment, $public); + $public = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::base64_encode($public); + $key .= "Public-Lines: " . (\strlen($public) + 63 >> 6) . "\r\n"; + $key .= \chunk_split($public, 64); + if (empty($password) && !\is_string($password)) { + $source .= \FluentSmtpLib\phpseclib3\Common\Functions\Strings::packSSH2('s', $private); + switch ($version) { + case 3: + $hash = new \FluentSmtpLib\phpseclib3\Crypt\Hash('sha256'); + $hash->setKey(''); + break; + case 2: + $hash = new \FluentSmtpLib\phpseclib3\Crypt\Hash('sha1'); + $hash->setKey(\sha1('putty-private-key-file-mac-key', \true)); + } + } else { + $private .= \FluentSmtpLib\phpseclib3\Crypt\Random::string(16 - (\strlen($private) & 15)); + $source .= \FluentSmtpLib\phpseclib3\Common\Functions\Strings::packSSH2('s', $private); + $crypto = new \FluentSmtpLib\phpseclib3\Crypt\AES('cbc'); + switch ($version) { + case 3: + $salt = \FluentSmtpLib\phpseclib3\Crypt\Random::string(16); + $key .= "Key-Derivation: Argon2id\r\n"; + $key .= "Argon2-Memory: 8192\r\n"; + $key .= "Argon2-Passes: 13\r\n"; + $key .= "Argon2-Parallelism: 1\r\n"; + $key .= "Argon2-Salt: " . \FluentSmtpLib\phpseclib3\Common\Functions\Strings::bin2hex($salt) . "\r\n"; + \extract(self::generateV3Key($password, 'Argon2id', 8192, 13, $salt)); + $hash = new \FluentSmtpLib\phpseclib3\Crypt\Hash('sha256'); + $hash->setKey($hashkey); + break; + case 2: + $symkey = self::generateV2Key($password, 32); + $symiv = \str_repeat("\x00", $crypto->getBlockLength() >> 3); + $hashkey = 'putty-private-key-file-mac-key' . $password; + $hash = new \FluentSmtpLib\phpseclib3\Crypt\Hash('sha1'); + $hash->setKey(\sha1($hashkey, \true)); + } + $crypto->setKey($symkey); + $crypto->setIV($symiv); + $crypto->disablePadding(); + $private = $crypto->encrypt($private); + $mac = $hash->hash($source); + } + $private = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::base64_encode($private); + $key .= 'Private-Lines: ' . (\strlen($private) + 63 >> 6) . "\r\n"; + $key .= \chunk_split($private, 64); + $key .= 'Private-MAC: ' . \FluentSmtpLib\phpseclib3\Common\Functions\Strings::bin2hex($hash->hash($source)) . "\r\n"; + return $key; + } + /** + * Wrap a public key appropriately + * + * This is basically the format described in RFC 4716 (https://tools.ietf.org/html/rfc4716) + * + * @param string $key + * @param string $type + * @return string + */ + protected static function wrapPublicKey($key, $type) + { + $key = \pack('Na*a*', \strlen($type), $type, $key); + $key = "---- BEGIN SSH2 PUBLIC KEY ----\r\n" . 'Comment: "' . \str_replace(['\\', '"'], ['\\\\', '\\"'], self::$comment) . "\"\r\n" . \chunk_split(\FluentSmtpLib\phpseclib3\Common\Functions\Strings::base64_encode($key), 64) . '---- END SSH2 PUBLIC KEY ----'; + return $key; + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/Common/Formats/Signature/Raw.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/Common/Formats/Signature/Raw.php new file mode 100644 index 0000000..4b86b98 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/Common/Formats/Signature/Raw.php @@ -0,0 +1,53 @@ + + * @copyright 2016 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\Crypt\Common\Formats\Signature; + +use FluentSmtpLib\phpseclib3\Math\BigInteger; +/** + * Raw Signature Handler + * + * @author Jim Wigginton + */ +abstract class Raw +{ + /** + * Loads a signature + * + * @param array $sig + * @return array|bool + */ + public static function load($sig) + { + switch (\true) { + case !\is_array($sig): + case !isset($sig['r']) || !isset($sig['s']): + case !$sig['r'] instanceof \FluentSmtpLib\phpseclib3\Math\BigInteger: + case !$sig['s'] instanceof \FluentSmtpLib\phpseclib3\Math\BigInteger: + return \false; + } + return ['r' => $sig['r'], 's' => $sig['s']]; + } + /** + * Returns a signature in the appropriate format + * + * @param BigInteger $r + * @param BigInteger $s + * @return string + */ + public static function save(\FluentSmtpLib\phpseclib3\Math\BigInteger $r, \FluentSmtpLib\phpseclib3\Math\BigInteger $s) + { + return \compact('r', 's'); + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/Common/PrivateKey.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/Common/PrivateKey.php new file mode 100644 index 0000000..42a83f6 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/Common/PrivateKey.php @@ -0,0 +1,29 @@ + + * @copyright 2009 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\Crypt\Common; + +/** + * PrivateKey interface + * + * @author Jim Wigginton + */ +interface PrivateKey +{ + public function sign($message); + //public function decrypt($ciphertext); + public function getPublicKey(); + public function toString($type, array $options = []); + /** + * @param string|false $password + * @return mixed + */ + public function withPassword($password = \false); +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/Common/PublicKey.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/Common/PublicKey.php new file mode 100644 index 0000000..5ba6ff8 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/Common/PublicKey.php @@ -0,0 +1,24 @@ + + * @copyright 2009 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\Crypt\Common; + +/** + * PublicKey interface + * + * @author Jim Wigginton + */ +interface PublicKey +{ + public function verify($message, $signature); + //public function encrypt($plaintext); + public function toString($type, array $options = []); + public function getFingerprint($algorithm); +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/Common/StreamCipher.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/Common/StreamCipher.php new file mode 100644 index 0000000..a61eb70 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/Common/StreamCipher.php @@ -0,0 +1,51 @@ + + * @author Hans-Juergen Petrich + * @copyright 2007 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\Crypt\Common; + +/** + * Base Class for all stream cipher classes + * + * @author Jim Wigginton + */ +abstract class StreamCipher extends \FluentSmtpLib\phpseclib3\Crypt\Common\SymmetricKey +{ + /** + * Block Length of the cipher + * + * Stream ciphers do not have a block size + * + * @see \phpseclib3\Crypt\Common\SymmetricKey::block_size + * @var int + */ + protected $block_size = 0; + /** + * Default Constructor. + * + * @see \phpseclib3\Crypt\Common\SymmetricKey::__construct() + * @return StreamCipher + */ + public function __construct() + { + parent::__construct('stream'); + } + /** + * Stream ciphers not use an IV + * + * @return bool + */ + public function usesIV() + { + return \false; + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/Common/SymmetricKey.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/Common/SymmetricKey.php new file mode 100644 index 0000000..795b2f1 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/Common/SymmetricKey.php @@ -0,0 +1,3096 @@ + + * @author Hans-Juergen Petrich + * @copyright 2007 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\Crypt\Common; + +use FluentSmtpLib\phpseclib3\Common\Functions\Strings; +use FluentSmtpLib\phpseclib3\Crypt\Blowfish; +use FluentSmtpLib\phpseclib3\Crypt\Hash; +use FluentSmtpLib\phpseclib3\Exception\BadDecryptionException; +use FluentSmtpLib\phpseclib3\Exception\BadModeException; +use FluentSmtpLib\phpseclib3\Exception\InconsistentSetupException; +use FluentSmtpLib\phpseclib3\Exception\InsufficientSetupException; +use FluentSmtpLib\phpseclib3\Exception\UnsupportedAlgorithmException; +use FluentSmtpLib\phpseclib3\Math\BigInteger; +use FluentSmtpLib\phpseclib3\Math\BinaryField; +use FluentSmtpLib\phpseclib3\Math\PrimeField; +/** + * Base Class for all \phpseclib3\Crypt\* cipher classes + * + * @author Jim Wigginton + * @author Hans-Juergen Petrich + */ +abstract class SymmetricKey +{ + /** + * Encrypt / decrypt using the Counter mode. + * + * Set to -1 since that's what Crypt/Random.php uses to index the CTR mode. + * + * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Counter_.28CTR.29 + * @see \phpseclib3\Crypt\Common\SymmetricKey::encrypt() + * @see \phpseclib3\Crypt\Common\SymmetricKey::decrypt() + */ + const MODE_CTR = -1; + /** + * Encrypt / decrypt using the Electronic Code Book mode. + * + * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Electronic_codebook_.28ECB.29 + * @see \phpseclib3\Crypt\Common\SymmetricKey::encrypt() + * @see \phpseclib3\Crypt\Common\SymmetricKey::decrypt() + */ + const MODE_ECB = 1; + /** + * Encrypt / decrypt using the Code Book Chaining mode. + * + * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Cipher-block_chaining_.28CBC.29 + * @see \phpseclib3\Crypt\Common\SymmetricKey::encrypt() + * @see \phpseclib3\Crypt\Common\SymmetricKey::decrypt() + */ + const MODE_CBC = 2; + /** + * Encrypt / decrypt using the Cipher Feedback mode. + * + * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Cipher_feedback_.28CFB.29 + * @see \phpseclib3\Crypt\Common\SymmetricKey::encrypt() + * @see \phpseclib3\Crypt\Common\SymmetricKey::decrypt() + */ + const MODE_CFB = 3; + /** + * Encrypt / decrypt using the Cipher Feedback mode (8bit) + * + * @see \phpseclib3\Crypt\Common\SymmetricKey::encrypt() + * @see \phpseclib3\Crypt\Common\SymmetricKey::decrypt() + */ + const MODE_CFB8 = 7; + /** + * Encrypt / decrypt using the Output Feedback mode (8bit) + * + * @see \phpseclib3\Crypt\Common\SymmetricKey::encrypt() + * @see \phpseclib3\Crypt\Common\SymmetricKey::decrypt() + */ + const MODE_OFB8 = 8; + /** + * Encrypt / decrypt using the Output Feedback mode. + * + * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Output_feedback_.28OFB.29 + * @see \phpseclib3\Crypt\Common\SymmetricKey::encrypt() + * @see \phpseclib3\Crypt\Common\SymmetricKey::decrypt() + */ + const MODE_OFB = 4; + /** + * Encrypt / decrypt using Galois/Counter mode. + * + * @link https://en.wikipedia.org/wiki/Galois/Counter_Mode + * @see \phpseclib3\Crypt\Common\SymmetricKey::encrypt() + * @see \phpseclib3\Crypt\Common\SymmetricKey::decrypt() + */ + const MODE_GCM = 5; + /** + * Encrypt / decrypt using streaming mode. + * + * @see \phpseclib3\Crypt\Common\SymmetricKey::encrypt() + * @see \phpseclib3\Crypt\Common\SymmetricKey::decrypt() + */ + const MODE_STREAM = 6; + /** + * Mode Map + * + * @see \phpseclib3\Crypt\Common\SymmetricKey::__construct() + */ + const MODE_MAP = ['ctr' => self::MODE_CTR, 'ecb' => self::MODE_ECB, 'cbc' => self::MODE_CBC, 'cfb' => self::MODE_CFB, 'cfb8' => self::MODE_CFB8, 'ofb' => self::MODE_OFB, 'ofb8' => self::MODE_OFB8, 'gcm' => self::MODE_GCM, 'stream' => self::MODE_STREAM]; + /** + * Base value for the internal implementation $engine switch + * + * @see \phpseclib3\Crypt\Common\SymmetricKey::__construct() + */ + const ENGINE_INTERNAL = 1; + /** + * Base value for the eval() implementation $engine switch + * + * @see \phpseclib3\Crypt\Common\SymmetricKey::__construct() + */ + const ENGINE_EVAL = 2; + /** + * Base value for the mcrypt implementation $engine switch + * + * @see \phpseclib3\Crypt\Common\SymmetricKey::__construct() + */ + const ENGINE_MCRYPT = 3; + /** + * Base value for the openssl implementation $engine switch + * + * @see \phpseclib3\Crypt\Common\SymmetricKey::__construct() + */ + const ENGINE_OPENSSL = 4; + /** + * Base value for the libsodium implementation $engine switch + * + * @see \phpseclib3\Crypt\Common\SymmetricKey::__construct() + */ + const ENGINE_LIBSODIUM = 5; + /** + * Base value for the openssl / gcm implementation $engine switch + * + * @see \phpseclib3\Crypt\Common\SymmetricKey::__construct() + */ + const ENGINE_OPENSSL_GCM = 6; + /** + * Engine Reverse Map + * + * @see \phpseclib3\Crypt\Common\SymmetricKey::getEngine() + */ + const ENGINE_MAP = [self::ENGINE_INTERNAL => 'PHP', self::ENGINE_EVAL => 'Eval', self::ENGINE_MCRYPT => 'mcrypt', self::ENGINE_OPENSSL => 'OpenSSL', self::ENGINE_LIBSODIUM => 'libsodium', self::ENGINE_OPENSSL_GCM => 'OpenSSL (GCM)']; + /** + * The Encryption Mode + * + * @see self::__construct() + * @var int + */ + protected $mode; + /** + * The Block Length of the block cipher + * + * @var int + */ + protected $block_size = 16; + /** + * The Key + * + * @see self::setKey() + * @var string + */ + protected $key = \false; + /** + * HMAC Key + * + * @see self::setupGCM() + * @var ?string + */ + protected $hKey = \false; + /** + * The Initialization Vector + * + * @see self::setIV() + * @var string + */ + protected $iv = \false; + /** + * A "sliding" Initialization Vector + * + * @see self::enableContinuousBuffer() + * @see self::clearBuffers() + * @var string + */ + protected $encryptIV; + /** + * A "sliding" Initialization Vector + * + * @see self::enableContinuousBuffer() + * @see self::clearBuffers() + * @var string + */ + protected $decryptIV; + /** + * Continuous Buffer status + * + * @see self::enableContinuousBuffer() + * @var bool + */ + protected $continuousBuffer = \false; + /** + * Encryption buffer for CTR, OFB and CFB modes + * + * @see self::encrypt() + * @see self::clearBuffers() + * @var array + */ + protected $enbuffer; + /** + * Decryption buffer for CTR, OFB and CFB modes + * + * @see self::decrypt() + * @see self::clearBuffers() + * @var array + */ + protected $debuffer; + /** + * mcrypt resource for encryption + * + * The mcrypt resource can be recreated every time something needs to be created or it can be created just once. + * Since mcrypt operates in continuous mode, by default, it'll need to be recreated when in non-continuous mode. + * + * @see self::encrypt() + * @var resource + */ + private $enmcrypt; + /** + * mcrypt resource for decryption + * + * The mcrypt resource can be recreated every time something needs to be created or it can be created just once. + * Since mcrypt operates in continuous mode, by default, it'll need to be recreated when in non-continuous mode. + * + * @see self::decrypt() + * @var resource + */ + private $demcrypt; + /** + * Does the enmcrypt resource need to be (re)initialized? + * + * @see \phpseclib3\Crypt\Twofish::setKey() + * @see \phpseclib3\Crypt\Twofish::setIV() + * @var bool + */ + private $enchanged = \true; + /** + * Does the demcrypt resource need to be (re)initialized? + * + * @see \phpseclib3\Crypt\Twofish::setKey() + * @see \phpseclib3\Crypt\Twofish::setIV() + * @var bool + */ + private $dechanged = \true; + /** + * mcrypt resource for CFB mode + * + * mcrypt's CFB mode, in (and only in) buffered context, + * is broken, so phpseclib implements the CFB mode by it self, + * even when the mcrypt php extension is available. + * + * In order to do the CFB-mode work (fast) phpseclib + * use a separate ECB-mode mcrypt resource. + * + * @link http://phpseclib.sourceforge.net/cfb-demo.phps + * @see self::encrypt() + * @see self::decrypt() + * @see self::setupMcrypt() + * @var resource + */ + private $ecb; + /** + * Optimizing value while CFB-encrypting + * + * Only relevant if $continuousBuffer enabled + * and $engine == self::ENGINE_MCRYPT + * + * It's faster to re-init $enmcrypt if + * $buffer bytes > $cfb_init_len than + * using the $ecb resource furthermore. + * + * This value depends of the chosen cipher + * and the time it would be needed for it's + * initialization [by mcrypt_generic_init()] + * which, typically, depends on the complexity + * on its internaly Key-expanding algorithm. + * + * @see self::encrypt() + * @var int + */ + protected $cfb_init_len = 600; + /** + * Does internal cipher state need to be (re)initialized? + * + * @see self::setKey() + * @see self::setIV() + * @see self::disableContinuousBuffer() + * @var bool + */ + protected $changed = \true; + /** + * Does Eval engie need to be (re)initialized? + * + * @see self::setup() + * @var bool + */ + protected $nonIVChanged = \true; + /** + * Padding status + * + * @see self::enablePadding() + * @var bool + */ + private $padding = \true; + /** + * Is the mode one that is paddable? + * + * @see self::__construct() + * @var bool + */ + private $paddable = \false; + /** + * Holds which crypt engine internaly should be use, + * which will be determined automatically on __construct() + * + * Currently available $engines are: + * - self::ENGINE_LIBSODIUM (very fast, php-extension: libsodium, extension_loaded('libsodium') required) + * - self::ENGINE_OPENSSL_GCM (very fast, php-extension: openssl, extension_loaded('openssl') required) + * - self::ENGINE_OPENSSL (very fast, php-extension: openssl, extension_loaded('openssl') required) + * - self::ENGINE_MCRYPT (fast, php-extension: mcrypt, extension_loaded('mcrypt') required) + * - self::ENGINE_EVAL (medium, pure php-engine, no php-extension required) + * - self::ENGINE_INTERNAL (slower, pure php-engine, no php-extension required) + * + * @see self::setEngine() + * @see self::encrypt() + * @see self::decrypt() + * @var int + */ + protected $engine; + /** + * Holds the preferred crypt engine + * + * @see self::setEngine() + * @see self::setPreferredEngine() + * @var int + */ + private $preferredEngine; + /** + * The mcrypt specific name of the cipher + * + * Only used if $engine == self::ENGINE_MCRYPT + * + * @link http://www.php.net/mcrypt_module_open + * @link http://www.php.net/mcrypt_list_algorithms + * @see self::setupMcrypt() + * @var string + */ + protected $cipher_name_mcrypt; + /** + * The openssl specific name of the cipher + * + * Only used if $engine == self::ENGINE_OPENSSL + * + * @link http://www.php.net/openssl-get-cipher-methods + * @var string + */ + protected $cipher_name_openssl; + /** + * The openssl specific name of the cipher in ECB mode + * + * If OpenSSL does not support the mode we're trying to use (CTR) + * it can still be emulated with ECB mode. + * + * @link http://www.php.net/openssl-get-cipher-methods + * @var string + */ + protected $cipher_name_openssl_ecb; + /** + * The default salt used by setPassword() + * + * @see self::setPassword() + * @var string + */ + private $password_default_salt = 'phpseclib/salt'; + /** + * The name of the performance-optimized callback function + * + * Used by encrypt() / decrypt() + * only if $engine == self::ENGINE_INTERNAL + * + * @see self::encrypt() + * @see self::decrypt() + * @see self::setupInlineCrypt() + * @var Callback + */ + protected $inline_crypt; + /** + * If OpenSSL can be used in ECB but not in CTR we can emulate CTR + * + * @see self::openssl_ctr_process() + * @var bool + */ + private $openssl_emulate_ctr = \false; + /** + * Don't truncate / null pad key + * + * @see self::clearBuffers() + * @var bool + */ + private $skip_key_adjustment = \false; + /** + * Has the key length explicitly been set or should it be derived from the key, itself? + * + * @see self::setKeyLength() + * @var bool + */ + protected $explicit_key_length = \false; + /** + * Hash subkey for GHASH + * + * @see self::setupGCM() + * @see self::ghash() + * @var BinaryField\Integer + */ + private $h; + /** + * Additional authenticated data + * + * @var string + */ + protected $aad = ''; + /** + * Authentication Tag produced after a round of encryption + * + * @var string + */ + protected $newtag = \false; + /** + * Authentication Tag to be verified during decryption + * + * @var string + */ + protected $oldtag = \false; + /** + * GCM Binary Field + * + * @see self::__construct() + * @see self::ghash() + * @var BinaryField + */ + private static $gcmField; + /** + * Poly1305 Prime Field + * + * @see self::enablePoly1305() + * @see self::poly1305() + * @var PrimeField + */ + private static $poly1305Field; + /** + * Flag for using regular vs "safe" intval + * + * @see self::initialize_static_variables() + * @var boolean + */ + protected static $use_reg_intval; + /** + * Poly1305 Key + * + * @see self::setPoly1305Key() + * @see self::poly1305() + * @var string + */ + protected $poly1305Key; + /** + * Poly1305 Flag + * + * @see self::setPoly1305Key() + * @see self::enablePoly1305() + * @var boolean + */ + protected $usePoly1305 = \false; + /** + * The Original Initialization Vector + * + * GCM uses the nonce to build the IV but we want to be able to distinguish between nonce-derived + * IV's and user-set IV's + * + * @see self::setIV() + * @var string + */ + private $origIV = \false; + /** + * Nonce + * + * Only used with GCM. We could re-use setIV() but nonce's can be of a different length and + * toggling between GCM and other modes could be more complicated if we re-used setIV() + * + * @see self::setNonce() + * @var string + */ + protected $nonce = \false; + /** + * Default Constructor. + * + * $mode could be: + * + * - ecb + * + * - cbc + * + * - ctr + * + * - cfb + * + * - cfb8 + * + * - ofb + * + * - ofb8 + * + * - gcm + * + * @param string $mode + * @throws BadModeException if an invalid / unsupported mode is provided + */ + public function __construct($mode) + { + $mode = \strtolower($mode); + // necessary because of 5.6 compatibility; we can't do isset(self::MODE_MAP[$mode]) in 5.6 + $map = self::MODE_MAP; + if (!isset($map[$mode])) { + throw new \FluentSmtpLib\phpseclib3\Exception\BadModeException('No valid mode has been specified'); + } + $mode = self::MODE_MAP[$mode]; + // $mode dependent settings + switch ($mode) { + case self::MODE_ECB: + case self::MODE_CBC: + $this->paddable = \true; + break; + case self::MODE_CTR: + case self::MODE_CFB: + case self::MODE_CFB8: + case self::MODE_OFB: + case self::MODE_OFB8: + case self::MODE_STREAM: + $this->paddable = \false; + break; + case self::MODE_GCM: + if ($this->block_size != 16) { + throw new \FluentSmtpLib\phpseclib3\Exception\BadModeException('GCM is only valid for block ciphers with a block size of 128 bits'); + } + if (!isset(self::$gcmField)) { + self::$gcmField = new \FluentSmtpLib\phpseclib3\Math\BinaryField(128, 7, 2, 1, 0); + } + $this->paddable = \false; + break; + default: + throw new \FluentSmtpLib\phpseclib3\Exception\BadModeException('No valid mode has been specified'); + } + $this->mode = $mode; + static::initialize_static_variables(); + } + /** + * Initialize static variables + */ + protected static function initialize_static_variables() + { + if (!isset(self::$use_reg_intval)) { + switch (\true) { + // PHP_OS & "\xDF\xDF\xDF" == strtoupper(substr(PHP_OS, 0, 3)), but a lot faster + case (\PHP_OS & "\xdf\xdf\xdf") === 'WIN': + case !\function_exists('php_uname'): + case !\is_string(\php_uname('m')): + case (\php_uname('m') & "\xdf\xdf\xdf") != 'ARM': + case \defined('PHP_INT_SIZE') && \PHP_INT_SIZE == 8: + self::$use_reg_intval = \true; + break; + case (\php_uname('m') & "\xdf\xdf\xdf") == 'ARM': + switch (\true) { + /* PHP 7.0.0 introduced a bug that affected 32-bit ARM processors: + + https://github.com/php/php-src/commit/716da71446ebbd40fa6cf2cea8a4b70f504cc3cd + + altho the changelogs make no mention of it, this bug was fixed with this commit: + + https://github.com/php/php-src/commit/c1729272b17a1fe893d1a54e423d3b71470f3ee8 + + affected versions of PHP are: 7.0.x, 7.1.0 - 7.1.23 and 7.2.0 - 7.2.11 */ + case \PHP_VERSION_ID >= 70000 && \PHP_VERSION_ID <= 70123: + case \PHP_VERSION_ID >= 70200 && \PHP_VERSION_ID <= 70211: + self::$use_reg_intval = \false; + break; + default: + self::$use_reg_intval = \true; + } + } + } + } + /** + * Sets the initialization vector. + * + * setIV() is not required when ecb or gcm modes are being used. + * + * {@internal Can be overwritten by a sub class, but does not have to be} + * + * @param string $iv + * @throws \LengthException if the IV length isn't equal to the block size + * @throws \BadMethodCallException if an IV is provided when one shouldn't be + */ + public function setIV($iv) + { + if ($this->mode == self::MODE_ECB) { + throw new \BadMethodCallException('This mode does not require an IV.'); + } + if ($this->mode == self::MODE_GCM) { + throw new \BadMethodCallException('Use setNonce instead'); + } + if (!$this->usesIV()) { + throw new \BadMethodCallException('This algorithm does not use an IV.'); + } + if (\strlen($iv) != $this->block_size) { + throw new \LengthException('Received initialization vector of size ' . \strlen($iv) . ', but size ' . $this->block_size . ' is required'); + } + $this->iv = $this->origIV = $iv; + $this->changed = \true; + } + /** + * Enables Poly1305 mode. + * + * Once enabled Poly1305 cannot be disabled. + * + * @throws \BadMethodCallException if Poly1305 is enabled whilst in GCM mode + */ + public function enablePoly1305() + { + if ($this->mode == self::MODE_GCM) { + throw new \BadMethodCallException('Poly1305 cannot be used in GCM mode'); + } + $this->usePoly1305 = \true; + } + /** + * Enables Poly1305 mode. + * + * Once enabled Poly1305 cannot be disabled. If $key is not passed then an attempt to call createPoly1305Key + * will be made. + * + * @param string $key optional + * @throws \LengthException if the key isn't long enough + * @throws \BadMethodCallException if Poly1305 is enabled whilst in GCM mode + */ + public function setPoly1305Key($key = null) + { + if ($this->mode == self::MODE_GCM) { + throw new \BadMethodCallException('Poly1305 cannot be used in GCM mode'); + } + if (!\is_string($key) || \strlen($key) != 32) { + throw new \LengthException('The Poly1305 key must be 32 bytes long (256 bits)'); + } + if (!isset(self::$poly1305Field)) { + // 2^130-5 + self::$poly1305Field = new \FluentSmtpLib\phpseclib3\Math\PrimeField(new \FluentSmtpLib\phpseclib3\Math\BigInteger('3fffffffffffffffffffffffffffffffb', 16)); + } + $this->poly1305Key = $key; + $this->usePoly1305 = \true; + } + /** + * Sets the nonce. + * + * setNonce() is only required when gcm is used + * + * @param string $nonce + * @throws \BadMethodCallException if an nonce is provided when one shouldn't be + */ + public function setNonce($nonce) + { + if ($this->mode != self::MODE_GCM) { + throw new \BadMethodCallException('Nonces are only used in GCM mode.'); + } + $this->nonce = $nonce; + $this->setEngine(); + } + /** + * Sets additional authenticated data + * + * setAAD() is only used by gcm or in poly1305 mode + * + * @param string $aad + * @throws \BadMethodCallException if mode isn't GCM or if poly1305 isn't being utilized + */ + public function setAAD($aad) + { + if ($this->mode != self::MODE_GCM && !$this->usePoly1305) { + throw new \BadMethodCallException('Additional authenticated data is only utilized in GCM mode or with Poly1305'); + } + $this->aad = $aad; + } + /** + * Returns whether or not the algorithm uses an IV + * + * @return bool + */ + public function usesIV() + { + return $this->mode != self::MODE_GCM && $this->mode != self::MODE_ECB; + } + /** + * Returns whether or not the algorithm uses a nonce + * + * @return bool + */ + public function usesNonce() + { + return $this->mode == self::MODE_GCM; + } + /** + * Returns the current key length in bits + * + * @return int + */ + public function getKeyLength() + { + return $this->key_length << 3; + } + /** + * Returns the current block length in bits + * + * @return int + */ + public function getBlockLength() + { + return $this->block_size << 3; + } + /** + * Returns the current block length in bytes + * + * @return int + */ + public function getBlockLengthInBytes() + { + return $this->block_size; + } + /** + * Sets the key length. + * + * Keys with explicitly set lengths need to be treated accordingly + * + * @param int $length + */ + public function setKeyLength($length) + { + $this->explicit_key_length = $length >> 3; + if (\is_string($this->key) && \strlen($this->key) != $this->explicit_key_length) { + $this->key = \false; + throw new \FluentSmtpLib\phpseclib3\Exception\InconsistentSetupException('Key has already been set and is not ' . $this->explicit_key_length . ' bytes long'); + } + } + /** + * Sets the key. + * + * The min/max length(s) of the key depends on the cipher which is used. + * If the key not fits the length(s) of the cipher it will paded with null bytes + * up to the closest valid key length. If the key is more than max length, + * we trim the excess bits. + * + * If the key is not explicitly set, it'll be assumed to be all null bytes. + * + * {@internal Could, but not must, extend by the child Crypt_* class} + * + * @param string $key + */ + public function setKey($key) + { + if ($this->explicit_key_length !== \false && \strlen($key) != $this->explicit_key_length) { + throw new \FluentSmtpLib\phpseclib3\Exception\InconsistentSetupException('Key length has already been set to ' . $this->explicit_key_length . ' bytes and this key is ' . \strlen($key) . ' bytes'); + } + $this->key = $key; + $this->key_length = \strlen($key); + $this->setEngine(); + } + /** + * Sets the password. + * + * Depending on what $method is set to, setPassword()'s (optional) parameters are as follows: + * {@link http://en.wikipedia.org/wiki/PBKDF2 pbkdf2} or pbkdf1: + * $hash, $salt, $count, $dkLen + * + * Where $hash (default = sha1) currently supports the following hashes: see: Crypt/Hash.php + * {@link https://en.wikipedia.org/wiki/Bcrypt bcypt}: + * $salt, $rounds, $keylen + * + * This is a modified version of bcrypt used by OpenSSH. + * + * {@internal Could, but not must, extend by the child Crypt_* class} + * + * @see Crypt/Hash.php + * @param string $password + * @param string $method + * @param int|string ...$func_args + * @throws \LengthException if pbkdf1 is being used and the derived key length exceeds the hash length + * @throws \RuntimeException if bcrypt is being used and a salt isn't provided + * @return bool + */ + public function setPassword($password, $method = 'pbkdf2', ...$func_args) + { + $key = ''; + $method = \strtolower($method); + switch ($method) { + case 'bcrypt': + if (!isset($func_args[2])) { + throw new \RuntimeException('A salt must be provided for bcrypt to work'); + } + $salt = $func_args[0]; + $rounds = isset($func_args[1]) ? $func_args[1] : 16; + $keylen = isset($func_args[2]) ? $func_args[2] : $this->key_length; + $key = \FluentSmtpLib\phpseclib3\Crypt\Blowfish::bcrypt_pbkdf($password, $salt, $keylen + $this->block_size, $rounds); + $this->setKey(\substr($key, 0, $keylen)); + $this->setIV(\substr($key, $keylen)); + return \true; + case 'pkcs12': + // from https://tools.ietf.org/html/rfc7292#appendix-B.2 + case 'pbkdf1': + case 'pbkdf2': + // Hash function + $hash = isset($func_args[0]) ? \strtolower($func_args[0]) : 'sha1'; + $hashObj = new \FluentSmtpLib\phpseclib3\Crypt\Hash(); + $hashObj->setHash($hash); + // WPA and WPA2 use the SSID as the salt + $salt = isset($func_args[1]) ? $func_args[1] : $this->password_default_salt; + // RFC2898#section-4.2 uses 1,000 iterations by default + // WPA and WPA2 use 4,096. + $count = isset($func_args[2]) ? $func_args[2] : 1000; + // Keylength + if (isset($func_args[3])) { + if ($func_args[3] <= 0) { + throw new \LengthException('Derived key length cannot be longer 0 or less'); + } + $dkLen = $func_args[3]; + } else { + $key_length = $this->explicit_key_length !== \false ? $this->explicit_key_length : $this->key_length; + $dkLen = $method == 'pbkdf1' ? 2 * $key_length : $key_length; + } + switch (\true) { + case $method == 'pkcs12': + /* + In this specification, however, all passwords are created from + BMPStrings with a NULL terminator. This means that each character in + the original BMPString is encoded in 2 bytes in big-endian format + (most-significant byte first). There are no Unicode byte order + marks. The 2 bytes produced from the last character in the BMPString + are followed by 2 additional bytes with the value 0x00. + + -- https://tools.ietf.org/html/rfc7292#appendix-B.1 + */ + $password = "\x00" . \chunk_split($password, 1, "\x00") . "\x00"; + /* + This standard specifies 3 different values for the ID byte mentioned + above: + + 1. If ID=1, then the pseudorandom bits being produced are to be used + as key material for performing encryption or decryption. + + 2. If ID=2, then the pseudorandom bits being produced are to be used + as an IV (Initial Value) for encryption or decryption. + + 3. If ID=3, then the pseudorandom bits being produced are to be used + as an integrity key for MACing. + */ + // Construct a string, D (the "diversifier"), by concatenating v/8 + // copies of ID. + $blockLength = $hashObj->getBlockLengthInBytes(); + $d1 = \str_repeat(\chr(1), $blockLength); + $d2 = \str_repeat(\chr(2), $blockLength); + $s = ''; + if (\strlen($salt)) { + while (\strlen($s) < $blockLength) { + $s .= $salt; + } + } + $s = \substr($s, 0, $blockLength); + $p = ''; + if (\strlen($password)) { + while (\strlen($p) < $blockLength) { + $p .= $password; + } + } + $p = \substr($p, 0, $blockLength); + $i = $s . $p; + $this->setKey(self::pkcs12helper($dkLen, $hashObj, $i, $d1, $count)); + if ($this->usesIV()) { + $this->setIV(self::pkcs12helper($this->block_size, $hashObj, $i, $d2, $count)); + } + return \true; + case $method == 'pbkdf1': + if ($dkLen > $hashObj->getLengthInBytes()) { + throw new \LengthException('Derived key length cannot be longer than the hash length'); + } + $t = $password . $salt; + for ($i = 0; $i < $count; ++$i) { + $t = $hashObj->hash($t); + } + $key = \substr($t, 0, $dkLen); + $this->setKey(\substr($key, 0, $dkLen >> 1)); + if ($this->usesIV()) { + $this->setIV(\substr($key, $dkLen >> 1)); + } + return \true; + case !\in_array($hash, \hash_algos()): + $i = 1; + $hashObj->setKey($password); + while (\strlen($key) < $dkLen) { + $f = $u = $hashObj->hash($salt . \pack('N', $i++)); + for ($j = 2; $j <= $count; ++$j) { + $u = $hashObj->hash($u); + $f ^= $u; + } + $key .= $f; + } + $key = \substr($key, 0, $dkLen); + break; + default: + $key = \hash_pbkdf2($hash, $password, $salt, $count, $dkLen, \true); + } + break; + default: + throw new \FluentSmtpLib\phpseclib3\Exception\UnsupportedAlgorithmException($method . ' is not a supported password hashing method'); + } + $this->setKey($key); + return \true; + } + /** + * PKCS#12 KDF Helper Function + * + * As discussed here: + * + * {@link https://tools.ietf.org/html/rfc7292#appendix-B} + * + * @see self::setPassword() + * @param int $n + * @param Hash $hashObj + * @param string $i + * @param string $d + * @param int $count + * @return string $a + */ + private static function pkcs12helper($n, $hashObj, $i, $d, $count) + { + static $one; + if (!isset($one)) { + $one = new \FluentSmtpLib\phpseclib3\Math\BigInteger(1); + } + $blockLength = $hashObj->getBlockLength() >> 3; + $c = \ceil($n / $hashObj->getLengthInBytes()); + $a = ''; + for ($j = 1; $j <= $c; $j++) { + $ai = $d . $i; + for ($k = 0; $k < $count; $k++) { + $ai = $hashObj->hash($ai); + } + $b = ''; + while (\strlen($b) < $blockLength) { + $b .= $ai; + } + $b = \substr($b, 0, $blockLength); + $b = new \FluentSmtpLib\phpseclib3\Math\BigInteger($b, 256); + $newi = ''; + for ($k = 0; $k < \strlen($i); $k += $blockLength) { + $temp = \substr($i, $k, $blockLength); + $temp = new \FluentSmtpLib\phpseclib3\Math\BigInteger($temp, 256); + $temp->setPrecision($blockLength << 3); + $temp = $temp->add($b); + $temp = $temp->add($one); + $newi .= $temp->toBytes(\false); + } + $i = $newi; + $a .= $ai; + } + return \substr($a, 0, $n); + } + /** + * Encrypts a message. + * + * $plaintext will be padded with additional bytes such that it's length is a multiple of the block size. Other cipher + * implementations may or may not pad in the same manner. Other common approaches to padding and the reasons why it's + * necessary are discussed in the following + * URL: + * + * {@link http://www.di-mgt.com.au/cryptopad.html http://www.di-mgt.com.au/cryptopad.html} + * + * An alternative to padding is to, separately, send the length of the file. This is what SSH, in fact, does. + * strlen($plaintext) will still need to be a multiple of the block size, however, arbitrary values can be added to make it that + * length. + * + * {@internal Could, but not must, extend by the child Crypt_* class} + * + * @see self::decrypt() + * @param string $plaintext + * @return string $ciphertext + */ + public function encrypt($plaintext) + { + if ($this->paddable) { + $plaintext = $this->pad($plaintext); + } + $this->setup(); + if ($this->mode == self::MODE_GCM) { + $oldIV = $this->iv; + \FluentSmtpLib\phpseclib3\Common\Functions\Strings::increment_str($this->iv); + $cipher = new static('ctr'); + $cipher->setKey($this->key); + $cipher->setIV($this->iv); + $ciphertext = $cipher->encrypt($plaintext); + $s = $this->ghash(self::nullPad128($this->aad) . self::nullPad128($ciphertext) . self::len64($this->aad) . self::len64($ciphertext)); + $cipher->encryptIV = $this->iv = $this->encryptIV = $this->decryptIV = $oldIV; + $this->newtag = $cipher->encrypt($s); + return $ciphertext; + } + if (isset($this->poly1305Key)) { + $cipher = clone $this; + unset($cipher->poly1305Key); + $this->usePoly1305 = \false; + $ciphertext = $cipher->encrypt($plaintext); + $this->newtag = $this->poly1305($ciphertext); + return $ciphertext; + } + if ($this->engine === self::ENGINE_OPENSSL) { + switch ($this->mode) { + case self::MODE_STREAM: + return \openssl_encrypt($plaintext, $this->cipher_name_openssl, $this->key, \OPENSSL_RAW_DATA | \OPENSSL_ZERO_PADDING); + case self::MODE_ECB: + return \openssl_encrypt($plaintext, $this->cipher_name_openssl, $this->key, \OPENSSL_RAW_DATA | \OPENSSL_ZERO_PADDING); + case self::MODE_CBC: + $result = \openssl_encrypt($plaintext, $this->cipher_name_openssl, $this->key, \OPENSSL_RAW_DATA | \OPENSSL_ZERO_PADDING, $this->encryptIV); + if ($this->continuousBuffer) { + $this->encryptIV = \substr($result, -$this->block_size); + } + return $result; + case self::MODE_CTR: + return $this->openssl_ctr_process($plaintext, $this->encryptIV, $this->enbuffer); + case self::MODE_CFB: + // cfb loosely routines inspired by openssl's: + // {@link http://cvs.openssl.org/fileview?f=openssl/crypto/modes/cfb128.c&v=1.3.2.2.2.1} + $ciphertext = ''; + if ($this->continuousBuffer) { + $iv =& $this->encryptIV; + $pos =& $this->enbuffer['pos']; + } else { + $iv = $this->encryptIV; + $pos = 0; + } + $len = \strlen($plaintext); + $i = 0; + if ($pos) { + $orig_pos = $pos; + $max = $this->block_size - $pos; + if ($len >= $max) { + $i = $max; + $len -= $max; + $pos = 0; + } else { + $i = $len; + $pos += $len; + $len = 0; + } + // ie. $i = min($max, $len), $len-= $i, $pos+= $i, $pos%= $blocksize + $ciphertext = \substr($iv, $orig_pos) ^ $plaintext; + $iv = \substr_replace($iv, $ciphertext, $orig_pos, $i); + $plaintext = \substr($plaintext, $i); + } + $overflow = $len % $this->block_size; + if ($overflow) { + $ciphertext .= \openssl_encrypt(\substr($plaintext, 0, -$overflow) . \str_repeat("\x00", $this->block_size), $this->cipher_name_openssl, $this->key, \OPENSSL_RAW_DATA | \OPENSSL_ZERO_PADDING, $iv); + $iv = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::pop($ciphertext, $this->block_size); + $size = $len - $overflow; + $block = $iv ^ \substr($plaintext, -$overflow); + $iv = \substr_replace($iv, $block, 0, $overflow); + $ciphertext .= $block; + $pos = $overflow; + } elseif ($len) { + $ciphertext = \openssl_encrypt($plaintext, $this->cipher_name_openssl, $this->key, \OPENSSL_RAW_DATA | \OPENSSL_ZERO_PADDING, $iv); + $iv = \substr($ciphertext, -$this->block_size); + } + return $ciphertext; + case self::MODE_CFB8: + $ciphertext = \openssl_encrypt($plaintext, $this->cipher_name_openssl, $this->key, \OPENSSL_RAW_DATA | \OPENSSL_ZERO_PADDING, $this->encryptIV); + if ($this->continuousBuffer) { + if (($len = \strlen($ciphertext)) >= $this->block_size) { + $this->encryptIV = \substr($ciphertext, -$this->block_size); + } else { + $this->encryptIV = \substr($this->encryptIV, $len - $this->block_size) . \substr($ciphertext, -$len); + } + } + return $ciphertext; + case self::MODE_OFB8: + $ciphertext = ''; + $len = \strlen($plaintext); + $iv = $this->encryptIV; + for ($i = 0; $i < $len; ++$i) { + $xor = \openssl_encrypt($iv, $this->cipher_name_openssl_ecb, $this->key, $this->openssl_options, $this->decryptIV); + $ciphertext .= $plaintext[$i] ^ $xor; + $iv = \substr($iv, 1) . $xor[0]; + } + if ($this->continuousBuffer) { + $this->encryptIV = $iv; + } + break; + case self::MODE_OFB: + return $this->openssl_ofb_process($plaintext, $this->encryptIV, $this->enbuffer); + } + } + if ($this->engine === self::ENGINE_MCRYPT) { + \set_error_handler(function () { + }); + if ($this->enchanged) { + \mcrypt_generic_init($this->enmcrypt, $this->key, $this->getIV($this->encryptIV)); + $this->enchanged = \false; + } + // re: {@link http://phpseclib.sourceforge.net/cfb-demo.phps} + // using mcrypt's default handing of CFB the above would output two different things. using phpseclib's + // rewritten CFB implementation the above outputs the same thing twice. + if ($this->mode == self::MODE_CFB && $this->continuousBuffer) { + $block_size = $this->block_size; + $iv =& $this->encryptIV; + $pos =& $this->enbuffer['pos']; + $len = \strlen($plaintext); + $ciphertext = ''; + $i = 0; + if ($pos) { + $orig_pos = $pos; + $max = $block_size - $pos; + if ($len >= $max) { + $i = $max; + $len -= $max; + $pos = 0; + } else { + $i = $len; + $pos += $len; + $len = 0; + } + $ciphertext = \substr($iv, $orig_pos) ^ $plaintext; + $iv = \substr_replace($iv, $ciphertext, $orig_pos, $i); + $this->enbuffer['enmcrypt_init'] = \true; + } + if ($len >= $block_size) { + if ($this->enbuffer['enmcrypt_init'] === \false || $len > $this->cfb_init_len) { + if ($this->enbuffer['enmcrypt_init'] === \true) { + \mcrypt_generic_init($this->enmcrypt, $this->key, $iv); + $this->enbuffer['enmcrypt_init'] = \false; + } + $ciphertext .= \mcrypt_generic($this->enmcrypt, \substr($plaintext, $i, $len - $len % $block_size)); + $iv = \substr($ciphertext, -$block_size); + $len %= $block_size; + } else { + while ($len >= $block_size) { + $iv = \mcrypt_generic($this->ecb, $iv) ^ \substr($plaintext, $i, $block_size); + $ciphertext .= $iv; + $len -= $block_size; + $i += $block_size; + } + } + } + if ($len) { + $iv = \mcrypt_generic($this->ecb, $iv); + $block = $iv ^ \substr($plaintext, -$len); + $iv = \substr_replace($iv, $block, 0, $len); + $ciphertext .= $block; + $pos = $len; + } + \restore_error_handler(); + return $ciphertext; + } + $ciphertext = \mcrypt_generic($this->enmcrypt, $plaintext); + if (!$this->continuousBuffer) { + \mcrypt_generic_init($this->enmcrypt, $this->key, $this->getIV($this->encryptIV)); + } + \restore_error_handler(); + return $ciphertext; + } + if ($this->engine === self::ENGINE_EVAL) { + $inline = $this->inline_crypt; + return $inline('encrypt', $plaintext); + } + $buffer =& $this->enbuffer; + $block_size = $this->block_size; + $ciphertext = ''; + switch ($this->mode) { + case self::MODE_ECB: + for ($i = 0; $i < \strlen($plaintext); $i += $block_size) { + $ciphertext .= $this->encryptBlock(\substr($plaintext, $i, $block_size)); + } + break; + case self::MODE_CBC: + $xor = $this->encryptIV; + for ($i = 0; $i < \strlen($plaintext); $i += $block_size) { + $block = \substr($plaintext, $i, $block_size); + $block = $this->encryptBlock($block ^ $xor); + $xor = $block; + $ciphertext .= $block; + } + if ($this->continuousBuffer) { + $this->encryptIV = $xor; + } + break; + case self::MODE_CTR: + $xor = $this->encryptIV; + if (\strlen($buffer['ciphertext'])) { + for ($i = 0; $i < \strlen($plaintext); $i += $block_size) { + $block = \substr($plaintext, $i, $block_size); + if (\strlen($block) > \strlen($buffer['ciphertext'])) { + $buffer['ciphertext'] .= $this->encryptBlock($xor); + \FluentSmtpLib\phpseclib3\Common\Functions\Strings::increment_str($xor); + } + $key = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::shift($buffer['ciphertext'], $block_size); + $ciphertext .= $block ^ $key; + } + } else { + for ($i = 0; $i < \strlen($plaintext); $i += $block_size) { + $block = \substr($plaintext, $i, $block_size); + $key = $this->encryptBlock($xor); + \FluentSmtpLib\phpseclib3\Common\Functions\Strings::increment_str($xor); + $ciphertext .= $block ^ $key; + } + } + if ($this->continuousBuffer) { + $this->encryptIV = $xor; + if ($start = \strlen($plaintext) % $block_size) { + $buffer['ciphertext'] = \substr($key, $start) . $buffer['ciphertext']; + } + } + break; + case self::MODE_CFB: + // cfb loosely routines inspired by openssl's: + // {@link http://cvs.openssl.org/fileview?f=openssl/crypto/modes/cfb128.c&v=1.3.2.2.2.1} + if ($this->continuousBuffer) { + $iv =& $this->encryptIV; + $pos =& $buffer['pos']; + } else { + $iv = $this->encryptIV; + $pos = 0; + } + $len = \strlen($plaintext); + $i = 0; + if ($pos) { + $orig_pos = $pos; + $max = $block_size - $pos; + if ($len >= $max) { + $i = $max; + $len -= $max; + $pos = 0; + } else { + $i = $len; + $pos += $len; + $len = 0; + } + // ie. $i = min($max, $len), $len-= $i, $pos+= $i, $pos%= $blocksize + $ciphertext = \substr($iv, $orig_pos) ^ $plaintext; + $iv = \substr_replace($iv, $ciphertext, $orig_pos, $i); + } + while ($len >= $block_size) { + $iv = $this->encryptBlock($iv) ^ \substr($plaintext, $i, $block_size); + $ciphertext .= $iv; + $len -= $block_size; + $i += $block_size; + } + if ($len) { + $iv = $this->encryptBlock($iv); + $block = $iv ^ \substr($plaintext, $i); + $iv = \substr_replace($iv, $block, 0, $len); + $ciphertext .= $block; + $pos = $len; + } + break; + case self::MODE_CFB8: + $ciphertext = ''; + $len = \strlen($plaintext); + $iv = $this->encryptIV; + for ($i = 0; $i < $len; ++$i) { + $ciphertext .= $c = $plaintext[$i] ^ $this->encryptBlock($iv); + $iv = \substr($iv, 1) . $c; + } + if ($this->continuousBuffer) { + if ($len >= $block_size) { + $this->encryptIV = \substr($ciphertext, -$block_size); + } else { + $this->encryptIV = \substr($this->encryptIV, $len - $block_size) . \substr($ciphertext, -$len); + } + } + break; + case self::MODE_OFB8: + $ciphertext = ''; + $len = \strlen($plaintext); + $iv = $this->encryptIV; + for ($i = 0; $i < $len; ++$i) { + $xor = $this->encryptBlock($iv); + $ciphertext .= $plaintext[$i] ^ $xor; + $iv = \substr($iv, 1) . $xor[0]; + } + if ($this->continuousBuffer) { + $this->encryptIV = $iv; + } + break; + case self::MODE_OFB: + $xor = $this->encryptIV; + if (\strlen($buffer['xor'])) { + for ($i = 0; $i < \strlen($plaintext); $i += $block_size) { + $block = \substr($plaintext, $i, $block_size); + if (\strlen($block) > \strlen($buffer['xor'])) { + $xor = $this->encryptBlock($xor); + $buffer['xor'] .= $xor; + } + $key = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::shift($buffer['xor'], $block_size); + $ciphertext .= $block ^ $key; + } + } else { + for ($i = 0; $i < \strlen($plaintext); $i += $block_size) { + $xor = $this->encryptBlock($xor); + $ciphertext .= \substr($plaintext, $i, $block_size) ^ $xor; + } + $key = $xor; + } + if ($this->continuousBuffer) { + $this->encryptIV = $xor; + if ($start = \strlen($plaintext) % $block_size) { + $buffer['xor'] = \substr($key, $start) . $buffer['xor']; + } + } + break; + case self::MODE_STREAM: + $ciphertext = $this->encryptBlock($plaintext); + break; + } + return $ciphertext; + } + /** + * Decrypts a message. + * + * If strlen($ciphertext) is not a multiple of the block size, null bytes will be added to the end of the string until + * it is. + * + * {@internal Could, but not must, extend by the child Crypt_* class} + * + * @see self::encrypt() + * @param string $ciphertext + * @return string $plaintext + * @throws \LengthException if we're inside a block cipher and the ciphertext length is not a multiple of the block size + */ + public function decrypt($ciphertext) + { + if ($this->paddable && \strlen($ciphertext) % $this->block_size) { + throw new \LengthException('The ciphertext length (' . \strlen($ciphertext) . ') needs to be a multiple of the block size (' . $this->block_size . ')'); + } + $this->setup(); + if ($this->mode == self::MODE_GCM || isset($this->poly1305Key)) { + if ($this->oldtag === \false) { + throw new \FluentSmtpLib\phpseclib3\Exception\InsufficientSetupException('Authentication Tag has not been set'); + } + if (isset($this->poly1305Key)) { + $newtag = $this->poly1305($ciphertext); + } else { + $oldIV = $this->iv; + \FluentSmtpLib\phpseclib3\Common\Functions\Strings::increment_str($this->iv); + $cipher = new static('ctr'); + $cipher->setKey($this->key); + $cipher->setIV($this->iv); + $plaintext = $cipher->decrypt($ciphertext); + $s = $this->ghash(self::nullPad128($this->aad) . self::nullPad128($ciphertext) . self::len64($this->aad) . self::len64($ciphertext)); + $cipher->encryptIV = $this->iv = $this->encryptIV = $this->decryptIV = $oldIV; + $newtag = $cipher->encrypt($s); + } + if ($this->oldtag != \substr($newtag, 0, \strlen($newtag))) { + $cipher = clone $this; + unset($cipher->poly1305Key); + $this->usePoly1305 = \false; + $plaintext = $cipher->decrypt($ciphertext); + $this->oldtag = \false; + throw new \FluentSmtpLib\phpseclib3\Exception\BadDecryptionException('Derived authentication tag and supplied authentication tag do not match'); + } + $this->oldtag = \false; + return $plaintext; + } + if ($this->engine === self::ENGINE_OPENSSL) { + switch ($this->mode) { + case self::MODE_STREAM: + $plaintext = \openssl_decrypt($ciphertext, $this->cipher_name_openssl, $this->key, \OPENSSL_RAW_DATA | \OPENSSL_ZERO_PADDING); + break; + case self::MODE_ECB: + $plaintext = \openssl_decrypt($ciphertext, $this->cipher_name_openssl, $this->key, \OPENSSL_RAW_DATA | \OPENSSL_ZERO_PADDING); + break; + case self::MODE_CBC: + $offset = $this->block_size; + $plaintext = \openssl_decrypt($ciphertext, $this->cipher_name_openssl, $this->key, \OPENSSL_RAW_DATA | \OPENSSL_ZERO_PADDING, $this->decryptIV); + if ($this->continuousBuffer) { + $this->decryptIV = \substr($ciphertext, -$offset, $this->block_size); + } + break; + case self::MODE_CTR: + $plaintext = $this->openssl_ctr_process($ciphertext, $this->decryptIV, $this->debuffer); + break; + case self::MODE_CFB: + // cfb loosely routines inspired by openssl's: + // {@link http://cvs.openssl.org/fileview?f=openssl/crypto/modes/cfb128.c&v=1.3.2.2.2.1} + $plaintext = ''; + if ($this->continuousBuffer) { + $iv =& $this->decryptIV; + $pos =& $this->debuffer['pos']; + } else { + $iv = $this->decryptIV; + $pos = 0; + } + $len = \strlen($ciphertext); + $i = 0; + if ($pos) { + $orig_pos = $pos; + $max = $this->block_size - $pos; + if ($len >= $max) { + $i = $max; + $len -= $max; + $pos = 0; + } else { + $i = $len; + $pos += $len; + $len = 0; + } + // ie. $i = min($max, $len), $len-= $i, $pos+= $i, $pos%= $this->blocksize + $plaintext = \substr($iv, $orig_pos) ^ $ciphertext; + $iv = \substr_replace($iv, \substr($ciphertext, 0, $i), $orig_pos, $i); + $ciphertext = \substr($ciphertext, $i); + } + $overflow = $len % $this->block_size; + if ($overflow) { + $plaintext .= \openssl_decrypt(\substr($ciphertext, 0, -$overflow), $this->cipher_name_openssl, $this->key, \OPENSSL_RAW_DATA | \OPENSSL_ZERO_PADDING, $iv); + if ($len - $overflow) { + $iv = \substr($ciphertext, -$overflow - $this->block_size, -$overflow); + } + $iv = \openssl_encrypt(\str_repeat("\x00", $this->block_size), $this->cipher_name_openssl, $this->key, \OPENSSL_RAW_DATA | \OPENSSL_ZERO_PADDING, $iv); + $plaintext .= $iv ^ \substr($ciphertext, -$overflow); + $iv = \substr_replace($iv, \substr($ciphertext, -$overflow), 0, $overflow); + $pos = $overflow; + } elseif ($len) { + $plaintext .= \openssl_decrypt($ciphertext, $this->cipher_name_openssl, $this->key, \OPENSSL_RAW_DATA | \OPENSSL_ZERO_PADDING, $iv); + $iv = \substr($ciphertext, -$this->block_size); + } + break; + case self::MODE_CFB8: + $plaintext = \openssl_decrypt($ciphertext, $this->cipher_name_openssl, $this->key, \OPENSSL_RAW_DATA | \OPENSSL_ZERO_PADDING, $this->decryptIV); + if ($this->continuousBuffer) { + if (($len = \strlen($ciphertext)) >= $this->block_size) { + $this->decryptIV = \substr($ciphertext, -$this->block_size); + } else { + $this->decryptIV = \substr($this->decryptIV, $len - $this->block_size) . \substr($ciphertext, -$len); + } + } + break; + case self::MODE_OFB8: + $plaintext = ''; + $len = \strlen($ciphertext); + $iv = $this->decryptIV; + for ($i = 0; $i < $len; ++$i) { + $xor = \openssl_encrypt($iv, $this->cipher_name_openssl_ecb, $this->key, $this->openssl_options, $this->decryptIV); + $plaintext .= $ciphertext[$i] ^ $xor; + $iv = \substr($iv, 1) . $xor[0]; + } + if ($this->continuousBuffer) { + $this->decryptIV = $iv; + } + break; + case self::MODE_OFB: + $plaintext = $this->openssl_ofb_process($ciphertext, $this->decryptIV, $this->debuffer); + } + return $this->paddable ? $this->unpad($plaintext) : $plaintext; + } + if ($this->engine === self::ENGINE_MCRYPT) { + \set_error_handler(function () { + }); + $block_size = $this->block_size; + if ($this->dechanged) { + \mcrypt_generic_init($this->demcrypt, $this->key, $this->getIV($this->decryptIV)); + $this->dechanged = \false; + } + if ($this->mode == self::MODE_CFB && $this->continuousBuffer) { + $iv =& $this->decryptIV; + $pos =& $this->debuffer['pos']; + $len = \strlen($ciphertext); + $plaintext = ''; + $i = 0; + if ($pos) { + $orig_pos = $pos; + $max = $block_size - $pos; + if ($len >= $max) { + $i = $max; + $len -= $max; + $pos = 0; + } else { + $i = $len; + $pos += $len; + $len = 0; + } + // ie. $i = min($max, $len), $len-= $i, $pos+= $i, $pos%= $blocksize + $plaintext = \substr($iv, $orig_pos) ^ $ciphertext; + $iv = \substr_replace($iv, \substr($ciphertext, 0, $i), $orig_pos, $i); + } + if ($len >= $block_size) { + $cb = \substr($ciphertext, $i, $len - $len % $block_size); + $plaintext .= \mcrypt_generic($this->ecb, $iv . $cb) ^ $cb; + $iv = \substr($cb, -$block_size); + $len %= $block_size; + } + if ($len) { + $iv = \mcrypt_generic($this->ecb, $iv); + $plaintext .= $iv ^ \substr($ciphertext, -$len); + $iv = \substr_replace($iv, \substr($ciphertext, -$len), 0, $len); + $pos = $len; + } + \restore_error_handler(); + return $plaintext; + } + $plaintext = \mdecrypt_generic($this->demcrypt, $ciphertext); + if (!$this->continuousBuffer) { + \mcrypt_generic_init($this->demcrypt, $this->key, $this->getIV($this->decryptIV)); + } + \restore_error_handler(); + return $this->paddable ? $this->unpad($plaintext) : $plaintext; + } + if ($this->engine === self::ENGINE_EVAL) { + $inline = $this->inline_crypt; + return $inline('decrypt', $ciphertext); + } + $block_size = $this->block_size; + $buffer =& $this->debuffer; + $plaintext = ''; + switch ($this->mode) { + case self::MODE_ECB: + for ($i = 0; $i < \strlen($ciphertext); $i += $block_size) { + $plaintext .= $this->decryptBlock(\substr($ciphertext, $i, $block_size)); + } + break; + case self::MODE_CBC: + $xor = $this->decryptIV; + for ($i = 0; $i < \strlen($ciphertext); $i += $block_size) { + $block = \substr($ciphertext, $i, $block_size); + $plaintext .= $this->decryptBlock($block) ^ $xor; + $xor = $block; + } + if ($this->continuousBuffer) { + $this->decryptIV = $xor; + } + break; + case self::MODE_CTR: + $xor = $this->decryptIV; + if (\strlen($buffer['ciphertext'])) { + for ($i = 0; $i < \strlen($ciphertext); $i += $block_size) { + $block = \substr($ciphertext, $i, $block_size); + if (\strlen($block) > \strlen($buffer['ciphertext'])) { + $buffer['ciphertext'] .= $this->encryptBlock($xor); + \FluentSmtpLib\phpseclib3\Common\Functions\Strings::increment_str($xor); + } + $key = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::shift($buffer['ciphertext'], $block_size); + $plaintext .= $block ^ $key; + } + } else { + for ($i = 0; $i < \strlen($ciphertext); $i += $block_size) { + $block = \substr($ciphertext, $i, $block_size); + $key = $this->encryptBlock($xor); + \FluentSmtpLib\phpseclib3\Common\Functions\Strings::increment_str($xor); + $plaintext .= $block ^ $key; + } + } + if ($this->continuousBuffer) { + $this->decryptIV = $xor; + if ($start = \strlen($ciphertext) % $block_size) { + $buffer['ciphertext'] = \substr($key, $start) . $buffer['ciphertext']; + } + } + break; + case self::MODE_CFB: + if ($this->continuousBuffer) { + $iv =& $this->decryptIV; + $pos =& $buffer['pos']; + } else { + $iv = $this->decryptIV; + $pos = 0; + } + $len = \strlen($ciphertext); + $i = 0; + if ($pos) { + $orig_pos = $pos; + $max = $block_size - $pos; + if ($len >= $max) { + $i = $max; + $len -= $max; + $pos = 0; + } else { + $i = $len; + $pos += $len; + $len = 0; + } + // ie. $i = min($max, $len), $len-= $i, $pos+= $i, $pos%= $blocksize + $plaintext = \substr($iv, $orig_pos) ^ $ciphertext; + $iv = \substr_replace($iv, \substr($ciphertext, 0, $i), $orig_pos, $i); + } + while ($len >= $block_size) { + $iv = $this->encryptBlock($iv); + $cb = \substr($ciphertext, $i, $block_size); + $plaintext .= $iv ^ $cb; + $iv = $cb; + $len -= $block_size; + $i += $block_size; + } + if ($len) { + $iv = $this->encryptBlock($iv); + $plaintext .= $iv ^ \substr($ciphertext, $i); + $iv = \substr_replace($iv, \substr($ciphertext, $i), 0, $len); + $pos = $len; + } + break; + case self::MODE_CFB8: + $plaintext = ''; + $len = \strlen($ciphertext); + $iv = $this->decryptIV; + for ($i = 0; $i < $len; ++$i) { + $plaintext .= $ciphertext[$i] ^ $this->encryptBlock($iv); + $iv = \substr($iv, 1) . $ciphertext[$i]; + } + if ($this->continuousBuffer) { + if ($len >= $block_size) { + $this->decryptIV = \substr($ciphertext, -$block_size); + } else { + $this->decryptIV = \substr($this->decryptIV, $len - $block_size) . \substr($ciphertext, -$len); + } + } + break; + case self::MODE_OFB8: + $plaintext = ''; + $len = \strlen($ciphertext); + $iv = $this->decryptIV; + for ($i = 0; $i < $len; ++$i) { + $xor = $this->encryptBlock($iv); + $plaintext .= $ciphertext[$i] ^ $xor; + $iv = \substr($iv, 1) . $xor[0]; + } + if ($this->continuousBuffer) { + $this->decryptIV = $iv; + } + break; + case self::MODE_OFB: + $xor = $this->decryptIV; + if (\strlen($buffer['xor'])) { + for ($i = 0; $i < \strlen($ciphertext); $i += $block_size) { + $block = \substr($ciphertext, $i, $block_size); + if (\strlen($block) > \strlen($buffer['xor'])) { + $xor = $this->encryptBlock($xor); + $buffer['xor'] .= $xor; + } + $key = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::shift($buffer['xor'], $block_size); + $plaintext .= $block ^ $key; + } + } else { + for ($i = 0; $i < \strlen($ciphertext); $i += $block_size) { + $xor = $this->encryptBlock($xor); + $plaintext .= \substr($ciphertext, $i, $block_size) ^ $xor; + } + $key = $xor; + } + if ($this->continuousBuffer) { + $this->decryptIV = $xor; + if ($start = \strlen($ciphertext) % $block_size) { + $buffer['xor'] = \substr($key, $start) . $buffer['xor']; + } + } + break; + case self::MODE_STREAM: + $plaintext = $this->decryptBlock($ciphertext); + break; + } + return $this->paddable ? $this->unpad($plaintext) : $plaintext; + } + /** + * Get the authentication tag + * + * Only used in GCM or Poly1305 mode + * + * @see self::encrypt() + * @param int $length optional + * @return string + * @throws \LengthException if $length isn't of a sufficient length + * @throws \RuntimeException if GCM mode isn't being used + */ + public function getTag($length = 16) + { + if ($this->mode != self::MODE_GCM && !$this->usePoly1305) { + throw new \BadMethodCallException('Authentication tags are only utilized in GCM mode or with Poly1305'); + } + if ($this->newtag === \false) { + throw new \BadMethodCallException('A tag can only be returned after a round of encryption has been performed'); + } + // the tag is 128-bits. it can't be greater than 16 bytes because that's bigger than the tag is. if it + // were 0 you might as well be doing CTR and less than 4 provides minimal security that could be trivially + // easily brute forced. + // see https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38d.pdf#page=36 + // for more info + if ($length < 4 || $length > 16) { + throw new \LengthException('The authentication tag must be between 4 and 16 bytes long'); + } + return $length == 16 ? $this->newtag : \substr($this->newtag, 0, $length); + } + /** + * Sets the authentication tag + * + * Only used in GCM mode + * + * @see self::decrypt() + * @param string $tag + * @throws \LengthException if $length isn't of a sufficient length + * @throws \RuntimeException if GCM mode isn't being used + */ + public function setTag($tag) + { + if ($this->usePoly1305 && !isset($this->poly1305Key) && \method_exists($this, 'createPoly1305Key')) { + $this->createPoly1305Key(); + } + if ($this->mode != self::MODE_GCM && !$this->usePoly1305) { + throw new \BadMethodCallException('Authentication tags are only utilized in GCM mode or with Poly1305'); + } + $length = \strlen($tag); + if ($length < 4 || $length > 16) { + throw new \LengthException('The authentication tag must be between 4 and 16 bytes long'); + } + $this->oldtag = $tag; + } + /** + * Get the IV + * + * mcrypt requires an IV even if ECB is used + * + * @see self::encrypt() + * @see self::decrypt() + * @param string $iv + * @return string + */ + protected function getIV($iv) + { + return $this->mode == self::MODE_ECB ? \str_repeat("\x00", $this->block_size) : $iv; + } + /** + * OpenSSL CTR Processor + * + * PHP's OpenSSL bindings do not operate in continuous mode so we'll wrap around it. Since the keystream + * for CTR is the same for both encrypting and decrypting this function is re-used by both SymmetricKey::encrypt() + * and SymmetricKey::decrypt(). Also, OpenSSL doesn't implement CTR for all of it's symmetric ciphers so this + * function will emulate CTR with ECB when necessary. + * + * @see self::encrypt() + * @see self::decrypt() + * @param string $plaintext + * @param string $encryptIV + * @param array $buffer + * @return string + */ + private function openssl_ctr_process($plaintext, &$encryptIV, &$buffer) + { + $ciphertext = ''; + $block_size = $this->block_size; + $key = $this->key; + if ($this->openssl_emulate_ctr) { + $xor = $encryptIV; + if (\strlen($buffer['ciphertext'])) { + for ($i = 0; $i < \strlen($plaintext); $i += $block_size) { + $block = \substr($plaintext, $i, $block_size); + if (\strlen($block) > \strlen($buffer['ciphertext'])) { + $buffer['ciphertext'] .= \openssl_encrypt($xor, $this->cipher_name_openssl_ecb, $key, \OPENSSL_RAW_DATA | \OPENSSL_ZERO_PADDING); + } + \FluentSmtpLib\phpseclib3\Common\Functions\Strings::increment_str($xor); + $otp = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::shift($buffer['ciphertext'], $block_size); + $ciphertext .= $block ^ $otp; + } + } else { + for ($i = 0; $i < \strlen($plaintext); $i += $block_size) { + $block = \substr($plaintext, $i, $block_size); + $otp = \openssl_encrypt($xor, $this->cipher_name_openssl_ecb, $key, \OPENSSL_RAW_DATA | \OPENSSL_ZERO_PADDING); + \FluentSmtpLib\phpseclib3\Common\Functions\Strings::increment_str($xor); + $ciphertext .= $block ^ $otp; + } + } + if ($this->continuousBuffer) { + $encryptIV = $xor; + if ($start = \strlen($plaintext) % $block_size) { + $buffer['ciphertext'] = \substr($key, $start) . $buffer['ciphertext']; + } + } + return $ciphertext; + } + if (\strlen($buffer['ciphertext'])) { + $ciphertext = $plaintext ^ \FluentSmtpLib\phpseclib3\Common\Functions\Strings::shift($buffer['ciphertext'], \strlen($plaintext)); + $plaintext = \substr($plaintext, \strlen($ciphertext)); + if (!\strlen($plaintext)) { + return $ciphertext; + } + } + $overflow = \strlen($plaintext) % $block_size; + if ($overflow) { + $plaintext2 = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::pop($plaintext, $overflow); + // ie. trim $plaintext to a multiple of $block_size and put rest of $plaintext in $plaintext2 + $encrypted = \openssl_encrypt($plaintext . \str_repeat("\x00", $block_size), $this->cipher_name_openssl, $key, \OPENSSL_RAW_DATA | \OPENSSL_ZERO_PADDING, $encryptIV); + $temp = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::pop($encrypted, $block_size); + $ciphertext .= $encrypted . ($plaintext2 ^ $temp); + if ($this->continuousBuffer) { + $buffer['ciphertext'] = \substr($temp, $overflow); + $encryptIV = $temp; + } + } elseif (!\strlen($buffer['ciphertext'])) { + $ciphertext .= \openssl_encrypt($plaintext . \str_repeat("\x00", $block_size), $this->cipher_name_openssl, $key, \OPENSSL_RAW_DATA | \OPENSSL_ZERO_PADDING, $encryptIV); + $temp = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::pop($ciphertext, $block_size); + if ($this->continuousBuffer) { + $encryptIV = $temp; + } + } + if ($this->continuousBuffer) { + $encryptIV = \openssl_decrypt($encryptIV, $this->cipher_name_openssl_ecb, $key, \OPENSSL_RAW_DATA | \OPENSSL_ZERO_PADDING); + if ($overflow) { + \FluentSmtpLib\phpseclib3\Common\Functions\Strings::increment_str($encryptIV); + } + } + return $ciphertext; + } + /** + * OpenSSL OFB Processor + * + * PHP's OpenSSL bindings do not operate in continuous mode so we'll wrap around it. Since the keystream + * for OFB is the same for both encrypting and decrypting this function is re-used by both SymmetricKey::encrypt() + * and SymmetricKey::decrypt(). + * + * @see self::encrypt() + * @see self::decrypt() + * @param string $plaintext + * @param string $encryptIV + * @param array $buffer + * @return string + */ + private function openssl_ofb_process($plaintext, &$encryptIV, &$buffer) + { + if (\strlen($buffer['xor'])) { + $ciphertext = $plaintext ^ $buffer['xor']; + $buffer['xor'] = \substr($buffer['xor'], \strlen($ciphertext)); + $plaintext = \substr($plaintext, \strlen($ciphertext)); + } else { + $ciphertext = ''; + } + $block_size = $this->block_size; + $len = \strlen($plaintext); + $key = $this->key; + $overflow = $len % $block_size; + if (\strlen($plaintext)) { + if ($overflow) { + $ciphertext .= \openssl_encrypt(\substr($plaintext, 0, -$overflow) . \str_repeat("\x00", $block_size), $this->cipher_name_openssl, $key, \OPENSSL_RAW_DATA | \OPENSSL_ZERO_PADDING, $encryptIV); + $xor = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::pop($ciphertext, $block_size); + if ($this->continuousBuffer) { + $encryptIV = $xor; + } + $ciphertext .= \FluentSmtpLib\phpseclib3\Common\Functions\Strings::shift($xor, $overflow) ^ \substr($plaintext, -$overflow); + if ($this->continuousBuffer) { + $buffer['xor'] = $xor; + } + } else { + $ciphertext = \openssl_encrypt($plaintext, $this->cipher_name_openssl, $key, \OPENSSL_RAW_DATA | \OPENSSL_ZERO_PADDING, $encryptIV); + if ($this->continuousBuffer) { + $encryptIV = \substr($ciphertext, -$block_size) ^ \substr($plaintext, -$block_size); + } + } + } + return $ciphertext; + } + /** + * phpseclib <-> OpenSSL Mode Mapper + * + * May need to be overwritten by classes extending this one in some cases + * + * @return string + */ + protected function openssl_translate_mode() + { + switch ($this->mode) { + case self::MODE_ECB: + return 'ecb'; + case self::MODE_CBC: + return 'cbc'; + case self::MODE_CTR: + case self::MODE_GCM: + return 'ctr'; + case self::MODE_CFB: + return 'cfb'; + case self::MODE_CFB8: + return 'cfb8'; + case self::MODE_OFB: + return 'ofb'; + } + } + /** + * Pad "packets". + * + * Block ciphers working by encrypting between their specified [$this->]block_size at a time + * If you ever need to encrypt or decrypt something that isn't of the proper length, it becomes necessary to + * pad the input so that it is of the proper length. + * + * Padding is enabled by default. Sometimes, however, it is undesirable to pad strings. Such is the case in SSH, + * where "packets" are padded with random bytes before being encrypted. Unpad these packets and you risk stripping + * away characters that shouldn't be stripped away. (SSH knows how many bytes are added because the length is + * transmitted separately) + * + * @see self::disablePadding() + */ + public function enablePadding() + { + $this->padding = \true; + } + /** + * Do not pad packets. + * + * @see self::enablePadding() + */ + public function disablePadding() + { + $this->padding = \false; + } + /** + * Treat consecutive "packets" as if they are a continuous buffer. + * + * Say you have a 32-byte plaintext $plaintext. Using the default behavior, the two following code snippets + * will yield different outputs: + * + * + * echo $rijndael->encrypt(substr($plaintext, 0, 16)); + * echo $rijndael->encrypt(substr($plaintext, 16, 16)); + * + * + * echo $rijndael->encrypt($plaintext); + * + * + * The solution is to enable the continuous buffer. Although this will resolve the above discrepancy, it creates + * another, as demonstrated with the following: + * + * + * $rijndael->encrypt(substr($plaintext, 0, 16)); + * echo $rijndael->decrypt($rijndael->encrypt(substr($plaintext, 16, 16))); + * + * + * echo $rijndael->decrypt($rijndael->encrypt(substr($plaintext, 16, 16))); + * + * + * With the continuous buffer disabled, these would yield the same output. With it enabled, they yield different + * outputs. The reason is due to the fact that the initialization vector's change after every encryption / + * decryption round when the continuous buffer is enabled. When it's disabled, they remain constant. + * + * Put another way, when the continuous buffer is enabled, the state of the \phpseclib3\Crypt\*() object changes after each + * encryption / decryption round, whereas otherwise, it'd remain constant. For this reason, it's recommended that + * continuous buffers not be used. They do offer better security and are, in fact, sometimes required (SSH uses them), + * however, they are also less intuitive and more likely to cause you problems. + * + * {@internal Could, but not must, extend by the child Crypt_* class} + * + * @see self::disableContinuousBuffer() + */ + public function enableContinuousBuffer() + { + if ($this->mode == self::MODE_ECB) { + return; + } + if ($this->mode == self::MODE_GCM) { + throw new \BadMethodCallException('This mode does not run in continuous mode'); + } + $this->continuousBuffer = \true; + $this->setEngine(); + } + /** + * Treat consecutive packets as if they are a discontinuous buffer. + * + * The default behavior. + * + * {@internal Could, but not must, extend by the child Crypt_* class} + * + * @see self::enableContinuousBuffer() + */ + public function disableContinuousBuffer() + { + if ($this->mode == self::MODE_ECB) { + return; + } + if (!$this->continuousBuffer) { + return; + } + $this->continuousBuffer = \false; + $this->setEngine(); + } + /** + * Test for engine validity + * + * @see self::__construct() + * @param int $engine + * @return bool + */ + protected function isValidEngineHelper($engine) + { + switch ($engine) { + case self::ENGINE_OPENSSL: + $this->openssl_emulate_ctr = \false; + $result = $this->cipher_name_openssl && \extension_loaded('openssl'); + if (!$result) { + return \false; + } + $methods = \openssl_get_cipher_methods(); + if (\in_array($this->cipher_name_openssl, $methods)) { + return \true; + } + // not all of openssl's symmetric cipher's support ctr. for those + // that don't we'll emulate it + switch ($this->mode) { + case self::MODE_CTR: + if (\in_array($this->cipher_name_openssl_ecb, $methods)) { + $this->openssl_emulate_ctr = \true; + return \true; + } + } + return \false; + case self::ENGINE_MCRYPT: + \set_error_handler(function () { + }); + $result = $this->cipher_name_mcrypt && \extension_loaded('mcrypt') && \in_array($this->cipher_name_mcrypt, \mcrypt_list_algorithms()); + \restore_error_handler(); + return $result; + case self::ENGINE_EVAL: + return \method_exists($this, 'setupInlineCrypt'); + case self::ENGINE_INTERNAL: + return \true; + } + return \false; + } + /** + * Test for engine validity + * + * @see self::__construct() + * @param string $engine + * @return bool + */ + public function isValidEngine($engine) + { + static $reverseMap; + if (!isset($reverseMap)) { + $reverseMap = \array_map('strtolower', self::ENGINE_MAP); + $reverseMap = \array_flip($reverseMap); + } + $engine = \strtolower($engine); + if (!isset($reverseMap[$engine])) { + return \false; + } + return $this->isValidEngineHelper($reverseMap[$engine]); + } + /** + * Sets the preferred crypt engine + * + * Currently, $engine could be: + * + * - libsodium[very fast] + * + * - OpenSSL [very fast] + * + * - mcrypt [fast] + * + * - Eval [slow] + * + * - PHP [slowest] + * + * If the preferred crypt engine is not available the fastest available one will be used + * + * @see self::__construct() + * @param string $engine + */ + public function setPreferredEngine($engine) + { + static $reverseMap; + if (!isset($reverseMap)) { + $reverseMap = \array_map('strtolower', self::ENGINE_MAP); + $reverseMap = \array_flip($reverseMap); + } + $engine = \is_string($engine) ? \strtolower($engine) : ''; + $this->preferredEngine = isset($reverseMap[$engine]) ? $reverseMap[$engine] : self::ENGINE_LIBSODIUM; + $this->setEngine(); + } + /** + * Returns the engine currently being utilized + * + * @see self::setEngine() + */ + public function getEngine() + { + return self::ENGINE_MAP[$this->engine]; + } + /** + * Sets the engine as appropriate + * + * @see self::__construct() + */ + protected function setEngine() + { + $this->engine = null; + $candidateEngines = [self::ENGINE_LIBSODIUM, self::ENGINE_OPENSSL_GCM, self::ENGINE_OPENSSL, self::ENGINE_MCRYPT, self::ENGINE_EVAL]; + if (isset($this->preferredEngine)) { + $temp = [$this->preferredEngine]; + $candidateEngines = \array_merge($temp, \array_diff($candidateEngines, $temp)); + } + foreach ($candidateEngines as $engine) { + if ($this->isValidEngineHelper($engine)) { + $this->engine = $engine; + break; + } + } + if (!$this->engine) { + $this->engine = self::ENGINE_INTERNAL; + } + if ($this->engine != self::ENGINE_MCRYPT && $this->enmcrypt) { + \set_error_handler(function () { + }); + // Closing the current mcrypt resource(s). _mcryptSetup() will, if needed, + // (re)open them with the module named in $this->cipher_name_mcrypt + \mcrypt_module_close($this->enmcrypt); + \mcrypt_module_close($this->demcrypt); + $this->enmcrypt = null; + $this->demcrypt = null; + if ($this->ecb) { + \mcrypt_module_close($this->ecb); + $this->ecb = null; + } + \restore_error_handler(); + } + $this->changed = $this->nonIVChanged = \true; + } + /** + * Encrypts a block + * + * Note: Must be extended by the child \phpseclib3\Crypt\* class + * + * @param string $in + * @return string + */ + protected abstract function encryptBlock($in); + /** + * Decrypts a block + * + * Note: Must be extended by the child \phpseclib3\Crypt\* class + * + * @param string $in + * @return string + */ + protected abstract function decryptBlock($in); + /** + * Setup the key (expansion) + * + * Only used if $engine == self::ENGINE_INTERNAL + * + * Note: Must extend by the child \phpseclib3\Crypt\* class + * + * @see self::setup() + */ + protected abstract function setupKey(); + /** + * Setup the self::ENGINE_INTERNAL $engine + * + * (re)init, if necessary, the internal cipher $engine and flush all $buffers + * Used (only) if $engine == self::ENGINE_INTERNAL + * + * _setup() will be called each time if $changed === true + * typically this happens when using one or more of following public methods: + * + * - setKey() + * + * - setIV() + * + * - disableContinuousBuffer() + * + * - First run of encrypt() / decrypt() with no init-settings + * + * {@internal setup() is always called before en/decryption.} + * + * {@internal Could, but not must, extend by the child Crypt_* class} + * + * @see self::setKey() + * @see self::setIV() + * @see self::disableContinuousBuffer() + */ + protected function setup() + { + if (!$this->changed) { + return; + } + $this->changed = \false; + if ($this->usePoly1305 && !isset($this->poly1305Key) && \method_exists($this, 'createPoly1305Key')) { + $this->createPoly1305Key(); + } + $this->enbuffer = $this->debuffer = ['ciphertext' => '', 'xor' => '', 'pos' => 0, 'enmcrypt_init' => \true]; + //$this->newtag = $this->oldtag = false; + if ($this->usesNonce()) { + if ($this->nonce === \false) { + throw new \FluentSmtpLib\phpseclib3\Exception\InsufficientSetupException('No nonce has been defined'); + } + if ($this->mode == self::MODE_GCM && !\in_array($this->engine, [self::ENGINE_LIBSODIUM, self::ENGINE_OPENSSL_GCM])) { + $this->setupGCM(); + } + } else { + $this->iv = $this->origIV; + } + if ($this->iv === \false && !\in_array($this->mode, [self::MODE_STREAM, self::MODE_ECB])) { + if ($this->mode != self::MODE_GCM || !\in_array($this->engine, [self::ENGINE_LIBSODIUM, self::ENGINE_OPENSSL_GCM])) { + throw new \FluentSmtpLib\phpseclib3\Exception\InsufficientSetupException('No IV has been defined'); + } + } + if ($this->key === \false) { + throw new \FluentSmtpLib\phpseclib3\Exception\InsufficientSetupException('No key has been defined'); + } + $this->encryptIV = $this->decryptIV = $this->iv; + switch ($this->engine) { + case self::ENGINE_MCRYPT: + $this->enchanged = $this->dechanged = \true; + \set_error_handler(function () { + }); + if (!isset($this->enmcrypt)) { + static $mcrypt_modes = [self::MODE_CTR => 'ctr', self::MODE_ECB => \MCRYPT_MODE_ECB, self::MODE_CBC => \MCRYPT_MODE_CBC, self::MODE_CFB => 'ncfb', self::MODE_CFB8 => \MCRYPT_MODE_CFB, self::MODE_OFB => \MCRYPT_MODE_NOFB, self::MODE_OFB8 => \MCRYPT_MODE_OFB, self::MODE_STREAM => \MCRYPT_MODE_STREAM]; + $this->demcrypt = \mcrypt_module_open($this->cipher_name_mcrypt, '', $mcrypt_modes[$this->mode], ''); + $this->enmcrypt = \mcrypt_module_open($this->cipher_name_mcrypt, '', $mcrypt_modes[$this->mode], ''); + // we need the $ecb mcrypt resource (only) in MODE_CFB with enableContinuousBuffer() + // to workaround mcrypt's broken ncfb implementation in buffered mode + // see: {@link http://phpseclib.sourceforge.net/cfb-demo.phps} + if ($this->mode == self::MODE_CFB) { + $this->ecb = \mcrypt_module_open($this->cipher_name_mcrypt, '', \MCRYPT_MODE_ECB, ''); + } + } + // else should mcrypt_generic_deinit be called? + if ($this->mode == self::MODE_CFB) { + \mcrypt_generic_init($this->ecb, $this->key, \str_repeat("\x00", $this->block_size)); + } + \restore_error_handler(); + break; + case self::ENGINE_INTERNAL: + $this->setupKey(); + break; + case self::ENGINE_EVAL: + if ($this->nonIVChanged) { + $this->setupKey(); + $this->setupInlineCrypt(); + } + } + $this->nonIVChanged = \false; + } + /** + * Pads a string + * + * Pads a string using the RSA PKCS padding standards so that its length is a multiple of the blocksize. + * $this->block_size - (strlen($text) % $this->block_size) bytes are added, each of which is equal to + * chr($this->block_size - (strlen($text) % $this->block_size) + * + * If padding is disabled and $text is not a multiple of the blocksize, the string will be padded regardless + * and padding will, hence forth, be enabled. + * + * @see self::unpad() + * @param string $text + * @throws \LengthException if padding is disabled and the plaintext's length is not a multiple of the block size + * @return string + */ + protected function pad($text) + { + $length = \strlen($text); + if (!$this->padding) { + if ($length % $this->block_size == 0) { + return $text; + } else { + throw new \LengthException("The plaintext's length ({$length}) is not a multiple of the block size ({$this->block_size}). Try enabling padding."); + } + } + $pad = $this->block_size - $length % $this->block_size; + return \str_pad($text, $length + $pad, \chr($pad)); + } + /** + * Unpads a string. + * + * If padding is enabled and the reported padding length is invalid the encryption key will be assumed to be wrong + * and false will be returned. + * + * @see self::pad() + * @param string $text + * @throws \LengthException if the ciphertext's length is not a multiple of the block size + * @return string + */ + protected function unpad($text) + { + if (!$this->padding) { + return $text; + } + $length = \ord($text[\strlen($text) - 1]); + if (!$length || $length > $this->block_size) { + throw new \FluentSmtpLib\phpseclib3\Exception\BadDecryptionException("The ciphertext has an invalid padding length ({$length}) compared to the block size ({$this->block_size})"); + } + return \substr($text, 0, -$length); + } + /** + * Setup the performance-optimized function for de/encrypt() + * + * Stores the created (or existing) callback function-name + * in $this->inline_crypt + * + * Internally for phpseclib developers: + * + * _setupInlineCrypt() would be called only if: + * + * - $this->engine === self::ENGINE_EVAL + * + * - each time on _setup(), after(!) _setupKey() + * + * + * This ensures that _setupInlineCrypt() has always a + * full ready2go initializated internal cipher $engine state + * where, for example, the keys already expanded, + * keys/block_size calculated and such. + * + * It is, each time if called, the responsibility of _setupInlineCrypt(): + * + * - to set $this->inline_crypt to a valid and fully working callback function + * as a (faster) replacement for encrypt() / decrypt() + * + * - NOT to create unlimited callback functions (for memory reasons!) + * no matter how often _setupInlineCrypt() would be called. At some + * point of amount they must be generic re-useable. + * + * - the code of _setupInlineCrypt() it self, + * and the generated callback code, + * must be, in following order: + * - 100% safe + * - 100% compatible to encrypt()/decrypt() + * - using only php5+ features/lang-constructs/php-extensions if + * compatibility (down to php4) or fallback is provided + * - readable/maintainable/understandable/commented and... not-cryptic-styled-code :-) + * - >= 10% faster than encrypt()/decrypt() [which is, by the way, + * the reason for the existence of _setupInlineCrypt() :-)] + * - memory-nice + * - short (as good as possible) + * + * Note: - _setupInlineCrypt() is using _createInlineCryptFunction() to create the full callback function code. + * - In case of using inline crypting, _setupInlineCrypt() must extend by the child \phpseclib3\Crypt\* class. + * - The following variable names are reserved: + * - $_* (all variable names prefixed with an underscore) + * - $self (object reference to it self. Do not use $this, but $self instead) + * - $in (the content of $in has to en/decrypt by the generated code) + * - The callback function should not use the 'return' statement, but en/decrypt'ing the content of $in only + * + * {@internal If a Crypt_* class providing inline crypting it must extend _setupInlineCrypt()} + * + * @see self::setup() + * @see self::createInlineCryptFunction() + * @see self::encrypt() + * @see self::decrypt() + */ + //protected function setupInlineCrypt(); + /** + * Creates the performance-optimized function for en/decrypt() + * + * Internally for phpseclib developers: + * + * _createInlineCryptFunction(): + * + * - merge the $cipher_code [setup'ed by _setupInlineCrypt()] + * with the current [$this->]mode of operation code + * + * - create the $inline function, which called by encrypt() / decrypt() + * as its replacement to speed up the en/decryption operations. + * + * - return the name of the created $inline callback function + * + * - used to speed up en/decryption + * + * + * + * The main reason why can speed up things [up to 50%] this way are: + * + * - using variables more effective then regular. + * (ie no use of expensive arrays but integers $k_0, $k_1 ... + * or even, for example, the pure $key[] values hardcoded) + * + * - avoiding 1000's of function calls of ie _encryptBlock() + * but inlining the crypt operations. + * in the mode of operation for() loop. + * + * - full loop unroll the (sometimes key-dependent) rounds + * avoiding this way ++$i counters and runtime-if's etc... + * + * The basic code architectur of the generated $inline en/decrypt() + * lambda function, in pseudo php, is: + * + * + * +----------------------------------------------------------------------------------------------+ + * | callback $inline = create_function: | + * | lambda_function_0001_crypt_ECB($action, $text) | + * | { | + * | INSERT PHP CODE OF: | + * | $cipher_code['init_crypt']; // general init code. | + * | // ie: $sbox'es declarations used for | + * | // encrypt and decrypt'ing. | + * | | + * | switch ($action) { | + * | case 'encrypt': | + * | INSERT PHP CODE OF: | + * | $cipher_code['init_encrypt']; // encrypt sepcific init code. | + * | ie: specified $key or $box | + * | declarations for encrypt'ing. | + * | | + * | foreach ($ciphertext) { | + * | $in = $block_size of $ciphertext; | + * | | + * | INSERT PHP CODE OF: | + * | $cipher_code['encrypt_block']; // encrypt's (string) $in, which is always: | + * | // strlen($in) == $this->block_size | + * | // here comes the cipher algorithm in action | + * | // for encryption. | + * | // $cipher_code['encrypt_block'] has to | + * | // encrypt the content of the $in variable | + * | | + * | $plaintext .= $in; | + * | } | + * | return $plaintext; | + * | | + * | case 'decrypt': | + * | INSERT PHP CODE OF: | + * | $cipher_code['init_decrypt']; // decrypt sepcific init code | + * | ie: specified $key or $box | + * | declarations for decrypt'ing. | + * | foreach ($plaintext) { | + * | $in = $block_size of $plaintext; | + * | | + * | INSERT PHP CODE OF: | + * | $cipher_code['decrypt_block']; // decrypt's (string) $in, which is always | + * | // strlen($in) == $this->block_size | + * | // here comes the cipher algorithm in action | + * | // for decryption. | + * | // $cipher_code['decrypt_block'] has to | + * | // decrypt the content of the $in variable | + * | $ciphertext .= $in; | + * | } | + * | return $ciphertext; | + * | } | + * | } | + * +----------------------------------------------------------------------------------------------+ + * + * + * See also the \phpseclib3\Crypt\*::_setupInlineCrypt()'s for + * productive inline $cipher_code's how they works. + * + * Structure of: + * + * $cipher_code = [ + * 'init_crypt' => (string) '', // optional + * 'init_encrypt' => (string) '', // optional + * 'init_decrypt' => (string) '', // optional + * 'encrypt_block' => (string) '', // required + * 'decrypt_block' => (string) '' // required + * ]; + * + * + * @see self::setupInlineCrypt() + * @see self::encrypt() + * @see self::decrypt() + * @param array $cipher_code + * @return string (the name of the created callback function) + */ + protected function createInlineCryptFunction($cipher_code) + { + $block_size = $this->block_size; + // optional + $init_crypt = isset($cipher_code['init_crypt']) ? $cipher_code['init_crypt'] : ''; + $init_encrypt = isset($cipher_code['init_encrypt']) ? $cipher_code['init_encrypt'] : ''; + $init_decrypt = isset($cipher_code['init_decrypt']) ? $cipher_code['init_decrypt'] : ''; + // required + $encrypt_block = $cipher_code['encrypt_block']; + $decrypt_block = $cipher_code['decrypt_block']; + // Generating mode of operation inline code, + // merged with the $cipher_code algorithm + // for encrypt- and decryption. + switch ($this->mode) { + case self::MODE_ECB: + $encrypt = $init_encrypt . ' + $_ciphertext = ""; + $_plaintext_len = strlen($_text); + + for ($_i = 0; $_i < $_plaintext_len; $_i+= ' . $block_size . ') { + $in = substr($_text, $_i, ' . $block_size . '); + ' . $encrypt_block . ' + $_ciphertext.= $in; + } + + return $_ciphertext; + '; + $decrypt = $init_decrypt . ' + $_plaintext = ""; + $_text = str_pad($_text, strlen($_text) + (' . $block_size . ' - strlen($_text) % ' . $block_size . ') % ' . $block_size . ', chr(0)); + $_ciphertext_len = strlen($_text); + + for ($_i = 0; $_i < $_ciphertext_len; $_i+= ' . $block_size . ') { + $in = substr($_text, $_i, ' . $block_size . '); + ' . $decrypt_block . ' + $_plaintext.= $in; + } + + return $this->unpad($_plaintext); + '; + break; + case self::MODE_CTR: + $encrypt = $init_encrypt . ' + $_ciphertext = ""; + $_plaintext_len = strlen($_text); + $_xor = $this->encryptIV; + $_buffer = &$this->enbuffer; + if (strlen($_buffer["ciphertext"])) { + for ($_i = 0; $_i < $_plaintext_len; $_i+= ' . $block_size . ') { + $_block = substr($_text, $_i, ' . $block_size . '); + if (strlen($_block) > strlen($_buffer["ciphertext"])) { + $in = $_xor; + ' . $encrypt_block . ' + \\phpseclib3\\Common\\Functions\\Strings::increment_str($_xor); + $_buffer["ciphertext"].= $in; + } + $_key = \\phpseclib3\\Common\\Functions\\Strings::shift($_buffer["ciphertext"], ' . $block_size . '); + $_ciphertext.= $_block ^ $_key; + } + } else { + for ($_i = 0; $_i < $_plaintext_len; $_i+= ' . $block_size . ') { + $_block = substr($_text, $_i, ' . $block_size . '); + $in = $_xor; + ' . $encrypt_block . ' + \\phpseclib3\\Common\\Functions\\Strings::increment_str($_xor); + $_key = $in; + $_ciphertext.= $_block ^ $_key; + } + } + if ($this->continuousBuffer) { + $this->encryptIV = $_xor; + if ($_start = $_plaintext_len % ' . $block_size . ') { + $_buffer["ciphertext"] = substr($_key, $_start) . $_buffer["ciphertext"]; + } + } + + return $_ciphertext; + '; + $decrypt = $init_encrypt . ' + $_plaintext = ""; + $_ciphertext_len = strlen($_text); + $_xor = $this->decryptIV; + $_buffer = &$this->debuffer; + + if (strlen($_buffer["ciphertext"])) { + for ($_i = 0; $_i < $_ciphertext_len; $_i+= ' . $block_size . ') { + $_block = substr($_text, $_i, ' . $block_size . '); + if (strlen($_block) > strlen($_buffer["ciphertext"])) { + $in = $_xor; + ' . $encrypt_block . ' + \\phpseclib3\\Common\\Functions\\Strings::increment_str($_xor); + $_buffer["ciphertext"].= $in; + } + $_key = \\phpseclib3\\Common\\Functions\\Strings::shift($_buffer["ciphertext"], ' . $block_size . '); + $_plaintext.= $_block ^ $_key; + } + } else { + for ($_i = 0; $_i < $_ciphertext_len; $_i+= ' . $block_size . ') { + $_block = substr($_text, $_i, ' . $block_size . '); + $in = $_xor; + ' . $encrypt_block . ' + \\phpseclib3\\Common\\Functions\\Strings::increment_str($_xor); + $_key = $in; + $_plaintext.= $_block ^ $_key; + } + } + if ($this->continuousBuffer) { + $this->decryptIV = $_xor; + if ($_start = $_ciphertext_len % ' . $block_size . ') { + $_buffer["ciphertext"] = substr($_key, $_start) . $_buffer["ciphertext"]; + } + } + + return $_plaintext; + '; + break; + case self::MODE_CFB: + $encrypt = $init_encrypt . ' + $_ciphertext = ""; + $_buffer = &$this->enbuffer; + + if ($this->continuousBuffer) { + $_iv = &$this->encryptIV; + $_pos = &$_buffer["pos"]; + } else { + $_iv = $this->encryptIV; + $_pos = 0; + } + $_len = strlen($_text); + $_i = 0; + if ($_pos) { + $_orig_pos = $_pos; + $_max = ' . $block_size . ' - $_pos; + if ($_len >= $_max) { + $_i = $_max; + $_len-= $_max; + $_pos = 0; + } else { + $_i = $_len; + $_pos+= $_len; + $_len = 0; + } + $_ciphertext = substr($_iv, $_orig_pos) ^ $_text; + $_iv = substr_replace($_iv, $_ciphertext, $_orig_pos, $_i); + } + while ($_len >= ' . $block_size . ') { + $in = $_iv; + ' . $encrypt_block . '; + $_iv = $in ^ substr($_text, $_i, ' . $block_size . '); + $_ciphertext.= $_iv; + $_len-= ' . $block_size . '; + $_i+= ' . $block_size . '; + } + if ($_len) { + $in = $_iv; + ' . $encrypt_block . ' + $_iv = $in; + $_block = $_iv ^ substr($_text, $_i); + $_iv = substr_replace($_iv, $_block, 0, $_len); + $_ciphertext.= $_block; + $_pos = $_len; + } + return $_ciphertext; + '; + $decrypt = $init_encrypt . ' + $_plaintext = ""; + $_buffer = &$this->debuffer; + + if ($this->continuousBuffer) { + $_iv = &$this->decryptIV; + $_pos = &$_buffer["pos"]; + } else { + $_iv = $this->decryptIV; + $_pos = 0; + } + $_len = strlen($_text); + $_i = 0; + if ($_pos) { + $_orig_pos = $_pos; + $_max = ' . $block_size . ' - $_pos; + if ($_len >= $_max) { + $_i = $_max; + $_len-= $_max; + $_pos = 0; + } else { + $_i = $_len; + $_pos+= $_len; + $_len = 0; + } + $_plaintext = substr($_iv, $_orig_pos) ^ $_text; + $_iv = substr_replace($_iv, substr($_text, 0, $_i), $_orig_pos, $_i); + } + while ($_len >= ' . $block_size . ') { + $in = $_iv; + ' . $encrypt_block . ' + $_iv = $in; + $cb = substr($_text, $_i, ' . $block_size . '); + $_plaintext.= $_iv ^ $cb; + $_iv = $cb; + $_len-= ' . $block_size . '; + $_i+= ' . $block_size . '; + } + if ($_len) { + $in = $_iv; + ' . $encrypt_block . ' + $_iv = $in; + $_plaintext.= $_iv ^ substr($_text, $_i); + $_iv = substr_replace($_iv, substr($_text, $_i), 0, $_len); + $_pos = $_len; + } + + return $_plaintext; + '; + break; + case self::MODE_CFB8: + $encrypt = $init_encrypt . ' + $_ciphertext = ""; + $_len = strlen($_text); + $_iv = $this->encryptIV; + + for ($_i = 0; $_i < $_len; ++$_i) { + $in = $_iv; + ' . $encrypt_block . ' + $_ciphertext .= ($_c = $_text[$_i] ^ $in); + $_iv = substr($_iv, 1) . $_c; + } + + if ($this->continuousBuffer) { + if ($_len >= ' . $block_size . ') { + $this->encryptIV = substr($_ciphertext, -' . $block_size . '); + } else { + $this->encryptIV = substr($this->encryptIV, $_len - ' . $block_size . ') . substr($_ciphertext, -$_len); + } + } + + return $_ciphertext; + '; + $decrypt = $init_encrypt . ' + $_plaintext = ""; + $_len = strlen($_text); + $_iv = $this->decryptIV; + + for ($_i = 0; $_i < $_len; ++$_i) { + $in = $_iv; + ' . $encrypt_block . ' + $_plaintext .= $_text[$_i] ^ $in; + $_iv = substr($_iv, 1) . $_text[$_i]; + } + + if ($this->continuousBuffer) { + if ($_len >= ' . $block_size . ') { + $this->decryptIV = substr($_text, -' . $block_size . '); + } else { + $this->decryptIV = substr($this->decryptIV, $_len - ' . $block_size . ') . substr($_text, -$_len); + } + } + + return $_plaintext; + '; + break; + case self::MODE_OFB8: + $encrypt = $init_encrypt . ' + $_ciphertext = ""; + $_len = strlen($_text); + $_iv = $this->encryptIV; + + for ($_i = 0; $_i < $_len; ++$_i) { + $in = $_iv; + ' . $encrypt_block . ' + $_ciphertext.= $_text[$_i] ^ $in; + $_iv = substr($_iv, 1) . $in[0]; + } + + if ($this->continuousBuffer) { + $this->encryptIV = $_iv; + } + + return $_ciphertext; + '; + $decrypt = $init_encrypt . ' + $_plaintext = ""; + $_len = strlen($_text); + $_iv = $this->decryptIV; + + for ($_i = 0; $_i < $_len; ++$_i) { + $in = $_iv; + ' . $encrypt_block . ' + $_plaintext.= $_text[$_i] ^ $in; + $_iv = substr($_iv, 1) . $in[0]; + } + + if ($this->continuousBuffer) { + $this->decryptIV = $_iv; + } + + return $_plaintext; + '; + break; + case self::MODE_OFB: + $encrypt = $init_encrypt . ' + $_ciphertext = ""; + $_plaintext_len = strlen($_text); + $_xor = $this->encryptIV; + $_buffer = &$this->enbuffer; + + if (strlen($_buffer["xor"])) { + for ($_i = 0; $_i < $_plaintext_len; $_i+= ' . $block_size . ') { + $_block = substr($_text, $_i, ' . $block_size . '); + if (strlen($_block) > strlen($_buffer["xor"])) { + $in = $_xor; + ' . $encrypt_block . ' + $_xor = $in; + $_buffer["xor"].= $_xor; + } + $_key = \\phpseclib3\\Common\\Functions\\Strings::shift($_buffer["xor"], ' . $block_size . '); + $_ciphertext.= $_block ^ $_key; + } + } else { + for ($_i = 0; $_i < $_plaintext_len; $_i+= ' . $block_size . ') { + $in = $_xor; + ' . $encrypt_block . ' + $_xor = $in; + $_ciphertext.= substr($_text, $_i, ' . $block_size . ') ^ $_xor; + } + $_key = $_xor; + } + if ($this->continuousBuffer) { + $this->encryptIV = $_xor; + if ($_start = $_plaintext_len % ' . $block_size . ') { + $_buffer["xor"] = substr($_key, $_start) . $_buffer["xor"]; + } + } + return $_ciphertext; + '; + $decrypt = $init_encrypt . ' + $_plaintext = ""; + $_ciphertext_len = strlen($_text); + $_xor = $this->decryptIV; + $_buffer = &$this->debuffer; + + if (strlen($_buffer["xor"])) { + for ($_i = 0; $_i < $_ciphertext_len; $_i+= ' . $block_size . ') { + $_block = substr($_text, $_i, ' . $block_size . '); + if (strlen($_block) > strlen($_buffer["xor"])) { + $in = $_xor; + ' . $encrypt_block . ' + $_xor = $in; + $_buffer["xor"].= $_xor; + } + $_key = \\phpseclib3\\Common\\Functions\\Strings::shift($_buffer["xor"], ' . $block_size . '); + $_plaintext.= $_block ^ $_key; + } + } else { + for ($_i = 0; $_i < $_ciphertext_len; $_i+= ' . $block_size . ') { + $in = $_xor; + ' . $encrypt_block . ' + $_xor = $in; + $_plaintext.= substr($_text, $_i, ' . $block_size . ') ^ $_xor; + } + $_key = $_xor; + } + if ($this->continuousBuffer) { + $this->decryptIV = $_xor; + if ($_start = $_ciphertext_len % ' . $block_size . ') { + $_buffer["xor"] = substr($_key, $_start) . $_buffer["xor"]; + } + } + return $_plaintext; + '; + break; + case self::MODE_STREAM: + $encrypt = $init_encrypt . ' + $_ciphertext = ""; + ' . $encrypt_block . ' + return $_ciphertext; + '; + $decrypt = $init_decrypt . ' + $_plaintext = ""; + ' . $decrypt_block . ' + return $_plaintext; + '; + break; + // case self::MODE_CBC: + default: + $encrypt = $init_encrypt . ' + $_ciphertext = ""; + $_plaintext_len = strlen($_text); + + $in = $this->encryptIV; + + for ($_i = 0; $_i < $_plaintext_len; $_i+= ' . $block_size . ') { + $in = substr($_text, $_i, ' . $block_size . ') ^ $in; + ' . $encrypt_block . ' + $_ciphertext.= $in; + } + + if ($this->continuousBuffer) { + $this->encryptIV = $in; + } + + return $_ciphertext; + '; + $decrypt = $init_decrypt . ' + $_plaintext = ""; + $_text = str_pad($_text, strlen($_text) + (' . $block_size . ' - strlen($_text) % ' . $block_size . ') % ' . $block_size . ', chr(0)); + $_ciphertext_len = strlen($_text); + + $_iv = $this->decryptIV; + + for ($_i = 0; $_i < $_ciphertext_len; $_i+= ' . $block_size . ') { + $in = $_block = substr($_text, $_i, ' . $block_size . '); + ' . $decrypt_block . ' + $_plaintext.= $in ^ $_iv; + $_iv = $_block; + } + + if ($this->continuousBuffer) { + $this->decryptIV = $_iv; + } + + return $this->unpad($_plaintext); + '; + break; + } + // Before discrediting this, please read the following: + // @see https://github.com/phpseclib/phpseclib/issues/1293 + // @see https://github.com/phpseclib/phpseclib/pull/1143 + eval('$func = function ($_action, $_text) { ' . $init_crypt . 'if ($_action == "encrypt") { ' . $encrypt . ' } else { ' . $decrypt . ' }};'); + return \Closure::bind($func, $this, static::class); + } + /** + * Convert float to int + * + * On ARM CPUs converting floats to ints doesn't always work + * + * @param string $x + * @return int + */ + protected static function safe_intval($x) + { + if (\is_int($x)) { + return $x; + } + if (self::$use_reg_intval) { + return \PHP_INT_SIZE == 4 && \PHP_VERSION_ID >= 80100 ? \intval($x) : $x; + } + return \fmod($x, 0x80000000) & 0x7fffffff | (\fmod(\floor($x / 0x80000000), 2) & 1) << 31; + } + /** + * eval()'able string for in-line float to int + * + * @return string + */ + protected static function safe_intval_inline() + { + if (self::$use_reg_intval) { + return \PHP_INT_SIZE == 4 && \PHP_VERSION_ID >= 80100 ? 'intval(%s)' : '%s'; + } + $safeint = '(is_int($temp = %s) ? $temp : (fmod($temp, 0x80000000) & 0x7FFFFFFF) | '; + return $safeint . '((fmod(floor($temp / 0x80000000), 2) & 1) << 31))'; + } + /** + * Sets up GCM parameters + * + * See steps 1-2 of https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38d.pdf#page=23 + * for more info + * + */ + private function setupGCM() + { + // don't keep on re-calculating $this->h + if (!$this->h || $this->hKey != $this->key) { + $cipher = new static('ecb'); + $cipher->setKey($this->key); + $cipher->disablePadding(); + $this->h = self::$gcmField->newInteger(\FluentSmtpLib\phpseclib3\Common\Functions\Strings::switchEndianness($cipher->encrypt("\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"))); + $this->hKey = $this->key; + } + if (\strlen($this->nonce) == 12) { + $this->iv = $this->nonce . "\x00\x00\x00\x01"; + } else { + $this->iv = $this->ghash(self::nullPad128($this->nonce) . \str_repeat("\x00", 8) . self::len64($this->nonce)); + } + } + /** + * Performs GHASH operation + * + * See https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38d.pdf#page=20 + * for more info + * + * @see self::decrypt() + * @see self::encrypt() + * @param string $x + * @return string + */ + private function ghash($x) + { + $h = $this->h; + $y = ["\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"]; + $x = \str_split($x, 16); + $n = 0; + // the switchEndianness calls are necessary because the multiplication algorithm in BinaryField/Integer + // interprets strings as polynomials in big endian order whereas in GCM they're interpreted in little + // endian order per https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38d.pdf#page=19. + // big endian order is what binary field elliptic curves use per http://www.secg.org/sec1-v2.pdf#page=18. + // we could switchEndianness here instead of in the while loop but doing so in the while loop seems like it + // might be slightly more performant + //$x = Strings::switchEndianness($x); + foreach ($x as $xn) { + $xn = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::switchEndianness($xn); + $t = $y[$n] ^ $xn; + $temp = self::$gcmField->newInteger($t); + $y[++$n] = $temp->multiply($h)->toBytes(); + $y[$n] = \substr($y[$n], 1); + } + $y[$n] = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::switchEndianness($y[$n]); + return $y[$n]; + } + /** + * Returns the bit length of a string in a packed format + * + * @see self::decrypt() + * @see self::encrypt() + * @see self::setupGCM() + * @param string $str + * @return string + */ + private static function len64($str) + { + return "\x00\x00\x00\x00" . \pack('N', 8 * \strlen($str)); + } + /** + * NULL pads a string to be a multiple of 128 + * + * @see self::decrypt() + * @see self::encrypt() + * @see self::setupGCM() + * @param string $str + * @return string + */ + protected static function nullPad128($str) + { + $len = \strlen($str); + return $str . \str_repeat("\x00", 16 * \ceil($len / 16) - $len); + } + /** + * Calculates Poly1305 MAC + * + * On my system ChaCha20, with libsodium, takes 0.5s. With this custom Poly1305 implementation + * it takes 1.2s. + * + * @see self::decrypt() + * @see self::encrypt() + * @param string $text + * @return string + */ + protected function poly1305($text) + { + $s = $this->poly1305Key; + // strlen($this->poly1305Key) == 32 + $r = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::shift($s, 16); + $r = \strrev($r); + $r &= "\x0f\xff\xff\xfc\x0f\xff\xff\xfc\x0f\xff\xff\xfc\x0f\xff\xff\xff"; + $s = \strrev($s); + $r = self::$poly1305Field->newInteger(new \FluentSmtpLib\phpseclib3\Math\BigInteger($r, 256)); + $s = self::$poly1305Field->newInteger(new \FluentSmtpLib\phpseclib3\Math\BigInteger($s, 256)); + $a = self::$poly1305Field->newInteger(new \FluentSmtpLib\phpseclib3\Math\BigInteger()); + $blocks = \str_split($text, 16); + foreach ($blocks as $block) { + $n = \strrev($block . \chr(1)); + $n = self::$poly1305Field->newInteger(new \FluentSmtpLib\phpseclib3\Math\BigInteger($n, 256)); + $a = $a->add($n); + $a = $a->multiply($r); + } + $r = $a->toBigInteger()->add($s->toBigInteger()); + $mask = "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff"; + return \strrev($r->toBytes()) & $mask; + } + /** + * Return the mode + * + * You can do $obj instanceof AES or whatever to get the cipher but you can't do that to get the mode + * + * @return string + */ + public function getMode() + { + return \array_flip(self::MODE_MAP)[$this->mode]; + } + /** + * Is the continuous buffer enabled? + * + * @return boolean + */ + public function continuousBufferEnabled() + { + return $this->continuousBuffer; + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/Common/Traits/Fingerprint.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/Common/Traits/Fingerprint.php new file mode 100644 index 0000000..416893d --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/Common/Traits/Fingerprint.php @@ -0,0 +1,55 @@ + + * @copyright 2015 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\Crypt\Common\Traits; + +use FluentSmtpLib\phpseclib3\Crypt\Hash; +/** + * Fingerprint Trait for Private Keys + * + * @author Jim Wigginton + */ +trait Fingerprint +{ + /** + * Returns the public key's fingerprint + * + * The public key's fingerprint is returned, which is equivalent to running `ssh-keygen -lf rsa.pub`. If there is + * no public key currently loaded, false is returned. + * Example output (md5): "c1:b1:30:29:d7:b8:de:6c:97:77:10:d7:46:41:63:87" (as specified by RFC 4716) + * + * @param string $algorithm The hashing algorithm to be used. Valid options are 'md5' and 'sha256'. False is returned + * for invalid values. + * @return mixed + */ + public function getFingerprint($algorithm = 'md5') + { + $type = self::validatePlugin('Keys', 'OpenSSH', 'savePublicKey'); + if ($type === \false) { + return \false; + } + $key = $this->toString('OpenSSH', ['binary' => \true]); + if ($key === \false) { + return \false; + } + switch ($algorithm) { + case 'sha256': + $hash = new \FluentSmtpLib\phpseclib3\Crypt\Hash('sha256'); + $base = \base64_encode($hash->hash($key)); + return \substr($base, 0, \strlen($base) - 1); + case 'md5': + return \substr(\chunk_split(\md5($key), 2, ':'), 0, -1); + default: + return \false; + } + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/Common/Traits/PasswordProtected.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/Common/Traits/PasswordProtected.php new file mode 100644 index 0000000..2c5c67d --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/Common/Traits/PasswordProtected.php @@ -0,0 +1,44 @@ + + * @copyright 2015 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\Crypt\Common\Traits; + +/** + * Password Protected Trait for Private Keys + * + * @author Jim Wigginton + */ +trait PasswordProtected +{ + /** + * Password + * + * @var string|bool + */ + private $password = \false; + /** + * Sets the password + * + * Private keys can be encrypted with a password. To unset the password, pass in the empty string or false. + * Or rather, pass in $password such that empty($password) && !is_string($password) is true. + * + * @see self::createKey() + * @see self::load() + * @param string|bool $password + */ + public function withPassword($password = \false) + { + $new = clone $this; + $new->password = $password; + return $new; + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/DES.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/DES.php new file mode 100644 index 0000000..b775c43 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/DES.php @@ -0,0 +1,522 @@ + + * setKey('abcdefgh'); + * + * $size = 10 * 1024; + * $plaintext = ''; + * for ($i = 0; $i < $size; $i++) { + * $plaintext.= 'a'; + * } + * + * echo $des->decrypt($des->encrypt($plaintext)); + * ?> + * + * + * @author Jim Wigginton + * @copyright 2007 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\Crypt; + +use FluentSmtpLib\phpseclib3\Crypt\Common\BlockCipher; +use FluentSmtpLib\phpseclib3\Exception\BadModeException; +/** + * Pure-PHP implementation of DES. + * + * @author Jim Wigginton + */ +class DES extends \FluentSmtpLib\phpseclib3\Crypt\Common\BlockCipher +{ + /** + * Contains $keys[self::ENCRYPT] + * + * @see \phpseclib3\Crypt\DES::setupKey() + * @see \phpseclib3\Crypt\DES::processBlock() + */ + const ENCRYPT = 0; + /** + * Contains $keys[self::DECRYPT] + * + * @see \phpseclib3\Crypt\DES::setupKey() + * @see \phpseclib3\Crypt\DES::processBlock() + */ + const DECRYPT = 1; + /** + * Block Length of the cipher + * + * @see \phpseclib3\Crypt\Common\SymmetricKey::block_size + * @var int + */ + protected $block_size = 8; + /** + * Key Length (in bytes) + * + * @see \phpseclib3\Crypt\Common\SymmetricKey::setKeyLength() + * @var int + */ + protected $key_length = 8; + /** + * The mcrypt specific name of the cipher + * + * @see \phpseclib3\Crypt\Common\SymmetricKey::cipher_name_mcrypt + * @var string + */ + protected $cipher_name_mcrypt = 'des'; + /** + * The OpenSSL names of the cipher / modes + * + * @see \phpseclib3\Crypt\Common\SymmetricKey::openssl_mode_names + * @var array + */ + protected $openssl_mode_names = [self::MODE_ECB => 'des-ecb', self::MODE_CBC => 'des-cbc', self::MODE_CFB => 'des-cfb', self::MODE_OFB => 'des-ofb']; + /** + * Optimizing value while CFB-encrypting + * + * @see \phpseclib3\Crypt\Common\SymmetricKey::cfb_init_len + * @var int + */ + protected $cfb_init_len = 500; + /** + * Switch for DES/3DES encryption + * + * Used only if $engine == self::ENGINE_INTERNAL + * + * @see self::setupKey() + * @see self::processBlock() + * @var int + */ + protected $des_rounds = 1; + /** + * max possible size of $key + * + * @see self::setKey() + * @var string + */ + protected $key_length_max = 8; + /** + * The Key Schedule + * + * @see self::setupKey() + * @var array + */ + private $keys; + /** + * Key Cache "key" + * + * @see self::setupKey() + * @var array + */ + private $kl; + /** + * Shuffle table. + * + * For each byte value index, the entry holds an 8-byte string + * with each byte containing all bits in the same state as the + * corresponding bit in the index value. + * + * @see self::processBlock() + * @see self::setupKey() + * @var array + */ + protected static $shuffle = ["\x00\x00\x00\x00\x00\x00\x00\x00", "\x00\x00\x00\x00\x00\x00\x00\xff", "\x00\x00\x00\x00\x00\x00\xff\x00", "\x00\x00\x00\x00\x00\x00\xff\xff", "\x00\x00\x00\x00\x00\xff\x00\x00", "\x00\x00\x00\x00\x00\xff\x00\xff", "\x00\x00\x00\x00\x00\xff\xff\x00", "\x00\x00\x00\x00\x00\xff\xff\xff", "\x00\x00\x00\x00\xff\x00\x00\x00", "\x00\x00\x00\x00\xff\x00\x00\xff", "\x00\x00\x00\x00\xff\x00\xff\x00", "\x00\x00\x00\x00\xff\x00\xff\xff", "\x00\x00\x00\x00\xff\xff\x00\x00", "\x00\x00\x00\x00\xff\xff\x00\xff", "\x00\x00\x00\x00\xff\xff\xff\x00", "\x00\x00\x00\x00\xff\xff\xff\xff", "\x00\x00\x00\xff\x00\x00\x00\x00", "\x00\x00\x00\xff\x00\x00\x00\xff", "\x00\x00\x00\xff\x00\x00\xff\x00", "\x00\x00\x00\xff\x00\x00\xff\xff", "\x00\x00\x00\xff\x00\xff\x00\x00", "\x00\x00\x00\xff\x00\xff\x00\xff", "\x00\x00\x00\xff\x00\xff\xff\x00", "\x00\x00\x00\xff\x00\xff\xff\xff", "\x00\x00\x00\xff\xff\x00\x00\x00", "\x00\x00\x00\xff\xff\x00\x00\xff", "\x00\x00\x00\xff\xff\x00\xff\x00", "\x00\x00\x00\xff\xff\x00\xff\xff", "\x00\x00\x00\xff\xff\xff\x00\x00", "\x00\x00\x00\xff\xff\xff\x00\xff", "\x00\x00\x00\xff\xff\xff\xff\x00", "\x00\x00\x00\xff\xff\xff\xff\xff", "\x00\x00\xff\x00\x00\x00\x00\x00", "\x00\x00\xff\x00\x00\x00\x00\xff", "\x00\x00\xff\x00\x00\x00\xff\x00", "\x00\x00\xff\x00\x00\x00\xff\xff", "\x00\x00\xff\x00\x00\xff\x00\x00", "\x00\x00\xff\x00\x00\xff\x00\xff", "\x00\x00\xff\x00\x00\xff\xff\x00", "\x00\x00\xff\x00\x00\xff\xff\xff", "\x00\x00\xff\x00\xff\x00\x00\x00", "\x00\x00\xff\x00\xff\x00\x00\xff", "\x00\x00\xff\x00\xff\x00\xff\x00", "\x00\x00\xff\x00\xff\x00\xff\xff", "\x00\x00\xff\x00\xff\xff\x00\x00", "\x00\x00\xff\x00\xff\xff\x00\xff", "\x00\x00\xff\x00\xff\xff\xff\x00", "\x00\x00\xff\x00\xff\xff\xff\xff", "\x00\x00\xff\xff\x00\x00\x00\x00", "\x00\x00\xff\xff\x00\x00\x00\xff", "\x00\x00\xff\xff\x00\x00\xff\x00", "\x00\x00\xff\xff\x00\x00\xff\xff", "\x00\x00\xff\xff\x00\xff\x00\x00", "\x00\x00\xff\xff\x00\xff\x00\xff", "\x00\x00\xff\xff\x00\xff\xff\x00", "\x00\x00\xff\xff\x00\xff\xff\xff", "\x00\x00\xff\xff\xff\x00\x00\x00", "\x00\x00\xff\xff\xff\x00\x00\xff", "\x00\x00\xff\xff\xff\x00\xff\x00", "\x00\x00\xff\xff\xff\x00\xff\xff", "\x00\x00\xff\xff\xff\xff\x00\x00", "\x00\x00\xff\xff\xff\xff\x00\xff", "\x00\x00\xff\xff\xff\xff\xff\x00", "\x00\x00\xff\xff\xff\xff\xff\xff", "\x00\xff\x00\x00\x00\x00\x00\x00", "\x00\xff\x00\x00\x00\x00\x00\xff", "\x00\xff\x00\x00\x00\x00\xff\x00", "\x00\xff\x00\x00\x00\x00\xff\xff", "\x00\xff\x00\x00\x00\xff\x00\x00", "\x00\xff\x00\x00\x00\xff\x00\xff", "\x00\xff\x00\x00\x00\xff\xff\x00", "\x00\xff\x00\x00\x00\xff\xff\xff", "\x00\xff\x00\x00\xff\x00\x00\x00", "\x00\xff\x00\x00\xff\x00\x00\xff", "\x00\xff\x00\x00\xff\x00\xff\x00", "\x00\xff\x00\x00\xff\x00\xff\xff", "\x00\xff\x00\x00\xff\xff\x00\x00", "\x00\xff\x00\x00\xff\xff\x00\xff", "\x00\xff\x00\x00\xff\xff\xff\x00", "\x00\xff\x00\x00\xff\xff\xff\xff", "\x00\xff\x00\xff\x00\x00\x00\x00", "\x00\xff\x00\xff\x00\x00\x00\xff", "\x00\xff\x00\xff\x00\x00\xff\x00", "\x00\xff\x00\xff\x00\x00\xff\xff", "\x00\xff\x00\xff\x00\xff\x00\x00", "\x00\xff\x00\xff\x00\xff\x00\xff", "\x00\xff\x00\xff\x00\xff\xff\x00", "\x00\xff\x00\xff\x00\xff\xff\xff", "\x00\xff\x00\xff\xff\x00\x00\x00", "\x00\xff\x00\xff\xff\x00\x00\xff", "\x00\xff\x00\xff\xff\x00\xff\x00", "\x00\xff\x00\xff\xff\x00\xff\xff", "\x00\xff\x00\xff\xff\xff\x00\x00", "\x00\xff\x00\xff\xff\xff\x00\xff", "\x00\xff\x00\xff\xff\xff\xff\x00", "\x00\xff\x00\xff\xff\xff\xff\xff", "\x00\xff\xff\x00\x00\x00\x00\x00", "\x00\xff\xff\x00\x00\x00\x00\xff", "\x00\xff\xff\x00\x00\x00\xff\x00", "\x00\xff\xff\x00\x00\x00\xff\xff", "\x00\xff\xff\x00\x00\xff\x00\x00", "\x00\xff\xff\x00\x00\xff\x00\xff", "\x00\xff\xff\x00\x00\xff\xff\x00", "\x00\xff\xff\x00\x00\xff\xff\xff", "\x00\xff\xff\x00\xff\x00\x00\x00", "\x00\xff\xff\x00\xff\x00\x00\xff", "\x00\xff\xff\x00\xff\x00\xff\x00", "\x00\xff\xff\x00\xff\x00\xff\xff", "\x00\xff\xff\x00\xff\xff\x00\x00", "\x00\xff\xff\x00\xff\xff\x00\xff", "\x00\xff\xff\x00\xff\xff\xff\x00", "\x00\xff\xff\x00\xff\xff\xff\xff", "\x00\xff\xff\xff\x00\x00\x00\x00", "\x00\xff\xff\xff\x00\x00\x00\xff", "\x00\xff\xff\xff\x00\x00\xff\x00", "\x00\xff\xff\xff\x00\x00\xff\xff", "\x00\xff\xff\xff\x00\xff\x00\x00", "\x00\xff\xff\xff\x00\xff\x00\xff", "\x00\xff\xff\xff\x00\xff\xff\x00", "\x00\xff\xff\xff\x00\xff\xff\xff", "\x00\xff\xff\xff\xff\x00\x00\x00", "\x00\xff\xff\xff\xff\x00\x00\xff", "\x00\xff\xff\xff\xff\x00\xff\x00", "\x00\xff\xff\xff\xff\x00\xff\xff", "\x00\xff\xff\xff\xff\xff\x00\x00", "\x00\xff\xff\xff\xff\xff\x00\xff", "\x00\xff\xff\xff\xff\xff\xff\x00", "\x00\xff\xff\xff\xff\xff\xff\xff", "\xff\x00\x00\x00\x00\x00\x00\x00", "\xff\x00\x00\x00\x00\x00\x00\xff", "\xff\x00\x00\x00\x00\x00\xff\x00", "\xff\x00\x00\x00\x00\x00\xff\xff", "\xff\x00\x00\x00\x00\xff\x00\x00", "\xff\x00\x00\x00\x00\xff\x00\xff", "\xff\x00\x00\x00\x00\xff\xff\x00", "\xff\x00\x00\x00\x00\xff\xff\xff", "\xff\x00\x00\x00\xff\x00\x00\x00", "\xff\x00\x00\x00\xff\x00\x00\xff", "\xff\x00\x00\x00\xff\x00\xff\x00", "\xff\x00\x00\x00\xff\x00\xff\xff", "\xff\x00\x00\x00\xff\xff\x00\x00", "\xff\x00\x00\x00\xff\xff\x00\xff", "\xff\x00\x00\x00\xff\xff\xff\x00", "\xff\x00\x00\x00\xff\xff\xff\xff", "\xff\x00\x00\xff\x00\x00\x00\x00", "\xff\x00\x00\xff\x00\x00\x00\xff", "\xff\x00\x00\xff\x00\x00\xff\x00", "\xff\x00\x00\xff\x00\x00\xff\xff", "\xff\x00\x00\xff\x00\xff\x00\x00", "\xff\x00\x00\xff\x00\xff\x00\xff", "\xff\x00\x00\xff\x00\xff\xff\x00", "\xff\x00\x00\xff\x00\xff\xff\xff", "\xff\x00\x00\xff\xff\x00\x00\x00", "\xff\x00\x00\xff\xff\x00\x00\xff", "\xff\x00\x00\xff\xff\x00\xff\x00", "\xff\x00\x00\xff\xff\x00\xff\xff", "\xff\x00\x00\xff\xff\xff\x00\x00", "\xff\x00\x00\xff\xff\xff\x00\xff", "\xff\x00\x00\xff\xff\xff\xff\x00", "\xff\x00\x00\xff\xff\xff\xff\xff", "\xff\x00\xff\x00\x00\x00\x00\x00", "\xff\x00\xff\x00\x00\x00\x00\xff", "\xff\x00\xff\x00\x00\x00\xff\x00", "\xff\x00\xff\x00\x00\x00\xff\xff", "\xff\x00\xff\x00\x00\xff\x00\x00", "\xff\x00\xff\x00\x00\xff\x00\xff", "\xff\x00\xff\x00\x00\xff\xff\x00", "\xff\x00\xff\x00\x00\xff\xff\xff", "\xff\x00\xff\x00\xff\x00\x00\x00", "\xff\x00\xff\x00\xff\x00\x00\xff", "\xff\x00\xff\x00\xff\x00\xff\x00", "\xff\x00\xff\x00\xff\x00\xff\xff", "\xff\x00\xff\x00\xff\xff\x00\x00", "\xff\x00\xff\x00\xff\xff\x00\xff", "\xff\x00\xff\x00\xff\xff\xff\x00", "\xff\x00\xff\x00\xff\xff\xff\xff", "\xff\x00\xff\xff\x00\x00\x00\x00", "\xff\x00\xff\xff\x00\x00\x00\xff", "\xff\x00\xff\xff\x00\x00\xff\x00", "\xff\x00\xff\xff\x00\x00\xff\xff", "\xff\x00\xff\xff\x00\xff\x00\x00", "\xff\x00\xff\xff\x00\xff\x00\xff", "\xff\x00\xff\xff\x00\xff\xff\x00", "\xff\x00\xff\xff\x00\xff\xff\xff", "\xff\x00\xff\xff\xff\x00\x00\x00", "\xff\x00\xff\xff\xff\x00\x00\xff", "\xff\x00\xff\xff\xff\x00\xff\x00", "\xff\x00\xff\xff\xff\x00\xff\xff", "\xff\x00\xff\xff\xff\xff\x00\x00", "\xff\x00\xff\xff\xff\xff\x00\xff", "\xff\x00\xff\xff\xff\xff\xff\x00", "\xff\x00\xff\xff\xff\xff\xff\xff", "\xff\xff\x00\x00\x00\x00\x00\x00", "\xff\xff\x00\x00\x00\x00\x00\xff", "\xff\xff\x00\x00\x00\x00\xff\x00", "\xff\xff\x00\x00\x00\x00\xff\xff", "\xff\xff\x00\x00\x00\xff\x00\x00", "\xff\xff\x00\x00\x00\xff\x00\xff", "\xff\xff\x00\x00\x00\xff\xff\x00", "\xff\xff\x00\x00\x00\xff\xff\xff", "\xff\xff\x00\x00\xff\x00\x00\x00", "\xff\xff\x00\x00\xff\x00\x00\xff", "\xff\xff\x00\x00\xff\x00\xff\x00", "\xff\xff\x00\x00\xff\x00\xff\xff", "\xff\xff\x00\x00\xff\xff\x00\x00", "\xff\xff\x00\x00\xff\xff\x00\xff", "\xff\xff\x00\x00\xff\xff\xff\x00", "\xff\xff\x00\x00\xff\xff\xff\xff", "\xff\xff\x00\xff\x00\x00\x00\x00", "\xff\xff\x00\xff\x00\x00\x00\xff", "\xff\xff\x00\xff\x00\x00\xff\x00", "\xff\xff\x00\xff\x00\x00\xff\xff", "\xff\xff\x00\xff\x00\xff\x00\x00", "\xff\xff\x00\xff\x00\xff\x00\xff", "\xff\xff\x00\xff\x00\xff\xff\x00", "\xff\xff\x00\xff\x00\xff\xff\xff", "\xff\xff\x00\xff\xff\x00\x00\x00", "\xff\xff\x00\xff\xff\x00\x00\xff", "\xff\xff\x00\xff\xff\x00\xff\x00", "\xff\xff\x00\xff\xff\x00\xff\xff", "\xff\xff\x00\xff\xff\xff\x00\x00", "\xff\xff\x00\xff\xff\xff\x00\xff", "\xff\xff\x00\xff\xff\xff\xff\x00", "\xff\xff\x00\xff\xff\xff\xff\xff", "\xff\xff\xff\x00\x00\x00\x00\x00", "\xff\xff\xff\x00\x00\x00\x00\xff", "\xff\xff\xff\x00\x00\x00\xff\x00", "\xff\xff\xff\x00\x00\x00\xff\xff", "\xff\xff\xff\x00\x00\xff\x00\x00", "\xff\xff\xff\x00\x00\xff\x00\xff", "\xff\xff\xff\x00\x00\xff\xff\x00", "\xff\xff\xff\x00\x00\xff\xff\xff", "\xff\xff\xff\x00\xff\x00\x00\x00", "\xff\xff\xff\x00\xff\x00\x00\xff", "\xff\xff\xff\x00\xff\x00\xff\x00", "\xff\xff\xff\x00\xff\x00\xff\xff", "\xff\xff\xff\x00\xff\xff\x00\x00", "\xff\xff\xff\x00\xff\xff\x00\xff", "\xff\xff\xff\x00\xff\xff\xff\x00", "\xff\xff\xff\x00\xff\xff\xff\xff", "\xff\xff\xff\xff\x00\x00\x00\x00", "\xff\xff\xff\xff\x00\x00\x00\xff", "\xff\xff\xff\xff\x00\x00\xff\x00", "\xff\xff\xff\xff\x00\x00\xff\xff", "\xff\xff\xff\xff\x00\xff\x00\x00", "\xff\xff\xff\xff\x00\xff\x00\xff", "\xff\xff\xff\xff\x00\xff\xff\x00", "\xff\xff\xff\xff\x00\xff\xff\xff", "\xff\xff\xff\xff\xff\x00\x00\x00", "\xff\xff\xff\xff\xff\x00\x00\xff", "\xff\xff\xff\xff\xff\x00\xff\x00", "\xff\xff\xff\xff\xff\x00\xff\xff", "\xff\xff\xff\xff\xff\xff\x00\x00", "\xff\xff\xff\xff\xff\xff\x00\xff", "\xff\xff\xff\xff\xff\xff\xff\x00", "\xff\xff\xff\xff\xff\xff\xff\xff"]; + /** + * IP mapping helper table. + * + * Indexing this table with each source byte performs the initial bit permutation. + * + * @var array + */ + protected static $ipmap = [0x0, 0x10, 0x1, 0x11, 0x20, 0x30, 0x21, 0x31, 0x2, 0x12, 0x3, 0x13, 0x22, 0x32, 0x23, 0x33, 0x40, 0x50, 0x41, 0x51, 0x60, 0x70, 0x61, 0x71, 0x42, 0x52, 0x43, 0x53, 0x62, 0x72, 0x63, 0x73, 0x4, 0x14, 0x5, 0x15, 0x24, 0x34, 0x25, 0x35, 0x6, 0x16, 0x7, 0x17, 0x26, 0x36, 0x27, 0x37, 0x44, 0x54, 0x45, 0x55, 0x64, 0x74, 0x65, 0x75, 0x46, 0x56, 0x47, 0x57, 0x66, 0x76, 0x67, 0x77, 0x80, 0x90, 0x81, 0x91, 0xa0, 0xb0, 0xa1, 0xb1, 0x82, 0x92, 0x83, 0x93, 0xa2, 0xb2, 0xa3, 0xb3, 0xc0, 0xd0, 0xc1, 0xd1, 0xe0, 0xf0, 0xe1, 0xf1, 0xc2, 0xd2, 0xc3, 0xd3, 0xe2, 0xf2, 0xe3, 0xf3, 0x84, 0x94, 0x85, 0x95, 0xa4, 0xb4, 0xa5, 0xb5, 0x86, 0x96, 0x87, 0x97, 0xa6, 0xb6, 0xa7, 0xb7, 0xc4, 0xd4, 0xc5, 0xd5, 0xe4, 0xf4, 0xe5, 0xf5, 0xc6, 0xd6, 0xc7, 0xd7, 0xe6, 0xf6, 0xe7, 0xf7, 0x8, 0x18, 0x9, 0x19, 0x28, 0x38, 0x29, 0x39, 0xa, 0x1a, 0xb, 0x1b, 0x2a, 0x3a, 0x2b, 0x3b, 0x48, 0x58, 0x49, 0x59, 0x68, 0x78, 0x69, 0x79, 0x4a, 0x5a, 0x4b, 0x5b, 0x6a, 0x7a, 0x6b, 0x7b, 0xc, 0x1c, 0xd, 0x1d, 0x2c, 0x3c, 0x2d, 0x3d, 0xe, 0x1e, 0xf, 0x1f, 0x2e, 0x3e, 0x2f, 0x3f, 0x4c, 0x5c, 0x4d, 0x5d, 0x6c, 0x7c, 0x6d, 0x7d, 0x4e, 0x5e, 0x4f, 0x5f, 0x6e, 0x7e, 0x6f, 0x7f, 0x88, 0x98, 0x89, 0x99, 0xa8, 0xb8, 0xa9, 0xb9, 0x8a, 0x9a, 0x8b, 0x9b, 0xaa, 0xba, 0xab, 0xbb, 0xc8, 0xd8, 0xc9, 0xd9, 0xe8, 0xf8, 0xe9, 0xf9, 0xca, 0xda, 0xcb, 0xdb, 0xea, 0xfa, 0xeb, 0xfb, 0x8c, 0x9c, 0x8d, 0x9d, 0xac, 0xbc, 0xad, 0xbd, 0x8e, 0x9e, 0x8f, 0x9f, 0xae, 0xbe, 0xaf, 0xbf, 0xcc, 0xdc, 0xcd, 0xdd, 0xec, 0xfc, 0xed, 0xfd, 0xce, 0xde, 0xcf, 0xdf, 0xee, 0xfe, 0xef, 0xff]; + /** + * Inverse IP mapping helper table. + * Indexing this table with a byte value reverses the bit order. + * + * @var array + */ + protected static $invipmap = [0x0, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0, 0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0, 0x8, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8, 0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8, 0x4, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4, 0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4, 0xc, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec, 0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc, 0x2, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2, 0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2, 0xa, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea, 0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa, 0x6, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6, 0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, 0x76, 0xf6, 0xe, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee, 0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe, 0x1, 0x81, 0x41, 0xc1, 0x21, 0xa1, 0x61, 0xe1, 0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1, 0x9, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9, 0x19, 0x99, 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9, 0x5, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5, 0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5, 0xd, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed, 0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd, 0x3, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3, 0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3, 0xb, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb, 0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb, 0x7, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0xe7, 0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7, 0xf, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef, 0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff]; + /** + * Pre-permuted S-box1 + * + * Each box ($sbox1-$sbox8) has been vectorized, then each value pre-permuted using the + * P table: concatenation can then be replaced by exclusive ORs. + * + * @var array + */ + protected static $sbox1 = [0x808200, 0x0, 0x8000, 0x808202, 0x808002, 0x8202, 0x2, 0x8000, 0x200, 0x808200, 0x808202, 0x200, 0x800202, 0x808002, 0x800000, 0x2, 0x202, 0x800200, 0x800200, 0x8200, 0x8200, 0x808000, 0x808000, 0x800202, 0x8002, 0x800002, 0x800002, 0x8002, 0x0, 0x202, 0x8202, 0x800000, 0x8000, 0x808202, 0x2, 0x808000, 0x808200, 0x800000, 0x800000, 0x200, 0x808002, 0x8000, 0x8200, 0x800002, 0x200, 0x2, 0x800202, 0x8202, 0x808202, 0x8002, 0x808000, 0x800202, 0x800002, 0x202, 0x8202, 0x808200, 0x202, 0x800200, 0x800200, 0x0, 0x8002, 0x8200, 0x0, 0x808002]; + /** + * Pre-permuted S-box2 + * + * @var array + */ + protected static $sbox2 = [0x40084010, 0x40004000, 0x4000, 0x84010, 0x80000, 0x10, 0x40080010, 0x40004010, 0x40000010, 0x40084010, 0x40084000, 0x40000000, 0x40004000, 0x80000, 0x10, 0x40080010, 0x84000, 0x80010, 0x40004010, 0x0, 0x40000000, 0x4000, 0x84010, 0x40080000, 0x80010, 0x40000010, 0x0, 0x84000, 0x4010, 0x40084000, 0x40080000, 0x4010, 0x0, 0x84010, 0x40080010, 0x80000, 0x40004010, 0x40080000, 0x40084000, 0x4000, 0x40080000, 0x40004000, 0x10, 0x40084010, 0x84010, 0x10, 0x4000, 0x40000000, 0x4010, 0x40084000, 0x80000, 0x40000010, 0x80010, 0x40004010, 0x40000010, 0x80010, 0x84000, 0x0, 0x40004000, 0x4010, 0x40000000, 0x40080010, 0x40084010, 0x84000]; + /** + * Pre-permuted S-box3 + * + * @var array + */ + protected static $sbox3 = [0x104, 0x4010100, 0x0, 0x4010004, 0x4000100, 0x0, 0x10104, 0x4000100, 0x10004, 0x4000004, 0x4000004, 0x10000, 0x4010104, 0x10004, 0x4010000, 0x104, 0x4000000, 0x4, 0x4010100, 0x100, 0x10100, 0x4010000, 0x4010004, 0x10104, 0x4000104, 0x10100, 0x10000, 0x4000104, 0x4, 0x4010104, 0x100, 0x4000000, 0x4010100, 0x4000000, 0x10004, 0x104, 0x10000, 0x4010100, 0x4000100, 0x0, 0x100, 0x10004, 0x4010104, 0x4000100, 0x4000004, 0x100, 0x0, 0x4010004, 0x4000104, 0x10000, 0x4000000, 0x4010104, 0x4, 0x10104, 0x10100, 0x4000004, 0x4010000, 0x4000104, 0x104, 0x4010000, 0x10104, 0x4, 0x4010004, 0x10100]; + /** + * Pre-permuted S-box4 + * + * @var array + */ + protected static $sbox4 = [0x80401000, 0x80001040, 0x80001040, 0x40, 0x401040, 0x80400040, 0x80400000, 0x80001000, 0x0, 0x401000, 0x401000, 0x80401040, 0x80000040, 0x0, 0x400040, 0x80400000, 0x80000000, 0x1000, 0x400000, 0x80401000, 0x40, 0x400000, 0x80001000, 0x1040, 0x80400040, 0x80000000, 0x1040, 0x400040, 0x1000, 0x401040, 0x80401040, 0x80000040, 0x400040, 0x80400000, 0x401000, 0x80401040, 0x80000040, 0x0, 0x0, 0x401000, 0x1040, 0x400040, 0x80400040, 0x80000000, 0x80401000, 0x80001040, 0x80001040, 0x40, 0x80401040, 0x80000040, 0x80000000, 0x1000, 0x80400000, 0x80001000, 0x401040, 0x80400040, 0x80001000, 0x1040, 0x400000, 0x80401000, 0x40, 0x400000, 0x1000, 0x401040]; + /** + * Pre-permuted S-box5 + * + * @var array + */ + protected static $sbox5 = [0x80, 0x1040080, 0x1040000, 0x21000080, 0x40000, 0x80, 0x20000000, 0x1040000, 0x20040080, 0x40000, 0x1000080, 0x20040080, 0x21000080, 0x21040000, 0x40080, 0x20000000, 0x1000000, 0x20040000, 0x20040000, 0x0, 0x20000080, 0x21040080, 0x21040080, 0x1000080, 0x21040000, 0x20000080, 0x0, 0x21000000, 0x1040080, 0x1000000, 0x21000000, 0x40080, 0x40000, 0x21000080, 0x80, 0x1000000, 0x20000000, 0x1040000, 0x21000080, 0x20040080, 0x1000080, 0x20000000, 0x21040000, 0x1040080, 0x20040080, 0x80, 0x1000000, 0x21040000, 0x21040080, 0x40080, 0x21000000, 0x21040080, 0x1040000, 0x0, 0x20040000, 0x21000000, 0x40080, 0x1000080, 0x20000080, 0x40000, 0x0, 0x20040000, 0x1040080, 0x20000080]; + /** + * Pre-permuted S-box6 + * + * @var array + */ + protected static $sbox6 = [0x10000008, 0x10200000, 0x2000, 0x10202008, 0x10200000, 0x8, 0x10202008, 0x200000, 0x10002000, 0x202008, 0x200000, 0x10000008, 0x200008, 0x10002000, 0x10000000, 0x2008, 0x0, 0x200008, 0x10002008, 0x2000, 0x202000, 0x10002008, 0x8, 0x10200008, 0x10200008, 0x0, 0x202008, 0x10202000, 0x2008, 0x202000, 0x10202000, 0x10000000, 0x10002000, 0x8, 0x10200008, 0x202000, 0x10202008, 0x200000, 0x2008, 0x10000008, 0x200000, 0x10002000, 0x10000000, 0x2008, 0x10000008, 0x10202008, 0x202000, 0x10200000, 0x202008, 0x10202000, 0x0, 0x10200008, 0x8, 0x2000, 0x10200000, 0x202008, 0x2000, 0x200008, 0x10002008, 0x0, 0x10202000, 0x10000000, 0x200008, 0x10002008]; + /** + * Pre-permuted S-box7 + * + * @var array + */ + protected static $sbox7 = [0x100000, 0x2100001, 0x2000401, 0x0, 0x400, 0x2000401, 0x100401, 0x2100400, 0x2100401, 0x100000, 0x0, 0x2000001, 0x1, 0x2000000, 0x2100001, 0x401, 0x2000400, 0x100401, 0x100001, 0x2000400, 0x2000001, 0x2100000, 0x2100400, 0x100001, 0x2100000, 0x400, 0x401, 0x2100401, 0x100400, 0x1, 0x2000000, 0x100400, 0x2000000, 0x100400, 0x100000, 0x2000401, 0x2000401, 0x2100001, 0x2100001, 0x1, 0x100001, 0x2000000, 0x2000400, 0x100000, 0x2100400, 0x401, 0x100401, 0x2100400, 0x401, 0x2000001, 0x2100401, 0x2100000, 0x100400, 0x0, 0x1, 0x2100401, 0x0, 0x100401, 0x2100000, 0x400, 0x2000001, 0x2000400, 0x400, 0x100001]; + /** + * Pre-permuted S-box8 + * + * @var array + */ + protected static $sbox8 = [0x8000820, 0x800, 0x20000, 0x8020820, 0x8000000, 0x8000820, 0x20, 0x8000000, 0x20020, 0x8020000, 0x8020820, 0x20800, 0x8020800, 0x20820, 0x800, 0x20, 0x8020000, 0x8000020, 0x8000800, 0x820, 0x20800, 0x20020, 0x8020020, 0x8020800, 0x820, 0x0, 0x0, 0x8020020, 0x8000020, 0x8000800, 0x20820, 0x20000, 0x20820, 0x20000, 0x8020800, 0x800, 0x20, 0x8020020, 0x800, 0x20820, 0x8000800, 0x20, 0x8000020, 0x8020000, 0x8020020, 0x8000000, 0x20000, 0x8000820, 0x0, 0x8020820, 0x20020, 0x8000020, 0x8020000, 0x8000800, 0x8000820, 0x0, 0x8020820, 0x20800, 0x20800, 0x820, 0x820, 0x20020, 0x8000000, 0x8020800]; + /** + * Default Constructor. + * + * @param string $mode + * @throws BadModeException if an invalid / unsupported mode is provided + */ + public function __construct($mode) + { + parent::__construct($mode); + if ($this->mode == self::MODE_STREAM) { + throw new \FluentSmtpLib\phpseclib3\Exception\BadModeException('Block ciphers cannot be ran in stream mode'); + } + } + /** + * Test for engine validity + * + * This is mainly just a wrapper to set things up for \phpseclib3\Crypt\Common\SymmetricKey::isValidEngine() + * + * @see \phpseclib3\Crypt\Common\SymmetricKey::isValidEngine() + * @param int $engine + * @return bool + */ + protected function isValidEngineHelper($engine) + { + if ($this->key_length_max == 8) { + if ($engine == self::ENGINE_OPENSSL) { + // quoting https://www.openssl.org/news/openssl-3.0-notes.html, OpenSSL 3.0.1 + // "Moved all variations of the EVP ciphers CAST5, BF, IDEA, SEED, RC2, RC4, RC5, and DES to the legacy provider" + // in theory openssl_get_cipher_methods() should catch this but, on GitHub Actions, at least, it does not + if (\defined('OPENSSL_VERSION_TEXT') && \version_compare(\preg_replace('#OpenSSL (\\d+\\.\\d+\\.\\d+) .*#', '$1', \OPENSSL_VERSION_TEXT), '3.0.1', '>=')) { + return \false; + } + $this->cipher_name_openssl_ecb = 'des-ecb'; + $this->cipher_name_openssl = 'des-' . $this->openssl_translate_mode(); + } + } + return parent::isValidEngineHelper($engine); + } + /** + * Sets the key. + * + * Keys must be 64-bits long or 8 bytes long. + * + * DES also requires that every eighth bit be a parity bit, however, we'll ignore that. + * + * @see \phpseclib3\Crypt\Common\SymmetricKey::setKey() + * @param string $key + */ + public function setKey($key) + { + if (!$this instanceof \FluentSmtpLib\phpseclib3\Crypt\TripleDES && \strlen($key) != 8) { + throw new \LengthException('Key of size ' . \strlen($key) . ' not supported by this algorithm. Only keys of size 8 are supported'); + } + // Sets the key + parent::setKey($key); + } + /** + * Encrypts a block + * + * @see \phpseclib3\Crypt\Common\SymmetricKey::encryptBlock() + * @see \phpseclib3\Crypt\Common\SymmetricKey::encrypt() + * @see self::encrypt() + * @param string $in + * @return string + */ + protected function encryptBlock($in) + { + return $this->processBlock($in, self::ENCRYPT); + } + /** + * Decrypts a block + * + * @see \phpseclib3\Crypt\Common\SymmetricKey::decryptBlock() + * @see \phpseclib3\Crypt\Common\SymmetricKey::decrypt() + * @see self::decrypt() + * @param string $in + * @return string + */ + protected function decryptBlock($in) + { + return $this->processBlock($in, self::DECRYPT); + } + /** + * Encrypts or decrypts a 64-bit block + * + * $mode should be either self::ENCRYPT or self::DECRYPT. See + * {@link http://en.wikipedia.org/wiki/Image:Feistel.png Feistel.png} to get a general + * idea of what this function does. + * + * @see self::encryptBlock() + * @see self::decryptBlock() + * @param string $block + * @param int $mode + * @return string + */ + private function processBlock($block, $mode) + { + static $sbox1, $sbox2, $sbox3, $sbox4, $sbox5, $sbox6, $sbox7, $sbox8, $shuffleip, $shuffleinvip; + if (!$sbox1) { + $sbox1 = \array_map('intval', self::$sbox1); + $sbox2 = \array_map('intval', self::$sbox2); + $sbox3 = \array_map('intval', self::$sbox3); + $sbox4 = \array_map('intval', self::$sbox4); + $sbox5 = \array_map('intval', self::$sbox5); + $sbox6 = \array_map('intval', self::$sbox6); + $sbox7 = \array_map('intval', self::$sbox7); + $sbox8 = \array_map('intval', self::$sbox8); + /* Merge $shuffle with $[inv]ipmap */ + for ($i = 0; $i < 256; ++$i) { + $shuffleip[] = self::$shuffle[self::$ipmap[$i]]; + $shuffleinvip[] = self::$shuffle[self::$invipmap[$i]]; + } + } + $keys = $this->keys[$mode]; + $ki = -1; + // Do the initial IP permutation. + $t = \unpack('Nl/Nr', $block); + list($l, $r) = [$t['l'], $t['r']]; + $block = $shuffleip[$r & 0xff] & "\x80\x80\x80\x80\x80\x80\x80\x80" | $shuffleip[$r >> 8 & 0xff] & "@@@@@@@@" | $shuffleip[$r >> 16 & 0xff] & " " | $shuffleip[$r >> 24 & 0xff] & "\x10\x10\x10\x10\x10\x10\x10\x10" | $shuffleip[$l & 0xff] & "\x08\x08\x08\x08\x08\x08\x08\x08" | $shuffleip[$l >> 8 & 0xff] & "\x04\x04\x04\x04\x04\x04\x04\x04" | $shuffleip[$l >> 16 & 0xff] & "\x02\x02\x02\x02\x02\x02\x02\x02" | $shuffleip[$l >> 24 & 0xff] & "\x01\x01\x01\x01\x01\x01\x01\x01"; + // Extract L0 and R0. + $t = \unpack('Nl/Nr', $block); + list($l, $r) = [$t['l'], $t['r']]; + for ($des_round = 0; $des_round < $this->des_rounds; ++$des_round) { + // Perform the 16 steps. + for ($i = 0; $i < 16; $i++) { + // start of "the Feistel (F) function" - see the following URL: + // http://en.wikipedia.org/wiki/Image:Data_Encryption_Standard_InfoBox_Diagram.png + // Merge key schedule. + $b1 = $r >> 3 & 0x1fffffff ^ $r << 29 ^ $keys[++$ki]; + $b2 = $r >> 31 & 0x1 ^ $r << 1 ^ $keys[++$ki]; + // S-box indexing. + $t = $sbox1[$b1 >> 24 & 0x3f] ^ $sbox2[$b2 >> 24 & 0x3f] ^ $sbox3[$b1 >> 16 & 0x3f] ^ $sbox4[$b2 >> 16 & 0x3f] ^ $sbox5[$b1 >> 8 & 0x3f] ^ $sbox6[$b2 >> 8 & 0x3f] ^ $sbox7[$b1 & 0x3f] ^ $sbox8[$b2 & 0x3f] ^ $l; + // end of "the Feistel (F) function" + $l = $r; + $r = $t; + } + // Last step should not permute L & R. + $t = $l; + $l = $r; + $r = $t; + } + // Perform the inverse IP permutation. + return $shuffleinvip[$r >> 24 & 0xff] & "\x80\x80\x80\x80\x80\x80\x80\x80" | $shuffleinvip[$l >> 24 & 0xff] & "@@@@@@@@" | $shuffleinvip[$r >> 16 & 0xff] & " " | $shuffleinvip[$l >> 16 & 0xff] & "\x10\x10\x10\x10\x10\x10\x10\x10" | $shuffleinvip[$r >> 8 & 0xff] & "\x08\x08\x08\x08\x08\x08\x08\x08" | $shuffleinvip[$l >> 8 & 0xff] & "\x04\x04\x04\x04\x04\x04\x04\x04" | $shuffleinvip[$r & 0xff] & "\x02\x02\x02\x02\x02\x02\x02\x02" | $shuffleinvip[$l & 0xff] & "\x01\x01\x01\x01\x01\x01\x01\x01"; + } + /** + * Creates the key schedule + * + * @see \phpseclib3\Crypt\Common\SymmetricKey::setupKey() + */ + protected function setupKey() + { + if (isset($this->kl['key']) && $this->key === $this->kl['key'] && $this->des_rounds === $this->kl['des_rounds']) { + // already expanded + return; + } + $this->kl = ['key' => $this->key, 'des_rounds' => $this->des_rounds]; + static $shifts = [ + // number of key bits shifted per round + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + ]; + static $pc1map = [0x0, 0x0, 0x8, 0x8, 0x4, 0x4, 0xc, 0xc, 0x2, 0x2, 0xa, 0xa, 0x6, 0x6, 0xe, 0xe, 0x10, 0x10, 0x18, 0x18, 0x14, 0x14, 0x1c, 0x1c, 0x12, 0x12, 0x1a, 0x1a, 0x16, 0x16, 0x1e, 0x1e, 0x20, 0x20, 0x28, 0x28, 0x24, 0x24, 0x2c, 0x2c, 0x22, 0x22, 0x2a, 0x2a, 0x26, 0x26, 0x2e, 0x2e, 0x30, 0x30, 0x38, 0x38, 0x34, 0x34, 0x3c, 0x3c, 0x32, 0x32, 0x3a, 0x3a, 0x36, 0x36, 0x3e, 0x3e, 0x40, 0x40, 0x48, 0x48, 0x44, 0x44, 0x4c, 0x4c, 0x42, 0x42, 0x4a, 0x4a, 0x46, 0x46, 0x4e, 0x4e, 0x50, 0x50, 0x58, 0x58, 0x54, 0x54, 0x5c, 0x5c, 0x52, 0x52, 0x5a, 0x5a, 0x56, 0x56, 0x5e, 0x5e, 0x60, 0x60, 0x68, 0x68, 0x64, 0x64, 0x6c, 0x6c, 0x62, 0x62, 0x6a, 0x6a, 0x66, 0x66, 0x6e, 0x6e, 0x70, 0x70, 0x78, 0x78, 0x74, 0x74, 0x7c, 0x7c, 0x72, 0x72, 0x7a, 0x7a, 0x76, 0x76, 0x7e, 0x7e, 0x80, 0x80, 0x88, 0x88, 0x84, 0x84, 0x8c, 0x8c, 0x82, 0x82, 0x8a, 0x8a, 0x86, 0x86, 0x8e, 0x8e, 0x90, 0x90, 0x98, 0x98, 0x94, 0x94, 0x9c, 0x9c, 0x92, 0x92, 0x9a, 0x9a, 0x96, 0x96, 0x9e, 0x9e, 0xa0, 0xa0, 0xa8, 0xa8, 0xa4, 0xa4, 0xac, 0xac, 0xa2, 0xa2, 0xaa, 0xaa, 0xa6, 0xa6, 0xae, 0xae, 0xb0, 0xb0, 0xb8, 0xb8, 0xb4, 0xb4, 0xbc, 0xbc, 0xb2, 0xb2, 0xba, 0xba, 0xb6, 0xb6, 0xbe, 0xbe, 0xc0, 0xc0, 0xc8, 0xc8, 0xc4, 0xc4, 0xcc, 0xcc, 0xc2, 0xc2, 0xca, 0xca, 0xc6, 0xc6, 0xce, 0xce, 0xd0, 0xd0, 0xd8, 0xd8, 0xd4, 0xd4, 0xdc, 0xdc, 0xd2, 0xd2, 0xda, 0xda, 0xd6, 0xd6, 0xde, 0xde, 0xe0, 0xe0, 0xe8, 0xe8, 0xe4, 0xe4, 0xec, 0xec, 0xe2, 0xe2, 0xea, 0xea, 0xe6, 0xe6, 0xee, 0xee, 0xf0, 0xf0, 0xf8, 0xf8, 0xf4, 0xf4, 0xfc, 0xfc, 0xf2, 0xf2, 0xfa, 0xfa, 0xf6, 0xf6, 0xfe, 0xfe]; + // Mapping tables for the PC-2 transformation. + static $pc2mapc1 = [0x0, 0x400, 0x200000, 0x200400, 0x1, 0x401, 0x200001, 0x200401, 0x2000000, 0x2000400, 0x2200000, 0x2200400, 0x2000001, 0x2000401, 0x2200001, 0x2200401]; + static $pc2mapc2 = [0x0, 0x800, 0x8000000, 0x8000800, 0x10000, 0x10800, 0x8010000, 0x8010800, 0x0, 0x800, 0x8000000, 0x8000800, 0x10000, 0x10800, 0x8010000, 0x8010800, 0x100, 0x900, 0x8000100, 0x8000900, 0x10100, 0x10900, 0x8010100, 0x8010900, 0x100, 0x900, 0x8000100, 0x8000900, 0x10100, 0x10900, 0x8010100, 0x8010900, 0x10, 0x810, 0x8000010, 0x8000810, 0x10010, 0x10810, 0x8010010, 0x8010810, 0x10, 0x810, 0x8000010, 0x8000810, 0x10010, 0x10810, 0x8010010, 0x8010810, 0x110, 0x910, 0x8000110, 0x8000910, 0x10110, 0x10910, 0x8010110, 0x8010910, 0x110, 0x910, 0x8000110, 0x8000910, 0x10110, 0x10910, 0x8010110, 0x8010910, 0x40000, 0x40800, 0x8040000, 0x8040800, 0x50000, 0x50800, 0x8050000, 0x8050800, 0x40000, 0x40800, 0x8040000, 0x8040800, 0x50000, 0x50800, 0x8050000, 0x8050800, 0x40100, 0x40900, 0x8040100, 0x8040900, 0x50100, 0x50900, 0x8050100, 0x8050900, 0x40100, 0x40900, 0x8040100, 0x8040900, 0x50100, 0x50900, 0x8050100, 0x8050900, 0x40010, 0x40810, 0x8040010, 0x8040810, 0x50010, 0x50810, 0x8050010, 0x8050810, 0x40010, 0x40810, 0x8040010, 0x8040810, 0x50010, 0x50810, 0x8050010, 0x8050810, 0x40110, 0x40910, 0x8040110, 0x8040910, 0x50110, 0x50910, 0x8050110, 0x8050910, 0x40110, 0x40910, 0x8040110, 0x8040910, 0x50110, 0x50910, 0x8050110, 0x8050910, 0x1000000, 0x1000800, 0x9000000, 0x9000800, 0x1010000, 0x1010800, 0x9010000, 0x9010800, 0x1000000, 0x1000800, 0x9000000, 0x9000800, 0x1010000, 0x1010800, 0x9010000, 0x9010800, 0x1000100, 0x1000900, 0x9000100, 0x9000900, 0x1010100, 0x1010900, 0x9010100, 0x9010900, 0x1000100, 0x1000900, 0x9000100, 0x9000900, 0x1010100, 0x1010900, 0x9010100, 0x9010900, 0x1000010, 0x1000810, 0x9000010, 0x9000810, 0x1010010, 0x1010810, 0x9010010, 0x9010810, 0x1000010, 0x1000810, 0x9000010, 0x9000810, 0x1010010, 0x1010810, 0x9010010, 0x9010810, 0x1000110, 0x1000910, 0x9000110, 0x9000910, 0x1010110, 0x1010910, 0x9010110, 0x9010910, 0x1000110, 0x1000910, 0x9000110, 0x9000910, 0x1010110, 0x1010910, 0x9010110, 0x9010910, 0x1040000, 0x1040800, 0x9040000, 0x9040800, 0x1050000, 0x1050800, 0x9050000, 0x9050800, 0x1040000, 0x1040800, 0x9040000, 0x9040800, 0x1050000, 0x1050800, 0x9050000, 0x9050800, 0x1040100, 0x1040900, 0x9040100, 0x9040900, 0x1050100, 0x1050900, 0x9050100, 0x9050900, 0x1040100, 0x1040900, 0x9040100, 0x9040900, 0x1050100, 0x1050900, 0x9050100, 0x9050900, 0x1040010, 0x1040810, 0x9040010, 0x9040810, 0x1050010, 0x1050810, 0x9050010, 0x9050810, 0x1040010, 0x1040810, 0x9040010, 0x9040810, 0x1050010, 0x1050810, 0x9050010, 0x9050810, 0x1040110, 0x1040910, 0x9040110, 0x9040910, 0x1050110, 0x1050910, 0x9050110, 0x9050910, 0x1040110, 0x1040910, 0x9040110, 0x9040910, 0x1050110, 0x1050910, 0x9050110, 0x9050910]; + static $pc2mapc3 = [0x0, 0x4, 0x1000, 0x1004, 0x0, 0x4, 0x1000, 0x1004, 0x10000000, 0x10000004, 0x10001000, 0x10001004, 0x10000000, 0x10000004, 0x10001000, 0x10001004, 0x20, 0x24, 0x1020, 0x1024, 0x20, 0x24, 0x1020, 0x1024, 0x10000020, 0x10000024, 0x10001020, 0x10001024, 0x10000020, 0x10000024, 0x10001020, 0x10001024, 0x80000, 0x80004, 0x81000, 0x81004, 0x80000, 0x80004, 0x81000, 0x81004, 0x10080000, 0x10080004, 0x10081000, 0x10081004, 0x10080000, 0x10080004, 0x10081000, 0x10081004, 0x80020, 0x80024, 0x81020, 0x81024, 0x80020, 0x80024, 0x81020, 0x81024, 0x10080020, 0x10080024, 0x10081020, 0x10081024, 0x10080020, 0x10080024, 0x10081020, 0x10081024, 0x20000000, 0x20000004, 0x20001000, 0x20001004, 0x20000000, 0x20000004, 0x20001000, 0x20001004, 0x30000000, 0x30000004, 0x30001000, 0x30001004, 0x30000000, 0x30000004, 0x30001000, 0x30001004, 0x20000020, 0x20000024, 0x20001020, 0x20001024, 0x20000020, 0x20000024, 0x20001020, 0x20001024, 0x30000020, 0x30000024, 0x30001020, 0x30001024, 0x30000020, 0x30000024, 0x30001020, 0x30001024, 0x20080000, 0x20080004, 0x20081000, 0x20081004, 0x20080000, 0x20080004, 0x20081000, 0x20081004, 0x30080000, 0x30080004, 0x30081000, 0x30081004, 0x30080000, 0x30080004, 0x30081000, 0x30081004, 0x20080020, 0x20080024, 0x20081020, 0x20081024, 0x20080020, 0x20080024, 0x20081020, 0x20081024, 0x30080020, 0x30080024, 0x30081020, 0x30081024, 0x30080020, 0x30080024, 0x30081020, 0x30081024, 0x2, 0x6, 0x1002, 0x1006, 0x2, 0x6, 0x1002, 0x1006, 0x10000002, 0x10000006, 0x10001002, 0x10001006, 0x10000002, 0x10000006, 0x10001002, 0x10001006, 0x22, 0x26, 0x1022, 0x1026, 0x22, 0x26, 0x1022, 0x1026, 0x10000022, 0x10000026, 0x10001022, 0x10001026, 0x10000022, 0x10000026, 0x10001022, 0x10001026, 0x80002, 0x80006, 0x81002, 0x81006, 0x80002, 0x80006, 0x81002, 0x81006, 0x10080002, 0x10080006, 0x10081002, 0x10081006, 0x10080002, 0x10080006, 0x10081002, 0x10081006, 0x80022, 0x80026, 0x81022, 0x81026, 0x80022, 0x80026, 0x81022, 0x81026, 0x10080022, 0x10080026, 0x10081022, 0x10081026, 0x10080022, 0x10080026, 0x10081022, 0x10081026, 0x20000002, 0x20000006, 0x20001002, 0x20001006, 0x20000002, 0x20000006, 0x20001002, 0x20001006, 0x30000002, 0x30000006, 0x30001002, 0x30001006, 0x30000002, 0x30000006, 0x30001002, 0x30001006, 0x20000022, 0x20000026, 0x20001022, 0x20001026, 0x20000022, 0x20000026, 0x20001022, 0x20001026, 0x30000022, 0x30000026, 0x30001022, 0x30001026, 0x30000022, 0x30000026, 0x30001022, 0x30001026, 0x20080002, 0x20080006, 0x20081002, 0x20081006, 0x20080002, 0x20080006, 0x20081002, 0x20081006, 0x30080002, 0x30080006, 0x30081002, 0x30081006, 0x30080002, 0x30080006, 0x30081002, 0x30081006, 0x20080022, 0x20080026, 0x20081022, 0x20081026, 0x20080022, 0x20080026, 0x20081022, 0x20081026, 0x30080022, 0x30080026, 0x30081022, 0x30081026, 0x30080022, 0x30080026, 0x30081022, 0x30081026]; + static $pc2mapc4 = [0x0, 0x100000, 0x8, 0x100008, 0x200, 0x100200, 0x208, 0x100208, 0x0, 0x100000, 0x8, 0x100008, 0x200, 0x100200, 0x208, 0x100208, 0x4000000, 0x4100000, 0x4000008, 0x4100008, 0x4000200, 0x4100200, 0x4000208, 0x4100208, 0x4000000, 0x4100000, 0x4000008, 0x4100008, 0x4000200, 0x4100200, 0x4000208, 0x4100208, 0x2000, 0x102000, 0x2008, 0x102008, 0x2200, 0x102200, 0x2208, 0x102208, 0x2000, 0x102000, 0x2008, 0x102008, 0x2200, 0x102200, 0x2208, 0x102208, 0x4002000, 0x4102000, 0x4002008, 0x4102008, 0x4002200, 0x4102200, 0x4002208, 0x4102208, 0x4002000, 0x4102000, 0x4002008, 0x4102008, 0x4002200, 0x4102200, 0x4002208, 0x4102208, 0x0, 0x100000, 0x8, 0x100008, 0x200, 0x100200, 0x208, 0x100208, 0x0, 0x100000, 0x8, 0x100008, 0x200, 0x100200, 0x208, 0x100208, 0x4000000, 0x4100000, 0x4000008, 0x4100008, 0x4000200, 0x4100200, 0x4000208, 0x4100208, 0x4000000, 0x4100000, 0x4000008, 0x4100008, 0x4000200, 0x4100200, 0x4000208, 0x4100208, 0x2000, 0x102000, 0x2008, 0x102008, 0x2200, 0x102200, 0x2208, 0x102208, 0x2000, 0x102000, 0x2008, 0x102008, 0x2200, 0x102200, 0x2208, 0x102208, 0x4002000, 0x4102000, 0x4002008, 0x4102008, 0x4002200, 0x4102200, 0x4002208, 0x4102208, 0x4002000, 0x4102000, 0x4002008, 0x4102008, 0x4002200, 0x4102200, 0x4002208, 0x4102208, 0x20000, 0x120000, 0x20008, 0x120008, 0x20200, 0x120200, 0x20208, 0x120208, 0x20000, 0x120000, 0x20008, 0x120008, 0x20200, 0x120200, 0x20208, 0x120208, 0x4020000, 0x4120000, 0x4020008, 0x4120008, 0x4020200, 0x4120200, 0x4020208, 0x4120208, 0x4020000, 0x4120000, 0x4020008, 0x4120008, 0x4020200, 0x4120200, 0x4020208, 0x4120208, 0x22000, 0x122000, 0x22008, 0x122008, 0x22200, 0x122200, 0x22208, 0x122208, 0x22000, 0x122000, 0x22008, 0x122008, 0x22200, 0x122200, 0x22208, 0x122208, 0x4022000, 0x4122000, 0x4022008, 0x4122008, 0x4022200, 0x4122200, 0x4022208, 0x4122208, 0x4022000, 0x4122000, 0x4022008, 0x4122008, 0x4022200, 0x4122200, 0x4022208, 0x4122208, 0x20000, 0x120000, 0x20008, 0x120008, 0x20200, 0x120200, 0x20208, 0x120208, 0x20000, 0x120000, 0x20008, 0x120008, 0x20200, 0x120200, 0x20208, 0x120208, 0x4020000, 0x4120000, 0x4020008, 0x4120008, 0x4020200, 0x4120200, 0x4020208, 0x4120208, 0x4020000, 0x4120000, 0x4020008, 0x4120008, 0x4020200, 0x4120200, 0x4020208, 0x4120208, 0x22000, 0x122000, 0x22008, 0x122008, 0x22200, 0x122200, 0x22208, 0x122208, 0x22000, 0x122000, 0x22008, 0x122008, 0x22200, 0x122200, 0x22208, 0x122208, 0x4022000, 0x4122000, 0x4022008, 0x4122008, 0x4022200, 0x4122200, 0x4022208, 0x4122208, 0x4022000, 0x4122000, 0x4022008, 0x4122008, 0x4022200, 0x4122200, 0x4022208, 0x4122208]; + static $pc2mapd1 = [0x0, 0x1, 0x8000000, 0x8000001, 0x200000, 0x200001, 0x8200000, 0x8200001, 0x2, 0x3, 0x8000002, 0x8000003, 0x200002, 0x200003, 0x8200002, 0x8200003]; + static $pc2mapd2 = [0x0, 0x100000, 0x800, 0x100800, 0x0, 0x100000, 0x800, 0x100800, 0x4000000, 0x4100000, 0x4000800, 0x4100800, 0x4000000, 0x4100000, 0x4000800, 0x4100800, 0x4, 0x100004, 0x804, 0x100804, 0x4, 0x100004, 0x804, 0x100804, 0x4000004, 0x4100004, 0x4000804, 0x4100804, 0x4000004, 0x4100004, 0x4000804, 0x4100804, 0x0, 0x100000, 0x800, 0x100800, 0x0, 0x100000, 0x800, 0x100800, 0x4000000, 0x4100000, 0x4000800, 0x4100800, 0x4000000, 0x4100000, 0x4000800, 0x4100800, 0x4, 0x100004, 0x804, 0x100804, 0x4, 0x100004, 0x804, 0x100804, 0x4000004, 0x4100004, 0x4000804, 0x4100804, 0x4000004, 0x4100004, 0x4000804, 0x4100804, 0x200, 0x100200, 0xa00, 0x100a00, 0x200, 0x100200, 0xa00, 0x100a00, 0x4000200, 0x4100200, 0x4000a00, 0x4100a00, 0x4000200, 0x4100200, 0x4000a00, 0x4100a00, 0x204, 0x100204, 0xa04, 0x100a04, 0x204, 0x100204, 0xa04, 0x100a04, 0x4000204, 0x4100204, 0x4000a04, 0x4100a04, 0x4000204, 0x4100204, 0x4000a04, 0x4100a04, 0x200, 0x100200, 0xa00, 0x100a00, 0x200, 0x100200, 0xa00, 0x100a00, 0x4000200, 0x4100200, 0x4000a00, 0x4100a00, 0x4000200, 0x4100200, 0x4000a00, 0x4100a00, 0x204, 0x100204, 0xa04, 0x100a04, 0x204, 0x100204, 0xa04, 0x100a04, 0x4000204, 0x4100204, 0x4000a04, 0x4100a04, 0x4000204, 0x4100204, 0x4000a04, 0x4100a04, 0x20000, 0x120000, 0x20800, 0x120800, 0x20000, 0x120000, 0x20800, 0x120800, 0x4020000, 0x4120000, 0x4020800, 0x4120800, 0x4020000, 0x4120000, 0x4020800, 0x4120800, 0x20004, 0x120004, 0x20804, 0x120804, 0x20004, 0x120004, 0x20804, 0x120804, 0x4020004, 0x4120004, 0x4020804, 0x4120804, 0x4020004, 0x4120004, 0x4020804, 0x4120804, 0x20000, 0x120000, 0x20800, 0x120800, 0x20000, 0x120000, 0x20800, 0x120800, 0x4020000, 0x4120000, 0x4020800, 0x4120800, 0x4020000, 0x4120000, 0x4020800, 0x4120800, 0x20004, 0x120004, 0x20804, 0x120804, 0x20004, 0x120004, 0x20804, 0x120804, 0x4020004, 0x4120004, 0x4020804, 0x4120804, 0x4020004, 0x4120004, 0x4020804, 0x4120804, 0x20200, 0x120200, 0x20a00, 0x120a00, 0x20200, 0x120200, 0x20a00, 0x120a00, 0x4020200, 0x4120200, 0x4020a00, 0x4120a00, 0x4020200, 0x4120200, 0x4020a00, 0x4120a00, 0x20204, 0x120204, 0x20a04, 0x120a04, 0x20204, 0x120204, 0x20a04, 0x120a04, 0x4020204, 0x4120204, 0x4020a04, 0x4120a04, 0x4020204, 0x4120204, 0x4020a04, 0x4120a04, 0x20200, 0x120200, 0x20a00, 0x120a00, 0x20200, 0x120200, 0x20a00, 0x120a00, 0x4020200, 0x4120200, 0x4020a00, 0x4120a00, 0x4020200, 0x4120200, 0x4020a00, 0x4120a00, 0x20204, 0x120204, 0x20a04, 0x120a04, 0x20204, 0x120204, 0x20a04, 0x120a04, 0x4020204, 0x4120204, 0x4020a04, 0x4120a04, 0x4020204, 0x4120204, 0x4020a04, 0x4120a04]; + static $pc2mapd3 = [0x0, 0x10000, 0x2000000, 0x2010000, 0x20, 0x10020, 0x2000020, 0x2010020, 0x40000, 0x50000, 0x2040000, 0x2050000, 0x40020, 0x50020, 0x2040020, 0x2050020, 0x2000, 0x12000, 0x2002000, 0x2012000, 0x2020, 0x12020, 0x2002020, 0x2012020, 0x42000, 0x52000, 0x2042000, 0x2052000, 0x42020, 0x52020, 0x2042020, 0x2052020, 0x0, 0x10000, 0x2000000, 0x2010000, 0x20, 0x10020, 0x2000020, 0x2010020, 0x40000, 0x50000, 0x2040000, 0x2050000, 0x40020, 0x50020, 0x2040020, 0x2050020, 0x2000, 0x12000, 0x2002000, 0x2012000, 0x2020, 0x12020, 0x2002020, 0x2012020, 0x42000, 0x52000, 0x2042000, 0x2052000, 0x42020, 0x52020, 0x2042020, 0x2052020, 0x10, 0x10010, 0x2000010, 0x2010010, 0x30, 0x10030, 0x2000030, 0x2010030, 0x40010, 0x50010, 0x2040010, 0x2050010, 0x40030, 0x50030, 0x2040030, 0x2050030, 0x2010, 0x12010, 0x2002010, 0x2012010, 0x2030, 0x12030, 0x2002030, 0x2012030, 0x42010, 0x52010, 0x2042010, 0x2052010, 0x42030, 0x52030, 0x2042030, 0x2052030, 0x10, 0x10010, 0x2000010, 0x2010010, 0x30, 0x10030, 0x2000030, 0x2010030, 0x40010, 0x50010, 0x2040010, 0x2050010, 0x40030, 0x50030, 0x2040030, 0x2050030, 0x2010, 0x12010, 0x2002010, 0x2012010, 0x2030, 0x12030, 0x2002030, 0x2012030, 0x42010, 0x52010, 0x2042010, 0x2052010, 0x42030, 0x52030, 0x2042030, 0x2052030, 0x20000000, 0x20010000, 0x22000000, 0x22010000, 0x20000020, 0x20010020, 0x22000020, 0x22010020, 0x20040000, 0x20050000, 0x22040000, 0x22050000, 0x20040020, 0x20050020, 0x22040020, 0x22050020, 0x20002000, 0x20012000, 0x22002000, 0x22012000, 0x20002020, 0x20012020, 0x22002020, 0x22012020, 0x20042000, 0x20052000, 0x22042000, 0x22052000, 0x20042020, 0x20052020, 0x22042020, 0x22052020, 0x20000000, 0x20010000, 0x22000000, 0x22010000, 0x20000020, 0x20010020, 0x22000020, 0x22010020, 0x20040000, 0x20050000, 0x22040000, 0x22050000, 0x20040020, 0x20050020, 0x22040020, 0x22050020, 0x20002000, 0x20012000, 0x22002000, 0x22012000, 0x20002020, 0x20012020, 0x22002020, 0x22012020, 0x20042000, 0x20052000, 0x22042000, 0x22052000, 0x20042020, 0x20052020, 0x22042020, 0x22052020, 0x20000010, 0x20010010, 0x22000010, 0x22010010, 0x20000030, 0x20010030, 0x22000030, 0x22010030, 0x20040010, 0x20050010, 0x22040010, 0x22050010, 0x20040030, 0x20050030, 0x22040030, 0x22050030, 0x20002010, 0x20012010, 0x22002010, 0x22012010, 0x20002030, 0x20012030, 0x22002030, 0x22012030, 0x20042010, 0x20052010, 0x22042010, 0x22052010, 0x20042030, 0x20052030, 0x22042030, 0x22052030, 0x20000010, 0x20010010, 0x22000010, 0x22010010, 0x20000030, 0x20010030, 0x22000030, 0x22010030, 0x20040010, 0x20050010, 0x22040010, 0x22050010, 0x20040030, 0x20050030, 0x22040030, 0x22050030, 0x20002010, 0x20012010, 0x22002010, 0x22012010, 0x20002030, 0x20012030, 0x22002030, 0x22012030, 0x20042010, 0x20052010, 0x22042010, 0x22052010, 0x20042030, 0x20052030, 0x22042030, 0x22052030]; + static $pc2mapd4 = [0x0, 0x400, 0x1000000, 0x1000400, 0x0, 0x400, 0x1000000, 0x1000400, 0x100, 0x500, 0x1000100, 0x1000500, 0x100, 0x500, 0x1000100, 0x1000500, 0x10000000, 0x10000400, 0x11000000, 0x11000400, 0x10000000, 0x10000400, 0x11000000, 0x11000400, 0x10000100, 0x10000500, 0x11000100, 0x11000500, 0x10000100, 0x10000500, 0x11000100, 0x11000500, 0x80000, 0x80400, 0x1080000, 0x1080400, 0x80000, 0x80400, 0x1080000, 0x1080400, 0x80100, 0x80500, 0x1080100, 0x1080500, 0x80100, 0x80500, 0x1080100, 0x1080500, 0x10080000, 0x10080400, 0x11080000, 0x11080400, 0x10080000, 0x10080400, 0x11080000, 0x11080400, 0x10080100, 0x10080500, 0x11080100, 0x11080500, 0x10080100, 0x10080500, 0x11080100, 0x11080500, 0x8, 0x408, 0x1000008, 0x1000408, 0x8, 0x408, 0x1000008, 0x1000408, 0x108, 0x508, 0x1000108, 0x1000508, 0x108, 0x508, 0x1000108, 0x1000508, 0x10000008, 0x10000408, 0x11000008, 0x11000408, 0x10000008, 0x10000408, 0x11000008, 0x11000408, 0x10000108, 0x10000508, 0x11000108, 0x11000508, 0x10000108, 0x10000508, 0x11000108, 0x11000508, 0x80008, 0x80408, 0x1080008, 0x1080408, 0x80008, 0x80408, 0x1080008, 0x1080408, 0x80108, 0x80508, 0x1080108, 0x1080508, 0x80108, 0x80508, 0x1080108, 0x1080508, 0x10080008, 0x10080408, 0x11080008, 0x11080408, 0x10080008, 0x10080408, 0x11080008, 0x11080408, 0x10080108, 0x10080508, 0x11080108, 0x11080508, 0x10080108, 0x10080508, 0x11080108, 0x11080508, 0x1000, 0x1400, 0x1001000, 0x1001400, 0x1000, 0x1400, 0x1001000, 0x1001400, 0x1100, 0x1500, 0x1001100, 0x1001500, 0x1100, 0x1500, 0x1001100, 0x1001500, 0x10001000, 0x10001400, 0x11001000, 0x11001400, 0x10001000, 0x10001400, 0x11001000, 0x11001400, 0x10001100, 0x10001500, 0x11001100, 0x11001500, 0x10001100, 0x10001500, 0x11001100, 0x11001500, 0x81000, 0x81400, 0x1081000, 0x1081400, 0x81000, 0x81400, 0x1081000, 0x1081400, 0x81100, 0x81500, 0x1081100, 0x1081500, 0x81100, 0x81500, 0x1081100, 0x1081500, 0x10081000, 0x10081400, 0x11081000, 0x11081400, 0x10081000, 0x10081400, 0x11081000, 0x11081400, 0x10081100, 0x10081500, 0x11081100, 0x11081500, 0x10081100, 0x10081500, 0x11081100, 0x11081500, 0x1008, 0x1408, 0x1001008, 0x1001408, 0x1008, 0x1408, 0x1001008, 0x1001408, 0x1108, 0x1508, 0x1001108, 0x1001508, 0x1108, 0x1508, 0x1001108, 0x1001508, 0x10001008, 0x10001408, 0x11001008, 0x11001408, 0x10001008, 0x10001408, 0x11001008, 0x11001408, 0x10001108, 0x10001508, 0x11001108, 0x11001508, 0x10001108, 0x10001508, 0x11001108, 0x11001508, 0x81008, 0x81408, 0x1081008, 0x1081408, 0x81008, 0x81408, 0x1081008, 0x1081408, 0x81108, 0x81508, 0x1081108, 0x1081508, 0x81108, 0x81508, 0x1081108, 0x1081508, 0x10081008, 0x10081408, 0x11081008, 0x11081408, 0x10081008, 0x10081408, 0x11081008, 0x11081408, 0x10081108, 0x10081508, 0x11081108, 0x11081508, 0x10081108, 0x10081508, 0x11081108, 0x11081508]; + $keys = []; + for ($des_round = 0; $des_round < $this->des_rounds; ++$des_round) { + // pad the key and remove extra characters as appropriate. + $key = \str_pad(\substr($this->key, $des_round * 8, 8), 8, "\x00"); + // Perform the PC/1 transformation and compute C and D. + $t = \unpack('Nl/Nr', $key); + list($l, $r) = [$t['l'], $t['r']]; + $key = self::$shuffle[$pc1map[$r & 0xff]] & "\x80\x80\x80\x80\x80\x80\x80\x00" | self::$shuffle[$pc1map[$r >> 8 & 0xff]] & "@@@@@@@\x00" | self::$shuffle[$pc1map[$r >> 16 & 0xff]] & " \x00" | self::$shuffle[$pc1map[$r >> 24 & 0xff]] & "\x10\x10\x10\x10\x10\x10\x10\x00" | self::$shuffle[$pc1map[$l & 0xff]] & "\x08\x08\x08\x08\x08\x08\x08\x00" | self::$shuffle[$pc1map[$l >> 8 & 0xff]] & "\x04\x04\x04\x04\x04\x04\x04\x00" | self::$shuffle[$pc1map[$l >> 16 & 0xff]] & "\x02\x02\x02\x02\x02\x02\x02\x00" | self::$shuffle[$pc1map[$l >> 24 & 0xff]] & "\x01\x01\x01\x01\x01\x01\x01\x00"; + $key = \unpack('Nc/Nd', $key); + $c = $key['c'] >> 4 & 0xfffffff; + $d = $key['d'] >> 4 & 0xffffff0 | $key['c'] & 0xf; + $keys[$des_round] = [self::ENCRYPT => [], self::DECRYPT => \array_fill(0, 32, 0)]; + for ($i = 0, $ki = 31; $i < 16; ++$i, $ki -= 2) { + $c <<= $shifts[$i]; + $c = ($c | $c >> 28) & 0xfffffff; + $d <<= $shifts[$i]; + $d = ($d | $d >> 28) & 0xfffffff; + // Perform the PC-2 transformation. + $cp = $pc2mapc1[$c >> 24] | $pc2mapc2[$c >> 16 & 0xff] | $pc2mapc3[$c >> 8 & 0xff] | $pc2mapc4[$c & 0xff]; + $dp = $pc2mapd1[$d >> 24] | $pc2mapd2[$d >> 16 & 0xff] | $pc2mapd3[$d >> 8 & 0xff] | $pc2mapd4[$d & 0xff]; + // Reorder: odd bytes/even bytes. Push the result in key schedule. + $val1 = $cp & \intval(0xff000000) | $cp << 8 & 0xff0000 | $dp >> 16 & 0xff00 | $dp >> 8 & 0xff; + $val2 = $cp << 8 & \intval(0xff000000) | $cp << 16 & 0xff0000 | $dp >> 8 & 0xff00 | $dp & 0xff; + $keys[$des_round][self::ENCRYPT][] = $val1; + $keys[$des_round][self::DECRYPT][$ki - 1] = $val1; + $keys[$des_round][self::ENCRYPT][] = $val2; + $keys[$des_round][self::DECRYPT][$ki] = $val2; + } + } + switch ($this->des_rounds) { + case 3: + // 3DES keys + $this->keys = [self::ENCRYPT => \array_merge($keys[0][self::ENCRYPT], $keys[1][self::DECRYPT], $keys[2][self::ENCRYPT]), self::DECRYPT => \array_merge($keys[2][self::DECRYPT], $keys[1][self::ENCRYPT], $keys[0][self::DECRYPT])]; + break; + // case 1: // DES keys + default: + $this->keys = [self::ENCRYPT => $keys[0][self::ENCRYPT], self::DECRYPT => $keys[0][self::DECRYPT]]; + } + } + /** + * Setup the performance-optimized function for de/encrypt() + * + * @see \phpseclib3\Crypt\Common\SymmetricKey::setupInlineCrypt() + */ + protected function setupInlineCrypt() + { + // Engine configuration for: + // - DES ($des_rounds == 1) or + // - 3DES ($des_rounds == 3) + $des_rounds = $this->des_rounds; + $init_crypt = 'static $sbox1, $sbox2, $sbox3, $sbox4, $sbox5, $sbox6, $sbox7, $sbox8, $shuffleip, $shuffleinvip; + if (!$sbox1) { + $sbox1 = array_map("intval", self::$sbox1); + $sbox2 = array_map("intval", self::$sbox2); + $sbox3 = array_map("intval", self::$sbox3); + $sbox4 = array_map("intval", self::$sbox4); + $sbox5 = array_map("intval", self::$sbox5); + $sbox6 = array_map("intval", self::$sbox6); + $sbox7 = array_map("intval", self::$sbox7); + $sbox8 = array_map("intval", self::$sbox8);' . ' + for ($i = 0; $i < 256; ++$i) { + $shuffleip[] = self::$shuffle[self::$ipmap[$i]]; + $shuffleinvip[] = self::$shuffle[self::$invipmap[$i]]; + } + } + '; + $k = [self::ENCRYPT => $this->keys[self::ENCRYPT], self::DECRYPT => $this->keys[self::DECRYPT]]; + $init_encrypt = ''; + $init_decrypt = ''; + // Creating code for en- and decryption. + $crypt_block = []; + foreach ([self::ENCRYPT, self::DECRYPT] as $c) { + /* Do the initial IP permutation. */ + $crypt_block[$c] = ' + $in = unpack("N*", $in); + $l = $in[1]; + $r = $in[2]; + $in = unpack("N*", + ($shuffleip[ $r & 0xFF] & "\\x80\\x80\\x80\\x80\\x80\\x80\\x80\\x80") | + ($shuffleip[($r >> 8) & 0xFF] & "\\x40\\x40\\x40\\x40\\x40\\x40\\x40\\x40") | + ($shuffleip[($r >> 16) & 0xFF] & "\\x20\\x20\\x20\\x20\\x20\\x20\\x20\\x20") | + ($shuffleip[($r >> 24) & 0xFF] & "\\x10\\x10\\x10\\x10\\x10\\x10\\x10\\x10") | + ($shuffleip[ $l & 0xFF] & "\\x08\\x08\\x08\\x08\\x08\\x08\\x08\\x08") | + ($shuffleip[($l >> 8) & 0xFF] & "\\x04\\x04\\x04\\x04\\x04\\x04\\x04\\x04") | + ($shuffleip[($l >> 16) & 0xFF] & "\\x02\\x02\\x02\\x02\\x02\\x02\\x02\\x02") | + ($shuffleip[($l >> 24) & 0xFF] & "\\x01\\x01\\x01\\x01\\x01\\x01\\x01\\x01") + ); + ' . ' + $l = $in[1]; + $r = $in[2]; + '; + $l = '$l'; + $r = '$r'; + // Perform DES or 3DES. + for ($ki = -1, $des_round = 0; $des_round < $des_rounds; ++$des_round) { + // Perform the 16 steps. + for ($i = 0; $i < 16; ++$i) { + // start of "the Feistel (F) function" - see the following URL: + // http://en.wikipedia.org/wiki/Image:Data_Encryption_Standard_InfoBox_Diagram.png + // Merge key schedule. + $crypt_block[$c] .= ' + $b1 = ((' . $r . ' >> 3) & 0x1FFFFFFF) ^ (' . $r . ' << 29) ^ ' . $k[$c][++$ki] . '; + $b2 = ((' . $r . ' >> 31) & 0x00000001) ^ (' . $r . ' << 1) ^ ' . $k[$c][++$ki] . ';' . $l . ' = $sbox1[($b1 >> 24) & 0x3F] ^ $sbox2[($b2 >> 24) & 0x3F] ^ + $sbox3[($b1 >> 16) & 0x3F] ^ $sbox4[($b2 >> 16) & 0x3F] ^ + $sbox5[($b1 >> 8) & 0x3F] ^ $sbox6[($b2 >> 8) & 0x3F] ^ + $sbox7[ $b1 & 0x3F] ^ $sbox8[ $b2 & 0x3F] ^ ' . $l . '; + '; + // end of "the Feistel (F) function" + // swap L & R + list($l, $r) = [$r, $l]; + } + list($l, $r) = [$r, $l]; + } + // Perform the inverse IP permutation. + $crypt_block[$c] .= '$in = + ($shuffleinvip[($l >> 24) & 0xFF] & "\\x80\\x80\\x80\\x80\\x80\\x80\\x80\\x80") | + ($shuffleinvip[($r >> 24) & 0xFF] & "\\x40\\x40\\x40\\x40\\x40\\x40\\x40\\x40") | + ($shuffleinvip[($l >> 16) & 0xFF] & "\\x20\\x20\\x20\\x20\\x20\\x20\\x20\\x20") | + ($shuffleinvip[($r >> 16) & 0xFF] & "\\x10\\x10\\x10\\x10\\x10\\x10\\x10\\x10") | + ($shuffleinvip[($l >> 8) & 0xFF] & "\\x08\\x08\\x08\\x08\\x08\\x08\\x08\\x08") | + ($shuffleinvip[($r >> 8) & 0xFF] & "\\x04\\x04\\x04\\x04\\x04\\x04\\x04\\x04") | + ($shuffleinvip[ $l & 0xFF] & "\\x02\\x02\\x02\\x02\\x02\\x02\\x02\\x02") | + ($shuffleinvip[ $r & 0xFF] & "\\x01\\x01\\x01\\x01\\x01\\x01\\x01\\x01"); + '; + } + // Creates the inline-crypt function + $this->inline_crypt = $this->createInlineCryptFunction(['init_crypt' => $init_crypt, 'init_encrypt' => $init_encrypt, 'init_decrypt' => $init_decrypt, 'encrypt_block' => $crypt_block[self::ENCRYPT], 'decrypt_block' => $crypt_block[self::DECRYPT]]); + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/DH.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/DH.php new file mode 100644 index 0000000..101c479 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/DH.php @@ -0,0 +1,295 @@ + + * + * + * + * @author Jim Wigginton + * @copyright 2016 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\Crypt; + +use FluentSmtpLib\phpseclib3\Crypt\Common\AsymmetricKey; +use FluentSmtpLib\phpseclib3\Crypt\DH\Parameters; +use FluentSmtpLib\phpseclib3\Crypt\DH\PrivateKey; +use FluentSmtpLib\phpseclib3\Crypt\DH\PublicKey; +use FluentSmtpLib\phpseclib3\Exception\NoKeyLoadedException; +use FluentSmtpLib\phpseclib3\Exception\UnsupportedOperationException; +use FluentSmtpLib\phpseclib3\Math\BigInteger; +/** + * Pure-PHP (EC)DH implementation + * + * @author Jim Wigginton + */ +abstract class DH extends \FluentSmtpLib\phpseclib3\Crypt\Common\AsymmetricKey +{ + /** + * Algorithm Name + * + * @var string + */ + const ALGORITHM = 'DH'; + /** + * DH prime + * + * @var BigInteger + */ + protected $prime; + /** + * DH Base + * + * Prime divisor of p-1 + * + * @var BigInteger + */ + protected $base; + /** + * Public Key + * + * @var BigInteger + */ + protected $publicKey; + /** + * Create DH parameters + * + * This method is a bit polymorphic. It can take any of the following: + * - two BigInteger's (prime and base) + * - an integer representing the size of the prime in bits (the base is assumed to be 2) + * - a string (eg. diffie-hellman-group14-sha1) + * + * @return Parameters + */ + public static function createParameters(...$args) + { + $class = new \ReflectionClass(static::class); + if ($class->isFinal()) { + throw new \RuntimeException('createParameters() should not be called from final classes (' . static::class . ')'); + } + $params = new \FluentSmtpLib\phpseclib3\Crypt\DH\Parameters(); + if (\count($args) == 2 && $args[0] instanceof \FluentSmtpLib\phpseclib3\Math\BigInteger && $args[1] instanceof \FluentSmtpLib\phpseclib3\Math\BigInteger) { + //if (!$args[0]->isPrime()) { + // throw new \InvalidArgumentException('The first parameter should be a prime number'); + //} + $params->prime = $args[0]; + $params->base = $args[1]; + return $params; + } elseif (\count($args) == 1 && \is_numeric($args[0])) { + $params->prime = \FluentSmtpLib\phpseclib3\Math\BigInteger::randomPrime($args[0]); + $params->base = new \FluentSmtpLib\phpseclib3\Math\BigInteger(2); + return $params; + } elseif (\count($args) != 1 || !\is_string($args[0])) { + throw new \InvalidArgumentException('Valid parameters are either: two BigInteger\'s (prime and base), a single integer (the length of the prime; base is assumed to be 2) or a string'); + } + switch ($args[0]) { + // see http://tools.ietf.org/html/rfc2409#section-6.2 and + // http://tools.ietf.org/html/rfc2412, appendex E + case 'diffie-hellman-group1-sha1': + $prime = 'FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74' . '020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F1437' . '4FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED' . 'EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381FFFFFFFFFFFFFFFF'; + break; + // see http://tools.ietf.org/html/rfc3526#section-3 + case 'diffie-hellman-group14-sha1': + // 2048-bit MODP Group + case 'diffie-hellman-group14-sha256': + $prime = 'FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74' . '020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F1437' . '4FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED' . 'EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF05' . '98DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB' . '9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B' . 'E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF695581718' . '3995497CEA956AE515D2261898FA051015728E5A8AACAA68FFFFFFFFFFFFFFFF'; + break; + // see https://tools.ietf.org/html/rfc3526#section-4 + case 'diffie-hellman-group15-sha512': + // 3072-bit MODP Group + $prime = 'FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74' . '020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F1437' . '4FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED' . 'EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF05' . '98DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB' . '9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B' . 'E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF695581718' . '3995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D04507A33' . 'A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7' . 'ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6BF12FFA06D98A0864' . 'D87602733EC86A64521F2B18177B200CBBE117577A615D6C770988C0BAD946E2' . '08E24FA074E5AB3143DB5BFCE0FD108E4B82D120A93AD2CAFFFFFFFFFFFFFFFF'; + break; + // see https://tools.ietf.org/html/rfc3526#section-5 + case 'diffie-hellman-group16-sha512': + // 4096-bit MODP Group + $prime = 'FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74' . '020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F1437' . '4FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED' . 'EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF05' . '98DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB' . '9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B' . 'E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF695581718' . '3995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D04507A33' . 'A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7' . 'ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6BF12FFA06D98A0864' . 'D87602733EC86A64521F2B18177B200CBBE117577A615D6C770988C0BAD946E2' . '08E24FA074E5AB3143DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D7' . '88719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA2583E9CA2AD44CE8' . 'DBBBC2DB04DE8EF92E8EFC141FBECAA6287C59474E6BC05D99B2964FA090C3A2' . '233BA186515BE7ED1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA9' . '93B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934063199FFFFFFFFFFFFFFFF'; + break; + // see https://tools.ietf.org/html/rfc3526#section-6 + case 'diffie-hellman-group17-sha512': + // 6144-bit MODP Group + $prime = 'FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74' . '020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F1437' . '4FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED' . 'EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF05' . '98DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB' . '9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B' . 'E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF695581718' . '3995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D04507A33' . 'A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7' . 'ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6BF12FFA06D98A0864' . 'D87602733EC86A64521F2B18177B200CBBE117577A615D6C770988C0BAD946E2' . '08E24FA074E5AB3143DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D7' . '88719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA2583E9CA2AD44CE8' . 'DBBBC2DB04DE8EF92E8EFC141FBECAA6287C59474E6BC05D99B2964FA090C3A2' . '233BA186515BE7ED1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA9' . '93B4EA988D8FDDC186FFB7DC90A6C08F4DF435C93402849236C3FAB4D27C7026' . 'C1D4DCB2602646DEC9751E763DBA37BDF8FF9406AD9E530EE5DB382F413001AE' . 'B06A53ED9027D831179727B0865A8918DA3EDBEBCF9B14ED44CE6CBACED4BB1B' . 'DB7F1447E6CC254B332051512BD7AF426FB8F401378CD2BF5983CA01C64B92EC' . 'F032EA15D1721D03F482D7CE6E74FEF6D55E702F46980C82B5A84031900B1C9E' . '59E7C97FBEC7E8F323A97A7E36CC88BE0F1D45B7FF585AC54BD407B22B4154AA' . 'CC8F6D7EBF48E1D814CC5ED20F8037E0A79715EEF29BE32806A1D58BB7C5DA76' . 'F550AA3D8A1FBFF0EB19CCB1A313D55CDA56C9EC2EF29632387FE8D76E3C0468' . '043E8F663F4860EE12BF2D5B0B7474D6E694F91E6DCC4024FFFFFFFFFFFFFFFF'; + break; + // see https://tools.ietf.org/html/rfc3526#section-7 + case 'diffie-hellman-group18-sha512': + // 8192-bit MODP Group + $prime = 'FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74' . '020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F1437' . '4FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED' . 'EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF05' . '98DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB' . '9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B' . 'E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF695581718' . '3995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D04507A33' . 'A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7' . 'ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6BF12FFA06D98A0864' . 'D87602733EC86A64521F2B18177B200CBBE117577A615D6C770988C0BAD946E2' . '08E24FA074E5AB3143DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D7' . '88719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA2583E9CA2AD44CE8' . 'DBBBC2DB04DE8EF92E8EFC141FBECAA6287C59474E6BC05D99B2964FA090C3A2' . '233BA186515BE7ED1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA9' . '93B4EA988D8FDDC186FFB7DC90A6C08F4DF435C93402849236C3FAB4D27C7026' . 'C1D4DCB2602646DEC9751E763DBA37BDF8FF9406AD9E530EE5DB382F413001AE' . 'B06A53ED9027D831179727B0865A8918DA3EDBEBCF9B14ED44CE6CBACED4BB1B' . 'DB7F1447E6CC254B332051512BD7AF426FB8F401378CD2BF5983CA01C64B92EC' . 'F032EA15D1721D03F482D7CE6E74FEF6D55E702F46980C82B5A84031900B1C9E' . '59E7C97FBEC7E8F323A97A7E36CC88BE0F1D45B7FF585AC54BD407B22B4154AA' . 'CC8F6D7EBF48E1D814CC5ED20F8037E0A79715EEF29BE32806A1D58BB7C5DA76' . 'F550AA3D8A1FBFF0EB19CCB1A313D55CDA56C9EC2EF29632387FE8D76E3C0468' . '043E8F663F4860EE12BF2D5B0B7474D6E694F91E6DBE115974A3926F12FEE5E4' . '38777CB6A932DF8CD8BEC4D073B931BA3BC832B68D9DD300741FA7BF8AFC47ED' . '2576F6936BA424663AAB639C5AE4F5683423B4742BF1C978238F16CBE39D652D' . 'E3FDB8BEFC848AD922222E04A4037C0713EB57A81A23F0C73473FC646CEA306B' . '4BCBC8862F8385DDFA9D4B7FA2C087E879683303ED5BDD3A062B3CF5B3A278A6' . '6D2A13F83F44F82DDF310EE074AB6A364597E899A0255DC164F31CC50846851D' . 'F9AB48195DED7EA1B1D510BD7EE74D73FAF36BC31ECFA268359046F4EB879F92' . '4009438B481C6CD7889A002ED5EE382BC9190DA6FC026E479558E4475677E9AA' . '9E3050E2765694DFC81F56E880B96E7160C980DD98EDD3DFFFFFFFFFFFFFFFFF'; + break; + default: + throw new \InvalidArgumentException('Invalid named prime provided'); + } + $params->prime = new \FluentSmtpLib\phpseclib3\Math\BigInteger($prime, 16); + $params->base = new \FluentSmtpLib\phpseclib3\Math\BigInteger(2); + return $params; + } + /** + * Create public / private key pair. + * + * The rationale for the second parameter is described in http://tools.ietf.org/html/rfc4419#section-6.2 : + * + * "To increase the speed of the key exchange, both client and server may + * reduce the size of their private exponents. It should be at least + * twice as long as the key material that is generated from the shared + * secret. For more details, see the paper by van Oorschot and Wiener + * [VAN-OORSCHOT]." + * + * $length is in bits + * + * @param Parameters $params + * @param int $length optional + * @return PrivateKey + */ + public static function createKey(\FluentSmtpLib\phpseclib3\Crypt\DH\Parameters $params, $length = 0) + { + $class = new \ReflectionClass(static::class); + if ($class->isFinal()) { + throw new \RuntimeException('createKey() should not be called from final classes (' . static::class . ')'); + } + $one = new \FluentSmtpLib\phpseclib3\Math\BigInteger(1); + if ($length) { + $max = $one->bitwise_leftShift($length); + $max = $max->subtract($one); + } else { + $max = $params->prime->subtract($one); + } + $key = new \FluentSmtpLib\phpseclib3\Crypt\DH\PrivateKey(); + $key->prime = $params->prime; + $key->base = $params->base; + $key->privateKey = \FluentSmtpLib\phpseclib3\Math\BigInteger::randomRange($one, $max); + $key->publicKey = $key->base->powMod($key->privateKey, $key->prime); + return $key; + } + /** + * Compute Shared Secret + * + * @param PrivateKey|EC $private + * @param PublicKey|BigInteger|string $public + * @return mixed + */ + public static function computeSecret($private, $public) + { + if ($private instanceof \FluentSmtpLib\phpseclib3\Crypt\DH\PrivateKey) { + // DH\PrivateKey + switch (\true) { + case $public instanceof \FluentSmtpLib\phpseclib3\Crypt\DH\PublicKey: + if (!$private->prime->equals($public->prime) || !$private->base->equals($public->base)) { + throw new \InvalidArgumentException('The public and private key do not share the same prime and / or base numbers'); + } + return $public->publicKey->powMod($private->privateKey, $private->prime)->toBytes(\true); + case \is_string($public): + $public = new \FluentSmtpLib\phpseclib3\Math\BigInteger($public, -256); + // fall-through + case $public instanceof \FluentSmtpLib\phpseclib3\Math\BigInteger: + return $public->powMod($private->privateKey, $private->prime)->toBytes(\true); + default: + throw new \InvalidArgumentException('$public needs to be an instance of DH\\PublicKey, a BigInteger or a string'); + } + } + if ($private instanceof \FluentSmtpLib\phpseclib3\Crypt\EC\PrivateKey) { + switch (\true) { + case $public instanceof \FluentSmtpLib\phpseclib3\Crypt\EC\PublicKey: + $public = $public->getEncodedCoordinates(); + // fall-through + case \is_string($public): + $point = $private->multiply($public); + switch ($private->getCurve()) { + case 'Curve25519': + case 'Curve448': + $secret = $point; + break; + default: + // according to https://www.secg.org/sec1-v2.pdf#page=33 only X is returned + $secret = \substr($point, 1, \strlen($point) - 1 >> 1); + } + /* + if (($secret[0] & "\x80") === "\x80") { + $secret = "\0$secret"; + } + */ + return $secret; + default: + throw new \InvalidArgumentException('$public needs to be an instance of EC\\PublicKey or a string (an encoded coordinate)'); + } + } + } + /** + * Load the key + * + * @param string $key + * @param string $password optional + * @return AsymmetricKey + */ + public static function load($key, $password = \false) + { + try { + return \FluentSmtpLib\phpseclib3\Crypt\EC::load($key, $password); + } catch (\FluentSmtpLib\phpseclib3\Exception\NoKeyLoadedException $e) { + } + return parent::load($key, $password); + } + /** + * OnLoad Handler + * + * @return bool + */ + protected static function onLoad(array $components) + { + if (!isset($components['privateKey']) && !isset($components['publicKey'])) { + $new = new \FluentSmtpLib\phpseclib3\Crypt\DH\Parameters(); + } else { + $new = isset($components['privateKey']) ? new \FluentSmtpLib\phpseclib3\Crypt\DH\PrivateKey() : new \FluentSmtpLib\phpseclib3\Crypt\DH\PublicKey(); + } + $new->prime = $components['prime']; + $new->base = $components['base']; + if (isset($components['privateKey'])) { + $new->privateKey = $components['privateKey']; + } + if (isset($components['publicKey'])) { + $new->publicKey = $components['publicKey']; + } + return $new; + } + /** + * Determines which hashing function should be used + * + * @param string $hash + */ + public function withHash($hash) + { + throw new \FluentSmtpLib\phpseclib3\Exception\UnsupportedOperationException('DH does not use a hash algorithm'); + } + /** + * Returns the hash algorithm currently being used + * + */ + public function getHash() + { + throw new \FluentSmtpLib\phpseclib3\Exception\UnsupportedOperationException('DH does not use a hash algorithm'); + } + /** + * Returns the parameters + * + * A public / private key is only returned if the currently loaded "key" contains an x or y + * value. + * + * @see self::getPublicKey() + * @return mixed + */ + public function getParameters() + { + $type = \FluentSmtpLib\phpseclib3\Crypt\DH::validatePlugin('Keys', 'PKCS1', 'saveParameters'); + $key = $type::saveParameters($this->prime, $this->base); + return \FluentSmtpLib\phpseclib3\Crypt\DH::load($key, 'PKCS1'); + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/DH/Formats/Keys/PKCS1.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/DH/Formats/Keys/PKCS1.php new file mode 100644 index 0000000..b8f0d83 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/DH/Formats/Keys/PKCS1.php @@ -0,0 +1,65 @@ + + * @copyright 2015 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\Crypt\DH\Formats\Keys; + +use FluentSmtpLib\phpseclib3\Crypt\Common\Formats\Keys\PKCS1 as Progenitor; +use FluentSmtpLib\phpseclib3\File\ASN1; +use FluentSmtpLib\phpseclib3\File\ASN1\Maps; +use FluentSmtpLib\phpseclib3\Math\BigInteger; +/** + * "PKCS1" Formatted DH Key Handler + * + * @author Jim Wigginton + */ +abstract class PKCS1 extends \FluentSmtpLib\phpseclib3\Crypt\Common\Formats\Keys\PKCS1 +{ + /** + * Break a public or private key down into its constituent components + * + * @param string $key + * @param string $password optional + * @return array + */ + public static function load($key, $password = '') + { + $key = parent::load($key, $password); + $decoded = \FluentSmtpLib\phpseclib3\File\ASN1::decodeBER($key); + if (!$decoded) { + throw new \RuntimeException('Unable to decode BER'); + } + $components = \FluentSmtpLib\phpseclib3\File\ASN1::asn1map($decoded[0], \FluentSmtpLib\phpseclib3\File\ASN1\Maps\DHParameter::MAP); + if (!\is_array($components)) { + throw new \RuntimeException('Unable to perform ASN1 mapping on parameters'); + } + return $components; + } + /** + * Convert EC parameters to the appropriate format + * + * @return string + */ + public static function saveParameters(\FluentSmtpLib\phpseclib3\Math\BigInteger $prime, \FluentSmtpLib\phpseclib3\Math\BigInteger $base, array $options = []) + { + $params = ['prime' => $prime, 'base' => $base]; + $params = \FluentSmtpLib\phpseclib3\File\ASN1::encodeDER($params, \FluentSmtpLib\phpseclib3\File\ASN1\Maps\DHParameter::MAP); + return "-----BEGIN DH PARAMETERS-----\r\n" . \chunk_split(\base64_encode($params), 64) . "-----END DH PARAMETERS-----\r\n"; + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/DH/Formats/Keys/PKCS8.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/DH/Formats/Keys/PKCS8.php new file mode 100644 index 0000000..3bfc171 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/DH/Formats/Keys/PKCS8.php @@ -0,0 +1,115 @@ + + * @copyright 2015 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\Crypt\DH\Formats\Keys; + +use FluentSmtpLib\phpseclib3\Crypt\Common\Formats\Keys\PKCS8 as Progenitor; +use FluentSmtpLib\phpseclib3\File\ASN1; +use FluentSmtpLib\phpseclib3\File\ASN1\Maps; +use FluentSmtpLib\phpseclib3\Math\BigInteger; +/** + * PKCS#8 Formatted DH Key Handler + * + * @author Jim Wigginton + */ +abstract class PKCS8 extends \FluentSmtpLib\phpseclib3\Crypt\Common\Formats\Keys\PKCS8 +{ + /** + * OID Name + * + * @var string + */ + const OID_NAME = 'dhKeyAgreement'; + /** + * OID Value + * + * @var string + */ + const OID_VALUE = '1.2.840.113549.1.3.1'; + /** + * Child OIDs loaded + * + * @var bool + */ + protected static $childOIDsLoaded = \false; + /** + * Break a public or private key down into its constituent components + * + * @param string $key + * @param string $password optional + * @return array + */ + public static function load($key, $password = '') + { + $key = parent::load($key, $password); + $type = isset($key['privateKey']) ? 'privateKey' : 'publicKey'; + $decoded = \FluentSmtpLib\phpseclib3\File\ASN1::decodeBER($key[$type . 'Algorithm']['parameters']->element); + if (empty($decoded)) { + throw new \RuntimeException('Unable to decode BER of parameters'); + } + $components = \FluentSmtpLib\phpseclib3\File\ASN1::asn1map($decoded[0], \FluentSmtpLib\phpseclib3\File\ASN1\Maps\DHParameter::MAP); + if (!\is_array($components)) { + throw new \RuntimeException('Unable to perform ASN1 mapping on parameters'); + } + $decoded = \FluentSmtpLib\phpseclib3\File\ASN1::decodeBER($key[$type]); + switch (\true) { + case !isset($decoded): + case !isset($decoded[0]['content']): + case !$decoded[0]['content'] instanceof \FluentSmtpLib\phpseclib3\Math\BigInteger: + throw new \RuntimeException('Unable to decode BER of parameters'); + } + $components[$type] = $decoded[0]['content']; + return $components; + } + /** + * Convert a private key to the appropriate format. + * + * @param BigInteger $prime + * @param BigInteger $base + * @param BigInteger $privateKey + * @param BigInteger $publicKey + * @param string $password optional + * @param array $options optional + * @return string + */ + public static function savePrivateKey(\FluentSmtpLib\phpseclib3\Math\BigInteger $prime, \FluentSmtpLib\phpseclib3\Math\BigInteger $base, \FluentSmtpLib\phpseclib3\Math\BigInteger $privateKey, \FluentSmtpLib\phpseclib3\Math\BigInteger $publicKey, $password = '', array $options = []) + { + $params = ['prime' => $prime, 'base' => $base]; + $params = \FluentSmtpLib\phpseclib3\File\ASN1::encodeDER($params, \FluentSmtpLib\phpseclib3\File\ASN1\Maps\DHParameter::MAP); + $params = new \FluentSmtpLib\phpseclib3\File\ASN1\Element($params); + $key = \FluentSmtpLib\phpseclib3\File\ASN1::encodeDER($privateKey, ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_INTEGER]); + return self::wrapPrivateKey($key, [], $params, $password, null, '', $options); + } + /** + * Convert a public key to the appropriate format + * + * @param BigInteger $prime + * @param BigInteger $base + * @param BigInteger $publicKey + * @param array $options optional + * @return string + */ + public static function savePublicKey(\FluentSmtpLib\phpseclib3\Math\BigInteger $prime, \FluentSmtpLib\phpseclib3\Math\BigInteger $base, \FluentSmtpLib\phpseclib3\Math\BigInteger $publicKey, array $options = []) + { + $params = ['prime' => $prime, 'base' => $base]; + $params = \FluentSmtpLib\phpseclib3\File\ASN1::encodeDER($params, \FluentSmtpLib\phpseclib3\File\ASN1\Maps\DHParameter::MAP); + $params = new \FluentSmtpLib\phpseclib3\File\ASN1\Element($params); + $key = \FluentSmtpLib\phpseclib3\File\ASN1::encodeDER($publicKey, ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_INTEGER]); + return self::wrapPublicKey($key, $params, null, $options); + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/DH/Parameters.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/DH/Parameters.php new file mode 100644 index 0000000..46c107e --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/DH/Parameters.php @@ -0,0 +1,33 @@ + + * @copyright 2015 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\Crypt\DH; + +use FluentSmtpLib\phpseclib3\Crypt\DH; +/** + * DH Parameters + * + * @author Jim Wigginton + */ +final class Parameters extends \FluentSmtpLib\phpseclib3\Crypt\DH +{ + /** + * Returns the parameters + * + * @param string $type + * @param array $options optional + * @return string + */ + public function toString($type = 'PKCS1', array $options = []) + { + $type = self::validatePlugin('Keys', 'PKCS1', 'saveParameters'); + return $type::saveParameters($this->prime, $this->base, $options); + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/DH/PrivateKey.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/DH/PrivateKey.php new file mode 100644 index 0000000..8f18c43 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/DH/PrivateKey.php @@ -0,0 +1,64 @@ + + * @copyright 2015 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\Crypt\DH; + +use FluentSmtpLib\phpseclib3\Crypt\Common; +use FluentSmtpLib\phpseclib3\Crypt\DH; +/** + * DH Private Key + * + * @author Jim Wigginton + */ +final class PrivateKey extends \FluentSmtpLib\phpseclib3\Crypt\DH +{ + use Common\Traits\PasswordProtected; + /** + * Private Key + * + * @var \phpseclib3\Math\BigInteger + */ + protected $privateKey; + /** + * Public Key + * + * @var \phpseclib3\Math\BigInteger + */ + protected $publicKey; + /** + * Returns the public key + * + * @return PublicKey + */ + public function getPublicKey() + { + $type = self::validatePlugin('Keys', 'PKCS8', 'savePublicKey'); + if (!isset($this->publicKey)) { + $this->publicKey = $this->base->powMod($this->privateKey, $this->prime); + } + $key = $type::savePublicKey($this->prime, $this->base, $this->publicKey); + return \FluentSmtpLib\phpseclib3\Crypt\DH::loadFormat('PKCS8', $key); + } + /** + * Returns the private key + * + * @param string $type + * @param array $options optional + * @return string + */ + public function toString($type, array $options = []) + { + $type = self::validatePlugin('Keys', $type, 'savePrivateKey'); + if (!isset($this->publicKey)) { + $this->publicKey = $this->base->powMod($this->privateKey, $this->prime); + } + return $type::savePrivateKey($this->prime, $this->base, $this->privateKey, $this->publicKey, $this->password, $options); + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/DH/PublicKey.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/DH/PublicKey.php new file mode 100644 index 0000000..aecd85d --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/DH/PublicKey.php @@ -0,0 +1,44 @@ + + * @copyright 2015 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\Crypt\DH; + +use FluentSmtpLib\phpseclib3\Crypt\Common; +use FluentSmtpLib\phpseclib3\Crypt\DH; +/** + * DH Public Key + * + * @author Jim Wigginton + */ +final class PublicKey extends \FluentSmtpLib\phpseclib3\Crypt\DH +{ + use Common\Traits\Fingerprint; + /** + * Returns the public key + * + * @param string $type + * @param array $options optional + * @return string + */ + public function toString($type, array $options = []) + { + $type = self::validatePlugin('Keys', $type, 'savePublicKey'); + return $type::savePublicKey($this->prime, $this->base, $this->publicKey, $options); + } + /** + * Returns the public key as a BigInteger + * + * @return \phpseclib3\Math\BigInteger + */ + public function toBigInteger() + { + return $this->publicKey; + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/DSA.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/DSA.php new file mode 100644 index 0000000..cc8c116 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/DSA.php @@ -0,0 +1,292 @@ + + * getPublicKey(); + * + * $plaintext = 'terrafrost'; + * + * $signature = $private->sign($plaintext); + * + * echo $public->verify($plaintext, $signature) ? 'verified' : 'unverified'; + * ?> + * + * + * @author Jim Wigginton + * @copyright 2016 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\Crypt; + +use FluentSmtpLib\phpseclib3\Crypt\Common\AsymmetricKey; +use FluentSmtpLib\phpseclib3\Crypt\DSA\Parameters; +use FluentSmtpLib\phpseclib3\Crypt\DSA\PrivateKey; +use FluentSmtpLib\phpseclib3\Crypt\DSA\PublicKey; +use FluentSmtpLib\phpseclib3\Exception\InsufficientSetupException; +use FluentSmtpLib\phpseclib3\Math\BigInteger; +/** + * Pure-PHP FIPS 186-4 compliant implementation of DSA. + * + * @author Jim Wigginton + */ +abstract class DSA extends \FluentSmtpLib\phpseclib3\Crypt\Common\AsymmetricKey +{ + /** + * Algorithm Name + * + * @var string + */ + const ALGORITHM = 'DSA'; + /** + * DSA Prime P + * + * @var BigInteger + */ + protected $p; + /** + * DSA Group Order q + * + * Prime divisor of p-1 + * + * @var BigInteger + */ + protected $q; + /** + * DSA Group Generator G + * + * @var BigInteger + */ + protected $g; + /** + * DSA public key value y + * + * @var BigInteger + */ + protected $y; + /** + * Signature Format + * + * @var string + */ + protected $sigFormat; + /** + * Signature Format (Short) + * + * @var string + */ + protected $shortFormat; + /** + * Create DSA parameters + * + * @param int $L + * @param int $N + * @return DSA|bool + */ + public static function createParameters($L = 2048, $N = 224) + { + self::initialize_static_variables(); + $class = new \ReflectionClass(static::class); + if ($class->isFinal()) { + throw new \RuntimeException('createParameters() should not be called from final classes (' . static::class . ')'); + } + if (!isset(self::$engines['PHP'])) { + self::useBestEngine(); + } + switch (\true) { + case $N == 160: + /* + in FIPS 186-1 and 186-2 N was fixed at 160 whereas K had an upper bound of 1024. + RFC 4253 (SSH Transport Layer Protocol) references FIPS 186-2 and as such most + SSH DSA implementations only support keys with an N of 160. + puttygen let's you set the size of L (but not the size of N) and uses 2048 as the + default L value. that's not really compliant with any of the FIPS standards, however, + for the purposes of maintaining compatibility with puttygen, we'll support it + */ + //case ($L >= 512 || $L <= 1024) && (($L & 0x3F) == 0) && $N == 160: + // FIPS 186-3 changed this as follows: + //case $L == 1024 && $N == 160: + case $L == 2048 && $N == 224: + case $L == 2048 && $N == 256: + case $L == 3072 && $N == 256: + break; + default: + throw new \InvalidArgumentException('Invalid values for N and L'); + } + $two = new \FluentSmtpLib\phpseclib3\Math\BigInteger(2); + $q = \FluentSmtpLib\phpseclib3\Math\BigInteger::randomPrime($N); + $divisor = $q->multiply($two); + do { + $x = \FluentSmtpLib\phpseclib3\Math\BigInteger::random($L); + list(, $c) = $x->divide($divisor); + $p = $x->subtract($c->subtract(self::$one)); + } while ($p->getLength() != $L || !$p->isPrime()); + $p_1 = $p->subtract(self::$one); + list($e) = $p_1->divide($q); + // quoting http://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.186-4.pdf#page=50 , + // "h could be obtained from a random number generator or from a counter that + // changes after each use". PuTTY (sshdssg.c) starts h off at 1 and increments + // it on each loop. wikipedia says "commonly h = 2 is used" so we'll just do that + $h = clone $two; + while (\true) { + $g = $h->powMod($e, $p); + if (!$g->equals(self::$one)) { + break; + } + $h = $h->add(self::$one); + } + $dsa = new \FluentSmtpLib\phpseclib3\Crypt\DSA\Parameters(); + $dsa->p = $p; + $dsa->q = $q; + $dsa->g = $g; + return $dsa; + } + /** + * Create public / private key pair. + * + * This method is a bit polymorphic. It can take a DSA/Parameters object, L / N as two distinct parameters or + * no parameters (at which point L and N will be generated with this method) + * + * Returns the private key, from which the publickey can be extracted + * + * @param int[] ...$args + * @return PrivateKey + */ + public static function createKey(...$args) + { + self::initialize_static_variables(); + $class = new \ReflectionClass(static::class); + if ($class->isFinal()) { + throw new \RuntimeException('createKey() should not be called from final classes (' . static::class . ')'); + } + if (!isset(self::$engines['PHP'])) { + self::useBestEngine(); + } + if (\count($args) == 2 && \is_int($args[0]) && \is_int($args[1])) { + $params = self::createParameters($args[0], $args[1]); + } elseif (\count($args) == 1 && $args[0] instanceof \FluentSmtpLib\phpseclib3\Crypt\DSA\Parameters) { + $params = $args[0]; + } elseif (!\count($args)) { + $params = self::createParameters(); + } else { + throw new \FluentSmtpLib\phpseclib3\Exception\InsufficientSetupException('Valid parameters are either two integers (L and N), a single DSA object or no parameters at all.'); + } + $private = new \FluentSmtpLib\phpseclib3\Crypt\DSA\PrivateKey(); + $private->p = $params->p; + $private->q = $params->q; + $private->g = $params->g; + $private->x = \FluentSmtpLib\phpseclib3\Math\BigInteger::randomRange(self::$one, $private->q->subtract(self::$one)); + $private->y = $private->g->powMod($private->x, $private->p); + //$public = clone $private; + //unset($public->x); + return $private->withHash($params->hash->getHash())->withSignatureFormat($params->shortFormat); + } + /** + * OnLoad Handler + * + * @return bool + */ + protected static function onLoad(array $components) + { + if (!isset(self::$engines['PHP'])) { + self::useBestEngine(); + } + if (!isset($components['x']) && !isset($components['y'])) { + $new = new \FluentSmtpLib\phpseclib3\Crypt\DSA\Parameters(); + } elseif (isset($components['x'])) { + $new = new \FluentSmtpLib\phpseclib3\Crypt\DSA\PrivateKey(); + $new->x = $components['x']; + } else { + $new = new \FluentSmtpLib\phpseclib3\Crypt\DSA\PublicKey(); + } + $new->p = $components['p']; + $new->q = $components['q']; + $new->g = $components['g']; + if (isset($components['y'])) { + $new->y = $components['y']; + } + return $new; + } + /** + * Constructor + * + * PublicKey and PrivateKey objects can only be created from abstract RSA class + */ + protected function __construct() + { + $this->sigFormat = self::validatePlugin('Signature', 'ASN1'); + $this->shortFormat = 'ASN1'; + parent::__construct(); + } + /** + * Returns the key size + * + * More specifically, this L (the length of DSA Prime P) and N (the length of DSA Group Order q) + * + * @return array + */ + public function getLength() + { + return ['L' => $this->p->getLength(), 'N' => $this->q->getLength()]; + } + /** + * Returns the current engine being used + * + * @see self::useInternalEngine() + * @see self::useBestEngine() + * @return string + */ + public function getEngine() + { + if (!isset(self::$engines['PHP'])) { + self::useBestEngine(); + } + return self::$engines['OpenSSL'] && \in_array($this->hash->getHash(), \openssl_get_md_methods()) ? 'OpenSSL' : 'PHP'; + } + /** + * Returns the parameters + * + * A public / private key is only returned if the currently loaded "key" contains an x or y + * value. + * + * @see self::getPublicKey() + * @return mixed + */ + public function getParameters() + { + $type = self::validatePlugin('Keys', 'PKCS1', 'saveParameters'); + $key = $type::saveParameters($this->p, $this->q, $this->g); + return \FluentSmtpLib\phpseclib3\Crypt\DSA::load($key, 'PKCS1')->withHash($this->hash->getHash())->withSignatureFormat($this->shortFormat); + } + /** + * Determines the signature padding mode + * + * Valid values are: ASN1, SSH2, Raw + * + * @param string $format + */ + public function withSignatureFormat($format) + { + $new = clone $this; + $new->shortFormat = $format; + $new->sigFormat = self::validatePlugin('Signature', $format); + return $new; + } + /** + * Returns the signature format currently being used + * + */ + public function getSignatureFormat() + { + return $this->shortFormat; + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/DSA/Formats/Keys/OpenSSH.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/DSA/Formats/Keys/OpenSSH.php new file mode 100644 index 0000000..90b3be1 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/DSA/Formats/Keys/OpenSSH.php @@ -0,0 +1,102 @@ + + * @copyright 2015 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\Crypt\DSA\Formats\Keys; + +use FluentSmtpLib\phpseclib3\Common\Functions\Strings; +use FluentSmtpLib\phpseclib3\Crypt\Common\Formats\Keys\OpenSSH as Progenitor; +use FluentSmtpLib\phpseclib3\Math\BigInteger; +/** + * OpenSSH Formatted DSA Key Handler + * + * @author Jim Wigginton + */ +abstract class OpenSSH extends \FluentSmtpLib\phpseclib3\Crypt\Common\Formats\Keys\OpenSSH +{ + /** + * Supported Key Types + * + * @var array + */ + protected static $types = ['ssh-dss']; + /** + * Break a public or private key down into its constituent components + * + * @param string $key + * @param string $password optional + * @return array + */ + public static function load($key, $password = '') + { + $parsed = parent::load($key, $password); + if (isset($parsed['paddedKey'])) { + list($type) = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::unpackSSH2('s', $parsed['paddedKey']); + if ($type != $parsed['type']) { + throw new \RuntimeException("The public and private keys are not of the same type ({$type} vs {$parsed['type']})"); + } + list($p, $q, $g, $y, $x, $comment) = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::unpackSSH2('i5s', $parsed['paddedKey']); + return \compact('p', 'q', 'g', 'y', 'x', 'comment'); + } + list($p, $q, $g, $y) = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::unpackSSH2('iiii', $parsed['publicKey']); + $comment = $parsed['comment']; + return \compact('p', 'q', 'g', 'y', 'comment'); + } + /** + * Convert a public key to the appropriate format + * + * @param BigInteger $p + * @param BigInteger $q + * @param BigInteger $g + * @param BigInteger $y + * @param array $options optional + * @return string + */ + public static function savePublicKey(\FluentSmtpLib\phpseclib3\Math\BigInteger $p, \FluentSmtpLib\phpseclib3\Math\BigInteger $q, \FluentSmtpLib\phpseclib3\Math\BigInteger $g, \FluentSmtpLib\phpseclib3\Math\BigInteger $y, array $options = []) + { + if ($q->getLength() != 160) { + throw new \InvalidArgumentException('SSH only supports keys with an N (length of Group Order q) of 160'); + } + // from : + // string "ssh-dss" + // mpint p + // mpint q + // mpint g + // mpint y + $DSAPublicKey = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::packSSH2('siiii', 'ssh-dss', $p, $q, $g, $y); + if (isset($options['binary']) ? $options['binary'] : self::$binary) { + return $DSAPublicKey; + } + $comment = isset($options['comment']) ? $options['comment'] : self::$comment; + $DSAPublicKey = 'ssh-dss ' . \base64_encode($DSAPublicKey) . ' ' . $comment; + return $DSAPublicKey; + } + /** + * Convert a private key to the appropriate format. + * + * @param BigInteger $p + * @param BigInteger $q + * @param BigInteger $g + * @param BigInteger $y + * @param BigInteger $x + * @param string $password optional + * @param array $options optional + * @return string + */ + public static function savePrivateKey(\FluentSmtpLib\phpseclib3\Math\BigInteger $p, \FluentSmtpLib\phpseclib3\Math\BigInteger $q, \FluentSmtpLib\phpseclib3\Math\BigInteger $g, \FluentSmtpLib\phpseclib3\Math\BigInteger $y, \FluentSmtpLib\phpseclib3\Math\BigInteger $x, $password = '', array $options = []) + { + $publicKey = self::savePublicKey($p, $q, $g, $y, ['binary' => \true]); + $privateKey = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::packSSH2('si5', 'ssh-dss', $p, $q, $g, $y, $x); + return self::wrapPrivateKey($publicKey, $privateKey, $password, $options); + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/DSA/Formats/Keys/PKCS1.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/DSA/Formats/Keys/PKCS1.php new file mode 100644 index 0000000..54df3c1 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/DSA/Formats/Keys/PKCS1.php @@ -0,0 +1,115 @@ + + * @copyright 2015 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\Crypt\DSA\Formats\Keys; + +use FluentSmtpLib\phpseclib3\Common\Functions\Strings; +use FluentSmtpLib\phpseclib3\Crypt\Common\Formats\Keys\PKCS1 as Progenitor; +use FluentSmtpLib\phpseclib3\File\ASN1; +use FluentSmtpLib\phpseclib3\File\ASN1\Maps; +use FluentSmtpLib\phpseclib3\Math\BigInteger; +/** + * PKCS#1 Formatted DSA Key Handler + * + * @author Jim Wigginton + */ +abstract class PKCS1 extends \FluentSmtpLib\phpseclib3\Crypt\Common\Formats\Keys\PKCS1 +{ + /** + * Break a public or private key down into its constituent components + * + * @param string $key + * @param string $password optional + * @return array + */ + public static function load($key, $password = '') + { + $key = parent::load($key, $password); + $decoded = \FluentSmtpLib\phpseclib3\File\ASN1::decodeBER($key); + if (!$decoded) { + throw new \RuntimeException('Unable to decode BER'); + } + $key = \FluentSmtpLib\phpseclib3\File\ASN1::asn1map($decoded[0], \FluentSmtpLib\phpseclib3\File\ASN1\Maps\DSAParams::MAP); + if (\is_array($key)) { + return $key; + } + $key = \FluentSmtpLib\phpseclib3\File\ASN1::asn1map($decoded[0], \FluentSmtpLib\phpseclib3\File\ASN1\Maps\DSAPrivateKey::MAP); + if (\is_array($key)) { + return $key; + } + $key = \FluentSmtpLib\phpseclib3\File\ASN1::asn1map($decoded[0], \FluentSmtpLib\phpseclib3\File\ASN1\Maps\DSAPublicKey::MAP); + if (\is_array($key)) { + return $key; + } + throw new \RuntimeException('Unable to perform ASN1 mapping'); + } + /** + * Convert DSA parameters to the appropriate format + * + * @param BigInteger $p + * @param BigInteger $q + * @param BigInteger $g + * @return string + */ + public static function saveParameters(\FluentSmtpLib\phpseclib3\Math\BigInteger $p, \FluentSmtpLib\phpseclib3\Math\BigInteger $q, \FluentSmtpLib\phpseclib3\Math\BigInteger $g) + { + $key = ['p' => $p, 'q' => $q, 'g' => $g]; + $key = \FluentSmtpLib\phpseclib3\File\ASN1::encodeDER($key, \FluentSmtpLib\phpseclib3\File\ASN1\Maps\DSAParams::MAP); + return "-----BEGIN DSA PARAMETERS-----\r\n" . \chunk_split(\FluentSmtpLib\phpseclib3\Common\Functions\Strings::base64_encode($key), 64) . "-----END DSA PARAMETERS-----\r\n"; + } + /** + * Convert a private key to the appropriate format. + * + * @param BigInteger $p + * @param BigInteger $q + * @param BigInteger $g + * @param BigInteger $y + * @param BigInteger $x + * @param string $password optional + * @param array $options optional + * @return string + */ + public static function savePrivateKey(\FluentSmtpLib\phpseclib3\Math\BigInteger $p, \FluentSmtpLib\phpseclib3\Math\BigInteger $q, \FluentSmtpLib\phpseclib3\Math\BigInteger $g, \FluentSmtpLib\phpseclib3\Math\BigInteger $y, \FluentSmtpLib\phpseclib3\Math\BigInteger $x, $password = '', array $options = []) + { + $key = ['version' => 0, 'p' => $p, 'q' => $q, 'g' => $g, 'y' => $y, 'x' => $x]; + $key = \FluentSmtpLib\phpseclib3\File\ASN1::encodeDER($key, \FluentSmtpLib\phpseclib3\File\ASN1\Maps\DSAPrivateKey::MAP); + return self::wrapPrivateKey($key, 'DSA', $password, $options); + } + /** + * Convert a public key to the appropriate format + * + * @param BigInteger $p + * @param BigInteger $q + * @param BigInteger $g + * @param BigInteger $y + * @return string + */ + public static function savePublicKey(\FluentSmtpLib\phpseclib3\Math\BigInteger $p, \FluentSmtpLib\phpseclib3\Math\BigInteger $q, \FluentSmtpLib\phpseclib3\Math\BigInteger $g, \FluentSmtpLib\phpseclib3\Math\BigInteger $y) + { + $key = \FluentSmtpLib\phpseclib3\File\ASN1::encodeDER($y, \FluentSmtpLib\phpseclib3\File\ASN1\Maps\DSAPublicKey::MAP); + return self::wrapPublicKey($key, 'DSA'); + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/DSA/Formats/Keys/PKCS8.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/DSA/Formats/Keys/PKCS8.php new file mode 100644 index 0000000..d8173b4 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/DSA/Formats/Keys/PKCS8.php @@ -0,0 +1,125 @@ + + * @copyright 2015 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\Crypt\DSA\Formats\Keys; + +use FluentSmtpLib\phpseclib3\Crypt\Common\Formats\Keys\PKCS8 as Progenitor; +use FluentSmtpLib\phpseclib3\File\ASN1; +use FluentSmtpLib\phpseclib3\File\ASN1\Maps; +use FluentSmtpLib\phpseclib3\Math\BigInteger; +/** + * PKCS#8 Formatted DSA Key Handler + * + * @author Jim Wigginton + */ +abstract class PKCS8 extends \FluentSmtpLib\phpseclib3\Crypt\Common\Formats\Keys\PKCS8 +{ + /** + * OID Name + * + * @var string + */ + const OID_NAME = 'id-dsa'; + /** + * OID Value + * + * @var string + */ + const OID_VALUE = '1.2.840.10040.4.1'; + /** + * Child OIDs loaded + * + * @var bool + */ + protected static $childOIDsLoaded = \false; + /** + * Break a public or private key down into its constituent components + * + * @param string $key + * @param string $password optional + * @return array + */ + public static function load($key, $password = '') + { + $key = parent::load($key, $password); + $type = isset($key['privateKey']) ? 'privateKey' : 'publicKey'; + $decoded = \FluentSmtpLib\phpseclib3\File\ASN1::decodeBER($key[$type . 'Algorithm']['parameters']->element); + if (!$decoded) { + throw new \RuntimeException('Unable to decode BER of parameters'); + } + $components = \FluentSmtpLib\phpseclib3\File\ASN1::asn1map($decoded[0], \FluentSmtpLib\phpseclib3\File\ASN1\Maps\DSAParams::MAP); + if (!\is_array($components)) { + throw new \RuntimeException('Unable to perform ASN1 mapping on parameters'); + } + $decoded = \FluentSmtpLib\phpseclib3\File\ASN1::decodeBER($key[$type]); + if (empty($decoded)) { + throw new \RuntimeException('Unable to decode BER'); + } + $var = $type == 'privateKey' ? 'x' : 'y'; + $components[$var] = \FluentSmtpLib\phpseclib3\File\ASN1::asn1map($decoded[0], \FluentSmtpLib\phpseclib3\File\ASN1\Maps\DSAPublicKey::MAP); + if (!$components[$var] instanceof \FluentSmtpLib\phpseclib3\Math\BigInteger) { + throw new \RuntimeException('Unable to perform ASN1 mapping'); + } + if (isset($key['meta'])) { + $components['meta'] = $key['meta']; + } + return $components; + } + /** + * Convert a private key to the appropriate format. + * + * @param BigInteger $p + * @param BigInteger $q + * @param BigInteger $g + * @param BigInteger $y + * @param BigInteger $x + * @param string $password optional + * @param array $options optional + * @return string + */ + public static function savePrivateKey(\FluentSmtpLib\phpseclib3\Math\BigInteger $p, \FluentSmtpLib\phpseclib3\Math\BigInteger $q, \FluentSmtpLib\phpseclib3\Math\BigInteger $g, \FluentSmtpLib\phpseclib3\Math\BigInteger $y, \FluentSmtpLib\phpseclib3\Math\BigInteger $x, $password = '', array $options = []) + { + $params = ['p' => $p, 'q' => $q, 'g' => $g]; + $params = \FluentSmtpLib\phpseclib3\File\ASN1::encodeDER($params, \FluentSmtpLib\phpseclib3\File\ASN1\Maps\DSAParams::MAP); + $params = new \FluentSmtpLib\phpseclib3\File\ASN1\Element($params); + $key = \FluentSmtpLib\phpseclib3\File\ASN1::encodeDER($x, \FluentSmtpLib\phpseclib3\File\ASN1\Maps\DSAPublicKey::MAP); + return self::wrapPrivateKey($key, [], $params, $password, null, '', $options); + } + /** + * Convert a public key to the appropriate format + * + * @param BigInteger $p + * @param BigInteger $q + * @param BigInteger $g + * @param BigInteger $y + * @param array $options optional + * @return string + */ + public static function savePublicKey(\FluentSmtpLib\phpseclib3\Math\BigInteger $p, \FluentSmtpLib\phpseclib3\Math\BigInteger $q, \FluentSmtpLib\phpseclib3\Math\BigInteger $g, \FluentSmtpLib\phpseclib3\Math\BigInteger $y, array $options = []) + { + $params = ['p' => $p, 'q' => $q, 'g' => $g]; + $params = \FluentSmtpLib\phpseclib3\File\ASN1::encodeDER($params, \FluentSmtpLib\phpseclib3\File\ASN1\Maps\DSAParams::MAP); + $params = new \FluentSmtpLib\phpseclib3\File\ASN1\Element($params); + $key = \FluentSmtpLib\phpseclib3\File\ASN1::encodeDER($y, \FluentSmtpLib\phpseclib3\File\ASN1\Maps\DSAPublicKey::MAP); + return self::wrapPublicKey($key, $params, null, $options); + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/DSA/Formats/Keys/PuTTY.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/DSA/Formats/Keys/PuTTY.php new file mode 100644 index 0000000..0dd8b0d --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/DSA/Formats/Keys/PuTTY.php @@ -0,0 +1,98 @@ + 160 kinda useless, hence this handlers not supporting such keys. + * + * PHP version 5 + * + * @author Jim Wigginton + * @copyright 2015 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\Crypt\DSA\Formats\Keys; + +use FluentSmtpLib\phpseclib3\Common\Functions\Strings; +use FluentSmtpLib\phpseclib3\Crypt\Common\Formats\Keys\PuTTY as Progenitor; +use FluentSmtpLib\phpseclib3\Math\BigInteger; +/** + * PuTTY Formatted DSA Key Handler + * + * @author Jim Wigginton + */ +abstract class PuTTY extends \FluentSmtpLib\phpseclib3\Crypt\Common\Formats\Keys\PuTTY +{ + /** + * Public Handler + * + * @var string + */ + const PUBLIC_HANDLER = 'FluentSmtpLib\\phpseclib3\\Crypt\\DSA\\Formats\\Keys\\OpenSSH'; + /** + * Algorithm Identifier + * + * @var array + */ + protected static $types = ['ssh-dss']; + /** + * Break a public or private key down into its constituent components + * + * @param string $key + * @param string $password optional + * @return array + */ + public static function load($key, $password = '') + { + $components = parent::load($key, $password); + if (!isset($components['private'])) { + return $components; + } + \extract($components); + unset($components['public'], $components['private']); + list($p, $q, $g, $y) = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::unpackSSH2('iiii', $public); + list($x) = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::unpackSSH2('i', $private); + return \compact('p', 'q', 'g', 'y', 'x', 'comment'); + } + /** + * Convert a private key to the appropriate format. + * + * @param BigInteger $p + * @param BigInteger $q + * @param BigInteger $g + * @param BigInteger $y + * @param BigInteger $x + * @param string $password optional + * @param array $options optional + * @return string + */ + public static function savePrivateKey(\FluentSmtpLib\phpseclib3\Math\BigInteger $p, \FluentSmtpLib\phpseclib3\Math\BigInteger $q, \FluentSmtpLib\phpseclib3\Math\BigInteger $g, \FluentSmtpLib\phpseclib3\Math\BigInteger $y, \FluentSmtpLib\phpseclib3\Math\BigInteger $x, $password = \false, array $options = []) + { + if ($q->getLength() != 160) { + throw new \InvalidArgumentException('SSH only supports keys with an N (length of Group Order q) of 160'); + } + $public = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::packSSH2('iiii', $p, $q, $g, $y); + $private = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::packSSH2('i', $x); + return self::wrapPrivateKey($public, $private, 'ssh-dss', $password, $options); + } + /** + * Convert a public key to the appropriate format + * + * @param BigInteger $p + * @param BigInteger $q + * @param BigInteger $g + * @param BigInteger $y + * @return string + */ + public static function savePublicKey(\FluentSmtpLib\phpseclib3\Math\BigInteger $p, \FluentSmtpLib\phpseclib3\Math\BigInteger $q, \FluentSmtpLib\phpseclib3\Math\BigInteger $g, \FluentSmtpLib\phpseclib3\Math\BigInteger $y) + { + if ($q->getLength() != 160) { + throw new \InvalidArgumentException('SSH only supports keys with an N (length of Group Order q) of 160'); + } + return self::wrapPublicKey(\FluentSmtpLib\phpseclib3\Common\Functions\Strings::packSSH2('iiii', $p, $q, $g, $y), 'ssh-dss'); + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/DSA/Formats/Keys/Raw.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/DSA/Formats/Keys/Raw.php new file mode 100644 index 0000000..81f9bd3 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/DSA/Formats/Keys/Raw.php @@ -0,0 +1,78 @@ + + * @copyright 2015 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\Crypt\DSA\Formats\Keys; + +use FluentSmtpLib\phpseclib3\Math\BigInteger; +/** + * Raw DSA Key Handler + * + * @author Jim Wigginton + */ +abstract class Raw +{ + /** + * Break a public or private key down into its constituent components + * + * @param array $key + * @param string $password optional + * @return array + */ + public static function load($key, $password = '') + { + if (!\is_array($key)) { + throw new \UnexpectedValueException('Key should be a array - not a ' . \gettype($key)); + } + switch (\true) { + case !isset($key['p']) || !isset($key['q']) || !isset($key['g']): + case !$key['p'] instanceof \FluentSmtpLib\phpseclib3\Math\BigInteger: + case !$key['q'] instanceof \FluentSmtpLib\phpseclib3\Math\BigInteger: + case !$key['g'] instanceof \FluentSmtpLib\phpseclib3\Math\BigInteger: + case !isset($key['x']) && !isset($key['y']): + case isset($key['x']) && !$key['x'] instanceof \FluentSmtpLib\phpseclib3\Math\BigInteger: + case isset($key['y']) && !$key['y'] instanceof \FluentSmtpLib\phpseclib3\Math\BigInteger: + throw new \UnexpectedValueException('Key appears to be malformed'); + } + $options = ['p' => 1, 'q' => 1, 'g' => 1, 'x' => 1, 'y' => 1]; + return \array_intersect_key($key, $options); + } + /** + * Convert a private key to the appropriate format. + * + * @param BigInteger $p + * @param BigInteger $q + * @param BigInteger $g + * @param BigInteger $y + * @param BigInteger $x + * @param string $password optional + * @return string + */ + public static function savePrivateKey(\FluentSmtpLib\phpseclib3\Math\BigInteger $p, \FluentSmtpLib\phpseclib3\Math\BigInteger $q, \FluentSmtpLib\phpseclib3\Math\BigInteger $g, \FluentSmtpLib\phpseclib3\Math\BigInteger $y, \FluentSmtpLib\phpseclib3\Math\BigInteger $x, $password = '') + { + return \compact('p', 'q', 'g', 'y', 'x'); + } + /** + * Convert a public key to the appropriate format + * + * @param BigInteger $p + * @param BigInteger $q + * @param BigInteger $g + * @param BigInteger $y + * @return string + */ + public static function savePublicKey(\FluentSmtpLib\phpseclib3\Math\BigInteger $p, \FluentSmtpLib\phpseclib3\Math\BigInteger $q, \FluentSmtpLib\phpseclib3\Math\BigInteger $g, \FluentSmtpLib\phpseclib3\Math\BigInteger $y) + { + return \compact('p', 'q', 'g', 'y'); + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/DSA/Formats/Keys/XML.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/DSA/Formats/Keys/XML.php new file mode 100644 index 0000000..ebdfced --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/DSA/Formats/Keys/XML.php @@ -0,0 +1,123 @@ + + * @copyright 2015 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\Crypt\DSA\Formats\Keys; + +use FluentSmtpLib\phpseclib3\Common\Functions\Strings; +use FluentSmtpLib\phpseclib3\Exception\BadConfigurationException; +use FluentSmtpLib\phpseclib3\Math\BigInteger; +/** + * XML Formatted DSA Key Handler + * + * @author Jim Wigginton + */ +abstract class XML +{ + /** + * Break a public or private key down into its constituent components + * + * @param string $key + * @param string $password optional + * @return array + */ + public static function load($key, $password = '') + { + if (!\FluentSmtpLib\phpseclib3\Common\Functions\Strings::is_stringable($key)) { + throw new \UnexpectedValueException('Key should be a string - not a ' . \gettype($key)); + } + if (!\class_exists('DOMDocument')) { + throw new \FluentSmtpLib\phpseclib3\Exception\BadConfigurationException('The dom extension is not setup correctly on this system'); + } + $use_errors = \libxml_use_internal_errors(\true); + $dom = new \DOMDocument(); + if (\substr($key, 0, 5) != '' . $key . ''; + } + if (!$dom->loadXML($key)) { + \libxml_use_internal_errors($use_errors); + throw new \UnexpectedValueException('Key does not appear to contain XML'); + } + $xpath = new \DOMXPath($dom); + $keys = ['p', 'q', 'g', 'y', 'j', 'seed', 'pgencounter']; + foreach ($keys as $key) { + // $dom->getElementsByTagName($key) is case-sensitive + $temp = $xpath->query("//*[translate(local-name(), 'ABCDEFGHIJKLMNOPQRSTUVWXYZ','abcdefghijklmnopqrstuvwxyz')='{$key}']"); + if (!$temp->length) { + continue; + } + $value = new \FluentSmtpLib\phpseclib3\Math\BigInteger(\FluentSmtpLib\phpseclib3\Common\Functions\Strings::base64_decode($temp->item(0)->nodeValue), 256); + switch ($key) { + case 'p': + // a prime modulus meeting the [DSS] requirements + // Parameters P, Q, and G can be public and common to a group of users. They might be known + // from application context. As such, they are optional but P and Q must either both appear + // or both be absent + $components['p'] = $value; + break; + case 'q': + // an integer in the range 2**159 < Q < 2**160 which is a prime divisor of P-1 + $components['q'] = $value; + break; + case 'g': + // an integer with certain properties with respect to P and Q + $components['g'] = $value; + break; + case 'y': + // G**X mod P (where X is part of the private key and not made public) + $components['y'] = $value; + // the remaining options do not do anything + case 'j': + // (P - 1) / Q + // Parameter J is available for inclusion solely for efficiency as it is calculatable from + // P and Q + case 'seed': + // a DSA prime generation seed + // Parameters seed and pgenCounter are used in the DSA prime number generation algorithm + // specified in [DSS]. As such, they are optional but must either both be present or both + // be absent + case 'pgencounter': + } + } + \libxml_use_internal_errors($use_errors); + if (!isset($components['y'])) { + throw new \UnexpectedValueException('Key is missing y component'); + } + switch (\true) { + case !isset($components['p']): + case !isset($components['q']): + case !isset($components['g']): + return ['y' => $components['y']]; + } + return $components; + } + /** + * Convert a public key to the appropriate format + * + * See https://www.w3.org/TR/xmldsig-core/#sec-DSAKeyValue + * + * @param BigInteger $p + * @param BigInteger $q + * @param BigInteger $g + * @param BigInteger $y + * @return string + */ + public static function savePublicKey(\FluentSmtpLib\phpseclib3\Math\BigInteger $p, \FluentSmtpLib\phpseclib3\Math\BigInteger $q, \FluentSmtpLib\phpseclib3\Math\BigInteger $g, \FluentSmtpLib\phpseclib3\Math\BigInteger $y) + { + return "\r\n" . '

    ' . \FluentSmtpLib\phpseclib3\Common\Functions\Strings::base64_encode($p->toBytes()) . "

    \r\n" . ' ' . \FluentSmtpLib\phpseclib3\Common\Functions\Strings::base64_encode($q->toBytes()) . "\r\n" . ' ' . \FluentSmtpLib\phpseclib3\Common\Functions\Strings::base64_encode($g->toBytes()) . "\r\n" . ' ' . \FluentSmtpLib\phpseclib3\Common\Functions\Strings::base64_encode($y->toBytes()) . "\r\n" . '
    '; + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/DSA/Formats/Signature/ASN1.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/DSA/Formats/Signature/ASN1.php new file mode 100644 index 0000000..83833f0 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/DSA/Formats/Signature/ASN1.php @@ -0,0 +1,57 @@ + + * @copyright 2016 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\Crypt\DSA\Formats\Signature; + +use FluentSmtpLib\phpseclib3\File\ASN1 as Encoder; +use FluentSmtpLib\phpseclib3\File\ASN1\Maps; +use FluentSmtpLib\phpseclib3\Math\BigInteger; +/** + * ASN1 Signature Handler + * + * @author Jim Wigginton + */ +abstract class ASN1 +{ + /** + * Loads a signature + * + * @param string $sig + * @return array|bool + */ + public static function load($sig) + { + if (!\is_string($sig)) { + return \false; + } + $decoded = \FluentSmtpLib\phpseclib3\File\ASN1::decodeBER($sig); + if (empty($decoded)) { + return \false; + } + $components = \FluentSmtpLib\phpseclib3\File\ASN1::asn1map($decoded[0], \FluentSmtpLib\phpseclib3\File\ASN1\Maps\DssSigValue::MAP); + return $components; + } + /** + * Returns a signature in the appropriate format + * + * @param BigInteger $r + * @param BigInteger $s + * @return string + */ + public static function save(\FluentSmtpLib\phpseclib3\Math\BigInteger $r, \FluentSmtpLib\phpseclib3\Math\BigInteger $s) + { + return \FluentSmtpLib\phpseclib3\File\ASN1::encodeDER(\compact('r', 's'), \FluentSmtpLib\phpseclib3\File\ASN1\Maps\DssSigValue::MAP); + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/DSA/Formats/Signature/Raw.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/DSA/Formats/Signature/Raw.php new file mode 100644 index 0000000..2148ea4 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/DSA/Formats/Signature/Raw.php @@ -0,0 +1,23 @@ + + * @copyright 2016 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\Crypt\DSA\Formats\Signature; + +use FluentSmtpLib\phpseclib3\Crypt\Common\Formats\Signature\Raw as Progenitor; +/** + * Raw DSA Signature Handler + * + * @author Jim Wigginton + */ +abstract class Raw extends \FluentSmtpLib\phpseclib3\Crypt\Common\Formats\Signature\Raw +{ +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/DSA/Formats/Signature/SSH2.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/DSA/Formats/Signature/SSH2.php new file mode 100644 index 0000000..8d673c0 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/DSA/Formats/Signature/SSH2.php @@ -0,0 +1,61 @@ + + * @copyright 2016 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\Crypt\DSA\Formats\Signature; + +use FluentSmtpLib\phpseclib3\Common\Functions\Strings; +use FluentSmtpLib\phpseclib3\Math\BigInteger; +/** + * SSH2 Signature Handler + * + * @author Jim Wigginton + */ +abstract class SSH2 +{ + /** + * Loads a signature + * + * @param string $sig + * @return mixed + */ + public static function load($sig) + { + if (!\is_string($sig)) { + return \false; + } + $result = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::unpackSSH2('ss', $sig); + if ($result === \false) { + return \false; + } + list($type, $blob) = $result; + if ($type != 'ssh-dss' || \strlen($blob) != 40) { + return \false; + } + return ['r' => new \FluentSmtpLib\phpseclib3\Math\BigInteger(\substr($blob, 0, 20), 256), 's' => new \FluentSmtpLib\phpseclib3\Math\BigInteger(\substr($blob, 20), 256)]; + } + /** + * Returns a signature in the appropriate format + * + * @param BigInteger $r + * @param BigInteger $s + * @return string + */ + public static function save(\FluentSmtpLib\phpseclib3\Math\BigInteger $r, \FluentSmtpLib\phpseclib3\Math\BigInteger $s) + { + if ($r->getLength() > 160 || $s->getLength() > 160) { + return \false; + } + return \FluentSmtpLib\phpseclib3\Common\Functions\Strings::packSSH2('ss', 'ssh-dss', \str_pad($r->toBytes(), 20, "\x00", \STR_PAD_LEFT) . \str_pad($s->toBytes(), 20, "\x00", \STR_PAD_LEFT)); + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/DSA/Parameters.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/DSA/Parameters.php new file mode 100644 index 0000000..f9d4159 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/DSA/Parameters.php @@ -0,0 +1,33 @@ + + * @copyright 2015 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\Crypt\DSA; + +use FluentSmtpLib\phpseclib3\Crypt\DSA; +/** + * DSA Parameters + * + * @author Jim Wigginton + */ +final class Parameters extends \FluentSmtpLib\phpseclib3\Crypt\DSA +{ + /** + * Returns the parameters + * + * @param string $type + * @param array $options optional + * @return string + */ + public function toString($type = 'PKCS1', array $options = []) + { + $type = self::validatePlugin('Keys', 'PKCS1', 'saveParameters'); + return $type::saveParameters($this->p, $this->q, $this->g, $options); + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/DSA/PrivateKey.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/DSA/PrivateKey.php new file mode 100644 index 0000000..ee4dff0 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/DSA/PrivateKey.php @@ -0,0 +1,131 @@ + + * @copyright 2015 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\Crypt\DSA; + +use FluentSmtpLib\phpseclib3\Crypt\Common; +use FluentSmtpLib\phpseclib3\Crypt\DSA; +use FluentSmtpLib\phpseclib3\Crypt\DSA\Formats\Signature\ASN1 as ASN1Signature; +use FluentSmtpLib\phpseclib3\Math\BigInteger; +/** + * DSA Private Key + * + * @author Jim Wigginton + */ +final class PrivateKey extends \FluentSmtpLib\phpseclib3\Crypt\DSA implements \FluentSmtpLib\phpseclib3\Crypt\Common\PrivateKey +{ + use Common\Traits\PasswordProtected; + /** + * DSA secret exponent x + * + * @var BigInteger + */ + protected $x; + /** + * Returns the public key + * + * If you do "openssl rsa -in private.rsa -pubout -outform PEM" you get a PKCS8 formatted key + * that contains a publicKeyAlgorithm AlgorithmIdentifier and a publicKey BIT STRING. + * An AlgorithmIdentifier contains an OID and a parameters field. With RSA public keys this + * parameters field is NULL. With DSA PKCS8 public keys it is not - it contains the p, q and g + * variables. The publicKey BIT STRING contains, simply, the y variable. This can be verified + * by getting a DSA PKCS8 public key: + * + * "openssl dsa -in private.dsa -pubout -outform PEM" + * + * ie. just swap out rsa with dsa in the rsa command above. + * + * A PKCS1 public key corresponds to the publicKey portion of the PKCS8 key. In the case of RSA + * the publicKey portion /is/ the key. In the case of DSA it is not. You cannot verify a signature + * without the parameters and the PKCS1 DSA public key format does not include the parameters. + * + * @see self::getPrivateKey() + * @return mixed + */ + public function getPublicKey() + { + $type = self::validatePlugin('Keys', 'PKCS8', 'savePublicKey'); + if (!isset($this->y)) { + $this->y = $this->g->powMod($this->x, $this->p); + } + $key = $type::savePublicKey($this->p, $this->q, $this->g, $this->y); + return \FluentSmtpLib\phpseclib3\Crypt\DSA::loadFormat('PKCS8', $key)->withHash($this->hash->getHash())->withSignatureFormat($this->shortFormat); + } + /** + * Create a signature + * + * @see self::verify() + * @param string $message + * @return mixed + */ + public function sign($message) + { + $format = $this->sigFormat; + if (self::$engines['OpenSSL'] && \in_array($this->hash->getHash(), \openssl_get_md_methods())) { + $signature = ''; + $result = \openssl_sign($message, $signature, $this->toString('PKCS8'), $this->hash->getHash()); + if ($result) { + if ($this->shortFormat == 'ASN1') { + return $signature; + } + \extract(\FluentSmtpLib\phpseclib3\Crypt\DSA\Formats\Signature\ASN1::load($signature)); + return $format::save($r, $s); + } + } + $h = $this->hash->hash($message); + $h = $this->bits2int($h); + while (\true) { + $k = \FluentSmtpLib\phpseclib3\Math\BigInteger::randomRange(self::$one, $this->q->subtract(self::$one)); + $r = $this->g->powMod($k, $this->p); + list(, $r) = $r->divide($this->q); + if ($r->equals(self::$zero)) { + continue; + } + $kinv = $k->modInverse($this->q); + $temp = $h->add($this->x->multiply($r)); + $temp = $kinv->multiply($temp); + list(, $s) = $temp->divide($this->q); + if (!$s->equals(self::$zero)) { + break; + } + } + // the following is an RFC6979 compliant implementation of deterministic DSA + // it's unused because it's mainly intended for use when a good CSPRNG isn't + // available. if phpseclib's CSPRNG isn't good then even key generation is + // suspect + /* + $h1 = $this->hash->hash($message); + $k = $this->computek($h1); + $r = $this->g->powMod($k, $this->p); + list(, $r) = $r->divide($this->q); + $kinv = $k->modInverse($this->q); + $h1 = $this->bits2int($h1); + $temp = $h1->add($this->x->multiply($r)); + $temp = $kinv->multiply($temp); + list(, $s) = $temp->divide($this->q); + */ + return $format::save($r, $s); + } + /** + * Returns the private key + * + * @param string $type + * @param array $options optional + * @return string + */ + public function toString($type, array $options = []) + { + $type = self::validatePlugin('Keys', $type, 'savePrivateKey'); + if (!isset($this->y)) { + $this->y = $this->g->powMod($this->x, $this->p); + } + return $type::savePrivateKey($this->p, $this->q, $this->g, $this->y, $this->x, $this->password, $options); + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/DSA/PublicKey.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/DSA/PublicKey.php new file mode 100644 index 0000000..34aca12 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/DSA/PublicKey.php @@ -0,0 +1,74 @@ + + * @copyright 2015 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\Crypt\DSA; + +use FluentSmtpLib\phpseclib3\Crypt\Common; +use FluentSmtpLib\phpseclib3\Crypt\DSA; +use FluentSmtpLib\phpseclib3\Crypt\DSA\Formats\Signature\ASN1 as ASN1Signature; +/** + * DSA Public Key + * + * @author Jim Wigginton + */ +final class PublicKey extends \FluentSmtpLib\phpseclib3\Crypt\DSA implements \FluentSmtpLib\phpseclib3\Crypt\Common\PublicKey +{ + use Common\Traits\Fingerprint; + /** + * Verify a signature + * + * @see self::verify() + * @param string $message + * @param string $signature + * @return mixed + */ + public function verify($message, $signature) + { + $format = $this->sigFormat; + $params = $format::load($signature); + if ($params === \false || \count($params) != 2) { + return \false; + } + \extract($params); + if (self::$engines['OpenSSL'] && \in_array($this->hash->getHash(), \openssl_get_md_methods())) { + $sig = $format != 'ASN1' ? \FluentSmtpLib\phpseclib3\Crypt\DSA\Formats\Signature\ASN1::save($r, $s) : $signature; + $result = \openssl_verify($message, $sig, $this->toString('PKCS8'), $this->hash->getHash()); + if ($result != -1) { + return (bool) $result; + } + } + $q_1 = $this->q->subtract(self::$one); + if (!$r->between(self::$one, $q_1) || !$s->between(self::$one, $q_1)) { + return \false; + } + $w = $s->modInverse($this->q); + $h = $this->hash->hash($message); + $h = $this->bits2int($h); + list(, $u1) = $h->multiply($w)->divide($this->q); + list(, $u2) = $r->multiply($w)->divide($this->q); + $v1 = $this->g->powMod($u1, $this->p); + $v2 = $this->y->powMod($u2, $this->p); + list(, $v) = $v1->multiply($v2)->divide($this->p); + list(, $v) = $v->divide($this->q); + return $v->equals($r); + } + /** + * Returns the public key + * + * @param string $type + * @param array $options optional + * @return string + */ + public function toString($type, array $options = []) + { + $type = self::validatePlugin('Keys', $type, 'savePublicKey'); + return $type::savePublicKey($this->p, $this->q, $this->g, $this->y, $options); + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC.php new file mode 100644 index 0000000..e2b2305 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC.php @@ -0,0 +1,414 @@ + + * getPublicKey(); + * + * $plaintext = 'terrafrost'; + * + * $signature = $private->sign($plaintext); + * + * echo $public->verify($plaintext, $signature) ? 'verified' : 'unverified'; + * ?> + * + * + * @author Jim Wigginton + * @copyright 2016 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\Crypt; + +use FluentSmtpLib\phpseclib3\Crypt\Common\AsymmetricKey; +use FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\Montgomery as MontgomeryCurve; +use FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\TwistedEdwards as TwistedEdwardsCurve; +use FluentSmtpLib\phpseclib3\Crypt\EC\Curves\Curve25519; +use FluentSmtpLib\phpseclib3\Crypt\EC\Curves\Ed25519; +use FluentSmtpLib\phpseclib3\Crypt\EC\Curves\Ed448; +use FluentSmtpLib\phpseclib3\Crypt\EC\Formats\Keys\PKCS1; +use FluentSmtpLib\phpseclib3\Crypt\EC\Parameters; +use FluentSmtpLib\phpseclib3\Crypt\EC\PrivateKey; +use FluentSmtpLib\phpseclib3\Crypt\EC\PublicKey; +use FluentSmtpLib\phpseclib3\Exception\UnsupportedAlgorithmException; +use FluentSmtpLib\phpseclib3\Exception\UnsupportedCurveException; +use FluentSmtpLib\phpseclib3\Exception\UnsupportedOperationException; +use FluentSmtpLib\phpseclib3\File\ASN1; +use FluentSmtpLib\phpseclib3\File\ASN1\Maps\ECParameters; +use FluentSmtpLib\phpseclib3\Math\BigInteger; +/** + * Pure-PHP implementation of EC. + * + * @author Jim Wigginton + */ +abstract class EC extends \FluentSmtpLib\phpseclib3\Crypt\Common\AsymmetricKey +{ + /** + * Algorithm Name + * + * @var string + */ + const ALGORITHM = 'EC'; + /** + * Public Key QA + * + * @var object[] + */ + protected $QA; + /** + * Curve + * + * @var EC\BaseCurves\Base + */ + protected $curve; + /** + * Signature Format + * + * @var string + */ + protected $format; + /** + * Signature Format (Short) + * + * @var string + */ + protected $shortFormat; + /** + * Curve Name + * + * @var string + */ + private $curveName; + /** + * Curve Order + * + * Used for deterministic ECDSA + * + * @var BigInteger + */ + protected $q; + /** + * Alias for the private key + * + * Used for deterministic ECDSA. AsymmetricKey expects $x. I don't like x because + * with x you have x * the base point yielding an (x, y)-coordinate that is the + * public key. But the x is different depending on which side of the equal sign + * you're on. It's less ambiguous if you do dA * base point = (x, y)-coordinate. + * + * @var BigInteger + */ + protected $x; + /** + * Context + * + * @var string + */ + protected $context; + /** + * Signature Format + * + * @var string + */ + protected $sigFormat; + /** + * Create public / private key pair. + * + * @param string $curve + * @return PrivateKey + */ + public static function createKey($curve) + { + self::initialize_static_variables(); + $class = new \ReflectionClass(static::class); + if ($class->isFinal()) { + throw new \RuntimeException('createKey() should not be called from final classes (' . static::class . ')'); + } + if (!isset(self::$engines['PHP'])) { + self::useBestEngine(); + } + $curve = \strtolower($curve); + if (self::$engines['libsodium'] && $curve == 'ed25519' && \function_exists('sodium_crypto_sign_keypair')) { + $kp = \sodium_crypto_sign_keypair(); + $privatekey = \FluentSmtpLib\phpseclib3\Crypt\EC::loadFormat('libsodium', \sodium_crypto_sign_secretkey($kp)); + //$publickey = EC::loadFormat('libsodium', sodium_crypto_sign_publickey($kp)); + $privatekey->curveName = 'Ed25519'; + //$publickey->curveName = $curve; + return $privatekey; + } + $privatekey = new \FluentSmtpLib\phpseclib3\Crypt\EC\PrivateKey(); + $curveName = $curve; + if (\preg_match('#(?:^curve|^ed)\\d+$#', $curveName)) { + $curveName = \ucfirst($curveName); + } elseif (\substr($curveName, 0, 10) == 'brainpoolp') { + $curveName = 'brainpoolP' . \substr($curveName, 10); + } + $curve = '\\FluentSmtpLib\\phpseclib3\\Crypt\\EC\\Curves\\' . $curveName; + if (!\class_exists($curve)) { + throw new \FluentSmtpLib\phpseclib3\Exception\UnsupportedCurveException('Named Curve of ' . $curveName . ' is not supported'); + } + $reflect = new \ReflectionClass($curve); + $curveName = $reflect->isFinal() ? $reflect->getParentClass()->getShortName() : $reflect->getShortName(); + $curve = new $curve(); + if ($curve instanceof \FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\TwistedEdwards) { + $arr = $curve->extractSecret(\FluentSmtpLib\phpseclib3\Crypt\Random::string($curve instanceof \FluentSmtpLib\phpseclib3\Crypt\EC\Curves\Ed448 ? 57 : 32)); + $privatekey->dA = $dA = $arr['dA']; + $privatekey->secret = $arr['secret']; + } else { + $privatekey->dA = $dA = $curve->createRandomMultiplier(); + } + if ($curve instanceof \FluentSmtpLib\phpseclib3\Crypt\EC\Curves\Curve25519 && self::$engines['libsodium']) { + //$r = pack('H*', '0900000000000000000000000000000000000000000000000000000000000000'); + //$QA = sodium_crypto_scalarmult($dA->toBytes(), $r); + $QA = \sodium_crypto_box_publickey_from_secretkey($dA->toBytes()); + $privatekey->QA = [$curve->convertInteger(new \FluentSmtpLib\phpseclib3\Math\BigInteger(\strrev($QA), 256))]; + } else { + $privatekey->QA = $curve->multiplyPoint($curve->getBasePoint(), $dA); + } + $privatekey->curve = $curve; + //$publickey = clone $privatekey; + //unset($publickey->dA); + //unset($publickey->x); + $privatekey->curveName = $curveName; + //$publickey->curveName = $curveName; + if ($privatekey->curve instanceof \FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\TwistedEdwards) { + return $privatekey->withHash($curve::HASH); + } + return $privatekey; + } + /** + * OnLoad Handler + * + * @return bool + */ + protected static function onLoad(array $components) + { + if (!isset(self::$engines['PHP'])) { + self::useBestEngine(); + } + if (!isset($components['dA']) && !isset($components['QA'])) { + $new = new \FluentSmtpLib\phpseclib3\Crypt\EC\Parameters(); + $new->curve = $components['curve']; + return $new; + } + $new = isset($components['dA']) ? new \FluentSmtpLib\phpseclib3\Crypt\EC\PrivateKey() : new \FluentSmtpLib\phpseclib3\Crypt\EC\PublicKey(); + $new->curve = $components['curve']; + $new->QA = $components['QA']; + if (isset($components['dA'])) { + $new->dA = $components['dA']; + $new->secret = $components['secret']; + } + if ($new->curve instanceof \FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\TwistedEdwards) { + return $new->withHash($components['curve']::HASH); + } + return $new; + } + /** + * Constructor + * + * PublicKey and PrivateKey objects can only be created from abstract RSA class + */ + protected function __construct() + { + $this->sigFormat = self::validatePlugin('Signature', 'ASN1'); + $this->shortFormat = 'ASN1'; + parent::__construct(); + } + /** + * Returns the curve + * + * Returns a string if it's a named curve, an array if not + * + * @return string|array + */ + public function getCurve() + { + if ($this->curveName) { + return $this->curveName; + } + if ($this->curve instanceof \FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\Montgomery) { + $this->curveName = $this->curve instanceof \FluentSmtpLib\phpseclib3\Crypt\EC\Curves\Curve25519 ? 'Curve25519' : 'Curve448'; + return $this->curveName; + } + if ($this->curve instanceof \FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\TwistedEdwards) { + $this->curveName = $this->curve instanceof \FluentSmtpLib\phpseclib3\Crypt\EC\Curves\Ed25519 ? 'Ed25519' : 'Ed448'; + return $this->curveName; + } + $params = $this->getParameters()->toString('PKCS8', ['namedCurve' => \true]); + $decoded = \FluentSmtpLib\phpseclib3\File\ASN1::extractBER($params); + $decoded = \FluentSmtpLib\phpseclib3\File\ASN1::decodeBER($decoded); + $decoded = \FluentSmtpLib\phpseclib3\File\ASN1::asn1map($decoded[0], \FluentSmtpLib\phpseclib3\File\ASN1\Maps\ECParameters::MAP); + if (isset($decoded['namedCurve'])) { + $this->curveName = $decoded['namedCurve']; + return $decoded['namedCurve']; + } + if (!$namedCurves) { + \FluentSmtpLib\phpseclib3\Crypt\EC\Formats\Keys\PKCS1::useSpecifiedCurve(); + } + return $decoded; + } + /** + * Returns the key size + * + * Quoting https://tools.ietf.org/html/rfc5656#section-2, + * + * "The size of a set of elliptic curve domain parameters on a prime + * curve is defined as the number of bits in the binary representation + * of the field order, commonly denoted by p. Size on a + * characteristic-2 curve is defined as the number of bits in the binary + * representation of the field, commonly denoted by m. A set of + * elliptic curve domain parameters defines a group of order n generated + * by a base point P" + * + * @return int + */ + public function getLength() + { + return $this->curve->getLength(); + } + /** + * Returns the current engine being used + * + * @see self::useInternalEngine() + * @see self::useBestEngine() + * @return string + */ + public function getEngine() + { + if (!isset(self::$engines['PHP'])) { + self::useBestEngine(); + } + if ($this->curve instanceof \FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\TwistedEdwards) { + return $this->curve instanceof \FluentSmtpLib\phpseclib3\Crypt\EC\Curves\Ed25519 && self::$engines['libsodium'] && !isset($this->context) ? 'libsodium' : 'PHP'; + } + return self::$engines['OpenSSL'] && \in_array($this->hash->getHash(), \openssl_get_md_methods()) ? 'OpenSSL' : 'PHP'; + } + /** + * Returns the public key coordinates as a string + * + * Used by ECDH + * + * @return string + */ + public function getEncodedCoordinates() + { + if ($this->curve instanceof \FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\Montgomery) { + return \strrev($this->QA[0]->toBytes(\true)); + } + if ($this->curve instanceof \FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\TwistedEdwards) { + return $this->curve->encodePoint($this->QA); + } + return "\x04" . $this->QA[0]->toBytes(\true) . $this->QA[1]->toBytes(\true); + } + /** + * Returns the parameters + * + * @see self::getPublicKey() + * @param string $type optional + * @return mixed + */ + public function getParameters($type = 'PKCS1') + { + $type = self::validatePlugin('Keys', $type, 'saveParameters'); + $key = $type::saveParameters($this->curve); + return \FluentSmtpLib\phpseclib3\Crypt\EC::load($key, 'PKCS1')->withHash($this->hash->getHash())->withSignatureFormat($this->shortFormat); + } + /** + * Determines the signature padding mode + * + * Valid values are: ASN1, SSH2, Raw + * + * @param string $format + */ + public function withSignatureFormat($format) + { + if ($this->curve instanceof \FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\Montgomery) { + throw new \FluentSmtpLib\phpseclib3\Exception\UnsupportedOperationException('Montgomery Curves cannot be used to create signatures'); + } + $new = clone $this; + $new->shortFormat = $format; + $new->sigFormat = self::validatePlugin('Signature', $format); + return $new; + } + /** + * Returns the signature format currently being used + * + */ + public function getSignatureFormat() + { + return $this->shortFormat; + } + /** + * Sets the context + * + * Used by Ed25519 / Ed448. + * + * @see self::sign() + * @see self::verify() + * @param string $context optional + */ + public function withContext($context = null) + { + if (!$this->curve instanceof \FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\TwistedEdwards) { + throw new \FluentSmtpLib\phpseclib3\Exception\UnsupportedCurveException('Only Ed25519 and Ed448 support contexts'); + } + $new = clone $this; + if (!isset($context)) { + $new->context = null; + return $new; + } + if (!\is_string($context)) { + throw new \InvalidArgumentException('setContext expects a string'); + } + if (\strlen($context) > 255) { + throw new \LengthException('The context is supposed to be, at most, 255 bytes long'); + } + $new->context = $context; + return $new; + } + /** + * Returns the signature format currently being used + * + */ + public function getContext() + { + return $this->context; + } + /** + * Determines which hashing function should be used + * + * @param string $hash + */ + public function withHash($hash) + { + if ($this->curve instanceof \FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\Montgomery) { + throw new \FluentSmtpLib\phpseclib3\Exception\UnsupportedOperationException('Montgomery Curves cannot be used to create signatures'); + } + if ($this->curve instanceof \FluentSmtpLib\phpseclib3\Crypt\EC\Curves\Ed25519 && $hash != 'sha512') { + throw new \FluentSmtpLib\phpseclib3\Exception\UnsupportedAlgorithmException('Ed25519 only supports sha512 as a hash'); + } + if ($this->curve instanceof \FluentSmtpLib\phpseclib3\Crypt\EC\Curves\Ed448 && $hash != 'shake256-912') { + throw new \FluentSmtpLib\phpseclib3\Exception\UnsupportedAlgorithmException('Ed448 only supports shake256 with a length of 114 bytes'); + } + return parent::withHash($hash); + } + /** + * __toString() magic method + * + * @return string + */ + public function __toString() + { + if ($this->curve instanceof \FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\Montgomery) { + return ''; + } + return parent::__toString(); + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/BaseCurves/Base.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/BaseCurves/Base.php new file mode 100644 index 0000000..734f870 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/BaseCurves/Base.php @@ -0,0 +1,192 @@ + + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://pear.php.net/package/Math_BigInteger + */ +namespace FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves; + +use FluentSmtpLib\phpseclib3\Math\BigInteger; +/** + * Base + * + * @author Jim Wigginton + */ +abstract class Base +{ + /** + * The Order + * + * @var BigInteger + */ + protected $order; + /** + * Finite Field Integer factory + * + * @var FiniteField\Integer + */ + protected $factory; + /** + * Returns a random integer + * + * @return object + */ + public function randomInteger() + { + return $this->factory->randomInteger(); + } + /** + * Converts a BigInteger to a FiniteField\Integer integer + * + * @return object + */ + public function convertInteger(\FluentSmtpLib\phpseclib3\Math\BigInteger $x) + { + return $this->factory->newInteger($x); + } + /** + * Returns the length, in bytes, of the modulo + * + * @return integer + */ + public function getLengthInBytes() + { + return $this->factory->getLengthInBytes(); + } + /** + * Returns the length, in bits, of the modulo + * + * @return integer + */ + public function getLength() + { + return $this->factory->getLength(); + } + /** + * Multiply a point on the curve by a scalar + * + * Uses the montgomery ladder technique as described here: + * + * https://en.wikipedia.org/wiki/Elliptic_curve_point_multiplication#Montgomery_ladder + * https://github.com/phpecc/phpecc/issues/16#issuecomment-59176772 + * + * @return array + */ + public function multiplyPoint(array $p, \FluentSmtpLib\phpseclib3\Math\BigInteger $d) + { + $alreadyInternal = isset($p[2]); + $r = $alreadyInternal ? [[], $p] : [[], $this->convertToInternal($p)]; + $d = $d->toBits(); + for ($i = 0; $i < \strlen($d); $i++) { + $d_i = (int) $d[$i]; + $r[1 - $d_i] = $this->addPoint($r[0], $r[1]); + $r[$d_i] = $this->doublePoint($r[$d_i]); + } + return $alreadyInternal ? $r[0] : $this->convertToAffine($r[0]); + } + /** + * Creates a random scalar multiplier + * + * @return BigInteger + */ + public function createRandomMultiplier() + { + static $one; + if (!isset($one)) { + $one = new \FluentSmtpLib\phpseclib3\Math\BigInteger(1); + } + return \FluentSmtpLib\phpseclib3\Math\BigInteger::randomRange($one, $this->order->subtract($one)); + } + /** + * Performs range check + */ + public function rangeCheck(\FluentSmtpLib\phpseclib3\Math\BigInteger $x) + { + static $zero; + if (!isset($zero)) { + $zero = new \FluentSmtpLib\phpseclib3\Math\BigInteger(); + } + if (!isset($this->order)) { + throw new \RuntimeException('setOrder needs to be called before this method'); + } + if ($x->compare($this->order) > 0 || $x->compare($zero) <= 0) { + throw new \RangeException('x must be between 1 and the order of the curve'); + } + } + /** + * Sets the Order + */ + public function setOrder(\FluentSmtpLib\phpseclib3\Math\BigInteger $order) + { + $this->order = $order; + } + /** + * Returns the Order + * + * @return BigInteger + */ + public function getOrder() + { + return $this->order; + } + /** + * Use a custom defined modular reduction function + * + * @return object + */ + public function setReduction(callable $func) + { + $this->factory->setReduction($func); + } + /** + * Returns the affine point + * + * @return object[] + */ + public function convertToAffine(array $p) + { + return $p; + } + /** + * Converts an affine point to a jacobian coordinate + * + * @return object[] + */ + public function convertToInternal(array $p) + { + return $p; + } + /** + * Negates a point + * + * @return object[] + */ + public function negatePoint(array $p) + { + $temp = [$p[0], $p[1]->negate()]; + if (isset($p[2])) { + $temp[] = $p[2]; + } + return $temp; + } + /** + * Multiply and Add Points + * + * @return int[] + */ + public function multiplyAddPoints(array $points, array $scalars) + { + $p1 = $this->convertToInternal($points[0]); + $p2 = $this->convertToInternal($points[1]); + $p1 = $this->multiplyPoint($p1, $scalars[0]); + $p2 = $this->multiplyPoint($p2, $scalars[1]); + $r = $this->addPoint($p1, $p2); + return $this->convertToAffine($r); + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/BaseCurves/Binary.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/BaseCurves/Binary.php new file mode 100644 index 0000000..8883019 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/BaseCurves/Binary.php @@ -0,0 +1,324 @@ + + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://pear.php.net/package/Math_BigInteger + */ +namespace FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves; + +use FluentSmtpLib\phpseclib3\Math\BigInteger; +use FluentSmtpLib\phpseclib3\Math\BinaryField; +use FluentSmtpLib\phpseclib3\Math\BinaryField\Integer as BinaryInteger; +/** + * Curves over y^2 + x*y = x^3 + a*x^2 + b + * + * @author Jim Wigginton + */ +class Binary extends \FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\Base +{ + /** + * Binary Field Integer factory + * + * @var BinaryField + */ + protected $factory; + /** + * Cofficient for x^1 + * + * @var object + */ + protected $a; + /** + * Cofficient for x^0 + * + * @var object + */ + protected $b; + /** + * Base Point + * + * @var object + */ + protected $p; + /** + * The number one over the specified finite field + * + * @var object + */ + protected $one; + /** + * The modulo + * + * @var BigInteger + */ + protected $modulo; + /** + * The Order + * + * @var BigInteger + */ + protected $order; + /** + * Sets the modulo + */ + public function setModulo(...$modulo) + { + $this->modulo = $modulo; + $this->factory = new \FluentSmtpLib\phpseclib3\Math\BinaryField(...$modulo); + $this->one = $this->factory->newInteger("\x01"); + } + /** + * Set coefficients a and b + * + * @param string $a + * @param string $b + */ + public function setCoefficients($a, $b) + { + if (!isset($this->factory)) { + throw new \RuntimeException('setModulo needs to be called before this method'); + } + $this->a = $this->factory->newInteger(\pack('H*', $a)); + $this->b = $this->factory->newInteger(\pack('H*', $b)); + } + /** + * Set x and y coordinates for the base point + * + * @param string|BinaryInteger $x + * @param string|BinaryInteger $y + */ + public function setBasePoint($x, $y) + { + switch (\true) { + case !\is_string($x) && !$x instanceof \FluentSmtpLib\phpseclib3\Math\BinaryField\Integer: + throw new \UnexpectedValueException('FluentSmtpLib\\Argument 1 passed to Binary::setBasePoint() must be a string or an instance of BinaryField\\Integer'); + case !\is_string($y) && !$y instanceof \FluentSmtpLib\phpseclib3\Math\BinaryField\Integer: + throw new \UnexpectedValueException('FluentSmtpLib\\Argument 2 passed to Binary::setBasePoint() must be a string or an instance of BinaryField\\Integer'); + } + if (!isset($this->factory)) { + throw new \RuntimeException('setModulo needs to be called before this method'); + } + $this->p = [\is_string($x) ? $this->factory->newInteger(\pack('H*', $x)) : $x, \is_string($y) ? $this->factory->newInteger(\pack('H*', $y)) : $y]; + } + /** + * Retrieve the base point as an array + * + * @return array + */ + public function getBasePoint() + { + if (!isset($this->factory)) { + throw new \RuntimeException('setModulo needs to be called before this method'); + } + /* + if (!isset($this->p)) { + throw new \RuntimeException('setBasePoint needs to be called before this method'); + } + */ + return $this->p; + } + /** + * Adds two points on the curve + * + * @return FiniteField[] + */ + public function addPoint(array $p, array $q) + { + if (!isset($this->factory)) { + throw new \RuntimeException('setModulo needs to be called before this method'); + } + if (!\count($p) || !\count($q)) { + if (\count($q)) { + return $q; + } + if (\count($p)) { + return $p; + } + return []; + } + if (!isset($p[2]) || !isset($q[2])) { + throw new \RuntimeException('Affine coordinates need to be manually converted to "Jacobi" coordinates or vice versa'); + } + if ($p[0]->equals($q[0])) { + return !$p[1]->equals($q[1]) ? [] : $this->doublePoint($p); + } + // formulas from http://hyperelliptic.org/EFD/g12o/auto-shortw-jacobian.html + list($x1, $y1, $z1) = $p; + list($x2, $y2, $z2) = $q; + $o1 = $z1->multiply($z1); + $b = $x2->multiply($o1); + if ($z2->equals($this->one)) { + $d = $y2->multiply($o1)->multiply($z1); + $e = $x1->add($b); + $f = $y1->add($d); + $z3 = $e->multiply($z1); + $h = $f->multiply($x2)->add($z3->multiply($y2)); + $i = $f->add($z3); + $g = $z3->multiply($z3); + $p1 = $this->a->multiply($g); + $p2 = $f->multiply($i); + $p3 = $e->multiply($e)->multiply($e); + $x3 = $p1->add($p2)->add($p3); + $y3 = $i->multiply($x3)->add($g->multiply($h)); + return [$x3, $y3, $z3]; + } + $o2 = $z2->multiply($z2); + $a = $x1->multiply($o2); + $c = $y1->multiply($o2)->multiply($z2); + $d = $y2->multiply($o1)->multiply($z1); + $e = $a->add($b); + $f = $c->add($d); + $g = $e->multiply($z1); + $h = $f->multiply($x2)->add($g->multiply($y2)); + $z3 = $g->multiply($z2); + $i = $f->add($z3); + $p1 = $this->a->multiply($z3->multiply($z3)); + $p2 = $f->multiply($i); + $p3 = $e->multiply($e)->multiply($e); + $x3 = $p1->add($p2)->add($p3); + $y3 = $i->multiply($x3)->add($g->multiply($g)->multiply($h)); + return [$x3, $y3, $z3]; + } + /** + * Doubles a point on a curve + * + * @return FiniteField[] + */ + public function doublePoint(array $p) + { + if (!isset($this->factory)) { + throw new \RuntimeException('setModulo needs to be called before this method'); + } + if (!\count($p)) { + return []; + } + if (!isset($p[2])) { + throw new \RuntimeException('Affine coordinates need to be manually converted to "Jacobi" coordinates or vice versa'); + } + // formulas from http://hyperelliptic.org/EFD/g12o/auto-shortw-jacobian.html + list($x1, $y1, $z1) = $p; + $a = $x1->multiply($x1); + $b = $a->multiply($a); + if ($z1->equals($this->one)) { + $x3 = $b->add($this->b); + $z3 = clone $x1; + $p1 = $a->add($y1)->add($z3)->multiply($this->b); + $p2 = $a->add($y1)->multiply($b); + $y3 = $p1->add($p2); + return [$x3, $y3, $z3]; + } + $c = $z1->multiply($z1); + $d = $c->multiply($c); + $x3 = $b->add($this->b->multiply($d->multiply($d))); + $z3 = $x1->multiply($c); + $p1 = $b->multiply($z3); + $p2 = $a->add($y1->multiply($z1))->add($z3)->multiply($x3); + $y3 = $p1->add($p2); + return [$x3, $y3, $z3]; + } + /** + * Returns the X coordinate and the derived Y coordinate + * + * Not supported because it is covered by patents. + * Quoting https://www.openssl.org/docs/man1.1.0/apps/ecparam.html , + * + * "Due to patent issues the compressed option is disabled by default for binary curves + * and can be enabled by defining the preprocessor macro OPENSSL_EC_BIN_PT_COMP at + * compile time." + * + * @return array + */ + public function derivePoint($m) + { + throw new \RuntimeException('Point compression on binary finite field elliptic curves is not supported'); + } + /** + * Tests whether or not the x / y values satisfy the equation + * + * @return boolean + */ + public function verifyPoint(array $p) + { + list($x, $y) = $p; + $lhs = $y->multiply($y); + $lhs = $lhs->add($x->multiply($y)); + $x2 = $x->multiply($x); + $x3 = $x2->multiply($x); + $rhs = $x3->add($this->a->multiply($x2))->add($this->b); + return $lhs->equals($rhs); + } + /** + * Returns the modulo + * + * @return BigInteger + */ + public function getModulo() + { + return $this->modulo; + } + /** + * Returns the a coefficient + * + * @return \phpseclib3\Math\PrimeField\Integer + */ + public function getA() + { + return $this->a; + } + /** + * Returns the a coefficient + * + * @return \phpseclib3\Math\PrimeField\Integer + */ + public function getB() + { + return $this->b; + } + /** + * Returns the affine point + * + * A Jacobian Coordinate is of the form (x, y, z). + * To convert a Jacobian Coordinate to an Affine Point + * you do (x / z^2, y / z^3) + * + * @return \phpseclib3\Math\PrimeField\Integer[] + */ + public function convertToAffine(array $p) + { + if (!isset($p[2])) { + return $p; + } + list($x, $y, $z) = $p; + $z = $this->one->divide($z); + $z2 = $z->multiply($z); + return [$x->multiply($z2), $y->multiply($z2)->multiply($z)]; + } + /** + * Converts an affine point to a jacobian coordinate + * + * @return \phpseclib3\Math\PrimeField\Integer[] + */ + public function convertToInternal(array $p) + { + if (isset($p[2])) { + return $p; + } + $p[2] = clone $this->one; + $p['fresh'] = \true; + return $p; + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/BaseCurves/KoblitzPrime.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/BaseCurves/KoblitzPrime.php new file mode 100644 index 0000000..631edd3 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/BaseCurves/KoblitzPrime.php @@ -0,0 +1,273 @@ + + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://pear.php.net/package/Math_BigInteger + */ +namespace FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves; + +use FluentSmtpLib\phpseclib3\Math\BigInteger; +use FluentSmtpLib\phpseclib3\Math\PrimeField; +/** + * Curves over y^2 = x^3 + b + * + * @author Jim Wigginton + */ +class KoblitzPrime extends \FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\Prime +{ + /** + * Basis + * + * @var list + */ + protected $basis; + /** + * Beta + * + * @var PrimeField\Integer + */ + protected $beta; + // don't overwrite setCoefficients() with one that only accepts one parameter so that + // one might be able to switch between KoblitzPrime and Prime more easily (for benchmarking + // purposes). + /** + * Multiply and Add Points + * + * Uses a efficiently computable endomorphism to achieve a slight speedup + * + * Adapted from: + * https://github.com/indutny/elliptic/blob/725bd91/lib/elliptic/curve/short.js#L219 + * + * @return int[] + */ + public function multiplyAddPoints(array $points, array $scalars) + { + static $zero, $one, $two; + if (!isset($two)) { + $two = new \FluentSmtpLib\phpseclib3\Math\BigInteger(2); + $one = new \FluentSmtpLib\phpseclib3\Math\BigInteger(1); + } + if (!isset($this->beta)) { + // get roots + $inv = $this->one->divide($this->two)->negate(); + $s = $this->three->negate()->squareRoot()->multiply($inv); + $betas = [$inv->add($s), $inv->subtract($s)]; + $this->beta = $betas[0]->compare($betas[1]) < 0 ? $betas[0] : $betas[1]; + //echo strtoupper($this->beta->toHex(true)) . "\n"; exit; + } + if (!isset($this->basis)) { + $factory = new \FluentSmtpLib\phpseclib3\Math\PrimeField($this->order); + $tempOne = $factory->newInteger($one); + $tempTwo = $factory->newInteger($two); + $tempThree = $factory->newInteger(new \FluentSmtpLib\phpseclib3\Math\BigInteger(3)); + $inv = $tempOne->divide($tempTwo)->negate(); + $s = $tempThree->negate()->squareRoot()->multiply($inv); + $lambdas = [$inv->add($s), $inv->subtract($s)]; + $lhs = $this->multiplyPoint($this->p, $lambdas[0])[0]; + $rhs = $this->p[0]->multiply($this->beta); + $lambda = $lhs->equals($rhs) ? $lambdas[0] : $lambdas[1]; + $this->basis = static::extendedGCD($lambda->toBigInteger(), $this->order); + ///* + foreach ($this->basis as $basis) { + echo \strtoupper($basis['a']->toHex(\true)) . "\n"; + echo \strtoupper($basis['b']->toHex(\true)) . "\n\n"; + } + exit; + //*/ + } + $npoints = $nscalars = []; + for ($i = 0; $i < \count($points); $i++) { + $p = $points[$i]; + $k = $scalars[$i]->toBigInteger(); + // begin split + list($v1, $v2) = $this->basis; + $c1 = $v2['b']->multiply($k); + list($c1, $r) = $c1->divide($this->order); + if ($this->order->compare($r->multiply($two)) <= 0) { + $c1 = $c1->add($one); + } + $c2 = $v1['b']->negate()->multiply($k); + list($c2, $r) = $c2->divide($this->order); + if ($this->order->compare($r->multiply($two)) <= 0) { + $c2 = $c2->add($one); + } + $p1 = $c1->multiply($v1['a']); + $p2 = $c2->multiply($v2['a']); + $q1 = $c1->multiply($v1['b']); + $q2 = $c2->multiply($v2['b']); + $k1 = $k->subtract($p1)->subtract($p2); + $k2 = $q1->add($q2)->negate(); + // end split + $beta = [$p[0]->multiply($this->beta), $p[1], clone $this->one]; + if (isset($p['naf'])) { + $beta['naf'] = \array_map(function ($p) { + return [$p[0]->multiply($this->beta), $p[1], clone $this->one]; + }, $p['naf']); + $beta['nafwidth'] = $p['nafwidth']; + } + if ($k1->isNegative()) { + $k1 = $k1->negate(); + $p = $this->negatePoint($p); + } + if ($k2->isNegative()) { + $k2 = $k2->negate(); + $beta = $this->negatePoint($beta); + } + $pos = 2 * $i; + $npoints[$pos] = $p; + $nscalars[$pos] = $this->factory->newInteger($k1); + $pos++; + $npoints[$pos] = $beta; + $nscalars[$pos] = $this->factory->newInteger($k2); + } + return parent::multiplyAddPoints($npoints, $nscalars); + } + /** + * Returns the numerator and denominator of the slope + * + * @return FiniteField[] + */ + protected function doublePointHelper(array $p) + { + $numerator = $this->three->multiply($p[0])->multiply($p[0]); + $denominator = $this->two->multiply($p[1]); + return [$numerator, $denominator]; + } + /** + * Doubles a jacobian coordinate on the curve + * + * See http://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#doubling-dbl-2009-l + * + * @return FiniteField[] + */ + protected function jacobianDoublePoint(array $p) + { + list($x1, $y1, $z1) = $p; + $a = $x1->multiply($x1); + $b = $y1->multiply($y1); + $c = $b->multiply($b); + $d = $x1->add($b); + $d = $d->multiply($d)->subtract($a)->subtract($c)->multiply($this->two); + $e = $this->three->multiply($a); + $f = $e->multiply($e); + $x3 = $f->subtract($this->two->multiply($d)); + $y3 = $e->multiply($d->subtract($x3))->subtract($this->eight->multiply($c)); + $z3 = $this->two->multiply($y1)->multiply($z1); + return [$x3, $y3, $z3]; + } + /** + * Doubles a "fresh" jacobian coordinate on the curve + * + * See http://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#doubling-mdbl-2007-bl + * + * @return FiniteField[] + */ + protected function jacobianDoublePointMixed(array $p) + { + list($x1, $y1) = $p; + $xx = $x1->multiply($x1); + $yy = $y1->multiply($y1); + $yyyy = $yy->multiply($yy); + $s = $x1->add($yy); + $s = $s->multiply($s)->subtract($xx)->subtract($yyyy)->multiply($this->two); + $m = $this->three->multiply($xx); + $t = $m->multiply($m)->subtract($this->two->multiply($s)); + $x3 = $t; + $y3 = $s->subtract($t); + $y3 = $m->multiply($y3)->subtract($this->eight->multiply($yyyy)); + $z3 = $this->two->multiply($y1); + return [$x3, $y3, $z3]; + } + /** + * Tests whether or not the x / y values satisfy the equation + * + * @return boolean + */ + public function verifyPoint(array $p) + { + list($x, $y) = $p; + $lhs = $y->multiply($y); + $temp = $x->multiply($x)->multiply($x); + $rhs = $temp->add($this->b); + return $lhs->equals($rhs); + } + /** + * Calculates the parameters needed from the Euclidean algorithm as discussed at + * http://diamond.boisestate.edu/~liljanab/MATH308/GuideToECC.pdf#page=148 + * + * @param BigInteger $u + * @param BigInteger $v + * @return BigInteger[] + */ + protected static function extendedGCD(\FluentSmtpLib\phpseclib3\Math\BigInteger $u, \FluentSmtpLib\phpseclib3\Math\BigInteger $v) + { + $one = new \FluentSmtpLib\phpseclib3\Math\BigInteger(1); + $zero = new \FluentSmtpLib\phpseclib3\Math\BigInteger(); + $a = clone $one; + $b = clone $zero; + $c = clone $zero; + $d = clone $one; + $stop = $v->bitwise_rightShift($v->getLength() >> 1); + $a1 = clone $zero; + $b1 = clone $zero; + $a2 = clone $zero; + $b2 = clone $zero; + $postGreatestIndex = 0; + while (!$v->equals($zero)) { + list($q) = $u->divide($v); + $temp = $u; + $u = $v; + $v = $temp->subtract($v->multiply($q)); + $temp = $a; + $a = $c; + $c = $temp->subtract($a->multiply($q)); + $temp = $b; + $b = $d; + $d = $temp->subtract($b->multiply($q)); + if ($v->compare($stop) > 0) { + $a0 = $v; + $b0 = $c; + } else { + $postGreatestIndex++; + } + if ($postGreatestIndex == 1) { + $a1 = $v; + $b1 = $c->negate(); + } + if ($postGreatestIndex == 2) { + $rhs = $a0->multiply($a0)->add($b0->multiply($b0)); + $lhs = $v->multiply($v)->add($b->multiply($b)); + if ($lhs->compare($rhs) <= 0) { + $a2 = $a0; + $b2 = $b0->negate(); + } else { + $a2 = $v; + $b2 = $c->negate(); + } + break; + } + } + return [['a' => $a1, 'b' => $b1], ['a' => $a2, 'b' => $b2]]; + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/BaseCurves/Montgomery.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/BaseCurves/Montgomery.php new file mode 100644 index 0000000..f606c81 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/BaseCurves/Montgomery.php @@ -0,0 +1,246 @@ + + * @copyright 2019 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://pear.php.net/package/Math_BigInteger + */ +namespace FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves; + +use FluentSmtpLib\phpseclib3\Crypt\EC\Curves\Curve25519; +use FluentSmtpLib\phpseclib3\Math\BigInteger; +use FluentSmtpLib\phpseclib3\Math\PrimeField; +use FluentSmtpLib\phpseclib3\Math\PrimeField\Integer as PrimeInteger; +/** + * Curves over y^2 = x^3 + a*x + x + * + * @author Jim Wigginton + */ +class Montgomery extends \FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\Base +{ + /** + * Prime Field Integer factory + * + * @var PrimeField + */ + protected $factory; + /** + * Cofficient for x + * + * @var object + */ + protected $a; + /** + * Constant used for point doubling + * + * @var object + */ + protected $a24; + /** + * The Number Zero + * + * @var object + */ + protected $zero; + /** + * The Number One + * + * @var object + */ + protected $one; + /** + * Base Point + * + * @var object + */ + protected $p; + /** + * The modulo + * + * @var BigInteger + */ + protected $modulo; + /** + * The Order + * + * @var BigInteger + */ + protected $order; + /** + * Sets the modulo + */ + public function setModulo(\FluentSmtpLib\phpseclib3\Math\BigInteger $modulo) + { + $this->modulo = $modulo; + $this->factory = new \FluentSmtpLib\phpseclib3\Math\PrimeField($modulo); + $this->zero = $this->factory->newInteger(new \FluentSmtpLib\phpseclib3\Math\BigInteger()); + $this->one = $this->factory->newInteger(new \FluentSmtpLib\phpseclib3\Math\BigInteger(1)); + } + /** + * Set coefficients a + */ + public function setCoefficients(\FluentSmtpLib\phpseclib3\Math\BigInteger $a) + { + if (!isset($this->factory)) { + throw new \RuntimeException('setModulo needs to be called before this method'); + } + $this->a = $this->factory->newInteger($a); + $two = $this->factory->newInteger(new \FluentSmtpLib\phpseclib3\Math\BigInteger(2)); + $four = $this->factory->newInteger(new \FluentSmtpLib\phpseclib3\Math\BigInteger(4)); + $this->a24 = $this->a->subtract($two)->divide($four); + } + /** + * Set x and y coordinates for the base point + * + * @param BigInteger|PrimeInteger $x + * @param BigInteger|PrimeInteger $y + * @return PrimeInteger[] + */ + public function setBasePoint($x, $y) + { + switch (\true) { + case !$x instanceof \FluentSmtpLib\phpseclib3\Math\BigInteger && !$x instanceof \FluentSmtpLib\phpseclib3\Math\PrimeField\Integer: + throw new \UnexpectedValueException('FluentSmtpLib\\Argument 1 passed to Prime::setBasePoint() must be an instance of either BigInteger or PrimeField\\Integer'); + case !$y instanceof \FluentSmtpLib\phpseclib3\Math\BigInteger && !$y instanceof \FluentSmtpLib\phpseclib3\Math\PrimeField\Integer: + throw new \UnexpectedValueException('FluentSmtpLib\\Argument 2 passed to Prime::setBasePoint() must be an instance of either BigInteger or PrimeField\\Integer'); + } + if (!isset($this->factory)) { + throw new \RuntimeException('setModulo needs to be called before this method'); + } + $this->p = [$x instanceof \FluentSmtpLib\phpseclib3\Math\BigInteger ? $this->factory->newInteger($x) : $x, $y instanceof \FluentSmtpLib\phpseclib3\Math\BigInteger ? $this->factory->newInteger($y) : $y]; + } + /** + * Retrieve the base point as an array + * + * @return array + */ + public function getBasePoint() + { + if (!isset($this->factory)) { + throw new \RuntimeException('setModulo needs to be called before this method'); + } + /* + if (!isset($this->p)) { + throw new \RuntimeException('setBasePoint needs to be called before this method'); + } + */ + return $this->p; + } + /** + * Doubles and adds a point on a curve + * + * See https://tools.ietf.org/html/draft-ietf-tls-curve25519-01#appendix-A.1.3 + * + * @return FiniteField[][] + */ + private function doubleAndAddPoint(array $p, array $q, \FluentSmtpLib\phpseclib3\Math\PrimeField\Integer $x1) + { + if (!isset($this->factory)) { + throw new \RuntimeException('setModulo needs to be called before this method'); + } + if (!\count($p) || !\count($q)) { + return []; + } + if (!isset($p[1])) { + throw new \RuntimeException('Affine coordinates need to be manually converted to XZ coordinates'); + } + list($x2, $z2) = $p; + list($x3, $z3) = $q; + $a = $x2->add($z2); + $aa = $a->multiply($a); + $b = $x2->subtract($z2); + $bb = $b->multiply($b); + $e = $aa->subtract($bb); + $c = $x3->add($z3); + $d = $x3->subtract($z3); + $da = $d->multiply($a); + $cb = $c->multiply($b); + $temp = $da->add($cb); + $x5 = $temp->multiply($temp); + $temp = $da->subtract($cb); + $z5 = $x1->multiply($temp->multiply($temp)); + $x4 = $aa->multiply($bb); + $temp = static::class == \FluentSmtpLib\phpseclib3\Crypt\EC\Curves\Curve25519::class ? $bb : $aa; + $z4 = $e->multiply($temp->add($this->a24->multiply($e))); + return [[$x4, $z4], [$x5, $z5]]; + } + /** + * Multiply a point on the curve by a scalar + * + * Uses the montgomery ladder technique as described here: + * + * https://en.wikipedia.org/wiki/Elliptic_curve_point_multiplication#Montgomery_ladder + * https://github.com/phpecc/phpecc/issues/16#issuecomment-59176772 + * + * @return array + */ + public function multiplyPoint(array $p, \FluentSmtpLib\phpseclib3\Math\BigInteger $d) + { + $p1 = [$this->one, $this->zero]; + $alreadyInternal = isset($p[1]); + $p2 = $this->convertToInternal($p); + $x = $p[0]; + $b = $d->toBits(); + $b = \str_pad($b, 256, '0', \STR_PAD_LEFT); + for ($i = 0; $i < \strlen($b); $i++) { + $b_i = (int) $b[$i]; + if ($b_i) { + list($p2, $p1) = $this->doubleAndAddPoint($p2, $p1, $x); + } else { + list($p1, $p2) = $this->doubleAndAddPoint($p1, $p2, $x); + } + } + return $alreadyInternal ? $p1 : $this->convertToAffine($p1); + } + /** + * Converts an affine point to an XZ coordinate + * + * From https://hyperelliptic.org/EFD/g1p/auto-montgom-xz.html + * + * XZ coordinates represent x y as X Z satsfying the following equations: + * + * x=X/Z + * + * @return \phpseclib3\Math\PrimeField\Integer[] + */ + public function convertToInternal(array $p) + { + if (empty($p)) { + return [clone $this->zero, clone $this->one]; + } + if (isset($p[1])) { + return $p; + } + $p[1] = clone $this->one; + return $p; + } + /** + * Returns the affine point + * + * @return \phpseclib3\Math\PrimeField\Integer[] + */ + public function convertToAffine(array $p) + { + if (!isset($p[1])) { + return $p; + } + list($x, $z) = $p; + return [$x->divide($z)]; + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/BaseCurves/Prime.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/BaseCurves/Prime.php new file mode 100644 index 0000000..76eff06 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/BaseCurves/Prime.php @@ -0,0 +1,695 @@ + + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://pear.php.net/package/Math_BigInteger + */ +namespace FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves; + +use FluentSmtpLib\phpseclib3\Common\Functions\Strings; +use FluentSmtpLib\phpseclib3\Math\BigInteger; +use FluentSmtpLib\phpseclib3\Math\Common\FiniteField\Integer; +use FluentSmtpLib\phpseclib3\Math\PrimeField; +use FluentSmtpLib\phpseclib3\Math\PrimeField\Integer as PrimeInteger; +/** + * Curves over y^2 = x^3 + a*x + b + * + * @author Jim Wigginton + */ +class Prime extends \FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\Base +{ + /** + * Prime Field Integer factory + * + * @var \phpseclib3\Math\PrimeFields + */ + protected $factory; + /** + * Cofficient for x^1 + * + * @var object + */ + protected $a; + /** + * Cofficient for x^0 + * + * @var object + */ + protected $b; + /** + * Base Point + * + * @var object + */ + protected $p; + /** + * The number one over the specified finite field + * + * @var object + */ + protected $one; + /** + * The number two over the specified finite field + * + * @var object + */ + protected $two; + /** + * The number three over the specified finite field + * + * @var object + */ + protected $three; + /** + * The number four over the specified finite field + * + * @var object + */ + protected $four; + /** + * The number eight over the specified finite field + * + * @var object + */ + protected $eight; + /** + * The modulo + * + * @var BigInteger + */ + protected $modulo; + /** + * The Order + * + * @var BigInteger + */ + protected $order; + /** + * Sets the modulo + */ + public function setModulo(\FluentSmtpLib\phpseclib3\Math\BigInteger $modulo) + { + $this->modulo = $modulo; + $this->factory = new \FluentSmtpLib\phpseclib3\Math\PrimeField($modulo); + $this->two = $this->factory->newInteger(new \FluentSmtpLib\phpseclib3\Math\BigInteger(2)); + $this->three = $this->factory->newInteger(new \FluentSmtpLib\phpseclib3\Math\BigInteger(3)); + // used by jacobian coordinates + $this->one = $this->factory->newInteger(new \FluentSmtpLib\phpseclib3\Math\BigInteger(1)); + $this->four = $this->factory->newInteger(new \FluentSmtpLib\phpseclib3\Math\BigInteger(4)); + $this->eight = $this->factory->newInteger(new \FluentSmtpLib\phpseclib3\Math\BigInteger(8)); + } + /** + * Set coefficients a and b + */ + public function setCoefficients(\FluentSmtpLib\phpseclib3\Math\BigInteger $a, \FluentSmtpLib\phpseclib3\Math\BigInteger $b) + { + if (!isset($this->factory)) { + throw new \RuntimeException('setModulo needs to be called before this method'); + } + $this->a = $this->factory->newInteger($a); + $this->b = $this->factory->newInteger($b); + } + /** + * Set x and y coordinates for the base point + * + * @param BigInteger|PrimeInteger $x + * @param BigInteger|PrimeInteger $y + * @return PrimeInteger[] + */ + public function setBasePoint($x, $y) + { + switch (\true) { + case !$x instanceof \FluentSmtpLib\phpseclib3\Math\BigInteger && !$x instanceof \FluentSmtpLib\phpseclib3\Math\PrimeField\Integer: + throw new \UnexpectedValueException('FluentSmtpLib\\Argument 1 passed to Prime::setBasePoint() must be an instance of either BigInteger or PrimeField\\Integer'); + case !$y instanceof \FluentSmtpLib\phpseclib3\Math\BigInteger && !$y instanceof \FluentSmtpLib\phpseclib3\Math\PrimeField\Integer: + throw new \UnexpectedValueException('FluentSmtpLib\\Argument 2 passed to Prime::setBasePoint() must be an instance of either BigInteger or PrimeField\\Integer'); + } + if (!isset($this->factory)) { + throw new \RuntimeException('setModulo needs to be called before this method'); + } + $this->p = [$x instanceof \FluentSmtpLib\phpseclib3\Math\BigInteger ? $this->factory->newInteger($x) : $x, $y instanceof \FluentSmtpLib\phpseclib3\Math\BigInteger ? $this->factory->newInteger($y) : $y]; + } + /** + * Retrieve the base point as an array + * + * @return array + */ + public function getBasePoint() + { + if (!isset($this->factory)) { + throw new \RuntimeException('setModulo needs to be called before this method'); + } + /* + if (!isset($this->p)) { + throw new \RuntimeException('setBasePoint needs to be called before this method'); + } + */ + return $this->p; + } + /** + * Adds two "fresh" jacobian form on the curve + * + * @return FiniteField[] + */ + protected function jacobianAddPointMixedXY(array $p, array $q) + { + list($u1, $s1) = $p; + list($u2, $s2) = $q; + if ($u1->equals($u2)) { + if (!$s1->equals($s2)) { + return []; + } else { + return $this->doublePoint($p); + } + } + $h = $u2->subtract($u1); + $r = $s2->subtract($s1); + $h2 = $h->multiply($h); + $h3 = $h2->multiply($h); + $v = $u1->multiply($h2); + $x3 = $r->multiply($r)->subtract($h3)->subtract($v->multiply($this->two)); + $y3 = $r->multiply($v->subtract($x3))->subtract($s1->multiply($h3)); + return [$x3, $y3, $h]; + } + /** + * Adds one "fresh" jacobian form on the curve + * + * The second parameter should be the "fresh" one + * + * @return FiniteField[] + */ + protected function jacobianAddPointMixedX(array $p, array $q) + { + list($u1, $s1, $z1) = $p; + list($x2, $y2) = $q; + $z12 = $z1->multiply($z1); + $u2 = $x2->multiply($z12); + $s2 = $y2->multiply($z12->multiply($z1)); + if ($u1->equals($u2)) { + if (!$s1->equals($s2)) { + return []; + } else { + return $this->doublePoint($p); + } + } + $h = $u2->subtract($u1); + $r = $s2->subtract($s1); + $h2 = $h->multiply($h); + $h3 = $h2->multiply($h); + $v = $u1->multiply($h2); + $x3 = $r->multiply($r)->subtract($h3)->subtract($v->multiply($this->two)); + $y3 = $r->multiply($v->subtract($x3))->subtract($s1->multiply($h3)); + $z3 = $h->multiply($z1); + return [$x3, $y3, $z3]; + } + /** + * Adds two jacobian coordinates on the curve + * + * @return FiniteField[] + */ + protected function jacobianAddPoint(array $p, array $q) + { + list($x1, $y1, $z1) = $p; + list($x2, $y2, $z2) = $q; + $z12 = $z1->multiply($z1); + $z22 = $z2->multiply($z2); + $u1 = $x1->multiply($z22); + $u2 = $x2->multiply($z12); + $s1 = $y1->multiply($z22->multiply($z2)); + $s2 = $y2->multiply($z12->multiply($z1)); + if ($u1->equals($u2)) { + if (!$s1->equals($s2)) { + return []; + } else { + return $this->doublePoint($p); + } + } + $h = $u2->subtract($u1); + $r = $s2->subtract($s1); + $h2 = $h->multiply($h); + $h3 = $h2->multiply($h); + $v = $u1->multiply($h2); + $x3 = $r->multiply($r)->subtract($h3)->subtract($v->multiply($this->two)); + $y3 = $r->multiply($v->subtract($x3))->subtract($s1->multiply($h3)); + $z3 = $h->multiply($z1)->multiply($z2); + return [$x3, $y3, $z3]; + } + /** + * Adds two points on the curve + * + * @return FiniteField[] + */ + public function addPoint(array $p, array $q) + { + if (!isset($this->factory)) { + throw new \RuntimeException('setModulo needs to be called before this method'); + } + if (!\count($p) || !\count($q)) { + if (\count($q)) { + return $q; + } + if (\count($p)) { + return $p; + } + return []; + } + // use jacobian coordinates + if (isset($p[2]) && isset($q[2])) { + if (isset($p['fresh']) && isset($q['fresh'])) { + return $this->jacobianAddPointMixedXY($p, $q); + } + if (isset($p['fresh'])) { + return $this->jacobianAddPointMixedX($q, $p); + } + if (isset($q['fresh'])) { + return $this->jacobianAddPointMixedX($p, $q); + } + return $this->jacobianAddPoint($p, $q); + } + if (isset($p[2]) || isset($q[2])) { + throw new \RuntimeException('Affine coordinates need to be manually converted to Jacobi coordinates or vice versa'); + } + if ($p[0]->equals($q[0])) { + if (!$p[1]->equals($q[1])) { + return []; + } else { + // eg. doublePoint + list($numerator, $denominator) = $this->doublePointHelper($p); + } + } else { + $numerator = $q[1]->subtract($p[1]); + $denominator = $q[0]->subtract($p[0]); + } + $slope = $numerator->divide($denominator); + $x = $slope->multiply($slope)->subtract($p[0])->subtract($q[0]); + $y = $slope->multiply($p[0]->subtract($x))->subtract($p[1]); + return [$x, $y]; + } + /** + * Returns the numerator and denominator of the slope + * + * @return FiniteField[] + */ + protected function doublePointHelper(array $p) + { + $numerator = $this->three->multiply($p[0])->multiply($p[0])->add($this->a); + $denominator = $this->two->multiply($p[1]); + return [$numerator, $denominator]; + } + /** + * Doubles a jacobian coordinate on the curve + * + * @return FiniteField[] + */ + protected function jacobianDoublePoint(array $p) + { + list($x, $y, $z) = $p; + $x2 = $x->multiply($x); + $y2 = $y->multiply($y); + $z2 = $z->multiply($z); + $s = $this->four->multiply($x)->multiply($y2); + $m1 = $this->three->multiply($x2); + $m2 = $this->a->multiply($z2->multiply($z2)); + $m = $m1->add($m2); + $x1 = $m->multiply($m)->subtract($this->two->multiply($s)); + $y1 = $m->multiply($s->subtract($x1))->subtract($this->eight->multiply($y2->multiply($y2))); + $z1 = $this->two->multiply($y)->multiply($z); + return [$x1, $y1, $z1]; + } + /** + * Doubles a "fresh" jacobian coordinate on the curve + * + * @return FiniteField[] + */ + protected function jacobianDoublePointMixed(array $p) + { + list($x, $y) = $p; + $x2 = $x->multiply($x); + $y2 = $y->multiply($y); + $s = $this->four->multiply($x)->multiply($y2); + $m1 = $this->three->multiply($x2); + $m = $m1->add($this->a); + $x1 = $m->multiply($m)->subtract($this->two->multiply($s)); + $y1 = $m->multiply($s->subtract($x1))->subtract($this->eight->multiply($y2->multiply($y2))); + $z1 = $this->two->multiply($y); + return [$x1, $y1, $z1]; + } + /** + * Doubles a point on a curve + * + * @return FiniteField[] + */ + public function doublePoint(array $p) + { + if (!isset($this->factory)) { + throw new \RuntimeException('setModulo needs to be called before this method'); + } + if (!\count($p)) { + return []; + } + // use jacobian coordinates + if (isset($p[2])) { + if (isset($p['fresh'])) { + return $this->jacobianDoublePointMixed($p); + } + return $this->jacobianDoublePoint($p); + } + list($numerator, $denominator) = $this->doublePointHelper($p); + $slope = $numerator->divide($denominator); + $x = $slope->multiply($slope)->subtract($p[0])->subtract($p[0]); + $y = $slope->multiply($p[0]->subtract($x))->subtract($p[1]); + return [$x, $y]; + } + /** + * Returns the X coordinate and the derived Y coordinate + * + * @return array + */ + public function derivePoint($m) + { + $y = \ord(\FluentSmtpLib\phpseclib3\Common\Functions\Strings::shift($m)); + $x = new \FluentSmtpLib\phpseclib3\Math\BigInteger($m, 256); + $xp = $this->convertInteger($x); + switch ($y) { + case 2: + $ypn = \false; + break; + case 3: + $ypn = \true; + break; + default: + throw new \RuntimeException('Coordinate not in recognized format'); + } + $temp = $xp->multiply($this->a); + $temp = $xp->multiply($xp)->multiply($xp)->add($temp); + $temp = $temp->add($this->b); + $b = $temp->squareRoot(); + if (!$b) { + throw new \RuntimeException('Unable to derive Y coordinate'); + } + $bn = $b->isOdd(); + $yp = $ypn == $bn ? $b : $b->negate(); + return [$xp, $yp]; + } + /** + * Tests whether or not the x / y values satisfy the equation + * + * @return boolean + */ + public function verifyPoint(array $p) + { + list($x, $y) = $p; + $lhs = $y->multiply($y); + $temp = $x->multiply($this->a); + $temp = $x->multiply($x)->multiply($x)->add($temp); + $rhs = $temp->add($this->b); + return $lhs->equals($rhs); + } + /** + * Returns the modulo + * + * @return BigInteger + */ + public function getModulo() + { + return $this->modulo; + } + /** + * Returns the a coefficient + * + * @return PrimeInteger + */ + public function getA() + { + return $this->a; + } + /** + * Returns the a coefficient + * + * @return PrimeInteger + */ + public function getB() + { + return $this->b; + } + /** + * Multiply and Add Points + * + * Adapted from: + * https://github.com/indutny/elliptic/blob/725bd91/lib/elliptic/curve/base.js#L125 + * + * @return int[] + */ + public function multiplyAddPoints(array $points, array $scalars) + { + $length = \count($points); + foreach ($points as &$point) { + $point = $this->convertToInternal($point); + } + $wnd = [$this->getNAFPoints($points[0], 7)]; + $wndWidth = [isset($points[0]['nafwidth']) ? $points[0]['nafwidth'] : 7]; + for ($i = 1; $i < $length; $i++) { + $wnd[] = $this->getNAFPoints($points[$i], 1); + $wndWidth[] = isset($points[$i]['nafwidth']) ? $points[$i]['nafwidth'] : 1; + } + $naf = []; + // comb all window NAFs + $max = 0; + for ($i = $length - 1; $i >= 1; $i -= 2) { + $a = $i - 1; + $b = $i; + if ($wndWidth[$a] != 1 || $wndWidth[$b] != 1) { + $naf[$a] = $scalars[$a]->getNAF($wndWidth[$a]); + $naf[$b] = $scalars[$b]->getNAF($wndWidth[$b]); + $max = \max(\count($naf[$a]), \count($naf[$b]), $max); + continue; + } + $comb = [ + $points[$a], + // 1 + null, + // 3 + null, + // 5 + $points[$b], + ]; + $comb[1] = $this->addPoint($points[$a], $points[$b]); + $comb[2] = $this->addPoint($points[$a], $this->negatePoint($points[$b])); + $index = [ + -3, + /* -1 -1 */ + -1, + /* -1 0 */ + -5, + /* -1 1 */ + -7, + /* 0 -1 */ + 0, + /* 0 -1 */ + 7, + /* 0 1 */ + 5, + /* 1 -1 */ + 1, + /* 1 0 */ + 3, + ]; + $jsf = self::getJSFPoints($scalars[$a], $scalars[$b]); + $max = \max(\count($jsf[0]), $max); + if ($max > 0) { + $naf[$a] = \array_fill(0, $max, 0); + $naf[$b] = \array_fill(0, $max, 0); + } else { + $naf[$a] = []; + $naf[$b] = []; + } + for ($j = 0; $j < $max; $j++) { + $ja = isset($jsf[0][$j]) ? $jsf[0][$j] : 0; + $jb = isset($jsf[1][$j]) ? $jsf[1][$j] : 0; + $naf[$a][$j] = $index[3 * ($ja + 1) + $jb + 1]; + $naf[$b][$j] = 0; + $wnd[$a] = $comb; + } + } + $acc = []; + $temp = [0, 0, 0, 0]; + for ($i = $max; $i >= 0; $i--) { + $k = 0; + while ($i >= 0) { + $zero = \true; + for ($j = 0; $j < $length; $j++) { + $temp[$j] = isset($naf[$j][$i]) ? $naf[$j][$i] : 0; + if ($temp[$j] != 0) { + $zero = \false; + } + } + if (!$zero) { + break; + } + $k++; + $i--; + } + if ($i >= 0) { + $k++; + } + while ($k--) { + $acc = $this->doublePoint($acc); + } + if ($i < 0) { + break; + } + for ($j = 0; $j < $length; $j++) { + $z = $temp[$j]; + $p = null; + if ($z == 0) { + continue; + } + $p = $z > 0 ? $wnd[$j][$z - 1 >> 1] : $this->negatePoint($wnd[$j][-$z - 1 >> 1]); + $acc = $this->addPoint($acc, $p); + } + } + return $this->convertToAffine($acc); + } + /** + * Precomputes NAF points + * + * Adapted from: + * https://github.com/indutny/elliptic/blob/725bd91/lib/elliptic/curve/base.js#L351 + * + * @return int[] + */ + private function getNAFPoints(array $point, $wnd) + { + if (isset($point['naf'])) { + return $point['naf']; + } + $res = [$point]; + $max = (1 << $wnd) - 1; + $dbl = $max == 1 ? null : $this->doublePoint($point); + for ($i = 1; $i < $max; $i++) { + $res[] = $this->addPoint($res[$i - 1], $dbl); + } + $point['naf'] = $res; + /* + $str = ''; + foreach ($res as $re) { + $re[0] = bin2hex($re[0]->toBytes()); + $re[1] = bin2hex($re[1]->toBytes()); + $str.= " ['$re[0]', '$re[1]'],\r\n"; + } + file_put_contents('temp.txt', $str); + exit; + */ + return $res; + } + /** + * Precomputes points in Joint Sparse Form + * + * Adapted from: + * https://github.com/indutny/elliptic/blob/725bd91/lib/elliptic/utils.js#L96 + * + * @return int[] + */ + private static function getJSFPoints(\FluentSmtpLib\phpseclib3\Math\Common\FiniteField\Integer $k1, \FluentSmtpLib\phpseclib3\Math\Common\FiniteField\Integer $k2) + { + static $three; + if (!isset($three)) { + $three = new \FluentSmtpLib\phpseclib3\Math\BigInteger(3); + } + $jsf = [[], []]; + $k1 = $k1->toBigInteger(); + $k2 = $k2->toBigInteger(); + $d1 = 0; + $d2 = 0; + while ($k1->compare(new \FluentSmtpLib\phpseclib3\Math\BigInteger(-$d1)) > 0 || $k2->compare(new \FluentSmtpLib\phpseclib3\Math\BigInteger(-$d2)) > 0) { + // first phase + $m14 = $k1->testBit(0) + 2 * $k1->testBit(1); + $m14 += $d1; + $m14 &= 3; + $m24 = $k2->testBit(0) + 2 * $k2->testBit(1); + $m24 += $d2; + $m24 &= 3; + if ($m14 == 3) { + $m14 = -1; + } + if ($m24 == 3) { + $m24 = -1; + } + $u1 = 0; + if ($m14 & 1) { + // if $m14 is odd + $m8 = $k1->testBit(0) + 2 * $k1->testBit(1) + 4 * $k1->testBit(2); + $m8 += $d1; + $m8 &= 7; + $u1 = ($m8 == 3 || $m8 == 5) && $m24 == 2 ? -$m14 : $m14; + } + $jsf[0][] = $u1; + $u2 = 0; + if ($m24 & 1) { + // if $m24 is odd + $m8 = $k2->testBit(0) + 2 * $k2->testBit(1) + 4 * $k2->testBit(2); + $m8 += $d2; + $m8 &= 7; + $u2 = ($m8 == 3 || $m8 == 5) && $m14 == 2 ? -$m24 : $m24; + } + $jsf[1][] = $u2; + // second phase + if (2 * $d1 == $u1 + 1) { + $d1 = 1 - $d1; + } + if (2 * $d2 == $u2 + 1) { + $d2 = 1 - $d2; + } + $k1 = $k1->bitwise_rightShift(1); + $k2 = $k2->bitwise_rightShift(1); + } + return $jsf; + } + /** + * Returns the affine point + * + * A Jacobian Coordinate is of the form (x, y, z). + * To convert a Jacobian Coordinate to an Affine Point + * you do (x / z^2, y / z^3) + * + * @return \phpseclib3\Math\PrimeField\Integer[] + */ + public function convertToAffine(array $p) + { + if (!isset($p[2])) { + return $p; + } + list($x, $y, $z) = $p; + $z = $this->one->divide($z); + $z2 = $z->multiply($z); + return [$x->multiply($z2), $y->multiply($z2)->multiply($z)]; + } + /** + * Converts an affine point to a jacobian coordinate + * + * @return \phpseclib3\Math\PrimeField\Integer[] + */ + public function convertToInternal(array $p) + { + if (isset($p[2])) { + return $p; + } + $p[2] = clone $this->one; + $p['fresh'] = \true; + return $p; + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/BaseCurves/TwistedEdwards.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/BaseCurves/TwistedEdwards.php new file mode 100644 index 0000000..f9502c9 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/BaseCurves/TwistedEdwards.php @@ -0,0 +1,190 @@ + + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://pear.php.net/package/Math_BigInteger + */ +namespace FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves; + +use FluentSmtpLib\phpseclib3\Math\BigInteger; +use FluentSmtpLib\phpseclib3\Math\PrimeField; +use FluentSmtpLib\phpseclib3\Math\PrimeField\Integer as PrimeInteger; +/** + * Curves over a*x^2 + y^2 = 1 + d*x^2*y^2 + * + * @author Jim Wigginton + */ +class TwistedEdwards extends \FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\Base +{ + /** + * The modulo + * + * @var BigInteger + */ + protected $modulo; + /** + * Cofficient for x^2 + * + * @var object + */ + protected $a; + /** + * Cofficient for x^2*y^2 + * + * @var object + */ + protected $d; + /** + * Base Point + * + * @var object[] + */ + protected $p; + /** + * The number zero over the specified finite field + * + * @var object + */ + protected $zero; + /** + * The number one over the specified finite field + * + * @var object + */ + protected $one; + /** + * The number two over the specified finite field + * + * @var object + */ + protected $two; + /** + * Sets the modulo + */ + public function setModulo(\FluentSmtpLib\phpseclib3\Math\BigInteger $modulo) + { + $this->modulo = $modulo; + $this->factory = new \FluentSmtpLib\phpseclib3\Math\PrimeField($modulo); + $this->zero = $this->factory->newInteger(new \FluentSmtpLib\phpseclib3\Math\BigInteger(0)); + $this->one = $this->factory->newInteger(new \FluentSmtpLib\phpseclib3\Math\BigInteger(1)); + $this->two = $this->factory->newInteger(new \FluentSmtpLib\phpseclib3\Math\BigInteger(2)); + } + /** + * Set coefficients a and b + */ + public function setCoefficients(\FluentSmtpLib\phpseclib3\Math\BigInteger $a, \FluentSmtpLib\phpseclib3\Math\BigInteger $d) + { + if (!isset($this->factory)) { + throw new \RuntimeException('setModulo needs to be called before this method'); + } + $this->a = $this->factory->newInteger($a); + $this->d = $this->factory->newInteger($d); + } + /** + * Set x and y coordinates for the base point + */ + public function setBasePoint($x, $y) + { + switch (\true) { + case !$x instanceof \FluentSmtpLib\phpseclib3\Math\BigInteger && !$x instanceof \FluentSmtpLib\phpseclib3\Math\PrimeField\Integer: + throw new \UnexpectedValueException('FluentSmtpLib\\Argument 1 passed to Prime::setBasePoint() must be an instance of either BigInteger or PrimeField\\Integer'); + case !$y instanceof \FluentSmtpLib\phpseclib3\Math\BigInteger && !$y instanceof \FluentSmtpLib\phpseclib3\Math\PrimeField\Integer: + throw new \UnexpectedValueException('FluentSmtpLib\\Argument 2 passed to Prime::setBasePoint() must be an instance of either BigInteger or PrimeField\\Integer'); + } + if (!isset($this->factory)) { + throw new \RuntimeException('setModulo needs to be called before this method'); + } + $this->p = [$x instanceof \FluentSmtpLib\phpseclib3\Math\BigInteger ? $this->factory->newInteger($x) : $x, $y instanceof \FluentSmtpLib\phpseclib3\Math\BigInteger ? $this->factory->newInteger($y) : $y]; + } + /** + * Returns the a coefficient + * + * @return PrimeInteger + */ + public function getA() + { + return $this->a; + } + /** + * Returns the a coefficient + * + * @return PrimeInteger + */ + public function getD() + { + return $this->d; + } + /** + * Retrieve the base point as an array + * + * @return array + */ + public function getBasePoint() + { + if (!isset($this->factory)) { + throw new \RuntimeException('setModulo needs to be called before this method'); + } + /* + if (!isset($this->p)) { + throw new \RuntimeException('setBasePoint needs to be called before this method'); + } + */ + return $this->p; + } + /** + * Returns the affine point + * + * @return PrimeField\Integer[] + */ + public function convertToAffine(array $p) + { + if (!isset($p[2])) { + return $p; + } + list($x, $y, $z) = $p; + $z = $this->one->divide($z); + return [$x->multiply($z), $y->multiply($z)]; + } + /** + * Returns the modulo + * + * @return BigInteger + */ + public function getModulo() + { + return $this->modulo; + } + /** + * Tests whether or not the x / y values satisfy the equation + * + * @return boolean + */ + public function verifyPoint(array $p) + { + list($x, $y) = $p; + $x2 = $x->multiply($x); + $y2 = $y->multiply($y); + $lhs = $this->a->multiply($x2)->add($y2); + $rhs = $this->d->multiply($x2)->multiply($y2)->add($this->one); + return $lhs->equals($rhs); + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/Curve25519.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/Curve25519.php new file mode 100644 index 0000000..cb145a3 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/Curve25519.php @@ -0,0 +1,73 @@ + + * @copyright 2019 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://pear.php.net/package/Math_BigInteger + */ +namespace FluentSmtpLib\phpseclib3\Crypt\EC\Curves; + +use FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\Montgomery; +use FluentSmtpLib\phpseclib3\Math\BigInteger; +class Curve25519 extends \FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\Montgomery +{ + public function __construct() + { + // 2^255 - 19 + $this->setModulo(new \FluentSmtpLib\phpseclib3\Math\BigInteger('7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFED', 16)); + $this->a24 = $this->factory->newInteger(new \FluentSmtpLib\phpseclib3\Math\BigInteger('121666')); + $this->p = [$this->factory->newInteger(new \FluentSmtpLib\phpseclib3\Math\BigInteger(9))]; + // 2^252 + 0x14def9dea2f79cd65812631a5cf5d3ed + $this->setOrder(new \FluentSmtpLib\phpseclib3\Math\BigInteger('1000000000000000000000000000000014DEF9DEA2F79CD65812631A5CF5D3ED', 16)); + /* + $this->setCoefficients( + new BigInteger('486662'), // a + ); + $this->setBasePoint( + new BigInteger(9), + new BigInteger('14781619447589544791020593568409986887264606134616475288964881837755586237401') + ); + */ + } + /** + * Multiply a point on the curve by a scalar + * + * Modifies the scalar as described at https://tools.ietf.org/html/rfc7748#page-8 + * + * @return array + */ + public function multiplyPoint(array $p, \FluentSmtpLib\phpseclib3\Math\BigInteger $d) + { + //$r = strrev(sodium_crypto_scalarmult($d->toBytes(), strrev($p[0]->toBytes()))); + //return [$this->factory->newInteger(new BigInteger($r, 256))]; + $d = $d->toBytes(); + $d &= "\xf8" . \str_repeat("\xff", 30) . ""; + $d = \strrev($d); + $d |= "@"; + $d = new \FluentSmtpLib\phpseclib3\Math\BigInteger($d, -256); + return parent::multiplyPoint($p, $d); + } + /** + * Creates a random scalar multiplier + * + * @return BigInteger + */ + public function createRandomMultiplier() + { + return \FluentSmtpLib\phpseclib3\Math\BigInteger::random(256); + } + /** + * Performs range check + */ + public function rangeCheck(\FluentSmtpLib\phpseclib3\Math\BigInteger $x) + { + if ($x->getLength() > 256 || $x->isNegative()) { + throw new \RangeException('x must be a positive integer less than 256 bytes in length'); + } + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/Curve448.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/Curve448.php new file mode 100644 index 0000000..d5e65e6 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/Curve448.php @@ -0,0 +1,76 @@ + + * @copyright 2019 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://pear.php.net/package/Math_BigInteger + */ +namespace FluentSmtpLib\phpseclib3\Crypt\EC\Curves; + +use FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\Montgomery; +use FluentSmtpLib\phpseclib3\Math\BigInteger; +class Curve448 extends \FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\Montgomery +{ + public function __construct() + { + // 2^448 - 2^224 - 1 + $this->setModulo(new \FluentSmtpLib\phpseclib3\Math\BigInteger('FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE' . 'FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF', 16)); + $this->a24 = $this->factory->newInteger(new \FluentSmtpLib\phpseclib3\Math\BigInteger('39081')); + $this->p = [$this->factory->newInteger(new \FluentSmtpLib\phpseclib3\Math\BigInteger(5))]; + // 2^446 - 0x8335dc163bb124b65129c96fde933d8d723a70aadc873d6d54a7bb0d + $this->setOrder(new \FluentSmtpLib\phpseclib3\Math\BigInteger('3FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF' . '7CCA23E9C44EDB49AED63690216CC2728DC58F552378C292AB5844F3', 16)); + /* + $this->setCoefficients( + new BigInteger('156326'), // a + ); + $this->setBasePoint( + new BigInteger(5), + new BigInteger( + '355293926785568175264127502063783334808976399387714271831880898' . + '435169088786967410002932673765864550910142774147268105838985595290' . + '606362') + ); + */ + } + /** + * Multiply a point on the curve by a scalar + * + * Modifies the scalar as described at https://tools.ietf.org/html/rfc7748#page-8 + * + * @return array + */ + public function multiplyPoint(array $p, \FluentSmtpLib\phpseclib3\Math\BigInteger $d) + { + //$r = strrev(sodium_crypto_scalarmult($d->toBytes(), strrev($p[0]->toBytes()))); + //return [$this->factory->newInteger(new BigInteger($r, 256))]; + $d = $d->toBytes(); + $d[0] = $d[0] & "\xfc"; + $d = \strrev($d); + $d |= "\x80"; + $d = new \FluentSmtpLib\phpseclib3\Math\BigInteger($d, 256); + return parent::multiplyPoint($p, $d); + } + /** + * Creates a random scalar multiplier + * + * @return BigInteger + */ + public function createRandomMultiplier() + { + return \FluentSmtpLib\phpseclib3\Math\BigInteger::random(446); + } + /** + * Performs range check + */ + public function rangeCheck(\FluentSmtpLib\phpseclib3\Math\BigInteger $x) + { + if ($x->getLength() > 448 || $x->isNegative()) { + throw new \RangeException('x must be a positive integer less than 446 bytes in length'); + } + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/Ed25519.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/Ed25519.php new file mode 100644 index 0000000..fa033f2 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/Ed25519.php @@ -0,0 +1,295 @@ + + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + */ +namespace FluentSmtpLib\phpseclib3\Crypt\EC\Curves; + +use FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\TwistedEdwards; +use FluentSmtpLib\phpseclib3\Crypt\Hash; +use FluentSmtpLib\phpseclib3\Crypt\Random; +use FluentSmtpLib\phpseclib3\Math\BigInteger; +class Ed25519 extends \FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\TwistedEdwards +{ + const HASH = 'sha512'; + /* + Per https://tools.ietf.org/html/rfc8032#page-6 EdDSA has several parameters, one of which is b: + + 2. An integer b with 2^(b-1) > p. EdDSA public keys have exactly b + bits, and EdDSA signatures have exactly 2*b bits. b is + recommended to be a multiple of 8, so public key and signature + lengths are an integral number of octets. + + SIZE corresponds to b + */ + const SIZE = 32; + public function __construct() + { + // 2^255 - 19 + $this->setModulo(new \FluentSmtpLib\phpseclib3\Math\BigInteger('7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFED', 16)); + $this->setCoefficients( + // -1 + new \FluentSmtpLib\phpseclib3\Math\BigInteger('7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEC', 16), + // a + // -121665/121666 + new \FluentSmtpLib\phpseclib3\Math\BigInteger('52036CEE2B6FFE738CC740797779E89800700A4D4141D8AB75EB4DCA135978A3', 16) + ); + $this->setBasePoint(new \FluentSmtpLib\phpseclib3\Math\BigInteger('216936D3CD6E53FEC0A4E231FDD6DC5C692CC7609525A7B2C9562D608F25D51A', 16), new \FluentSmtpLib\phpseclib3\Math\BigInteger('6666666666666666666666666666666666666666666666666666666666666658', 16)); + $this->setOrder(new \FluentSmtpLib\phpseclib3\Math\BigInteger('1000000000000000000000000000000014DEF9DEA2F79CD65812631A5CF5D3ED', 16)); + // algorithm 14.47 from http://cacr.uwaterloo.ca/hac/about/chap14.pdf#page=16 + /* + $this->setReduction(function($x) { + $parts = $x->bitwise_split(255); + $className = $this->className; + + if (count($parts) > 2) { + list(, $r) = $x->divide($className::$modulo); + return $r; + } + + $zero = new BigInteger(); + $c = new BigInteger(19); + + switch (count($parts)) { + case 2: + list($qi, $ri) = $parts; + break; + case 1: + $qi = $zero; + list($ri) = $parts; + break; + case 0: + return $zero; + } + $r = $ri; + + while ($qi->compare($zero) > 0) { + $temp = $qi->multiply($c)->bitwise_split(255); + if (count($temp) == 2) { + list($qi, $ri) = $temp; + } else { + $qi = $zero; + list($ri) = $temp; + } + $r = $r->add($ri); + } + + while ($r->compare($className::$modulo) > 0) { + $r = $r->subtract($className::$modulo); + } + return $r; + }); + */ + } + /** + * Recover X from Y + * + * Implements steps 2-4 at https://tools.ietf.org/html/rfc8032#section-5.1.3 + * + * Used by EC\Keys\Common.php + * + * @param BigInteger $y + * @param boolean $sign + * @return object[] + */ + public function recoverX(\FluentSmtpLib\phpseclib3\Math\BigInteger $y, $sign) + { + $y = $this->factory->newInteger($y); + $y2 = $y->multiply($y); + $u = $y2->subtract($this->one); + $v = $this->d->multiply($y2)->add($this->one); + $x2 = $u->divide($v); + if ($x2->equals($this->zero)) { + if ($sign) { + throw new \RuntimeException('Unable to recover X coordinate (x2 = 0)'); + } + return clone $this->zero; + } + // find the square root + /* we don't do $x2->squareRoot() because, quoting from + https://tools.ietf.org/html/rfc8032#section-5.1.1: + + "For point decoding or "decompression", square roots modulo p are + needed. They can be computed using the Tonelli-Shanks algorithm or + the special case for p = 5 (mod 8). To find a square root of a, + first compute the candidate root x = a^((p+3)/8) (mod p)." + */ + $exp = $this->getModulo()->add(new \FluentSmtpLib\phpseclib3\Math\BigInteger(3)); + $exp = $exp->bitwise_rightShift(3); + $x = $x2->pow($exp); + // If v x^2 = -u (mod p), set x <-- x * 2^((p-1)/4), which is a square root. + if (!$x->multiply($x)->subtract($x2)->equals($this->zero)) { + $temp = $this->getModulo()->subtract(new \FluentSmtpLib\phpseclib3\Math\BigInteger(1)); + $temp = $temp->bitwise_rightShift(2); + $temp = $this->two->pow($temp); + $x = $x->multiply($temp); + if (!$x->multiply($x)->subtract($x2)->equals($this->zero)) { + throw new \RuntimeException('Unable to recover X coordinate'); + } + } + if ($x->isOdd() != $sign) { + $x = $x->negate(); + } + return [$x, $y]; + } + /** + * Extract Secret Scalar + * + * Implements steps 1-3 at https://tools.ietf.org/html/rfc8032#section-5.1.5 + * + * Used by the various key handlers + * + * @param string $str + * @return array + */ + public function extractSecret($str) + { + if (\strlen($str) != 32) { + throw new \LengthException('Private Key should be 32-bytes long'); + } + // 1. Hash the 32-byte private key using SHA-512, storing the digest in + // a 64-octet large buffer, denoted h. Only the lower 32 bytes are + // used for generating the public key. + $hash = new \FluentSmtpLib\phpseclib3\Crypt\Hash('sha512'); + $h = $hash->hash($str); + $h = \substr($h, 0, 32); + // 2. Prune the buffer: The lowest three bits of the first octet are + // cleared, the highest bit of the last octet is cleared, and the + // second highest bit of the last octet is set. + $h[0] = $h[0] & \chr(0xf8); + $h = \strrev($h); + $h[0] = $h[0] & \chr(0x3f) | \chr(0x40); + // 3. Interpret the buffer as the little-endian integer, forming a + // secret scalar s. + $dA = new \FluentSmtpLib\phpseclib3\Math\BigInteger($h, 256); + return ['dA' => $dA, 'secret' => $str]; + } + /** + * Encode a point as a string + * + * @param array $point + * @return string + */ + public function encodePoint($point) + { + list($x, $y) = $point; + $y = $y->toBytes(); + $y[0] = $y[0] & \chr(0x7f); + if ($x->isOdd()) { + $y[0] = $y[0] | \chr(0x80); + } + $y = \strrev($y); + return $y; + } + /** + * Creates a random scalar multiplier + * + * @return \phpseclib3\Math\PrimeField\Integer + */ + public function createRandomMultiplier() + { + return $this->extractSecret(\FluentSmtpLib\phpseclib3\Crypt\Random::string(32))['dA']; + } + /** + * Converts an affine point to an extended homogeneous coordinate + * + * From https://tools.ietf.org/html/rfc8032#section-5.1.4 : + * + * A point (x,y) is represented in extended homogeneous coordinates (X, Y, Z, T), + * with x = X/Z, y = Y/Z, x * y = T/Z. + * + * @return \phpseclib3\Math\PrimeField\Integer[] + */ + public function convertToInternal(array $p) + { + if (empty($p)) { + return [clone $this->zero, clone $this->one, clone $this->one, clone $this->zero]; + } + if (isset($p[2])) { + return $p; + } + $p[2] = clone $this->one; + $p[3] = $p[0]->multiply($p[1]); + return $p; + } + /** + * Doubles a point on a curve + * + * @return FiniteField[] + */ + public function doublePoint(array $p) + { + if (!isset($this->factory)) { + throw new \RuntimeException('setModulo needs to be called before this method'); + } + if (!\count($p)) { + return []; + } + if (!isset($p[2])) { + throw new \RuntimeException('Affine coordinates need to be manually converted to "Jacobi" coordinates or vice versa'); + } + // from https://tools.ietf.org/html/rfc8032#page-12 + list($x1, $y1, $z1, $t1) = $p; + $a = $x1->multiply($x1); + $b = $y1->multiply($y1); + $c = $this->two->multiply($z1)->multiply($z1); + $h = $a->add($b); + $temp = $x1->add($y1); + $e = $h->subtract($temp->multiply($temp)); + $g = $a->subtract($b); + $f = $c->add($g); + $x3 = $e->multiply($f); + $y3 = $g->multiply($h); + $t3 = $e->multiply($h); + $z3 = $f->multiply($g); + return [$x3, $y3, $z3, $t3]; + } + /** + * Adds two points on the curve + * + * @return FiniteField[] + */ + public function addPoint(array $p, array $q) + { + if (!isset($this->factory)) { + throw new \RuntimeException('setModulo needs to be called before this method'); + } + if (!\count($p) || !\count($q)) { + if (\count($q)) { + return $q; + } + if (\count($p)) { + return $p; + } + return []; + } + if (!isset($p[2]) || !isset($q[2])) { + throw new \RuntimeException('Affine coordinates need to be manually converted to "Jacobi" coordinates or vice versa'); + } + if ($p[0]->equals($q[0])) { + return !$p[1]->equals($q[1]) ? [] : $this->doublePoint($p); + } + // from https://tools.ietf.org/html/rfc8032#page-12 + list($x1, $y1, $z1, $t1) = $p; + list($x2, $y2, $z2, $t2) = $q; + $a = $y1->subtract($x1)->multiply($y2->subtract($x2)); + $b = $y1->add($x1)->multiply($y2->add($x2)); + $c = $t1->multiply($this->two)->multiply($this->d)->multiply($t2); + $d = $z1->multiply($this->two)->multiply($z2); + $e = $b->subtract($a); + $f = $d->subtract($c); + $g = $d->add($c); + $h = $b->add($a); + $x3 = $e->multiply($f); + $y3 = $g->multiply($h); + $t3 = $e->multiply($h); + $z3 = $f->multiply($g); + return [$x3, $y3, $z3, $t3]; + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/Ed448.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/Ed448.php new file mode 100644 index 0000000..f530b93 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/Ed448.php @@ -0,0 +1,222 @@ + + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + */ +namespace FluentSmtpLib\phpseclib3\Crypt\EC\Curves; + +use FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\TwistedEdwards; +use FluentSmtpLib\phpseclib3\Crypt\Hash; +use FluentSmtpLib\phpseclib3\Crypt\Random; +use FluentSmtpLib\phpseclib3\Math\BigInteger; +class Ed448 extends \FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\TwistedEdwards +{ + const HASH = 'shake256-912'; + const SIZE = 57; + public function __construct() + { + // 2^448 - 2^224 - 1 + $this->setModulo(new \FluentSmtpLib\phpseclib3\Math\BigInteger('FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE' . 'FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF', 16)); + $this->setCoefficients( + new \FluentSmtpLib\phpseclib3\Math\BigInteger(1), + // -39081 + new \FluentSmtpLib\phpseclib3\Math\BigInteger('FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE' . 'FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF6756', 16) + ); + $this->setBasePoint(new \FluentSmtpLib\phpseclib3\Math\BigInteger('4F1970C66BED0DED221D15A622BF36DA9E146570470F1767EA6DE324' . 'A3D3A46412AE1AF72AB66511433B80E18B00938E2626A82BC70CC05E', 16), new \FluentSmtpLib\phpseclib3\Math\BigInteger('693F46716EB6BC248876203756C9C7624BEA73736CA3984087789C1E' . '05A0C2D73AD3FF1CE67C39C4FDBD132C4ED7C8AD9808795BF230FA14', 16)); + $this->setOrder(new \FluentSmtpLib\phpseclib3\Math\BigInteger('3FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF' . '7CCA23E9C44EDB49AED63690216CC2728DC58F552378C292AB5844F3', 16)); + } + /** + * Recover X from Y + * + * Implements steps 2-4 at https://tools.ietf.org/html/rfc8032#section-5.2.3 + * + * Used by EC\Keys\Common.php + * + * @param BigInteger $y + * @param boolean $sign + * @return object[] + */ + public function recoverX(\FluentSmtpLib\phpseclib3\Math\BigInteger $y, $sign) + { + $y = $this->factory->newInteger($y); + $y2 = $y->multiply($y); + $u = $y2->subtract($this->one); + $v = $this->d->multiply($y2)->subtract($this->one); + $x2 = $u->divide($v); + if ($x2->equals($this->zero)) { + if ($sign) { + throw new \RuntimeException('Unable to recover X coordinate (x2 = 0)'); + } + return clone $this->zero; + } + // find the square root + $exp = $this->getModulo()->add(new \FluentSmtpLib\phpseclib3\Math\BigInteger(1)); + $exp = $exp->bitwise_rightShift(2); + $x = $x2->pow($exp); + if (!$x->multiply($x)->subtract($x2)->equals($this->zero)) { + throw new \RuntimeException('Unable to recover X coordinate'); + } + if ($x->isOdd() != $sign) { + $x = $x->negate(); + } + return [$x, $y]; + } + /** + * Extract Secret Scalar + * + * Implements steps 1-3 at https://tools.ietf.org/html/rfc8032#section-5.2.5 + * + * Used by the various key handlers + * + * @param string $str + * @return array + */ + public function extractSecret($str) + { + if (\strlen($str) != 57) { + throw new \LengthException('Private Key should be 57-bytes long'); + } + // 1. Hash the 57-byte private key using SHAKE256(x, 114), storing the + // digest in a 114-octet large buffer, denoted h. Only the lower 57 + // bytes are used for generating the public key. + $hash = new \FluentSmtpLib\phpseclib3\Crypt\Hash('shake256-912'); + $h = $hash->hash($str); + $h = \substr($h, 0, 57); + // 2. Prune the buffer: The two least significant bits of the first + // octet are cleared, all eight bits the last octet are cleared, and + // the highest bit of the second to last octet is set. + $h[0] = $h[0] & \chr(0xfc); + $h = \strrev($h); + $h[0] = "\x00"; + $h[1] = $h[1] | \chr(0x80); + // 3. Interpret the buffer as the little-endian integer, forming a + // secret scalar s. + $dA = new \FluentSmtpLib\phpseclib3\Math\BigInteger($h, 256); + return ['dA' => $dA, 'secret' => $str]; + $dA->secret = $str; + return $dA; + } + /** + * Encode a point as a string + * + * @param array $point + * @return string + */ + public function encodePoint($point) + { + list($x, $y) = $point; + $y = "\x00" . $y->toBytes(); + if ($x->isOdd()) { + $y[0] = $y[0] | \chr(0x80); + } + $y = \strrev($y); + return $y; + } + /** + * Creates a random scalar multiplier + * + * @return \phpseclib3\Math\PrimeField\Integer + */ + public function createRandomMultiplier() + { + return $this->extractSecret(\FluentSmtpLib\phpseclib3\Crypt\Random::string(57))['dA']; + } + /** + * Converts an affine point to an extended homogeneous coordinate + * + * From https://tools.ietf.org/html/rfc8032#section-5.2.4 : + * + * A point (x,y) is represented in extended homogeneous coordinates (X, Y, Z, T), + * with x = X/Z, y = Y/Z, x * y = T/Z. + * + * @return \phpseclib3\Math\PrimeField\Integer[] + */ + public function convertToInternal(array $p) + { + if (empty($p)) { + return [clone $this->zero, clone $this->one, clone $this->one]; + } + if (isset($p[2])) { + return $p; + } + $p[2] = clone $this->one; + return $p; + } + /** + * Doubles a point on a curve + * + * @return FiniteField[] + */ + public function doublePoint(array $p) + { + if (!isset($this->factory)) { + throw new \RuntimeException('setModulo needs to be called before this method'); + } + if (!\count($p)) { + return []; + } + if (!isset($p[2])) { + throw new \RuntimeException('Affine coordinates need to be manually converted to "Jacobi" coordinates or vice versa'); + } + // from https://tools.ietf.org/html/rfc8032#page-18 + list($x1, $y1, $z1) = $p; + $b = $x1->add($y1); + $b = $b->multiply($b); + $c = $x1->multiply($x1); + $d = $y1->multiply($y1); + $e = $c->add($d); + $h = $z1->multiply($z1); + $j = $e->subtract($this->two->multiply($h)); + $x3 = $b->subtract($e)->multiply($j); + $y3 = $c->subtract($d)->multiply($e); + $z3 = $e->multiply($j); + return [$x3, $y3, $z3]; + } + /** + * Adds two points on the curve + * + * @return FiniteField[] + */ + public function addPoint(array $p, array $q) + { + if (!isset($this->factory)) { + throw new \RuntimeException('setModulo needs to be called before this method'); + } + if (!\count($p) || !\count($q)) { + if (\count($q)) { + return $q; + } + if (\count($p)) { + return $p; + } + return []; + } + if (!isset($p[2]) || !isset($q[2])) { + throw new \RuntimeException('Affine coordinates need to be manually converted to "Jacobi" coordinates or vice versa'); + } + if ($p[0]->equals($q[0])) { + return !$p[1]->equals($q[1]) ? [] : $this->doublePoint($p); + } + // from https://tools.ietf.org/html/rfc8032#page-17 + list($x1, $y1, $z1) = $p; + list($x2, $y2, $z2) = $q; + $a = $z1->multiply($z2); + $b = $a->multiply($a); + $c = $x1->multiply($x2); + $d = $y1->multiply($y2); + $e = $this->d->multiply($c)->multiply($d); + $f = $b->subtract($e); + $g = $b->add($e); + $h = $x1->add($y1)->multiply($x2->add($y2)); + $x3 = $a->multiply($f)->multiply($h->subtract($c)->subtract($d)); + $y3 = $a->multiply($g)->multiply($d->subtract($c)); + $z3 = $f->multiply($g); + return [$x3, $y3, $z3]; + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP160r1.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP160r1.php new file mode 100644 index 0000000..07b4f7b --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP160r1.php @@ -0,0 +1,26 @@ + + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://pear.php.net/package/Math_BigInteger + */ +namespace FluentSmtpLib\phpseclib3\Crypt\EC\Curves; + +use FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\Prime; +use FluentSmtpLib\phpseclib3\Math\BigInteger; +class brainpoolP160r1 extends \FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\Prime +{ + public function __construct() + { + $this->setModulo(new \FluentSmtpLib\phpseclib3\Math\BigInteger('E95E4A5F737059DC60DFC7AD95B3D8139515620F', 16)); + $this->setCoefficients(new \FluentSmtpLib\phpseclib3\Math\BigInteger('340E7BE2A280EB74E2BE61BADA745D97E8F7C300', 16), new \FluentSmtpLib\phpseclib3\Math\BigInteger('1E589A8595423412134FAA2DBDEC95C8D8675E58', 16)); + $this->setBasePoint(new \FluentSmtpLib\phpseclib3\Math\BigInteger('BED5AF16EA3F6A4F62938C4631EB5AF7BDBCDBC3', 16), new \FluentSmtpLib\phpseclib3\Math\BigInteger('1667CB477A1A8EC338F94741669C976316DA6321', 16)); + $this->setOrder(new \FluentSmtpLib\phpseclib3\Math\BigInteger('E95E4A5F737059DC60DF5991D45029409E60FC09', 16)); + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP160t1.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP160t1.php new file mode 100644 index 0000000..b84847e --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP160t1.php @@ -0,0 +1,43 @@ + + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://pear.php.net/package/Math_BigInteger + */ +namespace FluentSmtpLib\phpseclib3\Crypt\EC\Curves; + +use FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\Prime; +use FluentSmtpLib\phpseclib3\Math\BigInteger; +class brainpoolP160t1 extends \FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\Prime +{ + public function __construct() + { + $this->setModulo(new \FluentSmtpLib\phpseclib3\Math\BigInteger('E95E4A5F737059DC60DFC7AD95B3D8139515620F', 16)); + $this->setCoefficients( + new \FluentSmtpLib\phpseclib3\Math\BigInteger('E95E4A5F737059DC60DFC7AD95B3D8139515620C', 16), + // eg. -3 + new \FluentSmtpLib\phpseclib3\Math\BigInteger('7A556B6DAE535B7B51ED2C4D7DAA7A0B5C55F380', 16) + ); + $this->setBasePoint(new \FluentSmtpLib\phpseclib3\Math\BigInteger('B199B13B9B34EFC1397E64BAEB05ACC265FF2378', 16), new \FluentSmtpLib\phpseclib3\Math\BigInteger('ADD6718B7C7C1961F0991B842443772152C9E0AD', 16)); + $this->setOrder(new \FluentSmtpLib\phpseclib3\Math\BigInteger('E95E4A5F737059DC60DF5991D45029409E60FC09', 16)); + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP192r1.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP192r1.php new file mode 100644 index 0000000..4b38bc7 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP192r1.php @@ -0,0 +1,26 @@ + + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://pear.php.net/package/Math_BigInteger + */ +namespace FluentSmtpLib\phpseclib3\Crypt\EC\Curves; + +use FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\Prime; +use FluentSmtpLib\phpseclib3\Math\BigInteger; +class brainpoolP192r1 extends \FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\Prime +{ + public function __construct() + { + $this->setModulo(new \FluentSmtpLib\phpseclib3\Math\BigInteger('C302F41D932A36CDA7A3463093D18DB78FCE476DE1A86297', 16)); + $this->setCoefficients(new \FluentSmtpLib\phpseclib3\Math\BigInteger('6A91174076B1E0E19C39C031FE8685C1CAE040E5C69A28EF', 16), new \FluentSmtpLib\phpseclib3\Math\BigInteger('469A28EF7C28CCA3DC721D044F4496BCCA7EF4146FBF25C9', 16)); + $this->setBasePoint(new \FluentSmtpLib\phpseclib3\Math\BigInteger('C0A0647EAAB6A48753B033C56CB0F0900A2F5C4853375FD6', 16), new \FluentSmtpLib\phpseclib3\Math\BigInteger('14B690866ABD5BB88B5F4828C1490002E6773FA2FA299B8F', 16)); + $this->setOrder(new \FluentSmtpLib\phpseclib3\Math\BigInteger('C302F41D932A36CDA7A3462F9E9E916B5BE8F1029AC4ACC1', 16)); + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP192t1.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP192t1.php new file mode 100644 index 0000000..933868e --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP192t1.php @@ -0,0 +1,30 @@ + + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://pear.php.net/package/Math_BigInteger + */ +namespace FluentSmtpLib\phpseclib3\Crypt\EC\Curves; + +use FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\Prime; +use FluentSmtpLib\phpseclib3\Math\BigInteger; +class brainpoolP192t1 extends \FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\Prime +{ + public function __construct() + { + $this->setModulo(new \FluentSmtpLib\phpseclib3\Math\BigInteger('C302F41D932A36CDA7A3463093D18DB78FCE476DE1A86297', 16)); + $this->setCoefficients( + new \FluentSmtpLib\phpseclib3\Math\BigInteger('C302F41D932A36CDA7A3463093D18DB78FCE476DE1A86294', 16), + // eg. -3 + new \FluentSmtpLib\phpseclib3\Math\BigInteger('13D56FFAEC78681E68F9DEB43B35BEC2FB68542E27897B79', 16) + ); + $this->setBasePoint(new \FluentSmtpLib\phpseclib3\Math\BigInteger('3AE9E58C82F63C30282E1FE7BBF43FA72C446AF6F4618129', 16), new \FluentSmtpLib\phpseclib3\Math\BigInteger('097E2C5667C2223A902AB5CA449D0084B7E5B3DE7CCC01C9', 16)); + $this->setOrder(new \FluentSmtpLib\phpseclib3\Math\BigInteger('C302F41D932A36CDA7A3462F9E9E916B5BE8F1029AC4ACC1', 16)); + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP224r1.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP224r1.php new file mode 100644 index 0000000..9d4abc0 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP224r1.php @@ -0,0 +1,26 @@ + + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://pear.php.net/package/Math_BigInteger + */ +namespace FluentSmtpLib\phpseclib3\Crypt\EC\Curves; + +use FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\Prime; +use FluentSmtpLib\phpseclib3\Math\BigInteger; +class brainpoolP224r1 extends \FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\Prime +{ + public function __construct() + { + $this->setModulo(new \FluentSmtpLib\phpseclib3\Math\BigInteger('D7C134AA264366862A18302575D1D787B09F075797DA89F57EC8C0FF', 16)); + $this->setCoefficients(new \FluentSmtpLib\phpseclib3\Math\BigInteger('68A5E62CA9CE6C1C299803A6C1530B514E182AD8B0042A59CAD29F43', 16), new \FluentSmtpLib\phpseclib3\Math\BigInteger('2580F63CCFE44138870713B1A92369E33E2135D266DBB372386C400B', 16)); + $this->setBasePoint(new \FluentSmtpLib\phpseclib3\Math\BigInteger('0D9029AD2C7E5CF4340823B2A87DC68C9E4CE3174C1E6EFDEE12C07D', 16), new \FluentSmtpLib\phpseclib3\Math\BigInteger('58AA56F772C0726F24C6B89E4ECDAC24354B9E99CAA3F6D3761402CD', 16)); + $this->setOrder(new \FluentSmtpLib\phpseclib3\Math\BigInteger('D7C134AA264366862A18302575D0FB98D116BC4B6DDEBCA3A5A7939F', 16)); + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP224t1.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP224t1.php new file mode 100644 index 0000000..cc6f186 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP224t1.php @@ -0,0 +1,30 @@ + + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://pear.php.net/package/Math_BigInteger + */ +namespace FluentSmtpLib\phpseclib3\Crypt\EC\Curves; + +use FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\Prime; +use FluentSmtpLib\phpseclib3\Math\BigInteger; +class brainpoolP224t1 extends \FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\Prime +{ + public function __construct() + { + $this->setModulo(new \FluentSmtpLib\phpseclib3\Math\BigInteger('D7C134AA264366862A18302575D1D787B09F075797DA89F57EC8C0FF', 16)); + $this->setCoefficients( + new \FluentSmtpLib\phpseclib3\Math\BigInteger('D7C134AA264366862A18302575D1D787B09F075797DA89F57EC8C0FC', 16), + // eg. -3 + new \FluentSmtpLib\phpseclib3\Math\BigInteger('4B337D934104CD7BEF271BF60CED1ED20DA14C08B3BB64F18A60888D', 16) + ); + $this->setBasePoint(new \FluentSmtpLib\phpseclib3\Math\BigInteger('6AB1E344CE25FF3896424E7FFE14762ECB49F8928AC0C76029B4D580', 16), new \FluentSmtpLib\phpseclib3\Math\BigInteger('0374E9F5143E568CD23F3F4D7C0D4B1E41C8CC0D1C6ABD5F1A46DB4C', 16)); + $this->setOrder(new \FluentSmtpLib\phpseclib3\Math\BigInteger('D7C134AA264366862A18302575D0FB98D116BC4B6DDEBCA3A5A7939F', 16)); + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP256r1.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP256r1.php new file mode 100644 index 0000000..6b3db35 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP256r1.php @@ -0,0 +1,26 @@ + + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://pear.php.net/package/Math_BigInteger + */ +namespace FluentSmtpLib\phpseclib3\Crypt\EC\Curves; + +use FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\Prime; +use FluentSmtpLib\phpseclib3\Math\BigInteger; +class brainpoolP256r1 extends \FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\Prime +{ + public function __construct() + { + $this->setModulo(new \FluentSmtpLib\phpseclib3\Math\BigInteger('A9FB57DBA1EEA9BC3E660A909D838D726E3BF623D52620282013481D1F6E5377', 16)); + $this->setCoefficients(new \FluentSmtpLib\phpseclib3\Math\BigInteger('7D5A0975FC2C3057EEF67530417AFFE7FB8055C126DC5C6CE94A4B44F330B5D9', 16), new \FluentSmtpLib\phpseclib3\Math\BigInteger('26DC5C6CE94A4B44F330B5D9BBD77CBF958416295CF7E1CE6BCCDC18FF8C07B6', 16)); + $this->setBasePoint(new \FluentSmtpLib\phpseclib3\Math\BigInteger('8BD2AEB9CB7E57CB2C4B482FFC81B7AFB9DE27E1E3BD23C23A4453BD9ACE3262', 16), new \FluentSmtpLib\phpseclib3\Math\BigInteger('547EF835C3DAC4FD97F8461A14611DC9C27745132DED8E545C1D54C72F046997', 16)); + $this->setOrder(new \FluentSmtpLib\phpseclib3\Math\BigInteger('A9FB57DBA1EEA9BC3E660A909D838D718C397AA3B561A6F7901E0E82974856A7', 16)); + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP256t1.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP256t1.php new file mode 100644 index 0000000..2c3a9fa --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP256t1.php @@ -0,0 +1,30 @@ + + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://pear.php.net/package/Math_BigInteger + */ +namespace FluentSmtpLib\phpseclib3\Crypt\EC\Curves; + +use FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\Prime; +use FluentSmtpLib\phpseclib3\Math\BigInteger; +class brainpoolP256t1 extends \FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\Prime +{ + public function __construct() + { + $this->setModulo(new \FluentSmtpLib\phpseclib3\Math\BigInteger('A9FB57DBA1EEA9BC3E660A909D838D726E3BF623D52620282013481D1F6E5377', 16)); + $this->setCoefficients( + new \FluentSmtpLib\phpseclib3\Math\BigInteger('A9FB57DBA1EEA9BC3E660A909D838D726E3BF623D52620282013481D1F6E5374', 16), + // eg. -3 + new \FluentSmtpLib\phpseclib3\Math\BigInteger('662C61C430D84EA4FE66A7733D0B76B7BF93EBC4AF2F49256AE58101FEE92B04', 16) + ); + $this->setBasePoint(new \FluentSmtpLib\phpseclib3\Math\BigInteger('A3E8EB3CC1CFE7B7732213B23A656149AFA142C47AAFBC2B79A191562E1305F4', 16), new \FluentSmtpLib\phpseclib3\Math\BigInteger('2D996C823439C56D7F7B22E14644417E69BCB6DE39D027001DABE8F35B25C9BE', 16)); + $this->setOrder(new \FluentSmtpLib\phpseclib3\Math\BigInteger('A9FB57DBA1EEA9BC3E660A909D838D718C397AA3B561A6F7901E0E82974856A7', 16)); + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP320r1.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP320r1.php new file mode 100644 index 0000000..1fe7731 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP320r1.php @@ -0,0 +1,26 @@ + + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://pear.php.net/package/Math_BigInteger + */ +namespace FluentSmtpLib\phpseclib3\Crypt\EC\Curves; + +use FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\Prime; +use FluentSmtpLib\phpseclib3\Math\BigInteger; +class brainpoolP320r1 extends \FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\Prime +{ + public function __construct() + { + $this->setModulo(new \FluentSmtpLib\phpseclib3\Math\BigInteger('D35E472036BC4FB7E13C785ED201E065F98FCFA6F6F40DEF4F9' . '2B9EC7893EC28FCD412B1F1B32E27', 16)); + $this->setCoefficients(new \FluentSmtpLib\phpseclib3\Math\BigInteger('3EE30B568FBAB0F883CCEBD46D3F3BB8A2A73513F5EB79DA66190EB085FFA9F4' . '92F375A97D860EB4', 16), new \FluentSmtpLib\phpseclib3\Math\BigInteger('520883949DFDBC42D3AD198640688A6FE13F41349554B49ACC31DCCD88453981' . '6F5EB4AC8FB1F1A6', 16)); + $this->setBasePoint(new \FluentSmtpLib\phpseclib3\Math\BigInteger('43BD7E9AFB53D8B85289BCC48EE5BFE6F20137D10A087EB6E7871E2A10A599C7' . '10AF8D0D39E20611', 16), new \FluentSmtpLib\phpseclib3\Math\BigInteger('14FDD05545EC1CC8AB4093247F77275E0743FFED117182EAA9C77877AAAC6AC7' . 'D35245D1692E8EE1', 16)); + $this->setOrder(new \FluentSmtpLib\phpseclib3\Math\BigInteger('D35E472036BC4FB7E13C785ED201E065F98FCFA5B68F12A32D4' . '82EC7EE8658E98691555B44C59311', 16)); + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP320t1.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP320t1.php new file mode 100644 index 0000000..24c805a --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP320t1.php @@ -0,0 +1,30 @@ + + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://pear.php.net/package/Math_BigInteger + */ +namespace FluentSmtpLib\phpseclib3\Crypt\EC\Curves; + +use FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\Prime; +use FluentSmtpLib\phpseclib3\Math\BigInteger; +class brainpoolP320t1 extends \FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\Prime +{ + public function __construct() + { + $this->setModulo(new \FluentSmtpLib\phpseclib3\Math\BigInteger('D35E472036BC4FB7E13C785ED201E065F98FCFA6F6F40DEF4F9' . '2B9EC7893EC28FCD412B1F1B32E27', 16)); + $this->setCoefficients( + new \FluentSmtpLib\phpseclib3\Math\BigInteger('D35E472036BC4FB7E13C785ED201E065F98FCFA6F6F40DEF4F92B9EC7893EC28' . 'FCD412B1F1B32E24', 16), + // eg. -3 + new \FluentSmtpLib\phpseclib3\Math\BigInteger('A7F561E038EB1ED560B3D147DB782013064C19F27ED27C6780AAF77FB8A547CE' . 'B5B4FEF422340353', 16) + ); + $this->setBasePoint(new \FluentSmtpLib\phpseclib3\Math\BigInteger('925BE9FB01AFC6FB4D3E7D4990010F813408AB106C4F09CB7EE07868CC136FFF' . '3357F624A21BED52', 16), new \FluentSmtpLib\phpseclib3\Math\BigInteger('63BA3A7A27483EBF6671DBEF7ABB30EBEE084E58A0B077AD42A5A0989D1EE71B' . '1B9BC0455FB0D2C3', 16)); + $this->setOrder(new \FluentSmtpLib\phpseclib3\Math\BigInteger('D35E472036BC4FB7E13C785ED201E065F98FCFA5B68F12A32D4' . '82EC7EE8658E98691555B44C59311', 16)); + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP384r1.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP384r1.php new file mode 100644 index 0000000..0821f16 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP384r1.php @@ -0,0 +1,26 @@ + + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://pear.php.net/package/Math_BigInteger + */ +namespace FluentSmtpLib\phpseclib3\Crypt\EC\Curves; + +use FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\Prime; +use FluentSmtpLib\phpseclib3\Math\BigInteger; +class brainpoolP384r1 extends \FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\Prime +{ + public function __construct() + { + $this->setModulo(new \FluentSmtpLib\phpseclib3\Math\BigInteger('8CB91E82A3386D280F5D6F7E50E641DF152F7109ED5456B412B1DA197FB71123ACD3A729901D1A7' . '1874700133107EC53', 16)); + $this->setCoefficients(new \FluentSmtpLib\phpseclib3\Math\BigInteger('7BC382C63D8C150C3C72080ACE05AFA0C2BEA28E4FB22787139165EFBA91F90F8AA5814A503' . 'AD4EB04A8C7DD22CE2826', 16), new \FluentSmtpLib\phpseclib3\Math\BigInteger('4A8C7DD22CE28268B39B55416F0447C2FB77DE107DCD2A62E880EA53EEB62D57CB4390295DB' . 'C9943AB78696FA504C11', 16)); + $this->setBasePoint(new \FluentSmtpLib\phpseclib3\Math\BigInteger('1D1C64F068CF45FFA2A63A81B7C13F6B8847A3E77EF14FE3DB7FCAFE0CBD10E8E826E03436D' . '646AAEF87B2E247D4AF1E', 16), new \FluentSmtpLib\phpseclib3\Math\BigInteger('8ABE1D7520F9C2A45CB1EB8E95CFD55262B70B29FEEC5864E19C054FF99129280E464621779' . '1811142820341263C5315', 16)); + $this->setOrder(new \FluentSmtpLib\phpseclib3\Math\BigInteger('8CB91E82A3386D280F5D6F7E50E641DF152F7109ED5456B31F166E6CAC0425A7CF3AB6AF6B7FC31' . '03B883202E9046565', 16)); + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP384t1.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP384t1.php new file mode 100644 index 0000000..5e9c132 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP384t1.php @@ -0,0 +1,30 @@ + + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://pear.php.net/package/Math_BigInteger + */ +namespace FluentSmtpLib\phpseclib3\Crypt\EC\Curves; + +use FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\Prime; +use FluentSmtpLib\phpseclib3\Math\BigInteger; +class brainpoolP384t1 extends \FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\Prime +{ + public function __construct() + { + $this->setModulo(new \FluentSmtpLib\phpseclib3\Math\BigInteger('8CB91E82A3386D280F5D6F7E50E641DF152F7109ED5456B412B1DA197FB71123ACD3A729901D1A7' . '1874700133107EC53', 16)); + $this->setCoefficients( + new \FluentSmtpLib\phpseclib3\Math\BigInteger('8CB91E82A3386D280F5D6F7E50E641DF152F7109ED5456B412B1DA197FB71123ACD3A729901' . 'D1A71874700133107EC50', 16), + // eg. -3 + new \FluentSmtpLib\phpseclib3\Math\BigInteger('7F519EADA7BDA81BD826DBA647910F8C4B9346ED8CCDC64E4B1ABD11756DCE1D2074AA263B8' . '8805CED70355A33B471EE', 16) + ); + $this->setBasePoint(new \FluentSmtpLib\phpseclib3\Math\BigInteger('18DE98B02DB9A306F2AFCD7235F72A819B80AB12EBD653172476FECD462AABFFC4FF191B946' . 'A5F54D8D0AA2F418808CC', 16), new \FluentSmtpLib\phpseclib3\Math\BigInteger('25AB056962D30651A114AFD2755AD336747F93475B7A1FCA3B88F2B6A208CCFE469408584DC' . '2B2912675BF5B9E582928', 16)); + $this->setOrder(new \FluentSmtpLib\phpseclib3\Math\BigInteger('8CB91E82A3386D280F5D6F7E50E641DF152F7109ED5456B31F166E6CAC0425A7CF3AB6AF6B7FC31' . '03B883202E9046565', 16)); + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP512r1.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP512r1.php new file mode 100644 index 0000000..2a37915 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP512r1.php @@ -0,0 +1,26 @@ + + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://pear.php.net/package/Math_BigInteger + */ +namespace FluentSmtpLib\phpseclib3\Crypt\EC\Curves; + +use FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\Prime; +use FluentSmtpLib\phpseclib3\Math\BigInteger; +class brainpoolP512r1 extends \FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\Prime +{ + public function __construct() + { + $this->setModulo(new \FluentSmtpLib\phpseclib3\Math\BigInteger('AADD9DB8DBE9C48B3FD4E6AE33C9FC07CB308DB3B3C9D20ED6639CCA703308717D4D9B009BC' . '66842AECDA12AE6A380E62881FF2F2D82C68528AA6056583A48F3', 16)); + $this->setCoefficients(new \FluentSmtpLib\phpseclib3\Math\BigInteger('7830A3318B603B89E2327145AC234CC594CBDD8D3DF91610A83441CAEA9863BC2DED5D5AA82' . '53AA10A2EF1C98B9AC8B57F1117A72BF2C7B9E7C1AC4D77FC94CA', 16), new \FluentSmtpLib\phpseclib3\Math\BigInteger('3DF91610A83441CAEA9863BC2DED5D5AA8253AA10A2EF1C98B9AC8B57F1117A72BF2C7B9E7C' . '1AC4D77FC94CADC083E67984050B75EBAE5DD2809BD638016F723', 16)); + $this->setBasePoint(new \FluentSmtpLib\phpseclib3\Math\BigInteger('81AEE4BDD82ED9645A21322E9C4C6A9385ED9F70B5D916C1B43B62EEF4D0098EFF3B1F78E2D' . '0D48D50D1687B93B97D5F7C6D5047406A5E688B352209BCB9F822', 16), new \FluentSmtpLib\phpseclib3\Math\BigInteger('7DDE385D566332ECC0EABFA9CF7822FDF209F70024A57B1AA000C55B881F8111B2DCDE494A5' . 'F485E5BCA4BD88A2763AED1CA2B2FA8F0540678CD1E0F3AD80892', 16)); + $this->setOrder(new \FluentSmtpLib\phpseclib3\Math\BigInteger('AADD9DB8DBE9C48B3FD4E6AE33C9FC07CB308DB3B3C9D20ED6639CCA70330870553E5C414CA' . '92619418661197FAC10471DB1D381085DDADDB58796829CA90069', 16)); + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP512t1.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP512t1.php new file mode 100644 index 0000000..7549f1e --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP512t1.php @@ -0,0 +1,30 @@ + + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://pear.php.net/package/Math_BigInteger + */ +namespace FluentSmtpLib\phpseclib3\Crypt\EC\Curves; + +use FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\Prime; +use FluentSmtpLib\phpseclib3\Math\BigInteger; +class brainpoolP512t1 extends \FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\Prime +{ + public function __construct() + { + $this->setModulo(new \FluentSmtpLib\phpseclib3\Math\BigInteger('AADD9DB8DBE9C48B3FD4E6AE33C9FC07CB308DB3B3C9D20ED6639CCA703308717D4D9B009BC' . '66842AECDA12AE6A380E62881FF2F2D82C68528AA6056583A48F3', 16)); + $this->setCoefficients( + new \FluentSmtpLib\phpseclib3\Math\BigInteger('AADD9DB8DBE9C48B3FD4E6AE33C9FC07CB308DB3B3C9D20ED6639CCA703308717D4D9B009BC' . '66842AECDA12AE6A380E62881FF2F2D82C68528AA6056583A48F0', 16), + // eg. -3 + new \FluentSmtpLib\phpseclib3\Math\BigInteger('7CBBBCF9441CFAB76E1890E46884EAE321F70C0BCB4981527897504BEC3E36A62BCDFA23049' . '76540F6450085F2DAE145C22553B465763689180EA2571867423E', 16) + ); + $this->setBasePoint(new \FluentSmtpLib\phpseclib3\Math\BigInteger('640ECE5C12788717B9C1BA06CBC2A6FEBA85842458C56DDE9DB1758D39C0313D82BA51735CD' . 'B3EA499AA77A7D6943A64F7A3F25FE26F06B51BAA2696FA9035DA', 16), new \FluentSmtpLib\phpseclib3\Math\BigInteger('5B534BD595F5AF0FA2C892376C84ACE1BB4E3019B71634C01131159CAE03CEE9D9932184BEE' . 'F216BD71DF2DADF86A627306ECFF96DBB8BACE198B61E00F8B332', 16)); + $this->setOrder(new \FluentSmtpLib\phpseclib3\Math\BigInteger('AADD9DB8DBE9C48B3FD4E6AE33C9FC07CB308DB3B3C9D20ED6639CCA70330870553E5C414CA' . '92619418661197FAC10471DB1D381085DDADDB58796829CA90069', 16)); + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/nistb233.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/nistb233.php new file mode 100644 index 0000000..ed26dc7 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/nistb233.php @@ -0,0 +1,17 @@ + + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://pear.php.net/package/Math_BigInteger + */ +namespace FluentSmtpLib\phpseclib3\Crypt\EC\Curves; + +final class nistb233 extends \FluentSmtpLib\phpseclib3\Crypt\EC\Curves\sect233r1 +{ +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/nistb409.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/nistb409.php new file mode 100644 index 0000000..5d8876b --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/nistb409.php @@ -0,0 +1,17 @@ + + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://pear.php.net/package/Math_BigInteger + */ +namespace FluentSmtpLib\phpseclib3\Crypt\EC\Curves; + +final class nistb409 extends \FluentSmtpLib\phpseclib3\Crypt\EC\Curves\sect409r1 +{ +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/nistk163.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/nistk163.php new file mode 100644 index 0000000..9988742 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/nistk163.php @@ -0,0 +1,17 @@ + + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://pear.php.net/package/Math_BigInteger + */ +namespace FluentSmtpLib\phpseclib3\Crypt\EC\Curves; + +final class nistk163 extends \FluentSmtpLib\phpseclib3\Crypt\EC\Curves\sect163k1 +{ +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/nistk233.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/nistk233.php new file mode 100644 index 0000000..d488f31 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/nistk233.php @@ -0,0 +1,17 @@ + + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://pear.php.net/package/Math_BigInteger + */ +namespace FluentSmtpLib\phpseclib3\Crypt\EC\Curves; + +final class nistk233 extends \FluentSmtpLib\phpseclib3\Crypt\EC\Curves\sect233k1 +{ +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/nistk283.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/nistk283.php new file mode 100644 index 0000000..6c4810d --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/nistk283.php @@ -0,0 +1,17 @@ + + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://pear.php.net/package/Math_BigInteger + */ +namespace FluentSmtpLib\phpseclib3\Crypt\EC\Curves; + +final class nistk283 extends \FluentSmtpLib\phpseclib3\Crypt\EC\Curves\sect283k1 +{ +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/nistk409.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/nistk409.php new file mode 100644 index 0000000..8f12eb4 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/nistk409.php @@ -0,0 +1,17 @@ + + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://pear.php.net/package/Math_BigInteger + */ +namespace FluentSmtpLib\phpseclib3\Crypt\EC\Curves; + +final class nistk409 extends \FluentSmtpLib\phpseclib3\Crypt\EC\Curves\sect409k1 +{ +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/nistp192.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/nistp192.php new file mode 100644 index 0000000..2dda2ab --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/nistp192.php @@ -0,0 +1,17 @@ + + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://pear.php.net/package/Math_BigInteger + */ +namespace FluentSmtpLib\phpseclib3\Crypt\EC\Curves; + +final class nistp192 extends \FluentSmtpLib\phpseclib3\Crypt\EC\Curves\secp192r1 +{ +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/nistp224.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/nistp224.php new file mode 100644 index 0000000..d5ba9ee --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/nistp224.php @@ -0,0 +1,17 @@ + + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://pear.php.net/package/Math_BigInteger + */ +namespace FluentSmtpLib\phpseclib3\Crypt\EC\Curves; + +final class nistp224 extends \FluentSmtpLib\phpseclib3\Crypt\EC\Curves\secp224r1 +{ +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/nistp256.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/nistp256.php new file mode 100644 index 0000000..7b643f6 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/nistp256.php @@ -0,0 +1,17 @@ + + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://pear.php.net/package/Math_BigInteger + */ +namespace FluentSmtpLib\phpseclib3\Crypt\EC\Curves; + +final class nistp256 extends \FluentSmtpLib\phpseclib3\Crypt\EC\Curves\secp256r1 +{ +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/nistp384.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/nistp384.php new file mode 100644 index 0000000..f7ba2f1 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/nistp384.php @@ -0,0 +1,17 @@ + + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://pear.php.net/package/Math_BigInteger + */ +namespace FluentSmtpLib\phpseclib3\Crypt\EC\Curves; + +final class nistp384 extends \FluentSmtpLib\phpseclib3\Crypt\EC\Curves\secp384r1 +{ +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/nistp521.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/nistp521.php new file mode 100644 index 0000000..bcc3f63 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/nistp521.php @@ -0,0 +1,17 @@ + + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://pear.php.net/package/Math_BigInteger + */ +namespace FluentSmtpLib\phpseclib3\Crypt\EC\Curves; + +final class nistp521 extends \FluentSmtpLib\phpseclib3\Crypt\EC\Curves\secp521r1 +{ +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/nistt571.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/nistt571.php new file mode 100644 index 0000000..8bdc06a --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/nistt571.php @@ -0,0 +1,17 @@ + + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://pear.php.net/package/Math_BigInteger + */ +namespace FluentSmtpLib\phpseclib3\Crypt\EC\Curves; + +final class nistt571 extends \FluentSmtpLib\phpseclib3\Crypt\EC\Curves\sect571k1 +{ +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/prime192v1.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/prime192v1.php new file mode 100644 index 0000000..f999e4e --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/prime192v1.php @@ -0,0 +1,17 @@ + + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://pear.php.net/package/Math_BigInteger + */ +namespace FluentSmtpLib\phpseclib3\Crypt\EC\Curves; + +final class prime192v1 extends \FluentSmtpLib\phpseclib3\Crypt\EC\Curves\secp192r1 +{ +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/prime192v2.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/prime192v2.php new file mode 100644 index 0000000..7cdf7ca --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/prime192v2.php @@ -0,0 +1,26 @@ + + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://pear.php.net/package/Math_BigInteger + */ +namespace FluentSmtpLib\phpseclib3\Crypt\EC\Curves; + +use FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\Prime; +use FluentSmtpLib\phpseclib3\Math\BigInteger; +class prime192v2 extends \FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\Prime +{ + public function __construct() + { + $this->setModulo(new \FluentSmtpLib\phpseclib3\Math\BigInteger('FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFF', 16)); + $this->setCoefficients(new \FluentSmtpLib\phpseclib3\Math\BigInteger('FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFC', 16), new \FluentSmtpLib\phpseclib3\Math\BigInteger('CC22D6DFB95C6B25E49C0D6364A4E5980C393AA21668D953', 16)); + $this->setBasePoint(new \FluentSmtpLib\phpseclib3\Math\BigInteger('EEA2BAE7E1497842F2DE7769CFE9C989C072AD696F48034A', 16), new \FluentSmtpLib\phpseclib3\Math\BigInteger('6574D11D69B6EC7A672BB82A083DF2F2B0847DE970B2DE15', 16)); + $this->setOrder(new \FluentSmtpLib\phpseclib3\Math\BigInteger('FFFFFFFFFFFFFFFFFFFFFFFE5FB1A724DC80418648D8DD31', 16)); + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/prime192v3.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/prime192v3.php new file mode 100644 index 0000000..b05b71d --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/prime192v3.php @@ -0,0 +1,26 @@ + + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://pear.php.net/package/Math_BigInteger + */ +namespace FluentSmtpLib\phpseclib3\Crypt\EC\Curves; + +use FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\Prime; +use FluentSmtpLib\phpseclib3\Math\BigInteger; +class prime192v3 extends \FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\Prime +{ + public function __construct() + { + $this->setModulo(new \FluentSmtpLib\phpseclib3\Math\BigInteger('FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFF', 16)); + $this->setCoefficients(new \FluentSmtpLib\phpseclib3\Math\BigInteger('FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFC', 16), new \FluentSmtpLib\phpseclib3\Math\BigInteger('22123DC2395A05CAA7423DAECCC94760A7D462256BD56916', 16)); + $this->setBasePoint(new \FluentSmtpLib\phpseclib3\Math\BigInteger('7D29778100C65A1DA1783716588DCE2B8B4AEE8E228F1896', 16), new \FluentSmtpLib\phpseclib3\Math\BigInteger('38A90F22637337334B49DCB66A6DC8F9978ACA7648A943B0', 16)); + $this->setOrder(new \FluentSmtpLib\phpseclib3\Math\BigInteger('FFFFFFFFFFFFFFFFFFFFFFFF7A62D031C83F4294F640EC13', 16)); + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/prime239v1.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/prime239v1.php new file mode 100644 index 0000000..4d6ce7b --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/prime239v1.php @@ -0,0 +1,26 @@ + + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://pear.php.net/package/Math_BigInteger + */ +namespace FluentSmtpLib\phpseclib3\Crypt\EC\Curves; + +use FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\Prime; +use FluentSmtpLib\phpseclib3\Math\BigInteger; +class prime239v1 extends \FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\Prime +{ + public function __construct() + { + $this->setModulo(new \FluentSmtpLib\phpseclib3\Math\BigInteger('7FFFFFFFFFFFFFFFFFFFFFFF7FFFFFFFFFFF8000000000007FFFFFFFFFFF', 16)); + $this->setCoefficients(new \FluentSmtpLib\phpseclib3\Math\BigInteger('7FFFFFFFFFFFFFFFFFFFFFFF7FFFFFFFFFFF8000000000007FFFFFFFFFFC', 16), new \FluentSmtpLib\phpseclib3\Math\BigInteger('6B016C3BDCF18941D0D654921475CA71A9DB2FB27D1D37796185C2942C0A', 16)); + $this->setBasePoint(new \FluentSmtpLib\phpseclib3\Math\BigInteger('0FFA963CDCA8816CCC33B8642BEDF905C3D358573D3F27FBBD3B3CB9AAAF', 16), new \FluentSmtpLib\phpseclib3\Math\BigInteger('7DEBE8E4E90A5DAE6E4054CA530BA04654B36818CE226B39FCCB7B02F1AE', 16)); + $this->setOrder(new \FluentSmtpLib\phpseclib3\Math\BigInteger('7FFFFFFFFFFFFFFFFFFFFFFF7FFFFF9E5E9A9F5D9071FBD1522688909D0B', 16)); + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/prime239v2.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/prime239v2.php new file mode 100644 index 0000000..8c7f392 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/prime239v2.php @@ -0,0 +1,26 @@ + + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://pear.php.net/package/Math_BigInteger + */ +namespace FluentSmtpLib\phpseclib3\Crypt\EC\Curves; + +use FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\Prime; +use FluentSmtpLib\phpseclib3\Math\BigInteger; +class prime239v2 extends \FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\Prime +{ + public function __construct() + { + $this->setModulo(new \FluentSmtpLib\phpseclib3\Math\BigInteger('7FFFFFFFFFFFFFFFFFFFFFFF7FFFFFFFFFFF8000000000007FFFFFFFFFFF', 16)); + $this->setCoefficients(new \FluentSmtpLib\phpseclib3\Math\BigInteger('7FFFFFFFFFFFFFFFFFFFFFFF7FFFFFFFFFFF8000000000007FFFFFFFFFFC', 16), new \FluentSmtpLib\phpseclib3\Math\BigInteger('617FAB6832576CBBFED50D99F0249C3FEE58B94BA0038C7AE84C8C832F2C', 16)); + $this->setBasePoint(new \FluentSmtpLib\phpseclib3\Math\BigInteger('38AF09D98727705120C921BB5E9E26296A3CDCF2F35757A0EAFD87B830E7', 16), new \FluentSmtpLib\phpseclib3\Math\BigInteger('5B0125E4DBEA0EC7206DA0FC01D9B081329FB555DE6EF460237DFF8BE4BA', 16)); + $this->setOrder(new \FluentSmtpLib\phpseclib3\Math\BigInteger('7FFFFFFFFFFFFFFFFFFFFFFF800000CFA7E8594377D414C03821BC582063', 16)); + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/prime239v3.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/prime239v3.php new file mode 100644 index 0000000..d1028c4 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/prime239v3.php @@ -0,0 +1,26 @@ + + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://pear.php.net/package/Math_BigInteger + */ +namespace FluentSmtpLib\phpseclib3\Crypt\EC\Curves; + +use FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\Prime; +use FluentSmtpLib\phpseclib3\Math\BigInteger; +class prime239v3 extends \FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\Prime +{ + public function __construct() + { + $this->setModulo(new \FluentSmtpLib\phpseclib3\Math\BigInteger('7FFFFFFFFFFFFFFFFFFFFFFF7FFFFFFFFFFF8000000000007FFFFFFFFFFF', 16)); + $this->setCoefficients(new \FluentSmtpLib\phpseclib3\Math\BigInteger('7FFFFFFFFFFFFFFFFFFFFFFF7FFFFFFFFFFF8000000000007FFFFFFFFFFC', 16), new \FluentSmtpLib\phpseclib3\Math\BigInteger('255705FA2A306654B1F4CB03D6A750A30C250102D4988717D9BA15AB6D3E', 16)); + $this->setBasePoint(new \FluentSmtpLib\phpseclib3\Math\BigInteger('6768AE8E18BB92CFCF005C949AA2C6D94853D0E660BBF854B1C9505FE95A', 16), new \FluentSmtpLib\phpseclib3\Math\BigInteger('1607E6898F390C06BC1D552BAD226F3B6FCFE48B6E818499AF18E3ED6CF3', 16)); + $this->setOrder(new \FluentSmtpLib\phpseclib3\Math\BigInteger('7FFFFFFFFFFFFFFFFFFFFFFF7FFFFF975DEB41B3A6057C3C432146526551', 16)); + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/prime256v1.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/prime256v1.php new file mode 100644 index 0000000..1e27d86 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/prime256v1.php @@ -0,0 +1,17 @@ + + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://pear.php.net/package/Math_BigInteger + */ +namespace FluentSmtpLib\phpseclib3\Crypt\EC\Curves; + +final class prime256v1 extends \FluentSmtpLib\phpseclib3\Crypt\EC\Curves\secp256r1 +{ +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp112r1.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp112r1.php new file mode 100644 index 0000000..85be3db --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp112r1.php @@ -0,0 +1,26 @@ + + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://pear.php.net/package/Math_BigInteger + */ +namespace FluentSmtpLib\phpseclib3\Crypt\EC\Curves; + +use FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\Prime; +use FluentSmtpLib\phpseclib3\Math\BigInteger; +class secp112r1 extends \FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\Prime +{ + public function __construct() + { + $this->setModulo(new \FluentSmtpLib\phpseclib3\Math\BigInteger('DB7C2ABF62E35E668076BEAD208B', 16)); + $this->setCoefficients(new \FluentSmtpLib\phpseclib3\Math\BigInteger('DB7C2ABF62E35E668076BEAD2088', 16), new \FluentSmtpLib\phpseclib3\Math\BigInteger('659EF8BA043916EEDE8911702B22', 16)); + $this->setBasePoint(new \FluentSmtpLib\phpseclib3\Math\BigInteger('09487239995A5EE76B55F9C2F098', 16), new \FluentSmtpLib\phpseclib3\Math\BigInteger('A89CE5AF8724C0A23E0E0FF77500', 16)); + $this->setOrder(new \FluentSmtpLib\phpseclib3\Math\BigInteger('DB7C2ABF62E35E7628DFAC6561C5', 16)); + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp112r2.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp112r2.php new file mode 100644 index 0000000..7d42af6 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp112r2.php @@ -0,0 +1,27 @@ + + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://pear.php.net/package/Math_BigInteger + */ +namespace FluentSmtpLib\phpseclib3\Crypt\EC\Curves; + +use FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\Prime; +use FluentSmtpLib\phpseclib3\Math\BigInteger; +class secp112r2 extends \FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\Prime +{ + public function __construct() + { + // same modulo as secp112r1 + $this->setModulo(new \FluentSmtpLib\phpseclib3\Math\BigInteger('DB7C2ABF62E35E668076BEAD208B', 16)); + $this->setCoefficients(new \FluentSmtpLib\phpseclib3\Math\BigInteger('6127C24C05F38A0AAAF65C0EF02C', 16), new \FluentSmtpLib\phpseclib3\Math\BigInteger('51DEF1815DB5ED74FCC34C85D709', 16)); + $this->setBasePoint(new \FluentSmtpLib\phpseclib3\Math\BigInteger('4BA30AB5E892B4E1649DD0928643', 16), new \FluentSmtpLib\phpseclib3\Math\BigInteger('ADCD46F5882E3747DEF36E956E97', 16)); + $this->setOrder(new \FluentSmtpLib\phpseclib3\Math\BigInteger('36DF0AAFD8B8D7597CA10520D04B', 16)); + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp128r1.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp128r1.php new file mode 100644 index 0000000..2affef6 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp128r1.php @@ -0,0 +1,26 @@ + + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://pear.php.net/package/Math_BigInteger + */ +namespace FluentSmtpLib\phpseclib3\Crypt\EC\Curves; + +use FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\Prime; +use FluentSmtpLib\phpseclib3\Math\BigInteger; +class secp128r1 extends \FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\Prime +{ + public function __construct() + { + $this->setModulo(new \FluentSmtpLib\phpseclib3\Math\BigInteger('FFFFFFFDFFFFFFFFFFFFFFFFFFFFFFFF', 16)); + $this->setCoefficients(new \FluentSmtpLib\phpseclib3\Math\BigInteger('FFFFFFFDFFFFFFFFFFFFFFFFFFFFFFFC', 16), new \FluentSmtpLib\phpseclib3\Math\BigInteger('E87579C11079F43DD824993C2CEE5ED3', 16)); + $this->setBasePoint(new \FluentSmtpLib\phpseclib3\Math\BigInteger('161FF7528B899B2D0C28607CA52C5B86', 16), new \FluentSmtpLib\phpseclib3\Math\BigInteger('CF5AC8395BAFEB13C02DA292DDED7A83', 16)); + $this->setOrder(new \FluentSmtpLib\phpseclib3\Math\BigInteger('FFFFFFFE0000000075A30D1B9038A115', 16)); + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp128r2.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp128r2.php new file mode 100644 index 0000000..e2bf691 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp128r2.php @@ -0,0 +1,27 @@ + + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://pear.php.net/package/Math_BigInteger + */ +namespace FluentSmtpLib\phpseclib3\Crypt\EC\Curves; + +use FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\Prime; +use FluentSmtpLib\phpseclib3\Math\BigInteger; +class secp128r2 extends \FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\Prime +{ + public function __construct() + { + // same as secp128r1 + $this->setModulo(new \FluentSmtpLib\phpseclib3\Math\BigInteger('FFFFFFFDFFFFFFFFFFFFFFFFFFFFFFFF', 16)); + $this->setCoefficients(new \FluentSmtpLib\phpseclib3\Math\BigInteger('D6031998D1B3BBFEBF59CC9BBFF9AEE1', 16), new \FluentSmtpLib\phpseclib3\Math\BigInteger('5EEEFCA380D02919DC2C6558BB6D8A5D', 16)); + $this->setBasePoint(new \FluentSmtpLib\phpseclib3\Math\BigInteger('7B6AA5D85E572983E6FB32A7CDEBC140', 16), new \FluentSmtpLib\phpseclib3\Math\BigInteger('27B6916A894D3AEE7106FE805FC34B44', 16)); + $this->setOrder(new \FluentSmtpLib\phpseclib3\Math\BigInteger('3FFFFFFF7FFFFFFFBE0024720613B5A3', 16)); + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp160k1.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp160k1.php new file mode 100644 index 0000000..63209ed --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp160k1.php @@ -0,0 +1,31 @@ + + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://pear.php.net/package/Math_BigInteger + */ +namespace FluentSmtpLib\phpseclib3\Crypt\EC\Curves; + +use FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\KoblitzPrime; +use FluentSmtpLib\phpseclib3\Math\BigInteger; +class secp160k1 extends \FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\KoblitzPrime +{ + public function __construct() + { + // same as secp160r2 + $this->setModulo(new \FluentSmtpLib\phpseclib3\Math\BigInteger('FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFAC73', 16)); + $this->setCoefficients(new \FluentSmtpLib\phpseclib3\Math\BigInteger('0000000000000000000000000000000000000000', 16), new \FluentSmtpLib\phpseclib3\Math\BigInteger('0000000000000000000000000000000000000007', 16)); + $this->setBasePoint(new \FluentSmtpLib\phpseclib3\Math\BigInteger('3B4C382CE37AA192A4019E763036F4F5DD4D7EBB', 16), new \FluentSmtpLib\phpseclib3\Math\BigInteger('938CF935318FDCED6BC28286531733C3F03C4FEE', 16)); + $this->setOrder(new \FluentSmtpLib\phpseclib3\Math\BigInteger('0100000000000000000001B8FA16DFAB9ACA16B6B3', 16)); + $this->basis = []; + $this->basis[] = ['a' => new \FluentSmtpLib\phpseclib3\Math\BigInteger('0096341F1138933BC2F505', -16), 'b' => new \FluentSmtpLib\phpseclib3\Math\BigInteger('FF6E9D0418C67BB8D5F562', -16)]; + $this->basis[] = ['a' => new \FluentSmtpLib\phpseclib3\Math\BigInteger('01BDCB3A09AAAABEAFF4A8', -16), 'b' => new \FluentSmtpLib\phpseclib3\Math\BigInteger('04D12329FF0EF498EA67', -16)]; + $this->beta = $this->factory->newInteger(new \FluentSmtpLib\phpseclib3\Math\BigInteger('645B7345A143464942CC46D7CF4D5D1E1E6CBB68', -16)); + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp160r1.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp160r1.php new file mode 100644 index 0000000..58753b4 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp160r1.php @@ -0,0 +1,26 @@ + + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://pear.php.net/package/Math_BigInteger + */ +namespace FluentSmtpLib\phpseclib3\Crypt\EC\Curves; + +use FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\Prime; +use FluentSmtpLib\phpseclib3\Math\BigInteger; +class secp160r1 extends \FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\Prime +{ + public function __construct() + { + $this->setModulo(new \FluentSmtpLib\phpseclib3\Math\BigInteger('FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7FFFFFFF', 16)); + $this->setCoefficients(new \FluentSmtpLib\phpseclib3\Math\BigInteger('FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7FFFFFFC', 16), new \FluentSmtpLib\phpseclib3\Math\BigInteger('1C97BEFC54BD7A8B65ACF89F81D4D4ADC565FA45', 16)); + $this->setBasePoint(new \FluentSmtpLib\phpseclib3\Math\BigInteger('4A96B5688EF573284664698968C38BB913CBFC82', 16), new \FluentSmtpLib\phpseclib3\Math\BigInteger('23A628553168947D59DCC912042351377AC5FB32', 16)); + $this->setOrder(new \FluentSmtpLib\phpseclib3\Math\BigInteger('0100000000000000000001F4C8F927AED3CA752257', 16)); + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp160r2.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp160r2.php new file mode 100644 index 0000000..e4bd301 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp160r2.php @@ -0,0 +1,27 @@ + + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://pear.php.net/package/Math_BigInteger + */ +namespace FluentSmtpLib\phpseclib3\Crypt\EC\Curves; + +use FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\Prime; +use FluentSmtpLib\phpseclib3\Math\BigInteger; +class secp160r2 extends \FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\Prime +{ + public function __construct() + { + // same as secp160k1 + $this->setModulo(new \FluentSmtpLib\phpseclib3\Math\BigInteger('FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFAC73', 16)); + $this->setCoefficients(new \FluentSmtpLib\phpseclib3\Math\BigInteger('FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFAC70', 16), new \FluentSmtpLib\phpseclib3\Math\BigInteger('B4E134D3FB59EB8BAB57274904664D5AF50388BA', 16)); + $this->setBasePoint(new \FluentSmtpLib\phpseclib3\Math\BigInteger('52DCB034293A117E1F4FF11B30F7199D3144CE6D', 16), new \FluentSmtpLib\phpseclib3\Math\BigInteger('FEAFFEF2E331F296E071FA0DF9982CFEA7D43F2E', 16)); + $this->setOrder(new \FluentSmtpLib\phpseclib3\Math\BigInteger('0100000000000000000000351EE786A818F3A1A16B', 16)); + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp192k1.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp192k1.php new file mode 100644 index 0000000..0b3c17f --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp192k1.php @@ -0,0 +1,30 @@ + + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://pear.php.net/package/Math_BigInteger + */ +namespace FluentSmtpLib\phpseclib3\Crypt\EC\Curves; + +use FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\KoblitzPrime; +use FluentSmtpLib\phpseclib3\Math\BigInteger; +class secp192k1 extends \FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\KoblitzPrime +{ + public function __construct() + { + $this->setModulo(new \FluentSmtpLib\phpseclib3\Math\BigInteger('FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFEE37', 16)); + $this->setCoefficients(new \FluentSmtpLib\phpseclib3\Math\BigInteger('000000000000000000000000000000000000000000000000', 16), new \FluentSmtpLib\phpseclib3\Math\BigInteger('000000000000000000000000000000000000000000000003', 16)); + $this->setBasePoint(new \FluentSmtpLib\phpseclib3\Math\BigInteger('DB4FF10EC057E9AE26B07D0280B7F4341DA5D1B1EAE06C7D', 16), new \FluentSmtpLib\phpseclib3\Math\BigInteger('9B2F2F6D9C5628A7844163D015BE86344082AA88D95E2F9D', 16)); + $this->setOrder(new \FluentSmtpLib\phpseclib3\Math\BigInteger('FFFFFFFFFFFFFFFFFFFFFFFE26F2FC170F69466A74DEFD8D', 16)); + $this->basis = []; + $this->basis[] = ['a' => new \FluentSmtpLib\phpseclib3\Math\BigInteger('00B3FB3400DEC5C4ADCEB8655C', -16), 'b' => new \FluentSmtpLib\phpseclib3\Math\BigInteger('8EE96418CCF4CFC7124FDA0F', -16)]; + $this->basis[] = ['a' => new \FluentSmtpLib\phpseclib3\Math\BigInteger('01D90D03E8F096B9948B20F0A9', -16), 'b' => new \FluentSmtpLib\phpseclib3\Math\BigInteger('42E49819ABBA9474E1083F6B', -16)]; + $this->beta = $this->factory->newInteger(new \FluentSmtpLib\phpseclib3\Math\BigInteger('447A96E6C647963E2F7809FEAAB46947F34B0AA3CA0BBA74', -16)); + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp192r1.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp192r1.php new file mode 100644 index 0000000..93d35e2 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp192r1.php @@ -0,0 +1,68 @@ + + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://pear.php.net/package/Math_BigInteger + */ +namespace FluentSmtpLib\phpseclib3\Crypt\EC\Curves; + +use FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\Prime; +use FluentSmtpLib\phpseclib3\Math\BigInteger; +class secp192r1 extends \FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\Prime +{ + public function __construct() + { + $modulo = new \FluentSmtpLib\phpseclib3\Math\BigInteger('FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFF', 16); + $this->setModulo($modulo); + // algorithm 2.27 from http://diamond.boisestate.edu/~liljanab/MATH308/GuideToECC.pdf#page=66 + /* in theory this should be faster than regular modular reductions save for one small issue. + to convert to / from base-2**8 with BCMath you have to call bcmul() and bcdiv() a lot. + to convert to / from base-2**8 with PHP64 you have to call base256_rshift() a lot. + in short, converting to / from base-2**8 is pretty expensive and that expense is + enough to offset whatever else might be gained by a simplified reduction algorithm. + now, if PHP supported unsigned integers things might be different. no bit-shifting + would be required for the PHP engine and it'd be a lot faster. but as is, BigInteger + uses base-2**31 or base-2**26 depending on whether or not the system is has a 32-bit + or a 64-bit OS. + */ + /* + $m_length = $this->getLengthInBytes(); + $this->setReduction(function($c) use ($m_length) { + $cBytes = $c->toBytes(); + $className = $this->className; + + if (strlen($cBytes) > 2 * $m_length) { + list(, $r) = $c->divide($className::$modulo); + return $r; + } + + $c = str_pad($cBytes, 48, "\0", STR_PAD_LEFT); + $c = array_reverse(str_split($c, 8)); + + $null = "\0\0\0\0\0\0\0\0"; + $s1 = new BigInteger($c[2] . $c[1] . $c[0], 256); + $s2 = new BigInteger($null . $c[3] . $c[3], 256); + $s3 = new BigInteger($c[4] . $c[4] . $null, 256); + $s4 = new BigInteger($c[5] . $c[5] . $c[5], 256); + + $r = $s1->add($s2)->add($s3)->add($s4); + while ($r->compare($className::$modulo) >= 0) { + $r = $r->subtract($className::$modulo); + } + + return $r; + }); + */ + $this->setCoefficients(new \FluentSmtpLib\phpseclib3\Math\BigInteger('FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFC', 16), new \FluentSmtpLib\phpseclib3\Math\BigInteger('64210519E59C80E70FA7E9AB72243049FEB8DEECC146B9B1', 16)); + $this->setBasePoint(new \FluentSmtpLib\phpseclib3\Math\BigInteger('188DA80EB03090F67CBF20EB43A18800F4FF0AFD82FF1012', 16), new \FluentSmtpLib\phpseclib3\Math\BigInteger('07192B95FFC8DA78631011ED6B24CDD573F977A11E794811', 16)); + $this->setOrder(new \FluentSmtpLib\phpseclib3\Math\BigInteger('FFFFFFFFFFFFFFFFFFFFFFFF99DEF836146BC9B1B4D22831', 16)); + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp224k1.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp224k1.php new file mode 100644 index 0000000..8085035 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp224k1.php @@ -0,0 +1,30 @@ + + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://pear.php.net/package/Math_BigInteger + */ +namespace FluentSmtpLib\phpseclib3\Crypt\EC\Curves; + +use FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\KoblitzPrime; +use FluentSmtpLib\phpseclib3\Math\BigInteger; +class secp224k1 extends \FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\KoblitzPrime +{ + public function __construct() + { + $this->setModulo(new \FluentSmtpLib\phpseclib3\Math\BigInteger('FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFE56D', 16)); + $this->setCoefficients(new \FluentSmtpLib\phpseclib3\Math\BigInteger('00000000000000000000000000000000000000000000000000000000', 16), new \FluentSmtpLib\phpseclib3\Math\BigInteger('00000000000000000000000000000000000000000000000000000005', 16)); + $this->setBasePoint(new \FluentSmtpLib\phpseclib3\Math\BigInteger('A1455B334DF099DF30FC28A169A467E9E47075A90F7E650EB6B7A45C', 16), new \FluentSmtpLib\phpseclib3\Math\BigInteger('7E089FED7FBA344282CAFBD6F7E319F7C0B0BD59E2CA4BDB556D61A5', 16)); + $this->setOrder(new \FluentSmtpLib\phpseclib3\Math\BigInteger('010000000000000000000000000001DCE8D2EC6184CAF0A971769FB1F7', 16)); + $this->basis = []; + $this->basis[] = ['a' => new \FluentSmtpLib\phpseclib3\Math\BigInteger('00B8ADF1378A6EB73409FA6C9C637D', -16), 'b' => new \FluentSmtpLib\phpseclib3\Math\BigInteger('94730F82B358A3776A826298FA6F', -16)]; + $this->basis[] = ['a' => new \FluentSmtpLib\phpseclib3\Math\BigInteger('01DCE8D2EC6184CAF0A972769FCC8B', -16), 'b' => new \FluentSmtpLib\phpseclib3\Math\BigInteger('4D2100BA3DC75AAB747CCF355DEC', -16)]; + $this->beta = $this->factory->newInteger(new \FluentSmtpLib\phpseclib3\Math\BigInteger('01F178FFA4B17C89E6F73AECE2AAD57AF4C0A748B63C830947B27E04', -16)); + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp224r1.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp224r1.php new file mode 100644 index 0000000..855dff0 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp224r1.php @@ -0,0 +1,26 @@ + + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://pear.php.net/package/Math_BigInteger + */ +namespace FluentSmtpLib\phpseclib3\Crypt\EC\Curves; + +use FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\Prime; +use FluentSmtpLib\phpseclib3\Math\BigInteger; +class secp224r1 extends \FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\Prime +{ + public function __construct() + { + $this->setModulo(new \FluentSmtpLib\phpseclib3\Math\BigInteger('FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000000000000001', 16)); + $this->setCoefficients(new \FluentSmtpLib\phpseclib3\Math\BigInteger('FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFE', 16), new \FluentSmtpLib\phpseclib3\Math\BigInteger('B4050A850C04B3ABF54132565044B0B7D7BFD8BA270B39432355FFB4', 16)); + $this->setBasePoint(new \FluentSmtpLib\phpseclib3\Math\BigInteger('B70E0CBD6BB4BF7F321390B94A03C1D356C21122343280D6115C1D21', 16), new \FluentSmtpLib\phpseclib3\Math\BigInteger('BD376388B5F723FB4C22DFE6CD4375A05A07476444D5819985007E34', 16)); + $this->setOrder(new \FluentSmtpLib\phpseclib3\Math\BigInteger('FFFFFFFFFFFFFFFFFFFFFFFFFFFF16A2E0B8F03E13DD29455C5C2A3D', 16)); + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp256k1.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp256k1.php new file mode 100644 index 0000000..90a557f --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp256k1.php @@ -0,0 +1,34 @@ + + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://pear.php.net/package/Math_BigInteger + */ +namespace FluentSmtpLib\phpseclib3\Crypt\EC\Curves; + +//use phpseclib3\Crypt\EC\BaseCurves\Prime; +use FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\KoblitzPrime; +use FluentSmtpLib\phpseclib3\Math\BigInteger; +//class secp256k1 extends Prime +class secp256k1 extends \FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\KoblitzPrime +{ + public function __construct() + { + $this->setModulo(new \FluentSmtpLib\phpseclib3\Math\BigInteger('FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F', 16)); + $this->setCoefficients(new \FluentSmtpLib\phpseclib3\Math\BigInteger('0000000000000000000000000000000000000000000000000000000000000000', 16), new \FluentSmtpLib\phpseclib3\Math\BigInteger('0000000000000000000000000000000000000000000000000000000000000007', 16)); + $this->setOrder(new \FluentSmtpLib\phpseclib3\Math\BigInteger('FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141', 16)); + $this->setBasePoint(new \FluentSmtpLib\phpseclib3\Math\BigInteger('79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798', 16), new \FluentSmtpLib\phpseclib3\Math\BigInteger('483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8', 16)); + $this->basis = []; + $this->basis[] = ['a' => new \FluentSmtpLib\phpseclib3\Math\BigInteger('3086D221A7D46BCDE86C90E49284EB15', -16), 'b' => new \FluentSmtpLib\phpseclib3\Math\BigInteger('FF1BBC8129FEF177D790AB8056F5401B3D', -16)]; + $this->basis[] = ['a' => new \FluentSmtpLib\phpseclib3\Math\BigInteger('114CA50F7A8E2F3F657C1108D9D44CFD8', -16), 'b' => new \FluentSmtpLib\phpseclib3\Math\BigInteger('3086D221A7D46BCDE86C90E49284EB15', -16)]; + $this->beta = $this->factory->newInteger(new \FluentSmtpLib\phpseclib3\Math\BigInteger('7AE96A2B657C07106E64479EAC3434E99CF0497512F58995C1396C28719501EE', -16)); + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp256r1.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp256r1.php new file mode 100644 index 0000000..79663bb --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp256r1.php @@ -0,0 +1,26 @@ + + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://pear.php.net/package/Math_BigInteger + */ +namespace FluentSmtpLib\phpseclib3\Crypt\EC\Curves; + +use FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\Prime; +use FluentSmtpLib\phpseclib3\Math\BigInteger; +class secp256r1 extends \FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\Prime +{ + public function __construct() + { + $this->setModulo(new \FluentSmtpLib\phpseclib3\Math\BigInteger('FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF', 16)); + $this->setCoefficients(new \FluentSmtpLib\phpseclib3\Math\BigInteger('FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC', 16), new \FluentSmtpLib\phpseclib3\Math\BigInteger('5AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B', 16)); + $this->setBasePoint(new \FluentSmtpLib\phpseclib3\Math\BigInteger('6B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296', 16), new \FluentSmtpLib\phpseclib3\Math\BigInteger('4FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5', 16)); + $this->setOrder(new \FluentSmtpLib\phpseclib3\Math\BigInteger('FFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551', 16)); + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp384r1.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp384r1.php new file mode 100644 index 0000000..5ac4d46 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp384r1.php @@ -0,0 +1,26 @@ + + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://pear.php.net/package/Math_BigInteger + */ +namespace FluentSmtpLib\phpseclib3\Crypt\EC\Curves; + +use FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\Prime; +use FluentSmtpLib\phpseclib3\Math\BigInteger; +class secp384r1 extends \FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\Prime +{ + public function __construct() + { + $this->setModulo(new \FluentSmtpLib\phpseclib3\Math\BigInteger('FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFF0000000000000000FFFFFFFF', 16)); + $this->setCoefficients(new \FluentSmtpLib\phpseclib3\Math\BigInteger('FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFF0000000000000000FFFFFFFC', 16), new \FluentSmtpLib\phpseclib3\Math\BigInteger('B3312FA7E23EE7E4988E056BE3F82D19181D9C6EFE8141120314088F5013875AC656398D8A2ED19D2A85C8EDD3EC2AEF', 16)); + $this->setBasePoint(new \FluentSmtpLib\phpseclib3\Math\BigInteger('AA87CA22BE8B05378EB1C71EF320AD746E1D3B628BA79B9859F741E082542A385502F25DBF55296C3A545E3872760AB7', 16), new \FluentSmtpLib\phpseclib3\Math\BigInteger('3617DE4A96262C6F5D9E98BF9292DC29F8F41DBD289A147CE9DA3113B5F0B8C00A60B1CE1D7E819D7A431D7C90EA0E5F', 16)); + $this->setOrder(new \FluentSmtpLib\phpseclib3\Math\BigInteger('FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC7634D81F4372DDF581A0DB248B0A77AECEC196ACCC52973', 16)); + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp521r1.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp521r1.php new file mode 100644 index 0000000..ac1917a --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp521r1.php @@ -0,0 +1,26 @@ + + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://pear.php.net/package/Math_BigInteger + */ +namespace FluentSmtpLib\phpseclib3\Crypt\EC\Curves; + +use FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\Prime; +use FluentSmtpLib\phpseclib3\Math\BigInteger; +class secp521r1 extends \FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\Prime +{ + public function __construct() + { + $this->setModulo(new \FluentSmtpLib\phpseclib3\Math\BigInteger('01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF' . 'FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF' . 'FFFF', 16)); + $this->setCoefficients(new \FluentSmtpLib\phpseclib3\Math\BigInteger('01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF' . 'FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF' . 'FFFC', 16), new \FluentSmtpLib\phpseclib3\Math\BigInteger('0051953EB9618E1C9A1F929A21A0B68540EEA2DA725B99B315F3B8B489918EF1' . '09E156193951EC7E937B1652C0BD3BB1BF073573DF883D2C34F1EF451FD46B50' . '3F00', 16)); + $this->setBasePoint(new \FluentSmtpLib\phpseclib3\Math\BigInteger('00C6858E06B70404E9CD9E3ECB662395B4429C648139053FB521F828AF606B4D' . '3DBAA14B5E77EFE75928FE1DC127A2FFA8DE3348B3C1856A429BF97E7E31C2E5' . 'BD66', 16), new \FluentSmtpLib\phpseclib3\Math\BigInteger('011839296A789A3BC0045C8A5FB42C7D1BD998F54449579B446817AFBD17273E' . '662C97EE72995EF42640C550B9013FAD0761353C7086A272C24088BE94769FD1' . '6650', 16)); + $this->setOrder(new \FluentSmtpLib\phpseclib3\Math\BigInteger('01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF' . 'FFFA51868783BF2F966B7FCC0148F709A5D03BB5C9B8899C47AEBB6FB71E9138' . '6409', 16)); + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect113r1.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect113r1.php new file mode 100644 index 0000000..101d552 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect113r1.php @@ -0,0 +1,26 @@ + + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://pear.php.net/package/Math_BigInteger + */ +namespace FluentSmtpLib\phpseclib3\Crypt\EC\Curves; + +use FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\Binary; +use FluentSmtpLib\phpseclib3\Math\BigInteger; +class sect113r1 extends \FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\Binary +{ + public function __construct() + { + $this->setModulo(113, 9, 0); + $this->setCoefficients('003088250CA6E7C7FE649CE85820F7', '00E8BEE4D3E2260744188BE0E9C723'); + $this->setBasePoint('009D73616F35F4AB1407D73562C10F', '00A52830277958EE84D1315ED31886'); + $this->setOrder(new \FluentSmtpLib\phpseclib3\Math\BigInteger('0100000000000000D9CCEC8A39E56F', 16)); + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect113r2.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect113r2.php new file mode 100644 index 0000000..6dbc611 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect113r2.php @@ -0,0 +1,26 @@ + + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://pear.php.net/package/Math_BigInteger + */ +namespace FluentSmtpLib\phpseclib3\Crypt\EC\Curves; + +use FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\Binary; +use FluentSmtpLib\phpseclib3\Math\BigInteger; +class sect113r2 extends \FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\Binary +{ + public function __construct() + { + $this->setModulo(113, 9, 0); + $this->setCoefficients('00689918DBEC7E5A0DD6DFC0AA55C7', '0095E9A9EC9B297BD4BF36E059184F'); + $this->setBasePoint('01A57A6A7B26CA5EF52FCDB8164797', '00B3ADC94ED1FE674C06E695BABA1D'); + $this->setOrder(new \FluentSmtpLib\phpseclib3\Math\BigInteger('010000000000000108789B2496AF93', 16)); + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect131r1.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect131r1.php new file mode 100644 index 0000000..b921833 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect131r1.php @@ -0,0 +1,26 @@ + + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://pear.php.net/package/Math_BigInteger + */ +namespace FluentSmtpLib\phpseclib3\Crypt\EC\Curves; + +use FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\Binary; +use FluentSmtpLib\phpseclib3\Math\BigInteger; +class sect131r1 extends \FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\Binary +{ + public function __construct() + { + $this->setModulo(131, 8, 3, 2, 0); + $this->setCoefficients('07A11B09A76B562144418FF3FF8C2570B8', '0217C05610884B63B9C6C7291678F9D341'); + $this->setBasePoint('0081BAF91FDF9833C40F9C181343638399', '078C6E7EA38C001F73C8134B1B4EF9E150'); + $this->setOrder(new \FluentSmtpLib\phpseclib3\Math\BigInteger('0400000000000000023123953A9464B54D', 16)); + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect131r2.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect131r2.php new file mode 100644 index 0000000..a469544 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect131r2.php @@ -0,0 +1,26 @@ + + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://pear.php.net/package/Math_BigInteger + */ +namespace FluentSmtpLib\phpseclib3\Crypt\EC\Curves; + +use FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\Binary; +use FluentSmtpLib\phpseclib3\Math\BigInteger; +class sect131r2 extends \FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\Binary +{ + public function __construct() + { + $this->setModulo(131, 8, 3, 2, 0); + $this->setCoefficients('03E5A88919D7CAFCBF415F07C2176573B2', '04B8266A46C55657AC734CE38F018F2192'); + $this->setBasePoint('0356DCD8F2F95031AD652D23951BB366A8', '0648F06D867940A5366D9E265DE9EB240F'); + $this->setOrder(new \FluentSmtpLib\phpseclib3\Math\BigInteger('0400000000000000016954A233049BA98F', 16)); + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect163k1.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect163k1.php new file mode 100644 index 0000000..17b4c96 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect163k1.php @@ -0,0 +1,26 @@ + + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://pear.php.net/package/Math_BigInteger + */ +namespace FluentSmtpLib\phpseclib3\Crypt\EC\Curves; + +use FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\Binary; +use FluentSmtpLib\phpseclib3\Math\BigInteger; +class sect163k1 extends \FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\Binary +{ + public function __construct() + { + $this->setModulo(163, 7, 6, 3, 0); + $this->setCoefficients('000000000000000000000000000000000000000001', '000000000000000000000000000000000000000001'); + $this->setBasePoint('02FE13C0537BBC11ACAA07D793DE4E6D5E5C94EEE8', '0289070FB05D38FF58321F2E800536D538CCDAA3D9'); + $this->setOrder(new \FluentSmtpLib\phpseclib3\Math\BigInteger('04000000000000000000020108A2E0CC0D99F8A5EF', 16)); + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect163r1.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect163r1.php new file mode 100644 index 0000000..9b614ec --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect163r1.php @@ -0,0 +1,26 @@ + + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://pear.php.net/package/Math_BigInteger + */ +namespace FluentSmtpLib\phpseclib3\Crypt\EC\Curves; + +use FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\Binary; +use FluentSmtpLib\phpseclib3\Math\BigInteger; +class sect163r1 extends \FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\Binary +{ + public function __construct() + { + $this->setModulo(163, 7, 6, 3, 0); + $this->setCoefficients('07B6882CAAEFA84F9554FF8428BD88E246D2782AE2', '0713612DCDDCB40AAB946BDA29CA91F73AF958AFD9'); + $this->setBasePoint('0369979697AB43897789566789567F787A7876A654', '00435EDB42EFAFB2989D51FEFCE3C80988F41FF883'); + $this->setOrder(new \FluentSmtpLib\phpseclib3\Math\BigInteger('03FFFFFFFFFFFFFFFFFFFF48AAB689C29CA710279B', 16)); + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect163r2.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect163r2.php new file mode 100644 index 0000000..03754d9 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect163r2.php @@ -0,0 +1,26 @@ + + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://pear.php.net/package/Math_BigInteger + */ +namespace FluentSmtpLib\phpseclib3\Crypt\EC\Curves; + +use FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\Binary; +use FluentSmtpLib\phpseclib3\Math\BigInteger; +class sect163r2 extends \FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\Binary +{ + public function __construct() + { + $this->setModulo(163, 7, 6, 3, 0); + $this->setCoefficients('000000000000000000000000000000000000000001', '020A601907B8C953CA1481EB10512F78744A3205FD'); + $this->setBasePoint('03F0EBA16286A2D57EA0991168D4994637E8343E36', '00D51FBC6C71A0094FA2CDD545B11C5C0C797324F1'); + $this->setOrder(new \FluentSmtpLib\phpseclib3\Math\BigInteger('040000000000000000000292FE77E70C12A4234C33', 16)); + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect193r1.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect193r1.php new file mode 100644 index 0000000..6625105 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect193r1.php @@ -0,0 +1,26 @@ + + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://pear.php.net/package/Math_BigInteger + */ +namespace FluentSmtpLib\phpseclib3\Crypt\EC\Curves; + +use FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\Binary; +use FluentSmtpLib\phpseclib3\Math\BigInteger; +class sect193r1 extends \FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\Binary +{ + public function __construct() + { + $this->setModulo(193, 15, 0); + $this->setCoefficients('0017858FEB7A98975169E171F77B4087DE098AC8A911DF7B01', '00FDFB49BFE6C3A89FACADAA7A1E5BBC7CC1C2E5D831478814'); + $this->setBasePoint('01F481BC5F0FF84A74AD6CDF6FDEF4BF6179625372D8C0C5E1', '0025E399F2903712CCF3EA9E3A1AD17FB0B3201B6AF7CE1B05'); + $this->setOrder(new \FluentSmtpLib\phpseclib3\Math\BigInteger('01000000000000000000000000C7F34A778F443ACC920EBA49', 16)); + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect193r2.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect193r2.php new file mode 100644 index 0000000..19afb80 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect193r2.php @@ -0,0 +1,26 @@ + + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://pear.php.net/package/Math_BigInteger + */ +namespace FluentSmtpLib\phpseclib3\Crypt\EC\Curves; + +use FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\Binary; +use FluentSmtpLib\phpseclib3\Math\BigInteger; +class sect193r2 extends \FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\Binary +{ + public function __construct() + { + $this->setModulo(193, 15, 0); + $this->setCoefficients('0163F35A5137C2CE3EA6ED8667190B0BC43ECD69977702709B', '00C9BB9E8927D4D64C377E2AB2856A5B16E3EFB7F61D4316AE'); + $this->setBasePoint('00D9B67D192E0367C803F39E1A7E82CA14A651350AAE617E8F', '01CE94335607C304AC29E7DEFBD9CA01F596F927224CDECF6C'); + $this->setOrder(new \FluentSmtpLib\phpseclib3\Math\BigInteger('010000000000000000000000015AAB561B005413CCD4EE99D5', 16)); + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect233k1.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect233k1.php new file mode 100644 index 0000000..242f4a4 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect233k1.php @@ -0,0 +1,26 @@ + + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://pear.php.net/package/Math_BigInteger + */ +namespace FluentSmtpLib\phpseclib3\Crypt\EC\Curves; + +use FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\Binary; +use FluentSmtpLib\phpseclib3\Math\BigInteger; +class sect233k1 extends \FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\Binary +{ + public function __construct() + { + $this->setModulo(233, 74, 0); + $this->setCoefficients('000000000000000000000000000000000000000000000000000000000000', '000000000000000000000000000000000000000000000000000000000001'); + $this->setBasePoint('017232BA853A7E731AF129F22FF4149563A419C26BF50A4C9D6EEFAD6126', '01DB537DECE819B7F70F555A67C427A8CD9BF18AEB9B56E0C11056FAE6A3'); + $this->setOrder(new \FluentSmtpLib\phpseclib3\Math\BigInteger('8000000000000000000000000000069D5BB915BCD46EFB1AD5F173ABDF', 16)); + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect233r1.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect233r1.php new file mode 100644 index 0000000..596998f --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect233r1.php @@ -0,0 +1,26 @@ + + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://pear.php.net/package/Math_BigInteger + */ +namespace FluentSmtpLib\phpseclib3\Crypt\EC\Curves; + +use FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\Binary; +use FluentSmtpLib\phpseclib3\Math\BigInteger; +class sect233r1 extends \FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\Binary +{ + public function __construct() + { + $this->setModulo(233, 74, 0); + $this->setCoefficients('000000000000000000000000000000000000000000000000000000000001', '0066647EDE6C332C7F8C0923BB58213B333B20E9CE4281FE115F7D8F90AD'); + $this->setBasePoint('00FAC9DFCBAC8313BB2139F1BB755FEF65BC391F8B36F8F8EB7371FD558B', '01006A08A41903350678E58528BEBF8A0BEFF867A7CA36716F7E01F81052'); + $this->setOrder(new \FluentSmtpLib\phpseclib3\Math\BigInteger('01000000000000000000000000000013E974E72F8A6922031D2603CFE0D7', 16)); + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect239k1.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect239k1.php new file mode 100644 index 0000000..d7e7967 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect239k1.php @@ -0,0 +1,26 @@ + + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://pear.php.net/package/Math_BigInteger + */ +namespace FluentSmtpLib\phpseclib3\Crypt\EC\Curves; + +use FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\Binary; +use FluentSmtpLib\phpseclib3\Math\BigInteger; +class sect239k1 extends \FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\Binary +{ + public function __construct() + { + $this->setModulo(239, 158, 0); + $this->setCoefficients('000000000000000000000000000000000000000000000000000000000000', '000000000000000000000000000000000000000000000000000000000001'); + $this->setBasePoint('29A0B6A887A983E9730988A68727A8B2D126C44CC2CC7B2A6555193035DC', '76310804F12E549BDB011C103089E73510ACB275FC312A5DC6B76553F0CA'); + $this->setOrder(new \FluentSmtpLib\phpseclib3\Math\BigInteger('2000000000000000000000000000005A79FEC67CB6E91F1C1DA800E478A5', 16)); + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect283k1.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect283k1.php new file mode 100644 index 0000000..abf18d4 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect283k1.php @@ -0,0 +1,26 @@ + + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://pear.php.net/package/Math_BigInteger + */ +namespace FluentSmtpLib\phpseclib3\Crypt\EC\Curves; + +use FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\Binary; +use FluentSmtpLib\phpseclib3\Math\BigInteger; +class sect283k1 extends \FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\Binary +{ + public function __construct() + { + $this->setModulo(283, 12, 7, 5, 0); + $this->setCoefficients('000000000000000000000000000000000000000000000000000000000000000000000000', '000000000000000000000000000000000000000000000000000000000000000000000001'); + $this->setBasePoint('0503213F78CA44883F1A3B8162F188E553CD265F23C1567A16876913B0C2AC2458492836', '01CCDA380F1C9E318D90F95D07E5426FE87E45C0E8184698E45962364E34116177DD2259'); + $this->setOrder(new \FluentSmtpLib\phpseclib3\Math\BigInteger('01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE9AE2ED07577265DFF7F94451E061E163C61', 16)); + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect283r1.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect283r1.php new file mode 100644 index 0000000..8dfe01c --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect283r1.php @@ -0,0 +1,26 @@ + + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://pear.php.net/package/Math_BigInteger + */ +namespace FluentSmtpLib\phpseclib3\Crypt\EC\Curves; + +use FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\Binary; +use FluentSmtpLib\phpseclib3\Math\BigInteger; +class sect283r1 extends \FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\Binary +{ + public function __construct() + { + $this->setModulo(283, 12, 7, 5, 0); + $this->setCoefficients('000000000000000000000000000000000000000000000000000000000000000000000001', '027B680AC8B8596DA5A4AF8A19A0303FCA97FD7645309FA2A581485AF6263E313B79A2F5'); + $this->setBasePoint('05F939258DB7DD90E1934F8C70B0DFEC2EED25B8557EAC9C80E2E198F8CDBECD86B12053', '03676854FE24141CB98FE6D4B20D02B4516FF702350EDDB0826779C813F0DF45BE8112F4'); + $this->setOrder(new \FluentSmtpLib\phpseclib3\Math\BigInteger('03FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEF90399660FC938A90165B042A7CEFADB307', 16)); + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect409k1.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect409k1.php new file mode 100644 index 0000000..6aff7cc --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect409k1.php @@ -0,0 +1,26 @@ + + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://pear.php.net/package/Math_BigInteger + */ +namespace FluentSmtpLib\phpseclib3\Crypt\EC\Curves; + +use FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\Binary; +use FluentSmtpLib\phpseclib3\Math\BigInteger; +class sect409k1 extends \FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\Binary +{ + public function __construct() + { + $this->setModulo(409, 87, 0); + $this->setCoefficients('00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000', '00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001'); + $this->setBasePoint('0060F05F658F49C1AD3AB1890F7184210EFD0987E307C84C27ACCFB8F9F67CC2C460189EB5AAAA62EE222EB1B35540CFE9023746', '01E369050B7C4E42ACBA1DACBF04299C3460782F918EA427E6325165E9EA10E3DA5F6C42E9C55215AA9CA27A5863EC48D8E0286B'); + $this->setOrder(new \FluentSmtpLib\phpseclib3\Math\BigInteger('7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE5F' . '83B2D4EA20400EC4557D5ED3E3E7CA5B4B5C83B8E01E5FCF', 16)); + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect409r1.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect409r1.php new file mode 100644 index 0000000..c7dc158 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect409r1.php @@ -0,0 +1,26 @@ + + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://pear.php.net/package/Math_BigInteger + */ +namespace FluentSmtpLib\phpseclib3\Crypt\EC\Curves; + +use FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\Binary; +use FluentSmtpLib\phpseclib3\Math\BigInteger; +class sect409r1 extends \FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\Binary +{ + public function __construct() + { + $this->setModulo(409, 87, 0); + $this->setCoefficients('00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001', '0021A5C2C8EE9FEB5C4B9A753B7B476B7FD6422EF1F3DD674761FA99D6AC27C8A9A197B272822F6CD57A55AA4F50AE317B13545F'); + $this->setBasePoint('015D4860D088DDB3496B0C6064756260441CDE4AF1771D4DB01FFE5B34E59703DC255A868A1180515603AEAB60794E54BB7996A7', '0061B1CFAB6BE5F32BBFA78324ED106A7636B9C5A7BD198D0158AA4F5488D08F38514F1FDF4B4F40D2181B3681C364BA0273C706'); + $this->setOrder(new \FluentSmtpLib\phpseclib3\Math\BigInteger('010000000000000000000000000000000000000000000000000001E2' . 'AAD6A612F33307BE5FA47C3C9E052F838164CD37D9A21173', 16)); + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect571k1.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect571k1.php new file mode 100644 index 0000000..173c6cd --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect571k1.php @@ -0,0 +1,26 @@ + + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://pear.php.net/package/Math_BigInteger + */ +namespace FluentSmtpLib\phpseclib3\Crypt\EC\Curves; + +use FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\Binary; +use FluentSmtpLib\phpseclib3\Math\BigInteger; +class sect571k1 extends \FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\Binary +{ + public function __construct() + { + $this->setModulo(571, 10, 5, 2, 0); + $this->setCoefficients('000000000000000000000000000000000000000000000000000000000000000000000000' . '000000000000000000000000000000000000000000000000000000000000000000000000', '000000000000000000000000000000000000000000000000000000000000000000000000' . '000000000000000000000000000000000000000000000000000000000000000000000001'); + $this->setBasePoint('026EB7A859923FBC82189631F8103FE4AC9CA2970012D5D46024804801841CA443709584' . '93B205E647DA304DB4CEB08CBBD1BA39494776FB988B47174DCA88C7E2945283A01C8972', '0349DC807F4FBF374F4AEADE3BCA95314DD58CEC9F307A54FFC61EFC006D8A2C9D4979C0' . 'AC44AEA74FBEBBB9F772AEDCB620B01A7BA7AF1B320430C8591984F601CD4C143EF1C7A3'); + $this->setOrder(new \FluentSmtpLib\phpseclib3\Math\BigInteger('020000000000000000000000000000000000000000000000000000000000000000000000' . '131850E1F19A63E4B391A8DB917F4138B630D84BE5D639381E91DEB45CFE778F637C1001', 16)); + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect571r1.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect571r1.php new file mode 100644 index 0000000..571951b --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect571r1.php @@ -0,0 +1,26 @@ + + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://pear.php.net/package/Math_BigInteger + */ +namespace FluentSmtpLib\phpseclib3\Crypt\EC\Curves; + +use FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\Binary; +use FluentSmtpLib\phpseclib3\Math\BigInteger; +class sect571r1 extends \FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\Binary +{ + public function __construct() + { + $this->setModulo(571, 10, 5, 2, 0); + $this->setCoefficients('000000000000000000000000000000000000000000000000000000000000000000000000' . '000000000000000000000000000000000000000000000000000000000000000000000001', '02F40E7E2221F295DE297117B7F3D62F5C6A97FFCB8CEFF1CD6BA8CE4A9A18AD84FFABBD' . '8EFA59332BE7AD6756A66E294AFD185A78FF12AA520E4DE739BACA0C7FFEFF7F2955727A'); + $this->setBasePoint('0303001D34B856296C16C0D40D3CD7750A93D1D2955FA80AA5F40FC8DB7B2ABDBDE53950' . 'F4C0D293CDD711A35B67FB1499AE60038614F1394ABFA3B4C850D927E1E7769C8EEC2D19', '037BF27342DA639B6DCCFFFEB73D69D78C6C27A6009CBBCA1980F8533921E8A684423E43' . 'BAB08A576291AF8F461BB2A8B3531D2F0485C19B16E2F1516E23DD3C1A4827AF1B8AC15B'); + $this->setOrder(new \FluentSmtpLib\phpseclib3\Math\BigInteger('03FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF' . 'E661CE18FF55987308059B186823851EC7DD9CA1161DE93D5174D66E8382E9BB2FE84E47', 16)); + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Keys/Common.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Keys/Common.php new file mode 100644 index 0000000..e2c18f0 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Keys/Common.php @@ -0,0 +1,489 @@ + + * @copyright 2015 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\Crypt\EC\Formats\Keys; + +use FluentSmtpLib\phpseclib3\Common\Functions\Strings; +use FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\Base as BaseCurve; +use FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\Binary as BinaryCurve; +use FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\Prime as PrimeCurve; +use FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\TwistedEdwards as TwistedEdwardsCurve; +use FluentSmtpLib\phpseclib3\Exception\UnsupportedCurveException; +use FluentSmtpLib\phpseclib3\File\ASN1; +use FluentSmtpLib\phpseclib3\File\ASN1\Maps; +use FluentSmtpLib\phpseclib3\Math\BigInteger; +/** + * Generic EC Key Parsing Helper functions + * + * @author Jim Wigginton + */ +trait Common +{ + /** + * Curve OIDs + * + * @var array + */ + private static $curveOIDs = []; + /** + * Child OIDs loaded + * + * @var bool + */ + protected static $childOIDsLoaded = \false; + /** + * Use Named Curves + * + * @var bool + */ + private static $useNamedCurves = \true; + /** + * Initialize static variables + */ + private static function initialize_static_variables() + { + if (empty(self::$curveOIDs)) { + // the sec* curves are from the standards for efficient cryptography group + // sect* curves are curves over binary finite fields + // secp* curves are curves over prime finite fields + // sec*r* curves are regular curves; sec*k* curves are koblitz curves + // brainpool*r* curves are regular prime finite field curves + // brainpool*t* curves are twisted versions of the brainpool*r* curves + self::$curveOIDs = [ + 'prime192v1' => '1.2.840.10045.3.1.1', + // J.5.1, example 1 (aka secp192r1) + 'prime192v2' => '1.2.840.10045.3.1.2', + // J.5.1, example 2 + 'prime192v3' => '1.2.840.10045.3.1.3', + // J.5.1, example 3 + 'prime239v1' => '1.2.840.10045.3.1.4', + // J.5.2, example 1 + 'prime239v2' => '1.2.840.10045.3.1.5', + // J.5.2, example 2 + 'prime239v3' => '1.2.840.10045.3.1.6', + // J.5.2, example 3 + 'prime256v1' => '1.2.840.10045.3.1.7', + // J.5.3, example 1 (aka secp256r1) + // https://tools.ietf.org/html/rfc5656#section-10 + 'nistp256' => '1.2.840.10045.3.1.7', + // aka secp256r1 + 'nistp384' => '1.3.132.0.34', + // aka secp384r1 + 'nistp521' => '1.3.132.0.35', + // aka secp521r1 + 'nistk163' => '1.3.132.0.1', + // aka sect163k1 + 'nistp192' => '1.2.840.10045.3.1.1', + // aka secp192r1 + 'nistp224' => '1.3.132.0.33', + // aka secp224r1 + 'nistk233' => '1.3.132.0.26', + // aka sect233k1 + 'nistb233' => '1.3.132.0.27', + // aka sect233r1 + 'nistk283' => '1.3.132.0.16', + // aka sect283k1 + 'nistk409' => '1.3.132.0.36', + // aka sect409k1 + 'nistb409' => '1.3.132.0.37', + // aka sect409r1 + 'nistt571' => '1.3.132.0.38', + // aka sect571k1 + // from https://tools.ietf.org/html/rfc5915 + 'secp192r1' => '1.2.840.10045.3.1.1', + // aka prime192v1 + 'sect163k1' => '1.3.132.0.1', + 'sect163r2' => '1.3.132.0.15', + 'secp224r1' => '1.3.132.0.33', + 'sect233k1' => '1.3.132.0.26', + 'sect233r1' => '1.3.132.0.27', + 'secp256r1' => '1.2.840.10045.3.1.7', + // aka prime256v1 + 'sect283k1' => '1.3.132.0.16', + 'sect283r1' => '1.3.132.0.17', + 'secp384r1' => '1.3.132.0.34', + 'sect409k1' => '1.3.132.0.36', + 'sect409r1' => '1.3.132.0.37', + 'secp521r1' => '1.3.132.0.35', + 'sect571k1' => '1.3.132.0.38', + 'sect571r1' => '1.3.132.0.39', + // from http://www.secg.org/SEC2-Ver-1.0.pdf + 'secp112r1' => '1.3.132.0.6', + 'secp112r2' => '1.3.132.0.7', + 'secp128r1' => '1.3.132.0.28', + 'secp128r2' => '1.3.132.0.29', + 'secp160k1' => '1.3.132.0.9', + 'secp160r1' => '1.3.132.0.8', + 'secp160r2' => '1.3.132.0.30', + 'secp192k1' => '1.3.132.0.31', + 'secp224k1' => '1.3.132.0.32', + 'secp256k1' => '1.3.132.0.10', + 'sect113r1' => '1.3.132.0.4', + 'sect113r2' => '1.3.132.0.5', + 'sect131r1' => '1.3.132.0.22', + 'sect131r2' => '1.3.132.0.23', + 'sect163r1' => '1.3.132.0.2', + 'sect193r1' => '1.3.132.0.24', + 'sect193r2' => '1.3.132.0.25', + 'sect239k1' => '1.3.132.0.3', + // from http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.202.2977&rep=rep1&type=pdf#page=36 + /* + 'c2pnb163v1' => '1.2.840.10045.3.0.1', // J.4.1, example 1 + 'c2pnb163v2' => '1.2.840.10045.3.0.2', // J.4.1, example 2 + 'c2pnb163v3' => '1.2.840.10045.3.0.3', // J.4.1, example 3 + 'c2pnb172w1' => '1.2.840.10045.3.0.4', // J.4.2, example 1 + 'c2tnb191v1' => '1.2.840.10045.3.0.5', // J.4.3, example 1 + 'c2tnb191v2' => '1.2.840.10045.3.0.6', // J.4.3, example 2 + 'c2tnb191v3' => '1.2.840.10045.3.0.7', // J.4.3, example 3 + 'c2onb191v4' => '1.2.840.10045.3.0.8', // J.4.3, example 4 + 'c2onb191v5' => '1.2.840.10045.3.0.9', // J.4.3, example 5 + 'c2pnb208w1' => '1.2.840.10045.3.0.10', // J.4.4, example 1 + 'c2tnb239v1' => '1.2.840.10045.3.0.11', // J.4.5, example 1 + 'c2tnb239v2' => '1.2.840.10045.3.0.12', // J.4.5, example 2 + 'c2tnb239v3' => '1.2.840.10045.3.0.13', // J.4.5, example 3 + 'c2onb239v4' => '1.2.840.10045.3.0.14', // J.4.5, example 4 + 'c2onb239v5' => '1.2.840.10045.3.0.15', // J.4.5, example 5 + 'c2pnb272w1' => '1.2.840.10045.3.0.16', // J.4.6, example 1 + 'c2pnb304w1' => '1.2.840.10045.3.0.17', // J.4.7, example 1 + 'c2tnb359v1' => '1.2.840.10045.3.0.18', // J.4.8, example 1 + 'c2pnb368w1' => '1.2.840.10045.3.0.19', // J.4.9, example 1 + 'c2tnb431r1' => '1.2.840.10045.3.0.20', // J.4.10, example 1 + */ + // http://www.ecc-brainpool.org/download/Domain-parameters.pdf + // https://tools.ietf.org/html/rfc5639 + 'brainpoolP160r1' => '1.3.36.3.3.2.8.1.1.1', + 'brainpoolP160t1' => '1.3.36.3.3.2.8.1.1.2', + 'brainpoolP192r1' => '1.3.36.3.3.2.8.1.1.3', + 'brainpoolP192t1' => '1.3.36.3.3.2.8.1.1.4', + 'brainpoolP224r1' => '1.3.36.3.3.2.8.1.1.5', + 'brainpoolP224t1' => '1.3.36.3.3.2.8.1.1.6', + 'brainpoolP256r1' => '1.3.36.3.3.2.8.1.1.7', + 'brainpoolP256t1' => '1.3.36.3.3.2.8.1.1.8', + 'brainpoolP320r1' => '1.3.36.3.3.2.8.1.1.9', + 'brainpoolP320t1' => '1.3.36.3.3.2.8.1.1.10', + 'brainpoolP384r1' => '1.3.36.3.3.2.8.1.1.11', + 'brainpoolP384t1' => '1.3.36.3.3.2.8.1.1.12', + 'brainpoolP512r1' => '1.3.36.3.3.2.8.1.1.13', + 'brainpoolP512t1' => '1.3.36.3.3.2.8.1.1.14', + ]; + \FluentSmtpLib\phpseclib3\File\ASN1::loadOIDs([ + 'prime-field' => '1.2.840.10045.1.1', + 'characteristic-two-field' => '1.2.840.10045.1.2', + 'characteristic-two-basis' => '1.2.840.10045.1.2.3', + // per http://www.secg.org/SEC1-Ver-1.0.pdf#page=84, gnBasis "not used here" + 'gnBasis' => '1.2.840.10045.1.2.3.1', + // NULL + 'tpBasis' => '1.2.840.10045.1.2.3.2', + // Trinomial + 'ppBasis' => '1.2.840.10045.1.2.3.3', + ] + self::$curveOIDs); + } + } + /** + * Explicitly set the curve + * + * If the key contains an implicit curve phpseclib needs the curve + * to be explicitly provided + * + * @param BaseCurve $curve + */ + public static function setImplicitCurve(\FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\Base $curve) + { + self::$implicitCurve = $curve; + } + /** + * Returns an instance of \phpseclib3\Crypt\EC\BaseCurves\Base based + * on the curve parameters + * + * @param array $params + * @return BaseCurve|false + */ + protected static function loadCurveByParam(array $params) + { + if (\count($params) > 1) { + throw new \RuntimeException('No parameters are present'); + } + if (isset($params['namedCurve'])) { + $curve = '\\FluentSmtpLib\\phpseclib3\\Crypt\\EC\\Curves\\' . $params['namedCurve']; + if (!\class_exists($curve)) { + throw new \FluentSmtpLib\phpseclib3\Exception\UnsupportedCurveException('Named Curve of ' . $params['namedCurve'] . ' is not supported'); + } + return new $curve(); + } + if (isset($params['implicitCurve'])) { + if (!isset(self::$implicitCurve)) { + throw new \RuntimeException('Implicit curves can be provided by calling setImplicitCurve'); + } + return self::$implicitCurve; + } + if (isset($params['specifiedCurve'])) { + $data = $params['specifiedCurve']; + switch ($data['fieldID']['fieldType']) { + case 'prime-field': + $curve = new \FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\Prime(); + $curve->setModulo($data['fieldID']['parameters']); + $curve->setCoefficients(new \FluentSmtpLib\phpseclib3\Math\BigInteger($data['curve']['a'], 256), new \FluentSmtpLib\phpseclib3\Math\BigInteger($data['curve']['b'], 256)); + $point = self::extractPoint("\x00" . $data['base'], $curve); + $curve->setBasePoint(...$point); + $curve->setOrder($data['order']); + return $curve; + case 'characteristic-two-field': + $curve = new \FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\Binary(); + $params = \FluentSmtpLib\phpseclib3\File\ASN1::decodeBER($data['fieldID']['parameters']); + $params = \FluentSmtpLib\phpseclib3\File\ASN1::asn1map($params[0], \FluentSmtpLib\phpseclib3\File\ASN1\Maps\Characteristic_two::MAP); + $modulo = [(int) $params['m']->toString()]; + switch ($params['basis']) { + case 'tpBasis': + $modulo[] = (int) $params['parameters']->toString(); + break; + case 'ppBasis': + $temp = \FluentSmtpLib\phpseclib3\File\ASN1::decodeBER($params['parameters']); + $temp = \FluentSmtpLib\phpseclib3\File\ASN1::asn1map($temp[0], \FluentSmtpLib\phpseclib3\File\ASN1\Maps\Pentanomial::MAP); + $modulo[] = (int) $temp['k3']->toString(); + $modulo[] = (int) $temp['k2']->toString(); + $modulo[] = (int) $temp['k1']->toString(); + } + $modulo[] = 0; + $curve->setModulo(...$modulo); + $len = \ceil($modulo[0] / 8); + $curve->setCoefficients(\FluentSmtpLib\phpseclib3\Common\Functions\Strings::bin2hex($data['curve']['a']), \FluentSmtpLib\phpseclib3\Common\Functions\Strings::bin2hex($data['curve']['b'])); + $point = self::extractPoint("\x00" . $data['base'], $curve); + $curve->setBasePoint(...$point); + $curve->setOrder($data['order']); + return $curve; + default: + throw new \FluentSmtpLib\phpseclib3\Exception\UnsupportedCurveException('Field Type of ' . $data['fieldID']['fieldType'] . ' is not supported'); + } + } + throw new \RuntimeException('No valid parameters are present'); + } + /** + * Extract points from a string + * + * Supports both compressed and uncompressed points + * + * @param string $str + * @param BaseCurve $curve + * @return object[] + */ + public static function extractPoint($str, \FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\Base $curve) + { + if ($curve instanceof \FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\TwistedEdwards) { + // first step of point deciding as discussed at the following URL's: + // https://tools.ietf.org/html/rfc8032#section-5.1.3 + // https://tools.ietf.org/html/rfc8032#section-5.2.3 + $y = $str; + $y = \strrev($y); + $sign = (bool) (\ord($y[0]) & 0x80); + $y[0] = $y[0] & \chr(0x7f); + $y = new \FluentSmtpLib\phpseclib3\Math\BigInteger($y, 256); + if ($y->compare($curve->getModulo()) >= 0) { + throw new \RuntimeException('The Y coordinate should not be >= the modulo'); + } + $point = $curve->recoverX($y, $sign); + if (!$curve->verifyPoint($point)) { + throw new \RuntimeException('Unable to verify that point exists on curve'); + } + return $point; + } + // the first byte of a bit string represents the number of bits in the last byte that are to be ignored but, + // currently, bit strings wanting a non-zero amount of bits trimmed are not supported + if (($val = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::shift($str)) != "\x00") { + throw new \UnexpectedValueException('extractPoint expects the first byte to be null - not ' . \FluentSmtpLib\phpseclib3\Common\Functions\Strings::bin2hex($val)); + } + if ($str == "\x00") { + return []; + } + $keylen = \strlen($str); + $order = $curve->getLengthInBytes(); + // point compression is being used + if ($keylen == $order + 1) { + return $curve->derivePoint($str); + } + // point compression is not being used + if ($keylen == 2 * $order + 1) { + \preg_match("#(.)(.{{$order}})(.{{$order}})#s", $str, $matches); + list(, $w, $x, $y) = $matches; + if ($w != "\x04") { + throw new \UnexpectedValueException('The first byte of an uncompressed point should be 04 - not ' . \FluentSmtpLib\phpseclib3\Common\Functions\Strings::bin2hex($val)); + } + $point = [$curve->convertInteger(new \FluentSmtpLib\phpseclib3\Math\BigInteger($x, 256)), $curve->convertInteger(new \FluentSmtpLib\phpseclib3\Math\BigInteger($y, 256))]; + if (!$curve->verifyPoint($point)) { + throw new \RuntimeException('Unable to verify that point exists on curve'); + } + return $point; + } + throw new \UnexpectedValueException('The string representation of the points is not of an appropriate length'); + } + /** + * Encode Parameters + * + * @todo Maybe at some point this could be moved to __toString() for each of the curves? + * @param BaseCurve $curve + * @param bool $returnArray optional + * @param array $options optional + * @return string|false + */ + private static function encodeParameters(\FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\Base $curve, $returnArray = \false, array $options = []) + { + $useNamedCurves = isset($options['namedCurve']) ? $options['namedCurve'] : self::$useNamedCurves; + $reflect = new \ReflectionClass($curve); + $name = $reflect->getShortName(); + if ($useNamedCurves) { + if (isset(self::$curveOIDs[$name])) { + if ($reflect->isFinal()) { + $reflect = $reflect->getParentClass(); + $name = $reflect->getShortName(); + } + return $returnArray ? ['namedCurve' => $name] : \FluentSmtpLib\phpseclib3\File\ASN1::encodeDER(['namedCurve' => $name], \FluentSmtpLib\phpseclib3\File\ASN1\Maps\ECParameters::MAP); + } + foreach (new \DirectoryIterator(__DIR__ . '/../../Curves/') as $file) { + if ($file->getExtension() != 'php') { + continue; + } + $testName = $file->getBasename('.php'); + $class = '\\FluentSmtpLib\\phpseclib3\\Crypt\\EC\\Curves\\' . $testName; + $reflect = new \ReflectionClass($class); + if ($reflect->isFinal()) { + continue; + } + $candidate = new $class(); + switch ($name) { + case 'Prime': + if (!$candidate instanceof \FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\Prime) { + break; + } + if (!$candidate->getModulo()->equals($curve->getModulo())) { + break; + } + if ($candidate->getA()->toBytes() != $curve->getA()->toBytes()) { + break; + } + if ($candidate->getB()->toBytes() != $curve->getB()->toBytes()) { + break; + } + list($candidateX, $candidateY) = $candidate->getBasePoint(); + list($curveX, $curveY) = $curve->getBasePoint(); + if ($candidateX->toBytes() != $curveX->toBytes()) { + break; + } + if ($candidateY->toBytes() != $curveY->toBytes()) { + break; + } + return $returnArray ? ['namedCurve' => $testName] : \FluentSmtpLib\phpseclib3\File\ASN1::encodeDER(['namedCurve' => $testName], \FluentSmtpLib\phpseclib3\File\ASN1\Maps\ECParameters::MAP); + case 'Binary': + if (!$candidate instanceof \FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\Binary) { + break; + } + if ($candidate->getModulo() != $curve->getModulo()) { + break; + } + if ($candidate->getA()->toBytes() != $curve->getA()->toBytes()) { + break; + } + if ($candidate->getB()->toBytes() != $curve->getB()->toBytes()) { + break; + } + list($candidateX, $candidateY) = $candidate->getBasePoint(); + list($curveX, $curveY) = $curve->getBasePoint(); + if ($candidateX->toBytes() != $curveX->toBytes()) { + break; + } + if ($candidateY->toBytes() != $curveY->toBytes()) { + break; + } + return $returnArray ? ['namedCurve' => $testName] : \FluentSmtpLib\phpseclib3\File\ASN1::encodeDER(['namedCurve' => $testName], \FluentSmtpLib\phpseclib3\File\ASN1\Maps\ECParameters::MAP); + } + } + } + $order = $curve->getOrder(); + // we could try to calculate the order thusly: + // https://crypto.stackexchange.com/a/27914/4520 + // https://en.wikipedia.org/wiki/Schoof%E2%80%93Elkies%E2%80%93Atkin_algorithm + if (!$order) { + throw new \RuntimeException('Specified Curves need the order to be specified'); + } + $point = $curve->getBasePoint(); + $x = $point[0]->toBytes(); + $y = $point[1]->toBytes(); + if ($curve instanceof \FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\Prime) { + /* + * valid versions are: + * + * ecdpVer1: + * - neither the curve or the base point are generated verifiably randomly. + * ecdpVer2: + * - curve and base point are generated verifiably at random and curve.seed is present + * ecdpVer3: + * - base point is generated verifiably at random but curve is not. curve.seed is present + */ + // other (optional) parameters can be calculated using the methods discused at + // https://crypto.stackexchange.com/q/28947/4520 + $data = ['version' => 'ecdpVer1', 'fieldID' => ['fieldType' => 'prime-field', 'parameters' => $curve->getModulo()], 'curve' => ['a' => $curve->getA()->toBytes(), 'b' => $curve->getB()->toBytes()], 'base' => "\x04" . $x . $y, 'order' => $order]; + return $returnArray ? ['specifiedCurve' => $data] : \FluentSmtpLib\phpseclib3\File\ASN1::encodeDER(['specifiedCurve' => $data], \FluentSmtpLib\phpseclib3\File\ASN1\Maps\ECParameters::MAP); + } + if ($curve instanceof \FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\Binary) { + $modulo = $curve->getModulo(); + $basis = \count($modulo); + $m = \array_shift($modulo); + \array_pop($modulo); + // the last parameter should always be 0 + //rsort($modulo); + switch ($basis) { + case 3: + $basis = 'tpBasis'; + $modulo = new \FluentSmtpLib\phpseclib3\Math\BigInteger($modulo[0]); + break; + case 5: + $basis = 'ppBasis'; + // these should be in strictly ascending order (hence the commented out rsort above) + $modulo = ['k1' => new \FluentSmtpLib\phpseclib3\Math\BigInteger($modulo[2]), 'k2' => new \FluentSmtpLib\phpseclib3\Math\BigInteger($modulo[1]), 'k3' => new \FluentSmtpLib\phpseclib3\Math\BigInteger($modulo[0])]; + $modulo = \FluentSmtpLib\phpseclib3\File\ASN1::encodeDER($modulo, \FluentSmtpLib\phpseclib3\File\ASN1\Maps\Pentanomial::MAP); + $modulo = new \FluentSmtpLib\phpseclib3\File\ASN1\Element($modulo); + } + $params = \FluentSmtpLib\phpseclib3\File\ASN1::encodeDER(['m' => new \FluentSmtpLib\phpseclib3\Math\BigInteger($m), 'basis' => $basis, 'parameters' => $modulo], \FluentSmtpLib\phpseclib3\File\ASN1\Maps\Characteristic_two::MAP); + $params = new \FluentSmtpLib\phpseclib3\File\ASN1\Element($params); + $a = \ltrim($curve->getA()->toBytes(), "\x00"); + if (!\strlen($a)) { + $a = "\x00"; + } + $b = \ltrim($curve->getB()->toBytes(), "\x00"); + if (!\strlen($b)) { + $b = "\x00"; + } + $data = ['version' => 'ecdpVer1', 'fieldID' => ['fieldType' => 'characteristic-two-field', 'parameters' => $params], 'curve' => ['a' => $a, 'b' => $b], 'base' => "\x04" . $x . $y, 'order' => $order]; + return $returnArray ? ['specifiedCurve' => $data] : \FluentSmtpLib\phpseclib3\File\ASN1::encodeDER(['specifiedCurve' => $data], \FluentSmtpLib\phpseclib3\File\ASN1\Maps\ECParameters::MAP); + } + throw new \FluentSmtpLib\phpseclib3\Exception\UnsupportedCurveException('Curve cannot be serialized'); + } + /** + * Use Specified Curve + * + * A specified curve has all the coefficients, the base points, etc, explicitely included. + * A specified curve is a more verbose way of representing a curve + */ + public static function useSpecifiedCurve() + { + self::$useNamedCurves = \false; + } + /** + * Use Named Curve + * + * A named curve does not include any parameters. It is up to the EC parameters to + * know what the coefficients, the base points, etc, are from the name of the curve. + * A named curve is a more concise way of representing a curve + */ + public static function useNamedCurve() + { + self::$useNamedCurves = \true; + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Keys/JWK.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Keys/JWK.php new file mode 100644 index 0000000..94745dd --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Keys/JWK.php @@ -0,0 +1,155 @@ + + * @copyright 2015 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\Crypt\EC\Formats\Keys; + +use FluentSmtpLib\phpseclib3\Common\Functions\Strings; +use FluentSmtpLib\phpseclib3\Crypt\Common\Formats\Keys\JWK as Progenitor; +use FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\Base as BaseCurve; +use FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\TwistedEdwards as TwistedEdwardsCurve; +use FluentSmtpLib\phpseclib3\Crypt\EC\Curves\Ed25519; +use FluentSmtpLib\phpseclib3\Crypt\EC\Curves\secp256k1; +use FluentSmtpLib\phpseclib3\Crypt\EC\Curves\secp256r1; +use FluentSmtpLib\phpseclib3\Crypt\EC\Curves\secp384r1; +use FluentSmtpLib\phpseclib3\Crypt\EC\Curves\secp521r1; +use FluentSmtpLib\phpseclib3\Exception\UnsupportedCurveException; +use FluentSmtpLib\phpseclib3\Math\BigInteger; +/** + * JWK Formatted EC Handler + * + * @author Jim Wigginton + */ +abstract class JWK extends \FluentSmtpLib\phpseclib3\Crypt\Common\Formats\Keys\JWK +{ + use Common; + /** + * Break a public or private key down into its constituent components + * + * @param string $key + * @param string $password optional + * @return array + */ + public static function load($key, $password = '') + { + $key = parent::load($key, $password); + switch ($key->kty) { + case 'EC': + switch ($key->crv) { + case 'P-256': + case 'P-384': + case 'P-521': + case 'secp256k1': + break; + default: + throw new \FluentSmtpLib\phpseclib3\Exception\UnsupportedCurveException('Only P-256, P-384, P-521 and secp256k1 curves are accepted (' . $key->crv . ' provided)'); + } + break; + case 'OKP': + switch ($key->crv) { + case 'Ed25519': + case 'Ed448': + break; + default: + throw new \FluentSmtpLib\phpseclib3\Exception\UnsupportedCurveException('Only Ed25519 and Ed448 curves are accepted (' . $key->crv . ' provided)'); + } + break; + default: + throw new \Exception('Only EC and OKP JWK keys are supported'); + } + $curve = '\\FluentSmtpLib\\phpseclib3\\Crypt\\EC\\Curves\\' . \str_replace('P-', 'nistp', $key->crv); + $curve = new $curve(); + if ($curve instanceof \FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\TwistedEdwards) { + $QA = self::extractPoint(\FluentSmtpLib\phpseclib3\Common\Functions\Strings::base64url_decode($key->x), $curve); + if (!isset($key->d)) { + return \compact('curve', 'QA'); + } + $arr = $curve->extractSecret(\FluentSmtpLib\phpseclib3\Common\Functions\Strings::base64url_decode($key->d)); + return \compact('curve', 'QA') + $arr; + } + $QA = [$curve->convertInteger(new \FluentSmtpLib\phpseclib3\Math\BigInteger(\FluentSmtpLib\phpseclib3\Common\Functions\Strings::base64url_decode($key->x), 256)), $curve->convertInteger(new \FluentSmtpLib\phpseclib3\Math\BigInteger(\FluentSmtpLib\phpseclib3\Common\Functions\Strings::base64url_decode($key->y), 256))]; + if (!$curve->verifyPoint($QA)) { + throw new \RuntimeException('Unable to verify that point exists on curve'); + } + if (!isset($key->d)) { + return \compact('curve', 'QA'); + } + $dA = new \FluentSmtpLib\phpseclib3\Math\BigInteger(\FluentSmtpLib\phpseclib3\Common\Functions\Strings::base64url_decode($key->d), 256); + $curve->rangeCheck($dA); + return \compact('curve', 'dA', 'QA'); + } + /** + * Returns the alias that corresponds to a curve + * + * @return string + */ + private static function getAlias(\FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\Base $curve) + { + switch (\true) { + case $curve instanceof \FluentSmtpLib\phpseclib3\Crypt\EC\Curves\secp256r1: + return 'P-256'; + case $curve instanceof \FluentSmtpLib\phpseclib3\Crypt\EC\Curves\secp384r1: + return 'P-384'; + case $curve instanceof \FluentSmtpLib\phpseclib3\Crypt\EC\Curves\secp521r1: + return 'P-521'; + case $curve instanceof \FluentSmtpLib\phpseclib3\Crypt\EC\Curves\secp256k1: + return 'secp256k1'; + } + $reflect = new \ReflectionClass($curve); + $curveName = $reflect->isFinal() ? $reflect->getParentClass()->getShortName() : $reflect->getShortName(); + throw new \FluentSmtpLib\phpseclib3\Exception\UnsupportedCurveException("{$curveName} is not a supported curve"); + } + /** + * Return the array superstructure for an EC public key + * + * @param BaseCurve $curve + * @param \phpseclib3\Math\Common\FiniteField\Integer[] $publicKey + * @return array + */ + private static function savePublicKeyHelper(\FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\Base $curve, array $publicKey) + { + if ($curve instanceof \FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\TwistedEdwards) { + return ['kty' => 'OKP', 'crv' => $curve instanceof \FluentSmtpLib\phpseclib3\Crypt\EC\Curves\Ed25519 ? 'Ed25519' : 'Ed448', 'x' => \FluentSmtpLib\phpseclib3\Common\Functions\Strings::base64url_encode($curve->encodePoint($publicKey))]; + } + return ['kty' => 'EC', 'crv' => self::getAlias($curve), 'x' => \FluentSmtpLib\phpseclib3\Common\Functions\Strings::base64url_encode($publicKey[0]->toBytes()), 'y' => \FluentSmtpLib\phpseclib3\Common\Functions\Strings::base64url_encode($publicKey[1]->toBytes())]; + } + /** + * Convert an EC public key to the appropriate format + * + * @param BaseCurve $curve + * @param \phpseclib3\Math\Common\FiniteField\Integer[] $publicKey + * @param array $options optional + * @return string + */ + public static function savePublicKey(\FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\Base $curve, array $publicKey, array $options = []) + { + $key = self::savePublicKeyHelper($curve, $publicKey); + return self::wrapKey($key, $options); + } + /** + * Convert a private key to the appropriate format. + * + * @param BigInteger $privateKey + * @param Ed25519 $curve + * @param \phpseclib3\Math\Common\FiniteField\Integer[] $publicKey + * @param string $secret optional + * @param string $password optional + * @param array $options optional + * @return string + */ + public static function savePrivateKey(\FluentSmtpLib\phpseclib3\Math\BigInteger $privateKey, \FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\Base $curve, array $publicKey, $secret = null, $password = '', array $options = []) + { + $key = self::savePublicKeyHelper($curve, $publicKey); + $key['d'] = $curve instanceof \FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\TwistedEdwards ? $secret : $privateKey->toBytes(); + $key['d'] = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::base64url_encode($key['d']); + return self::wrapKey($key, $options); + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Keys/MontgomeryPrivate.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Keys/MontgomeryPrivate.php new file mode 100644 index 0000000..4371cee --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Keys/MontgomeryPrivate.php @@ -0,0 +1,93 @@ + + * @copyright 2015 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\Crypt\EC\Formats\Keys; + +use FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\Montgomery as MontgomeryCurve; +use FluentSmtpLib\phpseclib3\Crypt\EC\Curves\Curve25519; +use FluentSmtpLib\phpseclib3\Crypt\EC\Curves\Curve448; +use FluentSmtpLib\phpseclib3\Exception\UnsupportedFormatException; +use FluentSmtpLib\phpseclib3\Math\BigInteger; +/** + * Montgomery Curve Private Key Handler + * + * @author Jim Wigginton + */ +abstract class MontgomeryPrivate +{ + /** + * Is invisible flag + * + */ + const IS_INVISIBLE = \true; + /** + * Break a public or private key down into its constituent components + * + * @param string $key + * @param string $password optional + * @return array + */ + public static function load($key, $password = '') + { + switch (\strlen($key)) { + case 32: + $curve = new \FluentSmtpLib\phpseclib3\Crypt\EC\Curves\Curve25519(); + break; + case 56: + $curve = new \FluentSmtpLib\phpseclib3\Crypt\EC\Curves\Curve448(); + break; + default: + throw new \LengthException('The only supported lengths are 32 and 56'); + } + $components = ['curve' => $curve]; + $components['dA'] = new \FluentSmtpLib\phpseclib3\Math\BigInteger($key, 256); + $curve->rangeCheck($components['dA']); + // note that EC::getEncodedCoordinates does some additional "magic" (it does strrev on the result) + $components['QA'] = $components['curve']->multiplyPoint($components['curve']->getBasePoint(), $components['dA']); + return $components; + } + /** + * Convert an EC public key to the appropriate format + * + * @param MontgomeryCurve $curve + * @param \phpseclib3\Math\Common\FiniteField\Integer[] $publicKey + * @return string + */ + public static function savePublicKey(\FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\Montgomery $curve, array $publicKey) + { + return \strrev($publicKey[0]->toBytes()); + } + /** + * Convert a private key to the appropriate format. + * + * @param BigInteger $privateKey + * @param MontgomeryCurve $curve + * @param \phpseclib3\Math\Common\FiniteField\Integer[] $publicKey + * @param string $secret optional + * @param string $password optional + * @return string + */ + public static function savePrivateKey(\FluentSmtpLib\phpseclib3\Math\BigInteger $privateKey, \FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\Montgomery $curve, array $publicKey, $secret = null, $password = '') + { + if (!empty($password) && \is_string($password)) { + throw new \FluentSmtpLib\phpseclib3\Exception\UnsupportedFormatException('MontgomeryPrivate private keys do not support encryption'); + } + return $privateKey->toBytes(); + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Keys/MontgomeryPublic.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Keys/MontgomeryPublic.php new file mode 100644 index 0000000..1128a39 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Keys/MontgomeryPublic.php @@ -0,0 +1,65 @@ + + * @copyright 2015 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\Crypt\EC\Formats\Keys; + +use FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\Montgomery as MontgomeryCurve; +use FluentSmtpLib\phpseclib3\Crypt\EC\Curves\Curve25519; +use FluentSmtpLib\phpseclib3\Crypt\EC\Curves\Curve448; +use FluentSmtpLib\phpseclib3\Math\BigInteger; +/** + * Montgomery Public Key Handler + * + * @author Jim Wigginton + */ +abstract class MontgomeryPublic +{ + /** + * Is invisible flag + * + */ + const IS_INVISIBLE = \true; + /** + * Break a public or private key down into its constituent components + * + * @param string $key + * @param string $password optional + * @return array + */ + public static function load($key, $password = '') + { + switch (\strlen($key)) { + case 32: + $curve = new \FluentSmtpLib\phpseclib3\Crypt\EC\Curves\Curve25519(); + break; + case 56: + $curve = new \FluentSmtpLib\phpseclib3\Crypt\EC\Curves\Curve448(); + break; + default: + throw new \LengthException('The only supported lengths are 32 and 56'); + } + $components = ['curve' => $curve]; + $components['QA'] = [$components['curve']->convertInteger(new \FluentSmtpLib\phpseclib3\Math\BigInteger(\strrev($key), 256))]; + return $components; + } + /** + * Convert an EC public key to the appropriate format + * + * @param MontgomeryCurve $curve + * @param \phpseclib3\Math\Common\FiniteField\Integer[] $publicKey + * @return string + */ + public static function savePublicKey(\FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\Montgomery $curve, array $publicKey) + { + return \strrev($publicKey[0]->toBytes()); + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Keys/OpenSSH.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Keys/OpenSSH.php new file mode 100644 index 0000000..157f018 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Keys/OpenSSH.php @@ -0,0 +1,163 @@ + + * @copyright 2015 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\Crypt\EC\Formats\Keys; + +use FluentSmtpLib\phpseclib3\Common\Functions\Strings; +use FluentSmtpLib\phpseclib3\Crypt\Common\Formats\Keys\OpenSSH as Progenitor; +use FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\Base as BaseCurve; +use FluentSmtpLib\phpseclib3\Crypt\EC\Curves\Ed25519; +use FluentSmtpLib\phpseclib3\Exception\UnsupportedCurveException; +use FluentSmtpLib\phpseclib3\Math\BigInteger; +/** + * OpenSSH Formatted EC Key Handler + * + * @author Jim Wigginton + */ +abstract class OpenSSH extends \FluentSmtpLib\phpseclib3\Crypt\Common\Formats\Keys\OpenSSH +{ + use Common; + /** + * Supported Key Types + * + * @var array + */ + protected static $types = ['ecdsa-sha2-nistp256', 'ecdsa-sha2-nistp384', 'ecdsa-sha2-nistp521', 'ssh-ed25519']; + /** + * Break a public or private key down into its constituent components + * + * @param string $key + * @param string $password optional + * @return array + */ + public static function load($key, $password = '') + { + $parsed = parent::load($key, $password); + if (isset($parsed['paddedKey'])) { + $paddedKey = $parsed['paddedKey']; + list($type) = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::unpackSSH2('s', $paddedKey); + if ($type != $parsed['type']) { + throw new \RuntimeException("The public and private keys are not of the same type ({$type} vs {$parsed['type']})"); + } + if ($type == 'ssh-ed25519') { + list(, $key, $comment) = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::unpackSSH2('sss', $paddedKey); + $key = \FluentSmtpLib\phpseclib3\Crypt\EC\Formats\Keys\libsodium::load($key); + $key['comment'] = $comment; + return $key; + } + list($curveName, $publicKey, $privateKey, $comment) = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::unpackSSH2('ssis', $paddedKey); + $curve = self::loadCurveByParam(['namedCurve' => $curveName]); + $curve->rangeCheck($privateKey); + return ['curve' => $curve, 'dA' => $privateKey, 'QA' => self::extractPoint("\x00{$publicKey}", $curve), 'comment' => $comment]; + } + if ($parsed['type'] == 'ssh-ed25519') { + if (\FluentSmtpLib\phpseclib3\Common\Functions\Strings::shift($parsed['publicKey'], 4) != "\x00\x00\x00 ") { + throw new \RuntimeException('Length of ssh-ed25519 key should be 32'); + } + $curve = new \FluentSmtpLib\phpseclib3\Crypt\EC\Curves\Ed25519(); + $qa = self::extractPoint($parsed['publicKey'], $curve); + } else { + list($curveName, $publicKey) = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::unpackSSH2('ss', $parsed['publicKey']); + $curveName = '\\FluentSmtpLib\\phpseclib3\\Crypt\\EC\\Curves\\' . $curveName; + $curve = new $curveName(); + $qa = self::extractPoint("\x00" . $publicKey, $curve); + } + return ['curve' => $curve, 'QA' => $qa, 'comment' => $parsed['comment']]; + } + /** + * Returns the alias that corresponds to a curve + * + * @return string + */ + private static function getAlias(\FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\Base $curve) + { + self::initialize_static_variables(); + $reflect = new \ReflectionClass($curve); + $name = $reflect->getShortName(); + $oid = self::$curveOIDs[$name]; + $aliases = \array_filter(self::$curveOIDs, function ($v) use($oid) { + return $v == $oid; + }); + $aliases = \array_keys($aliases); + for ($i = 0; $i < \count($aliases); $i++) { + if (\in_array('ecdsa-sha2-' . $aliases[$i], self::$types)) { + $alias = $aliases[$i]; + break; + } + } + if (!isset($alias)) { + throw new \FluentSmtpLib\phpseclib3\Exception\UnsupportedCurveException($name . ' is not a curve that the OpenSSH plugin supports'); + } + return $alias; + } + /** + * Convert an EC public key to the appropriate format + * + * @param BaseCurve $curve + * @param \phpseclib3\Math\Common\FiniteField\Integer[] $publicKey + * @param array $options optional + * @return string + */ + public static function savePublicKey(\FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\Base $curve, array $publicKey, array $options = []) + { + $comment = isset($options['comment']) ? $options['comment'] : self::$comment; + if ($curve instanceof \FluentSmtpLib\phpseclib3\Crypt\EC\Curves\Ed25519) { + $key = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::packSSH2('ss', 'ssh-ed25519', $curve->encodePoint($publicKey)); + if (isset($options['binary']) ? $options['binary'] : self::$binary) { + return $key; + } + $key = 'ssh-ed25519 ' . \base64_encode($key) . ' ' . $comment; + return $key; + } + $alias = self::getAlias($curve); + $points = "\x04" . $publicKey[0]->toBytes() . $publicKey[1]->toBytes(); + $key = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::packSSH2('sss', 'ecdsa-sha2-' . $alias, $alias, $points); + if (isset($options['binary']) ? $options['binary'] : self::$binary) { + return $key; + } + $key = 'ecdsa-sha2-' . $alias . ' ' . \base64_encode($key) . ' ' . $comment; + return $key; + } + /** + * Convert a private key to the appropriate format. + * + * @param BigInteger $privateKey + * @param Ed25519 $curve + * @param \phpseclib3\Math\Common\FiniteField\Integer[] $publicKey + * @param string $secret optional + * @param string $password optional + * @param array $options optional + * @return string + */ + public static function savePrivateKey(\FluentSmtpLib\phpseclib3\Math\BigInteger $privateKey, \FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\Base $curve, array $publicKey, $secret = null, $password = '', array $options = []) + { + if ($curve instanceof \FluentSmtpLib\phpseclib3\Crypt\EC\Curves\Ed25519) { + if (!isset($secret)) { + throw new \RuntimeException('Private Key does not have a secret set'); + } + if (\strlen($secret) != 32) { + throw new \RuntimeException('Private Key secret is not of the correct length'); + } + $pubKey = $curve->encodePoint($publicKey); + $publicKey = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::packSSH2('ss', 'ssh-ed25519', $pubKey); + $privateKey = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::packSSH2('sss', 'ssh-ed25519', $pubKey, $secret . $pubKey); + return self::wrapPrivateKey($publicKey, $privateKey, $password, $options); + } + $alias = self::getAlias($curve); + $points = "\x04" . $publicKey[0]->toBytes() . $publicKey[1]->toBytes(); + $publicKey = self::savePublicKey($curve, $publicKey, ['binary' => \true]); + $privateKey = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::packSSH2('sssi', 'ecdsa-sha2-' . $alias, $alias, $points, $privateKey); + return self::wrapPrivateKey($publicKey, $privateKey, $password, $options); + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Keys/PKCS1.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Keys/PKCS1.php new file mode 100644 index 0000000..ef4d4c5 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Keys/PKCS1.php @@ -0,0 +1,154 @@ + + * @copyright 2015 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\Crypt\EC\Formats\Keys; + +use FluentSmtpLib\phpseclib3\Common\Functions\Strings; +use FluentSmtpLib\phpseclib3\Crypt\Common\Formats\Keys\PKCS1 as Progenitor; +use FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\Base as BaseCurve; +use FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\Montgomery as MontgomeryCurve; +use FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\TwistedEdwards as TwistedEdwardsCurve; +use FluentSmtpLib\phpseclib3\Exception\UnsupportedCurveException; +use FluentSmtpLib\phpseclib3\File\ASN1; +use FluentSmtpLib\phpseclib3\File\ASN1\Maps; +use FluentSmtpLib\phpseclib3\Math\BigInteger; +/** + * "PKCS1" (RFC5915) Formatted EC Key Handler + * + * @author Jim Wigginton + */ +abstract class PKCS1 extends \FluentSmtpLib\phpseclib3\Crypt\Common\Formats\Keys\PKCS1 +{ + use Common; + /** + * Break a public or private key down into its constituent components + * + * @param string $key + * @param string $password optional + * @return array + */ + public static function load($key, $password = '') + { + self::initialize_static_variables(); + if (!\FluentSmtpLib\phpseclib3\Common\Functions\Strings::is_stringable($key)) { + throw new \UnexpectedValueException('Key should be a string - not a ' . \gettype($key)); + } + if (\strpos($key, 'BEGIN EC PARAMETERS') && \strpos($key, 'BEGIN EC PRIVATE KEY')) { + $components = []; + \preg_match('#-*BEGIN EC PRIVATE KEY-*[^-]*-*END EC PRIVATE KEY-*#s', $key, $matches); + $decoded = parent::load($matches[0], $password); + $decoded = \FluentSmtpLib\phpseclib3\File\ASN1::decodeBER($decoded); + if (!$decoded) { + throw new \RuntimeException('Unable to decode BER'); + } + $ecPrivate = \FluentSmtpLib\phpseclib3\File\ASN1::asn1map($decoded[0], \FluentSmtpLib\phpseclib3\File\ASN1\Maps\ECPrivateKey::MAP); + if (!\is_array($ecPrivate)) { + throw new \RuntimeException('Unable to perform ASN1 mapping'); + } + if (isset($ecPrivate['parameters'])) { + $components['curve'] = self::loadCurveByParam($ecPrivate['parameters']); + } + \preg_match('#-*BEGIN EC PARAMETERS-*[^-]*-*END EC PARAMETERS-*#s', $key, $matches); + $decoded = parent::load($matches[0], ''); + $decoded = \FluentSmtpLib\phpseclib3\File\ASN1::decodeBER($decoded); + if (!$decoded) { + throw new \RuntimeException('Unable to decode BER'); + } + $ecParams = \FluentSmtpLib\phpseclib3\File\ASN1::asn1map($decoded[0], \FluentSmtpLib\phpseclib3\File\ASN1\Maps\ECParameters::MAP); + if (!\is_array($ecParams)) { + throw new \RuntimeException('Unable to perform ASN1 mapping'); + } + $ecParams = self::loadCurveByParam($ecParams); + // comparing $ecParams and $components['curve'] directly won't work because they'll have different Math\Common\FiniteField classes + // even if the modulo is the same + if (isset($components['curve']) && self::encodeParameters($ecParams, \false, []) != self::encodeParameters($components['curve'], \false, [])) { + throw new \RuntimeException('EC PARAMETERS does not correspond to EC PRIVATE KEY'); + } + if (!isset($components['curve'])) { + $components['curve'] = $ecParams; + } + $components['dA'] = new \FluentSmtpLib\phpseclib3\Math\BigInteger($ecPrivate['privateKey'], 256); + $components['curve']->rangeCheck($components['dA']); + $components['QA'] = isset($ecPrivate['publicKey']) ? self::extractPoint($ecPrivate['publicKey'], $components['curve']) : $components['curve']->multiplyPoint($components['curve']->getBasePoint(), $components['dA']); + return $components; + } + $key = parent::load($key, $password); + $decoded = \FluentSmtpLib\phpseclib3\File\ASN1::decodeBER($key); + if (!$decoded) { + throw new \RuntimeException('Unable to decode BER'); + } + $key = \FluentSmtpLib\phpseclib3\File\ASN1::asn1map($decoded[0], \FluentSmtpLib\phpseclib3\File\ASN1\Maps\ECParameters::MAP); + if (\is_array($key)) { + return ['curve' => self::loadCurveByParam($key)]; + } + $key = \FluentSmtpLib\phpseclib3\File\ASN1::asn1map($decoded[0], \FluentSmtpLib\phpseclib3\File\ASN1\Maps\ECPrivateKey::MAP); + if (!\is_array($key)) { + throw new \RuntimeException('Unable to perform ASN1 mapping'); + } + if (!isset($key['parameters'])) { + throw new \RuntimeException('Key cannot be loaded without parameters'); + } + $components = []; + $components['curve'] = self::loadCurveByParam($key['parameters']); + $components['dA'] = new \FluentSmtpLib\phpseclib3\Math\BigInteger($key['privateKey'], 256); + $components['QA'] = isset($ecPrivate['publicKey']) ? self::extractPoint($ecPrivate['publicKey'], $components['curve']) : $components['curve']->multiplyPoint($components['curve']->getBasePoint(), $components['dA']); + return $components; + } + /** + * Convert EC parameters to the appropriate format + * + * @return string + */ + public static function saveParameters(\FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\Base $curve, array $options = []) + { + self::initialize_static_variables(); + if ($curve instanceof \FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\TwistedEdwards || $curve instanceof \FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\Montgomery) { + throw new \FluentSmtpLib\phpseclib3\Exception\UnsupportedCurveException('TwistedEdwards and Montgomery Curves are not supported'); + } + $key = self::encodeParameters($curve, \false, $options); + return "-----BEGIN EC PARAMETERS-----\r\n" . \chunk_split(\FluentSmtpLib\phpseclib3\Common\Functions\Strings::base64_encode($key), 64) . "-----END EC PARAMETERS-----\r\n"; + } + /** + * Convert a private key to the appropriate format. + * + * @param BigInteger $privateKey + * @param BaseCurve $curve + * @param \phpseclib3\Math\Common\FiniteField\Integer[] $publicKey + * @param string $secret optional + * @param string $password optional + * @param array $options optional + * @return string + */ + public static function savePrivateKey(\FluentSmtpLib\phpseclib3\Math\BigInteger $privateKey, \FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\Base $curve, array $publicKey, $secret = null, $password = '', array $options = []) + { + self::initialize_static_variables(); + if ($curve instanceof \FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\TwistedEdwards || $curve instanceof \FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\Montgomery) { + throw new \FluentSmtpLib\phpseclib3\Exception\UnsupportedCurveException('TwistedEdwards Curves are not supported'); + } + $publicKey = "\x04" . $publicKey[0]->toBytes() . $publicKey[1]->toBytes(); + $key = ['version' => 'ecPrivkeyVer1', 'privateKey' => $privateKey->toBytes(), 'parameters' => new \FluentSmtpLib\phpseclib3\File\ASN1\Element(self::encodeParameters($curve)), 'publicKey' => "\x00" . $publicKey]; + $key = \FluentSmtpLib\phpseclib3\File\ASN1::encodeDER($key, \FluentSmtpLib\phpseclib3\File\ASN1\Maps\ECPrivateKey::MAP); + return self::wrapPrivateKey($key, 'EC', $password, $options); + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Keys/PKCS8.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Keys/PKCS8.php new file mode 100644 index 0000000..bb56ea2 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Keys/PKCS8.php @@ -0,0 +1,185 @@ + + * @copyright 2015 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\Crypt\EC\Formats\Keys; + +use FluentSmtpLib\phpseclib3\Crypt\Common\Formats\Keys\PKCS8 as Progenitor; +use FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\Base as BaseCurve; +use FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\Montgomery as MontgomeryCurve; +use FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\TwistedEdwards as TwistedEdwardsCurve; +use FluentSmtpLib\phpseclib3\Crypt\EC\Curves\Ed25519; +use FluentSmtpLib\phpseclib3\Crypt\EC\Curves\Ed448; +use FluentSmtpLib\phpseclib3\Exception\UnsupportedCurveException; +use FluentSmtpLib\phpseclib3\File\ASN1; +use FluentSmtpLib\phpseclib3\File\ASN1\Maps; +use FluentSmtpLib\phpseclib3\Math\BigInteger; +/** + * PKCS#8 Formatted EC Key Handler + * + * @author Jim Wigginton + */ +abstract class PKCS8 extends \FluentSmtpLib\phpseclib3\Crypt\Common\Formats\Keys\PKCS8 +{ + use Common; + /** + * OID Name + * + * @var array + */ + const OID_NAME = ['id-ecPublicKey', 'id-Ed25519', 'id-Ed448']; + /** + * OID Value + * + * @var string + */ + const OID_VALUE = ['1.2.840.10045.2.1', '1.3.101.112', '1.3.101.113']; + /** + * Break a public or private key down into its constituent components + * + * @param string $key + * @param string $password optional + * @return array + */ + public static function load($key, $password = '') + { + // initialize_static_variables() is defined in both the trait and the parent class + // when it's defined in two places it's the traits one that's called + // the parent one is needed, as well, but the parent one is called by other methods + // in the parent class as needed and in the context of the parent it's the parent + // one that's called + self::initialize_static_variables(); + $key = parent::load($key, $password); + $type = isset($key['privateKey']) ? 'privateKey' : 'publicKey'; + switch ($key[$type . 'Algorithm']['algorithm']) { + case 'id-Ed25519': + case 'id-Ed448': + return self::loadEdDSA($key); + } + $decoded = \FluentSmtpLib\phpseclib3\File\ASN1::decodeBER($key[$type . 'Algorithm']['parameters']->element); + if (!$decoded) { + throw new \RuntimeException('Unable to decode BER'); + } + $params = \FluentSmtpLib\phpseclib3\File\ASN1::asn1map($decoded[0], \FluentSmtpLib\phpseclib3\File\ASN1\Maps\ECParameters::MAP); + if (!$params) { + throw new \RuntimeException('FluentSmtpLib\\Unable to decode the parameters using Maps\\ECParameters'); + } + $components = []; + $components['curve'] = self::loadCurveByParam($params); + if ($type == 'publicKey') { + $components['QA'] = self::extractPoint("\x00" . $key['publicKey'], $components['curve']); + return $components; + } + $decoded = \FluentSmtpLib\phpseclib3\File\ASN1::decodeBER($key['privateKey']); + if (!$decoded) { + throw new \RuntimeException('Unable to decode BER'); + } + $key = \FluentSmtpLib\phpseclib3\File\ASN1::asn1map($decoded[0], \FluentSmtpLib\phpseclib3\File\ASN1\Maps\ECPrivateKey::MAP); + if (isset($key['parameters']) && $params != $key['parameters']) { + throw new \RuntimeException('The PKCS8 parameter field does not match the private key parameter field'); + } + $components['dA'] = new \FluentSmtpLib\phpseclib3\Math\BigInteger($key['privateKey'], 256); + $components['curve']->rangeCheck($components['dA']); + $components['QA'] = isset($key['publicKey']) ? self::extractPoint($key['publicKey'], $components['curve']) : $components['curve']->multiplyPoint($components['curve']->getBasePoint(), $components['dA']); + return $components; + } + /** + * Break a public or private EdDSA key down into its constituent components + * + * @return array + */ + private static function loadEdDSA(array $key) + { + $components = []; + if (isset($key['privateKey'])) { + $components['curve'] = $key['privateKeyAlgorithm']['algorithm'] == 'id-Ed25519' ? new \FluentSmtpLib\phpseclib3\Crypt\EC\Curves\Ed25519() : new \FluentSmtpLib\phpseclib3\Crypt\EC\Curves\Ed448(); + $expected = \chr(\FluentSmtpLib\phpseclib3\File\ASN1::TYPE_OCTET_STRING) . \FluentSmtpLib\phpseclib3\File\ASN1::encodeLength($components['curve']::SIZE); + if (\substr($key['privateKey'], 0, 2) != $expected) { + throw new \RuntimeException('The first two bytes of the ' . $key['privateKeyAlgorithm']['algorithm'] . ' private key field should be 0x' . \bin2hex($expected)); + } + $arr = $components['curve']->extractSecret(\substr($key['privateKey'], 2)); + $components['dA'] = $arr['dA']; + $components['secret'] = $arr['secret']; + } + if (isset($key['publicKey'])) { + if (!isset($components['curve'])) { + $components['curve'] = $key['publicKeyAlgorithm']['algorithm'] == 'id-Ed25519' ? new \FluentSmtpLib\phpseclib3\Crypt\EC\Curves\Ed25519() : new \FluentSmtpLib\phpseclib3\Crypt\EC\Curves\Ed448(); + } + $components['QA'] = self::extractPoint($key['publicKey'], $components['curve']); + } + if (isset($key['privateKey']) && !isset($components['QA'])) { + $components['QA'] = $components['curve']->multiplyPoint($components['curve']->getBasePoint(), $components['dA']); + } + return $components; + } + /** + * Convert an EC public key to the appropriate format + * + * @param BaseCurve $curve + * @param \phpseclib3\Math\Common\FiniteField\Integer[] $publicKey + * @param array $options optional + * @return string + */ + public static function savePublicKey(\FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\Base $curve, array $publicKey, array $options = []) + { + self::initialize_static_variables(); + if ($curve instanceof \FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\Montgomery) { + throw new \FluentSmtpLib\phpseclib3\Exception\UnsupportedCurveException('Montgomery Curves are not supported'); + } + if ($curve instanceof \FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\TwistedEdwards) { + return self::wrapPublicKey($curve->encodePoint($publicKey), null, $curve instanceof \FluentSmtpLib\phpseclib3\Crypt\EC\Curves\Ed25519 ? 'id-Ed25519' : 'id-Ed448', $options); + } + $params = new \FluentSmtpLib\phpseclib3\File\ASN1\Element(self::encodeParameters($curve, \false, $options)); + $key = "\x04" . $publicKey[0]->toBytes() . $publicKey[1]->toBytes(); + return self::wrapPublicKey($key, $params, 'id-ecPublicKey', $options); + } + /** + * Convert a private key to the appropriate format. + * + * @param BigInteger $privateKey + * @param BaseCurve $curve + * @param \phpseclib3\Math\Common\FiniteField\Integer[] $publicKey + * @param string $secret optional + * @param string $password optional + * @param array $options optional + * @return string + */ + public static function savePrivateKey(\FluentSmtpLib\phpseclib3\Math\BigInteger $privateKey, \FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\Base $curve, array $publicKey, $secret = null, $password = '', array $options = []) + { + self::initialize_static_variables(); + if ($curve instanceof \FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\Montgomery) { + throw new \FluentSmtpLib\phpseclib3\Exception\UnsupportedCurveException('Montgomery Curves are not supported'); + } + if ($curve instanceof \FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\TwistedEdwards) { + return self::wrapPrivateKey(\chr(\FluentSmtpLib\phpseclib3\File\ASN1::TYPE_OCTET_STRING) . \FluentSmtpLib\phpseclib3\File\ASN1::encodeLength($curve::SIZE) . $secret, [], null, $password, $curve instanceof \FluentSmtpLib\phpseclib3\Crypt\EC\Curves\Ed25519 ? 'id-Ed25519' : 'id-Ed448'); + } + $publicKey = "\x04" . $publicKey[0]->toBytes() . $publicKey[1]->toBytes(); + $params = new \FluentSmtpLib\phpseclib3\File\ASN1\Element(self::encodeParameters($curve, \false, $options)); + $key = [ + 'version' => 'ecPrivkeyVer1', + 'privateKey' => $privateKey->toBytes(), + //'parameters' => $params, + 'publicKey' => "\x00" . $publicKey, + ]; + $key = \FluentSmtpLib\phpseclib3\File\ASN1::encodeDER($key, \FluentSmtpLib\phpseclib3\File\ASN1\Maps\ECPrivateKey::MAP); + return self::wrapPrivateKey($key, [], $params, $password, 'id-ecPublicKey', '', $options); + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Keys/PuTTY.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Keys/PuTTY.php new file mode 100644 index 0000000..8a6e32a --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Keys/PuTTY.php @@ -0,0 +1,115 @@ + + * @copyright 2015 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\Crypt\EC\Formats\Keys; + +use FluentSmtpLib\phpseclib3\Common\Functions\Strings; +use FluentSmtpLib\phpseclib3\Crypt\Common\Formats\Keys\PuTTY as Progenitor; +use FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\Base as BaseCurve; +use FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\TwistedEdwards as TwistedEdwardsCurve; +use FluentSmtpLib\phpseclib3\Math\BigInteger; +/** + * PuTTY Formatted EC Key Handler + * + * @author Jim Wigginton + */ +abstract class PuTTY extends \FluentSmtpLib\phpseclib3\Crypt\Common\Formats\Keys\PuTTY +{ + use Common; + /** + * Public Handler + * + * @var string + */ + const PUBLIC_HANDLER = 'FluentSmtpLib\\phpseclib3\\Crypt\\EC\\Formats\\Keys\\OpenSSH'; + /** + * Supported Key Types + * + * @var array + */ + protected static $types = ['ecdsa-sha2-nistp256', 'ecdsa-sha2-nistp384', 'ecdsa-sha2-nistp521', 'ssh-ed25519']; + /** + * Break a public or private key down into its constituent components + * + * @param string $key + * @param string $password optional + * @return array + */ + public static function load($key, $password = '') + { + $components = parent::load($key, $password); + if (!isset($components['private'])) { + return $components; + } + $private = $components['private']; + $temp = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::base64_encode(\FluentSmtpLib\phpseclib3\Common\Functions\Strings::packSSH2('s', $components['type']) . $components['public']); + $components = \FluentSmtpLib\phpseclib3\Crypt\EC\Formats\Keys\OpenSSH::load($components['type'] . ' ' . $temp . ' ' . $components['comment']); + if ($components['curve'] instanceof \FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\TwistedEdwards) { + if (\FluentSmtpLib\phpseclib3\Common\Functions\Strings::shift($private, 4) != "\x00\x00\x00 ") { + throw new \RuntimeException('Length of ssh-ed25519 key should be 32'); + } + $arr = $components['curve']->extractSecret($private); + $components['dA'] = $arr['dA']; + $components['secret'] = $arr['secret']; + } else { + list($components['dA']) = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::unpackSSH2('i', $private); + $components['curve']->rangeCheck($components['dA']); + } + return $components; + } + /** + * Convert a private key to the appropriate format. + * + * @param BigInteger $privateKey + * @param BaseCurve $curve + * @param \phpseclib3\Math\Common\FiniteField\Integer[] $publicKey + * @param string $secret optional + * @param string $password optional + * @param array $options optional + * @return string + */ + public static function savePrivateKey(\FluentSmtpLib\phpseclib3\Math\BigInteger $privateKey, \FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\Base $curve, array $publicKey, $secret = null, $password = \false, array $options = []) + { + self::initialize_static_variables(); + $public = \explode(' ', \FluentSmtpLib\phpseclib3\Crypt\EC\Formats\Keys\OpenSSH::savePublicKey($curve, $publicKey)); + $name = $public[0]; + $public = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::base64_decode($public[1]); + list(, $length) = \unpack('N', \FluentSmtpLib\phpseclib3\Common\Functions\Strings::shift($public, 4)); + \FluentSmtpLib\phpseclib3\Common\Functions\Strings::shift($public, $length); + // PuTTY pads private keys with a null byte per the following: + // https://github.com/github/putty/blob/a3d14d77f566a41fc61dfdc5c2e0e384c9e6ae8b/sshecc.c#L1926 + if (!$curve instanceof \FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\TwistedEdwards) { + $private = $privateKey->toBytes(); + if (!(\strlen($privateKey->toBits()) & 7)) { + $private = "\x00{$private}"; + } + } + $private = $curve instanceof \FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\TwistedEdwards ? \FluentSmtpLib\phpseclib3\Common\Functions\Strings::packSSH2('s', $secret) : \FluentSmtpLib\phpseclib3\Common\Functions\Strings::packSSH2('s', $private); + return self::wrapPrivateKey($public, $private, $name, $password, $options); + } + /** + * Convert an EC public key to the appropriate format + * + * @param BaseCurve $curve + * @param \phpseclib3\Math\Common\FiniteField[] $publicKey + * @return string + */ + public static function savePublicKey(\FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\Base $curve, array $publicKey) + { + $public = \explode(' ', \FluentSmtpLib\phpseclib3\Crypt\EC\Formats\Keys\OpenSSH::savePublicKey($curve, $publicKey)); + $type = $public[0]; + $public = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::base64_decode($public[1]); + list(, $length) = \unpack('N', \FluentSmtpLib\phpseclib3\Common\Functions\Strings::shift($public, 4)); + \FluentSmtpLib\phpseclib3\Common\Functions\Strings::shift($public, $length); + return self::wrapPublicKey($public, $type); + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Keys/XML.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Keys/XML.php new file mode 100644 index 0000000..1a80367 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Keys/XML.php @@ -0,0 +1,373 @@ + + * @copyright 2015 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\Crypt\EC\Formats\Keys; + +use FluentSmtpLib\phpseclib3\Common\Functions\Strings; +use FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\Base as BaseCurve; +use FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\Montgomery as MontgomeryCurve; +use FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\Prime as PrimeCurve; +use FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\TwistedEdwards as TwistedEdwardsCurve; +use FluentSmtpLib\phpseclib3\Exception\BadConfigurationException; +use FluentSmtpLib\phpseclib3\Exception\UnsupportedCurveException; +use FluentSmtpLib\phpseclib3\Math\BigInteger; +/** + * XML Formatted EC Key Handler + * + * @author Jim Wigginton + */ +abstract class XML +{ + use Common; + /** + * Default namespace + * + * @var string + */ + private static $namespace; + /** + * Flag for using RFC4050 syntax + * + * @var bool + */ + private static $rfc4050 = \false; + /** + * Break a public or private key down into its constituent components + * + * @param string $key + * @param string $password optional + * @return array + */ + public static function load($key, $password = '') + { + self::initialize_static_variables(); + if (!\FluentSmtpLib\phpseclib3\Common\Functions\Strings::is_stringable($key)) { + throw new \UnexpectedValueException('Key should be a string - not a ' . \gettype($key)); + } + if (!\class_exists('DOMDocument')) { + throw new \FluentSmtpLib\phpseclib3\Exception\BadConfigurationException('The dom extension is not setup correctly on this system'); + } + $use_errors = \libxml_use_internal_errors(\true); + $temp = self::isolateNamespace($key, 'http://www.w3.org/2009/xmldsig11#'); + if ($temp) { + $key = $temp; + } + $temp = self::isolateNamespace($key, 'http://www.w3.org/2001/04/xmldsig-more#'); + if ($temp) { + $key = $temp; + } + $dom = new \DOMDocument(); + if (\substr($key, 0, 5) != '' . $key . ''; + } + if (!$dom->loadXML($key)) { + \libxml_use_internal_errors($use_errors); + throw new \UnexpectedValueException('Key does not appear to contain XML'); + } + $xpath = new \DOMXPath($dom); + \libxml_use_internal_errors($use_errors); + $curve = self::loadCurveByParam($xpath); + $pubkey = self::query($xpath, 'publickey', 'Public Key is not present'); + $QA = self::query($xpath, 'ecdsakeyvalue')->length ? self::extractPointRFC4050($xpath, $curve) : self::extractPoint("\x00" . $pubkey, $curve); + \libxml_use_internal_errors($use_errors); + return \compact('curve', 'QA'); + } + /** + * Case-insensitive xpath query + * + * @param \DOMXPath $xpath + * @param string $name + * @param string $error optional + * @param bool $decode optional + * @return \DOMNodeList + */ + private static function query(\DOMXPath $xpath, $name, $error = null, $decode = \true) + { + $query = '/'; + $names = \explode('/', $name); + foreach ($names as $name) { + $query .= "/*[translate(local-name(), 'ABCDEFGHIJKLMNOPQRSTUVWXYZ','abcdefghijklmnopqrstuvwxyz')='{$name}']"; + } + $result = $xpath->query($query); + if (!isset($error)) { + return $result; + } + if (!$result->length) { + throw new \RuntimeException($error); + } + return $decode ? self::decodeValue($result->item(0)->textContent) : $result->item(0)->textContent; + } + /** + * Finds the first element in the relevant namespace, strips the namespacing and returns the XML for that element. + * + * @param string $xml + * @param string $ns + */ + private static function isolateNamespace($xml, $ns) + { + $dom = new \DOMDocument(); + if (!$dom->loadXML($xml)) { + return \false; + } + $xpath = new \DOMXPath($dom); + $nodes = $xpath->query("//*[namespace::*[.='{$ns}'] and not(../namespace::*[.='{$ns}'])]"); + if (!$nodes->length) { + return \false; + } + $node = $nodes->item(0); + $ns_name = $node->lookupPrefix($ns); + if ($ns_name) { + $node->removeAttributeNS($ns, $ns_name); + } + return $dom->saveXML($node); + } + /** + * Decodes the value + * + * @param string $value + */ + private static function decodeValue($value) + { + return \FluentSmtpLib\phpseclib3\Common\Functions\Strings::base64_decode(\str_replace(["\r", "\n", ' ', "\t"], '', $value)); + } + /** + * Extract points from an XML document + * + * @param \DOMXPath $xpath + * @param BaseCurve $curve + * @return object[] + */ + private static function extractPointRFC4050(\DOMXPath $xpath, \FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\Base $curve) + { + $x = self::query($xpath, 'publickey/x'); + $y = self::query($xpath, 'publickey/y'); + if (!$x->length || !$x->item(0)->hasAttribute('Value')) { + throw new \RuntimeException('Public Key / X coordinate not found'); + } + if (!$y->length || !$y->item(0)->hasAttribute('Value')) { + throw new \RuntimeException('Public Key / Y coordinate not found'); + } + $point = [$curve->convertInteger(new \FluentSmtpLib\phpseclib3\Math\BigInteger($x->item(0)->getAttribute('Value'))), $curve->convertInteger(new \FluentSmtpLib\phpseclib3\Math\BigInteger($y->item(0)->getAttribute('Value')))]; + if (!$curve->verifyPoint($point)) { + throw new \RuntimeException('Unable to verify that point exists on curve'); + } + return $point; + } + /** + * Returns an instance of \phpseclib3\Crypt\EC\BaseCurves\Base based + * on the curve parameters + * + * @param \DomXPath $xpath + * @return BaseCurve|false + */ + private static function loadCurveByParam(\DOMXPath $xpath) + { + $namedCurve = self::query($xpath, 'namedcurve'); + if ($namedCurve->length == 1) { + $oid = $namedCurve->item(0)->getAttribute('URN'); + $oid = \preg_replace('#[^\\d.]#', '', $oid); + $name = \array_search($oid, self::$curveOIDs); + if ($name === \false) { + throw new \FluentSmtpLib\phpseclib3\Exception\UnsupportedCurveException('Curve with OID of ' . $oid . ' is not supported'); + } + $curve = '\\FluentSmtpLib\\phpseclib3\\Crypt\\EC\\Curves\\' . $name; + if (!\class_exists($curve)) { + throw new \FluentSmtpLib\phpseclib3\Exception\UnsupportedCurveException('Named Curve of ' . $name . ' is not supported'); + } + return new $curve(); + } + $params = self::query($xpath, 'explicitparams'); + if ($params->length) { + return self::loadCurveByParamRFC4050($xpath); + } + $params = self::query($xpath, 'ecparameters'); + if (!$params->length) { + throw new \RuntimeException('No parameters are present'); + } + $fieldTypes = ['prime-field' => ['fieldid/prime/p'], 'gnb' => ['fieldid/gnb/m'], 'tnb' => ['fieldid/tnb/k'], 'pnb' => ['fieldid/pnb/k1', 'fieldid/pnb/k2', 'fieldid/pnb/k3'], 'unknown' => []]; + foreach ($fieldTypes as $type => $queries) { + foreach ($queries as $query) { + $result = self::query($xpath, $query); + if (!$result->length) { + continue 2; + } + $param = \preg_replace('#.*/#', '', $query); + ${$param} = self::decodeValue($result->item(0)->textContent); + } + break; + } + $a = self::query($xpath, 'curve/a', 'A coefficient is not present'); + $b = self::query($xpath, 'curve/b', 'B coefficient is not present'); + $base = self::query($xpath, 'base', 'Base point is not present'); + $order = self::query($xpath, 'order', 'Order is not present'); + switch ($type) { + case 'prime-field': + $curve = new \FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\Prime(); + $curve->setModulo(new \FluentSmtpLib\phpseclib3\Math\BigInteger($p, 256)); + $curve->setCoefficients(new \FluentSmtpLib\phpseclib3\Math\BigInteger($a, 256), new \FluentSmtpLib\phpseclib3\Math\BigInteger($b, 256)); + $point = self::extractPoint("\x00" . $base, $curve); + $curve->setBasePoint(...$point); + $curve->setOrder(new \FluentSmtpLib\phpseclib3\Math\BigInteger($order, 256)); + return $curve; + case 'gnb': + case 'tnb': + case 'pnb': + default: + throw new \FluentSmtpLib\phpseclib3\Exception\UnsupportedCurveException('Field Type of ' . $type . ' is not supported'); + } + } + /** + * Returns an instance of \phpseclib3\Crypt\EC\BaseCurves\Base based + * on the curve parameters + * + * @param \DomXPath $xpath + * @return BaseCurve|false + */ + private static function loadCurveByParamRFC4050(\DOMXPath $xpath) + { + $fieldTypes = ['prime-field' => ['primefieldparamstype/p'], 'unknown' => []]; + foreach ($fieldTypes as $type => $queries) { + foreach ($queries as $query) { + $result = self::query($xpath, $query); + if (!$result->length) { + continue 2; + } + $param = \preg_replace('#.*/#', '', $query); + ${$param} = $result->item(0)->textContent; + } + break; + } + $a = self::query($xpath, 'curveparamstype/a', 'A coefficient is not present', \false); + $b = self::query($xpath, 'curveparamstype/b', 'B coefficient is not present', \false); + $x = self::query($xpath, 'basepointparams/basepoint/ecpointtype/x', 'Base Point X is not present', \false); + $y = self::query($xpath, 'basepointparams/basepoint/ecpointtype/y', 'Base Point Y is not present', \false); + $order = self::query($xpath, 'order', 'Order is not present', \false); + switch ($type) { + case 'prime-field': + $curve = new \FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\Prime(); + $p = \str_replace(["\r", "\n", ' ', "\t"], '', $p); + $curve->setModulo(new \FluentSmtpLib\phpseclib3\Math\BigInteger($p)); + $a = \str_replace(["\r", "\n", ' ', "\t"], '', $a); + $b = \str_replace(["\r", "\n", ' ', "\t"], '', $b); + $curve->setCoefficients(new \FluentSmtpLib\phpseclib3\Math\BigInteger($a), new \FluentSmtpLib\phpseclib3\Math\BigInteger($b)); + $x = \str_replace(["\r", "\n", ' ', "\t"], '', $x); + $y = \str_replace(["\r", "\n", ' ', "\t"], '', $y); + $curve->setBasePoint(new \FluentSmtpLib\phpseclib3\Math\BigInteger($x), new \FluentSmtpLib\phpseclib3\Math\BigInteger($y)); + $order = \str_replace(["\r", "\n", ' ', "\t"], '', $order); + $curve->setOrder(new \FluentSmtpLib\phpseclib3\Math\BigInteger($order)); + return $curve; + default: + throw new \FluentSmtpLib\phpseclib3\Exception\UnsupportedCurveException('Field Type of ' . $type . ' is not supported'); + } + } + /** + * Sets the namespace. dsig11 is the most common one. + * + * Set to null to unset. Used only for creating public keys. + * + * @param string $namespace + */ + public static function setNamespace($namespace) + { + self::$namespace = $namespace; + } + /** + * Uses the XML syntax specified in https://tools.ietf.org/html/rfc4050 + */ + public static function enableRFC4050Syntax() + { + self::$rfc4050 = \true; + } + /** + * Uses the XML syntax specified in https://www.w3.org/TR/xmldsig-core/#sec-ECParameters + */ + public static function disableRFC4050Syntax() + { + self::$rfc4050 = \false; + } + /** + * Convert a public key to the appropriate format + * + * @param BaseCurve $curve + * @param \phpseclib3\Math\Common\FiniteField\Integer[] $publicKey + * @param array $options optional + * @return string + */ + public static function savePublicKey(\FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\Base $curve, array $publicKey, array $options = []) + { + self::initialize_static_variables(); + if ($curve instanceof \FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\TwistedEdwards || $curve instanceof \FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\Montgomery) { + throw new \FluentSmtpLib\phpseclib3\Exception\UnsupportedCurveException('TwistedEdwards and Montgomery Curves are not supported'); + } + if (empty(static::$namespace)) { + $pre = $post = ''; + } else { + $pre = static::$namespace . ':'; + $post = ':' . static::$namespace; + } + if (self::$rfc4050) { + return '<' . $pre . 'ECDSAKeyValue xmlns' . $post . '="http://www.w3.org/2001/04/xmldsig-more#">' . "\r\n" . self::encodeXMLParameters($curve, $pre, $options) . "\r\n" . '<' . $pre . 'PublicKey>' . "\r\n" . '<' . $pre . 'X Value="' . $publicKey[0] . '" />' . "\r\n" . '<' . $pre . 'Y Value="' . $publicKey[1] . '" />' . "\r\n" . '' . "\r\n" . ''; + } + $publicKey = "\x04" . $publicKey[0]->toBytes() . $publicKey[1]->toBytes(); + return '<' . $pre . 'ECDSAKeyValue xmlns' . $post . '="http://www.w3.org/2009/xmldsig11#">' . "\r\n" . self::encodeXMLParameters($curve, $pre, $options) . "\r\n" . '<' . $pre . 'PublicKey>' . \FluentSmtpLib\phpseclib3\Common\Functions\Strings::base64_encode($publicKey) . '' . "\r\n" . ''; + } + /** + * Encode Parameters + * + * @param BaseCurve $curve + * @param string $pre + * @param array $options optional + * @return string|false + */ + private static function encodeXMLParameters(\FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\Base $curve, $pre, array $options = []) + { + $result = self::encodeParameters($curve, \true, $options); + if (isset($result['namedCurve'])) { + $namedCurve = '<' . $pre . 'NamedCurve URI="urn:oid:' . self::$curveOIDs[$result['namedCurve']] . '" />'; + return self::$rfc4050 ? '' . \str_replace('URI', 'URN', $namedCurve) . '' : $namedCurve; + } + if (self::$rfc4050) { + $xml = '<' . $pre . 'ExplicitParams>' . "\r\n" . '<' . $pre . 'FieldParams>' . "\r\n"; + $temp = $result['specifiedCurve']; + switch ($temp['fieldID']['fieldType']) { + case 'prime-field': + $xml .= '<' . $pre . 'PrimeFieldParamsType>' . "\r\n" . '<' . $pre . 'P>' . $temp['fieldID']['parameters'] . '' . "\r\n" . '' . "\r\n"; + $a = $curve->getA(); + $b = $curve->getB(); + list($x, $y) = $curve->getBasePoint(); + break; + default: + throw new \FluentSmtpLib\phpseclib3\Exception\UnsupportedCurveException('Field Type of ' . $temp['fieldID']['fieldType'] . ' is not supported'); + } + $xml .= '' . "\r\n" . '<' . $pre . 'CurveParamsType>' . "\r\n" . '<' . $pre . 'A>' . $a . '' . "\r\n" . '<' . $pre . 'B>' . $b . '' . "\r\n" . '' . "\r\n" . '<' . $pre . 'BasePointParams>' . "\r\n" . '<' . $pre . 'BasePoint>' . "\r\n" . '<' . $pre . 'ECPointType>' . "\r\n" . '<' . $pre . 'X>' . $x . '' . "\r\n" . '<' . $pre . 'Y>' . $y . '' . "\r\n" . '' . "\r\n" . '' . "\r\n" . '<' . $pre . 'Order>' . $curve->getOrder() . '' . "\r\n" . '' . "\r\n" . '' . "\r\n"; + return $xml; + } + if (isset($result['specifiedCurve'])) { + $xml = '<' . $pre . 'ECParameters>' . "\r\n" . '<' . $pre . 'FieldID>' . "\r\n"; + $temp = $result['specifiedCurve']; + switch ($temp['fieldID']['fieldType']) { + case 'prime-field': + $xml .= '<' . $pre . 'Prime>' . "\r\n" . '<' . $pre . 'P>' . \FluentSmtpLib\phpseclib3\Common\Functions\Strings::base64_encode($temp['fieldID']['parameters']->toBytes()) . '' . "\r\n" . '' . "\r\n"; + break; + default: + throw new \FluentSmtpLib\phpseclib3\Exception\UnsupportedCurveException('Field Type of ' . $temp['fieldID']['fieldType'] . ' is not supported'); + } + $xml .= '' . "\r\n" . '<' . $pre . 'Curve>' . "\r\n" . '<' . $pre . 'A>' . \FluentSmtpLib\phpseclib3\Common\Functions\Strings::base64_encode($temp['curve']['a']) . '' . "\r\n" . '<' . $pre . 'B>' . \FluentSmtpLib\phpseclib3\Common\Functions\Strings::base64_encode($temp['curve']['b']) . '' . "\r\n" . '' . "\r\n" . '<' . $pre . 'Base>' . \FluentSmtpLib\phpseclib3\Common\Functions\Strings::base64_encode($temp['base']) . '' . "\r\n" . '<' . $pre . 'Order>' . \FluentSmtpLib\phpseclib3\Common\Functions\Strings::base64_encode($temp['order']) . '' . "\r\n" . ''; + return $xml; + } + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Keys/libsodium.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Keys/libsodium.php new file mode 100644 index 0000000..4171013 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Keys/libsodium.php @@ -0,0 +1,106 @@ + + * @copyright 2015 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\Crypt\EC\Formats\Keys; + +use FluentSmtpLib\phpseclib3\Crypt\EC\Curves\Ed25519; +use FluentSmtpLib\phpseclib3\Exception\UnsupportedFormatException; +use FluentSmtpLib\phpseclib3\Math\BigInteger; +/** + * libsodium Key Handler + * + * @author Jim Wigginton + */ +abstract class libsodium +{ + use Common; + /** + * Is invisible flag + * + */ + const IS_INVISIBLE = \true; + /** + * Break a public or private key down into its constituent components + * + * @param string $key + * @param string $password optional + * @return array + */ + public static function load($key, $password = '') + { + switch (\strlen($key)) { + case 32: + $public = $key; + break; + case 64: + $private = \substr($key, 0, 32); + $public = \substr($key, -32); + break; + case 96: + $public = \substr($key, -32); + if (\substr($key, 32, 32) != $public) { + throw new \RuntimeException('Keys with 96 bytes should have the 2nd and 3rd set of 32 bytes match'); + } + $private = \substr($key, 0, 32); + break; + default: + throw new \RuntimeException('libsodium keys need to either be 32 bytes long, 64 bytes long or 96 bytes long'); + } + $curve = new \FluentSmtpLib\phpseclib3\Crypt\EC\Curves\Ed25519(); + $components = ['curve' => $curve]; + if (isset($private)) { + $arr = $curve->extractSecret($private); + $components['dA'] = $arr['dA']; + $components['secret'] = $arr['secret']; + } + $components['QA'] = isset($public) ? self::extractPoint($public, $curve) : $curve->multiplyPoint($curve->getBasePoint(), $components['dA']); + return $components; + } + /** + * Convert an EC public key to the appropriate format + * + * @param Ed25519 $curve + * @param \phpseclib3\Math\Common\FiniteField\Integer[] $publicKey + * @return string + */ + public static function savePublicKey(\FluentSmtpLib\phpseclib3\Crypt\EC\Curves\Ed25519 $curve, array $publicKey) + { + return $curve->encodePoint($publicKey); + } + /** + * Convert a private key to the appropriate format. + * + * @param BigInteger $privateKey + * @param Ed25519 $curve + * @param \phpseclib3\Math\Common\FiniteField\Integer[] $publicKey + * @param string $secret optional + * @param string $password optional + * @return string + */ + public static function savePrivateKey(\FluentSmtpLib\phpseclib3\Math\BigInteger $privateKey, \FluentSmtpLib\phpseclib3\Crypt\EC\Curves\Ed25519 $curve, array $publicKey, $secret = null, $password = '') + { + if (!isset($secret)) { + throw new \RuntimeException('Private Key does not have a secret set'); + } + if (\strlen($secret) != 32) { + throw new \RuntimeException('Private Key secret is not of the correct length'); + } + if (!empty($password) && \is_string($password)) { + throw new \FluentSmtpLib\phpseclib3\Exception\UnsupportedFormatException('libsodium private keys do not support encryption'); + } + return $secret . $curve->encodePoint($publicKey); + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Signature/ASN1.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Signature/ASN1.php new file mode 100644 index 0000000..36d537c --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Signature/ASN1.php @@ -0,0 +1,57 @@ + + * @copyright 2016 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\Crypt\EC\Formats\Signature; + +use FluentSmtpLib\phpseclib3\File\ASN1 as Encoder; +use FluentSmtpLib\phpseclib3\File\ASN1\Maps\EcdsaSigValue; +use FluentSmtpLib\phpseclib3\Math\BigInteger; +/** + * ASN1 Signature Handler + * + * @author Jim Wigginton + */ +abstract class ASN1 +{ + /** + * Loads a signature + * + * @param string $sig + * @return array + */ + public static function load($sig) + { + if (!\is_string($sig)) { + return \false; + } + $decoded = \FluentSmtpLib\phpseclib3\File\ASN1::decodeBER($sig); + if (empty($decoded)) { + return \false; + } + $components = \FluentSmtpLib\phpseclib3\File\ASN1::asn1map($decoded[0], \FluentSmtpLib\phpseclib3\File\ASN1\Maps\EcdsaSigValue::MAP); + return $components; + } + /** + * Returns a signature in the appropriate format + * + * @param BigInteger $r + * @param BigInteger $s + * @return string + */ + public static function save(\FluentSmtpLib\phpseclib3\Math\BigInteger $r, \FluentSmtpLib\phpseclib3\Math\BigInteger $s) + { + return \FluentSmtpLib\phpseclib3\File\ASN1::encodeDER(\compact('r', 's'), \FluentSmtpLib\phpseclib3\File\ASN1\Maps\EcdsaSigValue::MAP); + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Signature/IEEE.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Signature/IEEE.php new file mode 100644 index 0000000..e7d6f78 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Signature/IEEE.php @@ -0,0 +1,62 @@ + + * @copyright 2016 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\Crypt\EC\Formats\Signature; + +use FluentSmtpLib\phpseclib3\Math\BigInteger; +/** + * ASN1 Signature Handler + * + * @author Jim Wigginton + */ +abstract class IEEE +{ + /** + * Loads a signature + * + * @param string $sig + * @return array + */ + public static function load($sig) + { + if (!\is_string($sig)) { + return \false; + } + $len = \strlen($sig); + if ($len & 1) { + return \false; + } + $r = new \FluentSmtpLib\phpseclib3\Math\BigInteger(\substr($sig, 0, $len >> 1), 256); + $s = new \FluentSmtpLib\phpseclib3\Math\BigInteger(\substr($sig, $len >> 1), 256); + return \compact('r', 's'); + } + /** + * Returns a signature in the appropriate format + * + * @param BigInteger $r + * @param BigInteger $s + * @param string $curve + * @param int $length + * @return string + */ + public static function save(\FluentSmtpLib\phpseclib3\Math\BigInteger $r, \FluentSmtpLib\phpseclib3\Math\BigInteger $s, $curve, $length) + { + $r = $r->toBytes(); + $s = $s->toBytes(); + $length = (int) \ceil($length / 8); + return \str_pad($r, $length, "\x00", \STR_PAD_LEFT) . \str_pad($s, $length, "\x00", \STR_PAD_LEFT); + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Signature/Raw.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Signature/Raw.php new file mode 100644 index 0000000..f2ac8c4 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Signature/Raw.php @@ -0,0 +1,23 @@ + + * @copyright 2016 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\Crypt\EC\Formats\Signature; + +use FluentSmtpLib\phpseclib3\Crypt\Common\Formats\Signature\Raw as Progenitor; +/** + * Raw DSA Signature Handler + * + * @author Jim Wigginton + */ +abstract class Raw extends \FluentSmtpLib\phpseclib3\Crypt\Common\Formats\Signature\Raw +{ +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Signature/SSH2.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Signature/SSH2.php new file mode 100644 index 0000000..37efb54 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Signature/SSH2.php @@ -0,0 +1,83 @@ + + * @copyright 2016 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\Crypt\EC\Formats\Signature; + +use FluentSmtpLib\phpseclib3\Common\Functions\Strings; +use FluentSmtpLib\phpseclib3\Math\BigInteger; +/** + * SSH2 Signature Handler + * + * @author Jim Wigginton + */ +abstract class SSH2 +{ + /** + * Loads a signature + * + * @param string $sig + * @return mixed + */ + public static function load($sig) + { + if (!\is_string($sig)) { + return \false; + } + $result = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::unpackSSH2('ss', $sig); + if ($result === \false) { + return \false; + } + list($type, $blob) = $result; + switch ($type) { + // see https://tools.ietf.org/html/rfc5656#section-3.1.2 + case 'ecdsa-sha2-nistp256': + case 'ecdsa-sha2-nistp384': + case 'ecdsa-sha2-nistp521': + break; + default: + return \false; + } + $result = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::unpackSSH2('ii', $blob); + if ($result === \false) { + return \false; + } + return ['r' => $result[0], 's' => $result[1]]; + } + /** + * Returns a signature in the appropriate format + * + * @param BigInteger $r + * @param BigInteger $s + * @param string $curve + * @return string + */ + public static function save(\FluentSmtpLib\phpseclib3\Math\BigInteger $r, \FluentSmtpLib\phpseclib3\Math\BigInteger $s, $curve) + { + switch ($curve) { + case 'secp256r1': + $curve = 'nistp256'; + break; + case 'secp384r1': + $curve = 'nistp384'; + break; + case 'secp521r1': + $curve = 'nistp521'; + break; + default: + return \false; + } + $blob = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::packSSH2('ii', $r, $s); + return \FluentSmtpLib\phpseclib3\Common\Functions\Strings::packSSH2('ss', 'ecdsa-sha2-' . $curve, $blob); + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Parameters.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Parameters.php new file mode 100644 index 0000000..b08eda6 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Parameters.php @@ -0,0 +1,33 @@ + + * @copyright 2015 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\Crypt\EC; + +use FluentSmtpLib\phpseclib3\Crypt\EC; +/** + * EC Parameters + * + * @author Jim Wigginton + */ +final class Parameters extends \FluentSmtpLib\phpseclib3\Crypt\EC +{ + /** + * Returns the parameters + * + * @param string $type + * @param array $options optional + * @return string + */ + public function toString($type = 'PKCS1', array $options = []) + { + $type = self::validatePlugin('Keys', 'PKCS1', 'saveParameters'); + return $type::saveParameters($this->curve, $options); + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/PrivateKey.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/PrivateKey.php new file mode 100644 index 0000000..84a152c --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/PrivateKey.php @@ -0,0 +1,249 @@ + + * @copyright 2015 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\Crypt\EC; + +use FluentSmtpLib\phpseclib3\Common\Functions\Strings; +use FluentSmtpLib\phpseclib3\Crypt\Common; +use FluentSmtpLib\phpseclib3\Crypt\EC; +use FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\Montgomery as MontgomeryCurve; +use FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\TwistedEdwards as TwistedEdwardsCurve; +use FluentSmtpLib\phpseclib3\Crypt\EC\Curves\Curve25519; +use FluentSmtpLib\phpseclib3\Crypt\EC\Curves\Ed25519; +use FluentSmtpLib\phpseclib3\Crypt\EC\Formats\Keys\PKCS1; +use FluentSmtpLib\phpseclib3\Crypt\EC\Formats\Signature\ASN1 as ASN1Signature; +use FluentSmtpLib\phpseclib3\Crypt\Hash; +use FluentSmtpLib\phpseclib3\Exception\UnsupportedOperationException; +use FluentSmtpLib\phpseclib3\Math\BigInteger; +/** + * EC Private Key + * + * @author Jim Wigginton + */ +final class PrivateKey extends \FluentSmtpLib\phpseclib3\Crypt\EC implements \FluentSmtpLib\phpseclib3\Crypt\Common\PrivateKey +{ + use Common\Traits\PasswordProtected; + /** + * Private Key dA + * + * sign() converts this to a BigInteger so one might wonder why this is a FiniteFieldInteger instead of + * a BigInteger. That's because a FiniteFieldInteger, when converted to a byte string, is null padded by + * a certain amount whereas a BigInteger isn't. + * + * @var object + */ + protected $dA; + /** + * @var string + */ + protected $secret; + /** + * Multiplies an encoded point by the private key + * + * Used by ECDH + * + * @param string $coordinates + * @return string + */ + public function multiply($coordinates) + { + if ($this->curve instanceof \FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\Montgomery) { + if ($this->curve instanceof \FluentSmtpLib\phpseclib3\Crypt\EC\Curves\Curve25519 && self::$engines['libsodium']) { + return \sodium_crypto_scalarmult($this->dA->toBytes(), $coordinates); + } + $point = [$this->curve->convertInteger(new \FluentSmtpLib\phpseclib3\Math\BigInteger(\strrev($coordinates), 256))]; + $point = $this->curve->multiplyPoint($point, $this->dA); + return \strrev($point[0]->toBytes(\true)); + } + if (!$this->curve instanceof \FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\TwistedEdwards) { + $coordinates = "\x00{$coordinates}"; + } + $point = \FluentSmtpLib\phpseclib3\Crypt\EC\Formats\Keys\PKCS1::extractPoint($coordinates, $this->curve); + $point = $this->curve->multiplyPoint($point, $this->dA); + if ($this->curve instanceof \FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\TwistedEdwards) { + return $this->curve->encodePoint($point); + } + if (empty($point)) { + throw new \RuntimeException('The infinity point is invalid'); + } + return "\x04" . $point[0]->toBytes(\true) . $point[1]->toBytes(\true); + } + /** + * Create a signature + * + * @see self::verify() + * @param string $message + * @return mixed + */ + public function sign($message) + { + if ($this->curve instanceof \FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\Montgomery) { + throw new \FluentSmtpLib\phpseclib3\Exception\UnsupportedOperationException('Montgomery Curves cannot be used to create signatures'); + } + $dA = $this->dA; + $order = $this->curve->getOrder(); + $shortFormat = $this->shortFormat; + $format = $this->sigFormat; + if ($format === \false) { + return \false; + } + if ($this->curve instanceof \FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\TwistedEdwards) { + if ($this->curve instanceof \FluentSmtpLib\phpseclib3\Crypt\EC\Curves\Ed25519 && self::$engines['libsodium'] && !isset($this->context)) { + $result = \sodium_crypto_sign_detached($message, $this->withPassword()->toString('libsodium')); + return $shortFormat == 'SSH2' ? \FluentSmtpLib\phpseclib3\Common\Functions\Strings::packSSH2('ss', 'ssh-' . \strtolower($this->getCurve()), $result) : $result; + } + // contexts (Ed25519ctx) are supported but prehashing (Ed25519ph) is not. + // quoting https://tools.ietf.org/html/rfc8032#section-8.5 , + // "The Ed25519ph and Ed448ph variants ... SHOULD NOT be used" + $A = $this->curve->encodePoint($this->QA); + $curve = $this->curve; + $hash = new \FluentSmtpLib\phpseclib3\Crypt\Hash($curve::HASH); + $secret = \substr($hash->hash($this->secret), $curve::SIZE); + if ($curve instanceof \FluentSmtpLib\phpseclib3\Crypt\EC\Curves\Ed25519) { + $dom = !isset($this->context) ? '' : 'SigEd25519 no Ed25519 collisions' . "\x00" . \chr(\strlen($this->context)) . $this->context; + } else { + $context = isset($this->context) ? $this->context : ''; + $dom = 'SigEd448' . "\x00" . \chr(\strlen($context)) . $context; + } + // SHA-512(dom2(F, C) || prefix || PH(M)) + $r = $hash->hash($dom . $secret . $message); + $r = \strrev($r); + $r = new \FluentSmtpLib\phpseclib3\Math\BigInteger($r, 256); + list(, $r) = $r->divide($order); + $R = $curve->multiplyPoint($curve->getBasePoint(), $r); + $R = $curve->encodePoint($R); + $k = $hash->hash($dom . $R . $A . $message); + $k = \strrev($k); + $k = new \FluentSmtpLib\phpseclib3\Math\BigInteger($k, 256); + list(, $k) = $k->divide($order); + $S = $k->multiply($dA)->add($r); + list(, $S) = $S->divide($order); + $S = \str_pad(\strrev($S->toBytes()), $curve::SIZE, "\x00"); + return $shortFormat == 'SSH2' ? \FluentSmtpLib\phpseclib3\Common\Functions\Strings::packSSH2('ss', 'ssh-' . \strtolower($this->getCurve()), $R . $S) : $R . $S; + } + if (self::$engines['OpenSSL'] && \in_array($this->hash->getHash(), \openssl_get_md_methods())) { + $signature = ''; + // altho PHP's OpenSSL bindings only supported EC key creation in PHP 7.1 they've long + // supported signing / verification + // we use specified curves to avoid issues with OpenSSL possibly not supporting a given named curve; + // doing this may mean some curve-specific optimizations can't be used but idk if OpenSSL even + // has curve-specific optimizations + $result = \openssl_sign($message, $signature, $this->withPassword()->toString('PKCS8', ['namedCurve' => \false]), $this->hash->getHash()); + if ($result) { + if ($shortFormat == 'ASN1') { + return $signature; + } + \extract(\FluentSmtpLib\phpseclib3\Crypt\EC\Formats\Signature\ASN1::load($signature)); + return $this->formatSignature($r, $s); + } + } + $e = $this->hash->hash($message); + $e = new \FluentSmtpLib\phpseclib3\Math\BigInteger($e, 256); + $Ln = $this->hash->getLength() - $order->getLength(); + $z = $Ln > 0 ? $e->bitwise_rightShift($Ln) : $e; + while (\true) { + $k = \FluentSmtpLib\phpseclib3\Math\BigInteger::randomRange(self::$one, $order->subtract(self::$one)); + list($x, $y) = $this->curve->multiplyPoint($this->curve->getBasePoint(), $k); + $x = $x->toBigInteger(); + list(, $r) = $x->divide($order); + if ($r->equals(self::$zero)) { + continue; + } + $kinv = $k->modInverse($order); + $temp = $z->add($dA->multiply($r)); + $temp = $kinv->multiply($temp); + list(, $s) = $temp->divide($order); + if (!$s->equals(self::$zero)) { + break; + } + } + // the following is an RFC6979 compliant implementation of deterministic ECDSA + // it's unused because it's mainly intended for use when a good CSPRNG isn't + // available. if phpseclib's CSPRNG isn't good then even key generation is + // suspect + /* + // if this were actually being used it'd probably be better if this lived in load() and createKey() + $this->q = $this->curve->getOrder(); + $dA = $this->dA->toBigInteger(); + $this->x = $dA; + + $h1 = $this->hash->hash($message); + $k = $this->computek($h1); + list($x, $y) = $this->curve->multiplyPoint($this->curve->getBasePoint(), $k); + $x = $x->toBigInteger(); + list(, $r) = $x->divide($this->q); + $kinv = $k->modInverse($this->q); + $h1 = $this->bits2int($h1); + $temp = $h1->add($dA->multiply($r)); + $temp = $kinv->multiply($temp); + list(, $s) = $temp->divide($this->q); + */ + return $this->formatSignature($r, $s); + } + /** + * Returns the private key + * + * @param string $type + * @param array $options optional + * @return string + */ + public function toString($type, array $options = []) + { + $type = self::validatePlugin('Keys', $type, 'savePrivateKey'); + return $type::savePrivateKey($this->dA, $this->curve, $this->QA, $this->secret, $this->password, $options); + } + /** + * Returns the public key + * + * @see self::getPrivateKey() + * @return mixed + */ + public function getPublicKey() + { + $format = 'PKCS8'; + if ($this->curve instanceof \FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\Montgomery) { + $format = 'MontgomeryPublic'; + } + $type = self::validatePlugin('Keys', $format, 'savePublicKey'); + $key = $type::savePublicKey($this->curve, $this->QA); + $key = \FluentSmtpLib\phpseclib3\Crypt\EC::loadFormat($format, $key); + if ($this->curve instanceof \FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\Montgomery) { + return $key; + } + $key = $key->withHash($this->hash->getHash())->withSignatureFormat($this->shortFormat); + if ($this->curve instanceof \FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\TwistedEdwards) { + $key = $key->withContext($this->context); + } + return $key; + } + /** + * Returns a signature in the appropriate format + * + * @return string + */ + private function formatSignature(\FluentSmtpLib\phpseclib3\Math\BigInteger $r, \FluentSmtpLib\phpseclib3\Math\BigInteger $s) + { + $format = $this->sigFormat; + $temp = new \ReflectionMethod($format, 'save'); + $paramCount = $temp->getNumberOfRequiredParameters(); + // @codingStandardsIgnoreStart + switch ($paramCount) { + case 2: + return $format::save($r, $s); + case 3: + return $format::save($r, $s, $this->getCurve()); + case 4: + return $format::save($r, $s, $this->getCurve(), $this->getLength()); + } + // @codingStandardsIgnoreEnd + // presumably the only way you could get to this is if you were using a custom plugin + throw new \FluentSmtpLib\phpseclib3\Exception\UnsupportedOperationException("{$format}::save() has {$paramCount} parameters - the only valid parameter counts are 2 or 3"); + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/PublicKey.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/PublicKey.php new file mode 100644 index 0000000..b1807b8 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/PublicKey.php @@ -0,0 +1,136 @@ + + * @copyright 2015 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\Crypt\EC; + +use FluentSmtpLib\phpseclib3\Common\Functions\Strings; +use FluentSmtpLib\phpseclib3\Crypt\Common; +use FluentSmtpLib\phpseclib3\Crypt\EC; +use FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\Montgomery as MontgomeryCurve; +use FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\TwistedEdwards as TwistedEdwardsCurve; +use FluentSmtpLib\phpseclib3\Crypt\EC\Curves\Ed25519; +use FluentSmtpLib\phpseclib3\Crypt\EC\Formats\Keys\PKCS1; +use FluentSmtpLib\phpseclib3\Crypt\EC\Formats\Signature\ASN1 as ASN1Signature; +use FluentSmtpLib\phpseclib3\Crypt\Hash; +use FluentSmtpLib\phpseclib3\Exception\UnsupportedOperationException; +use FluentSmtpLib\phpseclib3\Math\BigInteger; +/** + * EC Public Key + * + * @author Jim Wigginton + */ +final class PublicKey extends \FluentSmtpLib\phpseclib3\Crypt\EC implements \FluentSmtpLib\phpseclib3\Crypt\Common\PublicKey +{ + use Common\Traits\Fingerprint; + /** + * Verify a signature + * + * @see self::verify() + * @param string $message + * @param string $signature + * @return mixed + */ + public function verify($message, $signature) + { + if ($this->curve instanceof \FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\Montgomery) { + throw new \FluentSmtpLib\phpseclib3\Exception\UnsupportedOperationException('Montgomery Curves cannot be used to create signatures'); + } + $shortFormat = $this->shortFormat; + $format = $this->sigFormat; + if ($format === \false) { + return \false; + } + $order = $this->curve->getOrder(); + if ($this->curve instanceof \FluentSmtpLib\phpseclib3\Crypt\EC\BaseCurves\TwistedEdwards) { + if ($shortFormat == 'SSH2') { + list(, $signature) = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::unpackSSH2('ss', $signature); + } + if ($this->curve instanceof \FluentSmtpLib\phpseclib3\Crypt\EC\Curves\Ed25519 && self::$engines['libsodium'] && !isset($this->context)) { + return \sodium_crypto_sign_verify_detached($signature, $message, $this->toString('libsodium')); + } + $curve = $this->curve; + if (\strlen($signature) != 2 * $curve::SIZE) { + return \false; + } + $R = \substr($signature, 0, $curve::SIZE); + $S = \substr($signature, $curve::SIZE); + try { + $R = \FluentSmtpLib\phpseclib3\Crypt\EC\Formats\Keys\PKCS1::extractPoint($R, $curve); + $R = $this->curve->convertToInternal($R); + } catch (\Exception $e) { + return \false; + } + $S = \strrev($S); + $S = new \FluentSmtpLib\phpseclib3\Math\BigInteger($S, 256); + if ($S->compare($order) >= 0) { + return \false; + } + $A = $curve->encodePoint($this->QA); + if ($curve instanceof \FluentSmtpLib\phpseclib3\Crypt\EC\Curves\Ed25519) { + $dom2 = !isset($this->context) ? '' : 'SigEd25519 no Ed25519 collisions' . "\x00" . \chr(\strlen($this->context)) . $this->context; + } else { + $context = isset($this->context) ? $this->context : ''; + $dom2 = 'SigEd448' . "\x00" . \chr(\strlen($context)) . $context; + } + $hash = new \FluentSmtpLib\phpseclib3\Crypt\Hash($curve::HASH); + $k = $hash->hash($dom2 . \substr($signature, 0, $curve::SIZE) . $A . $message); + $k = \strrev($k); + $k = new \FluentSmtpLib\phpseclib3\Math\BigInteger($k, 256); + list(, $k) = $k->divide($order); + $qa = $curve->convertToInternal($this->QA); + $lhs = $curve->multiplyPoint($curve->getBasePoint(), $S); + $rhs = $curve->multiplyPoint($qa, $k); + $rhs = $curve->addPoint($rhs, $R); + $rhs = $curve->convertToAffine($rhs); + return $lhs[0]->equals($rhs[0]) && $lhs[1]->equals($rhs[1]); + } + $params = $format::load($signature); + if ($params === \false || \count($params) != 2) { + return \false; + } + \extract($params); + if (self::$engines['OpenSSL'] && \in_array($this->hash->getHash(), \openssl_get_md_methods())) { + $sig = $format != 'ASN1' ? \FluentSmtpLib\phpseclib3\Crypt\EC\Formats\Signature\ASN1::save($r, $s) : $signature; + $result = \openssl_verify($message, $sig, $this->toString('PKCS8', ['namedCurve' => \false]), $this->hash->getHash()); + if ($result != -1) { + return (bool) $result; + } + } + $n_1 = $order->subtract(self::$one); + if (!$r->between(self::$one, $n_1) || !$s->between(self::$one, $n_1)) { + return \false; + } + $e = $this->hash->hash($message); + $e = new \FluentSmtpLib\phpseclib3\Math\BigInteger($e, 256); + $Ln = $this->hash->getLength() - $order->getLength(); + $z = $Ln > 0 ? $e->bitwise_rightShift($Ln) : $e; + $w = $s->modInverse($order); + list(, $u1) = $z->multiply($w)->divide($order); + list(, $u2) = $r->multiply($w)->divide($order); + $u1 = $this->curve->convertInteger($u1); + $u2 = $this->curve->convertInteger($u2); + list($x1, $y1) = $this->curve->multiplyAddPoints([$this->curve->getBasePoint(), $this->QA], [$u1, $u2]); + $x1 = $x1->toBigInteger(); + list(, $x1) = $x1->divide($order); + return $x1->equals($r); + } + /** + * Returns the public key + * + * @param string $type + * @param array $options optional + * @return string + */ + public function toString($type, array $options = []) + { + $type = self::validatePlugin('Keys', $type, 'savePublicKey'); + return $type::savePublicKey($this->curve, $this->QA, $options); + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/Hash.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/Hash.php new file mode 100644 index 0000000..39e0aca --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/Hash.php @@ -0,0 +1,1391 @@ + + * setKey('abcdefg'); + * + * echo base64_encode($hash->hash('abcdefg')); + * ?> + * + * + * @author Jim Wigginton + * @copyright 2015 Jim Wigginton + * @author Andreas Fischer + * @copyright 2015 Andreas Fischer + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\Crypt; + +use FluentSmtpLib\phpseclib3\Common\Functions\Strings; +use FluentSmtpLib\phpseclib3\Exception\InsufficientSetupException; +use FluentSmtpLib\phpseclib3\Exception\UnsupportedAlgorithmException; +use FluentSmtpLib\phpseclib3\Math\BigInteger; +use FluentSmtpLib\phpseclib3\Math\PrimeField; +/** + * @author Jim Wigginton + * @author Andreas Fischer + */ +class Hash +{ + /** + * Padding Types + * + */ + const PADDING_KECCAK = 1; + /** + * Padding Types + * + */ + const PADDING_SHA3 = 2; + /** + * Padding Types + * + */ + const PADDING_SHAKE = 3; + /** + * Padding Type + * + * Only used by SHA3 + * + * @var int + */ + private $paddingType = 0; + /** + * Hash Parameter + * + * @see self::setHash() + * @var int + */ + private $hashParam; + /** + * Byte-length of hash output (Internal HMAC) + * + * @see self::setHash() + * @var int + */ + private $length; + /** + * Hash Algorithm + * + * @see self::setHash() + * @var string + */ + private $algo; + /** + * Key + * + * @see self::setKey() + * @var string + */ + private $key = \false; + /** + * Nonce + * + * @see self::setNonce() + * @var string + */ + private $nonce = \false; + /** + * Hash Parameters + * + * @var array + */ + private $parameters = []; + /** + * Computed Key + * + * @see self::_computeKey() + * @var string + */ + private $computedKey = \false; + /** + * Outer XOR (Internal HMAC) + * + * Used only for sha512 + * + * @see self::hash() + * @var string + */ + private $opad; + /** + * Inner XOR (Internal HMAC) + * + * Used only for sha512 + * + * @see self::hash() + * @var string + */ + private $ipad; + /** + * Recompute AES Key + * + * Used only for umac + * + * @see self::hash() + * @var boolean + */ + private $recomputeAESKey; + /** + * umac cipher object + * + * @see self::hash() + * @var AES + */ + private $c; + /** + * umac pad + * + * @see self::hash() + * @var string + */ + private $pad; + /** + * Block Size + * + * @var int + */ + private $blockSize; + /**#@+ + * UMAC variables + * + * @var PrimeField + */ + private static $factory36; + private static $factory64; + private static $factory128; + private static $offset64; + private static $offset128; + private static $marker64; + private static $marker128; + private static $maxwordrange64; + private static $maxwordrange128; + /**#@-*/ + /** + * Default Constructor. + * + * @param string $hash + */ + public function __construct($hash = 'sha256') + { + $this->setHash($hash); + } + /** + * Sets the key for HMACs + * + * Keys can be of any length. + * + * @param string $key + */ + public function setKey($key = \false) + { + $this->key = $key; + $this->computeKey(); + $this->recomputeAESKey = \true; + } + /** + * Sets the nonce for UMACs + * + * Keys can be of any length. + * + * @param string $nonce + */ + public function setNonce($nonce = \false) + { + switch (\true) { + case !\is_string($nonce): + case \strlen($nonce) > 0 && \strlen($nonce) <= 16: + $this->recomputeAESKey = \true; + $this->nonce = $nonce; + return; + } + throw new \LengthException('The nonce length must be between 1 and 16 bytes, inclusive'); + } + /** + * Pre-compute the key used by the HMAC + * + * Quoting http://tools.ietf.org/html/rfc2104#section-2, "Applications that use keys longer than B bytes + * will first hash the key using H and then use the resultant L byte string as the actual key to HMAC." + * + * As documented in https://www.reddit.com/r/PHP/comments/9nct2l/symfonypolyfill_hash_pbkdf2_correct_fix_for/ + * when doing an HMAC multiple times it's faster to compute the hash once instead of computing it during + * every call + * + */ + private function computeKey() + { + if ($this->key === \false) { + $this->computedKey = \false; + return; + } + if (\strlen($this->key) <= $this->getBlockLengthInBytes()) { + $this->computedKey = $this->key; + return; + } + $this->computedKey = \is_array($this->algo) ? \call_user_func($this->algo, $this->key) : \hash($this->algo, $this->key, \true); + } + /** + * Gets the hash function. + * + * As set by the constructor or by the setHash() method. + * + * @return string + */ + public function getHash() + { + return $this->hashParam; + } + /** + * Sets the hash function. + * + * @param string $hash + */ + public function setHash($hash) + { + $oldHash = $this->hashParam; + $this->hashParam = $hash = \strtolower($hash); + switch ($hash) { + case 'umac-32': + case 'umac-64': + case 'umac-96': + case 'umac-128': + if ($oldHash != $this->hashParam) { + $this->recomputeAESKey = \true; + } + $this->blockSize = 128; + $this->length = \abs(\substr($hash, -3)) >> 3; + $this->algo = 'umac'; + return; + case 'md2-96': + case 'md5-96': + case 'sha1-96': + case 'sha224-96': + case 'sha256-96': + case 'sha384-96': + case 'sha512-96': + case 'sha512/224-96': + case 'sha512/256-96': + $hash = \substr($hash, 0, -3); + $this->length = 12; + // 96 / 8 = 12 + break; + case 'md2': + case 'md5': + $this->length = 16; + break; + case 'sha1': + $this->length = 20; + break; + case 'sha224': + case 'sha512/224': + case 'sha3-224': + $this->length = 28; + break; + case 'keccak256': + $this->paddingType = self::PADDING_KECCAK; + // fall-through + case 'sha256': + case 'sha512/256': + case 'sha3-256': + $this->length = 32; + break; + case 'sha384': + case 'sha3-384': + $this->length = 48; + break; + case 'sha512': + case 'sha3-512': + $this->length = 64; + break; + default: + if (\preg_match('#^(shake(?:128|256))-(\\d+)$#', $hash, $matches)) { + $this->paddingType = self::PADDING_SHAKE; + $hash = $matches[1]; + $this->length = $matches[2] >> 3; + } else { + throw new \FluentSmtpLib\phpseclib3\Exception\UnsupportedAlgorithmException("{$hash} is not a supported algorithm"); + } + } + switch ($hash) { + case 'md2': + case 'md2-96': + $this->blockSize = 128; + break; + case 'md5-96': + case 'sha1-96': + case 'sha224-96': + case 'sha256-96': + case 'md5': + case 'sha1': + case 'sha224': + case 'sha256': + $this->blockSize = 512; + break; + case 'sha3-224': + $this->blockSize = 1152; + // 1600 - 2*224 + break; + case 'sha3-256': + case 'shake256': + case 'keccak256': + $this->blockSize = 1088; + // 1600 - 2*256 + break; + case 'sha3-384': + $this->blockSize = 832; + // 1600 - 2*384 + break; + case 'sha3-512': + $this->blockSize = 576; + // 1600 - 2*512 + break; + case 'shake128': + $this->blockSize = 1344; + // 1600 - 2*128 + break; + default: + $this->blockSize = 1024; + } + if (\in_array(\substr($hash, 0, 5), ['sha3-', 'shake', 'kecca'])) { + // PHP 7.1.0 introduced support for "SHA3 fixed mode algorithms": + // http://php.net/ChangeLog-7.php#7.1.0 + if (\version_compare(\PHP_VERSION, '7.1.0') < 0 || \substr($hash, 0, 5) != 'sha3-') { + //preg_match('#(\d+)$#', $hash, $matches); + //$this->parameters['capacity'] = 2 * $matches[1]; // 1600 - $this->blockSize + //$this->parameters['rate'] = 1600 - $this->parameters['capacity']; // == $this->blockSize + if (!$this->paddingType) { + $this->paddingType = self::PADDING_SHA3; + } + $this->parameters = ['capacity' => 1600 - $this->blockSize, 'rate' => $this->blockSize, 'length' => $this->length, 'padding' => $this->paddingType]; + $hash = ['FluentSmtpLib\\phpseclib3\\Crypt\\Hash', \PHP_INT_SIZE == 8 ? 'sha3_64' : 'sha3_32']; + } + } + if ($hash == 'sha512/224' || $hash == 'sha512/256') { + // PHP 7.1.0 introduced sha512/224 and sha512/256 support: + // http://php.net/ChangeLog-7.php#7.1.0 + if (\version_compare(\PHP_VERSION, '7.1.0') < 0) { + // from http://csrc.nist.gov/publications/fips/fips180-4/fips-180-4.pdf#page=24 + $initial = $hash == 'sha512/256' ? ['22312194FC2BF72C', '9F555FA3C84C64C2', '2393B86B6F53B151', '963877195940EABD', '96283EE2A88EFFE3', 'BE5E1E2553863992', '2B0199FC2C85B8AA', '0EB72DDC81C52CA2'] : ['8C3D37C819544DA2', '73E1996689DCD4D6', '1DFAB7AE32FF9C82', '679DD514582F9FCF', '0F6D2B697BD44DA8', '77E36F7304C48942', '3F9D85A86A1D36C8', '1112E6AD91D692A1']; + for ($i = 0; $i < 8; $i++) { + if (\PHP_INT_SIZE == 8) { + list(, $initial[$i]) = \unpack('J', \pack('H*', $initial[$i])); + } else { + $initial[$i] = new \FluentSmtpLib\phpseclib3\Math\BigInteger($initial[$i], 16); + $initial[$i]->setPrecision(64); + } + } + $this->parameters = \compact('initial'); + $hash = ['FluentSmtpLib\\phpseclib3\\Crypt\\Hash', \PHP_INT_SIZE == 8 ? 'sha512_64' : 'sha512']; + } + } + if (\is_array($hash)) { + $b = $this->blockSize >> 3; + $this->ipad = \str_repeat(\chr(0x36), $b); + $this->opad = \str_repeat(\chr(0x5c), $b); + } + $this->algo = $hash; + $this->computeKey(); + } + /** + * KDF: Key-Derivation Function + * + * The key-derivation function generates pseudorandom bits used to key the hash functions. + * + * @param int $index a non-negative integer less than 2^64 + * @param int $numbytes a non-negative integer less than 2^64 + * @return string string of length numbytes bytes + */ + private function kdf($index, $numbytes) + { + $this->c->setIV(\pack('N4', 0, $index, 0, 1)); + return $this->c->encrypt(\str_repeat("\x00", $numbytes)); + } + /** + * PDF Algorithm + * + * @return string string of length taglen bytes. + */ + private function pdf() + { + $k = $this->key; + $nonce = $this->nonce; + $taglen = $this->length; + // + // Extract and zero low bit(s) of Nonce if needed + // + if ($taglen <= 8) { + $last = \strlen($nonce) - 1; + $mask = $taglen == 4 ? "\x03" : "\x01"; + $index = $nonce[$last] & $mask; + $nonce[$last] = $nonce[$last] ^ $index; + } + // + // Make Nonce BLOCKLEN bytes by appending zeroes if needed + // + $nonce = \str_pad($nonce, 16, "\x00"); + // + // Generate subkey, encipher and extract indexed substring + // + $kp = $this->kdf(0, 16); + $c = new \FluentSmtpLib\phpseclib3\Crypt\AES('ctr'); + $c->disablePadding(); + $c->setKey($kp); + $c->setIV($nonce); + $t = $c->encrypt("\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"); + // we could use ord() but per https://paragonie.com/blog/2016/06/constant-time-encoding-boring-cryptography-rfc-4648-and-you + // unpack() doesn't leak timing info + return $taglen <= 8 ? \substr($t, \unpack('C', $index)[1] * $taglen, $taglen) : \substr($t, 0, $taglen); + } + /** + * UHASH Algorithm + * + * @param string $m string of length less than 2^67 bits. + * @param int $taglen the integer 4, 8, 12 or 16. + * @return string string of length taglen bytes. + */ + private function uhash($m, $taglen) + { + // + // One internal iteration per 4 bytes of output + // + $iters = $taglen >> 2; + // + // Define total key needed for all iterations using KDF. + // L1Key reuses most key material between iterations. + // + //$L1Key = $this->kdf(1, 1024 + ($iters - 1) * 16); + $L1Key = $this->kdf(1, (1024 + ($iters - 1)) * 16); + $L2Key = $this->kdf(2, $iters * 24); + $L3Key1 = $this->kdf(3, $iters * 64); + $L3Key2 = $this->kdf(4, $iters * 4); + // + // For each iteration, extract key and do three-layer hash. + // If bytelength(M) <= 1024, then skip L2-HASH. + // + $y = ''; + for ($i = 0; $i < $iters; $i++) { + $L1Key_i = \substr($L1Key, $i * 16, 1024); + $L2Key_i = \substr($L2Key, $i * 24, 24); + $L3Key1_i = \substr($L3Key1, $i * 64, 64); + $L3Key2_i = \substr($L3Key2, $i * 4, 4); + $a = self::L1Hash($L1Key_i, $m); + $b = \strlen($m) <= 1024 ? "\x00\x00\x00\x00\x00\x00\x00\x00{$a}" : self::L2Hash($L2Key_i, $a); + $c = self::L3Hash($L3Key1_i, $L3Key2_i, $b); + $y .= $c; + } + return $y; + } + /** + * L1-HASH Algorithm + * + * The first-layer hash breaks the message into 1024-byte chunks and + * hashes each with a function called NH. Concatenating the results + * forms a string, which is up to 128 times shorter than the original. + * + * @param string $k string of length 1024 bytes. + * @param string $m string of length less than 2^67 bits. + * @return string string of length (8 * ceil(bitlength(M)/8192)) bytes. + */ + private static function L1Hash($k, $m) + { + // + // Break M into 1024 byte chunks (final chunk may be shorter) + // + $m = \str_split($m, 1024); + // + // For each chunk, except the last: endian-adjust, NH hash + // and add bit-length. Use results to build Y. + // + $length = 1024 * 8; + $y = ''; + for ($i = 0; $i < \count($m) - 1; $i++) { + $m[$i] = \pack('N*', ...\unpack('V*', $m[$i])); + // ENDIAN-SWAP + $y .= \PHP_INT_SIZE == 8 ? static::nh64($k, $m[$i], $length) : static::nh32($k, $m[$i], $length); + } + // + // For the last chunk: pad to 32-byte boundary, endian-adjust, + // NH hash and add bit-length. Concatenate the result to Y. + // + $length = \count($m) ? \strlen($m[$i]) : 0; + $pad = 32 - $length % 32; + $pad = \max(32, $length + $pad % 32); + $m[$i] = \str_pad(isset($m[$i]) ? $m[$i] : '', $pad, "\x00"); + // zeropad + $m[$i] = \pack('N*', ...\unpack('V*', $m[$i])); + // ENDIAN-SWAP + $y .= \PHP_INT_SIZE == 8 ? static::nh64($k, $m[$i], $length * 8) : static::nh32($k, $m[$i], $length * 8); + return $y; + } + /** + * 32-bit safe 64-bit Multiply with 2x 32-bit ints + * + * @param int $x + * @param int $y + * @return string $x * $y + */ + private static function mul32_64($x, $y) + { + // see mul64() for a more detailed explanation of how this works + $x1 = $x >> 16 & 0xffff; + $x0 = $x & 0xffff; + $y1 = $y >> 16 & 0xffff; + $y0 = $y & 0xffff; + // the following 3x lines will possibly yield floats + $z2 = $x1 * $y1; + $z0 = $x0 * $y0; + $z1 = $x1 * $y0 + $x0 * $y1; + $a = \intval(\fmod($z0, 65536)); + $b = \intval($z0 / 65536) + \intval(\fmod($z1, 65536)); + $c = \intval($z1 / 65536) + \intval(\fmod($z2, 65536)) + \intval($b / 65536); + $b = \intval(\fmod($b, 65536)); + $d = \intval($z2 / 65536) + \intval($c / 65536); + $c = \intval(\fmod($c, 65536)); + $d = \intval(\fmod($d, 65536)); + return \pack('n4', $d, $c, $b, $a); + } + /** + * 32-bit safe 64-bit Addition with 2x 64-bit strings + * + * @param int $x + * @param int $y + * @return int $x * $y + */ + private static function add32_64($x, $y) + { + list(, $x1, $x2, $x3, $x4) = \unpack('n4', $x); + list(, $y1, $y2, $y3, $y4) = \unpack('n4', $y); + $a = $x4 + $y4; + $b = $x3 + $y3 + ($a >> 16); + $c = $x2 + $y2 + ($b >> 16); + $d = $x1 + $y1 + ($c >> 16); + return \pack('n4', $d, $c, $b, $a); + } + /** + * 32-bit safe 32-bit Addition with 2x 32-bit strings + * + * @param int $x + * @param int $y + * @return int $x * $y + */ + private static function add32($x, $y) + { + // see add64() for a more detailed explanation of how this works + $x1 = $x & 0xffff; + $x2 = $x >> 16 & 0xffff; + $y1 = $y & 0xffff; + $y2 = $y >> 16 & 0xffff; + $a = $x1 + $y1; + $b = $x2 + $y2 + ($a >> 16) << 16; + $a &= 0xffff; + return $a | $b; + } + /** + * NH Algorithm / 32-bit safe + * + * @param string $k string of length 1024 bytes. + * @param string $m string with length divisible by 32 bytes. + * @return string string of length 8 bytes. + */ + private static function nh32($k, $m, $length) + { + // + // Break M and K into 4-byte chunks + // + $k = \unpack('N*', $k); + $m = \unpack('N*', $m); + $t = \count($m); + // + // Perform NH hash on the chunks, pairing words for multiplication + // which are 4 apart to accommodate vector-parallelism. + // + $i = 1; + $y = "\x00\x00\x00\x00\x00\x00\x00\x00"; + while ($i <= $t) { + $temp = self::add32($m[$i], $k[$i]); + $temp2 = self::add32($m[$i + 4], $k[$i + 4]); + $y = self::add32_64($y, self::mul32_64($temp, $temp2)); + $temp = self::add32($m[$i + 1], $k[$i + 1]); + $temp2 = self::add32($m[$i + 5], $k[$i + 5]); + $y = self::add32_64($y, self::mul32_64($temp, $temp2)); + $temp = self::add32($m[$i + 2], $k[$i + 2]); + $temp2 = self::add32($m[$i + 6], $k[$i + 6]); + $y = self::add32_64($y, self::mul32_64($temp, $temp2)); + $temp = self::add32($m[$i + 3], $k[$i + 3]); + $temp2 = self::add32($m[$i + 7], $k[$i + 7]); + $y = self::add32_64($y, self::mul32_64($temp, $temp2)); + $i += 8; + } + return self::add32_64($y, \pack('N2', 0, $length)); + } + /** + * 64-bit Multiply with 2x 32-bit ints + * + * @param int $x + * @param int $y + * @return int $x * $y + */ + private static function mul64($x, $y) + { + // since PHP doesn't implement unsigned integers we'll implement them with signed integers + // to do this we'll use karatsuba multiplication + $x1 = $x >> 16; + $x0 = $x & 0xffff; + $y1 = $y >> 16; + $y0 = $y & 0xffff; + $z2 = $x1 * $y1; + // up to 32 bits long + $z0 = $x0 * $y0; + // up to 32 bits long + $z1 = $x1 * $y0 + $x0 * $y1; + // up to 33 bit long + // normally karatsuba multiplication calculates $z1 thusly: + //$z1 = ($x1 + $x0) * ($y0 + $y1) - $z2 - $z0; + // the idea being to eliminate one extra multiplication. for arbitrary precision math that makes sense + // but not for this purpose + // at this point karatsuba would normally return this: + //return ($z2 << 64) + ($z1 << 32) + $z0; + // the problem is that the output could be out of range for signed 64-bit ints, + // which would cause PHP to switch to floats, which would risk losing the lower few bits + // as such we'll OR 4x 16-bit blocks together like so: + /* + ........ | ........ | ........ | ........ + upper $z2 | lower $z2 | lower $z1 | lower $z0 + | +upper $z1 | +upper $z0 | + + $carry | + $carry | | + */ + // technically upper $z1 is 17 bit - not 16 - but the most significant digit of that will + // just get added to $carry + $a = $z0 & 0xffff; + $b = ($z0 >> 16) + ($z1 & 0xffff); + $c = ($z1 >> 16) + ($z2 & 0xffff) + ($b >> 16); + $b = ($b & 0xffff) << 16; + $d = ($z2 >> 16) + ($c >> 16); + $c = ($c & 0xffff) << 32; + $d = ($d & 0xffff) << 48; + return $a | $b | $c | $d; + } + /** + * 64-bit Addition with 2x 64-bit ints + * + * @param int $x + * @param int $y + * @return int $x + $y + */ + private static function add64($x, $y) + { + // doing $x + $y risks returning a result that's out of range for signed 64-bit ints + // in that event PHP would convert the result to a float and precision would be lost + // so we'll just add 2x 32-bit ints together like so: + /* + ........ | ........ + upper $x | lower $x + +upper $y |+lower $y + + $carry | + */ + $x1 = $x & 0xffffffff; + $x2 = $x >> 32 & 0xffffffff; + $y1 = $y & 0xffffffff; + $y2 = $y >> 32 & 0xffffffff; + $a = $x1 + $y1; + $b = $x2 + $y2 + ($a >> 32) << 32; + $a &= 0xffffffff; + return $a | $b; + } + /** + * NH Algorithm / 64-bit safe + * + * @param string $k string of length 1024 bytes. + * @param string $m string with length divisible by 32 bytes. + * @return string string of length 8 bytes. + */ + private static function nh64($k, $m, $length) + { + // + // Break M and K into 4-byte chunks + // + $k = \unpack('N*', $k); + $m = \unpack('N*', $m); + $t = \count($m); + // + // Perform NH hash on the chunks, pairing words for multiplication + // which are 4 apart to accommodate vector-parallelism. + // + $i = 1; + $y = 0; + while ($i <= $t) { + $temp = $m[$i] + $k[$i] & 0xffffffff; + $temp2 = $m[$i + 4] + $k[$i + 4] & 0xffffffff; + $y = self::add64($y, self::mul64($temp, $temp2)); + $temp = $m[$i + 1] + $k[$i + 1] & 0xffffffff; + $temp2 = $m[$i + 5] + $k[$i + 5] & 0xffffffff; + $y = self::add64($y, self::mul64($temp, $temp2)); + $temp = $m[$i + 2] + $k[$i + 2] & 0xffffffff; + $temp2 = $m[$i + 6] + $k[$i + 6] & 0xffffffff; + $y = self::add64($y, self::mul64($temp, $temp2)); + $temp = $m[$i + 3] + $k[$i + 3] & 0xffffffff; + $temp2 = $m[$i + 7] + $k[$i + 7] & 0xffffffff; + $y = self::add64($y, self::mul64($temp, $temp2)); + $i += 8; + } + return \pack('J', self::add64($y, $length)); + } + /** + * L2-HASH: Second-Layer Hash + * + * The second-layer rehashes the L1-HASH output using a polynomial hash + * called POLY. If the L1-HASH output is long, then POLY is called once + * on a prefix of the L1-HASH output and called using different settings + * on the remainder. (This two-step hashing of the L1-HASH output is + * needed only if the message length is greater than 16 megabytes.) + * Careful implementation of POLY is necessary to avoid a possible + * timing attack (see Section 6.6 for more information). + * + * @param string $k string of length 24 bytes. + * @param string $m string of length less than 2^64 bytes. + * @return string string of length 16 bytes. + */ + private static function L2Hash($k, $m) + { + // + // Extract keys and restrict to special key-sets + // + $k64 = $k & "\x01\xff\xff\xff\x01\xff\xff\xff"; + $k64 = new \FluentSmtpLib\phpseclib3\Math\BigInteger($k64, 256); + $k128 = \substr($k, 8) & "\x01\xff\xff\xff\x01\xff\xff\xff\x01\xff\xff\xff\x01\xff\xff\xff"; + $k128 = new \FluentSmtpLib\phpseclib3\Math\BigInteger($k128, 256); + // + // If M is no more than 2^17 bytes, hash under 64-bit prime, + // otherwise, hash first 2^17 bytes under 64-bit prime and + // remainder under 128-bit prime. + // + if (\strlen($m) <= 0x20000) { + // 2^14 64-bit words + $y = self::poly(64, self::$maxwordrange64, $k64, $m); + } else { + $m_1 = \substr($m, 0, 0x20000); + // 1 << 17 + $m_2 = \substr($m, 0x20000) . "\x80"; + $length = \strlen($m_2); + $pad = 16 - $length % 16; + $pad %= 16; + $m_2 = \str_pad($m_2, $length + $pad, "\x00"); + // zeropad + $y = self::poly(64, self::$maxwordrange64, $k64, $m_1); + $y = \str_pad($y, 16, "\x00", \STR_PAD_LEFT); + $y = self::poly(128, self::$maxwordrange128, $k128, $y . $m_2); + } + return \str_pad($y, 16, "\x00", \STR_PAD_LEFT); + } + /** + * POLY Algorithm + * + * @param int $wordbits the integer 64 or 128. + * @param BigInteger $maxwordrange positive integer less than 2^wordbits. + * @param BigInteger $k integer in the range 0 ... prime(wordbits) - 1. + * @param string $m string with length divisible by (wordbits / 8) bytes. + * @return integer in the range 0 ... prime(wordbits) - 1. + */ + private static function poly($wordbits, $maxwordrange, $k, $m) + { + // + // Define constants used for fixing out-of-range words + // + $wordbytes = $wordbits >> 3; + if ($wordbits == 128) { + $factory = self::$factory128; + $offset = self::$offset128; + $marker = self::$marker128; + } else { + $factory = self::$factory64; + $offset = self::$offset64; + $marker = self::$marker64; + } + $k = $factory->newInteger($k); + // + // Break M into chunks of length wordbytes bytes + // + $m_i = \str_split($m, $wordbytes); + // + // Each input word m is compared with maxwordrange. If not smaller + // then 'marker' and (m - offset), both in range, are hashed. + // + $y = $factory->newInteger(new \FluentSmtpLib\phpseclib3\Math\BigInteger(1)); + foreach ($m_i as $m) { + $m = $factory->newInteger(new \FluentSmtpLib\phpseclib3\Math\BigInteger($m, 256)); + if ($m->compare($maxwordrange) >= 0) { + $y = $k->multiply($y)->add($marker); + $y = $k->multiply($y)->add($m->subtract($offset)); + } else { + $y = $k->multiply($y)->add($m); + } + } + return $y->toBytes(); + } + /** + * L3-HASH: Third-Layer Hash + * + * The output from L2-HASH is 16 bytes long. This final hash function + * hashes the 16-byte string to a fixed length of 4 bytes. + * + * @param string $k1 string of length 64 bytes. + * @param string $k2 string of length 4 bytes. + * @param string $m string of length 16 bytes. + * @return string string of length 4 bytes. + */ + private static function L3Hash($k1, $k2, $m) + { + $factory = self::$factory36; + $y = $factory->newInteger(new \FluentSmtpLib\phpseclib3\Math\BigInteger()); + for ($i = 0; $i < 8; $i++) { + $m_i = $factory->newInteger(new \FluentSmtpLib\phpseclib3\Math\BigInteger(\substr($m, 2 * $i, 2), 256)); + $k_i = $factory->newInteger(new \FluentSmtpLib\phpseclib3\Math\BigInteger(\substr($k1, 8 * $i, 8), 256)); + $y = $y->add($m_i->multiply($k_i)); + } + $y = \str_pad(\substr($y->toBytes(), -4), 4, "\x00", \STR_PAD_LEFT); + $y = $y ^ $k2; + return $y; + } + /** + * Compute the Hash / HMAC / UMAC. + * + * @param string $text + * @return string + */ + public function hash($text) + { + $algo = $this->algo; + if ($algo == 'umac') { + if ($this->recomputeAESKey) { + if (!\is_string($this->nonce)) { + throw new \FluentSmtpLib\phpseclib3\Exception\InsufficientSetupException('No nonce has been set'); + } + if (!\is_string($this->key)) { + throw new \FluentSmtpLib\phpseclib3\Exception\InsufficientSetupException('No key has been set'); + } + if (\strlen($this->key) != 16) { + throw new \LengthException('Key must be 16 bytes long'); + } + if (!isset(self::$maxwordrange64)) { + $one = new \FluentSmtpLib\phpseclib3\Math\BigInteger(1); + $prime36 = new \FluentSmtpLib\phpseclib3\Math\BigInteger("\x00\x00\x00\x0f\xff\xff\xff\xfb", 256); + self::$factory36 = new \FluentSmtpLib\phpseclib3\Math\PrimeField($prime36); + $prime64 = new \FluentSmtpLib\phpseclib3\Math\BigInteger("\xff\xff\xff\xff\xff\xff\xff\xc5", 256); + self::$factory64 = new \FluentSmtpLib\phpseclib3\Math\PrimeField($prime64); + $prime128 = new \FluentSmtpLib\phpseclib3\Math\BigInteger("\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xffa", 256); + self::$factory128 = new \FluentSmtpLib\phpseclib3\Math\PrimeField($prime128); + self::$offset64 = new \FluentSmtpLib\phpseclib3\Math\BigInteger("\x01\x00\x00\x00\x00\x00\x00\x00\x00", 256); + self::$offset64 = self::$factory64->newInteger(self::$offset64->subtract($prime64)); + self::$offset128 = new \FluentSmtpLib\phpseclib3\Math\BigInteger("\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 256); + self::$offset128 = self::$factory128->newInteger(self::$offset128->subtract($prime128)); + self::$marker64 = self::$factory64->newInteger($prime64->subtract($one)); + self::$marker128 = self::$factory128->newInteger($prime128->subtract($one)); + $maxwordrange64 = $one->bitwise_leftShift(64)->subtract($one->bitwise_leftShift(32)); + self::$maxwordrange64 = self::$factory64->newInteger($maxwordrange64); + $maxwordrange128 = $one->bitwise_leftShift(128)->subtract($one->bitwise_leftShift(96)); + self::$maxwordrange128 = self::$factory128->newInteger($maxwordrange128); + } + $this->c = new \FluentSmtpLib\phpseclib3\Crypt\AES('ctr'); + $this->c->disablePadding(); + $this->c->setKey($this->key); + $this->pad = $this->pdf(); + $this->recomputeAESKey = \false; + } + $hashedmessage = $this->uhash($text, $this->length); + return $hashedmessage ^ $this->pad; + } + if (\is_array($algo)) { + if (empty($this->key) || !\is_string($this->key)) { + return \substr($algo($text, ...\array_values($this->parameters)), 0, $this->length); + } + // SHA3 HMACs are discussed at https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.202.pdf#page=30 + $key = \str_pad($this->computedKey, $b, \chr(0)); + $temp = $this->ipad ^ $key; + $temp .= $text; + $temp = \substr($algo($temp, ...\array_values($this->parameters)), 0, $this->length); + $output = $this->opad ^ $key; + $output .= $temp; + $output = $algo($output, ...\array_values($this->parameters)); + return \substr($output, 0, $this->length); + } + $output = !empty($this->key) || \is_string($this->key) ? \hash_hmac($algo, $text, $this->computedKey, \true) : \hash($algo, $text, \true); + return \strlen($output) > $this->length ? \substr($output, 0, $this->length) : $output; + } + /** + * Returns the hash length (in bits) + * + * @return int + */ + public function getLength() + { + return $this->length << 3; + } + /** + * Returns the hash length (in bytes) + * + * @return int + */ + public function getLengthInBytes() + { + return $this->length; + } + /** + * Returns the block length (in bits) + * + * @return int + */ + public function getBlockLength() + { + return $this->blockSize; + } + /** + * Returns the block length (in bytes) + * + * @return int + */ + public function getBlockLengthInBytes() + { + return $this->blockSize >> 3; + } + /** + * Pads SHA3 based on the mode + * + * @param int $padLength + * @param int $padType + * @return string + */ + private static function sha3_pad($padLength, $padType) + { + switch ($padType) { + case self::PADDING_KECCAK: + $temp = \chr(0x1) . \str_repeat("\x00", $padLength - 1); + $temp[$padLength - 1] = $temp[$padLength - 1] | \chr(0x80); + return $temp; + case self::PADDING_SHAKE: + $temp = \chr(0x1f) . \str_repeat("\x00", $padLength - 1); + $temp[$padLength - 1] = $temp[$padLength - 1] | \chr(0x80); + return $temp; + //case self::PADDING_SHA3: + default: + // from https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.202.pdf#page=36 + return $padLength == 1 ? \chr(0x86) : \chr(0x6) . \str_repeat("\x00", $padLength - 2) . \chr(0x80); + } + } + /** + * Pure-PHP 32-bit implementation of SHA3 + * + * Whereas BigInteger.php's 32-bit engine works on PHP 64-bit this 32-bit implementation + * of SHA3 will *not* work on PHP 64-bit. This is because this implementation + * employees bitwise NOTs and bitwise left shifts. And the round constants only work + * on 32-bit PHP. eg. dechex(-2147483648) returns 80000000 on 32-bit PHP and + * FFFFFFFF80000000 on 64-bit PHP. Sure, we could do bitwise ANDs but that would slow + * things down. + * + * SHA512 requires BigInteger to simulate 64-bit unsigned integers because SHA2 employees + * addition whereas SHA3 just employees bitwise operators. PHP64 only supports signed + * 64-bit integers, which complicates addition, whereas that limitation isn't an issue + * for SHA3. + * + * In https://ws680.nist.gov/publication/get_pdf.cfm?pub_id=919061#page=16 KECCAK[C] is + * defined as "the KECCAK instance with KECCAK-f[1600] as the underlying permutation and + * capacity c". This is relevant because, altho the KECCAK standard defines a mode + * (KECCAK-f[800]) designed for 32-bit machines that mode is incompatible with SHA3 + * + * @param string $p + * @param int $c + * @param int $r + * @param int $d + * @param int $padType + */ + private static function sha3_32($p, $c, $r, $d, $padType) + { + $block_size = $r >> 3; + $padLength = $block_size - \strlen($p) % $block_size; + $num_ints = $block_size >> 2; + $p .= static::sha3_pad($padLength, $padType); + $n = \strlen($p) / $r; + // number of blocks + $s = [[[0, 0], [0, 0], [0, 0], [0, 0], [0, 0]], [[0, 0], [0, 0], [0, 0], [0, 0], [0, 0]], [[0, 0], [0, 0], [0, 0], [0, 0], [0, 0]], [[0, 0], [0, 0], [0, 0], [0, 0], [0, 0]], [[0, 0], [0, 0], [0, 0], [0, 0], [0, 0]]]; + $p = \str_split($p, $block_size); + foreach ($p as $pi) { + $pi = \unpack('V*', $pi); + $x = $y = 0; + for ($i = 1; $i <= $num_ints; $i += 2) { + $s[$x][$y][0] ^= $pi[$i + 1]; + $s[$x][$y][1] ^= $pi[$i]; + if (++$y == 5) { + $y = 0; + $x++; + } + } + static::processSHA3Block32($s); + } + $z = ''; + $i = $j = 0; + while (\strlen($z) < $d) { + $z .= \pack('V2', $s[$i][$j][1], $s[$i][$j++][0]); + if ($j == 5) { + $j = 0; + $i++; + if ($i == 5) { + $i = 0; + static::processSHA3Block32($s); + } + } + } + return $z; + } + /** + * 32-bit block processing method for SHA3 + * + * @param array $s + */ + private static function processSHA3Block32(&$s) + { + static $rotationOffsets = [[0, 1, 62, 28, 27], [36, 44, 6, 55, 20], [3, 10, 43, 25, 39], [41, 45, 15, 21, 8], [18, 2, 61, 56, 14]]; + // the standards give these constants in hexadecimal notation. it's tempting to want to use + // that same notation, here, however, we can't, because 0x80000000, on PHP32, is a positive + // float - not the negative int that we need to be in PHP32. so we use -2147483648 instead + static $roundConstants = [[0, 1], [0, 32898], [-2147483648, 32906], [-2147483648, -2147450880], [0, 32907], [0, -2147483647], [-2147483648, -2147450751], [-2147483648, 32777], [0, 138], [0, 136], [0, -2147450871], [0, -2147483638], [0, -2147450741], [-2147483648, 139], [-2147483648, 32905], [-2147483648, 32771], [-2147483648, 32770], [-2147483648, 128], [0, 32778], [-2147483648, -2147483638], [-2147483648, -2147450751], [-2147483648, 32896], [0, -2147483647], [-2147483648, -2147450872]]; + for ($round = 0; $round < 24; $round++) { + // theta step + $parity = $rotated = []; + for ($i = 0; $i < 5; $i++) { + $parity[] = [$s[0][$i][0] ^ $s[1][$i][0] ^ $s[2][$i][0] ^ $s[3][$i][0] ^ $s[4][$i][0], $s[0][$i][1] ^ $s[1][$i][1] ^ $s[2][$i][1] ^ $s[3][$i][1] ^ $s[4][$i][1]]; + $rotated[] = static::rotateLeft32($parity[$i], 1); + } + $temp = [[$parity[4][0] ^ $rotated[1][0], $parity[4][1] ^ $rotated[1][1]], [$parity[0][0] ^ $rotated[2][0], $parity[0][1] ^ $rotated[2][1]], [$parity[1][0] ^ $rotated[3][0], $parity[1][1] ^ $rotated[3][1]], [$parity[2][0] ^ $rotated[4][0], $parity[2][1] ^ $rotated[4][1]], [$parity[3][0] ^ $rotated[0][0], $parity[3][1] ^ $rotated[0][1]]]; + for ($i = 0; $i < 5; $i++) { + for ($j = 0; $j < 5; $j++) { + $s[$i][$j][0] ^= $temp[$j][0]; + $s[$i][$j][1] ^= $temp[$j][1]; + } + } + $st = $s; + // rho and pi steps + for ($i = 0; $i < 5; $i++) { + for ($j = 0; $j < 5; $j++) { + $st[(2 * $i + 3 * $j) % 5][$j] = static::rotateLeft32($s[$j][$i], $rotationOffsets[$j][$i]); + } + } + // chi step + for ($i = 0; $i < 5; $i++) { + $s[$i][0] = [$st[$i][0][0] ^ ~$st[$i][1][0] & $st[$i][2][0], $st[$i][0][1] ^ ~$st[$i][1][1] & $st[$i][2][1]]; + $s[$i][1] = [$st[$i][1][0] ^ ~$st[$i][2][0] & $st[$i][3][0], $st[$i][1][1] ^ ~$st[$i][2][1] & $st[$i][3][1]]; + $s[$i][2] = [$st[$i][2][0] ^ ~$st[$i][3][0] & $st[$i][4][0], $st[$i][2][1] ^ ~$st[$i][3][1] & $st[$i][4][1]]; + $s[$i][3] = [$st[$i][3][0] ^ ~$st[$i][4][0] & $st[$i][0][0], $st[$i][3][1] ^ ~$st[$i][4][1] & $st[$i][0][1]]; + $s[$i][4] = [$st[$i][4][0] ^ ~$st[$i][0][0] & $st[$i][1][0], $st[$i][4][1] ^ ~$st[$i][0][1] & $st[$i][1][1]]; + } + // iota step + $s[0][0][0] ^= $roundConstants[$round][0]; + $s[0][0][1] ^= $roundConstants[$round][1]; + } + } + /** + * Rotate 32-bit int + * + * @param array $x + * @param int $shift + */ + private static function rotateLeft32($x, $shift) + { + if ($shift < 32) { + list($hi, $lo) = $x; + } else { + $shift -= 32; + list($lo, $hi) = $x; + } + $mask = -1 ^ -1 << $shift; + return [$hi << $shift | $lo >> 32 - $shift & $mask, $lo << $shift | $hi >> 32 - $shift & $mask]; + } + /** + * Pure-PHP 64-bit implementation of SHA3 + * + * @param string $p + * @param int $c + * @param int $r + * @param int $d + * @param int $padType + */ + private static function sha3_64($p, $c, $r, $d, $padType) + { + $block_size = $r >> 3; + $padLength = $block_size - \strlen($p) % $block_size; + $num_ints = $block_size >> 2; + $p .= static::sha3_pad($padLength, $padType); + $n = \strlen($p) / $r; + // number of blocks + $s = [[0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0]]; + $p = \str_split($p, $block_size); + foreach ($p as $pi) { + $pi = \unpack('P*', $pi); + $x = $y = 0; + foreach ($pi as $subpi) { + $s[$x][$y++] ^= $subpi; + if ($y == 5) { + $y = 0; + $x++; + } + } + static::processSHA3Block64($s); + } + $z = ''; + $i = $j = 0; + while (\strlen($z) < $d) { + $z .= \pack('P', $s[$i][$j++]); + if ($j == 5) { + $j = 0; + $i++; + if ($i == 5) { + $i = 0; + static::processSHA3Block64($s); + } + } + } + return $z; + } + /** + * 64-bit block processing method for SHA3 + * + * @param array $s + */ + private static function processSHA3Block64(&$s) + { + static $rotationOffsets = [[0, 1, 62, 28, 27], [36, 44, 6, 55, 20], [3, 10, 43, 25, 39], [41, 45, 15, 21, 8], [18, 2, 61, 56, 14]]; + static $roundConstants = [1, 32898, -9223372036854742902, -9223372034707259392, 32907, 2147483649, -9223372034707259263, -9223372036854743031, 138, 136, 2147516425, 2147483658, 2147516555, -9223372036854775669, -9223372036854742903, -9223372036854743037, -9223372036854743038, -9223372036854775680, 32778, -9223372034707292150, -9223372034707259263, -9223372036854742912, 2147483649, -9223372034707259384]; + for ($round = 0; $round < 24; $round++) { + // theta step + $parity = []; + for ($i = 0; $i < 5; $i++) { + $parity[] = $s[0][$i] ^ $s[1][$i] ^ $s[2][$i] ^ $s[3][$i] ^ $s[4][$i]; + } + $temp = [$parity[4] ^ static::rotateLeft64($parity[1], 1), $parity[0] ^ static::rotateLeft64($parity[2], 1), $parity[1] ^ static::rotateLeft64($parity[3], 1), $parity[2] ^ static::rotateLeft64($parity[4], 1), $parity[3] ^ static::rotateLeft64($parity[0], 1)]; + for ($i = 0; $i < 5; $i++) { + for ($j = 0; $j < 5; $j++) { + $s[$i][$j] ^= $temp[$j]; + } + } + $st = $s; + // rho and pi steps + for ($i = 0; $i < 5; $i++) { + for ($j = 0; $j < 5; $j++) { + $st[(2 * $i + 3 * $j) % 5][$j] = static::rotateLeft64($s[$j][$i], $rotationOffsets[$j][$i]); + } + } + // chi step + for ($i = 0; $i < 5; $i++) { + $s[$i] = [$st[$i][0] ^ ~$st[$i][1] & $st[$i][2], $st[$i][1] ^ ~$st[$i][2] & $st[$i][3], $st[$i][2] ^ ~$st[$i][3] & $st[$i][4], $st[$i][3] ^ ~$st[$i][4] & $st[$i][0], $st[$i][4] ^ ~$st[$i][0] & $st[$i][1]]; + } + // iota step + $s[0][0] ^= $roundConstants[$round]; + } + } + /** + * Left rotate 64-bit int + * + * @param int $x + * @param int $shift + */ + private static function rotateLeft64($x, $shift) + { + $mask = -1 ^ -1 << $shift; + return $x << $shift | $x >> 64 - $shift & $mask; + } + /** + * Right rotate 64-bit int + * + * @param int $x + * @param int $shift + */ + private static function rotateRight64($x, $shift) + { + $mask = -1 ^ -1 << 64 - $shift; + return $x >> $shift & $mask | $x << 64 - $shift; + } + /** + * Pure-PHP implementation of SHA512 + * + * @param string $m + * @param array $hash + * @return string + */ + private static function sha512($m, $hash) + { + static $k; + if (!isset($k)) { + // Initialize table of round constants + // (first 64 bits of the fractional parts of the cube roots of the first 80 primes 2..409) + $k = ['428a2f98d728ae22', '7137449123ef65cd', 'b5c0fbcfec4d3b2f', 'e9b5dba58189dbbc', '3956c25bf348b538', '59f111f1b605d019', '923f82a4af194f9b', 'ab1c5ed5da6d8118', 'd807aa98a3030242', '12835b0145706fbe', '243185be4ee4b28c', '550c7dc3d5ffb4e2', '72be5d74f27b896f', '80deb1fe3b1696b1', '9bdc06a725c71235', 'c19bf174cf692694', 'e49b69c19ef14ad2', 'efbe4786384f25e3', '0fc19dc68b8cd5b5', '240ca1cc77ac9c65', '2de92c6f592b0275', '4a7484aa6ea6e483', '5cb0a9dcbd41fbd4', '76f988da831153b5', '983e5152ee66dfab', 'a831c66d2db43210', 'b00327c898fb213f', 'bf597fc7beef0ee4', 'c6e00bf33da88fc2', 'd5a79147930aa725', '06ca6351e003826f', '142929670a0e6e70', '27b70a8546d22ffc', '2e1b21385c26c926', '4d2c6dfc5ac42aed', '53380d139d95b3df', '650a73548baf63de', '766a0abb3c77b2a8', '81c2c92e47edaee6', '92722c851482353b', 'a2bfe8a14cf10364', 'a81a664bbc423001', 'c24b8b70d0f89791', 'c76c51a30654be30', 'd192e819d6ef5218', 'd69906245565a910', 'f40e35855771202a', '106aa07032bbd1b8', '19a4c116b8d2d0c8', '1e376c085141ab53', '2748774cdf8eeb99', '34b0bcb5e19b48a8', '391c0cb3c5c95a63', '4ed8aa4ae3418acb', '5b9cca4f7763e373', '682e6ff3d6b2b8a3', '748f82ee5defb2fc', '78a5636f43172f60', '84c87814a1f0ab72', '8cc702081a6439ec', '90befffa23631e28', 'a4506cebde82bde9', 'bef9a3f7b2c67915', 'c67178f2e372532b', 'ca273eceea26619c', 'd186b8c721c0c207', 'eada7dd6cde0eb1e', 'f57d4f7fee6ed178', '06f067aa72176fba', '0a637dc5a2c898a6', '113f9804bef90dae', '1b710b35131c471b', '28db77f523047d84', '32caab7b40c72493', '3c9ebe0a15c9bebc', '431d67c49c100d4c', '4cc5d4becb3e42b6', '597f299cfc657e2a', '5fcb6fab3ad6faec', '6c44198c4a475817']; + for ($i = 0; $i < 80; $i++) { + $k[$i] = new \FluentSmtpLib\phpseclib3\Math\BigInteger($k[$i], 16); + } + } + // Pre-processing + $length = \strlen($m); + // to round to nearest 112 mod 128, we'll add 128 - (length + (128 - 112)) % 128 + $m .= \str_repeat(\chr(0), 128 - ($length + 16 & 0x7f)); + $m[$length] = \chr(0x80); + // we don't support hashing strings 512MB long + $m .= \pack('N4', 0, 0, 0, $length << 3); + // Process the message in successive 1024-bit chunks + $chunks = \str_split($m, 128); + foreach ($chunks as $chunk) { + $w = []; + for ($i = 0; $i < 16; $i++) { + $temp = new \FluentSmtpLib\phpseclib3\Math\BigInteger(\FluentSmtpLib\phpseclib3\Common\Functions\Strings::shift($chunk, 8), 256); + $temp->setPrecision(64); + $w[] = $temp; + } + // Extend the sixteen 32-bit words into eighty 32-bit words + for ($i = 16; $i < 80; $i++) { + $temp = [$w[$i - 15]->bitwise_rightRotate(1), $w[$i - 15]->bitwise_rightRotate(8), $w[$i - 15]->bitwise_rightShift(7)]; + $s0 = $temp[0]->bitwise_xor($temp[1]); + $s0 = $s0->bitwise_xor($temp[2]); + $temp = [$w[$i - 2]->bitwise_rightRotate(19), $w[$i - 2]->bitwise_rightRotate(61), $w[$i - 2]->bitwise_rightShift(6)]; + $s1 = $temp[0]->bitwise_xor($temp[1]); + $s1 = $s1->bitwise_xor($temp[2]); + $w[$i] = clone $w[$i - 16]; + $w[$i] = $w[$i]->add($s0); + $w[$i] = $w[$i]->add($w[$i - 7]); + $w[$i] = $w[$i]->add($s1); + } + // Initialize hash value for this chunk + $a = clone $hash[0]; + $b = clone $hash[1]; + $c = clone $hash[2]; + $d = clone $hash[3]; + $e = clone $hash[4]; + $f = clone $hash[5]; + $g = clone $hash[6]; + $h = clone $hash[7]; + // Main loop + for ($i = 0; $i < 80; $i++) { + $temp = [$a->bitwise_rightRotate(28), $a->bitwise_rightRotate(34), $a->bitwise_rightRotate(39)]; + $s0 = $temp[0]->bitwise_xor($temp[1]); + $s0 = $s0->bitwise_xor($temp[2]); + $temp = [$a->bitwise_and($b), $a->bitwise_and($c), $b->bitwise_and($c)]; + $maj = $temp[0]->bitwise_xor($temp[1]); + $maj = $maj->bitwise_xor($temp[2]); + $t2 = $s0->add($maj); + $temp = [$e->bitwise_rightRotate(14), $e->bitwise_rightRotate(18), $e->bitwise_rightRotate(41)]; + $s1 = $temp[0]->bitwise_xor($temp[1]); + $s1 = $s1->bitwise_xor($temp[2]); + $temp = [$e->bitwise_and($f), $g->bitwise_and($e->bitwise_not())]; + $ch = $temp[0]->bitwise_xor($temp[1]); + $t1 = $h->add($s1); + $t1 = $t1->add($ch); + $t1 = $t1->add($k[$i]); + $t1 = $t1->add($w[$i]); + $h = clone $g; + $g = clone $f; + $f = clone $e; + $e = $d->add($t1); + $d = clone $c; + $c = clone $b; + $b = clone $a; + $a = $t1->add($t2); + } + // Add this chunk's hash to result so far + $hash = [$hash[0]->add($a), $hash[1]->add($b), $hash[2]->add($c), $hash[3]->add($d), $hash[4]->add($e), $hash[5]->add($f), $hash[6]->add($g), $hash[7]->add($h)]; + } + // Produce the final hash value (big-endian) + // (\phpseclib3\Crypt\Hash::hash() trims the output for hashes but not for HMACs. as such, we trim the output here) + $temp = $hash[0]->toBytes() . $hash[1]->toBytes() . $hash[2]->toBytes() . $hash[3]->toBytes() . $hash[4]->toBytes() . $hash[5]->toBytes() . $hash[6]->toBytes() . $hash[7]->toBytes(); + return $temp; + } + /** + * Pure-PHP implementation of SHA512 + * + * @param string $m + * @param array $hash + * @return string + */ + private static function sha512_64($m, $hash) + { + static $k; + if (!isset($k)) { + // Initialize table of round constants + // (first 64 bits of the fractional parts of the cube roots of the first 80 primes 2..409) + $k = ['428a2f98d728ae22', '7137449123ef65cd', 'b5c0fbcfec4d3b2f', 'e9b5dba58189dbbc', '3956c25bf348b538', '59f111f1b605d019', '923f82a4af194f9b', 'ab1c5ed5da6d8118', 'd807aa98a3030242', '12835b0145706fbe', '243185be4ee4b28c', '550c7dc3d5ffb4e2', '72be5d74f27b896f', '80deb1fe3b1696b1', '9bdc06a725c71235', 'c19bf174cf692694', 'e49b69c19ef14ad2', 'efbe4786384f25e3', '0fc19dc68b8cd5b5', '240ca1cc77ac9c65', '2de92c6f592b0275', '4a7484aa6ea6e483', '5cb0a9dcbd41fbd4', '76f988da831153b5', '983e5152ee66dfab', 'a831c66d2db43210', 'b00327c898fb213f', 'bf597fc7beef0ee4', 'c6e00bf33da88fc2', 'd5a79147930aa725', '06ca6351e003826f', '142929670a0e6e70', '27b70a8546d22ffc', '2e1b21385c26c926', '4d2c6dfc5ac42aed', '53380d139d95b3df', '650a73548baf63de', '766a0abb3c77b2a8', '81c2c92e47edaee6', '92722c851482353b', 'a2bfe8a14cf10364', 'a81a664bbc423001', 'c24b8b70d0f89791', 'c76c51a30654be30', 'd192e819d6ef5218', 'd69906245565a910', 'f40e35855771202a', '106aa07032bbd1b8', '19a4c116b8d2d0c8', '1e376c085141ab53', '2748774cdf8eeb99', '34b0bcb5e19b48a8', '391c0cb3c5c95a63', '4ed8aa4ae3418acb', '5b9cca4f7763e373', '682e6ff3d6b2b8a3', '748f82ee5defb2fc', '78a5636f43172f60', '84c87814a1f0ab72', '8cc702081a6439ec', '90befffa23631e28', 'a4506cebde82bde9', 'bef9a3f7b2c67915', 'c67178f2e372532b', 'ca273eceea26619c', 'd186b8c721c0c207', 'eada7dd6cde0eb1e', 'f57d4f7fee6ed178', '06f067aa72176fba', '0a637dc5a2c898a6', '113f9804bef90dae', '1b710b35131c471b', '28db77f523047d84', '32caab7b40c72493', '3c9ebe0a15c9bebc', '431d67c49c100d4c', '4cc5d4becb3e42b6', '597f299cfc657e2a', '5fcb6fab3ad6faec', '6c44198c4a475817']; + for ($i = 0; $i < 80; $i++) { + list(, $k[$i]) = \unpack('J', \pack('H*', $k[$i])); + } + } + // Pre-processing + $length = \strlen($m); + // to round to nearest 112 mod 128, we'll add 128 - (length + (128 - 112)) % 128 + $m .= \str_repeat(\chr(0), 128 - ($length + 16 & 0x7f)); + $m[$length] = \chr(0x80); + // we don't support hashing strings 512MB long + $m .= \pack('N4', 0, 0, 0, $length << 3); + // Process the message in successive 1024-bit chunks + $chunks = \str_split($m, 128); + foreach ($chunks as $chunk) { + $w = []; + for ($i = 0; $i < 16; $i++) { + list(, $w[]) = \unpack('J', \FluentSmtpLib\phpseclib3\Common\Functions\Strings::shift($chunk, 8)); + } + // Extend the sixteen 32-bit words into eighty 32-bit words + for ($i = 16; $i < 80; $i++) { + $temp = [self::rotateRight64($w[$i - 15], 1), self::rotateRight64($w[$i - 15], 8), $w[$i - 15] >> 7 & 0x1ffffffffffffff]; + $s0 = $temp[0] ^ $temp[1] ^ $temp[2]; + $temp = [self::rotateRight64($w[$i - 2], 19), self::rotateRight64($w[$i - 2], 61), $w[$i - 2] >> 6 & 0x3ffffffffffffff]; + $s1 = $temp[0] ^ $temp[1] ^ $temp[2]; + $w[$i] = $w[$i - 16]; + $w[$i] = self::add64($w[$i], $s0); + $w[$i] = self::add64($w[$i], $w[$i - 7]); + $w[$i] = self::add64($w[$i], $s1); + } + // Initialize hash value for this chunk + list($a, $b, $c, $d, $e, $f, $g, $h) = $hash; + // Main loop + for ($i = 0; $i < 80; $i++) { + $temp = [self::rotateRight64($a, 28), self::rotateRight64($a, 34), self::rotateRight64($a, 39)]; + $s0 = $temp[0] ^ $temp[1] ^ $temp[2]; + $temp = [$a & $b, $a & $c, $b & $c]; + $maj = $temp[0] ^ $temp[1] ^ $temp[2]; + $t2 = self::add64($s0, $maj); + $temp = [self::rotateRight64($e, 14), self::rotateRight64($e, 18), self::rotateRight64($e, 41)]; + $s1 = $temp[0] ^ $temp[1] ^ $temp[2]; + $ch = $e & $f ^ $g & ~$e; + $t1 = self::add64($h, $s1); + $t1 = self::add64($t1, $ch); + $t1 = self::add64($t1, $k[$i]); + $t1 = self::add64($t1, $w[$i]); + $h = $g; + $g = $f; + $f = $e; + $e = self::add64($d, $t1); + $d = $c; + $c = $b; + $b = $a; + $a = self::add64($t1, $t2); + } + // Add this chunk's hash to result so far + $hash = [self::add64($hash[0], $a), self::add64($hash[1], $b), self::add64($hash[2], $c), self::add64($hash[3], $d), self::add64($hash[4], $e), self::add64($hash[5], $f), self::add64($hash[6], $g), self::add64($hash[7], $h)]; + } + // Produce the final hash value (big-endian) + // (\phpseclib3\Crypt\Hash::hash() trims the output for hashes but not for HMACs. as such, we trim the output here) + return \pack('J*', ...$hash); + } + /** + * __toString() magic method + */ + public function __toString() + { + return $this->getHash(); + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/PublicKeyLoader.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/PublicKeyLoader.php new file mode 100644 index 0000000..75e94b5 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/PublicKeyLoader.php @@ -0,0 +1,102 @@ + + * @copyright 2009 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\Crypt; + +use FluentSmtpLib\phpseclib3\Crypt\Common\AsymmetricKey; +use FluentSmtpLib\phpseclib3\Crypt\Common\PrivateKey; +use FluentSmtpLib\phpseclib3\Crypt\Common\PublicKey; +use FluentSmtpLib\phpseclib3\Exception\NoKeyLoadedException; +use FluentSmtpLib\phpseclib3\File\X509; +/** + * PublicKeyLoader + * + * @author Jim Wigginton + */ +abstract class PublicKeyLoader +{ + /** + * Loads a public or private key + * + * @return AsymmetricKey + * @param string|array $key + * @param string $password optional + */ + public static function load($key, $password = \false) + { + try { + return \FluentSmtpLib\phpseclib3\Crypt\EC::load($key, $password); + } catch (\FluentSmtpLib\phpseclib3\Exception\NoKeyLoadedException $e) { + } + try { + return \FluentSmtpLib\phpseclib3\Crypt\RSA::load($key, $password); + } catch (\FluentSmtpLib\phpseclib3\Exception\NoKeyLoadedException $e) { + } + try { + return \FluentSmtpLib\phpseclib3\Crypt\DSA::load($key, $password); + } catch (\FluentSmtpLib\phpseclib3\Exception\NoKeyLoadedException $e) { + } + try { + $x509 = new \FluentSmtpLib\phpseclib3\File\X509(); + $x509->loadX509($key); + $key = $x509->getPublicKey(); + if ($key) { + return $key; + } + } catch (\Exception $e) { + } + throw new \FluentSmtpLib\phpseclib3\Exception\NoKeyLoadedException('Unable to read key'); + } + /** + * Loads a private key + * + * @return PrivateKey + * @param string|array $key + * @param string $password optional + */ + public static function loadPrivateKey($key, $password = \false) + { + $key = self::load($key, $password); + if (!$key instanceof \FluentSmtpLib\phpseclib3\Crypt\Common\PrivateKey) { + throw new \FluentSmtpLib\phpseclib3\Exception\NoKeyLoadedException('The key that was loaded was not a private key'); + } + return $key; + } + /** + * Loads a public key + * + * @return PublicKey + * @param string|array $key + */ + public static function loadPublicKey($key) + { + $key = self::load($key); + if (!$key instanceof \FluentSmtpLib\phpseclib3\Crypt\Common\PublicKey) { + throw new \FluentSmtpLib\phpseclib3\Exception\NoKeyLoadedException('The key that was loaded was not a public key'); + } + return $key; + } + /** + * Loads parameters + * + * @return AsymmetricKey + * @param string|array $key + */ + public static function loadParameters($key) + { + $key = self::load($key); + if (!$key instanceof \FluentSmtpLib\phpseclib3\Crypt\Common\PrivateKey && !$key instanceof \FluentSmtpLib\phpseclib3\Crypt\Common\PublicKey) { + throw new \FluentSmtpLib\phpseclib3\Exception\NoKeyLoadedException('The key that was loaded was not a parameter'); + } + return $key; + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/RC2.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/RC2.php new file mode 100644 index 0000000..8ac91d1 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/RC2.php @@ -0,0 +1,478 @@ + + * setKey('abcdefgh'); + * + * $plaintext = str_repeat('a', 1024); + * + * echo $rc2->decrypt($rc2->encrypt($plaintext)); + * ?> + * + * + * @author Patrick Monnerat + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\Crypt; + +use FluentSmtpLib\phpseclib3\Crypt\Common\BlockCipher; +use FluentSmtpLib\phpseclib3\Exception\BadModeException; +/** + * Pure-PHP implementation of RC2. + * + */ +class RC2 extends \FluentSmtpLib\phpseclib3\Crypt\Common\BlockCipher +{ + /** + * Block Length of the cipher + * + * @see \phpseclib3\Crypt\Common\SymmetricKey::block_size + * @var int + */ + protected $block_size = 8; + /** + * The Key + * + * @see \phpseclib3\Crypt\Common\SymmetricKey::key + * @see self::setKey() + * @var string + */ + protected $key; + /** + * The Original (unpadded) Key + * + * @see \phpseclib3\Crypt\Common\SymmetricKey::key + * @see self::setKey() + * @see self::encrypt() + * @see self::decrypt() + * @var string + */ + private $orig_key; + /** + * Key Length (in bytes) + * + * @see \phpseclib3\Crypt\RC2::setKeyLength() + * @var int + */ + protected $key_length = 16; + // = 128 bits + /** + * The mcrypt specific name of the cipher + * + * @see \phpseclib3\Crypt\Common\SymmetricKey::cipher_name_mcrypt + * @var string + */ + protected $cipher_name_mcrypt = 'rc2'; + /** + * Optimizing value while CFB-encrypting + * + * @see \phpseclib3\Crypt\Common\SymmetricKey::cfb_init_len + * @var int + */ + protected $cfb_init_len = 500; + /** + * The key length in bits. + * + * {@internal Should be in range [1..1024].} + * + * {@internal Changing this value after setting the key has no effect.} + * + * @see self::setKeyLength() + * @see self::setKey() + * @var int + */ + private $default_key_length = 1024; + /** + * The key length in bits. + * + * {@internal Should be in range [1..1024].} + * + * @see self::isValidEnine() + * @see self::setKey() + * @var int + */ + private $current_key_length; + /** + * The Key Schedule + * + * @see self::setupKey() + * @var array + */ + private $keys; + /** + * Key expansion randomization table. + * Twice the same 256-value sequence to save a modulus in key expansion. + * + * @see self::setKey() + * @var array + */ + private static $pitable = [0xd9, 0x78, 0xf9, 0xc4, 0x19, 0xdd, 0xb5, 0xed, 0x28, 0xe9, 0xfd, 0x79, 0x4a, 0xa0, 0xd8, 0x9d, 0xc6, 0x7e, 0x37, 0x83, 0x2b, 0x76, 0x53, 0x8e, 0x62, 0x4c, 0x64, 0x88, 0x44, 0x8b, 0xfb, 0xa2, 0x17, 0x9a, 0x59, 0xf5, 0x87, 0xb3, 0x4f, 0x13, 0x61, 0x45, 0x6d, 0x8d, 0x9, 0x81, 0x7d, 0x32, 0xbd, 0x8f, 0x40, 0xeb, 0x86, 0xb7, 0x7b, 0xb, 0xf0, 0x95, 0x21, 0x22, 0x5c, 0x6b, 0x4e, 0x82, 0x54, 0xd6, 0x65, 0x93, 0xce, 0x60, 0xb2, 0x1c, 0x73, 0x56, 0xc0, 0x14, 0xa7, 0x8c, 0xf1, 0xdc, 0x12, 0x75, 0xca, 0x1f, 0x3b, 0xbe, 0xe4, 0xd1, 0x42, 0x3d, 0xd4, 0x30, 0xa3, 0x3c, 0xb6, 0x26, 0x6f, 0xbf, 0xe, 0xda, 0x46, 0x69, 0x7, 0x57, 0x27, 0xf2, 0x1d, 0x9b, 0xbc, 0x94, 0x43, 0x3, 0xf8, 0x11, 0xc7, 0xf6, 0x90, 0xef, 0x3e, 0xe7, 0x6, 0xc3, 0xd5, 0x2f, 0xc8, 0x66, 0x1e, 0xd7, 0x8, 0xe8, 0xea, 0xde, 0x80, 0x52, 0xee, 0xf7, 0x84, 0xaa, 0x72, 0xac, 0x35, 0x4d, 0x6a, 0x2a, 0x96, 0x1a, 0xd2, 0x71, 0x5a, 0x15, 0x49, 0x74, 0x4b, 0x9f, 0xd0, 0x5e, 0x4, 0x18, 0xa4, 0xec, 0xc2, 0xe0, 0x41, 0x6e, 0xf, 0x51, 0xcb, 0xcc, 0x24, 0x91, 0xaf, 0x50, 0xa1, 0xf4, 0x70, 0x39, 0x99, 0x7c, 0x3a, 0x85, 0x23, 0xb8, 0xb4, 0x7a, 0xfc, 0x2, 0x36, 0x5b, 0x25, 0x55, 0x97, 0x31, 0x2d, 0x5d, 0xfa, 0x98, 0xe3, 0x8a, 0x92, 0xae, 0x5, 0xdf, 0x29, 0x10, 0x67, 0x6c, 0xba, 0xc9, 0xd3, 0x0, 0xe6, 0xcf, 0xe1, 0x9e, 0xa8, 0x2c, 0x63, 0x16, 0x1, 0x3f, 0x58, 0xe2, 0x89, 0xa9, 0xd, 0x38, 0x34, 0x1b, 0xab, 0x33, 0xff, 0xb0, 0xbb, 0x48, 0xc, 0x5f, 0xb9, 0xb1, 0xcd, 0x2e, 0xc5, 0xf3, 0xdb, 0x47, 0xe5, 0xa5, 0x9c, 0x77, 0xa, 0xa6, 0x20, 0x68, 0xfe, 0x7f, 0xc1, 0xad, 0xd9, 0x78, 0xf9, 0xc4, 0x19, 0xdd, 0xb5, 0xed, 0x28, 0xe9, 0xfd, 0x79, 0x4a, 0xa0, 0xd8, 0x9d, 0xc6, 0x7e, 0x37, 0x83, 0x2b, 0x76, 0x53, 0x8e, 0x62, 0x4c, 0x64, 0x88, 0x44, 0x8b, 0xfb, 0xa2, 0x17, 0x9a, 0x59, 0xf5, 0x87, 0xb3, 0x4f, 0x13, 0x61, 0x45, 0x6d, 0x8d, 0x9, 0x81, 0x7d, 0x32, 0xbd, 0x8f, 0x40, 0xeb, 0x86, 0xb7, 0x7b, 0xb, 0xf0, 0x95, 0x21, 0x22, 0x5c, 0x6b, 0x4e, 0x82, 0x54, 0xd6, 0x65, 0x93, 0xce, 0x60, 0xb2, 0x1c, 0x73, 0x56, 0xc0, 0x14, 0xa7, 0x8c, 0xf1, 0xdc, 0x12, 0x75, 0xca, 0x1f, 0x3b, 0xbe, 0xe4, 0xd1, 0x42, 0x3d, 0xd4, 0x30, 0xa3, 0x3c, 0xb6, 0x26, 0x6f, 0xbf, 0xe, 0xda, 0x46, 0x69, 0x7, 0x57, 0x27, 0xf2, 0x1d, 0x9b, 0xbc, 0x94, 0x43, 0x3, 0xf8, 0x11, 0xc7, 0xf6, 0x90, 0xef, 0x3e, 0xe7, 0x6, 0xc3, 0xd5, 0x2f, 0xc8, 0x66, 0x1e, 0xd7, 0x8, 0xe8, 0xea, 0xde, 0x80, 0x52, 0xee, 0xf7, 0x84, 0xaa, 0x72, 0xac, 0x35, 0x4d, 0x6a, 0x2a, 0x96, 0x1a, 0xd2, 0x71, 0x5a, 0x15, 0x49, 0x74, 0x4b, 0x9f, 0xd0, 0x5e, 0x4, 0x18, 0xa4, 0xec, 0xc2, 0xe0, 0x41, 0x6e, 0xf, 0x51, 0xcb, 0xcc, 0x24, 0x91, 0xaf, 0x50, 0xa1, 0xf4, 0x70, 0x39, 0x99, 0x7c, 0x3a, 0x85, 0x23, 0xb8, 0xb4, 0x7a, 0xfc, 0x2, 0x36, 0x5b, 0x25, 0x55, 0x97, 0x31, 0x2d, 0x5d, 0xfa, 0x98, 0xe3, 0x8a, 0x92, 0xae, 0x5, 0xdf, 0x29, 0x10, 0x67, 0x6c, 0xba, 0xc9, 0xd3, 0x0, 0xe6, 0xcf, 0xe1, 0x9e, 0xa8, 0x2c, 0x63, 0x16, 0x1, 0x3f, 0x58, 0xe2, 0x89, 0xa9, 0xd, 0x38, 0x34, 0x1b, 0xab, 0x33, 0xff, 0xb0, 0xbb, 0x48, 0xc, 0x5f, 0xb9, 0xb1, 0xcd, 0x2e, 0xc5, 0xf3, 0xdb, 0x47, 0xe5, 0xa5, 0x9c, 0x77, 0xa, 0xa6, 0x20, 0x68, 0xfe, 0x7f, 0xc1, 0xad]; + /** + * Inverse key expansion randomization table. + * + * @see self::setKey() + * @var array + */ + private static $invpitable = [0xd1, 0xda, 0xb9, 0x6f, 0x9c, 0xc8, 0x78, 0x66, 0x80, 0x2c, 0xf8, 0x37, 0xea, 0xe0, 0x62, 0xa4, 0xcb, 0x71, 0x50, 0x27, 0x4b, 0x95, 0xd9, 0x20, 0x9d, 0x4, 0x91, 0xe3, 0x47, 0x6a, 0x7e, 0x53, 0xfa, 0x3a, 0x3b, 0xb4, 0xa8, 0xbc, 0x5f, 0x68, 0x8, 0xca, 0x8f, 0x14, 0xd7, 0xc0, 0xef, 0x7b, 0x5b, 0xbf, 0x2f, 0xe5, 0xe2, 0x8c, 0xba, 0x12, 0xe1, 0xaf, 0xb2, 0x54, 0x5d, 0x59, 0x76, 0xdb, 0x32, 0xa2, 0x58, 0x6e, 0x1c, 0x29, 0x64, 0xf3, 0xe9, 0x96, 0xc, 0x98, 0x19, 0x8d, 0x3e, 0x26, 0xab, 0xa5, 0x85, 0x16, 0x40, 0xbd, 0x49, 0x67, 0xdc, 0x22, 0x94, 0xbb, 0x3c, 0xc1, 0x9b, 0xeb, 0x45, 0x28, 0x18, 0xd8, 0x1a, 0x42, 0x7d, 0xcc, 0xfb, 0x65, 0x8e, 0x3d, 0xcd, 0x2a, 0xa3, 0x60, 0xae, 0x93, 0x8a, 0x48, 0x97, 0x51, 0x15, 0xf7, 0x1, 0xb, 0xb7, 0x36, 0xb1, 0x2e, 0x11, 0xfd, 0x84, 0x2d, 0x3f, 0x13, 0x88, 0xb3, 0x34, 0x24, 0x1b, 0xde, 0xc5, 0x1d, 0x4d, 0x2b, 0x17, 0x31, 0x74, 0xa9, 0xc6, 0x43, 0x6d, 0x39, 0x90, 0xbe, 0xc3, 0xb0, 0x21, 0x6b, 0xf6, 0xf, 0xd5, 0x99, 0xd, 0xac, 0x1f, 0x5c, 0x9e, 0xf5, 0xf9, 0x4c, 0xd6, 0xdf, 0x89, 0xe4, 0x8b, 0xff, 0xc7, 0xaa, 0xe7, 0xed, 0x46, 0x25, 0xb6, 0x6, 0x5e, 0x35, 0xb5, 0xec, 0xce, 0xe8, 0x6c, 0x30, 0x55, 0x61, 0x4a, 0xfe, 0xa0, 0x79, 0x3, 0xf0, 0x10, 0x72, 0x7c, 0xcf, 0x52, 0xa6, 0xa7, 0xee, 0x44, 0xd3, 0x9a, 0x57, 0x92, 0xd0, 0x5a, 0x7a, 0x41, 0x7f, 0xe, 0x0, 0x63, 0xf2, 0x4f, 0x5, 0x83, 0xc9, 0xa1, 0xd4, 0xdd, 0xc4, 0x56, 0xf4, 0xd2, 0x77, 0x81, 0x9, 0x82, 0x33, 0x9f, 0x7, 0x86, 0x75, 0x38, 0x4e, 0x69, 0xf1, 0xad, 0x23, 0x73, 0x87, 0x70, 0x2, 0xc2, 0x1e, 0xb8, 0xa, 0xfc, 0xe6]; + /** + * Default Constructor. + * + * @param string $mode + * @throws \InvalidArgumentException if an invalid / unsupported mode is provided + */ + public function __construct($mode) + { + parent::__construct($mode); + if ($this->mode == self::MODE_STREAM) { + throw new \FluentSmtpLib\phpseclib3\Exception\BadModeException('Block ciphers cannot be ran in stream mode'); + } + } + /** + * Test for engine validity + * + * This is mainly just a wrapper to set things up for \phpseclib3\Crypt\Common\SymmetricKey::isValidEngine() + * + * @see \phpseclib3\Crypt\Common\SymmetricKey::__construct() + * @param int $engine + * @return bool + */ + protected function isValidEngineHelper($engine) + { + switch ($engine) { + case self::ENGINE_OPENSSL: + if ($this->current_key_length != 128 || \strlen($this->orig_key) < 16) { + return \false; + } + // quoting https://www.openssl.org/news/openssl-3.0-notes.html, OpenSSL 3.0.1 + // "Moved all variations of the EVP ciphers CAST5, BF, IDEA, SEED, RC2, RC4, RC5, and DES to the legacy provider" + // in theory openssl_get_cipher_methods() should catch this but, on GitHub Actions, at least, it does not + if (\defined('OPENSSL_VERSION_TEXT') && \version_compare(\preg_replace('#OpenSSL (\\d+\\.\\d+\\.\\d+) .*#', '$1', \OPENSSL_VERSION_TEXT), '3.0.1', '>=')) { + return \false; + } + $this->cipher_name_openssl_ecb = 'rc2-ecb'; + $this->cipher_name_openssl = 'rc2-' . $this->openssl_translate_mode(); + } + return parent::isValidEngineHelper($engine); + } + /** + * Sets the key length. + * + * Valid key lengths are 8 to 1024. + * Calling this function after setting the key has no effect until the next + * \phpseclib3\Crypt\RC2::setKey() call. + * + * @param int $length in bits + * @throws \LengthException if the key length isn't supported + */ + public function setKeyLength($length) + { + if ($length < 8 || $length > 1024) { + throw new \LengthException('Key size of ' . $length . ' bits is not supported by this algorithm. Only keys between 1 and 1024 bits, inclusive, are supported'); + } + $this->default_key_length = $this->current_key_length = $length; + $this->explicit_key_length = $length >> 3; + } + /** + * Returns the current key length + * + * @return int + */ + public function getKeyLength() + { + return $this->current_key_length; + } + /** + * Sets the key. + * + * Keys can be of any length. RC2, itself, uses 8 to 1024 bit keys (eg. + * strlen($key) <= 128), however, we only use the first 128 bytes if $key + * has more then 128 bytes in it, and set $key to a single null byte if + * it is empty. + * + * @see \phpseclib3\Crypt\Common\SymmetricKey::setKey() + * @param string $key + * @param int|boolean $t1 optional Effective key length in bits. + * @throws \LengthException if the key length isn't supported + */ + public function setKey($key, $t1 = \false) + { + $this->orig_key = $key; + if ($t1 === \false) { + $t1 = $this->default_key_length; + } + if ($t1 < 1 || $t1 > 1024) { + throw new \LengthException('Key size of ' . $length . ' bits is not supported by this algorithm. Only keys between 1 and 1024 bits, inclusive, are supported'); + } + $this->current_key_length = $t1; + if (\strlen($key) < 1 || \strlen($key) > 128) { + throw new \LengthException('Key of size ' . \strlen($key) . ' not supported by this algorithm. Only keys of sizes between 8 and 1024 bits, inclusive, are supported'); + } + $t = \strlen($key); + // The mcrypt RC2 implementation only supports effective key length + // of 1024 bits. It is however possible to handle effective key + // lengths in range 1..1024 by expanding the key and applying + // inverse pitable mapping to the first byte before submitting it + // to mcrypt. + // Key expansion. + $l = \array_values(\unpack('C*', $key)); + $t8 = $t1 + 7 >> 3; + $tm = 0xff >> 8 * $t8 - $t1; + // Expand key. + $pitable = self::$pitable; + for ($i = $t; $i < 128; $i++) { + $l[$i] = $pitable[$l[$i - 1] + $l[$i - $t]]; + } + $i = 128 - $t8; + $l[$i] = $pitable[$l[$i] & $tm]; + while ($i--) { + $l[$i] = $pitable[$l[$i + 1] ^ $l[$i + $t8]]; + } + // Prepare the key for mcrypt. + $l[0] = self::$invpitable[$l[0]]; + \array_unshift($l, 'C*'); + $this->key = \pack(...$l); + $this->key_length = \strlen($this->key); + $this->changed = $this->nonIVChanged = \true; + $this->setEngine(); + } + /** + * Encrypts a message. + * + * Mostly a wrapper for \phpseclib3\Crypt\Common\SymmetricKey::encrypt, with some additional OpenSSL handling code + * + * @see self::decrypt() + * @param string $plaintext + * @return string $ciphertext + */ + public function encrypt($plaintext) + { + if ($this->engine == self::ENGINE_OPENSSL) { + $temp = $this->key; + $this->key = $this->orig_key; + $result = parent::encrypt($plaintext); + $this->key = $temp; + return $result; + } + return parent::encrypt($plaintext); + } + /** + * Decrypts a message. + * + * Mostly a wrapper for \phpseclib3\Crypt\Common\SymmetricKey::decrypt, with some additional OpenSSL handling code + * + * @see self::encrypt() + * @param string $ciphertext + * @return string $plaintext + */ + public function decrypt($ciphertext) + { + if ($this->engine == self::ENGINE_OPENSSL) { + $temp = $this->key; + $this->key = $this->orig_key; + $result = parent::decrypt($ciphertext); + $this->key = $temp; + return $result; + } + return parent::decrypt($ciphertext); + } + /** + * Encrypts a block + * + * @see \phpseclib3\Crypt\Common\SymmetricKey::encryptBlock() + * @see \phpseclib3\Crypt\Common\SymmetricKey::encrypt() + * @param string $in + * @return string + */ + protected function encryptBlock($in) + { + list($r0, $r1, $r2, $r3) = \array_values(\unpack('v*', $in)); + $keys = $this->keys; + $limit = 20; + $actions = [$limit => 44, 44 => 64]; + $j = 0; + for (;;) { + // Mixing round. + $r0 = ($r0 + $keys[$j++] + (($r1 ^ $r2) & $r3 ^ $r1) & 0xffff) << 1; + $r0 |= $r0 >> 16; + $r1 = ($r1 + $keys[$j++] + (($r2 ^ $r3) & $r0 ^ $r2) & 0xffff) << 2; + $r1 |= $r1 >> 16; + $r2 = ($r2 + $keys[$j++] + (($r3 ^ $r0) & $r1 ^ $r3) & 0xffff) << 3; + $r2 |= $r2 >> 16; + $r3 = ($r3 + $keys[$j++] + (($r0 ^ $r1) & $r2 ^ $r0) & 0xffff) << 5; + $r3 |= $r3 >> 16; + if ($j === $limit) { + if ($limit === 64) { + break; + } + // Mashing round. + $r0 += $keys[$r3 & 0x3f]; + $r1 += $keys[$r0 & 0x3f]; + $r2 += $keys[$r1 & 0x3f]; + $r3 += $keys[$r2 & 0x3f]; + $limit = $actions[$limit]; + } + } + return \pack('vvvv', $r0, $r1, $r2, $r3); + } + /** + * Decrypts a block + * + * @see \phpseclib3\Crypt\Common\SymmetricKey::decryptBlock() + * @see \phpseclib3\Crypt\Common\SymmetricKey::decrypt() + * @param string $in + * @return string + */ + protected function decryptBlock($in) + { + list($r0, $r1, $r2, $r3) = \array_values(\unpack('v*', $in)); + $keys = $this->keys; + $limit = 44; + $actions = [$limit => 20, 20 => 0]; + $j = 64; + for (;;) { + // R-mixing round. + $r3 = ($r3 | $r3 << 16) >> 5; + $r3 = $r3 - $keys[--$j] - (($r0 ^ $r1) & $r2 ^ $r0) & 0xffff; + $r2 = ($r2 | $r2 << 16) >> 3; + $r2 = $r2 - $keys[--$j] - (($r3 ^ $r0) & $r1 ^ $r3) & 0xffff; + $r1 = ($r1 | $r1 << 16) >> 2; + $r1 = $r1 - $keys[--$j] - (($r2 ^ $r3) & $r0 ^ $r2) & 0xffff; + $r0 = ($r0 | $r0 << 16) >> 1; + $r0 = $r0 - $keys[--$j] - (($r1 ^ $r2) & $r3 ^ $r1) & 0xffff; + if ($j === $limit) { + if ($limit === 0) { + break; + } + // R-mashing round. + $r3 = $r3 - $keys[$r2 & 0x3f] & 0xffff; + $r2 = $r2 - $keys[$r1 & 0x3f] & 0xffff; + $r1 = $r1 - $keys[$r0 & 0x3f] & 0xffff; + $r0 = $r0 - $keys[$r3 & 0x3f] & 0xffff; + $limit = $actions[$limit]; + } + } + return \pack('vvvv', $r0, $r1, $r2, $r3); + } + /** + * Creates the key schedule + * + * @see \phpseclib3\Crypt\Common\SymmetricKey::setupKey() + */ + protected function setupKey() + { + if (!isset($this->key)) { + $this->setKey(''); + } + // Key has already been expanded in \phpseclib3\Crypt\RC2::setKey(): + // Only the first value must be altered. + $l = \unpack('Ca/Cb/v*', $this->key); + \array_unshift($l, self::$pitable[$l['a']] | $l['b'] << 8); + unset($l['a']); + unset($l['b']); + $this->keys = $l; + } + /** + * Setup the performance-optimized function for de/encrypt() + * + * @see \phpseclib3\Crypt\Common\SymmetricKey::setupInlineCrypt() + */ + protected function setupInlineCrypt() + { + // Init code for both, encrypt and decrypt. + $init_crypt = '$keys = $this->keys;'; + $keys = $this->keys; + // $in is the current 8 bytes block which has to be en/decrypt + $encrypt_block = $decrypt_block = ' + $in = unpack("v4", $in); + $r0 = $in[1]; + $r1 = $in[2]; + $r2 = $in[3]; + $r3 = $in[4]; + '; + // Create code for encryption. + $limit = 20; + $actions = [$limit => 44, 44 => 64]; + $j = 0; + for (;;) { + // Mixing round. + $encrypt_block .= ' + $r0 = (($r0 + ' . $keys[$j++] . ' + + ((($r1 ^ $r2) & $r3) ^ $r1)) & 0xFFFF) << 1; + $r0 |= $r0 >> 16; + $r1 = (($r1 + ' . $keys[$j++] . ' + + ((($r2 ^ $r3) & $r0) ^ $r2)) & 0xFFFF) << 2; + $r1 |= $r1 >> 16; + $r2 = (($r2 + ' . $keys[$j++] . ' + + ((($r3 ^ $r0) & $r1) ^ $r3)) & 0xFFFF) << 3; + $r2 |= $r2 >> 16; + $r3 = (($r3 + ' . $keys[$j++] . ' + + ((($r0 ^ $r1) & $r2) ^ $r0)) & 0xFFFF) << 5; + $r3 |= $r3 >> 16;'; + if ($j === $limit) { + if ($limit === 64) { + break; + } + // Mashing round. + $encrypt_block .= ' + $r0 += $keys[$r3 & 0x3F]; + $r1 += $keys[$r0 & 0x3F]; + $r2 += $keys[$r1 & 0x3F]; + $r3 += $keys[$r2 & 0x3F];'; + $limit = $actions[$limit]; + } + } + $encrypt_block .= '$in = pack("v4", $r0, $r1, $r2, $r3);'; + // Create code for decryption. + $limit = 44; + $actions = [$limit => 20, 20 => 0]; + $j = 64; + for (;;) { + // R-mixing round. + $decrypt_block .= ' + $r3 = ($r3 | ($r3 << 16)) >> 5; + $r3 = ($r3 - ' . $keys[--$j] . ' - + ((($r0 ^ $r1) & $r2) ^ $r0)) & 0xFFFF; + $r2 = ($r2 | ($r2 << 16)) >> 3; + $r2 = ($r2 - ' . $keys[--$j] . ' - + ((($r3 ^ $r0) & $r1) ^ $r3)) & 0xFFFF; + $r1 = ($r1 | ($r1 << 16)) >> 2; + $r1 = ($r1 - ' . $keys[--$j] . ' - + ((($r2 ^ $r3) & $r0) ^ $r2)) & 0xFFFF; + $r0 = ($r0 | ($r0 << 16)) >> 1; + $r0 = ($r0 - ' . $keys[--$j] . ' - + ((($r1 ^ $r2) & $r3) ^ $r1)) & 0xFFFF;'; + if ($j === $limit) { + if ($limit === 0) { + break; + } + // R-mashing round. + $decrypt_block .= ' + $r3 = ($r3 - $keys[$r2 & 0x3F]) & 0xFFFF; + $r2 = ($r2 - $keys[$r1 & 0x3F]) & 0xFFFF; + $r1 = ($r1 - $keys[$r0 & 0x3F]) & 0xFFFF; + $r0 = ($r0 - $keys[$r3 & 0x3F]) & 0xFFFF;'; + $limit = $actions[$limit]; + } + } + $decrypt_block .= '$in = pack("v4", $r0, $r1, $r2, $r3);'; + // Creates the inline-crypt function + $this->inline_crypt = $this->createInlineCryptFunction(['init_crypt' => $init_crypt, 'encrypt_block' => $encrypt_block, 'decrypt_block' => $decrypt_block]); + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/RC4.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/RC4.php new file mode 100644 index 0000000..17e7952 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/RC4.php @@ -0,0 +1,258 @@ + + * setKey('abcdefgh'); + * + * $size = 10 * 1024; + * $plaintext = ''; + * for ($i = 0; $i < $size; $i++) { + * $plaintext.= 'a'; + * } + * + * echo $rc4->decrypt($rc4->encrypt($plaintext)); + * ?> + * + * + * @author Jim Wigginton + * @copyright 2007 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\Crypt; + +use FluentSmtpLib\phpseclib3\Crypt\Common\StreamCipher; +/** + * Pure-PHP implementation of RC4. + * + * @author Jim Wigginton + */ +class RC4 extends \FluentSmtpLib\phpseclib3\Crypt\Common\StreamCipher +{ + /** + * @see \phpseclib3\Crypt\RC4::_crypt() + */ + const ENCRYPT = 0; + /** + * @see \phpseclib3\Crypt\RC4::_crypt() + */ + const DECRYPT = 1; + /** + * Key Length (in bytes) + * + * @see \phpseclib3\Crypt\RC4::setKeyLength() + * @var int + */ + protected $key_length = 128; + // = 1024 bits + /** + * The mcrypt specific name of the cipher + * + * @see \phpseclib3\Crypt\Common\SymmetricKey::cipher_name_mcrypt + * @var string + */ + protected $cipher_name_mcrypt = 'arcfour'; + /** + * The Key + * + * @see self::setKey() + * @var string + */ + protected $key; + /** + * The Key Stream for decryption and encryption + * + * @see self::setKey() + * @var array + */ + private $stream; + /** + * Test for engine validity + * + * This is mainly just a wrapper to set things up for \phpseclib3\Crypt\Common\SymmetricKey::isValidEngine() + * + * @see \phpseclib3\Crypt\Common\SymmetricKey::__construct() + * @param int $engine + * @return bool + */ + protected function isValidEngineHelper($engine) + { + if ($engine == self::ENGINE_OPENSSL) { + if ($this->continuousBuffer) { + return \false; + } + // quoting https://www.openssl.org/news/openssl-3.0-notes.html, OpenSSL 3.0.1 + // "Moved all variations of the EVP ciphers CAST5, BF, IDEA, SEED, RC2, RC4, RC5, and DES to the legacy provider" + // in theory openssl_get_cipher_methods() should catch this but, on GitHub Actions, at least, it does not + if (\defined('OPENSSL_VERSION_TEXT') && \version_compare(\preg_replace('#OpenSSL (\\d+\\.\\d+\\.\\d+) .*#', '$1', \OPENSSL_VERSION_TEXT), '3.0.1', '>=')) { + return \false; + } + $this->cipher_name_openssl = 'rc4-40'; + } + return parent::isValidEngineHelper($engine); + } + /** + * Sets the key length + * + * Keys can be between 1 and 256 bytes long. + * + * @param int $length + * @throws \LengthException if the key length is invalid + */ + public function setKeyLength($length) + { + if ($length < 8 || $length > 2048) { + throw new \LengthException('Key size of ' . $length . ' bits is not supported by this algorithm. Only keys between 1 and 256 bytes are supported'); + } + $this->key_length = $length >> 3; + parent::setKeyLength($length); + } + /** + * Sets the key length + * + * Keys can be between 1 and 256 bytes long. + * + * @param string $key + */ + public function setKey($key) + { + $length = \strlen($key); + if ($length < 1 || $length > 256) { + throw new \LengthException('Key size of ' . $length . ' bytes is not supported by RC4. Keys must be between 1 and 256 bytes long'); + } + parent::setKey($key); + } + /** + * Encrypts a message. + * + * @see \phpseclib3\Crypt\Common\SymmetricKey::decrypt() + * @see self::crypt() + * @param string $plaintext + * @return string $ciphertext + */ + public function encrypt($plaintext) + { + if ($this->engine != self::ENGINE_INTERNAL) { + return parent::encrypt($plaintext); + } + return $this->crypt($plaintext, self::ENCRYPT); + } + /** + * Decrypts a message. + * + * $this->decrypt($this->encrypt($plaintext)) == $this->encrypt($this->encrypt($plaintext)). + * At least if the continuous buffer is disabled. + * + * @see \phpseclib3\Crypt\Common\SymmetricKey::encrypt() + * @see self::crypt() + * @param string $ciphertext + * @return string $plaintext + */ + public function decrypt($ciphertext) + { + if ($this->engine != self::ENGINE_INTERNAL) { + return parent::decrypt($ciphertext); + } + return $this->crypt($ciphertext, self::DECRYPT); + } + /** + * Encrypts a block + * + * @param string $in + */ + protected function encryptBlock($in) + { + // RC4 does not utilize this method + } + /** + * Decrypts a block + * + * @param string $in + */ + protected function decryptBlock($in) + { + // RC4 does not utilize this method + } + /** + * Setup the key (expansion) + * + * @see \phpseclib3\Crypt\Common\SymmetricKey::_setupKey() + */ + protected function setupKey() + { + $key = $this->key; + $keyLength = \strlen($key); + $keyStream = \range(0, 255); + $j = 0; + for ($i = 0; $i < 256; $i++) { + $j = $j + $keyStream[$i] + \ord($key[$i % $keyLength]) & 255; + $temp = $keyStream[$i]; + $keyStream[$i] = $keyStream[$j]; + $keyStream[$j] = $temp; + } + $this->stream = []; + $this->stream[self::DECRYPT] = $this->stream[self::ENCRYPT] = [ + 0, + // index $i + 0, + // index $j + $keyStream, + ]; + } + /** + * Encrypts or decrypts a message. + * + * @see self::encrypt() + * @see self::decrypt() + * @param string $text + * @param int $mode + * @return string $text + */ + private function crypt($text, $mode) + { + if ($this->changed) { + $this->setup(); + } + $stream =& $this->stream[$mode]; + if ($this->continuousBuffer) { + $i =& $stream[0]; + $j =& $stream[1]; + $keyStream =& $stream[2]; + } else { + $i = $stream[0]; + $j = $stream[1]; + $keyStream = $stream[2]; + } + $len = \strlen($text); + for ($k = 0; $k < $len; ++$k) { + $i = $i + 1 & 255; + $ksi = $keyStream[$i]; + $j = $j + $ksi & 255; + $ksj = $keyStream[$j]; + $keyStream[$i] = $ksj; + $keyStream[$j] = $ksi; + $text[$k] = $text[$k] ^ \chr($keyStream[$ksj + $ksi & 255]); + } + return $text; + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php new file mode 100644 index 0000000..6ccfaf3 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php @@ -0,0 +1,824 @@ + + * getPublicKey(); + * + * $plaintext = 'terrafrost'; + * + * $ciphertext = $public->encrypt($plaintext); + * + * echo $private->decrypt($ciphertext); + * ?> + * + * + * Here's an example of how to create signatures and verify signatures with this library: + * + * getPublicKey(); + * + * $plaintext = 'terrafrost'; + * + * $signature = $private->sign($plaintext); + * + * echo $public->verify($plaintext, $signature) ? 'verified' : 'unverified'; + * ?> + * + * + * One thing to consider when using this: so phpseclib uses PSS mode by default. + * Technically, id-RSASSA-PSS has a different key format than rsaEncryption. So + * should phpseclib save to the id-RSASSA-PSS format by default or the + * rsaEncryption format? For stand-alone keys I figure rsaEncryption is better + * because SSH doesn't use PSS and idk how many SSH servers would be able to + * decode an id-RSASSA-PSS key. For X.509 certificates the id-RSASSA-PSS + * format is used by default (unless you change it up to use PKCS1 instead) + * + * @author Jim Wigginton + * @copyright 2009 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\Crypt; + +use FluentSmtpLib\phpseclib3\Crypt\Common\AsymmetricKey; +use FluentSmtpLib\phpseclib3\Crypt\RSA\Formats\Keys\PSS; +use FluentSmtpLib\phpseclib3\Crypt\RSA\PrivateKey; +use FluentSmtpLib\phpseclib3\Crypt\RSA\PublicKey; +use FluentSmtpLib\phpseclib3\Exception\InconsistentSetupException; +use FluentSmtpLib\phpseclib3\Exception\UnsupportedAlgorithmException; +use FluentSmtpLib\phpseclib3\Math\BigInteger; +/** + * Pure-PHP PKCS#1 compliant implementation of RSA. + * + * @author Jim Wigginton + */ +abstract class RSA extends \FluentSmtpLib\phpseclib3\Crypt\Common\AsymmetricKey +{ + /** + * Algorithm Name + * + * @var string + */ + const ALGORITHM = 'RSA'; + /** + * Use {@link http://en.wikipedia.org/wiki/Optimal_Asymmetric_Encryption_Padding Optimal Asymmetric Encryption Padding} + * (OAEP) for encryption / decryption. + * + * Uses sha256 by default + * + * @see self::setHash() + * @see self::setMGFHash() + * @see self::encrypt() + * @see self::decrypt() + */ + const ENCRYPTION_OAEP = 1; + /** + * Use PKCS#1 padding. + * + * Although self::PADDING_OAEP / self::PADDING_PSS offers more security, including PKCS#1 padding is necessary for purposes of backwards + * compatibility with protocols (like SSH-1) written before OAEP's introduction. + * + * @see self::encrypt() + * @see self::decrypt() + */ + const ENCRYPTION_PKCS1 = 2; + /** + * Do not use any padding + * + * Although this method is not recommended it can none-the-less sometimes be useful if you're trying to decrypt some legacy + * stuff, if you're trying to diagnose why an encrypted message isn't decrypting, etc. + * + * @see self::encrypt() + * @see self::decrypt() + */ + const ENCRYPTION_NONE = 4; + /** + * Use the Probabilistic Signature Scheme for signing + * + * Uses sha256 and 0 as the salt length + * + * @see self::setSaltLength() + * @see self::setMGFHash() + * @see self::setHash() + * @see self::sign() + * @see self::verify() + * @see self::setHash() + */ + const SIGNATURE_PSS = 16; + /** + * Use a relaxed version of PKCS#1 padding for signature verification + * + * @see self::sign() + * @see self::verify() + * @see self::setHash() + */ + const SIGNATURE_RELAXED_PKCS1 = 32; + /** + * Use PKCS#1 padding for signature verification + * + * @see self::sign() + * @see self::verify() + * @see self::setHash() + */ + const SIGNATURE_PKCS1 = 64; + /** + * Encryption padding mode + * + * @var int + */ + protected $encryptionPadding = self::ENCRYPTION_OAEP; + /** + * Signature padding mode + * + * @var int + */ + protected $signaturePadding = self::SIGNATURE_PSS; + /** + * Length of hash function output + * + * @var int + */ + protected $hLen; + /** + * Length of salt + * + * @var int + */ + protected $sLen; + /** + * Label + * + * @var string + */ + protected $label = ''; + /** + * Hash function for the Mask Generation Function + * + * @var Hash + */ + protected $mgfHash; + /** + * Length of MGF hash function output + * + * @var int + */ + protected $mgfHLen; + /** + * Modulus (ie. n) + * + * @var Math\BigInteger + */ + protected $modulus; + /** + * Modulus length + * + * @var Math\BigInteger + */ + protected $k; + /** + * Exponent (ie. e or d) + * + * @var Math\BigInteger + */ + protected $exponent; + /** + * Default public exponent + * + * @var int + * @link http://en.wikipedia.org/wiki/65537_%28number%29 + */ + private static $defaultExponent = 65537; + /** + * Enable Blinding? + * + * @var bool + */ + protected static $enableBlinding = \true; + /** + * OpenSSL configuration file name. + * + * @see self::createKey() + * @var ?string + */ + protected static $configFile; + /** + * Smallest Prime + * + * Per , this number ought not result in primes smaller + * than 256 bits. As a consequence if the key you're trying to create is 1024 bits and you've set smallestPrime + * to 384 bits then you're going to get a 384 bit prime and a 640 bit prime (384 + 1024 % 384). At least if + * engine is set to self::ENGINE_INTERNAL. If Engine is set to self::ENGINE_OPENSSL then smallest Prime is + * ignored (ie. multi-prime RSA support is more intended as a way to speed up RSA key generation when there's + * a chance neither gmp nor OpenSSL are installed) + * + * @var int + */ + private static $smallestPrime = 4096; + /** + * Public Exponent + * + * @var Math\BigInteger + */ + protected $publicExponent; + /** + * Sets the public exponent for key generation + * + * This will be 65537 unless changed. + * + * @param int $val + */ + public static function setExponent($val) + { + self::$defaultExponent = $val; + } + /** + * Sets the smallest prime number in bits. Used for key generation + * + * This will be 4096 unless changed. + * + * @param int $val + */ + public static function setSmallestPrime($val) + { + self::$smallestPrime = $val; + } + /** + * Sets the OpenSSL config file path + * + * Set to the empty string to use the default config file + * + * @param string $val + */ + public static function setOpenSSLConfigPath($val) + { + self::$configFile = $val; + } + /** + * Create a private key + * + * The public key can be extracted from the private key + * + * @return PrivateKey + * @param int $bits + */ + public static function createKey($bits = 2048) + { + self::initialize_static_variables(); + $class = new \ReflectionClass(static::class); + if ($class->isFinal()) { + throw new \RuntimeException('createKey() should not be called from final classes (' . static::class . ')'); + } + $regSize = $bits >> 1; + // divide by two to see how many bits P and Q would be + if ($regSize > self::$smallestPrime) { + $num_primes = \floor($bits / self::$smallestPrime); + $regSize = self::$smallestPrime; + } else { + $num_primes = 2; + } + if ($num_primes == 2 && $bits >= 384 && self::$defaultExponent == 65537) { + if (!isset(self::$engines['PHP'])) { + self::useBestEngine(); + } + // OpenSSL uses 65537 as the exponent and requires RSA keys be 384 bits minimum + if (self::$engines['OpenSSL']) { + $config = []; + if (self::$configFile) { + $config['config'] = self::$configFile; + } + $rsa = \openssl_pkey_new(['private_key_bits' => $bits] + $config); + \openssl_pkey_export($rsa, $privatekeystr, null, $config); + // clear the buffer of error strings stemming from a minimalistic openssl.cnf + // https://github.com/php/php-src/issues/11054 talks about other errors this'll pick up + while (\openssl_error_string() !== \false) { + } + return \FluentSmtpLib\phpseclib3\Crypt\RSA::load($privatekeystr); + } + } + static $e; + if (!isset($e)) { + $e = new \FluentSmtpLib\phpseclib3\Math\BigInteger(self::$defaultExponent); + } + $n = clone self::$one; + $exponents = $coefficients = $primes = []; + $lcm = ['top' => clone self::$one, 'bottom' => \false]; + do { + for ($i = 1; $i <= $num_primes; $i++) { + if ($i != $num_primes) { + $primes[$i] = \FluentSmtpLib\phpseclib3\Math\BigInteger::randomPrime($regSize); + } else { + \extract(\FluentSmtpLib\phpseclib3\Math\BigInteger::minMaxBits($bits)); + /** @var BigInteger $min + * @var BigInteger $max + */ + list($min) = $min->divide($n); + $min = $min->add(self::$one); + list($max) = $max->divide($n); + $primes[$i] = \FluentSmtpLib\phpseclib3\Math\BigInteger::randomRangePrime($min, $max); + } + // the first coefficient is calculated differently from the rest + // ie. instead of being $primes[1]->modInverse($primes[2]), it's $primes[2]->modInverse($primes[1]) + if ($i > 2) { + $coefficients[$i] = $n->modInverse($primes[$i]); + } + $n = $n->multiply($primes[$i]); + $temp = $primes[$i]->subtract(self::$one); + // textbook RSA implementations use Euler's totient function instead of the least common multiple. + // see http://en.wikipedia.org/wiki/Euler%27s_totient_function + $lcm['top'] = $lcm['top']->multiply($temp); + $lcm['bottom'] = $lcm['bottom'] === \false ? $temp : $lcm['bottom']->gcd($temp); + } + list($temp) = $lcm['top']->divide($lcm['bottom']); + $gcd = $temp->gcd($e); + $i0 = 1; + } while (!$gcd->equals(self::$one)); + $coefficients[2] = $primes[2]->modInverse($primes[1]); + $d = $e->modInverse($temp); + foreach ($primes as $i => $prime) { + $temp = $prime->subtract(self::$one); + $exponents[$i] = $e->modInverse($temp); + } + // from : + // RSAPrivateKey ::= SEQUENCE { + // version Version, + // modulus INTEGER, -- n + // publicExponent INTEGER, -- e + // privateExponent INTEGER, -- d + // prime1 INTEGER, -- p + // prime2 INTEGER, -- q + // exponent1 INTEGER, -- d mod (p-1) + // exponent2 INTEGER, -- d mod (q-1) + // coefficient INTEGER, -- (inverse of q) mod p + // otherPrimeInfos OtherPrimeInfos OPTIONAL + // } + $privatekey = new \FluentSmtpLib\phpseclib3\Crypt\RSA\PrivateKey(); + $privatekey->modulus = $n; + $privatekey->k = $bits >> 3; + $privatekey->publicExponent = $e; + $privatekey->exponent = $d; + $privatekey->primes = $primes; + $privatekey->exponents = $exponents; + $privatekey->coefficients = $coefficients; + /* + $publickey = new PublicKey; + $publickey->modulus = $n; + $publickey->k = $bits >> 3; + $publickey->exponent = $e; + $publickey->publicExponent = $e; + $publickey->isPublic = true; + */ + return $privatekey; + } + /** + * OnLoad Handler + * + * @return bool + */ + protected static function onLoad(array $components) + { + $key = $components['isPublicKey'] ? new \FluentSmtpLib\phpseclib3\Crypt\RSA\PublicKey() : new \FluentSmtpLib\phpseclib3\Crypt\RSA\PrivateKey(); + $key->modulus = $components['modulus']; + $key->publicExponent = $components['publicExponent']; + $key->k = $key->modulus->getLengthInBytes(); + if ($components['isPublicKey'] || !isset($components['privateExponent'])) { + $key->exponent = $key->publicExponent; + } else { + $key->privateExponent = $components['privateExponent']; + $key->exponent = $key->privateExponent; + $key->primes = $components['primes']; + $key->exponents = $components['exponents']; + $key->coefficients = $components['coefficients']; + } + if ($components['format'] == \FluentSmtpLib\phpseclib3\Crypt\RSA\Formats\Keys\PSS::class) { + // in the X509 world RSA keys are assumed to use PKCS1 padding by default. only if the key is + // explicitly a PSS key is the use of PSS assumed. phpseclib does not work like this. phpseclib + // uses PSS padding by default. it assumes the more secure method by default and altho it provides + // for the less secure PKCS1 method you have to go out of your way to use it. this is consistent + // with the latest trends in crypto. libsodium (NaCl) is actually a little more extreme in that + // not only does it defaults to the most secure methods - it doesn't even let you choose less + // secure methods + //$key = $key->withPadding(self::SIGNATURE_PSS); + if (isset($components['hash'])) { + $key = $key->withHash($components['hash']); + } + if (isset($components['MGFHash'])) { + $key = $key->withMGFHash($components['MGFHash']); + } + if (isset($components['saltLength'])) { + $key = $key->withSaltLength($components['saltLength']); + } + } + return $key; + } + /** + * Initialize static variables + */ + protected static function initialize_static_variables() + { + if (!isset(self::$configFile)) { + self::$configFile = \dirname(__FILE__) . '/../openssl.cnf'; + } + parent::initialize_static_variables(); + } + /** + * Constructor + * + * PublicKey and PrivateKey objects can only be created from abstract RSA class + */ + protected function __construct() + { + parent::__construct(); + $this->hLen = $this->hash->getLengthInBytes(); + $this->mgfHash = new \FluentSmtpLib\phpseclib3\Crypt\Hash('sha256'); + $this->mgfHLen = $this->mgfHash->getLengthInBytes(); + } + /** + * Integer-to-Octet-String primitive + * + * See {@link http://tools.ietf.org/html/rfc3447#section-4.1 RFC3447#section-4.1}. + * + * @param bool|Math\BigInteger $x + * @param int $xLen + * @return bool|string + */ + protected function i2osp($x, $xLen) + { + if ($x === \false) { + return \false; + } + $x = $x->toBytes(); + if (\strlen($x) > $xLen) { + throw new \OutOfRangeException('Resultant string length out of range'); + } + return \str_pad($x, $xLen, \chr(0), \STR_PAD_LEFT); + } + /** + * Octet-String-to-Integer primitive + * + * See {@link http://tools.ietf.org/html/rfc3447#section-4.2 RFC3447#section-4.2}. + * + * @param string $x + * @return Math\BigInteger + */ + protected function os2ip($x) + { + return new \FluentSmtpLib\phpseclib3\Math\BigInteger($x, 256); + } + /** + * EMSA-PKCS1-V1_5-ENCODE + * + * See {@link http://tools.ietf.org/html/rfc3447#section-9.2 RFC3447#section-9.2}. + * + * @param string $m + * @param int $emLen + * @throws \LengthException if the intended encoded message length is too short + * @return string + */ + protected function emsa_pkcs1_v1_5_encode($m, $emLen) + { + $h = $this->hash->hash($m); + // see http://tools.ietf.org/html/rfc3447#page-43 + switch ($this->hash->getHash()) { + case 'md2': + $t = "0 0\f\x06\x08*\x86H\x86\xf7\r\x02\x02\x05\x00\x04\x10"; + break; + case 'md5': + $t = "0 0\f\x06\x08*\x86H\x86\xf7\r\x02\x05\x05\x00\x04\x10"; + break; + case 'sha1': + $t = "0!0\t\x06\x05+\x0e\x03\x02\x1a\x05\x00\x04\x14"; + break; + case 'sha256': + $t = "010\r\x06\t`\x86H\x01e\x03\x04\x02\x01\x05\x00\x04 "; + break; + case 'sha384': + $t = "0A0\r\x06\t`\x86H\x01e\x03\x04\x02\x02\x05\x00\x040"; + break; + case 'sha512': + $t = "0Q0\r\x06\t`\x86H\x01e\x03\x04\x02\x03\x05\x00\x04@"; + break; + // from https://www.emc.com/collateral/white-papers/h11300-pkcs-1v2-2-rsa-cryptography-standard-wp.pdf#page=40 + case 'sha224': + $t = "0-0\r\x06\t`\x86H\x01e\x03\x04\x02\x04\x05\x00\x04\x1c"; + break; + case 'sha512/224': + $t = "0-0\r\x06\t`\x86H\x01e\x03\x04\x02\x05\x05\x00\x04\x1c"; + break; + case 'sha512/256': + $t = "010\r\x06\t`\x86H\x01e\x03\x04\x02\x06\x05\x00\x04 "; + } + $t .= $h; + $tLen = \strlen($t); + if ($emLen < $tLen + 11) { + throw new \LengthException('Intended encoded message length too short'); + } + $ps = \str_repeat(\chr(0xff), $emLen - $tLen - 3); + $em = "\x00\x01{$ps}\x00{$t}"; + return $em; + } + /** + * EMSA-PKCS1-V1_5-ENCODE (without NULL) + * + * Quoting https://tools.ietf.org/html/rfc8017#page-65, + * + * "The parameters field associated with id-sha1, id-sha224, id-sha256, + * id-sha384, id-sha512, id-sha512/224, and id-sha512/256 should + * generally be omitted, but if present, it shall have a value of type + * NULL" + * + * @param string $m + * @param int $emLen + * @return string + */ + protected function emsa_pkcs1_v1_5_encode_without_null($m, $emLen) + { + $h = $this->hash->hash($m); + // see http://tools.ietf.org/html/rfc3447#page-43 + switch ($this->hash->getHash()) { + case 'sha1': + $t = "0\x1f0\x07\x06\x05+\x0e\x03\x02\x1a\x04\x14"; + break; + case 'sha256': + $t = "0/0\v\x06\t`\x86H\x01e\x03\x04\x02\x01\x04 "; + break; + case 'sha384': + $t = "0?0\v\x06\t`\x86H\x01e\x03\x04\x02\x02\x040"; + break; + case 'sha512': + $t = "0O0\v\x06\t`\x86H\x01e\x03\x04\x02\x03\x04@"; + break; + // from https://www.emc.com/collateral/white-papers/h11300-pkcs-1v2-2-rsa-cryptography-standard-wp.pdf#page=40 + case 'sha224': + $t = "0+0\v\x06\t`\x86H\x01e\x03\x04\x02\x04\x04\x1c"; + break; + case 'sha512/224': + $t = "0+0\v\x06\t`\x86H\x01e\x03\x04\x02\x05\x04\x1c"; + break; + case 'sha512/256': + $t = "0/0\v\x06\t`\x86H\x01e\x03\x04\x02\x06\x04 "; + break; + default: + throw new \FluentSmtpLib\phpseclib3\Exception\UnsupportedAlgorithmException('md2 and md5 require NULLs'); + } + $t .= $h; + $tLen = \strlen($t); + if ($emLen < $tLen + 11) { + throw new \LengthException('Intended encoded message length too short'); + } + $ps = \str_repeat(\chr(0xff), $emLen - $tLen - 3); + $em = "\x00\x01{$ps}\x00{$t}"; + return $em; + } + /** + * MGF1 + * + * See {@link http://tools.ietf.org/html/rfc3447#appendix-B.2.1 RFC3447#appendix-B.2.1}. + * + * @param string $mgfSeed + * @param int $maskLen + * @return string + */ + protected function mgf1($mgfSeed, $maskLen) + { + // if $maskLen would yield strings larger than 4GB, PKCS#1 suggests a "Mask too long" error be output. + $t = ''; + $count = \ceil($maskLen / $this->mgfHLen); + for ($i = 0; $i < $count; $i++) { + $c = \pack('N', $i); + $t .= $this->mgfHash->hash($mgfSeed . $c); + } + return \substr($t, 0, $maskLen); + } + /** + * Returns the key size + * + * More specifically, this returns the size of the modulo in bits. + * + * @return int + */ + public function getLength() + { + return !isset($this->modulus) ? 0 : $this->modulus->getLength(); + } + /** + * Determines which hashing function should be used + * + * Used with signature production / verification and (if the encryption mode is self::PADDING_OAEP) encryption and + * decryption. + * + * @param string $hash + */ + public function withHash($hash) + { + $new = clone $this; + // Crypt\Hash supports algorithms that PKCS#1 doesn't support. md5-96 and sha1-96, for example. + switch (\strtolower($hash)) { + case 'md2': + case 'md5': + case 'sha1': + case 'sha256': + case 'sha384': + case 'sha512': + case 'sha224': + case 'sha512/224': + case 'sha512/256': + $new->hash = new \FluentSmtpLib\phpseclib3\Crypt\Hash($hash); + break; + default: + throw new \FluentSmtpLib\phpseclib3\Exception\UnsupportedAlgorithmException('The only supported hash algorithms are: md2, md5, sha1, sha256, sha384, sha512, sha224, sha512/224, sha512/256'); + } + $new->hLen = $new->hash->getLengthInBytes(); + return $new; + } + /** + * Determines which hashing function should be used for the mask generation function + * + * The mask generation function is used by self::PADDING_OAEP and self::PADDING_PSS and although it's + * best if Hash and MGFHash are set to the same thing this is not a requirement. + * + * @param string $hash + */ + public function withMGFHash($hash) + { + $new = clone $this; + // Crypt\Hash supports algorithms that PKCS#1 doesn't support. md5-96 and sha1-96, for example. + switch (\strtolower($hash)) { + case 'md2': + case 'md5': + case 'sha1': + case 'sha256': + case 'sha384': + case 'sha512': + case 'sha224': + case 'sha512/224': + case 'sha512/256': + $new->mgfHash = new \FluentSmtpLib\phpseclib3\Crypt\Hash($hash); + break; + default: + throw new \FluentSmtpLib\phpseclib3\Exception\UnsupportedAlgorithmException('The only supported hash algorithms are: md2, md5, sha1, sha256, sha384, sha512, sha224, sha512/224, sha512/256'); + } + $new->mgfHLen = $new->mgfHash->getLengthInBytes(); + return $new; + } + /** + * Returns the MGF hash algorithm currently being used + * + */ + public function getMGFHash() + { + return clone $this->mgfHash; + } + /** + * Determines the salt length + * + * Used by RSA::PADDING_PSS + * + * To quote from {@link http://tools.ietf.org/html/rfc3447#page-38 RFC3447#page-38}: + * + * Typical salt lengths in octets are hLen (the length of the output + * of the hash function Hash) and 0. + * + * @param int $sLen + */ + public function withSaltLength($sLen) + { + $new = clone $this; + $new->sLen = $sLen; + return $new; + } + /** + * Returns the salt length currently being used + * + */ + public function getSaltLength() + { + return $this->sLen !== null ? $this->sLen : $this->hLen; + } + /** + * Determines the label + * + * Used by RSA::PADDING_OAEP + * + * To quote from {@link http://tools.ietf.org/html/rfc3447#page-17 RFC3447#page-17}: + * + * Both the encryption and the decryption operations of RSAES-OAEP take + * the value of a label L as input. In this version of PKCS #1, L is + * the empty string; other uses of the label are outside the scope of + * this document. + * + * @param string $label + */ + public function withLabel($label) + { + $new = clone $this; + $new->label = $label; + return $new; + } + /** + * Returns the label currently being used + * + */ + public function getLabel() + { + return $this->label; + } + /** + * Determines the padding modes + * + * Example: $key->withPadding(RSA::ENCRYPTION_PKCS1 | RSA::SIGNATURE_PKCS1); + * + * @param int $padding + */ + public function withPadding($padding) + { + $masks = [self::ENCRYPTION_OAEP, self::ENCRYPTION_PKCS1, self::ENCRYPTION_NONE]; + $encryptedCount = 0; + $selected = 0; + foreach ($masks as $mask) { + if ($padding & $mask) { + $selected = $mask; + $encryptedCount++; + } + } + if ($encryptedCount > 1) { + throw new \FluentSmtpLib\phpseclib3\Exception\InconsistentSetupException('Multiple encryption padding modes have been selected; at most only one should be selected'); + } + $encryptionPadding = $selected; + $masks = [self::SIGNATURE_PSS, self::SIGNATURE_RELAXED_PKCS1, self::SIGNATURE_PKCS1]; + $signatureCount = 0; + $selected = 0; + foreach ($masks as $mask) { + if ($padding & $mask) { + $selected = $mask; + $signatureCount++; + } + } + if ($signatureCount > 1) { + throw new \FluentSmtpLib\phpseclib3\Exception\InconsistentSetupException('Multiple signature padding modes have been selected; at most only one should be selected'); + } + $signaturePadding = $selected; + $new = clone $this; + if ($encryptedCount) { + $new->encryptionPadding = $encryptionPadding; + } + if ($signatureCount) { + $new->signaturePadding = $signaturePadding; + } + return $new; + } + /** + * Returns the padding currently being used + * + */ + public function getPadding() + { + return $this->signaturePadding | $this->encryptionPadding; + } + /** + * Returns the current engine being used + * + * OpenSSL is only used in this class (and it's subclasses) for key generation + * Even then it depends on the parameters you're using. It's not used for + * multi-prime RSA nor is it used if the key length is outside of the range + * supported by OpenSSL + * + * @see self::useInternalEngine() + * @see self::useBestEngine() + * @return string + */ + public function getEngine() + { + if (!isset(self::$engines['PHP'])) { + self::useBestEngine(); + } + return self::$engines['OpenSSL'] && self::$defaultExponent == 65537 ? 'OpenSSL' : 'PHP'; + } + /** + * Enable RSA Blinding + * + */ + public static function enableBlinding() + { + static::$enableBlinding = \true; + } + /** + * Disable RSA Blinding + * + */ + public static function disableBlinding() + { + static::$enableBlinding = \false; + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA/Formats/Keys/JWK.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA/Formats/Keys/JWK.php new file mode 100644 index 0000000..f7b5278 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA/Formats/Keys/JWK.php @@ -0,0 +1,116 @@ + + * @copyright 2015 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\Crypt\RSA\Formats\Keys; + +use FluentSmtpLib\phpseclib3\Common\Functions\Strings; +use FluentSmtpLib\phpseclib3\Crypt\Common\Formats\Keys\JWK as Progenitor; +use FluentSmtpLib\phpseclib3\Math\BigInteger; +/** + * JWK Formatted RSA Handler + * + * @author Jim Wigginton + */ +abstract class JWK extends \FluentSmtpLib\phpseclib3\Crypt\Common\Formats\Keys\JWK +{ + /** + * Break a public or private key down into its constituent components + * + * @param string $key + * @param string $password optional + * @return array + */ + public static function load($key, $password = '') + { + $key = parent::load($key, $password); + if ($key->kty != 'RSA') { + throw new \RuntimeException('Only RSA JWK keys are supported'); + } + $count = $publicCount = 0; + $vars = ['n', 'e', 'd', 'p', 'q', 'dp', 'dq', 'qi']; + foreach ($vars as $var) { + if (!isset($key->{$var}) || !\is_string($key->{$var})) { + continue; + } + $count++; + $value = new \FluentSmtpLib\phpseclib3\Math\BigInteger(\FluentSmtpLib\phpseclib3\Common\Functions\Strings::base64url_decode($key->{$var}), 256); + switch ($var) { + case 'n': + $publicCount++; + $components['modulus'] = $value; + break; + case 'e': + $publicCount++; + $components['publicExponent'] = $value; + break; + case 'd': + $components['privateExponent'] = $value; + break; + case 'p': + $components['primes'][1] = $value; + break; + case 'q': + $components['primes'][2] = $value; + break; + case 'dp': + $components['exponents'][1] = $value; + break; + case 'dq': + $components['exponents'][2] = $value; + break; + case 'qi': + $components['coefficients'][2] = $value; + } + } + if ($count == \count($vars)) { + return $components + ['isPublicKey' => \false]; + } + if ($count == 2 && $publicCount == 2) { + return $components + ['isPublicKey' => \true]; + } + throw new \UnexpectedValueException('Key does not have an appropriate number of RSA parameters'); + } + /** + * Convert a private key to the appropriate format. + * + * @param BigInteger $n + * @param BigInteger $e + * @param BigInteger $d + * @param array $primes + * @param array $exponents + * @param array $coefficients + * @param string $password optional + * @param array $options optional + * @return string + */ + public static function savePrivateKey(\FluentSmtpLib\phpseclib3\Math\BigInteger $n, \FluentSmtpLib\phpseclib3\Math\BigInteger $e, \FluentSmtpLib\phpseclib3\Math\BigInteger $d, array $primes, array $exponents, array $coefficients, $password = '', array $options = []) + { + if (\count($primes) != 2) { + throw new \InvalidArgumentException('JWK does not support multi-prime RSA keys'); + } + $key = ['kty' => 'RSA', 'n' => \FluentSmtpLib\phpseclib3\Common\Functions\Strings::base64url_encode($n->toBytes()), 'e' => \FluentSmtpLib\phpseclib3\Common\Functions\Strings::base64url_encode($e->toBytes()), 'd' => \FluentSmtpLib\phpseclib3\Common\Functions\Strings::base64url_encode($d->toBytes()), 'p' => \FluentSmtpLib\phpseclib3\Common\Functions\Strings::base64url_encode($primes[1]->toBytes()), 'q' => \FluentSmtpLib\phpseclib3\Common\Functions\Strings::base64url_encode($primes[2]->toBytes()), 'dp' => \FluentSmtpLib\phpseclib3\Common\Functions\Strings::base64url_encode($exponents[1]->toBytes()), 'dq' => \FluentSmtpLib\phpseclib3\Common\Functions\Strings::base64url_encode($exponents[2]->toBytes()), 'qi' => \FluentSmtpLib\phpseclib3\Common\Functions\Strings::base64url_encode($coefficients[2]->toBytes())]; + return self::wrapKey($key, $options); + } + /** + * Convert a public key to the appropriate format + * + * @param BigInteger $n + * @param BigInteger $e + * @param array $options optional + * @return string + */ + public static function savePublicKey(\FluentSmtpLib\phpseclib3\Math\BigInteger $n, \FluentSmtpLib\phpseclib3\Math\BigInteger $e, array $options = []) + { + $key = ['kty' => 'RSA', 'n' => \FluentSmtpLib\phpseclib3\Common\Functions\Strings::base64url_encode($n->toBytes()), 'e' => \FluentSmtpLib\phpseclib3\Common\Functions\Strings::base64url_encode($e->toBytes())]; + return self::wrapKey($key, $options); + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA/Formats/Keys/MSBLOB.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA/Formats/Keys/MSBLOB.php new file mode 100644 index 0000000..6e2936f --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA/Formats/Keys/MSBLOB.php @@ -0,0 +1,207 @@ + + * @copyright 2015 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\Crypt\RSA\Formats\Keys; + +use FluentSmtpLib\phpseclib3\Common\Functions\Strings; +use FluentSmtpLib\phpseclib3\Exception\UnsupportedFormatException; +use FluentSmtpLib\phpseclib3\Math\BigInteger; +/** + * Microsoft BLOB Formatted RSA Key Handler + * + * @author Jim Wigginton + */ +abstract class MSBLOB +{ + /** + * Public/Private Key Pair + * + */ + const PRIVATEKEYBLOB = 0x7; + /** + * Public Key + * + */ + const PUBLICKEYBLOB = 0x6; + /** + * Public Key + * + */ + const PUBLICKEYBLOBEX = 0xa; + /** + * RSA public key exchange algorithm + * + */ + const CALG_RSA_KEYX = 0xa400; + /** + * RSA public key exchange algorithm + * + */ + const CALG_RSA_SIGN = 0x2400; + /** + * Public Key + * + */ + const RSA1 = 0x31415352; + /** + * Private Key + * + */ + const RSA2 = 0x32415352; + /** + * Break a public or private key down into its constituent components + * + * @param string $key + * @param string $password optional + * @return array + */ + public static function load($key, $password = '') + { + if (!\FluentSmtpLib\phpseclib3\Common\Functions\Strings::is_stringable($key)) { + throw new \UnexpectedValueException('Key should be a string - not a ' . \gettype($key)); + } + $key = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::base64_decode($key); + if (!\is_string($key)) { + throw new \UnexpectedValueException('Base64 decoding produced an error'); + } + if (\strlen($key) < 20) { + throw new \UnexpectedValueException('Key appears to be malformed'); + } + // PUBLICKEYSTRUC publickeystruc + // https://msdn.microsoft.com/en-us/library/windows/desktop/aa387453(v=vs.85).aspx + \extract(\unpack('atype/aversion/vreserved/Valgo', \FluentSmtpLib\phpseclib3\Common\Functions\Strings::shift($key, 8))); + /** + * @var string $type + * @var string $version + * @var integer $reserved + * @var integer $algo + */ + switch (\ord($type)) { + case self::PUBLICKEYBLOB: + case self::PUBLICKEYBLOBEX: + $publickey = \true; + break; + case self::PRIVATEKEYBLOB: + $publickey = \false; + break; + default: + throw new \UnexpectedValueException('Key appears to be malformed'); + } + $components = ['isPublicKey' => $publickey]; + // https://msdn.microsoft.com/en-us/library/windows/desktop/aa375549(v=vs.85).aspx + switch ($algo) { + case self::CALG_RSA_KEYX: + case self::CALG_RSA_SIGN: + break; + default: + throw new \UnexpectedValueException('Key appears to be malformed'); + } + // RSAPUBKEY rsapubkey + // https://msdn.microsoft.com/en-us/library/windows/desktop/aa387685(v=vs.85).aspx + // could do V for pubexp but that's unsigned 32-bit whereas some PHP installs only do signed 32-bit + \extract(\unpack('Vmagic/Vbitlen/a4pubexp', \FluentSmtpLib\phpseclib3\Common\Functions\Strings::shift($key, 12))); + /** + * @var integer $magic + * @var integer $bitlen + * @var string $pubexp + */ + switch ($magic) { + case self::RSA2: + $components['isPublicKey'] = \false; + // fall-through + case self::RSA1: + break; + default: + throw new \UnexpectedValueException('Key appears to be malformed'); + } + $baseLength = $bitlen / 16; + if (\strlen($key) != 2 * $baseLength && \strlen($key) != 9 * $baseLength) { + throw new \UnexpectedValueException('Key appears to be malformed'); + } + $components[$components['isPublicKey'] ? 'publicExponent' : 'privateExponent'] = new \FluentSmtpLib\phpseclib3\Math\BigInteger(\strrev($pubexp), 256); + // BYTE modulus[rsapubkey.bitlen/8] + $components['modulus'] = new \FluentSmtpLib\phpseclib3\Math\BigInteger(\strrev(\FluentSmtpLib\phpseclib3\Common\Functions\Strings::shift($key, $bitlen / 8)), 256); + if ($publickey) { + return $components; + } + $components['isPublicKey'] = \false; + // BYTE prime1[rsapubkey.bitlen/16] + $components['primes'] = [1 => new \FluentSmtpLib\phpseclib3\Math\BigInteger(\strrev(\FluentSmtpLib\phpseclib3\Common\Functions\Strings::shift($key, $bitlen / 16)), 256)]; + // BYTE prime2[rsapubkey.bitlen/16] + $components['primes'][] = new \FluentSmtpLib\phpseclib3\Math\BigInteger(\strrev(\FluentSmtpLib\phpseclib3\Common\Functions\Strings::shift($key, $bitlen / 16)), 256); + // BYTE exponent1[rsapubkey.bitlen/16] + $components['exponents'] = [1 => new \FluentSmtpLib\phpseclib3\Math\BigInteger(\strrev(\FluentSmtpLib\phpseclib3\Common\Functions\Strings::shift($key, $bitlen / 16)), 256)]; + // BYTE exponent2[rsapubkey.bitlen/16] + $components['exponents'][] = new \FluentSmtpLib\phpseclib3\Math\BigInteger(\strrev(\FluentSmtpLib\phpseclib3\Common\Functions\Strings::shift($key, $bitlen / 16)), 256); + // BYTE coefficient[rsapubkey.bitlen/16] + $components['coefficients'] = [2 => new \FluentSmtpLib\phpseclib3\Math\BigInteger(\strrev(\FluentSmtpLib\phpseclib3\Common\Functions\Strings::shift($key, $bitlen / 16)), 256)]; + if (isset($components['privateExponent'])) { + $components['publicExponent'] = $components['privateExponent']; + } + // BYTE privateExponent[rsapubkey.bitlen/8] + $components['privateExponent'] = new \FluentSmtpLib\phpseclib3\Math\BigInteger(\strrev(\FluentSmtpLib\phpseclib3\Common\Functions\Strings::shift($key, $bitlen / 8)), 256); + return $components; + } + /** + * Convert a private key to the appropriate format. + * + * @param BigInteger $n + * @param BigInteger $e + * @param BigInteger $d + * @param array $primes + * @param array $exponents + * @param array $coefficients + * @param string $password optional + * @return string + */ + public static function savePrivateKey(\FluentSmtpLib\phpseclib3\Math\BigInteger $n, \FluentSmtpLib\phpseclib3\Math\BigInteger $e, \FluentSmtpLib\phpseclib3\Math\BigInteger $d, array $primes, array $exponents, array $coefficients, $password = '') + { + if (\count($primes) != 2) { + throw new \InvalidArgumentException('MSBLOB does not support multi-prime RSA keys'); + } + if (!empty($password) && \is_string($password)) { + throw new \FluentSmtpLib\phpseclib3\Exception\UnsupportedFormatException('MSBLOB private keys do not support encryption'); + } + $n = \strrev($n->toBytes()); + $e = \str_pad(\strrev($e->toBytes()), 4, "\x00"); + $key = \pack('aavV', \chr(self::PRIVATEKEYBLOB), \chr(2), 0, self::CALG_RSA_KEYX); + $key .= \pack('VVa*', self::RSA2, 8 * \strlen($n), $e); + $key .= $n; + $key .= \strrev($primes[1]->toBytes()); + $key .= \strrev($primes[2]->toBytes()); + $key .= \strrev($exponents[1]->toBytes()); + $key .= \strrev($exponents[2]->toBytes()); + $key .= \strrev($coefficients[2]->toBytes()); + $key .= \strrev($d->toBytes()); + return \FluentSmtpLib\phpseclib3\Common\Functions\Strings::base64_encode($key); + } + /** + * Convert a public key to the appropriate format + * + * @param BigInteger $n + * @param BigInteger $e + * @return string + */ + public static function savePublicKey(\FluentSmtpLib\phpseclib3\Math\BigInteger $n, \FluentSmtpLib\phpseclib3\Math\BigInteger $e) + { + $n = \strrev($n->toBytes()); + $e = \str_pad(\strrev($e->toBytes()), 4, "\x00"); + $key = \pack('aavV', \chr(self::PUBLICKEYBLOB), \chr(2), 0, self::CALG_RSA_KEYX); + $key .= \pack('VVa*', self::RSA1, 8 * \strlen($n), $e); + $key .= $n; + return \FluentSmtpLib\phpseclib3\Common\Functions\Strings::base64_encode($key); + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA/Formats/Keys/OpenSSH.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA/Formats/Keys/OpenSSH.php new file mode 100644 index 0000000..95aa9c5 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA/Formats/Keys/OpenSSH.php @@ -0,0 +1,101 @@ + + * @copyright 2015 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\Crypt\RSA\Formats\Keys; + +use FluentSmtpLib\phpseclib3\Common\Functions\Strings; +use FluentSmtpLib\phpseclib3\Crypt\Common\Formats\Keys\OpenSSH as Progenitor; +use FluentSmtpLib\phpseclib3\Math\BigInteger; +/** + * OpenSSH Formatted RSA Key Handler + * + * @author Jim Wigginton + */ +abstract class OpenSSH extends \FluentSmtpLib\phpseclib3\Crypt\Common\Formats\Keys\OpenSSH +{ + /** + * Supported Key Types + * + * @var array + */ + protected static $types = ['ssh-rsa']; + /** + * Break a public or private key down into its constituent components + * + * @param string $key + * @param string $password optional + * @return array + */ + public static function load($key, $password = '') + { + static $one; + if (!isset($one)) { + $one = new \FluentSmtpLib\phpseclib3\Math\BigInteger(1); + } + $parsed = parent::load($key, $password); + if (isset($parsed['paddedKey'])) { + list($type) = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::unpackSSH2('s', $parsed['paddedKey']); + if ($type != $parsed['type']) { + throw new \RuntimeException("The public and private keys are not of the same type ({$type} vs {$parsed['type']})"); + } + $primes = $coefficients = []; + list($modulus, $publicExponent, $privateExponent, $coefficients[2], $primes[1], $primes[2], $comment, ) = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::unpackSSH2('i6s', $parsed['paddedKey']); + $temp = $primes[1]->subtract($one); + $exponents = [1 => $publicExponent->modInverse($temp)]; + $temp = $primes[2]->subtract($one); + $exponents[] = $publicExponent->modInverse($temp); + $isPublicKey = \false; + return \compact('publicExponent', 'modulus', 'privateExponent', 'primes', 'coefficients', 'exponents', 'comment', 'isPublicKey'); + } + list($publicExponent, $modulus) = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::unpackSSH2('ii', $parsed['publicKey']); + return ['isPublicKey' => \true, 'modulus' => $modulus, 'publicExponent' => $publicExponent, 'comment' => $parsed['comment']]; + } + /** + * Convert a public key to the appropriate format + * + * @param BigInteger $n + * @param BigInteger $e + * @param array $options optional + * @return string + */ + public static function savePublicKey(\FluentSmtpLib\phpseclib3\Math\BigInteger $n, \FluentSmtpLib\phpseclib3\Math\BigInteger $e, array $options = []) + { + $RSAPublicKey = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::packSSH2('sii', 'ssh-rsa', $e, $n); + if (isset($options['binary']) ? $options['binary'] : self::$binary) { + return $RSAPublicKey; + } + $comment = isset($options['comment']) ? $options['comment'] : self::$comment; + $RSAPublicKey = 'ssh-rsa ' . \base64_encode($RSAPublicKey) . ' ' . $comment; + return $RSAPublicKey; + } + /** + * Convert a private key to the appropriate format. + * + * @param BigInteger $n + * @param BigInteger $e + * @param BigInteger $d + * @param array $primes + * @param array $exponents + * @param array $coefficients + * @param string $password optional + * @param array $options optional + * @return string + */ + public static function savePrivateKey(\FluentSmtpLib\phpseclib3\Math\BigInteger $n, \FluentSmtpLib\phpseclib3\Math\BigInteger $e, \FluentSmtpLib\phpseclib3\Math\BigInteger $d, array $primes, array $exponents, array $coefficients, $password = '', array $options = []) + { + $publicKey = self::savePublicKey($n, $e, ['binary' => \true]); + $privateKey = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::packSSH2('si6', 'ssh-rsa', $n, $e, $d, $coefficients[2], $primes[1], $primes[2]); + return self::wrapPrivateKey($publicKey, $privateKey, $password, $options); + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA/Formats/Keys/PKCS1.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA/Formats/Keys/PKCS1.php new file mode 100644 index 0000000..ace05d6 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA/Formats/Keys/PKCS1.php @@ -0,0 +1,120 @@ + + * @copyright 2015 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\Crypt\RSA\Formats\Keys; + +use FluentSmtpLib\phpseclib3\Common\Functions\Strings; +use FluentSmtpLib\phpseclib3\Crypt\Common\Formats\Keys\PKCS1 as Progenitor; +use FluentSmtpLib\phpseclib3\File\ASN1; +use FluentSmtpLib\phpseclib3\File\ASN1\Maps; +use FluentSmtpLib\phpseclib3\Math\BigInteger; +/** + * PKCS#1 Formatted RSA Key Handler + * + * @author Jim Wigginton + */ +abstract class PKCS1 extends \FluentSmtpLib\phpseclib3\Crypt\Common\Formats\Keys\PKCS1 +{ + /** + * Break a public or private key down into its constituent components + * + * @param string $key + * @param string $password optional + * @return array + */ + public static function load($key, $password = '') + { + if (!\FluentSmtpLib\phpseclib3\Common\Functions\Strings::is_stringable($key)) { + throw new \UnexpectedValueException('Key should be a string - not a ' . \gettype($key)); + } + if (\strpos($key, 'PUBLIC') !== \false) { + $components = ['isPublicKey' => \true]; + } elseif (\strpos($key, 'PRIVATE') !== \false) { + $components = ['isPublicKey' => \false]; + } else { + $components = []; + } + $key = parent::load($key, $password); + $decoded = \FluentSmtpLib\phpseclib3\File\ASN1::decodeBER($key); + if (!$decoded) { + throw new \RuntimeException('Unable to decode BER'); + } + $key = \FluentSmtpLib\phpseclib3\File\ASN1::asn1map($decoded[0], \FluentSmtpLib\phpseclib3\File\ASN1\Maps\RSAPrivateKey::MAP); + if (\is_array($key)) { + $components += ['modulus' => $key['modulus'], 'publicExponent' => $key['publicExponent'], 'privateExponent' => $key['privateExponent'], 'primes' => [1 => $key['prime1'], $key['prime2']], 'exponents' => [1 => $key['exponent1'], $key['exponent2']], 'coefficients' => [2 => $key['coefficient']]]; + if ($key['version'] == 'multi') { + foreach ($key['otherPrimeInfos'] as $primeInfo) { + $components['primes'][] = $primeInfo['prime']; + $components['exponents'][] = $primeInfo['exponent']; + $components['coefficients'][] = $primeInfo['coefficient']; + } + } + if (!isset($components['isPublicKey'])) { + $components['isPublicKey'] = \false; + } + return $components; + } + $key = \FluentSmtpLib\phpseclib3\File\ASN1::asn1map($decoded[0], \FluentSmtpLib\phpseclib3\File\ASN1\Maps\RSAPublicKey::MAP); + if (!\is_array($key)) { + throw new \RuntimeException('Unable to perform ASN1 mapping'); + } + if (!isset($components['isPublicKey'])) { + $components['isPublicKey'] = \true; + } + return $components + $key; + } + /** + * Convert a private key to the appropriate format. + * + * @param BigInteger $n + * @param BigInteger $e + * @param BigInteger $d + * @param array $primes + * @param array $exponents + * @param array $coefficients + * @param string $password optional + * @param array $options optional + * @return string + */ + public static function savePrivateKey(\FluentSmtpLib\phpseclib3\Math\BigInteger $n, \FluentSmtpLib\phpseclib3\Math\BigInteger $e, \FluentSmtpLib\phpseclib3\Math\BigInteger $d, array $primes, array $exponents, array $coefficients, $password = '', array $options = []) + { + $num_primes = \count($primes); + $key = ['version' => $num_primes == 2 ? 'two-prime' : 'multi', 'modulus' => $n, 'publicExponent' => $e, 'privateExponent' => $d, 'prime1' => $primes[1], 'prime2' => $primes[2], 'exponent1' => $exponents[1], 'exponent2' => $exponents[2], 'coefficient' => $coefficients[2]]; + for ($i = 3; $i <= $num_primes; $i++) { + $key['otherPrimeInfos'][] = ['prime' => $primes[$i], 'exponent' => $exponents[$i], 'coefficient' => $coefficients[$i]]; + } + $key = \FluentSmtpLib\phpseclib3\File\ASN1::encodeDER($key, \FluentSmtpLib\phpseclib3\File\ASN1\Maps\RSAPrivateKey::MAP); + return self::wrapPrivateKey($key, 'RSA', $password, $options); + } + /** + * Convert a public key to the appropriate format + * + * @param BigInteger $n + * @param BigInteger $e + * @return string + */ + public static function savePublicKey(\FluentSmtpLib\phpseclib3\Math\BigInteger $n, \FluentSmtpLib\phpseclib3\Math\BigInteger $e) + { + $key = ['modulus' => $n, 'publicExponent' => $e]; + $key = \FluentSmtpLib\phpseclib3\File\ASN1::encodeDER($key, \FluentSmtpLib\phpseclib3\File\ASN1\Maps\RSAPublicKey::MAP); + return self::wrapPublicKey($key, 'RSA'); + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA/Formats/Keys/PKCS8.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA/Formats/Keys/PKCS8.php new file mode 100644 index 0000000..38eeb74 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA/Formats/Keys/PKCS8.php @@ -0,0 +1,111 @@ + + * @copyright 2015 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\Crypt\RSA\Formats\Keys; + +use FluentSmtpLib\phpseclib3\Crypt\Common\Formats\Keys\PKCS8 as Progenitor; +use FluentSmtpLib\phpseclib3\File\ASN1; +use FluentSmtpLib\phpseclib3\Math\BigInteger; +/** + * PKCS#8 Formatted RSA Key Handler + * + * @author Jim Wigginton + */ +abstract class PKCS8 extends \FluentSmtpLib\phpseclib3\Crypt\Common\Formats\Keys\PKCS8 +{ + /** + * OID Name + * + * @var string + */ + const OID_NAME = 'rsaEncryption'; + /** + * OID Value + * + * @var string + */ + const OID_VALUE = '1.2.840.113549.1.1.1'; + /** + * Child OIDs loaded + * + * @var bool + */ + protected static $childOIDsLoaded = \false; + /** + * Break a public or private key down into its constituent components + * + * @param string $key + * @param string $password optional + * @return array + */ + public static function load($key, $password = '') + { + $key = parent::load($key, $password); + if (isset($key['privateKey'])) { + $components['isPublicKey'] = \false; + $type = 'private'; + } else { + $components['isPublicKey'] = \true; + $type = 'public'; + } + $result = $components + \FluentSmtpLib\phpseclib3\Crypt\RSA\Formats\Keys\PKCS1::load($key[$type . 'Key']); + if (isset($key['meta'])) { + $result['meta'] = $key['meta']; + } + return $result; + } + /** + * Convert a private key to the appropriate format. + * + * @param BigInteger $n + * @param BigInteger $e + * @param BigInteger $d + * @param array $primes + * @param array $exponents + * @param array $coefficients + * @param string $password optional + * @param array $options optional + * @return string + */ + public static function savePrivateKey(\FluentSmtpLib\phpseclib3\Math\BigInteger $n, \FluentSmtpLib\phpseclib3\Math\BigInteger $e, \FluentSmtpLib\phpseclib3\Math\BigInteger $d, array $primes, array $exponents, array $coefficients, $password = '', array $options = []) + { + $key = \FluentSmtpLib\phpseclib3\Crypt\RSA\Formats\Keys\PKCS1::savePrivateKey($n, $e, $d, $primes, $exponents, $coefficients); + $key = \FluentSmtpLib\phpseclib3\File\ASN1::extractBER($key); + return self::wrapPrivateKey($key, [], null, $password, null, '', $options); + } + /** + * Convert a public key to the appropriate format + * + * @param BigInteger $n + * @param BigInteger $e + * @param array $options optional + * @return string + */ + public static function savePublicKey(\FluentSmtpLib\phpseclib3\Math\BigInteger $n, \FluentSmtpLib\phpseclib3\Math\BigInteger $e, array $options = []) + { + $key = \FluentSmtpLib\phpseclib3\Crypt\RSA\Formats\Keys\PKCS1::savePublicKey($n, $e); + $key = \FluentSmtpLib\phpseclib3\File\ASN1::extractBER($key); + return self::wrapPublicKey($key, null, null, $options); + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA/Formats/Keys/PSS.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA/Formats/Keys/PSS.php new file mode 100644 index 0000000..aa5cc25 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA/Formats/Keys/PSS.php @@ -0,0 +1,193 @@ + + * @copyright 2015 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\Crypt\RSA\Formats\Keys; + +use FluentSmtpLib\phpseclib3\Common\Functions\Strings; +use FluentSmtpLib\phpseclib3\Crypt\Common\Formats\Keys\PKCS8 as Progenitor; +use FluentSmtpLib\phpseclib3\File\ASN1; +use FluentSmtpLib\phpseclib3\File\ASN1\Maps; +use FluentSmtpLib\phpseclib3\Math\BigInteger; +/** + * PKCS#8 Formatted RSA-PSS Key Handler + * + * @author Jim Wigginton + */ +abstract class PSS extends \FluentSmtpLib\phpseclib3\Crypt\Common\Formats\Keys\PKCS8 +{ + /** + * OID Name + * + * @var string + */ + const OID_NAME = 'id-RSASSA-PSS'; + /** + * OID Value + * + * @var string + */ + const OID_VALUE = '1.2.840.113549.1.1.10'; + /** + * OIDs loaded + * + * @var bool + */ + private static $oidsLoaded = \false; + /** + * Child OIDs loaded + * + * @var bool + */ + protected static $childOIDsLoaded = \false; + /** + * Initialize static variables + */ + private static function initialize_static_variables() + { + if (!self::$oidsLoaded) { + \FluentSmtpLib\phpseclib3\File\ASN1::loadOIDs(['md2' => '1.2.840.113549.2.2', 'md4' => '1.2.840.113549.2.4', 'md5' => '1.2.840.113549.2.5', 'id-sha1' => '1.3.14.3.2.26', 'id-sha256' => '2.16.840.1.101.3.4.2.1', 'id-sha384' => '2.16.840.1.101.3.4.2.2', 'id-sha512' => '2.16.840.1.101.3.4.2.3', 'id-sha224' => '2.16.840.1.101.3.4.2.4', 'id-sha512/224' => '2.16.840.1.101.3.4.2.5', 'id-sha512/256' => '2.16.840.1.101.3.4.2.6', 'id-mgf1' => '1.2.840.113549.1.1.8']); + self::$oidsLoaded = \true; + } + } + /** + * Break a public or private key down into its constituent components + * + * @param string $key + * @param string $password optional + * @return array + */ + public static function load($key, $password = '') + { + self::initialize_static_variables(); + if (!\FluentSmtpLib\phpseclib3\Common\Functions\Strings::is_stringable($key)) { + throw new \UnexpectedValueException('Key should be a string - not a ' . \gettype($key)); + } + $components = ['isPublicKey' => \strpos($key, 'PUBLIC') !== \false]; + $key = parent::load($key, $password); + $type = isset($key['privateKey']) ? 'private' : 'public'; + $result = $components + \FluentSmtpLib\phpseclib3\Crypt\RSA\Formats\Keys\PKCS1::load($key[$type . 'Key']); + if (isset($key[$type . 'KeyAlgorithm']['parameters'])) { + $decoded = \FluentSmtpLib\phpseclib3\File\ASN1::decodeBER($key[$type . 'KeyAlgorithm']['parameters']); + if ($decoded === \false) { + throw new \UnexpectedValueException('Unable to decode parameters'); + } + $params = \FluentSmtpLib\phpseclib3\File\ASN1::asn1map($decoded[0], \FluentSmtpLib\phpseclib3\File\ASN1\Maps\RSASSA_PSS_params::MAP); + } else { + $params = []; + } + if (isset($params['maskGenAlgorithm']['parameters'])) { + $decoded = \FluentSmtpLib\phpseclib3\File\ASN1::decodeBER($params['maskGenAlgorithm']['parameters']); + if ($decoded === \false) { + throw new \UnexpectedValueException('Unable to decode parameters'); + } + $params['maskGenAlgorithm']['parameters'] = \FluentSmtpLib\phpseclib3\File\ASN1::asn1map($decoded[0], \FluentSmtpLib\phpseclib3\File\ASN1\Maps\HashAlgorithm::MAP); + } else { + $params['maskGenAlgorithm'] = ['algorithm' => 'id-mgf1', 'parameters' => ['algorithm' => 'id-sha1']]; + } + if (!isset($params['hashAlgorithm']['algorithm'])) { + $params['hashAlgorithm']['algorithm'] = 'id-sha1'; + } + $result['hash'] = \str_replace('id-', '', $params['hashAlgorithm']['algorithm']); + $result['MGFHash'] = \str_replace('id-', '', $params['maskGenAlgorithm']['parameters']['algorithm']); + if (isset($params['saltLength'])) { + $result['saltLength'] = (int) $params['saltLength']->toString(); + } + if (isset($key['meta'])) { + $result['meta'] = $key['meta']; + } + return $result; + } + /** + * Convert a private key to the appropriate format. + * + * @param BigInteger $n + * @param BigInteger $e + * @param BigInteger $d + * @param array $primes + * @param array $exponents + * @param array $coefficients + * @param string $password optional + * @param array $options optional + * @return string + */ + public static function savePrivateKey(\FluentSmtpLib\phpseclib3\Math\BigInteger $n, \FluentSmtpLib\phpseclib3\Math\BigInteger $e, \FluentSmtpLib\phpseclib3\Math\BigInteger $d, array $primes, array $exponents, array $coefficients, $password = '', array $options = []) + { + self::initialize_static_variables(); + $key = \FluentSmtpLib\phpseclib3\Crypt\RSA\Formats\Keys\PKCS1::savePrivateKey($n, $e, $d, $primes, $exponents, $coefficients); + $key = \FluentSmtpLib\phpseclib3\File\ASN1::extractBER($key); + $params = self::savePSSParams($options); + return self::wrapPrivateKey($key, [], $params, $password, null, '', $options); + } + /** + * Convert a public key to the appropriate format + * + * @param BigInteger $n + * @param BigInteger $e + * @param array $options optional + * @return string + */ + public static function savePublicKey(\FluentSmtpLib\phpseclib3\Math\BigInteger $n, \FluentSmtpLib\phpseclib3\Math\BigInteger $e, array $options = []) + { + self::initialize_static_variables(); + $key = \FluentSmtpLib\phpseclib3\Crypt\RSA\Formats\Keys\PKCS1::savePublicKey($n, $e); + $key = \FluentSmtpLib\phpseclib3\File\ASN1::extractBER($key); + $params = self::savePSSParams($options); + return self::wrapPublicKey($key, $params); + } + /** + * Encodes PSS parameters + * + * @param array $options + * @return string + */ + public static function savePSSParams(array $options) + { + /* + The trailerField field is an integer. It provides + compatibility with IEEE Std 1363a-2004 [P1363A]. The value + MUST be 1, which represents the trailer field with hexadecimal + value 0xBC. Other trailer fields, including the trailer field + composed of HashID concatenated with 0xCC that is specified in + IEEE Std 1363a, are not supported. Implementations that + perform signature generation MUST omit the trailerField field, + indicating that the default trailer field value was used. + Implementations that perform signature validation MUST + recognize both a present trailerField field with value 1 and an + absent trailerField field. + + source: https://tools.ietf.org/html/rfc4055#page-9 + */ + $params = ['trailerField' => new \FluentSmtpLib\phpseclib3\Math\BigInteger(1)]; + if (isset($options['hash'])) { + $params['hashAlgorithm']['algorithm'] = 'id-' . $options['hash']; + } + if (isset($options['MGFHash'])) { + $temp = ['algorithm' => 'id-' . $options['MGFHash']]; + $temp = \FluentSmtpLib\phpseclib3\File\ASN1::encodeDER($temp, \FluentSmtpLib\phpseclib3\File\ASN1\Maps\HashAlgorithm::MAP); + $params['maskGenAlgorithm'] = ['algorithm' => 'id-mgf1', 'parameters' => new \FluentSmtpLib\phpseclib3\File\ASN1\Element($temp)]; + } + if (isset($options['saltLength'])) { + $params['saltLength'] = new \FluentSmtpLib\phpseclib3\Math\BigInteger($options['saltLength']); + } + return new \FluentSmtpLib\phpseclib3\File\ASN1\Element(\FluentSmtpLib\phpseclib3\File\ASN1::encodeDER($params, \FluentSmtpLib\phpseclib3\File\ASN1\Maps\RSASSA_PSS_params::MAP)); + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA/Formats/Keys/PuTTY.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA/Formats/Keys/PuTTY.php new file mode 100644 index 0000000..883bd91 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA/Formats/Keys/PuTTY.php @@ -0,0 +1,107 @@ + + * @copyright 2015 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\Crypt\RSA\Formats\Keys; + +use FluentSmtpLib\phpseclib3\Common\Functions\Strings; +use FluentSmtpLib\phpseclib3\Crypt\Common\Formats\Keys\PuTTY as Progenitor; +use FluentSmtpLib\phpseclib3\Math\BigInteger; +/** + * PuTTY Formatted RSA Key Handler + * + * @author Jim Wigginton + */ +abstract class PuTTY extends \FluentSmtpLib\phpseclib3\Crypt\Common\Formats\Keys\PuTTY +{ + /** + * Public Handler + * + * @var string + */ + const PUBLIC_HANDLER = 'FluentSmtpLib\\phpseclib3\\Crypt\\RSA\\Formats\\Keys\\OpenSSH'; + /** + * Algorithm Identifier + * + * @var array + */ + protected static $types = ['ssh-rsa']; + /** + * Break a public or private key down into its constituent components + * + * @param string $key + * @param string $password optional + * @return array + */ + public static function load($key, $password = '') + { + static $one; + if (!isset($one)) { + $one = new \FluentSmtpLib\phpseclib3\Math\BigInteger(1); + } + $components = parent::load($key, $password); + if (!isset($components['private'])) { + return $components; + } + \extract($components); + unset($components['public'], $components['private']); + $isPublicKey = \false; + $result = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::unpackSSH2('ii', $public); + if ($result === \false) { + throw new \UnexpectedValueException('Key appears to be malformed'); + } + list($publicExponent, $modulus) = $result; + $result = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::unpackSSH2('iiii', $private); + if ($result === \false) { + throw new \UnexpectedValueException('Key appears to be malformed'); + } + $primes = $coefficients = []; + list($privateExponent, $primes[1], $primes[2], $coefficients[2]) = $result; + $temp = $primes[1]->subtract($one); + $exponents = [1 => $publicExponent->modInverse($temp)]; + $temp = $primes[2]->subtract($one); + $exponents[] = $publicExponent->modInverse($temp); + return \compact('publicExponent', 'modulus', 'privateExponent', 'primes', 'coefficients', 'exponents', 'comment', 'isPublicKey'); + } + /** + * Convert a private key to the appropriate format. + * + * @param BigInteger $n + * @param BigInteger $e + * @param BigInteger $d + * @param array $primes + * @param array $exponents + * @param array $coefficients + * @param string $password optional + * @param array $options optional + * @return string + */ + public static function savePrivateKey(\FluentSmtpLib\phpseclib3\Math\BigInteger $n, \FluentSmtpLib\phpseclib3\Math\BigInteger $e, \FluentSmtpLib\phpseclib3\Math\BigInteger $d, array $primes, array $exponents, array $coefficients, $password = '', array $options = []) + { + if (\count($primes) != 2) { + throw new \InvalidArgumentException('PuTTY does not support multi-prime RSA keys'); + } + $public = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::packSSH2('ii', $e, $n); + $private = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::packSSH2('iiii', $d, $primes[1], $primes[2], $coefficients[2]); + return self::wrapPrivateKey($public, $private, 'ssh-rsa', $password, $options); + } + /** + * Convert a public key to the appropriate format + * + * @param BigInteger $n + * @param BigInteger $e + * @return string + */ + public static function savePublicKey(\FluentSmtpLib\phpseclib3\Math\BigInteger $n, \FluentSmtpLib\phpseclib3\Math\BigInteger $e) + { + return self::wrapPublicKey(\FluentSmtpLib\phpseclib3\Common\Functions\Strings::packSSH2('ii', $e, $n), 'ssh-rsa'); + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA/Formats/Keys/Raw.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA/Formats/Keys/Raw.php new file mode 100644 index 0000000..09726fc --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA/Formats/Keys/Raw.php @@ -0,0 +1,153 @@ + + * @copyright 2015 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\Crypt\RSA\Formats\Keys; + +use FluentSmtpLib\phpseclib3\Math\BigInteger; +/** + * Raw RSA Key Handler + * + * @author Jim Wigginton + */ +abstract class Raw +{ + /** + * Break a public or private key down into its constituent components + * + * @param string $key + * @param string $password optional + * @return array + */ + public static function load($key, $password = '') + { + if (!\is_array($key)) { + throw new \UnexpectedValueException('Key should be a array - not a ' . \gettype($key)); + } + $key = \array_change_key_case($key, \CASE_LOWER); + $components = ['isPublicKey' => \false]; + foreach (['e', 'exponent', 'publicexponent', 0, 'privateexponent', 'd'] as $index) { + if (isset($key[$index])) { + $components['publicExponent'] = $key[$index]; + break; + } + } + foreach (['n', 'modulo', 'modulus', 1] as $index) { + if (isset($key[$index])) { + $components['modulus'] = $key[$index]; + break; + } + } + if (!isset($components['publicExponent']) || !isset($components['modulus'])) { + throw new \UnexpectedValueException('Modulus / exponent not present'); + } + if (isset($key['primes'])) { + $components['primes'] = $key['primes']; + } elseif (isset($key['p']) && isset($key['q'])) { + $indices = [['p', 'q'], ['prime1', 'prime2']]; + foreach ($indices as $index) { + list($i0, $i1) = $index; + if (isset($key[$i0]) && isset($key[$i1])) { + $components['primes'] = [1 => $key[$i0], $key[$i1]]; + } + } + } + if (isset($key['exponents'])) { + $components['exponents'] = $key['exponents']; + } else { + $indices = [['dp', 'dq'], ['exponent1', 'exponent2']]; + foreach ($indices as $index) { + list($i0, $i1) = $index; + if (isset($key[$i0]) && isset($key[$i1])) { + $components['exponents'] = [1 => $key[$i0], $key[$i1]]; + } + } + } + if (isset($key['coefficients'])) { + $components['coefficients'] = $key['coefficients']; + } else { + foreach (['inverseq', 'q\'', 'coefficient'] as $index) { + if (isset($key[$index])) { + $components['coefficients'] = [2 => $key[$index]]; + } + } + } + if (!isset($components['primes'])) { + $components['isPublicKey'] = \true; + return $components; + } + if (!isset($components['exponents'])) { + $one = new \FluentSmtpLib\phpseclib3\Math\BigInteger(1); + $temp = $components['primes'][1]->subtract($one); + $exponents = [1 => $components['publicExponent']->modInverse($temp)]; + $temp = $components['primes'][2]->subtract($one); + $exponents[] = $components['publicExponent']->modInverse($temp); + $components['exponents'] = $exponents; + } + if (!isset($components['coefficients'])) { + $components['coefficients'] = [2 => $components['primes'][2]->modInverse($components['primes'][1])]; + } + foreach (['privateexponent', 'd'] as $index) { + if (isset($key[$index])) { + $components['privateExponent'] = $key[$index]; + break; + } + } + return $components; + } + /** + * Convert a private key to the appropriate format. + * + * @param BigInteger $n + * @param BigInteger $e + * @param BigInteger $d + * @param array $primes + * @param array $exponents + * @param array $coefficients + * @param string $password optional + * @param array $options optional + * @return array + */ + public static function savePrivateKey(\FluentSmtpLib\phpseclib3\Math\BigInteger $n, \FluentSmtpLib\phpseclib3\Math\BigInteger $e, \FluentSmtpLib\phpseclib3\Math\BigInteger $d, array $primes, array $exponents, array $coefficients, $password = '', array $options = []) + { + if (!empty($password) && \is_string($password)) { + throw new \FluentSmtpLib\phpseclib3\Crypt\RSA\Formats\Keys\UnsupportedFormatException('Raw private keys do not support encryption'); + } + return ['e' => clone $e, 'n' => clone $n, 'd' => clone $d, 'primes' => \array_map(function ($var) { + return clone $var; + }, $primes), 'exponents' => \array_map(function ($var) { + return clone $var; + }, $exponents), 'coefficients' => \array_map(function ($var) { + return clone $var; + }, $coefficients)]; + } + /** + * Convert a public key to the appropriate format + * + * @param BigInteger $n + * @param BigInteger $e + * @return array + */ + public static function savePublicKey(\FluentSmtpLib\phpseclib3\Math\BigInteger $n, \FluentSmtpLib\phpseclib3\Math\BigInteger $e) + { + return ['e' => clone $e, 'n' => clone $n]; + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA/Formats/Keys/XML.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA/Formats/Keys/XML.php new file mode 100644 index 0000000..028af36 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA/Formats/Keys/XML.php @@ -0,0 +1,140 @@ + + * @copyright 2015 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\Crypt\RSA\Formats\Keys; + +use FluentSmtpLib\phpseclib3\Common\Functions\Strings; +use FluentSmtpLib\phpseclib3\Exception\BadConfigurationException; +use FluentSmtpLib\phpseclib3\Exception\UnsupportedFormatException; +use FluentSmtpLib\phpseclib3\Math\BigInteger; +/** + * XML Formatted RSA Key Handler + * + * @author Jim Wigginton + */ +abstract class XML +{ + /** + * Break a public or private key down into its constituent components + * + * @param string $key + * @param string $password optional + * @return array + */ + public static function load($key, $password = '') + { + if (!\FluentSmtpLib\phpseclib3\Common\Functions\Strings::is_stringable($key)) { + throw new \UnexpectedValueException('Key should be a string - not a ' . \gettype($key)); + } + if (!\class_exists('DOMDocument')) { + throw new \FluentSmtpLib\phpseclib3\Exception\BadConfigurationException('The dom extension is not setup correctly on this system'); + } + $components = ['isPublicKey' => \false, 'primes' => [], 'exponents' => [], 'coefficients' => []]; + $use_errors = \libxml_use_internal_errors(\true); + $dom = new \DOMDocument(); + if (\substr($key, 0, 5) != '' . $key . ''; + } + if (!$dom->loadXML($key)) { + \libxml_use_internal_errors($use_errors); + throw new \UnexpectedValueException('Key does not appear to contain XML'); + } + $xpath = new \DOMXPath($dom); + $keys = ['modulus', 'exponent', 'p', 'q', 'dp', 'dq', 'inverseq', 'd']; + foreach ($keys as $key) { + // $dom->getElementsByTagName($key) is case-sensitive + $temp = $xpath->query("//*[translate(local-name(), 'ABCDEFGHIJKLMNOPQRSTUVWXYZ','abcdefghijklmnopqrstuvwxyz')='{$key}']"); + if (!$temp->length) { + continue; + } + $value = new \FluentSmtpLib\phpseclib3\Math\BigInteger(\FluentSmtpLib\phpseclib3\Common\Functions\Strings::base64_decode($temp->item(0)->nodeValue), 256); + switch ($key) { + case 'modulus': + $components['modulus'] = $value; + break; + case 'exponent': + $components['publicExponent'] = $value; + break; + case 'p': + $components['primes'][1] = $value; + break; + case 'q': + $components['primes'][2] = $value; + break; + case 'dp': + $components['exponents'][1] = $value; + break; + case 'dq': + $components['exponents'][2] = $value; + break; + case 'inverseq': + $components['coefficients'][2] = $value; + break; + case 'd': + $components['privateExponent'] = $value; + } + } + \libxml_use_internal_errors($use_errors); + foreach ($components as $key => $value) { + if (\is_array($value) && !\count($value)) { + unset($components[$key]); + } + } + if (isset($components['modulus']) && isset($components['publicExponent'])) { + if (\count($components) == 3) { + $components['isPublicKey'] = \true; + } + return $components; + } + throw new \UnexpectedValueException('Modulus / exponent not present'); + } + /** + * Convert a private key to the appropriate format. + * + * @param BigInteger $n + * @param BigInteger $e + * @param BigInteger $d + * @param array $primes + * @param array $exponents + * @param array $coefficients + * @param string $password optional + * @return string + */ + public static function savePrivateKey(\FluentSmtpLib\phpseclib3\Math\BigInteger $n, \FluentSmtpLib\phpseclib3\Math\BigInteger $e, \FluentSmtpLib\phpseclib3\Math\BigInteger $d, array $primes, array $exponents, array $coefficients, $password = '') + { + if (\count($primes) != 2) { + throw new \InvalidArgumentException('XML does not support multi-prime RSA keys'); + } + if (!empty($password) && \is_string($password)) { + throw new \FluentSmtpLib\phpseclib3\Exception\UnsupportedFormatException('XML private keys do not support encryption'); + } + return "\r\n" . ' ' . \FluentSmtpLib\phpseclib3\Common\Functions\Strings::base64_encode($n->toBytes()) . "\r\n" . ' ' . \FluentSmtpLib\phpseclib3\Common\Functions\Strings::base64_encode($e->toBytes()) . "\r\n" . '

    ' . \FluentSmtpLib\phpseclib3\Common\Functions\Strings::base64_encode($primes[1]->toBytes()) . "

    \r\n" . ' ' . \FluentSmtpLib\phpseclib3\Common\Functions\Strings::base64_encode($primes[2]->toBytes()) . "\r\n" . ' ' . \FluentSmtpLib\phpseclib3\Common\Functions\Strings::base64_encode($exponents[1]->toBytes()) . "\r\n" . ' ' . \FluentSmtpLib\phpseclib3\Common\Functions\Strings::base64_encode($exponents[2]->toBytes()) . "\r\n" . ' ' . \FluentSmtpLib\phpseclib3\Common\Functions\Strings::base64_encode($coefficients[2]->toBytes()) . "\r\n" . ' ' . \FluentSmtpLib\phpseclib3\Common\Functions\Strings::base64_encode($d->toBytes()) . "\r\n" . '
    '; + } + /** + * Convert a public key to the appropriate format + * + * @param BigInteger $n + * @param BigInteger $e + * @return string + */ + public static function savePublicKey(\FluentSmtpLib\phpseclib3\Math\BigInteger $n, \FluentSmtpLib\phpseclib3\Math\BigInteger $e) + { + return "\r\n" . ' ' . \FluentSmtpLib\phpseclib3\Common\Functions\Strings::base64_encode($n->toBytes()) . "\r\n" . ' ' . \FluentSmtpLib\phpseclib3\Common\Functions\Strings::base64_encode($e->toBytes()) . "\r\n" . ''; + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA/PrivateKey.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA/PrivateKey.php new file mode 100644 index 0000000..e77f306 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA/PrivateKey.php @@ -0,0 +1,441 @@ + + * @copyright 2015 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\Crypt\RSA; + +use FluentSmtpLib\phpseclib3\Crypt\Common; +use FluentSmtpLib\phpseclib3\Crypt\Random; +use FluentSmtpLib\phpseclib3\Crypt\RSA; +use FluentSmtpLib\phpseclib3\Crypt\RSA\Formats\Keys\PSS; +use FluentSmtpLib\phpseclib3\Exception\UnsupportedFormatException; +use FluentSmtpLib\phpseclib3\Math\BigInteger; +/** + * Raw RSA Key Handler + * + * @author Jim Wigginton + */ +final class PrivateKey extends \FluentSmtpLib\phpseclib3\Crypt\RSA implements \FluentSmtpLib\phpseclib3\Crypt\Common\PrivateKey +{ + use Common\Traits\PasswordProtected; + /** + * Primes for Chinese Remainder Theorem (ie. p and q) + * + * @var array + */ + protected $primes; + /** + * Exponents for Chinese Remainder Theorem (ie. dP and dQ) + * + * @var array + */ + protected $exponents; + /** + * Coefficients for Chinese Remainder Theorem (ie. qInv) + * + * @var array + */ + protected $coefficients; + /** + * Private Exponent + * + * @var BigInteger + */ + protected $privateExponent; + /** + * RSADP + * + * See {@link http://tools.ietf.org/html/rfc3447#section-5.1.2 RFC3447#section-5.1.2}. + * + * @return bool|BigInteger + */ + private function rsadp(\FluentSmtpLib\phpseclib3\Math\BigInteger $c) + { + if ($c->compare(self::$zero) < 0 || $c->compare($this->modulus) > 0) { + throw new \OutOfRangeException('Ciphertext representative out of range'); + } + return $this->exponentiate($c); + } + /** + * RSASP1 + * + * See {@link http://tools.ietf.org/html/rfc3447#section-5.2.1 RFC3447#section-5.2.1}. + * + * @return bool|BigInteger + */ + private function rsasp1(\FluentSmtpLib\phpseclib3\Math\BigInteger $m) + { + if ($m->compare(self::$zero) < 0 || $m->compare($this->modulus) > 0) { + throw new \OutOfRangeException('Signature representative out of range'); + } + return $this->exponentiate($m); + } + /** + * Exponentiate + * + * @param BigInteger $x + * @return BigInteger + */ + protected function exponentiate(\FluentSmtpLib\phpseclib3\Math\BigInteger $x) + { + switch (\true) { + case empty($this->primes): + case $this->primes[1]->equals(self::$zero): + case empty($this->coefficients): + case $this->coefficients[2]->equals(self::$zero): + case empty($this->exponents): + case $this->exponents[1]->equals(self::$zero): + return $x->modPow($this->exponent, $this->modulus); + } + $num_primes = \count($this->primes); + if (!static::$enableBlinding) { + $m_i = [1 => $x->modPow($this->exponents[1], $this->primes[1]), 2 => $x->modPow($this->exponents[2], $this->primes[2])]; + $h = $m_i[1]->subtract($m_i[2]); + $h = $h->multiply($this->coefficients[2]); + list(, $h) = $h->divide($this->primes[1]); + $m = $m_i[2]->add($h->multiply($this->primes[2])); + $r = $this->primes[1]; + for ($i = 3; $i <= $num_primes; $i++) { + $m_i = $x->modPow($this->exponents[$i], $this->primes[$i]); + $r = $r->multiply($this->primes[$i - 1]); + $h = $m_i->subtract($m); + $h = $h->multiply($this->coefficients[$i]); + list(, $h) = $h->divide($this->primes[$i]); + $m = $m->add($r->multiply($h)); + } + } else { + $smallest = $this->primes[1]; + for ($i = 2; $i <= $num_primes; $i++) { + if ($smallest->compare($this->primes[$i]) > 0) { + $smallest = $this->primes[$i]; + } + } + $r = \FluentSmtpLib\phpseclib3\Math\BigInteger::randomRange(self::$one, $smallest->subtract(self::$one)); + $m_i = [1 => $this->blind($x, $r, 1), 2 => $this->blind($x, $r, 2)]; + $h = $m_i[1]->subtract($m_i[2]); + $h = $h->multiply($this->coefficients[2]); + list(, $h) = $h->divide($this->primes[1]); + $m = $m_i[2]->add($h->multiply($this->primes[2])); + $r = $this->primes[1]; + for ($i = 3; $i <= $num_primes; $i++) { + $m_i = $this->blind($x, $r, $i); + $r = $r->multiply($this->primes[$i - 1]); + $h = $m_i->subtract($m); + $h = $h->multiply($this->coefficients[$i]); + list(, $h) = $h->divide($this->primes[$i]); + $m = $m->add($r->multiply($h)); + } + } + return $m; + } + /** + * Performs RSA Blinding + * + * Protects against timing attacks by employing RSA Blinding. + * Returns $x->modPow($this->exponents[$i], $this->primes[$i]) + * + * @param BigInteger $x + * @param BigInteger $r + * @param int $i + * @return BigInteger + */ + private function blind(\FluentSmtpLib\phpseclib3\Math\BigInteger $x, \FluentSmtpLib\phpseclib3\Math\BigInteger $r, $i) + { + $x = $x->multiply($r->modPow($this->publicExponent, $this->primes[$i])); + $x = $x->modPow($this->exponents[$i], $this->primes[$i]); + $r = $r->modInverse($this->primes[$i]); + $x = $x->multiply($r); + list(, $x) = $x->divide($this->primes[$i]); + return $x; + } + /** + * EMSA-PSS-ENCODE + * + * See {@link http://tools.ietf.org/html/rfc3447#section-9.1.1 RFC3447#section-9.1.1}. + * + * @return string + * @param string $m + * @throws \RuntimeException on encoding error + * @param int $emBits + */ + private function emsa_pss_encode($m, $emBits) + { + // if $m is larger than two million terrabytes and you're using sha1, PKCS#1 suggests a "Label too long" error + // be output. + $emLen = $emBits + 1 >> 3; + // ie. ceil($emBits / 8) + $sLen = $this->sLen !== null ? $this->sLen : $this->hLen; + $mHash = $this->hash->hash($m); + if ($emLen < $this->hLen + $sLen + 2) { + throw new \LengthException('RSA modulus too short'); + } + $salt = \FluentSmtpLib\phpseclib3\Crypt\Random::string($sLen); + $m2 = "\x00\x00\x00\x00\x00\x00\x00\x00" . $mHash . $salt; + $h = $this->hash->hash($m2); + $ps = \str_repeat(\chr(0), $emLen - $sLen - $this->hLen - 2); + $db = $ps . \chr(1) . $salt; + $dbMask = $this->mgf1($h, $emLen - $this->hLen - 1); + // ie. stlren($db) + $maskedDB = $db ^ $dbMask; + $maskedDB[0] = ~\chr(0xff << ($emBits & 7)) & $maskedDB[0]; + $em = $maskedDB . $h . \chr(0xbc); + return $em; + } + /** + * RSASSA-PSS-SIGN + * + * See {@link http://tools.ietf.org/html/rfc3447#section-8.1.1 RFC3447#section-8.1.1}. + * + * @param string $m + * @return bool|string + */ + private function rsassa_pss_sign($m) + { + // EMSA-PSS encoding + $em = $this->emsa_pss_encode($m, 8 * $this->k - 1); + // RSA signature + $m = $this->os2ip($em); + $s = $this->rsasp1($m); + $s = $this->i2osp($s, $this->k); + // Output the signature S + return $s; + } + /** + * RSASSA-PKCS1-V1_5-SIGN + * + * See {@link http://tools.ietf.org/html/rfc3447#section-8.2.1 RFC3447#section-8.2.1}. + * + * @param string $m + * @throws \LengthException if the RSA modulus is too short + * @return bool|string + */ + private function rsassa_pkcs1_v1_5_sign($m) + { + // EMSA-PKCS1-v1_5 encoding + // If the encoding operation outputs "intended encoded message length too short," output "RSA modulus + // too short" and stop. + try { + $em = $this->emsa_pkcs1_v1_5_encode($m, $this->k); + } catch (\LengthException $e) { + throw new \LengthException('RSA modulus too short'); + } + // RSA signature + $m = $this->os2ip($em); + $s = $this->rsasp1($m); + $s = $this->i2osp($s, $this->k); + // Output the signature S + return $s; + } + /** + * Create a signature + * + * @see self::verify() + * @param string $message + * @return string + */ + public function sign($message) + { + switch ($this->signaturePadding) { + case self::SIGNATURE_PKCS1: + case self::SIGNATURE_RELAXED_PKCS1: + return $this->rsassa_pkcs1_v1_5_sign($message); + //case self::SIGNATURE_PSS: + default: + return $this->rsassa_pss_sign($message); + } + } + /** + * RSAES-PKCS1-V1_5-DECRYPT + * + * See {@link http://tools.ietf.org/html/rfc3447#section-7.2.2 RFC3447#section-7.2.2}. + * + * @param string $c + * @return bool|string + */ + private function rsaes_pkcs1_v1_5_decrypt($c) + { + // Length checking + if (\strlen($c) != $this->k) { + // or if k < 11 + throw new \LengthException('Ciphertext representative too long'); + } + // RSA decryption + $c = $this->os2ip($c); + $m = $this->rsadp($c); + $em = $this->i2osp($m, $this->k); + // EME-PKCS1-v1_5 decoding + if (\ord($em[0]) != 0 || \ord($em[1]) > 2) { + throw new \RuntimeException('Decryption error'); + } + $ps = \substr($em, 2, \strpos($em, \chr(0), 2) - 2); + $m = \substr($em, \strlen($ps) + 3); + if (\strlen($ps) < 8) { + throw new \RuntimeException('Decryption error'); + } + // Output M + return $m; + } + /** + * RSAES-OAEP-DECRYPT + * + * See {@link http://tools.ietf.org/html/rfc3447#section-7.1.2 RFC3447#section-7.1.2}. The fact that the error + * messages aren't distinguishable from one another hinders debugging, but, to quote from RFC3447#section-7.1.2: + * + * Note. Care must be taken to ensure that an opponent cannot + * distinguish the different error conditions in Step 3.g, whether by + * error message or timing, or, more generally, learn partial + * information about the encoded message EM. Otherwise an opponent may + * be able to obtain useful information about the decryption of the + * ciphertext C, leading to a chosen-ciphertext attack such as the one + * observed by Manger [36]. + * + * @param string $c + * @return bool|string + */ + private function rsaes_oaep_decrypt($c) + { + // Length checking + // if $l is larger than two million terrabytes and you're using sha1, PKCS#1 suggests a "Label too long" error + // be output. + if (\strlen($c) != $this->k || $this->k < 2 * $this->hLen + 2) { + throw new \LengthException('Ciphertext representative too long'); + } + // RSA decryption + $c = $this->os2ip($c); + $m = $this->rsadp($c); + $em = $this->i2osp($m, $this->k); + // EME-OAEP decoding + $lHash = $this->hash->hash($this->label); + $y = \ord($em[0]); + $maskedSeed = \substr($em, 1, $this->hLen); + $maskedDB = \substr($em, $this->hLen + 1); + $seedMask = $this->mgf1($maskedDB, $this->hLen); + $seed = $maskedSeed ^ $seedMask; + $dbMask = $this->mgf1($seed, $this->k - $this->hLen - 1); + $db = $maskedDB ^ $dbMask; + $lHash2 = \substr($db, 0, $this->hLen); + $m = \substr($db, $this->hLen); + $hashesMatch = \hash_equals($lHash, $lHash2); + $leadingZeros = 1; + $patternMatch = 0; + $offset = 0; + for ($i = 0; $i < \strlen($m); $i++) { + $patternMatch |= $leadingZeros & $m[$i] === "\x01"; + $leadingZeros &= $m[$i] === "\x00"; + $offset += $patternMatch ? 0 : 1; + } + // we do | instead of || to avoid https://en.wikipedia.org/wiki/Short-circuit_evaluation + // to protect against timing attacks + if (!$hashesMatch | !$patternMatch) { + throw new \RuntimeException('Decryption error'); + } + // Output the message M + return \substr($m, $offset + 1); + } + /** + * Raw Encryption / Decryption + * + * Doesn't use padding and is not recommended. + * + * @param string $m + * @return bool|string + * @throws \LengthException if strlen($m) > $this->k + */ + private function raw_encrypt($m) + { + if (\strlen($m) > $this->k) { + throw new \LengthException('Ciphertext representative too long'); + } + $temp = $this->os2ip($m); + $temp = $this->rsadp($temp); + return $this->i2osp($temp, $this->k); + } + /** + * Decryption + * + * @see self::encrypt() + * @param string $ciphertext + * @return bool|string + */ + public function decrypt($ciphertext) + { + switch ($this->encryptionPadding) { + case self::ENCRYPTION_NONE: + return $this->raw_encrypt($ciphertext); + case self::ENCRYPTION_PKCS1: + return $this->rsaes_pkcs1_v1_5_decrypt($ciphertext); + //case self::ENCRYPTION_OAEP: + default: + return $this->rsaes_oaep_decrypt($ciphertext); + } + } + /** + * Returns the public key + * + * @return mixed + */ + public function getPublicKey() + { + $type = self::validatePlugin('Keys', 'PKCS8', 'savePublicKey'); + if (empty($this->modulus) || empty($this->publicExponent)) { + throw new \RuntimeException('Public key components not found'); + } + $key = $type::savePublicKey($this->modulus, $this->publicExponent); + return \FluentSmtpLib\phpseclib3\Crypt\RSA::loadFormat('PKCS8', $key)->withHash($this->hash->getHash())->withMGFHash($this->mgfHash->getHash())->withSaltLength($this->sLen)->withLabel($this->label)->withPadding($this->signaturePadding | $this->encryptionPadding); + } + /** + * Returns the private key + * + * @param string $type + * @param array $options optional + * @return string + */ + public function toString($type, array $options = []) + { + $type = self::validatePlugin('Keys', $type, empty($this->primes) ? 'savePublicKey' : 'savePrivateKey'); + if ($type == \FluentSmtpLib\phpseclib3\Crypt\RSA\Formats\Keys\PSS::class) { + if ($this->signaturePadding == self::SIGNATURE_PSS) { + $options += ['hash' => $this->hash->getHash(), 'MGFHash' => $this->mgfHash->getHash(), 'saltLength' => $this->getSaltLength()]; + } else { + throw new \FluentSmtpLib\phpseclib3\Exception\UnsupportedFormatException('The PSS format can only be used when the signature method has been explicitly set to PSS'); + } + } + if (empty($this->primes)) { + return $type::savePublicKey($this->modulus, $this->exponent, $options); + } + return $type::savePrivateKey($this->modulus, $this->publicExponent, $this->exponent, $this->primes, $this->exponents, $this->coefficients, $this->password, $options); + /* + $key = $type::savePrivateKey($this->modulus, $this->publicExponent, $this->exponent, $this->primes, $this->exponents, $this->coefficients, $this->password, $options); + if ($key !== false || count($this->primes) == 2) { + return $key; + } + + $nSize = $this->getSize() >> 1; + + $primes = [1 => clone self::$one, clone self::$one]; + $i = 1; + foreach ($this->primes as $prime) { + $primes[$i] = $primes[$i]->multiply($prime); + if ($primes[$i]->getLength() >= $nSize) { + $i++; + } + } + + $exponents = []; + $coefficients = [2 => $primes[2]->modInverse($primes[1])]; + + foreach ($primes as $i => $prime) { + $temp = $prime->subtract(self::$one); + $exponents[$i] = $this->modulus->modInverse($temp); + } + + return $type::savePrivateKey($this->modulus, $this->publicExponent, $this->exponent, $primes, $exponents, $coefficients, $this->password, $options); + */ + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA/PublicKey.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA/PublicKey.php new file mode 100644 index 0000000..8fb38bd --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA/PublicKey.php @@ -0,0 +1,439 @@ + + * @copyright 2015 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\Crypt\RSA; + +use FluentSmtpLib\phpseclib3\Common\Functions\Strings; +use FluentSmtpLib\phpseclib3\Crypt\Common; +use FluentSmtpLib\phpseclib3\Crypt\Hash; +use FluentSmtpLib\phpseclib3\Crypt\Random; +use FluentSmtpLib\phpseclib3\Crypt\RSA; +use FluentSmtpLib\phpseclib3\Crypt\RSA\Formats\Keys\PSS; +use FluentSmtpLib\phpseclib3\Exception\UnsupportedAlgorithmException; +use FluentSmtpLib\phpseclib3\Exception\UnsupportedFormatException; +use FluentSmtpLib\phpseclib3\File\ASN1; +use FluentSmtpLib\phpseclib3\File\ASN1\Maps\DigestInfo; +use FluentSmtpLib\phpseclib3\Math\BigInteger; +/** + * Raw RSA Key Handler + * + * @author Jim Wigginton + */ +final class PublicKey extends \FluentSmtpLib\phpseclib3\Crypt\RSA implements \FluentSmtpLib\phpseclib3\Crypt\Common\PublicKey +{ + use Common\Traits\Fingerprint; + /** + * Exponentiate + * + * @param BigInteger $x + * @return BigInteger + */ + private function exponentiate(\FluentSmtpLib\phpseclib3\Math\BigInteger $x) + { + return $x->modPow($this->exponent, $this->modulus); + } + /** + * RSAVP1 + * + * See {@link http://tools.ietf.org/html/rfc3447#section-5.2.2 RFC3447#section-5.2.2}. + * + * @param BigInteger $s + * @return bool|BigInteger + */ + private function rsavp1($s) + { + if ($s->compare(self::$zero) < 0 || $s->compare($this->modulus) > 0) { + return \false; + } + return $this->exponentiate($s); + } + /** + * RSASSA-PKCS1-V1_5-VERIFY + * + * See {@link http://tools.ietf.org/html/rfc3447#section-8.2.2 RFC3447#section-8.2.2}. + * + * @param string $m + * @param string $s + * @throws \LengthException if the RSA modulus is too short + * @return bool + */ + private function rsassa_pkcs1_v1_5_verify($m, $s) + { + // Length checking + if (\strlen($s) != $this->k) { + return \false; + } + // RSA verification + $s = $this->os2ip($s); + $m2 = $this->rsavp1($s); + if ($m2 === \false) { + return \false; + } + $em = $this->i2osp($m2, $this->k); + if ($em === \false) { + return \false; + } + // EMSA-PKCS1-v1_5 encoding + $exception = \false; + // If the encoding operation outputs "intended encoded message length too short," output "RSA modulus + // too short" and stop. + try { + $em2 = $this->emsa_pkcs1_v1_5_encode($m, $this->k); + $r1 = \hash_equals($em, $em2); + } catch (\LengthException $e) { + $exception = \true; + } + try { + $em3 = $this->emsa_pkcs1_v1_5_encode_without_null($m, $this->k); + $r2 = \hash_equals($em, $em3); + } catch (\LengthException $e) { + $exception = \true; + } catch (\FluentSmtpLib\phpseclib3\Exception\UnsupportedAlgorithmException $e) { + $r2 = \false; + } + if ($exception) { + throw new \LengthException('RSA modulus too short'); + } + // Compare + return $r1 || $r2; + } + /** + * RSASSA-PKCS1-V1_5-VERIFY (relaxed matching) + * + * Per {@link http://tools.ietf.org/html/rfc3447#page-43 RFC3447#page-43} PKCS1 v1.5 + * specified the use BER encoding rather than DER encoding that PKCS1 v2.0 specified. + * This means that under rare conditions you can have a perfectly valid v1.5 signature + * that fails to validate with _rsassa_pkcs1_v1_5_verify(). PKCS1 v2.1 also recommends + * that if you're going to validate these types of signatures you "should indicate + * whether the underlying BER encoding is a DER encoding and hence whether the signature + * is valid with respect to the specification given in [PKCS1 v2.0+]". so if you do + * $rsa->getLastPadding() and get RSA::PADDING_RELAXED_PKCS1 back instead of + * RSA::PADDING_PKCS1... that means BER encoding was used. + * + * @param string $m + * @param string $s + * @return bool + */ + private function rsassa_pkcs1_v1_5_relaxed_verify($m, $s) + { + // Length checking + if (\strlen($s) != $this->k) { + return \false; + } + // RSA verification + $s = $this->os2ip($s); + $m2 = $this->rsavp1($s); + if ($m2 === \false) { + return \false; + } + $em = $this->i2osp($m2, $this->k); + if ($em === \false) { + return \false; + } + if (\FluentSmtpLib\phpseclib3\Common\Functions\Strings::shift($em, 2) != "\x00\x01") { + return \false; + } + $em = \ltrim($em, "\xff"); + if (\FluentSmtpLib\phpseclib3\Common\Functions\Strings::shift($em) != "\x00") { + return \false; + } + $decoded = \FluentSmtpLib\phpseclib3\File\ASN1::decodeBER($em); + if (!\is_array($decoded) || empty($decoded[0]) || \strlen($em) > $decoded[0]['length']) { + return \false; + } + static $oids; + if (!isset($oids)) { + $oids = [ + 'md2' => '1.2.840.113549.2.2', + 'md4' => '1.2.840.113549.2.4', + // from PKCS1 v1.5 + 'md5' => '1.2.840.113549.2.5', + 'id-sha1' => '1.3.14.3.2.26', + 'id-sha256' => '2.16.840.1.101.3.4.2.1', + 'id-sha384' => '2.16.840.1.101.3.4.2.2', + 'id-sha512' => '2.16.840.1.101.3.4.2.3', + // from PKCS1 v2.2 + 'id-sha224' => '2.16.840.1.101.3.4.2.4', + 'id-sha512/224' => '2.16.840.1.101.3.4.2.5', + 'id-sha512/256' => '2.16.840.1.101.3.4.2.6', + ]; + \FluentSmtpLib\phpseclib3\File\ASN1::loadOIDs($oids); + } + $decoded = \FluentSmtpLib\phpseclib3\File\ASN1::asn1map($decoded[0], \FluentSmtpLib\phpseclib3\File\ASN1\Maps\DigestInfo::MAP); + if (!isset($decoded) || $decoded === \false) { + return \false; + } + if (!isset($oids[$decoded['digestAlgorithm']['algorithm']])) { + return \false; + } + if (isset($decoded['digestAlgorithm']['parameters']) && $decoded['digestAlgorithm']['parameters'] !== ['null' => '']) { + return \false; + } + $hash = $decoded['digestAlgorithm']['algorithm']; + $hash = \substr($hash, 0, 3) == 'id-' ? \substr($hash, 3) : $hash; + $hash = new \FluentSmtpLib\phpseclib3\Crypt\Hash($hash); + $em = $hash->hash($m); + $em2 = $decoded['digest']; + return \hash_equals($em, $em2); + } + /** + * EMSA-PSS-VERIFY + * + * See {@link http://tools.ietf.org/html/rfc3447#section-9.1.2 RFC3447#section-9.1.2}. + * + * @param string $m + * @param string $em + * @param int $emBits + * @return string + */ + private function emsa_pss_verify($m, $em, $emBits) + { + // if $m is larger than two million terrabytes and you're using sha1, PKCS#1 suggests a "Label too long" error + // be output. + $emLen = $emBits + 7 >> 3; + // ie. ceil($emBits / 8); + $sLen = $this->sLen !== null ? $this->sLen : $this->hLen; + $mHash = $this->hash->hash($m); + if ($emLen < $this->hLen + $sLen + 2) { + return \false; + } + if ($em[\strlen($em) - 1] != \chr(0xbc)) { + return \false; + } + $maskedDB = \substr($em, 0, -$this->hLen - 1); + $h = \substr($em, -$this->hLen - 1, $this->hLen); + $temp = \chr(0xff << ($emBits & 7)); + if ((~$maskedDB[0] & $temp) != $temp) { + return \false; + } + $dbMask = $this->mgf1($h, $emLen - $this->hLen - 1); + $db = $maskedDB ^ $dbMask; + $db[0] = ~\chr(0xff << ($emBits & 7)) & $db[0]; + $temp = $emLen - $this->hLen - $sLen - 2; + if (\substr($db, 0, $temp) != \str_repeat(\chr(0), $temp) || \ord($db[$temp]) != 1) { + return \false; + } + $salt = \substr($db, $temp + 1); + // should be $sLen long + $m2 = "\x00\x00\x00\x00\x00\x00\x00\x00" . $mHash . $salt; + $h2 = $this->hash->hash($m2); + return \hash_equals($h, $h2); + } + /** + * RSASSA-PSS-VERIFY + * + * See {@link http://tools.ietf.org/html/rfc3447#section-8.1.2 RFC3447#section-8.1.2}. + * + * @param string $m + * @param string $s + * @return bool|string + */ + private function rsassa_pss_verify($m, $s) + { + // Length checking + if (\strlen($s) != $this->k) { + return \false; + } + // RSA verification + $modBits = \strlen($this->modulus->toBits()); + $s2 = $this->os2ip($s); + $m2 = $this->rsavp1($s2); + $em = $this->i2osp($m2, $this->k); + if ($em === \false) { + return \false; + } + // EMSA-PSS verification + return $this->emsa_pss_verify($m, $em, $modBits - 1); + } + /** + * Verifies a signature + * + * @see self::sign() + * @param string $message + * @param string $signature + * @return bool + */ + public function verify($message, $signature) + { + switch ($this->signaturePadding) { + case self::SIGNATURE_RELAXED_PKCS1: + return $this->rsassa_pkcs1_v1_5_relaxed_verify($message, $signature); + case self::SIGNATURE_PKCS1: + return $this->rsassa_pkcs1_v1_5_verify($message, $signature); + //case self::SIGNATURE_PSS: + default: + return $this->rsassa_pss_verify($message, $signature); + } + } + /** + * RSAES-PKCS1-V1_5-ENCRYPT + * + * See {@link http://tools.ietf.org/html/rfc3447#section-7.2.1 RFC3447#section-7.2.1}. + * + * @param string $m + * @param bool $pkcs15_compat optional + * @throws \LengthException if strlen($m) > $this->k - 11 + * @return bool|string + */ + private function rsaes_pkcs1_v1_5_encrypt($m, $pkcs15_compat = \false) + { + $mLen = \strlen($m); + // Length checking + if ($mLen > $this->k - 11) { + throw new \LengthException('Message too long'); + } + // EME-PKCS1-v1_5 encoding + $psLen = $this->k - $mLen - 3; + $ps = ''; + while (\strlen($ps) != $psLen) { + $temp = \FluentSmtpLib\phpseclib3\Crypt\Random::string($psLen - \strlen($ps)); + $temp = \str_replace("\x00", '', $temp); + $ps .= $temp; + } + $type = 2; + $em = \chr(0) . \chr($type) . $ps . \chr(0) . $m; + // RSA encryption + $m = $this->os2ip($em); + $c = $this->rsaep($m); + $c = $this->i2osp($c, $this->k); + // Output the ciphertext C + return $c; + } + /** + * RSAES-OAEP-ENCRYPT + * + * See {@link http://tools.ietf.org/html/rfc3447#section-7.1.1 RFC3447#section-7.1.1} and + * {http://en.wikipedia.org/wiki/Optimal_Asymmetric_Encryption_Padding OAES}. + * + * @param string $m + * @throws \LengthException if strlen($m) > $this->k - 2 * $this->hLen - 2 + * @return string + */ + private function rsaes_oaep_encrypt($m) + { + $mLen = \strlen($m); + // Length checking + // if $l is larger than two million terrabytes and you're using sha1, PKCS#1 suggests a "Label too long" error + // be output. + if ($mLen > $this->k - 2 * $this->hLen - 2) { + throw new \LengthException('Message too long'); + } + // EME-OAEP encoding + $lHash = $this->hash->hash($this->label); + $ps = \str_repeat(\chr(0), $this->k - $mLen - 2 * $this->hLen - 2); + $db = $lHash . $ps . \chr(1) . $m; + $seed = \FluentSmtpLib\phpseclib3\Crypt\Random::string($this->hLen); + $dbMask = $this->mgf1($seed, $this->k - $this->hLen - 1); + $maskedDB = $db ^ $dbMask; + $seedMask = $this->mgf1($maskedDB, $this->hLen); + $maskedSeed = $seed ^ $seedMask; + $em = \chr(0) . $maskedSeed . $maskedDB; + // RSA encryption + $m = $this->os2ip($em); + $c = $this->rsaep($m); + $c = $this->i2osp($c, $this->k); + // Output the ciphertext C + return $c; + } + /** + * RSAEP + * + * See {@link http://tools.ietf.org/html/rfc3447#section-5.1.1 RFC3447#section-5.1.1}. + * + * @param BigInteger $m + * @return bool|BigInteger + */ + private function rsaep($m) + { + if ($m->compare(self::$zero) < 0 || $m->compare($this->modulus) > 0) { + throw new \OutOfRangeException('Message representative out of range'); + } + return $this->exponentiate($m); + } + /** + * Raw Encryption / Decryption + * + * Doesn't use padding and is not recommended. + * + * @param string $m + * @return bool|string + * @throws \LengthException if strlen($m) > $this->k + */ + private function raw_encrypt($m) + { + if (\strlen($m) > $this->k) { + throw new \LengthException('Message too long'); + } + $temp = $this->os2ip($m); + $temp = $this->rsaep($temp); + return $this->i2osp($temp, $this->k); + } + /** + * Encryption + * + * Both self::PADDING_OAEP and self::PADDING_PKCS1 both place limits on how long $plaintext can be. + * If $plaintext exceeds those limits it will be broken up so that it does and the resultant ciphertext's will + * be concatenated together. + * + * @see self::decrypt() + * @param string $plaintext + * @return bool|string + * @throws \LengthException if the RSA modulus is too short + */ + public function encrypt($plaintext) + { + switch ($this->encryptionPadding) { + case self::ENCRYPTION_NONE: + return $this->raw_encrypt($plaintext); + case self::ENCRYPTION_PKCS1: + return $this->rsaes_pkcs1_v1_5_encrypt($plaintext); + //case self::ENCRYPTION_OAEP: + default: + return $this->rsaes_oaep_encrypt($plaintext); + } + } + /** + * Returns the public key + * + * The public key is only returned under two circumstances - if the private key had the public key embedded within it + * or if the public key was set via setPublicKey(). If the currently loaded key is supposed to be the public key this + * function won't return it since this library, for the most part, doesn't distinguish between public and private keys. + * + * @param string $type + * @param array $options optional + * @return mixed + */ + public function toString($type, array $options = []) + { + $type = self::validatePlugin('Keys', $type, 'savePublicKey'); + if ($type == \FluentSmtpLib\phpseclib3\Crypt\RSA\Formats\Keys\PSS::class) { + if ($this->signaturePadding == self::SIGNATURE_PSS) { + $options += ['hash' => $this->hash->getHash(), 'MGFHash' => $this->mgfHash->getHash(), 'saltLength' => $this->getSaltLength()]; + } else { + throw new \FluentSmtpLib\phpseclib3\Exception\UnsupportedFormatException('The PSS format can only be used when the signature method has been explicitly set to PSS'); + } + } + return $type::savePublicKey($this->modulus, $this->publicExponent, $options); + } + /** + * Converts a public key to a private key + * + * @return RSA + */ + public function asPrivateKey() + { + $new = new \FluentSmtpLib\phpseclib3\Crypt\RSA\PrivateKey(); + $new->exponent = $this->exponent; + $new->modulus = $this->modulus; + $new->k = $this->k; + $new->format = $this->format; + return $new->withHash($this->hash->getHash())->withMGFHash($this->mgfHash->getHash())->withSaltLength($this->sLen)->withLabel($this->label)->withPadding($this->signaturePadding | $this->encryptionPadding); + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/Random.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/Random.php new file mode 100644 index 0000000..e44cbe7 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/Random.php @@ -0,0 +1,202 @@ + + * + * + * + * @author Jim Wigginton + * @copyright 2007 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\Crypt; + +/** + * Pure-PHP Random Number Generator + * + * @author Jim Wigginton + */ +abstract class Random +{ + /** + * Generate a random string. + * + * Although microoptimizations are generally discouraged as they impair readability this function is ripe with + * microoptimizations because this function has the potential of being called a huge number of times. + * eg. for RSA key generation. + * + * @param int $length + * @throws \RuntimeException if a symmetric cipher is needed but not loaded + * @return string + */ + public static function string($length) + { + if (!$length) { + return ''; + } + try { + return \random_bytes($length); + } catch (\Exception $e) { + // random_compat will throw an Exception, which in PHP 5 does not implement Throwable + } catch (\Throwable $e) { + // If a sufficient source of randomness is unavailable, random_bytes() will throw an + // object that implements the Throwable interface (Exception, TypeError, Error). + // We don't actually need to do anything here. The string() method should just continue + // as normal. Note, however, that if we don't have a sufficient source of randomness for + // random_bytes(), most of the other calls here will fail too, so we'll end up using + // the PHP implementation. + } + // at this point we have no choice but to use a pure-PHP CSPRNG + // cascade entropy across multiple PHP instances by fixing the session and collecting all + // environmental variables, including the previous session data and the current session + // data. + // + // mt_rand seeds itself by looking at the PID and the time, both of which are (relatively) + // easy to guess at. linux uses mouse clicks, keyboard timings, etc, as entropy sources, but + // PHP isn't low level to be able to use those as sources and on a web server there's not likely + // going to be a ton of keyboard or mouse action. web servers do have one thing that we can use + // however, a ton of people visiting the website. obviously you don't want to base your seeding + // solely on parameters a potential attacker sends but (1) not everything in $_SERVER is controlled + // by the user and (2) this isn't just looking at the data sent by the current user - it's based + // on the data sent by all users. one user requests the page and a hash of their info is saved. + // another user visits the page and the serialization of their data is utilized along with the + // server environment stuff and a hash of the previous http request data (which itself utilizes + // a hash of the session data before that). certainly an attacker should be assumed to have + // full control over his own http requests. he, however, is not going to have control over + // everyone's http requests. + static $crypto = \false, $v; + if ($crypto === \false) { + // save old session data + $old_session_id = \session_id(); + $old_use_cookies = \ini_get('session.use_cookies'); + $old_session_cache_limiter = \session_cache_limiter(); + $_OLD_SESSION = isset($_SESSION) ? $_SESSION : \false; + if ($old_session_id != '') { + \session_write_close(); + } + \session_id(1); + \ini_set('session.use_cookies', 0); + \session_cache_limiter(''); + \session_start(); + $v = (isset($_SERVER) ? self::safe_serialize($_SERVER) : '') . (isset($_POST) ? self::safe_serialize($_POST) : '') . (isset($_GET) ? self::safe_serialize($_GET) : '') . (isset($_COOKIE) ? self::safe_serialize($_COOKIE) : '') . (\version_compare(\PHP_VERSION, '8.1.0', '>=') ? \serialize($GLOBALS) : self::safe_serialize($GLOBALS)) . self::safe_serialize($_SESSION) . self::safe_serialize($_OLD_SESSION); + $v = $seed = $_SESSION['seed'] = \sha1($v, \true); + if (!isset($_SESSION['count'])) { + $_SESSION['count'] = 0; + } + $_SESSION['count']++; + \session_write_close(); + // restore old session data + if ($old_session_id != '') { + \session_id($old_session_id); + \session_start(); + \ini_set('session.use_cookies', $old_use_cookies); + \session_cache_limiter($old_session_cache_limiter); + } else { + if ($_OLD_SESSION !== \false) { + $_SESSION = $_OLD_SESSION; + unset($_OLD_SESSION); + } else { + unset($_SESSION); + } + } + // in SSH2 a shared secret and an exchange hash are generated through the key exchange process. + // the IV client to server is the hash of that "nonce" with the letter A and for the encryption key it's the letter C. + // if the hash doesn't produce enough a key or an IV that's long enough concat successive hashes of the + // original hash and the current hash. we'll be emulating that. for more info see the following URL: + // + // http://tools.ietf.org/html/rfc4253#section-7.2 + // + // see the is_string($crypto) part for an example of how to expand the keys + $key = \sha1($seed . 'A', \true); + $iv = \sha1($seed . 'C', \true); + // ciphers are used as per the nist.gov link below. also, see this link: + // + // http://en.wikipedia.org/wiki/Cryptographically_secure_pseudorandom_number_generator#Designs_based_on_cryptographic_primitives + switch (\true) { + case \class_exists('FluentSmtpLib\\phpseclib3\\Crypt\\AES'): + $crypto = new \FluentSmtpLib\phpseclib3\Crypt\AES('ctr'); + break; + case \class_exists('FluentSmtpLib\\phpseclib3\\Crypt\\Twofish'): + $crypto = new \FluentSmtpLib\phpseclib3\Crypt\Twofish('ctr'); + break; + case \class_exists('FluentSmtpLib\\phpseclib3\\Crypt\\Blowfish'): + $crypto = new \FluentSmtpLib\phpseclib3\Crypt\Blowfish('ctr'); + break; + case \class_exists('FluentSmtpLib\\phpseclib3\\Crypt\\TripleDES'): + $crypto = new \FluentSmtpLib\phpseclib3\Crypt\TripleDES('ctr'); + break; + case \class_exists('FluentSmtpLib\\phpseclib3\\Crypt\\DES'): + $crypto = new \FluentSmtpLib\phpseclib3\Crypt\DES('ctr'); + break; + case \class_exists('FluentSmtpLib\\phpseclib3\\Crypt\\RC4'): + $crypto = new \FluentSmtpLib\phpseclib3\Crypt\RC4(); + break; + default: + throw new \RuntimeException(__CLASS__ . ' requires at least one symmetric cipher be loaded'); + } + $crypto->setKey(\substr($key, 0, $crypto->getKeyLength() >> 3)); + $crypto->setIV(\substr($iv, 0, $crypto->getBlockLength() >> 3)); + $crypto->enableContinuousBuffer(); + } + //return $crypto->encrypt(str_repeat("\0", $length)); + // the following is based off of ANSI X9.31: + // + // http://csrc.nist.gov/groups/STM/cavp/documents/rng/931rngext.pdf + // + // OpenSSL uses that same standard for it's random numbers: + // + // http://www.opensource.apple.com/source/OpenSSL/OpenSSL-38/openssl/fips-1.0/rand/fips_rand.c + // (do a search for "ANS X9.31 A.2.4") + $result = ''; + while (\strlen($result) < $length) { + $i = $crypto->encrypt(\microtime()); + // strlen(microtime()) == 21 + $r = $crypto->encrypt($i ^ $v); + // strlen($v) == 20 + $v = $crypto->encrypt($r ^ $i); + // strlen($r) == 20 + $result .= $r; + } + return \substr($result, 0, $length); + } + /** + * Safely serialize variables + * + * If a class has a private __sleep() it'll emit a warning + * @return mixed + * @param mixed $arr + */ + private static function safe_serialize(&$arr) + { + if (\is_object($arr)) { + return ''; + } + if (!\is_array($arr)) { + return \serialize($arr); + } + // prevent circular array recursion + if (isset($arr['__phpseclib_marker'])) { + return ''; + } + $safearr = []; + $arr['__phpseclib_marker'] = \true; + foreach (\array_keys($arr) as $key) { + // do not recurse on the '__phpseclib_marker' key itself, for smaller memory usage + if ($key !== '__phpseclib_marker') { + $safearr[$key] = self::safe_serialize($arr[$key]); + } + } + unset($arr['__phpseclib_marker']); + return \serialize($safearr); + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/Rijndael.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/Rijndael.php new file mode 100644 index 0000000..62241d0 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/Rijndael.php @@ -0,0 +1,1048 @@ + + * setKey('abcdefghijklmnop'); + * + * $size = 10 * 1024; + * $plaintext = ''; + * for ($i = 0; $i < $size; $i++) { + * $plaintext.= 'a'; + * } + * + * echo $rijndael->decrypt($rijndael->encrypt($plaintext)); + * ?> + * + * + * @author Jim Wigginton + * @copyright 2008 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\Crypt; + +use FluentSmtpLib\phpseclib3\Common\Functions\Strings; +use FluentSmtpLib\phpseclib3\Crypt\Common\BlockCipher; +use FluentSmtpLib\phpseclib3\Exception\BadDecryptionException; +use FluentSmtpLib\phpseclib3\Exception\BadModeException; +use FluentSmtpLib\phpseclib3\Exception\InconsistentSetupException; +use FluentSmtpLib\phpseclib3\Exception\InsufficientSetupException; +/** + * Pure-PHP implementation of Rijndael. + * + * @author Jim Wigginton + */ +class Rijndael extends \FluentSmtpLib\phpseclib3\Crypt\Common\BlockCipher +{ + /** + * The mcrypt specific name of the cipher + * + * Mcrypt is useable for 128/192/256-bit $block_size/$key_length. For 160/224 not. + * \phpseclib3\Crypt\Rijndael determines automatically whether mcrypt is useable + * or not for the current $block_size/$key_length. + * In case of, $cipher_name_mcrypt will be set dynamically at run time accordingly. + * + * @see \phpseclib3\Crypt\Common\SymmetricKey::cipher_name_mcrypt + * @see \phpseclib3\Crypt\Common\SymmetricKey::engine + * @see self::isValidEngine() + * @var string + */ + protected $cipher_name_mcrypt = 'rijndael-128'; + /** + * The Key Schedule + * + * @see self::setup() + * @var array + */ + private $w; + /** + * The Inverse Key Schedule + * + * @see self::setup() + * @var array + */ + private $dw; + /** + * The Block Length divided by 32 + * + * {@internal The max value is 256 / 32 = 8, the min value is 128 / 32 = 4. Exists in conjunction with $block_size + * because the encryption / decryption / key schedule creation requires this number and not $block_size. We could + * derive this from $block_size or vice versa, but that'd mean we'd have to do multiple shift operations, so in lieu + * of that, we'll just precompute it once.} + * + * @see self::setBlockLength() + * @var int + */ + private $Nb = 4; + /** + * The Key Length (in bytes) + * + * {@internal The max value is 256 / 8 = 32, the min value is 128 / 8 = 16. Exists in conjunction with $Nk + * because the encryption / decryption / key schedule creation requires this number and not $key_length. We could + * derive this from $key_length or vice versa, but that'd mean we'd have to do multiple shift operations, so in lieu + * of that, we'll just precompute it once.} + * + * @see self::setKeyLength() + * @var int + */ + protected $key_length = 16; + /** + * The Key Length divided by 32 + * + * @see self::setKeyLength() + * @var int + * @internal The max value is 256 / 32 = 8, the min value is 128 / 32 = 4 + */ + private $Nk = 4; + /** + * The Number of Rounds + * + * {@internal The max value is 14, the min value is 10.} + * + * @var int + */ + private $Nr; + /** + * Shift offsets + * + * @var array + */ + private $c; + /** + * Holds the last used key- and block_size information + * + * @var array + */ + private $kl; + /** + * Default Constructor. + * + * @param string $mode + * @throws \InvalidArgumentException if an invalid / unsupported mode is provided + */ + public function __construct($mode) + { + parent::__construct($mode); + if ($this->mode == self::MODE_STREAM) { + throw new \FluentSmtpLib\phpseclib3\Exception\BadModeException('Block ciphers cannot be ran in stream mode'); + } + } + /** + * Sets the key length. + * + * Valid key lengths are 128, 160, 192, 224, and 256. + * + * Note: phpseclib extends Rijndael (and AES) for using 160- and 224-bit keys but they are officially not defined + * and the most (if not all) implementations are not able using 160/224-bit keys but round/pad them up to + * 192/256 bits as, for example, mcrypt will do. + * + * That said, if you want be compatible with other Rijndael and AES implementations, + * you should not setKeyLength(160) or setKeyLength(224). + * + * Additional: In case of 160- and 224-bit keys, phpseclib will/can, for that reason, not use + * the mcrypt php extension, even if available. + * This results then in slower encryption. + * + * @throws \LengthException if the key length is invalid + * @param int $length + */ + public function setKeyLength($length) + { + switch ($length) { + case 128: + case 160: + case 192: + case 224: + case 256: + $this->key_length = $length >> 3; + break; + default: + throw new \LengthException('Key size of ' . $length . ' bits is not supported by this algorithm. Only keys of sizes 128, 160, 192, 224 or 256 bits are supported'); + } + parent::setKeyLength($length); + } + /** + * Sets the key. + * + * Rijndael supports five different key lengths + * + * @see setKeyLength() + * @param string $key + * @throws \LengthException if the key length isn't supported + */ + public function setKey($key) + { + switch (\strlen($key)) { + case 16: + case 20: + case 24: + case 28: + case 32: + break; + default: + throw new \LengthException('Key of size ' . \strlen($key) . ' not supported by this algorithm. Only keys of sizes 16, 20, 24, 28 or 32 are supported'); + } + parent::setKey($key); + } + /** + * Sets the block length + * + * Valid block lengths are 128, 160, 192, 224, and 256. + * + * @param int $length + */ + public function setBlockLength($length) + { + switch ($length) { + case 128: + case 160: + case 192: + case 224: + case 256: + break; + default: + throw new \LengthException('Key size of ' . $length . ' bits is not supported by this algorithm. Only keys of sizes 128, 160, 192, 224 or 256 bits are supported'); + } + $this->Nb = $length >> 5; + $this->block_size = $length >> 3; + $this->changed = $this->nonIVChanged = \true; + $this->setEngine(); + } + /** + * Test for engine validity + * + * This is mainly just a wrapper to set things up for \phpseclib3\Crypt\Common\SymmetricKey::isValidEngine() + * + * @see \phpseclib3\Crypt\Common\SymmetricKey::__construct() + * @param int $engine + * @return bool + */ + protected function isValidEngineHelper($engine) + { + switch ($engine) { + case self::ENGINE_LIBSODIUM: + return \function_exists('sodium_crypto_aead_aes256gcm_is_available') && \sodium_crypto_aead_aes256gcm_is_available() && $this->mode == self::MODE_GCM && $this->key_length == 32 && $this->nonce && \strlen($this->nonce) == 12 && $this->block_size == 16; + case self::ENGINE_OPENSSL_GCM: + if (!\extension_loaded('openssl')) { + return \false; + } + $methods = \openssl_get_cipher_methods(); + return $this->mode == self::MODE_GCM && \version_compare(\PHP_VERSION, '7.1.0', '>=') && \in_array('aes-' . $this->getKeyLength() . '-gcm', $methods) && $this->block_size == 16; + case self::ENGINE_OPENSSL: + if ($this->block_size != 16) { + return \false; + } + $this->cipher_name_openssl_ecb = 'aes-' . ($this->key_length << 3) . '-ecb'; + $this->cipher_name_openssl = 'aes-' . ($this->key_length << 3) . '-' . $this->openssl_translate_mode(); + break; + case self::ENGINE_MCRYPT: + $this->cipher_name_mcrypt = 'rijndael-' . ($this->block_size << 3); + if ($this->key_length % 8) { + // is it a 160/224-bit key? + // mcrypt is not usable for them, only for 128/192/256-bit keys + return \false; + } + } + return parent::isValidEngineHelper($engine); + } + /** + * Encrypts a block + * + * @param string $in + * @return string + */ + protected function encryptBlock($in) + { + static $tables; + if (empty($tables)) { + $tables =& $this->getTables(); + } + $t0 = $tables[0]; + $t1 = $tables[1]; + $t2 = $tables[2]; + $t3 = $tables[3]; + $sbox = $tables[4]; + $state = []; + $words = \unpack('N*', $in); + $c = $this->c; + $w = $this->w; + $Nb = $this->Nb; + $Nr = $this->Nr; + // addRoundKey + $wc = $Nb - 1; + foreach ($words as $word) { + $state[] = $word ^ $w[++$wc]; + } + // fips-197.pdf#page=19, "Figure 5. Pseudo Code for the Cipher", states that this loop has four components - + // subBytes, shiftRows, mixColumns, and addRoundKey. fips-197.pdf#page=30, "Implementation Suggestions Regarding + // Various Platforms" suggests that performs enhanced implementations are described in Rijndael-ammended.pdf. + // Rijndael-ammended.pdf#page=20, "Implementation aspects / 32-bit processor", discusses such an optimization. + // Unfortunately, the description given there is not quite correct. Per aes.spec.v316.pdf#page=19 [1], + // equation (7.4.7) is supposed to use addition instead of subtraction, so we'll do that here, as well. + // [1] http://fp.gladman.plus.com/cryptography_technology/rijndael/aes.spec.v316.pdf + $temp = []; + for ($round = 1; $round < $Nr; ++$round) { + $i = 0; + // $c[0] == 0 + $j = $c[1]; + $k = $c[2]; + $l = $c[3]; + while ($i < $Nb) { + $temp[$i] = $t0[$state[$i] >> 24 & 0xff] ^ $t1[$state[$j] >> 16 & 0xff] ^ $t2[$state[$k] >> 8 & 0xff] ^ $t3[$state[$l] & 0xff] ^ $w[++$wc]; + ++$i; + $j = ($j + 1) % $Nb; + $k = ($k + 1) % $Nb; + $l = ($l + 1) % $Nb; + } + $state = $temp; + } + // subWord + for ($i = 0; $i < $Nb; ++$i) { + $state[$i] = $sbox[$state[$i] & 0xff] | $sbox[$state[$i] >> 8 & 0xff] << 8 | $sbox[$state[$i] >> 16 & 0xff] << 16 | $sbox[$state[$i] >> 24 & 0xff] << 24; + } + // shiftRows + addRoundKey + $i = 0; + // $c[0] == 0 + $j = $c[1]; + $k = $c[2]; + $l = $c[3]; + while ($i < $Nb) { + $temp[$i] = $state[$i] & \intval(0xff000000) ^ $state[$j] & 0xff0000 ^ $state[$k] & 0xff00 ^ $state[$l] & 0xff ^ $w[$i]; + ++$i; + $j = ($j + 1) % $Nb; + $k = ($k + 1) % $Nb; + $l = ($l + 1) % $Nb; + } + return \pack('N*', ...$temp); + } + /** + * Decrypts a block + * + * @param string $in + * @return string + */ + protected function decryptBlock($in) + { + static $invtables; + if (empty($invtables)) { + $invtables =& $this->getInvTables(); + } + $dt0 = $invtables[0]; + $dt1 = $invtables[1]; + $dt2 = $invtables[2]; + $dt3 = $invtables[3]; + $isbox = $invtables[4]; + $state = []; + $words = \unpack('N*', $in); + $c = $this->c; + $dw = $this->dw; + $Nb = $this->Nb; + $Nr = $this->Nr; + // addRoundKey + $wc = $Nb - 1; + foreach ($words as $word) { + $state[] = $word ^ $dw[++$wc]; + } + $temp = []; + for ($round = $Nr - 1; $round > 0; --$round) { + $i = 0; + // $c[0] == 0 + $j = $Nb - $c[1]; + $k = $Nb - $c[2]; + $l = $Nb - $c[3]; + while ($i < $Nb) { + $temp[$i] = $dt0[$state[$i] >> 24 & 0xff] ^ $dt1[$state[$j] >> 16 & 0xff] ^ $dt2[$state[$k] >> 8 & 0xff] ^ $dt3[$state[$l] & 0xff] ^ $dw[++$wc]; + ++$i; + $j = ($j + 1) % $Nb; + $k = ($k + 1) % $Nb; + $l = ($l + 1) % $Nb; + } + $state = $temp; + } + // invShiftRows + invSubWord + addRoundKey + $i = 0; + // $c[0] == 0 + $j = $Nb - $c[1]; + $k = $Nb - $c[2]; + $l = $Nb - $c[3]; + while ($i < $Nb) { + $word = $state[$i] & \intval(0xff000000) | $state[$j] & 0xff0000 | $state[$k] & 0xff00 | $state[$l] & 0xff; + $temp[$i] = $dw[$i] ^ ($isbox[$word & 0xff] | $isbox[$word >> 8 & 0xff] << 8 | $isbox[$word >> 16 & 0xff] << 16 | $isbox[$word >> 24 & 0xff] << 24); + ++$i; + $j = ($j + 1) % $Nb; + $k = ($k + 1) % $Nb; + $l = ($l + 1) % $Nb; + } + return \pack('N*', ...$temp); + } + /** + * Setup the self::ENGINE_INTERNAL $engine + * + * (re)init, if necessary, the internal cipher $engine and flush all $buffers + * Used (only) if $engine == self::ENGINE_INTERNAL + * + * _setup() will be called each time if $changed === true + * typically this happens when using one or more of following public methods: + * + * - setKey() + * + * - setIV() + * + * - disableContinuousBuffer() + * + * - First run of encrypt() / decrypt() with no init-settings + * + * {@internal setup() is always called before en/decryption.} + * + * {@internal Could, but not must, extend by the child Crypt_* class} + * + * @see self::setKey() + * @see self::setIV() + * @see self::disableContinuousBuffer() + */ + protected function setup() + { + if (!$this->changed) { + return; + } + parent::setup(); + if (\is_string($this->iv) && \strlen($this->iv) != $this->block_size) { + throw new \FluentSmtpLib\phpseclib3\Exception\InconsistentSetupException('The IV length (' . \strlen($this->iv) . ') does not match the block size (' . $this->block_size . ')'); + } + } + /** + * Setup the key (expansion) + * + * @see \phpseclib3\Crypt\Common\SymmetricKey::setupKey() + */ + protected function setupKey() + { + // Each number in $rcon is equal to the previous number multiplied by two in Rijndael's finite field. + // See http://en.wikipedia.org/wiki/Finite_field_arithmetic#Multiplicative_inverse + static $rcon; + if (!isset($rcon)) { + $rcon = [0, 0x1000000, 0x2000000, 0x4000000, 0x8000000, 0x10000000, 0x20000000, 0x40000000, 0x80000000, 0x1b000000, 0x36000000, 0x6c000000, 0xd8000000, 0xab000000, 0x4d000000, 0x9a000000, 0x2f000000, 0x5e000000, 0xbc000000, 0x63000000, 0xc6000000, 0x97000000, 0x35000000, 0x6a000000, 0xd4000000, 0xb3000000, 0x7d000000, 0xfa000000, 0xef000000, 0xc5000000, 0x91000000]; + $rcon = \array_map('intval', $rcon); + } + if (isset($this->kl['key']) && $this->key === $this->kl['key'] && $this->key_length === $this->kl['key_length'] && $this->block_size === $this->kl['block_size']) { + // already expanded + return; + } + $this->kl = ['key' => $this->key, 'key_length' => $this->key_length, 'block_size' => $this->block_size]; + $this->Nk = $this->key_length >> 2; + // see Rijndael-ammended.pdf#page=44 + $this->Nr = \max($this->Nk, $this->Nb) + 6; + // shift offsets for Nb = 5, 7 are defined in Rijndael-ammended.pdf#page=44, + // "Table 8: Shift offsets in Shiftrow for the alternative block lengths" + // shift offsets for Nb = 4, 6, 8 are defined in Rijndael-ammended.pdf#page=14, + // "Table 2: Shift offsets for different block lengths" + switch ($this->Nb) { + case 4: + case 5: + case 6: + $this->c = [0, 1, 2, 3]; + break; + case 7: + $this->c = [0, 1, 2, 4]; + break; + case 8: + $this->c = [0, 1, 3, 4]; + } + $w = \array_values(\unpack('N*words', $this->key)); + $length = $this->Nb * ($this->Nr + 1); + for ($i = $this->Nk; $i < $length; $i++) { + $temp = $w[$i - 1]; + if ($i % $this->Nk == 0) { + // according to , "the size of an integer is platform-dependent". + // on a 32-bit machine, it's 32-bits, and on a 64-bit machine, it's 64-bits. on a 32-bit machine, + // 0xFFFFFFFF << 8 == 0xFFFFFF00, but on a 64-bit machine, it equals 0xFFFFFFFF00. as such, doing 'and' + // with 0xFFFFFFFF (or 0xFFFFFF00) on a 32-bit machine is unnecessary, but on a 64-bit machine, it is. + $temp = $temp << 8 & \intval(0xffffff00) | $temp >> 24 & 0xff; + // rotWord + $temp = $this->subWord($temp) ^ $rcon[$i / $this->Nk]; + } elseif ($this->Nk > 6 && $i % $this->Nk == 4) { + $temp = $this->subWord($temp); + } + $w[$i] = $w[$i - $this->Nk] ^ $temp; + } + // convert the key schedule from a vector of $Nb * ($Nr + 1) length to a matrix with $Nr + 1 rows and $Nb columns + // and generate the inverse key schedule. more specifically, + // according to (section 5.3.3), + // "The key expansion for the Inverse Cipher is defined as follows: + // 1. Apply the Key Expansion. + // 2. Apply InvMixColumn to all Round Keys except the first and the last one." + // also, see fips-197.pdf#page=27, "5.3.5 Equivalent Inverse Cipher" + list($dt0, $dt1, $dt2, $dt3) = $this->getInvTables(); + $temp = $this->w = $this->dw = []; + for ($i = $row = $col = 0; $i < $length; $i++, $col++) { + if ($col == $this->Nb) { + if ($row == 0) { + $this->dw[0] = $this->w[0]; + } else { + // subWord + invMixColumn + invSubWord = invMixColumn + $j = 0; + while ($j < $this->Nb) { + $dw = $this->subWord($this->w[$row][$j]); + $temp[$j] = $dt0[$dw >> 24 & 0xff] ^ $dt1[$dw >> 16 & 0xff] ^ $dt2[$dw >> 8 & 0xff] ^ $dt3[$dw & 0xff]; + $j++; + } + $this->dw[$row] = $temp; + } + $col = 0; + $row++; + } + $this->w[$row][$col] = $w[$i]; + } + $this->dw[$row] = $this->w[$row]; + // Converting to 1-dim key arrays (both ascending) + $this->dw = \array_reverse($this->dw); + $w = \array_pop($this->w); + $dw = \array_pop($this->dw); + foreach ($this->w as $r => $wr) { + foreach ($wr as $c => $wc) { + $w[] = $wc; + $dw[] = $this->dw[$r][$c]; + } + } + $this->w = $w; + $this->dw = $dw; + } + /** + * Performs S-Box substitutions + * + * @return array + * @param int $word + */ + private function subWord($word) + { + static $sbox; + if (empty($sbox)) { + list(, , , , $sbox) = self::getTables(); + } + return $sbox[$word & 0xff] | $sbox[$word >> 8 & 0xff] << 8 | $sbox[$word >> 16 & 0xff] << 16 | $sbox[$word >> 24 & 0xff] << 24; + } + /** + * Provides the mixColumns and sboxes tables + * + * @see self::encryptBlock() + * @see self::setupInlineCrypt() + * @see self::subWord() + * @return array &$tables + */ + protected function &getTables() + { + static $tables; + if (empty($tables)) { + // according to (section 5.2.1), + // precomputed tables can be used in the mixColumns phase. in that example, they're assigned t0...t3, so + // those are the names we'll use. + $t3 = \array_map('intval', [ + // with array_map('intval', ...) we ensure we have only int's and not + // some slower floats converted by php automatically on high values + 0x6363a5c6, + 0x7c7c84f8, + 0x777799ee, + 0x7b7b8df6, + 0xf2f20dff, + 0x6b6bbdd6, + 0x6f6fb1de, + 0xc5c55491, + 0x30305060, + 0x1010302, + 0x6767a9ce, + 0x2b2b7d56, + 0xfefe19e7, + 0xd7d762b5, + 0xababe64d, + 0x76769aec, + 0xcaca458f, + 0x82829d1f, + 0xc9c94089, + 0x7d7d87fa, + 0xfafa15ef, + 0x5959ebb2, + 0x4747c98e, + 0xf0f00bfb, + 0xadadec41, + 0xd4d467b3, + 0xa2a2fd5f, + 0xafafea45, + 0x9c9cbf23, + 0xa4a4f753, + 0x727296e4, + 0xc0c05b9b, + 0xb7b7c275, + 0xfdfd1ce1, + 0x9393ae3d, + 0x26266a4c, + 0x36365a6c, + 0x3f3f417e, + 0xf7f702f5, + 0xcccc4f83, + 0x34345c68, + 0xa5a5f451, + 0xe5e534d1, + 0xf1f108f9, + 0x717193e2, + 0xd8d873ab, + 0x31315362, + 0x15153f2a, + 0x4040c08, + 0xc7c75295, + 0x23236546, + 0xc3c35e9d, + 0x18182830, + 0x9696a137, + 0x5050f0a, + 0x9a9ab52f, + 0x707090e, + 0x12123624, + 0x80809b1b, + 0xe2e23ddf, + 0xebeb26cd, + 0x2727694e, + 0xb2b2cd7f, + 0x75759fea, + 0x9091b12, + 0x83839e1d, + 0x2c2c7458, + 0x1a1a2e34, + 0x1b1b2d36, + 0x6e6eb2dc, + 0x5a5aeeb4, + 0xa0a0fb5b, + 0x5252f6a4, + 0x3b3b4d76, + 0xd6d661b7, + 0xb3b3ce7d, + 0x29297b52, + 0xe3e33edd, + 0x2f2f715e, + 0x84849713, + 0x5353f5a6, + 0xd1d168b9, + 0x0, + 0xeded2cc1, + 0x20206040, + 0xfcfc1fe3, + 0xb1b1c879, + 0x5b5bedb6, + 0x6a6abed4, + 0xcbcb468d, + 0xbebed967, + 0x39394b72, + 0x4a4ade94, + 0x4c4cd498, + 0x5858e8b0, + 0xcfcf4a85, + 0xd0d06bbb, + 0xefef2ac5, + 0xaaaae54f, + 0xfbfb16ed, + 0x4343c586, + 0x4d4dd79a, + 0x33335566, + 0x85859411, + 0x4545cf8a, + 0xf9f910e9, + 0x2020604, + 0x7f7f81fe, + 0x5050f0a0, + 0x3c3c4478, + 0x9f9fba25, + 0xa8a8e34b, + 0x5151f3a2, + 0xa3a3fe5d, + 0x4040c080, + 0x8f8f8a05, + 0x9292ad3f, + 0x9d9dbc21, + 0x38384870, + 0xf5f504f1, + 0xbcbcdf63, + 0xb6b6c177, + 0xdada75af, + 0x21216342, + 0x10103020, + 0xffff1ae5, + 0xf3f30efd, + 0xd2d26dbf, + 0xcdcd4c81, + 0xc0c1418, + 0x13133526, + 0xecec2fc3, + 0x5f5fe1be, + 0x9797a235, + 0x4444cc88, + 0x1717392e, + 0xc4c45793, + 0xa7a7f255, + 0x7e7e82fc, + 0x3d3d477a, + 0x6464acc8, + 0x5d5de7ba, + 0x19192b32, + 0x737395e6, + 0x6060a0c0, + 0x81819819, + 0x4f4fd19e, + 0xdcdc7fa3, + 0x22226644, + 0x2a2a7e54, + 0x9090ab3b, + 0x8888830b, + 0x4646ca8c, + 0xeeee29c7, + 0xb8b8d36b, + 0x14143c28, + 0xdede79a7, + 0x5e5ee2bc, + 0xb0b1d16, + 0xdbdb76ad, + 0xe0e03bdb, + 0x32325664, + 0x3a3a4e74, + 0xa0a1e14, + 0x4949db92, + 0x6060a0c, + 0x24246c48, + 0x5c5ce4b8, + 0xc2c25d9f, + 0xd3d36ebd, + 0xacacef43, + 0x6262a6c4, + 0x9191a839, + 0x9595a431, + 0xe4e437d3, + 0x79798bf2, + 0xe7e732d5, + 0xc8c8438b, + 0x3737596e, + 0x6d6db7da, + 0x8d8d8c01, + 0xd5d564b1, + 0x4e4ed29c, + 0xa9a9e049, + 0x6c6cb4d8, + 0x5656faac, + 0xf4f407f3, + 0xeaea25cf, + 0x6565afca, + 0x7a7a8ef4, + 0xaeaee947, + 0x8081810, + 0xbabad56f, + 0x787888f0, + 0x25256f4a, + 0x2e2e725c, + 0x1c1c2438, + 0xa6a6f157, + 0xb4b4c773, + 0xc6c65197, + 0xe8e823cb, + 0xdddd7ca1, + 0x74749ce8, + 0x1f1f213e, + 0x4b4bdd96, + 0xbdbddc61, + 0x8b8b860d, + 0x8a8a850f, + 0x707090e0, + 0x3e3e427c, + 0xb5b5c471, + 0x6666aacc, + 0x4848d890, + 0x3030506, + 0xf6f601f7, + 0xe0e121c, + 0x6161a3c2, + 0x35355f6a, + 0x5757f9ae, + 0xb9b9d069, + 0x86869117, + 0xc1c15899, + 0x1d1d273a, + 0x9e9eb927, + 0xe1e138d9, + 0xf8f813eb, + 0x9898b32b, + 0x11113322, + 0x6969bbd2, + 0xd9d970a9, + 0x8e8e8907, + 0x9494a733, + 0x9b9bb62d, + 0x1e1e223c, + 0x87879215, + 0xe9e920c9, + 0xcece4987, + 0x5555ffaa, + 0x28287850, + 0xdfdf7aa5, + 0x8c8c8f03, + 0xa1a1f859, + 0x89898009, + 0xd0d171a, + 0xbfbfda65, + 0xe6e631d7, + 0x4242c684, + 0x6868b8d0, + 0x4141c382, + 0x9999b029, + 0x2d2d775a, + 0xf0f111e, + 0xb0b0cb7b, + 0x5454fca8, + 0xbbbbd66d, + 0x16163a2c, + ]); + foreach ($t3 as $t3i) { + $t0[] = $t3i << 24 & \intval(0xff000000) | $t3i >> 8 & 0xffffff; + $t1[] = $t3i << 16 & \intval(0xffff0000) | $t3i >> 16 & 0xffff; + $t2[] = $t3i << 8 & \intval(0xffffff00) | $t3i >> 24 & 0xff; + } + $tables = [ + // The Precomputed mixColumns tables t0 - t3 + $t0, + $t1, + $t2, + $t3, + // The SubByte S-Box + [0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x1, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76, 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0, 0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15, 0x4, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x5, 0x9a, 0x7, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75, 0x9, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84, 0x53, 0xd1, 0x0, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf, 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x2, 0x7f, 0x50, 0x3c, 0x9f, 0xa8, 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2, 0xcd, 0xc, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73, 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0xb, 0xdb, 0xe0, 0x32, 0x3a, 0xa, 0x49, 0x6, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79, 0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x8, 0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a, 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x3, 0xf6, 0xe, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e, 0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf, 0x8c, 0xa1, 0x89, 0xd, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0xf, 0xb0, 0x54, 0xbb, 0x16], + ]; + } + return $tables; + } + /** + * Provides the inverse mixColumns and inverse sboxes tables + * + * @see self::decryptBlock() + * @see self::setupInlineCrypt() + * @see self::setupKey() + * @return array &$tables + */ + protected function &getInvTables() + { + static $tables; + if (empty($tables)) { + $dt3 = \array_map('intval', [0xf4a75051, 0x4165537e, 0x17a4c31a, 0x275e963a, 0xab6bcb3b, 0x9d45f11f, 0xfa58abac, 0xe303934b, 0x30fa5520, 0x766df6ad, 0xcc769188, 0x24c25f5, 0xe5d7fc4f, 0x2acbd7c5, 0x35448026, 0x62a38fb5, 0xb15a49de, 0xba1b6725, 0xea0e9845, 0xfec0e15d, 0x2f7502c3, 0x4cf01281, 0x4697a38d, 0xd3f9c66b, 0x8f5fe703, 0x929c9515, 0x6d7aebbf, 0x5259da95, 0xbe832dd4, 0x7421d358, 0xe0692949, 0xc9c8448e, 0xc2896a75, 0x8e7978f4, 0x583e6b99, 0xb971dd27, 0xe14fb6be, 0x88ad17f0, 0x20ac66c9, 0xce3ab47d, 0xdf4a1863, 0x1a3182e5, 0x51336097, 0x537f4562, 0x6477e0b1, 0x6bae84bb, 0x81a01cfe, 0x82b94f9, 0x48685870, 0x45fd198f, 0xde6c8794, 0x7bf8b752, 0x73d323ab, 0x4b02e272, 0x1f8f57e3, 0x55ab2a66, 0xeb2807b2, 0xb5c2032f, 0xc57b9a86, 0x3708a5d3, 0x2887f230, 0xbfa5b223, 0x36aba02, 0x16825ced, 0xcf1c2b8a, 0x79b492a7, 0x7f2f0f3, 0x69e2a14e, 0xdaf4cd65, 0x5bed506, 0x34621fd1, 0xa6fe8ac4, 0x2e539d34, 0xf355a0a2, 0x8ae13205, 0xf6eb75a4, 0x83ec390b, 0x60efaa40, 0x719f065e, 0x6e1051bd, 0x218af93e, 0xdd063d96, 0x3e05aedd, 0xe6bd464d, 0x548db591, 0xc45d0571, 0x6d46f04, 0x5015ff60, 0x98fb2419, 0xbde997d6, 0x4043cc89, 0xd99e7767, 0xe842bdb0, 0x898b8807, 0x195b38e7, 0xc8eedb79, 0x7c0a47a1, 0x420fe97c, 0x841ec9f8, 0x0, 0x80868309, 0x2bed4832, 0x1170ac1e, 0x5a724e6c, 0xefffbfd, 0x8538560f, 0xaed51e3d, 0x2d392736, 0xfd9640a, 0x5ca62168, 0x5b54d19b, 0x362e3a24, 0xa67b10c, 0x57e70f93, 0xee96d2b4, 0x9b919e1b, 0xc0c54f80, 0xdc20a261, 0x774b695a, 0x121a161c, 0x93ba0ae2, 0xa02ae5c0, 0x22e0433c, 0x1b171d12, 0x90d0b0e, 0x8bc7adf2, 0xb6a8b92d, 0x1ea9c814, 0xf1198557, 0x75074caf, 0x99ddbbee, 0x7f60fda3, 0x1269ff7, 0x72f5bc5c, 0x663bc544, 0xfb7e345b, 0x4329768b, 0x23c6dccb, 0xedfc68b6, 0xe4f163b8, 0x31dccad7, 0x63851042, 0x97224013, 0xc6112084, 0x4a247d85, 0xbb3df8d2, 0xf93211ae, 0x29a16dc7, 0x9e2f4b1d, 0xb230f3dc, 0x8652ec0d, 0xc1e3d077, 0xb3166c2b, 0x70b999a9, 0x9448fa11, 0xe9642247, 0xfc8cc4a8, 0xf03f1aa0, 0x7d2cd856, 0x3390ef22, 0x494ec787, 0x38d1c1d9, 0xcaa2fe8c, 0xd40b3698, 0xf581cfa6, 0x7ade28a5, 0xb78e26da, 0xadbfa43f, 0x3a9de42c, 0x78920d50, 0x5fcc9b6a, 0x7e466254, 0x8d13c2f6, 0xd8b8e890, 0x39f75e2e, 0xc3aff582, 0x5d80be9f, 0xd0937c69, 0xd52da96f, 0x2512b3cf, 0xac993bc8, 0x187da710, 0x9c636ee8, 0x3bbb7bdb, 0x267809cd, 0x5918f46e, 0x9ab701ec, 0x4f9aa883, 0x956e65e6, 0xffe67eaa, 0xbccf0821, 0x15e8e6ef, 0xe79bd9ba, 0x6f36ce4a, 0x9f09d4ea, 0xb07cd629, 0xa4b2af31, 0x3f23312a, 0xa59430c6, 0xa266c035, 0x4ebc3774, 0x82caa6fc, 0x90d0b0e0, 0xa7d81533, 0x4984af1, 0xecdaf741, 0xcd500e7f, 0x91f62f17, 0x4dd68d76, 0xefb04d43, 0xaa4d54cc, 0x9604dfe4, 0xd1b5e39e, 0x6a881b4c, 0x2c1fb8c1, 0x65517f46, 0x5eea049d, 0x8c355d01, 0x877473fa, 0xb412efb, 0x671d5ab3, 0xdbd25292, 0x105633e9, 0xd647136d, 0xd7618c9a, 0xa10c7a37, 0xf8148e59, 0x133c89eb, 0xa927eece, 0x61c935b7, 0x1ce5ede1, 0x47b13c7a, 0xd2df599c, 0xf2733f55, 0x14ce7918, 0xc737bf73, 0xf7cdea53, 0xfdaa5b5f, 0x3d6f14df, 0x44db8678, 0xaff381ca, 0x68c43eb9, 0x24342c38, 0xa3405fc2, 0x1dc37216, 0xe2250cbc, 0x3c498b28, 0xd9541ff, 0xa8017139, 0xcb3de08, 0xb4e49cd8, 0x56c19064, 0xcb84617b, 0x32b670d5, 0x6c5c7448, 0xb85742d0]); + foreach ($dt3 as $dt3i) { + $dt0[] = $dt3i << 24 & \intval(0xff000000) | $dt3i >> 8 & 0xffffff; + $dt1[] = $dt3i << 16 & \intval(0xffff0000) | $dt3i >> 16 & 0xffff; + $dt2[] = $dt3i << 8 & \intval(0xffffff00) | $dt3i >> 24 & 0xff; + } + $tables = [ + // The Precomputed inverse mixColumns tables dt0 - dt3 + $dt0, + $dt1, + $dt2, + $dt3, + // The inverse SubByte S-Box + [0x52, 0x9, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb, 0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb, 0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0xb, 0x42, 0xfa, 0xc3, 0x4e, 0x8, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25, 0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92, 0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84, 0x90, 0xd8, 0xab, 0x0, 0x8c, 0xbc, 0xd3, 0xa, 0xf7, 0xe4, 0x58, 0x5, 0xb8, 0xb3, 0x45, 0x6, 0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0xf, 0x2, 0xc1, 0xaf, 0xbd, 0x3, 0x1, 0x13, 0x8a, 0x6b, 0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73, 0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e, 0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89, 0x6f, 0xb7, 0x62, 0xe, 0xaa, 0x18, 0xbe, 0x1b, 0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4, 0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x7, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f, 0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0xd, 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef, 0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61, 0x17, 0x2b, 0x4, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0xc, 0x7d], + ]; + } + return $tables; + } + /** + * Setup the performance-optimized function for de/encrypt() + * + * @see \phpseclib3\Crypt\Common\SymmetricKey::setupInlineCrypt() + */ + protected function setupInlineCrypt() + { + $w = $this->w; + $dw = $this->dw; + $init_encrypt = ''; + $init_decrypt = ''; + $Nr = $this->Nr; + $Nb = $this->Nb; + $c = $this->c; + // Generating encrypt code: + $init_encrypt .= ' + if (empty($tables)) { + $tables = &$this->getTables(); + } + $t0 = $tables[0]; + $t1 = $tables[1]; + $t2 = $tables[2]; + $t3 = $tables[3]; + $sbox = $tables[4]; + '; + $s = 'e'; + $e = 's'; + $wc = $Nb - 1; + // Preround: addRoundKey + $encrypt_block = '$in = unpack("N*", $in);' . "\n"; + for ($i = 0; $i < $Nb; ++$i) { + $encrypt_block .= '$s' . $i . ' = $in[' . ($i + 1) . '] ^ ' . $w[++$wc] . ";\n"; + } + // Mainrounds: shiftRows + subWord + mixColumns + addRoundKey + for ($round = 1; $round < $Nr; ++$round) { + list($s, $e) = [$e, $s]; + for ($i = 0; $i < $Nb; ++$i) { + $encrypt_block .= '$' . $e . $i . ' = + $t0[($' . $s . $i . ' >> 24) & 0xff] ^ + $t1[($' . $s . ($i + $c[1]) % $Nb . ' >> 16) & 0xff] ^ + $t2[($' . $s . ($i + $c[2]) % $Nb . ' >> 8) & 0xff] ^ + $t3[ $' . $s . ($i + $c[3]) % $Nb . ' & 0xff] ^ + ' . $w[++$wc] . ";\n"; + } + } + // Finalround: subWord + shiftRows + addRoundKey + for ($i = 0; $i < $Nb; ++$i) { + $encrypt_block .= '$' . $e . $i . ' = + $sbox[ $' . $e . $i . ' & 0xff] | + ($sbox[($' . $e . $i . ' >> 8) & 0xff] << 8) | + ($sbox[($' . $e . $i . ' >> 16) & 0xff] << 16) | + ($sbox[($' . $e . $i . ' >> 24) & 0xff] << 24);' . "\n"; + } + $encrypt_block .= '$in = pack("N*"' . "\n"; + for ($i = 0; $i < $Nb; ++$i) { + $encrypt_block .= ', + ($' . $e . $i . ' & ' . (int) 0xff000000 . ') ^ + ($' . $e . ($i + $c[1]) % $Nb . ' & 0x00FF0000 ) ^ + ($' . $e . ($i + $c[2]) % $Nb . ' & 0x0000FF00 ) ^ + ($' . $e . ($i + $c[3]) % $Nb . ' & 0x000000FF ) ^ + ' . $w[$i] . "\n"; + } + $encrypt_block .= ');'; + // Generating decrypt code: + $init_decrypt .= ' + if (empty($invtables)) { + $invtables = &$this->getInvTables(); + } + $dt0 = $invtables[0]; + $dt1 = $invtables[1]; + $dt2 = $invtables[2]; + $dt3 = $invtables[3]; + $isbox = $invtables[4]; + '; + $s = 'e'; + $e = 's'; + $wc = $Nb - 1; + // Preround: addRoundKey + $decrypt_block = '$in = unpack("N*", $in);' . "\n"; + for ($i = 0; $i < $Nb; ++$i) { + $decrypt_block .= '$s' . $i . ' = $in[' . ($i + 1) . '] ^ ' . $dw[++$wc] . ';' . "\n"; + } + // Mainrounds: shiftRows + subWord + mixColumns + addRoundKey + for ($round = 1; $round < $Nr; ++$round) { + list($s, $e) = [$e, $s]; + for ($i = 0; $i < $Nb; ++$i) { + $decrypt_block .= '$' . $e . $i . ' = + $dt0[($' . $s . $i . ' >> 24) & 0xff] ^ + $dt1[($' . $s . ($Nb + $i - $c[1]) % $Nb . ' >> 16) & 0xff] ^ + $dt2[($' . $s . ($Nb + $i - $c[2]) % $Nb . ' >> 8) & 0xff] ^ + $dt3[ $' . $s . ($Nb + $i - $c[3]) % $Nb . ' & 0xff] ^ + ' . $dw[++$wc] . ";\n"; + } + } + // Finalround: subWord + shiftRows + addRoundKey + for ($i = 0; $i < $Nb; ++$i) { + $decrypt_block .= '$' . $e . $i . ' = + $isbox[ $' . $e . $i . ' & 0xff] | + ($isbox[($' . $e . $i . ' >> 8) & 0xff] << 8) | + ($isbox[($' . $e . $i . ' >> 16) & 0xff] << 16) | + ($isbox[($' . $e . $i . ' >> 24) & 0xff] << 24);' . "\n"; + } + $decrypt_block .= '$in = pack("N*"' . "\n"; + for ($i = 0; $i < $Nb; ++$i) { + $decrypt_block .= ', + ($' . $e . $i . ' & ' . (int) 0xff000000 . ') ^ + ($' . $e . ($Nb + $i - $c[1]) % $Nb . ' & 0x00FF0000 ) ^ + ($' . $e . ($Nb + $i - $c[2]) % $Nb . ' & 0x0000FF00 ) ^ + ($' . $e . ($Nb + $i - $c[3]) % $Nb . ' & 0x000000FF ) ^ + ' . $dw[$i] . "\n"; + } + $decrypt_block .= ');'; + $this->inline_crypt = $this->createInlineCryptFunction(['init_crypt' => 'static $tables; static $invtables;', 'init_encrypt' => $init_encrypt, 'init_decrypt' => $init_decrypt, 'encrypt_block' => $encrypt_block, 'decrypt_block' => $decrypt_block]); + } + /** + * Encrypts a message. + * + * @see self::decrypt() + * @see parent::encrypt() + * @param string $plaintext + * @return string + */ + public function encrypt($plaintext) + { + $this->setup(); + switch ($this->engine) { + case self::ENGINE_LIBSODIUM: + $this->newtag = \sodium_crypto_aead_aes256gcm_encrypt($plaintext, $this->aad, $this->nonce, $this->key); + return \FluentSmtpLib\phpseclib3\Common\Functions\Strings::shift($this->newtag, \strlen($plaintext)); + case self::ENGINE_OPENSSL_GCM: + return \openssl_encrypt($plaintext, 'aes-' . $this->getKeyLength() . '-gcm', $this->key, \OPENSSL_RAW_DATA, $this->nonce, $this->newtag, $this->aad); + } + return parent::encrypt($plaintext); + } + /** + * Decrypts a message. + * + * @see self::encrypt() + * @see parent::decrypt() + * @param string $ciphertext + * @return string + */ + public function decrypt($ciphertext) + { + $this->setup(); + switch ($this->engine) { + case self::ENGINE_LIBSODIUM: + if ($this->oldtag === \false) { + throw new \FluentSmtpLib\phpseclib3\Exception\InsufficientSetupException('Authentication Tag has not been set'); + } + if (\strlen($this->oldtag) != 16) { + break; + } + $plaintext = \sodium_crypto_aead_aes256gcm_decrypt($ciphertext . $this->oldtag, $this->aad, $this->nonce, $this->key); + if ($plaintext === \false) { + $this->oldtag = \false; + throw new \FluentSmtpLib\phpseclib3\Exception\BadDecryptionException('Error decrypting ciphertext with libsodium'); + } + return $plaintext; + case self::ENGINE_OPENSSL_GCM: + if ($this->oldtag === \false) { + throw new \FluentSmtpLib\phpseclib3\Exception\InsufficientSetupException('Authentication Tag has not been set'); + } + $plaintext = \openssl_decrypt($ciphertext, 'aes-' . $this->getKeyLength() . '-gcm', $this->key, \OPENSSL_RAW_DATA, $this->nonce, $this->oldtag, $this->aad); + if ($plaintext === \false) { + $this->oldtag = \false; + throw new \FluentSmtpLib\phpseclib3\Exception\BadDecryptionException('Error decrypting ciphertext with OpenSSL'); + } + return $plaintext; + } + return parent::decrypt($ciphertext); + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/Salsa20.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/Salsa20.php new file mode 100644 index 0000000..84c58b6 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/Salsa20.php @@ -0,0 +1,457 @@ + + * @copyright 2019 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\Crypt; + +use FluentSmtpLib\phpseclib3\Common\Functions\Strings; +use FluentSmtpLib\phpseclib3\Crypt\Common\StreamCipher; +use FluentSmtpLib\phpseclib3\Exception\BadDecryptionException; +use FluentSmtpLib\phpseclib3\Exception\InsufficientSetupException; +/** + * Pure-PHP implementation of Salsa20. + * + * @author Jim Wigginton + */ +class Salsa20 extends \FluentSmtpLib\phpseclib3\Crypt\Common\StreamCipher +{ + /** + * Part 1 of the state + * + * @var string|false + */ + protected $p1 = \false; + /** + * Part 2 of the state + * + * @var string|false + */ + protected $p2 = \false; + /** + * Key Length (in bytes) + * + * @var int + */ + protected $key_length = 32; + // = 256 bits + /** + * @see \phpseclib3\Crypt\Salsa20::crypt() + */ + const ENCRYPT = 0; + /** + * @see \phpseclib3\Crypt\Salsa20::crypt() + */ + const DECRYPT = 1; + /** + * Encryption buffer for continuous mode + * + * @var array + */ + protected $enbuffer; + /** + * Decryption buffer for continuous mode + * + * @var array + */ + protected $debuffer; + /** + * Counter + * + * @var int + */ + protected $counter = 0; + /** + * Using Generated Poly1305 Key + * + * @var boolean + */ + protected $usingGeneratedPoly1305Key = \false; + /** + * Salsa20 uses a nonce + * + * @return bool + */ + public function usesNonce() + { + return \true; + } + /** + * Sets the key. + * + * @param string $key + * @throws \LengthException if the key length isn't supported + */ + public function setKey($key) + { + switch (\strlen($key)) { + case 16: + case 32: + break; + default: + throw new \LengthException('Key of size ' . \strlen($key) . ' not supported by this algorithm. Only keys of sizes 16 or 32 are supported'); + } + parent::setKey($key); + } + /** + * Sets the nonce. + * + * @param string $nonce + */ + public function setNonce($nonce) + { + if (\strlen($nonce) != 8) { + throw new \LengthException('Nonce of size ' . \strlen($key) . ' not supported by this algorithm. Only an 64-bit nonce is supported'); + } + $this->nonce = $nonce; + $this->changed = \true; + $this->setEngine(); + } + /** + * Sets the counter. + * + * @param int $counter + */ + public function setCounter($counter) + { + $this->counter = $counter; + $this->setEngine(); + } + /** + * Creates a Poly1305 key using the method discussed in RFC8439 + * + * See https://tools.ietf.org/html/rfc8439#section-2.6.1 + */ + protected function createPoly1305Key() + { + if ($this->nonce === \false) { + throw new \FluentSmtpLib\phpseclib3\Exception\InsufficientSetupException('No nonce has been defined'); + } + if ($this->key === \false) { + throw new \FluentSmtpLib\phpseclib3\Exception\InsufficientSetupException('No key has been defined'); + } + $c = clone $this; + $c->setCounter(0); + $c->usePoly1305 = \false; + $block = $c->encrypt(\str_repeat("\x00", 256)); + $this->setPoly1305Key(\substr($block, 0, 32)); + if ($this->counter == 0) { + $this->counter++; + } + } + /** + * Setup the self::ENGINE_INTERNAL $engine + * + * (re)init, if necessary, the internal cipher $engine + * + * _setup() will be called each time if $changed === true + * typically this happens when using one or more of following public methods: + * + * - setKey() + * + * - setNonce() + * + * - First run of encrypt() / decrypt() with no init-settings + * + * @see self::setKey() + * @see self::setNonce() + * @see self::disableContinuousBuffer() + */ + protected function setup() + { + if (!$this->changed) { + return; + } + $this->enbuffer = $this->debuffer = ['ciphertext' => '', 'counter' => $this->counter]; + $this->changed = $this->nonIVChanged = \false; + if ($this->nonce === \false) { + throw new \FluentSmtpLib\phpseclib3\Exception\InsufficientSetupException('No nonce has been defined'); + } + if ($this->key === \false) { + throw new \FluentSmtpLib\phpseclib3\Exception\InsufficientSetupException('No key has been defined'); + } + if ($this->usePoly1305 && !isset($this->poly1305Key)) { + $this->usingGeneratedPoly1305Key = \true; + $this->createPoly1305Key(); + } + $key = $this->key; + if (\strlen($key) == 16) { + $constant = 'expand 16-byte k'; + $key .= $key; + } else { + $constant = 'expand 32-byte k'; + } + $this->p1 = \substr($constant, 0, 4) . \substr($key, 0, 16) . \substr($constant, 4, 4) . $this->nonce . "\x00\x00\x00\x00"; + $this->p2 = \substr($constant, 8, 4) . \substr($key, 16, 16) . \substr($constant, 12, 4); + } + /** + * Setup the key (expansion) + */ + protected function setupKey() + { + // Salsa20 does not utilize this method + } + /** + * Encrypts a message. + * + * @see \phpseclib3\Crypt\Common\SymmetricKey::decrypt() + * @see self::crypt() + * @param string $plaintext + * @return string $ciphertext + */ + public function encrypt($plaintext) + { + $ciphertext = $this->crypt($plaintext, self::ENCRYPT); + if (isset($this->poly1305Key)) { + $this->newtag = $this->poly1305($ciphertext); + } + return $ciphertext; + } + /** + * Decrypts a message. + * + * $this->decrypt($this->encrypt($plaintext)) == $this->encrypt($this->encrypt($plaintext)). + * At least if the continuous buffer is disabled. + * + * @see \phpseclib3\Crypt\Common\SymmetricKey::encrypt() + * @see self::crypt() + * @param string $ciphertext + * @return string $plaintext + */ + public function decrypt($ciphertext) + { + if (isset($this->poly1305Key)) { + if ($this->oldtag === \false) { + throw new \FluentSmtpLib\phpseclib3\Exception\InsufficientSetupException('Authentication Tag has not been set'); + } + $newtag = $this->poly1305($ciphertext); + if ($this->oldtag != \substr($newtag, 0, \strlen($this->oldtag))) { + $this->oldtag = \false; + throw new \FluentSmtpLib\phpseclib3\Exception\BadDecryptionException('Derived authentication tag and supplied authentication tag do not match'); + } + $this->oldtag = \false; + } + return $this->crypt($ciphertext, self::DECRYPT); + } + /** + * Encrypts a block + * + * @param string $in + */ + protected function encryptBlock($in) + { + // Salsa20 does not utilize this method + } + /** + * Decrypts a block + * + * @param string $in + */ + protected function decryptBlock($in) + { + // Salsa20 does not utilize this method + } + /** + * Encrypts or decrypts a message. + * + * @see self::encrypt() + * @see self::decrypt() + * @param string $text + * @param int $mode + * @return string $text + */ + private function crypt($text, $mode) + { + $this->setup(); + if (!$this->continuousBuffer) { + if ($this->engine == self::ENGINE_OPENSSL) { + $iv = \pack('V', $this->counter) . $this->p2; + return \openssl_encrypt($text, $this->cipher_name_openssl, $this->key, \OPENSSL_RAW_DATA, $iv); + } + $i = $this->counter; + $blocks = \str_split($text, 64); + foreach ($blocks as &$block) { + $block ^= static::salsa20($this->p1 . \pack('V', $i++) . $this->p2); + } + unset($block); + return \implode('', $blocks); + } + if ($mode == self::ENCRYPT) { + $buffer =& $this->enbuffer; + } else { + $buffer =& $this->debuffer; + } + if (!\strlen($buffer['ciphertext'])) { + $ciphertext = ''; + } else { + $ciphertext = $text ^ \FluentSmtpLib\phpseclib3\Common\Functions\Strings::shift($buffer['ciphertext'], \strlen($text)); + $text = \substr($text, \strlen($ciphertext)); + if (!\strlen($text)) { + return $ciphertext; + } + } + $overflow = \strlen($text) % 64; + // & 0x3F + if ($overflow) { + $text2 = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::pop($text, $overflow); + if ($this->engine == self::ENGINE_OPENSSL) { + $iv = \pack('V', $buffer['counter']) . $this->p2; + // at this point $text should be a multiple of 64 + $buffer['counter'] += (\strlen($text) >> 6) + 1; + // ie. divide by 64 + $encrypted = \openssl_encrypt($text . \str_repeat("\x00", 64), $this->cipher_name_openssl, $this->key, \OPENSSL_RAW_DATA, $iv); + $temp = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::pop($encrypted, 64); + } else { + $blocks = \str_split($text, 64); + if (\strlen($text)) { + foreach ($blocks as &$block) { + $block ^= static::salsa20($this->p1 . \pack('V', $buffer['counter']++) . $this->p2); + } + unset($block); + } + $encrypted = \implode('', $blocks); + $temp = static::salsa20($this->p1 . \pack('V', $buffer['counter']++) . $this->p2); + } + $ciphertext .= $encrypted . ($text2 ^ $temp); + $buffer['ciphertext'] = \substr($temp, $overflow); + } elseif (!\strlen($buffer['ciphertext'])) { + if ($this->engine == self::ENGINE_OPENSSL) { + $iv = \pack('V', $buffer['counter']) . $this->p2; + $buffer['counter'] += \strlen($text) >> 6; + $ciphertext .= \openssl_encrypt($text, $this->cipher_name_openssl, $this->key, \OPENSSL_RAW_DATA, $iv); + } else { + $blocks = \str_split($text, 64); + foreach ($blocks as &$block) { + $block ^= static::salsa20($this->p1 . \pack('V', $buffer['counter']++) . $this->p2); + } + unset($block); + $ciphertext .= \implode('', $blocks); + } + } + return $ciphertext; + } + /** + * Left Rotate + * + * @param int $x + * @param int $n + * @return int + */ + protected static function leftRotate($x, $n) + { + if (\PHP_INT_SIZE == 8) { + $r1 = $x << $n; + $r1 &= 0xffffffff; + $r2 = ($x & 0xffffffff) >> 32 - $n; + } else { + $x = (int) $x; + $r1 = $x << $n; + $r2 = $x >> 32 - $n; + $r2 &= (1 << $n) - 1; + } + return $r1 | $r2; + } + /** + * The quarterround function + * + * @param int $a + * @param int $b + * @param int $c + * @param int $d + */ + protected static function quarterRound(&$a, &$b, &$c, &$d) + { + $b ^= self::leftRotate($a + $d, 7); + $c ^= self::leftRotate($b + $a, 9); + $d ^= self::leftRotate($c + $b, 13); + $a ^= self::leftRotate($d + $c, 18); + } + /** + * The doubleround function + * + * @param int $x0 (by reference) + * @param int $x1 (by reference) + * @param int $x2 (by reference) + * @param int $x3 (by reference) + * @param int $x4 (by reference) + * @param int $x5 (by reference) + * @param int $x6 (by reference) + * @param int $x7 (by reference) + * @param int $x8 (by reference) + * @param int $x9 (by reference) + * @param int $x10 (by reference) + * @param int $x11 (by reference) + * @param int $x12 (by reference) + * @param int $x13 (by reference) + * @param int $x14 (by reference) + * @param int $x15 (by reference) + */ + protected static function doubleRound(&$x0, &$x1, &$x2, &$x3, &$x4, &$x5, &$x6, &$x7, &$x8, &$x9, &$x10, &$x11, &$x12, &$x13, &$x14, &$x15) + { + // columnRound + static::quarterRound($x0, $x4, $x8, $x12); + static::quarterRound($x5, $x9, $x13, $x1); + static::quarterRound($x10, $x14, $x2, $x6); + static::quarterRound($x15, $x3, $x7, $x11); + // rowRound + static::quarterRound($x0, $x1, $x2, $x3); + static::quarterRound($x5, $x6, $x7, $x4); + static::quarterRound($x10, $x11, $x8, $x9); + static::quarterRound($x15, $x12, $x13, $x14); + } + /** + * The Salsa20 hash function function + * + * @param string $x + */ + protected static function salsa20($x) + { + $z = $x = \unpack('V*', $x); + for ($i = 0; $i < 10; $i++) { + static::doubleRound($z[1], $z[2], $z[3], $z[4], $z[5], $z[6], $z[7], $z[8], $z[9], $z[10], $z[11], $z[12], $z[13], $z[14], $z[15], $z[16]); + } + for ($i = 1; $i <= 16; $i++) { + $x[$i] += $z[$i]; + } + return \pack('V*', ...$x); + } + /** + * Calculates Poly1305 MAC + * + * @see self::decrypt() + * @see self::encrypt() + * @param string $ciphertext + * @return string + */ + protected function poly1305($ciphertext) + { + if (!$this->usingGeneratedPoly1305Key) { + return parent::poly1305($this->aad . $ciphertext); + } else { + /* + sodium_crypto_aead_chacha20poly1305_encrypt does not calculate the poly1305 tag + the same way sodium_crypto_aead_chacha20poly1305_ietf_encrypt does. you can see + how the latter encrypts it in Salsa20::encrypt(). here's how the former encrypts + it: + + $this->newtag = $this->poly1305( + $this->aad . + pack('V', strlen($this->aad)) . "\0\0\0\0" . + $ciphertext . + pack('V', strlen($ciphertext)) . "\0\0\0\0" + ); + + phpseclib opts to use the IETF construction, even when the nonce is 64-bits + instead of 96-bits + */ + return parent::poly1305(self::nullPad128($this->aad) . self::nullPad128($ciphertext) . \pack('V', \strlen($this->aad)) . "\x00\x00\x00\x00" . \pack('V', \strlen($ciphertext)) . "\x00\x00\x00\x00"); + } + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/TripleDES.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/TripleDES.php new file mode 100644 index 0000000..0532eb3 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/TripleDES.php @@ -0,0 +1,384 @@ + + * setKey('abcdefghijklmnopqrstuvwx'); + * + * $size = 10 * 1024; + * $plaintext = ''; + * for ($i = 0; $i < $size; $i++) { + * $plaintext.= 'a'; + * } + * + * echo $des->decrypt($des->encrypt($plaintext)); + * ?> + * + * + * @author Jim Wigginton + * @copyright 2007 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\Crypt; + +/** + * Pure-PHP implementation of Triple DES. + * + * @author Jim Wigginton + */ +class TripleDES extends \FluentSmtpLib\phpseclib3\Crypt\DES +{ + /** + * Encrypt / decrypt using inner chaining + * + * Inner chaining is used by SSH-1 and is generally considered to be less secure then outer chaining (self::MODE_CBC3). + */ + const MODE_3CBC = -2; + /** + * Encrypt / decrypt using outer chaining + * + * Outer chaining is used by SSH-2 and when the mode is set to \phpseclib3\Crypt\Common\BlockCipher::MODE_CBC. + */ + const MODE_CBC3 = self::MODE_CBC; + /** + * Key Length (in bytes) + * + * @see \phpseclib3\Crypt\TripleDES::setKeyLength() + * @var int + */ + protected $key_length = 24; + /** + * The mcrypt specific name of the cipher + * + * @see \phpseclib3\Crypt\DES::cipher_name_mcrypt + * @see \phpseclib3\Crypt\Common\SymmetricKey::cipher_name_mcrypt + * @var string + */ + protected $cipher_name_mcrypt = 'tripledes'; + /** + * Optimizing value while CFB-encrypting + * + * @see \phpseclib3\Crypt\Common\SymmetricKey::cfb_init_len + * @var int + */ + protected $cfb_init_len = 750; + /** + * max possible size of $key + * + * @see self::setKey() + * @see \phpseclib3\Crypt\DES::setKey() + * @var string + */ + protected $key_length_max = 24; + /** + * Internal flag whether using self::MODE_3CBC or not + * + * @var bool + */ + private $mode_3cbc; + /** + * The \phpseclib3\Crypt\DES objects + * + * Used only if $mode_3cbc === true + * + * @var array + */ + private $des; + /** + * Default Constructor. + * + * Determines whether or not the mcrypt or OpenSSL extensions should be used. + * + * $mode could be: + * + * - ecb + * + * - cbc + * + * - ctr + * + * - cfb + * + * - ofb + * + * - 3cbc + * + * - cbc3 (same as cbc) + * + * @see \phpseclib3\Crypt\DES::__construct() + * @see \phpseclib3\Crypt\Common\SymmetricKey::__construct() + * @param string $mode + */ + public function __construct($mode) + { + switch (\strtolower($mode)) { + // In case of self::MODE_3CBC, we init as CRYPT_DES_MODE_CBC + // and additional flag us internally as 3CBC + case '3cbc': + parent::__construct('cbc'); + $this->mode_3cbc = \true; + // This three $des'es will do the 3CBC work (if $key > 64bits) + $this->des = [new \FluentSmtpLib\phpseclib3\Crypt\DES('cbc'), new \FluentSmtpLib\phpseclib3\Crypt\DES('cbc'), new \FluentSmtpLib\phpseclib3\Crypt\DES('cbc')]; + // we're going to be doing the padding, ourselves, so disable it in the \phpseclib3\Crypt\DES objects + $this->des[0]->disablePadding(); + $this->des[1]->disablePadding(); + $this->des[2]->disablePadding(); + break; + case 'cbc3': + $mode = 'cbc'; + // fall-through + // If not 3CBC, we init as usual + default: + parent::__construct($mode); + if ($this->mode == self::MODE_STREAM) { + throw new \FluentSmtpLib\phpseclib3\Crypt\BadModeException('Block ciphers cannot be ran in stream mode'); + } + } + } + /** + * Test for engine validity + * + * This is mainly just a wrapper to set things up for \phpseclib3\Crypt\Common\SymmetricKey::isValidEngine() + * + * @see \phpseclib3\Crypt\Common\SymmetricKey::__construct() + * @param int $engine + * @return bool + */ + protected function isValidEngineHelper($engine) + { + if ($engine == self::ENGINE_OPENSSL) { + $this->cipher_name_openssl_ecb = 'des-ede3'; + $mode = $this->openssl_translate_mode(); + $this->cipher_name_openssl = $mode == 'ecb' ? 'des-ede3' : 'des-ede3-' . $mode; + } + return parent::isValidEngineHelper($engine); + } + /** + * Sets the initialization vector. + * + * SetIV is not required when \phpseclib3\Crypt\Common\SymmetricKey::MODE_ECB is being used. + * + * @see \phpseclib3\Crypt\Common\SymmetricKey::setIV() + * @param string $iv + */ + public function setIV($iv) + { + parent::setIV($iv); + if ($this->mode_3cbc) { + $this->des[0]->setIV($iv); + $this->des[1]->setIV($iv); + $this->des[2]->setIV($iv); + } + } + /** + * Sets the key length. + * + * Valid key lengths are 128 and 192 bits. + * + * If you want to use a 64-bit key use DES.php + * + * @see \phpseclib3\Crypt\Common\SymmetricKey:setKeyLength() + * @throws \LengthException if the key length is invalid + * @param int $length + */ + public function setKeyLength($length) + { + switch ($length) { + case 128: + case 192: + break; + default: + throw new \LengthException('Key size of ' . $length . ' bits is not supported by this algorithm. Only keys of sizes 128 or 192 bits are supported'); + } + parent::setKeyLength($length); + } + /** + * Sets the key. + * + * Triple DES can use 128-bit (eg. strlen($key) == 16) or 192-bit (eg. strlen($key) == 24) keys. + * + * DES also requires that every eighth bit be a parity bit, however, we'll ignore that. + * + * @see \phpseclib3\Crypt\DES::setKey() + * @see \phpseclib3\Crypt\Common\SymmetricKey::setKey() + * @throws \LengthException if the key length is invalid + * @param string $key + */ + public function setKey($key) + { + if ($this->explicit_key_length !== \false && \strlen($key) != $this->explicit_key_length) { + throw new \LengthException('Key length has already been set to ' . $this->explicit_key_length . ' bytes and this key is ' . \strlen($key) . ' bytes'); + } + switch (\strlen($key)) { + case 16: + $key .= \substr($key, 0, 8); + break; + case 24: + break; + default: + throw new \LengthException('Key of size ' . \strlen($key) . ' not supported by this algorithm. Only keys of sizes 16 or 24 are supported'); + } + // copied from self::setKey() + $this->key = $key; + $this->key_length = \strlen($key); + $this->changed = $this->nonIVChanged = \true; + $this->setEngine(); + if ($this->mode_3cbc) { + $this->des[0]->setKey(\substr($key, 0, 8)); + $this->des[1]->setKey(\substr($key, 8, 8)); + $this->des[2]->setKey(\substr($key, 16, 8)); + } + } + /** + * Encrypts a message. + * + * @see \phpseclib3\Crypt\Common\SymmetricKey::encrypt() + * @param string $plaintext + * @return string $cipertext + */ + public function encrypt($plaintext) + { + // parent::en/decrypt() is able to do all the work for all modes and keylengths, + // except for: self::MODE_3CBC (inner chaining CBC) with a key > 64bits + // if the key is smaller then 8, do what we'd normally do + if ($this->mode_3cbc && \strlen($this->key) > 8) { + return $this->des[2]->encrypt($this->des[1]->decrypt($this->des[0]->encrypt($this->pad($plaintext)))); + } + return parent::encrypt($plaintext); + } + /** + * Decrypts a message. + * + * @see \phpseclib3\Crypt\Common\SymmetricKey::decrypt() + * @param string $ciphertext + * @return string $plaintext + */ + public function decrypt($ciphertext) + { + if ($this->mode_3cbc && \strlen($this->key) > 8) { + return $this->unpad($this->des[0]->decrypt($this->des[1]->encrypt($this->des[2]->decrypt(\str_pad($ciphertext, \strlen($ciphertext) + 7 & 0xfffffff8, "\x00"))))); + } + return parent::decrypt($ciphertext); + } + /** + * Treat consecutive "packets" as if they are a continuous buffer. + * + * Say you have a 16-byte plaintext $plaintext. Using the default behavior, the two following code snippets + * will yield different outputs: + * + * + * echo $des->encrypt(substr($plaintext, 0, 8)); + * echo $des->encrypt(substr($plaintext, 8, 8)); + * + * + * echo $des->encrypt($plaintext); + * + * + * The solution is to enable the continuous buffer. Although this will resolve the above discrepancy, it creates + * another, as demonstrated with the following: + * + * + * $des->encrypt(substr($plaintext, 0, 8)); + * echo $des->decrypt($des->encrypt(substr($plaintext, 8, 8))); + * + * + * echo $des->decrypt($des->encrypt(substr($plaintext, 8, 8))); + * + * + * With the continuous buffer disabled, these would yield the same output. With it enabled, they yield different + * outputs. The reason is due to the fact that the initialization vector's change after every encryption / + * decryption round when the continuous buffer is enabled. When it's disabled, they remain constant. + * + * Put another way, when the continuous buffer is enabled, the state of the \phpseclib3\Crypt\DES() object changes after each + * encryption / decryption round, whereas otherwise, it'd remain constant. For this reason, it's recommended that + * continuous buffers not be used. They do offer better security and are, in fact, sometimes required (SSH uses them), + * however, they are also less intuitive and more likely to cause you problems. + * + * @see \phpseclib3\Crypt\Common\SymmetricKey::enableContinuousBuffer() + * @see self::disableContinuousBuffer() + */ + public function enableContinuousBuffer() + { + parent::enableContinuousBuffer(); + if ($this->mode_3cbc) { + $this->des[0]->enableContinuousBuffer(); + $this->des[1]->enableContinuousBuffer(); + $this->des[2]->enableContinuousBuffer(); + } + } + /** + * Treat consecutive packets as if they are a discontinuous buffer. + * + * The default behavior. + * + * @see \phpseclib3\Crypt\Common\SymmetricKey::disableContinuousBuffer() + * @see self::enableContinuousBuffer() + */ + public function disableContinuousBuffer() + { + parent::disableContinuousBuffer(); + if ($this->mode_3cbc) { + $this->des[0]->disableContinuousBuffer(); + $this->des[1]->disableContinuousBuffer(); + $this->des[2]->disableContinuousBuffer(); + } + } + /** + * Creates the key schedule + * + * @see \phpseclib3\Crypt\DES::setupKey() + * @see \phpseclib3\Crypt\Common\SymmetricKey::setupKey() + */ + protected function setupKey() + { + switch (\true) { + // if $key <= 64bits we configure our internal pure-php cipher engine + // to act as regular [1]DES, not as 3DES. mcrypt.so::tripledes does the same. + case \strlen($this->key) <= 8: + $this->des_rounds = 1; + break; + // otherwise, if $key > 64bits, we configure our engine to work as 3DES. + default: + $this->des_rounds = 3; + // (only) if 3CBC is used we have, of course, to setup the $des[0-2] keys also separately. + if ($this->mode_3cbc) { + $this->des[0]->setupKey(); + $this->des[1]->setupKey(); + $this->des[2]->setupKey(); + // because $des[0-2] will, now, do all the work we can return here + // not need unnecessary stress parent::setupKey() with our, now unused, $key. + return; + } + } + // setup our key + parent::setupKey(); + } + /** + * Sets the internal crypt engine + * + * @see \phpseclib3\Crypt\Common\SymmetricKey::__construct() + * @see \phpseclib3\Crypt\Common\SymmetricKey::setPreferredEngine() + * @param int $engine + */ + public function setPreferredEngine($engine) + { + if ($this->mode_3cbc) { + $this->des[0]->setPreferredEngine($engine); + $this->des[1]->setPreferredEngine($engine); + $this->des[2]->setPreferredEngine($engine); + } + parent::setPreferredEngine($engine); + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/Twofish.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/Twofish.php new file mode 100644 index 0000000..03d0d1b --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Crypt/Twofish.php @@ -0,0 +1,506 @@ + + * setKey('12345678901234567890123456789012'); + * + * $plaintext = str_repeat('a', 1024); + * + * echo $twofish->decrypt($twofish->encrypt($plaintext)); + * ?> + * + * + * @author Jim Wigginton + * @author Hans-Juergen Petrich + * @copyright 2007 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\Crypt; + +use FluentSmtpLib\phpseclib3\Crypt\Common\BlockCipher; +use FluentSmtpLib\phpseclib3\Exception\BadModeException; +/** + * Pure-PHP implementation of Twofish. + * + * @author Jim Wigginton + * @author Hans-Juergen Petrich + */ +class Twofish extends \FluentSmtpLib\phpseclib3\Crypt\Common\BlockCipher +{ + /** + * The mcrypt specific name of the cipher + * + * @see \phpseclib3\Crypt\Common\SymmetricKey::cipher_name_mcrypt + * @var string + */ + protected $cipher_name_mcrypt = 'twofish'; + /** + * Optimizing value while CFB-encrypting + * + * @see \phpseclib3\Crypt\Common\SymmetricKey::cfb_init_len + * @var int + */ + protected $cfb_init_len = 800; + /** + * Q-Table + * + * @var array + */ + private static $q0 = [0xa9, 0x67, 0xb3, 0xe8, 0x4, 0xfd, 0xa3, 0x76, 0x9a, 0x92, 0x80, 0x78, 0xe4, 0xdd, 0xd1, 0x38, 0xd, 0xc6, 0x35, 0x98, 0x18, 0xf7, 0xec, 0x6c, 0x43, 0x75, 0x37, 0x26, 0xfa, 0x13, 0x94, 0x48, 0xf2, 0xd0, 0x8b, 0x30, 0x84, 0x54, 0xdf, 0x23, 0x19, 0x5b, 0x3d, 0x59, 0xf3, 0xae, 0xa2, 0x82, 0x63, 0x1, 0x83, 0x2e, 0xd9, 0x51, 0x9b, 0x7c, 0xa6, 0xeb, 0xa5, 0xbe, 0x16, 0xc, 0xe3, 0x61, 0xc0, 0x8c, 0x3a, 0xf5, 0x73, 0x2c, 0x25, 0xb, 0xbb, 0x4e, 0x89, 0x6b, 0x53, 0x6a, 0xb4, 0xf1, 0xe1, 0xe6, 0xbd, 0x45, 0xe2, 0xf4, 0xb6, 0x66, 0xcc, 0x95, 0x3, 0x56, 0xd4, 0x1c, 0x1e, 0xd7, 0xfb, 0xc3, 0x8e, 0xb5, 0xe9, 0xcf, 0xbf, 0xba, 0xea, 0x77, 0x39, 0xaf, 0x33, 0xc9, 0x62, 0x71, 0x81, 0x79, 0x9, 0xad, 0x24, 0xcd, 0xf9, 0xd8, 0xe5, 0xc5, 0xb9, 0x4d, 0x44, 0x8, 0x86, 0xe7, 0xa1, 0x1d, 0xaa, 0xed, 0x6, 0x70, 0xb2, 0xd2, 0x41, 0x7b, 0xa0, 0x11, 0x31, 0xc2, 0x27, 0x90, 0x20, 0xf6, 0x60, 0xff, 0x96, 0x5c, 0xb1, 0xab, 0x9e, 0x9c, 0x52, 0x1b, 0x5f, 0x93, 0xa, 0xef, 0x91, 0x85, 0x49, 0xee, 0x2d, 0x4f, 0x8f, 0x3b, 0x47, 0x87, 0x6d, 0x46, 0xd6, 0x3e, 0x69, 0x64, 0x2a, 0xce, 0xcb, 0x2f, 0xfc, 0x97, 0x5, 0x7a, 0xac, 0x7f, 0xd5, 0x1a, 0x4b, 0xe, 0xa7, 0x5a, 0x28, 0x14, 0x3f, 0x29, 0x88, 0x3c, 0x4c, 0x2, 0xb8, 0xda, 0xb0, 0x17, 0x55, 0x1f, 0x8a, 0x7d, 0x57, 0xc7, 0x8d, 0x74, 0xb7, 0xc4, 0x9f, 0x72, 0x7e, 0x15, 0x22, 0x12, 0x58, 0x7, 0x99, 0x34, 0x6e, 0x50, 0xde, 0x68, 0x65, 0xbc, 0xdb, 0xf8, 0xc8, 0xa8, 0x2b, 0x40, 0xdc, 0xfe, 0x32, 0xa4, 0xca, 0x10, 0x21, 0xf0, 0xd3, 0x5d, 0xf, 0x0, 0x6f, 0x9d, 0x36, 0x42, 0x4a, 0x5e, 0xc1, 0xe0]; + /** + * Q-Table + * + * @var array + */ + private static $q1 = [0x75, 0xf3, 0xc6, 0xf4, 0xdb, 0x7b, 0xfb, 0xc8, 0x4a, 0xd3, 0xe6, 0x6b, 0x45, 0x7d, 0xe8, 0x4b, 0xd6, 0x32, 0xd8, 0xfd, 0x37, 0x71, 0xf1, 0xe1, 0x30, 0xf, 0xf8, 0x1b, 0x87, 0xfa, 0x6, 0x3f, 0x5e, 0xba, 0xae, 0x5b, 0x8a, 0x0, 0xbc, 0x9d, 0x6d, 0xc1, 0xb1, 0xe, 0x80, 0x5d, 0xd2, 0xd5, 0xa0, 0x84, 0x7, 0x14, 0xb5, 0x90, 0x2c, 0xa3, 0xb2, 0x73, 0x4c, 0x54, 0x92, 0x74, 0x36, 0x51, 0x38, 0xb0, 0xbd, 0x5a, 0xfc, 0x60, 0x62, 0x96, 0x6c, 0x42, 0xf7, 0x10, 0x7c, 0x28, 0x27, 0x8c, 0x13, 0x95, 0x9c, 0xc7, 0x24, 0x46, 0x3b, 0x70, 0xca, 0xe3, 0x85, 0xcb, 0x11, 0xd0, 0x93, 0xb8, 0xa6, 0x83, 0x20, 0xff, 0x9f, 0x77, 0xc3, 0xcc, 0x3, 0x6f, 0x8, 0xbf, 0x40, 0xe7, 0x2b, 0xe2, 0x79, 0xc, 0xaa, 0x82, 0x41, 0x3a, 0xea, 0xb9, 0xe4, 0x9a, 0xa4, 0x97, 0x7e, 0xda, 0x7a, 0x17, 0x66, 0x94, 0xa1, 0x1d, 0x3d, 0xf0, 0xde, 0xb3, 0xb, 0x72, 0xa7, 0x1c, 0xef, 0xd1, 0x53, 0x3e, 0x8f, 0x33, 0x26, 0x5f, 0xec, 0x76, 0x2a, 0x49, 0x81, 0x88, 0xee, 0x21, 0xc4, 0x1a, 0xeb, 0xd9, 0xc5, 0x39, 0x99, 0xcd, 0xad, 0x31, 0x8b, 0x1, 0x18, 0x23, 0xdd, 0x1f, 0x4e, 0x2d, 0xf9, 0x48, 0x4f, 0xf2, 0x65, 0x8e, 0x78, 0x5c, 0x58, 0x19, 0x8d, 0xe5, 0x98, 0x57, 0x67, 0x7f, 0x5, 0x64, 0xaf, 0x63, 0xb6, 0xfe, 0xf5, 0xb7, 0x3c, 0xa5, 0xce, 0xe9, 0x68, 0x44, 0xe0, 0x4d, 0x43, 0x69, 0x29, 0x2e, 0xac, 0x15, 0x59, 0xa8, 0xa, 0x9e, 0x6e, 0x47, 0xdf, 0x34, 0x35, 0x6a, 0xcf, 0xdc, 0x22, 0xc9, 0xc0, 0x9b, 0x89, 0xd4, 0xed, 0xab, 0x12, 0xa2, 0xd, 0x52, 0xbb, 0x2, 0x2f, 0xa9, 0xd7, 0x61, 0x1e, 0xb4, 0x50, 0x4, 0xf6, 0xc2, 0x16, 0x25, 0x86, 0x56, 0x55, 0x9, 0xbe, 0x91]; + /** + * M-Table + * + * @var array + */ + private static $m0 = [0xbcbc3275, 0xecec21f3, 0x202043c6, 0xb3b3c9f4, 0xdada03db, 0x2028b7b, 0xe2e22bfb, 0x9e9efac8, 0xc9c9ec4a, 0xd4d409d3, 0x18186be6, 0x1e1e9f6b, 0x98980e45, 0xb2b2387d, 0xa6a6d2e8, 0x2626b74b, 0x3c3c57d6, 0x93938a32, 0x8282eed8, 0x525298fd, 0x7b7bd437, 0xbbbb3771, 0x5b5b97f1, 0x474783e1, 0x24243c30, 0x5151e20f, 0xbabac6f8, 0x4a4af31b, 0xbfbf4887, 0xd0d70fa, 0xb0b0b306, 0x7575de3f, 0xd2d2fd5e, 0x7d7d20ba, 0x666631ae, 0x3a3aa35b, 0x59591c8a, 0x0, 0xcdcd93bc, 0x1a1ae09d, 0xaeae2c6d, 0x7f7fabc1, 0x2b2bc7b1, 0xbebeb90e, 0xe0e0a080, 0x8a8a105d, 0x3b3b52d2, 0x6464bad5, 0xd8d888a0, 0xe7e7a584, 0x5f5fe807, 0x1b1b1114, 0x2c2cc2b5, 0xfcfcb490, 0x3131272c, 0x808065a3, 0x73732ab2, 0xc0c8173, 0x79795f4c, 0x6b6b4154, 0x4b4b0292, 0x53536974, 0x94948f36, 0x83831f51, 0x2a2a3638, 0xc4c49cb0, 0x2222c8bd, 0xd5d5f85a, 0xbdbdc3fc, 0x48487860, 0xffffce62, 0x4c4c0796, 0x4141776c, 0xc7c7e642, 0xebeb24f7, 0x1c1c1410, 0x5d5d637c, 0x36362228, 0x6767c027, 0xe9e9af8c, 0x4444f913, 0x1414ea95, 0xf5f5bb9c, 0xcfcf18c7, 0x3f3f2d24, 0xc0c0e346, 0x7272db3b, 0x54546c70, 0x29294cca, 0xf0f035e3, 0x808fe85, 0xc6c617cb, 0xf3f34f11, 0x8c8ce4d0, 0xa4a45993, 0xcaca96b8, 0x68683ba6, 0xb8b84d83, 0x38382820, 0xe5e52eff, 0xadad569f, 0xb0b8477, 0xc8c81dc3, 0x9999ffcc, 0x5858ed03, 0x19199a6f, 0xe0e0a08, 0x95957ebf, 0x70705040, 0xf7f730e7, 0x6e6ecf2b, 0x1f1f6ee2, 0xb5b53d79, 0x9090f0c, 0x616134aa, 0x57571682, 0x9f9f0b41, 0x9d9d803a, 0x111164ea, 0x2525cdb9, 0xafafdde4, 0x4545089a, 0xdfdf8da4, 0xa3a35c97, 0xeaead57e, 0x353558da, 0xededd07a, 0x4343fc17, 0xf8f8cb66, 0xfbfbb194, 0x3737d3a1, 0xfafa401d, 0xc2c2683d, 0xb4b4ccf0, 0x32325dde, 0x9c9c71b3, 0x5656e70b, 0xe3e3da72, 0x878760a7, 0x15151b1c, 0xf9f93aef, 0x6363bfd1, 0x3434a953, 0x9a9a853e, 0xb1b1428f, 0x7c7cd133, 0x88889b26, 0x3d3da65f, 0xa1a1d7ec, 0xe4e4df76, 0x8181942a, 0x91910149, 0xf0ffb81, 0xeeeeaa88, 0x161661ee, 0xd7d77321, 0x9797f5c4, 0xa5a5a81a, 0xfefe3feb, 0x6d6db5d9, 0x7878aec5, 0xc5c56d39, 0x1d1de599, 0x7676a4cd, 0x3e3edcad, 0xcbcb6731, 0xb6b6478b, 0xefef5b01, 0x12121e18, 0x6060c523, 0x6a6ab0dd, 0x4d4df61f, 0xcecee94e, 0xdede7c2d, 0x55559df9, 0x7e7e5a48, 0x2121b24f, 0x3037af2, 0xa0a02665, 0x5e5e198e, 0x5a5a6678, 0x65654b5c, 0x62624e58, 0xfdfd4519, 0x606f48d, 0x404086e5, 0xf2f2be98, 0x3333ac57, 0x17179067, 0x5058e7f, 0xe8e85e05, 0x4f4f7d64, 0x89896aaf, 0x10109563, 0x74742fb6, 0xa0a75fe, 0x5c5c92f5, 0x9b9b74b7, 0x2d2d333c, 0x3030d6a5, 0x2e2e49ce, 0x494989e9, 0x46467268, 0x77775544, 0xa8a8d8e0, 0x9696044d, 0x2828bd43, 0xa9a92969, 0xd9d97929, 0x8686912e, 0xd1d187ac, 0xf4f44a15, 0x8d8d1559, 0xd6d682a8, 0xb9b9bc0a, 0x42420d9e, 0xf6f6c16e, 0x2f2fb847, 0xdddd06df, 0x23233934, 0xcccc6235, 0xf1f1c46a, 0xc1c112cf, 0x8585ebdc, 0x8f8f9e22, 0x7171a1c9, 0x9090f0c0, 0xaaaa539b, 0x101f189, 0x8b8be1d4, 0x4e4e8ced, 0x8e8e6fab, 0xababa212, 0x6f6f3ea2, 0xe6e6540d, 0xdbdbf252, 0x92927bbb, 0xb7b7b602, 0x6969ca2f, 0x3939d9a9, 0xd3d30cd7, 0xa7a72361, 0xa2a2ad1e, 0xc3c399b4, 0x6c6c4450, 0x7070504, 0x4047ff6, 0x272746c2, 0xacaca716, 0xd0d07625, 0x50501386, 0xdcdcf756, 0x84841a55, 0xe1e15109, 0x7a7a25be, 0x1313ef91]; + /** + * M-Table + * + * @var array + */ + private static $m1 = [0xa9d93939, 0x67901717, 0xb3719c9c, 0xe8d2a6a6, 0x4050707, 0xfd985252, 0xa3658080, 0x76dfe4e4, 0x9a084545, 0x92024b4b, 0x80a0e0e0, 0x78665a5a, 0xe4ddafaf, 0xddb06a6a, 0xd1bf6363, 0x38362a2a, 0xd54e6e6, 0xc6432020, 0x3562cccc, 0x98bef2f2, 0x181e1212, 0xf724ebeb, 0xecd7a1a1, 0x6c774141, 0x43bd2828, 0x7532bcbc, 0x37d47b7b, 0x269b8888, 0xfa700d0d, 0x13f94444, 0x94b1fbfb, 0x485a7e7e, 0xf27a0303, 0xd0e48c8c, 0x8b47b6b6, 0x303c2424, 0x84a5e7e7, 0x54416b6b, 0xdf06dddd, 0x23c56060, 0x1945fdfd, 0x5ba33a3a, 0x3d68c2c2, 0x59158d8d, 0xf321ecec, 0xae316666, 0xa23e6f6f, 0x82165757, 0x63951010, 0x15befef, 0x834db8b8, 0x2e918686, 0xd9b56d6d, 0x511f8383, 0x9b53aaaa, 0x7c635d5d, 0xa63b6868, 0xeb3ffefe, 0xa5d63030, 0xbe257a7a, 0x16a7acac, 0xc0f0909, 0xe335f0f0, 0x6123a7a7, 0xc0f09090, 0x8cafe9e9, 0x3a809d9d, 0xf5925c5c, 0x73810c0c, 0x2c273131, 0x2576d0d0, 0xbe75656, 0xbb7b9292, 0x4ee9cece, 0x89f10101, 0x6b9f1e1e, 0x53a93434, 0x6ac4f1f1, 0xb499c3c3, 0xf1975b5b, 0xe1834747, 0xe66b1818, 0xbdc82222, 0x450e9898, 0xe26e1f1f, 0xf4c9b3b3, 0xb62f7474, 0x66cbf8f8, 0xccff9999, 0x95ea1414, 0x3ed5858, 0x56f7dcdc, 0xd4e18b8b, 0x1c1b1515, 0x1eada2a2, 0xd70cd3d3, 0xfb2be2e2, 0xc31dc8c8, 0x8e195e5e, 0xb5c22c2c, 0xe9894949, 0xcf12c1c1, 0xbf7e9595, 0xba207d7d, 0xea641111, 0x77840b0b, 0x396dc5c5, 0xaf6a8989, 0x33d17c7c, 0xc9a17171, 0x62ceffff, 0x7137bbbb, 0x81fb0f0f, 0x793db5b5, 0x951e1e1, 0xaddc3e3e, 0x242d3f3f, 0xcda47676, 0xf99d5555, 0xd8ee8282, 0xe5864040, 0xc5ae7878, 0xb9cd2525, 0x4d049696, 0x44557777, 0x80a0e0e, 0x86135050, 0xe730f7f7, 0xa1d33737, 0x1d40fafa, 0xaa346161, 0xed8c4e4e, 0x6b3b0b0, 0x706c5454, 0xb22a7373, 0xd2523b3b, 0x410b9f9f, 0x7b8b0202, 0xa088d8d8, 0x114ff3f3, 0x3167cbcb, 0xc2462727, 0x27c06767, 0x90b4fcfc, 0x20283838, 0xf67f0404, 0x60784848, 0xff2ee5e5, 0x96074c4c, 0x5c4b6565, 0xb1c72b2b, 0xab6f8e8e, 0x9e0d4242, 0x9cbbf5f5, 0x52f2dbdb, 0x1bf34a4a, 0x5fa63d3d, 0x9359a4a4, 0xabcb9b9, 0xef3af9f9, 0x91ef1313, 0x85fe0808, 0x49019191, 0xee611616, 0x2d7cdede, 0x4fb22121, 0x8f42b1b1, 0x3bdb7272, 0x47b82f2f, 0x8748bfbf, 0x6d2caeae, 0x46e3c0c0, 0xd6573c3c, 0x3e859a9a, 0x6929a9a9, 0x647d4f4f, 0x2a948181, 0xce492e2e, 0xcb17c6c6, 0x2fca6969, 0xfcc3bdbd, 0x975ca3a3, 0x55ee8e8, 0x7ad0eded, 0xac87d1d1, 0x7f8e0505, 0xd5ba6464, 0x1aa8a5a5, 0x4bb72626, 0xeb9bebe, 0xa7608787, 0x5af8d5d5, 0x28223636, 0x14111b1b, 0x3fde7575, 0x2979d9d9, 0x88aaeeee, 0x3c332d2d, 0x4c5f7979, 0x2b6b7b7, 0xb896caca, 0xda583535, 0xb09cc4c4, 0x17fc4343, 0x551a8484, 0x1ff64d4d, 0x8a1c5959, 0x7d38b2b2, 0x57ac3333, 0xc718cfcf, 0x8df40606, 0x74695353, 0xb7749b9b, 0xc4f59797, 0x9f56adad, 0x72dae3e3, 0x7ed5eaea, 0x154af4f4, 0x229e8f8f, 0x12a2abab, 0x584e6262, 0x7e85f5f, 0x99e51d1d, 0x34392323, 0x6ec1f6f6, 0x50446c6c, 0xde5d3232, 0x68724646, 0x6526a0a0, 0xbc93cdcd, 0xdb03dada, 0xf8c6baba, 0xc8fa9e9e, 0xa882d6d6, 0x2bcf6e6e, 0x40507070, 0xdceb8585, 0xfe750a0a, 0x328a9393, 0xa48ddfdf, 0xca4c2929, 0x10141c1c, 0x2173d7d7, 0xf0ccb4b4, 0xd309d4d4, 0x5d108a8a, 0xfe25151, 0x0, 0x6f9a1919, 0x9de01a1a, 0x368f9494, 0x42e6c7c7, 0x4aecc9c9, 0x5efdd2d2, 0xc1ab7f7f, 0xe0d8a8a8]; + /** + * M-Table + * + * @var array + */ + private static $m2 = [0xbc75bc32, 0xecf3ec21, 0x20c62043, 0xb3f4b3c9, 0xdadbda03, 0x27b028b, 0xe2fbe22b, 0x9ec89efa, 0xc94ac9ec, 0xd4d3d409, 0x18e6186b, 0x1e6b1e9f, 0x9845980e, 0xb27db238, 0xa6e8a6d2, 0x264b26b7, 0x3cd63c57, 0x9332938a, 0x82d882ee, 0x52fd5298, 0x7b377bd4, 0xbb71bb37, 0x5bf15b97, 0x47e14783, 0x2430243c, 0x510f51e2, 0xbaf8bac6, 0x4a1b4af3, 0xbf87bf48, 0xdfa0d70, 0xb006b0b3, 0x753f75de, 0xd25ed2fd, 0x7dba7d20, 0x66ae6631, 0x3a5b3aa3, 0x598a591c, 0x0, 0xcdbccd93, 0x1a9d1ae0, 0xae6dae2c, 0x7fc17fab, 0x2bb12bc7, 0xbe0ebeb9, 0xe080e0a0, 0x8a5d8a10, 0x3bd23b52, 0x64d564ba, 0xd8a0d888, 0xe784e7a5, 0x5f075fe8, 0x1b141b11, 0x2cb52cc2, 0xfc90fcb4, 0x312c3127, 0x80a38065, 0x73b2732a, 0xc730c81, 0x794c795f, 0x6b546b41, 0x4b924b02, 0x53745369, 0x9436948f, 0x8351831f, 0x2a382a36, 0xc4b0c49c, 0x22bd22c8, 0xd55ad5f8, 0xbdfcbdc3, 0x48604878, 0xff62ffce, 0x4c964c07, 0x416c4177, 0xc742c7e6, 0xebf7eb24, 0x1c101c14, 0x5d7c5d63, 0x36283622, 0x672767c0, 0xe98ce9af, 0x441344f9, 0x149514ea, 0xf59cf5bb, 0xcfc7cf18, 0x3f243f2d, 0xc046c0e3, 0x723b72db, 0x5470546c, 0x29ca294c, 0xf0e3f035, 0x88508fe, 0xc6cbc617, 0xf311f34f, 0x8cd08ce4, 0xa493a459, 0xcab8ca96, 0x68a6683b, 0xb883b84d, 0x38203828, 0xe5ffe52e, 0xad9fad56, 0xb770b84, 0xc8c3c81d, 0x99cc99ff, 0x580358ed, 0x196f199a, 0xe080e0a, 0x95bf957e, 0x70407050, 0xf7e7f730, 0x6e2b6ecf, 0x1fe21f6e, 0xb579b53d, 0x90c090f, 0x61aa6134, 0x57825716, 0x9f419f0b, 0x9d3a9d80, 0x11ea1164, 0x25b925cd, 0xafe4afdd, 0x459a4508, 0xdfa4df8d, 0xa397a35c, 0xea7eead5, 0x35da3558, 0xed7aedd0, 0x431743fc, 0xf866f8cb, 0xfb94fbb1, 0x37a137d3, 0xfa1dfa40, 0xc23dc268, 0xb4f0b4cc, 0x32de325d, 0x9cb39c71, 0x560b56e7, 0xe372e3da, 0x87a78760, 0x151c151b, 0xf9eff93a, 0x63d163bf, 0x345334a9, 0x9a3e9a85, 0xb18fb142, 0x7c337cd1, 0x8826889b, 0x3d5f3da6, 0xa1eca1d7, 0xe476e4df, 0x812a8194, 0x91499101, 0xf810ffb, 0xee88eeaa, 0x16ee1661, 0xd721d773, 0x97c497f5, 0xa51aa5a8, 0xfeebfe3f, 0x6dd96db5, 0x78c578ae, 0xc539c56d, 0x1d991de5, 0x76cd76a4, 0x3ead3edc, 0xcb31cb67, 0xb68bb647, 0xef01ef5b, 0x1218121e, 0x602360c5, 0x6add6ab0, 0x4d1f4df6, 0xce4ecee9, 0xde2dde7c, 0x55f9559d, 0x7e487e5a, 0x214f21b2, 0x3f2037a, 0xa065a026, 0x5e8e5e19, 0x5a785a66, 0x655c654b, 0x6258624e, 0xfd19fd45, 0x68d06f4, 0x40e54086, 0xf298f2be, 0x335733ac, 0x17671790, 0x57f058e, 0xe805e85e, 0x4f644f7d, 0x89af896a, 0x10631095, 0x74b6742f, 0xafe0a75, 0x5cf55c92, 0x9bb79b74, 0x2d3c2d33, 0x30a530d6, 0x2ece2e49, 0x49e94989, 0x46684672, 0x77447755, 0xa8e0a8d8, 0x964d9604, 0x284328bd, 0xa969a929, 0xd929d979, 0x862e8691, 0xd1acd187, 0xf415f44a, 0x8d598d15, 0xd6a8d682, 0xb90ab9bc, 0x429e420d, 0xf66ef6c1, 0x2f472fb8, 0xdddfdd06, 0x23342339, 0xcc35cc62, 0xf16af1c4, 0xc1cfc112, 0x85dc85eb, 0x8f228f9e, 0x71c971a1, 0x90c090f0, 0xaa9baa53, 0x18901f1, 0x8bd48be1, 0x4eed4e8c, 0x8eab8e6f, 0xab12aba2, 0x6fa26f3e, 0xe60de654, 0xdb52dbf2, 0x92bb927b, 0xb702b7b6, 0x692f69ca, 0x39a939d9, 0xd3d7d30c, 0xa761a723, 0xa21ea2ad, 0xc3b4c399, 0x6c506c44, 0x7040705, 0x4f6047f, 0x27c22746, 0xac16aca7, 0xd025d076, 0x50865013, 0xdc56dcf7, 0x8455841a, 0xe109e151, 0x7abe7a25, 0x139113ef]; + /** + * M-Table + * + * @var array + */ + private static $m3 = [0xd939a9d9, 0x90176790, 0x719cb371, 0xd2a6e8d2, 0x5070405, 0x9852fd98, 0x6580a365, 0xdfe476df, 0x8459a08, 0x24b9202, 0xa0e080a0, 0x665a7866, 0xddafe4dd, 0xb06addb0, 0xbf63d1bf, 0x362a3836, 0x54e60d54, 0x4320c643, 0x62cc3562, 0xbef298be, 0x1e12181e, 0x24ebf724, 0xd7a1ecd7, 0x77416c77, 0xbd2843bd, 0x32bc7532, 0xd47b37d4, 0x9b88269b, 0x700dfa70, 0xf94413f9, 0xb1fb94b1, 0x5a7e485a, 0x7a03f27a, 0xe48cd0e4, 0x47b68b47, 0x3c24303c, 0xa5e784a5, 0x416b5441, 0x6dddf06, 0xc56023c5, 0x45fd1945, 0xa33a5ba3, 0x68c23d68, 0x158d5915, 0x21ecf321, 0x3166ae31, 0x3e6fa23e, 0x16578216, 0x95106395, 0x5bef015b, 0x4db8834d, 0x91862e91, 0xb56dd9b5, 0x1f83511f, 0x53aa9b53, 0x635d7c63, 0x3b68a63b, 0x3ffeeb3f, 0xd630a5d6, 0x257abe25, 0xa7ac16a7, 0xf090c0f, 0x35f0e335, 0x23a76123, 0xf090c0f0, 0xafe98caf, 0x809d3a80, 0x925cf592, 0x810c7381, 0x27312c27, 0x76d02576, 0xe7560be7, 0x7b92bb7b, 0xe9ce4ee9, 0xf10189f1, 0x9f1e6b9f, 0xa93453a9, 0xc4f16ac4, 0x99c3b499, 0x975bf197, 0x8347e183, 0x6b18e66b, 0xc822bdc8, 0xe98450e, 0x6e1fe26e, 0xc9b3f4c9, 0x2f74b62f, 0xcbf866cb, 0xff99ccff, 0xea1495ea, 0xed5803ed, 0xf7dc56f7, 0xe18bd4e1, 0x1b151c1b, 0xada21ead, 0xcd3d70c, 0x2be2fb2b, 0x1dc8c31d, 0x195e8e19, 0xc22cb5c2, 0x8949e989, 0x12c1cf12, 0x7e95bf7e, 0x207dba20, 0x6411ea64, 0x840b7784, 0x6dc5396d, 0x6a89af6a, 0xd17c33d1, 0xa171c9a1, 0xceff62ce, 0x37bb7137, 0xfb0f81fb, 0x3db5793d, 0x51e10951, 0xdc3eaddc, 0x2d3f242d, 0xa476cda4, 0x9d55f99d, 0xee82d8ee, 0x8640e586, 0xae78c5ae, 0xcd25b9cd, 0x4964d04, 0x55774455, 0xa0e080a, 0x13508613, 0x30f7e730, 0xd337a1d3, 0x40fa1d40, 0x3461aa34, 0x8c4eed8c, 0xb3b006b3, 0x6c54706c, 0x2a73b22a, 0x523bd252, 0xb9f410b, 0x8b027b8b, 0x88d8a088, 0x4ff3114f, 0x67cb3167, 0x4627c246, 0xc06727c0, 0xb4fc90b4, 0x28382028, 0x7f04f67f, 0x78486078, 0x2ee5ff2e, 0x74c9607, 0x4b655c4b, 0xc72bb1c7, 0x6f8eab6f, 0xd429e0d, 0xbbf59cbb, 0xf2db52f2, 0xf34a1bf3, 0xa63d5fa6, 0x59a49359, 0xbcb90abc, 0x3af9ef3a, 0xef1391ef, 0xfe0885fe, 0x1914901, 0x6116ee61, 0x7cde2d7c, 0xb2214fb2, 0x42b18f42, 0xdb723bdb, 0xb82f47b8, 0x48bf8748, 0x2cae6d2c, 0xe3c046e3, 0x573cd657, 0x859a3e85, 0x29a96929, 0x7d4f647d, 0x94812a94, 0x492ece49, 0x17c6cb17, 0xca692fca, 0xc3bdfcc3, 0x5ca3975c, 0x5ee8055e, 0xd0ed7ad0, 0x87d1ac87, 0x8e057f8e, 0xba64d5ba, 0xa8a51aa8, 0xb7264bb7, 0xb9be0eb9, 0x6087a760, 0xf8d55af8, 0x22362822, 0x111b1411, 0xde753fde, 0x79d92979, 0xaaee88aa, 0x332d3c33, 0x5f794c5f, 0xb6b702b6, 0x96cab896, 0x5835da58, 0x9cc4b09c, 0xfc4317fc, 0x1a84551a, 0xf64d1ff6, 0x1c598a1c, 0x38b27d38, 0xac3357ac, 0x18cfc718, 0xf4068df4, 0x69537469, 0x749bb774, 0xf597c4f5, 0x56ad9f56, 0xdae372da, 0xd5ea7ed5, 0x4af4154a, 0x9e8f229e, 0xa2ab12a2, 0x4e62584e, 0xe85f07e8, 0xe51d99e5, 0x39233439, 0xc1f66ec1, 0x446c5044, 0x5d32de5d, 0x72466872, 0x26a06526, 0x93cdbc93, 0x3dadb03, 0xc6baf8c6, 0xfa9ec8fa, 0x82d6a882, 0xcf6e2bcf, 0x50704050, 0xeb85dceb, 0x750afe75, 0x8a93328a, 0x8ddfa48d, 0x4c29ca4c, 0x141c1014, 0x73d72173, 0xccb4f0cc, 0x9d4d309, 0x108a5d10, 0xe2510fe2, 0x0, 0x9a196f9a, 0xe01a9de0, 0x8f94368f, 0xe6c742e6, 0xecc94aec, 0xfdd25efd, 0xab7fc1ab, 0xd8a8e0d8]; + /** + * The Key Schedule Array + * + * @var array + */ + private $K = []; + /** + * The Key depended S-Table 0 + * + * @var array + */ + private $S0 = []; + /** + * The Key depended S-Table 1 + * + * @var array + */ + private $S1 = []; + /** + * The Key depended S-Table 2 + * + * @var array + */ + private $S2 = []; + /** + * The Key depended S-Table 3 + * + * @var array + */ + private $S3 = []; + /** + * Holds the last used key + * + * @var array + */ + private $kl; + /** + * The Key Length (in bytes) + * + * @see Crypt_Twofish::setKeyLength() + * @var int + */ + protected $key_length = 16; + /** + * Default Constructor. + * + * @param string $mode + * @throws BadModeException if an invalid / unsupported mode is provided + */ + public function __construct($mode) + { + parent::__construct($mode); + if ($this->mode == self::MODE_STREAM) { + throw new \FluentSmtpLib\phpseclib3\Exception\BadModeException('Block ciphers cannot be ran in stream mode'); + } + } + /** + * Initialize Static Variables + */ + protected static function initialize_static_variables() + { + if (\is_float(self::$m3[0])) { + self::$m0 = \array_map('intval', self::$m0); + self::$m1 = \array_map('intval', self::$m1); + self::$m2 = \array_map('intval', self::$m2); + self::$m3 = \array_map('intval', self::$m3); + self::$q0 = \array_map('intval', self::$q0); + self::$q1 = \array_map('intval', self::$q1); + } + parent::initialize_static_variables(); + } + /** + * Sets the key length. + * + * Valid key lengths are 128, 192 or 256 bits + * + * @param int $length + */ + public function setKeyLength($length) + { + switch ($length) { + case 128: + case 192: + case 256: + break; + default: + throw new \LengthException('Key of size ' . $length . ' not supported by this algorithm. Only keys of sizes 16, 24 or 32 supported'); + } + parent::setKeyLength($length); + } + /** + * Sets the key. + * + * Rijndael supports five different key lengths + * + * @see setKeyLength() + * @param string $key + * @throws \LengthException if the key length isn't supported + */ + public function setKey($key) + { + switch (\strlen($key)) { + case 16: + case 24: + case 32: + break; + default: + throw new \LengthException('Key of size ' . \strlen($key) . ' not supported by this algorithm. Only keys of sizes 16, 24 or 32 supported'); + } + parent::setKey($key); + } + /** + * Setup the key (expansion) + * + * @see \phpseclib3\Crypt\Common\SymmetricKey::_setupKey() + */ + protected function setupKey() + { + if (isset($this->kl['key']) && $this->key === $this->kl['key']) { + // already expanded + return; + } + $this->kl = ['key' => $this->key]; + /* Key expanding and generating the key-depended s-boxes */ + $le_longs = \unpack('V*', $this->key); + $key = \unpack('C*', $this->key); + $m0 = self::$m0; + $m1 = self::$m1; + $m2 = self::$m2; + $m3 = self::$m3; + $q0 = self::$q0; + $q1 = self::$q1; + $K = $S0 = $S1 = $S2 = $S3 = []; + switch (\strlen($this->key)) { + case 16: + list($s7, $s6, $s5, $s4) = $this->mdsrem($le_longs[1], $le_longs[2]); + list($s3, $s2, $s1, $s0) = $this->mdsrem($le_longs[3], $le_longs[4]); + for ($i = 0, $j = 1; $i < 40; $i += 2, $j += 2) { + $A = $m0[$q0[$q0[$i] ^ $key[9]] ^ $key[1]] ^ $m1[$q0[$q1[$i] ^ $key[10]] ^ $key[2]] ^ $m2[$q1[$q0[$i] ^ $key[11]] ^ $key[3]] ^ $m3[$q1[$q1[$i] ^ $key[12]] ^ $key[4]]; + $B = $m0[$q0[$q0[$j] ^ $key[13]] ^ $key[5]] ^ $m1[$q0[$q1[$j] ^ $key[14]] ^ $key[6]] ^ $m2[$q1[$q0[$j] ^ $key[15]] ^ $key[7]] ^ $m3[$q1[$q1[$j] ^ $key[16]] ^ $key[8]]; + $B = $B << 8 | $B >> 24 & 0xff; + $A = self::safe_intval($A + $B); + $K[] = $A; + $A = self::safe_intval($A + $B); + $K[] = $A << 9 | $A >> 23 & 0x1ff; + } + for ($i = 0; $i < 256; ++$i) { + $S0[$i] = $m0[$q0[$q0[$i] ^ $s4] ^ $s0]; + $S1[$i] = $m1[$q0[$q1[$i] ^ $s5] ^ $s1]; + $S2[$i] = $m2[$q1[$q0[$i] ^ $s6] ^ $s2]; + $S3[$i] = $m3[$q1[$q1[$i] ^ $s7] ^ $s3]; + } + break; + case 24: + list($sb, $sa, $s9, $s8) = $this->mdsrem($le_longs[1], $le_longs[2]); + list($s7, $s6, $s5, $s4) = $this->mdsrem($le_longs[3], $le_longs[4]); + list($s3, $s2, $s1, $s0) = $this->mdsrem($le_longs[5], $le_longs[6]); + for ($i = 0, $j = 1; $i < 40; $i += 2, $j += 2) { + $A = $m0[$q0[$q0[$q1[$i] ^ $key[17]] ^ $key[9]] ^ $key[1]] ^ $m1[$q0[$q1[$q1[$i] ^ $key[18]] ^ $key[10]] ^ $key[2]] ^ $m2[$q1[$q0[$q0[$i] ^ $key[19]] ^ $key[11]] ^ $key[3]] ^ $m3[$q1[$q1[$q0[$i] ^ $key[20]] ^ $key[12]] ^ $key[4]]; + $B = $m0[$q0[$q0[$q1[$j] ^ $key[21]] ^ $key[13]] ^ $key[5]] ^ $m1[$q0[$q1[$q1[$j] ^ $key[22]] ^ $key[14]] ^ $key[6]] ^ $m2[$q1[$q0[$q0[$j] ^ $key[23]] ^ $key[15]] ^ $key[7]] ^ $m3[$q1[$q1[$q0[$j] ^ $key[24]] ^ $key[16]] ^ $key[8]]; + $B = $B << 8 | $B >> 24 & 0xff; + $A = self::safe_intval($A + $B); + $K[] = $A; + $A = self::safe_intval($A + $B); + $K[] = $A << 9 | $A >> 23 & 0x1ff; + } + for ($i = 0; $i < 256; ++$i) { + $S0[$i] = $m0[$q0[$q0[$q1[$i] ^ $s8] ^ $s4] ^ $s0]; + $S1[$i] = $m1[$q0[$q1[$q1[$i] ^ $s9] ^ $s5] ^ $s1]; + $S2[$i] = $m2[$q1[$q0[$q0[$i] ^ $sa] ^ $s6] ^ $s2]; + $S3[$i] = $m3[$q1[$q1[$q0[$i] ^ $sb] ^ $s7] ^ $s3]; + } + break; + default: + // 32 + list($sf, $se, $sd, $sc) = $this->mdsrem($le_longs[1], $le_longs[2]); + list($sb, $sa, $s9, $s8) = $this->mdsrem($le_longs[3], $le_longs[4]); + list($s7, $s6, $s5, $s4) = $this->mdsrem($le_longs[5], $le_longs[6]); + list($s3, $s2, $s1, $s0) = $this->mdsrem($le_longs[7], $le_longs[8]); + for ($i = 0, $j = 1; $i < 40; $i += 2, $j += 2) { + $A = $m0[$q0[$q0[$q1[$q1[$i] ^ $key[25]] ^ $key[17]] ^ $key[9]] ^ $key[1]] ^ $m1[$q0[$q1[$q1[$q0[$i] ^ $key[26]] ^ $key[18]] ^ $key[10]] ^ $key[2]] ^ $m2[$q1[$q0[$q0[$q0[$i] ^ $key[27]] ^ $key[19]] ^ $key[11]] ^ $key[3]] ^ $m3[$q1[$q1[$q0[$q1[$i] ^ $key[28]] ^ $key[20]] ^ $key[12]] ^ $key[4]]; + $B = $m0[$q0[$q0[$q1[$q1[$j] ^ $key[29]] ^ $key[21]] ^ $key[13]] ^ $key[5]] ^ $m1[$q0[$q1[$q1[$q0[$j] ^ $key[30]] ^ $key[22]] ^ $key[14]] ^ $key[6]] ^ $m2[$q1[$q0[$q0[$q0[$j] ^ $key[31]] ^ $key[23]] ^ $key[15]] ^ $key[7]] ^ $m3[$q1[$q1[$q0[$q1[$j] ^ $key[32]] ^ $key[24]] ^ $key[16]] ^ $key[8]]; + $B = $B << 8 | $B >> 24 & 0xff; + $A = self::safe_intval($A + $B); + $K[] = $A; + $A = self::safe_intval($A + $B); + $K[] = $A << 9 | $A >> 23 & 0x1ff; + } + for ($i = 0; $i < 256; ++$i) { + $S0[$i] = $m0[$q0[$q0[$q1[$q1[$i] ^ $sc] ^ $s8] ^ $s4] ^ $s0]; + $S1[$i] = $m1[$q0[$q1[$q1[$q0[$i] ^ $sd] ^ $s9] ^ $s5] ^ $s1]; + $S2[$i] = $m2[$q1[$q0[$q0[$q0[$i] ^ $se] ^ $sa] ^ $s6] ^ $s2]; + $S3[$i] = $m3[$q1[$q1[$q0[$q1[$i] ^ $sf] ^ $sb] ^ $s7] ^ $s3]; + } + } + $this->K = $K; + $this->S0 = $S0; + $this->S1 = $S1; + $this->S2 = $S2; + $this->S3 = $S3; + } + /** + * _mdsrem function using by the twofish cipher algorithm + * + * @param string $A + * @param string $B + * @return array + */ + private function mdsrem($A, $B) + { + // No gain by unrolling this loop. + for ($i = 0; $i < 8; ++$i) { + // Get most significant coefficient. + $t = 0xff & $B >> 24; + // Shift the others up. + $B = $B << 8 | 0xff & $A >> 24; + $A <<= 8; + $u = $t << 1; + // Subtract the modular polynomial on overflow. + if ($t & 0x80) { + $u ^= 0x14d; + } + // Remove t * (a * x^2 + 1). + $B ^= $t ^ $u << 16; + // Form u = a*t + t/a = t*(a + 1/a). + $u ^= 0x7fffffff & $t >> 1; + // Add the modular polynomial on underflow. + if ($t & 0x1) { + $u ^= 0xa6; + } + // Remove t * (a + 1/a) * (x^3 + x). + $B ^= $u << 24 | $u << 8; + } + return [0xff & $B >> 24, 0xff & $B >> 16, 0xff & $B >> 8, 0xff & $B]; + } + /** + * Encrypts a block + * + * @param string $in + * @return string + */ + protected function encryptBlock($in) + { + $S0 = $this->S0; + $S1 = $this->S1; + $S2 = $this->S2; + $S3 = $this->S3; + $K = $this->K; + $in = \unpack("V4", $in); + $R0 = $K[0] ^ $in[1]; + $R1 = $K[1] ^ $in[2]; + $R2 = $K[2] ^ $in[3]; + $R3 = $K[3] ^ $in[4]; + $ki = 7; + while ($ki < 39) { + $t0 = $S0[$R0 & 0xff] ^ $S1[$R0 >> 8 & 0xff] ^ $S2[$R0 >> 16 & 0xff] ^ $S3[$R0 >> 24 & 0xff]; + $t1 = $S0[$R1 >> 24 & 0xff] ^ $S1[$R1 & 0xff] ^ $S2[$R1 >> 8 & 0xff] ^ $S3[$R1 >> 16 & 0xff]; + $R2 ^= self::safe_intval($t0 + $t1 + $K[++$ki]); + $R2 = $R2 >> 1 & 0x7fffffff | $R2 << 31; + $R3 = ($R3 >> 31 & 1 | $R3 << 1) ^ self::safe_intval($t0 + ($t1 << 1) + $K[++$ki]); + $t0 = $S0[$R2 & 0xff] ^ $S1[$R2 >> 8 & 0xff] ^ $S2[$R2 >> 16 & 0xff] ^ $S3[$R2 >> 24 & 0xff]; + $t1 = $S0[$R3 >> 24 & 0xff] ^ $S1[$R3 & 0xff] ^ $S2[$R3 >> 8 & 0xff] ^ $S3[$R3 >> 16 & 0xff]; + $R0 ^= self::safe_intval($t0 + $t1 + $K[++$ki]); + $R0 = $R0 >> 1 & 0x7fffffff | $R0 << 31; + $R1 = ($R1 >> 31 & 1 | $R1 << 1) ^ self::safe_intval($t0 + ($t1 << 1) + $K[++$ki]); + } + // @codingStandardsIgnoreStart + return \pack("V4", $K[4] ^ $R2, $K[5] ^ $R3, $K[6] ^ $R0, $K[7] ^ $R1); + // @codingStandardsIgnoreEnd + } + /** + * Decrypts a block + * + * @param string $in + * @return string + */ + protected function decryptBlock($in) + { + $S0 = $this->S0; + $S1 = $this->S1; + $S2 = $this->S2; + $S3 = $this->S3; + $K = $this->K; + $in = \unpack("V4", $in); + $R0 = $K[4] ^ $in[1]; + $R1 = $K[5] ^ $in[2]; + $R2 = $K[6] ^ $in[3]; + $R3 = $K[7] ^ $in[4]; + $ki = 40; + while ($ki > 8) { + $t0 = $S0[$R0 & 0xff] ^ $S1[$R0 >> 8 & 0xff] ^ $S2[$R0 >> 16 & 0xff] ^ $S3[$R0 >> 24 & 0xff]; + $t1 = $S0[$R1 >> 24 & 0xff] ^ $S1[$R1 & 0xff] ^ $S2[$R1 >> 8 & 0xff] ^ $S3[$R1 >> 16 & 0xff]; + $R3 ^= self::safe_intval($t0 + ($t1 << 1) + $K[--$ki]); + $R3 = $R3 >> 1 & 0x7fffffff | $R3 << 31; + $R2 = ($R2 >> 31 & 0x1 | $R2 << 1) ^ self::safe_intval($t0 + $t1 + $K[--$ki]); + $t0 = $S0[$R2 & 0xff] ^ $S1[$R2 >> 8 & 0xff] ^ $S2[$R2 >> 16 & 0xff] ^ $S3[$R2 >> 24 & 0xff]; + $t1 = $S0[$R3 >> 24 & 0xff] ^ $S1[$R3 & 0xff] ^ $S2[$R3 >> 8 & 0xff] ^ $S3[$R3 >> 16 & 0xff]; + $R1 ^= self::safe_intval($t0 + ($t1 << 1) + $K[--$ki]); + $R1 = $R1 >> 1 & 0x7fffffff | $R1 << 31; + $R0 = ($R0 >> 31 & 0x1 | $R0 << 1) ^ self::safe_intval($t0 + $t1 + $K[--$ki]); + } + // @codingStandardsIgnoreStart + return \pack("V4", $K[0] ^ $R2, $K[1] ^ $R3, $K[2] ^ $R0, $K[3] ^ $R1); + // @codingStandardsIgnoreEnd + } + /** + * Setup the performance-optimized function for de/encrypt() + * + * @see \phpseclib3\Crypt\Common\SymmetricKey::_setupInlineCrypt() + */ + protected function setupInlineCrypt() + { + $K = $this->K; + $init_crypt = ' + static $S0, $S1, $S2, $S3; + if (!$S0) { + for ($i = 0; $i < 256; ++$i) { + $S0[] = (int)$this->S0[$i]; + $S1[] = (int)$this->S1[$i]; + $S2[] = (int)$this->S2[$i]; + $S3[] = (int)$this->S3[$i]; + } + } + '; + $safeint = self::safe_intval_inline(); + // Generating encrypt code: + $encrypt_block = ' + $in = unpack("V4", $in); + $R0 = ' . $K[0] . ' ^ $in[1]; + $R1 = ' . $K[1] . ' ^ $in[2]; + $R2 = ' . $K[2] . ' ^ $in[3]; + $R3 = ' . $K[3] . ' ^ $in[4]; + '; + for ($ki = 7, $i = 0; $i < 8; ++$i) { + $encrypt_block .= ' + $t0 = $S0[ $R0 & 0xff] ^ + $S1[($R0 >> 8) & 0xff] ^ + $S2[($R0 >> 16) & 0xff] ^ + $S3[($R0 >> 24) & 0xff]; + $t1 = $S0[($R1 >> 24) & 0xff] ^ + $S1[ $R1 & 0xff] ^ + $S2[($R1 >> 8) & 0xff] ^ + $S3[($R1 >> 16) & 0xff]; + $R2^= ' . \sprintf($safeint, '$t0 + $t1 + ' . $K[++$ki]) . '; + $R2 = ($R2 >> 1 & 0x7fffffff) | ($R2 << 31); + $R3 = ((($R3 >> 31) & 1) | ($R3 << 1)) ^ ' . \sprintf($safeint, '($t0 + ($t1 << 1) + ' . $K[++$ki] . ')') . '; + + $t0 = $S0[ $R2 & 0xff] ^ + $S1[($R2 >> 8) & 0xff] ^ + $S2[($R2 >> 16) & 0xff] ^ + $S3[($R2 >> 24) & 0xff]; + $t1 = $S0[($R3 >> 24) & 0xff] ^ + $S1[ $R3 & 0xff] ^ + $S2[($R3 >> 8) & 0xff] ^ + $S3[($R3 >> 16) & 0xff]; + $R0^= ' . \sprintf($safeint, '($t0 + $t1 + ' . $K[++$ki] . ')') . '; + $R0 = ($R0 >> 1 & 0x7fffffff) | ($R0 << 31); + $R1 = ((($R1 >> 31) & 1) | ($R1 << 1)) ^ ' . \sprintf($safeint, '($t0 + ($t1 << 1) + ' . $K[++$ki] . ')') . '; + '; + } + $encrypt_block .= ' + $in = pack("V4", ' . $K[4] . ' ^ $R2, + ' . $K[5] . ' ^ $R3, + ' . $K[6] . ' ^ $R0, + ' . $K[7] . ' ^ $R1); + '; + // Generating decrypt code: + $decrypt_block = ' + $in = unpack("V4", $in); + $R0 = ' . $K[4] . ' ^ $in[1]; + $R1 = ' . $K[5] . ' ^ $in[2]; + $R2 = ' . $K[6] . ' ^ $in[3]; + $R3 = ' . $K[7] . ' ^ $in[4]; + '; + for ($ki = 40, $i = 0; $i < 8; ++$i) { + $decrypt_block .= ' + $t0 = $S0[$R0 & 0xff] ^ + $S1[$R0 >> 8 & 0xff] ^ + $S2[$R0 >> 16 & 0xff] ^ + $S3[$R0 >> 24 & 0xff]; + $t1 = $S0[$R1 >> 24 & 0xff] ^ + $S1[$R1 & 0xff] ^ + $S2[$R1 >> 8 & 0xff] ^ + $S3[$R1 >> 16 & 0xff]; + $R3^= ' . \sprintf($safeint, '$t0 + ($t1 << 1) + ' . $K[--$ki]) . '; + $R3 = $R3 >> 1 & 0x7fffffff | $R3 << 31; + $R2 = ($R2 >> 31 & 0x1 | $R2 << 1) ^ ' . \sprintf($safeint, '($t0 + $t1 + ' . $K[--$ki] . ')') . '; + + $t0 = $S0[$R2 & 0xff] ^ + $S1[$R2 >> 8 & 0xff] ^ + $S2[$R2 >> 16 & 0xff] ^ + $S3[$R2 >> 24 & 0xff]; + $t1 = $S0[$R3 >> 24 & 0xff] ^ + $S1[$R3 & 0xff] ^ + $S2[$R3 >> 8 & 0xff] ^ + $S3[$R3 >> 16 & 0xff]; + $R1^= ' . \sprintf($safeint, '$t0 + ($t1 << 1) + ' . $K[--$ki]) . '; + $R1 = $R1 >> 1 & 0x7fffffff | $R1 << 31; + $R0 = ($R0 >> 31 & 0x1 | $R0 << 1) ^ ' . \sprintf($safeint, '($t0 + $t1 + ' . $K[--$ki] . ')') . '; + '; + } + $decrypt_block .= ' + $in = pack("V4", ' . $K[0] . ' ^ $R2, + ' . $K[1] . ' ^ $R3, + ' . $K[2] . ' ^ $R0, + ' . $K[3] . ' ^ $R1); + '; + $this->inline_crypt = $this->createInlineCryptFunction(['init_crypt' => $init_crypt, 'init_encrypt' => '', 'init_decrypt' => '', 'encrypt_block' => $encrypt_block, 'decrypt_block' => $decrypt_block]); + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Exception/BadConfigurationException.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Exception/BadConfigurationException.php new file mode 100644 index 0000000..dbb7f32 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Exception/BadConfigurationException.php @@ -0,0 +1,22 @@ + + * @copyright 2015 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\Exception; + +/** + * BadConfigurationException + * + * @author Jim Wigginton + */ +class BadConfigurationException extends \RuntimeException +{ +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Exception/BadDecryptionException.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Exception/BadDecryptionException.php new file mode 100644 index 0000000..91ee6ef --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Exception/BadDecryptionException.php @@ -0,0 +1,22 @@ + + * @copyright 2015 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\Exception; + +/** + * BadDecryptionException + * + * @author Jim Wigginton + */ +class BadDecryptionException extends \RuntimeException +{ +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Exception/BadModeException.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Exception/BadModeException.php new file mode 100644 index 0000000..1613532 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Exception/BadModeException.php @@ -0,0 +1,22 @@ + + * @copyright 2015 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\Exception; + +/** + * BadModeException + * + * @author Jim Wigginton + */ +class BadModeException extends \RuntimeException +{ +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Exception/ConnectionClosedException.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Exception/ConnectionClosedException.php new file mode 100644 index 0000000..b881262 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Exception/ConnectionClosedException.php @@ -0,0 +1,22 @@ + + * @copyright 2015 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\Exception; + +/** + * ConnectionClosedException + * + * @author Jim Wigginton + */ +class ConnectionClosedException extends \RuntimeException +{ +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Exception/FileNotFoundException.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Exception/FileNotFoundException.php new file mode 100644 index 0000000..604e79f --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Exception/FileNotFoundException.php @@ -0,0 +1,22 @@ + + * @copyright 2015 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\Exception; + +/** + * FileNotFoundException + * + * @author Jim Wigginton + */ +class FileNotFoundException extends \RuntimeException +{ +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Exception/InconsistentSetupException.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Exception/InconsistentSetupException.php new file mode 100644 index 0000000..60b9796 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Exception/InconsistentSetupException.php @@ -0,0 +1,22 @@ + + * @copyright 2015 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\Exception; + +/** + * InconsistentSetupException + * + * @author Jim Wigginton + */ +class InconsistentSetupException extends \RuntimeException +{ +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Exception/InsufficientSetupException.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Exception/InsufficientSetupException.php new file mode 100644 index 0000000..6f1ca7c --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Exception/InsufficientSetupException.php @@ -0,0 +1,22 @@ + + * @copyright 2015 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\Exception; + +/** + * InsufficientSetupException + * + * @author Jim Wigginton + */ +class InsufficientSetupException extends \RuntimeException +{ +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Exception/InvalidPacketLengthException.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Exception/InvalidPacketLengthException.php new file mode 100644 index 0000000..bf1567d --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Exception/InvalidPacketLengthException.php @@ -0,0 +1,10 @@ + + * @copyright 2015 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\Exception; + +/** + * NoKeyLoadedException + * + * @author Jim Wigginton + */ +class NoKeyLoadedException extends \RuntimeException +{ +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Exception/NoSupportedAlgorithmsException.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Exception/NoSupportedAlgorithmsException.php new file mode 100644 index 0000000..c653bca --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Exception/NoSupportedAlgorithmsException.php @@ -0,0 +1,22 @@ + + * @copyright 2015 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\Exception; + +/** + * NoSupportedAlgorithmsException + * + * @author Jim Wigginton + */ +class NoSupportedAlgorithmsException extends \RuntimeException +{ +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Exception/TimeoutException.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Exception/TimeoutException.php new file mode 100644 index 0000000..0199e06 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Exception/TimeoutException.php @@ -0,0 +1,10 @@ + + * @copyright 2015 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\Exception; + +/** + * UnableToConnectException + * + * @author Jim Wigginton + */ +class UnableToConnectException extends \RuntimeException +{ +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Exception/UnsupportedAlgorithmException.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Exception/UnsupportedAlgorithmException.php new file mode 100644 index 0000000..ea0d8ea --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Exception/UnsupportedAlgorithmException.php @@ -0,0 +1,22 @@ + + * @copyright 2015 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\Exception; + +/** + * UnsupportedAlgorithmException + * + * @author Jim Wigginton + */ +class UnsupportedAlgorithmException extends \RuntimeException +{ +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Exception/UnsupportedCurveException.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Exception/UnsupportedCurveException.php new file mode 100644 index 0000000..ca383b0 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Exception/UnsupportedCurveException.php @@ -0,0 +1,22 @@ + + * @copyright 2015 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\Exception; + +/** + * UnsupportedCurveException + * + * @author Jim Wigginton + */ +class UnsupportedCurveException extends \RuntimeException +{ +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Exception/UnsupportedFormatException.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Exception/UnsupportedFormatException.php new file mode 100644 index 0000000..9590814 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Exception/UnsupportedFormatException.php @@ -0,0 +1,22 @@ + + * @copyright 2015 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\Exception; + +/** + * UnsupportedFormatException + * + * @author Jim Wigginton + */ +class UnsupportedFormatException extends \RuntimeException +{ +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Exception/UnsupportedOperationException.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Exception/UnsupportedOperationException.php new file mode 100644 index 0000000..877d938 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Exception/UnsupportedOperationException.php @@ -0,0 +1,22 @@ + + * @copyright 2015 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\Exception; + +/** + * UnsupportedOperationException + * + * @author Jim Wigginton + */ +class UnsupportedOperationException extends \RuntimeException +{ +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ANSI.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ANSI.php new file mode 100644 index 0000000..ea2c00e --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ANSI.php @@ -0,0 +1,553 @@ + + * @copyright 2012 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\File; + +/** + * Pure-PHP ANSI Decoder + * + * @author Jim Wigginton + */ +class ANSI +{ + /** + * Max Width + * + * @var int + */ + private $max_x; + /** + * Max Height + * + * @var int + */ + private $max_y; + /** + * Max History + * + * @var int + */ + private $max_history; + /** + * History + * + * @var array + */ + private $history; + /** + * History Attributes + * + * @var array + */ + private $history_attrs; + /** + * Current Column + * + * @var int + */ + private $x; + /** + * Current Row + * + * @var int + */ + private $y; + /** + * Old Column + * + * @var int + */ + private $old_x; + /** + * Old Row + * + * @var int + */ + private $old_y; + /** + * An empty attribute cell + * + * @var object + */ + private $base_attr_cell; + /** + * The current attribute cell + * + * @var object + */ + private $attr_cell; + /** + * An empty attribute row + * + * @var array + */ + private $attr_row; + /** + * The current screen text + * + * @var list + */ + private $screen; + /** + * The current screen attributes + * + * @var array + */ + private $attrs; + /** + * Current ANSI code + * + * @var string + */ + private $ansi; + /** + * Tokenization + * + * @var array + */ + private $tokenization; + /** + * Default Constructor. + * + * @return ANSI + */ + public function __construct() + { + $attr_cell = new \stdClass(); + $attr_cell->bold = \false; + $attr_cell->underline = \false; + $attr_cell->blink = \false; + $attr_cell->background = 'black'; + $attr_cell->foreground = 'white'; + $attr_cell->reverse = \false; + $this->base_attr_cell = clone $attr_cell; + $this->attr_cell = clone $attr_cell; + $this->setHistory(200); + $this->setDimensions(80, 24); + } + /** + * Set terminal width and height + * + * Resets the screen as well + * + * @param int $x + * @param int $y + */ + public function setDimensions($x, $y) + { + $this->max_x = $x - 1; + $this->max_y = $y - 1; + $this->x = $this->y = 0; + $this->history = $this->history_attrs = []; + $this->attr_row = \array_fill(0, $this->max_x + 2, $this->base_attr_cell); + $this->screen = \array_fill(0, $this->max_y + 1, ''); + $this->attrs = \array_fill(0, $this->max_y + 1, $this->attr_row); + $this->ansi = ''; + } + /** + * Set the number of lines that should be logged past the terminal height + * + * @param int $history + */ + public function setHistory($history) + { + $this->max_history = $history; + } + /** + * Load a string + * + * @param string $source + */ + public function loadString($source) + { + $this->setDimensions($this->max_x + 1, $this->max_y + 1); + $this->appendString($source); + } + /** + * Appdend a string + * + * @param string $source + */ + public function appendString($source) + { + $this->tokenization = ['']; + for ($i = 0; $i < \strlen($source); $i++) { + if (\strlen($this->ansi)) { + $this->ansi .= $source[$i]; + $chr = \ord($source[$i]); + // http://en.wikipedia.org/wiki/ANSI_escape_code#Sequence_elements + // single character CSI's not currently supported + switch (\true) { + case $this->ansi == "\x1b=": + $this->ansi = ''; + continue 2; + case \strlen($this->ansi) == 2 && $chr >= 64 && $chr <= 95 && $chr != \ord('['): + case \strlen($this->ansi) > 2 && $chr >= 64 && $chr <= 126: + break; + default: + continue 2; + } + $this->tokenization[] = $this->ansi; + $this->tokenization[] = ''; + // http://ascii-table.com/ansi-escape-sequences-vt-100.php + switch ($this->ansi) { + case "\x1b[H": + // Move cursor to upper left corner + $this->old_x = $this->x; + $this->old_y = $this->y; + $this->x = $this->y = 0; + break; + case "\x1b[J": + // Clear screen from cursor down + $this->history = \array_merge($this->history, \array_slice(\array_splice($this->screen, $this->y + 1), 0, $this->old_y)); + $this->screen = \array_merge($this->screen, \array_fill($this->y, $this->max_y, '')); + $this->history_attrs = \array_merge($this->history_attrs, \array_slice(\array_splice($this->attrs, $this->y + 1), 0, $this->old_y)); + $this->attrs = \array_merge($this->attrs, \array_fill($this->y, $this->max_y, $this->attr_row)); + if (\count($this->history) == $this->max_history) { + \array_shift($this->history); + \array_shift($this->history_attrs); + } + // fall-through + case "\x1b[K": + // Clear screen from cursor right + $this->screen[$this->y] = \substr($this->screen[$this->y], 0, $this->x); + \array_splice($this->attrs[$this->y], $this->x + 1, $this->max_x - $this->x, \array_fill($this->x, $this->max_x - ($this->x - 1), $this->base_attr_cell)); + break; + case "\x1b[2K": + // Clear entire line + $this->screen[$this->y] = \str_repeat(' ', $this->x); + $this->attrs[$this->y] = $this->attr_row; + break; + case "\x1b[?1h": + // set cursor key to application + case "\x1b[?25h": + // show the cursor + case "\x1b(B": + // set united states g0 character set + break; + case "\x1bE": + // Move to next line + $this->newLine(); + $this->x = 0; + break; + default: + switch (\true) { + case \preg_match('#\\x1B\\[(\\d+)B#', $this->ansi, $match): + // Move cursor down n lines + $this->old_y = $this->y; + $this->y += (int) $match[1]; + break; + case \preg_match('#\\x1B\\[(\\d+);(\\d+)H#', $this->ansi, $match): + // Move cursor to screen location v,h + $this->old_x = $this->x; + $this->old_y = $this->y; + $this->x = $match[2] - 1; + $this->y = (int) $match[1] - 1; + break; + case \preg_match('#\\x1B\\[(\\d+)C#', $this->ansi, $match): + // Move cursor right n lines + $this->old_x = $this->x; + $this->x += $match[1]; + break; + case \preg_match('#\\x1B\\[(\\d+)D#', $this->ansi, $match): + // Move cursor left n lines + $this->old_x = $this->x; + $this->x -= $match[1]; + if ($this->x < 0) { + $this->x = 0; + } + break; + case \preg_match('#\\x1B\\[(\\d+);(\\d+)r#', $this->ansi, $match): + // Set top and bottom lines of a window + break; + case \preg_match('#\\x1B\\[(\\d*(?:;\\d*)*)m#', $this->ansi, $match): + // character attributes + $attr_cell =& $this->attr_cell; + $mods = \explode(';', $match[1]); + foreach ($mods as $mod) { + switch ($mod) { + case '': + case '0': + // Turn off character attributes + $attr_cell = clone $this->base_attr_cell; + break; + case '1': + // Turn bold mode on + $attr_cell->bold = \true; + break; + case '4': + // Turn underline mode on + $attr_cell->underline = \true; + break; + case '5': + // Turn blinking mode on + $attr_cell->blink = \true; + break; + case '7': + // Turn reverse video on + $attr_cell->reverse = !$attr_cell->reverse; + $temp = $attr_cell->background; + $attr_cell->background = $attr_cell->foreground; + $attr_cell->foreground = $temp; + break; + default: + // set colors + //$front = $attr_cell->reverse ? &$attr_cell->background : &$attr_cell->foreground; + $front =& $attr_cell->{$attr_cell->reverse ? 'background' : 'foreground'}; + //$back = $attr_cell->reverse ? &$attr_cell->foreground : &$attr_cell->background; + $back =& $attr_cell->{$attr_cell->reverse ? 'foreground' : 'background'}; + switch ($mod) { + // @codingStandardsIgnoreStart + case '30': + $front = 'black'; + break; + case '31': + $front = 'red'; + break; + case '32': + $front = 'green'; + break; + case '33': + $front = 'yellow'; + break; + case '34': + $front = 'blue'; + break; + case '35': + $front = 'magenta'; + break; + case '36': + $front = 'cyan'; + break; + case '37': + $front = 'white'; + break; + case '40': + $back = 'black'; + break; + case '41': + $back = 'red'; + break; + case '42': + $back = 'green'; + break; + case '43': + $back = 'yellow'; + break; + case '44': + $back = 'blue'; + break; + case '45': + $back = 'magenta'; + break; + case '46': + $back = 'cyan'; + break; + case '47': + $back = 'white'; + break; + // @codingStandardsIgnoreEnd + default: + //user_error('Unsupported attribute: ' . $mod); + $this->ansi = ''; + break 2; + } + } + } + break; + default: + } + } + $this->ansi = ''; + continue; + } + $this->tokenization[\count($this->tokenization) - 1] .= $source[$i]; + switch ($source[$i]) { + case "\r": + $this->x = 0; + break; + case "\n": + $this->newLine(); + break; + case "\x08": + // backspace + if ($this->x) { + $this->x--; + $this->attrs[$this->y][$this->x] = clone $this->base_attr_cell; + $this->screen[$this->y] = \substr_replace($this->screen[$this->y], $source[$i], $this->x, 1); + } + break; + case "\x0f": + // shift + break; + case "\x1b": + // start ANSI escape code + $this->tokenization[\count($this->tokenization) - 1] = \substr($this->tokenization[\count($this->tokenization) - 1], 0, -1); + //if (!strlen($this->tokenization[count($this->tokenization) - 1])) { + // array_pop($this->tokenization); + //} + $this->ansi .= "\x1b"; + break; + default: + $this->attrs[$this->y][$this->x] = clone $this->attr_cell; + if ($this->x > \strlen($this->screen[$this->y])) { + $this->screen[$this->y] = \str_repeat(' ', $this->x); + } + $this->screen[$this->y] = \substr_replace($this->screen[$this->y], $source[$i], $this->x, 1); + if ($this->x > $this->max_x) { + $this->x = 0; + $this->newLine(); + } else { + $this->x++; + } + } + } + } + /** + * Add a new line + * + * Also update the $this->screen and $this->history buffers + * + */ + private function newLine() + { + //if ($this->y < $this->max_y) { + // $this->y++; + //} + while ($this->y >= $this->max_y) { + $this->history = \array_merge($this->history, [\array_shift($this->screen)]); + $this->screen[] = ''; + $this->history_attrs = \array_merge($this->history_attrs, [\array_shift($this->attrs)]); + $this->attrs[] = $this->attr_row; + if (\count($this->history) >= $this->max_history) { + \array_shift($this->history); + \array_shift($this->history_attrs); + } + $this->y--; + } + $this->y++; + } + /** + * Returns the current coordinate without preformating + * + * @param \stdClass $last_attr + * @param \stdClass $cur_attr + * @param string $char + * @return string + */ + private function processCoordinate(\stdClass $last_attr, \stdClass $cur_attr, $char) + { + $output = ''; + if ($last_attr != $cur_attr) { + $close = $open = ''; + if ($last_attr->foreground != $cur_attr->foreground) { + if ($cur_attr->foreground != 'white') { + $open .= ''; + } + if ($last_attr->foreground != 'white') { + $close = '' . $close; + } + } + if ($last_attr->background != $cur_attr->background) { + if ($cur_attr->background != 'black') { + $open .= ''; + } + if ($last_attr->background != 'black') { + $close = '' . $close; + } + } + if ($last_attr->bold != $cur_attr->bold) { + if ($cur_attr->bold) { + $open .= ''; + } else { + $close = '' . $close; + } + } + if ($last_attr->underline != $cur_attr->underline) { + if ($cur_attr->underline) { + $open .= ''; + } else { + $close = '' . $close; + } + } + if ($last_attr->blink != $cur_attr->blink) { + if ($cur_attr->blink) { + $open .= ''; + } else { + $close = '' . $close; + } + } + $output .= $close . $open; + } + $output .= \htmlspecialchars($char); + return $output; + } + /** + * Returns the current screen without preformating + * + * @return string + */ + private function getScreenHelper() + { + $output = ''; + $last_attr = $this->base_attr_cell; + for ($i = 0; $i <= $this->max_y; $i++) { + for ($j = 0; $j <= $this->max_x; $j++) { + $cur_attr = $this->attrs[$i][$j]; + $output .= $this->processCoordinate($last_attr, $cur_attr, isset($this->screen[$i][$j]) ? $this->screen[$i][$j] : ''); + $last_attr = $this->attrs[$i][$j]; + } + $output .= "\r\n"; + } + $output = \substr($output, 0, -2); + // close any remaining open tags + $output .= $this->processCoordinate($last_attr, $this->base_attr_cell, ''); + return \rtrim($output); + } + /** + * Returns the current screen + * + * @return string + */ + public function getScreen() + { + return '
    ' . $this->getScreenHelper() . '
    '; + } + /** + * Returns the current screen and the x previous lines + * + * @return string + */ + public function getHistory() + { + $scrollback = ''; + $last_attr = $this->base_attr_cell; + for ($i = 0; $i < \count($this->history); $i++) { + for ($j = 0; $j <= $this->max_x + 1; $j++) { + $cur_attr = $this->history_attrs[$i][$j]; + $scrollback .= $this->processCoordinate($last_attr, $cur_attr, isset($this->history[$i][$j]) ? $this->history[$i][$j] : ''); + $last_attr = $this->history_attrs[$i][$j]; + } + $scrollback .= "\r\n"; + } + $base_attr_cell = $this->base_attr_cell; + $this->base_attr_cell = $last_attr; + $scrollback .= $this->getScreen(); + $this->base_attr_cell = $base_attr_cell; + return '
    ' . $scrollback . '
    '; + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1.php new file mode 100644 index 0000000..42d8e01 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1.php @@ -0,0 +1,1398 @@ + + * @copyright 2012 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\File; + +use FluentSmtpLib\phpseclib3\Common\Functions\Strings; +use FluentSmtpLib\phpseclib3\File\ASN1\Element; +use FluentSmtpLib\phpseclib3\Math\BigInteger; +/** + * Pure-PHP ASN.1 Parser + * + * @author Jim Wigginton + */ +abstract class ASN1 +{ + // Tag Classes + // http://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf#page=12 + const CLASS_UNIVERSAL = 0; + const CLASS_APPLICATION = 1; + const CLASS_CONTEXT_SPECIFIC = 2; + const CLASS_PRIVATE = 3; + // Tag Classes + // http://www.obj-sys.com/asn1tutorial/node124.html + const TYPE_BOOLEAN = 1; + const TYPE_INTEGER = 2; + const TYPE_BIT_STRING = 3; + const TYPE_OCTET_STRING = 4; + const TYPE_NULL = 5; + const TYPE_OBJECT_IDENTIFIER = 6; + //const TYPE_OBJECT_DESCRIPTOR = 7; + //const TYPE_INSTANCE_OF = 8; // EXTERNAL + const TYPE_REAL = 9; + const TYPE_ENUMERATED = 10; + //const TYPE_EMBEDDED = 11; + const TYPE_UTF8_STRING = 12; + //const TYPE_RELATIVE_OID = 13; + const TYPE_SEQUENCE = 16; + // SEQUENCE OF + const TYPE_SET = 17; + // SET OF + // More Tag Classes + // http://www.obj-sys.com/asn1tutorial/node10.html + const TYPE_NUMERIC_STRING = 18; + const TYPE_PRINTABLE_STRING = 19; + const TYPE_TELETEX_STRING = 20; + // T61String + const TYPE_VIDEOTEX_STRING = 21; + const TYPE_IA5_STRING = 22; + const TYPE_UTC_TIME = 23; + const TYPE_GENERALIZED_TIME = 24; + const TYPE_GRAPHIC_STRING = 25; + const TYPE_VISIBLE_STRING = 26; + // ISO646String + const TYPE_GENERAL_STRING = 27; + const TYPE_UNIVERSAL_STRING = 28; + //const TYPE_CHARACTER_STRING = 29; + const TYPE_BMP_STRING = 30; + // Tag Aliases + // These tags are kinda place holders for other tags. + const TYPE_CHOICE = -1; + const TYPE_ANY = -2; + /** + * ASN.1 object identifiers + * + * @var array + * @link http://en.wikipedia.org/wiki/Object_identifier + */ + private static $oids = []; + /** + * ASN.1 object identifier reverse mapping + * + * @var array + */ + private static $reverseOIDs = []; + /** + * Default date format + * + * @var string + * @link http://php.net/class.datetime + */ + private static $format = 'D, d M Y H:i:s O'; + /** + * Filters + * + * If the mapping type is self::TYPE_ANY what do we actually encode it as? + * + * @var array + * @see self::encode_der() + */ + private static $filters; + /** + * Current Location of most recent ASN.1 encode process + * + * Useful for debug purposes + * + * @var array + * @see self::encode_der() + */ + private static $location; + /** + * DER Encoded String + * + * In case we need to create ASN1\Element object's.. + * + * @var string + * @see self::decodeDER() + */ + private static $encoded; + /** + * Type mapping table for the ANY type. + * + * Structured or unknown types are mapped to a \phpseclib3\File\ASN1\Element. + * Unambiguous types get the direct mapping (int/real/bool). + * Others are mapped as a choice, with an extra indexing level. + * + * @var array + */ + const ANY_MAP = [ + self::TYPE_BOOLEAN => \true, + self::TYPE_INTEGER => \true, + self::TYPE_BIT_STRING => 'bitString', + self::TYPE_OCTET_STRING => 'octetString', + self::TYPE_NULL => 'null', + self::TYPE_OBJECT_IDENTIFIER => 'objectIdentifier', + self::TYPE_REAL => \true, + self::TYPE_ENUMERATED => 'enumerated', + self::TYPE_UTF8_STRING => 'utf8String', + self::TYPE_NUMERIC_STRING => 'numericString', + self::TYPE_PRINTABLE_STRING => 'printableString', + self::TYPE_TELETEX_STRING => 'teletexString', + self::TYPE_VIDEOTEX_STRING => 'videotexString', + self::TYPE_IA5_STRING => 'ia5String', + self::TYPE_UTC_TIME => 'utcTime', + self::TYPE_GENERALIZED_TIME => 'generalTime', + self::TYPE_GRAPHIC_STRING => 'graphicString', + self::TYPE_VISIBLE_STRING => 'visibleString', + self::TYPE_GENERAL_STRING => 'generalString', + self::TYPE_UNIVERSAL_STRING => 'universalString', + //self::TYPE_CHARACTER_STRING => 'characterString', + self::TYPE_BMP_STRING => 'bmpString', + ]; + /** + * String type to character size mapping table. + * + * Non-convertable types are absent from this table. + * size == 0 indicates variable length encoding. + * + * @var array + */ + const STRING_TYPE_SIZE = [self::TYPE_UTF8_STRING => 0, self::TYPE_BMP_STRING => 2, self::TYPE_UNIVERSAL_STRING => 4, self::TYPE_PRINTABLE_STRING => 1, self::TYPE_TELETEX_STRING => 1, self::TYPE_IA5_STRING => 1, self::TYPE_VISIBLE_STRING => 1]; + /** + * Parse BER-encoding + * + * Serves a similar purpose to openssl's asn1parse + * + * @param Element|string $encoded + * @return ?array + */ + public static function decodeBER($encoded) + { + if ($encoded instanceof \FluentSmtpLib\phpseclib3\File\ASN1\Element) { + $encoded = $encoded->element; + } + self::$encoded = $encoded; + $decoded = self::decode_ber($encoded); + if ($decoded === \false) { + return null; + } + return [$decoded]; + } + /** + * Parse BER-encoding (Helper function) + * + * Sometimes we want to get the BER encoding of a particular tag. $start lets us do that without having to reencode. + * $encoded is passed by reference for the recursive calls done for self::TYPE_BIT_STRING and + * self::TYPE_OCTET_STRING. In those cases, the indefinite length is used. + * + * @param string $encoded + * @param int $start + * @param int $encoded_pos + * @return array|bool + */ + private static function decode_ber($encoded, $start = 0, $encoded_pos = 0) + { + $current = ['start' => $start]; + if (!isset($encoded[$encoded_pos])) { + return \false; + } + $type = \ord($encoded[$encoded_pos++]); + $startOffset = 1; + $constructed = $type >> 5 & 1; + $tag = $type & 0x1f; + if ($tag == 0x1f) { + $tag = 0; + // process septets (since the eighth bit is ignored, it's not an octet) + do { + if (!isset($encoded[$encoded_pos])) { + return \false; + } + $temp = \ord($encoded[$encoded_pos++]); + $startOffset++; + $loop = $temp >> 7; + $tag <<= 7; + $temp &= 0x7f; + // "bits 7 to 1 of the first subsequent octet shall not all be zero" + if ($startOffset == 2 && $temp == 0) { + return \false; + } + $tag |= $temp; + } while ($loop); + } + $start += $startOffset; + // Length, as discussed in paragraph 8.1.3 of X.690-0207.pdf#page=13 + if (!isset($encoded[$encoded_pos])) { + return \false; + } + $length = \ord($encoded[$encoded_pos++]); + $start++; + if ($length == 0x80) { + // indefinite length + // "[A sender shall] use the indefinite form (see 8.1.3.6) if the encoding is constructed and is not all + // immediately available." -- paragraph 8.1.3.2.c + $length = \strlen($encoded) - $encoded_pos; + } elseif ($length & 0x80) { + // definite length, long form + // technically, the long form of the length can be represented by up to 126 octets (bytes), but we'll only + // support it up to four. + $length &= 0x7f; + $temp = \substr($encoded, $encoded_pos, $length); + $encoded_pos += $length; + // tags of indefinte length don't really have a header length; this length includes the tag + $current += ['headerlength' => $length + 2]; + $start += $length; + \extract(\unpack('Nlength', \substr(\str_pad($temp, 4, \chr(0), \STR_PAD_LEFT), -4))); + /** @var integer $length */ + } else { + $current += ['headerlength' => 2]; + } + if ($length > \strlen($encoded) - $encoded_pos) { + return \false; + } + $content = \substr($encoded, $encoded_pos, $length); + $content_pos = 0; + // at this point $length can be overwritten. it's only accurate for definite length things as is + /* Class is UNIVERSAL, APPLICATION, PRIVATE, or CONTEXT-SPECIFIC. The UNIVERSAL class is restricted to the ASN.1 + built-in types. It defines an application-independent data type that must be distinguishable from all other + data types. The other three classes are user defined. The APPLICATION class distinguishes data types that + have a wide, scattered use within a particular presentation context. PRIVATE distinguishes data types within + a particular organization or country. CONTEXT-SPECIFIC distinguishes members of a sequence or set, the + alternatives of a CHOICE, or universally tagged set members. Only the class number appears in braces for this + data type; the term CONTEXT-SPECIFIC does not appear. + + -- http://www.obj-sys.com/asn1tutorial/node12.html */ + $class = $type >> 6 & 3; + switch ($class) { + case self::CLASS_APPLICATION: + case self::CLASS_PRIVATE: + case self::CLASS_CONTEXT_SPECIFIC: + if (!$constructed) { + return ['type' => $class, 'constant' => $tag, 'content' => $content, 'length' => $length + $start - $current['start']] + $current; + } + $newcontent = []; + $remainingLength = $length; + while ($remainingLength > 0) { + $temp = self::decode_ber($content, $start, $content_pos); + if ($temp === \false) { + break; + } + $length = $temp['length']; + // end-of-content octets - see paragraph 8.1.5 + if (\substr($content, $content_pos + $length, 2) == "\x00\x00") { + $length += 2; + $start += $length; + $newcontent[] = $temp; + break; + } + $start += $length; + $remainingLength -= $length; + $newcontent[] = $temp; + $content_pos += $length; + } + return [ + 'type' => $class, + 'constant' => $tag, + // the array encapsulation is for BC with the old format + 'content' => $newcontent, + // the only time when $content['headerlength'] isn't defined is when the length is indefinite. + // the absence of $content['headerlength'] is how we know if something is indefinite or not. + // technically, it could be defined to be 2 and then another indicator could be used but whatever. + 'length' => $start - $current['start'], + ] + $current; + } + $current += ['type' => $tag]; + // decode UNIVERSAL tags + switch ($tag) { + case self::TYPE_BOOLEAN: + // "The contents octets shall consist of a single octet." -- paragraph 8.2.1 + if ($constructed || \strlen($content) != 1) { + return \false; + } + $current['content'] = (bool) \ord($content[$content_pos]); + break; + case self::TYPE_INTEGER: + case self::TYPE_ENUMERATED: + if ($constructed) { + return \false; + } + $current['content'] = new \FluentSmtpLib\phpseclib3\Math\BigInteger(\substr($content, $content_pos), -256); + break; + case self::TYPE_REAL: + // not currently supported + return \false; + case self::TYPE_BIT_STRING: + // The initial octet shall encode, as an unsigned binary integer with bit 1 as the least significant bit, + // the number of unused bits in the final subsequent octet. The number shall be in the range zero to + // seven. + if (!$constructed) { + $current['content'] = \substr($content, $content_pos); + } else { + $temp = self::decode_ber($content, $start, $content_pos); + if ($temp === \false) { + return \false; + } + $length -= \strlen($content) - $content_pos; + $last = \count($temp) - 1; + for ($i = 0; $i < $last; $i++) { + // all subtags should be bit strings + if ($temp[$i]['type'] != self::TYPE_BIT_STRING) { + return \false; + } + $current['content'] .= \substr($temp[$i]['content'], 1); + } + // all subtags should be bit strings + if ($temp[$last]['type'] != self::TYPE_BIT_STRING) { + return \false; + } + $current['content'] = $temp[$last]['content'][0] . $current['content'] . \substr($temp[$i]['content'], 1); + } + break; + case self::TYPE_OCTET_STRING: + if (!$constructed) { + $current['content'] = \substr($content, $content_pos); + } else { + $current['content'] = ''; + $length = 0; + while (\substr($content, $content_pos, 2) != "\x00\x00") { + $temp = self::decode_ber($content, $length + $start, $content_pos); + if ($temp === \false) { + return \false; + } + $content_pos += $temp['length']; + // all subtags should be octet strings + if ($temp['type'] != self::TYPE_OCTET_STRING) { + return \false; + } + $current['content'] .= $temp['content']; + $length += $temp['length']; + } + if (\substr($content, $content_pos, 2) == "\x00\x00") { + $length += 2; + // +2 for the EOC + } + } + break; + case self::TYPE_NULL: + // "The contents octets shall not contain any octets." -- paragraph 8.8.2 + if ($constructed || \strlen($content)) { + return \false; + } + break; + case self::TYPE_SEQUENCE: + case self::TYPE_SET: + if (!$constructed) { + return \false; + } + $offset = 0; + $current['content'] = []; + $content_len = \strlen($content); + while ($content_pos < $content_len) { + // if indefinite length construction was used and we have an end-of-content string next + // see paragraphs 8.1.1.3, 8.1.3.2, 8.1.3.6, 8.1.5, and (for an example) 8.6.4.2 + if (!isset($current['headerlength']) && \substr($content, $content_pos, 2) == "\x00\x00") { + $length = $offset + 2; + // +2 for the EOC + break 2; + } + $temp = self::decode_ber($content, $start + $offset, $content_pos); + if ($temp === \false) { + return \false; + } + $content_pos += $temp['length']; + $current['content'][] = $temp; + $offset += $temp['length']; + } + break; + case self::TYPE_OBJECT_IDENTIFIER: + if ($constructed) { + return \false; + } + $current['content'] = self::decodeOID(\substr($content, $content_pos)); + if ($current['content'] === \false) { + return \false; + } + break; + /* Each character string type shall be encoded as if it had been declared: + [UNIVERSAL x] IMPLICIT OCTET STRING + + -- X.690-0207.pdf#page=23 (paragraph 8.21.3) + + Per that, we're not going to do any validation. If there are any illegal characters in the string, + we don't really care */ + case self::TYPE_NUMERIC_STRING: + // 0,1,2,3,4,5,6,7,8,9, and space + case self::TYPE_PRINTABLE_STRING: + // Upper and lower case letters, digits, space, apostrophe, left/right parenthesis, plus sign, comma, + // hyphen, full stop, solidus, colon, equal sign, question mark + case self::TYPE_TELETEX_STRING: + // The Teletex character set in CCITT's T61, space, and delete + // see http://en.wikipedia.org/wiki/Teletex#Character_sets + case self::TYPE_VIDEOTEX_STRING: + // The Videotex character set in CCITT's T.100 and T.101, space, and delete + case self::TYPE_VISIBLE_STRING: + // Printing character sets of international ASCII, and space + case self::TYPE_IA5_STRING: + // International Alphabet 5 (International ASCII) + case self::TYPE_GRAPHIC_STRING: + // All registered G sets, and space + case self::TYPE_GENERAL_STRING: + // All registered C and G sets, space and delete + case self::TYPE_UTF8_STRING: + // ???? + case self::TYPE_BMP_STRING: + if ($constructed) { + return \false; + } + $current['content'] = \substr($content, $content_pos); + break; + case self::TYPE_UTC_TIME: + case self::TYPE_GENERALIZED_TIME: + if ($constructed) { + return \false; + } + $current['content'] = self::decodeTime(\substr($content, $content_pos), $tag); + break; + default: + return \false; + } + $start += $length; + // ie. length is the length of the full TLV encoding - it's not just the length of the value + return $current + ['length' => $start - $current['start']]; + } + /** + * ASN.1 Map + * + * Provides an ASN.1 semantic mapping ($mapping) from a parsed BER-encoding to a human readable format. + * + * "Special" mappings may be applied on a per tag-name basis via $special. + * + * @param array $decoded + * @param array $mapping + * @param array $special + * @return array|bool|Element|string|null + */ + public static function asn1map(array $decoded, $mapping, $special = []) + { + if (isset($mapping['explicit']) && \is_array($decoded['content'])) { + $decoded = $decoded['content'][0]; + } + switch (\true) { + case $mapping['type'] == self::TYPE_ANY: + $intype = $decoded['type']; + // !isset(self::ANY_MAP[$intype]) produces a fatal error on PHP 5.6 + if (isset($decoded['constant']) || !\array_key_exists($intype, self::ANY_MAP) || \ord(self::$encoded[$decoded['start']]) & 0x20) { + return new \FluentSmtpLib\phpseclib3\File\ASN1\Element(\substr(self::$encoded, $decoded['start'], $decoded['length'])); + } + $inmap = self::ANY_MAP[$intype]; + if (\is_string($inmap)) { + return [$inmap => self::asn1map($decoded, ['type' => $intype] + $mapping, $special)]; + } + break; + case $mapping['type'] == self::TYPE_CHOICE: + foreach ($mapping['children'] as $key => $option) { + switch (\true) { + case isset($option['constant']) && $option['constant'] == $decoded['constant']: + case !isset($option['constant']) && $option['type'] == $decoded['type']: + $value = self::asn1map($decoded, $option, $special); + break; + case !isset($option['constant']) && $option['type'] == self::TYPE_CHOICE: + $v = self::asn1map($decoded, $option, $special); + if (isset($v)) { + $value = $v; + } + } + if (isset($value)) { + if (isset($special[$key])) { + $value = $special[$key]($value); + } + return [$key => $value]; + } + } + return null; + case isset($mapping['implicit']): + case isset($mapping['explicit']): + case $decoded['type'] == $mapping['type']: + break; + default: + // if $decoded['type'] and $mapping['type'] are both strings, but different types of strings, + // let it through + switch (\true) { + case $decoded['type'] < 18: + // self::TYPE_NUMERIC_STRING == 18 + case $decoded['type'] > 30: + // self::TYPE_BMP_STRING == 30 + case $mapping['type'] < 18: + case $mapping['type'] > 30: + return null; + } + } + if (isset($mapping['implicit'])) { + $decoded['type'] = $mapping['type']; + } + switch ($decoded['type']) { + case self::TYPE_SEQUENCE: + $map = []; + // ignore the min and max + if (isset($mapping['min']) && isset($mapping['max'])) { + $child = $mapping['children']; + foreach ($decoded['content'] as $content) { + if (($map[] = self::asn1map($content, $child, $special)) === null) { + return null; + } + } + return $map; + } + $n = \count($decoded['content']); + $i = 0; + foreach ($mapping['children'] as $key => $child) { + $maymatch = $i < $n; + // Match only existing input. + if ($maymatch) { + $temp = $decoded['content'][$i]; + if ($child['type'] != self::TYPE_CHOICE) { + // Get the mapping and input class & constant. + $childClass = $tempClass = self::CLASS_UNIVERSAL; + $constant = null; + if (isset($temp['constant'])) { + $tempClass = $temp['type']; + } + if (isset($child['class'])) { + $childClass = $child['class']; + $constant = $child['cast']; + } elseif (isset($child['constant'])) { + $childClass = self::CLASS_CONTEXT_SPECIFIC; + $constant = $child['constant']; + } + if (isset($constant) && isset($temp['constant'])) { + // Can only match if constants and class match. + $maymatch = $constant == $temp['constant'] && $childClass == $tempClass; + } else { + // Can only match if no constant expected and type matches or is generic. + $maymatch = !isset($child['constant']) && \array_search($child['type'], [$temp['type'], self::TYPE_ANY, self::TYPE_CHOICE]) !== \false; + } + } + } + if ($maymatch) { + // Attempt submapping. + $candidate = self::asn1map($temp, $child, $special); + $maymatch = $candidate !== null; + } + if ($maymatch) { + // Got the match: use it. + if (isset($special[$key])) { + $candidate = $special[$key]($candidate); + } + $map[$key] = $candidate; + $i++; + } elseif (isset($child['default'])) { + $map[$key] = $child['default']; + } elseif (!isset($child['optional'])) { + return null; + // Syntax error. + } + } + // Fail mapping if all input items have not been consumed. + return $i < $n ? null : $map; + // the main diff between sets and sequences is the encapsulation of the foreach in another for loop + case self::TYPE_SET: + $map = []; + // ignore the min and max + if (isset($mapping['min']) && isset($mapping['max'])) { + $child = $mapping['children']; + foreach ($decoded['content'] as $content) { + if (($map[] = self::asn1map($content, $child, $special)) === null) { + return null; + } + } + return $map; + } + for ($i = 0; $i < \count($decoded['content']); $i++) { + $temp = $decoded['content'][$i]; + $tempClass = self::CLASS_UNIVERSAL; + if (isset($temp['constant'])) { + $tempClass = $temp['type']; + } + foreach ($mapping['children'] as $key => $child) { + if (isset($map[$key])) { + continue; + } + $maymatch = \true; + if ($child['type'] != self::TYPE_CHOICE) { + $childClass = self::CLASS_UNIVERSAL; + $constant = null; + if (isset($child['class'])) { + $childClass = $child['class']; + $constant = $child['cast']; + } elseif (isset($child['constant'])) { + $childClass = self::CLASS_CONTEXT_SPECIFIC; + $constant = $child['constant']; + } + if (isset($constant) && isset($temp['constant'])) { + // Can only match if constants and class match. + $maymatch = $constant == $temp['constant'] && $childClass == $tempClass; + } else { + // Can only match if no constant expected and type matches or is generic. + $maymatch = !isset($child['constant']) && \array_search($child['type'], [$temp['type'], self::TYPE_ANY, self::TYPE_CHOICE]) !== \false; + } + } + if ($maymatch) { + // Attempt submapping. + $candidate = self::asn1map($temp, $child, $special); + $maymatch = $candidate !== null; + } + if (!$maymatch) { + break; + } + // Got the match: use it. + if (isset($special[$key])) { + $candidate = $special[$key]($candidate); + } + $map[$key] = $candidate; + break; + } + } + foreach ($mapping['children'] as $key => $child) { + if (!isset($map[$key])) { + if (isset($child['default'])) { + $map[$key] = $child['default']; + } elseif (!isset($child['optional'])) { + return null; + } + } + } + return $map; + case self::TYPE_OBJECT_IDENTIFIER: + return isset(self::$oids[$decoded['content']]) ? self::$oids[$decoded['content']] : $decoded['content']; + case self::TYPE_UTC_TIME: + case self::TYPE_GENERALIZED_TIME: + // for explicitly tagged optional stuff + if (\is_array($decoded['content'])) { + $decoded['content'] = $decoded['content'][0]['content']; + } + // for implicitly tagged optional stuff + // in theory, doing isset($mapping['implicit']) would work but malformed certs do exist + // in the wild that OpenSSL decodes without issue so we'll support them as well + if (!\is_object($decoded['content'])) { + $decoded['content'] = self::decodeTime($decoded['content'], $decoded['type']); + } + return $decoded['content'] ? $decoded['content']->format(self::$format) : \false; + case self::TYPE_BIT_STRING: + if (isset($mapping['mapping'])) { + $offset = \ord($decoded['content'][0]); + $size = (\strlen($decoded['content']) - 1) * 8 - $offset; + /* + From X.680-0207.pdf#page=46 (21.7): + + "When a "NamedBitList" is used in defining a bitstring type ASN.1 encoding rules are free to add (or remove) + arbitrarily any trailing 0 bits to (or from) values that are being encoded or decoded. Application designers should + therefore ensure that different semantics are not associated with such values which differ only in the number of trailing + 0 bits." + */ + $bits = \count($mapping['mapping']) == $size ? [] : \array_fill(0, \count($mapping['mapping']) - $size, \false); + for ($i = \strlen($decoded['content']) - 1; $i > 0; $i--) { + $current = \ord($decoded['content'][$i]); + for ($j = $offset; $j < 8; $j++) { + $bits[] = (bool) ($current & 1 << $j); + } + $offset = 0; + } + $values = []; + $map = \array_reverse($mapping['mapping']); + foreach ($map as $i => $value) { + if ($bits[$i]) { + $values[] = $value; + } + } + return $values; + } + // fall-through + case self::TYPE_OCTET_STRING: + return $decoded['content']; + case self::TYPE_NULL: + return ''; + case self::TYPE_BOOLEAN: + case self::TYPE_NUMERIC_STRING: + case self::TYPE_PRINTABLE_STRING: + case self::TYPE_TELETEX_STRING: + case self::TYPE_VIDEOTEX_STRING: + case self::TYPE_IA5_STRING: + case self::TYPE_GRAPHIC_STRING: + case self::TYPE_VISIBLE_STRING: + case self::TYPE_GENERAL_STRING: + case self::TYPE_UNIVERSAL_STRING: + case self::TYPE_UTF8_STRING: + case self::TYPE_BMP_STRING: + return $decoded['content']; + case self::TYPE_INTEGER: + case self::TYPE_ENUMERATED: + $temp = $decoded['content']; + if (isset($mapping['implicit'])) { + $temp = new \FluentSmtpLib\phpseclib3\Math\BigInteger($decoded['content'], -256); + } + if (isset($mapping['mapping'])) { + $temp = (int) $temp->toString(); + return isset($mapping['mapping'][$temp]) ? $mapping['mapping'][$temp] : \false; + } + return $temp; + } + } + /** + * DER-decode the length + * + * DER supports lengths up to (2**8)**127, however, we'll only support lengths up to (2**8)**4. See + * {@link http://itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf#p=13 X.690 paragraph 8.1.3} for more information. + * + * @param string $string + * @return int + */ + public static function decodeLength(&$string) + { + $length = \ord(\FluentSmtpLib\phpseclib3\Common\Functions\Strings::shift($string)); + if ($length & 0x80) { + // definite length, long form + $length &= 0x7f; + $temp = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::shift($string, $length); + list(, $length) = \unpack('N', \substr(\str_pad($temp, 4, \chr(0), \STR_PAD_LEFT), -4)); + } + return $length; + } + /** + * ASN.1 Encode + * + * DER-encodes an ASN.1 semantic mapping ($mapping). Some libraries would probably call this function + * an ASN.1 compiler. + * + * "Special" mappings can be applied via $special. + * + * @param Element|string|array $source + * @param array $mapping + * @param array $special + * @return string + */ + public static function encodeDER($source, $mapping, $special = []) + { + self::$location = []; + return self::encode_der($source, $mapping, null, $special); + } + /** + * ASN.1 Encode (Helper function) + * + * @param Element|string|array|null $source + * @param array $mapping + * @param int $idx + * @param array $special + * @return string + */ + private static function encode_der($source, array $mapping, $idx = null, array $special = []) + { + if ($source instanceof \FluentSmtpLib\phpseclib3\File\ASN1\Element) { + return $source->element; + } + // do not encode (implicitly optional) fields with value set to default + if (isset($mapping['default']) && $source === $mapping['default']) { + return ''; + } + if (isset($idx)) { + if (isset($special[$idx])) { + $source = $special[$idx]($source); + } + self::$location[] = $idx; + } + $tag = $mapping['type']; + switch ($tag) { + case self::TYPE_SET: + // Children order is not important, thus process in sequence. + case self::TYPE_SEQUENCE: + $tag |= 0x20; + // set the constructed bit + // ignore the min and max + if (isset($mapping['min']) && isset($mapping['max'])) { + $value = []; + $child = $mapping['children']; + foreach ($source as $content) { + $temp = self::encode_der($content, $child, null, $special); + if ($temp === \false) { + return \false; + } + $value[] = $temp; + } + /* "The encodings of the component values of a set-of value shall appear in ascending order, the encodings being compared + as octet strings with the shorter components being padded at their trailing end with 0-octets. + NOTE - The padding octets are for comparison purposes only and do not appear in the encodings." + + -- sec 11.6 of http://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf */ + if ($mapping['type'] == self::TYPE_SET) { + \sort($value); + } + $value = \implode('', $value); + break; + } + $value = ''; + foreach ($mapping['children'] as $key => $child) { + if (!\array_key_exists($key, $source)) { + if (!isset($child['optional'])) { + return \false; + } + continue; + } + $temp = self::encode_der($source[$key], $child, $key, $special); + if ($temp === \false) { + return \false; + } + // An empty child encoding means it has been optimized out. + // Else we should have at least one tag byte. + if ($temp === '') { + continue; + } + // if isset($child['constant']) is true then isset($child['optional']) should be true as well + if (isset($child['constant'])) { + /* + From X.680-0207.pdf#page=58 (30.6): + + "The tagging construction specifies explicit tagging if any of the following holds: + ... + c) the "Tag Type" alternative is used and the value of "TagDefault" for the module is IMPLICIT TAGS or + AUTOMATIC TAGS, but the type defined by "Type" is an untagged choice type, an untagged open type, or + an untagged "DummyReference" (see ITU-T Rec. X.683 | ISO/IEC 8824-4, 8.3)." + */ + if (isset($child['explicit']) || $child['type'] == self::TYPE_CHOICE) { + $subtag = \chr(self::CLASS_CONTEXT_SPECIFIC << 6 | 0x20 | $child['constant']); + $temp = $subtag . self::encodeLength(\strlen($temp)) . $temp; + } else { + $subtag = \chr(self::CLASS_CONTEXT_SPECIFIC << 6 | \ord($temp[0]) & 0x20 | $child['constant']); + $temp = $subtag . \substr($temp, 1); + } + } + $value .= $temp; + } + break; + case self::TYPE_CHOICE: + $temp = \false; + foreach ($mapping['children'] as $key => $child) { + if (!isset($source[$key])) { + continue; + } + $temp = self::encode_der($source[$key], $child, $key, $special); + if ($temp === \false) { + return \false; + } + // An empty child encoding means it has been optimized out. + // Else we should have at least one tag byte. + if ($temp === '') { + continue; + } + $tag = \ord($temp[0]); + // if isset($child['constant']) is true then isset($child['optional']) should be true as well + if (isset($child['constant'])) { + if (isset($child['explicit']) || $child['type'] == self::TYPE_CHOICE) { + $subtag = \chr(self::CLASS_CONTEXT_SPECIFIC << 6 | 0x20 | $child['constant']); + $temp = $subtag . self::encodeLength(\strlen($temp)) . $temp; + } else { + $subtag = \chr(self::CLASS_CONTEXT_SPECIFIC << 6 | \ord($temp[0]) & 0x20 | $child['constant']); + $temp = $subtag . \substr($temp, 1); + } + } + } + if (isset($idx)) { + \array_pop(self::$location); + } + if ($temp && isset($mapping['cast'])) { + $temp[0] = \chr($mapping['class'] << 6 | $tag & 0x20 | $mapping['cast']); + } + return $temp; + case self::TYPE_INTEGER: + case self::TYPE_ENUMERATED: + if (!isset($mapping['mapping'])) { + if (\is_numeric($source)) { + $source = new \FluentSmtpLib\phpseclib3\Math\BigInteger($source); + } + $value = $source->toBytes(\true); + } else { + $value = \array_search($source, $mapping['mapping']); + if ($value === \false) { + return \false; + } + $value = new \FluentSmtpLib\phpseclib3\Math\BigInteger($value); + $value = $value->toBytes(\true); + } + if (!\strlen($value)) { + $value = \chr(0); + } + break; + case self::TYPE_UTC_TIME: + case self::TYPE_GENERALIZED_TIME: + $format = $mapping['type'] == self::TYPE_UTC_TIME ? 'y' : 'Y'; + $format .= 'mdHis'; + // if $source does _not_ include timezone information within it then assume that the timezone is GMT + $date = new \DateTime($source, new \DateTimeZone('GMT')); + // if $source _does_ include timezone information within it then convert the time to GMT + $date->setTimezone(new \DateTimeZone('GMT')); + $value = $date->format($format) . 'Z'; + break; + case self::TYPE_BIT_STRING: + if (isset($mapping['mapping'])) { + $bits = \array_fill(0, \count($mapping['mapping']), 0); + $size = 0; + for ($i = 0; $i < \count($mapping['mapping']); $i++) { + if (\in_array($mapping['mapping'][$i], $source)) { + $bits[$i] = 1; + $size = $i; + } + } + if (isset($mapping['min']) && $mapping['min'] >= 1 && $size < $mapping['min']) { + $size = $mapping['min'] - 1; + } + $offset = 8 - ($size + 1 & 7); + $offset = $offset !== 8 ? $offset : 0; + $value = \chr($offset); + for ($i = $size + 1; $i < \count($mapping['mapping']); $i++) { + unset($bits[$i]); + } + $bits = \implode('', \array_pad($bits, $size + $offset + 1, 0)); + $bytes = \explode(' ', \rtrim(\chunk_split($bits, 8, ' '))); + foreach ($bytes as $byte) { + $value .= \chr(\bindec($byte)); + } + break; + } + // fall-through + case self::TYPE_OCTET_STRING: + /* The initial octet shall encode, as an unsigned binary integer with bit 1 as the least significant bit, + the number of unused bits in the final subsequent octet. The number shall be in the range zero to seven. + + -- http://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf#page=16 */ + $value = $source; + break; + case self::TYPE_OBJECT_IDENTIFIER: + $value = self::encodeOID($source); + break; + case self::TYPE_ANY: + $loc = self::$location; + if (isset($idx)) { + \array_pop(self::$location); + } + switch (\true) { + case !isset($source): + return self::encode_der(null, ['type' => self::TYPE_NULL] + $mapping, null, $special); + case \is_int($source): + case $source instanceof \FluentSmtpLib\phpseclib3\Math\BigInteger: + return self::encode_der($source, ['type' => self::TYPE_INTEGER] + $mapping, null, $special); + case \is_float($source): + return self::encode_der($source, ['type' => self::TYPE_REAL] + $mapping, null, $special); + case \is_bool($source): + return self::encode_der($source, ['type' => self::TYPE_BOOLEAN] + $mapping, null, $special); + case \is_array($source) && \count($source) == 1: + $typename = \implode('', \array_keys($source)); + $outtype = \array_search($typename, self::ANY_MAP, \true); + if ($outtype !== \false) { + return self::encode_der($source[$typename], ['type' => $outtype] + $mapping, null, $special); + } + } + $filters = self::$filters; + foreach ($loc as $part) { + if (!isset($filters[$part])) { + $filters = \false; + break; + } + $filters = $filters[$part]; + } + if ($filters === \false) { + throw new \RuntimeException('No filters defined for ' . \implode('/', $loc)); + } + return self::encode_der($source, $filters + $mapping, null, $special); + case self::TYPE_NULL: + $value = ''; + break; + case self::TYPE_NUMERIC_STRING: + case self::TYPE_TELETEX_STRING: + case self::TYPE_PRINTABLE_STRING: + case self::TYPE_UNIVERSAL_STRING: + case self::TYPE_UTF8_STRING: + case self::TYPE_BMP_STRING: + case self::TYPE_IA5_STRING: + case self::TYPE_VISIBLE_STRING: + case self::TYPE_VIDEOTEX_STRING: + case self::TYPE_GRAPHIC_STRING: + case self::TYPE_GENERAL_STRING: + $value = $source; + break; + case self::TYPE_BOOLEAN: + $value = $source ? "\xff" : "\x00"; + break; + default: + throw new \RuntimeException('Mapping provides no type definition for ' . \implode('/', self::$location)); + } + if (isset($idx)) { + \array_pop(self::$location); + } + if (isset($mapping['cast'])) { + if (isset($mapping['explicit']) || $mapping['type'] == self::TYPE_CHOICE) { + $value = \chr($tag) . self::encodeLength(\strlen($value)) . $value; + $tag = $mapping['class'] << 6 | 0x20 | $mapping['cast']; + } else { + $tag = $mapping['class'] << 6 | \ord($temp[0]) & 0x20 | $mapping['cast']; + } + } + return \chr($tag) . self::encodeLength(\strlen($value)) . $value; + } + /** + * BER-decode the OID + * + * Called by _decode_ber() + * + * @param string $content + * @return string + */ + public static function decodeOID($content) + { + static $eighty; + if (!$eighty) { + $eighty = new \FluentSmtpLib\phpseclib3\Math\BigInteger(80); + } + $oid = []; + $pos = 0; + $len = \strlen($content); + // see https://github.com/openjdk/jdk/blob/2deb318c9f047ec5a4b160d66a4b52f93688ec42/src/java.base/share/classes/sun/security/util/ObjectIdentifier.java#L55 + if ($len > 4096) { + //throw new \RuntimeException("Object identifier size is limited to 4096 bytes ($len bytes present)"); + return \false; + } + if (\ord($content[$len - 1]) & 0x80) { + return \false; + } + $n = new \FluentSmtpLib\phpseclib3\Math\BigInteger(); + while ($pos < $len) { + $temp = \ord($content[$pos++]); + $n = $n->bitwise_leftShift(7); + $n = $n->bitwise_or(new \FluentSmtpLib\phpseclib3\Math\BigInteger($temp & 0x7f)); + if (~$temp & 0x80) { + $oid[] = $n; + $n = new \FluentSmtpLib\phpseclib3\Math\BigInteger(); + } + } + $part1 = \array_shift($oid); + $first = \floor(\ord($content[0]) / 40); + /* + "This packing of the first two object identifier components recognizes that only three values are allocated from the root + node, and at most 39 subsequent values from nodes reached by X = 0 and X = 1." + + -- https://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf#page=22 + */ + if ($first <= 2) { + // ie. 0 <= ord($content[0]) < 120 (0x78) + \array_unshift($oid, \ord($content[0]) % 40); + \array_unshift($oid, $first); + } else { + \array_unshift($oid, $part1->subtract($eighty)); + \array_unshift($oid, 2); + } + return \implode('.', $oid); + } + /** + * DER-encode the OID + * + * Called by _encode_der() + * + * @param string $source + * @return string + */ + public static function encodeOID($source) + { + static $mask, $zero, $forty; + if (!$mask) { + $mask = new \FluentSmtpLib\phpseclib3\Math\BigInteger(0x7f); + $zero = new \FluentSmtpLib\phpseclib3\Math\BigInteger(); + $forty = new \FluentSmtpLib\phpseclib3\Math\BigInteger(40); + } + if (!\preg_match('#(?:\\d+\\.)+#', $source)) { + $oid = isset(self::$reverseOIDs[$source]) ? self::$reverseOIDs[$source] : \false; + } else { + $oid = $source; + } + if ($oid === \false) { + throw new \RuntimeException('Invalid OID'); + } + $parts = \explode('.', $oid); + $part1 = \array_shift($parts); + $part2 = \array_shift($parts); + $first = new \FluentSmtpLib\phpseclib3\Math\BigInteger($part1); + $first = $first->multiply($forty); + $first = $first->add(new \FluentSmtpLib\phpseclib3\Math\BigInteger($part2)); + \array_unshift($parts, $first->toString()); + $value = ''; + foreach ($parts as $part) { + if (!$part) { + $temp = "\x00"; + } else { + $temp = ''; + $part = new \FluentSmtpLib\phpseclib3\Math\BigInteger($part); + while (!$part->equals($zero)) { + $submask = $part->bitwise_and($mask); + $submask->setPrecision(8); + $temp = (\chr(0x80) | $submask->toBytes()) . $temp; + $part = $part->bitwise_rightShift(7); + } + $temp[\strlen($temp) - 1] = $temp[\strlen($temp) - 1] & \chr(0x7f); + } + $value .= $temp; + } + return $value; + } + /** + * BER-decode the time + * + * Called by _decode_ber() and in the case of implicit tags asn1map(). + * + * @param string $content + * @param int $tag + * @return \DateTime|false + */ + private static function decodeTime($content, $tag) + { + /* UTCTime: + http://tools.ietf.org/html/rfc5280#section-4.1.2.5.1 + http://www.obj-sys.com/asn1tutorial/node15.html + + GeneralizedTime: + http://tools.ietf.org/html/rfc5280#section-4.1.2.5.2 + http://www.obj-sys.com/asn1tutorial/node14.html */ + $format = 'YmdHis'; + if ($tag == self::TYPE_UTC_TIME) { + // https://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf#page=28 says "the seconds + // element shall always be present" but none-the-less I've seen X509 certs where it isn't and if the + // browsers parse it phpseclib ought to too + if (\preg_match('#^(\\d{10})(Z|[+-]\\d{4})$#', $content, $matches)) { + $content = $matches[1] . '00' . $matches[2]; + } + $prefix = \substr($content, 0, 2) >= 50 ? '19' : '20'; + $content = $prefix . $content; + } elseif (\strpos($content, '.') !== \false) { + $format .= '.u'; + } + if ($content[\strlen($content) - 1] == 'Z') { + $content = \substr($content, 0, -1) . '+0000'; + } + if (\strpos($content, '-') !== \false || \strpos($content, '+') !== \false) { + $format .= 'O'; + } + // error supression isn't necessary as of PHP 7.0: + // http://php.net/manual/en/migration70.other-changes.php + return @\DateTime::createFromFormat($format, $content); + } + /** + * Set the time format + * + * Sets the time / date format for asn1map(). + * + * @param string $format + */ + public static function setTimeFormat($format) + { + self::$format = $format; + } + /** + * Load OIDs + * + * Load the relevant OIDs for a particular ASN.1 semantic mapping. + * Previously loaded OIDs are retained. + * + * @param array $oids + */ + public static function loadOIDs(array $oids) + { + self::$reverseOIDs += $oids; + self::$oids = \array_flip(self::$reverseOIDs); + } + /** + * Set filters + * + * See \phpseclib3\File\X509, etc, for an example. + * Previously loaded filters are not retained. + * + * @param array $filters + */ + public static function setFilters(array $filters) + { + self::$filters = $filters; + } + /** + * String type conversion + * + * This is a lazy conversion, dealing only with character size. + * No real conversion table is used. + * + * @param string $in + * @param int $from + * @param int $to + * @return string + */ + public static function convert($in, $from = self::TYPE_UTF8_STRING, $to = self::TYPE_UTF8_STRING) + { + // isset(self::STRING_TYPE_SIZE[$from] returns a fatal error on PHP 5.6 + if (!\array_key_exists($from, self::STRING_TYPE_SIZE) || !\array_key_exists($to, self::STRING_TYPE_SIZE)) { + return \false; + } + $insize = self::STRING_TYPE_SIZE[$from]; + $outsize = self::STRING_TYPE_SIZE[$to]; + $inlength = \strlen($in); + $out = ''; + for ($i = 0; $i < $inlength;) { + if ($inlength - $i < $insize) { + return \false; + } + // Get an input character as a 32-bit value. + $c = \ord($in[$i++]); + switch (\true) { + case $insize == 4: + $c = $c << 8 | \ord($in[$i++]); + $c = $c << 8 | \ord($in[$i++]); + // fall-through + case $insize == 2: + $c = $c << 8 | \ord($in[$i++]); + // fall-through + case $insize == 1: + break; + case ($c & 0x80) == 0x0: + break; + case ($c & 0x40) == 0x0: + return \false; + default: + $bit = 6; + do { + if ($bit > 25 || $i >= $inlength || (\ord($in[$i]) & 0xc0) != 0x80) { + return \false; + } + $c = $c << 6 | \ord($in[$i++]) & 0x3f; + $bit += 5; + $mask = 1 << $bit; + } while ($c & $bit); + $c &= $mask - 1; + break; + } + // Convert and append the character to output string. + $v = ''; + switch (\true) { + case $outsize == 4: + $v .= \chr($c & 0xff); + $c >>= 8; + $v .= \chr($c & 0xff); + $c >>= 8; + // fall-through + case $outsize == 2: + $v .= \chr($c & 0xff); + $c >>= 8; + // fall-through + case $outsize == 1: + $v .= \chr($c & 0xff); + $c >>= 8; + if ($c) { + return \false; + } + break; + case ($c & (\PHP_INT_SIZE == 8 ? 0x80000000 : 1 << 31)) != 0: + return \false; + case $c >= 0x4000000: + $v .= \chr(0x80 | $c & 0x3f); + $c = $c >> 6 | 0x4000000; + // fall-through + case $c >= 0x200000: + $v .= \chr(0x80 | $c & 0x3f); + $c = $c >> 6 | 0x200000; + // fall-through + case $c >= 0x10000: + $v .= \chr(0x80 | $c & 0x3f); + $c = $c >> 6 | 0x10000; + // fall-through + case $c >= 0x800: + $v .= \chr(0x80 | $c & 0x3f); + $c = $c >> 6 | 0x800; + // fall-through + case $c >= 0x80: + $v .= \chr(0x80 | $c & 0x3f); + $c = $c >> 6 | 0xc0; + // fall-through + default: + $v .= \chr($c); + break; + } + $out .= \strrev($v); + } + return $out; + } + /** + * Extract raw BER from Base64 encoding + * + * @param string $str + * @return string + */ + public static function extractBER($str) + { + /* X.509 certs are assumed to be base64 encoded but sometimes they'll have additional things in them + * above and beyond the ceritificate. + * ie. some may have the following preceding the -----BEGIN CERTIFICATE----- line: + * + * Bag Attributes + * localKeyID: 01 00 00 00 + * subject=/O=organization/OU=org unit/CN=common name + * issuer=/O=organization/CN=common name + */ + if (\strlen($str) > \ini_get('pcre.backtrack_limit')) { + $temp = $str; + } else { + $temp = \preg_replace('#.*?^-+[^-]+-+[\\r\\n ]*$#ms', '', $str, 1); + $temp = \preg_replace('#-+END.*[\\r\\n ]*.*#ms', '', $temp, 1); + } + // remove new lines + $temp = \str_replace(["\r", "\n", ' '], '', $temp); + // remove the -----BEGIN CERTIFICATE----- and -----END CERTIFICATE----- stuff + $temp = \preg_replace('#^-+[^-]+-+|-+[^-]+-+$#', '', $temp); + $temp = \preg_match('#^[a-zA-Z\\d/+]*={0,2}$#', $temp) ? \FluentSmtpLib\phpseclib3\Common\Functions\Strings::base64_decode($temp) : \false; + return $temp != \false ? $temp : $str; + } + /** + * DER-encode the length + * + * DER supports lengths up to (2**8)**127, however, we'll only support lengths up to (2**8)**4. See + * {@link http://itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf#p=13 X.690 paragraph 8.1.3} for more information. + * + * @param int $length + * @return string + */ + public static function encodeLength($length) + { + if ($length <= 0x7f) { + return \chr($length); + } + $temp = \ltrim(\pack('N', $length), \chr(0)); + return \pack('Ca*', 0x80 | \strlen($temp), $temp); + } + /** + * Returns the OID corresponding to a name + * + * What's returned in the associative array returned by loadX509() (or load*()) is either a name or an OID if + * no OID to name mapping is available. The problem with this is that what may be an unmapped OID in one version + * of phpseclib may not be unmapped in the next version, so apps that are looking at this OID may not be able + * to work from version to version. + * + * This method will return the OID if a name is passed to it and if no mapping is avialable it'll assume that + * what's being passed to it already is an OID and return that instead. A few examples. + * + * getOID('2.16.840.1.101.3.4.2.1') == '2.16.840.1.101.3.4.2.1' + * getOID('id-sha256') == '2.16.840.1.101.3.4.2.1' + * getOID('zzz') == 'zzz' + * + * @param string $name + * @return string + */ + public static function getOID($name) + { + return isset(self::$reverseOIDs[$name]) ? self::$reverseOIDs[$name] : $name; + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Element.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Element.php new file mode 100644 index 0000000..a9969dc --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Element.php @@ -0,0 +1,41 @@ + + * @copyright 2012 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\File\ASN1; + +/** + * ASN.1 Raw Element + * + * An ASN.1 ANY mapping will return an ASN1\Element object. Use of this object + * will also bypass the normal encoding rules in ASN1::encodeDER() + * + * @author Jim Wigginton + */ +class Element +{ + /** + * Raw element value + * + * @var string + */ + public $element; + /** + * Constructor + * + * @param string $encoded + * @return Element + */ + public function __construct($encoded) + { + $this->element = $encoded; + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/AccessDescription.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/AccessDescription.php new file mode 100644 index 0000000..664adcc --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/AccessDescription.php @@ -0,0 +1,24 @@ + + * @copyright 2016 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\File\ASN1\Maps; + +use FluentSmtpLib\phpseclib3\File\ASN1; +/** + * AccessDescription + * + * @author Jim Wigginton + */ +abstract class AccessDescription +{ + const MAP = ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_SEQUENCE, 'children' => ['accessMethod' => ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_OBJECT_IDENTIFIER], 'accessLocation' => \FluentSmtpLib\phpseclib3\File\ASN1\Maps\GeneralName::MAP]]; +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/AdministrationDomainName.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/AdministrationDomainName.php new file mode 100644 index 0000000..aaaf0e2 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/AdministrationDomainName.php @@ -0,0 +1,31 @@ + + * @copyright 2016 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\File\ASN1\Maps; + +use FluentSmtpLib\phpseclib3\File\ASN1; +/** + * AdministrationDomainName + * + * @author Jim Wigginton + */ +abstract class AdministrationDomainName +{ + const MAP = [ + 'type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_CHOICE, + // if class isn't present it's assumed to be \phpseclib3\File\ASN1::CLASS_UNIVERSAL or + // (if constant is present) \phpseclib3\File\ASN1::CLASS_CONTEXT_SPECIFIC + 'class' => \FluentSmtpLib\phpseclib3\File\ASN1::CLASS_APPLICATION, + 'cast' => 2, + 'children' => ['numeric' => ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_NUMERIC_STRING], 'printable' => ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_PRINTABLE_STRING]], + ]; +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/AlgorithmIdentifier.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/AlgorithmIdentifier.php new file mode 100644 index 0000000..ec6da19 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/AlgorithmIdentifier.php @@ -0,0 +1,24 @@ + + * @copyright 2016 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\File\ASN1\Maps; + +use FluentSmtpLib\phpseclib3\File\ASN1; +/** + * AlgorithmIdentifier + * + * @author Jim Wigginton + */ +abstract class AlgorithmIdentifier +{ + const MAP = ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_SEQUENCE, 'children' => ['algorithm' => ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_OBJECT_IDENTIFIER], 'parameters' => ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_ANY, 'optional' => \true]]]; +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/AnotherName.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/AnotherName.php new file mode 100644 index 0000000..6ddbdbc --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/AnotherName.php @@ -0,0 +1,24 @@ + + * @copyright 2016 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\File\ASN1\Maps; + +use FluentSmtpLib\phpseclib3\File\ASN1; +/** + * AnotherName + * + * @author Jim Wigginton + */ +abstract class AnotherName +{ + const MAP = ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_SEQUENCE, 'children' => ['type-id' => ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_OBJECT_IDENTIFIER], 'value' => ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_ANY, 'constant' => 0, 'optional' => \true, 'explicit' => \true]]]; +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Attribute.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Attribute.php new file mode 100644 index 0000000..1fbf676 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Attribute.php @@ -0,0 +1,24 @@ + + * @copyright 2016 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\File\ASN1\Maps; + +use FluentSmtpLib\phpseclib3\File\ASN1; +/** + * Attribute + * + * @author Jim Wigginton + */ +abstract class Attribute +{ + const MAP = ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_SEQUENCE, 'children' => ['type' => \FluentSmtpLib\phpseclib3\File\ASN1\Maps\AttributeType::MAP, 'value' => ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_SET, 'min' => 1, 'max' => -1, 'children' => \FluentSmtpLib\phpseclib3\File\ASN1\Maps\AttributeValue::MAP]]]; +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/AttributeType.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/AttributeType.php new file mode 100644 index 0000000..02bee7f --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/AttributeType.php @@ -0,0 +1,24 @@ + + * @copyright 2016 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\File\ASN1\Maps; + +use FluentSmtpLib\phpseclib3\File\ASN1; +/** + * AttributeType + * + * @author Jim Wigginton + */ +abstract class AttributeType +{ + const MAP = ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_OBJECT_IDENTIFIER]; +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/AttributeTypeAndValue.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/AttributeTypeAndValue.php new file mode 100644 index 0000000..28bbe39 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/AttributeTypeAndValue.php @@ -0,0 +1,24 @@ + + * @copyright 2016 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\File\ASN1\Maps; + +use FluentSmtpLib\phpseclib3\File\ASN1; +/** + * AttributeTypeAndValue + * + * @author Jim Wigginton + */ +abstract class AttributeTypeAndValue +{ + const MAP = ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_SEQUENCE, 'children' => ['type' => \FluentSmtpLib\phpseclib3\File\ASN1\Maps\AttributeType::MAP, 'value' => \FluentSmtpLib\phpseclib3\File\ASN1\Maps\AttributeValue::MAP]]; +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/AttributeValue.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/AttributeValue.php new file mode 100644 index 0000000..4483b9d --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/AttributeValue.php @@ -0,0 +1,24 @@ + + * @copyright 2016 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\File\ASN1\Maps; + +use FluentSmtpLib\phpseclib3\File\ASN1; +/** + * AttributeValue + * + * @author Jim Wigginton + */ +abstract class AttributeValue +{ + const MAP = ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_ANY]; +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Attributes.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Attributes.php new file mode 100644 index 0000000..cf05259 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Attributes.php @@ -0,0 +1,24 @@ + + * @copyright 2016 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\File\ASN1\Maps; + +use FluentSmtpLib\phpseclib3\File\ASN1; +/** + * Attributes + * + * @author Jim Wigginton + */ +abstract class Attributes +{ + const MAP = ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_SET, 'min' => 1, 'max' => -1, 'children' => \FluentSmtpLib\phpseclib3\File\ASN1\Maps\Attribute::MAP]; +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/AuthorityInfoAccessSyntax.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/AuthorityInfoAccessSyntax.php new file mode 100644 index 0000000..fca0dae --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/AuthorityInfoAccessSyntax.php @@ -0,0 +1,24 @@ + + * @copyright 2016 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\File\ASN1\Maps; + +use FluentSmtpLib\phpseclib3\File\ASN1; +/** + * AuthorityInfoAccessSyntax + * + * @author Jim Wigginton + */ +abstract class AuthorityInfoAccessSyntax +{ + const MAP = ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_SEQUENCE, 'min' => 1, 'max' => -1, 'children' => \FluentSmtpLib\phpseclib3\File\ASN1\Maps\AccessDescription::MAP]; +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/AuthorityKeyIdentifier.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/AuthorityKeyIdentifier.php new file mode 100644 index 0000000..ab1021b --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/AuthorityKeyIdentifier.php @@ -0,0 +1,24 @@ + + * @copyright 2016 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\File\ASN1\Maps; + +use FluentSmtpLib\phpseclib3\File\ASN1; +/** + * AuthorityKeyIdentifier + * + * @author Jim Wigginton + */ +abstract class AuthorityKeyIdentifier +{ + const MAP = ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_SEQUENCE, 'children' => ['keyIdentifier' => ['constant' => 0, 'optional' => \true, 'implicit' => \true] + \FluentSmtpLib\phpseclib3\File\ASN1\Maps\KeyIdentifier::MAP, 'authorityCertIssuer' => ['constant' => 1, 'optional' => \true, 'implicit' => \true] + \FluentSmtpLib\phpseclib3\File\ASN1\Maps\GeneralNames::MAP, 'authorityCertSerialNumber' => ['constant' => 2, 'optional' => \true, 'implicit' => \true] + \FluentSmtpLib\phpseclib3\File\ASN1\Maps\CertificateSerialNumber::MAP]]; +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/BaseDistance.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/BaseDistance.php new file mode 100644 index 0000000..9ab8c42 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/BaseDistance.php @@ -0,0 +1,24 @@ + + * @copyright 2016 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\File\ASN1\Maps; + +use FluentSmtpLib\phpseclib3\File\ASN1; +/** + * BaseDistance + * + * @author Jim Wigginton + */ +abstract class BaseDistance +{ + const MAP = ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_INTEGER]; +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/BasicConstraints.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/BasicConstraints.php new file mode 100644 index 0000000..8698243 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/BasicConstraints.php @@ -0,0 +1,24 @@ + + * @copyright 2016 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\File\ASN1\Maps; + +use FluentSmtpLib\phpseclib3\File\ASN1; +/** + * BasicConstraints + * + * @author Jim Wigginton + */ +abstract class BasicConstraints +{ + const MAP = ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_SEQUENCE, 'children' => ['cA' => ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_BOOLEAN, 'optional' => \true, 'default' => \false], 'pathLenConstraint' => ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_INTEGER, 'optional' => \true]]]; +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/BuiltInDomainDefinedAttribute.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/BuiltInDomainDefinedAttribute.php new file mode 100644 index 0000000..d1c17ed --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/BuiltInDomainDefinedAttribute.php @@ -0,0 +1,24 @@ + + * @copyright 2016 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\File\ASN1\Maps; + +use FluentSmtpLib\phpseclib3\File\ASN1; +/** + * BuiltInDomainDefinedAttribute + * + * @author Jim Wigginton + */ +abstract class BuiltInDomainDefinedAttribute +{ + const MAP = ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_SEQUENCE, 'children' => ['type' => ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_PRINTABLE_STRING], 'value' => ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_PRINTABLE_STRING]]]; +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/BuiltInDomainDefinedAttributes.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/BuiltInDomainDefinedAttributes.php new file mode 100644 index 0000000..9203e10 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/BuiltInDomainDefinedAttributes.php @@ -0,0 +1,30 @@ + + * @copyright 2016 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\File\ASN1\Maps; + +use FluentSmtpLib\phpseclib3\File\ASN1; +/** + * BuiltInDomainDefinedAttributes + * + * @author Jim Wigginton + */ +abstract class BuiltInDomainDefinedAttributes +{ + const MAP = [ + 'type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_SEQUENCE, + 'min' => 1, + 'max' => 4, + // ub-domain-defined-attributes + 'children' => \FluentSmtpLib\phpseclib3\File\ASN1\Maps\BuiltInDomainDefinedAttribute::MAP, + ]; +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/BuiltInStandardAttributes.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/BuiltInStandardAttributes.php new file mode 100644 index 0000000..5cb116e --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/BuiltInStandardAttributes.php @@ -0,0 +1,24 @@ + + * @copyright 2016 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\File\ASN1\Maps; + +use FluentSmtpLib\phpseclib3\File\ASN1; +/** + * BuiltInStandardAttributes + * + * @author Jim Wigginton + */ +abstract class BuiltInStandardAttributes +{ + const MAP = ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_SEQUENCE, 'children' => ['country-name' => ['optional' => \true] + \FluentSmtpLib\phpseclib3\File\ASN1\Maps\CountryName::MAP, 'administration-domain-name' => ['optional' => \true] + \FluentSmtpLib\phpseclib3\File\ASN1\Maps\AdministrationDomainName::MAP, 'network-address' => ['constant' => 0, 'optional' => \true, 'implicit' => \true] + \FluentSmtpLib\phpseclib3\File\ASN1\Maps\NetworkAddress::MAP, 'terminal-identifier' => ['constant' => 1, 'optional' => \true, 'implicit' => \true] + \FluentSmtpLib\phpseclib3\File\ASN1\Maps\TerminalIdentifier::MAP, 'private-domain-name' => ['constant' => 2, 'optional' => \true, 'explicit' => \true] + \FluentSmtpLib\phpseclib3\File\ASN1\Maps\PrivateDomainName::MAP, 'organization-name' => ['constant' => 3, 'optional' => \true, 'implicit' => \true] + \FluentSmtpLib\phpseclib3\File\ASN1\Maps\OrganizationName::MAP, 'numeric-user-identifier' => ['constant' => 4, 'optional' => \true, 'implicit' => \true] + \FluentSmtpLib\phpseclib3\File\ASN1\Maps\NumericUserIdentifier::MAP, 'personal-name' => ['constant' => 5, 'optional' => \true, 'implicit' => \true] + \FluentSmtpLib\phpseclib3\File\ASN1\Maps\PersonalName::MAP, 'organizational-unit-names' => ['constant' => 6, 'optional' => \true, 'implicit' => \true] + \FluentSmtpLib\phpseclib3\File\ASN1\Maps\OrganizationalUnitNames::MAP]]; +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CPSuri.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CPSuri.php new file mode 100644 index 0000000..e0eb8c0 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CPSuri.php @@ -0,0 +1,24 @@ + + * @copyright 2016 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\File\ASN1\Maps; + +use FluentSmtpLib\phpseclib3\File\ASN1; +/** + * CPSuri + * + * @author Jim Wigginton + */ +abstract class CPSuri +{ + const MAP = ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_IA5_STRING]; +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CRLDistributionPoints.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CRLDistributionPoints.php new file mode 100644 index 0000000..0680640 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CRLDistributionPoints.php @@ -0,0 +1,24 @@ + + * @copyright 2016 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\File\ASN1\Maps; + +use FluentSmtpLib\phpseclib3\File\ASN1; +/** + * CRLDistributionPoints + * + * @author Jim Wigginton + */ +abstract class CRLDistributionPoints +{ + const MAP = ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_SEQUENCE, 'min' => 1, 'max' => -1, 'children' => \FluentSmtpLib\phpseclib3\File\ASN1\Maps\DistributionPoint::MAP]; +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CRLNumber.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CRLNumber.php new file mode 100644 index 0000000..36c43d1 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CRLNumber.php @@ -0,0 +1,24 @@ + + * @copyright 2016 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\File\ASN1\Maps; + +use FluentSmtpLib\phpseclib3\File\ASN1; +/** + * CRLNumber + * + * @author Jim Wigginton + */ +abstract class CRLNumber +{ + const MAP = ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_INTEGER]; +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CRLReason.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CRLReason.php new file mode 100644 index 0000000..b0c05d0 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CRLReason.php @@ -0,0 +1,36 @@ + + * @copyright 2016 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\File\ASN1\Maps; + +use FluentSmtpLib\phpseclib3\File\ASN1; +/** + * CRLReason + * + * @author Jim Wigginton + */ +abstract class CRLReason +{ + const MAP = ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_ENUMERATED, 'mapping' => [ + 'unspecified', + 'keyCompromise', + 'cACompromise', + 'affiliationChanged', + 'superseded', + 'cessationOfOperation', + 'certificateHold', + // Value 7 is not used. + 8 => 'removeFromCRL', + 'privilegeWithdrawn', + 'aACompromise', + ]]; +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CertPolicyId.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CertPolicyId.php new file mode 100644 index 0000000..7cf8114 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CertPolicyId.php @@ -0,0 +1,24 @@ + + * @copyright 2016 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\File\ASN1\Maps; + +use FluentSmtpLib\phpseclib3\File\ASN1; +/** + * CertPolicyId + * + * @author Jim Wigginton + */ +abstract class CertPolicyId +{ + const MAP = ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_OBJECT_IDENTIFIER]; +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Certificate.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Certificate.php new file mode 100644 index 0000000..23ff48c --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Certificate.php @@ -0,0 +1,24 @@ + + * @copyright 2016 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\File\ASN1\Maps; + +use FluentSmtpLib\phpseclib3\File\ASN1; +/** + * Certificate + * + * @author Jim Wigginton + */ +abstract class Certificate +{ + const MAP = ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_SEQUENCE, 'children' => ['tbsCertificate' => \FluentSmtpLib\phpseclib3\File\ASN1\Maps\TBSCertificate::MAP, 'signatureAlgorithm' => \FluentSmtpLib\phpseclib3\File\ASN1\Maps\AlgorithmIdentifier::MAP, 'signature' => ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_BIT_STRING]]]; +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CertificateIssuer.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CertificateIssuer.php new file mode 100644 index 0000000..de7bf90 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CertificateIssuer.php @@ -0,0 +1,23 @@ + + * @copyright 2016 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\File\ASN1\Maps; + +/** + * CertificateIssuer + * + * @author Jim Wigginton + */ +abstract class CertificateIssuer +{ + const MAP = \FluentSmtpLib\phpseclib3\File\ASN1\Maps\GeneralNames::MAP; +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CertificateList.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CertificateList.php new file mode 100644 index 0000000..9e2d3ab --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CertificateList.php @@ -0,0 +1,24 @@ + + * @copyright 2016 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\File\ASN1\Maps; + +use FluentSmtpLib\phpseclib3\File\ASN1; +/** + * CertificateList + * + * @author Jim Wigginton + */ +abstract class CertificateList +{ + const MAP = ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_SEQUENCE, 'children' => ['tbsCertList' => \FluentSmtpLib\phpseclib3\File\ASN1\Maps\TBSCertList::MAP, 'signatureAlgorithm' => \FluentSmtpLib\phpseclib3\File\ASN1\Maps\AlgorithmIdentifier::MAP, 'signature' => ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_BIT_STRING]]]; +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CertificatePolicies.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CertificatePolicies.php new file mode 100644 index 0000000..f4e10ba --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CertificatePolicies.php @@ -0,0 +1,24 @@ + + * @copyright 2016 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\File\ASN1\Maps; + +use FluentSmtpLib\phpseclib3\File\ASN1; +/** + * CertificatePolicies + * + * @author Jim Wigginton + */ +abstract class CertificatePolicies +{ + const MAP = ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_SEQUENCE, 'min' => 1, 'max' => -1, 'children' => \FluentSmtpLib\phpseclib3\File\ASN1\Maps\PolicyInformation::MAP]; +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CertificateSerialNumber.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CertificateSerialNumber.php new file mode 100644 index 0000000..a9c53fd --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CertificateSerialNumber.php @@ -0,0 +1,24 @@ + + * @copyright 2016 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\File\ASN1\Maps; + +use FluentSmtpLib\phpseclib3\File\ASN1; +/** + * CertificateSerialNumber + * + * @author Jim Wigginton + */ +abstract class CertificateSerialNumber +{ + const MAP = ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_INTEGER]; +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CertificationRequest.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CertificationRequest.php new file mode 100644 index 0000000..adf0102 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CertificationRequest.php @@ -0,0 +1,24 @@ + + * @copyright 2016 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\File\ASN1\Maps; + +use FluentSmtpLib\phpseclib3\File\ASN1; +/** + * CertificationRequest + * + * @author Jim Wigginton + */ +abstract class CertificationRequest +{ + const MAP = ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_SEQUENCE, 'children' => ['certificationRequestInfo' => \FluentSmtpLib\phpseclib3\File\ASN1\Maps\CertificationRequestInfo::MAP, 'signatureAlgorithm' => \FluentSmtpLib\phpseclib3\File\ASN1\Maps\AlgorithmIdentifier::MAP, 'signature' => ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_BIT_STRING]]]; +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CertificationRequestInfo.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CertificationRequestInfo.php new file mode 100644 index 0000000..b4a1755 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CertificationRequestInfo.php @@ -0,0 +1,24 @@ + + * @copyright 2016 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\File\ASN1\Maps; + +use FluentSmtpLib\phpseclib3\File\ASN1; +/** + * CertificationRequestInfo + * + * @author Jim Wigginton + */ +abstract class CertificationRequestInfo +{ + const MAP = ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_SEQUENCE, 'children' => ['version' => ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_INTEGER, 'mapping' => ['v1']], 'subject' => \FluentSmtpLib\phpseclib3\File\ASN1\Maps\Name::MAP, 'subjectPKInfo' => \FluentSmtpLib\phpseclib3\File\ASN1\Maps\SubjectPublicKeyInfo::MAP, 'attributes' => ['constant' => 0, 'optional' => \true, 'implicit' => \true] + \FluentSmtpLib\phpseclib3\File\ASN1\Maps\Attributes::MAP]]; +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Characteristic_two.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Characteristic_two.php new file mode 100644 index 0000000..343aeb4 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Characteristic_two.php @@ -0,0 +1,29 @@ + + * @copyright 2016 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\File\ASN1\Maps; + +use FluentSmtpLib\phpseclib3\File\ASN1; +/** + * Characteristic_two + * + * @author Jim Wigginton + */ +abstract class Characteristic_two +{ + const MAP = ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_SEQUENCE, 'children' => [ + 'm' => ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_INTEGER], + // field size 2**m + 'basis' => ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_OBJECT_IDENTIFIER], + 'parameters' => ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_ANY, 'optional' => \true], + ]]; +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CountryName.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CountryName.php new file mode 100644 index 0000000..580a4be --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CountryName.php @@ -0,0 +1,31 @@ + + * @copyright 2016 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\File\ASN1\Maps; + +use FluentSmtpLib\phpseclib3\File\ASN1; +/** + * CountryName + * + * @author Jim Wigginton + */ +abstract class CountryName +{ + const MAP = [ + 'type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_CHOICE, + // if class isn't present it's assumed to be \phpseclib3\File\ASN1::CLASS_UNIVERSAL or + // (if constant is present) \phpseclib3\File\ASN1::CLASS_CONTEXT_SPECIFIC + 'class' => \FluentSmtpLib\phpseclib3\File\ASN1::CLASS_APPLICATION, + 'cast' => 1, + 'children' => ['x121-dcc-code' => ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_NUMERIC_STRING], 'iso-3166-alpha2-code' => ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_PRINTABLE_STRING]], + ]; +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Curve.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Curve.php new file mode 100644 index 0000000..ef0def7 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Curve.php @@ -0,0 +1,24 @@ + + * @copyright 2016 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\File\ASN1\Maps; + +use FluentSmtpLib\phpseclib3\File\ASN1; +/** + * Curve + * + * @author Jim Wigginton + */ +abstract class Curve +{ + const MAP = ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_SEQUENCE, 'children' => ['a' => \FluentSmtpLib\phpseclib3\File\ASN1\Maps\FieldElement::MAP, 'b' => \FluentSmtpLib\phpseclib3\File\ASN1\Maps\FieldElement::MAP, 'seed' => ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_BIT_STRING, 'optional' => \true]]]; +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/DHParameter.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/DHParameter.php new file mode 100644 index 0000000..dbc98cc --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/DHParameter.php @@ -0,0 +1,26 @@ + + * @copyright 2016 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\File\ASN1\Maps; + +use FluentSmtpLib\phpseclib3\File\ASN1; +/** + * DHParameter + * + * @author Jim Wigginton + */ +abstract class DHParameter +{ + const MAP = ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_SEQUENCE, 'children' => ['prime' => ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_INTEGER], 'base' => ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_INTEGER], 'privateValueLength' => ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_INTEGER, 'optional' => \true]]]; +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/DSAParams.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/DSAParams.php new file mode 100644 index 0000000..a4edda3 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/DSAParams.php @@ -0,0 +1,24 @@ + + * @copyright 2016 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\File\ASN1\Maps; + +use FluentSmtpLib\phpseclib3\File\ASN1; +/** + * DSAParams + * + * @author Jim Wigginton + */ +abstract class DSAParams +{ + const MAP = ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_SEQUENCE, 'children' => ['p' => ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_INTEGER], 'q' => ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_INTEGER], 'g' => ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_INTEGER]]]; +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/DSAPrivateKey.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/DSAPrivateKey.php new file mode 100644 index 0000000..d616b9c --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/DSAPrivateKey.php @@ -0,0 +1,24 @@ + + * @copyright 2016 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\File\ASN1\Maps; + +use FluentSmtpLib\phpseclib3\File\ASN1; +/** + * DSAPrivateKey + * + * @author Jim Wigginton + */ +abstract class DSAPrivateKey +{ + const MAP = ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_SEQUENCE, 'children' => ['version' => ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_INTEGER], 'p' => ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_INTEGER], 'q' => ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_INTEGER], 'g' => ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_INTEGER], 'y' => ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_INTEGER], 'x' => ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_INTEGER]]]; +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/DSAPublicKey.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/DSAPublicKey.php new file mode 100644 index 0000000..15aff3f --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/DSAPublicKey.php @@ -0,0 +1,24 @@ + + * @copyright 2016 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\File\ASN1\Maps; + +use FluentSmtpLib\phpseclib3\File\ASN1; +/** + * DSAPublicKey + * + * @author Jim Wigginton + */ +abstract class DSAPublicKey +{ + const MAP = ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_INTEGER]; +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/DigestInfo.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/DigestInfo.php new file mode 100644 index 0000000..457f2e6 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/DigestInfo.php @@ -0,0 +1,26 @@ + + * @copyright 2016 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\File\ASN1\Maps; + +use FluentSmtpLib\phpseclib3\File\ASN1; +/** + * DigestInfo + * + * from https://tools.ietf.org/html/rfc2898#appendix-A.3 + * + * @author Jim Wigginton + */ +abstract class DigestInfo +{ + const MAP = ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_SEQUENCE, 'children' => ['digestAlgorithm' => \FluentSmtpLib\phpseclib3\File\ASN1\Maps\AlgorithmIdentifier::MAP, 'digest' => ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_OCTET_STRING]]]; +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/DirectoryString.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/DirectoryString.php new file mode 100644 index 0000000..1d75074 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/DirectoryString.php @@ -0,0 +1,24 @@ + + * @copyright 2016 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\File\ASN1\Maps; + +use FluentSmtpLib\phpseclib3\File\ASN1; +/** + * DirectoryString + * + * @author Jim Wigginton + */ +abstract class DirectoryString +{ + const MAP = ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_CHOICE, 'children' => ['teletexString' => ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_TELETEX_STRING], 'printableString' => ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_PRINTABLE_STRING], 'universalString' => ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_UNIVERSAL_STRING], 'utf8String' => ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_UTF8_STRING], 'bmpString' => ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_BMP_STRING]]]; +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/DisplayText.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/DisplayText.php new file mode 100644 index 0000000..37df55d --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/DisplayText.php @@ -0,0 +1,24 @@ + + * @copyright 2016 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\File\ASN1\Maps; + +use FluentSmtpLib\phpseclib3\File\ASN1; +/** + * DisplayText + * + * @author Jim Wigginton + */ +abstract class DisplayText +{ + const MAP = ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_CHOICE, 'children' => ['ia5String' => ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_IA5_STRING], 'visibleString' => ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_VISIBLE_STRING], 'bmpString' => ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_BMP_STRING], 'utf8String' => ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_UTF8_STRING]]]; +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/DistributionPoint.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/DistributionPoint.php new file mode 100644 index 0000000..6867ed9 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/DistributionPoint.php @@ -0,0 +1,24 @@ + + * @copyright 2016 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\File\ASN1\Maps; + +use FluentSmtpLib\phpseclib3\File\ASN1; +/** + * DistributionPoint + * + * @author Jim Wigginton + */ +abstract class DistributionPoint +{ + const MAP = ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_SEQUENCE, 'children' => ['distributionPoint' => ['constant' => 0, 'optional' => \true, 'explicit' => \true] + \FluentSmtpLib\phpseclib3\File\ASN1\Maps\DistributionPointName::MAP, 'reasons' => ['constant' => 1, 'optional' => \true, 'implicit' => \true] + \FluentSmtpLib\phpseclib3\File\ASN1\Maps\ReasonFlags::MAP, 'cRLIssuer' => ['constant' => 2, 'optional' => \true, 'implicit' => \true] + \FluentSmtpLib\phpseclib3\File\ASN1\Maps\GeneralNames::MAP]]; +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/DistributionPointName.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/DistributionPointName.php new file mode 100644 index 0000000..c2c899b --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/DistributionPointName.php @@ -0,0 +1,24 @@ + + * @copyright 2016 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\File\ASN1\Maps; + +use FluentSmtpLib\phpseclib3\File\ASN1; +/** + * DistributionPointName + * + * @author Jim Wigginton + */ +abstract class DistributionPointName +{ + const MAP = ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_CHOICE, 'children' => ['fullName' => ['constant' => 0, 'optional' => \true, 'implicit' => \true] + \FluentSmtpLib\phpseclib3\File\ASN1\Maps\GeneralNames::MAP, 'nameRelativeToCRLIssuer' => ['constant' => 1, 'optional' => \true, 'implicit' => \true] + \FluentSmtpLib\phpseclib3\File\ASN1\Maps\RelativeDistinguishedName::MAP]]; +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/DssSigValue.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/DssSigValue.php new file mode 100644 index 0000000..5c81b57 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/DssSigValue.php @@ -0,0 +1,24 @@ + + * @copyright 2016 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\File\ASN1\Maps; + +use FluentSmtpLib\phpseclib3\File\ASN1; +/** + * DssSigValue + * + * @author Jim Wigginton + */ +abstract class DssSigValue +{ + const MAP = ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_SEQUENCE, 'children' => ['r' => ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_INTEGER], 's' => ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_INTEGER]]]; +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/ECParameters.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/ECParameters.php new file mode 100644 index 0000000..89e8634 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/ECParameters.php @@ -0,0 +1,36 @@ + + * @copyright 2016 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\File\ASN1\Maps; + +use FluentSmtpLib\phpseclib3\File\ASN1; +/** + * ECParameters + * + * ECParameters ::= CHOICE { + * namedCurve OBJECT IDENTIFIER + * -- implicitCurve NULL + * -- specifiedCurve SpecifiedECDomain + * } + * -- implicitCurve and specifiedCurve MUST NOT be used in PKIX. + * -- Details for SpecifiedECDomain can be found in [X9.62]. + * -- Any future additions to this CHOICE should be coordinated + * -- with ANSI X9. + * + * @author Jim Wigginton + */ +abstract class ECParameters +{ + const MAP = ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_CHOICE, 'children' => ['namedCurve' => ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_OBJECT_IDENTIFIER], 'implicitCurve' => ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_NULL], 'specifiedCurve' => \FluentSmtpLib\phpseclib3\File\ASN1\Maps\SpecifiedECDomain::MAP]]; +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/ECPoint.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/ECPoint.php new file mode 100644 index 0000000..3275ef5 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/ECPoint.php @@ -0,0 +1,24 @@ + + * @copyright 2016 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\File\ASN1\Maps; + +use FluentSmtpLib\phpseclib3\File\ASN1; +/** + * ECPoint + * + * @author Jim Wigginton + */ +abstract class ECPoint +{ + const MAP = ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_OCTET_STRING]; +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/ECPrivateKey.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/ECPrivateKey.php new file mode 100644 index 0000000..c25aff3 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/ECPrivateKey.php @@ -0,0 +1,26 @@ + + * @copyright 2016 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\File\ASN1\Maps; + +use FluentSmtpLib\phpseclib3\File\ASN1; +/** + * ECPrivateKey + * + * @author Jim Wigginton + */ +abstract class ECPrivateKey +{ + const MAP = ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_SEQUENCE, 'children' => ['version' => ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_INTEGER, 'mapping' => [1 => 'ecPrivkeyVer1']], 'privateKey' => ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_OCTET_STRING], 'parameters' => ['constant' => 0, 'optional' => \true, 'explicit' => \true] + \FluentSmtpLib\phpseclib3\File\ASN1\Maps\ECParameters::MAP, 'publicKey' => ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_BIT_STRING, 'constant' => 1, 'optional' => \true, 'explicit' => \true]]]; +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/EDIPartyName.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/EDIPartyName.php new file mode 100644 index 0000000..d5b2803 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/EDIPartyName.php @@ -0,0 +1,29 @@ + + * @copyright 2016 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\File\ASN1\Maps; + +use FluentSmtpLib\phpseclib3\File\ASN1; +/** + * EDIPartyName + * + * @author Jim Wigginton + */ +abstract class EDIPartyName +{ + const MAP = ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_SEQUENCE, 'children' => [ + 'nameAssigner' => ['constant' => 0, 'optional' => \true, 'implicit' => \true] + \FluentSmtpLib\phpseclib3\File\ASN1\Maps\DirectoryString::MAP, + // partyName is technically required but \phpseclib3\File\ASN1 doesn't currently support non-optional constants and + // setting it to optional gets the job done in any event. + 'partyName' => ['constant' => 1, 'optional' => \true, 'implicit' => \true] + \FluentSmtpLib\phpseclib3\File\ASN1\Maps\DirectoryString::MAP, + ]]; +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/EcdsaSigValue.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/EcdsaSigValue.php new file mode 100644 index 0000000..56b81f8 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/EcdsaSigValue.php @@ -0,0 +1,24 @@ + + * @copyright 2016 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\File\ASN1\Maps; + +use FluentSmtpLib\phpseclib3\File\ASN1; +/** + * EcdsaSigValue + * + * @author Jim Wigginton + */ +abstract class EcdsaSigValue +{ + const MAP = ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_SEQUENCE, 'children' => ['r' => ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_INTEGER], 's' => ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_INTEGER]]]; +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/EncryptedData.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/EncryptedData.php new file mode 100644 index 0000000..bcaff8a --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/EncryptedData.php @@ -0,0 +1,24 @@ + + * @copyright 2016 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\File\ASN1\Maps; + +use FluentSmtpLib\phpseclib3\File\ASN1; +/** + * EncryptedData + * + * @author Jim Wigginton + */ +abstract class EncryptedData +{ + const MAP = ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_OCTET_STRING]; +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/EncryptedPrivateKeyInfo.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/EncryptedPrivateKeyInfo.php new file mode 100644 index 0000000..ecf82b3 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/EncryptedPrivateKeyInfo.php @@ -0,0 +1,24 @@ + + * @copyright 2016 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\File\ASN1\Maps; + +use FluentSmtpLib\phpseclib3\File\ASN1; +/** + * EncryptedPrivateKeyInfo + * + * @author Jim Wigginton + */ +abstract class EncryptedPrivateKeyInfo +{ + const MAP = ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_SEQUENCE, 'children' => ['encryptionAlgorithm' => \FluentSmtpLib\phpseclib3\File\ASN1\Maps\AlgorithmIdentifier::MAP, 'encryptedData' => \FluentSmtpLib\phpseclib3\File\ASN1\Maps\EncryptedData::MAP]]; +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/ExtKeyUsageSyntax.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/ExtKeyUsageSyntax.php new file mode 100644 index 0000000..1be75e0 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/ExtKeyUsageSyntax.php @@ -0,0 +1,24 @@ + + * @copyright 2016 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\File\ASN1\Maps; + +use FluentSmtpLib\phpseclib3\File\ASN1; +/** + * ExtKeyUsageSyntax + * + * @author Jim Wigginton + */ +abstract class ExtKeyUsageSyntax +{ + const MAP = ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_SEQUENCE, 'min' => 1, 'max' => -1, 'children' => \FluentSmtpLib\phpseclib3\File\ASN1\Maps\KeyPurposeId::MAP]; +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Extension.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Extension.php new file mode 100644 index 0000000..5a304d2 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Extension.php @@ -0,0 +1,30 @@ + + * @copyright 2016 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\File\ASN1\Maps; + +use FluentSmtpLib\phpseclib3\File\ASN1; +/** + * Extension + * + * A certificate using system MUST reject the certificate if it encounters + * a critical extension it does not recognize; however, a non-critical + * extension may be ignored if it is not recognized. + * + * http://tools.ietf.org/html/rfc5280#section-4.2 + * + * @author Jim Wigginton + */ +abstract class Extension +{ + const MAP = ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_SEQUENCE, 'children' => ['extnId' => ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_OBJECT_IDENTIFIER], 'critical' => ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_BOOLEAN, 'optional' => \true, 'default' => \false], 'extnValue' => ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_OCTET_STRING]]]; +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/ExtensionAttribute.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/ExtensionAttribute.php new file mode 100644 index 0000000..fc18246 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/ExtensionAttribute.php @@ -0,0 +1,24 @@ + + * @copyright 2016 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\File\ASN1\Maps; + +use FluentSmtpLib\phpseclib3\File\ASN1; +/** + * ExtensionAttribute + * + * @author Jim Wigginton + */ +abstract class ExtensionAttribute +{ + const MAP = ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_SEQUENCE, 'children' => ['extension-attribute-type' => ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_PRINTABLE_STRING, 'constant' => 0, 'optional' => \true, 'implicit' => \true], 'extension-attribute-value' => ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_ANY, 'constant' => 1, 'optional' => \true, 'explicit' => \true]]]; +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/ExtensionAttributes.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/ExtensionAttributes.php new file mode 100644 index 0000000..69461ab --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/ExtensionAttributes.php @@ -0,0 +1,30 @@ + + * @copyright 2016 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\File\ASN1\Maps; + +use FluentSmtpLib\phpseclib3\File\ASN1; +/** + * ExtensionAttributes + * + * @author Jim Wigginton + */ +abstract class ExtensionAttributes +{ + const MAP = [ + 'type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_SET, + 'min' => 1, + 'max' => 256, + // ub-extension-attributes + 'children' => \FluentSmtpLib\phpseclib3\File\ASN1\Maps\ExtensionAttribute::MAP, + ]; +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Extensions.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Extensions.php new file mode 100644 index 0000000..5a9828a --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Extensions.php @@ -0,0 +1,31 @@ + + * @copyright 2016 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\File\ASN1\Maps; + +use FluentSmtpLib\phpseclib3\File\ASN1; +/** + * Extensions + * + * @author Jim Wigginton + */ +abstract class Extensions +{ + const MAP = [ + 'type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_SEQUENCE, + 'min' => 1, + // technically, it's MAX, but we'll assume anything < 0 is MAX + 'max' => -1, + // if 'children' isn't an array then 'min' and 'max' must be defined + 'children' => \FluentSmtpLib\phpseclib3\File\ASN1\Maps\Extension::MAP, + ]; +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/FieldElement.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/FieldElement.php new file mode 100644 index 0000000..2695d10 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/FieldElement.php @@ -0,0 +1,24 @@ + + * @copyright 2016 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\File\ASN1\Maps; + +use FluentSmtpLib\phpseclib3\File\ASN1; +/** + * FieldElement + * + * @author Jim Wigginton + */ +abstract class FieldElement +{ + const MAP = ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_OCTET_STRING]; +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/FieldID.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/FieldID.php new file mode 100644 index 0000000..f7a3e24 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/FieldID.php @@ -0,0 +1,24 @@ + + * @copyright 2016 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\File\ASN1\Maps; + +use FluentSmtpLib\phpseclib3\File\ASN1; +/** + * FieldID + * + * @author Jim Wigginton + */ +abstract class FieldID +{ + const MAP = ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_SEQUENCE, 'children' => ['fieldType' => ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_OBJECT_IDENTIFIER], 'parameters' => ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_ANY, 'optional' => \true]]]; +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/GeneralName.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/GeneralName.php new file mode 100644 index 0000000..f679e02 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/GeneralName.php @@ -0,0 +1,24 @@ + + * @copyright 2016 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\File\ASN1\Maps; + +use FluentSmtpLib\phpseclib3\File\ASN1; +/** + * GeneralName + * + * @author Jim Wigginton + */ +abstract class GeneralName +{ + const MAP = ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_CHOICE, 'children' => ['otherName' => ['constant' => 0, 'optional' => \true, 'implicit' => \true] + \FluentSmtpLib\phpseclib3\File\ASN1\Maps\AnotherName::MAP, 'rfc822Name' => ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_IA5_STRING, 'constant' => 1, 'optional' => \true, 'implicit' => \true], 'dNSName' => ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_IA5_STRING, 'constant' => 2, 'optional' => \true, 'implicit' => \true], 'x400Address' => ['constant' => 3, 'optional' => \true, 'implicit' => \true] + \FluentSmtpLib\phpseclib3\File\ASN1\Maps\ORAddress::MAP, 'directoryName' => ['constant' => 4, 'optional' => \true, 'explicit' => \true] + \FluentSmtpLib\phpseclib3\File\ASN1\Maps\Name::MAP, 'ediPartyName' => ['constant' => 5, 'optional' => \true, 'implicit' => \true] + \FluentSmtpLib\phpseclib3\File\ASN1\Maps\EDIPartyName::MAP, 'uniformResourceIdentifier' => ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_IA5_STRING, 'constant' => 6, 'optional' => \true, 'implicit' => \true], 'iPAddress' => ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_OCTET_STRING, 'constant' => 7, 'optional' => \true, 'implicit' => \true], 'registeredID' => ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_OBJECT_IDENTIFIER, 'constant' => 8, 'optional' => \true, 'implicit' => \true]]]; +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/GeneralNames.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/GeneralNames.php new file mode 100644 index 0000000..2e10903 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/GeneralNames.php @@ -0,0 +1,24 @@ + + * @copyright 2016 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\File\ASN1\Maps; + +use FluentSmtpLib\phpseclib3\File\ASN1; +/** + * GeneralNames + * + * @author Jim Wigginton + */ +abstract class GeneralNames +{ + const MAP = ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_SEQUENCE, 'min' => 1, 'max' => -1, 'children' => \FluentSmtpLib\phpseclib3\File\ASN1\Maps\GeneralName::MAP]; +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/GeneralSubtree.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/GeneralSubtree.php new file mode 100644 index 0000000..3670144 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/GeneralSubtree.php @@ -0,0 +1,24 @@ + + * @copyright 2016 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\File\ASN1\Maps; + +use FluentSmtpLib\phpseclib3\File\ASN1; +/** + * GeneralSubtree + * + * @author Jim Wigginton + */ +abstract class GeneralSubtree +{ + const MAP = ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_SEQUENCE, 'children' => ['base' => \FluentSmtpLib\phpseclib3\File\ASN1\Maps\GeneralName::MAP, 'minimum' => ['constant' => 0, 'optional' => \true, 'implicit' => \true, 'default' => '0'] + \FluentSmtpLib\phpseclib3\File\ASN1\Maps\BaseDistance::MAP, 'maximum' => ['constant' => 1, 'optional' => \true, 'implicit' => \true] + \FluentSmtpLib\phpseclib3\File\ASN1\Maps\BaseDistance::MAP]]; +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/GeneralSubtrees.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/GeneralSubtrees.php new file mode 100644 index 0000000..5c20583 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/GeneralSubtrees.php @@ -0,0 +1,24 @@ + + * @copyright 2016 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\File\ASN1\Maps; + +use FluentSmtpLib\phpseclib3\File\ASN1; +/** + * GeneralSubtrees + * + * @author Jim Wigginton + */ +abstract class GeneralSubtrees +{ + const MAP = ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_SEQUENCE, 'min' => 1, 'max' => -1, 'children' => \FluentSmtpLib\phpseclib3\File\ASN1\Maps\GeneralSubtree::MAP]; +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/HashAlgorithm.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/HashAlgorithm.php new file mode 100644 index 0000000..5e39139 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/HashAlgorithm.php @@ -0,0 +1,23 @@ + + * @copyright 2016 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\File\ASN1\Maps; + +/** + * HashAglorithm + * + * @author Jim Wigginton + */ +abstract class HashAlgorithm +{ + const MAP = \FluentSmtpLib\phpseclib3\File\ASN1\Maps\AlgorithmIdentifier::MAP; +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/HoldInstructionCode.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/HoldInstructionCode.php new file mode 100644 index 0000000..015ee9f --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/HoldInstructionCode.php @@ -0,0 +1,24 @@ + + * @copyright 2016 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\File\ASN1\Maps; + +use FluentSmtpLib\phpseclib3\File\ASN1; +/** + * HoldInstructionCode + * + * @author Jim Wigginton + */ +abstract class HoldInstructionCode +{ + const MAP = ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_OBJECT_IDENTIFIER]; +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/InvalidityDate.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/InvalidityDate.php new file mode 100644 index 0000000..f4f4d99 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/InvalidityDate.php @@ -0,0 +1,24 @@ + + * @copyright 2016 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\File\ASN1\Maps; + +use FluentSmtpLib\phpseclib3\File\ASN1; +/** + * InvalidityDate + * + * @author Jim Wigginton + */ +abstract class InvalidityDate +{ + const MAP = ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_GENERALIZED_TIME]; +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/IssuerAltName.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/IssuerAltName.php new file mode 100644 index 0000000..35cac11 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/IssuerAltName.php @@ -0,0 +1,23 @@ + + * @copyright 2016 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\File\ASN1\Maps; + +/** + * IssuerAltName + * + * @author Jim Wigginton + */ +abstract class IssuerAltName +{ + const MAP = \FluentSmtpLib\phpseclib3\File\ASN1\Maps\GeneralNames::MAP; +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/IssuingDistributionPoint.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/IssuingDistributionPoint.php new file mode 100644 index 0000000..a0713db --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/IssuingDistributionPoint.php @@ -0,0 +1,24 @@ + + * @copyright 2016 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\File\ASN1\Maps; + +use FluentSmtpLib\phpseclib3\File\ASN1; +/** + * IssuingDistributionPoint + * + * @author Jim Wigginton + */ +abstract class IssuingDistributionPoint +{ + const MAP = ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_SEQUENCE, 'children' => ['distributionPoint' => ['constant' => 0, 'optional' => \true, 'explicit' => \true] + \FluentSmtpLib\phpseclib3\File\ASN1\Maps\DistributionPointName::MAP, 'onlyContainsUserCerts' => ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_BOOLEAN, 'constant' => 1, 'optional' => \true, 'default' => \false, 'implicit' => \true], 'onlyContainsCACerts' => ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_BOOLEAN, 'constant' => 2, 'optional' => \true, 'default' => \false, 'implicit' => \true], 'onlySomeReasons' => ['constant' => 3, 'optional' => \true, 'implicit' => \true] + \FluentSmtpLib\phpseclib3\File\ASN1\Maps\ReasonFlags::MAP, 'indirectCRL' => ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_BOOLEAN, 'constant' => 4, 'optional' => \true, 'default' => \false, 'implicit' => \true], 'onlyContainsAttributeCerts' => ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_BOOLEAN, 'constant' => 5, 'optional' => \true, 'default' => \false, 'implicit' => \true]]]; +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/KeyIdentifier.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/KeyIdentifier.php new file mode 100644 index 0000000..43fdcfd --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/KeyIdentifier.php @@ -0,0 +1,24 @@ + + * @copyright 2016 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\File\ASN1\Maps; + +use FluentSmtpLib\phpseclib3\File\ASN1; +/** + * KeyIdentifier + * + * @author Jim Wigginton + */ +abstract class KeyIdentifier +{ + const MAP = ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_OCTET_STRING]; +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/KeyPurposeId.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/KeyPurposeId.php new file mode 100644 index 0000000..80baa78 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/KeyPurposeId.php @@ -0,0 +1,24 @@ + + * @copyright 2016 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\File\ASN1\Maps; + +use FluentSmtpLib\phpseclib3\File\ASN1; +/** + * KeyPurposeId + * + * @author Jim Wigginton + */ +abstract class KeyPurposeId +{ + const MAP = ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_OBJECT_IDENTIFIER]; +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/KeyUsage.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/KeyUsage.php new file mode 100644 index 0000000..f51fbf7 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/KeyUsage.php @@ -0,0 +1,24 @@ + + * @copyright 2016 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\File\ASN1\Maps; + +use FluentSmtpLib\phpseclib3\File\ASN1; +/** + * KeyUsage + * + * @author Jim Wigginton + */ +abstract class KeyUsage +{ + const MAP = ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_BIT_STRING, 'mapping' => ['digitalSignature', 'nonRepudiation', 'keyEncipherment', 'dataEncipherment', 'keyAgreement', 'keyCertSign', 'cRLSign', 'encipherOnly', 'decipherOnly']]; +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/MaskGenAlgorithm.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/MaskGenAlgorithm.php new file mode 100644 index 0000000..28ecc86 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/MaskGenAlgorithm.php @@ -0,0 +1,23 @@ + + * @copyright 2016 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\File\ASN1\Maps; + +/** + * MaskGenAglorithm + * + * @author Jim Wigginton + */ +abstract class MaskGenAlgorithm +{ + const MAP = \FluentSmtpLib\phpseclib3\File\ASN1\Maps\AlgorithmIdentifier::MAP; +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Name.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Name.php new file mode 100644 index 0000000..8ab1001 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Name.php @@ -0,0 +1,24 @@ + + * @copyright 2016 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\File\ASN1\Maps; + +use FluentSmtpLib\phpseclib3\File\ASN1; +/** + * Name + * + * @author Jim Wigginton + */ +abstract class Name +{ + const MAP = ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_CHOICE, 'children' => ['rdnSequence' => \FluentSmtpLib\phpseclib3\File\ASN1\Maps\RDNSequence::MAP]]; +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/NameConstraints.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/NameConstraints.php new file mode 100644 index 0000000..795c69c --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/NameConstraints.php @@ -0,0 +1,24 @@ + + * @copyright 2016 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\File\ASN1\Maps; + +use FluentSmtpLib\phpseclib3\File\ASN1; +/** + * NameConstraints + * + * @author Jim Wigginton + */ +abstract class NameConstraints +{ + const MAP = ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_SEQUENCE, 'children' => ['permittedSubtrees' => ['constant' => 0, 'optional' => \true, 'implicit' => \true] + \FluentSmtpLib\phpseclib3\File\ASN1\Maps\GeneralSubtrees::MAP, 'excludedSubtrees' => ['constant' => 1, 'optional' => \true, 'implicit' => \true] + \FluentSmtpLib\phpseclib3\File\ASN1\Maps\GeneralSubtrees::MAP]]; +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/NetworkAddress.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/NetworkAddress.php new file mode 100644 index 0000000..494d154 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/NetworkAddress.php @@ -0,0 +1,24 @@ + + * @copyright 2016 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\File\ASN1\Maps; + +use FluentSmtpLib\phpseclib3\File\ASN1; +/** + * NetworkAddress + * + * @author Jim Wigginton + */ +abstract class NetworkAddress +{ + const MAP = ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_NUMERIC_STRING]; +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/NoticeReference.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/NoticeReference.php new file mode 100644 index 0000000..2162940 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/NoticeReference.php @@ -0,0 +1,24 @@ + + * @copyright 2016 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\File\ASN1\Maps; + +use FluentSmtpLib\phpseclib3\File\ASN1; +/** + * NoticeReference + * + * @author Jim Wigginton + */ +abstract class NoticeReference +{ + const MAP = ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_SEQUENCE, 'children' => ['organization' => \FluentSmtpLib\phpseclib3\File\ASN1\Maps\DisplayText::MAP, 'noticeNumbers' => ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_SEQUENCE, 'min' => 1, 'max' => 200, 'children' => ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_INTEGER]]]]; +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/NumericUserIdentifier.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/NumericUserIdentifier.php new file mode 100644 index 0000000..96f8c15 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/NumericUserIdentifier.php @@ -0,0 +1,24 @@ + + * @copyright 2016 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\File\ASN1\Maps; + +use FluentSmtpLib\phpseclib3\File\ASN1; +/** + * NumericUserIdentifier + * + * @author Jim Wigginton + */ +abstract class NumericUserIdentifier +{ + const MAP = ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_NUMERIC_STRING]; +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/ORAddress.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/ORAddress.php new file mode 100644 index 0000000..10c7f92 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/ORAddress.php @@ -0,0 +1,24 @@ + + * @copyright 2016 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\File\ASN1\Maps; + +use FluentSmtpLib\phpseclib3\File\ASN1; +/** + * ORAddress + * + * @author Jim Wigginton + */ +abstract class ORAddress +{ + const MAP = ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_SEQUENCE, 'children' => ['built-in-standard-attributes' => \FluentSmtpLib\phpseclib3\File\ASN1\Maps\BuiltInStandardAttributes::MAP, 'built-in-domain-defined-attributes' => ['optional' => \true] + \FluentSmtpLib\phpseclib3\File\ASN1\Maps\BuiltInDomainDefinedAttributes::MAP, 'extension-attributes' => ['optional' => \true] + \FluentSmtpLib\phpseclib3\File\ASN1\Maps\ExtensionAttributes::MAP]]; +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/OneAsymmetricKey.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/OneAsymmetricKey.php new file mode 100644 index 0000000..c2aeea6 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/OneAsymmetricKey.php @@ -0,0 +1,26 @@ + + * @copyright 2016 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\File\ASN1\Maps; + +use FluentSmtpLib\phpseclib3\File\ASN1; +/** + * OneAsymmetricKey + * + * @author Jim Wigginton + */ +abstract class OneAsymmetricKey +{ + const MAP = ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_SEQUENCE, 'children' => ['version' => ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_INTEGER, 'mapping' => ['v1', 'v2']], 'privateKeyAlgorithm' => \FluentSmtpLib\phpseclib3\File\ASN1\Maps\AlgorithmIdentifier::MAP, 'privateKey' => \FluentSmtpLib\phpseclib3\File\ASN1\Maps\PrivateKey::MAP, 'attributes' => ['constant' => 0, 'optional' => \true, 'implicit' => \true] + \FluentSmtpLib\phpseclib3\File\ASN1\Maps\Attributes::MAP, 'publicKey' => ['constant' => 1, 'optional' => \true, 'implicit' => \true] + \FluentSmtpLib\phpseclib3\File\ASN1\Maps\PublicKey::MAP]]; +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/OrganizationName.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/OrganizationName.php new file mode 100644 index 0000000..d9e60cc --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/OrganizationName.php @@ -0,0 +1,24 @@ + + * @copyright 2016 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\File\ASN1\Maps; + +use FluentSmtpLib\phpseclib3\File\ASN1; +/** + * OrganizationName + * + * @author Jim Wigginton + */ +abstract class OrganizationName +{ + const MAP = ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_PRINTABLE_STRING]; +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/OrganizationalUnitNames.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/OrganizationalUnitNames.php new file mode 100644 index 0000000..2f86e34 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/OrganizationalUnitNames.php @@ -0,0 +1,30 @@ + + * @copyright 2016 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\File\ASN1\Maps; + +use FluentSmtpLib\phpseclib3\File\ASN1; +/** + * OrganizationalUnitNames + * + * @author Jim Wigginton + */ +abstract class OrganizationalUnitNames +{ + const MAP = [ + 'type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_SEQUENCE, + 'min' => 1, + 'max' => 4, + // ub-organizational-units + 'children' => ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_PRINTABLE_STRING], + ]; +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/OtherPrimeInfo.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/OtherPrimeInfo.php new file mode 100644 index 0000000..2c9a305 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/OtherPrimeInfo.php @@ -0,0 +1,31 @@ + + * @copyright 2016 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\File\ASN1\Maps; + +use FluentSmtpLib\phpseclib3\File\ASN1; +/** + * OtherPrimeInfo + * + * @author Jim Wigginton + */ +abstract class OtherPrimeInfo +{ + // version must be multi if otherPrimeInfos present + const MAP = ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_SEQUENCE, 'children' => [ + 'prime' => ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_INTEGER], + // ri + 'exponent' => ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_INTEGER], + // di + 'coefficient' => ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_INTEGER], + ]]; +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/OtherPrimeInfos.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/OtherPrimeInfos.php new file mode 100644 index 0000000..ac6070a --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/OtherPrimeInfos.php @@ -0,0 +1,25 @@ + + * @copyright 2016 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\File\ASN1\Maps; + +use FluentSmtpLib\phpseclib3\File\ASN1; +/** + * OtherPrimeInfos + * + * @author Jim Wigginton + */ +abstract class OtherPrimeInfos +{ + // version must be multi if otherPrimeInfos present + const MAP = ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_SEQUENCE, 'min' => 1, 'max' => -1, 'children' => \FluentSmtpLib\phpseclib3\File\ASN1\Maps\OtherPrimeInfo::MAP]; +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PBEParameter.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PBEParameter.php new file mode 100644 index 0000000..2081ea1 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PBEParameter.php @@ -0,0 +1,26 @@ + + * @copyright 2016 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\File\ASN1\Maps; + +use FluentSmtpLib\phpseclib3\File\ASN1; +/** + * PBEParameter + * + * from https://tools.ietf.org/html/rfc2898#appendix-A.3 + * + * @author Jim Wigginton + */ +abstract class PBEParameter +{ + const MAP = ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_SEQUENCE, 'children' => ['salt' => ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_OCTET_STRING], 'iterationCount' => ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_INTEGER]]]; +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PBES2params.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PBES2params.php new file mode 100644 index 0000000..787449c --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PBES2params.php @@ -0,0 +1,26 @@ + + * @copyright 2016 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\File\ASN1\Maps; + +use FluentSmtpLib\phpseclib3\File\ASN1; +/** + * PBES2params + * + * from https://tools.ietf.org/html/rfc2898#appendix-A.3 + * + * @author Jim Wigginton + */ +abstract class PBES2params +{ + const MAP = ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_SEQUENCE, 'children' => ['keyDerivationFunc' => \FluentSmtpLib\phpseclib3\File\ASN1\Maps\AlgorithmIdentifier::MAP, 'encryptionScheme' => \FluentSmtpLib\phpseclib3\File\ASN1\Maps\AlgorithmIdentifier::MAP]]; +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PBKDF2params.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PBKDF2params.php new file mode 100644 index 0000000..8b8eb98 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PBKDF2params.php @@ -0,0 +1,33 @@ + + * @copyright 2016 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\File\ASN1\Maps; + +use FluentSmtpLib\phpseclib3\File\ASN1; +/** + * PBKDF2params + * + * from https://tools.ietf.org/html/rfc2898#appendix-A.3 + * + * @author Jim Wigginton + */ +abstract class PBKDF2params +{ + const MAP = ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_SEQUENCE, 'children' => [ + // technically, this is a CHOICE in RFC2898 but the other "choice" is, currently, more of a placeholder + // in the RFC + 'salt' => ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_OCTET_STRING], + 'iterationCount' => ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_INTEGER], + 'keyLength' => ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_INTEGER, 'optional' => \true], + 'prf' => \FluentSmtpLib\phpseclib3\File\ASN1\Maps\AlgorithmIdentifier::MAP + ['optional' => \true], + ]]; +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PBMAC1params.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PBMAC1params.php new file mode 100644 index 0000000..abb66ba --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PBMAC1params.php @@ -0,0 +1,26 @@ + + * @copyright 2016 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\File\ASN1\Maps; + +use FluentSmtpLib\phpseclib3\File\ASN1; +/** + * PBMAC1params + * + * from https://tools.ietf.org/html/rfc2898#appendix-A.3 + * + * @author Jim Wigginton + */ +abstract class PBMAC1params +{ + const MAP = ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_SEQUENCE, 'children' => ['keyDerivationFunc' => \FluentSmtpLib\phpseclib3\File\ASN1\Maps\AlgorithmIdentifier::MAP, 'messageAuthScheme' => \FluentSmtpLib\phpseclib3\File\ASN1\Maps\AlgorithmIdentifier::MAP]]; +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PKCS9String.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PKCS9String.php new file mode 100644 index 0000000..ed17238 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PKCS9String.php @@ -0,0 +1,24 @@ + + * @copyright 2016 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\File\ASN1\Maps; + +use FluentSmtpLib\phpseclib3\File\ASN1; +/** + * PKCS9String + * + * @author Jim Wigginton + */ +abstract class PKCS9String +{ + const MAP = ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_CHOICE, 'children' => ['ia5String' => ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_IA5_STRING], 'directoryString' => \FluentSmtpLib\phpseclib3\File\ASN1\Maps\DirectoryString::MAP]]; +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Pentanomial.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Pentanomial.php new file mode 100644 index 0000000..b1054dc --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Pentanomial.php @@ -0,0 +1,30 @@ + + * @copyright 2016 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\File\ASN1\Maps; + +use FluentSmtpLib\phpseclib3\File\ASN1; +/** + * Pentanomial + * + * @author Jim Wigginton + */ +abstract class Pentanomial +{ + const MAP = ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_SEQUENCE, 'children' => [ + 'k1' => ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_INTEGER], + // k1 > 0 + 'k2' => ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_INTEGER], + // k2 > k1 + 'k3' => ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_INTEGER], + ]]; +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PersonalName.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PersonalName.php new file mode 100644 index 0000000..5d97f0e --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PersonalName.php @@ -0,0 +1,24 @@ + + * @copyright 2016 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\File\ASN1\Maps; + +use FluentSmtpLib\phpseclib3\File\ASN1; +/** + * PersonalName + * + * @author Jim Wigginton + */ +abstract class PersonalName +{ + const MAP = ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_SET, 'children' => ['surname' => ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_PRINTABLE_STRING, 'constant' => 0, 'optional' => \true, 'implicit' => \true], 'given-name' => ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_PRINTABLE_STRING, 'constant' => 1, 'optional' => \true, 'implicit' => \true], 'initials' => ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_PRINTABLE_STRING, 'constant' => 2, 'optional' => \true, 'implicit' => \true], 'generation-qualifier' => ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_PRINTABLE_STRING, 'constant' => 3, 'optional' => \true, 'implicit' => \true]]]; +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PolicyInformation.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PolicyInformation.php new file mode 100644 index 0000000..62fb8c2 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PolicyInformation.php @@ -0,0 +1,24 @@ + + * @copyright 2016 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\File\ASN1\Maps; + +use FluentSmtpLib\phpseclib3\File\ASN1; +/** + * PolicyInformation + * + * @author Jim Wigginton + */ +abstract class PolicyInformation +{ + const MAP = ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_SEQUENCE, 'children' => ['policyIdentifier' => \FluentSmtpLib\phpseclib3\File\ASN1\Maps\CertPolicyId::MAP, 'policyQualifiers' => ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_SEQUENCE, 'min' => 0, 'max' => -1, 'optional' => \true, 'children' => \FluentSmtpLib\phpseclib3\File\ASN1\Maps\PolicyQualifierInfo::MAP]]]; +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PolicyMappings.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PolicyMappings.php new file mode 100644 index 0000000..80d5b03 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PolicyMappings.php @@ -0,0 +1,24 @@ + + * @copyright 2016 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\File\ASN1\Maps; + +use FluentSmtpLib\phpseclib3\File\ASN1; +/** + * PolicyMappings + * + * @author Jim Wigginton + */ +abstract class PolicyMappings +{ + const MAP = ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_SEQUENCE, 'min' => 1, 'max' => -1, 'children' => ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_SEQUENCE, 'children' => ['issuerDomainPolicy' => \FluentSmtpLib\phpseclib3\File\ASN1\Maps\CertPolicyId::MAP, 'subjectDomainPolicy' => \FluentSmtpLib\phpseclib3\File\ASN1\Maps\CertPolicyId::MAP]]]; +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PolicyQualifierId.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PolicyQualifierId.php new file mode 100644 index 0000000..8ea1fdc --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PolicyQualifierId.php @@ -0,0 +1,24 @@ + + * @copyright 2016 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\File\ASN1\Maps; + +use FluentSmtpLib\phpseclib3\File\ASN1; +/** + * PolicyQualifierId + * + * @author Jim Wigginton + */ +abstract class PolicyQualifierId +{ + const MAP = ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_OBJECT_IDENTIFIER]; +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PolicyQualifierInfo.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PolicyQualifierInfo.php new file mode 100644 index 0000000..b9af110 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PolicyQualifierInfo.php @@ -0,0 +1,24 @@ + + * @copyright 2016 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\File\ASN1\Maps; + +use FluentSmtpLib\phpseclib3\File\ASN1; +/** + * PolicyQualifierInfo + * + * @author Jim Wigginton + */ +abstract class PolicyQualifierInfo +{ + const MAP = ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_SEQUENCE, 'children' => ['policyQualifierId' => \FluentSmtpLib\phpseclib3\File\ASN1\Maps\PolicyQualifierId::MAP, 'qualifier' => ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_ANY]]]; +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PostalAddress.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PostalAddress.php new file mode 100644 index 0000000..d7d027b --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PostalAddress.php @@ -0,0 +1,24 @@ + + * @copyright 2016 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\File\ASN1\Maps; + +use FluentSmtpLib\phpseclib3\File\ASN1; +/** + * PostalAddress + * + * @author Jim Wigginton + */ +abstract class PostalAddress +{ + const MAP = ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_SEQUENCE, 'optional' => \true, 'min' => 1, 'max' => -1, 'children' => \FluentSmtpLib\phpseclib3\File\ASN1\Maps\DirectoryString::MAP]; +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Prime_p.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Prime_p.php new file mode 100644 index 0000000..d688379 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Prime_p.php @@ -0,0 +1,24 @@ + + * @copyright 2016 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\File\ASN1\Maps; + +use FluentSmtpLib\phpseclib3\File\ASN1; +/** + * Prime_p + * + * @author Jim Wigginton + */ +abstract class Prime_p +{ + const MAP = ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_INTEGER]; +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PrivateDomainName.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PrivateDomainName.php new file mode 100644 index 0000000..8023cc6 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PrivateDomainName.php @@ -0,0 +1,24 @@ + + * @copyright 2016 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\File\ASN1\Maps; + +use FluentSmtpLib\phpseclib3\File\ASN1; +/** + * PrivateDomainName + * + * @author Jim Wigginton + */ +abstract class PrivateDomainName +{ + const MAP = ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_CHOICE, 'children' => ['numeric' => ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_NUMERIC_STRING], 'printable' => ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_PRINTABLE_STRING]]]; +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PrivateKey.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PrivateKey.php new file mode 100644 index 0000000..42d1139 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PrivateKey.php @@ -0,0 +1,24 @@ + + * @copyright 2016 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\File\ASN1\Maps; + +use FluentSmtpLib\phpseclib3\File\ASN1; +/** + * PrivateKey + * + * @author Jim Wigginton + */ +abstract class PrivateKey +{ + const MAP = ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_OCTET_STRING]; +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PrivateKeyInfo.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PrivateKeyInfo.php new file mode 100644 index 0000000..52f5600 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PrivateKeyInfo.php @@ -0,0 +1,24 @@ + + * @copyright 2016 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\File\ASN1\Maps; + +use FluentSmtpLib\phpseclib3\File\ASN1; +/** + * PrivateKeyInfo + * + * @author Jim Wigginton + */ +abstract class PrivateKeyInfo +{ + const MAP = ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_SEQUENCE, 'children' => ['version' => ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_INTEGER, 'mapping' => ['v1']], 'privateKeyAlgorithm' => \FluentSmtpLib\phpseclib3\File\ASN1\Maps\AlgorithmIdentifier::MAP, 'privateKey' => \FluentSmtpLib\phpseclib3\File\ASN1\Maps\PrivateKey::MAP, 'attributes' => ['constant' => 0, 'optional' => \true, 'implicit' => \true] + \FluentSmtpLib\phpseclib3\File\ASN1\Maps\Attributes::MAP]]; +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PrivateKeyUsagePeriod.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PrivateKeyUsagePeriod.php new file mode 100644 index 0000000..734b6d1 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PrivateKeyUsagePeriod.php @@ -0,0 +1,24 @@ + + * @copyright 2016 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\File\ASN1\Maps; + +use FluentSmtpLib\phpseclib3\File\ASN1; +/** + * PrivateKeyUsagePeriod + * + * @author Jim Wigginton + */ +abstract class PrivateKeyUsagePeriod +{ + const MAP = ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_SEQUENCE, 'children' => ['notBefore' => ['constant' => 0, 'optional' => \true, 'implicit' => \true, 'type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_GENERALIZED_TIME], 'notAfter' => ['constant' => 1, 'optional' => \true, 'implicit' => \true, 'type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_GENERALIZED_TIME]]]; +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PublicKey.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PublicKey.php new file mode 100644 index 0000000..15aae74 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PublicKey.php @@ -0,0 +1,24 @@ + + * @copyright 2016 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\File\ASN1\Maps; + +use FluentSmtpLib\phpseclib3\File\ASN1; +/** + * PublicKey + * + * @author Jim Wigginton + */ +abstract class PublicKey +{ + const MAP = ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_BIT_STRING]; +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PublicKeyAndChallenge.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PublicKeyAndChallenge.php new file mode 100644 index 0000000..367fa16 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PublicKeyAndChallenge.php @@ -0,0 +1,24 @@ + + * @copyright 2016 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\File\ASN1\Maps; + +use FluentSmtpLib\phpseclib3\File\ASN1; +/** + * PublicKeyAndChallenge + * + * @author Jim Wigginton + */ +abstract class PublicKeyAndChallenge +{ + const MAP = ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_SEQUENCE, 'children' => ['spki' => \FluentSmtpLib\phpseclib3\File\ASN1\Maps\SubjectPublicKeyInfo::MAP, 'challenge' => ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_IA5_STRING]]]; +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PublicKeyInfo.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PublicKeyInfo.php new file mode 100644 index 0000000..82f4711 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PublicKeyInfo.php @@ -0,0 +1,27 @@ + + * @copyright 2016 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\File\ASN1\Maps; + +use FluentSmtpLib\phpseclib3\File\ASN1; +/** + * PublicKeyInfo + * + * this format is not formally defined anywhere but is none-the-less the form you + * get when you do "openssl rsa -in private.pem -outform PEM -pubout" + * + * @author Jim Wigginton + */ +abstract class PublicKeyInfo +{ + const MAP = ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_SEQUENCE, 'children' => ['publicKeyAlgorithm' => \FluentSmtpLib\phpseclib3\File\ASN1\Maps\AlgorithmIdentifier::MAP, 'publicKey' => ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_BIT_STRING]]]; +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/RC2CBCParameter.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/RC2CBCParameter.php new file mode 100644 index 0000000..85016ee --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/RC2CBCParameter.php @@ -0,0 +1,26 @@ + + * @copyright 2016 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\File\ASN1\Maps; + +use FluentSmtpLib\phpseclib3\File\ASN1; +/** + * RC2CBCParameter + * + * from https://tools.ietf.org/html/rfc2898#appendix-A.3 + * + * @author Jim Wigginton + */ +abstract class RC2CBCParameter +{ + const MAP = ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_SEQUENCE, 'children' => ['rc2ParametersVersion' => ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_INTEGER, 'optional' => \true], 'iv' => ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_OCTET_STRING]]]; +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/RDNSequence.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/RDNSequence.php new file mode 100644 index 0000000..82161a0 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/RDNSequence.php @@ -0,0 +1,36 @@ + + * @copyright 2016 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\File\ASN1\Maps; + +use FluentSmtpLib\phpseclib3\File\ASN1; +/** + * RDNSequence + * + * In practice, RDNs containing multiple name-value pairs (called "multivalued RDNs") are rare, + * but they can be useful at times when either there is no unique attribute in the entry or you + * want to ensure that the entry's DN contains some useful identifying information. + * + * - https://www.opends.org/wiki/page/DefinitionRelativeDistinguishedName + * + * @author Jim Wigginton + */ +abstract class RDNSequence +{ + const MAP = [ + 'type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_SEQUENCE, + // RDNSequence does not define a min or a max, which means it doesn't have one + 'min' => 0, + 'max' => -1, + 'children' => \FluentSmtpLib\phpseclib3\File\ASN1\Maps\RelativeDistinguishedName::MAP, + ]; +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/RSAPrivateKey.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/RSAPrivateKey.php new file mode 100644 index 0000000..67fcfad --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/RSAPrivateKey.php @@ -0,0 +1,44 @@ + + * @copyright 2016 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\File\ASN1\Maps; + +use FluentSmtpLib\phpseclib3\File\ASN1; +/** + * RSAPrivateKey + * + * @author Jim Wigginton + */ +abstract class RSAPrivateKey +{ + // version must be multi if otherPrimeInfos present + const MAP = ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_SEQUENCE, 'children' => [ + 'version' => ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_INTEGER, 'mapping' => ['two-prime', 'multi']], + 'modulus' => ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_INTEGER], + // n + 'publicExponent' => ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_INTEGER], + // e + 'privateExponent' => ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_INTEGER], + // d + 'prime1' => ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_INTEGER], + // p + 'prime2' => ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_INTEGER], + // q + 'exponent1' => ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_INTEGER], + // d mod (p-1) + 'exponent2' => ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_INTEGER], + // d mod (q-1) + 'coefficient' => ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_INTEGER], + // (inverse of q) mod p + 'otherPrimeInfos' => \FluentSmtpLib\phpseclib3\File\ASN1\Maps\OtherPrimeInfos::MAP + ['optional' => \true], + ]]; +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/RSAPublicKey.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/RSAPublicKey.php new file mode 100644 index 0000000..ffc8b46 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/RSAPublicKey.php @@ -0,0 +1,24 @@ + + * @copyright 2016 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\File\ASN1\Maps; + +use FluentSmtpLib\phpseclib3\File\ASN1; +/** + * RSAPublicKey + * + * @author Jim Wigginton + */ +abstract class RSAPublicKey +{ + const MAP = ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_SEQUENCE, 'children' => ['modulus' => ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_INTEGER], 'publicExponent' => ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_INTEGER]]]; +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/RSASSA_PSS_params.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/RSASSA_PSS_params.php new file mode 100644 index 0000000..87a68df --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/RSASSA_PSS_params.php @@ -0,0 +1,26 @@ + + * @copyright 2016 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\File\ASN1\Maps; + +use FluentSmtpLib\phpseclib3\File\ASN1; +/** + * RSASSA_PSS_params + * + * @author Jim Wigginton + */ +abstract class RSASSA_PSS_params +{ + const MAP = ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_SEQUENCE, 'children' => ['hashAlgorithm' => ['constant' => 0, 'optional' => \true, 'explicit' => \true] + \FluentSmtpLib\phpseclib3\File\ASN1\Maps\HashAlgorithm::MAP, 'maskGenAlgorithm' => ['constant' => 1, 'optional' => \true, 'explicit' => \true] + \FluentSmtpLib\phpseclib3\File\ASN1\Maps\MaskGenAlgorithm::MAP, 'saltLength' => ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_INTEGER, 'constant' => 2, 'optional' => \true, 'explicit' => \true, 'default' => 20], 'trailerField' => ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_INTEGER, 'constant' => 3, 'optional' => \true, 'explicit' => \true, 'default' => 1]]]; +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/ReasonFlags.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/ReasonFlags.php new file mode 100644 index 0000000..d1c26e1 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/ReasonFlags.php @@ -0,0 +1,24 @@ + + * @copyright 2016 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\File\ASN1\Maps; + +use FluentSmtpLib\phpseclib3\File\ASN1; +/** + * ReasonFlags + * + * @author Jim Wigginton + */ +abstract class ReasonFlags +{ + const MAP = ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_BIT_STRING, 'mapping' => ['unused', 'keyCompromise', 'cACompromise', 'affiliationChanged', 'superseded', 'cessationOfOperation', 'certificateHold', 'privilegeWithdrawn', 'aACompromise']]; +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/RelativeDistinguishedName.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/RelativeDistinguishedName.php new file mode 100644 index 0000000..5734a24 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/RelativeDistinguishedName.php @@ -0,0 +1,30 @@ + + * @copyright 2016 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\File\ASN1\Maps; + +use FluentSmtpLib\phpseclib3\File\ASN1; +/** + * RelativeDistinguishedName + * + * In practice, RDNs containing multiple name-value pairs (called "multivalued RDNs") are rare, + * but they can be useful at times when either there is no unique attribute in the entry or you + * want to ensure that the entry's DN contains some useful identifying information. + * + * - https://www.opends.org/wiki/page/DefinitionRelativeDistinguishedName + * + * @author Jim Wigginton + */ +abstract class RelativeDistinguishedName +{ + const MAP = ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_SET, 'min' => 1, 'max' => -1, 'children' => \FluentSmtpLib\phpseclib3\File\ASN1\Maps\AttributeTypeAndValue::MAP]; +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/RevokedCertificate.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/RevokedCertificate.php new file mode 100644 index 0000000..c0e851a --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/RevokedCertificate.php @@ -0,0 +1,24 @@ + + * @copyright 2016 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\File\ASN1\Maps; + +use FluentSmtpLib\phpseclib3\File\ASN1; +/** + * RevokedCertificate + * + * @author Jim Wigginton + */ +abstract class RevokedCertificate +{ + const MAP = ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_SEQUENCE, 'children' => ['userCertificate' => \FluentSmtpLib\phpseclib3\File\ASN1\Maps\CertificateSerialNumber::MAP, 'revocationDate' => \FluentSmtpLib\phpseclib3\File\ASN1\Maps\Time::MAP, 'crlEntryExtensions' => ['optional' => \true] + \FluentSmtpLib\phpseclib3\File\ASN1\Maps\Extensions::MAP]]; +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/SignedPublicKeyAndChallenge.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/SignedPublicKeyAndChallenge.php new file mode 100644 index 0000000..c46b4ec --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/SignedPublicKeyAndChallenge.php @@ -0,0 +1,24 @@ + + * @copyright 2016 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\File\ASN1\Maps; + +use FluentSmtpLib\phpseclib3\File\ASN1; +/** + * SignedPublicKeyAndChallenge + * + * @author Jim Wigginton + */ +abstract class SignedPublicKeyAndChallenge +{ + const MAP = ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_SEQUENCE, 'children' => ['publicKeyAndChallenge' => \FluentSmtpLib\phpseclib3\File\ASN1\Maps\PublicKeyAndChallenge::MAP, 'signatureAlgorithm' => \FluentSmtpLib\phpseclib3\File\ASN1\Maps\AlgorithmIdentifier::MAP, 'signature' => ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_BIT_STRING]]]; +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/SpecifiedECDomain.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/SpecifiedECDomain.php new file mode 100644 index 0000000..866ede5 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/SpecifiedECDomain.php @@ -0,0 +1,26 @@ + + * @copyright 2016 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\File\ASN1\Maps; + +use FluentSmtpLib\phpseclib3\File\ASN1; +/** + * SpecifiedECDomain + * + * @author Jim Wigginton + */ +abstract class SpecifiedECDomain +{ + const MAP = ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_SEQUENCE, 'children' => ['version' => ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_INTEGER, 'mapping' => [1 => 'ecdpVer1', 'ecdpVer2', 'ecdpVer3']], 'fieldID' => \FluentSmtpLib\phpseclib3\File\ASN1\Maps\FieldID::MAP, 'curve' => \FluentSmtpLib\phpseclib3\File\ASN1\Maps\Curve::MAP, 'base' => \FluentSmtpLib\phpseclib3\File\ASN1\Maps\ECPoint::MAP, 'order' => ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_INTEGER], 'cofactor' => ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_INTEGER, 'optional' => \true], 'hash' => ['optional' => \true] + \FluentSmtpLib\phpseclib3\File\ASN1\Maps\HashAlgorithm::MAP]]; +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/SubjectAltName.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/SubjectAltName.php new file mode 100644 index 0000000..7ad5c70 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/SubjectAltName.php @@ -0,0 +1,23 @@ + + * @copyright 2016 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\File\ASN1\Maps; + +/** + * SubjectAltName + * + * @author Jim Wigginton + */ +abstract class SubjectAltName +{ + const MAP = \FluentSmtpLib\phpseclib3\File\ASN1\Maps\GeneralNames::MAP; +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/SubjectDirectoryAttributes.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/SubjectDirectoryAttributes.php new file mode 100644 index 0000000..786a056 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/SubjectDirectoryAttributes.php @@ -0,0 +1,24 @@ + + * @copyright 2016 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\File\ASN1\Maps; + +use FluentSmtpLib\phpseclib3\File\ASN1; +/** + * SubjectDirectoryAttributes + * + * @author Jim Wigginton + */ +abstract class SubjectDirectoryAttributes +{ + const MAP = ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_SEQUENCE, 'min' => 1, 'max' => -1, 'children' => \FluentSmtpLib\phpseclib3\File\ASN1\Maps\Attribute::MAP]; +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/SubjectInfoAccessSyntax.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/SubjectInfoAccessSyntax.php new file mode 100644 index 0000000..8f49f0b --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/SubjectInfoAccessSyntax.php @@ -0,0 +1,24 @@ + + * @copyright 2016 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\File\ASN1\Maps; + +use FluentSmtpLib\phpseclib3\File\ASN1; +/** + * SubjectInfoAccessSyntax + * + * @author Jim Wigginton + */ +abstract class SubjectInfoAccessSyntax +{ + const MAP = ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_SEQUENCE, 'min' => 1, 'max' => -1, 'children' => \FluentSmtpLib\phpseclib3\File\ASN1\Maps\AccessDescription::MAP]; +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/SubjectPublicKeyInfo.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/SubjectPublicKeyInfo.php new file mode 100644 index 0000000..bccedab --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/SubjectPublicKeyInfo.php @@ -0,0 +1,24 @@ + + * @copyright 2016 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\File\ASN1\Maps; + +use FluentSmtpLib\phpseclib3\File\ASN1; +/** + * SubjectPublicKeyInfo + * + * @author Jim Wigginton + */ +abstract class SubjectPublicKeyInfo +{ + const MAP = ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_SEQUENCE, 'children' => ['algorithm' => \FluentSmtpLib\phpseclib3\File\ASN1\Maps\AlgorithmIdentifier::MAP, 'subjectPublicKey' => ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_BIT_STRING]]]; +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/TBSCertList.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/TBSCertList.php new file mode 100644 index 0000000..9d591a5 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/TBSCertList.php @@ -0,0 +1,24 @@ + + * @copyright 2016 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\File\ASN1\Maps; + +use FluentSmtpLib\phpseclib3\File\ASN1; +/** + * TBSCertList + * + * @author Jim Wigginton + */ +abstract class TBSCertList +{ + const MAP = ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_SEQUENCE, 'children' => ['version' => ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_INTEGER, 'mapping' => ['v1', 'v2'], 'optional' => \true, 'default' => 'v1'], 'signature' => \FluentSmtpLib\phpseclib3\File\ASN1\Maps\AlgorithmIdentifier::MAP, 'issuer' => \FluentSmtpLib\phpseclib3\File\ASN1\Maps\Name::MAP, 'thisUpdate' => \FluentSmtpLib\phpseclib3\File\ASN1\Maps\Time::MAP, 'nextUpdate' => ['optional' => \true] + \FluentSmtpLib\phpseclib3\File\ASN1\Maps\Time::MAP, 'revokedCertificates' => ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_SEQUENCE, 'optional' => \true, 'min' => 0, 'max' => -1, 'children' => \FluentSmtpLib\phpseclib3\File\ASN1\Maps\RevokedCertificate::MAP], 'crlExtensions' => ['constant' => 0, 'optional' => \true, 'explicit' => \true] + \FluentSmtpLib\phpseclib3\File\ASN1\Maps\Extensions::MAP]]; +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/TBSCertificate.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/TBSCertificate.php new file mode 100644 index 0000000..f15ccbf --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/TBSCertificate.php @@ -0,0 +1,41 @@ + + * @copyright 2016 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\File\ASN1\Maps; + +use FluentSmtpLib\phpseclib3\File\ASN1; +/** + * TBSCertificate + * + * @author Jim Wigginton + */ +abstract class TBSCertificate +{ + // assert($TBSCertificate['children']['signature'] == $Certificate['children']['signatureAlgorithm']) + const MAP = ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_SEQUENCE, 'children' => [ + // technically, default implies optional, but we'll define it as being optional, none-the-less, just to + // reenforce that fact + 'version' => ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_INTEGER, 'constant' => 0, 'optional' => \true, 'explicit' => \true, 'mapping' => ['v1', 'v2', 'v3'], 'default' => 'v1'], + 'serialNumber' => \FluentSmtpLib\phpseclib3\File\ASN1\Maps\CertificateSerialNumber::MAP, + 'signature' => \FluentSmtpLib\phpseclib3\File\ASN1\Maps\AlgorithmIdentifier::MAP, + 'issuer' => \FluentSmtpLib\phpseclib3\File\ASN1\Maps\Name::MAP, + 'validity' => \FluentSmtpLib\phpseclib3\File\ASN1\Maps\Validity::MAP, + 'subject' => \FluentSmtpLib\phpseclib3\File\ASN1\Maps\Name::MAP, + 'subjectPublicKeyInfo' => \FluentSmtpLib\phpseclib3\File\ASN1\Maps\SubjectPublicKeyInfo::MAP, + // implicit means that the T in the TLV structure is to be rewritten, regardless of the type + 'issuerUniqueID' => ['constant' => 1, 'optional' => \true, 'implicit' => \true] + \FluentSmtpLib\phpseclib3\File\ASN1\Maps\UniqueIdentifier::MAP, + 'subjectUniqueID' => ['constant' => 2, 'optional' => \true, 'implicit' => \true] + \FluentSmtpLib\phpseclib3\File\ASN1\Maps\UniqueIdentifier::MAP, + // doesn't use the EXPLICIT keyword but if + // it's not IMPLICIT, it's EXPLICIT + 'extensions' => ['constant' => 3, 'optional' => \true, 'explicit' => \true] + \FluentSmtpLib\phpseclib3\File\ASN1\Maps\Extensions::MAP, + ]]; +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/TerminalIdentifier.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/TerminalIdentifier.php new file mode 100644 index 0000000..9b37e05 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/TerminalIdentifier.php @@ -0,0 +1,24 @@ + + * @copyright 2016 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\File\ASN1\Maps; + +use FluentSmtpLib\phpseclib3\File\ASN1; +/** + * TerminalIdentifier + * + * @author Jim Wigginton + */ +abstract class TerminalIdentifier +{ + const MAP = ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_PRINTABLE_STRING]; +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Time.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Time.php new file mode 100644 index 0000000..f6d4f8f --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Time.php @@ -0,0 +1,24 @@ + + * @copyright 2016 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\File\ASN1\Maps; + +use FluentSmtpLib\phpseclib3\File\ASN1; +/** + * Time + * + * @author Jim Wigginton + */ +abstract class Time +{ + const MAP = ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_CHOICE, 'children' => ['utcTime' => ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_UTC_TIME], 'generalTime' => ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_GENERALIZED_TIME]]]; +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Trinomial.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Trinomial.php new file mode 100644 index 0000000..ad74d28 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Trinomial.php @@ -0,0 +1,24 @@ + + * @copyright 2016 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\File\ASN1\Maps; + +use FluentSmtpLib\phpseclib3\File\ASN1; +/** + * Trinomial + * + * @author Jim Wigginton + */ +abstract class Trinomial +{ + const MAP = ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_INTEGER]; +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/UniqueIdentifier.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/UniqueIdentifier.php new file mode 100644 index 0000000..12112b5 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/UniqueIdentifier.php @@ -0,0 +1,24 @@ + + * @copyright 2016 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\File\ASN1\Maps; + +use FluentSmtpLib\phpseclib3\File\ASN1; +/** + * UniqueIdentifier + * + * @author Jim Wigginton + */ +abstract class UniqueIdentifier +{ + const MAP = ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_BIT_STRING]; +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/UserNotice.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/UserNotice.php new file mode 100644 index 0000000..0985681 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/UserNotice.php @@ -0,0 +1,24 @@ + + * @copyright 2016 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\File\ASN1\Maps; + +use FluentSmtpLib\phpseclib3\File\ASN1; +/** + * UserNotice + * + * @author Jim Wigginton + */ +abstract class UserNotice +{ + const MAP = ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_SEQUENCE, 'children' => ['noticeRef' => ['optional' => \true, 'implicit' => \true] + \FluentSmtpLib\phpseclib3\File\ASN1\Maps\NoticeReference::MAP, 'explicitText' => ['optional' => \true, 'implicit' => \true] + \FluentSmtpLib\phpseclib3\File\ASN1\Maps\DisplayText::MAP]]; +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Validity.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Validity.php new file mode 100644 index 0000000..fb246d0 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Validity.php @@ -0,0 +1,24 @@ + + * @copyright 2016 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\File\ASN1\Maps; + +use FluentSmtpLib\phpseclib3\File\ASN1; +/** + * Validity + * + * @author Jim Wigginton + */ +abstract class Validity +{ + const MAP = ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_SEQUENCE, 'children' => ['notBefore' => \FluentSmtpLib\phpseclib3\File\ASN1\Maps\Time::MAP, 'notAfter' => \FluentSmtpLib\phpseclib3\File\ASN1\Maps\Time::MAP]]; +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/netscape_ca_policy_url.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/netscape_ca_policy_url.php new file mode 100644 index 0000000..861c2b6 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/netscape_ca_policy_url.php @@ -0,0 +1,24 @@ + + * @copyright 2016 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\File\ASN1\Maps; + +use FluentSmtpLib\phpseclib3\File\ASN1; +/** + * netscape_ca_policy_url + * + * @author Jim Wigginton + */ +abstract class netscape_ca_policy_url +{ + const MAP = ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_IA5_STRING]; +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/netscape_cert_type.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/netscape_cert_type.php new file mode 100644 index 0000000..547cf2e --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/netscape_cert_type.php @@ -0,0 +1,26 @@ + + * @copyright 2016 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\File\ASN1\Maps; + +use FluentSmtpLib\phpseclib3\File\ASN1; +/** + * netscape_cert_type + * + * mapping is from + * + * @author Jim Wigginton + */ +abstract class netscape_cert_type +{ + const MAP = ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_BIT_STRING, 'mapping' => ['SSLClient', 'SSLServer', 'Email', 'ObjectSigning', 'Reserved', 'SSLCA', 'EmailCA', 'ObjectSigningCA']]; +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/netscape_comment.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/netscape_comment.php new file mode 100644 index 0000000..29e3b49 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/netscape_comment.php @@ -0,0 +1,24 @@ + + * @copyright 2016 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\File\ASN1\Maps; + +use FluentSmtpLib\phpseclib3\File\ASN1; +/** + * netscape_comment + * + * @author Jim Wigginton + */ +abstract class netscape_comment +{ + const MAP = ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_IA5_STRING]; +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/X509.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/X509.php new file mode 100644 index 0000000..181f301 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/File/X509.php @@ -0,0 +1,3520 @@ + + * @copyright 2012 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\File; + +use FluentSmtpLib\phpseclib3\Common\Functions\Strings; +use FluentSmtpLib\phpseclib3\Crypt\Common\PrivateKey; +use FluentSmtpLib\phpseclib3\Crypt\Common\PublicKey; +use FluentSmtpLib\phpseclib3\Crypt\DSA; +use FluentSmtpLib\phpseclib3\Crypt\EC; +use FluentSmtpLib\phpseclib3\Crypt\Hash; +use FluentSmtpLib\phpseclib3\Crypt\PublicKeyLoader; +use FluentSmtpLib\phpseclib3\Crypt\Random; +use FluentSmtpLib\phpseclib3\Crypt\RSA; +use FluentSmtpLib\phpseclib3\Crypt\RSA\Formats\Keys\PSS; +use FluentSmtpLib\phpseclib3\Exception\UnsupportedAlgorithmException; +use FluentSmtpLib\phpseclib3\File\ASN1\Element; +use FluentSmtpLib\phpseclib3\File\ASN1\Maps; +use FluentSmtpLib\phpseclib3\Math\BigInteger; +/** + * Pure-PHP X.509 Parser + * + * @author Jim Wigginton + */ +class X509 +{ + /** + * Flag to only accept signatures signed by certificate authorities + * + * Not really used anymore but retained all the same to suppress E_NOTICEs from old installs + * + */ + const VALIDATE_SIGNATURE_BY_CA = 1; + /** + * Return internal array representation + * + * @see \phpseclib3\File\X509::getDN() + */ + const DN_ARRAY = 0; + /** + * Return string + * + * @see \phpseclib3\File\X509::getDN() + */ + const DN_STRING = 1; + /** + * Return ASN.1 name string + * + * @see \phpseclib3\File\X509::getDN() + */ + const DN_ASN1 = 2; + /** + * Return OpenSSL compatible array + * + * @see \phpseclib3\File\X509::getDN() + */ + const DN_OPENSSL = 3; + /** + * Return canonical ASN.1 RDNs string + * + * @see \phpseclib3\File\X509::getDN() + */ + const DN_CANON = 4; + /** + * Return name hash for file indexing + * + * @see \phpseclib3\File\X509::getDN() + */ + const DN_HASH = 5; + /** + * Save as PEM + * + * ie. a base64-encoded PEM with a header and a footer + * + * @see \phpseclib3\File\X509::saveX509() + * @see \phpseclib3\File\X509::saveCSR() + * @see \phpseclib3\File\X509::saveCRL() + */ + const FORMAT_PEM = 0; + /** + * Save as DER + * + * @see \phpseclib3\File\X509::saveX509() + * @see \phpseclib3\File\X509::saveCSR() + * @see \phpseclib3\File\X509::saveCRL() + */ + const FORMAT_DER = 1; + /** + * Save as a SPKAC + * + * @see \phpseclib3\File\X509::saveX509() + * @see \phpseclib3\File\X509::saveCSR() + * @see \phpseclib3\File\X509::saveCRL() + * + * Only works on CSRs. Not currently supported. + */ + const FORMAT_SPKAC = 2; + /** + * Auto-detect the format + * + * Used only by the load*() functions + * + * @see \phpseclib3\File\X509::saveX509() + * @see \phpseclib3\File\X509::saveCSR() + * @see \phpseclib3\File\X509::saveCRL() + */ + const FORMAT_AUTO_DETECT = 3; + /** + * Attribute value disposition. + * If disposition is >= 0, this is the index of the target value. + */ + const ATTR_ALL = -1; + // All attribute values (array). + const ATTR_APPEND = -2; + // Add a value. + const ATTR_REPLACE = -3; + // Clear first, then add a value. + /** + * Distinguished Name + * + * @var array + */ + private $dn; + /** + * Public key + * + * @var string|PublicKey + */ + private $publicKey; + /** + * Private key + * + * @var string|PrivateKey + */ + private $privateKey; + /** + * The certificate authorities + * + * @var array + */ + private $CAs = []; + /** + * The currently loaded certificate + * + * @var array + */ + private $currentCert; + /** + * The signature subject + * + * There's no guarantee \phpseclib3\File\X509 is going to re-encode an X.509 cert in the same way it was originally + * encoded so we take save the portion of the original cert that the signature would have made for. + * + * @var string + */ + private $signatureSubject; + /** + * Certificate Start Date + * + * @var string + */ + private $startDate; + /** + * Certificate End Date + * + * @var string|Element + */ + private $endDate; + /** + * Serial Number + * + * @var string + */ + private $serialNumber; + /** + * Key Identifier + * + * See {@link http://tools.ietf.org/html/rfc5280#section-4.2.1.1 RFC5280#section-4.2.1.1} and + * {@link http://tools.ietf.org/html/rfc5280#section-4.2.1.2 RFC5280#section-4.2.1.2}. + * + * @var string + */ + private $currentKeyIdentifier; + /** + * CA Flag + * + * @var bool + */ + private $caFlag = \false; + /** + * SPKAC Challenge + * + * @var string + */ + private $challenge; + /** + * @var array + */ + private $extensionValues = []; + /** + * OIDs loaded + * + * @var bool + */ + private static $oidsLoaded = \false; + /** + * Recursion Limit + * + * @var int + */ + private static $recur_limit = 5; + /** + * URL fetch flag + * + * @var bool + */ + private static $disable_url_fetch = \false; + /** + * @var array + */ + private static $extensions = []; + /** + * @var ?array + */ + private $ipAddresses = null; + /** + * @var ?array + */ + private $domains = null; + /** + * Default Constructor. + * + * @return X509 + */ + public function __construct() + { + // Explicitly Tagged Module, 1988 Syntax + // http://tools.ietf.org/html/rfc5280#appendix-A.1 + if (!self::$oidsLoaded) { + // OIDs from RFC5280 and those RFCs mentioned in RFC5280#section-4.1.1.2 + \FluentSmtpLib\phpseclib3\File\ASN1::loadOIDs([ + //'id-pkix' => '1.3.6.1.5.5.7', + //'id-pe' => '1.3.6.1.5.5.7.1', + //'id-qt' => '1.3.6.1.5.5.7.2', + //'id-kp' => '1.3.6.1.5.5.7.3', + //'id-ad' => '1.3.6.1.5.5.7.48', + 'id-qt-cps' => '1.3.6.1.5.5.7.2.1', + 'id-qt-unotice' => '1.3.6.1.5.5.7.2.2', + 'id-ad-ocsp' => '1.3.6.1.5.5.7.48.1', + 'id-ad-caIssuers' => '1.3.6.1.5.5.7.48.2', + 'id-ad-timeStamping' => '1.3.6.1.5.5.7.48.3', + 'id-ad-caRepository' => '1.3.6.1.5.5.7.48.5', + //'id-at' => '2.5.4', + 'id-at-name' => '2.5.4.41', + 'id-at-surname' => '2.5.4.4', + 'id-at-givenName' => '2.5.4.42', + 'id-at-initials' => '2.5.4.43', + 'id-at-generationQualifier' => '2.5.4.44', + 'id-at-commonName' => '2.5.4.3', + 'id-at-localityName' => '2.5.4.7', + 'id-at-stateOrProvinceName' => '2.5.4.8', + 'id-at-organizationName' => '2.5.4.10', + 'id-at-organizationalUnitName' => '2.5.4.11', + 'id-at-title' => '2.5.4.12', + 'id-at-description' => '2.5.4.13', + 'id-at-dnQualifier' => '2.5.4.46', + 'id-at-countryName' => '2.5.4.6', + 'id-at-serialNumber' => '2.5.4.5', + 'id-at-pseudonym' => '2.5.4.65', + 'id-at-postalCode' => '2.5.4.17', + 'id-at-streetAddress' => '2.5.4.9', + 'id-at-uniqueIdentifier' => '2.5.4.45', + 'id-at-role' => '2.5.4.72', + 'id-at-postalAddress' => '2.5.4.16', + 'jurisdictionOfIncorporationCountryName' => '1.3.6.1.4.1.311.60.2.1.3', + 'jurisdictionOfIncorporationStateOrProvinceName' => '1.3.6.1.4.1.311.60.2.1.2', + 'jurisdictionLocalityName' => '1.3.6.1.4.1.311.60.2.1.1', + 'id-at-businessCategory' => '2.5.4.15', + //'id-domainComponent' => '0.9.2342.19200300.100.1.25', + //'pkcs-9' => '1.2.840.113549.1.9', + 'pkcs-9-at-emailAddress' => '1.2.840.113549.1.9.1', + //'id-ce' => '2.5.29', + 'id-ce-authorityKeyIdentifier' => '2.5.29.35', + 'id-ce-subjectKeyIdentifier' => '2.5.29.14', + 'id-ce-keyUsage' => '2.5.29.15', + 'id-ce-privateKeyUsagePeriod' => '2.5.29.16', + 'id-ce-certificatePolicies' => '2.5.29.32', + //'anyPolicy' => '2.5.29.32.0', + 'id-ce-policyMappings' => '2.5.29.33', + 'id-ce-subjectAltName' => '2.5.29.17', + 'id-ce-issuerAltName' => '2.5.29.18', + 'id-ce-subjectDirectoryAttributes' => '2.5.29.9', + 'id-ce-basicConstraints' => '2.5.29.19', + 'id-ce-nameConstraints' => '2.5.29.30', + 'id-ce-policyConstraints' => '2.5.29.36', + 'id-ce-cRLDistributionPoints' => '2.5.29.31', + 'id-ce-extKeyUsage' => '2.5.29.37', + //'anyExtendedKeyUsage' => '2.5.29.37.0', + 'id-kp-serverAuth' => '1.3.6.1.5.5.7.3.1', + 'id-kp-clientAuth' => '1.3.6.1.5.5.7.3.2', + 'id-kp-codeSigning' => '1.3.6.1.5.5.7.3.3', + 'id-kp-emailProtection' => '1.3.6.1.5.5.7.3.4', + 'id-kp-timeStamping' => '1.3.6.1.5.5.7.3.8', + 'id-kp-OCSPSigning' => '1.3.6.1.5.5.7.3.9', + 'id-ce-inhibitAnyPolicy' => '2.5.29.54', + 'id-ce-freshestCRL' => '2.5.29.46', + 'id-pe-authorityInfoAccess' => '1.3.6.1.5.5.7.1.1', + 'id-pe-subjectInfoAccess' => '1.3.6.1.5.5.7.1.11', + 'id-ce-cRLNumber' => '2.5.29.20', + 'id-ce-issuingDistributionPoint' => '2.5.29.28', + 'id-ce-deltaCRLIndicator' => '2.5.29.27', + 'id-ce-cRLReasons' => '2.5.29.21', + 'id-ce-certificateIssuer' => '2.5.29.29', + 'id-ce-holdInstructionCode' => '2.5.29.23', + //'holdInstruction' => '1.2.840.10040.2', + 'id-holdinstruction-none' => '1.2.840.10040.2.1', + 'id-holdinstruction-callissuer' => '1.2.840.10040.2.2', + 'id-holdinstruction-reject' => '1.2.840.10040.2.3', + 'id-ce-invalidityDate' => '2.5.29.24', + 'rsaEncryption' => '1.2.840.113549.1.1.1', + 'md2WithRSAEncryption' => '1.2.840.113549.1.1.2', + 'md5WithRSAEncryption' => '1.2.840.113549.1.1.4', + 'sha1WithRSAEncryption' => '1.2.840.113549.1.1.5', + 'sha224WithRSAEncryption' => '1.2.840.113549.1.1.14', + 'sha256WithRSAEncryption' => '1.2.840.113549.1.1.11', + 'sha384WithRSAEncryption' => '1.2.840.113549.1.1.12', + 'sha512WithRSAEncryption' => '1.2.840.113549.1.1.13', + 'id-ecPublicKey' => '1.2.840.10045.2.1', + 'ecdsa-with-SHA1' => '1.2.840.10045.4.1', + // from https://tools.ietf.org/html/rfc5758#section-3.2 + 'ecdsa-with-SHA224' => '1.2.840.10045.4.3.1', + 'ecdsa-with-SHA256' => '1.2.840.10045.4.3.2', + 'ecdsa-with-SHA384' => '1.2.840.10045.4.3.3', + 'ecdsa-with-SHA512' => '1.2.840.10045.4.3.4', + 'id-dsa' => '1.2.840.10040.4.1', + 'id-dsa-with-sha1' => '1.2.840.10040.4.3', + // from https://tools.ietf.org/html/rfc5758#section-3.1 + 'id-dsa-with-sha224' => '2.16.840.1.101.3.4.3.1', + 'id-dsa-with-sha256' => '2.16.840.1.101.3.4.3.2', + // from https://tools.ietf.org/html/rfc8410: + 'id-Ed25519' => '1.3.101.112', + 'id-Ed448' => '1.3.101.113', + 'id-RSASSA-PSS' => '1.2.840.113549.1.1.10', + //'id-sha224' => '2.16.840.1.101.3.4.2.4', + //'id-sha256' => '2.16.840.1.101.3.4.2.1', + //'id-sha384' => '2.16.840.1.101.3.4.2.2', + //'id-sha512' => '2.16.840.1.101.3.4.2.3', + //'id-GostR3411-94-with-GostR3410-94' => '1.2.643.2.2.4', + //'id-GostR3411-94-with-GostR3410-2001' => '1.2.643.2.2.3', + //'id-GostR3410-2001' => '1.2.643.2.2.20', + //'id-GostR3410-94' => '1.2.643.2.2.19', + // Netscape Object Identifiers from "Netscape Certificate Extensions" + 'netscape' => '2.16.840.1.113730', + 'netscape-cert-extension' => '2.16.840.1.113730.1', + 'netscape-cert-type' => '2.16.840.1.113730.1.1', + 'netscape-comment' => '2.16.840.1.113730.1.13', + 'netscape-ca-policy-url' => '2.16.840.1.113730.1.8', + // the following are X.509 extensions not supported by phpseclib + 'id-pe-logotype' => '1.3.6.1.5.5.7.1.12', + 'entrustVersInfo' => '1.2.840.113533.7.65.0', + 'verisignPrivate' => '2.16.840.1.113733.1.6.9', + // for Certificate Signing Requests + // see http://tools.ietf.org/html/rfc2985 + 'pkcs-9-at-unstructuredName' => '1.2.840.113549.1.9.2', + // PKCS #9 unstructured name + 'pkcs-9-at-challengePassword' => '1.2.840.113549.1.9.7', + // Challenge password for certificate revocations + 'pkcs-9-at-extensionRequest' => '1.2.840.113549.1.9.14', + ]); + } + } + /** + * Load X.509 certificate + * + * Returns an associative array describing the X.509 cert or a false if the cert failed to load + * + * @param array|string $cert + * @param int $mode + * @return mixed + */ + public function loadX509($cert, $mode = self::FORMAT_AUTO_DETECT) + { + if (\is_array($cert) && isset($cert['tbsCertificate'])) { + unset($this->currentCert); + unset($this->currentKeyIdentifier); + $this->dn = $cert['tbsCertificate']['subject']; + if (!isset($this->dn)) { + return \false; + } + $this->currentCert = $cert; + $currentKeyIdentifier = $this->getExtension('id-ce-subjectKeyIdentifier'); + $this->currentKeyIdentifier = \is_string($currentKeyIdentifier) ? $currentKeyIdentifier : null; + unset($this->signatureSubject); + return $cert; + } + if ($mode != self::FORMAT_DER) { + $newcert = \FluentSmtpLib\phpseclib3\File\ASN1::extractBER($cert); + if ($mode == self::FORMAT_PEM && $cert == $newcert) { + return \false; + } + $cert = $newcert; + } + if ($cert === \false) { + $this->currentCert = \false; + return \false; + } + $decoded = \FluentSmtpLib\phpseclib3\File\ASN1::decodeBER($cert); + if ($decoded) { + $x509 = \FluentSmtpLib\phpseclib3\File\ASN1::asn1map($decoded[0], \FluentSmtpLib\phpseclib3\File\ASN1\Maps\Certificate::MAP); + } + if (!isset($x509) || $x509 === \false) { + $this->currentCert = \false; + return \false; + } + $this->signatureSubject = \substr($cert, $decoded[0]['content'][0]['start'], $decoded[0]['content'][0]['length']); + if ($this->isSubArrayValid($x509, 'tbsCertificate/extensions')) { + $this->mapInExtensions($x509, 'tbsCertificate/extensions'); + } + $this->mapInDNs($x509, 'tbsCertificate/issuer/rdnSequence'); + $this->mapInDNs($x509, 'tbsCertificate/subject/rdnSequence'); + $key = $x509['tbsCertificate']['subjectPublicKeyInfo']; + $key = \FluentSmtpLib\phpseclib3\File\ASN1::encodeDER($key, \FluentSmtpLib\phpseclib3\File\ASN1\Maps\SubjectPublicKeyInfo::MAP); + $x509['tbsCertificate']['subjectPublicKeyInfo']['subjectPublicKey'] = "-----BEGIN PUBLIC KEY-----\r\n" . \chunk_split(\base64_encode($key), 64) . "-----END PUBLIC KEY-----"; + $this->currentCert = $x509; + $this->dn = $x509['tbsCertificate']['subject']; + $currentKeyIdentifier = $this->getExtension('id-ce-subjectKeyIdentifier'); + $this->currentKeyIdentifier = \is_string($currentKeyIdentifier) ? $currentKeyIdentifier : null; + return $x509; + } + /** + * Save X.509 certificate + * + * @param array $cert + * @param int $format optional + * @return string + */ + public function saveX509(array $cert, $format = self::FORMAT_PEM) + { + if (!\is_array($cert) || !isset($cert['tbsCertificate'])) { + return \false; + } + switch (\true) { + // "case !$a: case !$b: break; default: whatever();" is the same thing as "if ($a && $b) whatever()" + case !($algorithm = $this->subArray($cert, 'tbsCertificate/subjectPublicKeyInfo/algorithm/algorithm')): + case \is_object($cert['tbsCertificate']['subjectPublicKeyInfo']['subjectPublicKey']): + break; + default: + $cert['tbsCertificate']['subjectPublicKeyInfo'] = new \FluentSmtpLib\phpseclib3\File\ASN1\Element(\base64_decode(\preg_replace('#-.+-|[\\r\\n]#', '', $cert['tbsCertificate']['subjectPublicKeyInfo']['subjectPublicKey']))); + } + $filters = []; + $type_utf8_string = ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_UTF8_STRING]; + $filters['tbsCertificate']['signature']['parameters'] = $type_utf8_string; + $filters['tbsCertificate']['signature']['issuer']['rdnSequence']['value'] = $type_utf8_string; + $filters['tbsCertificate']['issuer']['rdnSequence']['value'] = $type_utf8_string; + $filters['tbsCertificate']['subject']['rdnSequence']['value'] = $type_utf8_string; + $filters['tbsCertificate']['subjectPublicKeyInfo']['algorithm']['parameters'] = $type_utf8_string; + $filters['signatureAlgorithm']['parameters'] = $type_utf8_string; + $filters['authorityCertIssuer']['directoryName']['rdnSequence']['value'] = $type_utf8_string; + //$filters['policyQualifiers']['qualifier'] = $type_utf8_string; + $filters['distributionPoint']['fullName']['directoryName']['rdnSequence']['value'] = $type_utf8_string; + $filters['directoryName']['rdnSequence']['value'] = $type_utf8_string; + foreach (self::$extensions as $extension) { + $filters['tbsCertificate']['extensions'][] = $extension; + } + /* in the case of policyQualifiers/qualifier, the type has to be \phpseclib3\File\ASN1::TYPE_IA5_STRING. + \phpseclib3\File\ASN1::TYPE_PRINTABLE_STRING will cause OpenSSL's X.509 parser to spit out random + characters. + */ + $filters['policyQualifiers']['qualifier'] = ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_IA5_STRING]; + \FluentSmtpLib\phpseclib3\File\ASN1::setFilters($filters); + $this->mapOutExtensions($cert, 'tbsCertificate/extensions'); + $this->mapOutDNs($cert, 'tbsCertificate/issuer/rdnSequence'); + $this->mapOutDNs($cert, 'tbsCertificate/subject/rdnSequence'); + $cert = \FluentSmtpLib\phpseclib3\File\ASN1::encodeDER($cert, \FluentSmtpLib\phpseclib3\File\ASN1\Maps\Certificate::MAP); + switch ($format) { + case self::FORMAT_DER: + return $cert; + // case self::FORMAT_PEM: + default: + return "-----BEGIN CERTIFICATE-----\r\n" . \chunk_split(\FluentSmtpLib\phpseclib3\Common\Functions\Strings::base64_encode($cert), 64) . '-----END CERTIFICATE-----'; + } + } + /** + * Map extension values from octet string to extension-specific internal + * format. + * + * @param array $root (by reference) + * @param string $path + */ + private function mapInExtensions(array &$root, $path) + { + $extensions =& $this->subArrayUnchecked($root, $path); + if ($extensions) { + for ($i = 0; $i < \count($extensions); $i++) { + $id = $extensions[$i]['extnId']; + $value =& $extensions[$i]['extnValue']; + /* [extnValue] contains the DER encoding of an ASN.1 value + corresponding to the extension type identified by extnID */ + $map = $this->getMapping($id); + if (!\is_bool($map)) { + $decoder = $id == 'id-ce-nameConstraints' ? [static::class, 'decodeNameConstraintIP'] : [static::class, 'decodeIP']; + $decoded = \FluentSmtpLib\phpseclib3\File\ASN1::decodeBER($value); + if (!$decoded) { + continue; + } + $mapped = \FluentSmtpLib\phpseclib3\File\ASN1::asn1map($decoded[0], $map, ['iPAddress' => $decoder]); + $value = $mapped === \false ? $decoded[0] : $mapped; + if ($id == 'id-ce-certificatePolicies') { + for ($j = 0; $j < \count($value); $j++) { + if (!isset($value[$j]['policyQualifiers'])) { + continue; + } + for ($k = 0; $k < \count($value[$j]['policyQualifiers']); $k++) { + $subid = $value[$j]['policyQualifiers'][$k]['policyQualifierId']; + $map = $this->getMapping($subid); + $subvalue =& $value[$j]['policyQualifiers'][$k]['qualifier']; + if ($map !== \false) { + $decoded = \FluentSmtpLib\phpseclib3\File\ASN1::decodeBER($subvalue); + if (!$decoded) { + continue; + } + $mapped = \FluentSmtpLib\phpseclib3\File\ASN1::asn1map($decoded[0], $map); + $subvalue = $mapped === \false ? $decoded[0] : $mapped; + } + } + } + } + } + } + } + } + /** + * Map extension values from extension-specific internal format to + * octet string. + * + * @param array $root (by reference) + * @param string $path + */ + private function mapOutExtensions(array &$root, $path) + { + $extensions =& $this->subArray($root, $path, !empty($this->extensionValues)); + foreach ($this->extensionValues as $id => $data) { + \extract($data); + $newext = ['extnId' => $id, 'extnValue' => $value, 'critical' => $critical]; + if ($replace) { + foreach ($extensions as $key => $value) { + if ($value['extnId'] == $id) { + $extensions[$key] = $newext; + continue 2; + } + } + } + $extensions[] = $newext; + } + if (\is_array($extensions)) { + $size = \count($extensions); + for ($i = 0; $i < $size; $i++) { + if ($extensions[$i] instanceof \FluentSmtpLib\phpseclib3\File\ASN1\Element) { + continue; + } + $id = $extensions[$i]['extnId']; + $value =& $extensions[$i]['extnValue']; + switch ($id) { + case 'id-ce-certificatePolicies': + for ($j = 0; $j < \count($value); $j++) { + if (!isset($value[$j]['policyQualifiers'])) { + continue; + } + for ($k = 0; $k < \count($value[$j]['policyQualifiers']); $k++) { + $subid = $value[$j]['policyQualifiers'][$k]['policyQualifierId']; + $map = $this->getMapping($subid); + $subvalue =& $value[$j]['policyQualifiers'][$k]['qualifier']; + if ($map !== \false) { + // by default \phpseclib3\File\ASN1 will try to render qualifier as a \phpseclib3\File\ASN1::TYPE_IA5_STRING since it's + // actual type is \phpseclib3\File\ASN1::TYPE_ANY + $subvalue = new \FluentSmtpLib\phpseclib3\File\ASN1\Element(\FluentSmtpLib\phpseclib3\File\ASN1::encodeDER($subvalue, $map)); + } + } + } + break; + case 'id-ce-authorityKeyIdentifier': + // use 00 as the serial number instead of an empty string + if (isset($value['authorityCertSerialNumber'])) { + if ($value['authorityCertSerialNumber']->toBytes() == '') { + $temp = \chr(\FluentSmtpLib\phpseclib3\File\ASN1::CLASS_CONTEXT_SPECIFIC << 6 | 2) . "\x01\x00"; + $value['authorityCertSerialNumber'] = new \FluentSmtpLib\phpseclib3\File\ASN1\Element($temp); + } + } + } + /* [extnValue] contains the DER encoding of an ASN.1 value + corresponding to the extension type identified by extnID */ + $map = $this->getMapping($id); + if (\is_bool($map)) { + if (!$map) { + //user_error($id . ' is not a currently supported extension'); + unset($extensions[$i]); + } + } else { + $value = \FluentSmtpLib\phpseclib3\File\ASN1::encodeDER($value, $map, ['iPAddress' => [static::class, 'encodeIP']]); + } + } + } + } + /** + * Map attribute values from ANY type to attribute-specific internal + * format. + * + * @param array $root (by reference) + * @param string $path + */ + private function mapInAttributes(&$root, $path) + { + $attributes =& $this->subArray($root, $path); + if (\is_array($attributes)) { + for ($i = 0; $i < \count($attributes); $i++) { + $id = $attributes[$i]['type']; + /* $value contains the DER encoding of an ASN.1 value + corresponding to the attribute type identified by type */ + $map = $this->getMapping($id); + if (\is_array($attributes[$i]['value'])) { + $values =& $attributes[$i]['value']; + for ($j = 0; $j < \count($values); $j++) { + $value = \FluentSmtpLib\phpseclib3\File\ASN1::encodeDER($values[$j], \FluentSmtpLib\phpseclib3\File\ASN1\Maps\AttributeValue::MAP); + $decoded = \FluentSmtpLib\phpseclib3\File\ASN1::decodeBER($value); + if (!\is_bool($map)) { + if (!$decoded) { + continue; + } + $mapped = \FluentSmtpLib\phpseclib3\File\ASN1::asn1map($decoded[0], $map); + if ($mapped !== \false) { + $values[$j] = $mapped; + } + if ($id == 'pkcs-9-at-extensionRequest' && $this->isSubArrayValid($values, $j)) { + $this->mapInExtensions($values, $j); + } + } elseif ($map) { + $values[$j] = $value; + } + } + } + } + } + } + /** + * Map attribute values from attribute-specific internal format to + * ANY type. + * + * @param array $root (by reference) + * @param string $path + */ + private function mapOutAttributes(&$root, $path) + { + $attributes =& $this->subArray($root, $path); + if (\is_array($attributes)) { + $size = \count($attributes); + for ($i = 0; $i < $size; $i++) { + /* [value] contains the DER encoding of an ASN.1 value + corresponding to the attribute type identified by type */ + $id = $attributes[$i]['type']; + $map = $this->getMapping($id); + if ($map === \false) { + //user_error($id . ' is not a currently supported attribute', E_USER_NOTICE); + unset($attributes[$i]); + } elseif (\is_array($attributes[$i]['value'])) { + $values =& $attributes[$i]['value']; + for ($j = 0; $j < \count($values); $j++) { + switch ($id) { + case 'pkcs-9-at-extensionRequest': + $this->mapOutExtensions($values, $j); + break; + } + if (!\is_bool($map)) { + $temp = \FluentSmtpLib\phpseclib3\File\ASN1::encodeDER($values[$j], $map); + $decoded = \FluentSmtpLib\phpseclib3\File\ASN1::decodeBER($temp); + if (!$decoded) { + continue; + } + $values[$j] = \FluentSmtpLib\phpseclib3\File\ASN1::asn1map($decoded[0], \FluentSmtpLib\phpseclib3\File\ASN1\Maps\AttributeValue::MAP); + } + } + } + } + } + } + /** + * Map DN values from ANY type to DN-specific internal + * format. + * + * @param array $root (by reference) + * @param string $path + */ + private function mapInDNs(array &$root, $path) + { + $dns =& $this->subArray($root, $path); + if (\is_array($dns)) { + for ($i = 0; $i < \count($dns); $i++) { + for ($j = 0; $j < \count($dns[$i]); $j++) { + $type = $dns[$i][$j]['type']; + $value =& $dns[$i][$j]['value']; + if (\is_object($value) && $value instanceof \FluentSmtpLib\phpseclib3\File\ASN1\Element) { + $map = $this->getMapping($type); + if (!\is_bool($map)) { + $decoded = \FluentSmtpLib\phpseclib3\File\ASN1::decodeBER($value); + if (!$decoded) { + continue; + } + $value = \FluentSmtpLib\phpseclib3\File\ASN1::asn1map($decoded[0], $map); + } + } + } + } + } + } + /** + * Map DN values from DN-specific internal format to + * ANY type. + * + * @param array $root (by reference) + * @param string $path + */ + private function mapOutDNs(array &$root, $path) + { + $dns =& $this->subArray($root, $path); + if (\is_array($dns)) { + $size = \count($dns); + for ($i = 0; $i < $size; $i++) { + for ($j = 0; $j < \count($dns[$i]); $j++) { + $type = $dns[$i][$j]['type']; + $value =& $dns[$i][$j]['value']; + if (\is_object($value) && $value instanceof \FluentSmtpLib\phpseclib3\File\ASN1\Element) { + continue; + } + $map = $this->getMapping($type); + if (!\is_bool($map)) { + $value = new \FluentSmtpLib\phpseclib3\File\ASN1\Element(\FluentSmtpLib\phpseclib3\File\ASN1::encodeDER($value, $map)); + } + } + } + } + } + /** + * Associate an extension ID to an extension mapping + * + * @param string $extnId + * @return mixed + */ + private function getMapping($extnId) + { + if (!\is_string($extnId)) { + // eg. if it's a \phpseclib3\File\ASN1\Element object + return \true; + } + if (isset(self::$extensions[$extnId])) { + return self::$extensions[$extnId]; + } + switch ($extnId) { + case 'id-ce-keyUsage': + return \FluentSmtpLib\phpseclib3\File\ASN1\Maps\KeyUsage::MAP; + case 'id-ce-basicConstraints': + return \FluentSmtpLib\phpseclib3\File\ASN1\Maps\BasicConstraints::MAP; + case 'id-ce-subjectKeyIdentifier': + return \FluentSmtpLib\phpseclib3\File\ASN1\Maps\KeyIdentifier::MAP; + case 'id-ce-cRLDistributionPoints': + return \FluentSmtpLib\phpseclib3\File\ASN1\Maps\CRLDistributionPoints::MAP; + case 'id-ce-authorityKeyIdentifier': + return \FluentSmtpLib\phpseclib3\File\ASN1\Maps\AuthorityKeyIdentifier::MAP; + case 'id-ce-certificatePolicies': + return \FluentSmtpLib\phpseclib3\File\ASN1\Maps\CertificatePolicies::MAP; + case 'id-ce-extKeyUsage': + return \FluentSmtpLib\phpseclib3\File\ASN1\Maps\ExtKeyUsageSyntax::MAP; + case 'id-pe-authorityInfoAccess': + return \FluentSmtpLib\phpseclib3\File\ASN1\Maps\AuthorityInfoAccessSyntax::MAP; + case 'id-ce-subjectAltName': + return \FluentSmtpLib\phpseclib3\File\ASN1\Maps\SubjectAltName::MAP; + case 'id-ce-subjectDirectoryAttributes': + return \FluentSmtpLib\phpseclib3\File\ASN1\Maps\SubjectDirectoryAttributes::MAP; + case 'id-ce-privateKeyUsagePeriod': + return \FluentSmtpLib\phpseclib3\File\ASN1\Maps\PrivateKeyUsagePeriod::MAP; + case 'id-ce-issuerAltName': + return \FluentSmtpLib\phpseclib3\File\ASN1\Maps\IssuerAltName::MAP; + case 'id-ce-policyMappings': + return \FluentSmtpLib\phpseclib3\File\ASN1\Maps\PolicyMappings::MAP; + case 'id-ce-nameConstraints': + return \FluentSmtpLib\phpseclib3\File\ASN1\Maps\NameConstraints::MAP; + case 'netscape-cert-type': + return \FluentSmtpLib\phpseclib3\File\ASN1\Maps\netscape_cert_type::MAP; + case 'netscape-comment': + return \FluentSmtpLib\phpseclib3\File\ASN1\Maps\netscape_comment::MAP; + case 'netscape-ca-policy-url': + return \FluentSmtpLib\phpseclib3\File\ASN1\Maps\netscape_ca_policy_url::MAP; + // since id-qt-cps isn't a constructed type it will have already been decoded as a string by the time it gets + // back around to asn1map() and we don't want it decoded again. + //case 'id-qt-cps': + // return Maps\CPSuri::MAP; + case 'id-qt-unotice': + return \FluentSmtpLib\phpseclib3\File\ASN1\Maps\UserNotice::MAP; + // the following OIDs are unsupported but we don't want them to give notices when calling saveX509(). + case 'id-pe-logotype': + // http://www.ietf.org/rfc/rfc3709.txt + case 'entrustVersInfo': + // http://support.microsoft.com/kb/287547 + case '1.3.6.1.4.1.311.20.2': + // szOID_ENROLL_CERTTYPE_EXTENSION + case '1.3.6.1.4.1.311.21.1': + // szOID_CERTSRV_CA_VERSION + // "SET Secure Electronic Transaction Specification" + // http://www.maithean.com/docs/set_bk3.pdf + case '2.23.42.7.0': + // id-set-hashedRootKey + // "Certificate Transparency" + // https://tools.ietf.org/html/rfc6962 + case '1.3.6.1.4.1.11129.2.4.2': + // "Qualified Certificate statements" + // https://tools.ietf.org/html/rfc3739#section-3.2.6 + case '1.3.6.1.5.5.7.1.3': + return \true; + // CSR attributes + case 'pkcs-9-at-unstructuredName': + return \FluentSmtpLib\phpseclib3\File\ASN1\Maps\PKCS9String::MAP; + case 'pkcs-9-at-challengePassword': + return \FluentSmtpLib\phpseclib3\File\ASN1\Maps\DirectoryString::MAP; + case 'pkcs-9-at-extensionRequest': + return \FluentSmtpLib\phpseclib3\File\ASN1\Maps\Extensions::MAP; + // CRL extensions. + case 'id-ce-cRLNumber': + return \FluentSmtpLib\phpseclib3\File\ASN1\Maps\CRLNumber::MAP; + case 'id-ce-deltaCRLIndicator': + return \FluentSmtpLib\phpseclib3\File\ASN1\Maps\CRLNumber::MAP; + case 'id-ce-issuingDistributionPoint': + return \FluentSmtpLib\phpseclib3\File\ASN1\Maps\IssuingDistributionPoint::MAP; + case 'id-ce-freshestCRL': + return \FluentSmtpLib\phpseclib3\File\ASN1\Maps\CRLDistributionPoints::MAP; + case 'id-ce-cRLReasons': + return \FluentSmtpLib\phpseclib3\File\ASN1\Maps\CRLReason::MAP; + case 'id-ce-invalidityDate': + return \FluentSmtpLib\phpseclib3\File\ASN1\Maps\InvalidityDate::MAP; + case 'id-ce-certificateIssuer': + return \FluentSmtpLib\phpseclib3\File\ASN1\Maps\CertificateIssuer::MAP; + case 'id-ce-holdInstructionCode': + return \FluentSmtpLib\phpseclib3\File\ASN1\Maps\HoldInstructionCode::MAP; + case 'id-at-postalAddress': + return \FluentSmtpLib\phpseclib3\File\ASN1\Maps\PostalAddress::MAP; + } + return \false; + } + /** + * Load an X.509 certificate as a certificate authority + * + * @param string $cert + * @return bool + */ + public function loadCA($cert) + { + $olddn = $this->dn; + $oldcert = $this->currentCert; + $oldsigsubj = $this->signatureSubject; + $oldkeyid = $this->currentKeyIdentifier; + $cert = $this->loadX509($cert); + if (!$cert) { + $this->dn = $olddn; + $this->currentCert = $oldcert; + $this->signatureSubject = $oldsigsubj; + $this->currentKeyIdentifier = $oldkeyid; + return \false; + } + /* From RFC5280 "PKIX Certificate and CRL Profile": + + If the keyUsage extension is present, then the subject public key + MUST NOT be used to verify signatures on certificates or CRLs unless + the corresponding keyCertSign or cRLSign bit is set. */ + //$keyUsage = $this->getExtension('id-ce-keyUsage'); + //if ($keyUsage && !in_array('keyCertSign', $keyUsage)) { + // return false; + //} + /* From RFC5280 "PKIX Certificate and CRL Profile": + + The cA boolean indicates whether the certified public key may be used + to verify certificate signatures. If the cA boolean is not asserted, + then the keyCertSign bit in the key usage extension MUST NOT be + asserted. If the basic constraints extension is not present in a + version 3 certificate, or the extension is present but the cA boolean + is not asserted, then the certified public key MUST NOT be used to + verify certificate signatures. */ + //$basicConstraints = $this->getExtension('id-ce-basicConstraints'); + //if (!$basicConstraints || !$basicConstraints['cA']) { + // return false; + //} + $this->CAs[] = $cert; + $this->dn = $olddn; + $this->currentCert = $oldcert; + $this->signatureSubject = $oldsigsubj; + return \true; + } + /** + * Validate an X.509 certificate against a URL + * + * From RFC2818 "HTTP over TLS": + * + * Matching is performed using the matching rules specified by + * [RFC2459]. If more than one identity of a given type is present in + * the certificate (e.g., more than one dNSName name, a match in any one + * of the set is considered acceptable.) Names may contain the wildcard + * character * which is considered to match any single domain name + * component or component fragment. E.g., *.a.com matches foo.a.com but + * not bar.foo.a.com. f*.com matches foo.com but not bar.com. + * + * @param string $url + * @return bool + */ + public function validateURL($url) + { + if (!\is_array($this->currentCert) || !isset($this->currentCert['tbsCertificate'])) { + return \false; + } + $components = \parse_url($url); + if (!isset($components['host'])) { + return \false; + } + if ($names = $this->getExtension('id-ce-subjectAltName')) { + foreach ($names as $name) { + foreach ($name as $key => $value) { + $value = \preg_quote($value); + $value = \str_replace('\\*', '[^.]*', $value); + switch ($key) { + case 'dNSName': + /* From RFC2818 "HTTP over TLS": + + If a subjectAltName extension of type dNSName is present, that MUST + be used as the identity. Otherwise, the (most specific) Common Name + field in the Subject field of the certificate MUST be used. Although + the use of the Common Name is existing practice, it is deprecated and + Certification Authorities are encouraged to use the dNSName instead. */ + if (\preg_match('#^' . $value . '$#', $components['host'])) { + return \true; + } + break; + case 'iPAddress': + /* From RFC2818 "HTTP over TLS": + + In some cases, the URI is specified as an IP address rather than a + hostname. In this case, the iPAddress subjectAltName must be present + in the certificate and must exactly match the IP in the URI. */ + if (\preg_match('#(?:\\d{1-3}\\.){4}#', $components['host'] . '.') && \preg_match('#^' . $value . '$#', $components['host'])) { + return \true; + } + } + } + } + return \false; + } + if ($value = $this->getDNProp('id-at-commonName')) { + $value = \str_replace(['.', '*'], ['\\.', '[^.]*'], $value[0]); + return \preg_match('#^' . $value . '$#', $components['host']) === 1; + } + return \false; + } + /** + * Validate a date + * + * If $date isn't defined it is assumed to be the current date. + * + * @param \DateTimeInterface|string $date optional + * @return bool + */ + public function validateDate($date = null) + { + if (!\is_array($this->currentCert) || !isset($this->currentCert['tbsCertificate'])) { + return \false; + } + if (!isset($date)) { + $date = new \DateTimeImmutable('now', new \DateTimeZone(@\date_default_timezone_get())); + } + $notBefore = $this->currentCert['tbsCertificate']['validity']['notBefore']; + $notBefore = isset($notBefore['generalTime']) ? $notBefore['generalTime'] : $notBefore['utcTime']; + $notAfter = $this->currentCert['tbsCertificate']['validity']['notAfter']; + $notAfter = isset($notAfter['generalTime']) ? $notAfter['generalTime'] : $notAfter['utcTime']; + if (\is_string($date)) { + $date = new \DateTimeImmutable($date, new \DateTimeZone(@\date_default_timezone_get())); + } + $notBefore = new \DateTimeImmutable($notBefore, new \DateTimeZone(@\date_default_timezone_get())); + $notAfter = new \DateTimeImmutable($notAfter, new \DateTimeZone(@\date_default_timezone_get())); + return $date >= $notBefore && $date <= $notAfter; + } + /** + * Fetches a URL + * + * @param string $url + * @return bool|string + */ + private static function fetchURL($url) + { + if (self::$disable_url_fetch) { + return \false; + } + $parts = \parse_url($url); + $data = ''; + switch ($parts['scheme']) { + case 'http': + $fsock = @\fsockopen($parts['host'], isset($parts['port']) ? $parts['port'] : 80); + if (!$fsock) { + return \false; + } + $path = $parts['path']; + if (isset($parts['query'])) { + $path .= '?' . $parts['query']; + } + \fputs($fsock, "GET {$path} HTTP/1.0\r\n"); + \fputs($fsock, "Host: {$parts['host']}\r\n\r\n"); + $line = \fgets($fsock, 1024); + if (\strlen($line) < 3) { + return \false; + } + \preg_match('#HTTP/1.\\d (\\d{3})#', $line, $temp); + if ($temp[1] != '200') { + return \false; + } + // skip the rest of the headers in the http response + while (!\feof($fsock) && \fgets($fsock, 1024) != "\r\n") { + } + while (!\feof($fsock)) { + $temp = \fread($fsock, 1024); + if ($temp === \false) { + return \false; + } + $data .= $temp; + } + break; + } + return $data; + } + /** + * Validates an intermediate cert as identified via authority info access extension + * + * See https://tools.ietf.org/html/rfc4325 for more info + * + * @param bool $caonly + * @param int $count + * @return bool + */ + private function testForIntermediate($caonly, $count) + { + $opts = $this->getExtension('id-pe-authorityInfoAccess'); + if (!\is_array($opts)) { + return \false; + } + foreach ($opts as $opt) { + if ($opt['accessMethod'] == 'id-ad-caIssuers') { + // accessLocation is a GeneralName. GeneralName fields support stuff like email addresses, IP addresses, LDAP, + // etc, but we're only supporting URI's. URI's and LDAP are the only thing https://tools.ietf.org/html/rfc4325 + // discusses + if (isset($opt['accessLocation']['uniformResourceIdentifier'])) { + $url = $opt['accessLocation']['uniformResourceIdentifier']; + break; + } + } + } + if (!isset($url)) { + return \false; + } + $cert = static::fetchURL($url); + if (!\is_string($cert)) { + return \false; + } + $parent = new static(); + $parent->CAs = $this->CAs; + /* + "Conforming applications that support HTTP or FTP for accessing + certificates MUST be able to accept .cer files and SHOULD be able + to accept .p7c files." -- https://tools.ietf.org/html/rfc4325 + + A .p7c file is 'a "certs-only" CMS message as specified in RFC 2797" + + These are currently unsupported + */ + if (!\is_array($parent->loadX509($cert))) { + return \false; + } + if (!$parent->validateSignatureCountable($caonly, ++$count)) { + return \false; + } + $this->CAs[] = $parent->currentCert; + //$this->loadCA($cert); + return \true; + } + /** + * Validate a signature + * + * Works on X.509 certs, CSR's and CRL's. + * Returns true if the signature is verified, false if it is not correct or null on error + * + * By default returns false for self-signed certs. Call validateSignature(false) to make this support + * self-signed. + * + * The behavior of this function is inspired by {@link http://php.net/openssl-verify openssl_verify}. + * + * @param bool $caonly optional + * @return mixed + */ + public function validateSignature($caonly = \true) + { + return $this->validateSignatureCountable($caonly, 0); + } + /** + * Validate a signature + * + * Performs said validation whilst keeping track of how many times validation method is called + * + * @param bool $caonly + * @param int $count + * @return mixed + */ + private function validateSignatureCountable($caonly, $count) + { + if (!\is_array($this->currentCert) || !isset($this->signatureSubject)) { + return null; + } + if ($count == self::$recur_limit) { + return \false; + } + /* TODO: + "emailAddress attribute values are not case-sensitive (e.g., "subscriber@example.com" is the same as "SUBSCRIBER@EXAMPLE.COM")." + -- http://tools.ietf.org/html/rfc5280#section-4.1.2.6 + + implement pathLenConstraint in the id-ce-basicConstraints extension */ + switch (\true) { + case isset($this->currentCert['tbsCertificate']): + // self-signed cert + switch (\true) { + case !\defined('FluentSmtpLib\\FILE_X509_IGNORE_TYPE') && $this->currentCert['tbsCertificate']['issuer'] === $this->currentCert['tbsCertificate']['subject']: + case \defined('FluentSmtpLib\\FILE_X509_IGNORE_TYPE') && $this->getIssuerDN(self::DN_STRING) === $this->getDN(self::DN_STRING): + $authorityKey = $this->getExtension('id-ce-authorityKeyIdentifier'); + $subjectKeyID = $this->getExtension('id-ce-subjectKeyIdentifier'); + switch (\true) { + case !\is_array($authorityKey): + case !$subjectKeyID: + case isset($authorityKey['keyIdentifier']) && $authorityKey['keyIdentifier'] === $subjectKeyID: + $signingCert = $this->currentCert; + } + } + if (!empty($this->CAs)) { + for ($i = 0; $i < \count($this->CAs); $i++) { + // even if the cert is a self-signed one we still want to see if it's a CA; + // if not, we'll conditionally return an error + $ca = $this->CAs[$i]; + switch (\true) { + case !\defined('FluentSmtpLib\\FILE_X509_IGNORE_TYPE') && $this->currentCert['tbsCertificate']['issuer'] === $ca['tbsCertificate']['subject']: + case \defined('FluentSmtpLib\\FILE_X509_IGNORE_TYPE') && $this->getDN(self::DN_STRING, $this->currentCert['tbsCertificate']['issuer']) === $this->getDN(self::DN_STRING, $ca['tbsCertificate']['subject']): + $authorityKey = $this->getExtension('id-ce-authorityKeyIdentifier'); + $subjectKeyID = $this->getExtension('id-ce-subjectKeyIdentifier', $ca); + switch (\true) { + case !\is_array($authorityKey): + case !$subjectKeyID: + case isset($authorityKey['keyIdentifier']) && $authorityKey['keyIdentifier'] === $subjectKeyID: + if (\is_array($authorityKey) && isset($authorityKey['authorityCertSerialNumber']) && !$authorityKey['authorityCertSerialNumber']->equals($ca['tbsCertificate']['serialNumber'])) { + break 2; + // serial mismatch - check other ca + } + $signingCert = $ca; + // working cert + break 3; + } + } + } + if (\count($this->CAs) == $i && $caonly) { + return $this->testForIntermediate($caonly, $count) && $this->validateSignature($caonly); + } + } elseif (!isset($signingCert) || $caonly) { + return $this->testForIntermediate($caonly, $count) && $this->validateSignature($caonly); + } + return $this->validateSignatureHelper($signingCert['tbsCertificate']['subjectPublicKeyInfo']['algorithm']['algorithm'], $signingCert['tbsCertificate']['subjectPublicKeyInfo']['subjectPublicKey'], $this->currentCert['signatureAlgorithm']['algorithm'], \substr($this->currentCert['signature'], 1), $this->signatureSubject); + case isset($this->currentCert['certificationRequestInfo']): + return $this->validateSignatureHelper($this->currentCert['certificationRequestInfo']['subjectPKInfo']['algorithm']['algorithm'], $this->currentCert['certificationRequestInfo']['subjectPKInfo']['subjectPublicKey'], $this->currentCert['signatureAlgorithm']['algorithm'], \substr($this->currentCert['signature'], 1), $this->signatureSubject); + case isset($this->currentCert['publicKeyAndChallenge']): + return $this->validateSignatureHelper($this->currentCert['publicKeyAndChallenge']['spki']['algorithm']['algorithm'], $this->currentCert['publicKeyAndChallenge']['spki']['subjectPublicKey'], $this->currentCert['signatureAlgorithm']['algorithm'], \substr($this->currentCert['signature'], 1), $this->signatureSubject); + case isset($this->currentCert['tbsCertList']): + if (!empty($this->CAs)) { + for ($i = 0; $i < \count($this->CAs); $i++) { + $ca = $this->CAs[$i]; + switch (\true) { + case !\defined('FluentSmtpLib\\FILE_X509_IGNORE_TYPE') && $this->currentCert['tbsCertList']['issuer'] === $ca['tbsCertificate']['subject']: + case \defined('FluentSmtpLib\\FILE_X509_IGNORE_TYPE') && $this->getDN(self::DN_STRING, $this->currentCert['tbsCertList']['issuer']) === $this->getDN(self::DN_STRING, $ca['tbsCertificate']['subject']): + $authorityKey = $this->getExtension('id-ce-authorityKeyIdentifier'); + $subjectKeyID = $this->getExtension('id-ce-subjectKeyIdentifier', $ca); + switch (\true) { + case !\is_array($authorityKey): + case !$subjectKeyID: + case isset($authorityKey['keyIdentifier']) && $authorityKey['keyIdentifier'] === $subjectKeyID: + if (\is_array($authorityKey) && isset($authorityKey['authorityCertSerialNumber']) && !$authorityKey['authorityCertSerialNumber']->equals($ca['tbsCertificate']['serialNumber'])) { + break 2; + // serial mismatch - check other ca + } + $signingCert = $ca; + // working cert + break 3; + } + } + } + } + if (!isset($signingCert)) { + return \false; + } + return $this->validateSignatureHelper($signingCert['tbsCertificate']['subjectPublicKeyInfo']['algorithm']['algorithm'], $signingCert['tbsCertificate']['subjectPublicKeyInfo']['subjectPublicKey'], $this->currentCert['signatureAlgorithm']['algorithm'], \substr($this->currentCert['signature'], 1), $this->signatureSubject); + default: + return \false; + } + } + /** + * Validates a signature + * + * Returns true if the signature is verified and false if it is not correct. + * If the algorithms are unsupposed an exception is thrown. + * + * @param string $publicKeyAlgorithm + * @param string $publicKey + * @param string $signatureAlgorithm + * @param string $signature + * @param string $signatureSubject + * @throws UnsupportedAlgorithmException if the algorithm is unsupported + * @return bool + */ + private function validateSignatureHelper($publicKeyAlgorithm, $publicKey, $signatureAlgorithm, $signature, $signatureSubject) + { + switch ($publicKeyAlgorithm) { + case 'id-RSASSA-PSS': + $key = \FluentSmtpLib\phpseclib3\Crypt\RSA::loadFormat('PSS', $publicKey); + break; + case 'rsaEncryption': + $key = \FluentSmtpLib\phpseclib3\Crypt\RSA::loadFormat('PKCS8', $publicKey); + switch ($signatureAlgorithm) { + case 'id-RSASSA-PSS': + break; + case 'md2WithRSAEncryption': + case 'md5WithRSAEncryption': + case 'sha1WithRSAEncryption': + case 'sha224WithRSAEncryption': + case 'sha256WithRSAEncryption': + case 'sha384WithRSAEncryption': + case 'sha512WithRSAEncryption': + $key = $key->withHash(\preg_replace('#WithRSAEncryption$#', '', $signatureAlgorithm))->withPadding(\FluentSmtpLib\phpseclib3\Crypt\RSA::SIGNATURE_PKCS1); + break; + default: + throw new \FluentSmtpLib\phpseclib3\Exception\UnsupportedAlgorithmException('Signature algorithm unsupported'); + } + break; + case 'id-Ed25519': + case 'id-Ed448': + $key = \FluentSmtpLib\phpseclib3\Crypt\EC::loadFormat('PKCS8', $publicKey); + break; + case 'id-ecPublicKey': + $key = \FluentSmtpLib\phpseclib3\Crypt\EC::loadFormat('PKCS8', $publicKey); + switch ($signatureAlgorithm) { + case 'ecdsa-with-SHA1': + case 'ecdsa-with-SHA224': + case 'ecdsa-with-SHA256': + case 'ecdsa-with-SHA384': + case 'ecdsa-with-SHA512': + $key = $key->withHash(\preg_replace('#^ecdsa-with-#', '', \strtolower($signatureAlgorithm))); + break; + default: + throw new \FluentSmtpLib\phpseclib3\Exception\UnsupportedAlgorithmException('Signature algorithm unsupported'); + } + break; + case 'id-dsa': + $key = \FluentSmtpLib\phpseclib3\Crypt\DSA::loadFormat('PKCS8', $publicKey); + switch ($signatureAlgorithm) { + case 'id-dsa-with-sha1': + case 'id-dsa-with-sha224': + case 'id-dsa-with-sha256': + $key = $key->withHash(\preg_replace('#^id-dsa-with-#', '', \strtolower($signatureAlgorithm))); + break; + default: + throw new \FluentSmtpLib\phpseclib3\Exception\UnsupportedAlgorithmException('Signature algorithm unsupported'); + } + break; + default: + throw new \FluentSmtpLib\phpseclib3\Exception\UnsupportedAlgorithmException('Public key algorithm unsupported'); + } + return $key->verify($signatureSubject, $signature); + } + /** + * Sets the recursion limit + * + * When validating a signature it may be necessary to download intermediate certs from URI's. + * An intermediate cert that linked to itself would result in an infinite loop so to prevent + * that we set a recursion limit. A negative number means that there is no recursion limit. + * + * @param int $count + */ + public static function setRecurLimit($count) + { + self::$recur_limit = $count; + } + /** + * Prevents URIs from being automatically retrieved + * + */ + public static function disableURLFetch() + { + self::$disable_url_fetch = \true; + } + /** + * Allows URIs to be automatically retrieved + * + */ + public static function enableURLFetch() + { + self::$disable_url_fetch = \false; + } + /** + * Decodes an IP address + * + * Takes in a base64 encoded "blob" and returns a human readable IP address + * + * @param string $ip + * @return string + */ + public static function decodeIP($ip) + { + return \inet_ntop($ip); + } + /** + * Decodes an IP address in a name constraints extension + * + * Takes in a base64 encoded "blob" and returns a human readable IP address / mask + * + * @param string $ip + * @return array + */ + public static function decodeNameConstraintIP($ip) + { + $size = \strlen($ip) >> 1; + $mask = \substr($ip, $size); + $ip = \substr($ip, 0, $size); + return [\inet_ntop($ip), \inet_ntop($mask)]; + } + /** + * Encodes an IP address + * + * Takes a human readable IP address into a base64-encoded "blob" + * + * @param string|array $ip + * @return string + */ + public static function encodeIP($ip) + { + return \is_string($ip) ? \inet_pton($ip) : \inet_pton($ip[0]) . \inet_pton($ip[1]); + } + /** + * "Normalizes" a Distinguished Name property + * + * @param string $propName + * @return mixed + */ + private function translateDNProp($propName) + { + switch (\strtolower($propName)) { + case 'jurisdictionofincorporationcountryname': + case 'jurisdictioncountryname': + case 'jurisdictionc': + return 'jurisdictionOfIncorporationCountryName'; + case 'jurisdictionofincorporationstateorprovincename': + case 'jurisdictionstateorprovincename': + case 'jurisdictionst': + return 'jurisdictionOfIncorporationStateOrProvinceName'; + case 'jurisdictionlocalityname': + case 'jurisdictionl': + return 'jurisdictionLocalityName'; + case 'id-at-businesscategory': + case 'businesscategory': + return 'id-at-businessCategory'; + case 'id-at-countryname': + case 'countryname': + case 'c': + return 'id-at-countryName'; + case 'id-at-organizationname': + case 'organizationname': + case 'o': + return 'id-at-organizationName'; + case 'id-at-dnqualifier': + case 'dnqualifier': + return 'id-at-dnQualifier'; + case 'id-at-commonname': + case 'commonname': + case 'cn': + return 'id-at-commonName'; + case 'id-at-stateorprovincename': + case 'stateorprovincename': + case 'state': + case 'province': + case 'provincename': + case 'st': + return 'id-at-stateOrProvinceName'; + case 'id-at-localityname': + case 'localityname': + case 'l': + return 'id-at-localityName'; + case 'id-emailaddress': + case 'emailaddress': + return 'pkcs-9-at-emailAddress'; + case 'id-at-serialnumber': + case 'serialnumber': + return 'id-at-serialNumber'; + case 'id-at-postalcode': + case 'postalcode': + return 'id-at-postalCode'; + case 'id-at-streetaddress': + case 'streetaddress': + return 'id-at-streetAddress'; + case 'id-at-name': + case 'name': + return 'id-at-name'; + case 'id-at-givenname': + case 'givenname': + return 'id-at-givenName'; + case 'id-at-surname': + case 'surname': + case 'sn': + return 'id-at-surname'; + case 'id-at-initials': + case 'initials': + return 'id-at-initials'; + case 'id-at-generationqualifier': + case 'generationqualifier': + return 'id-at-generationQualifier'; + case 'id-at-organizationalunitname': + case 'organizationalunitname': + case 'ou': + return 'id-at-organizationalUnitName'; + case 'id-at-pseudonym': + case 'pseudonym': + return 'id-at-pseudonym'; + case 'id-at-title': + case 'title': + return 'id-at-title'; + case 'id-at-description': + case 'description': + return 'id-at-description'; + case 'id-at-role': + case 'role': + return 'id-at-role'; + case 'id-at-uniqueidentifier': + case 'uniqueidentifier': + case 'x500uniqueidentifier': + return 'id-at-uniqueIdentifier'; + case 'postaladdress': + case 'id-at-postaladdress': + return 'id-at-postalAddress'; + default: + return \false; + } + } + /** + * Set a Distinguished Name property + * + * @param string $propName + * @param mixed $propValue + * @param string $type optional + * @return bool + */ + public function setDNProp($propName, $propValue, $type = 'utf8String') + { + if (empty($this->dn)) { + $this->dn = ['rdnSequence' => []]; + } + if (($propName = $this->translateDNProp($propName)) === \false) { + return \false; + } + foreach ((array) $propValue as $v) { + if (!\is_array($v) && isset($type)) { + $v = [$type => $v]; + } + $this->dn['rdnSequence'][] = [['type' => $propName, 'value' => $v]]; + } + return \true; + } + /** + * Remove Distinguished Name properties + * + * @param string $propName + */ + public function removeDNProp($propName) + { + if (empty($this->dn)) { + return; + } + if (($propName = $this->translateDNProp($propName)) === \false) { + return; + } + $dn =& $this->dn['rdnSequence']; + $size = \count($dn); + for ($i = 0; $i < $size; $i++) { + if ($dn[$i][0]['type'] == $propName) { + unset($dn[$i]); + } + } + $dn = \array_values($dn); + // fix for https://bugs.php.net/75433 affecting PHP 7.2 + if (!isset($dn[0])) { + $dn = \array_splice($dn, 0, 0); + } + } + /** + * Get Distinguished Name properties + * + * @param string $propName + * @param array $dn optional + * @param bool $withType optional + * @return mixed + */ + public function getDNProp($propName, $dn = null, $withType = \false) + { + if (!isset($dn)) { + $dn = $this->dn; + } + if (empty($dn)) { + return \false; + } + if (($propName = $this->translateDNProp($propName)) === \false) { + return \false; + } + $filters = []; + $filters['value'] = ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_UTF8_STRING]; + \FluentSmtpLib\phpseclib3\File\ASN1::setFilters($filters); + $this->mapOutDNs($dn, 'rdnSequence'); + $dn = $dn['rdnSequence']; + $result = []; + for ($i = 0; $i < \count($dn); $i++) { + if ($dn[$i][0]['type'] == $propName) { + $v = $dn[$i][0]['value']; + if (!$withType) { + if (\is_array($v)) { + foreach ($v as $type => $s) { + $type = \array_search($type, \FluentSmtpLib\phpseclib3\File\ASN1::ANY_MAP); + if ($type !== \false && \array_key_exists($type, \FluentSmtpLib\phpseclib3\File\ASN1::STRING_TYPE_SIZE)) { + $s = \FluentSmtpLib\phpseclib3\File\ASN1::convert($s, $type); + if ($s !== \false) { + $v = $s; + break; + } + } + } + if (\is_array($v)) { + $v = \array_pop($v); + // Always strip data type. + } + } elseif (\is_object($v) && $v instanceof \FluentSmtpLib\phpseclib3\File\ASN1\Element) { + $map = $this->getMapping($propName); + if (!\is_bool($map)) { + $decoded = \FluentSmtpLib\phpseclib3\File\ASN1::decodeBER($v); + if (!$decoded) { + return \false; + } + $v = \FluentSmtpLib\phpseclib3\File\ASN1::asn1map($decoded[0], $map); + } + } + } + $result[] = $v; + } + } + return $result; + } + /** + * Set a Distinguished Name + * + * @param mixed $dn + * @param bool $merge optional + * @param string $type optional + * @return bool + */ + public function setDN($dn, $merge = \false, $type = 'utf8String') + { + if (!$merge) { + $this->dn = null; + } + if (\is_array($dn)) { + if (isset($dn['rdnSequence'])) { + $this->dn = $dn; + // No merge here. + return \true; + } + // handles stuff generated by openssl_x509_parse() + foreach ($dn as $prop => $value) { + if (!$this->setDNProp($prop, $value, $type)) { + return \false; + } + } + return \true; + } + // handles everything else + $results = \preg_split('#((?:^|, *|/)(?:C=|O=|OU=|CN=|L=|ST=|SN=|postalCode=|streetAddress=|emailAddress=|serialNumber=|organizationalUnitName=|title=|description=|role=|x500UniqueIdentifier=|postalAddress=))#', $dn, -1, \PREG_SPLIT_DELIM_CAPTURE); + for ($i = 1; $i < \count($results); $i += 2) { + $prop = \trim($results[$i], ', =/'); + $value = $results[$i + 1]; + if (!$this->setDNProp($prop, $value, $type)) { + return \false; + } + } + return \true; + } + /** + * Get the Distinguished Name for a certificates subject + * + * @param mixed $format optional + * @param array $dn optional + * @return array|bool|string + */ + public function getDN($format = self::DN_ARRAY, $dn = null) + { + if (!isset($dn)) { + $dn = isset($this->currentCert['tbsCertList']) ? $this->currentCert['tbsCertList']['issuer'] : $this->dn; + } + switch ((int) $format) { + case self::DN_ARRAY: + return $dn; + case self::DN_ASN1: + $filters = []; + $filters['rdnSequence']['value'] = ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_UTF8_STRING]; + \FluentSmtpLib\phpseclib3\File\ASN1::setFilters($filters); + $this->mapOutDNs($dn, 'rdnSequence'); + return \FluentSmtpLib\phpseclib3\File\ASN1::encodeDER($dn, \FluentSmtpLib\phpseclib3\File\ASN1\Maps\Name::MAP); + case self::DN_CANON: + // No SEQUENCE around RDNs and all string values normalized as + // trimmed lowercase UTF-8 with all spacing as one blank. + // constructed RDNs will not be canonicalized + $filters = []; + $filters['value'] = ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_UTF8_STRING]; + \FluentSmtpLib\phpseclib3\File\ASN1::setFilters($filters); + $result = ''; + $this->mapOutDNs($dn, 'rdnSequence'); + foreach ($dn['rdnSequence'] as $rdn) { + foreach ($rdn as $i => $attr) { + $attr =& $rdn[$i]; + if (\is_array($attr['value'])) { + foreach ($attr['value'] as $type => $v) { + $type = \array_search($type, \FluentSmtpLib\phpseclib3\File\ASN1::ANY_MAP, \true); + if ($type !== \false && \array_key_exists($type, \FluentSmtpLib\phpseclib3\File\ASN1::STRING_TYPE_SIZE)) { + $v = \FluentSmtpLib\phpseclib3\File\ASN1::convert($v, $type); + if ($v !== \false) { + $v = \preg_replace('/\\s+/', ' ', $v); + $attr['value'] = \strtolower(\trim($v)); + break; + } + } + } + } + } + $result .= \FluentSmtpLib\phpseclib3\File\ASN1::encodeDER($rdn, \FluentSmtpLib\phpseclib3\File\ASN1\Maps\RelativeDistinguishedName::MAP); + } + return $result; + case self::DN_HASH: + $dn = $this->getDN(self::DN_CANON, $dn); + $hash = new \FluentSmtpLib\phpseclib3\Crypt\Hash('sha1'); + $hash = $hash->hash($dn); + \extract(\unpack('Vhash', $hash)); + return \strtolower(\FluentSmtpLib\phpseclib3\Common\Functions\Strings::bin2hex(\pack('N', $hash))); + } + // Default is to return a string. + $start = \true; + $output = ''; + $result = []; + $filters = []; + $filters['rdnSequence']['value'] = ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_UTF8_STRING]; + \FluentSmtpLib\phpseclib3\File\ASN1::setFilters($filters); + $this->mapOutDNs($dn, 'rdnSequence'); + foreach ($dn['rdnSequence'] as $field) { + $prop = $field[0]['type']; + $value = $field[0]['value']; + $delim = ', '; + switch ($prop) { + case 'id-at-countryName': + $desc = 'C'; + break; + case 'id-at-stateOrProvinceName': + $desc = 'ST'; + break; + case 'id-at-organizationName': + $desc = 'O'; + break; + case 'id-at-organizationalUnitName': + $desc = 'OU'; + break; + case 'id-at-commonName': + $desc = 'CN'; + break; + case 'id-at-localityName': + $desc = 'L'; + break; + case 'id-at-surname': + $desc = 'SN'; + break; + case 'id-at-uniqueIdentifier': + $delim = '/'; + $desc = 'x500UniqueIdentifier'; + break; + case 'id-at-postalAddress': + $delim = '/'; + $desc = 'postalAddress'; + break; + default: + $delim = '/'; + $desc = \preg_replace('#.+-([^-]+)$#', '$1', $prop); + } + if (!$start) { + $output .= $delim; + } + if (\is_array($value)) { + foreach ($value as $type => $v) { + $type = \array_search($type, \FluentSmtpLib\phpseclib3\File\ASN1::ANY_MAP, \true); + if ($type !== \false && \array_key_exists($type, \FluentSmtpLib\phpseclib3\File\ASN1::STRING_TYPE_SIZE)) { + $v = \FluentSmtpLib\phpseclib3\File\ASN1::convert($v, $type); + if ($v !== \false) { + $value = $v; + break; + } + } + } + if (\is_array($value)) { + $value = \array_pop($value); + // Always strip data type. + } + } elseif (\is_object($value) && $value instanceof \FluentSmtpLib\phpseclib3\File\ASN1\Element) { + $callback = function ($x) { + return '\\x' . \bin2hex($x[0]); + }; + $value = \strtoupper(\preg_replace_callback('#[^\\x20-\\x7E]#', $callback, $value->element)); + } + $output .= $desc . '=' . $value; + $result[$desc] = isset($result[$desc]) ? \array_merge((array) $result[$desc], [$value]) : $value; + $start = \false; + } + return $format == self::DN_OPENSSL ? $result : $output; + } + /** + * Get the Distinguished Name for a certificate/crl issuer + * + * @param int $format optional + * @return mixed + */ + public function getIssuerDN($format = self::DN_ARRAY) + { + switch (\true) { + case !isset($this->currentCert) || !\is_array($this->currentCert): + break; + case isset($this->currentCert['tbsCertificate']): + return $this->getDN($format, $this->currentCert['tbsCertificate']['issuer']); + case isset($this->currentCert['tbsCertList']): + return $this->getDN($format, $this->currentCert['tbsCertList']['issuer']); + } + return \false; + } + /** + * Get the Distinguished Name for a certificate/csr subject + * Alias of getDN() + * + * @param int $format optional + * @return mixed + */ + public function getSubjectDN($format = self::DN_ARRAY) + { + switch (\true) { + case !empty($this->dn): + return $this->getDN($format); + case !isset($this->currentCert) || !\is_array($this->currentCert): + break; + case isset($this->currentCert['tbsCertificate']): + return $this->getDN($format, $this->currentCert['tbsCertificate']['subject']); + case isset($this->currentCert['certificationRequestInfo']): + return $this->getDN($format, $this->currentCert['certificationRequestInfo']['subject']); + } + return \false; + } + /** + * Get an individual Distinguished Name property for a certificate/crl issuer + * + * @param string $propName + * @param bool $withType optional + * @return mixed + */ + public function getIssuerDNProp($propName, $withType = \false) + { + switch (\true) { + case !isset($this->currentCert) || !\is_array($this->currentCert): + break; + case isset($this->currentCert['tbsCertificate']): + return $this->getDNProp($propName, $this->currentCert['tbsCertificate']['issuer'], $withType); + case isset($this->currentCert['tbsCertList']): + return $this->getDNProp($propName, $this->currentCert['tbsCertList']['issuer'], $withType); + } + return \false; + } + /** + * Get an individual Distinguished Name property for a certificate/csr subject + * + * @param string $propName + * @param bool $withType optional + * @return mixed + */ + public function getSubjectDNProp($propName, $withType = \false) + { + switch (\true) { + case !empty($this->dn): + return $this->getDNProp($propName, null, $withType); + case !isset($this->currentCert) || !\is_array($this->currentCert): + break; + case isset($this->currentCert['tbsCertificate']): + return $this->getDNProp($propName, $this->currentCert['tbsCertificate']['subject'], $withType); + case isset($this->currentCert['certificationRequestInfo']): + return $this->getDNProp($propName, $this->currentCert['certificationRequestInfo']['subject'], $withType); + } + return \false; + } + /** + * Get the certificate chain for the current cert + * + * @return mixed + */ + public function getChain() + { + $chain = [$this->currentCert]; + if (!\is_array($this->currentCert) || !isset($this->currentCert['tbsCertificate'])) { + return \false; + } + while (\true) { + $currentCert = $chain[\count($chain) - 1]; + for ($i = 0; $i < \count($this->CAs); $i++) { + $ca = $this->CAs[$i]; + if ($currentCert['tbsCertificate']['issuer'] === $ca['tbsCertificate']['subject']) { + $authorityKey = $this->getExtension('id-ce-authorityKeyIdentifier', $currentCert); + $subjectKeyID = $this->getExtension('id-ce-subjectKeyIdentifier', $ca); + switch (\true) { + case !\is_array($authorityKey): + case \is_array($authorityKey) && isset($authorityKey['keyIdentifier']) && $authorityKey['keyIdentifier'] === $subjectKeyID: + if ($currentCert === $ca) { + break 3; + } + $chain[] = $ca; + break 2; + } + } + } + if ($i == \count($this->CAs)) { + break; + } + } + foreach ($chain as $key => $value) { + $chain[$key] = new \FluentSmtpLib\phpseclib3\File\X509(); + $chain[$key]->loadX509($value); + } + return $chain; + } + /** + * Returns the current cert + * + * @return array|bool + */ + public function &getCurrentCert() + { + return $this->currentCert; + } + /** + * Set public key + * + * Key needs to be a \phpseclib3\Crypt\RSA object + * + * @param PublicKey $key + * @return void + */ + public function setPublicKey(\FluentSmtpLib\phpseclib3\Crypt\Common\PublicKey $key) + { + $this->publicKey = $key; + } + /** + * Set private key + * + * Key needs to be a \phpseclib3\Crypt\RSA object + * + * @param PrivateKey $key + */ + public function setPrivateKey(\FluentSmtpLib\phpseclib3\Crypt\Common\PrivateKey $key) + { + $this->privateKey = $key; + } + /** + * Set challenge + * + * Used for SPKAC CSR's + * + * @param string $challenge + */ + public function setChallenge($challenge) + { + $this->challenge = $challenge; + } + /** + * Gets the public key + * + * Returns a \phpseclib3\Crypt\RSA object or a false. + * + * @return mixed + */ + public function getPublicKey() + { + if (isset($this->publicKey)) { + return $this->publicKey; + } + if (isset($this->currentCert) && \is_array($this->currentCert)) { + $paths = ['tbsCertificate/subjectPublicKeyInfo', 'certificationRequestInfo/subjectPKInfo', 'publicKeyAndChallenge/spki']; + foreach ($paths as $path) { + $keyinfo = $this->subArray($this->currentCert, $path); + if (!empty($keyinfo)) { + break; + } + } + } + if (empty($keyinfo)) { + return \false; + } + $key = $keyinfo['subjectPublicKey']; + switch ($keyinfo['algorithm']['algorithm']) { + case 'id-RSASSA-PSS': + return \FluentSmtpLib\phpseclib3\Crypt\RSA::loadFormat('PSS', $key); + case 'rsaEncryption': + return \FluentSmtpLib\phpseclib3\Crypt\RSA::loadFormat('PKCS8', $key)->withPadding(\FluentSmtpLib\phpseclib3\Crypt\RSA::SIGNATURE_PKCS1); + case 'id-ecPublicKey': + case 'id-Ed25519': + case 'id-Ed448': + return \FluentSmtpLib\phpseclib3\Crypt\EC::loadFormat('PKCS8', $key); + case 'id-dsa': + return \FluentSmtpLib\phpseclib3\Crypt\DSA::loadFormat('PKCS8', $key); + } + return \false; + } + /** + * Load a Certificate Signing Request + * + * @param string $csr + * @param int $mode + * @return mixed + */ + public function loadCSR($csr, $mode = self::FORMAT_AUTO_DETECT) + { + if (\is_array($csr) && isset($csr['certificationRequestInfo'])) { + unset($this->currentCert); + unset($this->currentKeyIdentifier); + unset($this->signatureSubject); + $this->dn = $csr['certificationRequestInfo']['subject']; + if (!isset($this->dn)) { + return \false; + } + $this->currentCert = $csr; + return $csr; + } + // see http://tools.ietf.org/html/rfc2986 + if ($mode != self::FORMAT_DER) { + $newcsr = \FluentSmtpLib\phpseclib3\File\ASN1::extractBER($csr); + if ($mode == self::FORMAT_PEM && $csr == $newcsr) { + return \false; + } + $csr = $newcsr; + } + $orig = $csr; + if ($csr === \false) { + $this->currentCert = \false; + return \false; + } + $decoded = \FluentSmtpLib\phpseclib3\File\ASN1::decodeBER($csr); + if (!$decoded) { + $this->currentCert = \false; + return \false; + } + $csr = \FluentSmtpLib\phpseclib3\File\ASN1::asn1map($decoded[0], \FluentSmtpLib\phpseclib3\File\ASN1\Maps\CertificationRequest::MAP); + if (!isset($csr) || $csr === \false) { + $this->currentCert = \false; + return \false; + } + $this->mapInAttributes($csr, 'certificationRequestInfo/attributes'); + $this->mapInDNs($csr, 'certificationRequestInfo/subject/rdnSequence'); + $this->dn = $csr['certificationRequestInfo']['subject']; + $this->signatureSubject = \substr($orig, $decoded[0]['content'][0]['start'], $decoded[0]['content'][0]['length']); + $key = $csr['certificationRequestInfo']['subjectPKInfo']; + $key = \FluentSmtpLib\phpseclib3\File\ASN1::encodeDER($key, \FluentSmtpLib\phpseclib3\File\ASN1\Maps\SubjectPublicKeyInfo::MAP); + $csr['certificationRequestInfo']['subjectPKInfo']['subjectPublicKey'] = "-----BEGIN PUBLIC KEY-----\r\n" . \chunk_split(\base64_encode($key), 64) . "-----END PUBLIC KEY-----"; + $this->currentKeyIdentifier = null; + $this->currentCert = $csr; + $this->publicKey = null; + $this->publicKey = $this->getPublicKey(); + return $csr; + } + /** + * Save CSR request + * + * @param array $csr + * @param int $format optional + * @return string + */ + public function saveCSR(array $csr, $format = self::FORMAT_PEM) + { + if (!\is_array($csr) || !isset($csr['certificationRequestInfo'])) { + return \false; + } + switch (\true) { + case !($algorithm = $this->subArray($csr, 'certificationRequestInfo/subjectPKInfo/algorithm/algorithm')): + case \is_object($csr['certificationRequestInfo']['subjectPKInfo']['subjectPublicKey']): + break; + default: + $csr['certificationRequestInfo']['subjectPKInfo'] = new \FluentSmtpLib\phpseclib3\File\ASN1\Element(\base64_decode(\preg_replace('#-.+-|[\\r\\n]#', '', $csr['certificationRequestInfo']['subjectPKInfo']['subjectPublicKey']))); + } + $filters = []; + $filters['certificationRequestInfo']['subject']['rdnSequence']['value'] = ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_UTF8_STRING]; + \FluentSmtpLib\phpseclib3\File\ASN1::setFilters($filters); + $this->mapOutDNs($csr, 'certificationRequestInfo/subject/rdnSequence'); + $this->mapOutAttributes($csr, 'certificationRequestInfo/attributes'); + $csr = \FluentSmtpLib\phpseclib3\File\ASN1::encodeDER($csr, \FluentSmtpLib\phpseclib3\File\ASN1\Maps\CertificationRequest::MAP); + switch ($format) { + case self::FORMAT_DER: + return $csr; + // case self::FORMAT_PEM: + default: + return "-----BEGIN CERTIFICATE REQUEST-----\r\n" . \chunk_split(\FluentSmtpLib\phpseclib3\Common\Functions\Strings::base64_encode($csr), 64) . '-----END CERTIFICATE REQUEST-----'; + } + } + /** + * Load a SPKAC CSR + * + * SPKAC's are produced by the HTML5 keygen element: + * + * https://developer.mozilla.org/en-US/docs/HTML/Element/keygen + * + * @param string $spkac + * @return mixed + */ + public function loadSPKAC($spkac) + { + if (\is_array($spkac) && isset($spkac['publicKeyAndChallenge'])) { + unset($this->currentCert); + unset($this->currentKeyIdentifier); + unset($this->signatureSubject); + $this->currentCert = $spkac; + return $spkac; + } + // see http://www.w3.org/html/wg/drafts/html/master/forms.html#signedpublickeyandchallenge + // OpenSSL produces SPKAC's that are preceded by the string SPKAC= + $temp = \preg_replace('#(?:SPKAC=)|[ \\r\\n\\\\]#', '', $spkac); + $temp = \preg_match('#^[a-zA-Z\\d/+]*={0,2}$#', $temp) ? \FluentSmtpLib\phpseclib3\Common\Functions\Strings::base64_decode($temp) : \false; + if ($temp != \false) { + $spkac = $temp; + } + $orig = $spkac; + if ($spkac === \false) { + $this->currentCert = \false; + return \false; + } + $decoded = \FluentSmtpLib\phpseclib3\File\ASN1::decodeBER($spkac); + if (!$decoded) { + $this->currentCert = \false; + return \false; + } + $spkac = \FluentSmtpLib\phpseclib3\File\ASN1::asn1map($decoded[0], \FluentSmtpLib\phpseclib3\File\ASN1\Maps\SignedPublicKeyAndChallenge::MAP); + if (!isset($spkac) || !\is_array($spkac)) { + $this->currentCert = \false; + return \false; + } + $this->signatureSubject = \substr($orig, $decoded[0]['content'][0]['start'], $decoded[0]['content'][0]['length']); + $key = $spkac['publicKeyAndChallenge']['spki']; + $key = \FluentSmtpLib\phpseclib3\File\ASN1::encodeDER($key, \FluentSmtpLib\phpseclib3\File\ASN1\Maps\SubjectPublicKeyInfo::MAP); + $spkac['publicKeyAndChallenge']['spki']['subjectPublicKey'] = "-----BEGIN PUBLIC KEY-----\r\n" . \chunk_split(\base64_encode($key), 64) . "-----END PUBLIC KEY-----"; + $this->currentKeyIdentifier = null; + $this->currentCert = $spkac; + $this->publicKey = null; + $this->publicKey = $this->getPublicKey(); + return $spkac; + } + /** + * Save a SPKAC CSR request + * + * @param array $spkac + * @param int $format optional + * @return string + */ + public function saveSPKAC(array $spkac, $format = self::FORMAT_PEM) + { + if (!\is_array($spkac) || !isset($spkac['publicKeyAndChallenge'])) { + return \false; + } + $algorithm = $this->subArray($spkac, 'publicKeyAndChallenge/spki/algorithm/algorithm'); + switch (\true) { + case !$algorithm: + case \is_object($spkac['publicKeyAndChallenge']['spki']['subjectPublicKey']): + break; + default: + $spkac['publicKeyAndChallenge']['spki'] = new \FluentSmtpLib\phpseclib3\File\ASN1\Element(\base64_decode(\preg_replace('#-.+-|[\\r\\n]#', '', $spkac['publicKeyAndChallenge']['spki']['subjectPublicKey']))); + } + $spkac = \FluentSmtpLib\phpseclib3\File\ASN1::encodeDER($spkac, \FluentSmtpLib\phpseclib3\File\ASN1\Maps\SignedPublicKeyAndChallenge::MAP); + switch ($format) { + case self::FORMAT_DER: + return $spkac; + // case self::FORMAT_PEM: + default: + // OpenSSL's implementation of SPKAC requires the SPKAC be preceded by SPKAC= and since there are pretty much + // no other SPKAC decoders phpseclib will use that same format + return 'SPKAC=' . \FluentSmtpLib\phpseclib3\Common\Functions\Strings::base64_encode($spkac); + } + } + /** + * Load a Certificate Revocation List + * + * @param string $crl + * @param int $mode + * @return mixed + */ + public function loadCRL($crl, $mode = self::FORMAT_AUTO_DETECT) + { + if (\is_array($crl) && isset($crl['tbsCertList'])) { + $this->currentCert = $crl; + unset($this->signatureSubject); + return $crl; + } + if ($mode != self::FORMAT_DER) { + $newcrl = \FluentSmtpLib\phpseclib3\File\ASN1::extractBER($crl); + if ($mode == self::FORMAT_PEM && $crl == $newcrl) { + return \false; + } + $crl = $newcrl; + } + $orig = $crl; + if ($crl === \false) { + $this->currentCert = \false; + return \false; + } + $decoded = \FluentSmtpLib\phpseclib3\File\ASN1::decodeBER($crl); + if (!$decoded) { + $this->currentCert = \false; + return \false; + } + $crl = \FluentSmtpLib\phpseclib3\File\ASN1::asn1map($decoded[0], \FluentSmtpLib\phpseclib3\File\ASN1\Maps\CertificateList::MAP); + if (!isset($crl) || $crl === \false) { + $this->currentCert = \false; + return \false; + } + $this->signatureSubject = \substr($orig, $decoded[0]['content'][0]['start'], $decoded[0]['content'][0]['length']); + $this->mapInDNs($crl, 'tbsCertList/issuer/rdnSequence'); + if ($this->isSubArrayValid($crl, 'tbsCertList/crlExtensions')) { + $this->mapInExtensions($crl, 'tbsCertList/crlExtensions'); + } + if ($this->isSubArrayValid($crl, 'tbsCertList/revokedCertificates')) { + $rclist_ref =& $this->subArrayUnchecked($crl, 'tbsCertList/revokedCertificates'); + if ($rclist_ref) { + $rclist = $crl['tbsCertList']['revokedCertificates']; + foreach ($rclist as $i => $extension) { + if ($this->isSubArrayValid($rclist, "{$i}/crlEntryExtensions")) { + $this->mapInExtensions($rclist_ref, "{$i}/crlEntryExtensions"); + } + } + } + } + $this->currentKeyIdentifier = null; + $this->currentCert = $crl; + return $crl; + } + /** + * Save Certificate Revocation List. + * + * @param array $crl + * @param int $format optional + * @return string + */ + public function saveCRL(array $crl, $format = self::FORMAT_PEM) + { + if (!\is_array($crl) || !isset($crl['tbsCertList'])) { + return \false; + } + $filters = []; + $filters['tbsCertList']['issuer']['rdnSequence']['value'] = ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_UTF8_STRING]; + $filters['tbsCertList']['signature']['parameters'] = ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_UTF8_STRING]; + $filters['signatureAlgorithm']['parameters'] = ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_UTF8_STRING]; + if (empty($crl['tbsCertList']['signature']['parameters'])) { + $filters['tbsCertList']['signature']['parameters'] = ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_NULL]; + } + if (empty($crl['signatureAlgorithm']['parameters'])) { + $filters['signatureAlgorithm']['parameters'] = ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_NULL]; + } + \FluentSmtpLib\phpseclib3\File\ASN1::setFilters($filters); + $this->mapOutDNs($crl, 'tbsCertList/issuer/rdnSequence'); + $this->mapOutExtensions($crl, 'tbsCertList/crlExtensions'); + $rclist =& $this->subArray($crl, 'tbsCertList/revokedCertificates'); + if (\is_array($rclist)) { + foreach ($rclist as $i => $extension) { + $this->mapOutExtensions($rclist, "{$i}/crlEntryExtensions"); + } + } + $crl = \FluentSmtpLib\phpseclib3\File\ASN1::encodeDER($crl, \FluentSmtpLib\phpseclib3\File\ASN1\Maps\CertificateList::MAP); + switch ($format) { + case self::FORMAT_DER: + return $crl; + // case self::FORMAT_PEM: + default: + return "-----BEGIN X509 CRL-----\r\n" . \chunk_split(\FluentSmtpLib\phpseclib3\Common\Functions\Strings::base64_encode($crl), 64) . '-----END X509 CRL-----'; + } + } + /** + * Helper function to build a time field according to RFC 3280 section + * - 4.1.2.5 Validity + * - 5.1.2.4 This Update + * - 5.1.2.5 Next Update + * - 5.1.2.6 Revoked Certificates + * by choosing utcTime iff year of date given is before 2050 and generalTime else. + * + * @param string $date in format date('D, d M Y H:i:s O') + * @return array|Element + */ + private function timeField($date) + { + if ($date instanceof \FluentSmtpLib\phpseclib3\File\ASN1\Element) { + return $date; + } + $dateObj = new \DateTimeImmutable($date, new \DateTimeZone('GMT')); + $year = $dateObj->format('Y'); + // the same way ASN1.php parses this + if ($year < 2050) { + return ['utcTime' => $date]; + } else { + return ['generalTime' => $date]; + } + } + /** + * Sign an X.509 certificate + * + * $issuer's private key needs to be loaded. + * $subject can be either an existing X.509 cert (if you want to resign it), + * a CSR or something with the DN and public key explicitly set. + * + * @return mixed + */ + public function sign(\FluentSmtpLib\phpseclib3\File\X509 $issuer, \FluentSmtpLib\phpseclib3\File\X509 $subject) + { + if (!\is_object($issuer->privateKey) || empty($issuer->dn)) { + return \false; + } + if (isset($subject->publicKey) && !($subjectPublicKey = $subject->formatSubjectPublicKey())) { + return \false; + } + $currentCert = isset($this->currentCert) ? $this->currentCert : null; + $signatureSubject = isset($this->signatureSubject) ? $this->signatureSubject : null; + $signatureAlgorithm = self::identifySignatureAlgorithm($issuer->privateKey); + if (isset($subject->currentCert) && \is_array($subject->currentCert) && isset($subject->currentCert['tbsCertificate'])) { + $this->currentCert = $subject->currentCert; + $this->currentCert['tbsCertificate']['signature'] = $signatureAlgorithm; + $this->currentCert['signatureAlgorithm'] = $signatureAlgorithm; + if (!empty($this->startDate)) { + $this->currentCert['tbsCertificate']['validity']['notBefore'] = $this->timeField($this->startDate); + } + if (!empty($this->endDate)) { + $this->currentCert['tbsCertificate']['validity']['notAfter'] = $this->timeField($this->endDate); + } + if (!empty($this->serialNumber)) { + $this->currentCert['tbsCertificate']['serialNumber'] = $this->serialNumber; + } + if (!empty($subject->dn)) { + $this->currentCert['tbsCertificate']['subject'] = $subject->dn; + } + if (!empty($subject->publicKey)) { + $this->currentCert['tbsCertificate']['subjectPublicKeyInfo'] = $subjectPublicKey; + } + $this->removeExtension('id-ce-authorityKeyIdentifier'); + if (isset($subject->domains)) { + $this->removeExtension('id-ce-subjectAltName'); + } + } elseif (isset($subject->currentCert) && \is_array($subject->currentCert) && isset($subject->currentCert['tbsCertList'])) { + return \false; + } else { + if (!isset($subject->publicKey)) { + return \false; + } + $startDate = new \DateTimeImmutable('now', new \DateTimeZone(@\date_default_timezone_get())); + $startDate = !empty($this->startDate) ? $this->startDate : $startDate->format('D, d M Y H:i:s O'); + $endDate = new \DateTimeImmutable('+1 year', new \DateTimeZone(@\date_default_timezone_get())); + $endDate = !empty($this->endDate) ? $this->endDate : $endDate->format('D, d M Y H:i:s O'); + /* "The serial number MUST be a positive integer" + "Conforming CAs MUST NOT use serialNumber values longer than 20 octets." + -- https://tools.ietf.org/html/rfc5280#section-4.1.2.2 + + for the integer to be positive the leading bit needs to be 0 hence the + application of a bitmap + */ + $serialNumber = !empty($this->serialNumber) ? $this->serialNumber : new \FluentSmtpLib\phpseclib3\Math\BigInteger(\FluentSmtpLib\phpseclib3\Crypt\Random::string(20) & "" . \str_repeat("\xff", 19), 256); + $this->currentCert = ['tbsCertificate' => [ + 'version' => 'v3', + 'serialNumber' => $serialNumber, + // $this->setSerialNumber() + 'signature' => $signatureAlgorithm, + 'issuer' => \false, + // this is going to be overwritten later + 'validity' => [ + 'notBefore' => $this->timeField($startDate), + // $this->setStartDate() + 'notAfter' => $this->timeField($endDate), + ], + 'subject' => $subject->dn, + 'subjectPublicKeyInfo' => $subjectPublicKey, + ], 'signatureAlgorithm' => $signatureAlgorithm, 'signature' => \false]; + // Copy extensions from CSR. + $csrexts = $subject->getAttribute('pkcs-9-at-extensionRequest', 0); + if (!empty($csrexts)) { + $this->currentCert['tbsCertificate']['extensions'] = $csrexts; + } + } + $this->currentCert['tbsCertificate']['issuer'] = $issuer->dn; + if (isset($issuer->currentKeyIdentifier)) { + $this->setExtension('id-ce-authorityKeyIdentifier', [ + //'authorityCertIssuer' => array( + // array( + // 'directoryName' => $issuer->dn + // ) + //), + 'keyIdentifier' => $issuer->currentKeyIdentifier, + ]); + //$extensions = &$this->currentCert['tbsCertificate']['extensions']; + //if (isset($issuer->serialNumber)) { + // $extensions[count($extensions) - 1]['authorityCertSerialNumber'] = $issuer->serialNumber; + //} + //unset($extensions); + } + if (isset($subject->currentKeyIdentifier)) { + $this->setExtension('id-ce-subjectKeyIdentifier', $subject->currentKeyIdentifier); + } + $altName = []; + if (isset($subject->domains) && \count($subject->domains)) { + $altName = \array_map(['\\FluentSmtpLib\\phpseclib3\\File\\X509', 'dnsName'], $subject->domains); + } + if (isset($subject->ipAddresses) && \count($subject->ipAddresses)) { + // should an IP address appear as the CN if no domain name is specified? idk + //$ips = count($subject->domains) ? $subject->ipAddresses : array_slice($subject->ipAddresses, 1); + $ipAddresses = []; + foreach ($subject->ipAddresses as $ipAddress) { + $encoded = $subject->ipAddress($ipAddress); + if ($encoded !== \false) { + $ipAddresses[] = $encoded; + } + } + if (\count($ipAddresses)) { + $altName = \array_merge($altName, $ipAddresses); + } + } + if (!empty($altName)) { + $this->setExtension('id-ce-subjectAltName', $altName); + } + if ($this->caFlag) { + $keyUsage = $this->getExtension('id-ce-keyUsage'); + if (!$keyUsage) { + $keyUsage = []; + } + $this->setExtension('id-ce-keyUsage', \array_values(\array_unique(\array_merge($keyUsage, ['cRLSign', 'keyCertSign'])))); + $basicConstraints = $this->getExtension('id-ce-basicConstraints'); + if (!$basicConstraints) { + $basicConstraints = []; + } + $this->setExtension('id-ce-basicConstraints', \array_merge(['cA' => \true], $basicConstraints), \true); + if (!isset($subject->currentKeyIdentifier)) { + $this->setExtension('id-ce-subjectKeyIdentifier', $this->computeKeyIdentifier($this->currentCert), \false, \false); + } + } + // resync $this->signatureSubject + // save $tbsCertificate in case there are any \phpseclib3\File\ASN1\Element objects in it + $tbsCertificate = $this->currentCert['tbsCertificate']; + $this->loadX509($this->saveX509($this->currentCert)); + $result = $this->currentCert; + $this->currentCert['signature'] = $result['signature'] = "\x00" . $issuer->privateKey->sign($this->signatureSubject); + $result['tbsCertificate'] = $tbsCertificate; + $this->currentCert = $currentCert; + $this->signatureSubject = $signatureSubject; + return $result; + } + /** + * Sign a CSR + * + * @return mixed + */ + public function signCSR() + { + if (!\is_object($this->privateKey) || empty($this->dn)) { + return \false; + } + $origPublicKey = $this->publicKey; + $this->publicKey = $this->privateKey->getPublicKey(); + $publicKey = $this->formatSubjectPublicKey(); + $this->publicKey = $origPublicKey; + $currentCert = isset($this->currentCert) ? $this->currentCert : null; + $signatureSubject = isset($this->signatureSubject) ? $this->signatureSubject : null; + $signatureAlgorithm = self::identifySignatureAlgorithm($this->privateKey); + if (isset($this->currentCert) && \is_array($this->currentCert) && isset($this->currentCert['certificationRequestInfo'])) { + $this->currentCert['signatureAlgorithm'] = $signatureAlgorithm; + if (!empty($this->dn)) { + $this->currentCert['certificationRequestInfo']['subject'] = $this->dn; + } + $this->currentCert['certificationRequestInfo']['subjectPKInfo'] = $publicKey; + } else { + $this->currentCert = ['certificationRequestInfo' => ['version' => 'v1', 'subject' => $this->dn, 'subjectPKInfo' => $publicKey, 'attributes' => []], 'signatureAlgorithm' => $signatureAlgorithm, 'signature' => \false]; + } + // resync $this->signatureSubject + // save $certificationRequestInfo in case there are any \phpseclib3\File\ASN1\Element objects in it + $certificationRequestInfo = $this->currentCert['certificationRequestInfo']; + $this->loadCSR($this->saveCSR($this->currentCert)); + $result = $this->currentCert; + $this->currentCert['signature'] = $result['signature'] = "\x00" . $this->privateKey->sign($this->signatureSubject); + $result['certificationRequestInfo'] = $certificationRequestInfo; + $this->currentCert = $currentCert; + $this->signatureSubject = $signatureSubject; + return $result; + } + /** + * Sign a SPKAC + * + * @return mixed + */ + public function signSPKAC() + { + if (!\is_object($this->privateKey)) { + return \false; + } + $origPublicKey = $this->publicKey; + $this->publicKey = $this->privateKey->getPublicKey(); + $publicKey = $this->formatSubjectPublicKey(); + $this->publicKey = $origPublicKey; + $currentCert = isset($this->currentCert) ? $this->currentCert : null; + $signatureSubject = isset($this->signatureSubject) ? $this->signatureSubject : null; + $signatureAlgorithm = self::identifySignatureAlgorithm($this->privateKey); + // re-signing a SPKAC seems silly but since everything else supports re-signing why not? + if (isset($this->currentCert) && \is_array($this->currentCert) && isset($this->currentCert['publicKeyAndChallenge'])) { + $this->currentCert['signatureAlgorithm'] = $signatureAlgorithm; + $this->currentCert['publicKeyAndChallenge']['spki'] = $publicKey; + if (!empty($this->challenge)) { + // the bitwise AND ensures that the output is a valid IA5String + $this->currentCert['publicKeyAndChallenge']['challenge'] = $this->challenge & \str_repeat("", \strlen($this->challenge)); + } + } else { + $this->currentCert = ['publicKeyAndChallenge' => [ + 'spki' => $publicKey, + // quoting , + // "A challenge string that is submitted along with the public key. Defaults to an empty string if not specified." + // both Firefox and OpenSSL ("openssl spkac -key private.key") behave this way + // we could alternatively do this instead if we ignored the specs: + // Random::string(8) & str_repeat("\x7F", 8) + 'challenge' => !empty($this->challenge) ? $this->challenge : '', + ], 'signatureAlgorithm' => $signatureAlgorithm, 'signature' => \false]; + } + // resync $this->signatureSubject + // save $publicKeyAndChallenge in case there are any \phpseclib3\File\ASN1\Element objects in it + $publicKeyAndChallenge = $this->currentCert['publicKeyAndChallenge']; + $this->loadSPKAC($this->saveSPKAC($this->currentCert)); + $result = $this->currentCert; + $this->currentCert['signature'] = $result['signature'] = "\x00" . $this->privateKey->sign($this->signatureSubject); + $result['publicKeyAndChallenge'] = $publicKeyAndChallenge; + $this->currentCert = $currentCert; + $this->signatureSubject = $signatureSubject; + return $result; + } + /** + * Sign a CRL + * + * $issuer's private key needs to be loaded. + * + * @return mixed + */ + public function signCRL(\FluentSmtpLib\phpseclib3\File\X509 $issuer, \FluentSmtpLib\phpseclib3\File\X509 $crl) + { + if (!\is_object($issuer->privateKey) || empty($issuer->dn)) { + return \false; + } + $currentCert = isset($this->currentCert) ? $this->currentCert : null; + $signatureSubject = isset($this->signatureSubject) ? $this->signatureSubject : null; + $signatureAlgorithm = self::identifySignatureAlgorithm($issuer->privateKey); + $thisUpdate = new \DateTimeImmutable('now', new \DateTimeZone(@\date_default_timezone_get())); + $thisUpdate = !empty($this->startDate) ? $this->startDate : $thisUpdate->format('D, d M Y H:i:s O'); + if (isset($crl->currentCert) && \is_array($crl->currentCert) && isset($crl->currentCert['tbsCertList'])) { + $this->currentCert = $crl->currentCert; + $this->currentCert['tbsCertList']['signature'] = $signatureAlgorithm; + $this->currentCert['signatureAlgorithm'] = $signatureAlgorithm; + } else { + $this->currentCert = ['tbsCertList' => [ + 'version' => 'v2', + 'signature' => $signatureAlgorithm, + 'issuer' => \false, + // this is going to be overwritten later + 'thisUpdate' => $this->timeField($thisUpdate), + ], 'signatureAlgorithm' => $signatureAlgorithm, 'signature' => \false]; + } + $tbsCertList =& $this->currentCert['tbsCertList']; + $tbsCertList['issuer'] = $issuer->dn; + $tbsCertList['thisUpdate'] = $this->timeField($thisUpdate); + if (!empty($this->endDate)) { + $tbsCertList['nextUpdate'] = $this->timeField($this->endDate); + // $this->setEndDate() + } else { + unset($tbsCertList['nextUpdate']); + } + if (!empty($this->serialNumber)) { + $crlNumber = $this->serialNumber; + } else { + $crlNumber = $this->getExtension('id-ce-cRLNumber'); + // "The CRL number is a non-critical CRL extension that conveys a + // monotonically increasing sequence number for a given CRL scope and + // CRL issuer. This extension allows users to easily determine when a + // particular CRL supersedes another CRL." + // -- https://tools.ietf.org/html/rfc5280#section-5.2.3 + $crlNumber = $crlNumber !== \false ? $crlNumber->add(new \FluentSmtpLib\phpseclib3\Math\BigInteger(1)) : null; + } + $this->removeExtension('id-ce-authorityKeyIdentifier'); + $this->removeExtension('id-ce-issuerAltName'); + // Be sure version >= v2 if some extension found. + $version = isset($tbsCertList['version']) ? $tbsCertList['version'] : 0; + if (!$version) { + if (!empty($tbsCertList['crlExtensions'])) { + $version = 'v2'; + // v2. + } elseif (!empty($tbsCertList['revokedCertificates'])) { + foreach ($tbsCertList['revokedCertificates'] as $cert) { + if (!empty($cert['crlEntryExtensions'])) { + $version = 'v2'; + // v2. + } + } + } + if ($version) { + $tbsCertList['version'] = $version; + } + } + // Store additional extensions. + if (!empty($tbsCertList['version'])) { + // At least v2. + if (!empty($crlNumber)) { + $this->setExtension('id-ce-cRLNumber', $crlNumber); + } + if (isset($issuer->currentKeyIdentifier)) { + $this->setExtension('id-ce-authorityKeyIdentifier', [ + //'authorityCertIssuer' => array( + // ] + // 'directoryName' => $issuer->dn + // ] + //), + 'keyIdentifier' => $issuer->currentKeyIdentifier, + ]); + //$extensions = &$tbsCertList['crlExtensions']; + //if (isset($issuer->serialNumber)) { + // $extensions[count($extensions) - 1]['authorityCertSerialNumber'] = $issuer->serialNumber; + //} + //unset($extensions); + } + $issuerAltName = $this->getExtension('id-ce-subjectAltName', $issuer->currentCert); + if ($issuerAltName !== \false) { + $this->setExtension('id-ce-issuerAltName', $issuerAltName); + } + } + if (empty($tbsCertList['revokedCertificates'])) { + unset($tbsCertList['revokedCertificates']); + } + unset($tbsCertList); + // resync $this->signatureSubject + // save $tbsCertList in case there are any \phpseclib3\File\ASN1\Element objects in it + $tbsCertList = $this->currentCert['tbsCertList']; + $this->loadCRL($this->saveCRL($this->currentCert)); + $result = $this->currentCert; + $this->currentCert['signature'] = $result['signature'] = "\x00" . $issuer->privateKey->sign($this->signatureSubject); + $result['tbsCertList'] = $tbsCertList; + $this->currentCert = $currentCert; + $this->signatureSubject = $signatureSubject; + return $result; + } + /** + * Identify signature algorithm from key settings + * + * @param PrivateKey $key + * @throws UnsupportedAlgorithmException if the algorithm is unsupported + * @return array + */ + private static function identifySignatureAlgorithm(\FluentSmtpLib\phpseclib3\Crypt\Common\PrivateKey $key) + { + if ($key instanceof \FluentSmtpLib\phpseclib3\Crypt\RSA) { + if ($key->getPadding() & \FluentSmtpLib\phpseclib3\Crypt\RSA::SIGNATURE_PSS) { + $r = \FluentSmtpLib\phpseclib3\Crypt\RSA\Formats\Keys\PSS::load($key->withPassword()->toString('PSS')); + return ['algorithm' => 'id-RSASSA-PSS', 'parameters' => \FluentSmtpLib\phpseclib3\Crypt\RSA\Formats\Keys\PSS::savePSSParams($r)]; + } + switch ($key->getHash()) { + case 'md2': + case 'md5': + case 'sha1': + case 'sha224': + case 'sha256': + case 'sha384': + case 'sha512': + return ['algorithm' => $key->getHash() . 'WithRSAEncryption', 'parameters' => null]; + } + throw new \FluentSmtpLib\phpseclib3\Exception\UnsupportedAlgorithmException('The only supported hash algorithms for RSA are: md2, md5, sha1, sha224, sha256, sha384, sha512'); + } + if ($key instanceof \FluentSmtpLib\phpseclib3\Crypt\DSA) { + switch ($key->getHash()) { + case 'sha1': + case 'sha224': + case 'sha256': + return ['algorithm' => 'id-dsa-with-' . $key->getHash()]; + } + throw new \FluentSmtpLib\phpseclib3\Exception\UnsupportedAlgorithmException('The only supported hash algorithms for DSA are: sha1, sha224, sha256'); + } + if ($key instanceof \FluentSmtpLib\phpseclib3\Crypt\EC) { + switch ($key->getCurve()) { + case 'Ed25519': + case 'Ed448': + return ['algorithm' => 'id-' . $key->getCurve()]; + } + switch ($key->getHash()) { + case 'sha1': + case 'sha224': + case 'sha256': + case 'sha384': + case 'sha512': + return ['algorithm' => 'ecdsa-with-' . \strtoupper($key->getHash())]; + } + throw new \FluentSmtpLib\phpseclib3\Exception\UnsupportedAlgorithmException('The only supported hash algorithms for EC are: sha1, sha224, sha256, sha384, sha512'); + } + throw new \FluentSmtpLib\phpseclib3\Exception\UnsupportedAlgorithmException('The only supported public key classes are: RSA, DSA, EC'); + } + /** + * Set certificate start date + * + * @param \DateTimeInterface|string $date + */ + public function setStartDate($date) + { + if (!\is_object($date) || !$date instanceof \DateTimeInterface) { + $date = new \DateTimeImmutable($date, new \DateTimeZone(@\date_default_timezone_get())); + } + $this->startDate = $date->format('D, d M Y H:i:s O'); + } + /** + * Set certificate end date + * + * @param \DateTimeInterface|string $date + */ + public function setEndDate($date) + { + /* + To indicate that a certificate has no well-defined expiration date, + the notAfter SHOULD be assigned the GeneralizedTime value of + 99991231235959Z. + + -- http://tools.ietf.org/html/rfc5280#section-4.1.2.5 + */ + if (\is_string($date) && \strtolower($date) === 'lifetime') { + $temp = '99991231235959Z'; + $temp = \chr(\FluentSmtpLib\phpseclib3\File\ASN1::TYPE_GENERALIZED_TIME) . \FluentSmtpLib\phpseclib3\File\ASN1::encodeLength(\strlen($temp)) . $temp; + $this->endDate = new \FluentSmtpLib\phpseclib3\File\ASN1\Element($temp); + } else { + if (!\is_object($date) || !$date instanceof \DateTimeInterface) { + $date = new \DateTimeImmutable($date, new \DateTimeZone(@\date_default_timezone_get())); + } + $this->endDate = $date->format('D, d M Y H:i:s O'); + } + } + /** + * Set Serial Number + * + * @param string $serial + * @param int $base optional + */ + public function setSerialNumber($serial, $base = -256) + { + $this->serialNumber = new \FluentSmtpLib\phpseclib3\Math\BigInteger($serial, $base); + } + /** + * Turns the certificate into a certificate authority + * + */ + public function makeCA() + { + $this->caFlag = \true; + } + /** + * Check for validity of subarray + * + * This is intended for use in conjunction with _subArrayUnchecked(), + * implementing the checks included in _subArray() but without copying + * a potentially large array by passing its reference by-value to is_array(). + * + * @param array $root + * @param string $path + * @return boolean + */ + private function isSubArrayValid(array $root, $path) + { + if (!\is_array($root)) { + return \false; + } + foreach (\explode('/', $path) as $i) { + if (!\is_array($root)) { + return \false; + } + if (!isset($root[$i])) { + return \true; + } + $root = $root[$i]; + } + return \true; + } + /** + * Get a reference to a subarray + * + * This variant of _subArray() does no is_array() checking, + * so $root should be checked with _isSubArrayValid() first. + * + * This is here for performance reasons: + * Passing a reference (i.e. $root) by-value (i.e. to is_array()) + * creates a copy. If $root is an especially large array, this is expensive. + * + * @param array $root + * @param string $path absolute path with / as component separator + * @param bool $create optional + * @return array|false + */ + private function &subArrayUnchecked(array &$root, $path, $create = \false) + { + $false = \false; + foreach (\explode('/', $path) as $i) { + if (!isset($root[$i])) { + if (!$create) { + return $false; + } + $root[$i] = []; + } + $root =& $root[$i]; + } + return $root; + } + /** + * Get a reference to a subarray + * + * @param array $root + * @param string $path absolute path with / as component separator + * @param bool $create optional + * @return array|false + */ + private function &subArray(&$root, $path, $create = \false) + { + $false = \false; + if (!\is_array($root)) { + return $false; + } + foreach (\explode('/', $path) as $i) { + if (!\is_array($root)) { + return $false; + } + if (!isset($root[$i])) { + if (!$create) { + return $false; + } + $root[$i] = []; + } + $root =& $root[$i]; + } + return $root; + } + /** + * Get a reference to an extension subarray + * + * @param array $root + * @param string $path optional absolute path with / as component separator + * @param bool $create optional + * @return array|false + */ + private function &extensions(&$root, $path = null, $create = \false) + { + if (!isset($root)) { + $root = $this->currentCert; + } + switch (\true) { + case !empty($path): + case !\is_array($root): + break; + case isset($root['tbsCertificate']): + $path = 'tbsCertificate/extensions'; + break; + case isset($root['tbsCertList']): + $path = 'tbsCertList/crlExtensions'; + break; + case isset($root['certificationRequestInfo']): + $pth = 'certificationRequestInfo/attributes'; + $attributes =& $this->subArray($root, $pth, $create); + if (\is_array($attributes)) { + foreach ($attributes as $key => $value) { + if ($value['type'] == 'pkcs-9-at-extensionRequest') { + $path = "{$pth}/{$key}/value/0"; + break 2; + } + } + if ($create) { + $key = \count($attributes); + $attributes[] = ['type' => 'pkcs-9-at-extensionRequest', 'value' => []]; + $path = "{$pth}/{$key}/value/0"; + } + } + break; + } + $extensions =& $this->subArray($root, $path, $create); + if (!\is_array($extensions)) { + $false = \false; + return $false; + } + return $extensions; + } + /** + * Remove an Extension + * + * @param string $id + * @param string $path optional + * @return bool + */ + private function removeExtensionHelper($id, $path = null) + { + $extensions =& $this->extensions($this->currentCert, $path); + if (!\is_array($extensions)) { + return \false; + } + $result = \false; + foreach ($extensions as $key => $value) { + if ($value['extnId'] == $id) { + unset($extensions[$key]); + $result = \true; + } + } + $extensions = \array_values($extensions); + // fix for https://bugs.php.net/75433 affecting PHP 7.2 + if (!isset($extensions[0])) { + $extensions = \array_splice($extensions, 0, 0); + } + return $result; + } + /** + * Get an Extension + * + * Returns the extension if it exists and false if not + * + * @param string $id + * @param array $cert optional + * @param string $path optional + * @return mixed + */ + private function getExtensionHelper($id, $cert = null, $path = null) + { + $extensions = $this->extensions($cert, $path); + if (!\is_array($extensions)) { + return \false; + } + foreach ($extensions as $key => $value) { + if ($value['extnId'] == $id) { + return $value['extnValue']; + } + } + return \false; + } + /** + * Returns a list of all extensions in use + * + * @param array $cert optional + * @param string $path optional + * @return array + */ + private function getExtensionsHelper($cert = null, $path = null) + { + $exts = $this->extensions($cert, $path); + $extensions = []; + if (\is_array($exts)) { + foreach ($exts as $extension) { + $extensions[] = $extension['extnId']; + } + } + return $extensions; + } + /** + * Set an Extension + * + * @param string $id + * @param mixed $value + * @param bool $critical optional + * @param bool $replace optional + * @param string $path optional + * @return bool + */ + private function setExtensionHelper($id, $value, $critical = \false, $replace = \true, $path = null) + { + $extensions =& $this->extensions($this->currentCert, $path, \true); + if (!\is_array($extensions)) { + return \false; + } + $newext = ['extnId' => $id, 'critical' => $critical, 'extnValue' => $value]; + foreach ($extensions as $key => $value) { + if ($value['extnId'] == $id) { + if (!$replace) { + return \false; + } + $extensions[$key] = $newext; + return \true; + } + } + $extensions[] = $newext; + return \true; + } + /** + * Remove a certificate, CSR or CRL Extension + * + * @param string $id + * @return bool + */ + public function removeExtension($id) + { + return $this->removeExtensionHelper($id); + } + /** + * Get a certificate, CSR or CRL Extension + * + * Returns the extension if it exists and false if not + * + * @param string $id + * @param array $cert optional + * @param string $path + * @return mixed + */ + public function getExtension($id, $cert = null, $path = null) + { + return $this->getExtensionHelper($id, $cert, $path); + } + /** + * Returns a list of all extensions in use in certificate, CSR or CRL + * + * @param array $cert optional + * @param string $path optional + * @return array + */ + public function getExtensions($cert = null, $path = null) + { + return $this->getExtensionsHelper($cert, $path); + } + /** + * Set a certificate, CSR or CRL Extension + * + * @param string $id + * @param mixed $value + * @param bool $critical optional + * @param bool $replace optional + * @return bool + */ + public function setExtension($id, $value, $critical = \false, $replace = \true) + { + return $this->setExtensionHelper($id, $value, $critical, $replace); + } + /** + * Remove a CSR attribute. + * + * @param string $id + * @param int $disposition optional + * @return bool + */ + public function removeAttribute($id, $disposition = self::ATTR_ALL) + { + $attributes =& $this->subArray($this->currentCert, 'certificationRequestInfo/attributes'); + if (!\is_array($attributes)) { + return \false; + } + $result = \false; + foreach ($attributes as $key => $attribute) { + if ($attribute['type'] == $id) { + $n = \count($attribute['value']); + switch (\true) { + case $disposition == self::ATTR_APPEND: + case $disposition == self::ATTR_REPLACE: + return \false; + case $disposition >= $n: + $disposition -= $n; + break; + case $disposition == self::ATTR_ALL: + case $n == 1: + unset($attributes[$key]); + $result = \true; + break; + default: + unset($attributes[$key]['value'][$disposition]); + $attributes[$key]['value'] = \array_values($attributes[$key]['value']); + $result = \true; + break; + } + if ($result && $disposition != self::ATTR_ALL) { + break; + } + } + } + $attributes = \array_values($attributes); + return $result; + } + /** + * Get a CSR attribute + * + * Returns the attribute if it exists and false if not + * + * @param string $id + * @param int $disposition optional + * @param array $csr optional + * @return mixed + */ + public function getAttribute($id, $disposition = self::ATTR_ALL, $csr = null) + { + if (empty($csr)) { + $csr = $this->currentCert; + } + $attributes = $this->subArray($csr, 'certificationRequestInfo/attributes'); + if (!\is_array($attributes)) { + return \false; + } + foreach ($attributes as $key => $attribute) { + if ($attribute['type'] == $id) { + $n = \count($attribute['value']); + switch (\true) { + case $disposition == self::ATTR_APPEND: + case $disposition == self::ATTR_REPLACE: + return \false; + case $disposition == self::ATTR_ALL: + return $attribute['value']; + case $disposition >= $n: + $disposition -= $n; + break; + default: + return $attribute['value'][$disposition]; + } + } + } + return \false; + } + /** + * Get all requested CSR extensions + * + * Returns the list of extensions if there are any and false if not + * + * @param array $csr optional + * @return mixed + */ + public function getRequestedCertificateExtensions($csr = null) + { + if (empty($csr)) { + $csr = $this->currentCert; + } + $requestedExtensions = $this->getAttribute('pkcs-9-at-extensionRequest'); + if ($requestedExtensions === \false) { + return \false; + } + return $this->getAttribute('pkcs-9-at-extensionRequest')[0]; + } + /** + * Returns a list of all CSR attributes in use + * + * @param array $csr optional + * @return array + */ + public function getAttributes($csr = null) + { + if (empty($csr)) { + $csr = $this->currentCert; + } + $attributes = $this->subArray($csr, 'certificationRequestInfo/attributes'); + $attrs = []; + if (\is_array($attributes)) { + foreach ($attributes as $attribute) { + $attrs[] = $attribute['type']; + } + } + return $attrs; + } + /** + * Set a CSR attribute + * + * @param string $id + * @param mixed $value + * @param int $disposition optional + * @return bool + */ + public function setAttribute($id, $value, $disposition = self::ATTR_ALL) + { + $attributes =& $this->subArray($this->currentCert, 'certificationRequestInfo/attributes', \true); + if (!\is_array($attributes)) { + return \false; + } + switch ($disposition) { + case self::ATTR_REPLACE: + $disposition = self::ATTR_APPEND; + // fall-through + case self::ATTR_ALL: + $this->removeAttribute($id); + break; + } + foreach ($attributes as $key => $attribute) { + if ($attribute['type'] == $id) { + $n = \count($attribute['value']); + switch (\true) { + case $disposition == self::ATTR_APPEND: + $last = $key; + break; + case $disposition >= $n: + $disposition -= $n; + break; + default: + $attributes[$key]['value'][$disposition] = $value; + return \true; + } + } + } + switch (\true) { + case $disposition >= 0: + return \false; + case isset($last): + $attributes[$last]['value'][] = $value; + break; + default: + $attributes[] = ['type' => $id, 'value' => $disposition == self::ATTR_ALL ? $value : [$value]]; + break; + } + return \true; + } + /** + * Sets the subject key identifier + * + * This is used by the id-ce-authorityKeyIdentifier and the id-ce-subjectKeyIdentifier extensions. + * + * @param string $value + */ + public function setKeyIdentifier($value) + { + if (empty($value)) { + unset($this->currentKeyIdentifier); + } else { + $this->currentKeyIdentifier = $value; + } + } + /** + * Compute a public key identifier. + * + * Although key identifiers may be set to any unique value, this function + * computes key identifiers from public key according to the two + * recommended methods (4.2.1.2 RFC 3280). + * Highly polymorphic: try to accept all possible forms of key: + * - Key object + * - \phpseclib3\File\X509 object with public or private key defined + * - Certificate or CSR array + * - \phpseclib3\File\ASN1\Element object + * - PEM or DER string + * + * @param mixed $key optional + * @param int $method optional + * @return string binary key identifier + */ + public function computeKeyIdentifier($key = null, $method = 1) + { + if (\is_null($key)) { + $key = $this; + } + switch (\true) { + case \is_string($key): + break; + case \is_array($key) && isset($key['tbsCertificate']['subjectPublicKeyInfo']['subjectPublicKey']): + return $this->computeKeyIdentifier($key['tbsCertificate']['subjectPublicKeyInfo']['subjectPublicKey'], $method); + case \is_array($key) && isset($key['certificationRequestInfo']['subjectPKInfo']['subjectPublicKey']): + return $this->computeKeyIdentifier($key['certificationRequestInfo']['subjectPKInfo']['subjectPublicKey'], $method); + case !\is_object($key): + return \false; + case $key instanceof \FluentSmtpLib\phpseclib3\File\ASN1\Element: + // Assume the element is a bitstring-packed key. + $decoded = \FluentSmtpLib\phpseclib3\File\ASN1::decodeBER($key->element); + if (!$decoded) { + return \false; + } + $raw = \FluentSmtpLib\phpseclib3\File\ASN1::asn1map($decoded[0], ['type' => \FluentSmtpLib\phpseclib3\File\ASN1::TYPE_BIT_STRING]); + if (empty($raw)) { + return \false; + } + // If the key is private, compute identifier from its corresponding public key. + $key = \FluentSmtpLib\phpseclib3\Crypt\PublicKeyLoader::load($raw); + if ($key instanceof \FluentSmtpLib\phpseclib3\Crypt\Common\PrivateKey) { + // If private. + return $this->computeKeyIdentifier($key, $method); + } + $key = $raw; + // Is a public key. + break; + case $key instanceof \FluentSmtpLib\phpseclib3\File\X509: + if (isset($key->publicKey)) { + return $this->computeKeyIdentifier($key->publicKey, $method); + } + if (isset($key->privateKey)) { + return $this->computeKeyIdentifier($key->privateKey, $method); + } + if (isset($key->currentCert['tbsCertificate']) || isset($key->currentCert['certificationRequestInfo'])) { + return $this->computeKeyIdentifier($key->currentCert, $method); + } + return \false; + default: + // Should be a key object (i.e.: \phpseclib3\Crypt\RSA). + $key = $key->getPublicKey(); + break; + } + // If in PEM format, convert to binary. + $key = \FluentSmtpLib\phpseclib3\File\ASN1::extractBER($key); + // Now we have the key string: compute its sha-1 sum. + $hash = new \FluentSmtpLib\phpseclib3\Crypt\Hash('sha1'); + $hash = $hash->hash($key); + if ($method == 2) { + $hash = \substr($hash, -8); + $hash[0] = \chr(\ord($hash[0]) & 0xf | 0x40); + } + return $hash; + } + /** + * Format a public key as appropriate + * + * @return array|false + */ + private function formatSubjectPublicKey() + { + $format = $this->publicKey instanceof \FluentSmtpLib\phpseclib3\Crypt\RSA && $this->publicKey->getPadding() & \FluentSmtpLib\phpseclib3\Crypt\RSA::SIGNATURE_PSS ? 'PSS' : 'PKCS8'; + $publicKey = \base64_decode(\preg_replace('#-.+-|[\\r\\n]#', '', $this->publicKey->toString($format))); + $decoded = \FluentSmtpLib\phpseclib3\File\ASN1::decodeBER($publicKey); + if (!$decoded) { + return \false; + } + $mapped = \FluentSmtpLib\phpseclib3\File\ASN1::asn1map($decoded[0], \FluentSmtpLib\phpseclib3\File\ASN1\Maps\SubjectPublicKeyInfo::MAP); + if (!\is_array($mapped)) { + return \false; + } + $mapped['subjectPublicKey'] = $this->publicKey->toString($format); + return $mapped; + } + /** + * Set the domain name's which the cert is to be valid for + * + * @param mixed ...$domains + * @return void + */ + public function setDomain(...$domains) + { + $this->domains = $domains; + $this->removeDNProp('id-at-commonName'); + $this->setDNProp('id-at-commonName', $this->domains[0]); + } + /** + * Set the IP Addresses's which the cert is to be valid for + * + * @param mixed[] ...$ipAddresses + */ + public function setIPAddress(...$ipAddresses) + { + $this->ipAddresses = $ipAddresses; + /* + if (!isset($this->domains)) { + $this->removeDNProp('id-at-commonName'); + $this->setDNProp('id-at-commonName', $this->ipAddresses[0]); + } + */ + } + /** + * Helper function to build domain array + * + * @param string $domain + * @return array + */ + private static function dnsName($domain) + { + return ['dNSName' => $domain]; + } + /** + * Helper function to build IP Address array + * + * (IPv6 is not currently supported) + * + * @param string $address + * @return array + */ + private function iPAddress($address) + { + return ['iPAddress' => $address]; + } + /** + * Get the index of a revoked certificate. + * + * @param array $rclist + * @param string $serial + * @param bool $create optional + * @return int|false + */ + private function revokedCertificate(array &$rclist, $serial, $create = \false) + { + $serial = new \FluentSmtpLib\phpseclib3\Math\BigInteger($serial); + foreach ($rclist as $i => $rc) { + if (!$serial->compare($rc['userCertificate'])) { + return $i; + } + } + if (!$create) { + return \false; + } + $i = \count($rclist); + $revocationDate = new \DateTimeImmutable('now', new \DateTimeZone(@\date_default_timezone_get())); + $rclist[] = ['userCertificate' => $serial, 'revocationDate' => $this->timeField($revocationDate->format('D, d M Y H:i:s O'))]; + return $i; + } + /** + * Revoke a certificate. + * + * @param string $serial + * @param string $date optional + * @return bool + */ + public function revoke($serial, $date = null) + { + if (isset($this->currentCert['tbsCertList'])) { + if (\is_array($rclist =& $this->subArray($this->currentCert, 'tbsCertList/revokedCertificates', \true))) { + if ($this->revokedCertificate($rclist, $serial) === \false) { + // If not yet revoked + if (($i = $this->revokedCertificate($rclist, $serial, \true)) !== \false) { + if (!empty($date)) { + $rclist[$i]['revocationDate'] = $this->timeField($date); + } + return \true; + } + } + } + } + return \false; + } + /** + * Unrevoke a certificate. + * + * @param string $serial + * @return bool + */ + public function unrevoke($serial) + { + if (\is_array($rclist =& $this->subArray($this->currentCert, 'tbsCertList/revokedCertificates'))) { + if (($i = $this->revokedCertificate($rclist, $serial)) !== \false) { + unset($rclist[$i]); + $rclist = \array_values($rclist); + return \true; + } + } + return \false; + } + /** + * Get a revoked certificate. + * + * @param string $serial + * @return mixed + */ + public function getRevoked($serial) + { + if (\is_array($rclist = $this->subArray($this->currentCert, 'tbsCertList/revokedCertificates'))) { + if (($i = $this->revokedCertificate($rclist, $serial)) !== \false) { + return $rclist[$i]; + } + } + return \false; + } + /** + * List revoked certificates + * + * @param array $crl optional + * @return array|bool + */ + public function listRevoked($crl = null) + { + if (!isset($crl)) { + $crl = $this->currentCert; + } + if (!isset($crl['tbsCertList'])) { + return \false; + } + $result = []; + if (\is_array($rclist = $this->subArray($crl, 'tbsCertList/revokedCertificates'))) { + foreach ($rclist as $rc) { + $result[] = $rc['userCertificate']->toString(); + } + } + return $result; + } + /** + * Remove a Revoked Certificate Extension + * + * @param string $serial + * @param string $id + * @return bool + */ + public function removeRevokedCertificateExtension($serial, $id) + { + if (\is_array($rclist =& $this->subArray($this->currentCert, 'tbsCertList/revokedCertificates'))) { + if (($i = $this->revokedCertificate($rclist, $serial)) !== \false) { + return $this->removeExtensionHelper($id, "tbsCertList/revokedCertificates/{$i}/crlEntryExtensions"); + } + } + return \false; + } + /** + * Get a Revoked Certificate Extension + * + * Returns the extension if it exists and false if not + * + * @param string $serial + * @param string $id + * @param array $crl optional + * @return mixed + */ + public function getRevokedCertificateExtension($serial, $id, $crl = null) + { + if (!isset($crl)) { + $crl = $this->currentCert; + } + if (\is_array($rclist = $this->subArray($crl, 'tbsCertList/revokedCertificates'))) { + if (($i = $this->revokedCertificate($rclist, $serial)) !== \false) { + return $this->getExtension($id, $crl, "tbsCertList/revokedCertificates/{$i}/crlEntryExtensions"); + } + } + return \false; + } + /** + * Returns a list of all extensions in use for a given revoked certificate + * + * @param string $serial + * @param array $crl optional + * @return array|bool + */ + public function getRevokedCertificateExtensions($serial, $crl = null) + { + if (!isset($crl)) { + $crl = $this->currentCert; + } + if (\is_array($rclist = $this->subArray($crl, 'tbsCertList/revokedCertificates'))) { + if (($i = $this->revokedCertificate($rclist, $serial)) !== \false) { + return $this->getExtensions($crl, "tbsCertList/revokedCertificates/{$i}/crlEntryExtensions"); + } + } + return \false; + } + /** + * Set a Revoked Certificate Extension + * + * @param string $serial + * @param string $id + * @param mixed $value + * @param bool $critical optional + * @param bool $replace optional + * @return bool + */ + public function setRevokedCertificateExtension($serial, $id, $value, $critical = \false, $replace = \true) + { + if (isset($this->currentCert['tbsCertList'])) { + if (\is_array($rclist =& $this->subArray($this->currentCert, 'tbsCertList/revokedCertificates', \true))) { + if (($i = $this->revokedCertificate($rclist, $serial, \true)) !== \false) { + return $this->setExtensionHelper($id, $value, $critical, $replace, "tbsCertList/revokedCertificates/{$i}/crlEntryExtensions"); + } + } + } + return \false; + } + /** + * Register the mapping for a custom/unsupported extension. + * + * @param string $id + * @param array $mapping + */ + public static function registerExtension($id, array $mapping) + { + if (isset(self::$extensions[$id]) && self::$extensions[$id] !== $mapping) { + throw new \RuntimeException('Extension ' . $id . ' has already been defined with a different mapping.'); + } + self::$extensions[$id] = $mapping; + } + /** + * Register the mapping for a custom/unsupported extension. + * + * @param string $id + * + * @return array|null + */ + public static function getRegisteredExtension($id) + { + return isset(self::$extensions[$id]) ? self::$extensions[$id] : null; + } + /** + * Register the mapping for a custom/unsupported extension. + * + * @param string $id + * @param mixed $value + * @param bool $critical + * @param bool $replace + */ + public function setExtensionValue($id, $value, $critical = \false, $replace = \false) + { + $this->extensionValues[$id] = \compact('critical', 'replace', 'value'); + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger.php new file mode 100644 index 0000000..3fe4eea --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger.php @@ -0,0 +1,802 @@ + + * add($b); + * + * echo $c->toString(); // outputs 5 + * ?> + * + * + * @author Jim Wigginton + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + */ +namespace FluentSmtpLib\phpseclib3\Math; + +use FluentSmtpLib\phpseclib3\Exception\BadConfigurationException; +use FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\Engine; +/** + * Pure-PHP arbitrary precision integer arithmetic library. Supports base-2, base-10, base-16, and base-256 + * numbers. + * + * @author Jim Wigginton + */ +class BigInteger implements \JsonSerializable +{ + /** + * Main Engine + * + * @var class-string + */ + private static $mainEngine; + /** + * Selected Engines + * + * @var list + */ + private static $engines; + /** + * The actual BigInteger object + * + * @var object + */ + private $value; + /** + * Mode independent value used for serialization. + * + * @see self::__sleep() + * @see self::__wakeup() + * @var string + */ + private $hex; + /** + * Precision (used only for serialization) + * + * @see self::__sleep() + * @see self::__wakeup() + * @var int + */ + private $precision; + /** + * Sets engine type. + * + * Throws an exception if the type is invalid + * + * @param string $main + * @param list $modexps optional + * @return void + */ + public static function setEngine($main, array $modexps = ['DefaultEngine']) + { + self::$engines = []; + $fqmain = '\\FluentSmtpLib\\phpseclib3\\Math\\BigInteger\\Engines\\' . $main; + if (!\class_exists($fqmain) || !\method_exists($fqmain, 'isValidEngine')) { + throw new \InvalidArgumentException("{$main} is not a valid engine"); + } + if (!$fqmain::isValidEngine()) { + throw new \FluentSmtpLib\phpseclib3\Exception\BadConfigurationException("{$main} is not setup correctly on this system"); + } + /** @var class-string $fqmain */ + self::$mainEngine = $fqmain; + $found = \false; + foreach ($modexps as $modexp) { + try { + $fqmain::setModExpEngine($modexp); + $found = \true; + break; + } catch (\Exception $e) { + } + } + if (!$found) { + throw new \FluentSmtpLib\phpseclib3\Exception\BadConfigurationException("No valid modular exponentiation engine found for {$main}"); + } + self::$engines = [$main, $modexp]; + } + /** + * Returns the engine type + * + * @return string[] + */ + public static function getEngine() + { + self::initialize_static_variables(); + return self::$engines; + } + /** + * Initialize static variables + */ + private static function initialize_static_variables() + { + if (!isset(self::$mainEngine)) { + $engines = [['GMP', ['DefaultEngine']], ['PHP64', ['OpenSSL']], ['BCMath', ['OpenSSL']], ['PHP32', ['OpenSSL']], ['PHP64', ['DefaultEngine']], ['PHP32', ['DefaultEngine']]]; + foreach ($engines as $engine) { + try { + self::setEngine($engine[0], $engine[1]); + return; + } catch (\Exception $e) { + } + } + throw new \UnexpectedValueException('No valid BigInteger found. This is only possible when JIT is enabled on Windows and neither the GMP or BCMath extensions are available so either disable JIT or install GMP / BCMath'); + } + } + /** + * Converts base-2, base-10, base-16, and binary strings (base-256) to BigIntegers. + * + * If the second parameter - $base - is negative, then it will be assumed that the number's are encoded using + * two's compliment. The sole exception to this is -10, which is treated the same as 10 is. + * + * @param string|int|Engine $x Base-10 number or base-$base number if $base set. + * @param int $base + */ + public function __construct($x = 0, $base = 10) + { + self::initialize_static_variables(); + if ($x instanceof self::$mainEngine) { + $this->value = clone $x; + } elseif ($x instanceof \FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\Engine) { + $this->value = new static("{$x}"); + $this->value->setPrecision($x->getPrecision()); + } else { + $this->value = new self::$mainEngine($x, $base); + } + } + /** + * Converts a BigInteger to a base-10 number. + * + * @return string + */ + public function toString() + { + return $this->value->toString(); + } + /** + * __toString() magic method + */ + public function __toString() + { + return (string) $this->value; + } + /** + * __debugInfo() magic method + * + * Will be called, automatically, when print_r() or var_dump() are called + */ + public function __debugInfo() + { + return $this->value->__debugInfo(); + } + /** + * Converts a BigInteger to a byte string (eg. base-256). + * + * @param bool $twos_compliment + * @return string + */ + public function toBytes($twos_compliment = \false) + { + return $this->value->toBytes($twos_compliment); + } + /** + * Converts a BigInteger to a hex string (eg. base-16). + * + * @param bool $twos_compliment + * @return string + */ + public function toHex($twos_compliment = \false) + { + return $this->value->toHex($twos_compliment); + } + /** + * Converts a BigInteger to a bit string (eg. base-2). + * + * Negative numbers are saved as positive numbers, unless $twos_compliment is set to true, at which point, they're + * saved as two's compliment. + * + * @param bool $twos_compliment + * @return string + */ + public function toBits($twos_compliment = \false) + { + return $this->value->toBits($twos_compliment); + } + /** + * Adds two BigIntegers. + * + * @param BigInteger $y + * @return BigInteger + */ + public function add(\FluentSmtpLib\phpseclib3\Math\BigInteger $y) + { + return new static($this->value->add($y->value)); + } + /** + * Subtracts two BigIntegers. + * + * @param BigInteger $y + * @return BigInteger + */ + public function subtract(\FluentSmtpLib\phpseclib3\Math\BigInteger $y) + { + return new static($this->value->subtract($y->value)); + } + /** + * Multiplies two BigIntegers + * + * @param BigInteger $x + * @return BigInteger + */ + public function multiply(\FluentSmtpLib\phpseclib3\Math\BigInteger $x) + { + return new static($this->value->multiply($x->value)); + } + /** + * Divides two BigIntegers. + * + * Returns an array whose first element contains the quotient and whose second element contains the + * "common residue". If the remainder would be positive, the "common residue" and the remainder are the + * same. If the remainder would be negative, the "common residue" is equal to the sum of the remainder + * and the divisor (basically, the "common residue" is the first positive modulo). + * + * Here's an example: + * + * divide($b); + * + * echo $quotient->toString(); // outputs 0 + * echo "\r\n"; + * echo $remainder->toString(); // outputs 10 + * ?> + * + * + * @param BigInteger $y + * @return BigInteger[] + */ + public function divide(\FluentSmtpLib\phpseclib3\Math\BigInteger $y) + { + list($q, $r) = $this->value->divide($y->value); + return [new static($q), new static($r)]; + } + /** + * Calculates modular inverses. + * + * Say you have (30 mod 17 * x mod 17) mod 17 == 1. x can be found using modular inverses. + * + * @param BigInteger $n + * @return BigInteger + */ + public function modInverse(\FluentSmtpLib\phpseclib3\Math\BigInteger $n) + { + return new static($this->value->modInverse($n->value)); + } + /** + * Calculates modular inverses. + * + * Say you have (30 mod 17 * x mod 17) mod 17 == 1. x can be found using modular inverses. + * + * @param BigInteger $n + * @return BigInteger[] + */ + public function extendedGCD(\FluentSmtpLib\phpseclib3\Math\BigInteger $n) + { + \extract($this->value->extendedGCD($n->value)); + /** + * @var BigInteger $gcd + * @var BigInteger $x + * @var BigInteger $y + */ + return ['gcd' => new static($gcd), 'x' => new static($x), 'y' => new static($y)]; + } + /** + * Calculates the greatest common divisor + * + * Say you have 693 and 609. The GCD is 21. + * + * @param BigInteger $n + * @return BigInteger + */ + public function gcd(\FluentSmtpLib\phpseclib3\Math\BigInteger $n) + { + return new static($this->value->gcd($n->value)); + } + /** + * Absolute value. + * + * @return BigInteger + */ + public function abs() + { + return new static($this->value->abs()); + } + /** + * Set Precision + * + * Some bitwise operations give different results depending on the precision being used. Examples include left + * shift, not, and rotates. + * + * @param int $bits + */ + public function setPrecision($bits) + { + $this->value->setPrecision($bits); + } + /** + * Get Precision + * + * Returns the precision if it exists, false if it doesn't + * + * @return int|bool + */ + public function getPrecision() + { + return $this->value->getPrecision(); + } + /** + * Serialize + * + * Will be called, automatically, when serialize() is called on a BigInteger object. + * + * __sleep() / __wakeup() have been around since PHP 4.0 + * + * \Serializable was introduced in PHP 5.1 and deprecated in PHP 8.1: + * https://wiki.php.net/rfc/phase_out_serializable + * + * __serialize() / __unserialize() were introduced in PHP 7.4: + * https://wiki.php.net/rfc/custom_object_serialization + * + * @return array + */ + public function __sleep() + { + $this->hex = $this->toHex(\true); + $vars = ['hex']; + if ($this->getPrecision() > 0) { + $vars[] = 'precision'; + } + return $vars; + } + /** + * Serialize + * + * Will be called, automatically, when unserialize() is called on a BigInteger object. + */ + public function __wakeup() + { + $temp = new static($this->hex, -16); + $this->value = $temp->value; + if ($this->precision > 0) { + // recalculate $this->bitmask + $this->setPrecision($this->precision); + } + } + /** + * JSON Serialize + * + * Will be called, automatically, when json_encode() is called on a BigInteger object. + * + * @return array{hex: string, precision?: int] + */ + #[\ReturnTypeWillChange] + public function jsonSerialize() + { + $result = ['hex' => $this->toHex(\true)]; + if ($this->precision > 0) { + $result['precision'] = $this->getPrecision(); + } + return $result; + } + /** + * Performs modular exponentiation. + * + * @param BigInteger $e + * @param BigInteger $n + * @return BigInteger + */ + public function powMod(\FluentSmtpLib\phpseclib3\Math\BigInteger $e, \FluentSmtpLib\phpseclib3\Math\BigInteger $n) + { + return new static($this->value->powMod($e->value, $n->value)); + } + /** + * Performs modular exponentiation. + * + * @param BigInteger $e + * @param BigInteger $n + * @return BigInteger + */ + public function modPow(\FluentSmtpLib\phpseclib3\Math\BigInteger $e, \FluentSmtpLib\phpseclib3\Math\BigInteger $n) + { + return new static($this->value->modPow($e->value, $n->value)); + } + /** + * Compares two numbers. + * + * Although one might think !$x->compare($y) means $x != $y, it, in fact, means the opposite. The reason for this + * is demonstrated thusly: + * + * $x > $y: $x->compare($y) > 0 + * $x < $y: $x->compare($y) < 0 + * $x == $y: $x->compare($y) == 0 + * + * Note how the same comparison operator is used. If you want to test for equality, use $x->equals($y). + * + * {@internal Could return $this->subtract($x), but that's not as fast as what we do do.} + * + * @param BigInteger $y + * @return int in case < 0 if $this is less than $y; > 0 if $this is greater than $y, and 0 if they are equal. + * @see self::equals() + */ + public function compare(\FluentSmtpLib\phpseclib3\Math\BigInteger $y) + { + return $this->value->compare($y->value); + } + /** + * Tests the equality of two numbers. + * + * If you need to see if one number is greater than or less than another number, use BigInteger::compare() + * + * @param BigInteger $x + * @return bool + */ + public function equals(\FluentSmtpLib\phpseclib3\Math\BigInteger $x) + { + return $this->value->equals($x->value); + } + /** + * Logical Not + * + * @return BigInteger + */ + public function bitwise_not() + { + return new static($this->value->bitwise_not()); + } + /** + * Logical And + * + * @param BigInteger $x + * @return BigInteger + */ + public function bitwise_and(\FluentSmtpLib\phpseclib3\Math\BigInteger $x) + { + return new static($this->value->bitwise_and($x->value)); + } + /** + * Logical Or + * + * @param BigInteger $x + * @return BigInteger + */ + public function bitwise_or(\FluentSmtpLib\phpseclib3\Math\BigInteger $x) + { + return new static($this->value->bitwise_or($x->value)); + } + /** + * Logical Exclusive Or + * + * @param BigInteger $x + * @return BigInteger + */ + public function bitwise_xor(\FluentSmtpLib\phpseclib3\Math\BigInteger $x) + { + return new static($this->value->bitwise_xor($x->value)); + } + /** + * Logical Right Shift + * + * Shifts BigInteger's by $shift bits, effectively dividing by 2**$shift. + * + * @param int $shift + * @return BigInteger + */ + public function bitwise_rightShift($shift) + { + return new static($this->value->bitwise_rightShift($shift)); + } + /** + * Logical Left Shift + * + * Shifts BigInteger's by $shift bits, effectively multiplying by 2**$shift. + * + * @param int $shift + * @return BigInteger + */ + public function bitwise_leftShift($shift) + { + return new static($this->value->bitwise_leftShift($shift)); + } + /** + * Logical Left Rotate + * + * Instead of the top x bits being dropped they're appended to the shifted bit string. + * + * @param int $shift + * @return BigInteger + */ + public function bitwise_leftRotate($shift) + { + return new static($this->value->bitwise_leftRotate($shift)); + } + /** + * Logical Right Rotate + * + * Instead of the bottom x bits being dropped they're prepended to the shifted bit string. + * + * @param int $shift + * @return BigInteger + */ + public function bitwise_rightRotate($shift) + { + return new static($this->value->bitwise_rightRotate($shift)); + } + /** + * Returns the smallest and largest n-bit number + * + * @param int $bits + * @return BigInteger[] + */ + public static function minMaxBits($bits) + { + self::initialize_static_variables(); + $class = self::$mainEngine; + \extract($class::minMaxBits($bits)); + /** @var BigInteger $min + * @var BigInteger $max + */ + return ['min' => new static($min), 'max' => new static($max)]; + } + /** + * Return the size of a BigInteger in bits + * + * @return int + */ + public function getLength() + { + return $this->value->getLength(); + } + /** + * Return the size of a BigInteger in bytes + * + * @return int + */ + public function getLengthInBytes() + { + return $this->value->getLengthInBytes(); + } + /** + * Generates a random number of a certain size + * + * Bit length is equal to $size + * + * @param int $size + * @return BigInteger + */ + public static function random($size) + { + self::initialize_static_variables(); + $class = self::$mainEngine; + return new static($class::random($size)); + } + /** + * Generates a random prime number of a certain size + * + * Bit length is equal to $size + * + * @param int $size + * @return BigInteger + */ + public static function randomPrime($size) + { + self::initialize_static_variables(); + $class = self::$mainEngine; + return new static($class::randomPrime($size)); + } + /** + * Generate a random prime number between a range + * + * If there's not a prime within the given range, false will be returned. + * + * @param BigInteger $min + * @param BigInteger $max + * @return false|BigInteger + */ + public static function randomRangePrime(\FluentSmtpLib\phpseclib3\Math\BigInteger $min, \FluentSmtpLib\phpseclib3\Math\BigInteger $max) + { + $class = self::$mainEngine; + return new static($class::randomRangePrime($min->value, $max->value)); + } + /** + * Generate a random number between a range + * + * Returns a random number between $min and $max where $min and $max + * can be defined using one of the two methods: + * + * BigInteger::randomRange($min, $max) + * BigInteger::randomRange($max, $min) + * + * @param BigInteger $min + * @param BigInteger $max + * @return BigInteger + */ + public static function randomRange(\FluentSmtpLib\phpseclib3\Math\BigInteger $min, \FluentSmtpLib\phpseclib3\Math\BigInteger $max) + { + $class = self::$mainEngine; + return new static($class::randomRange($min->value, $max->value)); + } + /** + * Checks a numer to see if it's prime + * + * Assuming the $t parameter is not set, this function has an error rate of 2**-80. The main motivation for the + * $t parameter is distributability. BigInteger::randomPrime() can be distributed across multiple pageloads + * on a website instead of just one. + * + * @param int|bool $t + * @return bool + */ + public function isPrime($t = \false) + { + return $this->value->isPrime($t); + } + /** + * Calculates the nth root of a biginteger. + * + * Returns the nth root of a positive biginteger, where n defaults to 2 + * + * @param int $n optional + * @return BigInteger + */ + public function root($n = 2) + { + return new static($this->value->root($n)); + } + /** + * Performs exponentiation. + * + * @param BigInteger $n + * @return BigInteger + */ + public function pow(\FluentSmtpLib\phpseclib3\Math\BigInteger $n) + { + return new static($this->value->pow($n->value)); + } + /** + * Return the minimum BigInteger between an arbitrary number of BigIntegers. + * + * @param BigInteger ...$nums + * @return BigInteger + */ + public static function min(\FluentSmtpLib\phpseclib3\Math\BigInteger ...$nums) + { + $class = self::$mainEngine; + $nums = \array_map(function ($num) { + return $num->value; + }, $nums); + return new static($class::min(...$nums)); + } + /** + * Return the maximum BigInteger between an arbitrary number of BigIntegers. + * + * @param BigInteger ...$nums + * @return BigInteger + */ + public static function max(\FluentSmtpLib\phpseclib3\Math\BigInteger ...$nums) + { + $class = self::$mainEngine; + $nums = \array_map(function ($num) { + return $num->value; + }, $nums); + return new static($class::max(...$nums)); + } + /** + * Tests BigInteger to see if it is between two integers, inclusive + * + * @param BigInteger $min + * @param BigInteger $max + * @return bool + */ + public function between(\FluentSmtpLib\phpseclib3\Math\BigInteger $min, \FluentSmtpLib\phpseclib3\Math\BigInteger $max) + { + return $this->value->between($min->value, $max->value); + } + /** + * Clone + */ + public function __clone() + { + $this->value = clone $this->value; + } + /** + * Is Odd? + * + * @return bool + */ + public function isOdd() + { + return $this->value->isOdd(); + } + /** + * Tests if a bit is set + * + * @param int $x + * @return bool + */ + public function testBit($x) + { + return $this->value->testBit($x); + } + /** + * Is Negative? + * + * @return bool + */ + public function isNegative() + { + return $this->value->isNegative(); + } + /** + * Negate + * + * Given $k, returns -$k + * + * @return BigInteger + */ + public function negate() + { + return new static($this->value->negate()); + } + /** + * Scan for 1 and right shift by that amount + * + * ie. $s = gmp_scan1($n, 0) and $r = gmp_div_q($n, gmp_pow(gmp_init('2'), $s)); + * + * @param BigInteger $r + * @return int + */ + public static function scan1divide(\FluentSmtpLib\phpseclib3\Math\BigInteger $r) + { + $class = self::$mainEngine; + return $class::scan1divide($r->value); + } + /** + * Create Recurring Modulo Function + * + * Sometimes it may be desirable to do repeated modulos with the same number outside of + * modular exponentiation + * + * @return callable + */ + public function createRecurringModuloFunction() + { + $func = $this->value->createRecurringModuloFunction(); + return function (\FluentSmtpLib\phpseclib3\Math\BigInteger $x) use($func) { + return new static($func($x->value)); + }; + } + /** + * Bitwise Split + * + * Splits BigInteger's into chunks of $split bits + * + * @param int $split + * @return BigInteger[] + */ + public function bitwise_split($split) + { + return \array_map(function ($val) { + return new static($val); + }, $this->value->bitwise_split($split)); + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/BCMath.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/BCMath.php new file mode 100644 index 0000000..b771c03 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/BCMath.php @@ -0,0 +1,601 @@ + + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://pear.php.net/package/Math_BigInteger + */ +namespace FluentSmtpLib\phpseclib3\Math\BigInteger\Engines; + +use FluentSmtpLib\phpseclib3\Common\Functions\Strings; +use FluentSmtpLib\phpseclib3\Exception\BadConfigurationException; +/** + * BCMath Engine. + * + * @author Jim Wigginton + */ +class BCMath extends \FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\Engine +{ + /** + * Can Bitwise operations be done fast? + * + * @see parent::bitwise_leftRotate() + * @see parent::bitwise_rightRotate() + */ + const FAST_BITWISE = \false; + /** + * Engine Directory + * + * @see parent::setModExpEngine + */ + const ENGINE_DIR = 'BCMath'; + /** + * Test for engine validity + * + * @return bool + * @see parent::__construct() + */ + public static function isValidEngine() + { + return \extension_loaded('bcmath'); + } + /** + * Default constructor + * + * @param mixed $x integer Base-10 number or base-$base number if $base set. + * @param int $base + * @see parent::__construct() + */ + public function __construct($x = 0, $base = 10) + { + if (!isset(static::$isValidEngine[static::class])) { + static::$isValidEngine[static::class] = self::isValidEngine(); + } + if (!static::$isValidEngine[static::class]) { + throw new \FluentSmtpLib\phpseclib3\Exception\BadConfigurationException('BCMath is not setup correctly on this system'); + } + $this->value = '0'; + parent::__construct($x, $base); + } + /** + * Initialize a BCMath BigInteger Engine instance + * + * @param int $base + * @see parent::__construct() + */ + protected function initialize($base) + { + switch (\abs($base)) { + case 256: + // round $len to the nearest 4 + $len = \strlen($this->value) + 3 & ~3; + $x = \str_pad($this->value, $len, \chr(0), \STR_PAD_LEFT); + $this->value = '0'; + for ($i = 0; $i < $len; $i += 4) { + $this->value = \bcmul($this->value, '4294967296', 0); + // 4294967296 == 2**32 + $this->value = \bcadd($this->value, 0x1000000 * \ord($x[$i]) + (\ord($x[$i + 1]) << 16 | \ord($x[$i + 2]) << 8 | \ord($x[$i + 3])), 0); + } + if ($this->is_negative) { + $this->value = '-' . $this->value; + } + break; + case 16: + $x = \strlen($this->value) & 1 ? '0' . $this->value : $this->value; + $temp = new self(\FluentSmtpLib\phpseclib3\Common\Functions\Strings::hex2bin($x), 256); + $this->value = $this->is_negative ? '-' . $temp->value : $temp->value; + $this->is_negative = \false; + break; + case 10: + // explicitly casting $x to a string is necessary, here, since doing $x[0] on -1 yields different + // results then doing it on '-1' does (modInverse does $x[0]) + $this->value = $this->value === '-' ? '0' : (string) $this->value; + } + } + /** + * Converts a BigInteger to a base-10 number. + * + * @return string + */ + public function toString() + { + if ($this->value === '0') { + return '0'; + } + return \ltrim($this->value, '0'); + } + /** + * Converts a BigInteger to a byte string (eg. base-256). + * + * @param bool $twos_compliment + * @return string + */ + public function toBytes($twos_compliment = \false) + { + if ($twos_compliment) { + return $this->toBytesHelper(); + } + $value = ''; + $current = $this->value; + if ($current[0] == '-') { + $current = \substr($current, 1); + } + while (\bccomp($current, '0', 0) > 0) { + $temp = \bcmod($current, '16777216'); + $value = \chr($temp >> 16) . \chr($temp >> 8) . \chr($temp) . $value; + $current = \bcdiv($current, '16777216', 0); + } + return $this->precision > 0 ? \substr(\str_pad($value, $this->precision >> 3, \chr(0), \STR_PAD_LEFT), -($this->precision >> 3)) : \ltrim($value, \chr(0)); + } + /** + * Adds two BigIntegers. + * + * @param BCMath $y + * @return BCMath + */ + public function add(\FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\BCMath $y) + { + $temp = new self(); + $temp->value = \bcadd($this->value, $y->value); + return $this->normalize($temp); + } + /** + * Subtracts two BigIntegers. + * + * @param BCMath $y + * @return BCMath + */ + public function subtract(\FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\BCMath $y) + { + $temp = new self(); + $temp->value = \bcsub($this->value, $y->value); + return $this->normalize($temp); + } + /** + * Multiplies two BigIntegers. + * + * @param BCMath $x + * @return BCMath + */ + public function multiply(\FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\BCMath $x) + { + $temp = new self(); + $temp->value = \bcmul($this->value, $x->value); + return $this->normalize($temp); + } + /** + * Divides two BigIntegers. + * + * Returns an array whose first element contains the quotient and whose second element contains the + * "common residue". If the remainder would be positive, the "common residue" and the remainder are the + * same. If the remainder would be negative, the "common residue" is equal to the sum of the remainder + * and the divisor (basically, the "common residue" is the first positive modulo). + * + * @param BCMath $y + * @return array{static, static} + */ + public function divide(\FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\BCMath $y) + { + $quotient = new self(); + $remainder = new self(); + $quotient->value = \bcdiv($this->value, $y->value, 0); + $remainder->value = \bcmod($this->value, $y->value); + if ($remainder->value[0] == '-') { + $remainder->value = \bcadd($remainder->value, $y->value[0] == '-' ? \substr($y->value, 1) : $y->value, 0); + } + return [$this->normalize($quotient), $this->normalize($remainder)]; + } + /** + * Calculates modular inverses. + * + * Say you have (30 mod 17 * x mod 17) mod 17 == 1. x can be found using modular inverses. + * + * @param BCMath $n + * @return false|BCMath + */ + public function modInverse(\FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\BCMath $n) + { + return $this->modInverseHelper($n); + } + /** + * Calculates the greatest common divisor and Bezout's identity. + * + * Say you have 693 and 609. The GCD is 21. Bezout's identity states that there exist integers x and y such that + * 693*x + 609*y == 21. In point of fact, there are actually an infinite number of x and y combinations and which + * combination is returned is dependent upon which mode is in use. See + * {@link http://en.wikipedia.org/wiki/B%C3%A9zout%27s_identity Bezout's identity - Wikipedia} for more information. + * + * @param BCMath $n + * @return array{gcd: static, x: static, y: static} + */ + public function extendedGCD(\FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\BCMath $n) + { + // it might be faster to use the binary xGCD algorithim here, as well, but (1) that algorithim works + // best when the base is a power of 2 and (2) i don't think it'd make much difference, anyway. as is, + // the basic extended euclidean algorithim is what we're using. + $u = $this->value; + $v = $n->value; + $a = '1'; + $b = '0'; + $c = '0'; + $d = '1'; + while (\bccomp($v, '0', 0) != 0) { + $q = \bcdiv($u, $v, 0); + $temp = $u; + $u = $v; + $v = \bcsub($temp, \bcmul($v, $q, 0), 0); + $temp = $a; + $a = $c; + $c = \bcsub($temp, \bcmul($a, $q, 0), 0); + $temp = $b; + $b = $d; + $d = \bcsub($temp, \bcmul($b, $q, 0), 0); + } + return ['gcd' => $this->normalize(new static($u)), 'x' => $this->normalize(new static($a)), 'y' => $this->normalize(new static($b))]; + } + /** + * Calculates the greatest common divisor + * + * Say you have 693 and 609. The GCD is 21. + * + * @param BCMath $n + * @return BCMath + */ + public function gcd(\FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\BCMath $n) + { + \extract($this->extendedGCD($n)); + /** @var BCMath $gcd */ + return $gcd; + } + /** + * Absolute value. + * + * @return BCMath + */ + public function abs() + { + $temp = new static(); + $temp->value = \strlen($this->value) && $this->value[0] == '-' ? \substr($this->value, 1) : $this->value; + return $temp; + } + /** + * Logical And + * + * @param BCMath $x + * @return BCMath + */ + public function bitwise_and(\FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\BCMath $x) + { + return $this->bitwiseAndHelper($x); + } + /** + * Logical Or + * + * @param BCMath $x + * @return BCMath + */ + public function bitwise_or(\FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\BCMath $x) + { + return $this->bitwiseOrHelper($x); + } + /** + * Logical Exclusive Or + * + * @param BCMath $x + * @return BCMath + */ + public function bitwise_xor(\FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\BCMath $x) + { + return $this->bitwiseXorHelper($x); + } + /** + * Logical Right Shift + * + * Shifts BigInteger's by $shift bits, effectively dividing by 2**$shift. + * + * @param int $shift + * @return BCMath + */ + public function bitwise_rightShift($shift) + { + $temp = new static(); + $temp->value = \bcdiv($this->value, \bcpow('2', $shift, 0), 0); + return $this->normalize($temp); + } + /** + * Logical Left Shift + * + * Shifts BigInteger's by $shift bits, effectively multiplying by 2**$shift. + * + * @param int $shift + * @return BCMath + */ + public function bitwise_leftShift($shift) + { + $temp = new static(); + $temp->value = \bcmul($this->value, \bcpow('2', $shift, 0), 0); + return $this->normalize($temp); + } + /** + * Compares two numbers. + * + * Although one might think !$x->compare($y) means $x != $y, it, in fact, means the opposite. The reason for this + * is demonstrated thusly: + * + * $x > $y: $x->compare($y) > 0 + * $x < $y: $x->compare($y) < 0 + * $x == $y: $x->compare($y) == 0 + * + * Note how the same comparison operator is used. If you want to test for equality, use $x->equals($y). + * + * {@internal Could return $this->subtract($x), but that's not as fast as what we do do.} + * + * @param BCMath $y + * @return int in case < 0 if $this is less than $y; > 0 if $this is greater than $y, and 0 if they are equal. + * @see self::equals() + */ + public function compare(\FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\BCMath $y) + { + return \bccomp($this->value, $y->value, 0); + } + /** + * Tests the equality of two numbers. + * + * If you need to see if one number is greater than or less than another number, use BigInteger::compare() + * + * @param BCMath $x + * @return bool + */ + public function equals(\FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\BCMath $x) + { + return $this->value == $x->value; + } + /** + * Performs modular exponentiation. + * + * @param BCMath $e + * @param BCMath $n + * @return BCMath + */ + public function modPow(\FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\BCMath $e, \FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\BCMath $n) + { + return $this->powModOuter($e, $n); + } + /** + * Performs modular exponentiation. + * + * Alias for modPow(). + * + * @param BCMath $e + * @param BCMath $n + * @return BCMath + */ + public function powMod(\FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\BCMath $e, \FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\BCMath $n) + { + return $this->powModOuter($e, $n); + } + /** + * Performs modular exponentiation. + * + * @param BCMath $e + * @param BCMath $n + * @return BCMath + */ + protected function powModInner(\FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\BCMath $e, \FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\BCMath $n) + { + try { + $class = static::$modexpEngine[static::class]; + return $class::powModHelper($this, $e, $n, static::class); + } catch (\Exception $err) { + return \FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\BCMath\DefaultEngine::powModHelper($this, $e, $n, static::class); + } + } + /** + * Normalize + * + * Removes leading zeros and truncates (if necessary) to maintain the appropriate precision + * + * @param BCMath $result + * @return BCMath + */ + protected function normalize(\FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\BCMath $result) + { + $result->precision = $this->precision; + $result->bitmask = $this->bitmask; + if ($result->bitmask !== \false) { + $result->value = \bcmod($result->value, $result->bitmask->value); + } + return $result; + } + /** + * Generate a random prime number between a range + * + * If there's not a prime within the given range, false will be returned. + * + * @param BCMath $min + * @param BCMath $max + * @return false|BCMath + */ + public static function randomRangePrime(\FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\BCMath $min, \FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\BCMath $max) + { + return self::randomRangePrimeOuter($min, $max); + } + /** + * Generate a random number between a range + * + * Returns a random number between $min and $max where $min and $max + * can be defined using one of the two methods: + * + * BigInteger::randomRange($min, $max) + * BigInteger::randomRange($max, $min) + * + * @param BCMath $min + * @param BCMath $max + * @return BCMath + */ + public static function randomRange(\FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\BCMath $min, \FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\BCMath $max) + { + return self::randomRangeHelper($min, $max); + } + /** + * Make the current number odd + * + * If the current number is odd it'll be unchanged. If it's even, one will be added to it. + * + * @see self::randomPrime() + */ + protected function make_odd() + { + if (!$this->isOdd()) { + $this->value = \bcadd($this->value, '1'); + } + } + /** + * Test the number against small primes. + * + * @see self::isPrime() + */ + protected function testSmallPrimes() + { + if ($this->value === '1') { + return \false; + } + if ($this->value === '2') { + return \true; + } + if ($this->value[\strlen($this->value) - 1] % 2 == 0) { + return \false; + } + $value = $this->value; + foreach (self::PRIMES as $prime) { + $r = \bcmod($this->value, $prime); + if ($r == '0') { + return $this->value == $prime; + } + } + return \true; + } + /** + * Scan for 1 and right shift by that amount + * + * ie. $s = gmp_scan1($n, 0) and $r = gmp_div_q($n, gmp_pow(gmp_init('2'), $s)); + * + * @param BCMath $r + * @return int + * @see self::isPrime() + */ + public static function scan1divide(\FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\BCMath $r) + { + $r_value =& $r->value; + $s = 0; + // if $n was 1, $r would be 0 and this would be an infinite loop, hence our $this->equals(static::$one[static::class]) check earlier + while ($r_value[\strlen($r_value) - 1] % 2 == 0) { + $r_value = \bcdiv($r_value, '2', 0); + ++$s; + } + return $s; + } + /** + * Performs exponentiation. + * + * @param BCMath $n + * @return BCMath + */ + public function pow(\FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\BCMath $n) + { + $temp = new self(); + $temp->value = \bcpow($this->value, $n->value); + return $this->normalize($temp); + } + /** + * Return the minimum BigInteger between an arbitrary number of BigIntegers. + * + * @param BCMath ...$nums + * @return BCMath + */ + public static function min(\FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\BCMath ...$nums) + { + return self::minHelper($nums); + } + /** + * Return the maximum BigInteger between an arbitrary number of BigIntegers. + * + * @param BCMath ...$nums + * @return BCMath + */ + public static function max(\FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\BCMath ...$nums) + { + return self::maxHelper($nums); + } + /** + * Tests BigInteger to see if it is between two integers, inclusive + * + * @param BCMath $min + * @param BCMath $max + * @return bool + */ + public function between(\FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\BCMath $min, \FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\BCMath $max) + { + return $this->compare($min) >= 0 && $this->compare($max) <= 0; + } + /** + * Set Bitmask + * + * @param int $bits + * @return Engine + * @see self::setPrecision() + */ + protected static function setBitmask($bits) + { + $temp = parent::setBitmask($bits); + return $temp->add(static::$one[static::class]); + } + /** + * Is Odd? + * + * @return bool + */ + public function isOdd() + { + return $this->value[\strlen($this->value) - 1] % 2 == 1; + } + /** + * Tests if a bit is set + * + * @return bool + */ + public function testBit($x) + { + return \bccomp(\bcmod($this->value, \bcpow('2', $x + 1, 0)), \bcpow('2', $x, 0), 0) >= 0; + } + /** + * Is Negative? + * + * @return bool + */ + public function isNegative() + { + return \strlen($this->value) && $this->value[0] == '-'; + } + /** + * Negate + * + * Given $k, returns -$k + * + * @return BCMath + */ + public function negate() + { + $temp = clone $this; + if (!\strlen($temp->value)) { + return $temp; + } + $temp->value = $temp->value[0] == '-' ? \substr($this->value, 1) : '-' . $this->value; + return $temp; + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/BCMath/Base.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/BCMath/Base.php new file mode 100644 index 0000000..6a98aab --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/BCMath/Base.php @@ -0,0 +1,102 @@ + + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://pear.php.net/package/Math_BigInteger + */ +namespace FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\BCMath; + +use FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\BCMath; +/** + * Sliding Window Exponentiation Engine + * + * @author Jim Wigginton + */ +abstract class Base extends \FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\BCMath +{ + /** + * Cache constants + * + * $cache[self::VARIABLE] tells us whether or not the cached data is still valid. + * + */ + const VARIABLE = 0; + /** + * $cache[self::DATA] contains the cached data. + * + */ + const DATA = 1; + /** + * Test for engine validity + * + * @return bool + */ + public static function isValidEngine() + { + return static::class != __CLASS__; + } + /** + * Performs modular exponentiation. + * + * @param BCMath $x + * @param BCMath $e + * @param BCMath $n + * @param string $class + * @return BCMath + */ + protected static function powModHelper(\FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\BCMath $x, \FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\BCMath $e, \FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\BCMath $n, $class) + { + if (empty($e->value)) { + $temp = new $class(); + $temp->value = '1'; + return $x->normalize($temp); + } + return $x->normalize(static::slidingWindow($x, $e, $n, $class)); + } + /** + * Modular reduction preparation + * + * @param string $x + * @param string $n + * @param string $class + * @see self::slidingWindow() + * @return string + */ + protected static function prepareReduce($x, $n, $class) + { + return static::reduce($x, $n); + } + /** + * Modular multiply + * + * @param string $x + * @param string $y + * @param string $n + * @param string $class + * @see self::slidingWindow() + * @return string + */ + protected static function multiplyReduce($x, $y, $n, $class) + { + return static::reduce(\bcmul($x, $y), $n); + } + /** + * Modular square + * + * @param string $x + * @param string $n + * @param string $class + * @see self::slidingWindow() + * @return string + */ + protected static function squareReduce($x, $n, $class) + { + return static::reduce(\bcmul($x, $x), $n); + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/BCMath/BuiltIn.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/BCMath/BuiltIn.php new file mode 100644 index 0000000..d2e1790 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/BCMath/BuiltIn.php @@ -0,0 +1,37 @@ + + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://pear.php.net/package/Math_BigInteger + */ +namespace FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\BCMath; + +use FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\BCMath; +/** + * Built-In BCMath Modular Exponentiation Engine + * + * @author Jim Wigginton + */ +abstract class BuiltIn extends \FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\BCMath +{ + /** + * Performs modular exponentiation. + * + * @param BCMath $x + * @param BCMath $e + * @param BCMath $n + * @return BCMath + */ + protected static function powModHelper(\FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\BCMath $x, \FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\BCMath $e, \FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\BCMath $n) + { + $temp = new \FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\BCMath(); + $temp->value = \bcpowmod($x->value, $e->value, $n->value); + return $x->normalize($temp); + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/BCMath/DefaultEngine.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/BCMath/DefaultEngine.php new file mode 100644 index 0000000..ca45034 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/BCMath/DefaultEngine.php @@ -0,0 +1,23 @@ + + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://pear.php.net/package/Math_BigInteger + */ +namespace FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\BCMath; + +use FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\BCMath\Reductions\Barrett; +/** + * PHP Default Modular Exponentiation Engine + * + * @author Jim Wigginton + */ +abstract class DefaultEngine extends \FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\BCMath\Reductions\Barrett +{ +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/BCMath/OpenSSL.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/BCMath/OpenSSL.php new file mode 100644 index 0000000..48c7a0c --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/BCMath/OpenSSL.php @@ -0,0 +1,23 @@ + + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://pear.php.net/package/Math_BigInteger + */ +namespace FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\BCMath; + +use FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\OpenSSL as Progenitor; +/** + * OpenSSL Modular Exponentiation Engine + * + * @author Jim Wigginton + */ +abstract class OpenSSL extends \FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\OpenSSL +{ +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/BCMath/Reductions/Barrett.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/BCMath/Reductions/Barrett.php new file mode 100644 index 0000000..475f44e --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/BCMath/Reductions/Barrett.php @@ -0,0 +1,164 @@ + + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://pear.php.net/package/Math_BigInteger + */ +namespace FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\BCMath\Reductions; + +use FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\BCMath\Base; +/** + * PHP Barrett Modular Exponentiation Engine + * + * @author Jim Wigginton + */ +abstract class Barrett extends \FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\BCMath\Base +{ + /** + * Cache constants + * + * $cache[self::VARIABLE] tells us whether or not the cached data is still valid. + * + */ + const VARIABLE = 0; + /** + * $cache[self::DATA] contains the cached data. + * + */ + const DATA = 1; + /** + * Barrett Modular Reduction + * + * See {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf#page=14 HAC 14.3.3} / + * {@link http://math.libtomcrypt.com/files/tommath.pdf#page=165 MPM 6.2.5} for more information. Modified slightly, + * so as not to require negative numbers (initially, this script didn't support negative numbers). + * + * Employs "folding", as described at + * {@link http://www.cosic.esat.kuleuven.be/publications/thesis-149.pdf#page=66 thesis-149.pdf#page=66}. To quote from + * it, "the idea [behind folding] is to find a value x' such that x (mod m) = x' (mod m), with x' being smaller than x." + * + * Unfortunately, the "Barrett Reduction with Folding" algorithm described in thesis-149.pdf is not, as written, all that + * usable on account of (1) its not using reasonable radix points as discussed in + * {@link http://math.libtomcrypt.com/files/tommath.pdf#page=162 MPM 6.2.2} and (2) the fact that, even with reasonable + * radix points, it only works when there are an even number of digits in the denominator. The reason for (2) is that + * (x >> 1) + (x >> 1) != x / 2 + x / 2. If x is even, they're the same, but if x is odd, they're not. See the in-line + * comments for details. + * + * @param string $n + * @param string $m + * @return string + */ + protected static function reduce($n, $m) + { + static $cache = [self::VARIABLE => [], self::DATA => []]; + $m_length = \strlen($m); + if (\strlen($n) > 2 * $m_length) { + return \bcmod($n, $m); + } + // if (m.length >> 1) + 2 <= m.length then m is too small and n can't be reduced + if ($m_length < 5) { + return self::regularBarrett($n, $m); + } + // n = 2 * m.length + $correctionNeeded = \false; + if ($m_length & 1) { + $correctionNeeded = \true; + $n .= '0'; + $m .= '0'; + $m_length++; + } + if (($key = \array_search($m, $cache[self::VARIABLE])) === \false) { + $key = \count($cache[self::VARIABLE]); + $cache[self::VARIABLE][] = $m; + $lhs = '1' . \str_repeat('0', $m_length + ($m_length >> 1)); + $u = \bcdiv($lhs, $m, 0); + $m1 = \bcsub($lhs, \bcmul($u, $m)); + $cache[self::DATA][] = [ + 'u' => $u, + // m.length >> 1 (technically (m.length >> 1) + 1) + 'm1' => $m1, + ]; + } else { + \extract($cache[self::DATA][$key]); + } + $cutoff = $m_length + ($m_length >> 1); + $lsd = \substr($n, -$cutoff); + $msd = \substr($n, 0, -$cutoff); + $temp = \bcmul($msd, $m1); + // m.length + (m.length >> 1) + $n = \bcadd($lsd, $temp); + // m.length + (m.length >> 1) + 1 (so basically we're adding two same length numbers) + //if ($m_length & 1) { + // return self::regularBarrett($n, $m); + //} + // (m.length + (m.length >> 1) + 1) - (m.length - 1) == (m.length >> 1) + 2 + $temp = \substr($n, 0, -$m_length + 1); + // if even: ((m.length >> 1) + 2) + (m.length >> 1) == m.length + 2 + // if odd: ((m.length >> 1) + 2) + (m.length >> 1) == (m.length - 1) + 2 == m.length + 1 + $temp = \bcmul($temp, $u); + // if even: (m.length + 2) - ((m.length >> 1) + 1) = m.length - (m.length >> 1) + 1 + // if odd: (m.length + 1) - ((m.length >> 1) + 1) = m.length - (m.length >> 1) + $temp = \substr($temp, 0, -($m_length >> 1) - 1); + // if even: (m.length - (m.length >> 1) + 1) + m.length = 2 * m.length - (m.length >> 1) + 1 + // if odd: (m.length - (m.length >> 1)) + m.length = 2 * m.length - (m.length >> 1) + $temp = \bcmul($temp, $m); + // at this point, if m had an odd number of digits, we'd be subtracting a 2 * m.length - (m.length >> 1) digit + // number from a m.length + (m.length >> 1) + 1 digit number. ie. there'd be an extra digit and the while loop + // following this comment would loop a lot (hence our calling _regularBarrett() in that situation). + $result = \bcsub($n, $temp); + //if (bccomp($result, '0') < 0) { + if ($result[0] == '-') { + $temp = '1' . \str_repeat('0', $m_length + 1); + $result = \bcadd($result, $temp); + } + while (\bccomp($result, $m) >= 0) { + $result = \bcsub($result, $m); + } + return $correctionNeeded ? \substr($result, 0, -1) : $result; + } + /** + * (Regular) Barrett Modular Reduction + * + * For numbers with more than four digits BigInteger::_barrett() is faster. The difference between that and this + * is that this function does not fold the denominator into a smaller form. + * + * @param string $x + * @param string $n + * @return string + */ + private static function regularBarrett($x, $n) + { + static $cache = [self::VARIABLE => [], self::DATA => []]; + $n_length = \strlen($n); + if (\strlen($x) > 2 * $n_length) { + return \bcmod($x, $n); + } + if (($key = \array_search($n, $cache[self::VARIABLE])) === \false) { + $key = \count($cache[self::VARIABLE]); + $cache[self::VARIABLE][] = $n; + $lhs = '1' . \str_repeat('0', 2 * $n_length); + $cache[self::DATA][] = \bcdiv($lhs, $n, 0); + } + $temp = \substr($x, 0, -$n_length + 1); + $temp = \bcmul($temp, $cache[self::DATA][$key]); + $temp = \substr($temp, 0, -$n_length - 1); + $r1 = \substr($x, -$n_length - 1); + $r2 = \substr(\bcmul($temp, $n), -$n_length - 1); + $result = \bcsub($r1, $r2); + //if (bccomp($result, '0') < 0) { + if ($result[0] == '-') { + $q = '1' . \str_repeat('0', $n_length + 1); + $result = \bcadd($result, $q); + } + while (\bccomp($result, $n) >= 0) { + $result = \bcsub($result, $n); + } + return $result; + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/BCMath/Reductions/EvalBarrett.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/BCMath/Reductions/EvalBarrett.php new file mode 100644 index 0000000..87da90d --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/BCMath/Reductions/EvalBarrett.php @@ -0,0 +1,96 @@ + + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://pear.php.net/package/Math_BigInteger + */ +namespace FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\BCMath\Reductions; + +use FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\BCMath; +use FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\BCMath\Base; +/** + * PHP Barrett Modular Exponentiation Engine + * + * @author Jim Wigginton + */ +abstract class EvalBarrett extends \FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\BCMath\Base +{ + /** + * Custom Reduction Function + * + * @see self::generateCustomReduction + */ + private static $custom_reduction; + /** + * Barrett Modular Reduction + * + * This calls a dynamically generated loop unrolled function that's specific to a given modulo. + * Array lookups are avoided as are if statements testing for how many bits the host OS supports, etc. + * + * @param string $n + * @param string $m + * @return string + */ + protected static function reduce($n, $m) + { + $inline = self::$custom_reduction; + return $inline($n); + } + /** + * Generate Custom Reduction + * + * @param BCMath $m + * @param string $class + * @return callable|void + */ + protected static function generateCustomReduction(\FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\BCMath $m, $class) + { + $m_length = \strlen($m); + if ($m_length < 5) { + $code = 'return bcmod($x, $n);'; + eval('$func = function ($n) { ' . $code . '};'); + self::$custom_reduction = $func; + return; + } + $lhs = '1' . \str_repeat('0', $m_length + ($m_length >> 1)); + $u = \bcdiv($lhs, $m, 0); + $m1 = \bcsub($lhs, \bcmul($u, $m)); + $cutoff = $m_length + ($m_length >> 1); + $m = "'{$m}'"; + $u = "'{$u}'"; + $m1 = "'{$m1}'"; + $code = ' + $lsd = substr($n, -' . $cutoff . '); + $msd = substr($n, 0, -' . $cutoff . '); + + $temp = bcmul($msd, ' . $m1 . '); + $n = bcadd($lsd, $temp); + + $temp = substr($n, 0, ' . (-$m_length + 1) . '); + $temp = bcmul($temp, ' . $u . '); + $temp = substr($temp, 0, ' . (-($m_length >> 1) - 1) . '); + $temp = bcmul($temp, ' . $m . '); + + $result = bcsub($n, $temp); + + if ($result[0] == \'-\') { + $temp = \'1' . \str_repeat('0', $m_length + 1) . '\'; + $result = bcadd($result, $temp); + } + + while (bccomp($result, ' . $m . ') >= 0) { + $result = bcsub($result, ' . $m . '); + } + + return $result;'; + eval('$func = function ($n) { ' . $code . '};'); + self::$custom_reduction = $func; + return $func; + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/Engine.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/Engine.php new file mode 100644 index 0000000..451eeca --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/Engine.php @@ -0,0 +1,1160 @@ + + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://pear.php.net/package/Math_BigInteger + */ +namespace FluentSmtpLib\phpseclib3\Math\BigInteger\Engines; + +use FluentSmtpLib\phpseclib3\Common\Functions\Strings; +use FluentSmtpLib\phpseclib3\Crypt\Random; +use FluentSmtpLib\phpseclib3\Exception\BadConfigurationException; +use FluentSmtpLib\phpseclib3\Math\BigInteger; +/** + * Base Engine. + * + * @author Jim Wigginton + */ +abstract class Engine implements \JsonSerializable +{ + /* final protected */ + const PRIMES = [3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211, 223, 227, 229, 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, 283, 293, 307, 311, 313, 317, 331, 337, 347, 349, 353, 359, 367, 373, 379, 383, 389, 397, 401, 409, 419, 421, 431, 433, 439, 443, 449, 457, 461, 463, 467, 479, 487, 491, 499, 503, 509, 521, 523, 541, 547, 557, 563, 569, 571, 577, 587, 593, 599, 601, 607, 613, 617, 619, 631, 641, 643, 647, 653, 659, 661, 673, 677, 683, 691, 701, 709, 719, 727, 733, 739, 743, 751, 757, 761, 769, 773, 787, 797, 809, 811, 821, 823, 827, 829, 839, 853, 857, 859, 863, 877, 881, 883, 887, 907, 911, 919, 929, 937, 941, 947, 953, 967, 971, 977, 983, 991, 997]; + /** + * BigInteger(0) + * + * @var array, static> + */ + protected static $zero = []; + /** + * BigInteger(1) + * + * @var array, static> + */ + protected static $one = []; + /** + * BigInteger(2) + * + * @var array, static> + */ + protected static $two = []; + /** + * Modular Exponentiation Engine + * + * @var array, class-string> + */ + protected static $modexpEngine; + /** + * Engine Validity Flag + * + * @var array, bool> + */ + protected static $isValidEngine; + /** + * Holds the BigInteger's value + * + * @var \GMP|string|array|int + */ + protected $value; + /** + * Holds the BigInteger's sign + * + * @var bool + */ + protected $is_negative; + /** + * Precision + * + * @see static::setPrecision() + * @var int + */ + protected $precision = -1; + /** + * Precision Bitmask + * + * @see static::setPrecision() + * @var static|false + */ + protected $bitmask = \false; + /** + * Recurring Modulo Function + * + * @var callable + */ + protected $reduce; + /** + * Mode independent value used for serialization. + * + * @see self::__sleep() + * @see self::__wakeup() + * @var string + */ + protected $hex; + /** + * Default constructor + * + * @param int|numeric-string $x integer Base-10 number or base-$base number if $base set. + * @param int $base + */ + public function __construct($x = 0, $base = 10) + { + if (!\array_key_exists(static::class, static::$zero)) { + static::$zero[static::class] = null; + // Placeholder to prevent infinite loop. + static::$zero[static::class] = new static(0); + static::$one[static::class] = new static(1); + static::$two[static::class] = new static(2); + } + // '0' counts as empty() but when the base is 256 '0' is equal to ord('0') or 48 + // '0' is the only value like this per http://php.net/empty + if (empty($x) && (\abs($base) != 256 || $x !== '0')) { + return; + } + switch ($base) { + case -256: + case 256: + if ($base == -256 && \ord($x[0]) & 0x80) { + $this->value = ~$x; + $this->is_negative = \true; + } else { + $this->value = $x; + $this->is_negative = \false; + } + $this->initialize($base); + if ($this->is_negative) { + $temp = $this->add(new static('-1')); + $this->value = $temp->value; + } + break; + case -16: + case 16: + if ($base > 0 && $x[0] == '-') { + $this->is_negative = \true; + $x = \substr($x, 1); + } + $x = \preg_replace('#^(?:0x)?([A-Fa-f0-9]*).*#s', '$1', $x); + $is_negative = \false; + if ($base < 0 && \hexdec($x[0]) >= 8) { + $this->is_negative = $is_negative = \true; + $x = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::bin2hex(~\FluentSmtpLib\phpseclib3\Common\Functions\Strings::hex2bin($x)); + } + $this->value = $x; + $this->initialize($base); + if ($is_negative) { + $temp = $this->add(new static('-1')); + $this->value = $temp->value; + } + break; + case -10: + case 10: + // (?value = \preg_replace('#(?value) || $this->value == '-') { + $this->value = '0'; + } + $this->initialize($base); + break; + case -2: + case 2: + if ($base > 0 && $x[0] == '-') { + $this->is_negative = \true; + $x = \substr($x, 1); + } + $x = \preg_replace('#^([01]*).*#s', '$1', $x); + $temp = new static(\FluentSmtpLib\phpseclib3\Common\Functions\Strings::bits2bin($x), 128 * $base); + // ie. either -16 or +16 + $this->value = $temp->value; + if ($temp->is_negative) { + $this->is_negative = \true; + } + break; + default: + } + } + /** + * Sets engine type. + * + * Throws an exception if the type is invalid + * + * @param class-string $engine + */ + public static function setModExpEngine($engine) + { + $fqengine = '\\FluentSmtpLib\\phpseclib3\\Math\\BigInteger\\Engines\\' . static::ENGINE_DIR . '\\' . $engine; + if (!\class_exists($fqengine) || !\method_exists($fqengine, 'isValidEngine')) { + throw new \InvalidArgumentException("{$engine} is not a valid engine"); + } + if (!$fqengine::isValidEngine()) { + throw new \FluentSmtpLib\phpseclib3\Exception\BadConfigurationException("{$engine} is not setup correctly on this system"); + } + static::$modexpEngine[static::class] = $fqengine; + } + /** + * Converts a BigInteger to a byte string (eg. base-256). + * + * Negative numbers are saved as positive numbers, unless $twos_compliment is set to true, at which point, they're + * saved as two's compliment. + * @return string + */ + protected function toBytesHelper() + { + $comparison = $this->compare(new static()); + if ($comparison == 0) { + return $this->precision > 0 ? \str_repeat(\chr(0), $this->precision + 1 >> 3) : ''; + } + $temp = $comparison < 0 ? $this->add(new static(1)) : $this; + $bytes = $temp->toBytes(); + if (!\strlen($bytes)) { + // eg. if the number we're trying to convert is -1 + $bytes = \chr(0); + } + if (\ord($bytes[0]) & 0x80) { + $bytes = \chr(0) . $bytes; + } + return $comparison < 0 ? ~$bytes : $bytes; + } + /** + * Converts a BigInteger to a hex string (eg. base-16). + * + * @param bool $twos_compliment + * @return string + */ + public function toHex($twos_compliment = \false) + { + return \FluentSmtpLib\phpseclib3\Common\Functions\Strings::bin2hex($this->toBytes($twos_compliment)); + } + /** + * Converts a BigInteger to a bit string (eg. base-2). + * + * Negative numbers are saved as positive numbers, unless $twos_compliment is set to true, at which point, they're + * saved as two's compliment. + * + * @param bool $twos_compliment + * @return string + */ + public function toBits($twos_compliment = \false) + { + $hex = $this->toBytes($twos_compliment); + $bits = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::bin2bits($hex); + $result = $this->precision > 0 ? \substr($bits, -$this->precision) : \ltrim($bits, '0'); + if ($twos_compliment && $this->compare(new static()) > 0 && $this->precision <= 0) { + return '0' . $result; + } + return $result; + } + /** + * Calculates modular inverses. + * + * Say you have (30 mod 17 * x mod 17) mod 17 == 1. x can be found using modular inverses. + * + * {@internal See {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf#page=21 HAC 14.64} for more information.} + * + * @param Engine $n + * @return static|false + */ + protected function modInverseHelper(\FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\Engine $n) + { + // $x mod -$n == $x mod $n. + $n = $n->abs(); + if ($this->compare(static::$zero[static::class]) < 0) { + $temp = $this->abs(); + $temp = $temp->modInverse($n); + return $this->normalize($n->subtract($temp)); + } + \extract($this->extendedGCD($n)); + /** + * @var Engine $gcd + * @var Engine $x + */ + if (!$gcd->equals(static::$one[static::class])) { + return \false; + } + $x = $x->compare(static::$zero[static::class]) < 0 ? $x->add($n) : $x; + return $this->compare(static::$zero[static::class]) < 0 ? $this->normalize($n->subtract($x)) : $this->normalize($x); + } + /** + * Serialize + * + * Will be called, automatically, when serialize() is called on a BigInteger object. + * + * @return array + */ + public function __sleep() + { + $this->hex = $this->toHex(\true); + $vars = ['hex']; + if ($this->precision > 0) { + $vars[] = 'precision'; + } + return $vars; + } + /** + * Serialize + * + * Will be called, automatically, when unserialize() is called on a BigInteger object. + * + * @return void + */ + public function __wakeup() + { + $temp = new static($this->hex, -16); + $this->value = $temp->value; + $this->is_negative = $temp->is_negative; + if ($this->precision > 0) { + // recalculate $this->bitmask + $this->setPrecision($this->precision); + } + } + /** + * JSON Serialize + * + * Will be called, automatically, when json_encode() is called on a BigInteger object. + * + * @return array{hex: string, precision?: int] + */ + #[\ReturnTypeWillChange] + public function jsonSerialize() + { + $result = ['hex' => $this->toHex(\true)]; + if ($this->precision > 0) { + $result['precision'] = $this->precision; + } + return $result; + } + /** + * Converts a BigInteger to a base-10 number. + * + * @return string + */ + public function __toString() + { + return $this->toString(); + } + /** + * __debugInfo() magic method + * + * Will be called, automatically, when print_r() or var_dump() are called + * + * @return array + */ + public function __debugInfo() + { + $result = ['value' => '0x' . $this->toHex(\true), 'engine' => \basename(static::class)]; + return $this->precision > 0 ? $result + ['precision' => $this->precision] : $result; + } + /** + * Set Precision + * + * Some bitwise operations give different results depending on the precision being used. Examples include left + * shift, not, and rotates. + * + * @param int $bits + */ + public function setPrecision($bits) + { + if ($bits < 1) { + $this->precision = -1; + $this->bitmask = \false; + return; + } + $this->precision = $bits; + $this->bitmask = static::setBitmask($bits); + $temp = $this->normalize($this); + $this->value = $temp->value; + } + /** + * Get Precision + * + * Returns the precision if it exists, -1 if it doesn't + * + * @return int + */ + public function getPrecision() + { + return $this->precision; + } + /** + * Set Bitmask + * @return static + * @param int $bits + * @see self::setPrecision() + */ + protected static function setBitmask($bits) + { + return new static(\chr((1 << ($bits & 0x7)) - 1) . \str_repeat(\chr(0xff), $bits >> 3), 256); + } + /** + * Logical Not + * + * @return Engine|string + */ + public function bitwise_not() + { + // calculuate "not" without regard to $this->precision + // (will always result in a smaller number. ie. ~1 isn't 1111 1110 - it's 0) + $temp = $this->toBytes(); + if ($temp == '') { + return $this->normalize(static::$zero[static::class]); + } + $pre_msb = \decbin(\ord($temp[0])); + $temp = ~$temp; + $msb = \decbin(\ord($temp[0])); + if (\strlen($msb) == 8) { + $msb = \substr($msb, \strpos($msb, '0')); + } + $temp[0] = \chr(\bindec($msb)); + // see if we need to add extra leading 1's + $current_bits = \strlen($pre_msb) + 8 * \strlen($temp) - 8; + $new_bits = $this->precision - $current_bits; + if ($new_bits <= 0) { + return $this->normalize(new static($temp, 256)); + } + // generate as many leading 1's as we need to. + $leading_ones = \chr((1 << ($new_bits & 0x7)) - 1) . \str_repeat(\chr(0xff), $new_bits >> 3); + self::base256_lshift($leading_ones, $current_bits); + $temp = \str_pad($temp, \strlen($leading_ones), \chr(0), \STR_PAD_LEFT); + return $this->normalize(new static($leading_ones | $temp, 256)); + } + /** + * Logical Left Shift + * + * Shifts binary strings $shift bits, essentially multiplying by 2**$shift. + * + * @param string $x + * @param int $shift + * @return void + */ + protected static function base256_lshift(&$x, $shift) + { + if ($shift == 0) { + return; + } + $num_bytes = $shift >> 3; + // eg. floor($shift/8) + $shift &= 7; + // eg. $shift % 8 + $carry = 0; + for ($i = \strlen($x) - 1; $i >= 0; --$i) { + $temp = \ord($x[$i]) << $shift | $carry; + $x[$i] = \chr($temp); + $carry = $temp >> 8; + } + $carry = $carry != 0 ? \chr($carry) : ''; + $x = $carry . $x . \str_repeat(\chr(0), $num_bytes); + } + /** + * Logical Left Rotate + * + * Instead of the top x bits being dropped they're appended to the shifted bit string. + * + * @param int $shift + * @return Engine + */ + public function bitwise_leftRotate($shift) + { + $bits = $this->toBytes(); + if ($this->precision > 0) { + $precision = $this->precision; + if (static::FAST_BITWISE) { + $mask = $this->bitmask->toBytes(); + } else { + $mask = $this->bitmask->subtract(new static(1)); + $mask = $mask->toBytes(); + } + } else { + $temp = \ord($bits[0]); + for ($i = 0; $temp >> $i; ++$i) { + } + $precision = 8 * \strlen($bits) - 8 + $i; + $mask = \chr((1 << ($precision & 0x7)) - 1) . \str_repeat(\chr(0xff), $precision >> 3); + } + if ($shift < 0) { + $shift += $precision; + } + $shift %= $precision; + if (!$shift) { + return clone $this; + } + $left = $this->bitwise_leftShift($shift); + $left = $left->bitwise_and(new static($mask, 256)); + $right = $this->bitwise_rightShift($precision - $shift); + $result = static::FAST_BITWISE ? $left->bitwise_or($right) : $left->add($right); + return $this->normalize($result); + } + /** + * Logical Right Rotate + * + * Instead of the bottom x bits being dropped they're prepended to the shifted bit string. + * + * @param int $shift + * @return Engine + */ + public function bitwise_rightRotate($shift) + { + return $this->bitwise_leftRotate(-$shift); + } + /** + * Returns the smallest and largest n-bit number + * + * @param int $bits + * @return array{min: static, max: static} + */ + public static function minMaxBits($bits) + { + $bytes = $bits >> 3; + $min = \str_repeat(\chr(0), $bytes); + $max = \str_repeat(\chr(0xff), $bytes); + $msb = $bits & 7; + if ($msb) { + $min = \chr(1 << $msb - 1) . $min; + $max = \chr((1 << $msb) - 1) . $max; + } else { + $min[0] = \chr(0x80); + } + return ['min' => new static($min, 256), 'max' => new static($max, 256)]; + } + /** + * Return the size of a BigInteger in bits + * + * @return int + */ + public function getLength() + { + return \strlen($this->toBits()); + } + /** + * Return the size of a BigInteger in bytes + * + * @return int + */ + public function getLengthInBytes() + { + return (int) \ceil($this->getLength() / 8); + } + /** + * Performs some pre-processing for powMod + * + * @param Engine $e + * @param Engine $n + * @return static|false + */ + protected function powModOuter(\FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\Engine $e, \FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\Engine $n) + { + $n = $this->bitmask !== \false && $this->bitmask->compare($n) < 0 ? $this->bitmask : $n->abs(); + if ($e->compare(new static()) < 0) { + $e = $e->abs(); + $temp = $this->modInverse($n); + if ($temp === \false) { + return \false; + } + return $this->normalize($temp->powModInner($e, $n)); + } + if ($this->compare($n) > 0) { + list(, $temp) = $this->divide($n); + return $temp->powModInner($e, $n); + } + return $this->powModInner($e, $n); + } + /** + * Sliding Window k-ary Modular Exponentiation + * + * Based on {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf#page=27 HAC 14.85} / + * {@link http://math.libtomcrypt.com/files/tommath.pdf#page=210 MPM 7.7}. In a departure from those algorithims, + * however, this function performs a modular reduction after every multiplication and squaring operation. + * As such, this function has the same preconditions that the reductions being used do. + * + * @template T of Engine + * @param Engine $x + * @param Engine $e + * @param Engine $n + * @param class-string $class + * @return T + */ + protected static function slidingWindow(\FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\Engine $x, \FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\Engine $e, \FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\Engine $n, $class) + { + static $window_ranges = [7, 25, 81, 241, 673, 1793]; + // from BigInteger.java's oddModPow function + //static $window_ranges = [0, 7, 36, 140, 450, 1303, 3529]; // from MPM 7.3.1 + $e_bits = $e->toBits(); + $e_length = \strlen($e_bits); + // calculate the appropriate window size. + // $window_size == 3 if $window_ranges is between 25 and 81, for example. + for ($i = 0, $window_size = 1; $i < \count($window_ranges) && $e_length > $window_ranges[$i]; ++$window_size, ++$i) { + } + $n_value = $n->value; + if (\method_exists(static::class, 'generateCustomReduction')) { + static::generateCustomReduction($n, $class); + } + // precompute $this^0 through $this^$window_size + $powers = []; + $powers[1] = static::prepareReduce($x->value, $n_value, $class); + $powers[2] = static::squareReduce($powers[1], $n_value, $class); + // we do every other number since substr($e_bits, $i, $j+1) (see below) is supposed to end + // in a 1. ie. it's supposed to be odd. + $temp = 1 << $window_size - 1; + for ($i = 1; $i < $temp; ++$i) { + $i2 = $i << 1; + $powers[$i2 + 1] = static::multiplyReduce($powers[$i2 - 1], $powers[2], $n_value, $class); + } + $result = new $class(1); + $result = static::prepareReduce($result->value, $n_value, $class); + for ($i = 0; $i < $e_length;) { + if (!$e_bits[$i]) { + $result = static::squareReduce($result, $n_value, $class); + ++$i; + } else { + for ($j = $window_size - 1; $j > 0; --$j) { + if (!empty($e_bits[$i + $j])) { + break; + } + } + // eg. the length of substr($e_bits, $i, $j + 1) + for ($k = 0; $k <= $j; ++$k) { + $result = static::squareReduce($result, $n_value, $class); + } + $result = static::multiplyReduce($result, $powers[\bindec(\substr($e_bits, $i, $j + 1))], $n_value, $class); + $i += $j + 1; + } + } + $temp = new $class(); + $temp->value = static::reduce($result, $n_value, $class); + return $temp; + } + /** + * Generates a random number of a certain size + * + * Bit length is equal to $size + * + * @param int $size + * @return Engine + */ + public static function random($size) + { + \extract(static::minMaxBits($size)); + /** + * @var BigInteger $min + * @var BigInteger $max + */ + return static::randomRange($min, $max); + } + /** + * Generates a random prime number of a certain size + * + * Bit length is equal to $size + * + * @param int $size + * @return Engine + */ + public static function randomPrime($size) + { + \extract(static::minMaxBits($size)); + /** + * @var static $min + * @var static $max + */ + return static::randomRangePrime($min, $max); + } + /** + * Performs some pre-processing for randomRangePrime + * + * @param Engine $min + * @param Engine $max + * @return static|false + */ + protected static function randomRangePrimeOuter(\FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\Engine $min, \FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\Engine $max) + { + $compare = $max->compare($min); + if (!$compare) { + return $min->isPrime() ? $min : \false; + } elseif ($compare < 0) { + // if $min is bigger then $max, swap $min and $max + $temp = $max; + $max = $min; + $min = $temp; + } + $length = $max->getLength(); + if ($length > 8196) { + throw new \RuntimeException("Generation of random prime numbers larger than 8196 has been disabled ({$length})"); + } + $x = static::randomRange($min, $max); + return static::randomRangePrimeInner($x, $min, $max); + } + /** + * Generate a random number between a range + * + * Returns a random number between $min and $max where $min and $max + * can be defined using one of the two methods: + * + * BigInteger::randomRange($min, $max) + * BigInteger::randomRange($max, $min) + * + * @param Engine $min + * @param Engine $max + * @return Engine + */ + protected static function randomRangeHelper(\FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\Engine $min, \FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\Engine $max) + { + $compare = $max->compare($min); + if (!$compare) { + return $min; + } elseif ($compare < 0) { + // if $min is bigger then $max, swap $min and $max + $temp = $max; + $max = $min; + $min = $temp; + } + if (!isset(static::$one[static::class])) { + static::$one[static::class] = new static(1); + } + $max = $max->subtract($min->subtract(static::$one[static::class])); + $size = \strlen(\ltrim($max->toBytes(), \chr(0))); + /* + doing $random % $max doesn't work because some numbers will be more likely to occur than others. + eg. if $max is 140 and $random's max is 255 then that'd mean both $random = 5 and $random = 145 + would produce 5 whereas the only value of random that could produce 139 would be 139. ie. + not all numbers would be equally likely. some would be more likely than others. + + creating a whole new random number until you find one that is within the range doesn't work + because, for sufficiently small ranges, the likelihood that you'd get a number within that range + would be pretty small. eg. with $random's max being 255 and if your $max being 1 the probability + would be pretty high that $random would be greater than $max. + + phpseclib works around this using the technique described here: + + http://crypto.stackexchange.com/questions/5708/creating-a-small-number-from-a-cryptographically-secure-random-string + */ + $random_max = new static(\chr(1) . \str_repeat("\x00", $size), 256); + $random = new static(\FluentSmtpLib\phpseclib3\Crypt\Random::string($size), 256); + list($max_multiple) = $random_max->divide($max); + $max_multiple = $max_multiple->multiply($max); + while ($random->compare($max_multiple) >= 0) { + $random = $random->subtract($max_multiple); + $random_max = $random_max->subtract($max_multiple); + $random = $random->bitwise_leftShift(8); + $random = $random->add(new static(\FluentSmtpLib\phpseclib3\Crypt\Random::string(1), 256)); + $random_max = $random_max->bitwise_leftShift(8); + list($max_multiple) = $random_max->divide($max); + $max_multiple = $max_multiple->multiply($max); + } + list(, $random) = $random->divide($max); + return $random->add($min); + } + /** + * Performs some post-processing for randomRangePrime + * + * @param Engine $x + * @param Engine $min + * @param Engine $max + * @return static|false + */ + protected static function randomRangePrimeInner(\FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\Engine $x, \FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\Engine $min, \FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\Engine $max) + { + if (!isset(static::$two[static::class])) { + static::$two[static::class] = new static('2'); + } + $x->make_odd(); + if ($x->compare($max) > 0) { + // if $x > $max then $max is even and if $min == $max then no prime number exists between the specified range + if ($min->equals($max)) { + return \false; + } + $x = clone $min; + $x->make_odd(); + } + $initial_x = clone $x; + while (\true) { + if ($x->isPrime()) { + return $x; + } + $x = $x->add(static::$two[static::class]); + if ($x->compare($max) > 0) { + $x = clone $min; + if ($x->equals(static::$two[static::class])) { + return $x; + } + $x->make_odd(); + } + if ($x->equals($initial_x)) { + return \false; + } + } + } + /** + * Sets the $t parameter for primality testing + * + * @return int + */ + protected function setupIsPrime() + { + $length = $this->getLengthInBytes(); + // see HAC 4.49 "Note (controlling the error probability)" + // @codingStandardsIgnoreStart + if ($length >= 163) { + $t = 2; + } else { + if ($length >= 106) { + $t = 3; + } else { + if ($length >= 81) { + $t = 4; + } else { + if ($length >= 68) { + $t = 5; + } else { + if ($length >= 56) { + $t = 6; + } else { + if ($length >= 50) { + $t = 7; + } else { + if ($length >= 43) { + $t = 8; + } else { + if ($length >= 37) { + $t = 9; + } else { + if ($length >= 31) { + $t = 12; + } else { + if ($length >= 25) { + $t = 15; + } else { + if ($length >= 18) { + $t = 18; + } else { + $t = 27; + } + } + } + } + } + } + } + } + } + } + } + // @codingStandardsIgnoreEnd + return $t; + } + /** + * Tests Primality + * + * Uses the {@link http://en.wikipedia.org/wiki/Miller%E2%80%93Rabin_primality_test Miller-Rabin primality test}. + * See {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap4.pdf#page=8 HAC 4.24} for more info. + * + * @param int $t + * @return bool + */ + protected function testPrimality($t) + { + if (!$this->testSmallPrimes()) { + return \false; + } + $n = clone $this; + $n_1 = $n->subtract(static::$one[static::class]); + $n_2 = $n->subtract(static::$two[static::class]); + $r = clone $n_1; + $s = static::scan1divide($r); + for ($i = 0; $i < $t; ++$i) { + $a = static::randomRange(static::$two[static::class], $n_2); + $y = $a->modPow($r, $n); + if (!$y->equals(static::$one[static::class]) && !$y->equals($n_1)) { + for ($j = 1; $j < $s && !$y->equals($n_1); ++$j) { + $y = $y->modPow(static::$two[static::class], $n); + if ($y->equals(static::$one[static::class])) { + return \false; + } + } + if (!$y->equals($n_1)) { + return \false; + } + } + } + return \true; + } + /** + * Checks a numer to see if it's prime + * + * Assuming the $t parameter is not set, this function has an error rate of 2**-80. The main motivation for the + * $t parameter is distributability. BigInteger::randomPrime() can be distributed across multiple pageloads + * on a website instead of just one. + * + * @param int|bool $t + * @return bool + */ + public function isPrime($t = \false) + { + // OpenSSL limits RSA keys to 16384 bits. The length of an RSA key is equal to the length of the modulo, which is + // produced by multiplying the primes p and q by one another. The largest number two 8196 bit primes can produce is + // a 16384 bit number so, basically, 8196 bit primes are the largest OpenSSL will generate and if that's the largest + // that it'll generate it also stands to reason that that's the largest you'll be able to test primality on + $length = $this->getLength(); + if ($length > 8196) { + throw new \RuntimeException("Primality testing is not supported for numbers larger than 8196 bits ({$length})"); + } + if (!$t) { + $t = $this->setupIsPrime(); + } + return $this->testPrimality($t); + } + /** + * Performs a few preliminary checks on root + * + * @param int $n + * @return Engine + */ + protected function rootHelper($n) + { + if ($n < 1) { + return clone static::$zero[static::class]; + } + // we want positive exponents + if ($this->compare(static::$one[static::class]) < 0) { + return clone static::$zero[static::class]; + } + // we want positive numbers + if ($this->compare(static::$two[static::class]) < 0) { + return clone static::$one[static::class]; + } + // n-th root of 1 or 2 is 1 + return $this->rootInner($n); + } + /** + * Calculates the nth root of a biginteger. + * + * Returns the nth root of a positive biginteger, where n defaults to 2 + * + * {@internal This function is based off of {@link http://mathforum.org/library/drmath/view/52605.html this page} and {@link http://stackoverflow.com/questions/11242920/calculating-nth-root-with-bcmath-in-php this stackoverflow question}.} + * + * @param int $n + * @return Engine + */ + protected function rootInner($n) + { + $n = new static($n); + // g is our guess number + $g = static::$two[static::class]; + // while (g^n < num) g=g*2 + while ($g->pow($n)->compare($this) < 0) { + $g = $g->multiply(static::$two[static::class]); + } + // if (g^n==num) num is a power of 2, we're lucky, end of job + // == 0 bccomp(bcpow($g, $n), $n->value)==0 + if ($g->pow($n)->equals($this) > 0) { + $root = $g; + return $this->normalize($root); + } + // if we're here num wasn't a power of 2 :( + $og = $g; + // og means original guess and here is our upper bound + $g = $g->divide(static::$two[static::class])[0]; + // g is set to be our lower bound + $step = $og->subtract($g)->divide(static::$two[static::class])[0]; + // step is the half of upper bound - lower bound + $g = $g->add($step); + // we start at lower bound + step , basically in the middle of our interval + // while step>1 + while ($step->compare(static::$one[static::class]) == 1) { + $guess = $g->pow($n); + $step = $step->divide(static::$two[static::class])[0]; + $comp = $guess->compare($this); + // compare our guess with real number + switch ($comp) { + case -1: + // if guess is lower we add the new step + $g = $g->add($step); + break; + case 1: + // if guess is higher we sub the new step + $g = $g->subtract($step); + break; + case 0: + // if guess is exactly the num we're done, we return the value + $root = $g; + break 2; + } + } + if ($comp == 1) { + $g = $g->subtract($step); + } + // whatever happened, g is the closest guess we can make so return it + $root = $g; + return $this->normalize($root); + } + /** + * Calculates the nth root of a biginteger. + * + * @param int $n + * @return Engine + */ + public function root($n = 2) + { + return $this->rootHelper($n); + } + /** + * Return the minimum BigInteger between an arbitrary number of BigIntegers. + * + * @param array $nums + * @return Engine + */ + protected static function minHelper(array $nums) + { + if (\count($nums) == 1) { + return $nums[0]; + } + $min = $nums[0]; + for ($i = 1; $i < \count($nums); $i++) { + $min = $min->compare($nums[$i]) > 0 ? $nums[$i] : $min; + } + return $min; + } + /** + * Return the minimum BigInteger between an arbitrary number of BigIntegers. + * + * @param array $nums + * @return Engine + */ + protected static function maxHelper(array $nums) + { + if (\count($nums) == 1) { + return $nums[0]; + } + $max = $nums[0]; + for ($i = 1; $i < \count($nums); $i++) { + $max = $max->compare($nums[$i]) < 0 ? $nums[$i] : $max; + } + return $max; + } + /** + * Create Recurring Modulo Function + * + * Sometimes it may be desirable to do repeated modulos with the same number outside of + * modular exponentiation + * + * @return callable + */ + public function createRecurringModuloFunction() + { + $class = static::class; + $fqengine = !\method_exists(static::$modexpEngine[static::class], 'reduce') ? '\\FluentSmtpLib\\phpseclib3\\Math\\BigInteger\\Engines\\' . static::ENGINE_DIR . '\\DefaultEngine' : static::$modexpEngine[static::class]; + if (\method_exists($fqengine, 'generateCustomReduction')) { + $func = $fqengine::generateCustomReduction($this, static::class); + return eval('return function(' . static::class . ' $x) use ($func, $class) { + $r = new $class(); + $r->value = $func($x->value); + return $r; + };'); + } + $n = $this->value; + return eval('return function(' . static::class . ' $x) use ($n, $fqengine, $class) { + $r = new $class(); + $r->value = $fqengine::reduce($x->value, $n, $class); + return $r; + };'); + } + /** + * Calculates the greatest common divisor and Bezout's identity. + * + * @param Engine $n + * @return array{gcd: Engine, x: Engine, y: Engine} + */ + protected function extendedGCDHelper(\FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\Engine $n) + { + $u = clone $this; + $v = clone $n; + $one = new static(1); + $zero = new static(); + $a = clone $one; + $b = clone $zero; + $c = clone $zero; + $d = clone $one; + while (!$v->equals($zero)) { + list($q) = $u->divide($v); + $temp = $u; + $u = $v; + $v = $temp->subtract($v->multiply($q)); + $temp = $a; + $a = $c; + $c = $temp->subtract($a->multiply($q)); + $temp = $b; + $b = $d; + $d = $temp->subtract($b->multiply($q)); + } + return ['gcd' => $u, 'x' => $a, 'y' => $b]; + } + /** + * Bitwise Split + * + * Splits BigInteger's into chunks of $split bits + * + * @param int $split + * @return Engine[] + */ + public function bitwise_split($split) + { + if ($split < 1) { + throw new \RuntimeException('Offset must be greater than 1'); + } + $mask = static::$one[static::class]->bitwise_leftShift($split)->subtract(static::$one[static::class]); + $num = clone $this; + $vals = []; + while (!$num->equals(static::$zero[static::class])) { + $vals[] = $num->bitwise_and($mask); + $num = $num->bitwise_rightShift($split); + } + return \array_reverse($vals); + } + /** + * Logical And + * + * @param Engine $x + * @return Engine + */ + protected function bitwiseAndHelper(\FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\Engine $x) + { + $left = $this->toBytes(\true); + $right = $x->toBytes(\true); + $length = \max(\strlen($left), \strlen($right)); + $left = \str_pad($left, $length, \chr(0), \STR_PAD_LEFT); + $right = \str_pad($right, $length, \chr(0), \STR_PAD_LEFT); + return $this->normalize(new static($left & $right, -256)); + } + /** + * Logical Or + * + * @param Engine $x + * @return Engine + */ + protected function bitwiseOrHelper(\FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\Engine $x) + { + $left = $this->toBytes(\true); + $right = $x->toBytes(\true); + $length = \max(\strlen($left), \strlen($right)); + $left = \str_pad($left, $length, \chr(0), \STR_PAD_LEFT); + $right = \str_pad($right, $length, \chr(0), \STR_PAD_LEFT); + return $this->normalize(new static($left | $right, -256)); + } + /** + * Logical Exclusive Or + * + * @param Engine $x + * @return Engine + */ + protected function bitwiseXorHelper(\FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\Engine $x) + { + $left = $this->toBytes(\true); + $right = $x->toBytes(\true); + $length = \max(\strlen($left), \strlen($right)); + $left = \str_pad($left, $length, \chr(0), \STR_PAD_LEFT); + $right = \str_pad($right, $length, \chr(0), \STR_PAD_LEFT); + return $this->normalize(new static($left ^ $right, -256)); + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/GMP.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/GMP.php new file mode 100644 index 0000000..2e29ac1 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/GMP.php @@ -0,0 +1,612 @@ + + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://pear.php.net/package/Math_BigInteger + */ +namespace FluentSmtpLib\phpseclib3\Math\BigInteger\Engines; + +use FluentSmtpLib\phpseclib3\Exception\BadConfigurationException; +/** + * GMP Engine. + * + * @author Jim Wigginton + */ +class GMP extends \FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\Engine +{ + /** + * Can Bitwise operations be done fast? + * + * @see parent::bitwise_leftRotate() + * @see parent::bitwise_rightRotate() + */ + const FAST_BITWISE = \true; + /** + * Engine Directory + * + * @see parent::setModExpEngine + */ + const ENGINE_DIR = 'GMP'; + /** + * Test for engine validity + * + * @return bool + * @see parent::__construct() + */ + public static function isValidEngine() + { + return \extension_loaded('gmp'); + } + /** + * Default constructor + * + * @param mixed $x integer Base-10 number or base-$base number if $base set. + * @param int $base + * @see parent::__construct() + */ + public function __construct($x = 0, $base = 10) + { + if (!isset(static::$isValidEngine[static::class])) { + static::$isValidEngine[static::class] = self::isValidEngine(); + } + if (!static::$isValidEngine[static::class]) { + throw new \FluentSmtpLib\phpseclib3\Exception\BadConfigurationException('GMP is not setup correctly on this system'); + } + if ($x instanceof \GMP) { + $this->value = $x; + return; + } + $this->value = \gmp_init(0); + parent::__construct($x, $base); + } + /** + * Initialize a GMP BigInteger Engine instance + * + * @param int $base + * @see parent::__construct() + */ + protected function initialize($base) + { + switch (\abs($base)) { + case 256: + $this->value = \gmp_import($this->value); + if ($this->is_negative) { + $this->value = -$this->value; + } + break; + case 16: + $temp = $this->is_negative ? '-0x' . $this->value : '0x' . $this->value; + $this->value = \gmp_init($temp); + break; + case 10: + $this->value = \gmp_init(isset($this->value) ? $this->value : '0'); + } + } + /** + * Converts a BigInteger to a base-10 number. + * + * @return string + */ + public function toString() + { + return (string) $this->value; + } + /** + * Converts a BigInteger to a bit string (eg. base-2). + * + * Negative numbers are saved as positive numbers, unless $twos_compliment is set to true, at which point, they're + * saved as two's compliment. + * + * @param bool $twos_compliment + * @return string + */ + public function toBits($twos_compliment = \false) + { + $hex = $this->toHex($twos_compliment); + $bits = \gmp_strval(\gmp_init($hex, 16), 2); + if ($this->precision > 0) { + $bits = \substr($bits, -$this->precision); + } + if ($twos_compliment && $this->compare(new static()) > 0 && $this->precision <= 0) { + return '0' . $bits; + } + return $bits; + } + /** + * Converts a BigInteger to a byte string (eg. base-256). + * + * @param bool $twos_compliment + * @return string + */ + public function toBytes($twos_compliment = \false) + { + if ($twos_compliment) { + return $this->toBytesHelper(); + } + if (\gmp_cmp($this->value, \gmp_init(0)) == 0) { + return $this->precision > 0 ? \str_repeat(\chr(0), $this->precision + 1 >> 3) : ''; + } + $temp = \gmp_export($this->value); + return $this->precision > 0 ? \substr(\str_pad($temp, $this->precision >> 3, \chr(0), \STR_PAD_LEFT), -($this->precision >> 3)) : \ltrim($temp, \chr(0)); + } + /** + * Adds two BigIntegers. + * + * @param GMP $y + * @return GMP + */ + public function add(\FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\GMP $y) + { + $temp = new self(); + $temp->value = $this->value + $y->value; + return $this->normalize($temp); + } + /** + * Subtracts two BigIntegers. + * + * @param GMP $y + * @return GMP + */ + public function subtract(\FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\GMP $y) + { + $temp = new self(); + $temp->value = $this->value - $y->value; + return $this->normalize($temp); + } + /** + * Multiplies two BigIntegers. + * + * @param GMP $x + * @return GMP + */ + public function multiply(\FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\GMP $x) + { + $temp = new self(); + $temp->value = $this->value * $x->value; + return $this->normalize($temp); + } + /** + * Divides two BigIntegers. + * + * Returns an array whose first element contains the quotient and whose second element contains the + * "common residue". If the remainder would be positive, the "common residue" and the remainder are the + * same. If the remainder would be negative, the "common residue" is equal to the sum of the remainder + * and the divisor (basically, the "common residue" is the first positive modulo). + * + * @param GMP $y + * @return array{GMP, GMP} + */ + public function divide(\FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\GMP $y) + { + $quotient = new self(); + $remainder = new self(); + list($quotient->value, $remainder->value) = \gmp_div_qr($this->value, $y->value); + if (\gmp_sign($remainder->value) < 0) { + $remainder->value = $remainder->value + \gmp_abs($y->value); + } + return [$this->normalize($quotient), $this->normalize($remainder)]; + } + /** + * Compares two numbers. + * + * Although one might think !$x->compare($y) means $x != $y, it, in fact, means the opposite. The reason for this + * is demonstrated thusly: + * + * $x > $y: $x->compare($y) > 0 + * $x < $y: $x->compare($y) < 0 + * $x == $y: $x->compare($y) == 0 + * + * Note how the same comparison operator is used. If you want to test for equality, use $x->equals($y). + * + * {@internal Could return $this->subtract($x), but that's not as fast as what we do do.} + * + * @param GMP $y + * @return int in case < 0 if $this is less than $y; > 0 if $this is greater than $y, and 0 if they are equal. + * @see self::equals() + */ + public function compare(\FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\GMP $y) + { + $r = \gmp_cmp($this->value, $y->value); + if ($r < -1) { + $r = -1; + } + if ($r > 1) { + $r = 1; + } + return $r; + } + /** + * Tests the equality of two numbers. + * + * If you need to see if one number is greater than or less than another number, use BigInteger::compare() + * + * @param GMP $x + * @return bool + */ + public function equals(\FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\GMP $x) + { + return $this->value == $x->value; + } + /** + * Calculates modular inverses. + * + * Say you have (30 mod 17 * x mod 17) mod 17 == 1. x can be found using modular inverses. + * + * @param GMP $n + * @return false|GMP + */ + public function modInverse(\FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\GMP $n) + { + $temp = new self(); + $temp->value = \gmp_invert($this->value, $n->value); + return $temp->value === \false ? \false : $this->normalize($temp); + } + /** + * Calculates the greatest common divisor and Bezout's identity. + * + * Say you have 693 and 609. The GCD is 21. Bezout's identity states that there exist integers x and y such that + * 693*x + 609*y == 21. In point of fact, there are actually an infinite number of x and y combinations and which + * combination is returned is dependent upon which mode is in use. See + * {@link http://en.wikipedia.org/wiki/B%C3%A9zout%27s_identity Bezout's identity - Wikipedia} for more information. + * + * @param GMP $n + * @return GMP[] + */ + public function extendedGCD(\FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\GMP $n) + { + \extract(\gmp_gcdext($this->value, $n->value)); + return ['gcd' => $this->normalize(new self($g)), 'x' => $this->normalize(new self($s)), 'y' => $this->normalize(new self($t))]; + } + /** + * Calculates the greatest common divisor + * + * Say you have 693 and 609. The GCD is 21. + * + * @param GMP $n + * @return GMP + */ + public function gcd(\FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\GMP $n) + { + $r = \gmp_gcd($this->value, $n->value); + return $this->normalize(new self($r)); + } + /** + * Absolute value. + * + * @return GMP + */ + public function abs() + { + $temp = new self(); + $temp->value = \gmp_abs($this->value); + return $temp; + } + /** + * Logical And + * + * @param GMP $x + * @return GMP + */ + public function bitwise_and(\FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\GMP $x) + { + $temp = new self(); + $temp->value = $this->value & $x->value; + return $this->normalize($temp); + } + /** + * Logical Or + * + * @param GMP $x + * @return GMP + */ + public function bitwise_or(\FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\GMP $x) + { + $temp = new self(); + $temp->value = $this->value | $x->value; + return $this->normalize($temp); + } + /** + * Logical Exclusive Or + * + * @param GMP $x + * @return GMP + */ + public function bitwise_xor(\FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\GMP $x) + { + $temp = new self(); + $temp->value = $this->value ^ $x->value; + return $this->normalize($temp); + } + /** + * Logical Right Shift + * + * Shifts BigInteger's by $shift bits, effectively dividing by 2**$shift. + * + * @param int $shift + * @return GMP + */ + public function bitwise_rightShift($shift) + { + // 0xFFFFFFFF >> 2 == -1 (on 32-bit systems) + // gmp_init('0xFFFFFFFF') >> 2 == gmp_init('0x3FFFFFFF') + $temp = new self(); + $temp->value = $this->value >> $shift; + return $this->normalize($temp); + } + /** + * Logical Left Shift + * + * Shifts BigInteger's by $shift bits, effectively multiplying by 2**$shift. + * + * @param int $shift + * @return GMP + */ + public function bitwise_leftShift($shift) + { + $temp = new self(); + $temp->value = $this->value << $shift; + return $this->normalize($temp); + } + /** + * Performs modular exponentiation. + * + * @param GMP $e + * @param GMP $n + * @return GMP + */ + public function modPow(\FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\GMP $e, \FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\GMP $n) + { + return $this->powModOuter($e, $n); + } + /** + * Performs modular exponentiation. + * + * Alias for modPow(). + * + * @param GMP $e + * @param GMP $n + * @return GMP + */ + public function powMod(\FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\GMP $e, \FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\GMP $n) + { + return $this->powModOuter($e, $n); + } + /** + * Performs modular exponentiation. + * + * @param GMP $e + * @param GMP $n + * @return GMP + */ + protected function powModInner(\FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\GMP $e, \FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\GMP $n) + { + $class = static::$modexpEngine[static::class]; + return $class::powModHelper($this, $e, $n); + } + /** + * Normalize + * + * Removes leading zeros and truncates (if necessary) to maintain the appropriate precision + * + * @param GMP $result + * @return GMP + */ + protected function normalize(\FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\GMP $result) + { + $result->precision = $this->precision; + $result->bitmask = $this->bitmask; + if ($result->bitmask !== \false) { + $flip = $result->value < 0; + if ($flip) { + $result->value = -$result->value; + } + $result->value = $result->value & $result->bitmask->value; + if ($flip) { + $result->value = -$result->value; + } + } + return $result; + } + /** + * Performs some post-processing for randomRangePrime + * + * @param Engine $x + * @param Engine $min + * @param Engine $max + * @return GMP + */ + protected static function randomRangePrimeInner(\FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\Engine $x, \FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\Engine $min, \FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\Engine $max) + { + $p = \gmp_nextprime($x->value); + if ($p <= $max->value) { + return new self($p); + } + if ($min->value != $x->value) { + $x = new self($x->value - 1); + } + return self::randomRangePrime($min, $x); + } + /** + * Generate a random prime number between a range + * + * If there's not a prime within the given range, false will be returned. + * + * @param GMP $min + * @param GMP $max + * @return false|GMP + */ + public static function randomRangePrime(\FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\GMP $min, \FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\GMP $max) + { + return self::randomRangePrimeOuter($min, $max); + } + /** + * Generate a random number between a range + * + * Returns a random number between $min and $max where $min and $max + * can be defined using one of the two methods: + * + * BigInteger::randomRange($min, $max) + * BigInteger::randomRange($max, $min) + * + * @param GMP $min + * @param GMP $max + * @return GMP + */ + public static function randomRange(\FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\GMP $min, \FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\GMP $max) + { + return self::randomRangeHelper($min, $max); + } + /** + * Make the current number odd + * + * If the current number is odd it'll be unchanged. If it's even, one will be added to it. + * + * @see self::randomPrime() + */ + protected function make_odd() + { + \gmp_setbit($this->value, 0); + } + /** + * Tests Primality + * + * @param int $t + * @return bool + */ + protected function testPrimality($t) + { + return \gmp_prob_prime($this->value, $t) != 0; + } + /** + * Calculates the nth root of a biginteger. + * + * Returns the nth root of a positive biginteger, where n defaults to 2 + * + * @param int $n + * @return GMP + */ + protected function rootInner($n) + { + $root = new self(); + $root->value = \gmp_root($this->value, $n); + return $this->normalize($root); + } + /** + * Performs exponentiation. + * + * @param GMP $n + * @return GMP + */ + public function pow(\FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\GMP $n) + { + $temp = new self(); + $temp->value = $this->value ** $n->value; + return $this->normalize($temp); + } + /** + * Return the minimum BigInteger between an arbitrary number of BigIntegers. + * + * @param GMP ...$nums + * @return GMP + */ + public static function min(\FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\GMP ...$nums) + { + return self::minHelper($nums); + } + /** + * Return the maximum BigInteger between an arbitrary number of BigIntegers. + * + * @param GMP ...$nums + * @return GMP + */ + public static function max(\FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\GMP ...$nums) + { + return self::maxHelper($nums); + } + /** + * Tests BigInteger to see if it is between two integers, inclusive + * + * @param GMP $min + * @param GMP $max + * @return bool + */ + public function between(\FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\GMP $min, \FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\GMP $max) + { + return $this->compare($min) >= 0 && $this->compare($max) <= 0; + } + /** + * Create Recurring Modulo Function + * + * Sometimes it may be desirable to do repeated modulos with the same number outside of + * modular exponentiation + * + * @return callable + */ + public function createRecurringModuloFunction() + { + $temp = $this->value; + return function (\FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\GMP $x) use($temp) { + return new \FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\GMP($x->value % $temp); + }; + } + /** + * Scan for 1 and right shift by that amount + * + * ie. $s = gmp_scan1($n, 0) and $r = gmp_div_q($n, gmp_pow(gmp_init('2'), $s)); + * + * @param GMP $r + * @return int + */ + public static function scan1divide(\FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\GMP $r) + { + $s = \gmp_scan1($r->value, 0); + $r->value >>= $s; + return $s; + } + /** + * Is Odd? + * + * @return bool + */ + public function isOdd() + { + return \gmp_testbit($this->value, 0); + } + /** + * Tests if a bit is set + * + * @return bool + */ + public function testBit($x) + { + return \gmp_testbit($this->value, $x); + } + /** + * Is Negative? + * + * @return bool + */ + public function isNegative() + { + return \gmp_sign($this->value) == -1; + } + /** + * Negate + * + * Given $k, returns -$k + * + * @return GMP + */ + public function negate() + { + $temp = clone $this; + $temp->value = -$this->value; + return $temp; + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/GMP/DefaultEngine.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/GMP/DefaultEngine.php new file mode 100644 index 0000000..f06ec25 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/GMP/DefaultEngine.php @@ -0,0 +1,37 @@ + + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://pear.php.net/package/Math_BigInteger + */ +namespace FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\GMP; + +use FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\GMP; +/** + * GMP Modular Exponentiation Engine + * + * @author Jim Wigginton + */ +abstract class DefaultEngine extends \FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\GMP +{ + /** + * Performs modular exponentiation. + * + * @param GMP $x + * @param GMP $e + * @param GMP $n + * @return GMP + */ + protected static function powModHelper(\FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\GMP $x, \FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\GMP $e, \FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\GMP $n) + { + $temp = new \FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\GMP(); + $temp->value = \gmp_powm($x->value, $e->value, $n->value); + return $x->normalize($temp); + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/OpenSSL.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/OpenSSL.php new file mode 100644 index 0000000..6edfc37 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/OpenSSL.php @@ -0,0 +1,58 @@ + + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://pear.php.net/package/Math_BigInteger + */ +namespace FluentSmtpLib\phpseclib3\Math\BigInteger\Engines; + +use FluentSmtpLib\phpseclib3\Crypt\RSA\Formats\Keys\PKCS8; +use FluentSmtpLib\phpseclib3\Math\BigInteger; +/** + * OpenSSL Modular Exponentiation Engine + * + * @author Jim Wigginton + */ +abstract class OpenSSL +{ + /** + * Test for engine validity + * + * @return bool + */ + public static function isValidEngine() + { + return \extension_loaded('openssl') && static::class != __CLASS__; + } + /** + * Performs modular exponentiation. + * + * @param Engine $x + * @param Engine $e + * @param Engine $n + * @return Engine + */ + public static function powModHelper(\FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\Engine $x, \FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\Engine $e, \FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\Engine $n) + { + if ($n->getLengthInBytes() < 31 || $n->getLengthInBytes() > 16384) { + throw new \OutOfRangeException('Only modulo between 31 and 16384 bits are accepted'); + } + $key = \FluentSmtpLib\phpseclib3\Crypt\RSA\Formats\Keys\PKCS8::savePublicKey(new \FluentSmtpLib\phpseclib3\Math\BigInteger($n), new \FluentSmtpLib\phpseclib3\Math\BigInteger($e)); + $plaintext = \str_pad($x->toBytes(), $n->getLengthInBytes(), "\x00", \STR_PAD_LEFT); + // this is easily prone to failure. if the modulo is a multiple of 2 or 3 or whatever it + // won't work and you'll get a "failure: error:0906D06C:PEM routines:PEM_read_bio:no start line" + // error. i suppose, for even numbers, we could do what PHP\Montgomery.php does, but then what + // about odd numbers divisible by 3, by 5, etc? + if (!\openssl_public_encrypt($plaintext, $result, $key, \OPENSSL_NO_PADDING)) { + throw new \UnexpectedValueException(\openssl_error_string()); + } + $class = \get_class($x); + return new $class($result, 256); + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/PHP.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/PHP.php new file mode 100644 index 0000000..be9c60f --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/PHP.php @@ -0,0 +1,1110 @@ + + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://pear.php.net/package/Math_BigInteger + */ +namespace FluentSmtpLib\phpseclib3\Math\BigInteger\Engines; + +use FluentSmtpLib\phpseclib3\Common\Functions\Strings; +use FluentSmtpLib\phpseclib3\Exception\BadConfigurationException; +/** + * Pure-PHP Engine. + * + * @author Jim Wigginton + */ +abstract class PHP extends \FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\Engine +{ + /**#@+ + * Array constants + * + * Rather than create a thousands and thousands of new BigInteger objects in repeated function calls to add() and + * multiply() or whatever, we'll just work directly on arrays, taking them in as parameters and returning them. + * + */ + /** + * $result[self::VALUE] contains the value. + */ + const VALUE = 0; + /** + * $result[self::SIGN] contains the sign. + */ + const SIGN = 1; + /**#@-*/ + /** + * Karatsuba Cutoff + * + * At what point do we switch between Karatsuba multiplication and schoolbook long multiplication? + * + */ + const KARATSUBA_CUTOFF = 25; + /** + * Can Bitwise operations be done fast? + * + * @see parent::bitwise_leftRotate() + * @see parent::bitwise_rightRotate() + */ + const FAST_BITWISE = \true; + /** + * Engine Directory + * + * @see parent::setModExpEngine + */ + const ENGINE_DIR = 'PHP'; + /** + * Default constructor + * + * @param mixed $x integer Base-10 number or base-$base number if $base set. + * @param int $base + * @return PHP + * @see parent::__construct() + */ + public function __construct($x = 0, $base = 10) + { + if (!isset(static::$isValidEngine[static::class])) { + static::$isValidEngine[static::class] = static::isValidEngine(); + } + if (!static::$isValidEngine[static::class]) { + throw new \FluentSmtpLib\phpseclib3\Exception\BadConfigurationException(static::class . ' is not setup correctly on this system'); + } + $this->value = []; + parent::__construct($x, $base); + } + /** + * Initialize a PHP BigInteger Engine instance + * + * @param int $base + * @see parent::__construct() + */ + protected function initialize($base) + { + switch (\abs($base)) { + case 16: + $x = \strlen($this->value) & 1 ? '0' . $this->value : $this->value; + $temp = new static(\FluentSmtpLib\phpseclib3\Common\Functions\Strings::hex2bin($x), 256); + $this->value = $temp->value; + break; + case 10: + $temp = new static(); + $multiplier = new static(); + $multiplier->value = [static::MAX10]; + $x = $this->value; + if ($x[0] == '-') { + $this->is_negative = \true; + $x = \substr($x, 1); + } + $x = \str_pad($x, \strlen($x) + (static::MAX10LEN - 1) * \strlen($x) % static::MAX10LEN, 0, \STR_PAD_LEFT); + while (\strlen($x)) { + $temp = $temp->multiply($multiplier); + $temp = $temp->add(new static($this->int2bytes(\substr($x, 0, static::MAX10LEN)), 256)); + $x = \substr($x, static::MAX10LEN); + } + $this->value = $temp->value; + } + } + /** + * Pads strings so that unpack may be used on them + * + * @param string $str + * @return string + */ + protected function pad($str) + { + $length = \strlen($str); + $pad = 4 - \strlen($str) % 4; + return \str_pad($str, $length + $pad, "\x00", \STR_PAD_LEFT); + } + /** + * Converts a BigInteger to a base-10 number. + * + * @return string + */ + public function toString() + { + if (!\count($this->value)) { + return '0'; + } + $temp = clone $this; + $temp->bitmask = \false; + $temp->is_negative = \false; + $divisor = new static(); + $divisor->value = [static::MAX10]; + $result = ''; + while (\count($temp->value)) { + list($temp, $mod) = $temp->divide($divisor); + $result = \str_pad(isset($mod->value[0]) ? $mod->value[0] : '', static::MAX10LEN, '0', \STR_PAD_LEFT) . $result; + } + $result = \ltrim($result, '0'); + if (empty($result)) { + $result = '0'; + } + if ($this->is_negative) { + $result = '-' . $result; + } + return $result; + } + /** + * Converts a BigInteger to a byte string (eg. base-256). + * + * @param bool $twos_compliment + * @return string + */ + public function toBytes($twos_compliment = \false) + { + if ($twos_compliment) { + return $this->toBytesHelper(); + } + if (!\count($this->value)) { + return $this->precision > 0 ? \str_repeat(\chr(0), $this->precision + 1 >> 3) : ''; + } + $result = $this->bitwise_small_split(8); + $result = \implode('', \array_map('chr', $result)); + return $this->precision > 0 ? \str_pad(\substr($result, -($this->precision + 7 >> 3)), $this->precision + 7 >> 3, \chr(0), \STR_PAD_LEFT) : $result; + } + /** + * Performs addition. + * + * @param array $x_value + * @param bool $x_negative + * @param array $y_value + * @param bool $y_negative + * @return array + */ + protected static function addHelper(array $x_value, $x_negative, array $y_value, $y_negative) + { + $x_size = \count($x_value); + $y_size = \count($y_value); + if ($x_size == 0) { + return [self::VALUE => $y_value, self::SIGN => $y_negative]; + } elseif ($y_size == 0) { + return [self::VALUE => $x_value, self::SIGN => $x_negative]; + } + // subtract, if appropriate + if ($x_negative != $y_negative) { + if ($x_value == $y_value) { + return [self::VALUE => [], self::SIGN => \false]; + } + $temp = self::subtractHelper($x_value, \false, $y_value, \false); + $temp[self::SIGN] = self::compareHelper($x_value, \false, $y_value, \false) > 0 ? $x_negative : $y_negative; + return $temp; + } + if ($x_size < $y_size) { + $size = $x_size; + $value = $y_value; + } else { + $size = $y_size; + $value = $x_value; + } + $value[\count($value)] = 0; + // just in case the carry adds an extra digit + $carry = 0; + for ($i = 0, $j = 1; $j < $size; $i += 2, $j += 2) { + //$sum = $x_value[$j] * static::BASE_FULL + $x_value[$i] + $y_value[$j] * static::BASE_FULL + $y_value[$i] + $carry; + $sum = ($x_value[$j] + $y_value[$j]) * static::BASE_FULL + $x_value[$i] + $y_value[$i] + $carry; + $carry = $sum >= static::MAX_DIGIT2; + // eg. floor($sum / 2**52); only possible values (in any base) are 0 and 1 + $sum = $carry ? $sum - static::MAX_DIGIT2 : $sum; + $temp = static::BASE === 26 ? \intval($sum / 0x4000000) : $sum >> 31; + $value[$i] = (int) ($sum - static::BASE_FULL * $temp); + // eg. a faster alternative to fmod($sum, 0x4000000) + $value[$j] = $temp; + } + if ($j == $size) { + // ie. if $y_size is odd + $sum = $x_value[$i] + $y_value[$i] + $carry; + $carry = $sum >= static::BASE_FULL; + $value[$i] = $carry ? $sum - static::BASE_FULL : $sum; + ++$i; + // ie. let $i = $j since we've just done $value[$i] + } + if ($carry) { + for (; $value[$i] == static::MAX_DIGIT; ++$i) { + $value[$i] = 0; + } + ++$value[$i]; + } + return [self::VALUE => self::trim($value), self::SIGN => $x_negative]; + } + /** + * Performs subtraction. + * + * @param array $x_value + * @param bool $x_negative + * @param array $y_value + * @param bool $y_negative + * @return array + */ + public static function subtractHelper(array $x_value, $x_negative, array $y_value, $y_negative) + { + $x_size = \count($x_value); + $y_size = \count($y_value); + if ($x_size == 0) { + return [self::VALUE => $y_value, self::SIGN => !$y_negative]; + } elseif ($y_size == 0) { + return [self::VALUE => $x_value, self::SIGN => $x_negative]; + } + // add, if appropriate (ie. -$x - +$y or +$x - -$y) + if ($x_negative != $y_negative) { + $temp = self::addHelper($x_value, \false, $y_value, \false); + $temp[self::SIGN] = $x_negative; + return $temp; + } + $diff = self::compareHelper($x_value, $x_negative, $y_value, $y_negative); + if (!$diff) { + return [self::VALUE => [], self::SIGN => \false]; + } + // switch $x and $y around, if appropriate. + if (!$x_negative && $diff < 0 || $x_negative && $diff > 0) { + $temp = $x_value; + $x_value = $y_value; + $y_value = $temp; + $x_negative = !$x_negative; + $x_size = \count($x_value); + $y_size = \count($y_value); + } + // at this point, $x_value should be at least as big as - if not bigger than - $y_value + $carry = 0; + for ($i = 0, $j = 1; $j < $y_size; $i += 2, $j += 2) { + $sum = ($x_value[$j] - $y_value[$j]) * static::BASE_FULL + $x_value[$i] - $y_value[$i] - $carry; + $carry = $sum < 0; + // eg. floor($sum / 2**52); only possible values (in any base) are 0 and 1 + $sum = $carry ? $sum + static::MAX_DIGIT2 : $sum; + $temp = static::BASE === 26 ? \intval($sum / 0x4000000) : $sum >> 31; + $x_value[$i] = (int) ($sum - static::BASE_FULL * $temp); + $x_value[$j] = $temp; + } + if ($j == $y_size) { + // ie. if $y_size is odd + $sum = $x_value[$i] - $y_value[$i] - $carry; + $carry = $sum < 0; + $x_value[$i] = $carry ? $sum + static::BASE_FULL : $sum; + ++$i; + } + if ($carry) { + for (; !$x_value[$i]; ++$i) { + $x_value[$i] = static::MAX_DIGIT; + } + --$x_value[$i]; + } + return [self::VALUE => self::trim($x_value), self::SIGN => $x_negative]; + } + /** + * Performs multiplication. + * + * @param array $x_value + * @param bool $x_negative + * @param array $y_value + * @param bool $y_negative + * @return array + */ + protected static function multiplyHelper(array $x_value, $x_negative, array $y_value, $y_negative) + { + //if ( $x_value == $y_value ) { + // return [ + // self::VALUE => self::square($x_value), + // self::SIGN => $x_sign != $y_value + // ]; + //} + $x_length = \count($x_value); + $y_length = \count($y_value); + if (!$x_length || !$y_length) { + // a 0 is being multiplied + return [self::VALUE => [], self::SIGN => \false]; + } + return [self::VALUE => \min($x_length, $y_length) < 2 * self::KARATSUBA_CUTOFF ? self::trim(self::regularMultiply($x_value, $y_value)) : self::trim(self::karatsuba($x_value, $y_value)), self::SIGN => $x_negative != $y_negative]; + } + /** + * Performs Karatsuba multiplication on two BigIntegers + * + * See {@link http://en.wikipedia.org/wiki/Karatsuba_algorithm Karatsuba algorithm} and + * {@link http://math.libtomcrypt.com/files/tommath.pdf#page=120 MPM 5.2.3}. + * + * @param array $x_value + * @param array $y_value + * @return array + */ + private static function karatsuba(array $x_value, array $y_value) + { + $m = \min(\count($x_value) >> 1, \count($y_value) >> 1); + if ($m < self::KARATSUBA_CUTOFF) { + return self::regularMultiply($x_value, $y_value); + } + $x1 = \array_slice($x_value, $m); + $x0 = \array_slice($x_value, 0, $m); + $y1 = \array_slice($y_value, $m); + $y0 = \array_slice($y_value, 0, $m); + $z2 = self::karatsuba($x1, $y1); + $z0 = self::karatsuba($x0, $y0); + $z1 = self::addHelper($x1, \false, $x0, \false); + $temp = self::addHelper($y1, \false, $y0, \false); + $z1 = self::karatsuba($z1[self::VALUE], $temp[self::VALUE]); + $temp = self::addHelper($z2, \false, $z0, \false); + $z1 = self::subtractHelper($z1, \false, $temp[self::VALUE], \false); + $z2 = \array_merge(\array_fill(0, 2 * $m, 0), $z2); + $z1[self::VALUE] = \array_merge(\array_fill(0, $m, 0), $z1[self::VALUE]); + $xy = self::addHelper($z2, \false, $z1[self::VALUE], $z1[self::SIGN]); + $xy = self::addHelper($xy[self::VALUE], $xy[self::SIGN], $z0, \false); + return $xy[self::VALUE]; + } + /** + * Performs long multiplication on two BigIntegers + * + * Modeled after 'multiply' in MutableBigInteger.java. + * + * @param array $x_value + * @param array $y_value + * @return array + */ + protected static function regularMultiply(array $x_value, array $y_value) + { + $x_length = \count($x_value); + $y_length = \count($y_value); + if (!$x_length || !$y_length) { + // a 0 is being multiplied + return []; + } + $product_value = self::array_repeat(0, $x_length + $y_length); + // the following for loop could be removed if the for loop following it + // (the one with nested for loops) initially set $i to 0, but + // doing so would also make the result in one set of unnecessary adds, + // since on the outermost loops first pass, $product->value[$k] is going + // to always be 0 + $carry = 0; + for ($j = 0; $j < $x_length; ++$j) { + // ie. $i = 0 + $temp = $x_value[$j] * $y_value[0] + $carry; + // $product_value[$k] == 0 + $carry = static::BASE === 26 ? \intval($temp / 0x4000000) : $temp >> 31; + $product_value[$j] = (int) ($temp - static::BASE_FULL * $carry); + } + $product_value[$j] = $carry; + // the above for loop is what the previous comment was talking about. the + // following for loop is the "one with nested for loops" + for ($i = 1; $i < $y_length; ++$i) { + $carry = 0; + for ($j = 0, $k = $i; $j < $x_length; ++$j, ++$k) { + $temp = $product_value[$k] + $x_value[$j] * $y_value[$i] + $carry; + $carry = static::BASE === 26 ? \intval($temp / 0x4000000) : $temp >> 31; + $product_value[$k] = (int) ($temp - static::BASE_FULL * $carry); + } + $product_value[$k] = $carry; + } + return $product_value; + } + /** + * Divides two BigIntegers. + * + * Returns an array whose first element contains the quotient and whose second element contains the + * "common residue". If the remainder would be positive, the "common residue" and the remainder are the + * same. If the remainder would be negative, the "common residue" is equal to the sum of the remainder + * and the divisor (basically, the "common residue" is the first positive modulo). + * + * @return array{static, static} + * @internal This function is based off of + * {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf#page=9 HAC 14.20}. + */ + protected function divideHelper(\FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\PHP $y) + { + if (\count($y->value) == 1) { + list($q, $r) = $this->divide_digit($this->value, $y->value[0]); + $quotient = new static(); + $remainder = new static(); + $quotient->value = $q; + $remainder->value = [$r]; + $quotient->is_negative = $this->is_negative != $y->is_negative; + return [$this->normalize($quotient), $this->normalize($remainder)]; + } + $x = clone $this; + $y = clone $y; + $x_sign = $x->is_negative; + $y_sign = $y->is_negative; + $x->is_negative = $y->is_negative = \false; + $diff = $x->compare($y); + if (!$diff) { + $temp = new static(); + $temp->value = [1]; + $temp->is_negative = $x_sign != $y_sign; + return [$this->normalize($temp), $this->normalize(static::$zero[static::class])]; + } + if ($diff < 0) { + // if $x is negative, "add" $y. + if ($x_sign) { + $x = $y->subtract($x); + } + return [$this->normalize(static::$zero[static::class]), $this->normalize($x)]; + } + // normalize $x and $y as described in HAC 14.23 / 14.24 + $msb = $y->value[\count($y->value) - 1]; + for ($shift = 0; !($msb & static::MSB); ++$shift) { + $msb <<= 1; + } + $x->lshift($shift); + $y->lshift($shift); + $y_value =& $y->value; + $x_max = \count($x->value) - 1; + $y_max = \count($y->value) - 1; + $quotient = new static(); + $quotient_value =& $quotient->value; + $quotient_value = self::array_repeat(0, $x_max - $y_max + 1); + static $temp, $lhs, $rhs; + if (!isset($temp)) { + $temp = new static(); + $lhs = new static(); + $rhs = new static(); + } + if (static::class != \get_class($temp)) { + $temp = new static(); + $lhs = new static(); + $rhs = new static(); + } + $temp_value =& $temp->value; + $rhs_value =& $rhs->value; + // $temp = $y << ($x_max - $y_max-1) in base 2**26 + $temp_value = \array_merge(self::array_repeat(0, $x_max - $y_max), $y_value); + while ($x->compare($temp) >= 0) { + // calculate the "common residue" + ++$quotient_value[$x_max - $y_max]; + $x = $x->subtract($temp); + $x_max = \count($x->value) - 1; + } + for ($i = $x_max; $i >= $y_max + 1; --$i) { + $x_value =& $x->value; + $x_window = [isset($x_value[$i]) ? $x_value[$i] : 0, isset($x_value[$i - 1]) ? $x_value[$i - 1] : 0, isset($x_value[$i - 2]) ? $x_value[$i - 2] : 0]; + $y_window = [$y_value[$y_max], $y_max > 0 ? $y_value[$y_max - 1] : 0]; + $q_index = $i - $y_max - 1; + if ($x_window[0] == $y_window[0]) { + $quotient_value[$q_index] = static::MAX_DIGIT; + } else { + $quotient_value[$q_index] = self::safe_divide($x_window[0] * static::BASE_FULL + $x_window[1], $y_window[0]); + } + $temp_value = [$y_window[1], $y_window[0]]; + $lhs->value = [$quotient_value[$q_index]]; + $lhs = $lhs->multiply($temp); + $rhs_value = [$x_window[2], $x_window[1], $x_window[0]]; + while ($lhs->compare($rhs) > 0) { + --$quotient_value[$q_index]; + $lhs->value = [$quotient_value[$q_index]]; + $lhs = $lhs->multiply($temp); + } + $adjust = self::array_repeat(0, $q_index); + $temp_value = [$quotient_value[$q_index]]; + $temp = $temp->multiply($y); + $temp_value =& $temp->value; + if (\count($temp_value)) { + $temp_value = \array_merge($adjust, $temp_value); + } + $x = $x->subtract($temp); + if ($x->compare(static::$zero[static::class]) < 0) { + $temp_value = \array_merge($adjust, $y_value); + $x = $x->add($temp); + --$quotient_value[$q_index]; + } + $x_max = \count($x_value) - 1; + } + // unnormalize the remainder + $x->rshift($shift); + $quotient->is_negative = $x_sign != $y_sign; + // calculate the "common residue", if appropriate + if ($x_sign) { + $y->rshift($shift); + $x = $y->subtract($x); + } + return [$this->normalize($quotient), $this->normalize($x)]; + } + /** + * Divides a BigInteger by a regular integer + * + * abc / x = a00 / x + b0 / x + c / x + * + * @param array $dividend + * @param int $divisor + * @return array + */ + private static function divide_digit(array $dividend, $divisor) + { + $carry = 0; + $result = []; + for ($i = \count($dividend) - 1; $i >= 0; --$i) { + $temp = static::BASE_FULL * $carry + $dividend[$i]; + $result[$i] = self::safe_divide($temp, $divisor); + $carry = (int) ($temp - $divisor * $result[$i]); + } + return [$result, $carry]; + } + /** + * Single digit division + * + * Even if int64 is being used the division operator will return a float64 value + * if the dividend is not evenly divisible by the divisor. Since a float64 doesn't + * have the precision of int64 this is a problem so, when int64 is being used, + * we'll guarantee that the dividend is divisible by first subtracting the remainder. + * + * @param int $x + * @param int $y + * @return int + */ + private static function safe_divide($x, $y) + { + if (static::BASE === 26) { + return (int) ($x / $y); + } + // static::BASE === 31 + /** @var int */ + return ($x - $x % $y) / $y; + } + /** + * Convert an array / boolean to a PHP BigInteger object + * + * @param array $arr + * @return static + */ + protected function convertToObj(array $arr) + { + $result = new static(); + $result->value = $arr[self::VALUE]; + $result->is_negative = $arr[self::SIGN]; + return $this->normalize($result); + } + /** + * Normalize + * + * Removes leading zeros and truncates (if necessary) to maintain the appropriate precision + * + * @param PHP $result + * @return static + */ + protected function normalize(\FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\PHP $result) + { + $result->precision = $this->precision; + $result->bitmask = $this->bitmask; + $value =& $result->value; + if (!\count($value)) { + $result->is_negative = \false; + return $result; + } + $value = static::trim($value); + if (!empty($result->bitmask->value)) { + $length = \min(\count($value), \count($result->bitmask->value)); + $value = \array_slice($value, 0, $length); + for ($i = 0; $i < $length; ++$i) { + $value[$i] = $value[$i] & $result->bitmask->value[$i]; + } + $value = static::trim($value); + } + return $result; + } + /** + * Compares two numbers. + * + * @param array $x_value + * @param bool $x_negative + * @param array $y_value + * @param bool $y_negative + * @return int + * @see static::compare() + */ + protected static function compareHelper(array $x_value, $x_negative, array $y_value, $y_negative) + { + if ($x_negative != $y_negative) { + return !$x_negative && $y_negative ? 1 : -1; + } + $result = $x_negative ? -1 : 1; + if (\count($x_value) != \count($y_value)) { + return \count($x_value) > \count($y_value) ? $result : -$result; + } + $size = \max(\count($x_value), \count($y_value)); + $x_value = \array_pad($x_value, $size, 0); + $y_value = \array_pad($y_value, $size, 0); + for ($i = \count($x_value) - 1; $i >= 0; --$i) { + if ($x_value[$i] != $y_value[$i]) { + return $x_value[$i] > $y_value[$i] ? $result : -$result; + } + } + return 0; + } + /** + * Absolute value. + * + * @return PHP + */ + public function abs() + { + $temp = new static(); + $temp->value = $this->value; + return $temp; + } + /** + * Trim + * + * Removes leading zeros + * + * @param list $value + * @return list + */ + protected static function trim(array $value) + { + for ($i = \count($value) - 1; $i >= 0; --$i) { + if ($value[$i]) { + break; + } + unset($value[$i]); + } + return $value; + } + /** + * Logical Right Shift + * + * Shifts BigInteger's by $shift bits, effectively dividing by 2**$shift. + * + * @param int $shift + * @return PHP + */ + public function bitwise_rightShift($shift) + { + $temp = new static(); + // could just replace lshift with this, but then all lshift() calls would need to be rewritten + // and I don't want to do that... + $temp->value = $this->value; + $temp->rshift($shift); + return $this->normalize($temp); + } + /** + * Logical Left Shift + * + * Shifts BigInteger's by $shift bits, effectively multiplying by 2**$shift. + * + * @param int $shift + * @return PHP + */ + public function bitwise_leftShift($shift) + { + $temp = new static(); + // could just replace _rshift with this, but then all _lshift() calls would need to be rewritten + // and I don't want to do that... + $temp->value = $this->value; + $temp->lshift($shift); + return $this->normalize($temp); + } + /** + * Converts 32-bit integers to bytes. + * + * @param int $x + * @return string + */ + private static function int2bytes($x) + { + return \ltrim(\pack('N', $x), \chr(0)); + } + /** + * Array Repeat + * + * @param int $input + * @param int $multiplier + * @return array + */ + protected static function array_repeat($input, $multiplier) + { + return $multiplier ? \array_fill(0, $multiplier, $input) : []; + } + /** + * Logical Left Shift + * + * Shifts BigInteger's by $shift bits. + * + * @param int $shift + */ + protected function lshift($shift) + { + if ($shift == 0) { + return; + } + $num_digits = (int) ($shift / static::BASE); + $shift %= static::BASE; + $shift = 1 << $shift; + $carry = 0; + for ($i = 0; $i < \count($this->value); ++$i) { + $temp = $this->value[$i] * $shift + $carry; + $carry = static::BASE === 26 ? \intval($temp / 0x4000000) : $temp >> 31; + $this->value[$i] = (int) ($temp - $carry * static::BASE_FULL); + } + if ($carry) { + $this->value[\count($this->value)] = $carry; + } + while ($num_digits--) { + \array_unshift($this->value, 0); + } + } + /** + * Logical Right Shift + * + * Shifts BigInteger's by $shift bits. + * + * @param int $shift + */ + protected function rshift($shift) + { + if ($shift == 0) { + return; + } + $num_digits = (int) ($shift / static::BASE); + $shift %= static::BASE; + $carry_shift = static::BASE - $shift; + $carry_mask = (1 << $shift) - 1; + if ($num_digits) { + $this->value = \array_slice($this->value, $num_digits); + } + $carry = 0; + for ($i = \count($this->value) - 1; $i >= 0; --$i) { + $temp = $this->value[$i] >> $shift | $carry; + $carry = ($this->value[$i] & $carry_mask) << $carry_shift; + $this->value[$i] = $temp; + } + $this->value = static::trim($this->value); + } + /** + * Performs modular exponentiation. + * + * @param PHP $e + * @param PHP $n + * @return PHP + */ + protected function powModInner(\FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\PHP $e, \FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\PHP $n) + { + try { + $class = static::$modexpEngine[static::class]; + return $class::powModHelper($this, $e, $n, static::class); + } catch (\Exception $err) { + return \FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\PHP\DefaultEngine::powModHelper($this, $e, $n, static::class); + } + } + /** + * Performs squaring + * + * @param list $x + * @return list + */ + protected static function square(array $x) + { + return \count($x) < 2 * self::KARATSUBA_CUTOFF ? self::trim(self::baseSquare($x)) : self::trim(self::karatsubaSquare($x)); + } + /** + * Performs traditional squaring on two BigIntegers + * + * Squaring can be done faster than multiplying a number by itself can be. See + * {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf#page=7 HAC 14.2.4} / + * {@link http://math.libtomcrypt.com/files/tommath.pdf#page=141 MPM 5.3} for more information. + * + * @param array $value + * @return array + */ + protected static function baseSquare(array $value) + { + if (empty($value)) { + return []; + } + $square_value = self::array_repeat(0, 2 * \count($value)); + for ($i = 0, $max_index = \count($value) - 1; $i <= $max_index; ++$i) { + $i2 = $i << 1; + $temp = $square_value[$i2] + $value[$i] * $value[$i]; + $carry = static::BASE === 26 ? \intval($temp / 0x4000000) : $temp >> 31; + $square_value[$i2] = (int) ($temp - static::BASE_FULL * $carry); + // note how we start from $i+1 instead of 0 as we do in multiplication. + for ($j = $i + 1, $k = $i2 + 1; $j <= $max_index; ++$j, ++$k) { + $temp = $square_value[$k] + 2 * $value[$j] * $value[$i] + $carry; + $carry = static::BASE === 26 ? \intval($temp / 0x4000000) : $temp >> 31; + $square_value[$k] = (int) ($temp - static::BASE_FULL * $carry); + } + // the following line can yield values larger 2**15. at this point, PHP should switch + // over to floats. + $square_value[$i + $max_index + 1] = $carry; + } + return $square_value; + } + /** + * Performs Karatsuba "squaring" on two BigIntegers + * + * See {@link http://en.wikipedia.org/wiki/Karatsuba_algorithm Karatsuba algorithm} and + * {@link http://math.libtomcrypt.com/files/tommath.pdf#page=151 MPM 5.3.4}. + * + * @param array $value + * @return array + */ + protected static function karatsubaSquare(array $value) + { + $m = \count($value) >> 1; + if ($m < self::KARATSUBA_CUTOFF) { + return self::baseSquare($value); + } + $x1 = \array_slice($value, $m); + $x0 = \array_slice($value, 0, $m); + $z2 = self::karatsubaSquare($x1); + $z0 = self::karatsubaSquare($x0); + $z1 = self::addHelper($x1, \false, $x0, \false); + $z1 = self::karatsubaSquare($z1[self::VALUE]); + $temp = self::addHelper($z2, \false, $z0, \false); + $z1 = self::subtractHelper($z1, \false, $temp[self::VALUE], \false); + $z2 = \array_merge(\array_fill(0, 2 * $m, 0), $z2); + $z1[self::VALUE] = \array_merge(\array_fill(0, $m, 0), $z1[self::VALUE]); + $xx = self::addHelper($z2, \false, $z1[self::VALUE], $z1[self::SIGN]); + $xx = self::addHelper($xx[self::VALUE], $xx[self::SIGN], $z0, \false); + return $xx[self::VALUE]; + } + /** + * Make the current number odd + * + * If the current number is odd it'll be unchanged. If it's even, one will be added to it. + * + * @see self::randomPrime() + */ + protected function make_odd() + { + $this->value[0] |= 1; + } + /** + * Test the number against small primes. + * + * @see self::isPrime() + */ + protected function testSmallPrimes() + { + if ($this->value == [1]) { + return \false; + } + if ($this->value == [2]) { + return \true; + } + if (~$this->value[0] & 1) { + return \false; + } + $value = $this->value; + foreach (static::PRIMES as $prime) { + list(, $r) = self::divide_digit($value, $prime); + if (!$r) { + return \count($value) == 1 && $value[0] == $prime; + } + } + return \true; + } + /** + * Scan for 1 and right shift by that amount + * + * ie. $s = gmp_scan1($n, 0) and $r = gmp_div_q($n, gmp_pow(gmp_init('2'), $s)); + * + * @param PHP $r + * @return int + * @see self::isPrime() + */ + public static function scan1divide(\FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\PHP $r) + { + $r_value =& $r->value; + for ($i = 0, $r_length = \count($r_value); $i < $r_length; ++$i) { + $temp = ~$r_value[$i] & static::MAX_DIGIT; + for ($j = 1; $temp >> $j & 1; ++$j) { + } + if ($j <= static::BASE) { + break; + } + } + $s = static::BASE * $i + $j; + $r->rshift($s); + return $s; + } + /** + * Performs exponentiation. + * + * @param PHP $n + * @return PHP + */ + protected function powHelper(\FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\PHP $n) + { + if ($n->compare(static::$zero[static::class]) == 0) { + return new static(1); + } + // n^0 = 1 + $temp = clone $this; + while (!$n->equals(static::$one[static::class])) { + $temp = $temp->multiply($this); + $n = $n->subtract(static::$one[static::class]); + } + return $temp; + } + /** + * Is Odd? + * + * @return bool + */ + public function isOdd() + { + return (bool) ($this->value[0] & 1); + } + /** + * Tests if a bit is set + * + * @return bool + */ + public function testBit($x) + { + $digit = (int) \floor($x / static::BASE); + $bit = $x % static::BASE; + if (!isset($this->value[$digit])) { + return \false; + } + return (bool) ($this->value[$digit] & 1 << $bit); + } + /** + * Is Negative? + * + * @return bool + */ + public function isNegative() + { + return $this->is_negative; + } + /** + * Negate + * + * Given $k, returns -$k + * + * @return static + */ + public function negate() + { + $temp = clone $this; + $temp->is_negative = !$temp->is_negative; + return $temp; + } + /** + * Bitwise Split + * + * Splits BigInteger's into chunks of $split bits + * + * @param int $split + * @return list + */ + public function bitwise_split($split) + { + if ($split < 1) { + throw new \RuntimeException('Offset must be greater than 1'); + } + $width = (int) ($split / static::BASE); + if (!$width) { + $arr = $this->bitwise_small_split($split); + return \array_map(function ($digit) { + $temp = new static(); + $temp->value = $digit != 0 ? [$digit] : []; + return $temp; + }, $arr); + } + $vals = []; + $val = $this->value; + $i = $overflow = 0; + $len = \count($val); + while ($i < $len) { + $digit = []; + if (!$overflow) { + $digit = \array_slice($val, $i, $width); + $i += $width; + $overflow = $split % static::BASE; + if ($overflow) { + $mask = (1 << $overflow) - 1; + $temp = isset($val[$i]) ? $val[$i] : 0; + $digit[] = $temp & $mask; + } + } else { + $remaining = static::BASE - $overflow; + $tempsplit = $split - $remaining; + $tempwidth = (int) ($tempsplit / static::BASE + 1); + $digit = \array_slice($val, $i, $tempwidth); + $i += $tempwidth; + $tempoverflow = $tempsplit % static::BASE; + if ($tempoverflow) { + $tempmask = (1 << $tempoverflow) - 1; + $temp = isset($val[$i]) ? $val[$i] : 0; + $digit[] = $temp & $tempmask; + } + $newbits = 0; + for ($j = \count($digit) - 1; $j >= 0; $j--) { + $temp = $digit[$j] & $mask; + $digit[$j] = $digit[$j] >> $overflow | $newbits << $remaining; + $newbits = $temp; + } + $overflow = $tempoverflow; + $mask = $tempmask; + } + $temp = new static(); + $temp->value = static::trim($digit); + $vals[] = $temp; + } + return \array_reverse($vals); + } + /** + * Bitwise Split where $split < static::BASE + * + * @param int $split + * @return list + */ + private function bitwise_small_split($split) + { + $vals = []; + $val = $this->value; + $mask = (1 << $split) - 1; + $i = $overflow = 0; + $len = \count($val); + $val[] = 0; + $remaining = static::BASE; + while ($i != $len) { + $digit = $val[$i] & $mask; + $val[$i] >>= $split; + if (!$overflow) { + $remaining -= $split; + $overflow = $split <= $remaining ? 0 : $split - $remaining; + if (!$remaining) { + $i++; + $remaining = static::BASE; + $overflow = 0; + } + } elseif (++$i != $len) { + $tempmask = (1 << $overflow) - 1; + $digit |= ($val[$i] & $tempmask) << $remaining; + $val[$i] >>= $overflow; + $remaining = static::BASE - $overflow; + $overflow = $split <= $remaining ? 0 : $split - $remaining; + } + $vals[] = $digit; + } + while ($vals[\count($vals) - 1] == 0) { + unset($vals[\count($vals) - 1]); + } + return \array_reverse($vals); + } + /** + * @return bool + */ + protected static function testJITOnWindows() + { + // see https://github.com/php/php-src/issues/11917 + if (\strtoupper(\substr(\PHP_OS, 0, 3)) === 'WIN' && \function_exists('opcache_get_status') && \PHP_VERSION_ID < 80213 && !\defined('FluentSmtpLib\\PHPSECLIB_ALLOW_JIT')) { + $status = \opcache_get_status(); + if ($status && isset($status['jit']) && $status['jit']['enabled'] && $status['jit']['on']) { + return \true; + } + } + return \false; + } + /** + * Return the size of a BigInteger in bits + * + * @return int + */ + public function getLength() + { + $max = \count($this->value) - 1; + return $max != -1 ? $max * static::BASE + \intval(\ceil(\log($this->value[$max] + 1, 2))) : 0; + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/PHP/Base.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/PHP/Base.php new file mode 100644 index 0000000..9c9332e --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/PHP/Base.php @@ -0,0 +1,133 @@ + + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://pear.php.net/package/Math_BigInteger + */ +namespace FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\PHP; + +use FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\PHP; +/** + * PHP Modular Exponentiation Engine + * + * @author Jim Wigginton + */ +abstract class Base extends \FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\PHP +{ + /** + * Cache constants + * + * $cache[self::VARIABLE] tells us whether or not the cached data is still valid. + * + */ + const VARIABLE = 0; + /** + * $cache[self::DATA] contains the cached data. + * + */ + const DATA = 1; + /** + * Test for engine validity + * + * @return bool + */ + public static function isValidEngine() + { + return static::class != __CLASS__; + } + /** + * Performs modular exponentiation. + * + * The most naive approach to modular exponentiation has very unreasonable requirements, and + * and although the approach involving repeated squaring does vastly better, it, too, is impractical + * for our purposes. The reason being that division - by far the most complicated and time-consuming + * of the basic operations (eg. +,-,*,/) - occurs multiple times within it. + * + * Modular reductions resolve this issue. Although an individual modular reduction takes more time + * then an individual division, when performed in succession (with the same modulo), they're a lot faster. + * + * The two most commonly used modular reductions are Barrett and Montgomery reduction. Montgomery reduction, + * although faster, only works when the gcd of the modulo and of the base being used is 1. In RSA, when the + * base is a power of two, the modulo - a product of two primes - is always going to have a gcd of 1 (because + * the product of two odd numbers is odd), but what about when RSA isn't used? + * + * In contrast, Barrett reduction has no such constraint. As such, some bigint implementations perform a + * Barrett reduction after every operation in the modpow function. Others perform Barrett reductions when the + * modulo is even and Montgomery reductions when the modulo is odd. BigInteger.java's modPow method, however, + * uses a trick involving the Chinese Remainder Theorem to factor the even modulo into two numbers - one odd and + * the other, a power of two - and recombine them, later. This is the method that this modPow function uses. + * {@link http://islab.oregonstate.edu/papers/j34monex.pdf Montgomery Reduction with Even Modulus} elaborates. + * + * @param PHP $x + * @param PHP $e + * @param PHP $n + * @param string $class + * @return PHP + */ + protected static function powModHelper(\FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\PHP $x, \FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\PHP $e, \FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\PHP $n, $class) + { + if (empty($e->value)) { + $temp = new $class(); + $temp->value = [1]; + return $x->normalize($temp); + } + if ($e->value == [1]) { + list(, $temp) = $x->divide($n); + return $x->normalize($temp); + } + if ($e->value == [2]) { + $temp = new $class(); + $temp->value = $class::square($x->value); + list(, $temp) = $temp->divide($n); + return $x->normalize($temp); + } + return $x->normalize(static::slidingWindow($x, $e, $n, $class)); + } + /** + * Modular reduction preparation + * + * @param array $x + * @param array $n + * @param string $class + * @see self::slidingWindow() + * @return array + */ + protected static function prepareReduce(array $x, array $n, $class) + { + return static::reduce($x, $n, $class); + } + /** + * Modular multiply + * + * @param array $x + * @param array $y + * @param array $n + * @param string $class + * @see self::slidingWindow() + * @return array + */ + protected static function multiplyReduce(array $x, array $y, array $n, $class) + { + $temp = $class::multiplyHelper($x, \false, $y, \false); + return static::reduce($temp[self::VALUE], $n, $class); + } + /** + * Modular square + * + * @param array $x + * @param array $n + * @param string $class + * @see self::slidingWindow() + * @return array + */ + protected static function squareReduce(array $x, array $n, $class) + { + return static::reduce($class::square($x), $n, $class); + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/PHP/DefaultEngine.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/PHP/DefaultEngine.php new file mode 100644 index 0000000..acd3383 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/PHP/DefaultEngine.php @@ -0,0 +1,23 @@ + + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://pear.php.net/package/Math_BigInteger + */ +namespace FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\PHP; + +use FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\PHP\Reductions\EvalBarrett; +/** + * PHP Default Modular Exponentiation Engine + * + * @author Jim Wigginton + */ +abstract class DefaultEngine extends \FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\PHP\Reductions\EvalBarrett +{ +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/PHP/Montgomery.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/PHP/Montgomery.php new file mode 100644 index 0000000..239a8c8 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/PHP/Montgomery.php @@ -0,0 +1,78 @@ + + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://pear.php.net/package/Math_BigInteger + */ +namespace FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\PHP; + +use FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\Engine; +use FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\PHP; +use FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\PHP\Reductions\PowerOfTwo; +/** + * PHP Montgomery Modular Exponentiation Engine + * + * @author Jim Wigginton + */ +abstract class Montgomery extends \FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\PHP\Base +{ + /** + * Test for engine validity + * + * @return bool + */ + public static function isValidEngine() + { + return static::class != __CLASS__; + } + /** + * Performs modular exponentiation. + * + * @template T of Engine + * @param Engine $x + * @param Engine $e + * @param Engine $n + * @param class-string $class + * @return T + */ + protected static function slidingWindow(\FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\Engine $x, \FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\Engine $e, \FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\Engine $n, $class) + { + // is the modulo odd? + if ($n->value[0] & 1) { + return parent::slidingWindow($x, $e, $n, $class); + } + // if it's not, it's even + // find the lowest set bit (eg. the max pow of 2 that divides $n) + for ($i = 0; $i < \count($n->value); ++$i) { + if ($n->value[$i]) { + $temp = \decbin($n->value[$i]); + $j = \strlen($temp) - \strrpos($temp, '1') - 1; + $j += $class::BASE * $i; + break; + } + } + // at this point, 2^$j * $n/(2^$j) == $n + $mod1 = clone $n; + $mod1->rshift($j); + $mod2 = new $class(); + $mod2->value = [1]; + $mod2->lshift($j); + $part1 = $mod1->value != [1] ? parent::slidingWindow($x, $e, $mod1, $class) : new $class(); + $part2 = \FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\PHP\Reductions\PowerOfTwo::slidingWindow($x, $e, $mod2, $class); + $y1 = $mod2->modInverse($mod1); + $y2 = $mod1->modInverse($mod2); + $result = $part1->multiply($mod2); + $result = $result->multiply($y1); + $temp = $part2->multiply($mod1); + $temp = $temp->multiply($y2); + $result = $result->add($temp); + list(, $result) = $result->divide($n); + return $result; + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/PHP/OpenSSL.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/PHP/OpenSSL.php new file mode 100644 index 0000000..08175a3 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/PHP/OpenSSL.php @@ -0,0 +1,23 @@ + + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://pear.php.net/package/Math_BigInteger + */ +namespace FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\PHP; + +use FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\OpenSSL as Progenitor; +/** + * OpenSSL Modular Exponentiation Engine + * + * @author Jim Wigginton + */ +abstract class OpenSSL extends \FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\OpenSSL +{ +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/PHP/Reductions/Barrett.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/PHP/Reductions/Barrett.php new file mode 100644 index 0000000..f7db8e3 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/PHP/Reductions/Barrett.php @@ -0,0 +1,253 @@ + + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://pear.php.net/package/Math_BigInteger + */ +namespace FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\PHP\Reductions; + +use FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\PHP; +use FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\PHP\Base; +/** + * PHP Barrett Modular Exponentiation Engine + * + * @author Jim Wigginton + */ +abstract class Barrett extends \FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\PHP\Base +{ + /** + * Barrett Modular Reduction + * + * See {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf#page=14 HAC 14.3.3} / + * {@link http://math.libtomcrypt.com/files/tommath.pdf#page=165 MPM 6.2.5} for more information. Modified slightly, + * so as not to require negative numbers (initially, this script didn't support negative numbers). + * + * Employs "folding", as described at + * {@link http://www.cosic.esat.kuleuven.be/publications/thesis-149.pdf#page=66 thesis-149.pdf#page=66}. To quote from + * it, "the idea [behind folding] is to find a value x' such that x (mod m) = x' (mod m), with x' being smaller than x." + * + * Unfortunately, the "Barrett Reduction with Folding" algorithm described in thesis-149.pdf is not, as written, all that + * usable on account of (1) its not using reasonable radix points as discussed in + * {@link http://math.libtomcrypt.com/files/tommath.pdf#page=162 MPM 6.2.2} and (2) the fact that, even with reasonable + * radix points, it only works when there are an even number of digits in the denominator. The reason for (2) is that + * (x >> 1) + (x >> 1) != x / 2 + x / 2. If x is even, they're the same, but if x is odd, they're not. See the in-line + * comments for details. + * + * @param array $n + * @param array $m + * @param class-string $class + * @return array + */ + protected static function reduce(array $n, array $m, $class) + { + static $cache = [self::VARIABLE => [], self::DATA => []]; + $m_length = \count($m); + // if (self::compareHelper($n, $static::square($m)) >= 0) { + if (\count($n) > 2 * $m_length) { + $lhs = new $class(); + $rhs = new $class(); + $lhs->value = $n; + $rhs->value = $m; + list(, $temp) = $lhs->divide($rhs); + return $temp->value; + } + // if (m.length >> 1) + 2 <= m.length then m is too small and n can't be reduced + if ($m_length < 5) { + return self::regularBarrett($n, $m, $class); + } + // n = 2 * m.length + $correctionNeeded = \false; + if ($m_length & 1) { + $correctionNeeded = \true; + \array_unshift($n, 0); + \array_unshift($m, 0); + $m_length++; + } + if (($key = \array_search($m, $cache[self::VARIABLE])) === \false) { + $key = \count($cache[self::VARIABLE]); + $cache[self::VARIABLE][] = $m; + $lhs = new $class(); + $lhs_value =& $lhs->value; + $lhs_value = self::array_repeat(0, $m_length + ($m_length >> 1)); + $lhs_value[] = 1; + $rhs = new $class(); + $rhs->value = $m; + list($u, $m1) = $lhs->divide($rhs); + $u = $u->value; + $m1 = $m1->value; + $cache[self::DATA][] = [ + 'u' => $u, + // m.length >> 1 (technically (m.length >> 1) + 1) + 'm1' => $m1, + ]; + } else { + \extract($cache[self::DATA][$key]); + } + $cutoff = $m_length + ($m_length >> 1); + $lsd = \array_slice($n, 0, $cutoff); + // m.length + (m.length >> 1) + $msd = \array_slice($n, $cutoff); + // m.length >> 1 + $lsd = self::trim($lsd); + $temp = $class::multiplyHelper($msd, \false, $m1, \false); + // m.length + (m.length >> 1) + $n = $class::addHelper($lsd, \false, $temp[self::VALUE], \false); + // m.length + (m.length >> 1) + 1 (so basically we're adding two same length numbers) + //if ($m_length & 1) { + // return self::regularBarrett($n[self::VALUE], $m, $class); + //} + // (m.length + (m.length >> 1) + 1) - (m.length - 1) == (m.length >> 1) + 2 + $temp = \array_slice($n[self::VALUE], $m_length - 1); + // if even: ((m.length >> 1) + 2) + (m.length >> 1) == m.length + 2 + // if odd: ((m.length >> 1) + 2) + (m.length >> 1) == (m.length - 1) + 2 == m.length + 1 + // note that these are upper bounds. let's say m.length is 2. then you'd be multiplying a + // 3 digit number by a 1 digit number. if you're doing 999 * 9 (in base 10) the result will + // be a 4 digit number. but if you're multiplying 111 * 1 then the result will be a 3 digit + // number. + $temp = $class::multiplyHelper($temp, \false, $u, \false); + // if even: (m.length + 2) - ((m.length >> 1) + 1) = m.length - (m.length >> 1) + 1 + // if odd: (m.length + 1) - ((m.length >> 1) + 1) = m.length - (m.length >> 1) + $temp = \array_slice($temp[self::VALUE], ($m_length >> 1) + 1); + // if even: (m.length - (m.length >> 1) + 1) + m.length = 2 * m.length - (m.length >> 1) + 1 + // if odd: (m.length - (m.length >> 1)) + m.length = 2 * m.length - (m.length >> 1) + $temp = $class::multiplyHelper($temp, \false, $m, \false); + // at this point, if m had an odd number of digits, we'd (probably) be subtracting a 2 * m.length - (m.length >> 1) + // digit number from a m.length + (m.length >> 1) + 1 digit number. ie. there'd be an extra digit and the while loop + // following this comment would loop a lot (hence our calling _regularBarrett() in that situation). + $result = $class::subtractHelper($n[self::VALUE], \false, $temp[self::VALUE], \false); + while (self::compareHelper($result[self::VALUE], $result[self::SIGN], $m, \false) >= 0) { + $result = $class::subtractHelper($result[self::VALUE], $result[self::SIGN], $m, \false); + } + if ($correctionNeeded) { + \array_shift($result[self::VALUE]); + } + return $result[self::VALUE]; + } + /** + * (Regular) Barrett Modular Reduction + * + * For numbers with more than four digits BigInteger::_barrett() is faster. The difference between that and this + * is that this function does not fold the denominator into a smaller form. + * + * @param array $x + * @param array $n + * @param string $class + * @return array + */ + private static function regularBarrett(array $x, array $n, $class) + { + static $cache = [self::VARIABLE => [], self::DATA => []]; + $n_length = \count($n); + if (\count($x) > 2 * $n_length) { + $lhs = new $class(); + $rhs = new $class(); + $lhs->value = $x; + $rhs->value = $n; + list(, $temp) = $lhs->divide($rhs); + return $temp->value; + } + if (($key = \array_search($n, $cache[self::VARIABLE])) === \false) { + $key = \count($cache[self::VARIABLE]); + $cache[self::VARIABLE][] = $n; + $lhs = new $class(); + $lhs_value =& $lhs->value; + $lhs_value = self::array_repeat(0, 2 * $n_length); + $lhs_value[] = 1; + $rhs = new $class(); + $rhs->value = $n; + list($temp, ) = $lhs->divide($rhs); + // m.length + $cache[self::DATA][] = $temp->value; + } + // 2 * m.length - (m.length - 1) = m.length + 1 + $temp = \array_slice($x, $n_length - 1); + // (m.length + 1) + m.length = 2 * m.length + 1 + $temp = $class::multiplyHelper($temp, \false, $cache[self::DATA][$key], \false); + // (2 * m.length + 1) - (m.length - 1) = m.length + 2 + $temp = \array_slice($temp[self::VALUE], $n_length + 1); + // m.length + 1 + $result = \array_slice($x, 0, $n_length + 1); + // m.length + 1 + $temp = self::multiplyLower($temp, \false, $n, \false, $n_length + 1, $class); + // $temp == array_slice($class::regularMultiply($temp, false, $n, false)->value, 0, $n_length + 1) + if (self::compareHelper($result, \false, $temp[self::VALUE], $temp[self::SIGN]) < 0) { + $corrector_value = self::array_repeat(0, $n_length + 1); + $corrector_value[\count($corrector_value)] = 1; + $result = $class::addHelper($result, \false, $corrector_value, \false); + $result = $result[self::VALUE]; + } + // at this point, we're subtracting a number with m.length + 1 digits from another number with m.length + 1 digits + $result = $class::subtractHelper($result, \false, $temp[self::VALUE], $temp[self::SIGN]); + while (self::compareHelper($result[self::VALUE], $result[self::SIGN], $n, \false) > 0) { + $result = $class::subtractHelper($result[self::VALUE], $result[self::SIGN], $n, \false); + } + return $result[self::VALUE]; + } + /** + * Performs long multiplication up to $stop digits + * + * If you're going to be doing array_slice($product->value, 0, $stop), some cycles can be saved. + * + * @see self::regularBarrett() + * @param array $x_value + * @param bool $x_negative + * @param array $y_value + * @param bool $y_negative + * @param int $stop + * @param string $class + * @return array + */ + private static function multiplyLower(array $x_value, $x_negative, array $y_value, $y_negative, $stop, $class) + { + $x_length = \count($x_value); + $y_length = \count($y_value); + if (!$x_length || !$y_length) { + // a 0 is being multiplied + return [self::VALUE => [], self::SIGN => \false]; + } + if ($x_length < $y_length) { + $temp = $x_value; + $x_value = $y_value; + $y_value = $temp; + $x_length = \count($x_value); + $y_length = \count($y_value); + } + $product_value = self::array_repeat(0, $x_length + $y_length); + // the following for loop could be removed if the for loop following it + // (the one with nested for loops) initially set $i to 0, but + // doing so would also make the result in one set of unnecessary adds, + // since on the outermost loops first pass, $product->value[$k] is going + // to always be 0 + $carry = 0; + for ($j = 0; $j < $x_length; ++$j) { + // ie. $i = 0, $k = $i + $temp = $x_value[$j] * $y_value[0] + $carry; + // $product_value[$k] == 0 + $carry = $class::BASE === 26 ? \intval($temp / 0x4000000) : $temp >> 31; + $product_value[$j] = (int) ($temp - $class::BASE_FULL * $carry); + } + if ($j < $stop) { + $product_value[$j] = $carry; + } + // the above for loop is what the previous comment was talking about. the + // following for loop is the "one with nested for loops" + for ($i = 1; $i < $y_length; ++$i) { + $carry = 0; + for ($j = 0, $k = $i; $j < $x_length && $k < $stop; ++$j, ++$k) { + $temp = $product_value[$k] + $x_value[$j] * $y_value[$i] + $carry; + $carry = $class::BASE === 26 ? \intval($temp / 0x4000000) : $temp >> 31; + $product_value[$k] = (int) ($temp - $class::BASE_FULL * $carry); + } + if ($k < $stop) { + $product_value[$k] = $carry; + } + } + return [self::VALUE => self::trim($product_value), self::SIGN => $x_negative != $y_negative]; + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/PHP/Reductions/Classic.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/PHP/Reductions/Classic.php new file mode 100644 index 0000000..9da8723 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/PHP/Reductions/Classic.php @@ -0,0 +1,40 @@ + + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://pear.php.net/package/Math_BigInteger + */ +namespace FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\PHP\Reductions; + +use FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\PHP\Base; +/** + * PHP Classic Modular Exponentiation Engine + * + * @author Jim Wigginton + */ +abstract class Classic extends \FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\PHP\Base +{ + /** + * Regular Division + * + * @param array $x + * @param array $n + * @param string $class + * @return array + */ + protected static function reduce(array $x, array $n, $class) + { + $lhs = new $class(); + $lhs->value = $x; + $rhs = new $class(); + $rhs->value = $n; + list(, $temp) = $lhs->divide($rhs); + return $temp->value; + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/PHP/Reductions/EvalBarrett.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/PHP/Reductions/EvalBarrett.php new file mode 100644 index 0000000..cb0425c --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/PHP/Reductions/EvalBarrett.php @@ -0,0 +1,423 @@ + + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://pear.php.net/package/Math_BigInteger + */ +namespace FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\PHP\Reductions; + +use FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\PHP; +use FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\PHP\Base; +/** + * PHP Dynamic Barrett Modular Exponentiation Engine + * + * @author Jim Wigginton + */ +abstract class EvalBarrett extends \FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\PHP\Base +{ + /** + * Custom Reduction Function + * + * @see self::generateCustomReduction + */ + private static $custom_reduction; + /** + * Barrett Modular Reduction + * + * This calls a dynamically generated loop unrolled function that's specific to a given modulo. + * Array lookups are avoided as are if statements testing for how many bits the host OS supports, etc. + * + * @param array $n + * @param array $m + * @param string $class + * @return array + */ + protected static function reduce(array $n, array $m, $class) + { + $inline = self::$custom_reduction; + return $inline($n); + } + /** + * Generate Custom Reduction + * + * @param PHP $m + * @param string $class + * @return callable + */ + protected static function generateCustomReduction(\FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\PHP $m, $class) + { + $m_length = \count($m->value); + if ($m_length < 5) { + $code = ' + $lhs = new ' . $class . '(); + $lhs->value = $x; + $rhs = new ' . $class . '(); + $rhs->value = [' . \implode(',', \array_map(self::class . '::float2string', $m->value)) . ']; + list(, $temp) = $lhs->divide($rhs); + return $temp->value; + '; + eval('$func = function ($x) { ' . $code . '};'); + self::$custom_reduction = $func; + //self::$custom_reduction = \Closure::bind($func, $m, $class); + return $func; + } + $correctionNeeded = \false; + if ($m_length & 1) { + $correctionNeeded = \true; + $m = clone $m; + \array_unshift($m->value, 0); + $m_length++; + } + $lhs = new $class(); + $lhs_value =& $lhs->value; + $lhs_value = self::array_repeat(0, $m_length + ($m_length >> 1)); + $lhs_value[] = 1; + $rhs = new $class(); + list($u, $m1) = $lhs->divide($m); + if ($class::BASE != 26) { + $u = $u->value; + } else { + $lhs_value = self::array_repeat(0, 2 * $m_length); + $lhs_value[] = 1; + $rhs = new $class(); + list($u) = $lhs->divide($m); + $u = $u->value; + } + $m = $m->value; + $m1 = $m1->value; + $cutoff = \count($m) + (\count($m) >> 1); + $code = $correctionNeeded ? 'array_unshift($n, 0);' : ''; + $code .= ' + if (count($n) > ' . 2 * \count($m) . ') { + $lhs = new ' . $class . '(); + $rhs = new ' . $class . '(); + $lhs->value = $n; + $rhs->value = [' . \implode(',', \array_map(self::class . '::float2string', $m)) . ']; + list(, $temp) = $lhs->divide($rhs); + return $temp->value; + } + + $lsd = array_slice($n, 0, ' . $cutoff . '); + $msd = array_slice($n, ' . $cutoff . ');'; + $code .= self::generateInlineTrim('msd'); + $code .= self::generateInlineMultiply('msd', $m1, 'temp', $class); + $code .= self::generateInlineAdd('lsd', 'temp', 'n', $class); + $code .= '$temp = array_slice($n, ' . (\count($m) - 1) . ');'; + $code .= self::generateInlineMultiply('temp', $u, 'temp2', $class); + $code .= self::generateInlineTrim('temp2'); + $code .= $class::BASE == 26 ? '$temp = array_slice($temp2, ' . (\count($m) + 1) . ');' : '$temp = array_slice($temp2, ' . ((\count($m) >> 1) + 1) . ');'; + $code .= self::generateInlineMultiply('temp', $m, 'temp2', $class); + $code .= self::generateInlineTrim('temp2'); + /* + if ($class::BASE == 26) { + $code.= '$n = array_slice($n, 0, ' . (count($m) + 1) . '); + $temp2 = array_slice($temp2, 0, ' . (count($m) + 1) . ');'; + } + */ + $code .= self::generateInlineSubtract2('n', 'temp2', 'temp', $class); + $subcode = self::generateInlineSubtract1('temp', $m, 'temp2', $class); + $subcode .= '$temp = $temp2;'; + $code .= self::generateInlineCompare($m, 'temp', $subcode); + if ($correctionNeeded) { + $code .= 'array_shift($temp);'; + } + $code .= 'return $temp;'; + eval('$func = function ($n) { ' . $code . '};'); + self::$custom_reduction = $func; + return $func; + //self::$custom_reduction = \Closure::bind($func, $m, $class); + } + /** + * Inline Trim + * + * Removes leading zeros + * + * @param string $name + * @return string + */ + private static function generateInlineTrim($name) + { + return ' + for ($i = count($' . $name . ') - 1; $i >= 0; --$i) { + if ($' . $name . '[$i]) { + break; + } + unset($' . $name . '[$i]); + }'; + } + /** + * Inline Multiply (unknown, known) + * + * @param string $input + * @param array $arr + * @param string $output + * @param string $class + * @return string + */ + private static function generateInlineMultiply($input, array $arr, $output, $class) + { + if (!\count($arr)) { + return 'return [];'; + } + $regular = ' + $length = count($' . $input . '); + if (!$length) { + $' . $output . ' = []; + }else{ + $' . $output . ' = array_fill(0, $length + ' . \count($arr) . ', 0); + $carry = 0;'; + for ($i = 0; $i < \count($arr); $i++) { + $regular .= ' + $subtemp = $' . $input . '[0] * ' . $arr[$i]; + $regular .= $i ? ' + $carry;' : ';'; + $regular .= '$carry = '; + $regular .= $class::BASE === 26 ? 'intval($subtemp / 0x4000000);' : '$subtemp >> 31;'; + $regular .= '$' . $output . '[' . $i . '] = '; + if ($class::BASE === 26) { + $regular .= '(int) ('; + } + $regular .= '$subtemp - ' . $class::BASE_FULL . ' * $carry'; + $regular .= $class::BASE === 26 ? ');' : ';'; + } + $regular .= '$' . $output . '[' . \count($arr) . '] = $carry;'; + $regular .= ' + for ($i = 1; $i < $length; ++$i) {'; + for ($j = 0; $j < \count($arr); $j++) { + $regular .= $j ? '$k++;' : '$k = $i;'; + $regular .= ' + $subtemp = $' . $output . '[$k] + $' . $input . '[$i] * ' . $arr[$j]; + $regular .= $j ? ' + $carry;' : ';'; + $regular .= '$carry = '; + $regular .= $class::BASE === 26 ? 'intval($subtemp / 0x4000000);' : '$subtemp >> 31;'; + $regular .= '$' . $output . '[$k] = '; + if ($class::BASE === 26) { + $regular .= '(int) ('; + } + $regular .= '$subtemp - ' . $class::BASE_FULL . ' * $carry'; + $regular .= $class::BASE === 26 ? ');' : ';'; + } + $regular .= '$' . $output . '[++$k] = $carry; $carry = 0;'; + $regular .= '}}'; + //if (count($arr) < 2 * self::KARATSUBA_CUTOFF) { + //} + return $regular; + } + /** + * Inline Addition + * + * @param string $x + * @param string $y + * @param string $result + * @param string $class + * @return string + */ + private static function generateInlineAdd($x, $y, $result, $class) + { + $code = ' + $length = max(count($' . $x . '), count($' . $y . ')); + $' . $result . ' = array_pad($' . $x . ', $length + 1, 0); + $_' . $y . ' = array_pad($' . $y . ', $length, 0); + $carry = 0; + for ($i = 0, $j = 1; $j < $length; $i+=2, $j+=2) { + $sum = ($' . $result . '[$j] + $_' . $y . '[$j]) * ' . $class::BASE_FULL . ' + + $' . $result . '[$i] + $_' . $y . '[$i] + + $carry; + $carry = $sum >= ' . self::float2string($class::MAX_DIGIT2) . '; + $sum = $carry ? $sum - ' . self::float2string($class::MAX_DIGIT2) . ' : $sum;'; + $code .= $class::BASE === 26 ? '$upper = intval($sum / 0x4000000); $' . $result . '[$i] = (int) ($sum - ' . $class::BASE_FULL . ' * $upper);' : '$upper = $sum >> 31; $' . $result . '[$i] = $sum - ' . $class::BASE_FULL . ' * $upper;'; + $code .= ' + $' . $result . '[$j] = $upper; + } + if ($j == $length) { + $sum = $' . $result . '[$i] + $_' . $y . '[$i] + $carry; + $carry = $sum >= ' . self::float2string($class::BASE_FULL) . '; + $' . $result . '[$i] = $carry ? $sum - ' . self::float2string($class::BASE_FULL) . ' : $sum; + ++$i; + } + if ($carry) { + for (; $' . $result . '[$i] == ' . $class::MAX_DIGIT . '; ++$i) { + $' . $result . '[$i] = 0; + } + ++$' . $result . '[$i]; + }'; + $code .= self::generateInlineTrim($result); + return $code; + } + /** + * Inline Subtraction 2 + * + * For when $known is more digits than $unknown. This is the harder use case to optimize for. + * + * @param string $known + * @param string $unknown + * @param string $result + * @param string $class + * @return string + */ + private static function generateInlineSubtract2($known, $unknown, $result, $class) + { + $code = ' + $' . $result . ' = $' . $known . '; + $carry = 0; + $size = count($' . $unknown . '); + for ($i = 0, $j = 1; $j < $size; $i+= 2, $j+= 2) { + $sum = ($' . $known . '[$j] - $' . $unknown . '[$j]) * ' . $class::BASE_FULL . ' + $' . $known . '[$i] + - $' . $unknown . '[$i] + - $carry; + $carry = $sum < 0; + if ($carry) { + $sum+= ' . self::float2string($class::MAX_DIGIT2) . '; + } + $subtemp = '; + $code .= $class::BASE === 26 ? 'intval($sum / 0x4000000);' : '$sum >> 31;'; + $code .= '$' . $result . '[$i] = '; + if ($class::BASE === 26) { + $code .= '(int) ('; + } + $code .= '$sum - ' . $class::BASE_FULL . ' * $subtemp'; + if ($class::BASE === 26) { + $code .= ')'; + } + $code .= '; + $' . $result . '[$j] = $subtemp; + } + if ($j == $size) { + $sum = $' . $known . '[$i] - $' . $unknown . '[$i] - $carry; + $carry = $sum < 0; + $' . $result . '[$i] = $carry ? $sum + ' . $class::BASE_FULL . ' : $sum; + ++$i; + } + + if ($carry) { + for (; !$' . $result . '[$i]; ++$i) { + $' . $result . '[$i] = ' . $class::MAX_DIGIT . '; + } + --$' . $result . '[$i]; + }'; + $code .= self::generateInlineTrim($result); + return $code; + } + /** + * Inline Subtraction 1 + * + * For when $unknown is more digits than $known. This is the easier use case to optimize for. + * + * @param string $unknown + * @param array $known + * @param string $result + * @param string $class + * @return string + */ + private static function generateInlineSubtract1($unknown, array $known, $result, $class) + { + $code = '$' . $result . ' = $' . $unknown . ';'; + for ($i = 0, $j = 1; $j < \count($known); $i += 2, $j += 2) { + $code .= '$sum = $' . $unknown . '[' . $j . '] * ' . $class::BASE_FULL . ' + $' . $unknown . '[' . $i . '] - '; + $code .= self::float2string($known[$j] * $class::BASE_FULL + $known[$i]); + if ($i != 0) { + $code .= ' - $carry'; + } + $code .= '; + if ($carry = $sum < 0) { + $sum+= ' . self::float2string($class::MAX_DIGIT2) . '; + } + $subtemp = '; + $code .= $class::BASE === 26 ? 'intval($sum / 0x4000000);' : '$sum >> 31;'; + $code .= ' + $' . $result . '[' . $i . '] = '; + if ($class::BASE === 26) { + $code .= ' (int) ('; + } + $code .= '$sum - ' . $class::BASE_FULL . ' * $subtemp'; + if ($class::BASE === 26) { + $code .= ')'; + } + $code .= '; + $' . $result . '[' . $j . '] = $subtemp;'; + } + $code .= '$i = ' . $i . ';'; + if ($j == \count($known)) { + $code .= ' + $sum = $' . $unknown . '[' . $i . '] - ' . $known[$i] . ' - $carry; + $carry = $sum < 0; + $' . $result . '[' . $i . '] = $carry ? $sum + ' . $class::BASE_FULL . ' : $sum; + ++$i;'; + } + $code .= ' + if ($carry) { + for (; !$' . $result . '[$i]; ++$i) { + $' . $result . '[$i] = ' . $class::MAX_DIGIT . '; + } + --$' . $result . '[$i]; + }'; + $code .= self::generateInlineTrim($result); + return $code; + } + /** + * Inline Comparison + * + * If $unknown >= $known then loop + * + * @param array $known + * @param string $unknown + * @param string $subcode + * @return string + */ + private static function generateInlineCompare(array $known, $unknown, $subcode) + { + $uniqid = \uniqid(); + $code = 'loop_' . $uniqid . ': + $clength = count($' . $unknown . '); + switch (true) { + case $clength < ' . \count($known) . ': + goto end_' . $uniqid . '; + case $clength > ' . \count($known) . ':'; + for ($i = \count($known) - 1; $i >= 0; $i--) { + $code .= ' + case $' . $unknown . '[' . $i . '] > ' . $known[$i] . ': + goto subcode_' . $uniqid . '; + case $' . $unknown . '[' . $i . '] < ' . $known[$i] . ': + goto end_' . $uniqid . ';'; + } + $code .= ' + default: + // do subcode + } + + subcode_' . $uniqid . ':' . $subcode . ' + goto loop_' . $uniqid . '; + + end_' . $uniqid . ':'; + return $code; + } + /** + * Convert a float to a string + * + * If you do echo floatval(pow(2, 52)) you'll get 4.6116860184274E+18. It /can/ be displayed without a loss of + * precision but displayed in this way there will be precision loss, hence the need for this method. + * + * @param int|float $num + * @return string + */ + private static function float2string($num) + { + if (!\is_float($num)) { + return (string) $num; + } + if ($num < 0) { + return '-' . self::float2string(\abs($num)); + } + $temp = ''; + while ($num) { + $temp = \fmod($num, 10) . $temp; + $num = \floor($num / 10); + } + return $temp; + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/PHP/Reductions/Montgomery.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/PHP/Reductions/Montgomery.php new file mode 100644 index 0000000..5300ba0 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/PHP/Reductions/Montgomery.php @@ -0,0 +1,113 @@ + + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://pear.php.net/package/Math_BigInteger + */ +namespace FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\PHP\Reductions; + +use FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\PHP\Montgomery as Progenitor; +/** + * PHP Montgomery Modular Exponentiation Engine + * + * @author Jim Wigginton + */ +abstract class Montgomery extends \FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\PHP\Montgomery +{ + /** + * Prepare a number for use in Montgomery Modular Reductions + * + * @param array $x + * @param array $n + * @param string $class + * @return array + */ + protected static function prepareReduce(array $x, array $n, $class) + { + $lhs = new $class(); + $lhs->value = \array_merge(self::array_repeat(0, \count($n)), $x); + $rhs = new $class(); + $rhs->value = $n; + list(, $temp) = $lhs->divide($rhs); + return $temp->value; + } + /** + * Montgomery Multiply + * + * Interleaves the montgomery reduction and long multiplication algorithms together as described in + * {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf#page=13 HAC 14.36} + * + * @param array $x + * @param array $n + * @param string $class + * @return array + */ + protected static function reduce(array $x, array $n, $class) + { + static $cache = [self::VARIABLE => [], self::DATA => []]; + if (($key = \array_search($n, $cache[self::VARIABLE])) === \false) { + $key = \count($cache[self::VARIABLE]); + $cache[self::VARIABLE][] = $x; + $cache[self::DATA][] = self::modInverse67108864($n, $class); + } + $k = \count($n); + $result = [self::VALUE => $x]; + for ($i = 0; $i < $k; ++$i) { + $temp = $result[self::VALUE][$i] * $cache[self::DATA][$key]; + $temp = $temp - $class::BASE_FULL * ($class::BASE === 26 ? \intval($temp / 0x4000000) : $temp >> 31); + $temp = $class::regularMultiply([$temp], $n); + $temp = \array_merge(self::array_repeat(0, $i), $temp); + $result = $class::addHelper($result[self::VALUE], \false, $temp, \false); + } + $result[self::VALUE] = \array_slice($result[self::VALUE], $k); + if (self::compareHelper($result, \false, $n, \false) >= 0) { + $result = $class::subtractHelper($result[self::VALUE], \false, $n, \false); + } + return $result[self::VALUE]; + } + /** + * Modular Inverse of a number mod 2**26 (eg. 67108864) + * + * Based off of the bnpInvDigit function implemented and justified in the following URL: + * + * {@link http://www-cs-students.stanford.edu/~tjw/jsbn/jsbn.js} + * + * The following URL provides more info: + * + * {@link http://groups.google.com/group/sci.crypt/msg/7a137205c1be7d85} + * + * As for why we do all the bitmasking... strange things can happen when converting from floats to ints. For + * instance, on some computers, var_dump((int) -4294967297) yields int(-1) and on others, it yields + * int(-2147483648). To avoid problems stemming from this, we use bitmasks to guarantee that ints aren't + * auto-converted to floats. The outermost bitmask is present because without it, there's no guarantee that + * the "residue" returned would be the so-called "common residue". We use fmod, in the last step, because the + * maximum possible $x is 26 bits and the maximum $result is 16 bits. Thus, we have to be able to handle up to + * 40 bits, which only 64-bit floating points will support. + * + * Thanks to Pedro Gimeno Fortea for input! + * + * @param array $x + * @param string $class + * @return int + */ + protected static function modInverse67108864(array $x, $class) + { + $x = -$x[0]; + $result = $x & 0x3; + // x**-1 mod 2**2 + $result = $result * (2 - $x * $result) & 0xf; + // x**-1 mod 2**4 + $result = $result * (2 - ($x & 0xff) * $result) & 0xff; + // x**-1 mod 2**8 + $result = $result * (2 - ($x & 0xffff) * $result & 0xffff) & 0xffff; + // x**-1 mod 2**16 + $result = $class::BASE == 26 ? \fmod($result * (2 - \fmod($x * $result, $class::BASE_FULL)), $class::BASE_FULL) : $result * (2 - $x * $result % $class::BASE_FULL) % $class::BASE_FULL; + return $result & $class::MAX_DIGIT; + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/PHP/Reductions/MontgomeryMult.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/PHP/Reductions/MontgomeryMult.php new file mode 100644 index 0000000..90d5dcb --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/PHP/Reductions/MontgomeryMult.php @@ -0,0 +1,68 @@ + + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://pear.php.net/package/Math_BigInteger + */ +namespace FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\PHP\Reductions; + +use FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\PHP; +/** + * PHP Montgomery Modular Exponentiation Engine with interleaved multiplication + * + * @author Jim Wigginton + */ +abstract class MontgomeryMult extends \FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\PHP\Reductions\Montgomery +{ + /** + * Montgomery Multiply + * + * Interleaves the montgomery reduction and long multiplication algorithms together as described in + * {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf#page=13 HAC 14.36} + * + * @see self::_prepMontgomery() + * @see self::_montgomery() + * @param array $x + * @param array $y + * @param array $m + * @param class-string $class + * @return array + */ + public static function multiplyReduce(array $x, array $y, array $m, $class) + { + // the following code, although not callable, can be run independently of the above code + // although the above code performed better in my benchmarks the following could might + // perform better under different circumstances. in lieu of deleting it it's just been + // made uncallable + static $cache = [self::VARIABLE => [], self::DATA => []]; + if (($key = \array_search($m, $cache[self::VARIABLE])) === \false) { + $key = \count($cache[self::VARIABLE]); + $cache[self::VARIABLE][] = $m; + $cache[self::DATA][] = self::modInverse67108864($m, $class); + } + $n = \max(\count($x), \count($y), \count($m)); + $x = \array_pad($x, $n, 0); + $y = \array_pad($y, $n, 0); + $m = \array_pad($m, $n, 0); + $a = [self::VALUE => self::array_repeat(0, $n + 1)]; + for ($i = 0; $i < $n; ++$i) { + $temp = $a[self::VALUE][0] + $x[$i] * $y[0]; + $temp = $temp - $class::BASE_FULL * ($class::BASE === 26 ? \intval($temp / 0x4000000) : $temp >> 31); + $temp = $temp * $cache[self::DATA][$key]; + $temp = $temp - $class::BASE_FULL * ($class::BASE === 26 ? \intval($temp / 0x4000000) : $temp >> 31); + $temp = $class::addHelper($class::regularMultiply([$x[$i]], $y), \false, $class::regularMultiply([$temp], $m), \false); + $a = $class::addHelper($a[self::VALUE], \false, $temp[self::VALUE], \false); + $a[self::VALUE] = \array_slice($a[self::VALUE], 1); + } + if (self::compareHelper($a[self::VALUE], \false, $m, \false) >= 0) { + $a = $class::subtractHelper($a[self::VALUE], \false, $m, \false); + } + return $a[self::VALUE]; + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/PHP/Reductions/PowerOfTwo.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/PHP/Reductions/PowerOfTwo.php new file mode 100644 index 0000000..58dd11c --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/PHP/Reductions/PowerOfTwo.php @@ -0,0 +1,54 @@ + + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://pear.php.net/package/Math_BigInteger + */ +namespace FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\PHP\Reductions; + +use FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\PHP\Base; +/** + * PHP Power Of Two Modular Exponentiation Engine + * + * @author Jim Wigginton + */ +abstract class PowerOfTwo extends \FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\PHP\Base +{ + /** + * Prepare a number for use in Montgomery Modular Reductions + * + * @param array $x + * @param array $n + * @param string $class + * @return array + */ + protected static function prepareReduce(array $x, array $n, $class) + { + return self::reduce($x, $n, $class); + } + /** + * Power Of Two Reduction + * + * @param array $x + * @param array $n + * @param string $class + * @return array + */ + protected static function reduce(array $x, array $n, $class) + { + $lhs = new $class(); + $lhs->value = $x; + $rhs = new $class(); + $rhs->value = $n; + $temp = new $class(); + $temp->value = [1]; + $result = $lhs->bitwise_and($rhs->subtract($temp)); + return $result->value; + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/PHP32.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/PHP32.php new file mode 100644 index 0000000..b74c42e --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/PHP32.php @@ -0,0 +1,341 @@ + + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://pear.php.net/package/Math_BigInteger + */ +namespace FluentSmtpLib\phpseclib3\Math\BigInteger\Engines; + +/** + * Pure-PHP 32-bit Engine. + * + * Uses 64-bit floats if int size is 4 bits + * + * @author Jim Wigginton + */ +class PHP32 extends \FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\PHP +{ + // Constants used by PHP.php + const BASE = 26; + const BASE_FULL = 0x4000000; + const MAX_DIGIT = 0x3ffffff; + const MSB = 0x2000000; + /** + * MAX10 in greatest MAX10LEN satisfying + * MAX10 = 10**MAX10LEN <= 2**BASE. + */ + const MAX10 = 10000000; + /** + * MAX10LEN in greatest MAX10LEN satisfying + * MAX10 = 10**MAX10LEN <= 2**BASE. + */ + const MAX10LEN = 7; + const MAX_DIGIT2 = 4503599627370496; + /** + * Initialize a PHP32 BigInteger Engine instance + * + * @param int $base + * @see parent::initialize() + */ + protected function initialize($base) + { + if ($base != 256 && $base != -256) { + return parent::initialize($base); + } + $val = $this->value; + $this->value = []; + $vals =& $this->value; + $i = \strlen($val); + if (!$i) { + return; + } + while (\true) { + $i -= 4; + if ($i < 0) { + if ($i == -4) { + break; + } + $val = \substr($val, 0, 4 + $i); + $val = \str_pad($val, 4, "\x00", \STR_PAD_LEFT); + if ($val == "\x00\x00\x00\x00") { + break; + } + $i = 0; + } + list(, $digit) = \unpack('N', \substr($val, $i, 4)); + if ($digit < 0) { + $digit += 0xffffffff + 1; + } + $step = \count($vals) & 3; + if ($step) { + $digit = (int) \floor($digit / \pow(2, 2 * $step)); + } + if ($step != 3) { + $digit = (int) \fmod($digit, static::BASE_FULL); + $i++; + } + $vals[] = $digit; + } + while (\end($vals) === 0) { + \array_pop($vals); + } + \reset($vals); + } + /** + * Test for engine validity + * + * @see parent::__construct() + * @return bool + */ + public static function isValidEngine() + { + return \PHP_INT_SIZE >= 4 && !self::testJITOnWindows(); + } + /** + * Adds two BigIntegers. + * + * @param PHP32 $y + * @return PHP32 + */ + public function add(\FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\PHP32 $y) + { + $temp = self::addHelper($this->value, $this->is_negative, $y->value, $y->is_negative); + return $this->convertToObj($temp); + } + /** + * Subtracts two BigIntegers. + * + * @param PHP32 $y + * @return PHP32 + */ + public function subtract(\FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\PHP32 $y) + { + $temp = self::subtractHelper($this->value, $this->is_negative, $y->value, $y->is_negative); + return $this->convertToObj($temp); + } + /** + * Multiplies two BigIntegers. + * + * @param PHP32 $y + * @return PHP32 + */ + public function multiply(\FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\PHP32 $y) + { + $temp = self::multiplyHelper($this->value, $this->is_negative, $y->value, $y->is_negative); + return $this->convertToObj($temp); + } + /** + * Divides two BigIntegers. + * + * Returns an array whose first element contains the quotient and whose second element contains the + * "common residue". If the remainder would be positive, the "common residue" and the remainder are the + * same. If the remainder would be negative, the "common residue" is equal to the sum of the remainder + * and the divisor (basically, the "common residue" is the first positive modulo). + * + * @param PHP32 $y + * @return array{PHP32, PHP32} + */ + public function divide(\FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\PHP32 $y) + { + return $this->divideHelper($y); + } + /** + * Calculates modular inverses. + * + * Say you have (30 mod 17 * x mod 17) mod 17 == 1. x can be found using modular inverses. + * @param PHP32 $n + * @return false|PHP32 + */ + public function modInverse(\FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\PHP32 $n) + { + return $this->modInverseHelper($n); + } + /** + * Calculates modular inverses. + * + * Say you have (30 mod 17 * x mod 17) mod 17 == 1. x can be found using modular inverses. + * @param PHP32 $n + * @return PHP32[] + */ + public function extendedGCD(\FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\PHP32 $n) + { + return $this->extendedGCDHelper($n); + } + /** + * Calculates the greatest common divisor + * + * Say you have 693 and 609. The GCD is 21. + * + * @param PHP32 $n + * @return PHP32 + */ + public function gcd(\FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\PHP32 $n) + { + return $this->extendedGCD($n)['gcd']; + } + /** + * Logical And + * + * @param PHP32 $x + * @return PHP32 + */ + public function bitwise_and(\FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\PHP32 $x) + { + return $this->bitwiseAndHelper($x); + } + /** + * Logical Or + * + * @param PHP32 $x + * @return PHP32 + */ + public function bitwise_or(\FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\PHP32 $x) + { + return $this->bitwiseOrHelper($x); + } + /** + * Logical Exclusive Or + * + * @param PHP32 $x + * @return PHP32 + */ + public function bitwise_xor(\FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\PHP32 $x) + { + return $this->bitwiseXorHelper($x); + } + /** + * Compares two numbers. + * + * Although one might think !$x->compare($y) means $x != $y, it, in fact, means the opposite. The reason for this is + * demonstrated thusly: + * + * $x > $y: $x->compare($y) > 0 + * $x < $y: $x->compare($y) < 0 + * $x == $y: $x->compare($y) == 0 + * + * Note how the same comparison operator is used. If you want to test for equality, use $x->equals($y). + * + * {@internal Could return $this->subtract($x), but that's not as fast as what we do do.} + * + * @param PHP32 $y + * @return int in case < 0 if $this is less than $y; > 0 if $this is greater than $y, and 0 if they are equal. + * @see self::equals() + */ + public function compare(\FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\PHP32 $y) + { + return $this->compareHelper($this->value, $this->is_negative, $y->value, $y->is_negative); + } + /** + * Tests the equality of two numbers. + * + * If you need to see if one number is greater than or less than another number, use BigInteger::compare() + * + * @param PHP32 $x + * @return bool + */ + public function equals(\FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\PHP32 $x) + { + return $this->value === $x->value && $this->is_negative == $x->is_negative; + } + /** + * Performs modular exponentiation. + * + * @param PHP32 $e + * @param PHP32 $n + * @return PHP32 + */ + public function modPow(\FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\PHP32 $e, \FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\PHP32 $n) + { + return $this->powModOuter($e, $n); + } + /** + * Performs modular exponentiation. + * + * Alias for modPow(). + * + * @param PHP32 $e + * @param PHP32 $n + * @return PHP32 + */ + public function powMod(\FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\PHP32 $e, \FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\PHP32 $n) + { + return $this->powModOuter($e, $n); + } + /** + * Generate a random prime number between a range + * + * If there's not a prime within the given range, false will be returned. + * + * @param PHP32 $min + * @param PHP32 $max + * @return false|PHP32 + */ + public static function randomRangePrime(\FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\PHP32 $min, \FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\PHP32 $max) + { + return self::randomRangePrimeOuter($min, $max); + } + /** + * Generate a random number between a range + * + * Returns a random number between $min and $max where $min and $max + * can be defined using one of the two methods: + * + * BigInteger::randomRange($min, $max) + * BigInteger::randomRange($max, $min) + * + * @param PHP32 $min + * @param PHP32 $max + * @return PHP32 + */ + public static function randomRange(\FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\PHP32 $min, \FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\PHP32 $max) + { + return self::randomRangeHelper($min, $max); + } + /** + * Performs exponentiation. + * + * @param PHP32 $n + * @return PHP32 + */ + public function pow(\FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\PHP32 $n) + { + return $this->powHelper($n); + } + /** + * Return the minimum BigInteger between an arbitrary number of BigIntegers. + * + * @param PHP32 ...$nums + * @return PHP32 + */ + public static function min(\FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\PHP32 ...$nums) + { + return self::minHelper($nums); + } + /** + * Return the maximum BigInteger between an arbitrary number of BigIntegers. + * + * @param PHP32 ...$nums + * @return PHP32 + */ + public static function max(\FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\PHP32 ...$nums) + { + return self::maxHelper($nums); + } + /** + * Tests BigInteger to see if it is between two integers, inclusive + * + * @param PHP32 $min + * @param PHP32 $max + * @return bool + */ + public function between(\FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\PHP32 $min, \FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\PHP32 $max) + { + return $this->compare($min) >= 0 && $this->compare($max) <= 0; + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/PHP64.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/PHP64.php new file mode 100644 index 0000000..8270339 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/PHP64.php @@ -0,0 +1,342 @@ + + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://pear.php.net/package/Math_BigInteger + */ +namespace FluentSmtpLib\phpseclib3\Math\BigInteger\Engines; + +/** + * Pure-PHP 64-bit Engine. + * + * Uses 64-bit integers if int size is 8 bits + * + * @author Jim Wigginton + */ +class PHP64 extends \FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\PHP +{ + // Constants used by PHP.php + const BASE = 31; + const BASE_FULL = 0x80000000; + const MAX_DIGIT = 0x7fffffff; + const MSB = 0x40000000; + /** + * MAX10 in greatest MAX10LEN satisfying + * MAX10 = 10**MAX10LEN <= 2**BASE. + */ + const MAX10 = 1000000000; + /** + * MAX10LEN in greatest MAX10LEN satisfying + * MAX10 = 10**MAX10LEN <= 2**BASE. + */ + const MAX10LEN = 9; + const MAX_DIGIT2 = 4611686018427387904; + /** + * Initialize a PHP64 BigInteger Engine instance + * + * @param int $base + * @see parent::initialize() + */ + protected function initialize($base) + { + if ($base != 256 && $base != -256) { + return parent::initialize($base); + } + $val = $this->value; + $this->value = []; + $vals =& $this->value; + $i = \strlen($val); + if (!$i) { + return; + } + while (\true) { + $i -= 4; + if ($i < 0) { + if ($i == -4) { + break; + } + $val = \substr($val, 0, 4 + $i); + $val = \str_pad($val, 4, "\x00", \STR_PAD_LEFT); + if ($val == "\x00\x00\x00\x00") { + break; + } + $i = 0; + } + list(, $digit) = \unpack('N', \substr($val, $i, 4)); + $step = \count($vals) & 7; + if (!$step) { + $digit &= static::MAX_DIGIT; + $i++; + } else { + $shift = 8 - $step; + $digit >>= $shift; + $shift = 32 - $shift; + $digit &= (1 << $shift) - 1; + $temp = $i > 0 ? \ord($val[$i - 1]) : 0; + $digit |= $temp << $shift & 0x7f000000; + } + $vals[] = $digit; + } + while (\end($vals) === 0) { + \array_pop($vals); + } + \reset($vals); + } + /** + * Test for engine validity + * + * @see parent::__construct() + * @return bool + */ + public static function isValidEngine() + { + return \PHP_INT_SIZE >= 8 && !self::testJITOnWindows(); + } + /** + * Adds two BigIntegers. + * + * @param PHP64 $y + * @return PHP64 + */ + public function add(\FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\PHP64 $y) + { + $temp = self::addHelper($this->value, $this->is_negative, $y->value, $y->is_negative); + return $this->convertToObj($temp); + } + /** + * Subtracts two BigIntegers. + * + * @param PHP64 $y + * @return PHP64 + */ + public function subtract(\FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\PHP64 $y) + { + $temp = self::subtractHelper($this->value, $this->is_negative, $y->value, $y->is_negative); + return $this->convertToObj($temp); + } + /** + * Multiplies two BigIntegers. + * + * @param PHP64 $y + * @return PHP64 + */ + public function multiply(\FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\PHP64 $y) + { + $temp = self::multiplyHelper($this->value, $this->is_negative, $y->value, $y->is_negative); + return $this->convertToObj($temp); + } + /** + * Divides two BigIntegers. + * + * Returns an array whose first element contains the quotient and whose second element contains the + * "common residue". If the remainder would be positive, the "common residue" and the remainder are the + * same. If the remainder would be negative, the "common residue" is equal to the sum of the remainder + * and the divisor (basically, the "common residue" is the first positive modulo). + * + * @param PHP64 $y + * @return array{PHP64, PHP64} + */ + public function divide(\FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\PHP64 $y) + { + return $this->divideHelper($y); + } + /** + * Calculates modular inverses. + * + * Say you have (30 mod 17 * x mod 17) mod 17 == 1. x can be found using modular inverses. + * @param PHP64 $n + * @return false|PHP64 + */ + public function modInverse(\FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\PHP64 $n) + { + return $this->modInverseHelper($n); + } + /** + * Calculates modular inverses. + * + * Say you have (30 mod 17 * x mod 17) mod 17 == 1. x can be found using modular inverses. + * @param PHP64 $n + * @return PHP64[] + */ + public function extendedGCD(\FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\PHP64 $n) + { + return $this->extendedGCDHelper($n); + } + /** + * Calculates the greatest common divisor + * + * Say you have 693 and 609. The GCD is 21. + * + * @param PHP64 $n + * @return PHP64 + */ + public function gcd(\FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\PHP64 $n) + { + return $this->extendedGCD($n)['gcd']; + } + /** + * Logical And + * + * @param PHP64 $x + * @return PHP64 + */ + public function bitwise_and(\FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\PHP64 $x) + { + return $this->bitwiseAndHelper($x); + } + /** + * Logical Or + * + * @param PHP64 $x + * @return PHP64 + */ + public function bitwise_or(\FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\PHP64 $x) + { + return $this->bitwiseOrHelper($x); + } + /** + * Logical Exclusive Or + * + * @param PHP64 $x + * @return PHP64 + */ + public function bitwise_xor(\FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\PHP64 $x) + { + return $this->bitwiseXorHelper($x); + } + /** + * Compares two numbers. + * + * Although one might think !$x->compare($y) means $x != $y, it, in fact, means the opposite. The reason for this is + * demonstrated thusly: + * + * $x > $y: $x->compare($y) > 0 + * $x < $y: $x->compare($y) < 0 + * $x == $y: $x->compare($y) == 0 + * + * Note how the same comparison operator is used. If you want to test for equality, use $x->equals($y). + * + * {@internal Could return $this->subtract($x), but that's not as fast as what we do do.} + * + * @param PHP64 $y + * @return int in case < 0 if $this is less than $y; > 0 if $this is greater than $y, and 0 if they are equal. + * @see self::equals() + */ + public function compare(\FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\PHP64 $y) + { + return parent::compareHelper($this->value, $this->is_negative, $y->value, $y->is_negative); + } + /** + * Tests the equality of two numbers. + * + * If you need to see if one number is greater than or less than another number, use BigInteger::compare() + * + * @param PHP64 $x + * @return bool + */ + public function equals(\FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\PHP64 $x) + { + return $this->value === $x->value && $this->is_negative == $x->is_negative; + } + /** + * Performs modular exponentiation. + * + * @param PHP64 $e + * @param PHP64 $n + * @return PHP64 + */ + public function modPow(\FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\PHP64 $e, \FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\PHP64 $n) + { + return $this->powModOuter($e, $n); + } + /** + * Performs modular exponentiation. + * + * Alias for modPow(). + * + * @param PHP64 $e + * @param PHP64 $n + * @return PHP64|false + */ + public function powMod(\FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\PHP64 $e, \FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\PHP64 $n) + { + return $this->powModOuter($e, $n); + } + /** + * Generate a random prime number between a range + * + * If there's not a prime within the given range, false will be returned. + * + * @param PHP64 $min + * @param PHP64 $max + * @return false|PHP64 + */ + public static function randomRangePrime(\FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\PHP64 $min, \FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\PHP64 $max) + { + return self::randomRangePrimeOuter($min, $max); + } + /** + * Generate a random number between a range + * + * Returns a random number between $min and $max where $min and $max + * can be defined using one of the two methods: + * + * BigInteger::randomRange($min, $max) + * BigInteger::randomRange($max, $min) + * + * @param PHP64 $min + * @param PHP64 $max + * @return PHP64 + */ + public static function randomRange(\FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\PHP64 $min, \FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\PHP64 $max) + { + return self::randomRangeHelper($min, $max); + } + /** + * Performs exponentiation. + * + * @param PHP64 $n + * @return PHP64 + */ + public function pow(\FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\PHP64 $n) + { + return $this->powHelper($n); + } + /** + * Return the minimum BigInteger between an arbitrary number of BigIntegers. + * + * @param PHP64 ...$nums + * @return PHP64 + */ + public static function min(\FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\PHP64 ...$nums) + { + return self::minHelper($nums); + } + /** + * Return the maximum BigInteger between an arbitrary number of BigIntegers. + * + * @param PHP64 ...$nums + * @return PHP64 + */ + public static function max(\FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\PHP64 ...$nums) + { + return self::maxHelper($nums); + } + /** + * Tests BigInteger to see if it is between two integers, inclusive + * + * @param PHP64 $min + * @param PHP64 $max + * @return bool + */ + public function between(\FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\PHP64 $min, \FluentSmtpLib\phpseclib3\Math\BigInteger\Engines\PHP64 $max) + { + return $this->compare($min) >= 0 && $this->compare($max) <= 0; + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Math/BinaryField.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Math/BinaryField.php new file mode 100644 index 0000000..888433d --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Math/BinaryField.php @@ -0,0 +1,183 @@ + + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + */ +namespace FluentSmtpLib\phpseclib3\Math; + +use FluentSmtpLib\phpseclib3\Common\Functions\Strings; +use FluentSmtpLib\phpseclib3\Math\BinaryField\Integer; +use FluentSmtpLib\phpseclib3\Math\Common\FiniteField; +/** + * Binary Finite Fields + * + * @author Jim Wigginton + */ +class BinaryField extends \FluentSmtpLib\phpseclib3\Math\Common\FiniteField +{ + /** + * Instance Counter + * + * @var int + */ + private static $instanceCounter = 0; + /** + * Keeps track of current instance + * + * @var int + */ + protected $instanceID; + /** @var BigInteger */ + private $randomMax; + /** + * Default constructor + */ + public function __construct(...$indices) + { + $m = \array_shift($indices); + if ($m > 571) { + /* sect571r1 and sect571k1 are the largest binary curves that https://www.secg.org/sec2-v2.pdf defines + altho theoretically there may be legit reasons to use binary finite fields with larger degrees + imposing a limit on the maximum size is both reasonable and precedented. in particular, + http://tools.ietf.org/html/rfc4253#section-6.1 (The Secure Shell (SSH) Transport Layer Protocol) says + "implementations SHOULD check that the packet length is reasonable in order for the implementation to + avoid denial of service and/or buffer overflow attacks" */ + throw new \OutOfBoundsException('Degrees larger than 571 are not supported'); + } + $val = \str_repeat('0', $m) . '1'; + foreach ($indices as $index) { + $val[$index] = '1'; + } + $modulo = static::base2ToBase256(\strrev($val)); + $mStart = 2 * $m - 2; + $t = \ceil($m / 8); + $finalMask = \chr((1 << $m % 8) - 1); + if ($finalMask == "\x00") { + $finalMask = "\xff"; + } + $bitLen = $mStart + 1; + $pad = \ceil($bitLen / 8); + $h = $bitLen & 7; + $h = $h ? 8 - $h : 0; + $r = \rtrim(\substr($val, 0, -1), '0'); + $u = [static::base2ToBase256(\strrev($r))]; + for ($i = 1; $i < 8; $i++) { + $u[] = static::base2ToBase256(\strrev(\str_repeat('0', $i) . $r)); + } + // implements algorithm 2.40 (in section 2.3.5) in "Guide to Elliptic Curve Cryptography" + // with W = 8 + $reduce = function ($c) use($u, $mStart, $m, $t, $finalMask, $pad, $h) { + $c = \str_pad($c, $pad, "\x00", \STR_PAD_LEFT); + for ($i = $mStart; $i >= $m;) { + $g = $h >> 3; + $mask = $h & 7; + $mask = $mask ? 1 << 7 - $mask : 0x80; + for (; $mask > 0; $mask >>= 1, $i--, $h++) { + if (\ord($c[$g]) & $mask) { + $temp = $i - $m; + $j = $temp >> 3; + $k = $temp & 7; + $t1 = $j ? \substr($c, 0, -$j) : $c; + $length = \strlen($t1); + if ($length) { + $t2 = \str_pad($u[$k], $length, "\x00", \STR_PAD_LEFT); + $temp = $t1 ^ $t2; + $c = $j ? \substr_replace($c, $temp, 0, $length) : $temp; + } + } + } + } + $c = \substr($c, -$t); + if (\strlen($c) == $t) { + $c[0] = $c[0] & $finalMask; + } + return \ltrim($c, "\x00"); + }; + $this->instanceID = self::$instanceCounter++; + \FluentSmtpLib\phpseclib3\Math\BinaryField\Integer::setModulo($this->instanceID, $modulo); + \FluentSmtpLib\phpseclib3\Math\BinaryField\Integer::setRecurringModuloFunction($this->instanceID, $reduce); + $this->randomMax = new \FluentSmtpLib\phpseclib3\Math\BigInteger($modulo, 2); + } + /** + * Returns an instance of a dynamically generated PrimeFieldInteger class + * + * @param string $num + * @return Integer + */ + public function newInteger($num) + { + return new \FluentSmtpLib\phpseclib3\Math\BinaryField\Integer($this->instanceID, $num instanceof \FluentSmtpLib\phpseclib3\Math\BigInteger ? $num->toBytes() : $num); + } + /** + * Returns an integer on the finite field between one and the prime modulo + * + * @return Integer + */ + public function randomInteger() + { + static $one; + if (!isset($one)) { + $one = new \FluentSmtpLib\phpseclib3\Math\BigInteger(1); + } + return new \FluentSmtpLib\phpseclib3\Math\BinaryField\Integer($this->instanceID, \FluentSmtpLib\phpseclib3\Math\BigInteger::randomRange($one, $this->randomMax)->toBytes()); + } + /** + * Returns the length of the modulo in bytes + * + * @return int + */ + public function getLengthInBytes() + { + return \strlen(\FluentSmtpLib\phpseclib3\Math\BinaryField\Integer::getModulo($this->instanceID)); + } + /** + * Returns the length of the modulo in bits + * + * @return int + */ + public function getLength() + { + return \strlen(\FluentSmtpLib\phpseclib3\Math\BinaryField\Integer::getModulo($this->instanceID)) << 3; + } + /** + * Converts a base-2 string to a base-256 string + * + * @param string $x + * @param int|null $size + * @return string + */ + public static function base2ToBase256($x, $size = null) + { + $str = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::bits2bin($x); + $pad = \strlen($x) >> 3; + if (\strlen($x) & 3) { + $pad++; + } + $str = \str_pad($str, $pad, "\x00", \STR_PAD_LEFT); + if (isset($size)) { + $str = \str_pad($str, $size, "\x00", \STR_PAD_LEFT); + } + return $str; + } + /** + * Converts a base-256 string to a base-2 string + * + * @param string $x + * @return string + */ + public static function base256ToBase2($x) + { + if (\function_exists('gmp_import')) { + return \gmp_strval(\gmp_import($x), 2); + } + return \FluentSmtpLib\phpseclib3\Common\Functions\Strings::bin2bits($x); + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Math/BinaryField/Integer.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Math/BinaryField/Integer.php new file mode 100644 index 0000000..57ce65a --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Math/BinaryField/Integer.php @@ -0,0 +1,442 @@ + + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + */ +namespace FluentSmtpLib\phpseclib3\Math\BinaryField; + +use FluentSmtpLib\phpseclib3\Common\Functions\Strings; +use FluentSmtpLib\phpseclib3\Math\BigInteger; +use FluentSmtpLib\phpseclib3\Math\BinaryField; +use FluentSmtpLib\phpseclib3\Math\Common\FiniteField\Integer as Base; +/** + * Binary Finite Fields + * + * @author Jim Wigginton + */ +class Integer extends \FluentSmtpLib\phpseclib3\Math\Common\FiniteField\Integer +{ + /** + * Holds the BinaryField's value + * + * @var string + */ + protected $value; + /** + * Keeps track of current instance + * + * @var int + */ + protected $instanceID; + /** + * Holds the PrimeField's modulo + * + * @var array + */ + protected static $modulo; + /** + * Holds a pre-generated function to perform modulo reductions + * + * @var callable[] + */ + protected static $reduce; + /** + * Default constructor + */ + public function __construct($instanceID, $num = '') + { + $this->instanceID = $instanceID; + if (!\strlen($num)) { + $this->value = ''; + } else { + $reduce = static::$reduce[$instanceID]; + $this->value = $reduce($num); + } + } + /** + * Set the modulo for a given instance + * @param int $instanceID + * @param string $modulo + */ + public static function setModulo($instanceID, $modulo) + { + static::$modulo[$instanceID] = $modulo; + } + /** + * Set the modulo for a given instance + */ + public static function setRecurringModuloFunction($instanceID, callable $function) + { + static::$reduce[$instanceID] = $function; + } + /** + * Tests a parameter to see if it's of the right instance + * + * Throws an exception if the incorrect class is being utilized + */ + private static function checkInstance(self $x, self $y) + { + if ($x->instanceID != $y->instanceID) { + throw new \UnexpectedValueException('The instances of the two BinaryField\\Integer objects do not match'); + } + } + /** + * Tests the equality of two numbers. + * + * @return bool + */ + public function equals(self $x) + { + static::checkInstance($this, $x); + return $this->value == $x->value; + } + /** + * Compares two numbers. + * + * @return int + */ + public function compare(self $x) + { + static::checkInstance($this, $x); + $a = $this->value; + $b = $x->value; + $length = \max(\strlen($a), \strlen($b)); + $a = \str_pad($a, $length, "\x00", \STR_PAD_LEFT); + $b = \str_pad($b, $length, "\x00", \STR_PAD_LEFT); + return \strcmp($a, $b); + } + /** + * Returns the degree of the polynomial + * + * @param string $x + * @return int + */ + private static function deg($x) + { + $x = \ltrim($x, "\x00"); + $xbit = \decbin(\ord($x[0])); + $xlen = $xbit == '0' ? 0 : \strlen($xbit); + $len = \strlen($x); + if (!$len) { + return -1; + } + return 8 * \strlen($x) - 9 + $xlen; + } + /** + * Perform polynomial division + * + * @return string[] + * @link https://en.wikipedia.org/wiki/Polynomial_greatest_common_divisor#Euclidean_division + */ + private static function polynomialDivide($x, $y) + { + // in wikipedia's description of the algorithm, lc() is the leading coefficient. over a binary field that's + // always going to be 1. + $q = \chr(0); + $d = static::deg($y); + $r = $x; + while (($degr = static::deg($r)) >= $d) { + $s = '1' . \str_repeat('0', $degr - $d); + $s = \FluentSmtpLib\phpseclib3\Math\BinaryField::base2ToBase256($s); + $length = \max(\strlen($s), \strlen($q)); + $q = !isset($q) ? $s : \str_pad($q, $length, "\x00", \STR_PAD_LEFT) ^ \str_pad($s, $length, "\x00", \STR_PAD_LEFT); + $s = static::polynomialMultiply($s, $y); + $length = \max(\strlen($r), \strlen($s)); + $r = \str_pad($r, $length, "\x00", \STR_PAD_LEFT) ^ \str_pad($s, $length, "\x00", \STR_PAD_LEFT); + } + return [\ltrim($q, "\x00"), \ltrim($r, "\x00")]; + } + /** + * Perform polynomial multiplation in the traditional way + * + * @return string + * @link https://en.wikipedia.org/wiki/Finite_field_arithmetic#Multiplication + */ + private static function regularPolynomialMultiply($x, $y) + { + $precomputed = [\ltrim($x, "\x00")]; + $x = \strrev(\FluentSmtpLib\phpseclib3\Math\BinaryField::base256ToBase2($x)); + $y = \strrev(\FluentSmtpLib\phpseclib3\Math\BinaryField::base256ToBase2($y)); + if (\strlen($x) == \strlen($y)) { + $length = \strlen($x); + } else { + $length = \max(\strlen($x), \strlen($y)); + $x = \str_pad($x, $length, '0'); + $y = \str_pad($y, $length, '0'); + } + $result = \str_repeat('0', 2 * $length - 1); + $result = \FluentSmtpLib\phpseclib3\Math\BinaryField::base2ToBase256($result); + $size = \strlen($result); + $x = \strrev($x); + // precompute left shift 1 through 7 + for ($i = 1; $i < 8; $i++) { + $precomputed[$i] = \FluentSmtpLib\phpseclib3\Math\BinaryField::base2ToBase256($x . \str_repeat('0', $i)); + } + for ($i = 0; $i < \strlen($y); $i++) { + if ($y[$i] == '1') { + $temp = $precomputed[$i & 7] . \str_repeat("\x00", $i >> 3); + $result ^= \str_pad($temp, $size, "\x00", \STR_PAD_LEFT); + } + } + return $result; + } + /** + * Perform polynomial multiplation + * + * Uses karatsuba multiplication to reduce x-bit multiplications to a series of 32-bit multiplications + * + * @return string + * @link https://en.wikipedia.org/wiki/Karatsuba_algorithm + */ + private static function polynomialMultiply($x, $y) + { + if (\strlen($x) == \strlen($y)) { + $length = \strlen($x); + } else { + $length = \max(\strlen($x), \strlen($y)); + $x = \str_pad($x, $length, "\x00", \STR_PAD_LEFT); + $y = \str_pad($y, $length, "\x00", \STR_PAD_LEFT); + } + switch (\true) { + case \PHP_INT_SIZE == 8 && $length <= 4: + return $length != 4 ? self::subMultiply(\str_pad($x, 4, "\x00", \STR_PAD_LEFT), \str_pad($y, 4, "\x00", \STR_PAD_LEFT)) : self::subMultiply($x, $y); + case \PHP_INT_SIZE == 4 || $length > 32: + return self::regularPolynomialMultiply($x, $y); + } + $m = $length >> 1; + $x1 = \substr($x, 0, -$m); + $x0 = \substr($x, -$m); + $y1 = \substr($y, 0, -$m); + $y0 = \substr($y, -$m); + $z2 = self::polynomialMultiply($x1, $y1); + $z0 = self::polynomialMultiply($x0, $y0); + $z1 = self::polynomialMultiply(self::subAdd2($x1, $x0), self::subAdd2($y1, $y0)); + $z1 = self::subAdd3($z1, $z2, $z0); + $xy = self::subAdd3($z2 . \str_repeat("\x00", 2 * $m), $z1 . \str_repeat("\x00", $m), $z0); + return \ltrim($xy, "\x00"); + } + /** + * Perform polynomial multiplication on 2x 32-bit numbers, returning + * a 64-bit number + * + * @param string $x + * @param string $y + * @return string + * @link https://www.bearssl.org/constanttime.html#ghash-for-gcm + */ + private static function subMultiply($x, $y) + { + $x = \unpack('N', $x)[1]; + $y = \unpack('N', $y)[1]; + $x0 = $x & 0x11111111; + $x1 = $x & 0x22222222; + $x2 = $x & 0x44444444; + $x3 = $x & 0x88888888; + $y0 = $y & 0x11111111; + $y1 = $y & 0x22222222; + $y2 = $y & 0x44444444; + $y3 = $y & 0x88888888; + $z0 = $x0 * $y0 ^ $x1 * $y3 ^ $x2 * $y2 ^ $x3 * $y1; + $z1 = $x0 * $y1 ^ $x1 * $y0 ^ $x2 * $y3 ^ $x3 * $y2; + $z2 = $x0 * $y2 ^ $x1 * $y1 ^ $x2 * $y0 ^ $x3 * $y3; + $z3 = $x0 * $y3 ^ $x1 * $y2 ^ $x2 * $y1 ^ $x3 * $y0; + $z0 &= 0x1111111111111111; + $z1 &= 0x2222222222222222; + $z2 &= 0x4444444444444444; + $z3 &= -8608480567731124088; + // 0x8888888888888888 gets interpreted as a float + $z = $z0 | $z1 | $z2 | $z3; + return \pack('J', $z); + } + /** + * Adds two numbers + * + * @param string $x + * @param string $y + * @return string + */ + private static function subAdd2($x, $y) + { + $length = \max(\strlen($x), \strlen($y)); + $x = \str_pad($x, $length, "\x00", \STR_PAD_LEFT); + $y = \str_pad($y, $length, "\x00", \STR_PAD_LEFT); + return $x ^ $y; + } + /** + * Adds three numbers + * + * @param string $x + * @param string $y + * @return string + */ + private static function subAdd3($x, $y, $z) + { + $length = \max(\strlen($x), \strlen($y), \strlen($z)); + $x = \str_pad($x, $length, "\x00", \STR_PAD_LEFT); + $y = \str_pad($y, $length, "\x00", \STR_PAD_LEFT); + $z = \str_pad($z, $length, "\x00", \STR_PAD_LEFT); + return $x ^ $y ^ $z; + } + /** + * Adds two BinaryFieldIntegers. + * + * @return static + */ + public function add(self $y) + { + static::checkInstance($this, $y); + $length = \strlen(static::$modulo[$this->instanceID]); + $x = \str_pad($this->value, $length, "\x00", \STR_PAD_LEFT); + $y = \str_pad($y->value, $length, "\x00", \STR_PAD_LEFT); + return new static($this->instanceID, $x ^ $y); + } + /** + * Subtracts two BinaryFieldIntegers. + * + * @return static + */ + public function subtract(self $x) + { + return $this->add($x); + } + /** + * Multiplies two BinaryFieldIntegers. + * + * @return static + */ + public function multiply(self $y) + { + static::checkInstance($this, $y); + return new static($this->instanceID, static::polynomialMultiply($this->value, $y->value)); + } + /** + * Returns the modular inverse of a BinaryFieldInteger + * + * @return static + */ + public function modInverse() + { + $remainder0 = static::$modulo[$this->instanceID]; + $remainder1 = $this->value; + if ($remainder1 == '') { + return new static($this->instanceID); + } + $aux0 = "\x00"; + $aux1 = "\x01"; + while ($remainder1 != "\x01") { + list($q, $r) = static::polynomialDivide($remainder0, $remainder1); + $remainder0 = $remainder1; + $remainder1 = $r; + // the auxiliary in row n is given by the sum of the auxiliary in + // row n-2 and the product of the quotient and the auxiliary in row + // n-1 + $temp = static::polynomialMultiply($aux1, $q); + $aux = \str_pad($aux0, \strlen($temp), "\x00", \STR_PAD_LEFT) ^ \str_pad($temp, \strlen($aux0), "\x00", \STR_PAD_LEFT); + $aux0 = $aux1; + $aux1 = $aux; + } + $temp = new static($this->instanceID); + $temp->value = \ltrim($aux1, "\x00"); + return $temp; + } + /** + * Divides two PrimeFieldIntegers. + * + * @return static + */ + public function divide(self $x) + { + static::checkInstance($this, $x); + $x = $x->modInverse(); + return $this->multiply($x); + } + /** + * Negate + * + * A negative number can be written as 0-12. With modulos, 0 is the same thing as the modulo + * so 0-12 is the same thing as modulo-12 + * + * @return object + */ + public function negate() + { + $x = \str_pad($this->value, \strlen(static::$modulo[$this->instanceID]), "\x00", \STR_PAD_LEFT); + return new static($this->instanceID, $x ^ static::$modulo[$this->instanceID]); + } + /** + * Returns the modulo + * + * @return string + */ + public static function getModulo($instanceID) + { + return static::$modulo[$instanceID]; + } + /** + * Converts an Integer to a byte string (eg. base-256). + * + * @return string + */ + public function toBytes() + { + return \str_pad($this->value, \strlen(static::$modulo[$this->instanceID]), "\x00", \STR_PAD_LEFT); + } + /** + * Converts an Integer to a hex string (eg. base-16). + * + * @return string + */ + public function toHex() + { + return \FluentSmtpLib\phpseclib3\Common\Functions\Strings::bin2hex($this->toBytes()); + } + /** + * Converts an Integer to a bit string (eg. base-2). + * + * @return string + */ + public function toBits() + { + //return str_pad(BinaryField::base256ToBase2($this->value), strlen(static::$modulo[$this->instanceID]), '0', STR_PAD_LEFT); + return \FluentSmtpLib\phpseclib3\Math\BinaryField::base256ToBase2($this->value); + } + /** + * Converts an Integer to a BigInteger + * + * @return string + */ + public function toBigInteger() + { + return new \FluentSmtpLib\phpseclib3\Math\BigInteger($this->value, 256); + } + /** + * __toString() magic method + * + */ + public function __toString() + { + return (string) $this->toBigInteger(); + } + /** + * __debugInfo() magic method + * + */ + public function __debugInfo() + { + return ['value' => $this->toHex()]; + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Math/Common/FiniteField.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Math/Common/FiniteField.php new file mode 100644 index 0000000..46a902e --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Math/Common/FiniteField.php @@ -0,0 +1,21 @@ + + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + */ +namespace FluentSmtpLib\phpseclib3\Math\Common; + +/** + * Finite Fields + * + * @author Jim Wigginton + */ +abstract class FiniteField +{ +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Math/Common/FiniteField/Integer.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Math/Common/FiniteField/Integer.php new file mode 100644 index 0000000..8d9f219 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Math/Common/FiniteField/Integer.php @@ -0,0 +1,42 @@ + + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + */ +namespace FluentSmtpLib\phpseclib3\Math\Common\FiniteField; + +/** + * Finite Field Integer + * + * @author Jim Wigginton + */ +abstract class Integer implements \JsonSerializable +{ + /** + * JSON Serialize + * + * Will be called, automatically, when json_encode() is called on a BigInteger object. + * + * PHP Serialize isn't supported because unserializing would require the factory be + * serialized as well and that just sounds like too much + * + * @return array{hex: string} + */ + #[\ReturnTypeWillChange] + public function jsonSerialize() + { + return ['hex' => $this->toHex(\true)]; + } + /** + * Converts an Integer to a hex string (eg. base-16). + * + * @return string + */ + public abstract function toHex(); +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Math/PrimeField.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Math/PrimeField.php new file mode 100644 index 0000000..89e2410 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Math/PrimeField.php @@ -0,0 +1,106 @@ + + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://pear.php.net/package/Math_BigInteger + */ +namespace FluentSmtpLib\phpseclib3\Math; + +use FluentSmtpLib\phpseclib3\Math\Common\FiniteField; +use FluentSmtpLib\phpseclib3\Math\PrimeField\Integer; +/** + * Prime Finite Fields + * + * @author Jim Wigginton + */ +class PrimeField extends \FluentSmtpLib\phpseclib3\Math\Common\FiniteField +{ + /** + * Instance Counter + * + * @var int + */ + private static $instanceCounter = 0; + /** + * Keeps track of current instance + * + * @var int + */ + protected $instanceID; + /** + * Default constructor + */ + public function __construct(\FluentSmtpLib\phpseclib3\Math\BigInteger $modulo) + { + if (!$modulo->isPrime()) { + throw new \UnexpectedValueException('PrimeField requires a prime number be passed to the constructor'); + } + $this->instanceID = self::$instanceCounter++; + \FluentSmtpLib\phpseclib3\Math\PrimeField\Integer::setModulo($this->instanceID, $modulo); + \FluentSmtpLib\phpseclib3\Math\PrimeField\Integer::setRecurringModuloFunction($this->instanceID, $modulo->createRecurringModuloFunction()); + } + /** + * Use a custom defined modular reduction function + * + * @return void + */ + public function setReduction(\Closure $func) + { + $this->reduce = $func->bindTo($this, $this); + } + /** + * Returns an instance of a dynamically generated PrimeFieldInteger class + * + * @return Integer + */ + public function newInteger(\FluentSmtpLib\phpseclib3\Math\BigInteger $num) + { + return new \FluentSmtpLib\phpseclib3\Math\PrimeField\Integer($this->instanceID, $num); + } + /** + * Returns an integer on the finite field between one and the prime modulo + * + * @return Integer + */ + public function randomInteger() + { + static $one; + if (!isset($one)) { + $one = new \FluentSmtpLib\phpseclib3\Math\BigInteger(1); + } + return new \FluentSmtpLib\phpseclib3\Math\PrimeField\Integer($this->instanceID, \FluentSmtpLib\phpseclib3\Math\BigInteger::randomRange($one, \FluentSmtpLib\phpseclib3\Math\PrimeField\Integer::getModulo($this->instanceID))); + } + /** + * Returns the length of the modulo in bytes + * + * @return int + */ + public function getLengthInBytes() + { + return \FluentSmtpLib\phpseclib3\Math\PrimeField\Integer::getModulo($this->instanceID)->getLengthInBytes(); + } + /** + * Returns the length of the modulo in bits + * + * @return int + */ + public function getLength() + { + return \FluentSmtpLib\phpseclib3\Math\PrimeField\Integer::getModulo($this->instanceID)->getLength(); + } + /** + * Destructor + */ + public function __destruct() + { + \FluentSmtpLib\phpseclib3\Math\PrimeField\Integer::cleanupCache($this->instanceID); + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Math/PrimeField/Integer.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Math/PrimeField/Integer.php new file mode 100644 index 0000000..e0de38c --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Math/PrimeField/Integer.php @@ -0,0 +1,371 @@ + + * @copyright 2017 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + */ +namespace FluentSmtpLib\phpseclib3\Math\PrimeField; + +use FluentSmtpLib\phpseclib3\Common\Functions\Strings; +use FluentSmtpLib\phpseclib3\Math\BigInteger; +use FluentSmtpLib\phpseclib3\Math\Common\FiniteField\Integer as Base; +/** + * Prime Finite Fields + * + * @author Jim Wigginton + */ +class Integer extends \FluentSmtpLib\phpseclib3\Math\Common\FiniteField\Integer +{ + /** + * Holds the PrimeField's value + * + * @var BigInteger + */ + protected $value; + /** + * Keeps track of current instance + * + * @var int + */ + protected $instanceID; + /** + * Holds the PrimeField's modulo + * + * @var array + */ + protected static $modulo; + /** + * Holds a pre-generated function to perform modulo reductions + * + * @var array + */ + protected static $reduce; + /** + * Zero + * + * @var BigInteger + */ + protected static $zero; + /** + * Default constructor + * + * @param int $instanceID + * @param BigInteger $num + */ + public function __construct($instanceID, $num = null) + { + $this->instanceID = $instanceID; + if (!isset($num)) { + $this->value = clone static::$zero[static::class]; + } else { + $reduce = static::$reduce[$instanceID]; + $this->value = $reduce($num); + } + } + /** + * Set the modulo for a given instance + * + * @param int $instanceID + * @return void + */ + public static function setModulo($instanceID, \FluentSmtpLib\phpseclib3\Math\BigInteger $modulo) + { + static::$modulo[$instanceID] = $modulo; + } + /** + * Set the modulo for a given instance + * + * @param int $instanceID + * @return void + */ + public static function setRecurringModuloFunction($instanceID, callable $function) + { + static::$reduce[$instanceID] = $function; + if (!isset(static::$zero[static::class])) { + static::$zero[static::class] = new \FluentSmtpLib\phpseclib3\Math\BigInteger(); + } + } + /** + * Delete the modulo for a given instance + */ + public static function cleanupCache($instanceID) + { + unset(static::$modulo[$instanceID]); + unset(static::$reduce[$instanceID]); + } + /** + * Returns the modulo + * + * @param int $instanceID + * @return BigInteger + */ + public static function getModulo($instanceID) + { + return static::$modulo[$instanceID]; + } + /** + * Tests a parameter to see if it's of the right instance + * + * Throws an exception if the incorrect class is being utilized + * + * @return void + */ + public static function checkInstance(self $x, self $y) + { + if ($x->instanceID != $y->instanceID) { + throw new \UnexpectedValueException('The instances of the two PrimeField\\Integer objects do not match'); + } + } + /** + * Tests the equality of two numbers. + * + * @return bool + */ + public function equals(self $x) + { + static::checkInstance($this, $x); + return $this->value->equals($x->value); + } + /** + * Compares two numbers. + * + * @return int + */ + public function compare(self $x) + { + static::checkInstance($this, $x); + return $this->value->compare($x->value); + } + /** + * Adds two PrimeFieldIntegers. + * + * @return static + */ + public function add(self $x) + { + static::checkInstance($this, $x); + $temp = new static($this->instanceID); + $temp->value = $this->value->add($x->value); + if ($temp->value->compare(static::$modulo[$this->instanceID]) >= 0) { + $temp->value = $temp->value->subtract(static::$modulo[$this->instanceID]); + } + return $temp; + } + /** + * Subtracts two PrimeFieldIntegers. + * + * @return static + */ + public function subtract(self $x) + { + static::checkInstance($this, $x); + $temp = new static($this->instanceID); + $temp->value = $this->value->subtract($x->value); + if ($temp->value->isNegative()) { + $temp->value = $temp->value->add(static::$modulo[$this->instanceID]); + } + return $temp; + } + /** + * Multiplies two PrimeFieldIntegers. + * + * @return static + */ + public function multiply(self $x) + { + static::checkInstance($this, $x); + return new static($this->instanceID, $this->value->multiply($x->value)); + } + /** + * Divides two PrimeFieldIntegers. + * + * @return static + */ + public function divide(self $x) + { + static::checkInstance($this, $x); + $denominator = $x->value->modInverse(static::$modulo[$this->instanceID]); + return new static($this->instanceID, $this->value->multiply($denominator)); + } + /** + * Performs power operation on a PrimeFieldInteger. + * + * @return static + */ + public function pow(\FluentSmtpLib\phpseclib3\Math\BigInteger $x) + { + $temp = new static($this->instanceID); + $temp->value = $this->value->powMod($x, static::$modulo[$this->instanceID]); + return $temp; + } + /** + * Calculates the square root + * + * @link https://en.wikipedia.org/wiki/Tonelli%E2%80%93Shanks_algorithm + * @return static|false + */ + public function squareRoot() + { + static $one, $two; + if (!isset($one)) { + $one = new \FluentSmtpLib\phpseclib3\Math\BigInteger(1); + $two = new \FluentSmtpLib\phpseclib3\Math\BigInteger(2); + } + $reduce = static::$reduce[$this->instanceID]; + $p_1 = static::$modulo[$this->instanceID]->subtract($one); + $q = clone $p_1; + $s = \FluentSmtpLib\phpseclib3\Math\BigInteger::scan1divide($q); + list($pow) = $p_1->divide($two); + for ($z = $one; !$z->equals(static::$modulo[$this->instanceID]); $z = $z->add($one)) { + $temp = $z->powMod($pow, static::$modulo[$this->instanceID]); + if ($temp->equals($p_1)) { + break; + } + } + $m = new \FluentSmtpLib\phpseclib3\Math\BigInteger($s); + $c = $z->powMod($q, static::$modulo[$this->instanceID]); + $t = $this->value->powMod($q, static::$modulo[$this->instanceID]); + list($temp) = $q->add($one)->divide($two); + $r = $this->value->powMod($temp, static::$modulo[$this->instanceID]); + while (!$t->equals($one)) { + for ($i = clone $one; $i->compare($m) < 0; $i = $i->add($one)) { + if ($t->powMod($two->pow($i), static::$modulo[$this->instanceID])->equals($one)) { + break; + } + } + if ($i->compare($m) == 0) { + return \false; + } + $b = $c->powMod($two->pow($m->subtract($i)->subtract($one)), static::$modulo[$this->instanceID]); + $m = $i; + $c = $reduce($b->multiply($b)); + $t = $reduce($t->multiply($c)); + $r = $reduce($r->multiply($b)); + } + return new static($this->instanceID, $r); + } + /** + * Is Odd? + * + * @return bool + */ + public function isOdd() + { + return $this->value->isOdd(); + } + /** + * Negate + * + * A negative number can be written as 0-12. With modulos, 0 is the same thing as the modulo + * so 0-12 is the same thing as modulo-12 + * + * @return static + */ + public function negate() + { + return new static($this->instanceID, static::$modulo[$this->instanceID]->subtract($this->value)); + } + /** + * Converts an Integer to a byte string (eg. base-256). + * + * @return string + */ + public function toBytes() + { + if (isset(static::$modulo[$this->instanceID])) { + $length = static::$modulo[$this->instanceID]->getLengthInBytes(); + return \str_pad($this->value->toBytes(), $length, "\x00", \STR_PAD_LEFT); + } + return $this->value->toBytes(); + } + /** + * Converts an Integer to a hex string (eg. base-16). + * + * @return string + */ + public function toHex() + { + return \FluentSmtpLib\phpseclib3\Common\Functions\Strings::bin2hex($this->toBytes()); + } + /** + * Converts an Integer to a bit string (eg. base-2). + * + * @return string + */ + public function toBits() + { + // return $this->value->toBits(); + static $length; + if (!isset($length)) { + $length = static::$modulo[$this->instanceID]->getLength(); + } + return \str_pad($this->value->toBits(), $length, '0', \STR_PAD_LEFT); + } + /** + * Returns the w-ary non-adjacent form (wNAF) + * + * @param int $w optional + * @return array + */ + public function getNAF($w = 1) + { + $w++; + $mask = new \FluentSmtpLib\phpseclib3\Math\BigInteger((1 << $w) - 1); + $sub = new \FluentSmtpLib\phpseclib3\Math\BigInteger(1 << $w); + //$sub = new BigInteger(1 << ($w - 1)); + $d = $this->toBigInteger(); + $d_i = []; + $i = 0; + while ($d->compare(static::$zero[static::class]) > 0) { + if ($d->isOdd()) { + // start mods + $bigInteger = $d->testBit($w - 1) ? $d->bitwise_and($mask)->subtract($sub) : $d->bitwise_and($mask); + // end mods + $d = $d->subtract($bigInteger); + $d_i[$i] = (int) $bigInteger->toString(); + } else { + $d_i[$i] = 0; + } + $shift = !$d->equals(static::$zero[static::class]) && $d->bitwise_and($mask)->equals(static::$zero[static::class]) ? $w : 1; + // $w or $w + 1? + $d = $d->bitwise_rightShift($shift); + while (--$shift > 0) { + $d_i[++$i] = 0; + } + $i++; + } + return $d_i; + } + /** + * Converts an Integer to a BigInteger + * + * @return BigInteger + */ + public function toBigInteger() + { + return clone $this->value; + } + /** + * __toString() magic method + * + * @return string + */ + public function __toString() + { + return (string) $this->value; + } + /** + * __debugInfo() magic method + * + * @return array + */ + public function __debugInfo() + { + return ['value' => $this->toHex()]; + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Net/SFTP.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Net/SFTP.php new file mode 100644 index 0000000..a5b3d96 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Net/SFTP.php @@ -0,0 +1,3177 @@ + + * login('username', 'password')) { + * exit('Login Failed'); + * } + * + * echo $sftp->pwd() . "\r\n"; + * $sftp->put('filename.ext', 'hello, world!'); + * print_r($sftp->nlist()); + * ?> + * + * + * @author Jim Wigginton + * @copyright 2009 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\Net; + +use FluentSmtpLib\phpseclib3\Common\Functions\Strings; +use FluentSmtpLib\phpseclib3\Exception\FileNotFoundException; +/** + * Pure-PHP implementations of SFTP. + * + * @author Jim Wigginton + */ +class SFTP extends \FluentSmtpLib\phpseclib3\Net\SSH2 +{ + /** + * SFTP channel constant + * + * \phpseclib3\Net\SSH2::exec() uses 0 and \phpseclib3\Net\SSH2::read() / \phpseclib3\Net\SSH2::write() use 1. + * + * @see \phpseclib3\Net\SSH2::send_channel_packet() + * @see \phpseclib3\Net\SSH2::get_channel_packet() + */ + const CHANNEL = 0x100; + /** + * Reads data from a local file. + * + * @see \phpseclib3\Net\SFTP::put() + */ + const SOURCE_LOCAL_FILE = 1; + /** + * Reads data from a string. + * + * @see \phpseclib3\Net\SFTP::put() + */ + // this value isn't really used anymore but i'm keeping it reserved for historical reasons + const SOURCE_STRING = 2; + /** + * Reads data from callback: + * function callback($length) returns string to proceed, null for EOF + * + * @see \phpseclib3\Net\SFTP::put() + */ + const SOURCE_CALLBACK = 16; + /** + * Resumes an upload + * + * @see \phpseclib3\Net\SFTP::put() + */ + const RESUME = 4; + /** + * Append a local file to an already existing remote file + * + * @see \phpseclib3\Net\SFTP::put() + */ + const RESUME_START = 8; + /** + * Packet Types + * + * @see self::__construct() + * @var array + * @access private + */ + private static $packet_types = []; + /** + * Status Codes + * + * @see self::__construct() + * @var array + * @access private + */ + private static $status_codes = []; + /** @var array */ + private static $attributes; + /** @var array */ + private static $open_flags; + /** @var array */ + private static $open_flags5; + /** @var array */ + private static $file_types; + /** + * The Request ID + * + * The request ID exists in the off chance that a packet is sent out-of-order. Of course, this library doesn't support + * concurrent actions, so it's somewhat academic, here. + * + * @var boolean + * @see self::_send_sftp_packet() + */ + private $use_request_id = \false; + /** + * The Packet Type + * + * The request ID exists in the off chance that a packet is sent out-of-order. Of course, this library doesn't support + * concurrent actions, so it's somewhat academic, here. + * + * @var int + * @see self::_get_sftp_packet() + */ + private $packet_type = -1; + /** + * Packet Buffer + * + * @var string + * @see self::_get_sftp_packet() + */ + private $packet_buffer = ''; + /** + * Extensions supported by the server + * + * @var array + * @see self::_initChannel() + */ + private $extensions = []; + /** + * Server SFTP version + * + * @var int + * @see self::_initChannel() + */ + private $version; + /** + * Default Server SFTP version + * + * @var int + * @see self::_initChannel() + */ + private $defaultVersion; + /** + * Preferred SFTP version + * + * @var int + * @see self::_initChannel() + */ + private $preferredVersion = 3; + /** + * Current working directory + * + * @var string|bool + * @see self::realpath() + * @see self::chdir() + */ + private $pwd = \false; + /** + * Packet Type Log + * + * @see self::getLog() + * @var array + */ + private $packet_type_log = []; + /** + * Packet Log + * + * @see self::getLog() + * @var array + */ + private $packet_log = []; + /** + * Real-time log file pointer + * + * @see self::_append_log() + * @var resource|closed-resource + */ + private $realtime_log_file; + /** + * Real-time log file size + * + * @see self::_append_log() + * @var int + */ + private $realtime_log_size; + /** + * Real-time log file wrap boolean + * + * @see self::_append_log() + * @var bool + */ + private $realtime_log_wrap; + /** + * Current log size + * + * Should never exceed self::LOG_MAX_SIZE + * + * @var int + */ + private $log_size; + /** + * Error information + * + * @see self::getSFTPErrors() + * @see self::getLastSFTPError() + * @var array + */ + private $sftp_errors = []; + /** + * Stat Cache + * + * Rather than always having to open a directory and close it immediately there after to see if a file is a directory + * we'll cache the results. + * + * @see self::_update_stat_cache() + * @see self::_remove_from_stat_cache() + * @see self::_query_stat_cache() + * @var array + */ + private $stat_cache = []; + /** + * Max SFTP Packet Size + * + * @see self::__construct() + * @see self::get() + * @var int + */ + private $max_sftp_packet; + /** + * Stat Cache Flag + * + * @see self::disableStatCache() + * @see self::enableStatCache() + * @var bool + */ + private $use_stat_cache = \true; + /** + * Sort Options + * + * @see self::_comparator() + * @see self::setListOrder() + * @var array + */ + protected $sortOptions = []; + /** + * Canonicalization Flag + * + * Determines whether or not paths should be canonicalized before being + * passed on to the remote server. + * + * @see self::enablePathCanonicalization() + * @see self::disablePathCanonicalization() + * @see self::realpath() + * @var bool + */ + private $canonicalize_paths = \true; + /** + * Request Buffers + * + * @see self::_get_sftp_packet() + * @var array + */ + private $requestBuffer = []; + /** + * Preserve timestamps on file downloads / uploads + * + * @see self::get() + * @see self::put() + * @var bool + */ + private $preserveTime = \false; + /** + * Arbitrary Length Packets Flag + * + * Determines whether or not packets of any length should be allowed, + * in cases where the server chooses the packet length (such as + * directory listings). By default, packets are only allowed to be + * 256 * 1024 bytes (SFTP_MAX_MSG_LENGTH from OpenSSH's sftp-common.h) + * + * @see self::enableArbitraryLengthPackets() + * @see self::_get_sftp_packet() + * @var bool + */ + private $allow_arbitrary_length_packets = \false; + /** + * Was the last packet due to the channels being closed or not? + * + * @see self::get() + * @see self::get_sftp_packet() + * @var bool + */ + private $channel_close = \false; + /** + * Has the SFTP channel been partially negotiated? + * + * @var bool + */ + private $partial_init = \false; + /** + * Default Constructor. + * + * Connects to an SFTP server + * + * $host can either be a string, representing the host, or a stream resource. + * + * @param mixed $host + * @param int $port + * @param int $timeout + */ + public function __construct($host, $port = 22, $timeout = 10) + { + parent::__construct($host, $port, $timeout); + $this->max_sftp_packet = 1 << 15; + if (empty(self::$packet_types)) { + self::$packet_types = [1 => 'NET_SFTP_INIT', 2 => 'NET_SFTP_VERSION', 3 => 'NET_SFTP_OPEN', 4 => 'NET_SFTP_CLOSE', 5 => 'NET_SFTP_READ', 6 => 'NET_SFTP_WRITE', 7 => 'NET_SFTP_LSTAT', 9 => 'NET_SFTP_SETSTAT', 10 => 'NET_SFTP_FSETSTAT', 11 => 'NET_SFTP_OPENDIR', 12 => 'NET_SFTP_READDIR', 13 => 'NET_SFTP_REMOVE', 14 => 'NET_SFTP_MKDIR', 15 => 'NET_SFTP_RMDIR', 16 => 'NET_SFTP_REALPATH', 17 => 'NET_SFTP_STAT', 18 => 'NET_SFTP_RENAME', 19 => 'NET_SFTP_READLINK', 20 => 'NET_SFTP_SYMLINK', 21 => 'NET_SFTP_LINK', 101 => 'NET_SFTP_STATUS', 102 => 'NET_SFTP_HANDLE', 103 => 'NET_SFTP_DATA', 104 => 'NET_SFTP_NAME', 105 => 'NET_SFTP_ATTRS', 200 => 'NET_SFTP_EXTENDED', 201 => 'NET_SFTP_EXTENDED_REPLY']; + self::$status_codes = [0 => 'NET_SFTP_STATUS_OK', 1 => 'NET_SFTP_STATUS_EOF', 2 => 'NET_SFTP_STATUS_NO_SUCH_FILE', 3 => 'NET_SFTP_STATUS_PERMISSION_DENIED', 4 => 'NET_SFTP_STATUS_FAILURE', 5 => 'NET_SFTP_STATUS_BAD_MESSAGE', 6 => 'NET_SFTP_STATUS_NO_CONNECTION', 7 => 'NET_SFTP_STATUS_CONNECTION_LOST', 8 => 'NET_SFTP_STATUS_OP_UNSUPPORTED', 9 => 'NET_SFTP_STATUS_INVALID_HANDLE', 10 => 'NET_SFTP_STATUS_NO_SUCH_PATH', 11 => 'NET_SFTP_STATUS_FILE_ALREADY_EXISTS', 12 => 'NET_SFTP_STATUS_WRITE_PROTECT', 13 => 'NET_SFTP_STATUS_NO_MEDIA', 14 => 'NET_SFTP_STATUS_NO_SPACE_ON_FILESYSTEM', 15 => 'NET_SFTP_STATUS_QUOTA_EXCEEDED', 16 => 'NET_SFTP_STATUS_UNKNOWN_PRINCIPAL', 17 => 'NET_SFTP_STATUS_LOCK_CONFLICT', 18 => 'NET_SFTP_STATUS_DIR_NOT_EMPTY', 19 => 'NET_SFTP_STATUS_NOT_A_DIRECTORY', 20 => 'NET_SFTP_STATUS_INVALID_FILENAME', 21 => 'NET_SFTP_STATUS_LINK_LOOP', 22 => 'NET_SFTP_STATUS_CANNOT_DELETE', 23 => 'NET_SFTP_STATUS_INVALID_PARAMETER', 24 => 'NET_SFTP_STATUS_FILE_IS_A_DIRECTORY', 25 => 'NET_SFTP_STATUS_BYTE_RANGE_LOCK_CONFLICT', 26 => 'NET_SFTP_STATUS_BYTE_RANGE_LOCK_REFUSED', 27 => 'NET_SFTP_STATUS_DELETE_PENDING', 28 => 'NET_SFTP_STATUS_FILE_CORRUPT', 29 => 'NET_SFTP_STATUS_OWNER_INVALID', 30 => 'NET_SFTP_STATUS_GROUP_INVALID', 31 => 'NET_SFTP_STATUS_NO_MATCHING_BYTE_RANGE_LOCK']; + // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-7.1 + // the order, in this case, matters quite a lot - see \phpseclib3\Net\SFTP::_parseAttributes() to understand why + self::$attributes = [ + 0x1 => 'NET_SFTP_ATTR_SIZE', + 0x2 => 'NET_SFTP_ATTR_UIDGID', + // defined in SFTPv3, removed in SFTPv4+ + 0x80 => 'NET_SFTP_ATTR_OWNERGROUP', + // defined in SFTPv4+ + 0x4 => 'NET_SFTP_ATTR_PERMISSIONS', + 0x8 => 'NET_SFTP_ATTR_ACCESSTIME', + 0x10 => 'NET_SFTP_ATTR_CREATETIME', + // SFTPv4+ + 0x20 => 'NET_SFTP_ATTR_MODIFYTIME', + 0x40 => 'NET_SFTP_ATTR_ACL', + 0x100 => 'NET_SFTP_ATTR_SUBSECOND_TIMES', + 0x200 => 'NET_SFTP_ATTR_BITS', + // SFTPv5+ + 0x400 => 'NET_SFTP_ATTR_ALLOCATION_SIZE', + // SFTPv6+ + 0x800 => 'NET_SFTP_ATTR_TEXT_HINT', + 0x1000 => 'NET_SFTP_ATTR_MIME_TYPE', + 0x2000 => 'NET_SFTP_ATTR_LINK_COUNT', + 0x4000 => 'NET_SFTP_ATTR_UNTRANSLATED_NAME', + 0x8000 => 'NET_SFTP_ATTR_CTIME', + // 0x80000000 will yield a floating point on 32-bit systems and converting floating points to integers + // yields inconsistent behavior depending on how php is compiled. so we left shift -1 (which, in + // two's compliment, consists of all 1 bits) by 31. on 64-bit systems this'll yield 0xFFFFFFFF80000000. + // that's not a problem, however, and 'anded' and a 32-bit number, as all the leading 1 bits are ignored. + \PHP_INT_SIZE == 4 ? -1 << 31 : 0x80000000 => 'NET_SFTP_ATTR_EXTENDED', + ]; + // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-04#section-6.3 + // the flag definitions change somewhat in SFTPv5+. if SFTPv5+ support is added to this library, maybe name + // the array for that $this->open5_flags and similarly alter the constant names. + self::$open_flags = [0x1 => 'NET_SFTP_OPEN_READ', 0x2 => 'NET_SFTP_OPEN_WRITE', 0x4 => 'NET_SFTP_OPEN_APPEND', 0x8 => 'NET_SFTP_OPEN_CREATE', 0x10 => 'NET_SFTP_OPEN_TRUNCATE', 0x20 => 'NET_SFTP_OPEN_EXCL', 0x40 => 'NET_SFTP_OPEN_TEXT']; + // SFTPv5+ changed the flags up: + // https://datatracker.ietf.org/doc/html/draft-ietf-secsh-filexfer-13#section-8.1.1.3 + self::$open_flags5 = [ + // when SSH_FXF_ACCESS_DISPOSITION is a 3 bit field that controls how the file is opened + 0x0 => 'NET_SFTP_OPEN_CREATE_NEW', + 0x1 => 'NET_SFTP_OPEN_CREATE_TRUNCATE', + 0x2 => 'NET_SFTP_OPEN_OPEN_EXISTING', + 0x3 => 'NET_SFTP_OPEN_OPEN_OR_CREATE', + 0x4 => 'NET_SFTP_OPEN_TRUNCATE_EXISTING', + // the rest of the flags are not supported + 0x8 => 'NET_SFTP_OPEN_APPEND_DATA', + // "the offset field of SS_FXP_WRITE requests is ignored" + 0x10 => 'NET_SFTP_OPEN_APPEND_DATA_ATOMIC', + 0x20 => 'NET_SFTP_OPEN_TEXT_MODE', + 0x40 => 'NET_SFTP_OPEN_BLOCK_READ', + 0x80 => 'NET_SFTP_OPEN_BLOCK_WRITE', + 0x100 => 'NET_SFTP_OPEN_BLOCK_DELETE', + 0x200 => 'NET_SFTP_OPEN_BLOCK_ADVISORY', + 0x400 => 'NET_SFTP_OPEN_NOFOLLOW', + 0x800 => 'NET_SFTP_OPEN_DELETE_ON_CLOSE', + 0x1000 => 'NET_SFTP_OPEN_ACCESS_AUDIT_ALARM_INFO', + 0x2000 => 'NET_SFTP_OPEN_ACCESS_BACKUP', + 0x4000 => 'NET_SFTP_OPEN_BACKUP_STREAM', + 0x8000 => 'NET_SFTP_OPEN_OVERRIDE_OWNER', + ]; + // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-04#section-5.2 + // see \phpseclib3\Net\SFTP::_parseLongname() for an explanation + self::$file_types = [ + 1 => 'NET_SFTP_TYPE_REGULAR', + 2 => 'NET_SFTP_TYPE_DIRECTORY', + 3 => 'NET_SFTP_TYPE_SYMLINK', + 4 => 'NET_SFTP_TYPE_SPECIAL', + 5 => 'NET_SFTP_TYPE_UNKNOWN', + // the following types were first defined for use in SFTPv5+ + // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-05#section-5.2 + 6 => 'NET_SFTP_TYPE_SOCKET', + 7 => 'NET_SFTP_TYPE_CHAR_DEVICE', + 8 => 'NET_SFTP_TYPE_BLOCK_DEVICE', + 9 => 'NET_SFTP_TYPE_FIFO', + ]; + self::define_array(self::$packet_types, self::$status_codes, self::$attributes, self::$open_flags, self::$open_flags5, self::$file_types); + } + if (!\defined('FluentSmtpLib\\NET_SFTP_QUEUE_SIZE')) { + \define('FluentSmtpLib\\NET_SFTP_QUEUE_SIZE', 32); + } + if (!\defined('FluentSmtpLib\\NET_SFTP_UPLOAD_QUEUE_SIZE')) { + \define('FluentSmtpLib\\NET_SFTP_UPLOAD_QUEUE_SIZE', 1024); + } + } + /** + * Check a few things before SFTP functions are called + * + * @return bool + */ + private function precheck() + { + if (!($this->bitmap & \FluentSmtpLib\phpseclib3\Net\SSH2::MASK_LOGIN)) { + return \false; + } + if ($this->pwd === \false) { + return $this->init_sftp_connection(); + } + return \true; + } + /** + * Partially initialize an SFTP connection + * + * @throws \UnexpectedValueException on receipt of unexpected packets + * @return bool + */ + private function partial_init_sftp_connection() + { + $response = $this->open_channel(self::CHANNEL, \true); + if ($response === \true && $this->isTimeout()) { + return \false; + } + $packet = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::packSSH2('CNsbs', NET_SSH2_MSG_CHANNEL_REQUEST, $this->server_channels[self::CHANNEL], 'subsystem', \true, 'sftp'); + $this->send_binary_packet($packet); + $this->channel_status[self::CHANNEL] = NET_SSH2_MSG_CHANNEL_REQUEST; + $response = $this->get_channel_packet(self::CHANNEL, \true); + if ($response === \false) { + // from PuTTY's psftp.exe + $command = "test -x /usr/lib/sftp-server && exec /usr/lib/sftp-server\n" . "test -x /usr/local/lib/sftp-server && exec /usr/local/lib/sftp-server\n" . "exec sftp-server"; + // we don't do $this->exec($command, false) because exec() operates on a different channel and plus the SSH_MSG_CHANNEL_OPEN that exec() does + // is redundant + $packet = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::packSSH2('CNsCs', NET_SSH2_MSG_CHANNEL_REQUEST, $this->server_channels[self::CHANNEL], 'exec', 1, $command); + $this->send_binary_packet($packet); + $this->channel_status[self::CHANNEL] = NET_SSH2_MSG_CHANNEL_REQUEST; + $response = $this->get_channel_packet(self::CHANNEL, \true); + if ($response === \false) { + return \false; + } + } elseif ($response === \true && $this->isTimeout()) { + return \false; + } + $this->channel_status[self::CHANNEL] = NET_SSH2_MSG_CHANNEL_DATA; + $this->send_sftp_packet(NET_SFTP_INIT, "\x00\x00\x00\x03"); + $response = $this->get_sftp_packet(); + if ($this->packet_type != NET_SFTP_VERSION) { + throw new \UnexpectedValueException('Expected NET_SFTP_VERSION. ' . 'Got packet type: ' . $this->packet_type); + } + $this->use_request_id = \true; + list($this->defaultVersion) = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::unpackSSH2('N', $response); + while (!empty($response)) { + list($key, $value) = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::unpackSSH2('ss', $response); + $this->extensions[$key] = $value; + } + $this->partial_init = \true; + return \true; + } + /** + * (Re)initializes the SFTP channel + * + * @return bool + */ + private function init_sftp_connection() + { + if (!$this->partial_init && !$this->partial_init_sftp_connection()) { + return \false; + } + /* + A Note on SFTPv4/5/6 support: + states the following: + + "If the client wishes to interoperate with servers that support noncontiguous version + numbers it SHOULD send '3'" + + Given that the server only sends its version number after the client has already done so, the above + seems to be suggesting that v3 should be the default version. This makes sense given that v3 is the + most popular. + + states the following; + + "If the server did not send the "versions" extension, or the version-from-list was not included, the + server MAY send a status response describing the failure, but MUST then close the channel without + processing any further requests." + + So what do you do if you have a client whose initial SSH_FXP_INIT packet says it implements v3 and + a server whose initial SSH_FXP_VERSION reply says it implements v4 and only v4? If it only implements + v4, the "versions" extension is likely not going to have been sent so version re-negotiation as discussed + in draft-ietf-secsh-filexfer-13 would be quite impossible. As such, what \phpseclib3\Net\SFTP would do is close the + channel and reopen it with a new and updated SSH_FXP_INIT packet. + */ + $this->version = $this->defaultVersion; + if (isset($this->extensions['versions']) && (!$this->preferredVersion || $this->preferredVersion != $this->version)) { + $versions = \explode(',', $this->extensions['versions']); + $supported = [6, 5, 4]; + if ($this->preferredVersion) { + $supported = \array_diff($supported, [$this->preferredVersion]); + \array_unshift($supported, $this->preferredVersion); + } + foreach ($supported as $ver) { + if (\in_array($ver, $versions)) { + if ($ver === $this->version) { + break; + } + $this->version = (int) $ver; + $packet = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::packSSH2('ss', 'version-select', "{$ver}"); + $this->send_sftp_packet(NET_SFTP_EXTENDED, $packet); + $response = $this->get_sftp_packet(); + if ($this->packet_type != NET_SFTP_STATUS) { + throw new \UnexpectedValueException('Expected NET_SFTP_STATUS. ' . 'Got packet type: ' . $this->packet_type); + } + list($status) = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::unpackSSH2('N', $response); + if ($status != NET_SFTP_STATUS_OK) { + $this->logError($response, $status); + throw new \UnexpectedValueException('Expected NET_SFTP_STATUS_OK. ' . ' Got ' . $status); + } + break; + } + } + } + /* + SFTPv4+ defines a 'newline' extension. SFTPv3 seems to have unofficial support for it via 'newline@vandyke.com', + however, I'm not sure what 'newline@vandyke.com' is supposed to do (the fact that it's unofficial means that it's + not in the official SFTPv3 specs) and 'newline@vandyke.com' / 'newline' are likely not drop-in substitutes for + one another due to the fact that 'newline' comes with a SSH_FXF_TEXT bitmask whereas it seems unlikely that + 'newline@vandyke.com' would. + */ + /* + if (isset($this->extensions['newline@vandyke.com'])) { + $this->extensions['newline'] = $this->extensions['newline@vandyke.com']; + unset($this->extensions['newline@vandyke.com']); + } + */ + if ($this->version < 2 || $this->version > 6) { + return \false; + } + $this->pwd = \true; + try { + $this->pwd = $this->realpath('.'); + } catch (\UnexpectedValueException $e) { + if (!$this->canonicalize_paths) { + throw $e; + } + $this->canonicalize_paths = \false; + $this->reset_sftp(); + return $this->init_sftp_connection(); + } + $this->update_stat_cache($this->pwd, []); + return \true; + } + /** + * Disable the stat cache + * + */ + public function disableStatCache() + { + $this->use_stat_cache = \false; + } + /** + * Enable the stat cache + * + */ + public function enableStatCache() + { + $this->use_stat_cache = \true; + } + /** + * Clear the stat cache + * + */ + public function clearStatCache() + { + $this->stat_cache = []; + } + /** + * Enable path canonicalization + * + */ + public function enablePathCanonicalization() + { + $this->canonicalize_paths = \true; + } + /** + * Disable path canonicalization + * + * If this is enabled then $sftp->pwd() will not return the canonicalized absolute path + * + */ + public function disablePathCanonicalization() + { + $this->canonicalize_paths = \false; + } + /** + * Enable arbitrary length packets + * + */ + public function enableArbitraryLengthPackets() + { + $this->allow_arbitrary_length_packets = \true; + } + /** + * Disable arbitrary length packets + * + */ + public function disableArbitraryLengthPackets() + { + $this->allow_arbitrary_length_packets = \false; + } + /** + * Returns the current directory name + * + * @return string|bool + */ + public function pwd() + { + if (!$this->precheck()) { + return \false; + } + return $this->pwd; + } + /** + * Logs errors + * + * @param string $response + * @param int $status + */ + private function logError($response, $status = -1) + { + if ($status == -1) { + list($status) = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::unpackSSH2('N', $response); + } + $error = self::$status_codes[$status]; + if ($this->version > 2) { + list($message) = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::unpackSSH2('s', $response); + $this->sftp_errors[] = "{$error}: {$message}"; + } else { + $this->sftp_errors[] = $error; + } + } + /** + * Canonicalize the Server-Side Path Name + * + * SFTP doesn't provide a mechanism by which the current working directory can be changed, so we'll emulate it. Returns + * the absolute (canonicalized) path. + * + * If canonicalize_paths has been disabled using disablePathCanonicalization(), $path is returned as-is. + * + * @see self::chdir() + * @see self::disablePathCanonicalization() + * @param string $path + * @throws \UnexpectedValueException on receipt of unexpected packets + * @return mixed + */ + public function realpath($path) + { + if ($this->precheck() === \false) { + return \false; + } + if (!$this->canonicalize_paths) { + if ($this->pwd === \true) { + return '.'; + } + if (!\strlen($path) || $path[0] != '/') { + $path = $this->pwd . '/' . $path; + } + $parts = \explode('/', $path); + $afterPWD = $beforePWD = []; + foreach ($parts as $part) { + switch ($part) { + //case '': // some SFTP servers /require/ double /'s. see https://github.com/phpseclib/phpseclib/pull/1137 + case '.': + break; + case '..': + if (!empty($afterPWD)) { + \array_pop($afterPWD); + } else { + $beforePWD[] = '..'; + } + break; + default: + $afterPWD[] = $part; + } + } + $beforePWD = \count($beforePWD) ? \implode('/', $beforePWD) : '.'; + return $beforePWD . '/' . \implode('/', $afterPWD); + } + if ($this->pwd === \true) { + // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.9 + $this->send_sftp_packet(NET_SFTP_REALPATH, \FluentSmtpLib\phpseclib3\Common\Functions\Strings::packSSH2('s', $path)); + $response = $this->get_sftp_packet(); + switch ($this->packet_type) { + case NET_SFTP_NAME: + // although SSH_FXP_NAME is implemented differently in SFTPv3 than it is in SFTPv4+, the following + // should work on all SFTP versions since the only part of the SSH_FXP_NAME packet the following looks + // at is the first part and that part is defined the same in SFTP versions 3 through 6. + list(, $filename) = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::unpackSSH2('Ns', $response); + return $filename; + case NET_SFTP_STATUS: + $this->logError($response); + return \false; + default: + throw new \UnexpectedValueException('Expected NET_SFTP_NAME or NET_SFTP_STATUS. ' . 'Got packet type: ' . $this->packet_type); + } + } + if (!\strlen($path) || $path[0] != '/') { + $path = $this->pwd . '/' . $path; + } + $path = \explode('/', $path); + $new = []; + foreach ($path as $dir) { + if (!\strlen($dir)) { + continue; + } + switch ($dir) { + case '..': + \array_pop($new); + // fall-through + case '.': + break; + default: + $new[] = $dir; + } + } + return '/' . \implode('/', $new); + } + /** + * Changes the current directory + * + * @param string $dir + * @throws \UnexpectedValueException on receipt of unexpected packets + * @return bool + */ + public function chdir($dir) + { + if (!$this->precheck()) { + return \false; + } + // assume current dir if $dir is empty + if ($dir === '') { + $dir = './'; + // suffix a slash if needed + } elseif ($dir[\strlen($dir) - 1] != '/') { + $dir .= '/'; + } + $dir = $this->realpath($dir); + // confirm that $dir is, in fact, a valid directory + if ($this->use_stat_cache && \is_array($this->query_stat_cache($dir))) { + $this->pwd = $dir; + return \true; + } + // we could do a stat on the alleged $dir to see if it's a directory but that doesn't tell us + // the currently logged in user has the appropriate permissions or not. maybe you could see if + // the file's uid / gid match the currently logged in user's uid / gid but how there's no easy + // way to get those with SFTP + $this->send_sftp_packet(NET_SFTP_OPENDIR, \FluentSmtpLib\phpseclib3\Common\Functions\Strings::packSSH2('s', $dir)); + // see \phpseclib3\Net\SFTP::nlist() for a more thorough explanation of the following + $response = $this->get_sftp_packet(); + switch ($this->packet_type) { + case NET_SFTP_HANDLE: + $handle = \substr($response, 4); + break; + case NET_SFTP_STATUS: + $this->logError($response); + return \false; + default: + throw new \UnexpectedValueException('Expected NET_SFTP_HANDLE or NET_SFTP_STATUS' . 'Got packet type: ' . $this->packet_type); + } + if (!$this->close_handle($handle)) { + return \false; + } + $this->update_stat_cache($dir, []); + $this->pwd = $dir; + return \true; + } + /** + * Returns a list of files in the given directory + * + * @param string $dir + * @param bool $recursive + * @return array|false + */ + public function nlist($dir = '.', $recursive = \false) + { + return $this->nlist_helper($dir, $recursive, ''); + } + /** + * Helper method for nlist + * + * @param string $dir + * @param bool $recursive + * @param string $relativeDir + * @return array|false + */ + private function nlist_helper($dir, $recursive, $relativeDir) + { + $files = $this->readlist($dir, \false); + // If we get an int back, then that is an "unexpected" status. + // We do not have a file list, so return false. + if (\is_int($files)) { + return \false; + } + if (!$recursive || $files === \false) { + return $files; + } + $result = []; + foreach ($files as $value) { + if ($value == '.' || $value == '..') { + $result[] = $relativeDir . $value; + continue; + } + if (\is_array($this->query_stat_cache($this->realpath($dir . '/' . $value)))) { + $temp = $this->nlist_helper($dir . '/' . $value, \true, $relativeDir . $value . '/'); + $temp = \is_array($temp) ? $temp : []; + $result = \array_merge($result, $temp); + } else { + $result[] = $relativeDir . $value; + } + } + return $result; + } + /** + * Returns a detailed list of files in the given directory + * + * @param string $dir + * @param bool $recursive + * @return array|false + */ + public function rawlist($dir = '.', $recursive = \false) + { + $files = $this->readlist($dir, \true); + // If we get an int back, then that is an "unexpected" status. + // We do not have a file list, so return false. + if (\is_int($files)) { + return \false; + } + if (!$recursive || $files === \false) { + return $files; + } + static $depth = 0; + foreach ($files as $key => $value) { + if ($depth != 0 && $key == '..') { + unset($files[$key]); + continue; + } + $is_directory = \false; + if ($key != '.' && $key != '..') { + if ($this->use_stat_cache) { + $is_directory = \is_array($this->query_stat_cache($this->realpath($dir . '/' . $key))); + } else { + $stat = $this->lstat($dir . '/' . $key); + $is_directory = $stat && $stat['type'] === NET_SFTP_TYPE_DIRECTORY; + } + } + if ($is_directory) { + $depth++; + $files[$key] = $this->rawlist($dir . '/' . $key, \true); + $depth--; + } else { + $files[$key] = (object) $value; + } + } + return $files; + } + /** + * Reads a list, be it detailed or not, of files in the given directory + * + * @param string $dir + * @param bool $raw + * @return array|false + * @throws \UnexpectedValueException on receipt of unexpected packets + */ + private function readlist($dir, $raw = \true) + { + if (!$this->precheck()) { + return \false; + } + $dir = $this->realpath($dir . '/'); + if ($dir === \false) { + return \false; + } + // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.1.2 + $this->send_sftp_packet(NET_SFTP_OPENDIR, \FluentSmtpLib\phpseclib3\Common\Functions\Strings::packSSH2('s', $dir)); + $response = $this->get_sftp_packet(); + switch ($this->packet_type) { + case NET_SFTP_HANDLE: + // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-9.2 + // since 'handle' is the last field in the SSH_FXP_HANDLE packet, we'll just remove the first four bytes that + // represent the length of the string and leave it at that + $handle = \substr($response, 4); + break; + case NET_SFTP_STATUS: + // presumably SSH_FX_NO_SUCH_FILE or SSH_FX_PERMISSION_DENIED + list($status) = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::unpackSSH2('N', $response); + $this->logError($response, $status); + return $status; + default: + throw new \UnexpectedValueException('Expected NET_SFTP_HANDLE or NET_SFTP_STATUS. ' . 'Got packet type: ' . $this->packet_type); + } + $this->update_stat_cache($dir, []); + $contents = []; + while (\true) { + // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.2.2 + // why multiple SSH_FXP_READDIR packets would be sent when the response to a single one can span arbitrarily many + // SSH_MSG_CHANNEL_DATA messages is not known to me. + $this->send_sftp_packet(NET_SFTP_READDIR, \FluentSmtpLib\phpseclib3\Common\Functions\Strings::packSSH2('s', $handle)); + $response = $this->get_sftp_packet(); + switch ($this->packet_type) { + case NET_SFTP_NAME: + list($count) = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::unpackSSH2('N', $response); + for ($i = 0; $i < $count; $i++) { + list($shortname) = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::unpackSSH2('s', $response); + // SFTPv4 "removed the long filename from the names structure-- it can now be + // built from information available in the attrs structure." + if ($this->version < 4) { + list($longname) = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::unpackSSH2('s', $response); + } + $attributes = $this->parseAttributes($response); + if (!isset($attributes['type']) && $this->version < 4) { + $fileType = $this->parseLongname($longname); + if ($fileType) { + $attributes['type'] = $fileType; + } + } + $contents[$shortname] = $attributes + ['filename' => $shortname]; + if (isset($attributes['type']) && $attributes['type'] == NET_SFTP_TYPE_DIRECTORY && ($shortname != '.' && $shortname != '..')) { + $this->update_stat_cache($dir . '/' . $shortname, []); + } else { + if ($shortname == '..') { + $temp = $this->realpath($dir . '/..') . '/.'; + } else { + $temp = $dir . '/' . $shortname; + } + $this->update_stat_cache($temp, (object) ['lstat' => $attributes]); + } + // SFTPv6 has an optional boolean end-of-list field, but we'll ignore that, since the + // final SSH_FXP_STATUS packet should tell us that, already. + } + break; + case NET_SFTP_STATUS: + list($status) = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::unpackSSH2('N', $response); + if ($status != NET_SFTP_STATUS_EOF) { + $this->logError($response, $status); + return $status; + } + break 2; + default: + throw new \UnexpectedValueException('Expected NET_SFTP_NAME or NET_SFTP_STATUS. ' . 'Got packet type: ' . $this->packet_type); + } + } + if (!$this->close_handle($handle)) { + return \false; + } + if (\count($this->sortOptions)) { + \uasort($contents, [&$this, 'comparator']); + } + return $raw ? $contents : \array_map('strval', \array_keys($contents)); + } + /** + * Compares two rawlist entries using parameters set by setListOrder() + * + * Intended for use with uasort() + * + * @param array $a + * @param array $b + * @return int + */ + private function comparator(array $a, array $b) + { + switch (\true) { + case $a['filename'] === '.' || $b['filename'] === '.': + if ($a['filename'] === $b['filename']) { + return 0; + } + return $a['filename'] === '.' ? -1 : 1; + case $a['filename'] === '..' || $b['filename'] === '..': + if ($a['filename'] === $b['filename']) { + return 0; + } + return $a['filename'] === '..' ? -1 : 1; + case isset($a['type']) && $a['type'] === NET_SFTP_TYPE_DIRECTORY: + if (!isset($b['type'])) { + return 1; + } + if ($b['type'] !== $a['type']) { + return -1; + } + break; + case isset($b['type']) && $b['type'] === NET_SFTP_TYPE_DIRECTORY: + return 1; + } + foreach ($this->sortOptions as $sort => $order) { + if (!isset($a[$sort]) || !isset($b[$sort])) { + if (isset($a[$sort])) { + return -1; + } + if (isset($b[$sort])) { + return 1; + } + return 0; + } + switch ($sort) { + case 'filename': + $result = \strcasecmp($a['filename'], $b['filename']); + if ($result) { + return $order === \SORT_DESC ? -$result : $result; + } + break; + case 'mode': + $a[$sort] &= 07777; + $b[$sort] &= 07777; + // fall-through + default: + if ($a[$sort] === $b[$sort]) { + break; + } + return $order === \SORT_ASC ? $a[$sort] - $b[$sort] : $b[$sort] - $a[$sort]; + } + } + } + /** + * Defines how nlist() and rawlist() will be sorted - if at all. + * + * If sorting is enabled directories and files will be sorted independently with + * directories appearing before files in the resultant array that is returned. + * + * Any parameter returned by stat is a valid sort parameter for this function. + * Filename comparisons are case insensitive. + * + * Examples: + * + * $sftp->setListOrder('filename', SORT_ASC); + * $sftp->setListOrder('size', SORT_DESC, 'filename', SORT_ASC); + * $sftp->setListOrder(true); + * Separates directories from files but doesn't do any sorting beyond that + * $sftp->setListOrder(); + * Don't do any sort of sorting + * + * @param string ...$args + */ + public function setListOrder(...$args) + { + $this->sortOptions = []; + if (empty($args)) { + return; + } + $len = \count($args) & 0x7ffffffe; + for ($i = 0; $i < $len; $i += 2) { + $this->sortOptions[$args[$i]] = $args[$i + 1]; + } + if (!\count($this->sortOptions)) { + $this->sortOptions = ['bogus' => \true]; + } + } + /** + * Save files / directories to cache + * + * @param string $path + * @param mixed $value + */ + private function update_stat_cache($path, $value) + { + if ($this->use_stat_cache === \false) { + return; + } + // preg_replace('#^/|/(?=/)|/$#', '', $dir) == str_replace('//', '/', trim($path, '/')) + $dirs = \explode('/', \preg_replace('#^/|/(?=/)|/$#', '', $path)); + $temp =& $this->stat_cache; + $max = \count($dirs) - 1; + foreach ($dirs as $i => $dir) { + // if $temp is an object that means one of two things. + // 1. a file was deleted and changed to a directory behind phpseclib's back + // 2. it's a symlink. when lstat is done it's unclear what it's a symlink to + if (\is_object($temp)) { + $temp = []; + } + if (!isset($temp[$dir])) { + $temp[$dir] = []; + } + if ($i === $max) { + if (\is_object($temp[$dir]) && \is_object($value)) { + if (!isset($value->stat) && isset($temp[$dir]->stat)) { + $value->stat = $temp[$dir]->stat; + } + if (!isset($value->lstat) && isset($temp[$dir]->lstat)) { + $value->lstat = $temp[$dir]->lstat; + } + } + $temp[$dir] = $value; + break; + } + $temp =& $temp[$dir]; + } + } + /** + * Remove files / directories from cache + * + * @param string $path + * @return bool + */ + private function remove_from_stat_cache($path) + { + $dirs = \explode('/', \preg_replace('#^/|/(?=/)|/$#', '', $path)); + $temp =& $this->stat_cache; + $max = \count($dirs) - 1; + foreach ($dirs as $i => $dir) { + if (!\is_array($temp)) { + return \false; + } + if ($i === $max) { + unset($temp[$dir]); + return \true; + } + if (!isset($temp[$dir])) { + return \false; + } + $temp =& $temp[$dir]; + } + } + /** + * Checks cache for path + * + * Mainly used by file_exists + * + * @param string $path + * @return mixed + */ + private function query_stat_cache($path) + { + $dirs = \explode('/', \preg_replace('#^/|/(?=/)|/$#', '', $path)); + $temp =& $this->stat_cache; + foreach ($dirs as $dir) { + if (!\is_array($temp)) { + return null; + } + if (!isset($temp[$dir])) { + return null; + } + $temp =& $temp[$dir]; + } + return $temp; + } + /** + * Returns general information about a file. + * + * Returns an array on success and false otherwise. + * + * @param string $filename + * @return array|false + */ + public function stat($filename) + { + if (!$this->precheck()) { + return \false; + } + $filename = $this->realpath($filename); + if ($filename === \false) { + return \false; + } + if ($this->use_stat_cache) { + $result = $this->query_stat_cache($filename); + if (\is_array($result) && isset($result['.']) && isset($result['.']->stat)) { + return $result['.']->stat; + } + if (\is_object($result) && isset($result->stat)) { + return $result->stat; + } + } + $stat = $this->stat_helper($filename, NET_SFTP_STAT); + if ($stat === \false) { + $this->remove_from_stat_cache($filename); + return \false; + } + if (isset($stat['type'])) { + if ($stat['type'] == NET_SFTP_TYPE_DIRECTORY) { + $filename .= '/.'; + } + $this->update_stat_cache($filename, (object) ['stat' => $stat]); + return $stat; + } + $pwd = $this->pwd; + $stat['type'] = $this->chdir($filename) ? NET_SFTP_TYPE_DIRECTORY : NET_SFTP_TYPE_REGULAR; + $this->pwd = $pwd; + if ($stat['type'] == NET_SFTP_TYPE_DIRECTORY) { + $filename .= '/.'; + } + $this->update_stat_cache($filename, (object) ['stat' => $stat]); + return $stat; + } + /** + * Returns general information about a file or symbolic link. + * + * Returns an array on success and false otherwise. + * + * @param string $filename + * @return array|false + */ + public function lstat($filename) + { + if (!$this->precheck()) { + return \false; + } + $filename = $this->realpath($filename); + if ($filename === \false) { + return \false; + } + if ($this->use_stat_cache) { + $result = $this->query_stat_cache($filename); + if (\is_array($result) && isset($result['.']) && isset($result['.']->lstat)) { + return $result['.']->lstat; + } + if (\is_object($result) && isset($result->lstat)) { + return $result->lstat; + } + } + $lstat = $this->stat_helper($filename, NET_SFTP_LSTAT); + if ($lstat === \false) { + $this->remove_from_stat_cache($filename); + return \false; + } + if (isset($lstat['type'])) { + if ($lstat['type'] == NET_SFTP_TYPE_DIRECTORY) { + $filename .= '/.'; + } + $this->update_stat_cache($filename, (object) ['lstat' => $lstat]); + return $lstat; + } + $stat = $this->stat_helper($filename, NET_SFTP_STAT); + if ($lstat != $stat) { + $lstat = \array_merge($lstat, ['type' => NET_SFTP_TYPE_SYMLINK]); + $this->update_stat_cache($filename, (object) ['lstat' => $lstat]); + return $stat; + } + $pwd = $this->pwd; + $lstat['type'] = $this->chdir($filename) ? NET_SFTP_TYPE_DIRECTORY : NET_SFTP_TYPE_REGULAR; + $this->pwd = $pwd; + if ($lstat['type'] == NET_SFTP_TYPE_DIRECTORY) { + $filename .= '/.'; + } + $this->update_stat_cache($filename, (object) ['lstat' => $lstat]); + return $lstat; + } + /** + * Returns general information about a file or symbolic link + * + * Determines information without calling \phpseclib3\Net\SFTP::realpath(). + * The second parameter can be either NET_SFTP_STAT or NET_SFTP_LSTAT. + * + * @param string $filename + * @param int $type + * @throws \UnexpectedValueException on receipt of unexpected packets + * @return array|false + */ + private function stat_helper($filename, $type) + { + // SFTPv4+ adds an additional 32-bit integer field - flags - to the following: + $packet = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::packSSH2('s', $filename); + $this->send_sftp_packet($type, $packet); + $response = $this->get_sftp_packet(); + switch ($this->packet_type) { + case NET_SFTP_ATTRS: + return $this->parseAttributes($response); + case NET_SFTP_STATUS: + $this->logError($response); + return \false; + } + throw new \UnexpectedValueException('Expected NET_SFTP_ATTRS or NET_SFTP_STATUS. ' . 'Got packet type: ' . $this->packet_type); + } + /** + * Truncates a file to a given length + * + * @param string $filename + * @param int $new_size + * @return bool + */ + public function truncate($filename, $new_size) + { + $attr = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::packSSH2('NQ', NET_SFTP_ATTR_SIZE, $new_size); + return $this->setstat($filename, $attr, \false); + } + /** + * Sets access and modification time of file. + * + * If the file does not exist, it will be created. + * + * @param string $filename + * @param int $time + * @param int $atime + * @throws \UnexpectedValueException on receipt of unexpected packets + * @return bool + */ + public function touch($filename, $time = null, $atime = null) + { + if (!$this->precheck()) { + return \false; + } + $filename = $this->realpath($filename); + if ($filename === \false) { + return \false; + } + if (!isset($time)) { + $time = \time(); + } + if (!isset($atime)) { + $atime = $time; + } + $attr = $this->version < 4 ? \pack('N3', NET_SFTP_ATTR_ACCESSTIME, $atime, $time) : \FluentSmtpLib\phpseclib3\Common\Functions\Strings::packSSH2('NQ2', NET_SFTP_ATTR_ACCESSTIME | NET_SFTP_ATTR_MODIFYTIME, $atime, $time); + $packet = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::packSSH2('s', $filename); + $packet .= $this->version >= 5 ? \pack('N2', 0, NET_SFTP_OPEN_OPEN_EXISTING) : \pack('N', NET_SFTP_OPEN_WRITE | NET_SFTP_OPEN_CREATE | NET_SFTP_OPEN_EXCL); + $packet .= $attr; + $this->send_sftp_packet(NET_SFTP_OPEN, $packet); + $response = $this->get_sftp_packet(); + switch ($this->packet_type) { + case NET_SFTP_HANDLE: + return $this->close_handle(\substr($response, 4)); + case NET_SFTP_STATUS: + $this->logError($response); + break; + default: + throw new \UnexpectedValueException('Expected NET_SFTP_HANDLE or NET_SFTP_STATUS. ' . 'Got packet type: ' . $this->packet_type); + } + return $this->setstat($filename, $attr, \false); + } + /** + * Changes file or directory owner + * + * $uid should be an int for SFTPv3 and a string for SFTPv4+. Ideally the string + * would be of the form "user@dns_domain" but it does not need to be. + * `$sftp->getSupportedVersions()['version']` will return the specific version + * that's being used. + * + * Returns true on success or false on error. + * + * @param string $filename + * @param int|string $uid + * @param bool $recursive + * @return bool + */ + public function chown($filename, $uid, $recursive = \false) + { + /* + quoting , + + "To avoid a representation that is tied to a particular underlying + implementation at the client or server, the use of UTF-8 strings has + been chosen. The string should be of the form "user@dns_domain". + This will allow for a client and server that do not use the same + local representation the ability to translate to a common syntax that + can be interpreted by both. In the case where there is no + translation available to the client or server, the attribute value + must be constructed without the "@"." + + phpseclib _could_ auto append the dns_domain to $uid BUT what if it shouldn't + have one? phpseclib would have no way of knowing so rather than guess phpseclib + will just use whatever value the user provided + */ + $attr = $this->version < 4 ? \pack('N3', NET_SFTP_ATTR_UIDGID, $uid, -1) : \FluentSmtpLib\phpseclib3\Common\Functions\Strings::packSSH2('Nss', NET_SFTP_ATTR_OWNERGROUP, $uid, ''); + return $this->setstat($filename, $attr, $recursive); + } + /** + * Changes file or directory group + * + * $gid should be an int for SFTPv3 and a string for SFTPv4+. Ideally the string + * would be of the form "user@dns_domain" but it does not need to be. + * `$sftp->getSupportedVersions()['version']` will return the specific version + * that's being used. + * + * Returns true on success or false on error. + * + * @param string $filename + * @param int|string $gid + * @param bool $recursive + * @return bool + */ + public function chgrp($filename, $gid, $recursive = \false) + { + $attr = $this->version < 4 ? \pack('N3', NET_SFTP_ATTR_UIDGID, -1, $gid) : \FluentSmtpLib\phpseclib3\Common\Functions\Strings::packSSH2('Nss', NET_SFTP_ATTR_OWNERGROUP, '', $gid); + return $this->setstat($filename, $attr, $recursive); + } + /** + * Set permissions on a file. + * + * Returns the new file permissions on success or false on error. + * If $recursive is true than this just returns true or false. + * + * @param int $mode + * @param string $filename + * @param bool $recursive + * @throws \UnexpectedValueException on receipt of unexpected packets + * @return mixed + */ + public function chmod($mode, $filename, $recursive = \false) + { + if (\is_string($mode) && \is_int($filename)) { + $temp = $mode; + $mode = $filename; + $filename = $temp; + } + $attr = \pack('N2', NET_SFTP_ATTR_PERMISSIONS, $mode & 07777); + if (!$this->setstat($filename, $attr, $recursive)) { + return \false; + } + if ($recursive) { + return \true; + } + $filename = $this->realpath($filename); + // rather than return what the permissions *should* be, we'll return what they actually are. this will also + // tell us if the file actually exists. + // incidentally, SFTPv4+ adds an additional 32-bit integer field - flags - to the following: + $packet = \pack('Na*', \strlen($filename), $filename); + $this->send_sftp_packet(NET_SFTP_STAT, $packet); + $response = $this->get_sftp_packet(); + switch ($this->packet_type) { + case NET_SFTP_ATTRS: + $attrs = $this->parseAttributes($response); + return $attrs['mode']; + case NET_SFTP_STATUS: + $this->logError($response); + return \false; + } + throw new \UnexpectedValueException('Expected NET_SFTP_ATTRS or NET_SFTP_STATUS. ' . 'Got packet type: ' . $this->packet_type); + } + /** + * Sets information about a file + * + * @param string $filename + * @param string $attr + * @param bool $recursive + * @throws \UnexpectedValueException on receipt of unexpected packets + * @return bool + */ + private function setstat($filename, $attr, $recursive) + { + if (!$this->precheck()) { + return \false; + } + $filename = $this->realpath($filename); + if ($filename === \false) { + return \false; + } + $this->remove_from_stat_cache($filename); + if ($recursive) { + $i = 0; + $result = $this->setstat_recursive($filename, $attr, $i); + $this->read_put_responses($i); + return $result; + } + $packet = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::packSSH2('s', $filename); + $packet .= $this->version >= 4 ? \pack('a*Ca*', \substr($attr, 0, 4), NET_SFTP_TYPE_UNKNOWN, \substr($attr, 4)) : $attr; + $this->send_sftp_packet(NET_SFTP_SETSTAT, $packet); + /* + "Because some systems must use separate system calls to set various attributes, it is possible that a failure + response will be returned, but yet some of the attributes may be have been successfully modified. If possible, + servers SHOULD avoid this situation; however, clients MUST be aware that this is possible." + + -- http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.6 + */ + $response = $this->get_sftp_packet(); + if ($this->packet_type != NET_SFTP_STATUS) { + throw new \UnexpectedValueException('Expected NET_SFTP_STATUS. ' . 'Got packet type: ' . $this->packet_type); + } + list($status) = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::unpackSSH2('N', $response); + if ($status != NET_SFTP_STATUS_OK) { + $this->logError($response, $status); + return \false; + } + return \true; + } + /** + * Recursively sets information on directories on the SFTP server + * + * Minimizes directory lookups and SSH_FXP_STATUS requests for speed. + * + * @param string $path + * @param string $attr + * @param int $i + * @return bool + */ + private function setstat_recursive($path, $attr, &$i) + { + if (!$this->read_put_responses($i)) { + return \false; + } + $i = 0; + $entries = $this->readlist($path, \true); + if ($entries === \false || \is_int($entries)) { + return $this->setstat($path, $attr, \false); + } + // normally $entries would have at least . and .. but it might not if the directories + // permissions didn't allow reading + if (empty($entries)) { + return \false; + } + unset($entries['.'], $entries['..']); + foreach ($entries as $filename => $props) { + if (!isset($props['type'])) { + return \false; + } + $temp = $path . '/' . $filename; + if ($props['type'] == NET_SFTP_TYPE_DIRECTORY) { + if (!$this->setstat_recursive($temp, $attr, $i)) { + return \false; + } + } else { + $packet = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::packSSH2('s', $temp); + $packet .= $this->version >= 4 ? \pack('Ca*', NET_SFTP_TYPE_UNKNOWN, $attr) : $attr; + $this->send_sftp_packet(NET_SFTP_SETSTAT, $packet); + $i++; + if ($i >= NET_SFTP_QUEUE_SIZE) { + if (!$this->read_put_responses($i)) { + return \false; + } + $i = 0; + } + } + } + $packet = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::packSSH2('s', $path); + $packet .= $this->version >= 4 ? \pack('Ca*', NET_SFTP_TYPE_UNKNOWN, $attr) : $attr; + $this->send_sftp_packet(NET_SFTP_SETSTAT, $packet); + $i++; + if ($i >= NET_SFTP_QUEUE_SIZE) { + if (!$this->read_put_responses($i)) { + return \false; + } + $i = 0; + } + return \true; + } + /** + * Return the target of a symbolic link + * + * @param string $link + * @throws \UnexpectedValueException on receipt of unexpected packets + * @return mixed + */ + public function readlink($link) + { + if (!$this->precheck()) { + return \false; + } + $link = $this->realpath($link); + $this->send_sftp_packet(NET_SFTP_READLINK, \FluentSmtpLib\phpseclib3\Common\Functions\Strings::packSSH2('s', $link)); + $response = $this->get_sftp_packet(); + switch ($this->packet_type) { + case NET_SFTP_NAME: + break; + case NET_SFTP_STATUS: + $this->logError($response); + return \false; + default: + throw new \UnexpectedValueException('Expected NET_SFTP_NAME or NET_SFTP_STATUS. ' . 'Got packet type: ' . $this->packet_type); + } + list($count) = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::unpackSSH2('N', $response); + // the file isn't a symlink + if (!$count) { + return \false; + } + list($filename) = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::unpackSSH2('s', $response); + return $filename; + } + /** + * Create a symlink + * + * symlink() creates a symbolic link to the existing target with the specified name link. + * + * @param string $target + * @param string $link + * @throws \UnexpectedValueException on receipt of unexpected packets + * @return bool + */ + public function symlink($target, $link) + { + if (!$this->precheck()) { + return \false; + } + //$target = $this->realpath($target); + $link = $this->realpath($link); + /* quoting https://datatracker.ietf.org/doc/html/draft-ietf-secsh-filexfer-09#section-12.1 : + + Changed the SYMLINK packet to be LINK and give it the ability to + create hard links. Also change it's packet number because many + implementation implemented SYMLINK with the arguments reversed. + Hopefully the new argument names make it clear which way is which. + */ + if ($this->version == 6) { + $type = NET_SFTP_LINK; + $packet = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::packSSH2('ssC', $link, $target, 1); + } else { + $type = NET_SFTP_SYMLINK; + /* quoting http://bxr.su/OpenBSD/usr.bin/ssh/PROTOCOL#347 : + + 3.1. sftp: Reversal of arguments to SSH_FXP_SYMLINK + + When OpenSSH's sftp-server was implemented, the order of the arguments + to the SSH_FXP_SYMLINK method was inadvertently reversed. Unfortunately, + the reversal was not noticed until the server was widely deployed. Since + fixing this to follow the specification would cause incompatibility, the + current order was retained. For correct operation, clients should send + SSH_FXP_SYMLINK as follows: + + uint32 id + string targetpath + string linkpath */ + $packet = \substr($this->server_identifier, 0, 15) == 'SSH-2.0-OpenSSH' ? \FluentSmtpLib\phpseclib3\Common\Functions\Strings::packSSH2('ss', $target, $link) : \FluentSmtpLib\phpseclib3\Common\Functions\Strings::packSSH2('ss', $link, $target); + } + $this->send_sftp_packet($type, $packet); + $response = $this->get_sftp_packet(); + if ($this->packet_type != NET_SFTP_STATUS) { + throw new \UnexpectedValueException('Expected NET_SFTP_STATUS. ' . 'Got packet type: ' . $this->packet_type); + } + list($status) = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::unpackSSH2('N', $response); + if ($status != NET_SFTP_STATUS_OK) { + $this->logError($response, $status); + return \false; + } + return \true; + } + /** + * Creates a directory. + * + * @param string $dir + * @param int $mode + * @param bool $recursive + * @return bool + */ + public function mkdir($dir, $mode = -1, $recursive = \false) + { + if (!$this->precheck()) { + return \false; + } + $dir = $this->realpath($dir); + if ($recursive) { + $dirs = \explode('/', \preg_replace('#/(?=/)|/$#', '', $dir)); + if (empty($dirs[0])) { + \array_shift($dirs); + $dirs[0] = '/' . $dirs[0]; + } + for ($i = 0; $i < \count($dirs); $i++) { + $temp = \array_slice($dirs, 0, $i + 1); + $temp = \implode('/', $temp); + $result = $this->mkdir_helper($temp, $mode); + } + return $result; + } + return $this->mkdir_helper($dir, $mode); + } + /** + * Helper function for directory creation + * + * @param string $dir + * @param int $mode + * @return bool + */ + private function mkdir_helper($dir, $mode) + { + // send SSH_FXP_MKDIR without any attributes (that's what the \0\0\0\0 is doing) + $this->send_sftp_packet(NET_SFTP_MKDIR, \FluentSmtpLib\phpseclib3\Common\Functions\Strings::packSSH2('s', $dir) . "\x00\x00\x00\x00"); + $response = $this->get_sftp_packet(); + if ($this->packet_type != NET_SFTP_STATUS) { + throw new \UnexpectedValueException('Expected NET_SFTP_STATUS. ' . 'Got packet type: ' . $this->packet_type); + } + list($status) = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::unpackSSH2('N', $response); + if ($status != NET_SFTP_STATUS_OK) { + $this->logError($response, $status); + return \false; + } + if ($mode !== -1) { + $this->chmod($mode, $dir); + } + return \true; + } + /** + * Removes a directory. + * + * @param string $dir + * @throws \UnexpectedValueException on receipt of unexpected packets + * @return bool + */ + public function rmdir($dir) + { + if (!$this->precheck()) { + return \false; + } + $dir = $this->realpath($dir); + if ($dir === \false) { + return \false; + } + $this->send_sftp_packet(NET_SFTP_RMDIR, \FluentSmtpLib\phpseclib3\Common\Functions\Strings::packSSH2('s', $dir)); + $response = $this->get_sftp_packet(); + if ($this->packet_type != NET_SFTP_STATUS) { + throw new \UnexpectedValueException('Expected NET_SFTP_STATUS. ' . 'Got packet type: ' . $this->packet_type); + } + list($status) = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::unpackSSH2('N', $response); + if ($status != NET_SFTP_STATUS_OK) { + // presumably SSH_FX_NO_SUCH_FILE or SSH_FX_PERMISSION_DENIED? + $this->logError($response, $status); + return \false; + } + $this->remove_from_stat_cache($dir); + // the following will do a soft delete, which would be useful if you deleted a file + // and then tried to do a stat on the deleted file. the above, in contrast, does + // a hard delete + //$this->update_stat_cache($dir, false); + return \true; + } + /** + * Uploads a file to the SFTP server. + * + * By default, \phpseclib3\Net\SFTP::put() does not read from the local filesystem. $data is dumped directly into $remote_file. + * So, for example, if you set $data to 'filename.ext' and then do \phpseclib3\Net\SFTP::get(), you will get a file, twelve bytes + * long, containing 'filename.ext' as its contents. + * + * Setting $mode to self::SOURCE_LOCAL_FILE will change the above behavior. With self::SOURCE_LOCAL_FILE, $remote_file will + * contain as many bytes as filename.ext does on your local filesystem. If your filename.ext is 1MB then that is how + * large $remote_file will be, as well. + * + * Setting $mode to self::SOURCE_CALLBACK will use $data as callback function, which gets only one parameter -- number + * of bytes to return, and returns a string if there is some data or null if there is no more data + * + * If $data is a resource then it'll be used as a resource instead. + * + * Currently, only binary mode is supported. As such, if the line endings need to be adjusted, you will need to take + * care of that, yourself. + * + * $mode can take an additional two parameters - self::RESUME and self::RESUME_START. These are bitwise AND'd with + * $mode. So if you want to resume upload of a 300mb file on the local file system you'd set $mode to the following: + * + * self::SOURCE_LOCAL_FILE | self::RESUME + * + * If you wanted to simply append the full contents of a local file to the full contents of a remote file you'd replace + * self::RESUME with self::RESUME_START. + * + * If $mode & (self::RESUME | self::RESUME_START) then self::RESUME_START will be assumed. + * + * $start and $local_start give you more fine grained control over this process and take precident over self::RESUME + * when they're non-negative. ie. $start could let you write at the end of a file (like self::RESUME) or in the middle + * of one. $local_start could let you start your reading from the end of a file (like self::RESUME_START) or in the + * middle of one. + * + * Setting $local_start to > 0 or $mode | self::RESUME_START doesn't do anything unless $mode | self::SOURCE_LOCAL_FILE. + * + * {@internal ASCII mode for SFTPv4/5/6 can be supported by adding a new function - \phpseclib3\Net\SFTP::setMode().} + * + * @param string $remote_file + * @param string|resource $data + * @param int $mode + * @param int $start + * @param int $local_start + * @param callable|null $progressCallback + * @throws \UnexpectedValueException on receipt of unexpected packets + * @throws \BadFunctionCallException if you're uploading via a callback and the callback function is invalid + * @throws FileNotFoundException if you're uploading via a file and the file doesn't exist + * @return bool + */ + public function put($remote_file, $data, $mode = self::SOURCE_STRING, $start = -1, $local_start = -1, $progressCallback = null) + { + if (!$this->precheck()) { + return \false; + } + $remote_file = $this->realpath($remote_file); + if ($remote_file === \false) { + return \false; + } + $this->remove_from_stat_cache($remote_file); + if ($this->version >= 5) { + $flags = NET_SFTP_OPEN_OPEN_OR_CREATE; + } else { + $flags = NET_SFTP_OPEN_WRITE | NET_SFTP_OPEN_CREATE; + // according to the SFTP specs, NET_SFTP_OPEN_APPEND should "force all writes to append data at the end of the file." + // in practice, it doesn't seem to do that. + //$flags|= ($mode & self::RESUME) ? NET_SFTP_OPEN_APPEND : NET_SFTP_OPEN_TRUNCATE; + } + if ($start >= 0) { + $offset = $start; + } elseif ($mode & (self::RESUME | self::RESUME_START)) { + // if NET_SFTP_OPEN_APPEND worked as it should _size() wouldn't need to be called + $stat = $this->stat($remote_file); + $offset = $stat !== \false && $stat['size'] ? $stat['size'] : 0; + } else { + $offset = 0; + if ($this->version >= 5) { + $flags = NET_SFTP_OPEN_CREATE_TRUNCATE; + } else { + $flags |= NET_SFTP_OPEN_TRUNCATE; + } + } + $this->remove_from_stat_cache($remote_file); + $packet = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::packSSH2('s', $remote_file); + $packet .= $this->version >= 5 ? \pack('N3', 0, $flags, 0) : \pack('N2', $flags, 0); + $this->send_sftp_packet(NET_SFTP_OPEN, $packet); + $response = $this->get_sftp_packet(); + switch ($this->packet_type) { + case NET_SFTP_HANDLE: + $handle = \substr($response, 4); + break; + case NET_SFTP_STATUS: + $this->logError($response); + return \false; + default: + throw new \UnexpectedValueException('Expected NET_SFTP_HANDLE or NET_SFTP_STATUS. ' . 'Got packet type: ' . $this->packet_type); + } + // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.2.3 + $dataCallback = \false; + switch (\true) { + case $mode & self::SOURCE_CALLBACK: + if (!\is_callable($data)) { + throw new \BadFunctionCallException("\$data should be is_callable() if you specify SOURCE_CALLBACK flag"); + } + $dataCallback = $data; + // do nothing + break; + case \is_resource($data): + $mode = $mode & ~self::SOURCE_LOCAL_FILE; + $info = \stream_get_meta_data($data); + if (isset($info['wrapper_type']) && $info['wrapper_type'] == 'PHP' && $info['stream_type'] == 'Input') { + $fp = \fopen('php://memory', 'w+'); + \stream_copy_to_stream($data, $fp); + \rewind($fp); + } else { + $fp = $data; + } + break; + case $mode & self::SOURCE_LOCAL_FILE: + if (!\is_file($data)) { + throw new \FluentSmtpLib\phpseclib3\Exception\FileNotFoundException("{$data} is not a valid file"); + } + $fp = @\fopen($data, 'rb'); + if (!$fp) { + return \false; + } + } + if (isset($fp)) { + $stat = \fstat($fp); + $size = !empty($stat) ? $stat['size'] : 0; + if ($local_start >= 0) { + \fseek($fp, $local_start); + $size -= $local_start; + } elseif ($mode & self::RESUME) { + \fseek($fp, $offset); + $size -= $offset; + } + } elseif ($dataCallback) { + $size = 0; + } else { + $size = \strlen($data); + } + $sent = 0; + $size = $size < 0 ? ($size & 0x7fffffff) + 0x80000000 : $size; + $sftp_packet_size = $this->max_sftp_packet; + // make the SFTP packet be exactly the SFTP packet size by including the bytes in the NET_SFTP_WRITE packets "header" + $sftp_packet_size -= \strlen($handle) + 25; + $i = $j = 0; + while ($dataCallback || ($size === 0 || $sent < $size)) { + if ($dataCallback) { + $temp = $dataCallback($sftp_packet_size); + if (\is_null($temp)) { + break; + } + } else { + $temp = isset($fp) ? \fread($fp, $sftp_packet_size) : \substr($data, $sent, $sftp_packet_size); + if ($temp === \false || $temp === '') { + break; + } + } + $subtemp = $offset + $sent; + $packet = \pack('Na*N3a*', \strlen($handle), $handle, $subtemp / 4294967296, $subtemp, \strlen($temp), $temp); + try { + $this->send_sftp_packet(NET_SFTP_WRITE, $packet, $j); + } catch (\Exception $e) { + if ($mode & self::SOURCE_LOCAL_FILE) { + \fclose($fp); + } + throw $e; + } + $sent += \strlen($temp); + if (\is_callable($progressCallback)) { + $progressCallback($sent); + } + $i++; + $j++; + if ($i == NET_SFTP_UPLOAD_QUEUE_SIZE) { + if (!$this->read_put_responses($i)) { + $i = 0; + break; + } + $i = 0; + } + } + $result = $this->close_handle($handle); + if (!$this->read_put_responses($i)) { + if ($mode & self::SOURCE_LOCAL_FILE) { + \fclose($fp); + } + $this->close_handle($handle); + return \false; + } + if ($mode & \FluentSmtpLib\phpseclib3\Net\SFTP::SOURCE_LOCAL_FILE) { + if (isset($fp) && \is_resource($fp)) { + \fclose($fp); + } + if ($this->preserveTime) { + $stat = \stat($data); + $attr = $this->version < 4 ? \pack('N3', NET_SFTP_ATTR_ACCESSTIME, $stat['atime'], $stat['mtime']) : \FluentSmtpLib\phpseclib3\Common\Functions\Strings::packSSH2('NQ2', NET_SFTP_ATTR_ACCESSTIME | NET_SFTP_ATTR_MODIFYTIME, $stat['atime'], $stat['mtime']); + if (!$this->setstat($remote_file, $attr, \false)) { + throw new \RuntimeException('Error setting file time'); + } + } + } + return $result; + } + /** + * Reads multiple successive SSH_FXP_WRITE responses + * + * Sending an SSH_FXP_WRITE packet and immediately reading its response isn't as efficient as blindly sending out $i + * SSH_FXP_WRITEs, in succession, and then reading $i responses. + * + * @param int $i + * @return bool + * @throws \UnexpectedValueException on receipt of unexpected packets + */ + private function read_put_responses($i) + { + while ($i--) { + $response = $this->get_sftp_packet(); + if ($this->packet_type != NET_SFTP_STATUS) { + throw new \UnexpectedValueException('Expected NET_SFTP_STATUS. ' . 'Got packet type: ' . $this->packet_type); + } + list($status) = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::unpackSSH2('N', $response); + if ($status != NET_SFTP_STATUS_OK) { + $this->logError($response, $status); + break; + } + } + return $i < 0; + } + /** + * Close handle + * + * @param string $handle + * @return bool + * @throws \UnexpectedValueException on receipt of unexpected packets + */ + private function close_handle($handle) + { + $this->send_sftp_packet(NET_SFTP_CLOSE, \pack('Na*', \strlen($handle), $handle)); + // "The client MUST release all resources associated with the handle regardless of the status." + // -- http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.1.3 + $response = $this->get_sftp_packet(); + if ($this->packet_type != NET_SFTP_STATUS) { + throw new \UnexpectedValueException('Expected NET_SFTP_STATUS. ' . 'Got packet type: ' . $this->packet_type); + } + list($status) = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::unpackSSH2('N', $response); + if ($status != NET_SFTP_STATUS_OK) { + $this->logError($response, $status); + return \false; + } + return \true; + } + /** + * Downloads a file from the SFTP server. + * + * Returns a string containing the contents of $remote_file if $local_file is left undefined or a boolean false if + * the operation was unsuccessful. If $local_file is defined, returns true or false depending on the success of the + * operation. + * + * $offset and $length can be used to download files in chunks. + * + * @param string $remote_file + * @param string|bool|resource|callable $local_file + * @param int $offset + * @param int $length + * @param callable|null $progressCallback + * @throws \UnexpectedValueException on receipt of unexpected packets + * @return string|bool + */ + public function get($remote_file, $local_file = \false, $offset = 0, $length = -1, $progressCallback = null) + { + if (!$this->precheck()) { + return \false; + } + $remote_file = $this->realpath($remote_file); + if ($remote_file === \false) { + return \false; + } + $packet = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::packSSH2('s', $remote_file); + $packet .= $this->version >= 5 ? \pack('N3', 0, NET_SFTP_OPEN_OPEN_EXISTING, 0) : \pack('N2', NET_SFTP_OPEN_READ, 0); + $this->send_sftp_packet(NET_SFTP_OPEN, $packet); + $response = $this->get_sftp_packet(); + switch ($this->packet_type) { + case NET_SFTP_HANDLE: + $handle = \substr($response, 4); + break; + case NET_SFTP_STATUS: + // presumably SSH_FX_NO_SUCH_FILE or SSH_FX_PERMISSION_DENIED + $this->logError($response); + return \false; + default: + throw new \UnexpectedValueException('Expected NET_SFTP_HANDLE or NET_SFTP_STATUS. ' . 'Got packet type: ' . $this->packet_type); + } + if (\is_resource($local_file)) { + $fp = $local_file; + $stat = \fstat($fp); + $res_offset = $stat['size']; + } else { + $res_offset = 0; + if ($local_file !== \false && !\is_callable($local_file)) { + $fp = \fopen($local_file, 'wb'); + if (!$fp) { + return \false; + } + } else { + $content = ''; + } + } + $fclose_check = $local_file !== \false && !\is_callable($local_file) && !\is_resource($local_file); + $start = $offset; + $read = 0; + while (\true) { + $i = 0; + while ($i < NET_SFTP_QUEUE_SIZE && ($length < 0 || $read < $length)) { + $tempoffset = $start + $read; + $packet_size = $length > 0 ? \min($this->max_sftp_packet, $length - $read) : $this->max_sftp_packet; + $packet = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::packSSH2('sN3', $handle, $tempoffset / 4294967296, $tempoffset, $packet_size); + try { + $this->send_sftp_packet(NET_SFTP_READ, $packet, $i); + } catch (\Exception $e) { + if ($fclose_check) { + \fclose($fp); + } + throw $e; + } + $packet = null; + $read += $packet_size; + $i++; + } + if (!$i) { + break; + } + $packets_sent = $i - 1; + $clear_responses = \false; + while ($i > 0) { + $i--; + if ($clear_responses) { + $this->get_sftp_packet($packets_sent - $i); + continue; + } else { + $response = $this->get_sftp_packet($packets_sent - $i); + } + switch ($this->packet_type) { + case NET_SFTP_DATA: + $temp = \substr($response, 4); + $offset += \strlen($temp); + if ($local_file === \false) { + $content .= $temp; + } elseif (\is_callable($local_file)) { + $local_file($temp); + } else { + \fputs($fp, $temp); + } + if (\is_callable($progressCallback)) { + \call_user_func($progressCallback, $offset); + } + $temp = null; + break; + case NET_SFTP_STATUS: + // could, in theory, return false if !strlen($content) but we'll hold off for the time being + $this->logError($response); + $clear_responses = \true; + // don't break out of the loop yet, so we can read the remaining responses + break; + default: + if ($fclose_check) { + \fclose($fp); + } + if ($this->channel_close) { + $this->partial_init = \false; + $this->init_sftp_connection(); + return \false; + } else { + throw new \UnexpectedValueException('Expected NET_SFTP_DATA or NET_SFTP_STATUS. ' . 'Got packet type: ' . $this->packet_type); + } + } + $response = null; + } + if ($clear_responses) { + break; + } + } + if ($fclose_check) { + \fclose($fp); + if ($this->preserveTime) { + $stat = $this->stat($remote_file); + \touch($local_file, $stat['mtime'], $stat['atime']); + } + } + if (!$this->close_handle($handle)) { + return \false; + } + // if $content isn't set that means a file was written to + return isset($content) ? $content : \true; + } + /** + * Deletes a file on the SFTP server. + * + * @param string $path + * @param bool $recursive + * @return bool + * @throws \UnexpectedValueException on receipt of unexpected packets + */ + public function delete($path, $recursive = \true) + { + if (!$this->precheck()) { + return \false; + } + if (\is_object($path)) { + // It's an object. Cast it as string before we check anything else. + $path = (string) $path; + } + if (!\is_string($path) || $path == '') { + return \false; + } + $path = $this->realpath($path); + if ($path === \false) { + return \false; + } + // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.3 + $this->send_sftp_packet(NET_SFTP_REMOVE, \pack('Na*', \strlen($path), $path)); + $response = $this->get_sftp_packet(); + if ($this->packet_type != NET_SFTP_STATUS) { + throw new \UnexpectedValueException('Expected NET_SFTP_STATUS. ' . 'Got packet type: ' . $this->packet_type); + } + // if $status isn't SSH_FX_OK it's probably SSH_FX_NO_SUCH_FILE or SSH_FX_PERMISSION_DENIED + list($status) = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::unpackSSH2('N', $response); + if ($status != NET_SFTP_STATUS_OK) { + $this->logError($response, $status); + if (!$recursive) { + return \false; + } + $i = 0; + $result = $this->delete_recursive($path, $i); + $this->read_put_responses($i); + return $result; + } + $this->remove_from_stat_cache($path); + return \true; + } + /** + * Recursively deletes directories on the SFTP server + * + * Minimizes directory lookups and SSH_FXP_STATUS requests for speed. + * + * @param string $path + * @param int $i + * @return bool + */ + private function delete_recursive($path, &$i) + { + if (!$this->read_put_responses($i)) { + return \false; + } + $i = 0; + $entries = $this->readlist($path, \true); + // The folder does not exist at all, so we cannot delete it. + if ($entries === NET_SFTP_STATUS_NO_SUCH_FILE) { + return \false; + } + // Normally $entries would have at least . and .. but it might not if the directories + // permissions didn't allow reading. If this happens then default to an empty list of files. + if ($entries === \false || \is_int($entries)) { + $entries = []; + } + unset($entries['.'], $entries['..']); + foreach ($entries as $filename => $props) { + if (!isset($props['type'])) { + return \false; + } + $temp = $path . '/' . $filename; + if ($props['type'] == NET_SFTP_TYPE_DIRECTORY) { + if (!$this->delete_recursive($temp, $i)) { + return \false; + } + } else { + $this->send_sftp_packet(NET_SFTP_REMOVE, \FluentSmtpLib\phpseclib3\Common\Functions\Strings::packSSH2('s', $temp)); + $this->remove_from_stat_cache($temp); + $i++; + if ($i >= NET_SFTP_QUEUE_SIZE) { + if (!$this->read_put_responses($i)) { + return \false; + } + $i = 0; + } + } + } + $this->send_sftp_packet(NET_SFTP_RMDIR, \FluentSmtpLib\phpseclib3\Common\Functions\Strings::packSSH2('s', $path)); + $this->remove_from_stat_cache($path); + $i++; + if ($i >= NET_SFTP_QUEUE_SIZE) { + if (!$this->read_put_responses($i)) { + return \false; + } + $i = 0; + } + return \true; + } + /** + * Checks whether a file or directory exists + * + * @param string $path + * @return bool + */ + public function file_exists($path) + { + if ($this->use_stat_cache) { + if (!$this->precheck()) { + return \false; + } + $path = $this->realpath($path); + $result = $this->query_stat_cache($path); + if (isset($result)) { + // return true if $result is an array or if it's an stdClass object + return $result !== \false; + } + } + return $this->stat($path) !== \false; + } + /** + * Tells whether the filename is a directory + * + * @param string $path + * @return bool + */ + public function is_dir($path) + { + $result = $this->get_stat_cache_prop($path, 'type'); + if ($result === \false) { + return \false; + } + return $result === NET_SFTP_TYPE_DIRECTORY; + } + /** + * Tells whether the filename is a regular file + * + * @param string $path + * @return bool + */ + public function is_file($path) + { + $result = $this->get_stat_cache_prop($path, 'type'); + if ($result === \false) { + return \false; + } + return $result === NET_SFTP_TYPE_REGULAR; + } + /** + * Tells whether the filename is a symbolic link + * + * @param string $path + * @return bool + */ + public function is_link($path) + { + $result = $this->get_lstat_cache_prop($path, 'type'); + if ($result === \false) { + return \false; + } + return $result === NET_SFTP_TYPE_SYMLINK; + } + /** + * Tells whether a file exists and is readable + * + * @param string $path + * @return bool + */ + public function is_readable($path) + { + if (!$this->precheck()) { + return \false; + } + $packet = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::packSSH2('sNN', $this->realpath($path), NET_SFTP_OPEN_READ, 0); + $this->send_sftp_packet(NET_SFTP_OPEN, $packet); + $response = $this->get_sftp_packet(); + switch ($this->packet_type) { + case NET_SFTP_HANDLE: + return \true; + case NET_SFTP_STATUS: + // presumably SSH_FX_NO_SUCH_FILE or SSH_FX_PERMISSION_DENIED + return \false; + default: + throw new \UnexpectedValueException('Expected NET_SFTP_HANDLE or NET_SFTP_STATUS. ' . 'Got packet type: ' . $this->packet_type); + } + } + /** + * Tells whether the filename is writable + * + * @param string $path + * @return bool + */ + public function is_writable($path) + { + if (!$this->precheck()) { + return \false; + } + $packet = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::packSSH2('sNN', $this->realpath($path), NET_SFTP_OPEN_WRITE, 0); + $this->send_sftp_packet(NET_SFTP_OPEN, $packet); + $response = $this->get_sftp_packet(); + switch ($this->packet_type) { + case NET_SFTP_HANDLE: + return \true; + case NET_SFTP_STATUS: + // presumably SSH_FX_NO_SUCH_FILE or SSH_FX_PERMISSION_DENIED + return \false; + default: + throw new \UnexpectedValueException('Expected SSH_FXP_HANDLE or SSH_FXP_STATUS. ' . 'Got packet type: ' . $this->packet_type); + } + } + /** + * Tells whether the filename is writeable + * + * Alias of is_writable + * + * @param string $path + * @return bool + */ + public function is_writeable($path) + { + return $this->is_writable($path); + } + /** + * Gets last access time of file + * + * @param string $path + * @return mixed + */ + public function fileatime($path) + { + return $this->get_stat_cache_prop($path, 'atime'); + } + /** + * Gets file modification time + * + * @param string $path + * @return mixed + */ + public function filemtime($path) + { + return $this->get_stat_cache_prop($path, 'mtime'); + } + /** + * Gets file permissions + * + * @param string $path + * @return mixed + */ + public function fileperms($path) + { + return $this->get_stat_cache_prop($path, 'mode'); + } + /** + * Gets file owner + * + * @param string $path + * @return mixed + */ + public function fileowner($path) + { + return $this->get_stat_cache_prop($path, 'uid'); + } + /** + * Gets file group + * + * @param string $path + * @return mixed + */ + public function filegroup($path) + { + return $this->get_stat_cache_prop($path, 'gid'); + } + /** + * Recursively go through rawlist() output to get the total filesize + * + * @return int + */ + private static function recursiveFilesize(array $files) + { + $size = 0; + foreach ($files as $name => $file) { + if ($name == '.' || $name == '..') { + continue; + } + $size += \is_array($file) ? self::recursiveFilesize($file) : $file->size; + } + return $size; + } + /** + * Gets file size + * + * @param string $path + * @param bool $recursive + * @return mixed + */ + public function filesize($path, $recursive = \false) + { + return !$recursive || $this->filetype($path) != 'dir' ? $this->get_stat_cache_prop($path, 'size') : self::recursiveFilesize($this->rawlist($path, \true)); + } + /** + * Gets file type + * + * @param string $path + * @return string|false + */ + public function filetype($path) + { + $type = $this->get_stat_cache_prop($path, 'type'); + if ($type === \false) { + return \false; + } + switch ($type) { + case NET_SFTP_TYPE_BLOCK_DEVICE: + return 'block'; + case NET_SFTP_TYPE_CHAR_DEVICE: + return 'char'; + case NET_SFTP_TYPE_DIRECTORY: + return 'dir'; + case NET_SFTP_TYPE_FIFO: + return 'fifo'; + case NET_SFTP_TYPE_REGULAR: + return 'file'; + case NET_SFTP_TYPE_SYMLINK: + return 'link'; + default: + return \false; + } + } + /** + * Return a stat properity + * + * Uses cache if appropriate. + * + * @param string $path + * @param string $prop + * @return mixed + */ + private function get_stat_cache_prop($path, $prop) + { + return $this->get_xstat_cache_prop($path, $prop, 'stat'); + } + /** + * Return an lstat properity + * + * Uses cache if appropriate. + * + * @param string $path + * @param string $prop + * @return mixed + */ + private function get_lstat_cache_prop($path, $prop) + { + return $this->get_xstat_cache_prop($path, $prop, 'lstat'); + } + /** + * Return a stat or lstat properity + * + * Uses cache if appropriate. + * + * @param string $path + * @param string $prop + * @param string $type + * @return mixed + */ + private function get_xstat_cache_prop($path, $prop, $type) + { + if (!$this->precheck()) { + return \false; + } + if ($this->use_stat_cache) { + $path = $this->realpath($path); + $result = $this->query_stat_cache($path); + if (\is_object($result) && isset($result->{$type})) { + return $result->{$type}[$prop]; + } + } + $result = $this->{$type}($path); + if ($result === \false || !isset($result[$prop])) { + return \false; + } + return $result[$prop]; + } + /** + * Renames a file or a directory on the SFTP server. + * + * If the file already exists this will return false + * + * @param string $oldname + * @param string $newname + * @return bool + * @throws \UnexpectedValueException on receipt of unexpected packets + */ + public function rename($oldname, $newname) + { + if (!$this->precheck()) { + return \false; + } + $oldname = $this->realpath($oldname); + $newname = $this->realpath($newname); + if ($oldname === \false || $newname === \false) { + return \false; + } + // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.3 + $packet = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::packSSH2('ss', $oldname, $newname); + if ($this->version >= 5) { + /* quoting https://datatracker.ietf.org/doc/html/draft-ietf-secsh-filexfer-05#section-6.5 , + + 'flags' is 0 or a combination of: + + SSH_FXP_RENAME_OVERWRITE 0x00000001 + SSH_FXP_RENAME_ATOMIC 0x00000002 + SSH_FXP_RENAME_NATIVE 0x00000004 + + (none of these are currently supported) */ + $packet .= "\x00\x00\x00\x00"; + } + $this->send_sftp_packet(NET_SFTP_RENAME, $packet); + $response = $this->get_sftp_packet(); + if ($this->packet_type != NET_SFTP_STATUS) { + throw new \UnexpectedValueException('Expected NET_SFTP_STATUS. ' . 'Got packet type: ' . $this->packet_type); + } + // if $status isn't SSH_FX_OK it's probably SSH_FX_NO_SUCH_FILE or SSH_FX_PERMISSION_DENIED + list($status) = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::unpackSSH2('N', $response); + if ($status != NET_SFTP_STATUS_OK) { + $this->logError($response, $status); + return \false; + } + // don't move the stat cache entry over since this operation could very well change the + // atime and mtime attributes + //$this->update_stat_cache($newname, $this->query_stat_cache($oldname)); + $this->remove_from_stat_cache($oldname); + $this->remove_from_stat_cache($newname); + return \true; + } + /** + * Parse Time + * + * See '7.7. Times' of draft-ietf-secsh-filexfer-13 for more info. + * + * @param string $key + * @param int $flags + * @param string $response + * @return array + */ + private function parseTime($key, $flags, &$response) + { + $attr = []; + list($attr[$key]) = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::unpackSSH2('Q', $response); + if ($flags & NET_SFTP_ATTR_SUBSECOND_TIMES) { + list($attr[$key . '-nseconds']) = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::unpackSSH2('N', $response); + } + return $attr; + } + /** + * Parse Attributes + * + * See '7. File Attributes' of draft-ietf-secsh-filexfer-13 for more info. + * + * @param string $response + * @return array + */ + protected function parseAttributes(&$response) + { + $attr = []; + if ($this->version >= 4) { + list($flags, $attr['type']) = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::unpackSSH2('NC', $response); + } else { + list($flags) = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::unpackSSH2('N', $response); + } + foreach (self::$attributes as $key => $value) { + switch ($flags & $key) { + case NET_SFTP_ATTR_UIDGID: + if ($this->version > 3) { + continue 2; + } + break; + case NET_SFTP_ATTR_CREATETIME: + case NET_SFTP_ATTR_MODIFYTIME: + case NET_SFTP_ATTR_ACL: + case NET_SFTP_ATTR_OWNERGROUP: + case NET_SFTP_ATTR_SUBSECOND_TIMES: + if ($this->version < 4) { + continue 2; + } + break; + case NET_SFTP_ATTR_BITS: + if ($this->version < 5) { + continue 2; + } + break; + case NET_SFTP_ATTR_ALLOCATION_SIZE: + case NET_SFTP_ATTR_TEXT_HINT: + case NET_SFTP_ATTR_MIME_TYPE: + case NET_SFTP_ATTR_LINK_COUNT: + case NET_SFTP_ATTR_UNTRANSLATED_NAME: + case NET_SFTP_ATTR_CTIME: + if ($this->version < 6) { + continue 2; + } + } + switch ($flags & $key) { + case NET_SFTP_ATTR_SIZE: + // 0x00000001 + // The size attribute is defined as an unsigned 64-bit integer. + // The following will use floats on 32-bit platforms, if necessary. + // As can be seen in the BigInteger class, floats are generally + // IEEE 754 binary64 "double precision" on such platforms and + // as such can represent integers of at least 2^50 without loss + // of precision. Interpreted in filesize, 2^50 bytes = 1024 TiB. + list($attr['size']) = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::unpackSSH2('Q', $response); + break; + case NET_SFTP_ATTR_UIDGID: + // 0x00000002 (SFTPv3 only) + list($attr['uid'], $attr['gid']) = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::unpackSSH2('NN', $response); + break; + case NET_SFTP_ATTR_PERMISSIONS: + // 0x00000004 + list($attr['mode']) = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::unpackSSH2('N', $response); + $fileType = $this->parseMode($attr['mode']); + if ($this->version < 4 && $fileType !== \false) { + $attr += ['type' => $fileType]; + } + break; + case NET_SFTP_ATTR_ACCESSTIME: + // 0x00000008 + if ($this->version >= 4) { + $attr += $this->parseTime('atime', $flags, $response); + break; + } + list($attr['atime'], $attr['mtime']) = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::unpackSSH2('NN', $response); + break; + case NET_SFTP_ATTR_CREATETIME: + // 0x00000010 (SFTPv4+) + $attr += $this->parseTime('createtime', $flags, $response); + break; + case NET_SFTP_ATTR_MODIFYTIME: + // 0x00000020 + $attr += $this->parseTime('mtime', $flags, $response); + break; + case NET_SFTP_ATTR_ACL: + // 0x00000040 + // access control list + // see https://datatracker.ietf.org/doc/html/draft-ietf-secsh-filexfer-04#section-5.7 + // currently unsupported + list($count) = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::unpackSSH2('N', $response); + for ($i = 0; $i < $count; $i++) { + list($type, $flag, $mask, $who) = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::unpackSSH2('N3s', $result); + } + break; + case NET_SFTP_ATTR_OWNERGROUP: + // 0x00000080 + list($attr['owner'], $attr['$group']) = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::unpackSSH2('ss', $response); + break; + case NET_SFTP_ATTR_SUBSECOND_TIMES: + // 0x00000100 + break; + case NET_SFTP_ATTR_BITS: + // 0x00000200 (SFTPv5+) + // see https://datatracker.ietf.org/doc/html/draft-ietf-secsh-filexfer-05#section-5.8 + // currently unsupported + // tells if you file is: + // readonly, system, hidden, case inensitive, archive, encrypted, compressed, sparse + // append only, immutable, sync + list($attrib_bits, $attrib_bits_valid) = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::unpackSSH2('N2', $response); + // if we were actually gonna implement the above it ought to be + // $attr['attrib-bits'] and $attr['attrib-bits-valid'] + // eg. - instead of _ + break; + case NET_SFTP_ATTR_ALLOCATION_SIZE: + // 0x00000400 (SFTPv6+) + // see https://datatracker.ietf.org/doc/html/draft-ietf-secsh-filexfer-13#section-7.4 + // represents the number of bytes that the file consumes on the disk. will + // usually be larger than the 'size' field + list($attr['allocation-size']) = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::unpackSSH2('Q', $response); + break; + case NET_SFTP_ATTR_TEXT_HINT: + // 0x00000800 + // https://datatracker.ietf.org/doc/html/draft-ietf-secsh-filexfer-13#section-7.10 + // currently unsupported + // tells if file is "known text", "guessed text", "known binary", "guessed binary" + list($text_hint) = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::unpackSSH2('C', $response); + // the above should be $attr['text-hint'] + break; + case NET_SFTP_ATTR_MIME_TYPE: + // 0x00001000 + // see https://datatracker.ietf.org/doc/html/draft-ietf-secsh-filexfer-13#section-7.11 + list($attr['mime-type']) = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::unpackSSH2('s', $response); + break; + case NET_SFTP_ATTR_LINK_COUNT: + // 0x00002000 + // see https://datatracker.ietf.org/doc/html/draft-ietf-secsh-filexfer-13#section-7.12 + list($attr['link-count']) = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::unpackSSH2('N', $response); + break; + case NET_SFTP_ATTR_UNTRANSLATED_NAME: + // 0x00004000 + // see https://datatracker.ietf.org/doc/html/draft-ietf-secsh-filexfer-13#section-7.13 + list($attr['untranslated-name']) = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::unpackSSH2('s', $response); + break; + case NET_SFTP_ATTR_CTIME: + // 0x00008000 + // 'ctime' contains the last time the file attributes were changed. The + // exact meaning of this field depends on the server. + $attr += $this->parseTime('ctime', $flags, $response); + break; + case NET_SFTP_ATTR_EXTENDED: + // 0x80000000 + list($count) = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::unpackSSH2('N', $response); + for ($i = 0; $i < $count; $i++) { + list($key, $value) = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::unpackSSH2('ss', $response); + $attr[$key] = $value; + } + } + } + return $attr; + } + /** + * Attempt to identify the file type + * + * Quoting the SFTP RFC, "Implementations MUST NOT send bits that are not defined" but they seem to anyway + * + * @param int $mode + * @return int + */ + private function parseMode($mode) + { + // values come from http://lxr.free-electrons.com/source/include/uapi/linux/stat.h#L12 + // see, also, http://linux.die.net/man/2/stat + switch ($mode & 0170000) { + // ie. 1111 0000 0000 0000 + case 00: + // no file type specified - figure out the file type using alternative means + return \false; + case 040000: + return NET_SFTP_TYPE_DIRECTORY; + case 0100000: + return NET_SFTP_TYPE_REGULAR; + case 0120000: + return NET_SFTP_TYPE_SYMLINK; + // new types introduced in SFTPv5+ + // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-05#section-5.2 + case 010000: + // named pipe (fifo) + return NET_SFTP_TYPE_FIFO; + case 020000: + // character special + return NET_SFTP_TYPE_CHAR_DEVICE; + case 060000: + // block special + return NET_SFTP_TYPE_BLOCK_DEVICE; + case 0140000: + // socket + return NET_SFTP_TYPE_SOCKET; + case 0160000: + // whiteout + // "SPECIAL should be used for files that are of + // a known type which cannot be expressed in the protocol" + return NET_SFTP_TYPE_SPECIAL; + default: + return NET_SFTP_TYPE_UNKNOWN; + } + } + /** + * Parse Longname + * + * SFTPv3 doesn't provide any easy way of identifying a file type. You could try to open + * a file as a directory and see if an error is returned or you could try to parse the + * SFTPv3-specific longname field of the SSH_FXP_NAME packet. That's what this function does. + * The result is returned using the + * {@link http://tools.ietf.org/html/draft-ietf-secsh-filexfer-04#section-5.2 SFTPv4 type constants}. + * + * If the longname is in an unrecognized format bool(false) is returned. + * + * @param string $longname + * @return mixed + */ + private function parseLongname($longname) + { + // http://en.wikipedia.org/wiki/Unix_file_types + // http://en.wikipedia.org/wiki/Filesystem_permissions#Notation_of_traditional_Unix_permissions + if (\preg_match('#^[^/]([r-][w-][xstST-]){3}#', $longname)) { + switch ($longname[0]) { + case '-': + return NET_SFTP_TYPE_REGULAR; + case 'd': + return NET_SFTP_TYPE_DIRECTORY; + case 'l': + return NET_SFTP_TYPE_SYMLINK; + default: + return NET_SFTP_TYPE_SPECIAL; + } + } + return \false; + } + /** + * Sends SFTP Packets + * + * See '6. General Packet Format' of draft-ietf-secsh-filexfer-13 for more info. + * + * @param int $type + * @param string $data + * @param int $request_id + * @see self::_get_sftp_packet() + * @see self::send_channel_packet() + * @return void + */ + private function send_sftp_packet($type, $data, $request_id = 1) + { + // in SSH2.php the timeout is cumulative per function call. eg. exec() will + // timeout after 10s. but for SFTP.php it's cumulative per packet + $this->curTimeout = $this->timeout; + $this->is_timeout = \false; + $packet = $this->use_request_id ? \pack('NCNa*', \strlen($data) + 5, $type, $request_id, $data) : \pack('NCa*', \strlen($data) + 1, $type, $data); + $start = \microtime(\true); + $this->send_channel_packet(self::CHANNEL, $packet); + $stop = \microtime(\true); + if (\defined('FluentSmtpLib\\NET_SFTP_LOGGING')) { + $packet_type = '-> ' . self::$packet_types[$type] . ' (' . \round($stop - $start, 4) . 's)'; + $this->append_log($packet_type, $data); + } + } + /** + * Resets the SFTP channel for re-use + */ + private function reset_sftp() + { + $this->use_request_id = \false; + $this->pwd = \false; + $this->requestBuffer = []; + $this->partial_init = \false; + } + /** + * Resets a connection for re-use + */ + protected function reset_connection() + { + parent::reset_connection(); + $this->reset_sftp(); + } + /** + * Receives SFTP Packets + * + * See '6. General Packet Format' of draft-ietf-secsh-filexfer-13 for more info. + * + * Incidentally, the number of SSH_MSG_CHANNEL_DATA messages has no bearing on the number of SFTP packets present. + * There can be one SSH_MSG_CHANNEL_DATA messages containing two SFTP packets or there can be two SSH_MSG_CHANNEL_DATA + * messages containing one SFTP packet. + * + * @see self::_send_sftp_packet() + * @return string + */ + private function get_sftp_packet($request_id = null) + { + $this->channel_close = \false; + if (isset($request_id) && isset($this->requestBuffer[$request_id])) { + $this->packet_type = $this->requestBuffer[$request_id]['packet_type']; + $temp = $this->requestBuffer[$request_id]['packet']; + unset($this->requestBuffer[$request_id]); + return $temp; + } + // in SSH2.php the timeout is cumulative per function call. eg. exec() will + // timeout after 10s. but for SFTP.php it's cumulative per packet + $this->curTimeout = $this->timeout; + $this->is_timeout = \false; + $start = \microtime(\true); + // SFTP packet length + while (\strlen($this->packet_buffer) < 4) { + $temp = $this->get_channel_packet(self::CHANNEL, \true); + if ($temp === \true) { + if ($this->channel_status[self::CHANNEL] === NET_SSH2_MSG_CHANNEL_CLOSE) { + $this->channel_close = \true; + } + $this->packet_type = \false; + $this->packet_buffer = ''; + return \false; + } + $this->packet_buffer .= $temp; + } + if (\strlen($this->packet_buffer) < 4) { + throw new \RuntimeException('Packet is too small'); + } + \extract(\unpack('Nlength', \FluentSmtpLib\phpseclib3\Common\Functions\Strings::shift($this->packet_buffer, 4))); + /** @var integer $length */ + $tempLength = $length; + $tempLength -= \strlen($this->packet_buffer); + // 256 * 1024 is what SFTP_MAX_MSG_LENGTH is set to in OpenSSH's sftp-common.h + if (!$this->allow_arbitrary_length_packets && !$this->use_request_id && $tempLength > 256 * 1024) { + throw new \RuntimeException('Invalid Size'); + } + // SFTP packet type and data payload + while ($tempLength > 0) { + $temp = $this->get_channel_packet(self::CHANNEL, \true); + if ($temp === \true) { + if ($this->channel_status[self::CHANNEL] === NET_SSH2_MSG_CHANNEL_CLOSE) { + $this->channel_close = \true; + } + $this->packet_type = \false; + $this->packet_buffer = ''; + return \false; + } + $this->packet_buffer .= $temp; + $tempLength -= \strlen($temp); + } + $stop = \microtime(\true); + $this->packet_type = \ord(\FluentSmtpLib\phpseclib3\Common\Functions\Strings::shift($this->packet_buffer)); + if ($this->use_request_id) { + \extract(\unpack('Npacket_id', \FluentSmtpLib\phpseclib3\Common\Functions\Strings::shift($this->packet_buffer, 4))); + // remove the request id + $length -= 5; + // account for the request id and the packet type + } else { + $length -= 1; + // account for the packet type + } + $packet = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::shift($this->packet_buffer, $length); + if (\defined('FluentSmtpLib\\NET_SFTP_LOGGING')) { + $packet_type = '<- ' . self::$packet_types[$this->packet_type] . ' (' . \round($stop - $start, 4) . 's)'; + $this->append_log($packet_type, $packet); + } + if (isset($request_id) && $this->use_request_id && $packet_id != $request_id) { + $this->requestBuffer[$packet_id] = ['packet_type' => $this->packet_type, 'packet' => $packet]; + return $this->get_sftp_packet($request_id); + } + return $packet; + } + /** + * Logs data packets + * + * Makes sure that only the last 1MB worth of packets will be logged + * + * @param string $message_number + * @param string $message + */ + private function append_log($message_number, $message) + { + $this->append_log_helper(NET_SFTP_LOGGING, $message_number, $message, $this->packet_type_log, $this->packet_log, $this->log_size, $this->realtime_log_file, $this->realtime_log_wrap, $this->realtime_log_size); + } + /** + * Returns a log of the packets that have been sent and received. + * + * Returns a string if NET_SFTP_LOGGING == self::LOG_COMPLEX, an array if NET_SFTP_LOGGING == self::LOG_SIMPLE and false if !defined('NET_SFTP_LOGGING') + * + * @return array|string|false + */ + public function getSFTPLog() + { + if (!\defined('FluentSmtpLib\\NET_SFTP_LOGGING')) { + return \false; + } + switch (NET_SFTP_LOGGING) { + case self::LOG_COMPLEX: + return $this->format_log($this->packet_log, $this->packet_type_log); + break; + //case self::LOG_SIMPLE: + default: + return $this->packet_type_log; + } + } + /** + * Returns all errors on the SFTP layer + * + * @return array + */ + public function getSFTPErrors() + { + return $this->sftp_errors; + } + /** + * Returns the last error on the SFTP layer + * + * @return string + */ + public function getLastSFTPError() + { + return \count($this->sftp_errors) ? $this->sftp_errors[\count($this->sftp_errors) - 1] : ''; + } + /** + * Get supported SFTP versions + * + * @return array + */ + public function getSupportedVersions() + { + if (!($this->bitmap & \FluentSmtpLib\phpseclib3\Net\SSH2::MASK_LOGIN)) { + return \false; + } + if (!$this->partial_init) { + $this->partial_init_sftp_connection(); + } + $temp = ['version' => $this->defaultVersion]; + if (isset($this->extensions['versions'])) { + $temp['extensions'] = $this->extensions['versions']; + } + return $temp; + } + /** + * Get supported SFTP extensions + * + * @return array + */ + public function getSupportedExtensions() + { + if (!($this->bitmap & \FluentSmtpLib\phpseclib3\Net\SSH2::MASK_LOGIN)) { + return \false; + } + if (!$this->partial_init) { + $this->partial_init_sftp_connection(); + } + return $this->extensions; + } + /** + * Get supported SFTP versions + * + * @return int|false + */ + public function getNegotiatedVersion() + { + if (!$this->precheck()) { + return \false; + } + return $this->version; + } + /** + * Set preferred version + * + * If you're preferred version isn't supported then the highest supported + * version of SFTP will be utilized. Set to null or false or int(0) to + * unset the preferred version + * + * @param int $version + */ + public function setPreferredVersion($version) + { + $this->preferredVersion = $version; + } + /** + * Disconnect + * + * @param int $reason + * @return false + */ + protected function disconnect_helper($reason) + { + $this->pwd = \false; + return parent::disconnect_helper($reason); + } + /** + * Enable Date Preservation + * + */ + public function enableDatePreservation() + { + $this->preserveTime = \true; + } + /** + * Disable Date Preservation + * + */ + public function disableDatePreservation() + { + $this->preserveTime = \false; + } + /** + * POSIX Rename + * + * Where rename() fails "if there already exists a file with the name specified by newpath" + * (draft-ietf-secsh-filexfer-02#section-6.5), posix_rename() overwrites the existing file in an atomic fashion. + * ie. "there is no observable instant in time where the name does not refer to either the old or the new file" + * (draft-ietf-secsh-filexfer-13#page-39). + * + * @param string $oldname + * @param string $newname + * @return bool + */ + public function posix_rename($oldname, $newname) + { + if (!$this->precheck()) { + return \false; + } + $oldname = $this->realpath($oldname); + $newname = $this->realpath($newname); + if ($oldname === \false || $newname === \false) { + return \false; + } + if ($this->version >= 5) { + $packet = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::packSSH2('ssN', $oldname, $newname, 2); + // 2 = SSH_FXP_RENAME_ATOMIC + $this->send_sftp_packet(NET_SFTP_RENAME, $packet); + } elseif (isset($this->extensions['posix-rename@openssh.com']) && $this->extensions['posix-rename@openssh.com'] === '1') { + $packet = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::packSSH2('sss', 'posix-rename@openssh.com', $oldname, $newname); + $this->send_sftp_packet(NET_SFTP_EXTENDED, $packet); + } else { + throw new \RuntimeException("Extension 'posix-rename@openssh.com' is not supported by the server. " . "Call getSupportedVersions() to see a list of supported extension"); + } + $response = $this->get_sftp_packet(); + if ($this->packet_type != NET_SFTP_STATUS) { + throw new \UnexpectedValueException('Expected NET_SFTP_STATUS. ' . 'Got packet type: ' . $this->packet_type); + } + // if $status isn't SSH_FX_OK it's probably SSH_FX_NO_SUCH_FILE or SSH_FX_PERMISSION_DENIED + list($status) = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::unpackSSH2('N', $response); + if ($status != NET_SFTP_STATUS_OK) { + $this->logError($response, $status); + return \false; + } + // don't move the stat cache entry over since this operation could very well change the + // atime and mtime attributes + //$this->update_stat_cache($newname, $this->query_stat_cache($oldname)); + $this->remove_from_stat_cache($oldname); + $this->remove_from_stat_cache($newname); + return \true; + } + /** + * Returns general information about a file system. + * + * The function statvfs() returns information about a mounted filesystem. + * @see https://man7.org/linux/man-pages/man3/statvfs.3.html + * + * @param string $path + * @return false|array{bsize: int, frsize: int, blocks: int, bfree: int, bavail: int, files: int, ffree: int, favail: int, fsid: int, flag: int, namemax: int} + */ + public function statvfs($path) + { + if (!$this->precheck()) { + return \false; + } + if (!isset($this->extensions['statvfs@openssh.com']) || $this->extensions['statvfs@openssh.com'] !== '2') { + throw new \RuntimeException("Extension 'statvfs@openssh.com' is not supported by the server. " . "Call getSupportedVersions() to see a list of supported extension"); + } + $realpath = $this->realpath($path); + if ($realpath === \false) { + return \false; + } + $packet = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::packSSH2('ss', 'statvfs@openssh.com', $realpath); + $this->send_sftp_packet(NET_SFTP_EXTENDED, $packet); + $response = $this->get_sftp_packet(); + if ($this->packet_type !== NET_SFTP_EXTENDED_REPLY) { + throw new \UnexpectedValueException('Expected SSH_FXP_EXTENDED_REPLY. ' . 'Got packet type: ' . $this->packet_type); + } + /** + * These requests return a SSH_FXP_STATUS reply on failure. On success they + * return the following SSH_FXP_EXTENDED_REPLY reply: + * + * uint32 id + * uint64 f_bsize file system block size + * uint64 f_frsize fundamental fs block size + * uint64 f_blocks number of blocks (unit f_frsize) + * uint64 f_bfree free blocks in file system + * uint64 f_bavail free blocks for non-root + * uint64 f_files total file inodes + * uint64 f_ffree free file inodes + * uint64 f_favail free file inodes for to non-root + * uint64 f_fsid file system id + * uint64 f_flag bit mask of f_flag values + * uint64 f_namemax maximum filename length + */ + return \array_combine(['bsize', 'frsize', 'blocks', 'bfree', 'bavail', 'files', 'ffree', 'favail', 'fsid', 'flag', 'namemax'], \FluentSmtpLib\phpseclib3\Common\Functions\Strings::unpackSSH2('QQQQQQQQQQQ', $response)); + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Net/SFTP/Stream.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Net/SFTP/Stream.php new file mode 100644 index 0000000..f6fd03f --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Net/SFTP/Stream.php @@ -0,0 +1,697 @@ + + * @copyright 2013 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\Net\SFTP; + +use FluentSmtpLib\phpseclib3\Crypt\Common\PrivateKey; +use FluentSmtpLib\phpseclib3\Net\SFTP; +use FluentSmtpLib\phpseclib3\Net\SSH2; +/** + * SFTP Stream Wrapper + * + * @author Jim Wigginton + */ +class Stream +{ + /** + * SFTP instances + * + * Rather than re-create the connection we re-use instances if possible + * + * @var array + */ + public static $instances; + /** + * SFTP instance + * + * @var object + */ + private $sftp; + /** + * Path + * + * @var string + */ + private $path; + /** + * Mode + * + * @var string + */ + private $mode; + /** + * Position + * + * @var int + */ + private $pos; + /** + * Size + * + * @var int + */ + private $size; + /** + * Directory entries + * + * @var array + */ + private $entries; + /** + * EOF flag + * + * @var bool + */ + private $eof; + /** + * Context resource + * + * Technically this needs to be publicly accessible so PHP can set it directly + * + * @var resource + */ + public $context; + /** + * Notification callback function + * + * @var callable + */ + private $notification; + /** + * Registers this class as a URL wrapper. + * + * @param string $protocol The wrapper name to be registered. + * @return bool True on success, false otherwise. + */ + public static function register($protocol = 'sftp') + { + if (\in_array($protocol, \stream_get_wrappers(), \true)) { + return \false; + } + return \stream_wrapper_register($protocol, \get_called_class()); + } + /** + * The Constructor + * + */ + public function __construct() + { + if (\defined('FluentSmtpLib\\NET_SFTP_STREAM_LOGGING')) { + echo "__construct()\r\n"; + } + } + /** + * Path Parser + * + * Extract a path from a URI and actually connect to an SSH server if appropriate + * + * If "notification" is set as a context parameter the message code for successful login is + * NET_SSH2_MSG_USERAUTH_SUCCESS. For a failed login it's NET_SSH2_MSG_USERAUTH_FAILURE. + * + * @param string $path + * @return string + */ + protected function parse_path($path) + { + $orig = $path; + \extract(\parse_url($path) + ['port' => 22]); + if (isset($query)) { + $path .= '?' . $query; + } elseif (\preg_match('/(\\?|\\?#)$/', $orig)) { + $path .= '?'; + } + if (isset($fragment)) { + $path .= '#' . $fragment; + } elseif ($orig[\strlen($orig) - 1] == '#') { + $path .= '#'; + } + if (!isset($host)) { + return \false; + } + if (isset($this->context)) { + $context = \stream_context_get_params($this->context); + if (isset($context['notification'])) { + $this->notification = $context['notification']; + } + } + if (\preg_match('/^{[a-z0-9]+}$/i', $host)) { + $host = \FluentSmtpLib\phpseclib3\Net\SSH2::getConnectionByResourceId($host); + if ($host === \false) { + return \false; + } + $this->sftp = $host; + } else { + if (isset($this->context)) { + $context = \stream_context_get_options($this->context); + } + if (isset($context[$scheme]['session'])) { + $sftp = $context[$scheme]['session']; + } + if (isset($context[$scheme]['sftp'])) { + $sftp = $context[$scheme]['sftp']; + } + if (isset($sftp) && $sftp instanceof \FluentSmtpLib\phpseclib3\Net\SFTP) { + $this->sftp = $sftp; + return $path; + } + if (isset($context[$scheme]['username'])) { + $user = $context[$scheme]['username']; + } + if (isset($context[$scheme]['password'])) { + $pass = $context[$scheme]['password']; + } + if (isset($context[$scheme]['privkey']) && $context[$scheme]['privkey'] instanceof \FluentSmtpLib\phpseclib3\Crypt\Common\PrivateKey) { + $pass = $context[$scheme]['privkey']; + } + if (!isset($user) || !isset($pass)) { + return \false; + } + // casting $pass to a string is necessary in the event that it's a \phpseclib3\Crypt\RSA object + if (isset(self::$instances[$host][$port][$user][(string) $pass])) { + $this->sftp = self::$instances[$host][$port][$user][(string) $pass]; + } else { + $this->sftp = new \FluentSmtpLib\phpseclib3\Net\SFTP($host, $port); + $this->sftp->disableStatCache(); + if (isset($this->notification) && \is_callable($this->notification)) { + /* if !is_callable($this->notification) we could do this: + + user_error('fopen(): failed to call user notifier', E_USER_WARNING); + + the ftp wrapper gives errors like that when the notifier isn't callable. + i've opted not to do that, however, since the ftp wrapper gives the line + on which the fopen occurred as the line number - not the line that the + user_error is on. + */ + \call_user_func($this->notification, \STREAM_NOTIFY_CONNECT, \STREAM_NOTIFY_SEVERITY_INFO, '', 0, 0, 0); + \call_user_func($this->notification, \STREAM_NOTIFY_AUTH_REQUIRED, \STREAM_NOTIFY_SEVERITY_INFO, '', 0, 0, 0); + if (!$this->sftp->login($user, $pass)) { + \call_user_func($this->notification, \STREAM_NOTIFY_AUTH_RESULT, \STREAM_NOTIFY_SEVERITY_ERR, 'Login Failure', NET_SSH2_MSG_USERAUTH_FAILURE, 0, 0); + return \false; + } + \call_user_func($this->notification, \STREAM_NOTIFY_AUTH_RESULT, \STREAM_NOTIFY_SEVERITY_INFO, 'Login Success', NET_SSH2_MSG_USERAUTH_SUCCESS, 0, 0); + } else { + if (!$this->sftp->login($user, $pass)) { + return \false; + } + } + self::$instances[$host][$port][$user][(string) $pass] = $this->sftp; + } + } + return $path; + } + /** + * Opens file or URL + * + * @param string $path + * @param string $mode + * @param int $options + * @param string $opened_path + * @return bool + */ + private function _stream_open($path, $mode, $options, &$opened_path) + { + $path = $this->parse_path($path); + if ($path === \false) { + return \false; + } + $this->path = $path; + $this->size = $this->sftp->filesize($path); + $this->mode = \preg_replace('#[bt]$#', '', $mode); + $this->eof = \false; + if ($this->size === \false) { + if ($this->mode[0] == 'r') { + return \false; + } else { + $this->sftp->touch($path); + $this->size = 0; + } + } else { + switch ($this->mode[0]) { + case 'x': + return \false; + case 'w': + $this->sftp->truncate($path, 0); + $this->size = 0; + } + } + $this->pos = $this->mode[0] != 'a' ? 0 : $this->size; + return \true; + } + /** + * Read from stream + * + * @param int $count + * @return mixed + */ + private function _stream_read($count) + { + switch ($this->mode) { + case 'w': + case 'a': + case 'x': + case 'c': + return \false; + } + // commented out because some files - eg. /dev/urandom - will say their size is 0 when in fact it's kinda infinite + //if ($this->pos >= $this->size) { + // $this->eof = true; + // return false; + //} + $result = $this->sftp->get($this->path, \false, $this->pos, $count); + if (isset($this->notification) && \is_callable($this->notification)) { + if ($result === \false) { + \call_user_func($this->notification, \STREAM_NOTIFY_FAILURE, \STREAM_NOTIFY_SEVERITY_ERR, $this->sftp->getLastSFTPError(), NET_SFTP_OPEN, 0, 0); + return 0; + } + // seems that PHP calls stream_read in 8k chunks + \call_user_func($this->notification, \STREAM_NOTIFY_PROGRESS, \STREAM_NOTIFY_SEVERITY_INFO, '', 0, \strlen($result), $this->size); + } + if (empty($result)) { + // ie. false or empty string + $this->eof = \true; + return \false; + } + $this->pos += \strlen($result); + return $result; + } + /** + * Write to stream + * + * @param string $data + * @return int|false + */ + private function _stream_write($data) + { + switch ($this->mode) { + case 'r': + return \false; + } + $result = $this->sftp->put($this->path, $data, \FluentSmtpLib\phpseclib3\Net\SFTP::SOURCE_STRING, $this->pos); + if (isset($this->notification) && \is_callable($this->notification)) { + if (!$result) { + \call_user_func($this->notification, \STREAM_NOTIFY_FAILURE, \STREAM_NOTIFY_SEVERITY_ERR, $this->sftp->getLastSFTPError(), NET_SFTP_OPEN, 0, 0); + return 0; + } + // seems that PHP splits up strings into 8k blocks before calling stream_write + \call_user_func($this->notification, \STREAM_NOTIFY_PROGRESS, \STREAM_NOTIFY_SEVERITY_INFO, '', 0, \strlen($data), \strlen($data)); + } + if ($result === \false) { + return \false; + } + $this->pos += \strlen($data); + if ($this->pos > $this->size) { + $this->size = $this->pos; + } + $this->eof = \false; + return \strlen($data); + } + /** + * Retrieve the current position of a stream + * + * @return int + */ + private function _stream_tell() + { + return $this->pos; + } + /** + * Tests for end-of-file on a file pointer + * + * In my testing there are four classes functions that normally effect the pointer: + * fseek, fputs / fwrite, fgets / fread and ftruncate. + * + * Only fgets / fread, however, results in feof() returning true. do fputs($fp, 'aaa') on a blank file and feof() + * will return false. do fread($fp, 1) and feof() will then return true. do fseek($fp, 10) on ablank file and feof() + * will return false. do fread($fp, 1) and feof() will then return true. + * + * @return bool + */ + private function _stream_eof() + { + return $this->eof; + } + /** + * Seeks to specific location in a stream + * + * @param int $offset + * @param int $whence + * @return bool + */ + private function _stream_seek($offset, $whence) + { + switch ($whence) { + case \SEEK_SET: + if ($offset < 0) { + return \false; + } + break; + case \SEEK_CUR: + $offset += $this->pos; + break; + case \SEEK_END: + $offset += $this->size; + } + $this->pos = $offset; + $this->eof = \false; + return \true; + } + /** + * Change stream options + * + * @param string $path + * @param int $option + * @param mixed $var + * @return bool + */ + private function _stream_metadata($path, $option, $var) + { + $path = $this->parse_path($path); + if ($path === \false) { + return \false; + } + // stream_metadata was introduced in PHP 5.4.0 but as of 5.4.11 the constants haven't been defined + // see http://www.php.net/streamwrapper.stream-metadata and https://bugs.php.net/64246 + // and https://github.com/php/php-src/blob/master/main/php_streams.h#L592 + switch ($option) { + case 1: + // PHP_STREAM_META_TOUCH + $time = isset($var[0]) ? $var[0] : null; + $atime = isset($var[1]) ? $var[1] : null; + return $this->sftp->touch($path, $time, $atime); + case 2: + // PHP_STREAM_OWNER_NAME + case 3: + // PHP_STREAM_GROUP_NAME + return \false; + case 4: + // PHP_STREAM_META_OWNER + return $this->sftp->chown($path, $var); + case 5: + // PHP_STREAM_META_GROUP + return $this->sftp->chgrp($path, $var); + case 6: + // PHP_STREAM_META_ACCESS + return $this->sftp->chmod($path, $var) !== \false; + } + } + /** + * Retrieve the underlaying resource + * + * @param int $cast_as + * @return resource + */ + private function _stream_cast($cast_as) + { + return $this->sftp->fsock; + } + /** + * Advisory file locking + * + * @param int $operation + * @return bool + */ + private function _stream_lock($operation) + { + return \false; + } + /** + * Renames a file or directory + * + * Attempts to rename oldname to newname, moving it between directories if necessary. + * If newname exists, it will be overwritten. This is a departure from what \phpseclib3\Net\SFTP + * does. + * + * @param string $path_from + * @param string $path_to + * @return bool + */ + private function _rename($path_from, $path_to) + { + $path1 = \parse_url($path_from); + $path2 = \parse_url($path_to); + unset($path1['path'], $path2['path']); + if ($path1 != $path2) { + return \false; + } + $path_from = $this->parse_path($path_from); + $path_to = \parse_url($path_to); + if ($path_from === \false) { + return \false; + } + $path_to = $path_to['path']; + // the $component part of parse_url() was added in PHP 5.1.2 + // "It is an error if there already exists a file with the name specified by newpath." + // -- http://tools.ietf.org/html/draft-ietf-secsh-filexfer-02#section-6.5 + if (!$this->sftp->rename($path_from, $path_to)) { + if ($this->sftp->stat($path_to)) { + return $this->sftp->delete($path_to, \true) && $this->sftp->rename($path_from, $path_to); + } + return \false; + } + return \true; + } + /** + * Open directory handle + * + * The only $options is "whether or not to enforce safe_mode (0x04)". Since safe mode was deprecated in 5.3 and + * removed in 5.4 I'm just going to ignore it. + * + * Also, nlist() is the best that this function is realistically going to be able to do. When an SFTP client + * sends a SSH_FXP_READDIR packet you don't generally get info on just one file but on multiple files. Quoting + * the SFTP specs: + * + * The SSH_FXP_NAME response has the following format: + * + * uint32 id + * uint32 count + * repeats count times: + * string filename + * string longname + * ATTRS attrs + * + * @param string $path + * @param int $options + * @return bool + */ + private function _dir_opendir($path, $options) + { + $path = $this->parse_path($path); + if ($path === \false) { + return \false; + } + $this->pos = 0; + $this->entries = $this->sftp->nlist($path); + return $this->entries !== \false; + } + /** + * Read entry from directory handle + * + * @return mixed + */ + private function _dir_readdir() + { + if (isset($this->entries[$this->pos])) { + return $this->entries[$this->pos++]; + } + return \false; + } + /** + * Rewind directory handle + * + * @return bool + */ + private function _dir_rewinddir() + { + $this->pos = 0; + return \true; + } + /** + * Close directory handle + * + * @return bool + */ + private function _dir_closedir() + { + return \true; + } + /** + * Create a directory + * + * Only valid $options is STREAM_MKDIR_RECURSIVE + * + * @param string $path + * @param int $mode + * @param int $options + * @return bool + */ + private function _mkdir($path, $mode, $options) + { + $path = $this->parse_path($path); + if ($path === \false) { + return \false; + } + return $this->sftp->mkdir($path, $mode, $options & \STREAM_MKDIR_RECURSIVE); + } + /** + * Removes a directory + * + * Only valid $options is STREAM_MKDIR_RECURSIVE per , however, + * does not have a $recursive parameter as mkdir() does so I don't know how + * STREAM_MKDIR_RECURSIVE is supposed to be set. Also, when I try it out with rmdir() I get 8 as + * $options. What does 8 correspond to? + * + * @param string $path + * @param int $options + * @return bool + */ + private function _rmdir($path, $options) + { + $path = $this->parse_path($path); + if ($path === \false) { + return \false; + } + return $this->sftp->rmdir($path); + } + /** + * Flushes the output + * + * See . Always returns true because \phpseclib3\Net\SFTP doesn't cache stuff before writing + * + * @return bool + */ + private function _stream_flush() + { + return \true; + } + /** + * Retrieve information about a file resource + * + * @return mixed + */ + private function _stream_stat() + { + $results = $this->sftp->stat($this->path); + if ($results === \false) { + return \false; + } + return $results; + } + /** + * Delete a file + * + * @param string $path + * @return bool + */ + private function _unlink($path) + { + $path = $this->parse_path($path); + if ($path === \false) { + return \false; + } + return $this->sftp->delete($path, \false); + } + /** + * Retrieve information about a file + * + * Ignores the STREAM_URL_STAT_QUIET flag because the entirety of \phpseclib3\Net\SFTP\Stream is quiet by default + * might be worthwhile to reconstruct bits 12-16 (ie. the file type) if mode doesn't have them but we'll + * cross that bridge when and if it's reached + * + * @param string $path + * @param int $flags + * @return mixed + */ + private function _url_stat($path, $flags) + { + $path = $this->parse_path($path); + if ($path === \false) { + return \false; + } + $results = $flags & \STREAM_URL_STAT_LINK ? $this->sftp->lstat($path) : $this->sftp->stat($path); + if ($results === \false) { + return \false; + } + return $results; + } + /** + * Truncate stream + * + * @param int $new_size + * @return bool + */ + private function _stream_truncate($new_size) + { + if (!$this->sftp->truncate($this->path, $new_size)) { + return \false; + } + $this->eof = \false; + $this->size = $new_size; + return \true; + } + /** + * Change stream options + * + * STREAM_OPTION_WRITE_BUFFER isn't supported for the same reason stream_flush isn't. + * The other two aren't supported because of limitations in \phpseclib3\Net\SFTP. + * + * @param int $option + * @param int $arg1 + * @param int $arg2 + * @return bool + */ + private function _stream_set_option($option, $arg1, $arg2) + { + return \false; + } + /** + * Close an resource + * + */ + private function _stream_close() + { + } + /** + * __call Magic Method + * + * When you're utilizing an SFTP stream you're not calling the methods in this class directly - PHP is calling them for you. + * Which kinda begs the question... what methods is PHP calling and what parameters is it passing to them? This function + * lets you figure that out. + * + * If NET_SFTP_STREAM_LOGGING is defined all calls will be output on the screen and then (regardless of whether or not + * NET_SFTP_STREAM_LOGGING is enabled) the parameters will be passed through to the appropriate method. + * + * @param string $name + * @param array $arguments + * @return mixed + */ + public function __call($name, array $arguments) + { + if (\defined('FluentSmtpLib\\NET_SFTP_STREAM_LOGGING')) { + echo $name . '('; + $last = \count($arguments) - 1; + foreach ($arguments as $i => $argument) { + \var_export($argument); + if ($i != $last) { + echo ','; + } + } + echo ")\r\n"; + } + $name = '_' . $name; + if (!\method_exists($this, $name)) { + return \false; + } + return $this->{$name}(...$arguments); + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Net/SSH2.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Net/SSH2.php new file mode 100644 index 0000000..da58a05 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/Net/SSH2.php @@ -0,0 +1,4748 @@ + + * login('username', 'password')) { + * exit('Login Failed'); + * } + * + * echo $ssh->exec('pwd'); + * echo $ssh->exec('ls -la'); + * ?> + * + * + * + * login('username', $key)) { + * exit('Login Failed'); + * } + * + * echo $ssh->read('username@username:~$'); + * $ssh->write("ls -la\n"); + * echo $ssh->read('username@username:~$'); + * ?> + * + * + * @author Jim Wigginton + * @copyright 2007 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\Net; + +use FluentSmtpLib\phpseclib3\Common\Functions\Strings; +use FluentSmtpLib\phpseclib3\Crypt\Blowfish; +use FluentSmtpLib\phpseclib3\Crypt\ChaCha20; +use FluentSmtpLib\phpseclib3\Crypt\Common\AsymmetricKey; +use FluentSmtpLib\phpseclib3\Crypt\Common\PrivateKey; +use FluentSmtpLib\phpseclib3\Crypt\Common\PublicKey; +use FluentSmtpLib\phpseclib3\Crypt\Common\SymmetricKey; +use FluentSmtpLib\phpseclib3\Crypt\DH; +use FluentSmtpLib\phpseclib3\Crypt\DSA; +use FluentSmtpLib\phpseclib3\Crypt\EC; +use FluentSmtpLib\phpseclib3\Crypt\Hash; +use FluentSmtpLib\phpseclib3\Crypt\Random; +use FluentSmtpLib\phpseclib3\Crypt\RC4; +use FluentSmtpLib\phpseclib3\Crypt\Rijndael; +use FluentSmtpLib\phpseclib3\Crypt\RSA; +use FluentSmtpLib\phpseclib3\Crypt\TripleDES; +// Used to do Diffie-Hellman key exchange and DSA/RSA signature verification. +use FluentSmtpLib\phpseclib3\Crypt\Twofish; +use FluentSmtpLib\phpseclib3\Exception\ConnectionClosedException; +use FluentSmtpLib\phpseclib3\Exception\InsufficientSetupException; +use FluentSmtpLib\phpseclib3\Exception\InvalidPacketLengthException; +use FluentSmtpLib\phpseclib3\Exception\NoSupportedAlgorithmsException; +use FluentSmtpLib\phpseclib3\Exception\TimeoutException; +use FluentSmtpLib\phpseclib3\Exception\UnableToConnectException; +use FluentSmtpLib\phpseclib3\Exception\UnsupportedAlgorithmException; +use FluentSmtpLib\phpseclib3\Exception\UnsupportedCurveException; +use FluentSmtpLib\phpseclib3\Math\BigInteger; +use FluentSmtpLib\phpseclib3\System\SSH\Agent; +/** + * Pure-PHP implementation of SSHv2. + * + * @author Jim Wigginton + */ +class SSH2 +{ + /**#@+ + * Compression Types + * + */ + /** + * No compression + */ + const NET_SSH2_COMPRESSION_NONE = 1; + /** + * zlib compression + */ + const NET_SSH2_COMPRESSION_ZLIB = 2; + /** + * zlib@openssh.com + */ + const NET_SSH2_COMPRESSION_ZLIB_AT_OPENSSH = 3; + /**#@-*/ + // Execution Bitmap Masks + const MASK_CONSTRUCTOR = 0x1; + const MASK_CONNECTED = 0x2; + const MASK_LOGIN_REQ = 0x4; + const MASK_LOGIN = 0x8; + const MASK_SHELL = 0x10; + const MASK_DISCONNECT = 0x20; + /* + * Channel constants + * + * RFC4254 refers not to client and server channels but rather to sender and recipient channels. we don't refer + * to them in that way because RFC4254 toggles the meaning. the client sends a SSH_MSG_CHANNEL_OPEN message with + * a sender channel and the server sends a SSH_MSG_CHANNEL_OPEN_CONFIRMATION in response, with a sender and a + * recipient channel. at first glance, you might conclude that SSH_MSG_CHANNEL_OPEN_CONFIRMATION's sender channel + * would be the same thing as SSH_MSG_CHANNEL_OPEN's sender channel, but it's not, per this snippet: + * The 'recipient channel' is the channel number given in the original + * open request, and 'sender channel' is the channel number allocated by + * the other side. + * + * @see \phpseclib3\Net\SSH2::send_channel_packet() + * @see \phpseclib3\Net\SSH2::get_channel_packet() + */ + const CHANNEL_EXEC = 1; + // PuTTy uses 0x100 + const CHANNEL_SHELL = 2; + const CHANNEL_SUBSYSTEM = 3; + const CHANNEL_AGENT_FORWARD = 4; + const CHANNEL_KEEP_ALIVE = 5; + /** + * Returns the message numbers + * + * @see \phpseclib3\Net\SSH2::getLog() + */ + const LOG_SIMPLE = 1; + /** + * Returns the message content + * + * @see \phpseclib3\Net\SSH2::getLog() + */ + const LOG_COMPLEX = 2; + /** + * Outputs the content real-time + */ + const LOG_REALTIME = 3; + /** + * Dumps the content real-time to a file + */ + const LOG_REALTIME_FILE = 4; + /** + * Outputs the message numbers real-time + */ + const LOG_SIMPLE_REALTIME = 5; + /* + * Dumps the message numbers real-time + */ + const LOG_REALTIME_SIMPLE = 5; + /** + * Make sure that the log never gets larger than this + * + * @see \phpseclib3\Net\SSH2::getLog() + */ + const LOG_MAX_SIZE = 1048576; + // 1024 * 1024 + /** + * Returns when a string matching $expect exactly is found + * + * @see \phpseclib3\Net\SSH2::read() + */ + const READ_SIMPLE = 1; + /** + * Returns when a string matching the regular expression $expect is found + * + * @see \phpseclib3\Net\SSH2::read() + */ + const READ_REGEX = 2; + /** + * Returns whenever a data packet is received. + * + * Some data packets may only contain a single character so it may be necessary + * to call read() multiple times when using this option + * + * @see \phpseclib3\Net\SSH2::read() + */ + const READ_NEXT = 3; + /** + * The SSH identifier + * + * @var string + */ + private $identifier; + /** + * The Socket Object + * + * @var resource|closed-resource|null + */ + public $fsock; + /** + * Execution Bitmap + * + * The bits that are set represent functions that have been called already. This is used to determine + * if a requisite function has been successfully executed. If not, an error should be thrown. + * + * @var int + */ + protected $bitmap = 0; + /** + * Error information + * + * @see self::getErrors() + * @see self::getLastError() + * @var array + */ + private $errors = []; + /** + * Server Identifier + * + * @see self::getServerIdentification() + * @var string|false + */ + protected $server_identifier = \false; + /** + * Key Exchange Algorithms + * + * @see self::getKexAlgorithims() + * @var array|false + */ + private $kex_algorithms = \false; + /** + * Key Exchange Algorithm + * + * @see self::getMethodsNegotiated() + * @var string|false + */ + private $kex_algorithm = \false; + /** + * Minimum Diffie-Hellman Group Bit Size in RFC 4419 Key Exchange Methods + * + * @see self::_key_exchange() + * @var int + */ + private $kex_dh_group_size_min = 1536; + /** + * Preferred Diffie-Hellman Group Bit Size in RFC 4419 Key Exchange Methods + * + * @see self::_key_exchange() + * @var int + */ + private $kex_dh_group_size_preferred = 2048; + /** + * Maximum Diffie-Hellman Group Bit Size in RFC 4419 Key Exchange Methods + * + * @see self::_key_exchange() + * @var int + */ + private $kex_dh_group_size_max = 4096; + /** + * Server Host Key Algorithms + * + * @see self::getServerHostKeyAlgorithms() + * @var array|false + */ + private $server_host_key_algorithms = \false; + /** + * Supported Private Key Algorithms + * + * In theory this should be the same as the Server Host Key Algorithms but, in practice, + * some servers (eg. Azure) will support rsa-sha2-512 as a server host key algorithm but + * not a private key algorithm + * + * @see self::privatekey_login() + * @var array|false + */ + private $supported_private_key_algorithms = \false; + /** + * Encryption Algorithms: Client to Server + * + * @see self::getEncryptionAlgorithmsClient2Server() + * @var array|false + */ + private $encryption_algorithms_client_to_server = \false; + /** + * Encryption Algorithms: Server to Client + * + * @see self::getEncryptionAlgorithmsServer2Client() + * @var array|false + */ + private $encryption_algorithms_server_to_client = \false; + /** + * MAC Algorithms: Client to Server + * + * @see self::getMACAlgorithmsClient2Server() + * @var array|false + */ + private $mac_algorithms_client_to_server = \false; + /** + * MAC Algorithms: Server to Client + * + * @see self::getMACAlgorithmsServer2Client() + * @var array|false + */ + private $mac_algorithms_server_to_client = \false; + /** + * Compression Algorithms: Client to Server + * + * @see self::getCompressionAlgorithmsClient2Server() + * @var array|false + */ + private $compression_algorithms_client_to_server = \false; + /** + * Compression Algorithms: Server to Client + * + * @see self::getCompressionAlgorithmsServer2Client() + * @var array|false + */ + private $compression_algorithms_server_to_client = \false; + /** + * Languages: Server to Client + * + * @see self::getLanguagesServer2Client() + * @var array|false + */ + private $languages_server_to_client = \false; + /** + * Languages: Client to Server + * + * @see self::getLanguagesClient2Server() + * @var array|false + */ + private $languages_client_to_server = \false; + /** + * Preferred Algorithms + * + * @see self::setPreferredAlgorithms() + * @var array + */ + private $preferred = []; + /** + * Block Size for Server to Client Encryption + * + * "Note that the length of the concatenation of 'packet_length', + * 'padding_length', 'payload', and 'random padding' MUST be a multiple + * of the cipher block size or 8, whichever is larger. This constraint + * MUST be enforced, even when using stream ciphers." + * + * -- http://tools.ietf.org/html/rfc4253#section-6 + * + * @see self::__construct() + * @see self::_send_binary_packet() + * @var int + */ + private $encrypt_block_size = 8; + /** + * Block Size for Client to Server Encryption + * + * @see self::__construct() + * @see self::_get_binary_packet() + * @var int + */ + private $decrypt_block_size = 8; + /** + * Server to Client Encryption Object + * + * @see self::_get_binary_packet() + * @var SymmetricKey|false + */ + private $decrypt = \false; + /** + * Decryption Algorithm Name + * + * @var string|null + */ + private $decryptName; + /** + * Decryption Invocation Counter + * + * Used by GCM + * + * @var string|null + */ + private $decryptInvocationCounter; + /** + * Fixed Part of Nonce + * + * Used by GCM + * + * @var string|null + */ + private $decryptFixedPart; + /** + * Server to Client Length Encryption Object + * + * @see self::_get_binary_packet() + * @var object + */ + private $lengthDecrypt = \false; + /** + * Client to Server Encryption Object + * + * @see self::_send_binary_packet() + * @var SymmetricKey|false + */ + private $encrypt = \false; + /** + * Encryption Algorithm Name + * + * @var string|null + */ + private $encryptName; + /** + * Encryption Invocation Counter + * + * Used by GCM + * + * @var string|null + */ + private $encryptInvocationCounter; + /** + * Fixed Part of Nonce + * + * Used by GCM + * + * @var string|null + */ + private $encryptFixedPart; + /** + * Client to Server Length Encryption Object + * + * @see self::_send_binary_packet() + * @var object + */ + private $lengthEncrypt = \false; + /** + * Client to Server HMAC Object + * + * @see self::_send_binary_packet() + * @var object + */ + private $hmac_create = \false; + /** + * Client to Server HMAC Name + * + * @var string|false + */ + private $hmac_create_name; + /** + * Client to Server ETM + * + * @var int|false + */ + private $hmac_create_etm; + /** + * Server to Client HMAC Object + * + * @see self::_get_binary_packet() + * @var object + */ + private $hmac_check = \false; + /** + * Server to Client HMAC Name + * + * @var string|false + */ + private $hmac_check_name; + /** + * Server to Client ETM + * + * @var int|false + */ + private $hmac_check_etm; + /** + * Size of server to client HMAC + * + * We need to know how big the HMAC will be for the server to client direction so that we know how many bytes to read. + * For the client to server side, the HMAC object will make the HMAC as long as it needs to be. All we need to do is + * append it. + * + * @see self::_get_binary_packet() + * @var int + */ + private $hmac_size = \false; + /** + * Server Public Host Key + * + * @see self::getServerPublicHostKey() + * @var string + */ + private $server_public_host_key; + /** + * Session identifier + * + * "The exchange hash H from the first key exchange is additionally + * used as the session identifier, which is a unique identifier for + * this connection." + * + * -- http://tools.ietf.org/html/rfc4253#section-7.2 + * + * @see self::_key_exchange() + * @var string + */ + private $session_id = \false; + /** + * Exchange hash + * + * The current exchange hash + * + * @see self::_key_exchange() + * @var string + */ + private $exchange_hash = \false; + /** + * Message Numbers + * + * @see self::__construct() + * @var array + * @access private + */ + private static $message_numbers = []; + /** + * Disconnection Message 'reason codes' defined in RFC4253 + * + * @see self::__construct() + * @var array + * @access private + */ + private static $disconnect_reasons = []; + /** + * SSH_MSG_CHANNEL_OPEN_FAILURE 'reason codes', defined in RFC4254 + * + * @see self::__construct() + * @var array + * @access private + */ + private static $channel_open_failure_reasons = []; + /** + * Terminal Modes + * + * @link http://tools.ietf.org/html/rfc4254#section-8 + * @see self::__construct() + * @var array + * @access private + */ + private static $terminal_modes = []; + /** + * SSH_MSG_CHANNEL_EXTENDED_DATA's data_type_codes + * + * @link http://tools.ietf.org/html/rfc4254#section-5.2 + * @see self::__construct() + * @var array + * @access private + */ + private static $channel_extended_data_type_codes = []; + /** + * Send Sequence Number + * + * See 'Section 6.4. Data Integrity' of rfc4253 for more info. + * + * @see self::_send_binary_packet() + * @var int + */ + private $send_seq_no = 0; + /** + * Get Sequence Number + * + * See 'Section 6.4. Data Integrity' of rfc4253 for more info. + * + * @see self::_get_binary_packet() + * @var int + */ + private $get_seq_no = 0; + /** + * Server Channels + * + * Maps client channels to server channels + * + * @see self::get_channel_packet() + * @see self::exec() + * @var array + */ + protected $server_channels = []; + /** + * Channel Read Buffers + * + * If a client requests a packet from one channel but receives two packets from another those packets should + * be placed in a buffer + * + * @see self::get_channel_packet() + * @see self::exec() + * @var array + */ + private $channel_buffers = []; + /** + * Channel Write Buffers + * + * If a client sends a packet and receives a timeout error mid-transmission, buffer the data written so it + * can be de-duplicated upon resuming write + * + * @see self::send_channel_packet() + * @var array + */ + private $channel_buffers_write = []; + /** + * Channel Status + * + * Contains the type of the last sent message + * + * @see self::get_channel_packet() + * @var array + */ + protected $channel_status = []; + /** + * The identifier of the interactive channel which was opened most recently + * + * @see self::getInteractiveChannelId() + * @var int + */ + private $channel_id_last_interactive = 0; + /** + * Packet Size + * + * Maximum packet size indexed by channel + * + * @see self::send_channel_packet() + * @var array + */ + private $packet_size_client_to_server = []; + /** + * Message Number Log + * + * @see self::getLog() + * @var array + */ + private $message_number_log = []; + /** + * Message Log + * + * @see self::getLog() + * @var array + */ + private $message_log = []; + /** + * The Window Size + * + * Bytes the other party can send before it must wait for the window to be adjusted (0x7FFFFFFF = 2GB) + * + * @var int + * @see self::send_channel_packet() + * @see self::exec() + */ + protected $window_size = 0x7fffffff; + /** + * What we resize the window to + * + * When PuTTY resizes the window it doesn't add an additional 0x7FFFFFFF bytes - it adds 0x40000000 bytes. + * Some SFTP clients (GoAnywhere) don't support adding 0x7FFFFFFF to the window size after the fact so + * we'll just do what PuTTY does + * + * @var int + * @see self::_send_channel_packet() + * @see self::exec() + */ + private $window_resize = 0x40000000; + /** + * Window size, server to client + * + * Window size indexed by channel + * + * @see self::send_channel_packet() + * @var array + */ + protected $window_size_server_to_client = []; + /** + * Window size, client to server + * + * Window size indexed by channel + * + * @see self::get_channel_packet() + * @var array + */ + private $window_size_client_to_server = []; + /** + * Server signature + * + * Verified against $this->session_id + * + * @see self::getServerPublicHostKey() + * @var string + */ + private $signature = ''; + /** + * Server signature format + * + * ssh-rsa or ssh-dss. + * + * @see self::getServerPublicHostKey() + * @var string + */ + private $signature_format = ''; + /** + * Interactive Buffer + * + * @see self::read() + * @var string + */ + private $interactiveBuffer = ''; + /** + * Current log size + * + * Should never exceed self::LOG_MAX_SIZE + * + * @see self::_send_binary_packet() + * @see self::_get_binary_packet() + * @var int + */ + private $log_size; + /** + * Timeout + * + * @see self::setTimeout() + */ + protected $timeout; + /** + * Current Timeout + * + * @see self::get_channel_packet() + */ + protected $curTimeout; + /** + * Keep Alive Interval + * + * @see self::setKeepAlive() + */ + private $keepAlive; + /** + * Real-time log file pointer + * + * @see self::_append_log() + * @var resource|closed-resource + */ + private $realtime_log_file; + /** + * Real-time log file size + * + * @see self::_append_log() + * @var int + */ + private $realtime_log_size; + /** + * Has the signature been validated? + * + * @see self::getServerPublicHostKey() + * @var bool + */ + private $signature_validated = \false; + /** + * Real-time log file wrap boolean + * + * @see self::_append_log() + * @var bool + */ + private $realtime_log_wrap; + /** + * Flag to suppress stderr from output + * + * @see self::enableQuietMode() + */ + private $quiet_mode = \false; + /** + * Time of last read/write network activity + * + * @var float + */ + private $last_packet = null; + /** + * Exit status returned from ssh if any + * + * @var int + */ + private $exit_status; + /** + * Flag to request a PTY when using exec() + * + * @var bool + * @see self::enablePTY() + */ + private $request_pty = \false; + /** + * Contents of stdError + * + * @var string + */ + private $stdErrorLog; + /** + * The Last Interactive Response + * + * @see self::_keyboard_interactive_process() + * @var string + */ + private $last_interactive_response = ''; + /** + * Keyboard Interactive Request / Responses + * + * @see self::_keyboard_interactive_process() + * @var array + */ + private $keyboard_requests_responses = []; + /** + * Banner Message + * + * Quoting from the RFC, "in some jurisdictions, sending a warning message before + * authentication may be relevant for getting legal protection." + * + * @see self::_filter() + * @see self::getBannerMessage() + * @var string + */ + private $banner_message = ''; + /** + * Did read() timeout or return normally? + * + * @see self::isTimeout() + * @var bool + */ + protected $is_timeout = \false; + /** + * Log Boundary + * + * @see self::_format_log() + * @var string + */ + private $log_boundary = ':'; + /** + * Log Long Width + * + * @see self::_format_log() + * @var int + */ + private $log_long_width = 65; + /** + * Log Short Width + * + * @see self::_format_log() + * @var int + */ + private $log_short_width = 16; + /** + * Hostname + * + * @see self::__construct() + * @see self::_connect() + * @var string + */ + private $host; + /** + * Port Number + * + * @see self::__construct() + * @see self::_connect() + * @var int + */ + private $port; + /** + * Number of columns for terminal window size + * + * @see self::getWindowColumns() + * @see self::setWindowColumns() + * @see self::setWindowSize() + * @var int + */ + private $windowColumns = 80; + /** + * Number of columns for terminal window size + * + * @see self::getWindowRows() + * @see self::setWindowRows() + * @see self::setWindowSize() + * @var int + */ + private $windowRows = 24; + /** + * Crypto Engine + * + * @see self::setCryptoEngine() + * @see self::_key_exchange() + * @var int + */ + private static $crypto_engine = \false; + /** + * A System_SSH_Agent for use in the SSH2 Agent Forwarding scenario + * + * @var Agent + */ + private $agent; + /** + * Connection storage to replicates ssh2 extension functionality: + * {@link http://php.net/manual/en/wrappers.ssh2.php#refsect1-wrappers.ssh2-examples} + * + * @var array> + */ + private static $connections; + /** + * Send the identification string first? + * + * @var bool + */ + private $send_id_string_first = \true; + /** + * Send the key exchange initiation packet first? + * + * @var bool + */ + private $send_kex_first = \true; + /** + * Some versions of OpenSSH incorrectly calculate the key size + * + * @var bool + */ + private $bad_key_size_fix = \false; + /** + * Should we try to re-connect to re-establish keys? + * + * @var bool + */ + private $login_credentials_finalized = \false; + /** + * Binary Packet Buffer + * + * @var object|null + */ + private $binary_packet_buffer = null; + /** + * Preferred Signature Format + * + * @var string|false + */ + protected $preferred_signature_format = \false; + /** + * Authentication Credentials + * + * @var array + */ + protected $auth = []; + /** + * Terminal + * + * @var string + */ + private $term = 'vt100'; + /** + * The authentication methods that may productively continue authentication. + * + * @see https://tools.ietf.org/html/rfc4252#section-5.1 + * @var array|null + */ + private $auth_methods_to_continue = null; + /** + * Compression method + * + * @var int + */ + private $compress = self::NET_SSH2_COMPRESSION_NONE; + /** + * Decompression method + * + * @var int + */ + private $decompress = self::NET_SSH2_COMPRESSION_NONE; + /** + * Compression context + * + * @var resource|false|null + */ + private $compress_context; + /** + * Decompression context + * + * @var resource|object + */ + private $decompress_context; + /** + * Regenerate Compression Context + * + * @var bool + */ + private $regenerate_compression_context = \false; + /** + * Regenerate Decompression Context + * + * @var bool + */ + private $regenerate_decompression_context = \false; + /** + * Smart multi-factor authentication flag + * + * @var bool + */ + private $smartMFA = \true; + /** + * How many channels are currently opened + * + * @var int + */ + private $channelCount = 0; + /** + * Does the server support multiple channels? If not then error out + * when multiple channels are attempted to be opened + * + * @var bool + */ + private $errorOnMultipleChannels; + /** + * Bytes Transferred Since Last Key Exchange + * + * Includes outbound and inbound totals + * + * @var int + */ + private $bytesTransferredSinceLastKEX = 0; + /** + * After how many transferred byte should phpseclib initiate a key re-exchange? + * + * @var int + */ + private $doKeyReexchangeAfterXBytes = 1024 * 1024 * 1024; + /** + * Has a key re-exchange been initialized? + * + * @var bool + * @access private + */ + private $keyExchangeInProgress = \false; + /** + * KEX Buffer + * + * If we're in the middle of a key exchange we want to buffer any additional packets we get until + * the key exchange is over + * + * @see self::_get_binary_packet() + * @see self::_key_exchange() + * @see self::exec() + * @var array + * @access private + */ + private $kex_buffer = []; + /** + * Strict KEX Flag + * + * If kex-strict-s-v00@openssh.com is present in the first KEX packet it need not + * be present in subsequent packet + * + * @see self::_key_exchange() + * @see self::exec() + * @var array + * @access private + */ + private $strict_kex_flag = \false; + /** + * Default Constructor. + * + * $host can either be a string, representing the host, or a stream resource. + * If $host is a stream resource then $port doesn't do anything, altho $timeout + * still will be used + * + * @param mixed $host + * @param int $port + * @param int $timeout + * @see self::login() + */ + public function __construct($host, $port = 22, $timeout = 10) + { + if (empty(self::$message_numbers)) { + self::$message_numbers = [ + 1 => 'NET_SSH2_MSG_DISCONNECT', + 2 => 'NET_SSH2_MSG_IGNORE', + 3 => 'NET_SSH2_MSG_UNIMPLEMENTED', + 4 => 'NET_SSH2_MSG_DEBUG', + 5 => 'NET_SSH2_MSG_SERVICE_REQUEST', + 6 => 'NET_SSH2_MSG_SERVICE_ACCEPT', + 7 => 'NET_SSH2_MSG_EXT_INFO', + // RFC 8308 + 20 => 'NET_SSH2_MSG_KEXINIT', + 21 => 'NET_SSH2_MSG_NEWKEYS', + 30 => 'NET_SSH2_MSG_KEXDH_INIT', + 31 => 'NET_SSH2_MSG_KEXDH_REPLY', + 50 => 'NET_SSH2_MSG_USERAUTH_REQUEST', + 51 => 'NET_SSH2_MSG_USERAUTH_FAILURE', + 52 => 'NET_SSH2_MSG_USERAUTH_SUCCESS', + 53 => 'NET_SSH2_MSG_USERAUTH_BANNER', + 80 => 'NET_SSH2_MSG_GLOBAL_REQUEST', + 81 => 'NET_SSH2_MSG_REQUEST_SUCCESS', + 82 => 'NET_SSH2_MSG_REQUEST_FAILURE', + 90 => 'NET_SSH2_MSG_CHANNEL_OPEN', + 91 => 'NET_SSH2_MSG_CHANNEL_OPEN_CONFIRMATION', + 92 => 'NET_SSH2_MSG_CHANNEL_OPEN_FAILURE', + 93 => 'NET_SSH2_MSG_CHANNEL_WINDOW_ADJUST', + 94 => 'NET_SSH2_MSG_CHANNEL_DATA', + 95 => 'NET_SSH2_MSG_CHANNEL_EXTENDED_DATA', + 96 => 'NET_SSH2_MSG_CHANNEL_EOF', + 97 => 'NET_SSH2_MSG_CHANNEL_CLOSE', + 98 => 'NET_SSH2_MSG_CHANNEL_REQUEST', + 99 => 'NET_SSH2_MSG_CHANNEL_SUCCESS', + 100 => 'NET_SSH2_MSG_CHANNEL_FAILURE', + ]; + self::$disconnect_reasons = [1 => 'NET_SSH2_DISCONNECT_HOST_NOT_ALLOWED_TO_CONNECT', 2 => 'NET_SSH2_DISCONNECT_PROTOCOL_ERROR', 3 => 'NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED', 4 => 'NET_SSH2_DISCONNECT_RESERVED', 5 => 'NET_SSH2_DISCONNECT_MAC_ERROR', 6 => 'NET_SSH2_DISCONNECT_COMPRESSION_ERROR', 7 => 'NET_SSH2_DISCONNECT_SERVICE_NOT_AVAILABLE', 8 => 'NET_SSH2_DISCONNECT_PROTOCOL_VERSION_NOT_SUPPORTED', 9 => 'NET_SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE', 10 => 'NET_SSH2_DISCONNECT_CONNECTION_LOST', 11 => 'NET_SSH2_DISCONNECT_BY_APPLICATION', 12 => 'NET_SSH2_DISCONNECT_TOO_MANY_CONNECTIONS', 13 => 'NET_SSH2_DISCONNECT_AUTH_CANCELLED_BY_USER', 14 => 'NET_SSH2_DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE', 15 => 'NET_SSH2_DISCONNECT_ILLEGAL_USER_NAME']; + self::$channel_open_failure_reasons = [1 => 'NET_SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED']; + self::$terminal_modes = [0 => 'NET_SSH2_TTY_OP_END']; + self::$channel_extended_data_type_codes = [1 => 'NET_SSH2_EXTENDED_DATA_STDERR']; + self::define_array( + self::$message_numbers, + self::$disconnect_reasons, + self::$channel_open_failure_reasons, + self::$terminal_modes, + self::$channel_extended_data_type_codes, + [60 => 'NET_SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ'], + [60 => 'NET_SSH2_MSG_USERAUTH_PK_OK'], + [60 => 'NET_SSH2_MSG_USERAUTH_INFO_REQUEST', 61 => 'NET_SSH2_MSG_USERAUTH_INFO_RESPONSE'], + // RFC 4419 - diffie-hellman-group-exchange-sha{1,256} + [30 => 'NET_SSH2_MSG_KEXDH_GEX_REQUEST_OLD', 31 => 'NET_SSH2_MSG_KEXDH_GEX_GROUP', 32 => 'NET_SSH2_MSG_KEXDH_GEX_INIT', 33 => 'NET_SSH2_MSG_KEXDH_GEX_REPLY', 34 => 'NET_SSH2_MSG_KEXDH_GEX_REQUEST'], + // RFC 5656 - Elliptic Curves (for curve25519-sha256@libssh.org) + [30 => 'NET_SSH2_MSG_KEX_ECDH_INIT', 31 => 'NET_SSH2_MSG_KEX_ECDH_REPLY'] + ); + } + /** + * Typehint is required due to a bug in Psalm: https://github.com/vimeo/psalm/issues/7508 + * @var \WeakReference|SSH2 + */ + self::$connections[$this->getResourceId()] = \class_exists('WeakReference') ? \WeakReference::create($this) : $this; + $this->timeout = $timeout; + if (\is_resource($host)) { + $this->fsock = $host; + return; + } + if (\FluentSmtpLib\phpseclib3\Common\Functions\Strings::is_stringable($host)) { + $this->host = $host; + $this->port = $port; + } + } + /** + * Set Crypto Engine Mode + * + * Possible $engine values: + * OpenSSL, mcrypt, Eval, PHP + * + * @param int $engine + */ + public static function setCryptoEngine($engine) + { + self::$crypto_engine = $engine; + } + /** + * Send Identification String First + * + * https://tools.ietf.org/html/rfc4253#section-4.2 says "when the connection has been established, + * both sides MUST send an identification string". It does not say which side sends it first. In + * theory it shouldn't matter but it is a fact of life that some SSH servers are simply buggy + * + */ + public function sendIdentificationStringFirst() + { + $this->send_id_string_first = \true; + } + /** + * Send Identification String Last + * + * https://tools.ietf.org/html/rfc4253#section-4.2 says "when the connection has been established, + * both sides MUST send an identification string". It does not say which side sends it first. In + * theory it shouldn't matter but it is a fact of life that some SSH servers are simply buggy + * + */ + public function sendIdentificationStringLast() + { + $this->send_id_string_first = \false; + } + /** + * Send SSH_MSG_KEXINIT First + * + * https://tools.ietf.org/html/rfc4253#section-7.1 says "key exchange begins by each sending + * sending the [SSH_MSG_KEXINIT] packet". It does not say which side sends it first. In theory + * it shouldn't matter but it is a fact of life that some SSH servers are simply buggy + * + */ + public function sendKEXINITFirst() + { + $this->send_kex_first = \true; + } + /** + * Send SSH_MSG_KEXINIT Last + * + * https://tools.ietf.org/html/rfc4253#section-7.1 says "key exchange begins by each sending + * sending the [SSH_MSG_KEXINIT] packet". It does not say which side sends it first. In theory + * it shouldn't matter but it is a fact of life that some SSH servers are simply buggy + * + */ + public function sendKEXINITLast() + { + $this->send_kex_first = \false; + } + /** + * stream_select wrapper + * + * Quoting https://stackoverflow.com/a/14262151/569976, + * "The general approach to `EINTR` is to simply handle the error and retry the operation again" + * + * This wrapper does that loop + */ + private static function stream_select(&$read, &$write, &$except, $seconds, $microseconds = null) + { + $remaining = $seconds + $microseconds / 1000000; + $start = \microtime(\true); + while (\true) { + $result = @\stream_select($read, $write, $except, $seconds, $microseconds); + if ($result !== \false) { + return $result; + } + $elapsed = \microtime(\true) - $start; + $seconds = (int) ($remaining - \floor($elapsed)); + $microseconds = (int) (1000000 * ($remaining - $seconds)); + if ($elapsed >= $remaining) { + return \false; + } + } + } + /** + * Connect to an SSHv2 server + * + * @throws \UnexpectedValueException on receipt of unexpected packets + * @throws \RuntimeException on other errors + */ + private function connect() + { + if ($this->bitmap & self::MASK_CONSTRUCTOR) { + return; + } + $this->bitmap |= self::MASK_CONSTRUCTOR; + $this->curTimeout = $this->timeout; + if (!\is_resource($this->fsock)) { + $start = \microtime(\true); + // with stream_select a timeout of 0 means that no timeout takes place; + // with fsockopen a timeout of 0 means that you instantly timeout + // to resolve this incompatibility a timeout of 100,000 will be used for fsockopen if timeout is 0 + $this->fsock = @\fsockopen($this->host, $this->port, $errno, $errstr, $this->curTimeout == 0 ? 100000 : $this->curTimeout); + if (!$this->fsock) { + $host = $this->host . ':' . $this->port; + throw new \FluentSmtpLib\phpseclib3\Exception\UnableToConnectException(\rtrim("Cannot connect to {$host}. Error {$errno}. {$errstr}")); + } + $elapsed = \microtime(\true) - $start; + if ($this->curTimeout) { + $this->curTimeout -= $elapsed; + if ($this->curTimeout < 0) { + throw new \RuntimeException('Connection timed out whilst attempting to open socket connection'); + } + } + if (\defined('FluentSmtpLib\\NET_SSH2_LOGGING')) { + $this->append_log('(fsockopen took ' . \round($elapsed, 4) . 's)', ''); + } + } + $this->identifier = $this->generate_identifier(); + if ($this->send_id_string_first) { + $start = \microtime(\true); + \fputs($this->fsock, $this->identifier . "\r\n"); + $elapsed = \round(\microtime(\true) - $start, 4); + if (\defined('FluentSmtpLib\\NET_SSH2_LOGGING')) { + $this->append_log("-> (network: {$elapsed})", $this->identifier . "\r\n"); + } + } + /* According to the SSH2 specs, + + "The server MAY send other lines of data before sending the version + string. Each line SHOULD be terminated by a Carriage Return and Line + Feed. Such lines MUST NOT begin with "SSH-", and SHOULD be encoded + in ISO-10646 UTF-8 [RFC3629] (language is not specified). Clients + MUST be able to process such lines." */ + $data = ''; + $totalElapsed = 0; + while (!\feof($this->fsock) && !\preg_match('#(.*)^(SSH-(\\d\\.\\d+).*)#ms', $data, $matches)) { + $line = ''; + while (\true) { + if ($this->curTimeout) { + if ($this->curTimeout < 0) { + throw new \RuntimeException('Connection timed out whilst receiving server identification string'); + } + $read = [$this->fsock]; + $write = $except = null; + $start = \microtime(\true); + $sec = (int) \floor($this->curTimeout); + $usec = (int) (1000000 * ($this->curTimeout - $sec)); + if (static::stream_select($read, $write, $except, $sec, $usec) === \false) { + throw new \RuntimeException('Connection timed out whilst receiving server identification string'); + } + $elapsed = \microtime(\true) - $start; + $totalElapsed += $elapsed; + $this->curTimeout -= $elapsed; + } + $temp = \stream_get_line($this->fsock, 255, "\n"); + if ($temp === \false) { + throw new \RuntimeException('Error reading SSH identification string; are you sure you\'re connecting to an SSH server?'); + } + $line .= $temp; + if (\strlen($temp) == 255) { + continue; + } + $line .= "\n"; + break; + } + $data .= $line; + } + if (\defined('FluentSmtpLib\\NET_SSH2_LOGGING')) { + $this->append_log('<- (network: ' . \round($totalElapsed, 4) . ')', $line); + } + if (\feof($this->fsock)) { + $this->bitmap = 0; + throw new \FluentSmtpLib\phpseclib3\Exception\ConnectionClosedException('Connection closed by server; are you sure you\'re connected to an SSH server?'); + } + $extra = $matches[1]; + $this->server_identifier = \trim($data, "\r\n"); + if (\strlen($extra)) { + $this->errors[] = $data; + } + if (\version_compare($matches[3], '1.99', '<')) { + $this->bitmap = 0; + throw new \FluentSmtpLib\phpseclib3\Exception\UnableToConnectException("Cannot connect to SSH {$matches[3]} servers"); + } + // Ubuntu's OpenSSH from 5.8 to 6.9 didn't work with multiple channels. see + // https://bugs.launchpad.net/ubuntu/+source/openssh/+bug/1334916 for more info. + // https://lists.ubuntu.com/archives/oneiric-changes/2011-July/005772.html discusses + // when consolekit was incorporated. + // https://marc.info/?l=openssh-unix-dev&m=163409903417589&w=2 discusses some of the + // issues with how Ubuntu incorporated consolekit + $pattern = '#^SSH-2\\.0-OpenSSH_([\\d.]+)[^ ]* Ubuntu-.*$#'; + $match = \preg_match($pattern, $this->server_identifier, $matches); + $match = $match && \version_compare('5.8', $matches[1], '<='); + $match = $match && \version_compare('6.9', $matches[1], '>='); + $this->errorOnMultipleChannels = $match; + if (!$this->send_id_string_first) { + $start = \microtime(\true); + \fputs($this->fsock, $this->identifier . "\r\n"); + $elapsed = \round(\microtime(\true) - $start, 4); + if (\defined('FluentSmtpLib\\NET_SSH2_LOGGING')) { + $this->append_log("-> (network: {$elapsed})", $this->identifier . "\r\n"); + } + } + $this->last_packet = \microtime(\true); + if (!$this->send_kex_first) { + $response = $this->get_binary_packet_or_close(NET_SSH2_MSG_KEXINIT); + $this->key_exchange($response); + } + if ($this->send_kex_first) { + $this->key_exchange(); + } + $this->bitmap |= self::MASK_CONNECTED; + return \true; + } + /** + * Generates the SSH identifier + * + * You should overwrite this method in your own class if you want to use another identifier + * + * @return string + */ + private function generate_identifier() + { + $identifier = 'SSH-2.0-phpseclib_3.0'; + $ext = []; + if (\extension_loaded('sodium')) { + $ext[] = 'libsodium'; + } + if (\extension_loaded('openssl')) { + $ext[] = 'openssl'; + } elseif (\extension_loaded('mcrypt')) { + $ext[] = 'mcrypt'; + } + if (\extension_loaded('gmp')) { + $ext[] = 'gmp'; + } elseif (\extension_loaded('bcmath')) { + $ext[] = 'bcmath'; + } + if (!empty($ext)) { + $identifier .= ' (' . \implode(', ', $ext) . ')'; + } + return $identifier; + } + /** + * Key Exchange + * + * @return bool + * @param string|bool $kexinit_payload_server optional + * @throws \UnexpectedValueException on receipt of unexpected packets + * @throws \RuntimeException on other errors + * @throws NoSupportedAlgorithmsException when none of the algorithms phpseclib has loaded are compatible + */ + private function key_exchange($kexinit_payload_server = \false) + { + $this->bytesTransferredSinceLastKEX = 0; + $preferred = $this->preferred; + // for the initial key exchange $send_kex is true (no key re-exchange has been started) + // for phpseclib initiated key exchanges $send_kex is false + $send_kex = !$this->keyExchangeInProgress; + $this->keyExchangeInProgress = \true; + $kex_algorithms = isset($preferred['kex']) ? $preferred['kex'] : \FluentSmtpLib\phpseclib3\Net\SSH2::getSupportedKEXAlgorithms(); + $server_host_key_algorithms = isset($preferred['hostkey']) ? $preferred['hostkey'] : \FluentSmtpLib\phpseclib3\Net\SSH2::getSupportedHostKeyAlgorithms(); + $s2c_encryption_algorithms = isset($preferred['server_to_client']['crypt']) ? $preferred['server_to_client']['crypt'] : \FluentSmtpLib\phpseclib3\Net\SSH2::getSupportedEncryptionAlgorithms(); + $c2s_encryption_algorithms = isset($preferred['client_to_server']['crypt']) ? $preferred['client_to_server']['crypt'] : \FluentSmtpLib\phpseclib3\Net\SSH2::getSupportedEncryptionAlgorithms(); + $s2c_mac_algorithms = isset($preferred['server_to_client']['mac']) ? $preferred['server_to_client']['mac'] : \FluentSmtpLib\phpseclib3\Net\SSH2::getSupportedMACAlgorithms(); + $c2s_mac_algorithms = isset($preferred['client_to_server']['mac']) ? $preferred['client_to_server']['mac'] : \FluentSmtpLib\phpseclib3\Net\SSH2::getSupportedMACAlgorithms(); + $s2c_compression_algorithms = isset($preferred['server_to_client']['comp']) ? $preferred['server_to_client']['comp'] : \FluentSmtpLib\phpseclib3\Net\SSH2::getSupportedCompressionAlgorithms(); + $c2s_compression_algorithms = isset($preferred['client_to_server']['comp']) ? $preferred['client_to_server']['comp'] : \FluentSmtpLib\phpseclib3\Net\SSH2::getSupportedCompressionAlgorithms(); + $kex_algorithms = \array_merge($kex_algorithms, ['ext-info-c', 'kex-strict-c-v00@openssh.com']); + // some SSH servers have buggy implementations of some of the above algorithms + switch (\true) { + case $this->server_identifier == 'SSH-2.0-SSHD': + case \substr($this->server_identifier, 0, 13) == 'SSH-2.0-DLINK': + if (!isset($preferred['server_to_client']['mac'])) { + $s2c_mac_algorithms = \array_values(\array_diff($s2c_mac_algorithms, ['hmac-sha1-96', 'hmac-md5-96'])); + } + if (!isset($preferred['client_to_server']['mac'])) { + $c2s_mac_algorithms = \array_values(\array_diff($c2s_mac_algorithms, ['hmac-sha1-96', 'hmac-md5-96'])); + } + break; + case \substr($this->server_identifier, 0, 24) == 'SSH-2.0-TurboFTP_SERVER_': + if (!isset($preferred['server_to_client']['crypt'])) { + $s2c_encryption_algorithms = \array_values(\array_diff($s2c_encryption_algorithms, ['aes128-gcm@openssh.com', 'aes256-gcm@openssh.com'])); + } + if (!isset($preferred['client_to_server']['crypt'])) { + $c2s_encryption_algorithms = \array_values(\array_diff($c2s_encryption_algorithms, ['aes128-gcm@openssh.com', 'aes256-gcm@openssh.com'])); + } + } + $client_cookie = \FluentSmtpLib\phpseclib3\Crypt\Random::string(16); + $kexinit_payload_client = \pack('Ca*', NET_SSH2_MSG_KEXINIT, $client_cookie); + $kexinit_payload_client .= \FluentSmtpLib\phpseclib3\Common\Functions\Strings::packSSH2( + 'L10bN', + $kex_algorithms, + $server_host_key_algorithms, + $c2s_encryption_algorithms, + $s2c_encryption_algorithms, + $c2s_mac_algorithms, + $s2c_mac_algorithms, + $c2s_compression_algorithms, + $s2c_compression_algorithms, + [], + // language, client to server + [], + // language, server to client + \false, + // first_kex_packet_follows + 0 + ); + if ($kexinit_payload_server === \false && $send_kex) { + $this->send_binary_packet($kexinit_payload_client); + while (\true) { + $kexinit_payload_server = $this->get_binary_packet(); + switch (\ord($kexinit_payload_server[0])) { + case NET_SSH2_MSG_KEXINIT: + break 2; + case NET_SSH2_MSG_DISCONNECT: + return $this->handleDisconnect($kexinit_payload_server); + } + $this->kex_buffer[] = $kexinit_payload_server; + } + $send_kex = \false; + } + $response = $kexinit_payload_server; + \FluentSmtpLib\phpseclib3\Common\Functions\Strings::shift($response, 1); + // skip past the message number (it should be SSH_MSG_KEXINIT) + $server_cookie = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::shift($response, 16); + list($this->kex_algorithms, $this->server_host_key_algorithms, $this->encryption_algorithms_client_to_server, $this->encryption_algorithms_server_to_client, $this->mac_algorithms_client_to_server, $this->mac_algorithms_server_to_client, $this->compression_algorithms_client_to_server, $this->compression_algorithms_server_to_client, $this->languages_client_to_server, $this->languages_server_to_client, $first_kex_packet_follows) = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::unpackSSH2('L10C', $response); + if (\in_array('kex-strict-s-v00@openssh.com', $this->kex_algorithms)) { + if ($this->session_id === \false) { + // [kex-strict-s-v00@openssh.com is] only valid in the initial SSH2_MSG_KEXINIT and MUST be ignored + // if [it is] present in subsequent SSH2_MSG_KEXINIT packets + $this->strict_kex_flag = \true; + if (\count($this->kex_buffer)) { + throw new \UnexpectedValueException('Possible Terrapin Attack detected'); + } + } + } + $this->supported_private_key_algorithms = $this->server_host_key_algorithms; + if ($send_kex) { + $this->send_binary_packet($kexinit_payload_client); + } + // we need to decide upon the symmetric encryption algorithms before we do the diffie-hellman key exchange + // we don't initialize any crypto-objects, yet - we do that, later. for now, we need the lengths to make the + // diffie-hellman key exchange as fast as possible + $decrypt = self::array_intersect_first($s2c_encryption_algorithms, $this->encryption_algorithms_server_to_client); + if (!$decrypt || ($decryptKeyLength = $this->encryption_algorithm_to_key_size($decrypt)) === null) { + $this->disconnect_helper(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); + throw new \FluentSmtpLib\phpseclib3\Exception\NoSupportedAlgorithmsException('No compatible server to client encryption algorithms found'); + } + $encrypt = self::array_intersect_first($c2s_encryption_algorithms, $this->encryption_algorithms_client_to_server); + if (!$encrypt || ($encryptKeyLength = $this->encryption_algorithm_to_key_size($encrypt)) === null) { + $this->disconnect_helper(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); + throw new \FluentSmtpLib\phpseclib3\Exception\NoSupportedAlgorithmsException('No compatible client to server encryption algorithms found'); + } + // through diffie-hellman key exchange a symmetric key is obtained + $this->kex_algorithm = self::array_intersect_first($kex_algorithms, $this->kex_algorithms); + if ($this->kex_algorithm === \false) { + $this->disconnect_helper(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); + throw new \FluentSmtpLib\phpseclib3\Exception\NoSupportedAlgorithmsException('No compatible key exchange algorithms found'); + } + $server_host_key_algorithm = self::array_intersect_first($server_host_key_algorithms, $this->server_host_key_algorithms); + if ($server_host_key_algorithm === \false) { + $this->disconnect_helper(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); + throw new \FluentSmtpLib\phpseclib3\Exception\NoSupportedAlgorithmsException('No compatible server host key algorithms found'); + } + $mac_algorithm_out = self::array_intersect_first($c2s_mac_algorithms, $this->mac_algorithms_client_to_server); + if ($mac_algorithm_out === \false) { + $this->disconnect_helper(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); + throw new \FluentSmtpLib\phpseclib3\Exception\NoSupportedAlgorithmsException('No compatible client to server message authentication algorithms found'); + } + $mac_algorithm_in = self::array_intersect_first($s2c_mac_algorithms, $this->mac_algorithms_server_to_client); + if ($mac_algorithm_in === \false) { + $this->disconnect_helper(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); + throw new \FluentSmtpLib\phpseclib3\Exception\NoSupportedAlgorithmsException('No compatible server to client message authentication algorithms found'); + } + $compression_map = ['none' => self::NET_SSH2_COMPRESSION_NONE, 'zlib' => self::NET_SSH2_COMPRESSION_ZLIB, 'zlib@openssh.com' => self::NET_SSH2_COMPRESSION_ZLIB_AT_OPENSSH]; + $compression_algorithm_in = self::array_intersect_first($s2c_compression_algorithms, $this->compression_algorithms_server_to_client); + if ($compression_algorithm_in === \false) { + $this->disconnect_helper(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); + throw new \FluentSmtpLib\phpseclib3\Exception\NoSupportedAlgorithmsException('No compatible server to client compression algorithms found'); + } + $this->decompress = $compression_map[$compression_algorithm_in]; + $compression_algorithm_out = self::array_intersect_first($c2s_compression_algorithms, $this->compression_algorithms_client_to_server); + if ($compression_algorithm_out === \false) { + $this->disconnect_helper(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); + throw new \FluentSmtpLib\phpseclib3\Exception\NoSupportedAlgorithmsException('No compatible client to server compression algorithms found'); + } + $this->compress = $compression_map[$compression_algorithm_out]; + switch ($this->kex_algorithm) { + case 'diffie-hellman-group15-sha512': + case 'diffie-hellman-group16-sha512': + case 'diffie-hellman-group17-sha512': + case 'diffie-hellman-group18-sha512': + case 'ecdh-sha2-nistp521': + $kexHash = new \FluentSmtpLib\phpseclib3\Crypt\Hash('sha512'); + break; + case 'ecdh-sha2-nistp384': + $kexHash = new \FluentSmtpLib\phpseclib3\Crypt\Hash('sha384'); + break; + case 'diffie-hellman-group-exchange-sha256': + case 'diffie-hellman-group14-sha256': + case 'ecdh-sha2-nistp256': + case 'curve25519-sha256@libssh.org': + case 'curve25519-sha256': + $kexHash = new \FluentSmtpLib\phpseclib3\Crypt\Hash('sha256'); + break; + default: + $kexHash = new \FluentSmtpLib\phpseclib3\Crypt\Hash('sha1'); + } + // Only relevant in diffie-hellman-group-exchange-sha{1,256}, otherwise empty. + $exchange_hash_rfc4419 = ''; + if (\strpos($this->kex_algorithm, 'curve25519-sha256') === 0 || \strpos($this->kex_algorithm, 'ecdh-sha2-nistp') === 0) { + $curve = \strpos($this->kex_algorithm, 'curve25519-sha256') === 0 ? 'Curve25519' : \substr($this->kex_algorithm, 10); + $ourPrivate = \FluentSmtpLib\phpseclib3\Crypt\EC::createKey($curve); + $ourPublicBytes = $ourPrivate->getPublicKey()->getEncodedCoordinates(); + $clientKexInitMessage = 'NET_SSH2_MSG_KEX_ECDH_INIT'; + $serverKexReplyMessage = 'NET_SSH2_MSG_KEX_ECDH_REPLY'; + } else { + if (\strpos($this->kex_algorithm, 'diffie-hellman-group-exchange') === 0) { + $dh_group_sizes_packed = \pack('NNN', $this->kex_dh_group_size_min, $this->kex_dh_group_size_preferred, $this->kex_dh_group_size_max); + $packet = \pack('Ca*', NET_SSH2_MSG_KEXDH_GEX_REQUEST, $dh_group_sizes_packed); + $this->send_binary_packet($packet); + $this->updateLogHistory('UNKNOWN (34)', 'NET_SSH2_MSG_KEXDH_GEX_REQUEST'); + $response = $this->get_binary_packet_or_close(NET_SSH2_MSG_KEXDH_GEX_GROUP); + list($type, $primeBytes, $gBytes) = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::unpackSSH2('Css', $response); + $this->updateLogHistory('NET_SSH2_MSG_KEXDH_REPLY', 'NET_SSH2_MSG_KEXDH_GEX_GROUP'); + $prime = new \FluentSmtpLib\phpseclib3\Math\BigInteger($primeBytes, -256); + $g = new \FluentSmtpLib\phpseclib3\Math\BigInteger($gBytes, -256); + $exchange_hash_rfc4419 = $dh_group_sizes_packed . \FluentSmtpLib\phpseclib3\Common\Functions\Strings::packSSH2('ss', $primeBytes, $gBytes); + $params = \FluentSmtpLib\phpseclib3\Crypt\DH::createParameters($prime, $g); + $clientKexInitMessage = 'NET_SSH2_MSG_KEXDH_GEX_INIT'; + $serverKexReplyMessage = 'NET_SSH2_MSG_KEXDH_GEX_REPLY'; + } else { + $params = \FluentSmtpLib\phpseclib3\Crypt\DH::createParameters($this->kex_algorithm); + $clientKexInitMessage = 'NET_SSH2_MSG_KEXDH_INIT'; + $serverKexReplyMessage = 'NET_SSH2_MSG_KEXDH_REPLY'; + } + $keyLength = \min($kexHash->getLengthInBytes(), \max($encryptKeyLength, $decryptKeyLength)); + $ourPrivate = \FluentSmtpLib\phpseclib3\Crypt\DH::createKey($params, 16 * $keyLength); + // 2 * 8 * $keyLength + $ourPublic = $ourPrivate->getPublicKey()->toBigInteger(); + $ourPublicBytes = $ourPublic->toBytes(\true); + } + $data = \pack('CNa*', \constant($clientKexInitMessage), \strlen($ourPublicBytes), $ourPublicBytes); + $this->send_binary_packet($data); + switch ($clientKexInitMessage) { + case 'NET_SSH2_MSG_KEX_ECDH_INIT': + $this->updateLogHistory('NET_SSH2_MSG_KEXDH_INIT', 'NET_SSH2_MSG_KEX_ECDH_INIT'); + break; + case 'NET_SSH2_MSG_KEXDH_GEX_INIT': + $this->updateLogHistory('UNKNOWN (32)', 'NET_SSH2_MSG_KEXDH_GEX_INIT'); + } + $response = $this->get_binary_packet_or_close(\constant($serverKexReplyMessage)); + list($type, $server_public_host_key, $theirPublicBytes, $this->signature) = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::unpackSSH2('Csss', $response); + switch ($serverKexReplyMessage) { + case 'NET_SSH2_MSG_KEX_ECDH_REPLY': + $this->updateLogHistory('NET_SSH2_MSG_KEXDH_REPLY', 'NET_SSH2_MSG_KEX_ECDH_REPLY'); + break; + case 'NET_SSH2_MSG_KEXDH_GEX_REPLY': + $this->updateLogHistory('UNKNOWN (33)', 'NET_SSH2_MSG_KEXDH_GEX_REPLY'); + } + $this->server_public_host_key = $server_public_host_key; + list($public_key_format) = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::unpackSSH2('s', $server_public_host_key); + if (\strlen($this->signature) < 4) { + throw new \LengthException('The signature needs at least four bytes'); + } + $temp = \unpack('Nlength', \substr($this->signature, 0, 4)); + $this->signature_format = \substr($this->signature, 4, $temp['length']); + $keyBytes = \FluentSmtpLib\phpseclib3\Crypt\DH::computeSecret($ourPrivate, $theirPublicBytes); + if (($keyBytes & "\xff\x80") === "\x00\x00") { + $keyBytes = \substr($keyBytes, 1); + } elseif (($keyBytes[0] & "\x80") === "\x80") { + $keyBytes = "\x00{$keyBytes}"; + } + $this->exchange_hash = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::packSSH2('s5', $this->identifier, $this->server_identifier, $kexinit_payload_client, $kexinit_payload_server, $this->server_public_host_key); + $this->exchange_hash .= $exchange_hash_rfc4419; + $this->exchange_hash .= \FluentSmtpLib\phpseclib3\Common\Functions\Strings::packSSH2('s3', $ourPublicBytes, $theirPublicBytes, $keyBytes); + $this->exchange_hash = $kexHash->hash($this->exchange_hash); + if ($this->session_id === \false) { + $this->session_id = $this->exchange_hash; + } + switch ($server_host_key_algorithm) { + case 'rsa-sha2-256': + case 'rsa-sha2-512': + //case 'ssh-rsa': + $expected_key_format = 'ssh-rsa'; + break; + default: + $expected_key_format = $server_host_key_algorithm; + } + if ($public_key_format != $expected_key_format || $this->signature_format != $server_host_key_algorithm) { + switch (\true) { + case $this->signature_format == $server_host_key_algorithm: + case $server_host_key_algorithm != 'rsa-sha2-256' && $server_host_key_algorithm != 'rsa-sha2-512': + case $this->signature_format != 'ssh-rsa': + $this->disconnect_helper(NET_SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE); + throw new \RuntimeException('Server Host Key Algorithm Mismatch (' . $this->signature_format . ' vs ' . $server_host_key_algorithm . ')'); + } + } + $packet = \pack('C', NET_SSH2_MSG_NEWKEYS); + $this->send_binary_packet($packet); + $this->get_binary_packet_or_close(NET_SSH2_MSG_NEWKEYS); + $this->keyExchangeInProgress = \false; + if ($this->strict_kex_flag) { + $this->get_seq_no = $this->send_seq_no = 0; + } + $keyBytes = \pack('Na*', \strlen($keyBytes), $keyBytes); + $this->encrypt = self::encryption_algorithm_to_crypt_instance($encrypt); + if ($this->encrypt) { + if (self::$crypto_engine) { + $this->encrypt->setPreferredEngine(self::$crypto_engine); + } + if ($this->encrypt->getBlockLengthInBytes()) { + $this->encrypt_block_size = $this->encrypt->getBlockLengthInBytes(); + } + $this->encrypt->disablePadding(); + if ($this->encrypt->usesIV()) { + $iv = $kexHash->hash($keyBytes . $this->exchange_hash . 'A' . $this->session_id); + while ($this->encrypt_block_size > \strlen($iv)) { + $iv .= $kexHash->hash($keyBytes . $this->exchange_hash . $iv); + } + $this->encrypt->setIV(\substr($iv, 0, $this->encrypt_block_size)); + } + switch ($encrypt) { + case 'aes128-gcm@openssh.com': + case 'aes256-gcm@openssh.com': + $nonce = $kexHash->hash($keyBytes . $this->exchange_hash . 'A' . $this->session_id); + $this->encryptFixedPart = \substr($nonce, 0, 4); + $this->encryptInvocationCounter = \substr($nonce, 4, 8); + // fall-through + case 'chacha20-poly1305@openssh.com': + break; + default: + $this->encrypt->enableContinuousBuffer(); + } + $key = $kexHash->hash($keyBytes . $this->exchange_hash . 'C' . $this->session_id); + while ($encryptKeyLength > \strlen($key)) { + $key .= $kexHash->hash($keyBytes . $this->exchange_hash . $key); + } + switch ($encrypt) { + case 'chacha20-poly1305@openssh.com': + $encryptKeyLength = 32; + $this->lengthEncrypt = self::encryption_algorithm_to_crypt_instance($encrypt); + $this->lengthEncrypt->setKey(\substr($key, 32, 32)); + } + $this->encrypt->setKey(\substr($key, 0, $encryptKeyLength)); + $this->encryptName = $encrypt; + } + $this->decrypt = self::encryption_algorithm_to_crypt_instance($decrypt); + if ($this->decrypt) { + if (self::$crypto_engine) { + $this->decrypt->setPreferredEngine(self::$crypto_engine); + } + if ($this->decrypt->getBlockLengthInBytes()) { + $this->decrypt_block_size = $this->decrypt->getBlockLengthInBytes(); + } + $this->decrypt->disablePadding(); + if ($this->decrypt->usesIV()) { + $iv = $kexHash->hash($keyBytes . $this->exchange_hash . 'B' . $this->session_id); + while ($this->decrypt_block_size > \strlen($iv)) { + $iv .= $kexHash->hash($keyBytes . $this->exchange_hash . $iv); + } + $this->decrypt->setIV(\substr($iv, 0, $this->decrypt_block_size)); + } + switch ($decrypt) { + case 'aes128-gcm@openssh.com': + case 'aes256-gcm@openssh.com': + // see https://tools.ietf.org/html/rfc5647#section-7.1 + $nonce = $kexHash->hash($keyBytes . $this->exchange_hash . 'B' . $this->session_id); + $this->decryptFixedPart = \substr($nonce, 0, 4); + $this->decryptInvocationCounter = \substr($nonce, 4, 8); + // fall-through + case 'chacha20-poly1305@openssh.com': + break; + default: + $this->decrypt->enableContinuousBuffer(); + } + $key = $kexHash->hash($keyBytes . $this->exchange_hash . 'D' . $this->session_id); + while ($decryptKeyLength > \strlen($key)) { + $key .= $kexHash->hash($keyBytes . $this->exchange_hash . $key); + } + switch ($decrypt) { + case 'chacha20-poly1305@openssh.com': + $decryptKeyLength = 32; + $this->lengthDecrypt = self::encryption_algorithm_to_crypt_instance($decrypt); + $this->lengthDecrypt->setKey(\substr($key, 32, 32)); + } + $this->decrypt->setKey(\substr($key, 0, $decryptKeyLength)); + $this->decryptName = $decrypt; + } + /* The "arcfour128" algorithm is the RC4 cipher, as described in + [SCHNEIER], using a 128-bit key. The first 1536 bytes of keystream + generated by the cipher MUST be discarded, and the first byte of the + first encrypted packet MUST be encrypted using the 1537th byte of + keystream. + + -- http://tools.ietf.org/html/rfc4345#section-4 */ + if ($encrypt == 'arcfour128' || $encrypt == 'arcfour256') { + $this->encrypt->encrypt(\str_repeat("\x00", 1536)); + } + if ($decrypt == 'arcfour128' || $decrypt == 'arcfour256') { + $this->decrypt->decrypt(\str_repeat("\x00", 1536)); + } + if (!$this->encrypt->usesNonce()) { + list($this->hmac_create, $createKeyLength) = self::mac_algorithm_to_hash_instance($mac_algorithm_out); + } else { + $this->hmac_create = new \stdClass(); + $this->hmac_create_name = $mac_algorithm_out; + //$mac_algorithm_out = 'none'; + $createKeyLength = 0; + } + if ($this->hmac_create instanceof \FluentSmtpLib\phpseclib3\Crypt\Hash) { + $key = $kexHash->hash($keyBytes . $this->exchange_hash . 'E' . $this->session_id); + while ($createKeyLength > \strlen($key)) { + $key .= $kexHash->hash($keyBytes . $this->exchange_hash . $key); + } + $this->hmac_create->setKey(\substr($key, 0, $createKeyLength)); + $this->hmac_create_name = $mac_algorithm_out; + $this->hmac_create_etm = \preg_match('#-etm@openssh\\.com$#', $mac_algorithm_out); + } + if (!$this->decrypt->usesNonce()) { + list($this->hmac_check, $checkKeyLength) = self::mac_algorithm_to_hash_instance($mac_algorithm_in); + $this->hmac_size = $this->hmac_check->getLengthInBytes(); + } else { + $this->hmac_check = new \stdClass(); + $this->hmac_check_name = $mac_algorithm_in; + //$mac_algorithm_in = 'none'; + $checkKeyLength = 0; + $this->hmac_size = 0; + } + if ($this->hmac_check instanceof \FluentSmtpLib\phpseclib3\Crypt\Hash) { + $key = $kexHash->hash($keyBytes . $this->exchange_hash . 'F' . $this->session_id); + while ($checkKeyLength > \strlen($key)) { + $key .= $kexHash->hash($keyBytes . $this->exchange_hash . $key); + } + $this->hmac_check->setKey(\substr($key, 0, $checkKeyLength)); + $this->hmac_check_name = $mac_algorithm_in; + $this->hmac_check_etm = \preg_match('#-etm@openssh\\.com$#', $mac_algorithm_in); + } + $this->regenerate_compression_context = $this->regenerate_decompression_context = \true; + return \true; + } + /** + * Maps an encryption algorithm name to the number of key bytes. + * + * @param string $algorithm Name of the encryption algorithm + * @return int|null Number of bytes as an integer or null for unknown + */ + private function encryption_algorithm_to_key_size($algorithm) + { + if ($this->bad_key_size_fix && self::bad_algorithm_candidate($algorithm)) { + return 16; + } + switch ($algorithm) { + case 'none': + return 0; + case 'aes128-gcm@openssh.com': + case 'aes128-cbc': + case 'aes128-ctr': + case 'arcfour': + case 'arcfour128': + case 'blowfish-cbc': + case 'blowfish-ctr': + case 'twofish128-cbc': + case 'twofish128-ctr': + return 16; + case '3des-cbc': + case '3des-ctr': + case 'aes192-cbc': + case 'aes192-ctr': + case 'twofish192-cbc': + case 'twofish192-ctr': + return 24; + case 'aes256-gcm@openssh.com': + case 'aes256-cbc': + case 'aes256-ctr': + case 'arcfour256': + case 'twofish-cbc': + case 'twofish256-cbc': + case 'twofish256-ctr': + return 32; + case 'chacha20-poly1305@openssh.com': + return 64; + } + return null; + } + /** + * Maps an encryption algorithm name to an instance of a subclass of + * \phpseclib3\Crypt\Common\SymmetricKey. + * + * @param string $algorithm Name of the encryption algorithm + * @return SymmetricKey|null + */ + private static function encryption_algorithm_to_crypt_instance($algorithm) + { + switch ($algorithm) { + case '3des-cbc': + return new \FluentSmtpLib\phpseclib3\Crypt\TripleDES('cbc'); + case '3des-ctr': + return new \FluentSmtpLib\phpseclib3\Crypt\TripleDES('ctr'); + case 'aes256-cbc': + case 'aes192-cbc': + case 'aes128-cbc': + return new \FluentSmtpLib\phpseclib3\Crypt\Rijndael('cbc'); + case 'aes256-ctr': + case 'aes192-ctr': + case 'aes128-ctr': + return new \FluentSmtpLib\phpseclib3\Crypt\Rijndael('ctr'); + case 'blowfish-cbc': + return new \FluentSmtpLib\phpseclib3\Crypt\Blowfish('cbc'); + case 'blowfish-ctr': + return new \FluentSmtpLib\phpseclib3\Crypt\Blowfish('ctr'); + case 'twofish128-cbc': + case 'twofish192-cbc': + case 'twofish256-cbc': + case 'twofish-cbc': + return new \FluentSmtpLib\phpseclib3\Crypt\Twofish('cbc'); + case 'twofish128-ctr': + case 'twofish192-ctr': + case 'twofish256-ctr': + return new \FluentSmtpLib\phpseclib3\Crypt\Twofish('ctr'); + case 'arcfour': + case 'arcfour128': + case 'arcfour256': + return new \FluentSmtpLib\phpseclib3\Crypt\RC4(); + case 'aes128-gcm@openssh.com': + case 'aes256-gcm@openssh.com': + return new \FluentSmtpLib\phpseclib3\Crypt\Rijndael('gcm'); + case 'chacha20-poly1305@openssh.com': + return new \FluentSmtpLib\phpseclib3\Crypt\ChaCha20(); + } + return null; + } + /** + * Maps an encryption algorithm name to an instance of a subclass of + * \phpseclib3\Crypt\Hash. + * + * @param string $algorithm Name of the encryption algorithm + * @return array{Hash, int}|null + */ + private static function mac_algorithm_to_hash_instance($algorithm) + { + switch ($algorithm) { + case 'umac-64@openssh.com': + case 'umac-64-etm@openssh.com': + return [new \FluentSmtpLib\phpseclib3\Crypt\Hash('umac-64'), 16]; + case 'umac-128@openssh.com': + case 'umac-128-etm@openssh.com': + return [new \FluentSmtpLib\phpseclib3\Crypt\Hash('umac-128'), 16]; + case 'hmac-sha2-512': + case 'hmac-sha2-512-etm@openssh.com': + return [new \FluentSmtpLib\phpseclib3\Crypt\Hash('sha512'), 64]; + case 'hmac-sha2-256': + case 'hmac-sha2-256-etm@openssh.com': + return [new \FluentSmtpLib\phpseclib3\Crypt\Hash('sha256'), 32]; + case 'hmac-sha1': + case 'hmac-sha1-etm@openssh.com': + return [new \FluentSmtpLib\phpseclib3\Crypt\Hash('sha1'), 20]; + case 'hmac-sha1-96': + return [new \FluentSmtpLib\phpseclib3\Crypt\Hash('sha1-96'), 20]; + case 'hmac-md5': + return [new \FluentSmtpLib\phpseclib3\Crypt\Hash('md5'), 16]; + case 'hmac-md5-96': + return [new \FluentSmtpLib\phpseclib3\Crypt\Hash('md5-96'), 16]; + } + } + /** + * Tests whether or not proposed algorithm has a potential for issues + * + * @link https://www.chiark.greenend.org.uk/~sgtatham/putty/wishlist/ssh2-aesctr-openssh.html + * @link https://bugzilla.mindrot.org/show_bug.cgi?id=1291 + * @param string $algorithm Name of the encryption algorithm + * @return bool + */ + private static function bad_algorithm_candidate($algorithm) + { + switch ($algorithm) { + case 'arcfour256': + case 'aes192-ctr': + case 'aes256-ctr': + return \true; + } + return \false; + } + /** + * Login + * + * The $password parameter can be a plaintext password, a \phpseclib3\Crypt\RSA|EC|DSA object, a \phpseclib3\System\SSH\Agent object or an array + * + * @param string $username + * @param string|PrivateKey|array[]|Agent|null ...$args + * @return bool + * @see self::_login() + */ + public function login($username, ...$args) + { + if (!$this->login_credentials_finalized) { + $this->auth[] = \func_get_args(); + } + // try logging with 'none' as an authentication method first since that's what + // PuTTY does + if (\substr($this->server_identifier, 0, 15) != 'SSH-2.0-CoreFTP' && $this->auth_methods_to_continue === null) { + if ($this->sublogin($username)) { + return \true; + } + if (!\count($args)) { + return \false; + } + } + return $this->sublogin($username, ...$args); + } + /** + * Login Helper + * + * @param string $username + * @param string|PrivateKey|array[]|Agent|null ...$args + * @return bool + * @see self::_login_helper() + */ + protected function sublogin($username, ...$args) + { + if (!($this->bitmap & self::MASK_CONSTRUCTOR)) { + $this->connect(); + } + if (empty($args)) { + return $this->login_helper($username); + } + foreach ($args as $arg) { + switch (\true) { + case $arg instanceof \FluentSmtpLib\phpseclib3\Crypt\Common\PublicKey: + throw new \UnexpectedValueException('A PublicKey object was passed to the login method instead of a PrivateKey object'); + case $arg instanceof \FluentSmtpLib\phpseclib3\Crypt\Common\PrivateKey: + case $arg instanceof \FluentSmtpLib\phpseclib3\System\SSH\Agent: + case \is_array($arg): + case \FluentSmtpLib\phpseclib3\Common\Functions\Strings::is_stringable($arg): + break; + default: + throw new \UnexpectedValueException('$password needs to either be an instance of \\phpseclib3\\Crypt\\Common\\PrivateKey, \\System\\SSH\\Agent, an array or a string'); + } + } + while (\count($args)) { + if (!$this->auth_methods_to_continue || !$this->smartMFA) { + $newargs = $args; + $args = []; + } else { + $newargs = []; + foreach ($this->auth_methods_to_continue as $method) { + switch ($method) { + case 'publickey': + foreach ($args as $key => $arg) { + if ($arg instanceof \FluentSmtpLib\phpseclib3\Crypt\Common\PrivateKey || $arg instanceof \FluentSmtpLib\phpseclib3\System\SSH\Agent) { + $newargs[] = $arg; + unset($args[$key]); + break; + } + } + break; + case 'keyboard-interactive': + $hasArray = $hasString = \false; + foreach ($args as $arg) { + if ($hasArray || \is_array($arg)) { + $hasArray = \true; + break; + } + if ($hasString || \FluentSmtpLib\phpseclib3\Common\Functions\Strings::is_stringable($arg)) { + $hasString = \true; + break; + } + } + if ($hasArray && $hasString) { + foreach ($args as $key => $arg) { + if (\is_array($arg)) { + $newargs[] = $arg; + break 2; + } + } + } + // fall-through + case 'password': + foreach ($args as $key => $arg) { + $newargs[] = $arg; + unset($args[$key]); + break; + } + } + } + } + if (!\count($newargs)) { + return \false; + } + foreach ($newargs as $arg) { + if ($this->login_helper($username, $arg)) { + $this->login_credentials_finalized = \true; + return \true; + } + } + } + return \false; + } + /** + * Login Helper + * + * {@internal It might be worthwhile, at some point, to protect against {@link http://tools.ietf.org/html/rfc4251#section-9.3.9 traffic analysis} + * by sending dummy SSH_MSG_IGNORE messages.} + * + * @param string $username + * @param string|AsymmetricKey|array[]|Agent|null ...$args + * @return bool + * @throws \UnexpectedValueException on receipt of unexpected packets + * @throws \RuntimeException on other errors + */ + private function login_helper($username, $password = null) + { + if (!($this->bitmap & self::MASK_CONNECTED)) { + return \false; + } + if (!($this->bitmap & self::MASK_LOGIN_REQ)) { + $packet = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::packSSH2('Cs', NET_SSH2_MSG_SERVICE_REQUEST, 'ssh-userauth'); + $this->send_binary_packet($packet); + try { + $response = $this->get_binary_packet_or_close(NET_SSH2_MSG_SERVICE_ACCEPT); + } catch (\FluentSmtpLib\phpseclib3\Exception\InvalidPacketLengthException $e) { + // the first opportunity to encounter the "bad key size" error + if (!$this->bad_key_size_fix && $this->decryptName != null && self::bad_algorithm_candidate($this->decryptName)) { + // bad_key_size_fix is only ever re-assigned to true here + // retry the connection with that new setting but we'll + // only try it once. + $this->bad_key_size_fix = \true; + return $this->reconnect(); + } + throw $e; + } + list($type) = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::unpackSSH2('C', $response); + list($service) = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::unpackSSH2('s', $response); + if ($service != 'ssh-userauth') { + $this->disconnect_helper(NET_SSH2_DISCONNECT_PROTOCOL_ERROR); + throw new \UnexpectedValueException('Expected SSH_MSG_SERVICE_ACCEPT'); + } + $this->bitmap |= self::MASK_LOGIN_REQ; + } + if (\strlen($this->last_interactive_response)) { + return !\FluentSmtpLib\phpseclib3\Common\Functions\Strings::is_stringable($password) && !\is_array($password) ? \false : $this->keyboard_interactive_process($password); + } + if ($password instanceof \FluentSmtpLib\phpseclib3\Crypt\Common\PrivateKey) { + return $this->privatekey_login($username, $password); + } + if ($password instanceof \FluentSmtpLib\phpseclib3\System\SSH\Agent) { + return $this->ssh_agent_login($username, $password); + } + if (\is_array($password)) { + if ($this->keyboard_interactive_login($username, $password)) { + $this->bitmap |= self::MASK_LOGIN; + return \true; + } + return \false; + } + if (!isset($password)) { + $packet = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::packSSH2('Cs3', NET_SSH2_MSG_USERAUTH_REQUEST, $username, 'ssh-connection', 'none'); + $this->send_binary_packet($packet); + $response = $this->get_binary_packet_or_close(); + list($type) = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::unpackSSH2('C', $response); + switch ($type) { + case NET_SSH2_MSG_USERAUTH_SUCCESS: + $this->bitmap |= self::MASK_LOGIN; + return \true; + case NET_SSH2_MSG_USERAUTH_FAILURE: + list($auth_methods) = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::unpackSSH2('L', $response); + $this->auth_methods_to_continue = $auth_methods; + // fall-through + default: + return \false; + } + } + $packet = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::packSSH2('Cs3bs', NET_SSH2_MSG_USERAUTH_REQUEST, $username, 'ssh-connection', 'password', \false, $password); + // remove the username and password from the logged packet + if (!\defined('FluentSmtpLib\\NET_SSH2_LOGGING')) { + $logged = null; + } else { + $logged = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::packSSH2('Cs3bs', NET_SSH2_MSG_USERAUTH_REQUEST, $username, 'ssh-connection', 'password', \false, 'password'); + } + $this->send_binary_packet($packet, $logged); + $response = $this->get_binary_packet_or_close(); + list($type) = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::unpackSSH2('C', $response); + switch ($type) { + case NET_SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ: + // in theory, the password can be changed + $this->updateLogHistory('UNKNOWN (60)', 'NET_SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ'); + list($message) = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::unpackSSH2('s', $response); + $this->errors[] = 'SSH_MSG_USERAUTH_PASSWD_CHANGEREQ: ' . $message; + return $this->disconnect_helper(NET_SSH2_DISCONNECT_AUTH_CANCELLED_BY_USER); + case NET_SSH2_MSG_USERAUTH_FAILURE: + // can we use keyboard-interactive authentication? if not then either the login is bad or the server employees + // multi-factor authentication + list($auth_methods, $partial_success) = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::unpackSSH2('Lb', $response); + $this->auth_methods_to_continue = $auth_methods; + if (!$partial_success && \in_array('keyboard-interactive', $auth_methods)) { + if ($this->keyboard_interactive_login($username, $password)) { + $this->bitmap |= self::MASK_LOGIN; + return \true; + } + return \false; + } + return \false; + case NET_SSH2_MSG_USERAUTH_SUCCESS: + $this->bitmap |= self::MASK_LOGIN; + return \true; + } + return \false; + } + /** + * Login via keyboard-interactive authentication + * + * See {@link http://tools.ietf.org/html/rfc4256 RFC4256} for details. This is not a full-featured keyboard-interactive authenticator. + * + * @param string $username + * @param string|array $password + * @return bool + */ + private function keyboard_interactive_login($username, $password) + { + $packet = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::packSSH2( + 'Cs5', + NET_SSH2_MSG_USERAUTH_REQUEST, + $username, + 'ssh-connection', + 'keyboard-interactive', + '', + // language tag + '' + ); + $this->send_binary_packet($packet); + return $this->keyboard_interactive_process($password); + } + /** + * Handle the keyboard-interactive requests / responses. + * + * @param string|array ...$responses + * @return bool + * @throws \RuntimeException on connection error + */ + private function keyboard_interactive_process(...$responses) + { + if (\strlen($this->last_interactive_response)) { + $response = $this->last_interactive_response; + } else { + $orig = $response = $this->get_binary_packet_or_close(); + } + list($type) = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::unpackSSH2('C', $response); + switch ($type) { + case NET_SSH2_MSG_USERAUTH_INFO_REQUEST: + list(, , , $num_prompts) = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::unpackSSH2('s3N', $response); + for ($i = 0; $i < \count($responses); $i++) { + if (\is_array($responses[$i])) { + foreach ($responses[$i] as $key => $value) { + $this->keyboard_requests_responses[$key] = $value; + } + unset($responses[$i]); + } + } + $responses = \array_values($responses); + if (isset($this->keyboard_requests_responses)) { + for ($i = 0; $i < $num_prompts; $i++) { + list($prompt, ) = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::unpackSSH2('sC', $response); + foreach ($this->keyboard_requests_responses as $key => $value) { + if (\substr($prompt, 0, \strlen($key)) == $key) { + $responses[] = $value; + break; + } + } + } + } + // see http://tools.ietf.org/html/rfc4256#section-3.2 + if (\strlen($this->last_interactive_response)) { + $this->last_interactive_response = ''; + } else { + $this->updateLogHistory('UNKNOWN (60)', 'NET_SSH2_MSG_USERAUTH_INFO_REQUEST'); + } + if (!\count($responses) && $num_prompts) { + $this->last_interactive_response = $orig; + return \false; + } + /* + After obtaining the requested information from the user, the client + MUST respond with an SSH_MSG_USERAUTH_INFO_RESPONSE message. + */ + // see http://tools.ietf.org/html/rfc4256#section-3.4 + $packet = $logged = \pack('CN', NET_SSH2_MSG_USERAUTH_INFO_RESPONSE, \count($responses)); + for ($i = 0; $i < \count($responses); $i++) { + $packet .= \FluentSmtpLib\phpseclib3\Common\Functions\Strings::packSSH2('s', $responses[$i]); + $logged .= \FluentSmtpLib\phpseclib3\Common\Functions\Strings::packSSH2('s', 'dummy-answer'); + } + $this->send_binary_packet($packet, $logged); + $this->updateLogHistory('UNKNOWN (61)', 'NET_SSH2_MSG_USERAUTH_INFO_RESPONSE'); + /* + After receiving the response, the server MUST send either an + SSH_MSG_USERAUTH_SUCCESS, SSH_MSG_USERAUTH_FAILURE, or another + SSH_MSG_USERAUTH_INFO_REQUEST message. + */ + // maybe phpseclib should force close the connection after x request / responses? unless something like that is done + // there could be an infinite loop of request / responses. + return $this->keyboard_interactive_process(); + case NET_SSH2_MSG_USERAUTH_SUCCESS: + return \true; + case NET_SSH2_MSG_USERAUTH_FAILURE: + list($auth_methods) = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::unpackSSH2('L', $response); + $this->auth_methods_to_continue = $auth_methods; + return \false; + } + return \false; + } + /** + * Login with an ssh-agent provided key + * + * @param string $username + * @param Agent $agent + * @return bool + */ + private function ssh_agent_login($username, \FluentSmtpLib\phpseclib3\System\SSH\Agent $agent) + { + $this->agent = $agent; + $keys = $agent->requestIdentities(); + $orig_algorithms = $this->supported_private_key_algorithms; + foreach ($keys as $key) { + if ($this->privatekey_login($username, $key)) { + return \true; + } + $this->supported_private_key_algorithms = $orig_algorithms; + } + return \false; + } + /** + * Login with an RSA private key + * + * {@internal It might be worthwhile, at some point, to protect against {@link http://tools.ietf.org/html/rfc4251#section-9.3.9 traffic analysis} + * by sending dummy SSH_MSG_IGNORE messages.} + * + * @param string $username + * @param PrivateKey $privatekey + * @return bool + * @throws \RuntimeException on connection error + */ + private function privatekey_login($username, \FluentSmtpLib\phpseclib3\Crypt\Common\PrivateKey $privatekey) + { + $publickey = $privatekey->getPublicKey(); + if ($publickey instanceof \FluentSmtpLib\phpseclib3\Crypt\RSA) { + $privatekey = $privatekey->withPadding(\FluentSmtpLib\phpseclib3\Crypt\RSA::SIGNATURE_PKCS1); + $algos = ['rsa-sha2-256', 'rsa-sha2-512', 'ssh-rsa']; + if (isset($this->preferred['hostkey'])) { + $algos = \array_intersect($algos, $this->preferred['hostkey']); + } + $algo = self::array_intersect_first($algos, $this->supported_private_key_algorithms); + switch ($algo) { + case 'rsa-sha2-512': + $hash = 'sha512'; + $signatureType = 'rsa-sha2-512'; + break; + case 'rsa-sha2-256': + $hash = 'sha256'; + $signatureType = 'rsa-sha2-256'; + break; + //case 'ssh-rsa': + default: + $hash = 'sha1'; + $signatureType = 'ssh-rsa'; + } + } elseif ($publickey instanceof \FluentSmtpLib\phpseclib3\Crypt\EC) { + $privatekey = $privatekey->withSignatureFormat('SSH2'); + $curveName = $privatekey->getCurve(); + switch ($curveName) { + case 'Ed25519': + $hash = 'sha512'; + $signatureType = 'ssh-ed25519'; + break; + case 'secp256r1': + // nistp256 + $hash = 'sha256'; + $signatureType = 'ecdsa-sha2-nistp256'; + break; + case 'secp384r1': + // nistp384 + $hash = 'sha384'; + $signatureType = 'ecdsa-sha2-nistp384'; + break; + case 'secp521r1': + // nistp521 + $hash = 'sha512'; + $signatureType = 'ecdsa-sha2-nistp521'; + break; + default: + if (\is_array($curveName)) { + throw new \FluentSmtpLib\phpseclib3\Exception\UnsupportedCurveException('Specified Curves are not supported by SSH2'); + } + throw new \FluentSmtpLib\phpseclib3\Exception\UnsupportedCurveException('Named Curve of ' . $curveName . ' is not supported by phpseclib3\'s SSH2 implementation'); + } + } elseif ($publickey instanceof \FluentSmtpLib\phpseclib3\Crypt\DSA) { + $privatekey = $privatekey->withSignatureFormat('SSH2'); + $hash = 'sha1'; + $signatureType = 'ssh-dss'; + } else { + throw new \FluentSmtpLib\phpseclib3\Exception\UnsupportedAlgorithmException('Please use either an RSA key, an EC one or a DSA key'); + } + $publickeyStr = $publickey->toString('OpenSSH', ['binary' => \true]); + $part1 = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::packSSH2('Csss', NET_SSH2_MSG_USERAUTH_REQUEST, $username, 'ssh-connection', 'publickey'); + $part2 = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::packSSH2('ss', $signatureType, $publickeyStr); + $packet = $part1 . \chr(0) . $part2; + $this->send_binary_packet($packet); + $response = $this->get_binary_packet_or_close(NET_SSH2_MSG_USERAUTH_SUCCESS, NET_SSH2_MSG_USERAUTH_FAILURE, NET_SSH2_MSG_USERAUTH_PK_OK); + list($type) = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::unpackSSH2('C', $response); + switch ($type) { + case NET_SSH2_MSG_USERAUTH_FAILURE: + list($auth_methods) = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::unpackSSH2('L', $response); + if (\in_array('publickey', $auth_methods) && \substr($signatureType, 0, 9) == 'rsa-sha2-') { + $this->supported_private_key_algorithms = \array_diff($this->supported_private_key_algorithms, ['rsa-sha2-256', 'rsa-sha2-512']); + return $this->privatekey_login($username, $privatekey); + } + $this->auth_methods_to_continue = $auth_methods; + $this->errors[] = 'SSH_MSG_USERAUTH_FAILURE'; + return \false; + case NET_SSH2_MSG_USERAUTH_PK_OK: + // we'll just take it on faith that the public key blob and the public key algorithm name are as + // they should be + $this->updateLogHistory('UNKNOWN (60)', 'NET_SSH2_MSG_USERAUTH_PK_OK'); + break; + case NET_SSH2_MSG_USERAUTH_SUCCESS: + $this->bitmap |= self::MASK_LOGIN; + return \true; + } + $packet = $part1 . \chr(1) . $part2; + $privatekey = $privatekey->withHash($hash); + $signature = $privatekey->sign(\FluentSmtpLib\phpseclib3\Common\Functions\Strings::packSSH2('s', $this->session_id) . $packet); + if ($publickey instanceof \FluentSmtpLib\phpseclib3\Crypt\RSA) { + $signature = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::packSSH2('ss', $signatureType, $signature); + } + $packet .= \FluentSmtpLib\phpseclib3\Common\Functions\Strings::packSSH2('s', $signature); + $this->send_binary_packet($packet); + $response = $this->get_binary_packet_or_close(NET_SSH2_MSG_USERAUTH_SUCCESS, NET_SSH2_MSG_USERAUTH_FAILURE); + list($type) = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::unpackSSH2('C', $response); + switch ($type) { + case NET_SSH2_MSG_USERAUTH_FAILURE: + // either the login is bad or the server employs multi-factor authentication + list($auth_methods) = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::unpackSSH2('L', $response); + $this->auth_methods_to_continue = $auth_methods; + return \false; + case NET_SSH2_MSG_USERAUTH_SUCCESS: + $this->bitmap |= self::MASK_LOGIN; + return \true; + } + } + /** + * Return the currently configured timeout + * + * @return int + */ + public function getTimeout() + { + return $this->timeout; + } + /** + * Set Timeout + * + * $ssh->exec('ping 127.0.0.1'); on a Linux host will never return and will run indefinitely. setTimeout() makes it so it'll timeout. + * Setting $timeout to false or 0 will revert to the default socket timeout. + * + * @param mixed $timeout + */ + public function setTimeout($timeout) + { + $this->timeout = $this->curTimeout = $timeout; + } + /** + * Set Keep Alive + * + * Sends an SSH2_MSG_IGNORE message every x seconds, if x is a positive non-zero number. + * + * @param int $interval + */ + public function setKeepAlive($interval) + { + $this->keepAlive = $interval; + } + /** + * Get the output from stdError + * + */ + public function getStdError() + { + return $this->stdErrorLog; + } + /** + * Execute Command + * + * If $callback is set to false then \phpseclib3\Net\SSH2::get_channel_packet(self::CHANNEL_EXEC) will need to be called manually. + * In all likelihood, this is not a feature you want to be taking advantage of. + * + * @param string $command + * @param callable $callback + * @return string|bool + * @psalm-return ($callback is callable ? bool : string|bool) + * @throws \RuntimeException on connection error + */ + public function exec($command, $callback = null) + { + $this->curTimeout = $this->timeout; + $this->is_timeout = \false; + $this->stdErrorLog = ''; + if (!$this->isAuthenticated()) { + return \false; + } + //if ($this->isPTYOpen()) { + // throw new \RuntimeException('If you want to run multiple exec()\'s you will need to disable (and re-enable if appropriate) a PTY for each one.'); + //} + $this->open_channel(self::CHANNEL_EXEC); + if ($this->request_pty === \true) { + $terminal_modes = \pack('C', NET_SSH2_TTY_OP_END); + $packet = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::packSSH2('CNsCsN4s', NET_SSH2_MSG_CHANNEL_REQUEST, $this->server_channels[self::CHANNEL_EXEC], 'pty-req', 1, $this->term, $this->windowColumns, $this->windowRows, 0, 0, $terminal_modes); + $this->send_binary_packet($packet); + $this->channel_status[self::CHANNEL_EXEC] = NET_SSH2_MSG_CHANNEL_REQUEST; + if (!$this->get_channel_packet(self::CHANNEL_EXEC)) { + $this->disconnect_helper(NET_SSH2_DISCONNECT_BY_APPLICATION); + throw new \RuntimeException('Unable to request pseudo-terminal'); + } + } + // sending a pty-req SSH_MSG_CHANNEL_REQUEST message is unnecessary and, in fact, in most cases, slows things + // down. the one place where it might be desirable is if you're doing something like \phpseclib3\Net\SSH2::exec('ping localhost &'). + // with a pty-req SSH_MSG_CHANNEL_REQUEST, exec() will return immediately and the ping process will then + // then immediately terminate. without such a request exec() will loop indefinitely. the ping process won't end but + // neither will your script. + // although, in theory, the size of SSH_MSG_CHANNEL_REQUEST could exceed the maximum packet size established by + // SSH_MSG_CHANNEL_OPEN_CONFIRMATION, RFC4254#section-5.1 states that the "maximum packet size" refers to the + // "maximum size of an individual data packet". ie. SSH_MSG_CHANNEL_DATA. RFC4254#section-5.2 corroborates. + $packet = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::packSSH2('CNsCs', NET_SSH2_MSG_CHANNEL_REQUEST, $this->server_channels[self::CHANNEL_EXEC], 'exec', 1, $command); + $this->send_binary_packet($packet); + $this->channel_status[self::CHANNEL_EXEC] = NET_SSH2_MSG_CHANNEL_REQUEST; + if (!$this->get_channel_packet(self::CHANNEL_EXEC)) { + return \false; + } + $this->channel_status[self::CHANNEL_EXEC] = NET_SSH2_MSG_CHANNEL_DATA; + if ($this->request_pty === \true) { + $this->channel_id_last_interactive = self::CHANNEL_EXEC; + return \true; + } + $output = ''; + while (\true) { + $temp = $this->get_channel_packet(self::CHANNEL_EXEC); + switch (\true) { + case $temp === \true: + return \is_callable($callback) ? \true : $output; + case $temp === \false: + return \false; + default: + if (\is_callable($callback)) { + if ($callback($temp) === \true) { + $this->close_channel(self::CHANNEL_EXEC); + return \true; + } + } else { + $output .= $temp; + } + } + } + } + /** + * How many channels are currently open? + * + * @return int + */ + public function getOpenChannelCount() + { + return $this->channelCount; + } + /** + * Opens a channel + * + * @param string $channel + * @param bool $skip_extended + * @return bool + */ + protected function open_channel($channel, $skip_extended = \false) + { + if (isset($this->channel_status[$channel]) && $this->channel_status[$channel] != NET_SSH2_MSG_CHANNEL_CLOSE) { + throw new \RuntimeException('Please close the channel (' . $channel . ') before trying to open it again'); + } + $this->channelCount++; + if ($this->channelCount > 1 && $this->errorOnMultipleChannels) { + throw new \RuntimeException("Ubuntu's OpenSSH from 5.8 to 6.9 doesn't work with multiple channels"); + } + // RFC4254 defines the (client) window size as "bytes the other party can send before it must wait for the window to + // be adjusted". 0x7FFFFFFF is, at 2GB, the max size. technically, it should probably be decremented, but, + // honestly, if you're transferring more than 2GB, you probably shouldn't be using phpseclib, anyway. + // see http://tools.ietf.org/html/rfc4254#section-5.2 for more info + $this->window_size_server_to_client[$channel] = $this->window_size; + // 0x8000 is the maximum max packet size, per http://tools.ietf.org/html/rfc4253#section-6.1, although since PuTTy + // uses 0x4000, that's what will be used here, as well. + $packet_size = 0x4000; + $packet = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::packSSH2('CsN3', NET_SSH2_MSG_CHANNEL_OPEN, 'session', $channel, $this->window_size_server_to_client[$channel], $packet_size); + $this->send_binary_packet($packet); + $this->channel_status[$channel] = NET_SSH2_MSG_CHANNEL_OPEN; + return $this->get_channel_packet($channel, $skip_extended); + } + /** + * Creates an interactive shell + * + * Returns bool(true) if the shell was opened. + * Returns bool(false) if the shell was already open. + * + * @see self::isShellOpen() + * @see self::read() + * @see self::write() + * @return bool + * @throws InsufficientSetupException if not authenticated + * @throws \UnexpectedValueException on receipt of unexpected packets + * @throws \RuntimeException on other errors + */ + public function openShell() + { + if (!$this->isAuthenticated()) { + throw new \FluentSmtpLib\phpseclib3\Exception\InsufficientSetupException('Operation disallowed prior to login()'); + } + $this->open_channel(self::CHANNEL_SHELL); + $terminal_modes = \pack('C', NET_SSH2_TTY_OP_END); + $packet = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::packSSH2( + 'CNsbsN4s', + NET_SSH2_MSG_CHANNEL_REQUEST, + $this->server_channels[self::CHANNEL_SHELL], + 'pty-req', + \true, + // want reply + $this->term, + $this->windowColumns, + $this->windowRows, + 0, + 0, + $terminal_modes + ); + $this->send_binary_packet($packet); + $this->channel_status[self::CHANNEL_SHELL] = NET_SSH2_MSG_CHANNEL_REQUEST; + if (!$this->get_channel_packet(self::CHANNEL_SHELL)) { + throw new \RuntimeException('Unable to request pty'); + } + $packet = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::packSSH2('CNsb', NET_SSH2_MSG_CHANNEL_REQUEST, $this->server_channels[self::CHANNEL_SHELL], 'shell', \true); + $this->send_binary_packet($packet); + $response = $this->get_channel_packet(self::CHANNEL_SHELL); + if ($response === \false) { + throw new \RuntimeException('Unable to request shell'); + } + $this->channel_status[self::CHANNEL_SHELL] = NET_SSH2_MSG_CHANNEL_DATA; + $this->channel_id_last_interactive = self::CHANNEL_SHELL; + $this->bitmap |= self::MASK_SHELL; + return \true; + } + /** + * Return the channel to be used with read(), write(), and reset(), if none were specified + * @deprecated for lack of transparency in intended channel target, to be potentially replaced + * with method which guarantees open-ness of all yielded channels and throws + * error for multiple open channels + * @see self::read() + * @see self::write() + * @return int + */ + private function get_interactive_channel() + { + switch (\true) { + case $this->is_channel_status_data(self::CHANNEL_SUBSYSTEM): + return self::CHANNEL_SUBSYSTEM; + case $this->is_channel_status_data(self::CHANNEL_EXEC): + return self::CHANNEL_EXEC; + default: + return self::CHANNEL_SHELL; + } + } + /** + * Indicates the DATA status on the given channel + * + * @param int $channel The channel number to evaluate + * @return bool + */ + private function is_channel_status_data($channel) + { + return isset($this->channel_status[$channel]) && $this->channel_status[$channel] == NET_SSH2_MSG_CHANNEL_DATA; + } + /** + * Return an available open channel + * + * @return int + */ + private function get_open_channel() + { + $channel = self::CHANNEL_EXEC; + do { + if (isset($this->channel_status[$channel]) && $this->channel_status[$channel] == NET_SSH2_MSG_CHANNEL_OPEN) { + return $channel; + } + } while ($channel++ < self::CHANNEL_SUBSYSTEM); + return \false; + } + /** + * Request agent forwarding of remote server + * + * @return bool + */ + public function requestAgentForwarding() + { + $request_channel = $this->get_open_channel(); + if ($request_channel === \false) { + return \false; + } + $packet = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::packSSH2('CNsC', NET_SSH2_MSG_CHANNEL_REQUEST, $this->server_channels[$request_channel], 'auth-agent-req@openssh.com', 1); + $this->channel_status[$request_channel] = NET_SSH2_MSG_CHANNEL_REQUEST; + $this->send_binary_packet($packet); + if (!$this->get_channel_packet($request_channel)) { + return \false; + } + $this->channel_status[$request_channel] = NET_SSH2_MSG_CHANNEL_OPEN; + return \true; + } + /** + * Returns the output of an interactive shell + * + * Returns when there's a match for $expect, which can take the form of a string literal or, + * if $mode == self::READ_REGEX, a regular expression. + * + * If not specifying a channel, an open interactive channel will be selected, or, if there are + * no open channels, an interactive shell will be created. If there are multiple open + * interactive channels, a legacy behavior will apply in which channel selection prioritizes + * an active subsystem, the exec pty, and, lastly, the shell. If using multiple interactive + * channels, callers are discouraged from relying on this legacy behavior and should specify + * the intended channel. + * + * @see self::write() + * @param string $expect + * @param int $mode One of the self::READ_* constants + * @param int|null $channel Channel id returned by self::getInteractiveChannelId() + * @return string|bool|null + * @throws \RuntimeException on connection error + * @throws InsufficientSetupException on unexpected channel status, possibly due to closure + */ + public function read($expect = '', $mode = self::READ_SIMPLE, $channel = null) + { + if (!$this->isAuthenticated()) { + throw new \FluentSmtpLib\phpseclib3\Exception\InsufficientSetupException('Operation disallowed prior to login()'); + } + $this->curTimeout = $this->timeout; + $this->is_timeout = \false; + if ($channel === null) { + $channel = $this->get_interactive_channel(); + } + if (!$this->is_channel_status_data($channel) && empty($this->channel_buffers[$channel])) { + if ($channel != self::CHANNEL_SHELL) { + throw new \FluentSmtpLib\phpseclib3\Exception\InsufficientSetupException('Data is not available on channel'); + } elseif (!$this->openShell()) { + throw new \RuntimeException('Unable to initiate an interactive shell session'); + } + } + if ($mode == self::READ_NEXT) { + return $this->get_channel_packet($channel); + } + $match = $expect; + while (\true) { + if ($mode == self::READ_REGEX) { + \preg_match($expect, \substr($this->interactiveBuffer, -1024), $matches); + $match = isset($matches[0]) ? $matches[0] : ''; + } + $pos = \strlen($match) ? \strpos($this->interactiveBuffer, $match) : \false; + if ($pos !== \false) { + return \FluentSmtpLib\phpseclib3\Common\Functions\Strings::shift($this->interactiveBuffer, $pos + \strlen($match)); + } + $response = $this->get_channel_packet($channel); + if ($response === \true) { + return \FluentSmtpLib\phpseclib3\Common\Functions\Strings::shift($this->interactiveBuffer, \strlen($this->interactiveBuffer)); + } + $this->interactiveBuffer .= $response; + } + } + /** + * Inputs a command into an interactive shell. + * + * If not specifying a channel, an open interactive channel will be selected, or, if there are + * no open channels, an interactive shell will be created. If there are multiple open + * interactive channels, a legacy behavior will apply in which channel selection prioritizes + * an active subsystem, the exec pty, and, lastly, the shell. If using multiple interactive + * channels, callers are discouraged from relying on this legacy behavior and should specify + * the intended channel. + * + * @see SSH2::read() + * @param string $cmd + * @param int|null $channel Channel id returned by self::getInteractiveChannelId() + * @return void + * @throws \RuntimeException on connection error + * @throws InsufficientSetupException on unexpected channel status, possibly due to closure + * @throws TimeoutException if the write could not be completed within the requested self::setTimeout() + */ + public function write($cmd, $channel = null) + { + if (!$this->isAuthenticated()) { + throw new \FluentSmtpLib\phpseclib3\Exception\InsufficientSetupException('Operation disallowed prior to login()'); + } + if ($channel === null) { + $channel = $this->get_interactive_channel(); + } + if (!$this->is_channel_status_data($channel)) { + if ($channel != self::CHANNEL_SHELL) { + throw new \FluentSmtpLib\phpseclib3\Exception\InsufficientSetupException('Data is not available on channel'); + } elseif (!$this->openShell()) { + throw new \RuntimeException('Unable to initiate an interactive shell session'); + } + } + $this->curTimeout = $this->timeout; + $this->is_timeout = \false; + $this->send_channel_packet($channel, $cmd); + } + /** + * Start a subsystem. + * + * Right now only one subsystem at a time is supported. To support multiple subsystem's stopSubsystem() could accept + * a string that contained the name of the subsystem, but at that point, only one subsystem of each type could be opened. + * To support multiple subsystem's of the same name maybe it'd be best if startSubsystem() generated a new channel id and + * returns that and then that that was passed into stopSubsystem() but that'll be saved for a future date and implemented + * if there's sufficient demand for such a feature. + * + * @see self::stopSubsystem() + * @param string $subsystem + * @return bool + */ + public function startSubsystem($subsystem) + { + $this->open_channel(self::CHANNEL_SUBSYSTEM); + $packet = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::packSSH2('CNsCs', NET_SSH2_MSG_CHANNEL_REQUEST, $this->server_channels[self::CHANNEL_SUBSYSTEM], 'subsystem', 1, $subsystem); + $this->send_binary_packet($packet); + $this->channel_status[self::CHANNEL_SUBSYSTEM] = NET_SSH2_MSG_CHANNEL_REQUEST; + if (!$this->get_channel_packet(self::CHANNEL_SUBSYSTEM)) { + return \false; + } + $this->channel_status[self::CHANNEL_SUBSYSTEM] = NET_SSH2_MSG_CHANNEL_DATA; + $this->channel_id_last_interactive = self::CHANNEL_SUBSYSTEM; + return \true; + } + /** + * Stops a subsystem. + * + * @see self::startSubsystem() + * @return bool + */ + public function stopSubsystem() + { + if ($this->isInteractiveChannelOpen(self::CHANNEL_SUBSYSTEM)) { + $this->close_channel(self::CHANNEL_SUBSYSTEM); + } + return \true; + } + /** + * Closes a channel + * + * If read() timed out you might want to just close the channel and have it auto-restart on the next read() call + * + * If not specifying a channel, an open interactive channel will be selected. If there are + * multiple open interactive channels, a legacy behavior will apply in which channel selection + * prioritizes an active subsystem, the exec pty, and, lastly, the shell. If using multiple + * interactive channels, callers are discouraged from relying on this legacy behavior and + * should specify the intended channel. + * + * @param int|null $channel Channel id returned by self::getInteractiveChannelId() + * @return void + */ + public function reset($channel = null) + { + if ($channel === null) { + $channel = $this->get_interactive_channel(); + } + if ($this->isInteractiveChannelOpen($channel)) { + $this->close_channel($channel); + } + } + /** + * Is timeout? + * + * Did exec() or read() return because they timed out or because they encountered the end? + * + */ + public function isTimeout() + { + return $this->is_timeout; + } + /** + * Disconnect + * + */ + public function disconnect() + { + $this->disconnect_helper(NET_SSH2_DISCONNECT_BY_APPLICATION); + if (isset($this->realtime_log_file) && \is_resource($this->realtime_log_file)) { + \fclose($this->realtime_log_file); + } + unset(self::$connections[$this->getResourceId()]); + } + /** + * Destructor. + * + * Will be called, automatically, if you're supporting just PHP5. If you're supporting PHP4, you'll need to call + * disconnect(). + * + */ + public function __destruct() + { + $this->disconnect(); + } + /** + * Is the connection still active? + * + * $level has 3x possible values: + * 0 (default): phpseclib takes a passive approach to see if the connection is still active by calling feof() + * on the socket + * 1: phpseclib takes an active approach to see if the connection is still active by sending an SSH_MSG_IGNORE + * packet that doesn't require a response + * 2: phpseclib takes an active approach to see if the connection is still active by sending an SSH_MSG_CHANNEL_OPEN + * packet and imediately trying to close that channel. some routers, in particular, however, will only let you + * open one channel, so this approach could yield false positives + * + * @param int $level + * @return bool + */ + public function isConnected($level = 0) + { + if (!\is_int($level) || $level < 0 || $level > 2) { + throw new \InvalidArgumentException('$level must be 0, 1 or 2'); + } + if ($level == 0) { + return $this->bitmap & self::MASK_CONNECTED && \is_resource($this->fsock) && !\feof($this->fsock); + } + try { + if ($level == 1) { + $this->send_binary_packet(\pack('CN', NET_SSH2_MSG_IGNORE, 0)); + } else { + $this->open_channel(self::CHANNEL_KEEP_ALIVE); + $this->close_channel(self::CHANNEL_KEEP_ALIVE); + } + return \true; + } catch (\Exception $e) { + return \false; + } + } + /** + * Have you successfully been logged in? + * + * @return bool + */ + public function isAuthenticated() + { + return (bool) ($this->bitmap & self::MASK_LOGIN); + } + /** + * Is the interactive shell active? + * + * @return bool + */ + public function isShellOpen() + { + return $this->isInteractiveChannelOpen(self::CHANNEL_SHELL); + } + /** + * Is the exec pty active? + * + * @return bool + */ + public function isPTYOpen() + { + return $this->isInteractiveChannelOpen(self::CHANNEL_EXEC); + } + /** + * Is the given interactive channel active? + * + * @param int $channel Channel id returned by self::getInteractiveChannelId() + * @return bool + */ + public function isInteractiveChannelOpen($channel) + { + return $this->isAuthenticated() && $this->is_channel_status_data($channel); + } + /** + * Returns a channel identifier, presently of the last interactive channel opened, regardless of current status. + * Returns 0 if no interactive channel has been opened. + * + * @see self::isInteractiveChannelOpen() + * @return int + */ + public function getInteractiveChannelId() + { + return $this->channel_id_last_interactive; + } + /** + * Pings a server connection, or tries to reconnect if the connection has gone down + * + * Inspired by http://php.net/manual/en/mysqli.ping.php + * + * @return bool + */ + public function ping() + { + if (!$this->isAuthenticated()) { + if (!empty($this->auth)) { + return $this->reconnect(); + } + return \false; + } + try { + $this->open_channel(self::CHANNEL_KEEP_ALIVE); + } catch (\RuntimeException $e) { + return $this->reconnect(); + } + $this->close_channel(self::CHANNEL_KEEP_ALIVE); + return \true; + } + /** + * In situ reconnect method + * + * @return boolean + */ + private function reconnect() + { + $this->disconnect_helper(NET_SSH2_DISCONNECT_BY_APPLICATION); + $this->connect(); + foreach ($this->auth as $auth) { + $result = $this->login(...$auth); + } + return $result; + } + /** + * Resets a connection for re-use + */ + protected function reset_connection() + { + if (\is_resource($this->fsock) && \get_resource_type($this->fsock) === 'stream') { + \fclose($this->fsock); + } + $this->fsock = null; + $this->bitmap = 0; + $this->binary_packet_buffer = null; + $this->decrypt = $this->encrypt = \false; + $this->decrypt_block_size = $this->encrypt_block_size = 8; + $this->hmac_check = $this->hmac_create = \false; + $this->hmac_size = \false; + $this->session_id = \false; + $this->last_packet = null; + $this->get_seq_no = $this->send_seq_no = 0; + $this->channel_status = []; + $this->channel_id_last_interactive = 0; + $this->channel_buffers = []; + $this->channel_buffers_write = []; + } + /** + * @return int[] second and microsecond stream timeout options based on user-requested timeout and keep-alive, or the default socket timeout by default, which mirrors PHP socket streams. + */ + private function get_stream_timeout() + { + $sec = \ini_get('default_socket_timeout'); + $usec = 0; + if ($this->curTimeout > 0) { + $sec = (int) \floor($this->curTimeout); + $usec = (int) (1000000 * ($this->curTimeout - $sec)); + } + if ($this->keepAlive > 0) { + $elapsed = \microtime(\true) - $this->last_packet; + $timeout = \max($this->keepAlive - $elapsed, 0); + if (!$this->curTimeout || $timeout < $this->curTimeout) { + $sec = (int) \floor($timeout); + $usec = (int) (1000000 * ($timeout - $sec)); + } + } + return [$sec, $usec]; + } + /** + * Retrieves the next packet with added timeout and type handling + * + * @param string $message_types Message types to enforce in response, closing if not met + * @return string + * @throws ConnectionClosedException If an error has occurred preventing read of the next packet + */ + private function get_binary_packet_or_close(...$message_types) + { + try { + $packet = $this->get_binary_packet(); + if (\count($message_types) > 0 && !\in_array(\ord($packet[0]), $message_types)) { + $this->disconnect_helper(NET_SSH2_DISCONNECT_PROTOCOL_ERROR); + throw new \FluentSmtpLib\phpseclib3\Exception\ConnectionClosedException('Bad message type. Expected: #' . \implode(', #', $message_types) . '. Got: #' . \ord($packet[0])); + } + return $packet; + } catch (\FluentSmtpLib\phpseclib3\Exception\TimeoutException $e) { + $this->disconnect_helper(NET_SSH2_DISCONNECT_BY_APPLICATION); + throw new \FluentSmtpLib\phpseclib3\Exception\ConnectionClosedException('Connection closed due to timeout'); + } + } + /** + * Gets Binary Packets + * + * See '6. Binary Packet Protocol' of rfc4253 for more info. + * + * @see self::_send_binary_packet() + * @return string + * @throws TimeoutException If user requested timeout was reached while waiting for next packet + * @throws ConnectionClosedException If an error has occurred preventing read of the next packet + */ + private function get_binary_packet() + { + if (!\is_resource($this->fsock)) { + throw new \InvalidArgumentException('fsock is not a resource.'); + } + if (!$this->keyExchangeInProgress && \count($this->kex_buffer)) { + return $this->filter(\array_shift($this->kex_buffer)); + } + if ($this->binary_packet_buffer == null) { + // buffer the packet to permit continued reads across timeouts + $this->binary_packet_buffer = (object) [ + 'read_time' => 0, + // the time to read the packet from the socket + 'raw' => '', + // the raw payload read from the socket + 'plain' => '', + // the packet in plain text, excluding packet_length header + 'packet_length' => null, + // the packet_length value pulled from the payload + 'size' => $this->decrypt_block_size, + ]; + } + $packet = $this->binary_packet_buffer; + while (\strlen($packet->raw) < $packet->size) { + if (\feof($this->fsock)) { + $this->disconnect_helper(NET_SSH2_DISCONNECT_CONNECTION_LOST); + throw new \FluentSmtpLib\phpseclib3\Exception\ConnectionClosedException('Connection closed by server'); + } + if ($this->curTimeout < 0) { + $this->is_timeout = \true; + throw new \FluentSmtpLib\phpseclib3\Exception\TimeoutException('Timed out waiting for server'); + } + $this->send_keep_alive(); + list($sec, $usec) = $this->get_stream_timeout(); + \stream_set_timeout($this->fsock, $sec, $usec); + $start = \microtime(\true); + $raw = \stream_get_contents($this->fsock, $packet->size - \strlen($packet->raw)); + $elapsed = \microtime(\true) - $start; + $packet->read_time += $elapsed; + if ($this->curTimeout > 0) { + $this->curTimeout -= $elapsed; + } + if ($raw === \false) { + $this->disconnect_helper(NET_SSH2_DISCONNECT_CONNECTION_LOST); + throw new \FluentSmtpLib\phpseclib3\Exception\ConnectionClosedException('Connection closed by server'); + } elseif (!\strlen($raw)) { + continue; + } + $packet->raw .= $raw; + if (!$packet->packet_length) { + $this->get_binary_packet_size($packet); + } + } + if (\strlen($packet->raw) != $packet->size) { + throw new \RuntimeException('Size of packet was not expected length'); + } + // destroy buffer as packet represents the entire payload and should be processed in full + $this->binary_packet_buffer = null; + // copy the raw payload, so as not to destroy original + $raw = $packet->raw; + if ($this->hmac_check instanceof \FluentSmtpLib\phpseclib3\Crypt\Hash) { + $hmac = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::pop($raw, $this->hmac_size); + } + $packet_length_header_size = 4; + if ($this->decrypt) { + switch ($this->decryptName) { + case 'aes128-gcm@openssh.com': + case 'aes256-gcm@openssh.com': + $this->decrypt->setNonce($this->decryptFixedPart . $this->decryptInvocationCounter); + \FluentSmtpLib\phpseclib3\Common\Functions\Strings::increment_str($this->decryptInvocationCounter); + $this->decrypt->setAAD(\FluentSmtpLib\phpseclib3\Common\Functions\Strings::shift($raw, $packet_length_header_size)); + $this->decrypt->setTag(\FluentSmtpLib\phpseclib3\Common\Functions\Strings::pop($raw, $this->decrypt_block_size)); + $packet->plain = $this->decrypt->decrypt($raw); + break; + case 'chacha20-poly1305@openssh.com': + // This should be impossible, but we are checking anyway to narrow the type for Psalm. + if (!$this->decrypt instanceof \FluentSmtpLib\phpseclib3\Crypt\ChaCha20) { + throw new \LogicException('$this->decrypt is not a ' . \FluentSmtpLib\phpseclib3\Crypt\ChaCha20::class); + } + $this->decrypt->setNonce(\pack('N2', 0, $this->get_seq_no)); + $this->decrypt->setCounter(0); + // this is the same approach that's implemented in Salsa20::createPoly1305Key() + // but we don't want to use the same AEAD construction that RFC8439 describes + // for ChaCha20-Poly1305 so we won't rely on it (see Salsa20::poly1305()) + $this->decrypt->setPoly1305Key($this->decrypt->encrypt(\str_repeat("\x00", 32))); + $this->decrypt->setAAD(\FluentSmtpLib\phpseclib3\Common\Functions\Strings::shift($raw, $packet_length_header_size)); + $this->decrypt->setCounter(1); + $this->decrypt->setTag(\FluentSmtpLib\phpseclib3\Common\Functions\Strings::pop($raw, 16)); + $packet->plain = $this->decrypt->decrypt($raw); + break; + default: + if (!$this->hmac_check instanceof \FluentSmtpLib\phpseclib3\Crypt\Hash || !$this->hmac_check_etm) { + // first block was already decrypted for contained packet_length header + \FluentSmtpLib\phpseclib3\Common\Functions\Strings::shift($raw, $this->decrypt_block_size); + if (\strlen($raw) > 0) { + $packet->plain .= $this->decrypt->decrypt($raw); + } + } else { + \FluentSmtpLib\phpseclib3\Common\Functions\Strings::shift($raw, $packet_length_header_size); + $packet->plain = $this->decrypt->decrypt($raw); + } + break; + } + } else { + \FluentSmtpLib\phpseclib3\Common\Functions\Strings::shift($raw, $packet_length_header_size); + $packet->plain = $raw; + } + if ($this->hmac_check instanceof \FluentSmtpLib\phpseclib3\Crypt\Hash) { + $reconstructed = !$this->hmac_check_etm ? \pack('Na*', $packet->packet_length, $packet->plain) : \substr($packet->raw, 0, -$this->hmac_size); + if (($this->hmac_check->getHash() & "\xff\xff\xff\xff") == 'umac') { + $this->hmac_check->setNonce("\x00\x00\x00\x00" . \pack('N', $this->get_seq_no)); + if ($hmac != $this->hmac_check->hash($reconstructed)) { + $this->disconnect_helper(NET_SSH2_DISCONNECT_MAC_ERROR); + throw new \FluentSmtpLib\phpseclib3\Exception\ConnectionClosedException('Invalid UMAC'); + } + } else { + if ($hmac != $this->hmac_check->hash(\pack('Na*', $this->get_seq_no, $reconstructed))) { + $this->disconnect_helper(NET_SSH2_DISCONNECT_MAC_ERROR); + throw new \FluentSmtpLib\phpseclib3\Exception\ConnectionClosedException('Invalid HMAC'); + } + } + } + $padding_length = 0; + $payload = $packet->plain; + \extract(\unpack('Cpadding_length', \FluentSmtpLib\phpseclib3\Common\Functions\Strings::shift($payload, 1))); + if ($padding_length > 0) { + \FluentSmtpLib\phpseclib3\Common\Functions\Strings::pop($payload, $padding_length); + } + if (!$this->keyExchangeInProgress) { + $this->bytesTransferredSinceLastKEX += $packet->packet_length + $padding_length + 5; + } + if (empty($payload)) { + $this->disconnect_helper(NET_SSH2_DISCONNECT_PROTOCOL_ERROR); + throw new \FluentSmtpLib\phpseclib3\Exception\ConnectionClosedException('Plaintext is too short'); + } + switch ($this->decompress) { + case self::NET_SSH2_COMPRESSION_ZLIB_AT_OPENSSH: + if (!$this->isAuthenticated()) { + break; + } + // fall-through + case self::NET_SSH2_COMPRESSION_ZLIB: + if ($this->regenerate_decompression_context) { + $this->regenerate_decompression_context = \false; + $cmf = \ord($payload[0]); + $cm = $cmf & 0xf; + if ($cm != 8) { + // deflate + throw new \FluentSmtpLib\phpseclib3\Exception\UnsupportedAlgorithmException("Only CM = 8 ('deflate') is supported ({$cm})"); + } + $cinfo = ($cmf & 0xf0) >> 4; + if ($cinfo > 7) { + throw new \RuntimeException("CINFO above 7 is not allowed ({$cinfo})"); + } + $windowSize = 1 << $cinfo + 8; + $flg = \ord($payload[1]); + //$fcheck = $flg && 0x0F; + if (($cmf << 8 | $flg) % 31) { + throw new \RuntimeException('fcheck failed'); + } + $fdict = \boolval($flg & 0x20); + $flevel = ($flg & 0xc0) >> 6; + $this->decompress_context = \inflate_init(\ZLIB_ENCODING_RAW, ['window' => $cinfo + 8]); + $payload = \substr($payload, 2); + } + if ($this->decompress_context) { + $payload = \inflate_add($this->decompress_context, $payload, \ZLIB_PARTIAL_FLUSH); + } + } + $this->get_seq_no++; + if (\defined('FluentSmtpLib\\NET_SSH2_LOGGING')) { + $current = \microtime(\true); + $message_number = isset(self::$message_numbers[\ord($payload[0])]) ? self::$message_numbers[\ord($payload[0])] : 'UNKNOWN (' . \ord($payload[0]) . ')'; + $message_number = '<- ' . $message_number . ' (since last: ' . \round($current - $this->last_packet, 4) . ', network: ' . \round($packet->read_time, 4) . 's)'; + $this->append_log($message_number, $payload); + } + $this->last_packet = \microtime(\true); + if ($this->bytesTransferredSinceLastKEX > $this->doKeyReexchangeAfterXBytes) { + $this->key_exchange(); + } + // don't filter if we're in the middle of a key exchange (since _filter might send out packets) + return $this->keyExchangeInProgress ? $payload : $this->filter($payload); + } + /** + * @param object $packet The packet object being constructed, passed by reference + * The size, packet_length, and plain properties of this object may be modified in processing + * @throws InvalidPacketLengthException if the packet length header is invalid + */ + private function get_binary_packet_size(&$packet) + { + $packet_length_header_size = 4; + if (\strlen($packet->raw) < $packet_length_header_size) { + return; + } + $packet_length = 0; + $added_validation_length = 0; + // indicates when the packet length header is included when validating packet length against block size + if ($this->decrypt) { + switch ($this->decryptName) { + case 'aes128-gcm@openssh.com': + case 'aes256-gcm@openssh.com': + \extract(\unpack('Npacket_length', \substr($packet->raw, 0, $packet_length_header_size))); + $packet->size = $packet_length_header_size + $packet_length + $this->decrypt_block_size; + // expect tag + break; + case 'chacha20-poly1305@openssh.com': + $this->lengthDecrypt->setNonce(\pack('N2', 0, $this->get_seq_no)); + $packet_length_header = $this->lengthDecrypt->decrypt(\substr($packet->raw, 0, $packet_length_header_size)); + \extract(\unpack('Npacket_length', $packet_length_header)); + $packet->size = $packet_length_header_size + $packet_length + 16; + // expect tag + break; + default: + if (!$this->hmac_check instanceof \FluentSmtpLib\phpseclib3\Crypt\Hash || !$this->hmac_check_etm) { + if (\strlen($packet->raw) < $this->decrypt_block_size) { + return; + } + $packet->plain = $this->decrypt->decrypt(\substr($packet->raw, 0, $this->decrypt_block_size)); + \extract(\unpack('Npacket_length', \FluentSmtpLib\phpseclib3\Common\Functions\Strings::shift($packet->plain, $packet_length_header_size))); + $packet->size = $packet_length_header_size + $packet_length; + $added_validation_length = $packet_length_header_size; + } else { + \extract(\unpack('Npacket_length', \substr($packet->raw, 0, $packet_length_header_size))); + $packet->size = $packet_length_header_size + $packet_length; + } + break; + } + } else { + \extract(\unpack('Npacket_length', \substr($packet->raw, 0, $packet_length_header_size))); + $packet->size = $packet_length_header_size + $packet_length; + $added_validation_length = $packet_length_header_size; + } + // quoting , + // "implementations SHOULD check that the packet length is reasonable" + // PuTTY uses 0x9000 as the actual max packet size and so to shall we + if ($packet_length <= 0 || $packet_length > 0x9000 || ($packet_length + $added_validation_length) % $this->decrypt_block_size != 0) { + $this->disconnect_helper(NET_SSH2_DISCONNECT_PROTOCOL_ERROR); + throw new \FluentSmtpLib\phpseclib3\Exception\InvalidPacketLengthException('Invalid packet length'); + } + if ($this->hmac_check instanceof \FluentSmtpLib\phpseclib3\Crypt\Hash) { + $packet->size += $this->hmac_size; + } + $packet->packet_length = $packet_length; + } + /** + * Handle Disconnect + * + * Because some binary packets need to be ignored... + * + * @see self::filter() + * @see self::key_exchange() + * @return boolean + * @access private + */ + private function handleDisconnect($payload) + { + \FluentSmtpLib\phpseclib3\Common\Functions\Strings::shift($payload, 1); + list($reason_code, $message) = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::unpackSSH2('Ns', $payload); + $this->errors[] = 'SSH_MSG_DISCONNECT: ' . self::$disconnect_reasons[$reason_code] . "\r\n{$message}"; + $this->disconnect_helper(NET_SSH2_DISCONNECT_CONNECTION_LOST); + throw new \FluentSmtpLib\phpseclib3\Exception\ConnectionClosedException('Connection closed by server'); + } + /** + * Filter Binary Packets + * + * Because some binary packets need to be ignored... + * + * @see self::_get_binary_packet() + * @param string $payload + * @return string + */ + private function filter($payload) + { + switch (\ord($payload[0])) { + case NET_SSH2_MSG_DISCONNECT: + return $this->handleDisconnect($payload); + case NET_SSH2_MSG_IGNORE: + $payload = $this->get_binary_packet(); + break; + case NET_SSH2_MSG_DEBUG: + \FluentSmtpLib\phpseclib3\Common\Functions\Strings::shift($payload, 2); + // second byte is "always_display" + list($message) = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::unpackSSH2('s', $payload); + $this->errors[] = "SSH_MSG_DEBUG: {$message}"; + $payload = $this->get_binary_packet(); + break; + case NET_SSH2_MSG_UNIMPLEMENTED: + break; + // return payload + case NET_SSH2_MSG_KEXINIT: + // this is here for server initiated key re-exchanges after the initial key exchange + if ($this->session_id !== \false) { + if (!$this->key_exchange($payload)) { + $this->disconnect_helper(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); + throw new \FluentSmtpLib\phpseclib3\Exception\ConnectionClosedException('Key exchange failed'); + } + $payload = $this->get_binary_packet(); + } + break; + case NET_SSH2_MSG_EXT_INFO: + \FluentSmtpLib\phpseclib3\Common\Functions\Strings::shift($payload, 1); + list($nr_extensions) = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::unpackSSH2('N', $payload); + for ($i = 0; $i < $nr_extensions; $i++) { + list($extension_name, $extension_value) = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::unpackSSH2('ss', $payload); + if ($extension_name == 'server-sig-algs') { + $this->supported_private_key_algorithms = \explode(',', $extension_value); + } + } + $payload = $this->get_binary_packet(); + } + // see http://tools.ietf.org/html/rfc4252#section-5.4; only called when the encryption has been activated and when we haven't already logged in + if ($this->bitmap & self::MASK_CONNECTED && !$this->isAuthenticated() && \ord($payload[0]) == NET_SSH2_MSG_USERAUTH_BANNER) { + \FluentSmtpLib\phpseclib3\Common\Functions\Strings::shift($payload, 1); + list($this->banner_message) = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::unpackSSH2('s', $payload); + $payload = $this->get_binary_packet(); + } + // only called when we've already logged in + if ($this->bitmap & self::MASK_CONNECTED && $this->isAuthenticated()) { + switch (\ord($payload[0])) { + case NET_SSH2_MSG_CHANNEL_REQUEST: + if (\strlen($payload) == 31) { + \extract(\unpack('cpacket_type/Nchannel/Nlength', $payload)); + if (\substr($payload, 9, $length) == 'keepalive@openssh.com' && isset($this->server_channels[$channel])) { + if (\ord(\substr($payload, 9 + $length))) { + // want reply + $this->send_binary_packet(\pack('CN', NET_SSH2_MSG_CHANNEL_SUCCESS, $this->server_channels[$channel])); + } + $payload = $this->get_binary_packet(); + } + } + break; + case NET_SSH2_MSG_GLOBAL_REQUEST: + // see http://tools.ietf.org/html/rfc4254#section-4 + \FluentSmtpLib\phpseclib3\Common\Functions\Strings::shift($payload, 1); + list($request_name) = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::unpackSSH2('s', $payload); + $this->errors[] = "SSH_MSG_GLOBAL_REQUEST: {$request_name}"; + $this->send_binary_packet(\pack('C', NET_SSH2_MSG_REQUEST_FAILURE)); + $payload = $this->get_binary_packet(); + break; + case NET_SSH2_MSG_CHANNEL_OPEN: + // see http://tools.ietf.org/html/rfc4254#section-5.1 + \FluentSmtpLib\phpseclib3\Common\Functions\Strings::shift($payload, 1); + list($data, $server_channel) = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::unpackSSH2('sN', $payload); + switch ($data) { + case 'auth-agent': + case 'auth-agent@openssh.com': + if (isset($this->agent)) { + $new_channel = self::CHANNEL_AGENT_FORWARD; + list($remote_window_size, $remote_maximum_packet_size) = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::unpackSSH2('NN', $payload); + $this->packet_size_client_to_server[$new_channel] = $remote_window_size; + $this->window_size_server_to_client[$new_channel] = $remote_maximum_packet_size; + $this->window_size_client_to_server[$new_channel] = $this->window_size; + $packet_size = 0x4000; + $packet = \pack('CN4', NET_SSH2_MSG_CHANNEL_OPEN_CONFIRMATION, $server_channel, $new_channel, $packet_size, $packet_size); + $this->server_channels[$new_channel] = $server_channel; + $this->channel_status[$new_channel] = NET_SSH2_MSG_CHANNEL_OPEN_CONFIRMATION; + $this->send_binary_packet($packet); + } + break; + default: + $packet = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::packSSH2( + 'CN2ss', + NET_SSH2_MSG_CHANNEL_OPEN_FAILURE, + $server_channel, + NET_SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED, + '', + // description + '' + ); + $this->send_binary_packet($packet); + } + $payload = $this->get_binary_packet(); + break; + } + } + return $payload; + } + /** + * Enable Quiet Mode + * + * Suppress stderr from output + * + */ + public function enableQuietMode() + { + $this->quiet_mode = \true; + } + /** + * Disable Quiet Mode + * + * Show stderr in output + * + */ + public function disableQuietMode() + { + $this->quiet_mode = \false; + } + /** + * Returns whether Quiet Mode is enabled or not + * + * @see self::enableQuietMode() + * @see self::disableQuietMode() + * @return bool + */ + public function isQuietModeEnabled() + { + return $this->quiet_mode; + } + /** + * Enable request-pty when using exec() + * + */ + public function enablePTY() + { + $this->request_pty = \true; + } + /** + * Disable request-pty when using exec() + * + */ + public function disablePTY() + { + if ($this->isPTYOpen()) { + $this->close_channel(self::CHANNEL_EXEC); + } + $this->request_pty = \false; + } + /** + * Returns whether request-pty is enabled or not + * + * @see self::enablePTY() + * @see self::disablePTY() + * @return bool + */ + public function isPTYEnabled() + { + return $this->request_pty; + } + /** + * Gets channel data + * + * Returns the data as a string. bool(true) is returned if: + * + * - the server closes the channel + * - if the connection times out + * - if a window adjust packet is received on the given negated client channel + * - if the channel status is CHANNEL_OPEN and the response was CHANNEL_OPEN_CONFIRMATION + * - if the channel status is CHANNEL_REQUEST and the response was CHANNEL_SUCCESS + * - if the channel status is CHANNEL_CLOSE and the response was CHANNEL_CLOSE + * + * bool(false) is returned if: + * + * - if the channel status is CHANNEL_REQUEST and the response was CHANNEL_FAILURE + * + * @param int $client_channel Specifies the channel to return data for, and data received + * on other channels is buffered. The respective negative value of a channel is + * also supported for the case that the caller is awaiting adjustment of the data + * window, and where data received on that respective channel is also buffered. + * @param bool $skip_extended + * @return mixed + * @throws \RuntimeException on connection error + */ + protected function get_channel_packet($client_channel, $skip_extended = \false) + { + if (!empty($this->channel_buffers[$client_channel])) { + switch ($this->channel_status[$client_channel]) { + case NET_SSH2_MSG_CHANNEL_REQUEST: + foreach ($this->channel_buffers[$client_channel] as $i => $packet) { + switch (\ord($packet[0])) { + case NET_SSH2_MSG_CHANNEL_SUCCESS: + case NET_SSH2_MSG_CHANNEL_FAILURE: + unset($this->channel_buffers[$client_channel][$i]); + return \substr($packet, 1); + } + } + break; + default: + return \substr(\array_shift($this->channel_buffers[$client_channel]), 1); + } + } + while (\true) { + try { + $response = $this->get_binary_packet(); + } catch (\FluentSmtpLib\phpseclib3\Exception\TimeoutException $e) { + return \true; + } + list($type) = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::unpackSSH2('C', $response); + if (\strlen($response) >= 4) { + list($channel) = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::unpackSSH2('N', $response); + } + // will not be setup yet on incoming channel open request + if (isset($channel) && isset($this->channel_status[$channel]) && isset($this->window_size_server_to_client[$channel])) { + $this->window_size_server_to_client[$channel] -= \strlen($response); + // resize the window, if appropriate + if ($this->window_size_server_to_client[$channel] < 0) { + // PuTTY does something more analogous to the following: + //if ($this->window_size_server_to_client[$channel] < 0x3FFFFFFF) { + $packet = \pack('CNN', NET_SSH2_MSG_CHANNEL_WINDOW_ADJUST, $this->server_channels[$channel], $this->window_resize); + $this->send_binary_packet($packet); + $this->window_size_server_to_client[$channel] += $this->window_resize; + } + switch ($type) { + case NET_SSH2_MSG_CHANNEL_WINDOW_ADJUST: + list($window_size) = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::unpackSSH2('N', $response); + $this->window_size_client_to_server[$channel] += $window_size; + if ($channel == -$client_channel) { + return \true; + } + continue 2; + case NET_SSH2_MSG_CHANNEL_EXTENDED_DATA: + /* + if ($client_channel == self::CHANNEL_EXEC) { + $this->send_channel_packet($client_channel, chr(0)); + } + */ + // currently, there's only one possible value for $data_type_code: NET_SSH2_EXTENDED_DATA_STDERR + list($data_type_code, $data) = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::unpackSSH2('Ns', $response); + $this->stdErrorLog .= $data; + if ($skip_extended || $this->quiet_mode) { + continue 2; + } + if ($client_channel == $channel && $this->channel_status[$channel] == NET_SSH2_MSG_CHANNEL_DATA) { + return $data; + } + $this->channel_buffers[$channel][] = \chr($type) . $data; + continue 2; + case NET_SSH2_MSG_CHANNEL_REQUEST: + if ($this->channel_status[$channel] == NET_SSH2_MSG_CHANNEL_CLOSE) { + continue 2; + } + list($value) = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::unpackSSH2('s', $response); + switch ($value) { + case 'exit-signal': + list(, $signal_name, , $error_message) = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::unpackSSH2('bsbs', $response); + $this->errors[] = "SSH_MSG_CHANNEL_REQUEST (exit-signal): {$signal_name}"; + if (\strlen($error_message)) { + $this->errors[\count($this->errors) - 1] .= "\r\n{$error_message}"; + } + $this->send_binary_packet(\pack('CN', NET_SSH2_MSG_CHANNEL_EOF, $this->server_channels[$client_channel])); + $this->send_binary_packet(\pack('CN', NET_SSH2_MSG_CHANNEL_CLOSE, $this->server_channels[$channel])); + $this->channel_status[$channel] = NET_SSH2_MSG_CHANNEL_EOF; + continue 3; + case 'exit-status': + list(, $this->exit_status) = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::unpackSSH2('CN', $response); + // "The client MAY ignore these messages." + // -- http://tools.ietf.org/html/rfc4254#section-6.10 + continue 3; + default: + // "Some systems may not implement signals, in which case they SHOULD ignore this message." + // -- http://tools.ietf.org/html/rfc4254#section-6.9 + continue 3; + } + } + switch ($this->channel_status[$channel]) { + case NET_SSH2_MSG_CHANNEL_OPEN: + switch ($type) { + case NET_SSH2_MSG_CHANNEL_OPEN_CONFIRMATION: + list($this->server_channels[$channel], $window_size, $this->packet_size_client_to_server[$channel]) = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::unpackSSH2('NNN', $response); + if ($window_size < 0) { + $window_size &= 0x7fffffff; + $window_size += 0x80000000; + } + $this->window_size_client_to_server[$channel] = $window_size; + $result = $client_channel == $channel ? \true : $this->get_channel_packet($client_channel, $skip_extended); + $this->on_channel_open(); + return $result; + case NET_SSH2_MSG_CHANNEL_OPEN_FAILURE: + $this->disconnect_helper(NET_SSH2_DISCONNECT_BY_APPLICATION); + throw new \RuntimeException('Unable to open channel'); + default: + if ($client_channel == $channel) { + $this->disconnect_helper(NET_SSH2_DISCONNECT_BY_APPLICATION); + throw new \RuntimeException('Unexpected response to open request'); + } + return $this->get_channel_packet($client_channel, $skip_extended); + } + break; + case NET_SSH2_MSG_CHANNEL_REQUEST: + switch ($type) { + case NET_SSH2_MSG_CHANNEL_SUCCESS: + return \true; + case NET_SSH2_MSG_CHANNEL_FAILURE: + return \false; + case NET_SSH2_MSG_CHANNEL_DATA: + list($data) = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::unpackSSH2('s', $response); + $this->channel_buffers[$channel][] = \chr($type) . $data; + return $this->get_channel_packet($client_channel, $skip_extended); + default: + $this->disconnect_helper(NET_SSH2_DISCONNECT_BY_APPLICATION); + throw new \RuntimeException('Unable to fulfill channel request'); + } + case NET_SSH2_MSG_CHANNEL_CLOSE: + if ($client_channel == $channel && $type == NET_SSH2_MSG_CHANNEL_CLOSE) { + return \true; + } + return $this->get_channel_packet($client_channel, $skip_extended); + } + } + // ie. $this->channel_status[$channel] == NET_SSH2_MSG_CHANNEL_DATA + switch ($type) { + case NET_SSH2_MSG_CHANNEL_DATA: + /* + if ($channel == self::CHANNEL_EXEC) { + // SCP requires null packets, such as this, be sent. further, in the case of the ssh.com SSH server + // this actually seems to make things twice as fast. more to the point, the message right after + // SSH_MSG_CHANNEL_DATA (usually SSH_MSG_IGNORE) won't block for as long as it would have otherwise. + // in OpenSSH it slows things down but only by a couple thousandths of a second. + $this->send_channel_packet($channel, chr(0)); + } + */ + list($data) = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::unpackSSH2('s', $response); + if ($channel == self::CHANNEL_AGENT_FORWARD) { + $agent_response = $this->agent->forwardData($data); + if (!\is_bool($agent_response)) { + $this->send_channel_packet($channel, $agent_response); + } + break; + } + if ($client_channel == $channel) { + return $data; + } + $this->channel_buffers[$channel][] = \chr($type) . $data; + break; + case NET_SSH2_MSG_CHANNEL_CLOSE: + $this->curTimeout = 5; + $this->close_channel_bitmap($channel); + if ($this->channel_status[$channel] != NET_SSH2_MSG_CHANNEL_EOF) { + $this->send_binary_packet(\pack('CN', NET_SSH2_MSG_CHANNEL_CLOSE, $this->server_channels[$channel])); + } + $this->channel_status[$channel] = NET_SSH2_MSG_CHANNEL_CLOSE; + $this->channelCount--; + if ($client_channel == $channel) { + return \true; + } + // fall-through + case NET_SSH2_MSG_CHANNEL_EOF: + break; + default: + $this->disconnect_helper(NET_SSH2_DISCONNECT_BY_APPLICATION); + throw new \RuntimeException("Error reading channel data ({$type})"); + } + } + } + /** + * Sends Binary Packets + * + * See '6. Binary Packet Protocol' of rfc4253 for more info. + * + * @param string $data + * @param string $logged + * @see self::_get_binary_packet() + * @return void + */ + protected function send_binary_packet($data, $logged = null) + { + if (!\is_resource($this->fsock) || \feof($this->fsock)) { + $this->disconnect_helper(NET_SSH2_DISCONNECT_CONNECTION_LOST); + throw new \FluentSmtpLib\phpseclib3\Exception\ConnectionClosedException('Connection closed prematurely'); + } + if (!isset($logged)) { + $logged = $data; + } + switch ($this->compress) { + case self::NET_SSH2_COMPRESSION_ZLIB_AT_OPENSSH: + if (!$this->isAuthenticated()) { + break; + } + // fall-through + case self::NET_SSH2_COMPRESSION_ZLIB: + if (!$this->regenerate_compression_context) { + $header = ''; + } else { + $this->regenerate_compression_context = \false; + $this->compress_context = \deflate_init(\ZLIB_ENCODING_RAW, ['window' => 15]); + $header = "x\x9c"; + } + if ($this->compress_context) { + $data = $header . \deflate_add($this->compress_context, $data, \ZLIB_PARTIAL_FLUSH); + } + } + // 4 (packet length) + 1 (padding length) + 4 (minimal padding amount) == 9 + $packet_length = \strlen($data) + 9; + if ($this->encrypt && $this->encrypt->usesNonce()) { + $packet_length -= 4; + } + // round up to the nearest $this->encrypt_block_size + $packet_length += ($this->encrypt_block_size - 1) * $packet_length % $this->encrypt_block_size; + // subtracting strlen($data) is obvious - subtracting 5 is necessary because of packet_length and padding_length + $padding_length = $packet_length - \strlen($data) - 5; + switch (\true) { + case $this->encrypt && $this->encrypt->usesNonce(): + case $this->hmac_create instanceof \FluentSmtpLib\phpseclib3\Crypt\Hash && $this->hmac_create_etm: + $padding_length += 4; + $packet_length += 4; + } + $padding = \FluentSmtpLib\phpseclib3\Crypt\Random::string($padding_length); + // we subtract 4 from packet_length because the packet_length field isn't supposed to include itself + $packet = \pack('NCa*', $packet_length - 4, $padding_length, $data . $padding); + $hmac = ''; + if ($this->hmac_create instanceof \FluentSmtpLib\phpseclib3\Crypt\Hash && !$this->hmac_create_etm) { + if (($this->hmac_create->getHash() & "\xff\xff\xff\xff") == 'umac') { + $this->hmac_create->setNonce("\x00\x00\x00\x00" . \pack('N', $this->send_seq_no)); + $hmac = $this->hmac_create->hash($packet); + } else { + $hmac = $this->hmac_create->hash(\pack('Na*', $this->send_seq_no, $packet)); + } + } + if ($this->encrypt) { + switch ($this->encryptName) { + case 'aes128-gcm@openssh.com': + case 'aes256-gcm@openssh.com': + $this->encrypt->setNonce($this->encryptFixedPart . $this->encryptInvocationCounter); + \FluentSmtpLib\phpseclib3\Common\Functions\Strings::increment_str($this->encryptInvocationCounter); + $this->encrypt->setAAD($temp = $packet & "\xff\xff\xff\xff"); + $packet = $temp . $this->encrypt->encrypt(\substr($packet, 4)); + break; + case 'chacha20-poly1305@openssh.com': + // This should be impossible, but we are checking anyway to narrow the type for Psalm. + if (!$this->encrypt instanceof \FluentSmtpLib\phpseclib3\Crypt\ChaCha20) { + throw new \LogicException('$this->encrypt is not a ' . \FluentSmtpLib\phpseclib3\Crypt\ChaCha20::class); + } + $nonce = \pack('N2', 0, $this->send_seq_no); + $this->encrypt->setNonce($nonce); + $this->lengthEncrypt->setNonce($nonce); + $length = $this->lengthEncrypt->encrypt($packet & "\xff\xff\xff\xff"); + $this->encrypt->setCounter(0); + // this is the same approach that's implemented in Salsa20::createPoly1305Key() + // but we don't want to use the same AEAD construction that RFC8439 describes + // for ChaCha20-Poly1305 so we won't rely on it (see Salsa20::poly1305()) + $this->encrypt->setPoly1305Key($this->encrypt->encrypt(\str_repeat("\x00", 32))); + $this->encrypt->setAAD($length); + $this->encrypt->setCounter(1); + $packet = $length . $this->encrypt->encrypt(\substr($packet, 4)); + break; + default: + $packet = $this->hmac_create instanceof \FluentSmtpLib\phpseclib3\Crypt\Hash && $this->hmac_create_etm ? ($packet & "\xff\xff\xff\xff") . $this->encrypt->encrypt(\substr($packet, 4)) : $this->encrypt->encrypt($packet); + } + } + if ($this->hmac_create instanceof \FluentSmtpLib\phpseclib3\Crypt\Hash && $this->hmac_create_etm) { + if (($this->hmac_create->getHash() & "\xff\xff\xff\xff") == 'umac') { + $this->hmac_create->setNonce("\x00\x00\x00\x00" . \pack('N', $this->send_seq_no)); + $hmac = $this->hmac_create->hash($packet); + } else { + $hmac = $this->hmac_create->hash(\pack('Na*', $this->send_seq_no, $packet)); + } + } + $this->send_seq_no++; + $packet .= $this->encrypt && $this->encrypt->usesNonce() ? $this->encrypt->getTag() : $hmac; + if (!$this->keyExchangeInProgress) { + $this->bytesTransferredSinceLastKEX += \strlen($packet); + } + $start = \microtime(\true); + $sent = @\fputs($this->fsock, $packet); + $stop = \microtime(\true); + if (\defined('FluentSmtpLib\\NET_SSH2_LOGGING')) { + $current = \microtime(\true); + $message_number = isset(self::$message_numbers[\ord($logged[0])]) ? self::$message_numbers[\ord($logged[0])] : 'UNKNOWN (' . \ord($logged[0]) . ')'; + $message_number = '-> ' . $message_number . ' (since last: ' . \round($current - $this->last_packet, 4) . ', network: ' . \round($stop - $start, 4) . 's)'; + $this->append_log($message_number, $logged); + } + $this->last_packet = \microtime(\true); + if (\strlen($packet) != $sent) { + $this->disconnect_helper(NET_SSH2_DISCONNECT_BY_APPLICATION); + $message = $sent === \false ? 'Unable to write ' . \strlen($packet) . ' bytes' : "Only {$sent} of " . \strlen($packet) . " bytes were sent"; + throw new \RuntimeException($message); + } + if ($this->bytesTransferredSinceLastKEX > $this->doKeyReexchangeAfterXBytes) { + $this->key_exchange(); + } + } + /** + * Sends a keep-alive message, if keep-alive is enabled and interval is met + */ + private function send_keep_alive() + { + if ($this->bitmap & self::MASK_CONNECTED) { + $elapsed = \microtime(\true) - $this->last_packet; + if ($this->keepAlive > 0 && $elapsed >= $this->keepAlive) { + $this->send_binary_packet(\pack('CN', NET_SSH2_MSG_IGNORE, 0)); + } + } + } + /** + * Logs data packets + * + * Makes sure that only the last 1MB worth of packets will be logged + * + * @param string $message_number + * @param string $message + */ + private function append_log($message_number, $message) + { + $this->append_log_helper(NET_SSH2_LOGGING, $message_number, $message, $this->message_number_log, $this->message_log, $this->log_size, $this->realtime_log_file, $this->realtime_log_wrap, $this->realtime_log_size); + } + /** + * Logs data packet helper + * + * @param int $constant + * @param string $message_number + * @param string $message + * @param array &$message_number_log + * @param array &$message_log + * @param int &$log_size + * @param resource &$realtime_log_file + * @param bool &$realtime_log_wrap + * @param int &$realtime_log_size + */ + protected function append_log_helper($constant, $message_number, $message, array &$message_number_log, array &$message_log, &$log_size, &$realtime_log_file, &$realtime_log_wrap, &$realtime_log_size) + { + // remove the byte identifying the message type from all but the first two messages (ie. the identification strings) + if (\strlen($message_number) > 2) { + \FluentSmtpLib\phpseclib3\Common\Functions\Strings::shift($message); + } + switch ($constant) { + // useful for benchmarks + case self::LOG_SIMPLE: + $message_number_log[] = $message_number; + break; + case self::LOG_SIMPLE_REALTIME: + echo $message_number; + echo \PHP_SAPI == 'cli' ? "\r\n" : '
    '; + @\flush(); + @\ob_flush(); + break; + // the most useful log for SSH2 + case self::LOG_COMPLEX: + $message_number_log[] = $message_number; + $log_size += \strlen($message); + $message_log[] = $message; + while ($log_size > self::LOG_MAX_SIZE) { + $log_size -= \strlen(\array_shift($message_log)); + \array_shift($message_number_log); + } + break; + // dump the output out realtime; packets may be interspersed with non packets, + // passwords won't be filtered out and select other packets may not be correctly + // identified + case self::LOG_REALTIME: + switch (\PHP_SAPI) { + case 'cli': + $start = $stop = "\r\n"; + break; + default: + $start = '
    ';
    +                        $stop = '
    '; + } + echo $start . $this->format_log([$message], [$message_number]) . $stop; + @\flush(); + @\ob_flush(); + break; + // basically the same thing as self::LOG_REALTIME with the caveat that NET_SSH2_LOG_REALTIME_FILENAME + // needs to be defined and that the resultant log file will be capped out at self::LOG_MAX_SIZE. + // the earliest part of the log file is denoted by the first <<< START >>> and is not going to necessarily + // at the beginning of the file + case self::LOG_REALTIME_FILE: + if (!isset($realtime_log_file)) { + // PHP doesn't seem to like using constants in fopen() + $filename = NET_SSH2_LOG_REALTIME_FILENAME; + $fp = \fopen($filename, 'w'); + $realtime_log_file = $fp; + } + if (!\is_resource($realtime_log_file)) { + break; + } + $entry = $this->format_log([$message], [$message_number]); + if ($realtime_log_wrap) { + $temp = "<<< START >>>\r\n"; + $entry .= $temp; + \fseek($realtime_log_file, \ftell($realtime_log_file) - \strlen($temp)); + } + $realtime_log_size += \strlen($entry); + if ($realtime_log_size > self::LOG_MAX_SIZE) { + \fseek($realtime_log_file, 0); + $realtime_log_size = \strlen($entry); + $realtime_log_wrap = \true; + } + \fputs($realtime_log_file, $entry); + break; + case self::LOG_REALTIME_SIMPLE: + echo $message_number; + echo \PHP_SAPI == 'cli' ? "\r\n" : '
    '; + } + } + /** + * Sends channel data + * + * Spans multiple SSH_MSG_CHANNEL_DATAs if appropriate + * + * @param int $client_channel + * @param string $data + * @return void + */ + protected function send_channel_packet($client_channel, $data) + { + if (isset($this->channel_buffers_write[$client_channel]) && \strpos($data, $this->channel_buffers_write[$client_channel]) === 0) { + // if buffer holds identical initial data content, resume send from the unmatched data portion + $data = \substr($data, \strlen($this->channel_buffers_write[$client_channel])); + } else { + $this->channel_buffers_write[$client_channel] = ''; + } + while (\strlen($data)) { + if (!$this->window_size_client_to_server[$client_channel]) { + // using an invalid channel will let the buffers be built up for the valid channels + $this->get_channel_packet(-$client_channel); + if ($this->isTimeout()) { + throw new \FluentSmtpLib\phpseclib3\Exception\TimeoutException('Timed out waiting for server'); + } elseif (!$this->window_size_client_to_server[$client_channel]) { + throw new \RuntimeException('Data window was not adjusted'); + } + } + /* The maximum amount of data allowed is determined by the maximum + packet size for the channel, and the current window size, whichever + is smaller. + -- http://tools.ietf.org/html/rfc4254#section-5.2 */ + $max_size = \min($this->packet_size_client_to_server[$client_channel], $this->window_size_client_to_server[$client_channel]); + $temp = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::shift($data, $max_size); + $packet = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::packSSH2('CNs', NET_SSH2_MSG_CHANNEL_DATA, $this->server_channels[$client_channel], $temp); + $this->window_size_client_to_server[$client_channel] -= \strlen($temp); + $this->send_binary_packet($packet); + $this->channel_buffers_write[$client_channel] .= $temp; + } + unset($this->channel_buffers_write[$client_channel]); + } + /** + * Closes and flushes a channel + * + * \phpseclib3\Net\SSH2 doesn't properly close most channels. For exec() channels are normally closed by the server + * and for SFTP channels are presumably closed when the client disconnects. This functions is intended + * for SCP more than anything. + * + * @param int $client_channel + * @param bool $want_reply + * @return void + */ + private function close_channel($client_channel, $want_reply = \false) + { + // see http://tools.ietf.org/html/rfc4254#section-5.3 + $this->send_binary_packet(\pack('CN', NET_SSH2_MSG_CHANNEL_EOF, $this->server_channels[$client_channel])); + if (!$want_reply) { + $this->send_binary_packet(\pack('CN', NET_SSH2_MSG_CHANNEL_CLOSE, $this->server_channels[$client_channel])); + } + $this->channel_status[$client_channel] = NET_SSH2_MSG_CHANNEL_CLOSE; + $this->channelCount--; + $this->curTimeout = 5; + while (!\is_bool($this->get_channel_packet($client_channel))) { + } + if ($want_reply) { + $this->send_binary_packet(\pack('CN', NET_SSH2_MSG_CHANNEL_CLOSE, $this->server_channels[$client_channel])); + } + $this->close_channel_bitmap($client_channel); + } + /** + * Maintains execution state bitmap in response to channel closure + * + * @param int $client_channel The channel number to maintain closure status of + * @return void + */ + private function close_channel_bitmap($client_channel) + { + switch ($client_channel) { + case self::CHANNEL_SHELL: + // Shell status has been maintained in the bitmap for backwards + // compatibility sake, but can be removed going forward + if ($this->bitmap & self::MASK_SHELL) { + $this->bitmap &= ~self::MASK_SHELL; + } + break; + } + } + /** + * Disconnect + * + * @param int $reason + * @return false + */ + protected function disconnect_helper($reason) + { + if ($this->bitmap & self::MASK_DISCONNECT) { + // Disregard subsequent disconnect requests + return \false; + } + $this->bitmap |= self::MASK_DISCONNECT; + if ($this->isConnected()) { + $data = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::packSSH2('CNss', NET_SSH2_MSG_DISCONNECT, $reason, '', ''); + try { + $this->send_binary_packet($data); + } catch (\Exception $e) { + } + } + $this->reset_connection(); + return \false; + } + /** + * Define Array + * + * Takes any number of arrays whose indices are integers and whose values are strings and defines a bunch of + * named constants from it, using the value as the name of the constant and the index as the value of the constant. + * If any of the constants that would be defined already exists, none of the constants will be defined. + * + * @param mixed[] ...$args + * @access protected + */ + protected static function define_array(...$args) + { + foreach ($args as $arg) { + foreach ($arg as $key => $value) { + if (!\defined($value)) { + \define($value, $key); + } else { + break 2; + } + } + } + } + /** + * Returns a log of the packets that have been sent and received. + * + * Returns a string if NET_SSH2_LOGGING == self::LOG_COMPLEX, an array if NET_SSH2_LOGGING == self::LOG_SIMPLE and false if !defined('NET_SSH2_LOGGING') + * + * @return array|false|string + */ + public function getLog() + { + if (!\defined('FluentSmtpLib\\NET_SSH2_LOGGING')) { + return \false; + } + switch (NET_SSH2_LOGGING) { + case self::LOG_SIMPLE: + return $this->message_number_log; + case self::LOG_COMPLEX: + $log = $this->format_log($this->message_log, $this->message_number_log); + return \PHP_SAPI == 'cli' ? $log : '
    ' . $log . '
    '; + default: + return \false; + } + } + /** + * Formats a log for printing + * + * @param array $message_log + * @param array $message_number_log + * @return string + */ + protected function format_log(array $message_log, array $message_number_log) + { + $output = ''; + for ($i = 0; $i < \count($message_log); $i++) { + $output .= $message_number_log[$i]; + $current_log = $message_log[$i]; + $j = 0; + if (\strlen($current_log)) { + $output .= "\r\n"; + } + do { + if (\strlen($current_log)) { + $output .= \str_pad(\dechex($j), 7, '0', \STR_PAD_LEFT) . '0 '; + } + $fragment = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::shift($current_log, $this->log_short_width); + $hex = \substr(\preg_replace_callback('#.#s', function ($matches) { + return $this->log_boundary . \str_pad(\dechex(\ord($matches[0])), 2, '0', \STR_PAD_LEFT); + }, $fragment), \strlen($this->log_boundary)); + // replace non ASCII printable characters with dots + // http://en.wikipedia.org/wiki/ASCII#ASCII_printable_characters + // also replace < with a . since < messes up the output on web browsers + $raw = \preg_replace('#[^\\x20-\\x7E]|<#', '.', $fragment); + $output .= \str_pad($hex, $this->log_long_width - $this->log_short_width, ' ') . $raw . "\r\n"; + $j++; + } while (\strlen($current_log)); + $output .= "\r\n"; + } + return $output; + } + /** + * Helper function for agent->on_channel_open() + * + * Used when channels are created to inform agent + * of said channel opening. Must be called after + * channel open confirmation received + * + */ + private function on_channel_open() + { + if (isset($this->agent)) { + $this->agent->registerChannelOpen($this); + } + } + /** + * Returns the first value of the intersection of two arrays or false if + * the intersection is empty. The order is defined by the first parameter. + * + * @param array $array1 + * @param array $array2 + * @return mixed False if intersection is empty, else intersected value. + */ + private static function array_intersect_first(array $array1, array $array2) + { + foreach ($array1 as $value) { + if (\in_array($value, $array2)) { + return $value; + } + } + return \false; + } + /** + * Returns all errors / debug messages on the SSH layer + * + * If you are looking for messages from the SFTP layer, please see SFTP::getSFTPErrors() + * + * @return string[] + */ + public function getErrors() + { + return $this->errors; + } + /** + * Returns the last error received on the SSH layer + * + * If you are looking for messages from the SFTP layer, please see SFTP::getLastSFTPError() + * + * @return string + */ + public function getLastError() + { + $count = \count($this->errors); + if ($count > 0) { + return $this->errors[$count - 1]; + } + } + /** + * Return the server identification. + * + * @return string|false + */ + public function getServerIdentification() + { + $this->connect(); + return $this->server_identifier; + } + /** + * Returns a list of algorithms the server supports + * + * @return array + */ + public function getServerAlgorithms() + { + $this->connect(); + return ['kex' => $this->kex_algorithms, 'hostkey' => $this->server_host_key_algorithms, 'client_to_server' => ['crypt' => $this->encryption_algorithms_client_to_server, 'mac' => $this->mac_algorithms_client_to_server, 'comp' => $this->compression_algorithms_client_to_server, 'lang' => $this->languages_client_to_server], 'server_to_client' => ['crypt' => $this->encryption_algorithms_server_to_client, 'mac' => $this->mac_algorithms_server_to_client, 'comp' => $this->compression_algorithms_server_to_client, 'lang' => $this->languages_server_to_client]]; + } + /** + * Returns a list of KEX algorithms that phpseclib supports + * + * @return array + */ + public static function getSupportedKEXAlgorithms() + { + $kex_algorithms = [ + // Elliptic Curve Diffie-Hellman Key Agreement (ECDH) using + // Curve25519. See doc/curve25519-sha256@libssh.org.txt in the + // libssh repository for more information. + 'curve25519-sha256', + 'curve25519-sha256@libssh.org', + 'ecdh-sha2-nistp256', + // RFC 5656 + 'ecdh-sha2-nistp384', + // RFC 5656 + 'ecdh-sha2-nistp521', + // RFC 5656 + 'diffie-hellman-group-exchange-sha256', + // RFC 4419 + 'diffie-hellman-group-exchange-sha1', + // RFC 4419 + // Diffie-Hellman Key Agreement (DH) using integer modulo prime + // groups. + 'diffie-hellman-group14-sha256', + 'diffie-hellman-group14-sha1', + // REQUIRED + 'diffie-hellman-group15-sha512', + 'diffie-hellman-group16-sha512', + 'diffie-hellman-group17-sha512', + 'diffie-hellman-group18-sha512', + 'diffie-hellman-group1-sha1', + ]; + return $kex_algorithms; + } + /** + * Returns a list of host key algorithms that phpseclib supports + * + * @return array + */ + public static function getSupportedHostKeyAlgorithms() + { + return [ + 'ssh-ed25519', + // https://tools.ietf.org/html/draft-ietf-curdle-ssh-ed25519-02 + 'ecdsa-sha2-nistp256', + // RFC 5656 + 'ecdsa-sha2-nistp384', + // RFC 5656 + 'ecdsa-sha2-nistp521', + // RFC 5656 + 'rsa-sha2-256', + // RFC 8332 + 'rsa-sha2-512', + // RFC 8332 + 'ssh-rsa', + // RECOMMENDED sign Raw RSA Key + 'ssh-dss', + ]; + } + /** + * Returns a list of symmetric key algorithms that phpseclib supports + * + * @return array + */ + public static function getSupportedEncryptionAlgorithms() + { + $algos = [ + // from : + 'aes128-gcm@openssh.com', + 'aes256-gcm@openssh.com', + // from : + 'arcfour256', + 'arcfour128', + //'arcfour', // OPTIONAL the ARCFOUR stream cipher with a 128-bit key + // CTR modes from : + 'aes128-ctr', + // RECOMMENDED AES (Rijndael) in SDCTR mode, with 128-bit key + 'aes192-ctr', + // RECOMMENDED AES with 192-bit key + 'aes256-ctr', + // RECOMMENDED AES with 256-bit key + // from : + // one of the big benefits of chacha20-poly1305 is speed. the problem is... + // libsodium doesn't generate the poly1305 keys in the way ssh does and openssl's PHP bindings don't even + // seem to support poly1305 currently. so even if libsodium or openssl are being used for the chacha20 + // part, pure-PHP has to be used for the poly1305 part and that's gonna cause a big slow down. + // speed-wise it winds up being faster to use AES (when openssl or mcrypt are available) and some HMAC + // (which is always gonna be super fast to compute thanks to the hash extension, which + // "is bundled and compiled into PHP by default") + 'chacha20-poly1305@openssh.com', + 'twofish128-ctr', + // OPTIONAL Twofish in SDCTR mode, with 128-bit key + 'twofish192-ctr', + // OPTIONAL Twofish with 192-bit key + 'twofish256-ctr', + // OPTIONAL Twofish with 256-bit key + 'aes128-cbc', + // RECOMMENDED AES with a 128-bit key + 'aes192-cbc', + // OPTIONAL AES with a 192-bit key + 'aes256-cbc', + // OPTIONAL AES in CBC mode, with a 256-bit key + 'twofish128-cbc', + // OPTIONAL Twofish with a 128-bit key + 'twofish192-cbc', + // OPTIONAL Twofish with a 192-bit key + 'twofish256-cbc', + 'twofish-cbc', + // OPTIONAL alias for "twofish256-cbc" + // (this is being retained for historical reasons) + 'blowfish-ctr', + // OPTIONAL Blowfish in SDCTR mode + 'blowfish-cbc', + // OPTIONAL Blowfish in CBC mode + '3des-ctr', + // RECOMMENDED Three-key 3DES in SDCTR mode + '3des-cbc', + ]; + if (self::$crypto_engine) { + $engines = [self::$crypto_engine]; + } else { + $engines = ['libsodium', 'OpenSSL (GCM)', 'OpenSSL', 'mcrypt', 'Eval', 'PHP']; + } + $ciphers = []; + foreach ($engines as $engine) { + foreach ($algos as $algo) { + $obj = self::encryption_algorithm_to_crypt_instance($algo); + if ($obj instanceof \FluentSmtpLib\phpseclib3\Crypt\Rijndael) { + $obj->setKeyLength(\preg_replace('#[^\\d]#', '', $algo)); + } + switch ($algo) { + // Eval engines do not exist for ChaCha20 or RC4 because they would not benefit from one. + // to benefit from an Eval engine they'd need to loop a variable amount of times, they'd + // need to do table lookups (eg. sbox subsitutions). ChaCha20 doesn't do either because + // it's a so-called ARX cipher, meaning that the only operations it does are add (A), rotate (R) + // and XOR (X). RC4 does do table lookups but being a stream cipher it works differently than + // block ciphers. with RC4 you XOR the plaintext against a keystream and the keystream changes + // as you encrypt stuff. the only table lookups are made against this keystream and thus table + // lookups are kinda unavoidable. with AES and DES, however, the table lookups that are done + // are done against substitution boxes (sboxes), which are invariant. + // OpenSSL can't be used as an engine, either, because OpenSSL doesn't support continuous buffers + // as SSH2 uses and altho you can emulate a continuous buffer with block ciphers you can't do so + // with stream ciphers. As for ChaCha20... for the ChaCha20 part OpenSSL could prob be used but + // the big slow down isn't with ChaCha20 - it's with Poly1305. SSH constructs the key for that + // differently than how OpenSSL does it (OpenSSL does it as the RFC describes, SSH doesn't). + // libsodium can't be used because it doesn't support RC4 and it doesn't construct the Poly1305 + // keys in the same way that SSH does + // mcrypt could prob be used for RC4 but mcrypt hasn't been included in PHP core for yearss + case 'chacha20-poly1305@openssh.com': + case 'arcfour128': + case 'arcfour256': + if ($engine != 'PHP') { + continue 2; + } + break; + case 'aes128-gcm@openssh.com': + case 'aes256-gcm@openssh.com': + if ($engine == 'OpenSSL') { + continue 2; + } + $obj->setNonce('dummydummydu'); + } + if ($obj->isValidEngine($engine)) { + $algos = \array_diff($algos, [$algo]); + $ciphers[] = $algo; + } + } + } + return $ciphers; + } + /** + * Returns a list of MAC algorithms that phpseclib supports + * + * @return array + */ + public static function getSupportedMACAlgorithms() + { + return [ + 'hmac-sha2-256-etm@openssh.com', + 'hmac-sha2-512-etm@openssh.com', + 'hmac-sha1-etm@openssh.com', + // from : + 'hmac-sha2-256', + // RECOMMENDED HMAC-SHA256 (digest length = key length = 32) + 'hmac-sha2-512', + // OPTIONAL HMAC-SHA512 (digest length = key length = 64) + 'hmac-sha1-96', + // RECOMMENDED first 96 bits of HMAC-SHA1 (digest length = 12, key length = 20) + 'hmac-sha1', + // REQUIRED HMAC-SHA1 (digest length = key length = 20) + 'hmac-md5-96', + // OPTIONAL first 96 bits of HMAC-MD5 (digest length = 12, key length = 16) + 'hmac-md5', + // OPTIONAL HMAC-MD5 (digest length = key length = 16) + 'umac-64-etm@openssh.com', + 'umac-128-etm@openssh.com', + // from : + 'umac-64@openssh.com', + 'umac-128@openssh.com', + ]; + } + /** + * Returns a list of compression algorithms that phpseclib supports + * + * @return array + */ + public static function getSupportedCompressionAlgorithms() + { + $algos = ['none']; + // REQUIRED no compression + if (\function_exists('deflate_init')) { + $algos[] = 'zlib@openssh.com'; + // https://datatracker.ietf.org/doc/html/draft-miller-secsh-compression-delayed + $algos[] = 'zlib'; + } + return $algos; + } + /** + * Return list of negotiated algorithms + * + * Uses the same format as https://www.php.net/ssh2-methods-negotiated + * + * @return array + */ + public function getAlgorithmsNegotiated() + { + $this->connect(); + $compression_map = [self::NET_SSH2_COMPRESSION_NONE => 'none', self::NET_SSH2_COMPRESSION_ZLIB => 'zlib', self::NET_SSH2_COMPRESSION_ZLIB_AT_OPENSSH => 'zlib@openssh.com']; + return ['kex' => $this->kex_algorithm, 'hostkey' => $this->signature_format, 'client_to_server' => ['crypt' => $this->encryptName, 'mac' => $this->hmac_create_name, 'comp' => $compression_map[$this->compress]], 'server_to_client' => ['crypt' => $this->decryptName, 'mac' => $this->hmac_check_name, 'comp' => $compression_map[$this->decompress]]]; + } + /** + * Force multiple channels (even if phpseclib has decided to disable them) + */ + public function forceMultipleChannels() + { + $this->errorOnMultipleChannels = \false; + } + /** + * Allows you to set the terminal + * + * @param string $term + */ + public function setTerminal($term) + { + $this->term = $term; + } + /** + * Accepts an associative array with up to four parameters as described at + * + * + * @param array $methods + */ + public function setPreferredAlgorithms(array $methods) + { + $keys = ['client_to_server', 'server_to_client']; + if (isset($methods['kex']) && \is_string($methods['kex'])) { + $methods['kex'] = \explode(',', $methods['kex']); + } + if (isset($methods['hostkey']) && \is_string($methods['hostkey'])) { + $methods['hostkey'] = \explode(',', $methods['hostkey']); + } + foreach ($keys as $key) { + if (isset($methods[$key])) { + $a =& $methods[$key]; + if (isset($a['crypt']) && \is_string($a['crypt'])) { + $a['crypt'] = \explode(',', $a['crypt']); + } + if (isset($a['comp']) && \is_string($a['comp'])) { + $a['comp'] = \explode(',', $a['comp']); + } + if (isset($a['mac']) && \is_string($a['mac'])) { + $a['mac'] = \explode(',', $a['mac']); + } + } + } + $preferred = $methods; + if (isset($preferred['kex'])) { + $preferred['kex'] = \array_intersect($preferred['kex'], static::getSupportedKEXAlgorithms()); + } + if (isset($preferred['hostkey'])) { + $preferred['hostkey'] = \array_intersect($preferred['hostkey'], static::getSupportedHostKeyAlgorithms()); + } + foreach ($keys as $key) { + if (isset($preferred[$key])) { + $a =& $preferred[$key]; + if (isset($a['crypt'])) { + $a['crypt'] = \array_intersect($a['crypt'], static::getSupportedEncryptionAlgorithms()); + } + if (isset($a['comp'])) { + $a['comp'] = \array_intersect($a['comp'], static::getSupportedCompressionAlgorithms()); + } + if (isset($a['mac'])) { + $a['mac'] = \array_intersect($a['mac'], static::getSupportedMACAlgorithms()); + } + } + } + $keys = ['kex', 'hostkey', 'client_to_server/crypt', 'client_to_server/comp', 'client_to_server/mac', 'server_to_client/crypt', 'server_to_client/comp', 'server_to_client/mac']; + foreach ($keys as $key) { + $p = $preferred; + $m = $methods; + $subkeys = \explode('/', $key); + foreach ($subkeys as $subkey) { + if (!isset($p[$subkey])) { + continue 2; + } + $p = $p[$subkey]; + $m = $m[$subkey]; + } + if (\count($p) != \count($m)) { + $diff = \array_diff($m, $p); + $msg = \count($diff) == 1 ? ' is not a supported algorithm' : ' are not supported algorithms'; + throw new \FluentSmtpLib\phpseclib3\Exception\UnsupportedAlgorithmException(\implode(', ', $diff) . $msg); + } + } + $this->preferred = $preferred; + } + /** + * Returns the banner message. + * + * Quoting from the RFC, "in some jurisdictions, sending a warning message before + * authentication may be relevant for getting legal protection." + * + * @return string + */ + public function getBannerMessage() + { + return $this->banner_message; + } + /** + * Returns the server public host key. + * + * Caching this the first time you connect to a server and checking the result on subsequent connections + * is recommended. Returns false if the server signature is not signed correctly with the public host key. + * + * @return string|false + * @throws \RuntimeException on badly formatted keys + * @throws NoSupportedAlgorithmsException when the key isn't in a supported format + */ + public function getServerPublicHostKey() + { + if (!($this->bitmap & self::MASK_CONSTRUCTOR)) { + $this->connect(); + } + $signature = $this->signature; + $server_public_host_key = \base64_encode($this->server_public_host_key); + if ($this->signature_validated) { + return $this->bitmap ? $this->signature_format . ' ' . $server_public_host_key : \false; + } + $this->signature_validated = \true; + switch ($this->signature_format) { + case 'ssh-ed25519': + case 'ecdsa-sha2-nistp256': + case 'ecdsa-sha2-nistp384': + case 'ecdsa-sha2-nistp521': + $key = \FluentSmtpLib\phpseclib3\Crypt\EC::loadFormat('OpenSSH', $server_public_host_key)->withSignatureFormat('SSH2'); + switch ($this->signature_format) { + case 'ssh-ed25519': + $hash = 'sha512'; + break; + case 'ecdsa-sha2-nistp256': + $hash = 'sha256'; + break; + case 'ecdsa-sha2-nistp384': + $hash = 'sha384'; + break; + case 'ecdsa-sha2-nistp521': + $hash = 'sha512'; + } + $key = $key->withHash($hash); + break; + case 'ssh-dss': + $key = \FluentSmtpLib\phpseclib3\Crypt\DSA::loadFormat('OpenSSH', $server_public_host_key)->withSignatureFormat('SSH2')->withHash('sha1'); + break; + case 'ssh-rsa': + case 'rsa-sha2-256': + case 'rsa-sha2-512': + // could be ssh-rsa, rsa-sha2-256, rsa-sha2-512 + // we don't check here because we already checked in key_exchange + // some signatures have the type embedded within the message and some don't + list(, $signature) = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::unpackSSH2('ss', $signature); + $key = \FluentSmtpLib\phpseclib3\Crypt\RSA::loadFormat('OpenSSH', $server_public_host_key)->withPadding(\FluentSmtpLib\phpseclib3\Crypt\RSA::SIGNATURE_PKCS1); + switch ($this->signature_format) { + case 'rsa-sha2-512': + $hash = 'sha512'; + break; + case 'rsa-sha2-256': + $hash = 'sha256'; + break; + //case 'ssh-rsa': + default: + $hash = 'sha1'; + } + $key = $key->withHash($hash); + break; + default: + $this->disconnect_helper(NET_SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE); + throw new \FluentSmtpLib\phpseclib3\Exception\NoSupportedAlgorithmsException('Unsupported signature format'); + } + if (!$key->verify($this->exchange_hash, $signature)) { + return $this->disconnect_helper(NET_SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE); + } + return $this->signature_format . ' ' . $server_public_host_key; + } + /** + * Returns the exit status of an SSH command or false. + * + * @return false|int + */ + public function getExitStatus() + { + if (\is_null($this->exit_status)) { + return \false; + } + return $this->exit_status; + } + /** + * Returns the number of columns for the terminal window size. + * + * @return int + */ + public function getWindowColumns() + { + return $this->windowColumns; + } + /** + * Returns the number of rows for the terminal window size. + * + * @return int + */ + public function getWindowRows() + { + return $this->windowRows; + } + /** + * Sets the number of columns for the terminal window size. + * + * @param int $value + */ + public function setWindowColumns($value) + { + $this->windowColumns = $value; + } + /** + * Sets the number of rows for the terminal window size. + * + * @param int $value + */ + public function setWindowRows($value) + { + $this->windowRows = $value; + } + /** + * Sets the number of columns and rows for the terminal window size. + * + * @param int $columns + * @param int $rows + */ + public function setWindowSize($columns = 80, $rows = 24) + { + $this->windowColumns = $columns; + $this->windowRows = $rows; + } + /** + * To String Magic Method + * + * @return string + */ + #[\ReturnTypeWillChange] + public function __toString() + { + return $this->getResourceId(); + } + /** + * Get Resource ID + * + * We use {} because that symbols should not be in URL according to + * {@link http://tools.ietf.org/html/rfc3986#section-2 RFC}. + * It will safe us from any conflicts, because otherwise regexp will + * match all alphanumeric domains. + * + * @return string + */ + public function getResourceId() + { + return '{' . \spl_object_hash($this) . '}'; + } + /** + * Return existing connection + * + * @param string $id + * + * @return bool|SSH2 will return false if no such connection + */ + public static function getConnectionByResourceId($id) + { + if (isset(self::$connections[$id])) { + return self::$connections[$id] instanceof \WeakReference ? self::$connections[$id]->get() : self::$connections[$id]; + } + return \false; + } + /** + * Return all excising connections + * + * @return array + */ + public static function getConnections() + { + if (!\class_exists('WeakReference')) { + /** @var array */ + return self::$connections; + } + $temp = []; + foreach (self::$connections as $key => $ref) { + $temp[$key] = $ref->get(); + } + return $temp; + } + /* + * Update packet types in log history + * + * @param string $old + * @param string $new + */ + private function updateLogHistory($old, $new) + { + if (\defined('FluentSmtpLib\\NET_SSH2_LOGGING') && NET_SSH2_LOGGING == self::LOG_COMPLEX) { + $this->message_number_log[\count($this->message_number_log) - 1] = \str_replace($old, $new, $this->message_number_log[\count($this->message_number_log) - 1]); + } + } + /** + * Return the list of authentication methods that may productively continue authentication. + * + * @see https://tools.ietf.org/html/rfc4252#section-5.1 + * @return array|null + */ + public function getAuthMethodsToContinue() + { + return $this->auth_methods_to_continue; + } + /** + * Enables "smart" multi-factor authentication (MFA) + */ + public function enableSmartMFA() + { + $this->smartMFA = \true; + } + /** + * Disables "smart" multi-factor authentication (MFA) + */ + public function disableSmartMFA() + { + $this->smartMFA = \false; + } + /** + * How many bytes until the next key re-exchange? + * + * @param int $bytes + */ + public function bytesUntilKeyReexchange($bytes) + { + $this->doKeyReexchangeAfterXBytes = $bytes; + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/System/SSH/Agent.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/System/SSH/Agent.php new file mode 100644 index 0000000..a019d50 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/System/SSH/Agent.php @@ -0,0 +1,270 @@ + + * login('username', $agent)) { + * exit('Login Failed'); + * } + * + * echo $ssh->exec('pwd'); + * echo $ssh->exec('ls -la'); + * ?> + * + * + * @author Jim Wigginton + * @copyright 2014 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\System\SSH; + +use FluentSmtpLib\phpseclib3\Common\Functions\Strings; +use FluentSmtpLib\phpseclib3\Crypt\Common\PublicKey; +use FluentSmtpLib\phpseclib3\Crypt\PublicKeyLoader; +use FluentSmtpLib\phpseclib3\Crypt\RSA; +use FluentSmtpLib\phpseclib3\Exception\BadConfigurationException; +use FluentSmtpLib\phpseclib3\Net\SSH2; +use FluentSmtpLib\phpseclib3\System\SSH\Agent\Identity; +/** + * Pure-PHP ssh-agent client identity factory + * + * requestIdentities() method pumps out \phpseclib3\System\SSH\Agent\Identity objects + * + * @author Jim Wigginton + */ +class Agent +{ + use Common\Traits\ReadBytes; + // Message numbers + // to request SSH1 keys you have to use SSH_AGENTC_REQUEST_RSA_IDENTITIES (1) + const SSH_AGENTC_REQUEST_IDENTITIES = 11; + // this is the SSH2 response; the SSH1 response is SSH_AGENT_RSA_IDENTITIES_ANSWER (2). + const SSH_AGENT_IDENTITIES_ANSWER = 12; + // the SSH1 request is SSH_AGENTC_RSA_CHALLENGE (3) + const SSH_AGENTC_SIGN_REQUEST = 13; + // the SSH1 response is SSH_AGENT_RSA_RESPONSE (4) + const SSH_AGENT_SIGN_RESPONSE = 14; + // Agent forwarding status + // no forwarding requested and not active + const FORWARD_NONE = 0; + // request agent forwarding when opportune + const FORWARD_REQUEST = 1; + // forwarding has been request and is active + const FORWARD_ACTIVE = 2; + /** + * Unused + */ + const SSH_AGENT_FAILURE = 5; + /** + * Socket Resource + * + * @var resource + */ + private $fsock; + /** + * Agent forwarding status + * + * @var int + */ + private $forward_status = self::FORWARD_NONE; + /** + * Buffer for accumulating forwarded authentication + * agent data arriving on SSH data channel destined + * for agent unix socket + * + * @var string + */ + private $socket_buffer = ''; + /** + * Tracking the number of bytes we are expecting + * to arrive for the agent socket on the SSH data + * channel + * + * @var int + */ + private $expected_bytes = 0; + /** + * Default Constructor + * + * @return Agent + * @throws BadConfigurationException if SSH_AUTH_SOCK cannot be found + * @throws \RuntimeException on connection errors + */ + public function __construct($address = null) + { + if (!$address) { + switch (\true) { + case isset($_SERVER['SSH_AUTH_SOCK']): + $address = $_SERVER['SSH_AUTH_SOCK']; + break; + case isset($_ENV['SSH_AUTH_SOCK']): + $address = $_ENV['SSH_AUTH_SOCK']; + break; + default: + throw new \FluentSmtpLib\phpseclib3\Exception\BadConfigurationException('SSH_AUTH_SOCK not found'); + } + } + if (\in_array('unix', \stream_get_transports())) { + $this->fsock = \fsockopen('unix://' . $address, 0, $errno, $errstr); + if (!$this->fsock) { + throw new \RuntimeException("Unable to connect to ssh-agent (Error {$errno}: {$errstr})"); + } + } else { + if (\substr($address, 0, 9) != '\\\\.\\pipe\\' || \strpos(\substr($address, 9), '\\') !== \false) { + throw new \RuntimeException('Address is not formatted as a named pipe should be'); + } + $this->fsock = \fopen($address, 'r+b'); + if (!$this->fsock) { + throw new \RuntimeException('Unable to open address'); + } + } + } + /** + * Request Identities + * + * See "2.5.2 Requesting a list of protocol 2 keys" + * Returns an array containing zero or more \phpseclib3\System\SSH\Agent\Identity objects + * + * @return array + * @throws \RuntimeException on receipt of unexpected packets + */ + public function requestIdentities() + { + if (!$this->fsock) { + return []; + } + $packet = \pack('NC', 1, self::SSH_AGENTC_REQUEST_IDENTITIES); + if (\strlen($packet) != \fputs($this->fsock, $packet)) { + throw new \RuntimeException('Connection closed while requesting identities'); + } + $length = \current(\unpack('N', $this->readBytes(4))); + $packet = $this->readBytes($length); + list($type, $keyCount) = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::unpackSSH2('CN', $packet); + if ($type != self::SSH_AGENT_IDENTITIES_ANSWER) { + throw new \RuntimeException('Unable to request identities'); + } + $identities = []; + for ($i = 0; $i < $keyCount; $i++) { + list($key_blob, $comment) = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::unpackSSH2('ss', $packet); + $temp = $key_blob; + list($key_type) = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::unpackSSH2('s', $temp); + switch ($key_type) { + case 'ssh-rsa': + case 'ssh-dss': + case 'ssh-ed25519': + case 'ecdsa-sha2-nistp256': + case 'ecdsa-sha2-nistp384': + case 'ecdsa-sha2-nistp521': + $key = \FluentSmtpLib\phpseclib3\Crypt\PublicKeyLoader::load($key_type . ' ' . \base64_encode($key_blob)); + } + // resources are passed by reference by default + if (isset($key)) { + $identity = (new \FluentSmtpLib\phpseclib3\System\SSH\Agent\Identity($this->fsock))->withPublicKey($key)->withPublicKeyBlob($key_blob)->withComment($comment); + $identities[] = $identity; + unset($key); + } + } + return $identities; + } + /** + * Returns the SSH Agent identity matching a given public key or null if no identity is found + * + * @return ?Identity + */ + public function findIdentityByPublicKey(\FluentSmtpLib\phpseclib3\Crypt\Common\PublicKey $key) + { + $identities = $this->requestIdentities(); + $key = (string) $key; + foreach ($identities as $identity) { + if ((string) $identity->getPublicKey() == $key) { + return $identity; + } + } + return null; + } + /** + * Signal that agent forwarding should + * be requested when a channel is opened + * + * @return void + */ + public function startSSHForwarding() + { + if ($this->forward_status == self::FORWARD_NONE) { + $this->forward_status = self::FORWARD_REQUEST; + } + } + /** + * Request agent forwarding of remote server + * + * @param SSH2 $ssh + * @return bool + */ + private function request_forwarding(\FluentSmtpLib\phpseclib3\Net\SSH2 $ssh) + { + if (!$ssh->requestAgentForwarding()) { + return \false; + } + $this->forward_status = self::FORWARD_ACTIVE; + return \true; + } + /** + * On successful channel open + * + * This method is called upon successful channel + * open to give the SSH Agent an opportunity + * to take further action. i.e. request agent forwarding + * + * @param SSH2 $ssh + */ + public function registerChannelOpen(\FluentSmtpLib\phpseclib3\Net\SSH2 $ssh) + { + if ($this->forward_status == self::FORWARD_REQUEST) { + $this->request_forwarding($ssh); + } + } + /** + * Forward data to SSH Agent and return data reply + * + * @param string $data + * @return string Data from SSH Agent + * @throws \RuntimeException on connection errors + */ + public function forwardData($data) + { + if ($this->expected_bytes > 0) { + $this->socket_buffer .= $data; + $this->expected_bytes -= \strlen($data); + } else { + $agent_data_bytes = \current(\unpack('N', $data)); + $current_data_bytes = \strlen($data); + $this->socket_buffer = $data; + if ($current_data_bytes != $agent_data_bytes + 4) { + $this->expected_bytes = $agent_data_bytes + 4 - $current_data_bytes; + return \false; + } + } + if (\strlen($this->socket_buffer) != \fwrite($this->fsock, $this->socket_buffer)) { + throw new \RuntimeException('Connection closed attempting to forward data to SSH agent'); + } + $this->socket_buffer = ''; + $this->expected_bytes = 0; + $agent_reply_bytes = \current(\unpack('N', $this->readBytes(4))); + $agent_reply_data = $this->readBytes($agent_reply_bytes); + $agent_reply_data = \current(\unpack('a*', $agent_reply_data)); + return \pack('Na*', $agent_reply_bytes, $agent_reply_data); + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/System/SSH/Agent/Identity.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/System/SSH/Agent/Identity.php new file mode 100644 index 0000000..0491327 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/System/SSH/Agent/Identity.php @@ -0,0 +1,303 @@ + + * @copyright 2009 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\System\SSH\Agent; + +use FluentSmtpLib\phpseclib3\Common\Functions\Strings; +use FluentSmtpLib\phpseclib3\Crypt\Common\PrivateKey; +use FluentSmtpLib\phpseclib3\Crypt\Common\PublicKey; +use FluentSmtpLib\phpseclib3\Crypt\DSA; +use FluentSmtpLib\phpseclib3\Crypt\EC; +use FluentSmtpLib\phpseclib3\Crypt\RSA; +use FluentSmtpLib\phpseclib3\Exception\UnsupportedAlgorithmException; +use FluentSmtpLib\phpseclib3\System\SSH\Agent; +use FluentSmtpLib\phpseclib3\System\SSH\Common\Traits\ReadBytes; +/** + * Pure-PHP ssh-agent client identity object + * + * Instantiation should only be performed by \phpseclib3\System\SSH\Agent class. + * This could be thought of as implementing an interface that phpseclib3\Crypt\RSA + * implements. ie. maybe a Net_SSH_Auth_PublicKey interface or something. + * The methods in this interface would be getPublicKey and sign since those are the + * methods phpseclib looks for to perform public key authentication. + * + * @author Jim Wigginton + * @internal + */ +class Identity implements \FluentSmtpLib\phpseclib3\Crypt\Common\PrivateKey +{ + use ReadBytes; + // Signature Flags + // See https://tools.ietf.org/html/draft-miller-ssh-agent-00#section-5.3 + const SSH_AGENT_RSA2_256 = 2; + const SSH_AGENT_RSA2_512 = 4; + /** + * Key Object + * + * @var PublicKey + * @see self::getPublicKey() + */ + private $key; + /** + * Key Blob + * + * @var string + * @see self::sign() + */ + private $key_blob; + /** + * Socket Resource + * + * @var resource + * @see self::sign() + */ + private $fsock; + /** + * Signature flags + * + * @var int + * @see self::sign() + * @see self::setHash() + */ + private $flags = 0; + /** + * Comment + * + * @var null|string + */ + private $comment; + /** + * Curve Aliases + * + * @var array + */ + private static $curveAliases = ['secp256r1' => 'nistp256', 'secp384r1' => 'nistp384', 'secp521r1' => 'nistp521', 'Ed25519' => 'Ed25519']; + /** + * Default Constructor. + * + * @param resource $fsock + */ + public function __construct($fsock) + { + $this->fsock = $fsock; + } + /** + * Set Public Key + * + * Called by \phpseclib3\System\SSH\Agent::requestIdentities() + * + * @param PublicKey $key + */ + public function withPublicKey(\FluentSmtpLib\phpseclib3\Crypt\Common\PublicKey $key) + { + if ($key instanceof \FluentSmtpLib\phpseclib3\Crypt\EC) { + if (\is_array($key->getCurve()) || !isset(self::$curveAliases[$key->getCurve()])) { + throw new \FluentSmtpLib\phpseclib3\Exception\UnsupportedAlgorithmException('The only supported curves are nistp256, nistp384, nistp512 and Ed25519'); + } + } + $new = clone $this; + $new->key = $key; + return $new; + } + /** + * Set Public Key + * + * Called by \phpseclib3\System\SSH\Agent::requestIdentities(). The key blob could be extracted from $this->key + * but this saves a small amount of computation. + * + * @param string $key_blob + */ + public function withPublicKeyBlob($key_blob) + { + $new = clone $this; + $new->key_blob = $key_blob; + return $new; + } + /** + * Get Public Key + * + * Wrapper for $this->key->getPublicKey() + * + * @return mixed + */ + public function getPublicKey() + { + return $this->key; + } + /** + * Sets the hash + * + * @param string $hash + */ + public function withHash($hash) + { + $new = clone $this; + $hash = \strtolower($hash); + if ($this->key instanceof \FluentSmtpLib\phpseclib3\Crypt\RSA) { + $new->flags = 0; + switch ($hash) { + case 'sha1': + break; + case 'sha256': + $new->flags = self::SSH_AGENT_RSA2_256; + break; + case 'sha512': + $new->flags = self::SSH_AGENT_RSA2_512; + break; + default: + throw new \FluentSmtpLib\phpseclib3\Exception\UnsupportedAlgorithmException('The only supported hashes for RSA are sha1, sha256 and sha512'); + } + } + if ($this->key instanceof \FluentSmtpLib\phpseclib3\Crypt\EC) { + switch ($this->key->getCurve()) { + case 'secp256r1': + $expectedHash = 'sha256'; + break; + case 'secp384r1': + $expectedHash = 'sha384'; + break; + //case 'secp521r1': + //case 'Ed25519': + default: + $expectedHash = 'sha512'; + } + if ($hash != $expectedHash) { + throw new \FluentSmtpLib\phpseclib3\Exception\UnsupportedAlgorithmException('The only supported hash for ' . self::$curveAliases[$this->key->getCurve()] . ' is ' . $expectedHash); + } + } + if ($this->key instanceof \FluentSmtpLib\phpseclib3\Crypt\DSA) { + if ($hash != 'sha1') { + throw new \FluentSmtpLib\phpseclib3\Exception\UnsupportedAlgorithmException('The only supported hash for DSA is sha1'); + } + } + return $new; + } + /** + * Sets the padding + * + * Only PKCS1 padding is supported + * + * @param string $padding + */ + public function withPadding($padding) + { + if (!$this->key instanceof \FluentSmtpLib\phpseclib3\Crypt\RSA) { + throw new \FluentSmtpLib\phpseclib3\Exception\UnsupportedAlgorithmException('Only RSA keys support padding'); + } + if ($padding != \FluentSmtpLib\phpseclib3\Crypt\RSA::SIGNATURE_PKCS1 && $padding != \FluentSmtpLib\phpseclib3\Crypt\RSA::SIGNATURE_RELAXED_PKCS1) { + throw new \FluentSmtpLib\phpseclib3\Exception\UnsupportedAlgorithmException('ssh-agent can only create PKCS1 signatures'); + } + return $this; + } + /** + * Determines the signature padding mode + * + * Valid values are: ASN1, SSH2, Raw + * + * @param string $format + */ + public function withSignatureFormat($format) + { + if ($this->key instanceof \FluentSmtpLib\phpseclib3\Crypt\RSA) { + throw new \FluentSmtpLib\phpseclib3\Exception\UnsupportedAlgorithmException('Only DSA and EC keys support signature format setting'); + } + if ($format != 'SSH2') { + throw new \FluentSmtpLib\phpseclib3\Exception\UnsupportedAlgorithmException('Only SSH2-formatted signatures are currently supported'); + } + return $this; + } + /** + * Returns the curve + * + * Returns a string if it's a named curve, an array if not + * + * @return string|array + */ + public function getCurve() + { + if (!$this->key instanceof \FluentSmtpLib\phpseclib3\Crypt\EC) { + throw new \FluentSmtpLib\phpseclib3\Exception\UnsupportedAlgorithmException('Only EC keys have curves'); + } + return $this->key->getCurve(); + } + /** + * Create a signature + * + * See "2.6.2 Protocol 2 private key signature request" + * + * @param string $message + * @return string + * @throws \RuntimeException on connection errors + * @throws UnsupportedAlgorithmException if the algorithm is unsupported + */ + public function sign($message) + { + // the last parameter (currently 0) is for flags and ssh-agent only defines one flag (for ssh-dss): SSH_AGENT_OLD_SIGNATURE + $packet = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::packSSH2('CssN', \FluentSmtpLib\phpseclib3\System\SSH\Agent::SSH_AGENTC_SIGN_REQUEST, $this->key_blob, $message, $this->flags); + $packet = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::packSSH2('s', $packet); + if (\strlen($packet) != \fputs($this->fsock, $packet)) { + throw new \RuntimeException('Connection closed during signing'); + } + $length = \current(\unpack('N', $this->readBytes(4))); + $packet = $this->readBytes($length); + list($type, $signature_blob) = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::unpackSSH2('Cs', $packet); + if ($type != \FluentSmtpLib\phpseclib3\System\SSH\Agent::SSH_AGENT_SIGN_RESPONSE) { + throw new \RuntimeException('Unable to retrieve signature'); + } + if (!$this->key instanceof \FluentSmtpLib\phpseclib3\Crypt\RSA) { + return $signature_blob; + } + list($type, $signature_blob) = \FluentSmtpLib\phpseclib3\Common\Functions\Strings::unpackSSH2('ss', $signature_blob); + return $signature_blob; + } + /** + * Returns the private key + * + * @param string $type + * @param array $options optional + * @return string + */ + public function toString($type, array $options = []) + { + throw new \RuntimeException('ssh-agent does not provide a mechanism to get the private key'); + } + /** + * Sets the password + * + * @param string|bool $password + * @return never + */ + public function withPassword($password = \false) + { + throw new \RuntimeException('ssh-agent does not provide a mechanism to get the private key'); + } + /** + * Sets the comment + */ + public function withComment($comment = null) + { + $new = clone $this; + $new->comment = $comment; + return $new; + } + /** + * Returns the comment + * + * @return null|string + */ + public function getComment() + { + return $this->comment; + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/System/SSH/Common/Traits/ReadBytes.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/System/SSH/Common/Traits/ReadBytes.php new file mode 100644 index 0000000..141009c --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/System/SSH/Common/Traits/ReadBytes.php @@ -0,0 +1,36 @@ + + * @copyright 2015 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ +namespace FluentSmtpLib\phpseclib3\System\SSH\Common\Traits; + +/** + * ReadBytes trait + * + * @author Jim Wigginton + */ +trait ReadBytes +{ + /** + * Read data + * + * @param int $length + * @throws \RuntimeException on connection errors + */ + public function readBytes($length) + { + $temp = \fread($this->fsock, $length); + if (\strlen($temp) != $length) { + throw new \RuntimeException("Expected {$length} bytes; got " . \strlen($temp)); + } + return $temp; + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/bootstrap.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/bootstrap.php new file mode 100644 index 0000000..df8dddd --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/phpseclib/phpseclib/phpseclib/bootstrap.php @@ -0,0 +1,20 @@ +getHeaders() as $name => $values) { + * echo $name . ": " . implode(", ", $values); + * } + * + * // Emit headers iteratively: + * foreach ($message->getHeaders() as $name => $values) { + * foreach ($values as $value) { + * header(sprintf('%s: %s', $name, $value), false); + * } + * } + * + * While header names are not case-sensitive, getHeaders() will preserve the + * exact case in which headers were originally specified. + * + * @return string[][] Returns an associative array of the message's headers. Each + * key MUST be a header name, and each value MUST be an array of strings + * for that header. + */ + public function getHeaders() : array; + /** + * Checks if a header exists by the given case-insensitive name. + * + * @param string $name Case-insensitive header field name. + * @return bool Returns true if any header names match the given header + * name using a case-insensitive string comparison. Returns false if + * no matching header name is found in the message. + */ + public function hasHeader(string $name) : bool; + /** + * Retrieves a message header value by the given case-insensitive name. + * + * This method returns an array of all the header values of the given + * case-insensitive header name. + * + * If the header does not appear in the message, this method MUST return an + * empty array. + * + * @param string $name Case-insensitive header field name. + * @return string[] An array of string values as provided for the given + * header. If the header does not appear in the message, this method MUST + * return an empty array. + */ + public function getHeader(string $name) : array; + /** + * Retrieves a comma-separated string of the values for a single header. + * + * This method returns all of the header values of the given + * case-insensitive header name as a string concatenated together using + * a comma. + * + * NOTE: Not all header values may be appropriately represented using + * comma concatenation. For such headers, use getHeader() instead + * and supply your own delimiter when concatenating. + * + * If the header does not appear in the message, this method MUST return + * an empty string. + * + * @param string $name Case-insensitive header field name. + * @return string A string of values as provided for the given header + * concatenated together using a comma. If the header does not appear in + * the message, this method MUST return an empty string. + */ + public function getHeaderLine(string $name) : string; + /** + * Return an instance with the provided value replacing the specified header. + * + * While header names are case-insensitive, the casing of the header will + * be preserved by this function, and returned from getHeaders(). + * + * This method MUST be implemented in such a way as to retain the + * immutability of the message, and MUST return an instance that has the + * new and/or updated header and value. + * + * @param string $name Case-insensitive header field name. + * @param string|string[] $value Header value(s). + * @return static + * @throws \InvalidArgumentException for invalid header names or values. + */ + public function withHeader(string $name, $value) : \FluentSmtpLib\Psr\Http\Message\MessageInterface; + /** + * Return an instance with the specified header appended with the given value. + * + * Existing values for the specified header will be maintained. The new + * value(s) will be appended to the existing list. If the header did not + * exist previously, it will be added. + * + * This method MUST be implemented in such a way as to retain the + * immutability of the message, and MUST return an instance that has the + * new header and/or value. + * + * @param string $name Case-insensitive header field name to add. + * @param string|string[] $value Header value(s). + * @return static + * @throws \InvalidArgumentException for invalid header names or values. + */ + public function withAddedHeader(string $name, $value) : \FluentSmtpLib\Psr\Http\Message\MessageInterface; + /** + * Return an instance without the specified header. + * + * Header resolution MUST be done without case-sensitivity. + * + * This method MUST be implemented in such a way as to retain the + * immutability of the message, and MUST return an instance that removes + * the named header. + * + * @param string $name Case-insensitive header field name to remove. + * @return static + */ + public function withoutHeader(string $name) : \FluentSmtpLib\Psr\Http\Message\MessageInterface; + /** + * Gets the body of the message. + * + * @return StreamInterface Returns the body as a stream. + */ + public function getBody() : \FluentSmtpLib\Psr\Http\Message\StreamInterface; + /** + * Return an instance with the specified message body. + * + * The body MUST be a StreamInterface object. + * + * This method MUST be implemented in such a way as to retain the + * immutability of the message, and MUST return a new instance that has the + * new body stream. + * + * @param StreamInterface $body Body. + * @return static + * @throws \InvalidArgumentException When the body is not valid. + */ + public function withBody(\FluentSmtpLib\Psr\Http\Message\StreamInterface $body) : \FluentSmtpLib\Psr\Http\Message\MessageInterface; +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/psr/http-message/src/RequestInterface.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/psr/http-message/src/RequestInterface.php new file mode 100644 index 0000000..e393be5 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/psr/http-message/src/RequestInterface.php @@ -0,0 +1,124 @@ +getQuery()` + * or from the `QUERY_STRING` server param. + * + * @return array + */ + public function getQueryParams() : array; + /** + * Return an instance with the specified query string arguments. + * + * These values SHOULD remain immutable over the course of the incoming + * request. They MAY be injected during instantiation, such as from PHP's + * $_GET superglobal, or MAY be derived from some other value such as the + * URI. In cases where the arguments are parsed from the URI, the data + * MUST be compatible with what PHP's parse_str() would return for + * purposes of how duplicate query parameters are handled, and how nested + * sets are handled. + * + * Setting query string arguments MUST NOT change the URI stored by the + * request, nor the values in the server params. + * + * This method MUST be implemented in such a way as to retain the + * immutability of the message, and MUST return an instance that has the + * updated query string arguments. + * + * @param array $query Array of query string arguments, typically from + * $_GET. + * @return static + */ + public function withQueryParams(array $query) : \FluentSmtpLib\Psr\Http\Message\ServerRequestInterface; + /** + * Retrieve normalized file upload data. + * + * This method returns upload metadata in a normalized tree, with each leaf + * an instance of Psr\Http\Message\UploadedFileInterface. + * + * These values MAY be prepared from $_FILES or the message body during + * instantiation, or MAY be injected via withUploadedFiles(). + * + * @return array An array tree of UploadedFileInterface instances; an empty + * array MUST be returned if no data is present. + */ + public function getUploadedFiles() : array; + /** + * Create a new instance with the specified uploaded files. + * + * This method MUST be implemented in such a way as to retain the + * immutability of the message, and MUST return an instance that has the + * updated body parameters. + * + * @param array $uploadedFiles An array tree of UploadedFileInterface instances. + * @return static + * @throws \InvalidArgumentException if an invalid structure is provided. + */ + public function withUploadedFiles(array $uploadedFiles) : \FluentSmtpLib\Psr\Http\Message\ServerRequestInterface; + /** + * Retrieve any parameters provided in the request body. + * + * If the request Content-Type is either application/x-www-form-urlencoded + * or multipart/form-data, and the request method is POST, this method MUST + * return the contents of $_POST. + * + * Otherwise, this method may return any results of deserializing + * the request body content; as parsing returns structured content, the + * potential types MUST be arrays or objects only. A null value indicates + * the absence of body content. + * + * @return null|array|object The deserialized body parameters, if any. + * These will typically be an array or object. + */ + public function getParsedBody(); + /** + * Return an instance with the specified body parameters. + * + * These MAY be injected during instantiation. + * + * If the request Content-Type is either application/x-www-form-urlencoded + * or multipart/form-data, and the request method is POST, use this method + * ONLY to inject the contents of $_POST. + * + * The data IS NOT REQUIRED to come from $_POST, but MUST be the results of + * deserializing the request body content. Deserialization/parsing returns + * structured data, and, as such, this method ONLY accepts arrays or objects, + * or a null value if nothing was available to parse. + * + * As an example, if content negotiation determines that the request data + * is a JSON payload, this method could be used to create a request + * instance with the deserialized parameters. + * + * This method MUST be implemented in such a way as to retain the + * immutability of the message, and MUST return an instance that has the + * updated body parameters. + * + * @param null|array|object $data The deserialized body data. This will + * typically be in an array or object. + * @return static + * @throws \InvalidArgumentException if an unsupported argument type is + * provided. + */ + public function withParsedBody($data) : \FluentSmtpLib\Psr\Http\Message\ServerRequestInterface; + /** + * Retrieve attributes derived from the request. + * + * The request "attributes" may be used to allow injection of any + * parameters derived from the request: e.g., the results of path + * match operations; the results of decrypting cookies; the results of + * deserializing non-form-encoded message bodies; etc. Attributes + * will be application and request specific, and CAN be mutable. + * + * @return array Attributes derived from the request. + */ + public function getAttributes() : array; + /** + * Retrieve a single derived request attribute. + * + * Retrieves a single derived request attribute as described in + * getAttributes(). If the attribute has not been previously set, returns + * the default value as provided. + * + * This method obviates the need for a hasAttribute() method, as it allows + * specifying a default value to return if the attribute is not found. + * + * @see getAttributes() + * @param string $name The attribute name. + * @param mixed $default Default value to return if the attribute does not exist. + * @return mixed + */ + public function getAttribute(string $name, $default = null); + /** + * Return an instance with the specified derived request attribute. + * + * This method allows setting a single derived request attribute as + * described in getAttributes(). + * + * This method MUST be implemented in such a way as to retain the + * immutability of the message, and MUST return an instance that has the + * updated attribute. + * + * @see getAttributes() + * @param string $name The attribute name. + * @param mixed $value The value of the attribute. + * @return static + */ + public function withAttribute(string $name, $value) : \FluentSmtpLib\Psr\Http\Message\ServerRequestInterface; + /** + * Return an instance that removes the specified derived request attribute. + * + * This method allows removing a single derived request attribute as + * described in getAttributes(). + * + * This method MUST be implemented in such a way as to retain the + * immutability of the message, and MUST return an instance that removes + * the attribute. + * + * @see getAttributes() + * @param string $name The attribute name. + * @return static + */ + public function withoutAttribute(string $name) : \FluentSmtpLib\Psr\Http\Message\ServerRequestInterface; +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/psr/http-message/src/StreamInterface.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/psr/http-message/src/StreamInterface.php new file mode 100644 index 0000000..d138fea --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/psr/http-message/src/StreamInterface.php @@ -0,0 +1,144 @@ + + * [user-info@]host[:port] + *
    + * + * If the port component is not set or is the standard port for the current + * scheme, it SHOULD NOT be included. + * + * @see https://tools.ietf.org/html/rfc3986#section-3.2 + * @return string The URI authority, in "[user-info@]host[:port]" format. + */ + public function getAuthority() : string; + /** + * Retrieve the user information component of the URI. + * + * If no user information is present, this method MUST return an empty + * string. + * + * If a user is present in the URI, this will return that value; + * additionally, if the password is also present, it will be appended to the + * user value, with a colon (":") separating the values. + * + * The trailing "@" character is not part of the user information and MUST + * NOT be added. + * + * @return string The URI user information, in "username[:password]" format. + */ + public function getUserInfo() : string; + /** + * Retrieve the host component of the URI. + * + * If no host is present, this method MUST return an empty string. + * + * The value returned MUST be normalized to lowercase, per RFC 3986 + * Section 3.2.2. + * + * @see http://tools.ietf.org/html/rfc3986#section-3.2.2 + * @return string The URI host. + */ + public function getHost() : string; + /** + * Retrieve the port component of the URI. + * + * If a port is present, and it is non-standard for the current scheme, + * this method MUST return it as an integer. If the port is the standard port + * used with the current scheme, this method SHOULD return null. + * + * If no port is present, and no scheme is present, this method MUST return + * a null value. + * + * If no port is present, but a scheme is present, this method MAY return + * the standard port for that scheme, but SHOULD return null. + * + * @return null|int The URI port. + */ + public function getPort() : ?int; + /** + * Retrieve the path component of the URI. + * + * The path can either be empty or absolute (starting with a slash) or + * rootless (not starting with a slash). Implementations MUST support all + * three syntaxes. + * + * Normally, the empty path "" and absolute path "/" are considered equal as + * defined in RFC 7230 Section 2.7.3. But this method MUST NOT automatically + * do this normalization because in contexts with a trimmed base path, e.g. + * the front controller, this difference becomes significant. It's the task + * of the user to handle both "" and "/". + * + * The value returned MUST be percent-encoded, but MUST NOT double-encode + * any characters. To determine what characters to encode, please refer to + * RFC 3986, Sections 2 and 3.3. + * + * As an example, if the value should include a slash ("/") not intended as + * delimiter between path segments, that value MUST be passed in encoded + * form (e.g., "%2F") to the instance. + * + * @see https://tools.ietf.org/html/rfc3986#section-2 + * @see https://tools.ietf.org/html/rfc3986#section-3.3 + * @return string The URI path. + */ + public function getPath() : string; + /** + * Retrieve the query string of the URI. + * + * If no query string is present, this method MUST return an empty string. + * + * The leading "?" character is not part of the query and MUST NOT be + * added. + * + * The value returned MUST be percent-encoded, but MUST NOT double-encode + * any characters. To determine what characters to encode, please refer to + * RFC 3986, Sections 2 and 3.4. + * + * As an example, if a value in a key/value pair of the query string should + * include an ampersand ("&") not intended as a delimiter between values, + * that value MUST be passed in encoded form (e.g., "%26") to the instance. + * + * @see https://tools.ietf.org/html/rfc3986#section-2 + * @see https://tools.ietf.org/html/rfc3986#section-3.4 + * @return string The URI query string. + */ + public function getQuery() : string; + /** + * Retrieve the fragment component of the URI. + * + * If no fragment is present, this method MUST return an empty string. + * + * The leading "#" character is not part of the fragment and MUST NOT be + * added. + * + * The value returned MUST be percent-encoded, but MUST NOT double-encode + * any characters. To determine what characters to encode, please refer to + * RFC 3986, Sections 2 and 3.5. + * + * @see https://tools.ietf.org/html/rfc3986#section-2 + * @see https://tools.ietf.org/html/rfc3986#section-3.5 + * @return string The URI fragment. + */ + public function getFragment() : string; + /** + * Return an instance with the specified scheme. + * + * This method MUST retain the state of the current instance, and return + * an instance that contains the specified scheme. + * + * Implementations MUST support the schemes "http" and "https" case + * insensitively, and MAY accommodate other schemes if required. + * + * An empty scheme is equivalent to removing the scheme. + * + * @param string $scheme The scheme to use with the new instance. + * @return static A new instance with the specified scheme. + * @throws \InvalidArgumentException for invalid or unsupported schemes. + */ + public function withScheme(string $scheme) : \FluentSmtpLib\Psr\Http\Message\UriInterface; + /** + * Return an instance with the specified user information. + * + * This method MUST retain the state of the current instance, and return + * an instance that contains the specified user information. + * + * Password is optional, but the user information MUST include the + * user; an empty string for the user is equivalent to removing user + * information. + * + * @param string $user The user name to use for authority. + * @param null|string $password The password associated with $user. + * @return static A new instance with the specified user information. + */ + public function withUserInfo(string $user, ?string $password = null) : \FluentSmtpLib\Psr\Http\Message\UriInterface; + /** + * Return an instance with the specified host. + * + * This method MUST retain the state of the current instance, and return + * an instance that contains the specified host. + * + * An empty host value is equivalent to removing the host. + * + * @param string $host The hostname to use with the new instance. + * @return static A new instance with the specified host. + * @throws \InvalidArgumentException for invalid hostnames. + */ + public function withHost(string $host) : \FluentSmtpLib\Psr\Http\Message\UriInterface; + /** + * Return an instance with the specified port. + * + * This method MUST retain the state of the current instance, and return + * an instance that contains the specified port. + * + * Implementations MUST raise an exception for ports outside the + * established TCP and UDP port ranges. + * + * A null value provided for the port is equivalent to removing the port + * information. + * + * @param null|int $port The port to use with the new instance; a null value + * removes the port information. + * @return static A new instance with the specified port. + * @throws \InvalidArgumentException for invalid ports. + */ + public function withPort(?int $port) : \FluentSmtpLib\Psr\Http\Message\UriInterface; + /** + * Return an instance with the specified path. + * + * This method MUST retain the state of the current instance, and return + * an instance that contains the specified path. + * + * The path can either be empty or absolute (starting with a slash) or + * rootless (not starting with a slash). Implementations MUST support all + * three syntaxes. + * + * If the path is intended to be domain-relative rather than path relative then + * it must begin with a slash ("/"). Paths not starting with a slash ("/") + * are assumed to be relative to some base path known to the application or + * consumer. + * + * Users can provide both encoded and decoded path characters. + * Implementations ensure the correct encoding as outlined in getPath(). + * + * @param string $path The path to use with the new instance. + * @return static A new instance with the specified path. + * @throws \InvalidArgumentException for invalid paths. + */ + public function withPath(string $path) : \FluentSmtpLib\Psr\Http\Message\UriInterface; + /** + * Return an instance with the specified query string. + * + * This method MUST retain the state of the current instance, and return + * an instance that contains the specified query string. + * + * Users can provide both encoded and decoded query characters. + * Implementations ensure the correct encoding as outlined in getQuery(). + * + * An empty query string value is equivalent to removing the query string. + * + * @param string $query The query string to use with the new instance. + * @return static A new instance with the specified query string. + * @throws \InvalidArgumentException for invalid query strings. + */ + public function withQuery(string $query) : \FluentSmtpLib\Psr\Http\Message\UriInterface; + /** + * Return an instance with the specified URI fragment. + * + * This method MUST retain the state of the current instance, and return + * an instance that contains the specified URI fragment. + * + * Users can provide both encoded and decoded fragment characters. + * Implementations ensure the correct encoding as outlined in getFragment(). + * + * An empty fragment value is equivalent to removing the fragment. + * + * @param string $fragment The fragment to use with the new instance. + * @return static A new instance with the specified fragment. + */ + public function withFragment(string $fragment) : \FluentSmtpLib\Psr\Http\Message\UriInterface; + /** + * Return the string representation as a URI reference. + * + * Depending on which components of the URI are present, the resulting + * string is either a full URI or relative reference according to RFC 3986, + * Section 4.1. The method concatenates the various components of the URI, + * using the appropriate delimiters: + * + * - If a scheme is present, it MUST be suffixed by ":". + * - If an authority is present, it MUST be prefixed by "//". + * - The path can be concatenated without delimiters. But there are two + * cases where the path has to be adjusted to make the URI reference + * valid as PHP does not allow to throw an exception in __toString(): + * - If the path is rootless and an authority is present, the path MUST + * be prefixed by "/". + * - If the path is starting with more than one "/" and no authority is + * present, the starting slashes MUST be reduced to one. + * - If a query is present, it MUST be prefixed by "?". + * - If a fragment is present, it MUST be prefixed by "#". + * + * @see http://tools.ietf.org/html/rfc3986#section-4.1 + * @return string + */ + public function __toString() : string; +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/psr/log/Psr/Log/AbstractLogger.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/psr/log/Psr/Log/AbstractLogger.php new file mode 100644 index 0000000..aea142b --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/psr/log/Psr/Log/AbstractLogger.php @@ -0,0 +1,121 @@ +log(\FluentSmtpLib\Psr\Log\LogLevel::EMERGENCY, $message, $context); + } + /** + * Action must be taken immediately. + * + * Example: Entire website down, database unavailable, etc. This should + * trigger the SMS alerts and wake you up. + * + * @param string $message + * @param mixed[] $context + * + * @return void + */ + public function alert($message, array $context = array()) + { + $this->log(\FluentSmtpLib\Psr\Log\LogLevel::ALERT, $message, $context); + } + /** + * Critical conditions. + * + * Example: Application component unavailable, unexpected exception. + * + * @param string $message + * @param mixed[] $context + * + * @return void + */ + public function critical($message, array $context = array()) + { + $this->log(\FluentSmtpLib\Psr\Log\LogLevel::CRITICAL, $message, $context); + } + /** + * Runtime errors that do not require immediate action but should typically + * be logged and monitored. + * + * @param string $message + * @param mixed[] $context + * + * @return void + */ + public function error($message, array $context = array()) + { + $this->log(\FluentSmtpLib\Psr\Log\LogLevel::ERROR, $message, $context); + } + /** + * Exceptional occurrences that are not errors. + * + * Example: Use of deprecated APIs, poor use of an API, undesirable things + * that are not necessarily wrong. + * + * @param string $message + * @param mixed[] $context + * + * @return void + */ + public function warning($message, array $context = array()) + { + $this->log(\FluentSmtpLib\Psr\Log\LogLevel::WARNING, $message, $context); + } + /** + * Normal but significant events. + * + * @param string $message + * @param mixed[] $context + * + * @return void + */ + public function notice($message, array $context = array()) + { + $this->log(\FluentSmtpLib\Psr\Log\LogLevel::NOTICE, $message, $context); + } + /** + * Interesting events. + * + * Example: User logs in, SQL logs. + * + * @param string $message + * @param mixed[] $context + * + * @return void + */ + public function info($message, array $context = array()) + { + $this->log(\FluentSmtpLib\Psr\Log\LogLevel::INFO, $message, $context); + } + /** + * Detailed debug information. + * + * @param string $message + * @param mixed[] $context + * + * @return void + */ + public function debug($message, array $context = array()) + { + $this->log(\FluentSmtpLib\Psr\Log\LogLevel::DEBUG, $message, $context); + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/psr/log/Psr/Log/InvalidArgumentException.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/psr/log/Psr/Log/InvalidArgumentException.php new file mode 100644 index 0000000..4a77315 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/psr/log/Psr/Log/InvalidArgumentException.php @@ -0,0 +1,7 @@ +logger = $logger; + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/psr/log/Psr/Log/LoggerInterface.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/psr/log/Psr/Log/LoggerInterface.php new file mode 100644 index 0000000..88e9188 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/psr/log/Psr/Log/LoggerInterface.php @@ -0,0 +1,117 @@ +log(\FluentSmtpLib\Psr\Log\LogLevel::EMERGENCY, $message, $context); + } + /** + * Action must be taken immediately. + * + * Example: Entire website down, database unavailable, etc. This should + * trigger the SMS alerts and wake you up. + * + * @param string $message + * @param array $context + * + * @return void + */ + public function alert($message, array $context = array()) + { + $this->log(\FluentSmtpLib\Psr\Log\LogLevel::ALERT, $message, $context); + } + /** + * Critical conditions. + * + * Example: Application component unavailable, unexpected exception. + * + * @param string $message + * @param array $context + * + * @return void + */ + public function critical($message, array $context = array()) + { + $this->log(\FluentSmtpLib\Psr\Log\LogLevel::CRITICAL, $message, $context); + } + /** + * Runtime errors that do not require immediate action but should typically + * be logged and monitored. + * + * @param string $message + * @param array $context + * + * @return void + */ + public function error($message, array $context = array()) + { + $this->log(\FluentSmtpLib\Psr\Log\LogLevel::ERROR, $message, $context); + } + /** + * Exceptional occurrences that are not errors. + * + * Example: Use of deprecated APIs, poor use of an API, undesirable things + * that are not necessarily wrong. + * + * @param string $message + * @param array $context + * + * @return void + */ + public function warning($message, array $context = array()) + { + $this->log(\FluentSmtpLib\Psr\Log\LogLevel::WARNING, $message, $context); + } + /** + * Normal but significant events. + * + * @param string $message + * @param array $context + * + * @return void + */ + public function notice($message, array $context = array()) + { + $this->log(\FluentSmtpLib\Psr\Log\LogLevel::NOTICE, $message, $context); + } + /** + * Interesting events. + * + * Example: User logs in, SQL logs. + * + * @param string $message + * @param array $context + * + * @return void + */ + public function info($message, array $context = array()) + { + $this->log(\FluentSmtpLib\Psr\Log\LogLevel::INFO, $message, $context); + } + /** + * Detailed debug information. + * + * @param string $message + * @param array $context + * + * @return void + */ + public function debug($message, array $context = array()) + { + $this->log(\FluentSmtpLib\Psr\Log\LogLevel::DEBUG, $message, $context); + } + /** + * Logs with an arbitrary level. + * + * @param mixed $level + * @param string $message + * @param array $context + * + * @return void + * + * @throws \Psr\Log\InvalidArgumentException + */ + public abstract function log($level, $message, array $context = array()); +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/psr/log/Psr/Log/NullLogger.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/psr/log/Psr/Log/NullLogger.php new file mode 100644 index 0000000..397285b --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/psr/log/Psr/Log/NullLogger.php @@ -0,0 +1,30 @@ +logger) { }` + * blocks. + */ +class NullLogger extends \FluentSmtpLib\Psr\Log\AbstractLogger +{ + /** + * Logs with an arbitrary level. + * + * @param mixed $level + * @param string $message + * @param array $context + * + * @return void + * + * @throws \Psr\Log\InvalidArgumentException + */ + public function log($level, $message, array $context = array()) + { + // noop + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/psr/log/Psr/Log/Test/DummyTest.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/psr/log/Psr/Log/Test/DummyTest.php new file mode 100644 index 0000000..107fb09 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/psr/log/Psr/Log/Test/DummyTest.php @@ -0,0 +1,18 @@ + ". + * + * Example ->error('Foo') would yield "error Foo". + * + * @return string[] + */ + public abstract function getLogs(); + public function testImplements() + { + $this->assertInstanceOf('FluentSmtpLib\\Psr\\Log\\LoggerInterface', $this->getLogger()); + } + /** + * @dataProvider provideLevelsAndMessages + */ + public function testLogsAtAllLevels($level, $message) + { + $logger = $this->getLogger(); + $logger->{$level}($message, array('user' => 'Bob')); + $logger->log($level, $message, array('user' => 'Bob')); + $expected = array($level . ' message of level ' . $level . ' with context: Bob', $level . ' message of level ' . $level . ' with context: Bob'); + $this->assertEquals($expected, $this->getLogs()); + } + public function provideLevelsAndMessages() + { + return array(\FluentSmtpLib\Psr\Log\LogLevel::EMERGENCY => array(\FluentSmtpLib\Psr\Log\LogLevel::EMERGENCY, 'message of level emergency with context: {user}'), \FluentSmtpLib\Psr\Log\LogLevel::ALERT => array(\FluentSmtpLib\Psr\Log\LogLevel::ALERT, 'message of level alert with context: {user}'), \FluentSmtpLib\Psr\Log\LogLevel::CRITICAL => array(\FluentSmtpLib\Psr\Log\LogLevel::CRITICAL, 'message of level critical with context: {user}'), \FluentSmtpLib\Psr\Log\LogLevel::ERROR => array(\FluentSmtpLib\Psr\Log\LogLevel::ERROR, 'message of level error with context: {user}'), \FluentSmtpLib\Psr\Log\LogLevel::WARNING => array(\FluentSmtpLib\Psr\Log\LogLevel::WARNING, 'message of level warning with context: {user}'), \FluentSmtpLib\Psr\Log\LogLevel::NOTICE => array(\FluentSmtpLib\Psr\Log\LogLevel::NOTICE, 'message of level notice with context: {user}'), \FluentSmtpLib\Psr\Log\LogLevel::INFO => array(\FluentSmtpLib\Psr\Log\LogLevel::INFO, 'message of level info with context: {user}'), \FluentSmtpLib\Psr\Log\LogLevel::DEBUG => array(\FluentSmtpLib\Psr\Log\LogLevel::DEBUG, 'message of level debug with context: {user}')); + } + /** + * @expectedException \Psr\Log\InvalidArgumentException + */ + public function testThrowsOnInvalidLevel() + { + $logger = $this->getLogger(); + $logger->log('invalid level', 'Foo'); + } + public function testContextReplacement() + { + $logger = $this->getLogger(); + $logger->info('{Message {nothing} {user} {foo.bar} a}', array('user' => 'Bob', 'foo.bar' => 'Bar')); + $expected = array('info {Message {nothing} Bob Bar a}'); + $this->assertEquals($expected, $this->getLogs()); + } + public function testObjectCastToString() + { + if (\method_exists($this, 'createPartialMock')) { + $dummy = $this->createPartialMock('FluentSmtpLib\\Psr\\Log\\Test\\DummyTest', array('__toString')); + } else { + $dummy = $this->getMock('FluentSmtpLib\\Psr\\Log\\Test\\DummyTest', array('__toString')); + } + $dummy->expects($this->once())->method('__toString')->will($this->returnValue('DUMMY')); + $this->getLogger()->warning($dummy); + $expected = array('warning DUMMY'); + $this->assertEquals($expected, $this->getLogs()); + } + public function testContextCanContainAnything() + { + $closed = \fopen('php://memory', 'r'); + \fclose($closed); + $context = array('bool' => \true, 'null' => null, 'string' => 'Foo', 'int' => 0, 'float' => 0.5, 'nested' => array('with object' => new \FluentSmtpLib\Psr\Log\Test\DummyTest()), 'object' => new \DateTime(), 'resource' => \fopen('php://memory', 'r'), 'closed' => $closed); + $this->getLogger()->warning('Crazy context data', $context); + $expected = array('warning Crazy context data'); + $this->assertEquals($expected, $this->getLogs()); + } + public function testContextExceptionKeyCanBeExceptionOrOtherValues() + { + $logger = $this->getLogger(); + $logger->warning('Random message', array('exception' => 'oops')); + $logger->critical('Uncaught Exception!', array('exception' => new \LogicException('Fail'))); + $expected = array('warning Random message', 'critical Uncaught Exception!'); + $this->assertEquals($expected, $this->getLogs()); + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/psr/log/Psr/Log/Test/TestLogger.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/psr/log/Psr/Log/Test/TestLogger.php new file mode 100644 index 0000000..e10aa74 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/psr/log/Psr/Log/Test/TestLogger.php @@ -0,0 +1,132 @@ + $level, 'message' => $message, 'context' => $context]; + $this->recordsByLevel[$record['level']][] = $record; + $this->records[] = $record; + } + public function hasRecords($level) + { + return isset($this->recordsByLevel[$level]); + } + public function hasRecord($record, $level) + { + if (\is_string($record)) { + $record = ['message' => $record]; + } + return $this->hasRecordThatPasses(function ($rec) use($record) { + if ($rec['message'] !== $record['message']) { + return \false; + } + if (isset($record['context']) && $rec['context'] !== $record['context']) { + return \false; + } + return \true; + }, $level); + } + public function hasRecordThatContains($message, $level) + { + return $this->hasRecordThatPasses(function ($rec) use($message) { + return \strpos($rec['message'], $message) !== \false; + }, $level); + } + public function hasRecordThatMatches($regex, $level) + { + return $this->hasRecordThatPasses(function ($rec) use($regex) { + return \preg_match($regex, $rec['message']) > 0; + }, $level); + } + public function hasRecordThatPasses(callable $predicate, $level) + { + if (!isset($this->recordsByLevel[$level])) { + return \false; + } + foreach ($this->recordsByLevel[$level] as $i => $rec) { + if (\call_user_func($predicate, $rec, $i)) { + return \true; + } + } + return \false; + } + public function __call($method, $args) + { + if (\preg_match('/(.*)(Debug|Info|Notice|Warning|Error|Critical|Alert|Emergency)(.*)/', $method, $matches) > 0) { + $genericMethod = $matches[1] . ('Records' !== $matches[3] ? 'Record' : '') . $matches[3]; + $level = \strtolower($matches[2]); + if (\method_exists($this, $genericMethod)) { + $args[] = $level; + return \call_user_func_array([$this, $genericMethod], $args); + } + } + throw new \BadMethodCallException('Call to undefined method ' . \get_class($this) . '::' . $method . '()'); + } + public function reset() + { + $this->records = []; + $this->recordsByLevel = []; + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/ralouphie/getallheaders/src/getallheaders.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/ralouphie/getallheaders/src/getallheaders.php new file mode 100644 index 0000000..c7285a5 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/ralouphie/getallheaders/src/getallheaders.php @@ -0,0 +1,46 @@ + 'Content-Type', + 'CONTENT_LENGTH' => 'Content-Length', + 'CONTENT_MD5' => 'Content-Md5', + ); + + foreach ($_SERVER as $key => $value) { + if (substr($key, 0, 5) === 'HTTP_') { + $key = substr($key, 5); + if (!isset($copy_server[$key]) || !isset($_SERVER[$key])) { + $key = str_replace(' ', '-', ucwords(strtolower(str_replace('_', ' ', $key)))); + $headers[$key] = $value; + } + } elseif (isset($copy_server[$key])) { + $headers[$copy_server[$key]] = $value; + } + } + + if (!isset($headers['Authorization'])) { + if (isset($_SERVER['REDIRECT_HTTP_AUTHORIZATION'])) { + $headers['Authorization'] = $_SERVER['REDIRECT_HTTP_AUTHORIZATION']; + } elseif (isset($_SERVER['PHP_AUTH_USER'])) { + $basic_pass = isset($_SERVER['PHP_AUTH_PW']) ? $_SERVER['PHP_AUTH_PW'] : ''; + $headers['Authorization'] = 'Basic ' . base64_encode($_SERVER['PHP_AUTH_USER'] . ':' . $basic_pass); + } elseif (isset($_SERVER['PHP_AUTH_DIGEST'])) { + $headers['Authorization'] = $_SERVER['PHP_AUTH_DIGEST']; + } + } + + return $headers; + } + +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/symfony/deprecation-contracts/function.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/symfony/deprecation-contracts/function.php new file mode 100644 index 0000000..7531aca --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/build/vendor/symfony/deprecation-contracts/function.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +if (!\function_exists('FluentSmtpLib\\trigger_deprecation')) { + /** + * Triggers a silenced deprecation notice. + * + * @param string $package The name of the Composer package that is triggering the deprecation + * @param string $version The version of the package that introduced the deprecation + * @param string $message The message of the deprecation + * @param mixed ...$args Values to insert in the message using printf() formatting + * + * @author Nicolas Grekas + */ + function trigger_deprecation(string $package, string $version, string $message, ...$args) : void + { + @\trigger_error(($package || $version ? "Since {$package} {$version}: " : '') . ($args ? \vsprintf($message, $args) : $message), \E_USER_DEPRECATED); + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/composer.json b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/composer.json new file mode 100644 index 0000000..16d6d2c --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/composer.json @@ -0,0 +1,46 @@ +{ + "name": "fluent-smtp/fluent-smtp-3rd-party", + "description": "FluentSMTP 3rd pary Libs", + "license": "Apache-2.0", + "type": "wordpress-plugin", + "homepage": "https://fluentsmtp.com", + "require-dev": {}, + "require": { + "google/apiclient": "^2.14.0" + }, + "extra": { + "google/apiclient-services": [ + "Gmail" + ] + }, + "config": { + "platform": { + "php": "7.4.33" + }, + "sort-packages": true + }, + "autoload": { + }, + "scripts": { + "post-install-cmd": [ + "@prefix-dependencies" + ], + "post-update-cmd": [ + "@prefix-dependencies" + ], + "prefix-dependencies": [ + "mkdir -p php-scoper && echo '{ \"require\": { \"humbug/php-scoper\": \"^0.13.0\" }, \"config\": { \"platform\": { \"php\": \"7.4\" }, \"allow-plugins\": { \"composer/package-versions-deprecated\": true } }, \"minimum-stability\": \"dev\", \"prefer-stable\": true }' > php-scoper/composer.json", + "@composer --working-dir=php-scoper install", + "@php -dxdebug.mode=off php-scoper/vendor/bin/php-scoper add --output-dir=./build/vendor --force --quiet", + "rm -rf php-scoper", + "@autoload-third-party" + ], + "autoload-third-party": [ + "echo '{ \"autoload\": { \"classmap\": [\"\"] } }' > build/composer.json", + "@composer --working-dir=build dump-autoload --classmap-authoritative --no-interaction", + "cp vendor/composer/autoload_files.php build/vendor/composer", + "rm -rf vendor && rm -rf composer.lock", + "rm -rf build/vendor/scoper-autoload.php" + ] + } +} diff --git a/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/index.php b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/index.php new file mode 100644 index 0000000..94899e6 --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/google-api-client/index.php @@ -0,0 +1,9 @@ + 'FluentSmtpLib', + 'finders' => array( + + // General dependencies, except Google API services. + Finder::create() + ->files() + ->ignoreVCS( true ) + ->notName( '/LICENSE|.*\\.md|.*\\.dist|Makefile|composer\\.(json|lock)/' ) + ->exclude( + array( + 'doc', + 'test', + 'test_old', + 'tests', + 'Tests', + 'vendor-bin', + ) + ) + ->path( '#^firebase/php-jwt#' ) + ->path( '#^google/apiclient/#' ) + ->path( '#^google/auth/#' ) + ->path( '#^guzzlehttp/#' ) + ->path( '#^monolog/#' ) + ->path( '#^phpseclib/phpseclib/phpseclib/#' ) + ->path( '#^psr/#' ) + ->path( '#^ralouphie/#' ) + ->path( '#^react/#' ) + ->path( '#^symfony/#' ) + ->path( '#^true/#' ) + ->in( 'vendor' ), + + // Google API service infrastructure classes. + Finder::create() + ->files() + ->ignoreVCS( true ) + ->notName( '/LICENSE|.*\\.md|.*\\.dist|Makefile|composer\\.json|composer\\.lock/' ) + ->exclude( + array( + 'doc', + 'test', + 'test_old', + 'tests', + 'Tests', + 'vendor-bin', + ) + ) + ->path( "#^google/apiclient-services/src/($google_services)/#" ) + ->in( 'vendor' ), + + // Google API service entry classes. + Finder::create() + ->files() + ->ignoreVCS( true ) + ->name( "#^($google_services)\.php$#" ) + ->depth( '== 0' ) + ->in( 'vendor/google/apiclient-services/src' ), + Finder::create() + ->files() + ->ignoreVCS( true ) + ->name( '#^autoload.php$#' ) + ->depth( '== 0' ) + ->in( 'vendor/google/apiclient-services' ), + ), + 'files-whitelist' => array( + // This dependency is a global function which should remain global. + 'vendor/ralouphie/getallheaders/src/getallheaders.php', + ), + 'patchers' => array( + function ( $file_path, $prefix, $contents ) { + // Avoid prefixing the `static` keyword in some places. + $contents = str_replace( "\\$prefix\\static", 'static', $contents ); + + if ( preg_match( '#google/apiclient/src/Google/Http/REST\.php$#', $file_path ) ) { + $contents = str_replace( "\\$prefix\\intVal", '\\intval', $contents ); + } + if ( false !== strpos( $file_path, 'vendor/google/apiclient/' ) || false !== strpos( $file_path, 'vendor/google/auth/' ) ) { + // Use modified prefix just for this patch. + $s_prefix = str_replace( '\\', '\\\\', $prefix ); + $contents = str_replace( "'\\\\GuzzleHttp\\\\ClientInterface", "'\\\\" . $s_prefix . '\\\\GuzzleHttp\\\\ClientInterface', $contents ); + $contents = str_replace( '"\\\\GuzzleHttp\\\\ClientInterface', '"\\\\' . $s_prefix . '\\\\GuzzleHttp\\\\ClientInterface', $contents ); + $contents = str_replace( "'GuzzleHttp\\\\ClientInterface", "'" . $s_prefix . '\\\\GuzzleHttp\\\\ClientInterface', $contents ); + $contents = str_replace( '"GuzzleHttp\\\\ClientInterface', '"' . $s_prefix . '\\\\GuzzleHttp\\\\ClientInterface', $contents ); + } + if ( false !== strpos( $file_path, 'vendor/google/apiclient/' ) ) { + $contents = str_replace( "'Google_", "'" . $prefix . '\Google_', $contents ); + $contents = str_replace( '"Google_', '"' . $prefix . '\Google_', $contents ); + } + + if ( false !== strpos( $file_path, 'phpseclib' ) ) { + // Use modified prefix just for this patch. + $s_prefix = str_replace( '\\', '\\\\', $prefix ); + $contents = str_replace( "'phpseclib3\\\\", "'\\\\" . $s_prefix . '\\\\phpseclib3\\\\', $contents ); + $contents = str_replace( "'\\\\phpseclib3", "'\\\\" . $s_prefix . '\\\\phpseclib3', $contents ); + } + + if ( + // Bootstrap files polyfill global functions using namespaced implementations. + preg_match( '#vendor/symfony/polyfill-.*/bootstrap\.php$#', $file_path ) + // The classes under Resources/stubs polyfill classes in the global namespace loaded via classmap. + || preg_match( '#vendor/symfony/polyfill-.*/Resources/stubs/.*\.php$#', $file_path ) + ) { + $contents = str_replace( "namespace $prefix;", "/* namespace $prefix intentionally removed */", $contents ); + } + return $contents; + }, + ), + 'whitelist' => array(), + 'whitelist-global-constants' => false, + 'whitelist-global-classes' => false, + 'whitelist-global-functions' => false, +); diff --git a/wp-content/plugins/fluent-smtp/includes/libs/index.php b/wp-content/plugins/fluent-smtp/includes/libs/index.php new file mode 100644 index 0000000..f0f663c --- /dev/null +++ b/wp-content/plugins/fluent-smtp/includes/libs/index.php @@ -0,0 +1 @@ +\n" +"Language-Team: \n" +"Language: \n" +"Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Generator: Loco https://localise.biz/\n" +"X-Loco-Version: 2.5.0; wp-5.6\n" +"X-Domain: fluent-smtp" + +#: app/Services/TransStrings.php:11 +msgid " - Simulated" +msgstr "" + +#: app/Http/Controllers/LoggerController.php:109 +msgid " And %d emails are failed to init the emails" +msgstr "" + +#: app/Http/Controllers/LoggerController.php:105 +msgid " But %d emails are reported to failed to send." +msgstr "" + +#: app/Services/TransStrings.php:12 +msgid " connection." +msgstr "" + +#: app/Services/Mailer/Providers/config.php:71 +msgid " for how to configure Amazon SES with FluentSMTP." +msgstr "" + +#: app/Services/Mailer/Providers/config.php:29 +msgid " for how to configure any SMTP with FluentSMTP." +msgstr "" + +#: app/Services/Mailer/Providers/config.php:176 +msgid " for how to configure Elastic Email with FluentSMTP." +msgstr "" + +#: app/Services/Mailer/Providers/config.php:88 +msgid " for how to configure Mailgun with FluentSMTP." +msgstr "" + +#: app/Services/Mailer/Providers/config.php:144 +msgid " for how to configure Netcore (formerly Pepipost) with FluentSMTP." +msgstr "" + +#: app/Services/Mailer/Providers/config.php:161 +msgid " for how to configure Postmark with FluentSMTP." +msgstr "" + +#: app/Services/Mailer/Providers/config.php:102 +msgid " for how to configure SendGrid with FluentSMTP." +msgstr "" + +#: app/Services/Mailer/Providers/config.php:116 +msgid " for how to configure Sendinblue with FluentSMTP." +msgstr "" + +#: app/Services/Mailer/Providers/config.php:130 +msgid " for how to configure SparkPost with FluentSMTP." +msgstr "" + +#: app/Services/Mailer/Providers/config.php:242 +msgid " for how to configure ToSend with FluentSMTP." +msgstr "" + +#: app/Services/TransStrings.php:13 +msgid " in the " +msgstr "" + +#: app/Services/TransStrings.php:14 +msgid " option in the Google Cloud Project." +msgstr "" + +#: app/Hooks/Handlers/AdminMenuHandler.php:232 +#, php-format +msgid "%1$s is a free plugin & it will be always free %2$s. %3$s" +msgstr "" + +#: app/Http/Controllers/LoggerController.php:46 +#, php-format +msgid "%s deleted successfully." +msgstr "" + +#: app/Services/TransStrings.php:361 +msgid "" +"(By default, the TLS encryption would be used if the server supports it. On " +"some servers, it could be a problem and may need to be disabled.)" +msgstr "" + +#: app/Services/TransStrings.php:15 +msgid "(Default: US East(N.Virginia) / us - east - 1)" +msgstr "" + +#: app/Services/TransStrings.php:352 +msgid "" +"(If you need to provide your SMTP server's credentials (username and " +"password) enable the authentication, in most cases this is required.)" +msgstr "" + +#: app/Hooks/Handlers/AdminMenuHandler.php:234 +msgid "(Learn why it's free)" +msgstr "" + +#: app/Services/TransStrings.php:16 +msgid "" +"(Optional) Share Non - Sensitive Data. It will help us to improve the " +"integrations" +msgstr "" + +#: app/Services/TransStrings.php:17 +msgid "(Re Authentication Required)" +msgstr "" + +#: app/Services/TransStrings.php:18 +msgid "*** It is very important to put " +msgstr "" + +#: app/Services/NotificationHelper.php:408 +msgid "**Email Subject:** " +msgstr "" + +#: app/Services/NotificationHelper.php:409 +msgid "**Error Message:** ```" +msgstr "" + +#: app/Services/NotificationHelper.php:406 +msgid "**Sending Driver:** " +msgstr "" + +#: app/Services/NotificationHelper.php:407 +msgid "**To Email Address:** " +msgstr "" + +#: app/Services/NotificationHelper.php:405 +msgid "**Website URL:** " +msgstr "" + +#: app/Hooks/Handlers/AdminMenuHandler.php:82 +msgid "" +". It's compatible with various SMTP services, including Amazon SES, SendGrid," +" MailGun, ElasticEmail, SendInBlue, Google, Microsoft, and others, providing " +"you with a wide range of options for your email needs." +msgstr "" + +#: app/Services/NotificationHelper.php:278 +#: app/Services/NotificationHelper.php:338 +#: app/Services/NotificationHelper.php:402 +#, php-format +msgid "[%s] Failed to send email" +msgstr "" + +#: app/Services/NotificationHelper.php:410 +msgid "[View Failed Email(s)](" +msgstr "" + +#: app/Services/TransStrings.php:318 +msgid "__DEFAULT_MAIl_WARNING" +msgstr "" + +#: app/Services/TransStrings.php:19 +msgid "A name for the connection." +msgstr "" + +#: app/Services/TransStrings.php:22 +msgid "About" +msgstr "" + +#: app/Services/TransStrings.php:23 +msgid "" +"Access Data: Active SMTP Connection Provider, installed plugin names, php & " +"mysql version" +msgstr "" + +#: app/Services/TransStrings.php:24 +msgid "Access Key" +msgstr "" + +#: app/Services/Mailer/Providers/AmazonSes/ValidatorTrait.php:20 +msgid "Access key is required." +msgstr "" + +#: app/Services/TransStrings.php:25 +msgid "Access Keys in Config File" +msgstr "" + +#: app/Services/TransStrings.php:26 +msgid "Access Token" +msgstr "" + +#: app/Services/TransStrings.php:27 +msgid "Actions" +msgstr "" + +#: app/Services/TransStrings.php:370 +msgid "activate " +msgstr "" + +#: app/Services/TransStrings.php:28 +msgid "Activation Pin" +msgstr "" + +#: app/Services/TransStrings.php:29 +msgid "Active Connections:" +msgstr "" + +#: app/Services/TransStrings.php:30 +msgid "Active Email Connections" +msgstr "" + +#: app/Services/TransStrings.php:31 +msgid "Active Senders:" +msgstr "" + +#: app/Services/TransStrings.php:32 +msgid "Add" +msgstr "" + +#: app/Services/TransStrings.php:33 +msgid "Add Additional Senders" +msgstr "" + +#: app/Services/TransStrings.php:34 +msgid "Add Another Connection" +msgstr "" + +#: app/Services/TransStrings.php:35 +msgid "Add Multi-Part Plain Text for HTML Emails (beta)" +msgstr "" + +#: app/Services/Mailer/Providers/config.php:68 +msgid "Africa (Cape Town)" +msgstr "" + +#: app/Services/TransStrings.php:36 +msgid "After 1 Year" +msgstr "" + +#: app/Services/TransStrings.php:37 +msgid "After 14 Days" +msgstr "" + +#: app/Services/TransStrings.php:38 +msgid "After 2 Years" +msgstr "" + +#: app/Services/TransStrings.php:39 +msgid "After 30 Days" +msgstr "" + +#: app/Services/TransStrings.php:40 +msgid "After 6 Months" +msgstr "" + +#: app/Services/TransStrings.php:41 +msgid "After 60 Days" +msgstr "" + +#: app/Services/TransStrings.php:42 +msgid "After 7 Days" +msgstr "" + +#: app/Services/TransStrings.php:43 +msgid "After 90 Days" +msgstr "" + +#: app/Services/TransStrings.php:44 +msgid "Alerts" +msgstr "" + +#: app/Hooks/Handlers/AdminMenuHandler.php:420 +msgid "All" +msgstr "" + +#: app/Services/TransStrings.php:45 +msgid "All Statuses" +msgstr "" + +#: app/Services/TransStrings.php:46 +msgid "All Time" +msgstr "" + +#: app/Services/Mailer/Providers/config.php:33 +msgid "Amazon SES" +msgstr "" + +#: app/Services/TransStrings.php:315 +msgid "" +"Another connection with same email address exists. This connection will " +"replace that connection" +msgstr "" + +#: app/Services/TransStrings.php:20 +msgid "API Key" +msgstr "" + +#: app/Services/Mailer/Providers/Postmark/ValidatorTrait.php:20 +#: app/Services/Mailer/Providers/ElasticMail/ValidatorTrait.php:19 +#: app/Services/Mailer/Providers/SparkPost/ValidatorTrait.php:20 +#: app/Services/Mailer/Providers/SendGrid/ValidatorTrait.php:20 +#: app/Services/Mailer/Providers/SendInBlue/ValidatorTrait.php:19 +#: app/Services/Mailer/Providers/ToSend/ValidatorTrait.php:23 +#: app/Services/Mailer/Providers/PepiPost/ValidatorTrait.php:20 +#: app/Services/Mailer/Providers/TransMail/ValidatorTrait.php:19 +#: app/Services/Mailer/Providers/Smtp2Go/ValidatorTrait.php:20 +#: app/Services/Mailer/Providers/Mailgun/ValidatorTrait.php:19 +msgid "Api key is required." +msgstr "" + +#: app/Services/TransStrings.php:21 +msgid "API Token" +msgstr "" + +#: app/Http/Controllers/PushoverController.php:20 +msgid "API Token is required" +msgstr "" + +#: app/Services/TransStrings.php:47 +msgid "App Callback URL(Use this URL to your APP)" +msgstr "" + +#: app/Services/TransStrings.php:48 +msgid "Application Client ID" +msgstr "" + +#: app/Services/Mailer/Providers/Gmail/Handler.php:117 +#: app/Services/Mailer/Providers/Outlook/Handler.php:82 +msgid "Application Client ID is required." +msgstr "" + +#: app/Services/TransStrings.php:49 +msgid "Application Client Secret" +msgstr "" + +#: app/Services/Mailer/Providers/Gmail/Handler.php:121 +#: app/Services/Mailer/Providers/Outlook/Handler.php:86 +msgid "Application Client Secret key is required." +msgstr "" + +#: app/Services/TransStrings.php:50 +msgid "Application Keys in Config File" +msgstr "" + +#: app/Services/TransStrings.php:51 +msgid "Apply" +msgstr "" + +#: app/Services/TransStrings.php:52 +msgid "" +"Are you sure you want to deactivate and remove settings for this channel?" +msgstr "" + +#: app/Services/TransStrings.php:53 +msgid "Are you sure you want to disconnect {title} notifications?" +msgstr "" + +#: app/Services/TransStrings.php:54 +msgid "Are you sure you want to remove this email address?" +msgstr "" + +#: app/Services/TransStrings.php:55 +msgid "Are you sure, you want to delete all the logs?" +msgstr "" + +#: app/Services/Mailer/Providers/config.php:59 +msgid "Asia Pacific (Mumbai)" +msgstr "" + +#: app/Services/Mailer/Providers/config.php:64 +msgid "Asia Pacific (Osaka)" +msgstr "" + +#: app/Services/Mailer/Providers/config.php:60 +msgid "Asia Pacific (Seoul)" +msgstr "" + +#: app/Services/Mailer/Providers/config.php:61 +msgid "Asia Pacific (Singapore)" +msgstr "" + +#: app/Services/Mailer/Providers/config.php:62 +msgid "Asia Pacific (Sydney)" +msgstr "" + +#: app/Services/Mailer/Providers/config.php:63 +msgid "Asia Pacific (Tokyo)" +msgstr "" + +#: app/Services/TransStrings.php:56 +msgid "Attachments" +msgstr "" + +#: app/Services/TransStrings.php:57 +msgid "Authenticate with Google & Get Access Token" +msgstr "" + +#: app/Services/TransStrings.php:58 +msgid "Authenticate with Office365 & Get Access Token" +msgstr "" + +#: app/Services/TransStrings.php:59 +msgid "Authentication" +msgstr "" + +#: app/Services/TransStrings.php:60 +msgid "Authorized Redirect URI" +msgstr "" + +#: app/Services/TransStrings.php:61 +msgid "Authorized Redirect URIs" +msgstr "" + +#: app/views/admin/digest_email.php:180 +msgid "Awesome! no failures! 🎉" +msgstr "" + +#: app/Http/Controllers/TelegramController.php:46 +msgid "Awesome! Please activate the connection from your telegram account." +msgstr "" + +#: app/Services/TransStrings.php:62 +msgid "Awesome! Please check your email inbox and confirm your subscription." +msgstr "" + +#: app/Http/Controllers/SlackController.php:53 +msgid "Awesome! You are redirecting to slack" +msgstr "" + +#: app/Services/Mailer/Providers/config.php:67 +msgid "AWS GovCloud (US)" +msgstr "" + +#: app/Services/TransStrings.php:63 +msgid "Back to Alerts" +msgstr "" + +#: app/Services/TransStrings.php:64 +msgid "Best WP DataTables Plugin for WordPress" +msgstr "" + +#: app/Services/TransStrings.php:65 +msgid "Bulk Action" +msgstr "" + +#: app/Services/TransStrings.php:66 +msgid "By Date" +msgstr "" + +#: app/Services/TransStrings.php:67 +msgid "" +"By disabling encryption, your API key will be stored in plain text in the " +"database. This is not recommended for security reasons. Enable only if your " +"security plugin rotate WP SALTS frequently." +msgstr "" + +#: app/Services/TransStrings.php:68 +msgid "" +"By disabling encryption, your Application Client Secret will be stored in " +"plain text in the database. This is not recommended for security reasons. " +"Enable only if your security plugin rotate WP SALTS frequently." +msgstr "" + +#: app/Services/TransStrings.php:69 +msgid "" +"By disabling encryption, your Secret Key will be stored in plain text in the " +"database. This is not recommended for security reasons. Enable only if your " +"security plugin rotate WP SALTS frequently." +msgstr "" + +#: app/Services/Mailer/Providers/config.php:52 +msgid "Canada (Central)" +msgstr "" + +#: app/Services/TransStrings.php:70 +msgid "Cancel" +msgstr "" + +#: app/Services/TransStrings.php:371 +msgid "cancel" +msgstr "" + +#: app/Services/TransStrings.php:372 +msgid "change" +msgstr "" + +#: app/Services/TransStrings.php:71 +msgid "Channel" +msgstr "" + +#: app/Http/Controllers/DiscordController.php:35 +msgid "Channel Name required" +msgstr "" + +#: app/Services/TransStrings.php:329 +msgid "check the documentation" +msgstr "" + +#: app/Services/TransStrings.php:373 +msgid "check the documentation first to create API keys at Microsoft" +msgstr "" + +#: app/Services/Mailer/Providers/config.php:69 +msgid "China (Ningxia)" +msgstr "" + +#: app/Services/TransStrings.php:374 +msgid "click here" +msgstr "" + +#: app/Services/TransStrings.php:72 +msgid "Close" +msgstr "" + +#: app/Services/TransStrings.php:73 +msgid "Community" +msgstr "" + +#: app/Services/TransStrings.php:74 +msgid "Configure" +msgstr "" + +#: app/Services/TransStrings.php:75 +msgid "Configure Discord Notification" +msgstr "" + +#: app/Hooks/Handlers/AdminMenuHandler.php:291 +msgid "Configure FluentSMTP" +msgstr "" + +#: app/Services/TransStrings.php:76 +msgid "Configure Pushover Notification" +msgstr "" + +#: app/Services/TransStrings.php:375 +msgid "confirm" +msgstr "" + +#: app/views/admin/email_html.php:27 +msgid "Congrats, test email was sent successfully!" +msgstr "" + +#: app/Services/TransStrings.php:77 +msgid "Connect With Your Email Providers" +msgstr "" + +#: app/Services/TransStrings.php:78 +msgid "Connected" +msgstr "" + +#: app/Services/TransStrings.php:82 +msgid "Connection deleted Successfully." +msgstr "" + +#: app/Services/TransStrings.php:79 +msgid "Connection Details" +msgstr "" + +#: app/views/admin/ses_connection_info.php:4 +#: app/views/admin/tosend_mailer_connection_info.php:3 +msgid "Connection Error: " +msgstr "" + +#: app/Services/TransStrings.php:80 +msgid "Connection Name " +msgstr "" + +#: app/Services/TransStrings.php:81 +msgid "Connection Provider" +msgstr "" + +#: app/Http/Controllers/TelegramController.php:81 +msgid "Connection successful" +msgstr "" + +#: app/views/admin/ses_connection_info.php:9 +#: app/views/admin/general_connection_info.php:4 +#: app/views/admin/tosend_mailer_connection_info.php:8 +msgid "Connection Type" +msgstr "" + +#: app/Services/TransStrings.php:83 +msgid "Continue" +msgstr "" + +#: app/Services/TransStrings.php:84 +msgid "Continue to Slack" +msgstr "" + +#: app/Services/TransStrings.php:331 +msgid "contribute on GitHub" +msgstr "" + +#: app/Services/TransStrings.php:85 +msgid "Contributors" +msgstr "" + +#: app/Services/TransStrings.php:376 +msgid "copy" +msgstr "" + +#: app/Services/TransStrings.php:86 +msgid "Create API Key." +msgstr "" + +#: app/Services/TransStrings.php:383 +msgid "Credential Verification Failed. Please check your inputs" +msgstr "" + +#: app/Services/TransStrings.php:87 +msgid "Cumulative" +msgstr "" + +#: app/Services/TransStrings.php:88 +msgid "Current verified senders:" +msgstr "" + +#: app/Hooks/Handlers/AdminMenuHandler.php:429 +msgid "Date" +msgstr "" + +#: app/Services/TransStrings.php:89 +msgid "Date-Time" +msgstr "" + +#: app/Services/TransStrings.php:90 +msgid "Days" +msgstr "" + +#: app/Services/TransStrings.php:91 +msgid "Deactivate" +msgstr "" + +#: app/Services/TransStrings.php:316 +msgid "" +"Default and Fallback connection cannot be the same. Please select different " +"connections." +msgstr "" + +#: app/Services/TransStrings.php:92 +msgid "Default Connection" +msgstr "" + +#: app/Services/TransStrings.php:335 +msgid "Define which endpoint you want to use for sending messages." +msgstr "" + +#: app/Services/TransStrings.php:93 +msgid "Delete All Logs" +msgstr "" + +#: app/Services/TransStrings.php:94 +msgid "Delete Logs" +msgstr "" + +#: app/Services/TransStrings.php:95 +msgid "Delete Logs:" +msgstr "" + +#: app/Services/TransStrings.php:96 +msgid "Delete Selected" +msgstr "" + +#: app/Services/TransStrings.php:377 +msgid "delete_logs_info" +msgstr "" + +#: app/Services/TransStrings.php:97 +msgid "Disable Encryption for API Key (Not Recommended)" +msgstr "" + +#: app/Services/TransStrings.php:98 +msgid "Disable Encryption for Application Client Secret (Not Recommended)" +msgstr "" + +#: app/Services/TransStrings.php:99 +msgid "Disable Encryption for Application Client Secret Key (Not Recommended)" +msgstr "" + +#: app/Services/TransStrings.php:101 +msgid "Disable Encryption for Secret Key (Not Recommended)" +msgstr "" + +#: app/Services/TransStrings.php:100 +msgid "Disable Encryption for SMTP Password (Not Recommended)" +msgstr "" + +#: app/Services/TransStrings.php:102 +msgid "Disable Logging for FluentCRM Emails" +msgstr "" + +#: app/Services/TransStrings.php:323 +msgid "Disable sending all emails. If you enable this, no email will be sent." +msgstr "" + +#: app/Services/TransStrings.php:103 +msgid "Disabled" +msgstr "" + +#: app/Services/TransStrings.php:104 +msgid "Disconnect" +msgstr "" + +#: app/Services/TransStrings.php:105 +msgid "Disconnect & Reconnect" +msgstr "" + +#: app/Services/Notification/config.php:37 +msgid "Discord" +msgstr "" + +#: app/Services/TransStrings.php:106 +msgid "Discord Channel Details: " +msgstr "" + +#: app/Http/Controllers/DiscordController.php:87 +msgid "Discord connection has been disconnected successfully" +msgstr "" + +#: app/Services/TransStrings.php:107 +msgid "Discord Webhook URL" +msgstr "" + +#: app/Services/TransStrings.php:108 +msgid "Documentation" +msgstr "" + +#: app/Services/TransStrings.php:354 +msgid "documentation" +msgstr "" + +#: app/Services/TransStrings.php:109 +msgid "Domain Name" +msgstr "" + +#: app/Services/Mailer/Providers/TransMail/ValidatorTrait.php:23 +#: app/Services/Mailer/Providers/Mailgun/ValidatorTrait.php:23 +msgid "Domain name is required." +msgstr "" + +#: app/Services/TransStrings.php:110 +msgid "Edit" +msgstr "" + +#: app/Services/TransStrings.php:111 +msgid "Edit Connection" +msgstr "" + +#: app/Services/Mailer/Providers/config.php:165 +msgid "Elastic Email" +msgstr "" + +#: app/Services/TransStrings.php:112 +msgid "ElasticEmail API Settings" +msgstr "" + +#: app/Services/TransStrings.php:113 +msgid "Email Address" +msgstr "" + +#: app/Services/Mailer/Providers/AmazonSes/Handler.php:249 +#: app/Services/Mailer/Providers/ToSend/Handler.php:318 +msgid "" +"Email address already exists with another connection. Please choose a " +"different email." +msgstr "" + +#: app/Services/TransStrings.php:114 +msgid "Email Body" +msgstr "" + +#: app/Http/Controllers/SettingsController.php:164 +msgid "Email delivered successfully." +msgstr "" + +#: app/Hooks/Handlers/AdminMenuHandler.php:311 +msgid "Email Disabled" +msgstr "" + +#: app/Services/Mailer/Providers/AmazonSes/Handler.php:281 +#: app/Services/Mailer/Providers/AmazonSes/Handler.php:286 +#: app/Services/Mailer/Providers/ToSend/Handler.php:338 +#: app/Services/Mailer/Providers/ToSend/Handler.php:347 +msgid "Email does not exists. Please try again." +msgstr "" + +#: app/Services/TransStrings.php:115 +msgid "Email Failed:" +msgstr "" + +#: app/Hooks/Handlers/SchedulerHandler.php:124 +msgid "Email Failures" +msgstr "" + +#: app/Http/Controllers/SettingsController.php:255 +msgid "Email has been added successfully" +msgstr "" + +#: app/Http/Controllers/SettingsController.php:292 +msgid "Email has been removed successfully" +msgstr "" + +#: app/Services/TransStrings.php:116 +msgid "Email Headers" +msgstr "" + +#: app/Services/TransStrings.php:117 +msgid "Email Log" +msgstr "" + +#: app/Services/TransStrings.php:321 +msgid "" +"Email Logging is currently turned off. Only Failed and resent emails will be " +"shown here" +msgstr "" + +#: app/Services/TransStrings.php:118 +msgid "Email Logs" +msgstr "" + +#: app/Services/TransStrings.php:119 +msgid "Email Marketing Automation and CRM Plugin for WordPress" +msgstr "" + +#: app/Services/TransStrings.php:120 +msgid "Email Sending Error Notifications" +msgstr "" + +#: app/views/admin/digest_email.php:82 +msgid "Email Sending Health" +msgstr "" + +#: app/Services/Mailer/Providers/Simulator/Handler.php:31 +msgid "Email sending was simulated, No Email was sent originally" +msgstr "" + +#: app/Http/Controllers/LoggerController.php:69 +msgid "Email sent successfully." +msgstr "" + +#: app/Services/TransStrings.php:121 +msgid "Email Simulation" +msgstr "" + +#: app/Services/NotificationHelper.php:433 +msgid "Email Subject:" +msgstr "" + +#: app/Services/TransStrings.php:322 +msgid "" +"Email summary is useful for getting weekly or daily emails about all the " +"email sending stats for this site." +msgstr "" + +#: app/Services/TransStrings.php:122 +msgid "Email Test" +msgstr "" + +#: app/Services/TransStrings.php:123 +msgid "Email Type" +msgstr "" + +#: app/Hooks/Handlers/SchedulerHandler.php:120 +#: app/views/admin/digest_email.php:115 +msgid "Emails Sent" +msgstr "" + +#: app/Services/TransStrings.php:125 +msgid "Enable email opens tracking on postmark(For HTML Emails only)." +msgstr "" + +#: app/Services/TransStrings.php:124 +msgid "Enable Email Summary" +msgstr "" + +#: app/Services/TransStrings.php:126 +msgid "Enable link tracking on postmark (For HTML Emails only)." +msgstr "" + +#: app/Services/TransStrings.php:325 +msgid "" +"Enable Multi-Part Plain Text version of your HTML Emails. This feature is in " +"beta" +msgstr "" + +#: app/Services/TransStrings.php:127 +msgid "Enabled" +msgstr "" + +#: app/Services/TransStrings.php:128 +msgid "Encryption" +msgstr "" + +#: app/Services/TransStrings.php:129 +msgid "End date" +msgstr "" + +#: app/Services/TransStrings.php:360 +msgid "" +"Enter email address where test email will be sent (By default, logged in " +"user email will be used if email address is not provided)." +msgstr "" + +#: app/Services/TransStrings.php:130 +msgid "Enter Full Screen" +msgstr "" + +#: app/Services/TransStrings.php:131 +msgid "Enter new email address ex: new_sender@" +msgstr "" + +#: app/Services/TransStrings.php:132 +msgid "Enter the sender email address(optional)." +msgstr "" + +#: app/Services/NotificationHelper.php:434 +msgid "Error Message:" +msgstr "" + +#: app/Services/Mailer/Providers/config.php:56 +msgid "EU (Frankfurt)" +msgstr "" + +#: app/Services/Mailer/Providers/config.php:53 +msgid "EU (Ireland)" +msgstr "" + +#: app/Services/Mailer/Providers/config.php:54 +msgid "EU (London)" +msgstr "" + +#: app/Services/Mailer/Providers/config.php:57 +msgid "Europe (Milan)" +msgstr "" + +#: app/Services/Mailer/Providers/config.php:55 +msgid "Europe (Paris)" +msgstr "" + +#: app/Services/Mailer/Providers/config.php:58 +msgid "Europe (Stockholm)" +msgstr "" + +#: app/Services/TransStrings.php:133 +#: app/Hooks/Handlers/AdminMenuHandler.php:431 +msgid "Failed" +msgstr "" + +#: app/views/admin/digest_email.php:165 +msgid "Failed Count" +msgstr "" + +#: app/Services/TransStrings.php:134 +msgid "Failed to deactivate channel" +msgstr "" + +#: app/Services/TransStrings.php:135 +msgid "Failed to load notification channels" +msgstr "" + +#: app/Hooks/Handlers/SchedulerHandler.php:249 +msgid "Failed to renew the token" +msgstr "" + +#: app/Services/Mailer/Providers/Gmail/Handler.php:282 +msgid "Failed to renew token with Gmail Api" +msgstr "" + +#: app/Services/TransStrings.php:136 +msgid "Failed to toggle channel" +msgstr "" + +#: app/Services/TransStrings.php:137 +msgid "Fallback Connection" +msgstr "" + +#: app/Services/TransStrings.php:364 +msgid "" +"Fallback Connection will be used if an email is failed to send in one " +"connection. Please select a different connection than the default connection" +msgstr "" + +#: app/Services/TransStrings.php:138 +msgid "Fastest Contact Form Builder Plugin for WordPress" +msgstr "" + +#: app/Services/TransStrings.php:139 +msgid "Filter" +msgstr "" + +#: app/Hooks/Handlers/AdminMenuHandler.php:349 +msgid "Fluent SMTP" +msgstr "" + +#: app/Models/Traits/SendTestEmailTrait.php:13 +#, php-format +msgid "Fluent SMTP: Test Email - %s" +msgstr "" + +#: app/Services/TransStrings.php:140 +msgid "FluentCRM Email Logging" +msgstr "" + +#. Name of the plugin +#: app/Hooks/Handlers/AdminMenuHandler.php:108 +msgid "FluentSMTP" +msgstr "" + +#. Author of the plugin +msgid "FluentSMTP & WPManageNinja Team" +msgstr "" + +#: app/Services/TransStrings.php:141 +msgid "FluentSMTP does not store your email notifications data." +msgstr "" + +#: app/Services/TransStrings.php:142 +msgid "FluentSMTP does not store your email notifications data. " +msgstr "" + +#: app/views/admin/digest_email.php:7 +msgid "FluentSMTP Email Health Report" +msgstr "" + +#: app/views/admin/email_html.php:30 +msgid "FluentSMTP is a free opensource plugin and it will be always free " +msgstr "" + +#: app/Services/TransStrings.php:143 +msgid "" +"FluentSMTP is built using the following open-source libraries and software" +msgstr "" + +#: app/Services/TransStrings.php:310 +msgid "" +"FluentSMTP is free and will be always free. This is our pledge to WordPress " +"community from WPManageNinja LLC." +msgstr "" + +#: app/Services/TransStrings.php:311 +msgid "" +"FluentSMTP is powered by community. We listen to our community users and " +"build products that add value to businesses and save time." +msgstr "" + +#: app/Services/TransStrings.php:314 +msgid "" +"FluentSMTP is powered by its users like you. Feel free to contribute on " +"Github. Thanks to all of our contributors." +msgstr "" + +#: app/Hooks/Handlers/AdminMenuHandler.php:286 +msgid "FluentSMTP needs to be configured for it to work." +msgstr "" + +#: app/views/admin/email_html.php:7 +msgid "FluentSMTP Test Email" +msgstr "" + +#: app/Services/TransStrings.php:144 +msgid "Follow this link to get a Domain Name from Mailgun:" +msgstr "" + +#: app/Services/TransStrings.php:145 +msgid "Follow this link to get an API Key from ElasticEmail: " +msgstr "" + +#: app/Services/TransStrings.php:146 +msgid "Follow this link to get an API Key from Mailgun:" +msgstr "" + +#: app/Services/TransStrings.php:340 +msgid "" +"Follow this link to get an API Key from Pepipost (Click Show button on " +"Settings Page):" +msgstr "" + +#: app/Services/TransStrings.php:342 +msgid "" +"Follow this link to get an API Key from Postmark (Your API key is in the API " +"Tokens tab of your):" +msgstr "" + +#: app/Services/TransStrings.php:148 +msgid "Follow this link to get an API Key from SendGrid:" +msgstr "" + +#: app/Services/TransStrings.php:147 +msgid "Follow this link to get an API Key from SMTP2GO:" +msgstr "" + +#: app/Services/TransStrings.php:149 +msgid "Follow this link to get an API Key:" +msgstr "" + +#: app/Hooks/Handlers/AdminMenuHandler.php:80 +msgid "For SMTP, you already have FluentSMTP Installed" +msgstr "" + +#: app/Services/TransStrings.php:150 +msgid "Force From Email (Recommended Settings: Enable)" +msgstr "" + +#: app/Services/TransStrings.php:151 app/views/admin/ses_connection_info.php:39 +#: app/views/admin/general_connection_info.php:16 +#: app/views/admin/tosend_mailer_connection_info.php:46 +msgid "Force Sender Name" +msgstr "" + +#: app/Services/TransStrings.php:378 +msgid "force_sender_tooltip" +msgstr "" + +#: app/Services/TransStrings.php:152 +msgid "Friday" +msgstr "" + +#: app/Services/TransStrings.php:153 +msgid "From" +msgstr "" + +#: app/Services/TransStrings.php:154 +msgid "From Email" +msgstr "" + +#: app/Services/TransStrings.php:155 +msgid "From Name" +msgstr "" + +#: app/Services/TransStrings.php:156 +msgid "Functionalities" +msgstr "" + +#: app/Services/TransStrings.php:157 +msgid "General Settings" +msgstr "" + +#: app/Http/Controllers/SettingsController.php:115 +msgid "General Settings has been updated" +msgstr "" + +#: app/Services/TransStrings.php:159 +msgid "Get a Domain Name." +msgstr "" + +#: app/Services/TransStrings.php:160 +msgid "Get a Private API Key." +msgstr "" + +#: app/Services/TransStrings.php:158 +msgid "Get API Key." +msgstr "" + +#: app/Services/TransStrings.php:319 +msgid "" +"Get real-time notification on your Discord Channel on any email sending " +"failure. Configure notification with Discord to start getting real time " +"notifications." +msgstr "" + +#: app/Services/TransStrings.php:346 +msgid "" +"Get real-time notification on your favorite messaging channel on any email " +"sending failure. Configure any of the following channel to start getting " +"real time notifications." +msgstr "" + +#: app/Services/TransStrings.php:349 +msgid "" +"Get real-time notification on your Slack Channel on any email sending " +"failure. Configure notification with Slack Bot to start getting real time " +"notifications." +msgstr "" + +#: app/Services/TransStrings.php:356 +#, php-format +msgid "" +"Get real-time notifications on your %1$s for any email sending failures. " +"Configure notifications with FluentSMTP's official %2$s to start receiving " +"real-time alerts." +msgstr "" + +#: app/Services/TransStrings.php:161 +msgid "Get v3 API Key." +msgstr "" + +#: app/Services/TransStrings.php:162 +msgid "Getting Started" +msgstr "" + +#: app/Services/TransStrings.php:163 +msgid "Gmail / Google Workspace API Settings" +msgstr "" + +#: app/Services/Mailer/Providers/config.php:193 +msgid "Gmail or Google Workspace" +msgstr "" + +#: app/Services/Mailer/Providers/config.php:208 +msgid "" +"Gmail/Google Workspace is not recommended for sending mass marketing emails." +msgstr "" + +#: app/Http/Controllers/SettingsController.php:309 +msgid "Go to Fluent Forms Dashboard" +msgstr "" + +#: app/Hooks/filters.php:17 +msgid "Go to Fluent SMTP Settings page" +msgstr "" + +#: app/Http/Controllers/SettingsController.php:313 +msgid "Go to FluentCRM Dashboard" +msgstr "" + +#: app/Hooks/Handlers/AdminMenuHandler.php:84 +msgid "Go To FluentSMTP Settings" +msgstr "" + +#: app/Http/Controllers/SettingsController.php:317 +msgid "Go to Ninja Tables Dashboard" +msgstr "" + +#: app/Services/TransStrings.php:330 +#, php-format +msgid "Google API version has been upgraded. Please %s." +msgstr "" + +#: app/Services/TransStrings.php:164 +msgid "Great!" +msgstr "" + +#: app/Services/TransStrings.php:165 +msgid "How can we help you?" +msgstr "" + +#. URI of the plugin +#. Author URI of the plugin +msgid "https://fluentsmtp.com" +msgstr "" + +#: app/Services/TransStrings.php:351 +#, php-format +msgid "I agree to the %1$s of this Slack integration." +msgstr "" + +#: app/Services/TransStrings.php:359 +#, php-format +msgid "I agree to the %s of this Telegram integration." +msgstr "" + +#: app/Services/TransStrings.php:166 +msgid "I have sent the code" +msgstr "" + +#: app/Services/TransStrings.php:365 +msgid "" +"If checked, the From Email setting above will be used for all emails (It " +"will check if the from email is listed to available connections)." +msgstr "" + +#: app/views/admin/digest_email.php:133 +msgid "If this is unusual you should probably check if your site is broken." +msgstr "" + +#: app/Services/TransStrings.php:331 +#, php-format +msgid "" +"If you are a developer and would like to contribute to the project, please %s" +msgstr "" + +#: app/Services/TransStrings.php:334 +#, php-format +msgid "" +"If you are operating under EU laws, you may be required to use the EU region." +" %s." +msgstr "" + +#: app/Services/TransStrings.php:339 +msgid "" +"If you change your WordPress SALT Keys, this credential will become invalid. " +"Please update this credential whenever the WP SALTS are modified." +msgstr "" + +#: app/Services/TransStrings.php:341 +msgid "" +"If you enable this then link tracking header will be added to the email for " +"Postmark." +msgstr "" + +#: app/Services/TransStrings.php:343 +msgid "" +"If you enable this then open tracking header will be added to the email for " +"Postmark." +msgstr "" + +#: app/Services/TransStrings.php:167 +msgid "If you find an issue or have a suggestion please " +msgstr "" + +#: app/Services/NotificationHelper.php:49 +msgid "If you get this message, then your site is connected successfully." +msgstr "" + +#: app/Services/TransStrings.php:168 +msgid "If you have a minute, consider " +msgstr "" + +#: app/Services/Converter.php:400 +msgid "Import data from your current plugin (Easy WP SMTP)" +msgstr "" + +#: app/Services/Converter.php:108 +msgid "Import data from your current plugin (WP Mail SMTP)" +msgstr "" + +#: app/Services/Converter.php:404 +msgid "Import From Easy WP SMTP" +msgstr "" + +#: app/Services/Converter.php:111 +msgid "Import From WP Mail SMTP" +msgstr "" + +#: app/views/admin/ses_connection_info.php:59 +msgid "Increase Sending Limits" +msgstr "" + +#: app/Services/TransStrings.php:169 +msgid "Install Fluent Forms (Free)" +msgstr "" + +#: app/Services/TransStrings.php:170 +msgid "Install FluentCRM (Free)" +msgstr "" + +#: app/Services/TransStrings.php:171 +msgid "Install Ninja Tables (Free)" +msgstr "" + +#: app/Services/TransStrings.php:172 +msgid "Installing... Please wait" +msgstr "" + +#: app/Services/Mailer/Providers/AmazonSes/Handler.php:242 +#: app/Services/Mailer/Providers/AmazonSes/Handler.php:270 +msgid "Invalid email address! Please use a verified domain." +msgstr "" + +#: app/Services/Mailer/Providers/ToSend/Handler.php:311 +msgid "Invalid email address! Please use an email with verified domain." +msgstr "" + +#: app/Services/Mailer/ValidatorTrait.php:20 +msgid "Invalid email address." +msgstr "" + +#: app/Services/Mailer/Providers/Gmail/Handler.php:323 +msgid "Invalid. Please re-authenticate" +msgstr "" + +#: app/Services/TransStrings.php:312 +msgid "" +"is a free and open-source WordPress Plugin. Our mission is to provide the " +"ultimate email delivery solution with your favorite Email sending service. " +"FluentSMTP is built for performance and speed." +msgstr "" + +#: app/Services/TransStrings.php:326 +msgid "" +"is the best and complete feature-rich Email Marketing & CRM solution. It is " +"also the simplest and fastest CRM and Marketing Plugin on WordPress. Manage " +"your customer relationships, build your email lists, send email campaigns, " +"build funnels, and make more profit and increase your conversion rates. (Yes," +" It’s Free!)" +msgstr "" + +#: app/Services/TransStrings.php:327 +msgid "" +"is the ultimate user-friendly, fast, customizable drag-and-drop WordPress " +"Contact Form Plugin that offers you all the premium features, plus many more " +"completely unique additional features." +msgstr "" + +#: app/Services/TransStrings.php:173 +msgid "Join FluentCRM Facebook Community" +msgstr "" + +#: app/Services/TransStrings.php:174 +msgid "Join FluentForms Facebook Community" +msgstr "" + +#: app/Services/TransStrings.php:313 +msgid "Join our communities and participate in great conversations." +msgstr "" + +#: app/Services/TransStrings.php:175 +msgid "Last 3 months" +msgstr "" + +#: app/Services/TransStrings.php:176 +msgid "Last 30 Days" +msgstr "" + +#: app/Services/TransStrings.php:177 +msgid "Last 7 Days" +msgstr "" + +#: app/Hooks/Handlers/AdminMenuHandler.php:414 +msgid "Last 7 days" +msgstr "" + +#: app/Services/TransStrings.php:178 +msgid "Last month" +msgstr "" + +#: app/Services/TransStrings.php:179 +msgid "Last step!" +msgstr "" + +#: app/Services/TransStrings.php:180 +msgid "Last week" +msgstr "" + +#: app/views/admin/email_html.php:30 +msgid "Learn why it's free" +msgstr "" + +#: app/Hooks/Handlers/AdminMenuHandler.php:82 +msgid "learn why it's free" +msgstr "" + +#: app/Services/TransStrings.php:181 +msgid "Less" +msgstr "" + +#: app/Hooks/Handlers/AdminMenuHandler.php:368 +msgid "Loading data..." +msgstr "" + +#: app/Services/TransStrings.php:182 +msgid "Log All Emails for Reporting" +msgstr "" + +#: app/Services/TransStrings.php:183 +msgid "Log Emails" +msgstr "" + +#: app/Services/TransStrings.php:336 +msgid "" +"Looking for a WordPress table plugin for your website? Then you’re in the " +"right place." +msgstr "" + +#: app/views/admin/digest_email.php:131 +msgid "Looks like no email has been sent to the time period" +msgstr "" + +#: app/views/admin/ses_connection_info.php:56 +msgid "" +"Looks like you are in sandbox mode. Please apply to Amazon AWS to approve " +"your account. " +msgstr "" + +#: app/Services/TransStrings.php:184 +msgid "Mailer" +msgstr "" + +#: app/Services/Mailer/Providers/config.php:75 +msgid "Mailgun" +msgstr "" + +#: app/Services/Mailer/Providers/TransMail/Handler.php:113 +#: app/Services/Mailer/Providers/Mailgun/Handler.php:108 +msgid "Mailgun API Error" +msgstr "" + +#: app/Services/TransStrings.php:185 +msgid "Mailgun API Settings" +msgstr "" + +#: app/Services/Mailer/Providers/ToSend/Handler.php:293 +msgid "" +"Make sure to verify your sender emails or domain in toSend dashboard and " +"available in the provided API Key." +msgstr "" + +#: app/Services/TransStrings.php:186 +msgid "Manage Additional Senders" +msgstr "" + +#: app/Services/TransStrings.php:187 +msgid "Marketing" +msgstr "" + +#: app/views/admin/ses_connection_info.php:14 +msgid "Max Send in 24 hours" +msgstr "" + +#: app/views/admin/ses_connection_info.php:26 +msgid "Max Sending Rate" +msgstr "" + +#: app/Services/TransStrings.php:188 +msgid "Meet " +msgstr "" + +#: app/Services/TransStrings.php:189 +msgid "Message Stream" +msgstr "" + +#: app/Services/Mailer/Providers/config.php:66 +msgid "Middle East (Bahrain)" +msgstr "" + +#: app/Services/TransStrings.php:190 +msgid "Monday" +msgstr "" + +#: app/views/admin/tosend_mailer_connection_info.php:13 +msgid "Monthly Email Limit" +msgstr "" + +#: app/Services/TransStrings.php:191 +msgid "More" +msgstr "" + +#: app/Services/TransStrings.php:334 +msgid "More information on Mailgun.com" +msgstr "" + +#: app/Services/Mailer/Providers/config.php:134 +msgid "Netcore Email API, formerly Pepipost" +msgstr "" + +#: app/Services/TransStrings.php:192 +msgid "Next" +msgstr "" + +#: app/Services/TransStrings.php:324 +msgid "No Emails will be sent from your WordPress." +msgstr "" + +#: app/Services/TransStrings.php:193 +msgid "None" +msgstr "" + +#: app/Services/Mailer/BaseHandler.php:442 +#: app/Services/Mailer/BaseHandler.php:447 +msgid "Not implemented" +msgstr "" + +#: app/Http/Controllers/SettingsController.php:724 +msgid "Notification channel updated successfully" +msgstr "" + +#: app/Services/TransStrings.php:194 +msgid "Notification Days" +msgstr "" + +#: app/Services/TransStrings.php:195 +msgid "Notification Email Addresses" +msgstr "" + +#: app/Services/TransStrings.php:196 +msgid "Notifications" +msgstr "" + +#: app/Services/TransStrings.php:197 +msgid "Off" +msgstr "" + +#: app/Services/TransStrings.php:198 +msgid "On" +msgstr "" + +#: app/Services/TransStrings.php:199 +msgid "Oops!" +msgstr "" + +#: app/Services/TransStrings.php:354 +msgid "open a GitHub issue" +msgstr "" + +#: app/Services/TransStrings.php:379 +msgid "open an issue on GitHub" +msgstr "" + +#: app/Services/TransStrings.php:200 +msgid "Outlook / Office365 API Settings" +msgstr "" + +#: app/Services/Mailer/Providers/config.php:212 +msgid "Outlook or Office 365" +msgstr "" + +#: app/Services/Mailer/Providers/config.php:227 +msgid "Outlook/Office365 is not recommended for sending mass marketing emails." +msgstr "" + +#: app/Services/TransStrings.php:201 +msgid "Pepipost API Settings" +msgstr "" + +#: app/Services/Mailer/Providers/config.php:246 +msgid "PHP mail()" +msgstr "" + +#: app/Services/TransStrings.php:202 +msgid "Pin copied to clipboard" +msgstr "" + +#: app/Services/TransStrings.php:203 +msgid "Please " +msgstr "" + +#: app/Services/TransStrings.php:329 +#, php-format +msgid "Please %s to create API keys on the Google Cloud Platform." +msgstr "" + +#: app/Services/TransStrings.php:205 +msgid "Please add another connection to use fallback feature" +msgstr "" + +#: app/Services/TransStrings.php:206 +msgid "Please authenticate with Google to get " +msgstr "" + +#: app/Services/TransStrings.php:207 +msgid "Please authenticate with Office365 to get " +msgstr "" + +#: app/Services/TransStrings.php:367 +msgid "Please configure your first email service provider connection" +msgstr "" + +#: app/Services/Mailer/Providers/AmazonSes/ValidatorTrait.php:28 +msgid "Please define FLUENTMAIL_AWS_ACCESS_KEY_ID in wp-config.php file." +msgstr "" + +#: app/Services/Mailer/Providers/AmazonSes/ValidatorTrait.php:32 +msgid "Please define FLUENTMAIL_AWS_SECRET_ACCESS_KEY in wp-config.php file." +msgstr "" + +#: app/Services/Mailer/Providers/ElasticMail/ValidatorTrait.php:23 +msgid "Please define FLUENTMAIL_ELASTICMAIL_API_KEY in wp-config.php file." +msgstr "" + +#: app/Services/Mailer/Providers/Gmail/Handler.php:125 +msgid "Please define FLUENTMAIL_GMAIL_CLIENT_ID in wp-config.php file." +msgstr "" + +#: app/Http/Controllers/SettingsController.php:535 +msgid "Please define FLUENTMAIL_GMAIL_CLIENT_ID in your wp-config.php file" +msgstr "" + +#: app/Services/Mailer/Providers/Gmail/Handler.php:131 +msgid "Please define FLUENTMAIL_GMAIL_CLIENT_SECRET in wp-config.php file." +msgstr "" + +#: app/Http/Controllers/SettingsController.php:544 +msgid "Please define FLUENTMAIL_GMAIL_CLIENT_SECRET in your wp-config.php file" +msgstr "" + +#: app/Services/Mailer/Providers/TransMail/ValidatorTrait.php:27 +#: app/Services/Mailer/Providers/Mailgun/ValidatorTrait.php:27 +msgid "Please define FLUENTMAIL_MAILGUN_API_KEY in wp-config.php file." +msgstr "" + +#: app/Services/Mailer/Providers/TransMail/ValidatorTrait.php:31 +#: app/Services/Mailer/Providers/Mailgun/ValidatorTrait.php:31 +msgid "Please define FLUENTMAIL_MAILGUN_DOMAIN in wp-config.php file." +msgstr "" + +#: app/Services/Mailer/Providers/Outlook/Handler.php:90 +msgid "Please define FLUENTMAIL_OUTLOOK_CLIENT_ID in wp-config.php file." +msgstr "" + +#: app/Http/Controllers/SettingsController.php:598 +msgid "Please define FLUENTMAIL_OUTLOOK_CLIENT_ID in your wp-config.php file" +msgstr "" + +#: app/Services/Mailer/Providers/Outlook/Handler.php:96 +msgid "Please define FLUENTMAIL_OUTLOOK_CLIENT_SECRET in wp-config.php file." +msgstr "" + +#: app/Http/Controllers/SettingsController.php:607 +msgid "" +"Please define FLUENTMAIL_OUTLOOK_CLIENT_SECRET in your wp-config.php file" +msgstr "" + +#: app/Services/Mailer/Providers/PepiPost/ValidatorTrait.php:24 +msgid "Please define FLUENTMAIL_PEPIPOST_API_KEY in wp-config.php file." +msgstr "" + +#: app/Services/Mailer/Providers/Postmark/ValidatorTrait.php:24 +msgid "Please define FLUENTMAIL_POSTMARK_API_KEY in wp-config.php file." +msgstr "" + +#: app/Services/Mailer/Providers/SendGrid/ValidatorTrait.php:24 +msgid "Please define FLUENTMAIL_SENDGRID_API_KEY in wp-config.php file." +msgstr "" + +#: app/Services/Mailer/Providers/SendInBlue/ValidatorTrait.php:23 +msgid "Please define FLUENTMAIL_SENDINBLUE_API_KEY in wp-config.php file." +msgstr "" + +#: app/Services/Mailer/Providers/Smtp2Go/ValidatorTrait.php:24 +msgid "Please define FLUENTMAIL_SMTP2GO_API_KEY in wp-config.php file." +msgstr "" + +#: app/Services/Mailer/Providers/Smtp/ValidatorTrait.php:33 +msgid "Please define FLUENTMAIL_SMTP_PASSWORD in wp-config.php file." +msgstr "" + +#: app/Services/Mailer/Providers/Smtp/ValidatorTrait.php:29 +msgid "Please define FLUENTMAIL_SMTP_USERNAME in wp-config.php file." +msgstr "" + +#: app/Services/Mailer/Providers/SparkPost/ValidatorTrait.php:24 +msgid "Please define FLUENTMAIL_SPARKPOST_API_KEY in wp-config.php file." +msgstr "" + +#: app/Services/Mailer/Providers/ToSend/ValidatorTrait.php:27 +msgid "Please define FLUENTMAIL_TOSEND_API_KEY in wp-config.php file." +msgstr "" + +#: app/Services/TransStrings.php:208 +msgid "Please enter a valid email address" +msgstr "" + +#: app/Services/TransStrings.php:357 +#, php-format +msgid "" +"Please find %s on Telegram and send the following text to activate this " +"connection." +msgstr "" + +#: app/Http/Controllers/SlackController.php:22 +#: app/Http/Controllers/SettingsController.php:242 +#: app/Http/Controllers/SettingsController.php:279 +#: app/Http/Controllers/TelegramController.php:23 +msgid "Please provide a valid email address" +msgstr "" + +#: app/Http/Controllers/DiscordController.php:29 +msgid "Please provide a valid Webhook URL" +msgstr "" + +#: app/Services/TransStrings.php:204 +msgid "Please Provide an email" +msgstr "" + +#: app/Http/Controllers/SettingsController.php:553 +#: app/Http/Controllers/SettingsController.php:621 +msgid "Please provide application client id" +msgstr "" + +#: app/Http/Controllers/SettingsController.php:561 +#: app/Http/Controllers/SettingsController.php:629 +msgid "Please provide application client secret" +msgstr "" + +#: app/Services/Mailer/Providers/Gmail/Handler.php:177 +#: app/Services/Mailer/Providers/Outlook/Handler.php:133 +msgid "Please Provide Auth Token." +msgstr "" + +#: app/Http/Controllers/TelegramController.php:60 +msgid "Please provide site token" +msgstr "" + +#: app/Services/TransStrings.php:382 +msgid "Please select your email service provider" +msgstr "" + +#: app/Services/TransStrings.php:209 +msgid "Please send test email to confirm if the connection is working or not." +msgstr "" + +#: app/Services/TransStrings.php:354 +#, php-format +msgid "" +"Please view the %1$s first. If you still can't find the answer, %2$s and we " +"will be happy to assist you with any problems." +msgstr "" + +#: app/Http/Controllers/SettingsController.php:330 +msgid "Plugin has been successfully installed." +msgstr "" + +#: app/Functions/helpers.php:894 +msgid "Possible Conflict: " +msgstr "" + +#: app/Services/Mailer/Providers/config.php:148 +msgid "Postmark" +msgstr "" + +#: app/Services/TransStrings.php:210 +msgid "Postmark API Settings" +msgstr "" + +#: app/Services/TransStrings.php:211 +msgid "Prev" +msgstr "" + +#: app/Services/TransStrings.php:212 +msgid "Private API Key" +msgstr "" + +#: app/Services/TransStrings.php:213 +msgid "Provider" +msgstr "" + +#: app/Services/TransStrings.php:380 +msgid "provider for the connection." +msgstr "" + +#: app/views/admin/email_html.php:53 +msgid "PS: if you have a minute please " +msgstr "" + +#: app/Services/Notification/config.php:51 +msgid "Pushover" +msgstr "" + +#: app/Services/NotificationHelper.php:316 +msgid "Pushover API error" +msgstr "" + +#: app/Services/TransStrings.php:214 +msgid "Pushover API Token" +msgstr "" + +#: app/Http/Controllers/PushoverController.php:78 +msgid "Pushover connection has been disconnected successfully" +msgstr "" + +#: app/Http/Controllers/PushoverController.php:47 +msgid "Pushover notification is not enabled" +msgstr "" + +#: app/Services/TransStrings.php:215 +msgid "Pushover User Key" +msgstr "" + +#: app/Services/TransStrings.php:216 +msgid "Quick Overview" +msgstr "" + +#: app/views/admin/ses_connection_info.php:56 +msgid "Read More here." +msgstr "" + +#: app/Services/TransStrings.php:330 +msgid "read the doc and upgrade your API connection" +msgstr "" + +#: app/Services/TransStrings.php:217 +#: app/Services/Mailer/Providers/config.php:29 +#: app/Services/Mailer/Providers/config.php:71 +#: app/Services/Mailer/Providers/config.php:88 +#: app/Services/Mailer/Providers/config.php:102 +#: app/Services/Mailer/Providers/config.php:116 +#: app/Services/Mailer/Providers/config.php:130 +#: app/Services/Mailer/Providers/config.php:144 +#: app/Services/Mailer/Providers/config.php:161 +#: app/Services/Mailer/Providers/config.php:176 +#: app/Services/Mailer/Providers/config.php:242 +msgid "Read the documentation" +msgstr "" + +#: app/Services/TransStrings.php:381 +msgid "read the documentation here" +msgstr "" + +#: app/Services/TransStrings.php:218 +msgid "Receiver's Telegram Username: " +msgstr "" + +#: app/Services/TransStrings.php:219 +msgid "Recommended Plugin" +msgstr "" + +#: app/Services/TransStrings.php:220 +msgid "Region " +msgstr "" + +#: app/Services/TransStrings.php:221 +msgid "Remove" +msgstr "" + +#: app/Services/TransStrings.php:222 +msgid "Resend" +msgstr "" + +#: app/Services/TransStrings.php:223 +msgid "Resend Selected Emails" +msgstr "" + +#: app/Services/TransStrings.php:224 +msgid "Resent Count" +msgstr "" + +#: app/Services/TransStrings.php:225 +msgid "Retry" +msgstr "" + +#: app/Services/TransStrings.php:347 +msgid "" +"Return Path indicates where non-delivery receipts - or bounce messages - are " +"to be sent. If unchecked, bounce messages may be lost. With this enabled, " +"you'll be emailed using \"From Email\" if any messages bounce as a result of " +"issues with the recipient’s email." +msgstr "" + +#: app/Services/TransStrings.php:348 +#, php-format +msgid "" +"Return Path indicates where non-delivery receipts—or bounce messages—%1$s " +"are to be sent. If unchecked, bounce messages may be lost. With this enabled," +" %2$s you'll be emailed using \"From Email\" if any messages bounce due to " +"issues with the recipient's email." +msgstr "" + +#: app/Services/TransStrings.php:226 +msgid "Run Another Test Email" +msgstr "" + +#: app/Services/TransStrings.php:233 +msgid "Saturday" +msgstr "" + +#: app/Services/TransStrings.php:234 +msgid "Save Connection Settings" +msgstr "" + +#: app/Services/TransStrings.php:235 +msgid "Save Email Logs:" +msgstr "" + +#: app/Services/TransStrings.php:236 +msgid "Save Settings" +msgstr "" + +#: app/Services/TransStrings.php:237 +msgid "Search Results for" +msgstr "" + +#: app/Services/TransStrings.php:238 +msgid "Search Type and Enter..." +msgstr "" + +#: app/Services/TransStrings.php:239 +msgid "Secret Key" +msgstr "" + +#: app/Services/Mailer/Providers/AmazonSes/ValidatorTrait.php:24 +msgid "Secret key is required." +msgstr "" + +#: app/Http/Controllers/Controller.php:59 +msgid "Security Failed. Please reload the page" +msgstr "" + +#: app/Services/TransStrings.php:240 +msgid "Select" +msgstr "" + +#: app/Services/TransStrings.php:244 +msgid "Select date and time" +msgstr "" + +#: app/Services/TransStrings.php:241 +msgid "Select Email or Type" +msgstr "" + +#: app/Services/TransStrings.php:242 +msgid "Select Provider" +msgstr "" + +#: app/Services/TransStrings.php:243 +msgid "Select Region" +msgstr "" + +#: app/Services/TransStrings.php:363 +msgid "" +"Select which connection will be used for sending transactional emails from " +"your WordPress. If you use multiple connection then email will be routed " +"based on source from email address" +msgstr "" + +#: app/Http/Controllers/LoggerController.php:102 +msgid "Selected Emails have been proceed to send." +msgstr "" + +#: app/Services/TransStrings.php:245 +msgid "Send Test Email" +msgstr "" + +#: app/Services/TransStrings.php:246 +msgid "Send Test Message" +msgstr "" + +#: app/Services/TransStrings.php:248 +msgid "Send this email in HTML or in plain text format." +msgstr "" + +#: app/Services/TransStrings.php:247 +msgid "Send To" +msgstr "" + +#: app/views/admin/ses_connection_info.php:31 +#: app/views/admin/general_connection_info.php:8 +#: app/views/admin/tosend_mailer_connection_info.php:38 +msgid "Sender Email" +msgstr "" + +#: app/Services/TransStrings.php:250 +msgid "Sender Email " +msgstr "" + +#: app/Services/TransStrings.php:251 +msgid "Sender Email Address" +msgstr "" + +#: app/Services/Mailer/ValidatorTrait.php:16 +msgid "Sender email is required." +msgstr "" + +#: app/Services/TransStrings.php:252 app/views/admin/ses_connection_info.php:35 +#: app/views/admin/general_connection_info.php:12 +#: app/views/admin/tosend_mailer_connection_info.php:42 +msgid "Sender Name" +msgstr "" + +#: app/Services/TransStrings.php:253 +msgid "Sender Settings" +msgstr "" + +#: app/Services/Mailer/Providers/config.php:92 +msgid "SendGrid" +msgstr "" + +#: app/Services/TransStrings.php:249 +msgid "SendGrid API Settings" +msgstr "" + +#: app/Services/Mailer/Providers/config.php:106 +msgid "Sendinblue" +msgstr "" + +#: app/Services/TransStrings.php:254 +msgid "Sendinblue API Settings" +msgstr "" + +#: app/Services/Mailer/Providers/SendInBlue/Handler.php:90 +msgid "SendInBlueError API Error" +msgstr "" + +#: app/Services/TransStrings.php:256 +msgid "Sending by time of day" +msgstr "" + +#: app/Services/NotificationHelper.php:431 +msgid "Sending Driver:" +msgstr "" + +#: app/views/admin/tosend_mailer_connection_info.php:32 +msgid "Sending left this month" +msgstr "" + +#: app/Services/TransStrings.php:255 +msgid "Sending Stats" +msgstr "" + +#: app/Hooks/Handlers/AdminMenuHandler.php:430 +msgid "Sent" +msgstr "" + +#: app/views/admin/ses_connection_info.php:20 +#: app/views/admin/tosend_mailer_connection_info.php:19 +msgid "Sent in last 24 hours" +msgstr "" + +#: app/Services/Mailer/BaseHandler.php:306 +msgid "Sent using fallback connection " +msgstr "" + +#: app/Services/TransStrings.php:257 +msgid "Server Response" +msgstr "" + +#: app/Services/TransStrings.php:258 +msgid "Set the return-path to match the From Email" +msgstr "" + +#: app/Hooks/filters.php:18 app/Services/TransStrings.php:259 +msgid "Settings" +msgstr "" + +#: app/Http/Controllers/SettingsController.php:676 +msgid "Settings has been updated successfully" +msgstr "" + +#: app/Http/Controllers/SettingsController.php:94 +#: app/Http/Controllers/SettingsController.php:138 +msgid "Settings saved successfully." +msgstr "" + +#: app/Hooks/Handlers/SchedulerHandler.php:114 +#, php-format +msgid "Showing %1$s of %2$s different subject lines failed in the past %3$s" +msgstr "" + +#: app/Hooks/Handlers/SchedulerHandler.php:107 +#, php-format +msgid "Showing %1$s of %2$s different subject lines sent in the past %3$s" +msgstr "" + +#: app/Services/TransStrings.php:332 app/Services/TransStrings.php:362 +msgid "" +"Simply copy the following snippet and replace the stars with the " +"corresponding credential. Then simply paste to wp-config.php file of your " +"WordPress installation" +msgstr "" + +#: app/Services/Notification/config.php:23 +msgid "Slack" +msgstr "" + +#: app/Services/TransStrings.php:260 +msgid "Slack Channel Details: " +msgstr "" + +#: app/Http/Controllers/SlackController.php:94 +msgid "Slack connection has been disconnected successfully" +msgstr "" + +#: app/Http/Controllers/SlackController.php:65 +#: app/Http/Controllers/DiscordController.php:57 +msgid "Slack notification is not enabled" +msgstr "" + +#: app/Services/TransStrings.php:227 +msgid "SMTP Host" +msgstr "" + +#: app/Services/Mailer/Providers/Smtp/ValidatorTrait.php:19 +msgid "SMTP host is required." +msgstr "" + +#: app/Services/TransStrings.php:228 +msgid "SMTP Password" +msgstr "" + +#: app/Services/Mailer/Providers/Smtp/ValidatorTrait.php:41 +msgid "SMTP password is required." +msgstr "" + +#: app/Services/TransStrings.php:229 +msgid "SMTP Port" +msgstr "" + +#: app/Services/Mailer/Providers/Smtp/ValidatorTrait.php:23 +msgid "SMTP port is required." +msgstr "" + +#: app/Services/Mailer/Providers/config.php:9 +msgid "SMTP server" +msgstr "" + +#: app/Services/TransStrings.php:230 +msgid "SMTP Username" +msgstr "" + +#: app/Services/Mailer/Providers/Smtp/ValidatorTrait.php:37 +msgid "SMTP username is required." +msgstr "" + +#: app/Hooks/filters.php:6 +msgid "SMTP/Mail Settings" +msgstr "" + +#: app/Services/Mailer/Providers/config.php:180 +msgid "SMTP2GO" +msgstr "" + +#: app/Services/TransStrings.php:231 +msgid "SMTP2GO API Settings" +msgstr "" + +#: app/Http/Controllers/LoggerController.php:73 +msgid "Something went wrong" +msgstr "" + +#: app/Services/Mailer/Providers/Postmark/Handler.php:22 +#: app/Services/Mailer/Providers/ElasticMail/Handler.php:24 +#: app/Services/Mailer/Providers/SparkPost/Handler.php:26 +#: app/Services/Mailer/Providers/SendGrid/Handler.php:26 +#: app/Services/Mailer/Providers/AmazonSes/Handler.php:26 +#: app/Services/Mailer/Providers/SendInBlue/Handler.php:34 +#: app/Services/Mailer/Providers/ToSend/Handler.php:22 +#: app/Services/Mailer/Providers/Smtp/Handler.php:23 +#: app/Services/Mailer/Providers/PepiPost/Handler.php:26 +#: app/Services/Mailer/Providers/TransMail/Handler.php:23 +#: app/Services/Mailer/Providers/Smtp2Go/Handler.php:24 +#: app/Services/Mailer/Providers/Mailgun/Handler.php:27 +#: app/Services/Mailer/Providers/Gmail/Handler.php:18 +#: app/Services/Mailer/Providers/DefaultMail/Handler.php:16 +#: app/Services/Mailer/Providers/Outlook/Handler.php:20 +msgid "Something went wrong!" +msgstr "" + +#: app/Services/Mailer/Providers/Outlook/API.php:76 +msgid "Something with wrong with Outlook API. Please check your API Settings" +msgstr "" + +#: app/Http/Controllers/SettingsController.php:211 +#: app/Http/Controllers/SettingsController.php:231 +#: app/Http/Controllers/SettingsController.php:268 +msgid "Sorry no connection found. Please reload the page and try again" +msgstr "" + +#: app/Services/TransStrings.php:261 +msgid "Sorry! No docs found" +msgstr "" + +#: app/Http/Controllers/SettingsController.php:466 +msgid "Sorry! The provider email is not valid" +msgstr "" + +#: app/Services/Mailer/Providers/AmazonSes/Handler.php:274 +msgid "Sorry! you can not remove this email from this connection" +msgstr "" + +#: app/Http/Controllers/SettingsController.php:323 +msgid "Sorry, You can not install this plugin" +msgstr "" + +#: app/Services/Mailer/Providers/config.php:65 +msgid "South America (São Paulo)" +msgstr "" + +#: app/Services/Mailer/Providers/config.php:120 +msgid "SparkPost" +msgstr "" + +#: app/Services/Mailer/Providers/SparkPost/Handler.php:79 +msgid "SparkPost API Error" +msgstr "" + +#: app/Services/TransStrings.php:262 +msgid "SparkPost API Settings" +msgstr "" + +#: app/Services/TransStrings.php:232 +msgid "SSL" +msgstr "" + +#: app/Services/TransStrings.php:263 +msgid "Start date" +msgstr "" + +#: app/Services/TransStrings.php:264 +msgid "Status" +msgstr "" + +#: app/Services/TransStrings.php:265 +msgid "Status:" +msgstr "" + +#: app/Services/TransStrings.php:268 +msgid "Store Access Keys in DB" +msgstr "" + +#: app/Services/TransStrings.php:266 +msgid "Store API Keys in Config File" +msgstr "" + +#: app/Services/TransStrings.php:267 +msgid "Store API Keys in DB" +msgstr "" + +#: app/Services/TransStrings.php:269 +msgid "Store Application Keys in DB" +msgstr "" + +#: app/Services/TransStrings.php:270 app/views/admin/digest_email.php:114 +#: app/views/admin/digest_email.php:164 +msgid "Subject" +msgstr "" + +#: app/Services/TransStrings.php:271 +msgid "Subscribe To Updates" +msgstr "" + +#: app/Services/TransStrings.php:353 +msgid "" +"Subscribe with your email to know about this plugin updates, releases and " +"useful tips." +msgstr "" + +#: app/Services/TransStrings.php:272 +msgid "Successful" +msgstr "" + +#: app/Services/TransStrings.php:273 +msgid "Summary Email" +msgstr "" + +#: app/Services/TransStrings.php:274 +msgid "Sunday" +msgstr "" + +#: app/Services/Notification/config.php:7 +msgid "Telegram" +msgstr "" + +#: app/Http/Controllers/TelegramController.php:158 +msgid "Telegram connection has been disconnected successfully" +msgstr "" + +#: app/Services/TransStrings.php:276 +msgid "Telegram Connection Status: " +msgstr "" + +#: app/Http/Controllers/TelegramController.php:93 +#: app/Http/Controllers/TelegramController.php:123 +msgid "Telegram notification is not enabled" +msgstr "" + +#: app/Services/TransStrings.php:351 app/Services/TransStrings.php:359 +msgid "terms and conditions" +msgstr "" + +#: app/Services/TransStrings.php:277 +msgid "Test Email Has been successfully sent" +msgstr "" + +#: app/Http/Controllers/SlackController.php:81 +#: app/Http/Controllers/PushoverController.php:64 +#: app/Http/Controllers/DiscordController.php:73 +#: app/Http/Controllers/TelegramController.php:137 +msgid "Test message sent successfully" +msgstr "" + +#: app/Services/TransStrings.php:368 +msgid "" +"Thank you for installing FluentSMTP - The ultimate SMTP & Email Service " +"Connection Plugin for WordPress" +msgstr "" + +#: app/views/admin/email_html.php:30 +msgid "" +"Thank you for using Fluent SMTP Plugin. The ultimate SMTP plugin you need " +"for making sure your emails are delivered." +msgstr "" + +#: app/Services/TransStrings.php:337 +msgid "" +"the best WP table plugin that comes with all the solutions to the problems " +"you face while creating tables on your posts/pages." +msgstr "" + +#: app/Services/Mailer/Providers/config.php:257 +msgid "" +"The Default option does not use SMTP or any Email Service Providers so it " +"will not improve email delivery on your site." +msgstr "" + +#: app/Services/TransStrings.php:317 +msgid "" +"The Default(none) option does not use SMTP and will not improve email " +"delivery on your site." +msgstr "" + +#: app/Services/TransStrings.php:278 +msgid "The email address already exists in the list" +msgstr "" + +#: app/Services/TransStrings.php:279 +msgid "The email address must match the domain: " +msgstr "" + +#: app/Services/TransStrings.php:280 +msgid "The email address which emails are sent from." +msgstr "" + +#: app/Http/Controllers/SettingsController.php:153 +msgid "The email field is required." +msgstr "" + +#: app/Services/Mailer/Providers/AmazonSes/Validator.php:50 +msgid "The from email is not verified" +msgstr "" + +#: app/Services/TransStrings.php:281 +msgid "The name which emails are sent from." +msgstr "" + +#. Description of the plugin +msgid "The Ultimate SMTP Connection Plugin for WordPress." +msgstr "" + +#: app/Services/Mailer/Providers/Factory.php:45 +msgid "There is no matching provider found by email: " +msgstr "" + +#: app/views/admin/email_html.php:42 +msgid "This email was sent from " +msgstr "" + +#: app/Services/TransStrings.php:338 +msgid "" +"This input will be securely encrypted using WP SALTS as encryption keys " +"before saving." +msgstr "" + +#: app/Services/NotificationHelper.php:48 +msgid "This is a test message for " +msgstr "" + +#: app/Hooks/Handlers/AdminMenuHandler.php:85 +msgid "" +"This notice is from FluentSMTP plugin to prevent plugin\n" +" conflict." +msgstr "" + +#: app/Services/TransStrings.php:282 +msgid "Thursday" +msgstr "" + +#: app/Services/TransStrings.php:275 +msgid "TLS" +msgstr "" + +#: app/Services/TransStrings.php:283 +msgid "To" +msgstr "" + +#: app/Services/NotificationHelper.php:432 +msgid "To Email Address:" +msgstr "" + +#: app/Services/TransStrings.php:284 +msgid "" +"To send emails you will need only a Mail Send access level for this API key." +msgstr "" + +#: app/Services/TransStrings.php:286 +#: app/Hooks/Handlers/AdminMenuHandler.php:407 +msgid "Today" +msgstr "" + +#: app/Services/Mailer/Providers/Gmail/Handler.php:318 +#: app/Services/Mailer/Providers/Outlook/Handler.php:192 +msgid "Token Validity" +msgstr "" + +#: app/Services/TransStrings.php:285 +msgid "ToSend API Settings" +msgstr "" + +#: app/Services/TransStrings.php:287 +msgid "Total Email Sent (Logged):" +msgstr "" + +#: app/Services/TransStrings.php:288 +msgid "Track Opens" +msgstr "" + +#: app/Services/TransStrings.php:289 +msgid "Transactional" +msgstr "" + +#: app/Services/Mailer/BaseHandler.php:309 +msgid "Tried to send using fallback but failed. " +msgstr "" + +#: app/Services/TransStrings.php:290 +msgid "Try Again" +msgstr "" + +#: app/Services/TransStrings.php:291 +msgid "Tuesday" +msgstr "" + +#: app/Services/TransStrings.php:292 +msgid "Turn On" +msgstr "" + +#: app/Services/TransStrings.php:293 +msgid "Type & press enter..." +msgstr "" + +#: app/Services/Mailer/Providers/ToSend/Handler.php:304 +msgid "Unable to verify the connection details. Please check the API Key." +msgstr "" + +#: app/Http/Controllers/SettingsController.php:198 +#: app/Services/Mailer/ValidatorTrait.php:36 +msgid "Unprocessable Entity" +msgstr "" + +#: app/Services/Mailer/Providers/config.php:48 +msgid "US East (N. Virginia)" +msgstr "" + +#: app/Services/Mailer/Providers/config.php:49 +msgid "US East (Ohio)" +msgstr "" + +#: app/Services/Mailer/Providers/config.php:50 +msgid "US West (N. California)" +msgstr "" + +#: app/Services/Mailer/Providers/config.php:51 +msgid "US West (Oregon)" +msgstr "" + +#: app/views/admin/tosend_mailer_connection_info.php:25 +msgid "Usage this month" +msgstr "" + +#: app/Services/TransStrings.php:294 +msgid "Use Auto TLS" +msgstr "" + +#: app/Services/TransStrings.php:295 +msgid "User Key" +msgstr "" + +#: app/Http/Controllers/PushoverController.php:26 +msgid "User Key is required" +msgstr "" + +#: app/views/admin/ses_connection_info.php:43 +#: app/views/admin/tosend_mailer_connection_info.php:51 +msgid "Valid Sending Emails" +msgstr "" + +#: app/Services/TransStrings.php:296 +msgid "Validating Data. Please wait..." +msgstr "" + +#: app/Hooks/Handlers/AdminMenuHandler.php:446 +msgid "View All" +msgstr "" + +#: app/Services/NotificationHelper.php:441 +msgid "View Failed Email(s)" +msgstr "" + +#: app/Services/TransStrings.php:297 +msgid "Warning" +msgstr "" + +#: app/Services/TransStrings.php:358 +msgid "" +"We could not fetch the Telegram notification status. Here is the server " +"response: " +msgstr "" + +#: app/Services/Converter.php:109 app/Services/Converter.php:401 +msgid "" +"We have detected other SMTP plugin's settings available on your site. Click " +"bellow to pre-populate the values" +msgstr "" + +#: app/Services/TransStrings.php:298 +msgid "We recommend activating only one notification channel at a time." +msgstr "" + +#: app/Http/Controllers/DiscordController.php:21 +msgid "Webhook URL is required" +msgstr "" + +#: app/Services/NotificationHelper.php:430 +msgid "Website URL:" +msgstr "" + +#: app/Services/TransStrings.php:299 +msgid "Wednesday" +msgstr "" + +#: app/Services/TransStrings.php:369 +msgid "Welcome to FluentSMTP" +msgstr "" + +#: app/Services/TransStrings.php:328 +msgid "" +"When checked, the From Name setting above will be used for all emails, " +"ignoring values set by other plugins." +msgstr "" + +#: app/Hooks/Handlers/AdminMenuHandler.php:279 +#, php-format +msgid "" +"WordPress version 5.5 or greater is required for FluentSMTP. You are using " +"version %s currently. Please update your WordPress Core to use FluentSMTP " +"Plugin." +msgstr "" + +#: app/Services/TransStrings.php:300 +msgid "Write a review (really appreciated 😊)" +msgstr "" + +#: app/Services/TransStrings.php:384 app/views/admin/email_html.php:53 +msgid "write a review for FluentSMTP" +msgstr "" + +#: app/Hooks/Handlers/AdminMenuHandler.php:235 +msgid "Write a review ★★★★★" +msgstr "" + +#: app/Services/TransStrings.php:301 +msgid "Yes, Deactivate" +msgstr "" + +#: app/Services/TransStrings.php:302 +msgid "Yes, Disconnect" +msgstr "" + +#: app/Http/Controllers/SettingsController.php:482 +msgid "You are subscribed to plugin update and monthly tips" +msgstr "" + +#: app/Services/Mailer/Providers/ToSend/Handler.php:342 +msgid "You can not remove the primary sender email of this connection." +msgstr "" + +#: app/Http/Controllers/Controller.php:51 +msgid "You do not have permission to do this action" +msgstr "" + +#: app/Hooks/Handlers/AdminMenuHandler.php:95 +msgid "You do not have permission to see this data" +msgstr "" + +#: app/Services/TransStrings.php:303 +msgid "You may add additional sending emails in this" +msgstr "" + +#: app/views/admin/digest_email.php:200 +msgid "" +"You received this email because the Email Sending Health Report is enabled " +"in your FluentSMTP settings. Simply turn it off to stop these emails at " +msgstr "" + +#: app/Hooks/Handlers/AdminMenuHandler.php:81 +msgid "" +"You seem to be looking for an SMTP plugin, but there's no need for another " +"one — FluentSMTP is already installed on your site. FluentSMTP is a " +"comprehensive, free, and open-source plugin with full features available " +"without any upsell" +msgstr "" + +#: app/Services/TransStrings.php:304 +msgid "Your Discord Channel Name (For Internal Use)" +msgstr "" + +#: app/Services/TransStrings.php:305 +msgid "Your Discord Channel Webhook URL" +msgstr "" + +#: app/Services/TransStrings.php:306 +msgid "Your Email" +msgstr "" + +#: app/Services/TransStrings.php:307 +msgid "Your Email Address" +msgstr "" + +#: app/views/admin/digest_email.php:182 +msgid "Your email sending health is perfect" +msgstr "" + +#: app/Services/TransStrings.php:366 +msgid "" +"Your emails will be routed automatically based on From email address. No " +"additional configuration is required." +msgstr "" + +#: app/Services/TransStrings.php:355 +#, php-format +msgid "" +"Your FluentSMTP plugin is currently integrated with Telegram. Receive timely " +"notifications from %s on Telegram for any email sending issues from your " +"website. This ongoing connection ensures you're always informed about your " +"email delivery status." +msgstr "" + +#: app/Services/TransStrings.php:320 +msgid "" +"Your FluentSMTP plugin is currently integrated with your Discord Channel. " +"Receive timely notifications on Discord for any email sending issues from " +"your website. This ongoing connection ensures you're always informed about " +"your email delivery status." +msgstr "" + +#: app/Services/TransStrings.php:350 +msgid "" +"Your FluentSMTP plugin is currently integrated with your Slack Channel. " +"Receive timely notifications on Slack for any email sending issues from your " +"website. This ongoing connection ensures you're always informed about your " +"email delivery status." +msgstr "" + +#: app/Services/TransStrings.php:333 +msgid "" +"Your Gmail / Google Workspace Authentication has been enabled. No further " +"action is needed. If you want to re-authenticate," +msgstr "" + +#: app/Services/TransStrings.php:308 +msgid "Your Name" +msgstr "" + +#: app/Http/Controllers/PushoverController.php:37 +#: app/Http/Controllers/DiscordController.php:46 +msgid "Your settings has been saved" +msgstr "" + +#: app/Services/TransStrings.php:309 +msgid "Your SMTP Username" +msgstr "" diff --git a/wp-content/plugins/fluent-smtp/language/index.php b/wp-content/plugins/fluent-smtp/language/index.php new file mode 100644 index 0000000..f0f663c --- /dev/null +++ b/wp-content/plugins/fluent-smtp/language/index.php @@ -0,0 +1 @@ +WPManageNinja LLC builds products for WordPress businesses and has a very stable business model. We want to give back to the community, and FluentSMTP is part of that. + +== 🎉 Available Email Service Connections == +* Amazon SES +* Gmail OAuth +* Google Workspace OAuth +* Outlook OAuth +* SendGrid +* Mailgun +* Brevo (Sendinblue) +* Pepipost +* Postmark +* Zoho ZeptoMail (TransMail) +* SparkPost +* SMTP2GO +* Elastic Mail +* Zoho via SMTP +* Any SMTP email provider +* More native integrations coming soon + +== 🎉 Fluent SMTP features == +Fluent SMTP is the fastest and most advanced WordPress Mail SMTP plugin on the market. We crafted this plugin for speed, reliability and scalability. + +* Real-Time Email Delivery +* Email Routing to multiple email connections +* Connect with Any Email Service Providers +* Fallback Email Connection +* Email Logging +* Resend Emails +* Detailed Reporting +* Super fast UI powered by VueJS + +Most importantly, this plugin is free and will always be free. +👉 Read why it's 100% free (always) 👈 + +[youtube https://www.youtube.com/watch?v=GwmkX6zImWw] + +== How does Fluent SMTP work? == +Fluent SMTP improves your WordPress mail by intercepting wp_mail calls, and then connecting with your email service providers to ensure deliverability. It uses a direct email service API to send emails faster and securely. It means emails are sent using the provider's direct API. For your native SMTP connections, it uses the proper host, port, and credentials to send your WP mails. + +== Email Logging and Debugging == +Fluent SMTP optionally logs your emails, so at any time you can check to see your site health. You can also resend your failed emails, or resend any previous emails from email logs. + +== 🎉 Amazon SES (Native API Connection) == +With Fluent SMTP SES Connection, you get the powerful, low-cost, high deliverability managed infrastructure from Amazon. With the support of Fluent SMTP, it's super easy to set up and configure Amazon SES API and send all your WordPress emails. The integration is with amazon's latest SES API so your emails will be delivered faster and the right way. + +Fluent SMTP optimizes the API connection so it creates CURL-Tunneling to send your emails even faster. With Amazon SES connection, You can send emails faster than any other plugins. + +== 🎉 Gmail or Google Workspace (Native API Connection) == +Fluent SMTP - WP Mail Plugin provides you options to connect with your Gmail or Google Workspace emails and send emails over their API. It's fast and secure. +[youtube https://www.youtube.com/watch?v=_d78bscNaX8] + +== 🎉 SendGrid API Connection == +SendGrid is the leading email sending service provider. You can rely on their globally distributed, cloud-based architecture for sending your WordPress Emails. + +With Fluent SMTP, You can set up your SendGrid email service connection API in less than a minute. With this direct API connection, send your WordPress Mails fast and secure way. + +Read about SendGrid connection documentation here + +== 🎉 Mailgun Email API Connection == +Mailgun is another leading email sending service provider and trusted by 225,000+ businesses. You can rely on their globally distributed, cloud-based architecture for sending your WordPress Emails. + +Get your message to the right person at the right time with global infrastructure and industry expertise you can rely on. + +With Fluent SMTP connection, You can set up your Mailgun email service connection in less than a minute. This is also a direct API connection so it's faster than their SMTP connection. + +Read about Mailgun connection documentation here + +== 🎉 Sendinblue API Connection == +Sendinblue is a platform for growing businesses and it has a great transactional email service. They serve more than 80,000 companies around the world and send millions of emails every day. + +If you use Sendinblue then with the help of Fluent SMTP, You can easily connect with its API and send Your WordPress emails via an API connection. + +Read about Sendinblue connection documentation here + +== 🎉 Pepipost Email API Connection == +Pepipost is a complete sending partner with a user-friendly dashboard and many extensive functions such as statistics and real-time information. + +With Fluent SMTP connection, You can set up your Pepipost email service connection in less than a minute. With a direct API connection so it's faster than their SMTP connection. + +Read about Pepipost API connection documentation here + +== 🎉 SparkPost Email API Connection == +SparkPost is a great email sending service with lots of analytics features. +With Fluent SMTP, You can set up your SparkPost email service connection with your WordPress in less than a minute. + +Read about SparkPost connection documentation here + +== 🎉 Elastic Email API Connection == +Elastic Email is a great solution for sending transactional and marketing emails with a user-friendly dashboard and many extensive functions such as statistics and real-time information. Fluent SMTP plugin is fully compatible with their official API and you can use it to send your WordPress emails via Elastic Email + +== 🎉 Outlook or Office365 API Connection == +Fluent SMTP provides you options to connect with your Outlook or Office 365 emails and send emails over their API. It's fast and secure. Using oAuth2 authentication system for the connection, You can easily setup the connection and send your emails with Office 365 / Outlook emails. + +Read the documentation for connecting Office 365 Email with WordPress + +== 🎉 SMTP2GO Email API Connection == +SMTP2GO is a convenient solution for sending transactional and marketing emails with a user-friendly dashboard and many extensive functions such as statistics and real-time information. Fluent SMTP plugin is fully compatible with their official API and you can use it to send your WordPress emails via SMTP2Go + + +== 🎉 Other SMTP == +Fluent SMTP plugin works with all major email services that offer SMTP connections such as Gmail, Yahoo, Microsoft Live, Zoho Mail, YandexMail, and any other email sending services. + +You can set the following options: + +* Specify an SMTP Host. +* Specify an SMTP Port. +* Choose the Encryption option. +* Choose to use SMTP authentication or not. +* Specify the SMTP username and password. +* That's it 💯 + +Read about SMTP connection documentation here + +== 🚀 MODERN. POWERFUL. SUPER FAST 🚀 == + +* Built with VueJS as a Single-page Application. +* Super fast and lean interface so anyone can use it without a learning curve. +* Super awesome Dashboard with charts, graphs, and stats to show how your emails are doing. + +== 🚀 Automatic Email Routing 🚀 == +With Fluent SMTP's unique multiple connection driver features, You can add as many email connections as you want. Based on your From Email Address, Fluent SMTP will route your emails to the appropriate email driver and send them securely. This is one of the unique and useful features that Fluent SMTP offers. + +Now, you can route your transactional emails with one connection and marketing emails with another connection. + +== 🚀 Email Logs and Reporting 🚀 == +Do you want to know how many and which emails your site is sending? Fluent SMTP got you covered. With powerful and super-fast email reporting and logs, you can easily view your WordPress emails. You can also view charts and graphs about your daily email stats. Also, you can resend any emails anytime you want. This is super helpful for storing emails for your records, auditing outgoing emails, and debugging during site development. + +Optionally, You can turn off this feature, and then only failed emails will be logged so you can take a look. Fluent SMTP uses a custom database table so your WordPress tables will not be bloated (we care). + +== 🚀 Real-time Notifications on Email Failures via Telegram, Slack, and Discord 🚀 == +Fluent SMTP has a unique feature that will notify you in real-time if any email fails to send. You can set up your Telegram, Slack, or Discord channel and Fluent SMTP will send you a notification if any email fails to send. This is super helpful for debugging and monitoring your site's email health. + +== 🚀 Security 🚀 == +Fluent SMTP is built by professionals and security and scalability in mind. Fluent SMTP provides several options for you to keep your email sending secure and safe. + +* Ability to store your SMTP / API credentials in wp-config.php. +* Ability to auto-delete old email logs. +* Fluent SMTP connects your email service providers directly via an API. + += 🚀Plain-Text Support with HTML Email on the fly 🚀= +FluentSMTP will automatically convert your HTML email to Plain-Text email on the fly. Then it will send your emails with multi-part mime type. This is super helpful for email deliverability and spam score. Please make sure, you activate that from the settings. + +== 👉 Credits 👈 == +Fluent SMTP is built by WPManageNinja LLC. And yes, It's built by the creator of popular plugins like FluentForms, FluentCRM, Ninja Tables. + +Fluent SMTP is a 100% free and open source plugin and we will never release a pro version. This does not mean that it lacks features. Our aim is to provide the ultimate SMTP/Email Service connection plugin for your WordPress Mails. We wrote an article about why we made this plugin and our plans for Fluent SMTP. + +The full source code is hosted on GitHub and you are welcome to contribute to the development of this awesome WP Mail Plugin. +👉 View on GitHub 👈 + += Compatible With.. = +* [Fluent Forms - The Fastest Form Builder Plugin](https://wordpress.org/plugins/fluentform/) +* [FluentCRM - Email Marketing Automation, Email Newsletter and CRM Plugin for WordPress](https://wordpress.org/plugins/fluent-crm/) +* [WooCommerce](https://wordpress.org/plugins/woocommerce/) +* [Elementor Forms](https://elementor.com/features/form-widget/) +* [Contact Form 7](https://wordpress.org/plugins/contact-form-7/) +* [Gravity Forms](http://www.gravityforms.com) +* [Contact Form by WPForms](https://wordpress.org/plugins/wpforms-lite/) +* [Forminator – Contact Form](https://wordpress.org/plugins/forminator/) +* [Ninja Forms Contact Form](https://wordpress.org/plugins/ninja-forms/) +* [Form Maker by 10Web](https://wordpress.org/plugins/form-maker/) +* [Formidable Form Builder](https://wordpress.org/plugins/formidable/) +* [GiveWP – Donation Plugin](https://wordpress.org/plugins/give/) +* [Fast Secure Contact Form](https://wordpress.org/plugins/si-contact-form/) +* [Visual Forms Builder](https://wordpress.org/plugins/visual-form-builder/) +* [Contact Form Builder](https://wordpress.org/plugins/contact-form-builder/) +* [PlanSo Forms](https://wordpress.org/plugins/planso-forms/) +* [FluentCRM](https://wordpress.org/plugins/fluent-crm) +* [SendPress Newsletters](https://wordpress.org/plugins/sendpress/) +* [WP HTML Mail](https://wordpress.org/plugins/wp-html-mail/) +* [WPForms Lite](https://wordpress.org/plugins/wpforms-lite/) +* [WP Forms Pro](https://wordpress.org/plugins/wpforms-lite/) +* [Email Templates](https://wordpress.org/plugins/email-templates/) +* .. and every other plugin that uses the WordPress API [wp_mail](https://codex.wordpress.org/Function_Reference/wp_mail) to send mail! + +== Easy Migration from WP Mail SMTP by WPForms == +If you currently using WP Mail SMTP by WPForms plugin and want to migrate to FluentSMTP then that is super easy. Within few seconds you are migrated. + +* Just install FluentSMTP plugin to your site. +* Go to Settings -> FluentSMTP. +* It will automatically show previous configuration from "WP Mail SMTP by WPForms". +* Click "Import From WP Mail SMTP" button and that's it. +* Disable "WP Mail SMTP by WPForms" and enjoy FluentSMTP. + +== Once Click Migration from Easy WP SMTP == +If you currently using Easy WP SMTP plugin and want to migrate to FluentSMTP then that is super easy. Within a few seconds you are migrated from "Easy WP SMTP". + +* Just install FluentSMTP plugin to your site. +* Go to Settings -> FluentSMTP. +* It will automatically show previous configuration from "Easy WP SMTP". +* Click "Import From Easy WP SMTP" button and that's it. +* Disable "Easy WP SMTP" and enjoy FluentSMTP. + +== What's Next == +If you like this plugin, then consider checking out our other plugins: + + + + +== Installation == + +1. Install Fluent SMTP either via the WordPress.org plugin repository or by uploading the files to your server. +2. Activate WP Fluent SMTP. +3. Navigate to the Settings area of Fluent SMTP in the WordPress admin. +4. Choose your SMTP option (Mailgun SMTP, SendGrid SMTP, Amazon SES, or Other SMTP) and follow the instructions to set it up. +5. Need more help? Get support with WPManageNinja Support. + +== Frequently Asked Questions == += Can I send email via SMTP from my WordPress site using this plugin? = + +Yes, FluentSMTP plugin's aim is to let you deliver your WordPress emails securely and as fast as possible. + + = Can I connect Amazon SES API with FluentSMTP? = + +Yes, FluentSMTP let you connect with native via Amazon SES API Key and Secret key and on the top of it, We optimize the api connection to send faster. + + = Can I store my Email Service Credentials to wp-config.php file? = + +Yes, when you create your connection you can choose how you want to store your connection credential. You can store at database or store at wp-config.php (recommended) file. + + = Can I send WordPress mails with SendGrid? = + + Yes, FluentSMTP let you connect your SendGrid via API. It's faster and reliable than SendGrid SMTP. But if you prefer SendGrid SMTP connection, You can also connect with that too. + + = Can I send WordPress mails with Mailgun? = + + Yes, FluentSMTP let you connect your Mailgun via API Key. It's faster and reliable than Mailgun SMTP. But if you prefer Mailgun SMTP connection, You can also connect with that too. + + = Can I send WordPress mails with Sendinblue? = + + Yes, FluentSMTP let you connect your Sendinblue email sending service via API Key. It's faster and reliable. But if you prefer Sendinblue SMTP connection, You can also connect with that too. + + = Can I send WordPress mails with SparkPost? = + + Yes, FluentSMTP let you connect your SparkPost email sending service via API Key. + += Can I send WordPress mails with Pepipost? = + +Yes, FluentSMTP let you connect your Pepipost email sending service via secure API Key. + += I am a developer, Where I can contribute to this project? = + +Thank you so much. We really appreciate it. Please check our github repository for more details. + += I found a bug, where I can report? = + +Please submit an issue in our support portal. If you are a developer please create a github issue. + += I found a security issue, where can I report it? = +We use Patchstack to manage our security report. Please report in the patchstack page. + +== Screenshots == +1. FluentSMTP Dashboard +2. Setting up a connection +3. Settings Overview +4. Sending a test email +5. Email Logs +6. View Email from Log + + +== Changelog == + += 2.2.95 (Date: Dec 28, 2025) = +- Added Multiple Notification Channels for Email Failure Notification +- Added Pushover Notification Support +- Added toSend Email Sending Provider +- Added Option to disable API Keys Encryption +- Fixed PHP 8.4 Compatibility Issues +- + += 2.2.92 (Date: Aug 27, 2025) = +- Fixed attachment handling issue with Elastic Email. +- Resolved import statement issue for SMTP2GO. +- Added PHP 8.4 support for FluentMail\App\Services\Mailer\Manager. +- Added new Amazon SES region: ap-northeast-3 (Asia Pacific – Osaka). +- Improved error handling in BaseHandler. +- Updated fallback email handling to return true on success. +- General bug fixes and performance improvements. +- Fix: Logger Resend Email respects Content-Type for HTML emails +- Styling Improvements +- Fix: Prevent redundant navigation error in Logs screen when refreshing +- Fix: Ensure Content-Type header is always logged for accurate email resends + += 2.2.90 (Date: Feb 07, 2025) = +- Added SMTP2GO Provider +- Improved Translations +- Added name attribute to attachment files +- Security: Updated Google SDK Library to the latest version & updated JS DomPurify Library +- Fixed: Email Failed Notification Issue with Slack +- Styling Improvements + += 2.2.83 (Date: Nov 22, 2024) = +- Fix unserialize parameter issue + += 2.2.82 (Date: Nov 22, 2024) = +- Security: Data Un-serialization issue fixed +- Sparkpost Recipient Issue fixed + += 2.2.81 (Date: Oct 20, 2024) = +* Security: Nonce Verification fixed for slack REQUEST (props to patchstack) +* Fixed WooCommerce Emailing Issue fixed when enabled text mode +* Fixed Translation issues +* Custom Header support for Postmark + += 2.2.80 (Date: July 02, 2024) = +* Added Plain Text Support: Convert HTML Emails to Plain Text and send as multi-part email +* Improved Translations +* Improved Internal Code Base + += 2.2.73 (Date: Apr 25, 2024) = +* Compatibility with PHP 8.X +* Added Day of the time sending chart + += 2.2.72 (Date: Mar 16, 2024) = +* Compatibility with PHP 8.4 +* Fix Slack Notification Issue + += 2.2.71 (Date: Jan 01, 2024 = +* Hot Fix: Fixing the issue with Input Fields + += 2.2.7 (Date: Jan 01, 2024) = +* Added RealTime Email Failure Notification via Telegram / Slack / Discord +* Added Option to add additional email addresses for Amazon SES +* UI Improvements + += 2.2.6 (Date: Oct 01, 2023) = +* Enable Encryption for All SMTP Connections Keys +* Migrate SendInBlue API to Brevo API +* Improved Plugin Conflict Detection and auto fix +* Fixed UI conflict with Other Plugins + += 2.2.5 (Date: Jul 06, 2023) = +* (Security Fix) Email subject is now sanitized and escaped when preview +* Showing Server Response by default on log +* Fix http_build_query issue for latest version of PHP +* Improved UI & UX for email preview + += 2.2.4 (Date: Feb 04, 2023) = +* Email preview is now sanitized +* you can now define `FLUENTMAIL_SIMULATE_EMAILS` to simulate emails programtically +* Fixed outlook API connection issues +* Fixed inline documentation links +* UX improvements + += 2.2.2 (Date: Nov 11, 2022) = +* Fix vendor Conflict for Google/Gmail Connection +* UI Improvement on Connection Wizard + += 2.2.1 (Date: Nov 08, 2022) = +* Refactored Google API integration +* Fix encoding issues for Outlook API connection +* ElasticEmail Attachment issues fixed +* Fixed digest email esc_* issues +* Added contributors to the plugin's about page. +* UI&UX Improvements + += 2.2.0 (Date: Aug 21, 2022) = +* Added Elastic Mail API +* PHP 8.0 & 8.1 compatibility +* UI Improvements + += 2.1.2 (Date: July 05, 2022) = +* Google/Gmail API Upgrade +* UI Improvements + += 2.1.1 (Date: March 12, 2022) = +* Improved Email Logging Screen +* Improved UI and Settings +* Fixed auto-delete old email logs + += 2.1.0 (Date: October 24, 2021) = +* Fix Cron Issues +* PHP 8.0 Compatibility issue fixed +* Multiple Connection UX improvement +* Ability to remove from email and name hook via filter + += 2.0.2 (Date: September 21, 2021) = +* Fixed Scheduled Database Cleanup +* Improvement on wp_mail loading and sending emails +* Pepipost Driver Improvement +* SendGrid Driver Improvement +* SendinBlue Drive Improvement + += 2.0.1 (Date: July 28, 2021) = +* Added Postmark API Connection +* Fix Dashboard Stat Number +* Fix Sanitization Issue + += 2.0.0 (Date: July 27, 2021) = +* Added Outlook / Office 365 API Connection +* Improvements of Amazon SES Connection +* Ability to disable force From Email for supported connections +* Added Fallback Connection feature +* Added One-Click migration from WP Mail SMTP Plugin +* Added One-Click migration from WP Easy SMTP Plugin +* UI Improvements +* Added nonce and sanitization for connection inputs + += 1.2.0 (Date: May 26, 2021) = +* Added Gmail and Google Workspace API Connection +* Added Built-in Docs +* UI Improvements +* PHP 8 compatibility issue fixed +* Bulk Send Emails from logs +* Added Email Simulator +* Amazon API Fix + += 1.1.1 (Date: April 26, 2021) = +* Database Warning Issue Fixed + += 1.1.0 (Date: April 25, 2021) = +* Fix Error Handling Issues +* DataBase Query Optimizations +* Amazon SES Connection Optimization +* UI Improvement +* VueJS loading improvements + += 1.0.1 (Date: January 24, 2021) = +* Fix UTF-8 issues +* Sendinblue wp-config constant issue fixed +* Fallback from name issue fixed +* Search for Email Logs has been fixed + += 1.0.0 (Date: January 18, 2021) = +* Initial Launch +* 349 git commits so far +* 698 cup of coffee (Just kidding, We lost count) +* Work of 3 Months +* Let's Make Email Sending Easier! + +== Upgrade Notice == +The latest Version is compatible with previous version, So nothing to worry diff --git a/wp-content/plugins/gustavoo-portfolio-core/gustavoo-portfolio-core.php b/wp-content/plugins/gustavoo-portfolio-core/gustavoo-portfolio-core.php new file mode 100644 index 0000000..a33b912 --- /dev/null +++ b/wp-content/plugins/gustavoo-portfolio-core/gustavoo-portfolio-core.php @@ -0,0 +1,90 @@ + + */ + function gso_portfolio_get_settings() { + return GSO_Portfolio_Settings::get_settings(); + } +} + +if ( ! function_exists( 'gso_portfolio_get_setting' ) ) { + /** + * Return one portfolio setting. + * + * @param string $key Setting key. + * @param mixed $default Fallback value. + * @return mixed + */ + function gso_portfolio_get_setting( $key, $default = null ) { + $settings = gso_portfolio_get_settings(); + + return array_key_exists( $key, $settings ) ? $settings[ $key ] : $default; + } +} + +if ( ! function_exists( 'gso_render_fluent_form' ) ) { + /** + * Render a Fluent Form using a trusted numeric ID. + * + * @param int $form_id Fluent Forms form ID. + * @return string + */ + function gso_render_fluent_form( $form_id ) { + $form_id = absint( $form_id ); + + if ( ! $form_id || ! shortcode_exists( 'fluentform' ) ) { + return ''; + } + + return do_shortcode( sprintf( '[fluentform id="%d"]', $form_id ) ); + } +} + +if ( ! function_exists( 'gso_portfolio_render_fluent_form' ) ) { + /** + * Backwards-compatible descriptive alias for the form helper. + * + * @param int $form_id Fluent Forms form ID. + * @return string + */ + function gso_portfolio_render_fluent_form( $form_id ) { + return gso_render_fluent_form( $form_id ); + } +} diff --git a/wp-content/plugins/gustavoo-portfolio-core/includes/class-gso-newsletter-widget.php b/wp-content/plugins/gustavoo-portfolio-core/includes/class-gso-newsletter-widget.php new file mode 100644 index 0000000..d071007 --- /dev/null +++ b/wp-content/plugins/gustavoo-portfolio-core/includes/class-gso-newsletter-widget.php @@ -0,0 +1,110 @@ + 'widget_gso_newsletter', + 'description' => __( 'Exibe o formulário global de newsletter configurado no Portfólio.', 'gustavoo-portfolio-core' ), + 'customize_selective_refresh' => true, + ) + ); + } + + /** + * Render the widget. + * + * @param array $args Sidebar wrappers. + * @param array $instance Saved settings. + * @return void + */ + public function widget( $args, $instance ) { + $settings = GSO_Portfolio_Settings::get_settings(); + $title = ! empty( $instance['title'] ) ? (string) $instance['title'] : (string) $settings['newsletter_heading']; + $text = ! empty( $instance['text'] ) ? (string) $instance['text'] : (string) $settings['newsletter_text']; + $form_id = ! empty( $instance['form_id'] ) ? absint( $instance['form_id'] ) : absint( $settings['newsletter_form_id'] ); + + echo $args['before_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Registered sidebar wrapper. + + if ( $title ) { + echo $args['before_title']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Registered sidebar wrapper. + echo esc_html( $title ); + echo $args['after_title']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Registered sidebar wrapper. + } + + if ( $text ) { + printf( '

    %s

    ', esc_html( $text ) ); + } + + $form = gso_render_fluent_form( $form_id ); + if ( $form ) { + echo $form; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Output is generated by Fluent Forms. + } elseif ( current_user_can( 'manage_options' ) ) { + printf( + '

    %2$s

    ', + esc_url( admin_url( 'edit.php?post_type=' . GSO_Projects::POST_TYPE . '&page=' . GSO_Portfolio_Settings::PAGE_SLUG ) ), + esc_html__( 'Configure o formulário de newsletter.', 'gustavoo-portfolio-core' ) + ); + } + + echo $args['after_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Registered sidebar wrapper. + } + + /** + * Render widget settings. + * + * @param array $instance Saved settings. + * @return void + */ + public function form( $instance ) { + $title = isset( $instance['title'] ) ? (string) $instance['title'] : ''; + $text = isset( $instance['text'] ) ? (string) $instance['text'] : ''; + $form_id = isset( $instance['form_id'] ) ? absint( $instance['form_id'] ) : 0; + ?> +

    + + +

    +

    + + +

    +

    + + +

    + $new_instance New values. + * @param array $old_instance Previous values. + * @return array + */ + public function update( $new_instance, $old_instance ) { + unset( $old_instance ); + + return array( + 'title' => isset( $new_instance['title'] ) ? sanitize_text_field( $new_instance['title'] ) : '', + 'text' => isset( $new_instance['text'] ) ? sanitize_textarea_field( $new_instance['text'] ) : '', + 'form_id' => isset( $new_instance['form_id'] ) ? absint( $new_instance['form_id'] ) : 0, + ); + } +} diff --git a/wp-content/plugins/gustavoo-portfolio-core/includes/class-gso-portfolio-core.php b/wp-content/plugins/gustavoo-portfolio-core/includes/class-gso-portfolio-core.php new file mode 100644 index 0000000..c5f330f --- /dev/null +++ b/wp-content/plugins/gustavoo-portfolio-core/includes/class-gso-portfolio-core.php @@ -0,0 +1,89 @@ + + */ + public static function defaults() { + return array( + 'hero_eyebrow' => __( 'Desenvolvimento de ponta a ponta', 'gustavoo-portfolio-core' ), + 'hero_title' => __( 'Transformo ideias em produtos digitais sólidos.', 'gustavoo-portfolio-core' ), + 'hero_description' => __( 'Gustavo, Desenvolvedor Full Stack Sênior. Web, mobile, infraestrutura e automação para tirar projetos do papel e fazê-los crescer.', 'gustavoo-portfolio-core' ), + 'hero_primary_label' => __( 'Ver projetos', 'gustavoo-portfolio-core' ), + 'hero_primary_url' => '#projetos', + 'hero_secondary_label' => __( 'Fale comigo', 'gustavoo-portfolio-core' ), + 'hero_secondary_url' => '#contato', + 'about_title' => __( 'Responsabilidade técnica do planejamento à produção.', 'gustavoo-portfolio-core' ), + 'about_text' => __( 'Mais do que escrever código, meu objetivo é garantir que seu projeto saia do papel e funcione exatamente como planejado. Assumo a responsabilidade técnica de ponta a ponta para que você foque no que importa: crescer.', 'gustavoo-portfolio-core' ), + 'about_secondary_text' => __( 'Já participei do desenvolvimento e lançamento de múltiplos projetos digitais, atuando da arquitetura técnica à divulgação e ao crescimento das plataformas. Essa combinação de visão técnica e entendimento de mercado orienta cada entrega.', 'gustavoo-portfolio-core' ), + 'contact_heading' => __( 'Tem um projeto em mente?', 'gustavoo-portfolio-core' ), + 'contact_text' => __( 'Conte o que você precisa construir ou melhorar. Responderei com os próximos passos para transformar a ideia em uma entrega concreta.', 'gustavoo-portfolio-core' ), + 'contact_form_id' => 0, + 'newsletter_heading' => __( 'Ideias úteis, direto na sua caixa de entrada.', 'gustavoo-portfolio-core' ), + 'newsletter_text' => __( 'Receba conteúdos sobre desenvolvimento, produto, infraestrutura e automação. Sem ruído e sem spam.', 'gustavoo-portfolio-core' ), + 'newsletter_form_id' => 0, + 'projects_title' => __( 'Projetos selecionados', 'gustavoo-portfolio-core' ), + 'projects_text' => __( 'Uma seleção de produtos, experiências e soluções que ajudei a colocar no mundo.', 'gustavoo-portfolio-core' ), + 'projects_limit' => 6, + 'blog_title' => __( 'Código, produto e bastidores', 'gustavoo-portfolio-core' ), + 'blog_text' => __( 'Análises práticas sobre desenvolvimento, infraestrutura, automação e crescimento de produtos digitais.', 'gustavoo-portfolio-core' ), + 'blog_limit' => 4, + 'email' => 'contato@gustavoo.me', + 'whatsapp' => '+55 31 99516-8069', + 'whatsapp_message' => __( 'Olá! Gostaria de falar sobre um projeto.', 'gustavoo-portfolio-core' ), + 'github_url' => 'https://github.com/gustavooth', + 'linkedin_url' => '', + 'instagram_url' => '', + 'availability_label' => __( 'Disponível para novos projetos', 'gustavoo-portfolio-core' ), + ); + } + + /** + * Add missing defaults without overwriting existing values. + * + * @return void + */ + public static function ensure_defaults() { + $existing = get_option( self::OPTION_NAME, null ); + + if ( null === $existing || ! is_array( $existing ) ) { + add_option( self::OPTION_NAME, self::defaults(), '', false ); + return; + } + + $merged = array_merge( self::defaults(), $existing ); + if ( $merged !== $existing ) { + update_option( self::OPTION_NAME, $merged, false ); + } + } + + /** + * Return settings merged with defaults. + * + * @return array + */ + public static function get_settings() { + $settings = get_option( self::OPTION_NAME, array() ); + + return array_merge( self::defaults(), is_array( $settings ) ? $settings : array() ); + } + + /** + * Add the Settings submenu below Portfolio. + * + * @return void + */ + public static function add_settings_page() { + add_submenu_page( + 'edit.php?post_type=' . GSO_Projects::POST_TYPE, + __( 'Configurações do portfólio', 'gustavoo-portfolio-core' ), + __( 'Configurações', 'gustavoo-portfolio-core' ), + 'manage_options', + self::PAGE_SLUG, + array( __CLASS__, 'render_settings_page' ) + ); + } + + /** + * Register the option, sections and fields. + * + * @return void + */ + public static function register_settings() { + register_setting( + self::OPTION_GROUP, + self::OPTION_NAME, + array( + 'type' => 'array', + 'description' => __( 'Configurações compartilhadas pelo tema Gustavo Portfolio.', 'gustavoo-portfolio-core' ), + 'sanitize_callback' => array( __CLASS__, 'sanitize_settings' ), + 'default' => self::defaults(), + ) + ); + + $sections = self::sections(); + $fields = self::fields(); + + foreach ( $sections as $section_id => $section ) { + add_settings_section( + $section_id, + $section['title'], + array( __CLASS__, 'render_section_description' ), + self::PAGE_SLUG, + array( 'description' => $section['description'] ) + ); + } + + foreach ( $fields as $key => $field ) { + add_settings_field( + 'gso-setting-' . str_replace( '_', '-', $key ), + $field['label'], + array( __CLASS__, 'render_field' ), + self::PAGE_SLUG, + $field['section'], + array_merge( $field, array( 'key' => $key ) ) + ); + } + } + + /** + * Settings page section definitions. + * + * @return array> + */ + private static function sections() { + return array( + 'gso-settings-hero' => array( + 'title' => __( 'Hero', 'gustavoo-portfolio-core' ), + 'description' => __( 'Mensagem principal e chamadas para ação da página inicial.', 'gustavoo-portfolio-core' ), + ), + 'gso-settings-about' => array( + 'title' => __( 'Sobre', 'gustavoo-portfolio-core' ), + 'description' => __( 'Apresentação profissional exibida na seção Sobre.', 'gustavoo-portfolio-core' ), + ), + 'gso-settings-content' => array( + 'title' => __( 'Projetos e blog', 'gustavoo-portfolio-core' ), + 'description' => __( 'Títulos, textos de apoio e limites das listagens da página inicial.', 'gustavoo-portfolio-core' ), + ), + 'gso-settings-contact' => array( + 'title' => __( 'Contato', 'gustavoo-portfolio-core' ), + 'description' => __( 'Canais diretos e formulário principal do Fluent Forms.', 'gustavoo-portfolio-core' ), + ), + 'gso-settings-newsletter' => array( + 'title' => __( 'Newsletter', 'gustavoo-portfolio-core' ), + 'description' => __( 'Conteúdo e formulário usados pelo CTA e pelo widget de newsletter.', 'gustavoo-portfolio-core' ), + ), + 'gso-settings-social' => array( + 'title' => __( 'Redes sociais', 'gustavoo-portfolio-core' ), + 'description' => __( 'Perfis públicos exibidos no tema.', 'gustavoo-portfolio-core' ), + ), + ); + } + + /** + * Settings field schema. + * + * @return array> + */ + private static function fields() { + return array( + 'hero_eyebrow' => self::field( 'gso-settings-hero', __( 'Sobretítulo', 'gustavoo-portfolio-core' ), 'text' ), + 'hero_title' => self::field( 'gso-settings-hero', __( 'Título', 'gustavoo-portfolio-core' ), 'text' ), + 'hero_description' => self::field( 'gso-settings-hero', __( 'Descrição', 'gustavoo-portfolio-core' ), 'textarea' ), + 'hero_primary_label' => self::field( 'gso-settings-hero', __( 'CTA principal — texto', 'gustavoo-portfolio-core' ), 'text' ), + 'hero_primary_url' => self::field( 'gso-settings-hero', __( 'CTA principal — destino', 'gustavoo-portfolio-core' ), 'link', __( 'Aceita URL HTTP(S), mailto:, tel:, caminho relativo ou âncora como #projetos.', 'gustavoo-portfolio-core' ) ), + 'hero_secondary_label' => self::field( 'gso-settings-hero', __( 'CTA secundário — texto', 'gustavoo-portfolio-core' ), 'text' ), + 'hero_secondary_url' => self::field( 'gso-settings-hero', __( 'CTA secundário — destino', 'gustavoo-portfolio-core' ), 'link', __( 'Aceita URL HTTP(S), mailto:, tel:, caminho relativo ou âncora.', 'gustavoo-portfolio-core' ) ), + 'about_title' => self::field( 'gso-settings-about', __( 'Título', 'gustavoo-portfolio-core' ), 'text' ), + 'about_text' => self::field( 'gso-settings-about', __( 'Texto', 'gustavoo-portfolio-core' ), 'textarea' ), + 'projects_title' => self::field( 'gso-settings-content', __( 'Projetos — título', 'gustavoo-portfolio-core' ), 'text' ), + 'projects_text' => self::field( 'gso-settings-content', __( 'Projetos — texto', 'gustavoo-portfolio-core' ), 'textarea' ), + 'projects_limit' => self::field( 'gso-settings-content', __( 'Projetos — quantidade', 'gustavoo-portfolio-core' ), 'number', __( 'Entre 1 e 24.', 'gustavoo-portfolio-core' ), array( 'min' => 1, 'max' => 24 ) ), + 'blog_title' => self::field( 'gso-settings-content', __( 'Blog — título', 'gustavoo-portfolio-core' ), 'text' ), + 'blog_text' => self::field( 'gso-settings-content', __( 'Blog — texto', 'gustavoo-portfolio-core' ), 'textarea' ), + 'blog_limit' => self::field( 'gso-settings-content', __( 'Blog — quantidade', 'gustavoo-portfolio-core' ), 'number', __( 'Entre 1 e 24.', 'gustavoo-portfolio-core' ), array( 'min' => 1, 'max' => 24 ) ), + 'contact_heading' => self::field( 'gso-settings-contact', __( 'Título', 'gustavoo-portfolio-core' ), 'text' ), + 'contact_text' => self::field( 'gso-settings-contact', __( 'Texto', 'gustavoo-portfolio-core' ), 'textarea' ), + 'contact_form_id' => self::field( 'gso-settings-contact', __( 'ID do formulário de contato', 'gustavoo-portfolio-core' ), 'number', __( 'ID numérico do Fluent Forms. O seed preenche este campo quando possível.', 'gustavoo-portfolio-core' ), array( 'min' => 0 ) ), + 'email' => self::field( 'gso-settings-contact', __( 'E-mail', 'gustavoo-portfolio-core' ), 'email' ), + 'whatsapp' => self::field( 'gso-settings-contact', __( 'WhatsApp', 'gustavoo-portfolio-core' ), 'tel', __( 'Inclua o DDI, por exemplo +55 31 99999-9999.', 'gustavoo-portfolio-core' ) ), + 'whatsapp_message' => self::field( 'gso-settings-contact', __( 'WhatsApp — mensagem inicial', 'gustavoo-portfolio-core' ), 'textarea', __( 'Texto que será preenchido automaticamente ao abrir o botão flutuante.', 'gustavoo-portfolio-core' ) ), + 'newsletter_heading' => self::field( 'gso-settings-newsletter', __( 'Título', 'gustavoo-portfolio-core' ), 'text' ), + 'newsletter_text' => self::field( 'gso-settings-newsletter', __( 'Texto', 'gustavoo-portfolio-core' ), 'textarea' ), + 'newsletter_form_id' => self::field( 'gso-settings-newsletter', __( 'ID do formulário de newsletter', 'gustavoo-portfolio-core' ), 'number', __( 'ID numérico do Fluent Forms. O seed preenche este campo quando possível.', 'gustavoo-portfolio-core' ), array( 'min' => 0 ) ), + 'github_url' => self::field( 'gso-settings-social', __( 'GitHub', 'gustavoo-portfolio-core' ), 'url' ), + 'linkedin_url' => self::field( 'gso-settings-social', __( 'LinkedIn', 'gustavoo-portfolio-core' ), 'url' ), + 'instagram_url' => self::field( 'gso-settings-social', __( 'Instagram', 'gustavoo-portfolio-core' ), 'url' ), + ); + } + + /** + * Build one field definition. + * + * @param string $section Section ID. + * @param string $label Label. + * @param string $type Field type. + * @param string $description Help text. + * @param array $extra Extra attributes. + * @return array + */ + private static function field( $section, $label, $type, $description = '', $extra = array() ) { + return array_merge( + array( + 'section' => $section, + 'label' => $label, + 'type' => $type, + 'description' => $description, + ), + $extra + ); + } + + /** + * Sanitize every documented setting and retain unknown extension keys. + * + * @param mixed $input Raw submitted option. + * @return array + */ + public static function sanitize_settings( $input ) { + $current = get_option( self::OPTION_NAME, array() ); + $output = array_merge( self::defaults(), is_array( $current ) ? $current : array() ); + $input = is_array( $input ) ? $input : array(); + + foreach ( self::fields() as $key => $field ) { + $value = $input[ $key ] ?? ''; + + switch ( $field['type'] ) { + case 'textarea': + $output[ $key ] = sanitize_textarea_field( $value ); + break; + case 'email': + $output[ $key ] = sanitize_email( $value ); + break; + case 'tel': + $output[ $key ] = self::sanitize_phone( $value ); + break; + case 'url': + $output[ $key ] = self::sanitize_public_url( $value ); + break; + case 'link': + $output[ $key ] = self::sanitize_link( $value ); + break; + case 'number': + $number = absint( $value ); + $min = isset( $field['min'] ) ? absint( $field['min'] ) : 0; + $max = isset( $field['max'] ) ? absint( $field['max'] ) : PHP_INT_MAX; + $output[ $key ] = min( $max, max( $min, $number ) ); + break; + default: + $output[ $key ] = sanitize_text_field( $value ); + } + } + + return $output; + } + + /** + * Sanitize a public social URL. + * + * @param mixed $value Raw value. + * @return string + */ + private static function sanitize_public_url( $value ) { + $url = esc_url_raw( trim( (string) $value ), array( 'http', 'https' ) ); + + return $url && wp_http_validate_url( $url ) ? $url : ''; + } + + /** + * Sanitize a CTA link, including safe same-page anchors. + * + * @param mixed $value Raw value. + * @return string + */ + private static function sanitize_link( $value ) { + $value = trim( sanitize_text_field( $value ) ); + + if ( preg_match( '/^#[A-Za-z][A-Za-z0-9_:.\-]*$/', $value ) ) { + return $value; + } + + return esc_url_raw( $value, array( 'http', 'https', 'mailto', 'tel' ) ); + } + + /** + * Keep common telephone punctuation and discard everything else. + * + * @param mixed $value Raw phone value. + * @return string + */ + private static function sanitize_phone( $value ) { + $value = sanitize_text_field( $value ); + $value = preg_replace( '/[^0-9+()\-\s]/', '', $value ); + + return is_string( $value ) ? trim( $value ) : ''; + } + + /** + * Render a section introduction. + * + * @param array $args Section arguments. + * @return void + */ + public static function render_section_description( $args ) { + if ( ! empty( $args['description'] ) ) { + echo '

    ' . esc_html( $args['description'] ) . '

    '; + } + } + + /** + * Render a settings field from its schema. + * + * @param array $args Field arguments. + * @return void + */ + public static function render_field( $args ) { + $settings = self::get_settings(); + $key = $args['key']; + $value = $settings[ $key ] ?? ''; + $id = 'gso-setting-' . str_replace( '_', '-', $key ); + $name = self::OPTION_NAME . '[' . $key . ']'; + + if ( 'textarea' === $args['type'] ) { + printf( + '', + esc_attr( $id ), + esc_attr( $name ), + esc_textarea( (string) $value ) + ); + } else { + $html_type = in_array( $args['type'], array( 'email', 'number', 'tel', 'url' ), true ) ? $args['type'] : 'text'; + $attrs = ''; + + if ( isset( $args['min'] ) ) { + $attrs .= ' min="' . esc_attr( (string) $args['min'] ) . '"'; + } + if ( isset( $args['max'] ) ) { + $attrs .= ' max="' . esc_attr( (string) $args['max'] ) . '"'; + } + + printf( + '', + 'number' === $html_type ? 'small-text' : 'regular-text', + esc_attr( $id ), + esc_attr( $name ), + esc_attr( $html_type ), + esc_attr( (string) $value ), + $attrs // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Attributes are assembled from escaped integers. + ); + } + + if ( ! empty( $args['description'] ) ) { + echo '

    ' . esc_html( $args['description'] ) . '

    '; + } + } + + /** + * Render the admin settings page. + * + * @return void + */ + public static function render_settings_page() { + if ( ! current_user_can( 'manage_options' ) ) { + return; + } + ?> +
    +

    +

    + +
    + +
    +
    + +
    +

    +
      +
    • +
    • +
    • +
    • +
    +
    + $links Existing links. + * @return array + */ + public static function plugin_action_links( $links ) { + $url = admin_url( 'edit.php?post_type=' . GSO_Projects::POST_TYPE . '&page=' . self::PAGE_SLUG ); + + array_unshift( + $links, + '' . esc_html__( 'Configurações', 'gustavoo-portfolio-core' ) . '' + ); + + return $links; + } +} diff --git a/wp-content/plugins/gustavoo-portfolio-core/includes/class-gso-projects.php b/wp-content/plugins/gustavoo-portfolio-core/includes/class-gso-projects.php new file mode 100644 index 0000000..f836170 --- /dev/null +++ b/wp-content/plugins/gustavoo-portfolio-core/includes/class-gso-projects.php @@ -0,0 +1,563 @@ + array( + 'name' => __( 'Projetos', 'gustavoo-portfolio-core' ), + 'singular_name' => __( 'Projeto', 'gustavoo-portfolio-core' ), + 'menu_name' => __( 'Portfólio', 'gustavoo-portfolio-core' ), + 'name_admin_bar' => __( 'Projeto', 'gustavoo-portfolio-core' ), + 'add_new' => __( 'Adicionar projeto', 'gustavoo-portfolio-core' ), + 'add_new_item' => __( 'Adicionar novo projeto', 'gustavoo-portfolio-core' ), + 'edit_item' => __( 'Editar projeto', 'gustavoo-portfolio-core' ), + 'new_item' => __( 'Novo projeto', 'gustavoo-portfolio-core' ), + 'view_item' => __( 'Ver projeto', 'gustavoo-portfolio-core' ), + 'search_items' => __( 'Buscar projetos', 'gustavoo-portfolio-core' ), + 'not_found' => __( 'Nenhum projeto encontrado.', 'gustavoo-portfolio-core' ), + 'not_found_in_trash' => __( 'Nenhum projeto encontrado na lixeira.', 'gustavoo-portfolio-core' ), + 'all_items' => __( 'Projetos', 'gustavoo-portfolio-core' ), + 'featured_image' => __( 'Imagem do projeto', 'gustavoo-portfolio-core' ), + 'set_featured_image' => __( 'Definir imagem do projeto', 'gustavoo-portfolio-core' ), + 'remove_featured_image' => __( 'Remover imagem do projeto', 'gustavoo-portfolio-core' ), + ), + 'public' => false, + 'publicly_queryable' => false, + 'exclude_from_search' => true, + 'show_ui' => true, + 'show_in_menu' => true, + 'show_in_admin_bar' => true, + 'show_in_nav_menus' => false, + 'show_in_rest' => true, + 'menu_position' => 25, + 'menu_icon' => 'dashicons-portfolio', + 'capability_type' => 'post', + 'map_meta_cap' => true, + 'hierarchical' => false, + 'has_archive' => false, + 'rewrite' => false, + 'query_var' => false, + 'supports' => array( 'title', 'editor', 'excerpt', 'thumbnail', 'page-attributes', 'revisions', 'custom-fields' ), + ) + ); + + register_taxonomy( + self::TAX_TYPE, + self::POST_TYPE, + array( + 'labels' => array( + 'name' => __( 'Tipos de projeto', 'gustavoo-portfolio-core' ), + 'singular_name' => __( 'Tipo de projeto', 'gustavoo-portfolio-core' ), + 'menu_name' => __( 'Tipos', 'gustavoo-portfolio-core' ), + 'all_items' => __( 'Todos os tipos', 'gustavoo-portfolio-core' ), + 'edit_item' => __( 'Editar tipo', 'gustavoo-portfolio-core' ), + 'add_new_item' => __( 'Adicionar tipo', 'gustavoo-portfolio-core' ), + 'search_items' => __( 'Buscar tipos', 'gustavoo-portfolio-core' ), + ), + 'public' => false, + 'publicly_queryable' => false, + 'show_ui' => true, + 'show_admin_column' => false, + 'show_in_rest' => true, + 'hierarchical' => true, + 'rewrite' => false, + ) + ); + + register_taxonomy( + self::TAX_TECH, + self::POST_TYPE, + array( + 'labels' => array( + 'name' => __( 'Tecnologias', 'gustavoo-portfolio-core' ), + 'singular_name' => __( 'Tecnologia', 'gustavoo-portfolio-core' ), + 'menu_name' => __( 'Tecnologias', 'gustavoo-portfolio-core' ), + 'all_items' => __( 'Todas as tecnologias', 'gustavoo-portfolio-core' ), + 'edit_item' => __( 'Editar tecnologia', 'gustavoo-portfolio-core' ), + 'add_new_item' => __( 'Adicionar tecnologia', 'gustavoo-portfolio-core' ), + 'search_items' => __( 'Buscar tecnologias', 'gustavoo-portfolio-core' ), + 'separate_items_with_commas' => __( 'Separe tecnologias com vírgulas', 'gustavoo-portfolio-core' ), + ), + 'public' => false, + 'publicly_queryable' => false, + 'show_ui' => true, + 'show_admin_column' => false, + 'show_in_rest' => true, + 'hierarchical' => false, + 'rewrite' => false, + ) + ); + + self::register_meta(); + } + + /** + * Register project metadata. + * + * @return void + */ + private static function register_meta() { + $common = array( + 'single' => true, + 'show_in_rest' => true, + 'auth_callback' => array( __CLASS__, 'authorize_meta' ), + ); + + register_post_meta( + self::POST_TYPE, + self::META_URL, + array_merge( + $common, + array( + 'type' => 'string', + 'sanitize_callback' => array( __CLASS__, 'sanitize_external_url' ), + ) + ) + ); + + foreach ( array( self::META_CLIENT, self::META_LINK_LABEL ) as $meta_key ) { + register_post_meta( + self::POST_TYPE, + $meta_key, + array_merge( + $common, + array( + 'type' => 'string', + 'sanitize_callback' => 'sanitize_text_field', + ) + ) + ); + } + + register_post_meta( + self::POST_TYPE, + self::META_YEAR, + array_merge( + $common, + array( + 'type' => 'integer', + 'sanitize_callback' => array( __CLASS__, 'sanitize_year' ), + ) + ) + ); + + register_post_meta( + self::POST_TYPE, + self::META_FEATURED, + array_merge( + $common, + array( + 'type' => 'boolean', + 'sanitize_callback' => 'rest_sanitize_boolean', + ) + ) + ); + + register_post_meta( + self::POST_TYPE, + self::META_ACCENT, + array_merge( + $common, + array( + 'type' => 'string', + 'sanitize_callback' => 'sanitize_hex_color', + ) + ) + ); + } + + /** + * Restrict meta updates to users who can edit the project. + * + * @param bool $allowed Current authorization result. + * @param string $meta_key Meta key. + * @param int $post_id Project ID. + * @return bool + */ + public static function authorize_meta( $allowed, $meta_key, $post_id ) { + unset( $allowed, $meta_key ); + + return current_user_can( 'edit_post', (int) $post_id ); + } + + /** + * Allow only valid external HTTP(S) URLs. + * + * @param mixed $value Raw URL. + * @return string + */ + public static function sanitize_external_url( $value ) { + $url = esc_url_raw( trim( (string) $value ), array( 'http', 'https' ) ); + + return $url && wp_http_validate_url( $url ) ? $url : ''; + } + + /** + * Validate a four-digit project year. + * + * @param mixed $value Raw year. + * @return int + */ + public static function sanitize_year( $value ) { + $year = absint( $value ); + $max_year = (int) gmdate( 'Y' ) + 5; + + return $year >= 1900 && $year <= $max_year ? $year : 0; + } + + /** + * Register the project details metabox. + * + * @return void + */ + public static function add_meta_boxes() { + add_meta_box( + 'gso-project-details', + __( 'Detalhes do projeto', 'gustavoo-portfolio-core' ), + array( __CLASS__, 'render_meta_box' ), + self::POST_TYPE, + 'normal', + 'high' + ); + } + + /** + * Render project fields. + * + * @param WP_Post $post Current project. + * @return void + */ + public static function render_meta_box( $post ) { + $url = (string) get_post_meta( $post->ID, self::META_URL, true ); + $client = (string) get_post_meta( $post->ID, self::META_CLIENT, true ); + $year = absint( get_post_meta( $post->ID, self::META_YEAR, true ) ); + $featured = (bool) get_post_meta( $post->ID, self::META_FEATURED, true ); + $link_label = (string) get_post_meta( $post->ID, self::META_LINK_LABEL, true ); + $accent = sanitize_hex_color( get_post_meta( $post->ID, self::META_ACCENT, true ) ); + + if ( ! $accent ) { + $accent = '#7cf3da'; + } + + wp_nonce_field( 'gso_save_project_details', 'gso_project_details_nonce' ); + ?> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + $columns Existing columns. + * @return array + */ + public static function admin_columns( $columns ) { + return array( + 'cb' => $columns['cb'] ?? '', + 'gso_thumbnail' => __( 'Imagem', 'gustavoo-portfolio-core' ), + 'title' => __( 'Projeto', 'gustavoo-portfolio-core' ), + 'gso_client' => __( 'Cliente', 'gustavoo-portfolio-core' ), + 'gso_type' => __( 'Tipo', 'gustavoo-portfolio-core' ), + 'gso_tech' => __( 'Tecnologias', 'gustavoo-portfolio-core' ), + 'gso_year' => __( 'Ano', 'gustavoo-portfolio-core' ), + 'gso_featured' => __( 'Destaque', 'gustavoo-portfolio-core' ), + 'gso_url' => __( 'URL', 'gustavoo-portfolio-core' ), + 'gso_menu_order' => __( 'Ordem', 'gustavoo-portfolio-core' ), + 'date' => $columns['date'] ?? __( 'Data', 'gustavoo-portfolio-core' ), + ); + } + + /** + * Render one project list cell. + * + * @param string $column Column ID. + * @param int $post_id Project ID. + * @return void + */ + public static function render_admin_column( $column, $post_id ) { + switch ( $column ) { + case 'gso_thumbnail': + if ( has_post_thumbnail( $post_id ) ) { + echo wp_kses_post( get_the_post_thumbnail( $post_id, array( 64, 48 ), array( 'style' => 'width:64px;height:48px;object-fit:cover;border-radius:4px;' ) ) ); + } else { + echo '' . esc_html__( 'Sem imagem', 'gustavoo-portfolio-core' ) . ''; + } + break; + case 'gso_client': + echo esc_html( (string) get_post_meta( $post_id, self::META_CLIENT, true ) ?: '—' ); + break; + case 'gso_type': + self::render_terms_column( $post_id, self::TAX_TYPE ); + break; + case 'gso_tech': + self::render_terms_column( $post_id, self::TAX_TECH ); + break; + case 'gso_year': + $year = absint( get_post_meta( $post_id, self::META_YEAR, true ) ); + echo $year ? esc_html( (string) $year ) : '—'; + break; + case 'gso_featured': + if ( get_post_meta( $post_id, self::META_FEATURED, true ) ) { + echo '' . esc_html__( 'Sim', 'gustavoo-portfolio-core' ) . ''; + } else { + echo '' . esc_html__( 'Não', 'gustavoo-portfolio-core' ) . ''; + } + break; + case 'gso_url': + $url = self::sanitize_external_url( get_post_meta( $post_id, self::META_URL, true ) ); + if ( $url ) { + $host = wp_parse_url( $url, PHP_URL_HOST ); + printf( + '%2$s %3$s', + esc_url( $url ), + esc_html( $host ?: $url ), + esc_html__( '(abre em nova aba)', 'gustavoo-portfolio-core' ) + ); + } else { + echo '—'; + } + break; + case 'gso_menu_order': + echo esc_html( (string) get_post_field( 'menu_order', $post_id ) ); + break; + } + } + + /** + * Render taxonomy terms in an admin column. + * + * @param int $post_id Project ID. + * @param string $taxonomy Taxonomy name. + * @return void + */ + private static function render_terms_column( $post_id, $taxonomy ) { + $terms = get_the_terms( $post_id, $taxonomy ); + + if ( ! $terms || is_wp_error( $terms ) ) { + echo '—'; + return; + } + + echo esc_html( implode( ', ', wp_list_pluck( $terms, 'name' ) ) ); + } + + /** + * Make selected columns sortable. + * + * @param array $columns Existing sortable columns. + * @return array + */ + public static function sortable_columns( $columns ) { + $columns['gso_client'] = 'gso_client'; + $columns['gso_year'] = 'gso_year'; + $columns['gso_featured'] = 'gso_featured'; + $columns['gso_menu_order'] = 'menu_order'; + + return $columns; + } + + /** + * Apply safe sorting to the main project admin query. + * + * @param WP_Query $query Current query. + * @return void + */ + public static function apply_admin_sorting( $query ) { + if ( ! is_admin() || ! $query->is_main_query() || self::POST_TYPE !== $query->get( 'post_type' ) ) { + return; + } + + switch ( $query->get( 'orderby' ) ) { + case 'gso_client': + $query->set( 'meta_key', self::META_CLIENT ); + $query->set( 'orderby', 'meta_value' ); + break; + case 'gso_year': + $query->set( 'meta_key', self::META_YEAR ); + $query->set( 'orderby', 'meta_value_num' ); + break; + case 'gso_featured': + $query->set( 'meta_key', self::META_FEATURED ); + $query->set( 'orderby', 'meta_value_num' ); + break; + case 'menu_order': + $query->set( 'orderby', 'menu_order title' ); + break; + } + } + + /** + * Add type and technology filters above the project list. + * + * @param string $post_type Current list post type. + * @return void + */ + public static function taxonomy_filters( $post_type ) { + if ( self::POST_TYPE !== $post_type ) { + return; + } + + foreach ( array( self::TAX_TYPE, self::TAX_TECH ) as $taxonomy ) { + $tax_object = get_taxonomy( $taxonomy ); + if ( ! $tax_object ) { + continue; + } + + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only list filter. + $selected = isset( $_GET[ $taxonomy ] ) ? sanitize_title( wp_unslash( $_GET[ $taxonomy ] ) ) : ''; + + wp_dropdown_categories( + array( + 'show_option_all' => sprintf( + /* translators: %s: taxonomy label. */ + __( 'Todos: %s', 'gustavoo-portfolio-core' ), + $tax_object->labels->name + ), + 'taxonomy' => $taxonomy, + 'name' => $taxonomy, + 'orderby' => 'name', + 'selected' => $selected, + 'hide_empty' => false, + 'hierarchical' => $tax_object->hierarchical, + 'value_field' => 'slug', + ) + ); + } + } +} diff --git a/wp-content/plugins/gustavoo-portfolio-core/includes/class-gso-seeder.php b/wp-content/plugins/gustavoo-portfolio-core/includes/class-gso-seeder.php new file mode 100644 index 0000000..2cc110f --- /dev/null +++ b/wp-content/plugins/gustavoo-portfolio-core/includes/class-gso-seeder.php @@ -0,0 +1,619 @@ + + */ + public static function get_state() { + $state = get_option( self::STATE_OPTION, array() ); + + return wp_parse_args( + is_array( $state ) ? $state : array(), + array( + 'version' => '', + 'status' => 'pending', + 'projects_seeded' => false, + 'contact_form_id' => 0, + 'newsletter_form_id' => 0, + 'crm_feeds' => false, + 'message' => '', + ) + ); + } + + /** + * Complete integration setup when Fluent Forms and FluentCRM are ready. + * + * @return void + */ + public static function maybe_seed() { + self::seed_site_configuration(); + + $state = self::get_state(); + + if ( self::VERSION === $state['version'] && 'complete' === $state['status'] ) { + self::maybe_migrate_contact_form(); + return; + } + + if ( ! post_type_exists( GSO_Projects::POST_TYPE ) ) { + GSO_Projects::register_content_types(); + } + + self::seed_projects(); + + if ( ! self::fluent_is_ready() ) { + $state['projects_seeded'] = true; + $state['status'] = 'waiting'; + $state['message'] = __( 'Aguardando Fluent Forms e FluentCRM ativos.', 'gustavoo-portfolio-core' ); + update_option( self::STATE_OPTION, $state, false ); + return; + } + + if ( ! add_option( self::LOCK_OPTION, time(), '', false ) ) { + return; + } + + try { + $contact_id = self::upsert_form( 'contact', __( 'Site — Contato e orçamento', 'gustavoo-portfolio-core' ), true ); + $newsletter_id = self::upsert_form( 'newsletter', __( 'Site — Newsletter', 'gustavoo-portfolio-core' ), false ); + + self::upsert_crm_feed( $contact_id, 'contact' ); + self::upsert_crm_feed( $newsletter_id, 'newsletter' ); + self::save_form_ids( $contact_id, $newsletter_id ); + + update_option( + self::STATE_OPTION, + array( + 'version' => self::VERSION, + 'status' => 'complete', + 'projects_seeded' => true, + 'contact_form_id' => $contact_id, + 'newsletter_form_id' => $newsletter_id, + 'crm_feeds' => true, + 'message' => __( 'Formulários e feeds do FluentCRM configurados.', 'gustavoo-portfolio-core' ), + ), + false + ); + } catch ( Throwable $error ) { + $state['status'] = 'error'; + $state['message'] = sanitize_text_field( $error->getMessage() ); + update_option( self::STATE_OPTION, $state, false ); + } finally { + delete_option( self::LOCK_OPTION ); + } + } + + /** + * Configure the WordPress pieces that make the bundled theme work on a + * fresh installation. + * + * Existing sites are left untouched: each value is written only when the + * corresponding WordPress setting still has its fresh-install value. + * + * @return void + */ + private static function seed_site_configuration() { + $state = get_option( self::SITE_OPTION, array() ); + + if ( ! is_array( $state ) || empty( $state['pages'] ) ) { + $home_id = self::ensure_seed_page( 'inicio', __( 'Início', 'gustavoo-portfolio-core' ) ); + $blog_id = self::ensure_seed_page( 'blog', __( 'Blog', 'gustavoo-portfolio-core' ) ); + + if ( $home_id && $blog_id && 'posts' === get_option( 'show_on_front' ) ) { + update_option( 'show_on_front', 'page' ); + update_option( 'page_on_front', $home_id ); + update_option( 'page_for_posts', $blog_id ); + } + + if ( $home_id && $blog_id ) { + $state = is_array( $state ) ? $state : array(); + $state['pages'] = true; + update_option( self::SITE_OPTION, $state, false ); + } + } + + if ( empty( $state['widgets'] ) ) { + self::seed_theme_widgets(); + $state = is_array( $state ) ? $state : array(); + $state['widgets'] = true; + update_option( self::SITE_OPTION, $state, false ); + } + } + + /** + * Create a required empty page once and identify it by a private marker. + * + * @param string $slug Page slug. + * @param string $title Page title. + * @return int + */ + private static function ensure_seed_page( $slug, $title ) { + $existing = get_posts( + array( + 'post_type' => 'page', + 'post_status' => 'any', + 'posts_per_page' => 1, + 'fields' => 'ids', + 'meta_key' => '_gso_seed_page', + 'meta_value' => $slug, + ) + ); + + if ( $existing ) { + return absint( $existing[0] ); + } + + $page = get_page_by_path( $slug, OBJECT, 'page' ); + if ( $page ) { + update_post_meta( $page->ID, '_gso_seed_page', $slug ); + return absint( $page->ID ); + } + + $page_id = wp_insert_post( + array( + 'post_type' => 'page', + 'post_status' => 'publish', + 'post_title' => $title, + 'post_name' => $slug, + ), + true + ); + + if ( is_wp_error( $page_id ) ) { + return 0; + } + + update_post_meta( $page_id, '_gso_seed_page', $slug ); + return absint( $page_id ); + } + + /** + * Reproduce the sidebar and footer widget arrangement from this site. + * Widgets are added only to empty areas, so a user's arrangement wins. + * + * @return void + */ + private static function seed_theme_widgets() { + $sidebars = wp_get_sidebars_widgets(); + $sidebars = is_array( $sidebars ) ? $sidebars : array(); + + $blog_widgets = isset( $sidebars['sidebar-blog'] ) ? (array) $sidebars['sidebar-blog'] : array(); + $can_seed_blog = empty( $blog_widgets ) || array( 'gustavoo_portfolio_newsletter-1' ) === $blog_widgets; + + if ( $can_seed_blog ) { + $newsletter = get_option( 'widget_gustavoo_portfolio_newsletter', array() ); + $newsletter = is_array( $newsletter ) ? $newsletter : array(); + $newsletter[1] = array( + 'title' => __( 'Newsletter', 'gustavoo-portfolio-core' ), + 'text' => __( 'Receba novos artigos sobre desenvolvimento, infraestrutura e automação.', 'gustavoo-portfolio-core' ), + 'form_id' => 0, + ); + $newsletter['_multiwidget'] = 1; + update_option( 'widget_gustavoo_portfolio_newsletter', $newsletter, false ); + + $blocks = get_option( 'widget_block', array() ); + $blocks = is_array( $blocks ) ? $blocks : array(); + $blocks[2] = array( 'content' => '' ); + $blocks[3] = array( 'content' => '

    Posts recentes

    ' ); + $blocks[4] = array( 'content' => '

    Comentários

    ' ); + $blocks[5] = array( 'content' => '

    Arquivos

    ' ); + $blocks[6] = array( 'content' => '

    Categorias

    ' ); + $blocks['_multiwidget'] = 1; + update_option( 'widget_block', $blocks, false ); + + $sidebars['sidebar-blog'] = array( 'gustavoo_portfolio_newsletter-1', 'block-2', 'block-3', 'block-4' ); + } + + if ( empty( $sidebars['footer-1'] ) ) { + $sidebars['footer-1'] = array( 'block-5', 'block-6' ); + } + + update_option( 'sidebars_widgets', $sidebars, false ); + } + + /** + * Repair the seeded contact form when an earlier migration did not persist. + * + * @return void + */ + private static function maybe_migrate_contact_form() { + if ( ! self::fluent_is_ready() ) { + return; + } + + $form = self::find_seeded_form( 'contact' ); + + if ( ! $form ) { + return; + } + + $fields = json_decode( (string) $form->form_fields, true ); + $has_message = false; + + foreach ( (array) ( $fields['fields'] ?? array() ) as $field ) { + if ( 'message' === (string) ( $field['attributes']['name'] ?? '' ) && 'textarea' === (string) ( $field['element'] ?? '' ) ) { + $has_message = true; + break; + } + } + + if ( ! $has_message ) { + try { + self::upsert_form( 'contact', __( 'Site — Contato e orçamento', 'gustavoo-portfolio-core' ), true ); + } catch ( Throwable $error ) { + // Keep the front end available if Fluent Forms is temporarily unavailable. + } + } + } + + /** + * Check the optional integrations without triggering autoload errors. + * + * @return bool + */ + private static function fluent_is_ready() { + return defined( 'FLUENTFORM' ) + && defined( 'FLUENTCRM' ) + && function_exists( 'fluentformLoadFile' ) + && function_exists( 'FluentCrmApi' ) + && class_exists( '\\FluentForm\\App\\Models\\Form' ) + && class_exists( '\\FluentForm\\App\\Models\\FormMeta' ) + && class_exists( '\\FluentForm\\App\\Services\\Form\\FormService' ) + && class_exists( '\\FluentForm\\App\\Services\\Integrations\\FormIntegrationService' ); + } + + /** + * Create or reuse a seed-owned Fluent Form. + * + * @param string $key Deterministic form key. + * @param string $title Form title. + * @param bool $include_whatsapp Include the contact message field. + * @return int + * @throws Exception When Fluent Forms cannot create the form. + */ + private static function upsert_form( $key, $title, $include_whatsapp ) { + $form = self::find_seeded_form( $key ); + + $defaults = fluentformLoadFile( 'Services/FormBuilder/DefaultElements.php' ); + $blank = \FluentForm\App\Models\Form::resolvePredefinedForm( + array( + 'predefined' => 'blank_form', + 'type' => 'form', + ) + ); + $structure = json_decode( (string) $blank['form_fields'], true ); + + if ( ! is_array( $defaults ) || ! is_array( $structure ) ) { + throw new Exception( 'Não foi possível carregar a estrutura padrão do Fluent Forms.' ); + } + + $email = $defaults['general']['input_email']; + $email['uniqElKey'] = 'el_gso_' . $key . '_email'; + $email['attributes']['name'] = 'email'; + $email['attributes']['placeholder'] = __( 'Seu melhor e-mail', 'gustavoo-portfolio-core' ); + $email['settings']['label'] = __( 'E-mail', 'gustavoo-portfolio-core' ); + $email['settings']['admin_field_label'] = __( 'E-mail', 'gustavoo-portfolio-core' ); + $email['settings']['validation_rules']['required']['value'] = true; + + $fields = array( $email ); + + if ( $include_whatsapp ) { + $whatsapp = $defaults['general']['input_mask']; + $whatsapp['uniqElKey'] = 'el_gso_' . $key . '_whatsapp'; + $whatsapp['attributes']['name'] = 'whatsapp'; + $whatsapp['attributes']['placeholder'] = '(31) 99999-9999'; + $whatsapp['attributes']['data-mask'] = '(00) 00000-0000'; + $whatsapp['settings']['label'] = __( 'WhatsApp', 'gustavoo-portfolio-core' ); + $whatsapp['settings']['admin_field_label'] = __( 'WhatsApp', 'gustavoo-portfolio-core' ); + $whatsapp['settings']['mobile_keyboard_type'] = 'tel'; + $whatsapp['settings']['temp_mask'] = 'custom'; + $whatsapp['settings']['data-mask-reverse'] = 'no'; + $whatsapp['settings']['validation_rules']['required']['value'] = true; + $fields[] = $whatsapp; + + $message = $defaults['general']['textarea']; + $message['uniqElKey'] = 'el_gso_' . $key . '_message'; + $message['attributes']['name'] = 'message'; + $message['attributes']['placeholder'] = __( 'Descreva o motivo do contato e o que você quer construir.', 'gustavoo-portfolio-core' ); + $message['attributes']['rows'] = 5; + $message['settings']['label'] = __( 'Como posso ajudar?', 'gustavoo-portfolio-core' ); + $message['settings']['admin_field_label'] = __( 'Motivo do contato', 'gustavoo-portfolio-core' ); + $message['settings']['validation_rules']['required']['value'] = true; + $fields[] = $message; + } + + $structure['fields'] = $fields; + $structure['submitButton']['settings']['button_ui']['text'] = $include_whatsapp + ? __( 'Solicitar contato', 'gustavoo-portfolio-core' ) + : __( 'Quero receber', 'gustavoo-portfolio-core' ); + + $service = new \FluentForm\App\Services\Form\FormService(); + if ( $form ) { + $service->update( + array( + 'form_id' => $form->id, + 'title' => $title, + 'status' => 'published', + 'formFields' => wp_json_encode( $structure, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES ), + ) + ); + self::localize_form_confirmation( $form->id, $include_whatsapp ); + return absint( $form->id ); + } + + $form = $service->store( + array( + 'predefined' => 'blank_form', + 'type' => 'form', + ) + ); + $form = $service->update( + array( + 'form_id' => $form->id, + 'title' => $title, + 'status' => 'published', + 'formFields' => wp_json_encode( $structure, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES ), + ) + ); + + \FluentForm\App\Models\FormMeta::persist( $form->id, self::FORM_META, $key ); + self::localize_form_confirmation( $form->id, $include_whatsapp ); + + return absint( $form->id ); + } + + /** + * Locate a form owned by this seed. + * + * @param string $key Seed key. + * @return object|null + */ + private static function find_seeded_form( $key ) { + $markers = \FluentForm\App\Models\FormMeta::where( 'meta_key', self::FORM_META ) + ->where( 'value', $key ) + ->get(); + + foreach ( $markers as $marker ) { + $form = \FluentForm\App\Models\Form::find( absint( $marker->form_id ) ); + if ( $form ) { + return $form; + } + } + + return null; + } + + /** + * Use a concise Portuguese success message. + * + * @param int $form_id Form ID. + * @param bool $is_contact_form Whether this is the lead form. + * @return void + */ + private static function localize_form_confirmation( $form_id, $is_contact_form ) { + $settings = \FluentForm\App\Models\FormMeta::retrieve( 'formSettings', $form_id, array() ); + $settings = is_array( $settings ) ? $settings : array(); + + $settings['confirmation'] = wp_parse_args( + array( + 'redirectTo' => 'samePage', + 'messageToShow' => $is_contact_form + ? __( 'Recebi seus dados. Entrarei em contato em breve.', 'gustavoo-portfolio-core' ) + : __( 'Inscrição recebida. Confira seu e-mail para confirmar.', 'gustavoo-portfolio-core' ), + 'samePageFormBehavior' => 'hide_form', + ), + isset( $settings['confirmation'] ) && is_array( $settings['confirmation'] ) ? $settings['confirmation'] : array() + ); + + \FluentForm\App\Models\FormMeta::persist( $form_id, 'formSettings', $settings ); + } + + /** + * Create the FluentCRM feed for one seed-owned form. + * + * @param int $form_id Form ID. + * @param string $context Contact or newsletter. + * @return void + */ + private static function upsert_crm_feed( $form_id, $context ) { + $existing_id = absint( \FluentForm\App\Models\FormMeta::retrieve( self::FEED_META, $form_id, 0 ) ); + $defaults = apply_filters( 'fluentform/get_integration_defaults_fluentcrm', array(), $form_id ); + $defaults = is_array( $defaults ) ? $defaults : array(); + $is_contact = 'contact' === $context; + $feed = array_replace( + $defaults, + array( + 'name' => $is_contact ? 'Site / Contato' : 'Site / Newsletter', + 'email' => 'email', + 'enabled' => true, + 'double_opt_in' => ! $is_contact, + 'other_fields' => $is_contact + ? array( + array( + 'label' => 'phone', + 'item_value' => '{inputs.whatsapp}', + ), + ) + : array(), + 'conditionals' => array( + 'status' => false, + 'type' => 'all', + 'conditions' => array(), + ), + ) + ); + + $result = ( new \FluentForm\App\Services\Integrations\FormIntegrationService() )->update( + array( + 'form_id' => $form_id, + 'integration_id' => $existing_id, + 'integration_name' => 'fluentcrm', + 'data_type' => 'array', + 'status' => true, + 'integration' => $feed, + ) + ); + + if ( ! empty( $result['integration_id'] ) ) { + \FluentForm\App\Models\FormMeta::persist( $form_id, self::FEED_META, absint( $result['integration_id'] ) ); + } + } + + /** + * Share form IDs with the theme settings option. + * + * @param int $contact_id Contact form ID. + * @param int $newsletter_id Newsletter form ID. + * @return void + */ + private static function save_form_ids( $contact_id, $newsletter_id ) { + $settings = GSO_Portfolio_Settings::get_settings(); + $settings['contact_form_id'] = absint( $contact_id ); + $settings['newsletter_form_id'] = absint( $newsletter_id ); + update_option( GSO_Portfolio_Settings::OPTION_NAME, $settings, false ); + } + + /** + * Add starter projects only when their deterministic markers are absent. + * + * @return void + */ + private static function seed_projects() { + $projects = array( + 'aurelia-online' => array( + 'title' => 'Aurelia Online', + 'excerpt' => __( 'Plataforma online desenvolvida com foco em performance, experiência do usuário e evolução contínua.', 'gustavoo-portfolio-core' ), + 'type' => 'Plataforma web', + 'technologies' => array( 'Web', 'APIs', 'Infraestrutura' ), + 'featured' => true, + ), + 'lumenix-engine' => array( + 'title' => 'Lumenix Engine', + 'excerpt' => __( 'Engine e ecossistema técnico para experiências interativas e produtos digitais.', 'gustavoo-portfolio-core' ), + 'type' => 'Games', + 'technologies' => array( 'C++', 'Games', 'Performance' ), + 'featured' => true, + ), + 'aurabet' => array( + 'title' => 'AuraBet', + 'excerpt' => __( 'Produto digital com arquitetura, integrações e infraestrutura preparadas para escala.', 'gustavoo-portfolio-core' ), + 'type' => 'Produto digital', + 'technologies' => array( 'Full Stack', 'Docker', 'APIs' ), + 'featured' => true, + ), + 'visionforge' => array( + 'title' => 'VisionForge', + 'excerpt' => __( 'Solução criada para transformar processos complexos em uma experiência clara e eficiente.', 'gustavoo-portfolio-core' ), + 'type' => 'Software', + 'technologies' => array( 'Automação', 'Dados', 'Web' ), + 'featured' => true, + ), + 'papel-e-cor' => array( + 'title' => 'Papel & Cor', + 'excerpt' => __( 'Experiência de e-commerce otimizada para catálogo, compra e conversão.', 'gustavoo-portfolio-core' ), + 'type' => 'E-commerce', + 'technologies' => array( 'WordPress', 'WooCommerce', 'UX' ), + 'featured' => false, + ), + 'lumina-hub' => array( + 'title' => 'Lumina Hub', + 'excerpt' => __( 'Hub digital que reúne conteúdo, serviços e integrações em uma única plataforma.', 'gustavoo-portfolio-core' ), + 'type' => 'Plataforma web', + 'technologies' => array( 'WordPress', 'APIs', 'Automação' ), + 'featured' => false, + ), + ); + + $order = 0; + foreach ( $projects as $key => $project ) { + $existing = get_posts( + array( + 'post_type' => GSO_Projects::POST_TYPE, + 'post_status' => 'any', + 'posts_per_page' => 1, + 'fields' => 'ids', + 'meta_key' => self::PROJECT_META, + 'meta_value' => $key, + ) + ); + + if ( $existing ) { + ++$order; + continue; + } + + $post_id = wp_insert_post( + array( + 'post_type' => GSO_Projects::POST_TYPE, + 'post_status' => 'publish', + 'post_title' => $project['title'], + 'post_excerpt' => $project['excerpt'], + 'menu_order' => $order, + ), + true + ); + + if ( is_wp_error( $post_id ) ) { + ++$order; + continue; + } + + update_post_meta( $post_id, self::PROJECT_META, $key ); + update_post_meta( $post_id, GSO_Projects::META_URL, 'https://gustavoo.me/#projetos' ); + update_post_meta( $post_id, GSO_Projects::META_CLIENT, __( 'Projeto selecionado', 'gustavoo-portfolio-core' ) ); + update_post_meta( $post_id, GSO_Projects::META_YEAR, (int) gmdate( 'Y' ) ); + update_post_meta( $post_id, GSO_Projects::META_LINK_LABEL, __( 'Conhecer projeto', 'gustavoo-portfolio-core' ) ); + update_post_meta( $post_id, GSO_Projects::META_FEATURED, $project['featured'] ? '1' : '' ); + wp_set_object_terms( $post_id, $project['type'], GSO_Projects::TAX_TYPE ); + wp_set_object_terms( $post_id, $project['technologies'], GSO_Projects::TAX_TECH ); + ++$order; + } + } + +} diff --git a/wp-content/themes/.gitkeep b/wp-content/themes/.gitkeep new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/wp-content/themes/.gitkeep @@ -0,0 +1 @@ + diff --git a/wp-content/themes/gustavoo-portfolio/404.php b/wp-content/themes/gustavoo-portfolio/404.php new file mode 100644 index 0000000..42dc3ca --- /dev/null +++ b/wp-content/themes/gustavoo-portfolio/404.php @@ -0,0 +1,57 @@ + 'post', + 'post_status' => 'publish', + 'posts_per_page' => 3, + 'ignore_sticky_posts' => true, + 'no_found_rows' => true, + ) +); +?> + +
    +
    +
    +

    404

    +

    +

    +
    + +
    + +
    +
    + + +
    +
    +
    +

    +

    +
    +
    + +
    +
    +
    + +
    + + + +
    +
    +
    +

    +

    + +
    + +
    +
    + +
    +
    + +
    + +
    + + + + +
    + + +
    +
    + + a, +.site-header .menu a:hover, +.site-header .current-menu-item > a { + color: var(--color-text); +} + +.primary-nav a:hover::after, +.primary-nav .current-menu-item > a::after, +.site-header .menu a:hover::after, +.site-header .current-menu-item > a::after { + transform: scaleX(1); + transform-origin: left; +} + +.header-cta { + min-height: 42px; + padding: 10px 16px; +} + +.nav-toggle { + position: relative; + display: none; + width: 46px; + height: 46px; + padding: 0; + color: var(--color-text); + background: rgba(255, 255, 255, 0.035); + border: 1px solid var(--color-line-soft); + border-radius: 12px; +} + +.nav-toggle__line, +.nav-toggle span:not(.screen-reader-text) { + position: absolute; + left: 12px; + width: 20px; + height: 2px; + background: currentColor; + border-radius: 999px; + transition: transform 200ms ease, top 200ms ease, opacity 200ms ease; +} + +.nav-toggle__line:nth-of-type(1), +.nav-toggle span:nth-last-child(3) { + top: 15px; +} + +.nav-toggle__line:nth-of-type(2), +.nav-toggle span:nth-last-child(2) { + top: 21px; +} + +.nav-toggle__line:nth-of-type(3), +.nav-toggle span:nth-last-child(1) { + top: 27px; +} + +.nav-toggle[aria-expanded="true"] .nav-toggle__line:nth-of-type(1) { + top: 21px; + transform: rotate(45deg); +} + +.nav-toggle[aria-expanded="true"] .nav-toggle__line:nth-of-type(2) { + opacity: 0; +} + +.nav-toggle[aria-expanded="true"] .nav-toggle__line:nth-of-type(3) { + top: 21px; + transform: rotate(-45deg); +} + +/* Hero */ +.hero-stage { + position: relative; + display: grid; + min-height: calc(100vh - var(--hero-peek)); + min-height: calc(100svh - var(--hero-peek)); + overflow: clip; + padding-top: var(--header-height); + isolation: isolate; + background: + radial-gradient(circle at 77% 46%, rgba(116, 213, 199, 0.16), transparent 28%), + radial-gradient(circle at 13% 72%, rgba(68, 111, 187, 0.1), transparent 33%), + linear-gradient(135deg, #071624 0%, #0a1c2d 62%, #0b2235 100%); +} + +.hero-stage::before { + position: absolute; + z-index: -2; + inset: 0; + content: ""; + opacity: 0.45; + background-image: + linear-gradient(rgba(142, 190, 207, 0.045) 1px, transparent 1px), + linear-gradient(90deg, rgba(142, 190, 207, 0.045) 1px, transparent 1px); + background-size: 52px 52px; + mask-image: linear-gradient(to bottom, black, transparent 92%); +} + +.hero-stage::after { + position: absolute; + z-index: -1; + width: min(55vw, 780px); + aspect-ratio: 1; + right: -12%; + bottom: -40%; + content: ""; + border: 1px solid rgba(116, 213, 199, 0.09); + border-radius: 50%; + box-shadow: + 0 0 0 100px rgba(116, 213, 199, 0.022), + 0 0 0 200px rgba(116, 213, 199, 0.016); +} + +.hero { + display: grid; + width: 100%; + align-items: center; + padding-block: clamp(54px, 7vh, 96px) clamp(88px, 11vh, 126px); +} + +.hero__inner { + display: grid; + width: min(var(--container), calc(100% - (2 * var(--gutter)))); + margin-inline: auto; + align-items: center; + gap: clamp(38px, 6vw, 88px); + grid-template-columns: minmax(0, 7fr) minmax(320px, 5fr); +} + +.hero__content { + position: relative; + z-index: 2; + max-width: 790px; +} + +.hero__title { + max-width: 13ch; + margin-bottom: 26px; + font-size: clamp(2.8rem, 5.25vw, 5.15rem); + font-weight: 790; + line-height: 0.99; + letter-spacing: -0.054em; +} + +.hero__description, +.hero__lead { + max-width: 65ch; + margin-bottom: 30px; + color: var(--color-text-soft); + font-size: clamp(1.04rem, 1.4vw, 1.27rem); + line-height: 1.62; +} + +.hero__actions { + display: flex; + margin-bottom: 34px; + flex-wrap: wrap; + gap: 12px; +} + +.hero__scope, +.scope-list { + display: flex; + margin: 0; + padding: 0; + flex-wrap: wrap; + gap: 8px; + list-style: none; +} + +.hero__scope li, +.scope-list li, +.chip, +.project-card__tag, +.post-card__category, +.entry-taxonomy a { + display: inline-flex; + min-height: 30px; + align-items: center; + padding: 6px 10px; + color: var(--color-text-soft); + background: rgba(116, 213, 199, 0.07); + border: 1px solid rgba(116, 213, 199, 0.18); + border-radius: 999px; + font-family: var(--font-mono); + font-size: 0.7rem; + line-height: 1.2; + letter-spacing: 0.02em; +} + +.hero__media { + position: relative; + z-index: 1; + display: grid; + justify-items: center; + place-items: center; +} + +.hero__media::before { + position: absolute; + z-index: -1; + width: 82%; + aspect-ratio: 1; + content: ""; + background: radial-gradient(circle, rgba(80, 229, 207, 0.14), transparent 68%); + filter: blur(10px); +} + +.hero__image, +.hero__media img { + width: min(44vw, 570px); + max-height: 65vh; + object-fit: contain; + filter: drop-shadow(0 28px 36px rgba(0, 0, 0, 0.28)); + animation: hero-float 7s ease-in-out infinite; +} + +.hero__status, +.availability-badge { + display: inline-flex; + align-items: center; + gap: 8px; + margin-bottom: 18px; + color: var(--color-text-soft); + font-family: var(--font-mono); + font-size: 0.74rem; +} + +.hero__status::before, +.availability-badge::before { + width: 8px; + height: 8px; + content: ""; + background: var(--color-success); + border-radius: 50%; + box-shadow: 0 0 0 5px rgba(123, 224, 161, 0.12); +} + +.scroll-cue { + position: absolute; + z-index: 3; + bottom: 20px; + left: 50%; + display: grid; + justify-items: center; + gap: 8px; + color: var(--color-text-muted); + font-family: var(--font-mono); + font-size: 0.66rem; + letter-spacing: 0.11em; + text-decoration: none; + text-transform: uppercase; + transform: translateX(-50%); +} + +.scroll-cue::after { + width: 1px; + height: 30px; + content: ""; + background: linear-gradient(var(--color-brand), transparent); + animation: scroll-cue 1.7s ease-in-out infinite; + transform-origin: top; +} + +.scroll-cue:hover { + color: var(--color-brand); +} + +@keyframes hero-float { + 0%, 100% { transform: translateY(0); } + 50% { transform: translateY(-8px); } +} + +@keyframes scroll-cue { + 0%, 100% { opacity: 0.35; transform: scaleY(0.55); } + 50% { opacity: 1; transform: scaleY(1); } +} + +/* Sections and shared headings */ +.section { + position: relative; + padding-block: var(--section-space); +} + +.section[id] { + scroll-margin-top: calc(var(--header-height) + 12px); +} + +.section--surface, +.section-surface { + background: var(--color-bg-alt); + border-block: 1px solid var(--color-line-soft); +} + +.section--raised { + background: var(--color-surface); +} + +.section--intro, +.intro-strip, +.capability-strip { + position: relative; + z-index: 4; + margin-top: 0; + padding-block: clamp(44px, 6vw, 72px); + background: var(--color-surface); + border-top: 1px solid rgba(116, 213, 199, 0.18); + border-radius: var(--radius-xl) var(--radius-xl) 0 0; + box-shadow: 0 -22px 70px rgba(0, 0, 0, 0.16); +} + +.section-heading { + display: flex; + margin-bottom: clamp(34px, 5vw, 58px); + align-items: end; + justify-content: space-between; + gap: 36px; +} + +.section-heading__content, +.section-heading > div:first-child { + max-width: 760px; +} + +.section-heading h2 { + margin-bottom: 0; +} + +.section-heading__copy, +.section-heading p { + max-width: 58ch; + margin: 14px 0 0; + color: var(--color-text-soft); + font-size: 1.04rem; +} + +.section-heading__link { + flex: 0 0 auto; +} + +/* About */ +.about-layout { + display: grid; + align-items: start; + gap: clamp(38px, 7vw, 90px); + grid-template-columns: minmax(0, 7fr) minmax(300px, 5fr); +} + +.about-copy, +.about__content { + max-width: 760px; +} + +.about-copy h2, +.about__content h2 { + margin-bottom: 26px; +} + +.about-copy p, +.about__content p { + color: var(--color-text-soft); + font-size: clamp(1rem, 1.3vw, 1.16rem); +} + +.about-copy p + p, +.about__content p + p { + margin-top: 18px; +} + +.about-signature { + display: inline-flex; + margin-top: 22px; + align-items: center; + gap: 14px; + color: var(--color-text); + font-weight: 720; +} + +.about-signature img { + width: 48px; + height: 48px; + object-fit: contain; +} + +.proof-grid, +.about-proof, +.stats-grid { + display: grid; + gap: 12px; + grid-template-columns: repeat(2, minmax(0, 1fr)); +} + +.proof-card, +.stat-card { + min-height: 138px; + padding: 22px; + background: linear-gradient(145deg, rgba(17, 44, 66, 0.92), rgba(13, 36, 56, 0.86)); + border: 1px solid var(--color-line-soft); + border-radius: var(--radius-md); + transition: transform 220ms var(--ease-out), border-color 220ms ease; +} + +.proof-card:hover, +.stat-card:hover { + border-color: rgba(116, 213, 199, 0.38); + transform: translateY(-3px); +} + +.proof-card__icon, +.stat-card__icon { + display: grid; + width: 40px; + height: 40px; + margin-bottom: 20px; + place-items: center; + color: var(--color-brand); + background: rgba(116, 213, 199, 0.08); + border: 1px solid rgba(116, 213, 199, 0.16); + border-radius: 11px; +} + +.proof-card strong, +.stat-card strong { + display: block; + margin-bottom: 5px; + color: var(--color-text); + font-size: 0.98rem; +} + +.proof-card span, +.stat-card span { + color: var(--color-text-muted); + font-family: var(--font-mono); + font-size: 0.68rem; + line-height: 1.45; +} + +/* Services */ +.services-grid { + display: grid; + gap: 18px; + grid-template-columns: repeat(3, minmax(0, 1fr)); +} + +.service-card { + position: relative; + min-height: 290px; + overflow: hidden; + padding: clamp(24px, 2.4vw, 32px); + background: linear-gradient(150deg, rgba(17, 44, 66, 0.9), rgba(10, 28, 45, 0.96)); + border: 1px solid var(--color-line-soft); + border-radius: var(--radius-md); + transition: + transform 240ms var(--ease-out), + border-color 240ms ease, + box-shadow 240ms ease, + background-color 240ms ease; +} + +.service-card::after { + position: absolute; + width: 170px; + height: 170px; + right: -90px; + bottom: -100px; + content: ""; + background: radial-gradient(circle, rgba(116, 213, 199, 0.13), transparent 68%); + transition: transform 350ms var(--ease-out); +} + +.service-card:hover { + background: linear-gradient(150deg, rgba(21, 53, 77, 0.96), rgba(10, 28, 45, 0.98)); + border-color: rgba(116, 213, 199, 0.42); + box-shadow: var(--shadow-card); + transform: translateY(-5px); +} + +.service-card:hover::after { + transform: scale(1.35); +} + +.service-card__top { + display: flex; + margin-bottom: 30px; + align-items: center; + justify-content: space-between; +} + +.service-card__icon { + display: grid; + width: 48px; + height: 48px; + place-items: center; + color: var(--color-brand); + background: rgba(116, 213, 199, 0.08); + border: 1px solid rgba(116, 213, 199, 0.18); + border-radius: 13px; +} + +.service-card__icon svg { + width: 24px; + height: 24px; +} + +.service-card__number { + color: rgba(184, 197, 206, 0.32); + font-family: var(--font-mono); + font-size: 0.72rem; +} + +.service-card h3 { + margin-bottom: 14px; +} + +.service-card p { + color: var(--color-text-soft); + font-size: 0.94rem; + line-height: 1.68; +} + +/* Project cards */ +.projects-grid { + display: grid; + gap: 20px; + grid-template-columns: repeat(3, minmax(0, 1fr)); +} + +.project-card, +.post-card { + position: relative; + display: flex; + min-width: 0; + overflow: hidden; + flex-direction: column; + background: rgba(13, 36, 56, 0.88); + border: 1px solid var(--color-line-soft); + border-radius: var(--radius-md); + box-shadow: 0 1px 0 rgba(255, 255, 255, 0.015) inset; + transition: + transform 240ms var(--ease-out), + border-color 240ms ease, + box-shadow 240ms ease; +} + +.project-card:hover, +.project-card:focus-within, +.post-card:hover, +.post-card:focus-within { + border-color: rgba(116, 213, 199, 0.42); + box-shadow: var(--shadow-card); + transform: translateY(-5px); +} + +.project-card__media, +.post-card__media { + position: relative; + aspect-ratio: 16 / 10; + overflow: hidden; + background: + radial-gradient(circle at 70% 30%, rgba(116, 213, 199, 0.2), transparent 28%), + linear-gradient(135deg, #102b42, #071521); + border-bottom: 1px solid var(--color-line-soft); +} + +.project-card__media::after, +.post-card__media::after { + position: absolute; + inset: 0; + pointer-events: none; + content: ""; + background: linear-gradient(to top, rgba(4, 16, 27, 0.2), transparent 45%); +} + +.project-card__media img, +.post-card__media img { + width: 100%; + height: 100%; + object-fit: cover; + transition: transform 450ms var(--ease-out), filter 300ms ease; +} + +.project-card:hover .project-card__media img, +.post-card:hover .post-card__media img { + transform: scale(1.04); +} + +.project-card__placeholder, +.project-placeholder { + position: absolute; + inset: 0; + display: grid; + place-items: center; + overflow: hidden; + color: rgba(245, 248, 250, 0.92); + font-family: var(--font-mono); + font-size: clamp(2rem, 4vw, 3.6rem); + font-weight: 750; + letter-spacing: -0.08em; +} + +.project-card__placeholder::before, +.project-placeholder::before { + position: absolute; + inset: 14%; + content: ""; + opacity: 0.5; + background-image: + linear-gradient(rgba(116, 213, 199, 0.13) 1px, transparent 1px), + linear-gradient(90deg, rgba(116, 213, 199, 0.13) 1px, transparent 1px); + background-size: 26px 26px; + border: 1px solid rgba(116, 213, 199, 0.16); + border-radius: 18px; + transform: rotate(-4deg); +} + +.project-card__body, +.post-card__body { + display: flex; + padding: 24px; + flex: 1; + flex-direction: column; +} + +.project-card__meta, +.post-card__meta, +.entry-meta { + display: flex; + margin-bottom: 13px; + align-items: center; + flex-wrap: wrap; + gap: 8px 12px; + color: var(--color-text-muted); + font-family: var(--font-mono); + font-size: 0.68rem; + line-height: 1.4; +} + +.project-card h3, +.post-card h2, +.post-card h3 { + margin-bottom: 12px; + font-size: clamp(1.18rem, 1.7vw, 1.45rem); + line-height: 1.2; +} + +.project-card h3 a, +.post-card h2 a, +.post-card h3 a { + color: var(--color-text); + text-decoration: none; +} + +.project-card h3 a::after, +.post-card h2 a::after, +.post-card h3 a::after { + position: absolute; + inset: 0; + content: ""; +} + +.project-card__excerpt, +.post-card__excerpt { + margin-bottom: 20px; + color: var(--color-text-soft); + font-size: 0.9rem; + line-height: 1.65; +} + +.project-card__footer, +.post-card__footer { + display: flex; + margin-top: auto; + align-items: center; + justify-content: space-between; + gap: 14px; +} + +.project-card__tags, +.entry-taxonomy { + position: relative; + z-index: 2; + display: flex; + flex-wrap: wrap; + gap: 6px; +} + +.card-link { + position: relative; + z-index: 2; + display: inline-flex; + min-width: max-content; + min-height: 40px; + align-items: center; + gap: 7px; + color: var(--color-brand); + font-size: 0.78rem; + font-weight: 720; + text-decoration: none; +} + +.project-card--featured { + grid-column: span 2; +} + +.project-card--featured .project-card__media { + aspect-ratio: 2 / 1; +} + +.projects-empty, +.no-results { + padding: clamp(30px, 5vw, 58px); + color: var(--color-text-soft); + background: rgba(13, 36, 56, 0.68); + border: 1px dashed rgba(116, 213, 199, 0.3); + border-radius: var(--radius-md); + text-align: center; + grid-column: 1 / -1; +} + +/* Posts */ +.posts-grid { + display: grid; + gap: 20px; + grid-template-columns: repeat(3, minmax(0, 1fr)); +} + +.post-card__media { + aspect-ratio: 16 / 9; +} + +.post-card__category { + color: var(--color-brand); +} + +.post-card time { + color: var(--color-text-muted); +} + +/* Contact and Fluent Forms */ +.contact-panel { + position: relative; + display: grid; + overflow: hidden; + padding: clamp(28px, 5vw, 62px); + align-items: center; + gap: clamp(38px, 7vw, 88px); + background: + radial-gradient(circle at 6% 90%, rgba(80, 229, 207, 0.1), transparent 30%), + linear-gradient(145deg, rgba(17, 44, 66, 0.96), rgba(7, 22, 36, 0.98)); + border: 1px solid rgba(116, 213, 199, 0.2); + border-radius: var(--radius-lg); + box-shadow: var(--shadow-card); + grid-template-columns: minmax(0, 5fr) minmax(340px, 5fr); +} + +.contact-panel::before { + position: absolute; + width: 360px; + height: 360px; + top: -260px; + right: -100px; + content: ""; + border: 1px solid rgba(116, 213, 199, 0.09); + border-radius: 50%; + box-shadow: 0 0 0 70px rgba(116, 213, 199, 0.025); +} + +.contact-panel__content { + position: relative; + z-index: 1; +} + +.contact-panel__content h2 { + margin-bottom: 22px; +} + +.contact-panel__content > p { + max-width: 54ch; + color: var(--color-text-soft); + font-size: 1.04rem; +} + +.contact-links { + display: grid; + margin: 28px 0 0; + padding: 0; + gap: 10px; + list-style: none; +} + +.contact-links a { + display: inline-flex; + min-height: 42px; + align-items: center; + gap: 10px; + color: var(--color-text-soft); + text-decoration: none; +} + +.contact-links a:hover { + color: var(--color-brand); +} + +.contact-panel__form, +.form-card { + position: relative; + z-index: 2; + padding: clamp(22px, 3vw, 34px); + background: rgba(4, 16, 27, 0.52); + border: 1px solid var(--color-line-soft); + border-radius: var(--radius-md); + -webkit-backdrop-filter: blur(12px); + backdrop-filter: blur(12px); +} + +.contact-panel .fluentform, +.newsletter-panel .fluentform, +.widget .fluentform { + color: var(--color-text); +} + +.contact-panel .ff-el-group, +.newsletter-panel .ff-el-group, +.widget .ff-el-group { + margin-bottom: 16px; +} + +.contact-panel .ff-el-input--label, +.newsletter-panel .ff-el-input--label, +.widget .ff-el-input--label { + margin-bottom: 7px; +} + +.contact-panel .ff-el-input--label label, +.newsletter-panel .ff-el-input--label label, +.widget .ff-el-input--label label, +.contact-panel .ff-el-form-check-label, +.newsletter-panel .ff-el-form-check-label, +.widget .ff-el-form-check-label { + color: var(--color-text-soft) !important; + font-size: 0.82rem; + font-weight: 620; +} + +.contact-panel .ff-el-form-control, +.newsletter-panel .ff-el-form-control, +.widget .ff-el-form-control { + width: 100%; + min-height: 54px; + padding: 13px 15px; + color: var(--color-text) !important; + background: #071827 !important; + border: 1px solid var(--color-line) !important; + border-radius: 10px !important; + box-shadow: none !important; + transition: border-color 180ms ease, box-shadow 180ms ease; +} + +.contact-panel textarea.ff-el-form-control, +.newsletter-panel textarea.ff-el-form-control, +.widget textarea.ff-el-form-control { + min-height: 130px; + resize: vertical; +} + +.contact-panel .ff-el-form-control::placeholder, +.newsletter-panel .ff-el-form-control::placeholder, +.widget .ff-el-form-control::placeholder { + color: #6f8595 !important; + opacity: 1; +} + +.contact-panel .ff-el-form-control:focus, +.newsletter-panel .ff-el-form-control:focus, +.widget .ff-el-form-control:focus { + border-color: var(--color-brand) !important; + box-shadow: 0 0 0 3px rgba(116, 213, 199, 0.16) !important; + outline: none; +} + +.contact-panel .ff-btn-submit, +.newsletter-panel .ff-btn-submit, +.widget .ff-btn-submit { + min-height: 50px; + padding: 13px 20px !important; + color: var(--color-brand-ink) !important; + background: var(--color-brand) !important; + border: 1px solid var(--color-brand) !important; + border-radius: 10px !important; + font-weight: 760 !important; + opacity: 1 !important; + transition: transform 180ms var(--ease-out), background 180ms ease, box-shadow 180ms ease; +} + +.contact-panel .ff-btn-submit:hover, +.newsletter-panel .ff-btn-submit:hover, +.widget .ff-btn-submit:hover { + background: var(--color-brand-strong) !important; + box-shadow: var(--shadow-brand); + transform: translateY(-2px); +} + +.contact-panel .ff-el-is-error .ff-el-form-control, +.newsletter-panel .ff-el-is-error .ff-el-form-control, +.widget .ff-el-is-error .ff-el-form-control { + border-color: var(--color-danger) !important; +} + +.contact-panel .error.text-danger, +.newsletter-panel .error.text-danger, +.widget .error.text-danger { + margin-top: 6px; + color: var(--color-danger) !important; + font-size: 0.75rem; +} + +.ff-message-success { + padding: 16px 18px !important; + color: #cbffdc !important; + background: rgba(123, 224, 161, 0.09) !important; + border: 1px solid rgba(123, 224, 161, 0.34) !important; + border-radius: 10px !important; + box-shadow: none !important; +} + +.form-fallback, +.plugin-notice { + padding: 18px; + color: var(--color-text-soft); + background: rgba(255, 255, 255, 0.025); + border: 1px dashed var(--color-line); + border-radius: 10px; + font-size: 0.88rem; +} + +/* Newsletter, shown globally */ +.newsletter-section { + padding-block: clamp(54px, 7vw, 90px); + background: var(--color-bg-deep); + border-top: 1px solid var(--color-line-soft); +} + +.newsletter-panel { + position: relative; + display: grid; + overflow: hidden; + padding: clamp(28px, 4vw, 48px); + align-items: center; + gap: 36px; + background: + linear-gradient(115deg, rgba(116, 213, 199, 0.08), transparent 42%), + var(--color-surface); + border: 1px solid rgba(116, 213, 199, 0.19); + border-radius: var(--radius-lg); + grid-template-columns: minmax(0, 1.2fr) minmax(340px, 0.8fr); +} + +.newsletter-panel::after { + position: absolute; + width: 210px; + height: 210px; + right: -100px; + bottom: -130px; + content: ""; + border: 1px solid rgba(116, 213, 199, 0.12); + border-radius: 50%; + box-shadow: 0 0 0 45px rgba(116, 213, 199, 0.026); +} + +.newsletter-panel__content, +.newsletter-panel__form { + position: relative; + z-index: 1; +} + +.newsletter-panel h2, +.newsletter-panel h3 { + margin-bottom: 12px; + font-size: clamp(1.55rem, 2.6vw, 2.45rem); +} + +.newsletter-panel p { + max-width: 55ch; + color: var(--color-text-soft); +} + +.newsletter-panel .frm-fluent-form, +.newsletter-panel form { + display: grid; + align-items: end; + gap: 10px; +} + +.newsletter-panel .ff_submit_btn_wrapper { + margin-bottom: 0; +} + +.newsletter-panel .ff-btn-submit { + width: 100%; +} + +/* Inner page headers */ +.page-hero, +.archive-hero, +.search-hero, +.entry-hero { + position: relative; + overflow: hidden; + padding-block: calc(var(--header-height) + clamp(68px, 8vw, 112px)) clamp(60px, 7vw, 98px); + background: + radial-gradient(circle at 78% 35%, rgba(116, 213, 199, 0.11), transparent 28%), + linear-gradient(145deg, var(--color-bg), var(--color-bg-alt)); + border-bottom: 1px solid var(--color-line-soft); +} + +.page-hero::after, +.archive-hero::after, +.entry-hero::after { + position: absolute; + inset: 0; + pointer-events: none; + content: ""; + opacity: 0.35; + background-image: linear-gradient(90deg, rgba(142, 190, 207, 0.045) 1px, transparent 1px); + background-size: 64px 100%; + mask-image: linear-gradient(90deg, transparent, black 30%, transparent); +} + +.page-hero__inner, +.archive-hero__inner, +.entry-hero__inner { + position: relative; + z-index: 1; + max-width: 920px; +} + +.page-hero h1, +.archive-hero h1, +.entry-hero h1 { + max-width: 17ch; + margin-bottom: 18px; + font-size: clamp(2.4rem, 5vw, 4.7rem); +} + +.page-hero p, +.archive-hero p, +.entry-hero p { + max-width: 65ch; + margin-bottom: 0; + color: var(--color-text-soft); + font-size: clamp(1rem, 1.4vw, 1.2rem); +} + +.content-shell { + padding-block: var(--section-space); +} + +.content-grid, +.archive-layout { + display: grid; + align-items: start; + gap: clamp(34px, 5vw, 64px); + grid-template-columns: minmax(0, 1fr) minmax(260px, 320px); +} + +.archive-posts { + display: grid; + gap: 20px; + grid-template-columns: repeat(2, minmax(0, 1fr)); +} + +/* Article */ +.article-layout { + display: grid; + width: min(1160px, calc(100% - (2 * var(--gutter)))); + margin-inline: auto; + padding-block: var(--section-space); + align-items: start; + gap: clamp(42px, 7vw, 88px); + grid-template-columns: minmax(0, var(--content)) minmax(260px, 310px); +} + +.article-layout--single { + justify-content: center; +} + +.article, +.entry-content { + min-width: 0; +} + +.article__featured, +.entry-featured-image { + width: min(1160px, calc(100% - (2 * var(--gutter)))); + margin: clamp(30px, 5vw, 58px) auto 0; + overflow: hidden; + border: 1px solid var(--color-line-soft); + border-radius: var(--radius-lg); + box-shadow: var(--shadow-card); +} + +.article__featured img, +.entry-featured-image img { + width: 100%; + max-height: 660px; + object-fit: cover; +} + +.entry-content { + color: #d4dde3; + font-size: clamp(1.04rem, 1.2vw, 1.13rem); + line-height: 1.78; +} + +.entry-content > * { + max-width: var(--content); + margin-inline: auto; +} + +.entry-content > .alignwide { + max-width: 1040px; +} + +.entry-content > .alignfull { + width: 100vw; + max-width: none; + margin-left: calc(50% - 50vw); +} + +.entry-content h2, +.entry-content h3, +.entry-content h4 { + margin-top: 2.2em; + margin-bottom: 0.75em; + scroll-margin-top: calc(var(--header-height) + 24px); +} + +.entry-content p, +.entry-content ul, +.entry-content ol, +.entry-content blockquote, +.entry-content pre, +.entry-content table, +.entry-content figure { + margin-bottom: 1.45em; +} + +.entry-content a { + color: var(--color-brand); + font-weight: 600; +} + +.entry-content a:hover { + color: var(--color-focus); +} + +.entry-content ul, +.entry-content ol { + padding-left: 1.3em; +} + +.entry-content li + li { + margin-top: 0.45em; +} + +.entry-content blockquote { + padding: 8px 0 8px 24px; + color: var(--color-text-soft); + border-left: 3px solid var(--color-brand); + font-size: 1.08em; + font-style: italic; +} + +.entry-content code, +.comment-content code { + padding: 0.15em 0.36em; + color: #b9fff5; + background: #04101b; + border: 1px solid rgba(116, 213, 199, 0.12); + border-radius: 5px; + font-family: var(--font-mono); + font-size: 0.86em; +} + +.entry-content pre, +.comment-content pre { + max-width: 100%; + padding: 22px; + overflow-x: auto; + color: #d9fdf7; + background: #040d16; + border: 1px solid var(--color-line-soft); + border-radius: var(--radius-sm); + font-family: var(--font-mono); + font-size: 0.86rem; + line-height: 1.65; +} + +.entry-content pre code { + padding: 0; + color: inherit; + background: transparent; + border: 0; +} + +.entry-content table { + width: 100%; + border-spacing: 0; + border-collapse: collapse; +} + +.entry-content th, +.entry-content td { + padding: 12px 14px; + border: 1px solid var(--color-line); + text-align: left; +} + +.entry-content th { + color: var(--color-text); + background: var(--color-surface); +} + +.entry-content figcaption, +.wp-caption-text { + margin-top: 8px; + color: var(--color-text-muted); + font-size: 0.76rem; + text-align: center; +} + +.entry-footer, +.article-footer { + margin-top: 46px; + padding-top: 26px; + border-top: 1px solid var(--color-line-soft); +} + +.post-navigation { + margin-top: 54px; + padding-top: 30px; + border-top: 1px solid var(--color-line-soft); +} + +.post-navigation .nav-links { + display: grid; + gap: 18px; + grid-template-columns: repeat(2, minmax(0, 1fr)); +} + +.post-navigation a { + display: grid; + min-height: 112px; + padding: 20px; + align-content: center; + color: var(--color-text); + background: var(--color-surface); + border: 1px solid var(--color-line-soft); + border-radius: var(--radius-sm); + text-decoration: none; +} + +.post-navigation a:hover { + color: var(--color-brand); + border-color: rgba(116, 213, 199, 0.35); +} + +.nav-subtitle { + color: var(--color-text-muted); + font-family: var(--font-mono); + font-size: 0.68rem; + text-transform: uppercase; +} + +.nav-title { + margin-top: 6px; + font-weight: 700; +} + +/* Sidebar and widgets */ +.sidebar, +.widget-area { + min-width: 0; +} + +.article-layout .sidebar { + position: sticky; + top: calc(var(--header-height) + 28px); +} + +.widget { + margin-bottom: 18px; + padding: 22px; + color: var(--color-text-soft); + background: rgba(13, 36, 56, 0.75); + border: 1px solid var(--color-line-soft); + border-radius: var(--radius-md); +} + +.widget-title, +.widget h2, +.widget h3 { + margin-bottom: 16px; + font-size: 1rem; + letter-spacing: -0.015em; +} + +.widget ul { + display: grid; + margin: 0; + padding: 0; + gap: 9px; + list-style: none; +} + +.widget li { + padding-bottom: 9px; + border-bottom: 1px solid rgba(142, 190, 207, 0.09); +} + +.widget li:last-child { + padding-bottom: 0; + border-bottom: 0; +} + +.widget a { + color: var(--color-text-soft); + text-decoration: none; +} + +.widget a:hover { + color: var(--color-brand); +} + +.search-form { + display: flex; + gap: 8px; +} + +.search-form label { + flex: 1; +} + +.search-field, +.comment-form input:not([type="submit"]), +.comment-form textarea { + width: 100%; + min-height: 48px; + padding: 11px 13px; + color: var(--color-text); + background: #071827; + border: 1px solid var(--color-line); + border-radius: 9px; +} + +.search-field:focus, +.comment-form input:not([type="submit"]):focus, +.comment-form textarea:focus { + border-color: var(--color-brand); + box-shadow: 0 0 0 3px rgba(116, 213, 199, 0.15); + outline: none; +} + +.search-submit { + min-width: 48px; + padding-inline: 15px; +} + +/* Pagination */ +.pagination, +.posts-navigation { + margin-top: 48px; +} + +.pagination .nav-links, +.posts-navigation .nav-links { + display: flex; + align-items: center; + flex-wrap: wrap; + gap: 8px; +} + +.page-numbers, +.posts-navigation a { + display: inline-flex; + min-width: 44px; + min-height: 44px; + padding: 10px 13px; + align-items: center; + justify-content: center; + color: var(--color-text-soft); + background: var(--color-surface); + border: 1px solid var(--color-line-soft); + border-radius: 9px; + text-decoration: none; +} + +.page-numbers.current, +.page-numbers:hover, +.posts-navigation a:hover { + color: var(--color-brand-ink); + background: var(--color-brand); + border-color: var(--color-brand); +} + +/* Comments */ +.comments-area { + margin-top: 64px; + padding-top: 44px; + border-top: 1px solid var(--color-line-soft); +} + +.comments-title, +.comment-reply-title { + margin-bottom: 28px; + font-size: clamp(1.45rem, 2vw, 2rem); +} + +#cancel-comment-reply-link { + display: inline-flex; + margin-left: 12px; + vertical-align: middle; + font-size: 0.62em; + font-weight: 650; + white-space: nowrap; +} + +.comment-list { + margin: 0 0 44px; + padding: 0; + list-style: none; +} + +.comment-list .children { + margin-left: 30px; + list-style: none; +} + +.comment-body { + margin-bottom: 18px; + padding: 22px; + background: var(--color-surface); + border: 1px solid var(--color-line-soft); + border-radius: var(--radius-sm); +} + +.comment-meta { + margin-bottom: 14px; + color: var(--color-text-muted); + font-size: 0.78rem; +} + +.comment-author { + color: var(--color-text); + font-weight: 700; +} + +.comment-author img { + display: inline-block; + margin-right: 9px; + border-radius: 50%; + vertical-align: middle; +} + +.comment-form { + display: grid; + gap: 14px; +} + +.comment-form p { + margin: 0; +} + +.comment-form label { + display: block; + margin-bottom: 6px; + color: var(--color-text-soft); + font-size: 0.82rem; + font-weight: 650; +} + +/* 404 */ +.error-404, +.not-found-page { + display: grid; + min-height: 70vh; + padding-block: calc(var(--header-height) + 80px) 90px; + place-items: center; + text-align: center; +} + +.error-404__inner, +.not-found-page__inner { + width: min(680px, calc(100% - (2 * var(--gutter)))); +} + +.error-404__code, +.not-found-page__code { + display: block; + margin-bottom: 12px; + color: rgba(116, 213, 199, 0.24); + font-family: var(--font-mono); + font-size: clamp(5rem, 16vw, 10rem); + font-weight: 800; + line-height: 0.9; + letter-spacing: -0.1em; +} + +.error-404 h1, +.not-found-page h1 { + margin-bottom: 18px; +} + +.error-404 p, +.not-found-page p { + margin-bottom: 28px; + color: var(--color-text-soft); +} + +.error-404 .search-form, +.not-found-page .search-form { + max-width: 520px; + margin-inline: auto; +} + +/* Footer */ +.site-footer { + position: relative; + padding-block: clamp(54px, 7vw, 88px) 28px; + background: var(--color-bg-deep); + border-top: 1px solid var(--color-line-soft); +} + +.footer-grid { + display: grid; + margin-bottom: 48px; + gap: clamp(32px, 6vw, 78px); + grid-template-columns: minmax(260px, 1.4fr) repeat(2, minmax(140px, 0.6fr)); +} + +.footer-brand { + max-width: 430px; +} + +.footer-brand .brand { + margin-bottom: 18px; +} + +.footer-brand p { + color: var(--color-text-muted); + font-size: 0.9rem; +} + +.footer-title { + margin-bottom: 16px; + color: var(--color-text); + font-family: var(--font-mono); + font-size: 0.72rem; + letter-spacing: 0.08em; + text-transform: uppercase; +} + +.footer-menu, +.footer-links, +.social-links { + display: flex; + margin: 0; + padding: 0; + flex-direction: column; + gap: 9px; + list-style: none; +} + +.footer-menu a, +.footer-links a { + color: var(--color-text-muted); + font-size: 0.88rem; + text-decoration: none; +} + +.footer-menu a:hover, +.footer-links a:hover { + color: var(--color-brand); +} + +.social-links { + margin-top: 20px; + flex-direction: row; + flex-wrap: wrap; +} + +.social-links a { + display: grid; + width: 42px; + height: 42px; + place-items: center; + color: var(--color-text-soft); + background: rgba(255, 255, 255, 0.025); + border: 1px solid var(--color-line-soft); + border-radius: 10px; + text-decoration: none; + transition: transform 180ms var(--ease-out), color 180ms ease, border-color 180ms ease; +} + +.social-links a:hover { + color: var(--color-brand); + border-color: rgba(116, 213, 199, 0.38); + transform: translateY(-2px); +} + +.social-links svg { + width: 18px; + height: 18px; +} + +.footer-bottom { + display: flex; + padding-top: 24px; + align-items: center; + justify-content: space-between; + gap: 20px; + color: var(--color-text-muted); + border-top: 1px solid var(--color-line-soft); + font-family: var(--font-mono); + font-size: 0.68rem; +} + +.back-to-top { + display: inline-flex; + min-height: 40px; + align-items: center; + gap: 7px; + color: var(--color-text-muted); + text-decoration: none; +} + +.back-to-top:hover { + color: var(--color-brand); +} + +.whatsapp-float-wrap { + position: fixed; + z-index: 900; + right: 22px; + bottom: 22px; + width: 54px; + height: 54px; +} + +.whatsapp-float { + display: grid; + width: 54px; + height: 54px; + place-items: center; + color: #062722; + background: #55e6bd; + border: 1px solid rgba(255, 255, 255, 0.25); + border-radius: 50%; + box-shadow: 0 12px 34px rgba(32, 191, 142, 0.3); + text-decoration: none; + transition: transform 180ms var(--ease-out), box-shadow 180ms ease; +} + +.whatsapp-float:hover { + color: #062722; + box-shadow: 0 16px 42px rgba(32, 191, 142, 0.42); + transform: translateY(-3px) scale(1.02); +} + +.whatsapp-float svg { + width: 25px; + height: 25px; +} + +/* WordPress helpers */ +.sticky { + border-color: rgba(116, 213, 199, 0.38); +} + +.bypostauthor { + border-left: 2px solid var(--color-brand); +} + +.alignleft { + float: left; + margin: 0 1.5em 1em 0; +} + +.alignright { + float: right; + margin: 0 0 1em 1.5em; +} + +.aligncenter { + display: block; + margin-right: auto; + margin-left: auto; +} + +.wp-block-image img, +.entry-content img { + border-radius: var(--radius-sm); +} + +.wp-block-separator { + margin-block: 2.5em; + border-color: var(--color-line-soft); +} + +.wp-block-button__link { + border-radius: 12px; +} + +/* Motion reveal: content remains visible until JS opts in. */ +.has-reveal [data-reveal] { + opacity: 0; + transform: translateY(18px); + transition: opacity 520ms var(--ease-out), transform 520ms var(--ease-out); +} + +.has-reveal [data-reveal].is-visible { + opacity: 1; + transform: translateY(0); +} + +@media (max-width: 1080px) { + :root { + --gutter: clamp(20px, 4vw, 48px); + } + + .hero__inner { + gap: 36px; + grid-template-columns: minmax(0, 6fr) minmax(280px, 4fr); + } + + .services-grid, + .projects-grid, + .posts-grid { + grid-template-columns: repeat(2, minmax(0, 1fr)); + } + + .project-card--featured { + grid-column: auto; + } + + .project-card--featured .project-card__media { + aspect-ratio: 16 / 10; + } + + .article-layout { + width: min(960px, calc(100% - (2 * var(--gutter)))); + grid-template-columns: minmax(0, 1fr); + } + + .article-layout .sidebar { + position: static; + display: grid; + gap: 18px; + grid-template-columns: repeat(2, minmax(0, 1fr)); + } + + .article-layout .sidebar .widget { + margin-bottom: 0; + } +} + +@media (max-width: 920px) { + :root { + --header-height: 72px; + --hero-peek: 64px; + } + + .brand__role, + .site-description, + .header-cta { + display: none; + } + + .nav-toggle { + z-index: 1002; + display: block; + } + + .primary-nav { + position: fixed; + z-index: 1001; + top: 0; + right: 0; + bottom: 0; + display: flex; + width: min(88vw, 390px); + padding: calc(var(--header-height) + 36px) 30px 34px; + align-items: stretch; + flex-direction: column; + justify-content: flex-start; + background: rgba(7, 22, 36, 0.98); + border-left: 1px solid var(--color-line-soft); + box-shadow: -28px 0 70px rgba(0, 0, 0, 0.34); + visibility: hidden; + opacity: 0; + transform: translateX(100%); + transition: transform 260ms var(--ease-out), opacity 200ms ease, visibility 0s linear 260ms; + } + + .primary-nav.is-open, + .primary-nav[aria-hidden="false"] { + visibility: visible; + opacity: 1; + transform: translateX(0); + transition-delay: 0s; + } + + .primary-nav ul, + .primary-menu, + .site-header .menu { + width: 100%; + align-items: stretch; + flex-direction: column; + gap: 4px; + } + + .primary-nav li, + .primary-nav a, + .site-header .menu a { + width: 100%; + } + + .primary-nav a, + .site-header .menu a { + min-height: 54px; + padding-inline: 12px; + font-size: 1rem; + border-bottom: 1px solid rgba(142, 190, 207, 0.08); + } + + .primary-nav a::after, + .site-header .menu a::after { + display: none; + } + + .hero { + padding-block: 54px 96px; + } + + .hero__inner { + grid-template-columns: minmax(0, 1fr); + } + + .hero__content { + max-width: 760px; + } + + .hero__title { + max-width: 14ch; + } + + .hero__media { + position: absolute; + right: -7%; + bottom: 3%; + width: min(43vw, 390px); + opacity: 0.26; + } + + .hero__image, + .hero__media img { + width: 100%; + max-height: 45vh; + } + + .about-layout, + .contact-panel, + .newsletter-panel, + .content-grid, + .archive-layout { + grid-template-columns: minmax(0, 1fr); + } + + .contact-panel__form, + .newsletter-panel__form { + max-width: 680px; + } + + .archive-posts { + grid-template-columns: repeat(2, minmax(0, 1fr)); + } +} + +@media (max-width: 782px) { + .admin-bar .site-header { + top: 46px; + } +} + +@media (max-width: 680px) { + :root { + --gutter: 20px; + --header-height: 68px; + --hero-peek: 56px; + --section-space: clamp(64px, 16vw, 84px); + } + + .brand__text, + .site-title { + display: none; + } + + .custom-logo, + .brand__image, + .site-logo { + width: 42px; + height: 42px; + } + + .hero-stage { + min-height: calc(100svh - var(--hero-peek)); + } + + .hero { + padding-block: 38px 92px; + } + + .hero__title { + margin-bottom: 21px; + font-size: clamp(2.55rem, 12vw, 3.8rem); + line-height: 1.01; + } + + .hero__description, + .hero__lead { + margin-bottom: 24px; + font-size: 1rem; + } + + .hero__media { + right: -24%; + bottom: 1%; + width: 74vw; + opacity: 0.16; + } + + .hero__actions { + align-items: stretch; + flex-direction: column; + } + + .hero__actions .button { + width: 100%; + } + + .scroll-cue { + right: 20px; + bottom: 16px; + left: auto; + transform: none; + } + + .section-heading { + align-items: flex-start; + flex-direction: column; + gap: 20px; + } + + .proof-grid, + .about-proof, + .stats-grid, + .services-grid, + .projects-grid, + .posts-grid, + .archive-posts, + .article-layout .sidebar, + .footer-grid { + grid-template-columns: minmax(0, 1fr); + } + + .service-card { + min-height: 0; + } + + .project-card__body, + .post-card__body { + padding: 21px; + } + + .contact-panel { + padding: 25px; + border-radius: var(--radius-md); + } + + .contact-panel__form, + .form-card { + padding: 18px; + } + + .newsletter-panel { + padding: 25px; + border-radius: var(--radius-md); + } + + .page-hero, + .archive-hero, + .search-hero, + .entry-hero { + padding-block: calc(var(--header-height) + 52px) 62px; + } + + .post-navigation .nav-links { + grid-template-columns: minmax(0, 1fr); + } + + .comment-list .children { + margin-left: 12px; + padding-left: 0; + } + + .footer-bottom { + align-items: flex-start; + flex-direction: column; + } + + .whatsapp-float-wrap { + right: 16px; + bottom: 16px; + } + + .whatsapp-float { + width: 50px; + height: 50px; + } + +} + +@media (max-height: 650px) and (min-width: 681px) { + .hero-stage { + min-height: 720px; + } + + .hero { + padding-block: 36px 90px; + } +} + +@media (prefers-reduced-motion: reduce) { + html { + scroll-behavior: auto; + } + + *, + *::before, + *::after { + scroll-behavior: auto !important; + animation-duration: 0.01ms !important; + animation-iteration-count: 1 !important; + transition-duration: 0.01ms !important; + } + + .has-reveal [data-reveal] { + opacity: 1; + transform: none; + } +} + +@media print { + :root { + --color-bg: #fff; + --color-text: #111; + --color-text-soft: #333; + } + + body { + color: #111; + background: #fff; + } + + .site-header, + .site-footer, + .newsletter-section, + .sidebar, + .post-navigation, + .comments-area { + display: none !important; + } + + .article-layout { + display: block; + width: 100%; + padding: 0; + } + + a { + color: #111; + } +} + +/* Classic template integration */ +.site-shell { + width: min(var(--container), calc(100% - (2 * var(--gutter)))); + margin-inline: auto; +} + +.site-header__inner { + display: flex; + height: 100%; + align-items: center; + justify-content: space-between; + gap: clamp(18px, 2.4vw, 32px); +} + +.site-header__inner .site-branding { + flex: 0 1 auto; + min-width: 0; +} + +.site-branding__text { + display: grid; + min-width: 0; + color: var(--color-text); + line-height: 1.05; + text-decoration: none; +} + +.site-branding__text:hover { color: var(--color-text); } +.site-branding__name { font-size: 0.92rem; font-weight: 780; } +.site-branding__tagline { + min-width: 0; + margin-top: 4px; + overflow: hidden; + color: var(--color-text-muted); + font-family: var(--font-mono); + font-size: 0.59rem; + letter-spacing: 0.045em; + text-overflow: ellipsis; + text-transform: uppercase; + white-space: nowrap; +} +.site-branding__tagline--short { display: none; } + +.primary-navigation { display: flex; align-items: center; gap: clamp(16px, 2vw, 30px); } +.primary-navigation a { + position: relative; + display: inline-flex; + min-height: 44px; + align-items: center; + color: var(--color-text-soft); + font-size: 0.84rem; + font-weight: 620; + text-decoration: none; + transition: color 180ms ease; +} + +.primary-navigation a::after { + position: absolute; + right: 0; + bottom: 7px; + left: 0; + height: 1px; + content: ""; + background: var(--color-brand); + transform: scaleX(0); + transform-origin: right; + transition: transform 220ms var(--ease-out); +} + +.primary-navigation a:hover, +.primary-navigation .current-menu-item > a { color: var(--color-text); } +.primary-navigation a:hover::after, +.primary-navigation .current-menu-item > a::after { transform: scaleX(1); transform-origin: left; } + +.site-header__contact { + display: inline-flex; + min-width: max-content; + min-height: 40px; + padding: 9px 14px; + align-items: center; + gap: 9px; + color: var(--color-text-soft); + background: rgba(116, 213, 199, 0.06); + border: 1px solid rgba(116, 213, 199, 0.22); + border-radius: 999px; + font-family: var(--font-mono); + font-size: 0.66rem; + text-decoration: none; +} + +.site-header__contact:hover { color: var(--color-brand); border-color: rgba(116, 213, 199, 0.48); } +.site-header__status, +.hero__availability-dot { + width: 8px; + height: 8px; + flex: 0 0 auto; + background: var(--color-success); + border-radius: 50%; + box-shadow: 0 0 0 5px rgba(123, 224, 161, 0.11); +} + +.menu-toggle { + position: relative; + display: none; + width: 46px; + height: 46px; + padding: 0; + color: var(--color-text); + background: rgba(255, 255, 255, 0.035); + border: 1px solid var(--color-line-soft); + border-radius: 12px; +} + +.menu-toggle__label { + position: absolute !important; + width: 1px; + height: 1px; + overflow: hidden; + clip-path: inset(50%); + white-space: nowrap; +} + +.menu-toggle__icon, +.menu-toggle__icon span { position: absolute; pointer-events: none; } +.menu-toggle__icon { inset: 0; } +.menu-toggle__icon span { + left: 50%; + width: 20px; + height: 2px; + background: currentColor; + border-radius: 999px; + transform: translateX(-50%); + transition: top 200ms ease, transform 200ms ease, opacity 160ms ease; +} +.menu-toggle__icon span:nth-child(1) { top: 15px; } +.menu-toggle__icon span:nth-child(2) { top: 22px; } +.menu-toggle__icon span:nth-child(3) { top: 29px; } +.menu-toggle[aria-expanded="true"] .menu-toggle__icon span:nth-child(1) { top: 22px; transform: translateX(-50%) rotate(45deg); } +.menu-toggle[aria-expanded="true"] .menu-toggle__icon span:nth-child(2) { opacity: 0; } +.menu-toggle[aria-expanded="true"] .menu-toggle__icon span:nth-child(3) { top: 22px; transform: translateX(-50%) rotate(-45deg); } + +.menu-drawer-close { display: none; } + +/* + * WordPress inserts its native Customizer shortcut just outside the left edge + * of a partial by default. The preview iframe clips that area behind the + * Customizer panel, so keep the native shortcut inside each theme section. + */ +.hero-stage > .customize-partial-edit-shortcut, +.section > .customize-partial-edit-shortcut { + z-index: 10; +} + +.hero-stage > .customize-partial-edit-shortcut button, +.section > .customize-partial-edit-shortcut button { + left: 22px !important; +} + +.hero-stage > .customize-partial-edit-shortcut button { + top: calc(var(--header-height) + 16px) !important; +} + +.section > .customize-partial-edit-shortcut button { + top: 18px !important; +} + +/* Native WordPress menu partials are attached to their rendered
      . */ +.primary-navigation .primary-menu[data-customize-partial-id], +.news-category-nav__list[data-customize-partial-id] { + position: relative; +} + +.primary-navigation .primary-menu[data-customize-partial-id] > .customize-partial-edit-shortcut, +.news-category-nav__list[data-customize-partial-id] > .customize-partial-edit-shortcut { + z-index: 20; +} + +.primary-navigation .primary-menu[data-customize-partial-id] > .customize-partial-edit-shortcut button, +.news-category-nav__list[data-customize-partial-id] > .customize-partial-edit-shortcut button { + left: -38px !important; + top: 0 !important; +} + +/* Explicit native shortcuts for the menu container and floating WhatsApp link. */ +#site-navigation[data-customize-partial-id] { + position: relative; +} + +.whatsapp-float-wrap[data-customize-partial-id] { position: fixed; } + +#site-navigation[data-customize-partial-id] > .customize-partial-edit-shortcut, +.whatsapp-float-wrap[data-customize-partial-id] > .customize-partial-edit-shortcut { + z-index: 1005; +} + +#site-navigation[data-customize-partial-id] > .customize-partial-edit-shortcut button { + left: -40px !important; + top: 8px !important; +} + +.whatsapp-float-wrap[data-customize-partial-id] > .customize-partial-edit-shortcut button { + left: -36px !important; + top: 2px !important; +} + +.hero.site-shell { + grid-template-columns: minmax(0, 7fr) minmax(320px, 5fr); + gap: clamp(38px, 6vw, 88px); +} + +.hero__visual { position: relative; z-index: 1; display: grid; place-items: center; } +.hero__visual-glow { + position: absolute; + z-index: -1; + width: 88%; + aspect-ratio: 1; + background: radial-gradient(circle, rgba(80, 229, 207, 0.16), transparent 68%); + filter: blur(12px); +} +.hero__visual .hero__image { width: min(43vw, 570px); max-height: 65vh; object-fit: contain; } +.hero__availability { + display: inline-flex; + margin: 0; + align-items: center; + gap: 10px; + color: var(--color-text-soft); + font-family: var(--font-mono); + font-size: 0.72rem; +} + +.section__header { max-width: 780px; margin-bottom: clamp(34px, 5vw, 58px); } +.section__header--split { + display: grid; + max-width: none; + align-items: end; + gap: 12px clamp(28px, 5vw, 70px); + grid-template-columns: minmax(0, 1fr) minmax(300px, 0.7fr); +} +.section__header--split .eyebrow { grid-column: 1 / -1; } +.section__title { margin-bottom: 0; } +.section__description { max-width: 58ch; margin: 0; color: var(--color-text-soft); } +.section__action { display: flex; margin-top: 36px; justify-content: center; } +.posts-grid--featured { grid-template-columns: repeat(2, minmax(0, 1fr)); } +.section--about { padding-top: clamp(52px, 6vw, 80px); } + +.about-layout__header { position: sticky; top: calc(var(--header-height) + 28px); margin-bottom: 0; } +.about-layout__content .prose p { color: var(--color-text-soft); font-size: clamp(1rem, 1.3vw, 1.16rem); } +.about-layout__content .prose p + p { margin-top: 18px; } +.about-facts { + display: grid; + margin: 36px 0 0; + gap: 12px; + grid-template-columns: repeat(3, minmax(0, 1fr)); +} +.about-fact { + min-width: 0; + padding: 19px; + background: linear-gradient(145deg, rgba(17, 44, 66, 0.92), rgba(13, 36, 56, 0.86)); + border: 1px solid var(--color-line-soft); + border-radius: var(--radius-sm); +} +.about-fact dt { margin-bottom: 7px; color: var(--color-brand); font-family: var(--font-mono); font-size: 0.68rem; text-transform: uppercase; } +.about-fact dd { margin: 0; color: var(--color-text-soft); font-size: 0.82rem; line-height: 1.5; } + +.service-card__number { display: block; margin-bottom: 52px; color: var(--color-brand); } +.service-card__title { margin-bottom: 14px; } +.service-card__description { margin-bottom: 0; } + +.project-card__link { + display: flex; + min-height: 100%; + flex: 1; + flex-direction: column; + color: inherit; + text-decoration: none; +} +.project-card__link:hover { color: inherit; } +.project-card__placeholder img { position: relative; z-index: 1; width: 80px; opacity: 0.88; } +.project-card__type { margin-bottom: 11px; color: var(--color-brand); font-family: var(--font-mono); font-size: 0.68rem; text-transform: uppercase; } +.project-card__description { margin-bottom: 18px; color: var(--color-text-soft); font-size: 0.9rem; line-height: 1.65; } +.project-card__tags { display: flex; margin: 2px 0 18px; padding: 0; flex-wrap: wrap; gap: 6px; list-style: none; } +.project-card__tags li { + display: inline-flex; + min-height: 28px; + padding: 5px 9px; + align-items: center; + color: var(--color-text-soft); + background: rgba(116, 213, 199, 0.07); + border: 1px solid rgba(116, 213, 199, 0.18); + border-radius: 999px; + font-family: var(--font-mono); + font-size: 0.66rem; +} +.project-card__cta, +.post-card__more, +.text-link { + display: inline-flex; + margin-top: auto; + align-items: center; + gap: 7px; + color: var(--color-brand); + font-size: 0.78rem; + font-weight: 720; + text-decoration: none; +} +.post-card__more { position: relative; z-index: 2; } + +.contact-list { display: grid; margin: 28px 0 0; padding: 0; gap: 10px; list-style: none; } +.contact-list li { display: grid; gap: 2px; } +.contact-list li > span { color: var(--color-text-muted); font-family: var(--font-mono); font-size: 0.65rem; text-transform: uppercase; } +.contact-list a { color: var(--color-text-soft); text-decoration: none; } +.contact-list a:hover { color: var(--color-brand); } +.contact-panel__description { color: var(--color-text-soft); } +.form-privacy { margin: 14px 0 0; color: var(--color-text-muted); font-size: 0.68rem; line-height: 1.5; } +.section--newsletter { padding-block: clamp(54px, 7vw, 90px); background: var(--color-bg-deep); border-top: 1px solid var(--color-line-soft); } + +.article-layout__main { min-width: 0; } +.article-layout__main .posts-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } +.content-narrow { + width: min(920px, calc(100% - (2 * var(--gutter)))); + padding-block: calc(var(--header-height) + clamp(68px, 8vw, 112px)) var(--section-space); +} +.page-article__header { margin-bottom: 38px; } +.page-article__title { margin-bottom: 0; } +.page-article__media { margin-bottom: 42px; overflow: hidden; border: 1px solid var(--color-line-soft); border-radius: var(--radius-lg); } +.page-article__media img { width: 100%; } +.entry-hero .entry-meta { margin: 24px 0 0; } +.entry-hero .entry-meta a { color: var(--color-brand); text-decoration: none; } +.entry-terms + .entry-terms { margin-top: 10px; } +.entry-terms > span { color: var(--color-text-muted); } +.entry-terms a { color: var(--color-brand); } + +/* Post reading layout — compact editorial treatment inspired by the reference. */ +.single-post-main { + background: linear-gradient(180deg, #061521 0%, #071a29 42%, #061521 100%); +} + +.single-post-main .entry-hero { + padding-block: calc(var(--header-height) + 44px) 26px; + background: linear-gradient(180deg, #071b2a 0%, #081824 100%); + border-bottom: 2px solid var(--color-brand); +} + +.entry-hero__inner { + display: flex; + align-items: center; + flex-direction: column; + text-align: center; +} + +.entry-breadcrumbs { + display: flex; + margin-bottom: 30px; + align-items: center; + gap: 7px; + align-self: stretch; + color: #7890a0; + font-family: var(--font-mono); + font-size: 0.61rem; + text-align: left; +} + +.entry-breadcrumbs a { + color: inherit; + text-decoration: none; +} + +.entry-breadcrumbs a:hover { color: var(--color-brand); } + +.entry-category-badge { + display: inline-flex; + margin-bottom: 12px; + padding: 4px 11px; + color: #052b28; + background: var(--color-brand); + border-radius: 3px; + font-family: var(--font-mono); + font-size: 0.61rem; + font-weight: 800; + letter-spacing: 0.025em; + line-height: 1.1; + text-decoration: none; + text-transform: uppercase; +} + +a.entry-category-badge:hover { color: #052b28; background: var(--color-brand-strong); } + +.single-post-main .entry-hero h1 { + max-width: 28ch; + margin-bottom: 0; + font-size: clamp(2rem, 4.3vw, 3.55rem); + line-height: 1.04; + text-transform: uppercase; +} + +.entry-post-meta { + display: flex; + min-height: 48px; + align-items: center; + justify-content: space-between; + gap: 18px; + color: #c6d2d9; + border-bottom: 1px solid rgba(142, 190, 207, 0.18); + font-family: var(--font-mono); + font-size: 0.62rem; +} + +.entry-post-meta__author { + display: flex; + align-items: center; + flex-wrap: wrap; + gap: 7px; +} + +.entry-post-meta__author strong { color: #f4fafb; } + +.entry-share { + display: flex; + align-items: center; + gap: 5px; +} + +.entry-share a { + display: inline-grid; + width: 25px; + height: 25px; + place-items: center; + color: #071624; + background: var(--color-brand); + border-radius: 50%; + font-family: var(--font-sans); + font-size: 0.62rem; + font-weight: 800; + text-decoration: none; +} + +.entry-share a svg { + width: 14px; + height: 14px; +} + +.entry-share a:nth-child(2) { background: #2b86d8; color: #fff; } +.entry-share a:nth-child(3) { background: #050b12; color: #fff; } +.entry-share a:nth-child(4) { background: #2879a9; color: #fff; } +.entry-share a:hover { transform: translateY(-2px); filter: brightness(1.12); } + +.single-post-main .entry-featured-image { + width: min(1160px, calc(100% - (2 * var(--gutter)))); + margin-top: 28px; + border-radius: 0; + box-shadow: 0 16px 36px rgba(0, 0, 0, 0.28); +} + +.single-post-main .entry-featured-image img { + max-height: 610px; + object-fit: cover; +} + +.single-post-main .article-layout { + padding-block: 26px 90px; + gap: clamp(30px, 5vw, 58px); +} + +.single-post-main .entry-content { + color: #d4dfe4; + font-size: 0.9rem; + line-height: 1.58; +} + +.single-post-main .entry-content h2, +.single-post-main .entry-content h3 { + margin-top: 1.7em; + font-size: 1.2rem; + letter-spacing: -0.02em; +} + +.single-post-main .entry-content p, +.single-post-main .entry-content ul, +.single-post-main .entry-content ol { + margin-bottom: 1.15em; +} + +.single-post-main .sidebar { + top: calc(var(--header-height) + 20px); +} + +.single-post-main .sidebar .widget { + padding: 18px; + background: linear-gradient(145deg, rgba(15, 40, 60, 0.96), rgba(8, 27, 42, 0.96)); + border-color: rgba(116, 213, 199, 0.12); + border-radius: 11px; +} + +.single-post-main .sidebar .widget h2, +.single-post-main .sidebar .widget h3 { + margin-bottom: 10px; + font-size: 0.82rem; +} + +.single-post-main .sidebar .widget_newsletter__text, +.single-post-main .sidebar .widget p { + font-size: 0.68rem; + line-height: 1.45; +} + +@media (max-width: 700px) { + .single-post-main .entry-hero { padding-block: calc(var(--header-height) + 28px) 22px; } + .entry-breadcrumbs { margin-bottom: 24px; } + .single-post-main .entry-hero h1 { font-size: clamp(1.65rem, 7vw, 2.25rem); } + .entry-post-meta { align-items: flex-start; flex-direction: column; padding-block: 10px; } + .entry-share { align-self: flex-end; } + .single-post-main .entry-featured-image { margin-top: 18px; } + .single-post-main .article-layout { padding-top: 20px; } +} + +/* Final single-post polish: compact editorial layout from the supplied reference. */ +.single-post-main { + --post-reading-font: "Segoe UI", "Helvetica Neue", Arial, sans-serif; +} + +.single-post-main .entry-hero { + padding-block: calc(var(--header-height) + 22px) 18px; + background: linear-gradient(180deg, #071b2a 0%, #081824 100%); +} + +.single-post-main .entry-breadcrumbs { display: none; } +.single-post-main .entry-category-badge { + margin-bottom: 10px; + padding: 4px 8px; + font-family: var(--post-reading-font); + font-size: 0.57rem; + letter-spacing: 0; +} + +.single-post-main .entry-hero h1 { + max-width: 620px; + font-family: var(--post-reading-font); + font-size: clamp(1.75rem, 4vw, 2.55rem); + font-weight: 750; + letter-spacing: -0.025em; + line-height: 1.08; + text-transform: none; +} + +.single-post-main .entry-post-meta { + min-height: 40px; + font-family: var(--post-reading-font); + font-size: 0.56rem; +} + +.single-post-main .entry-post-meta__author { gap: 4px; } +.single-post-main .entry-share { gap: 4px; } +.single-post-main .entry-share a { width: 19px; height: 19px; } +.single-post-main .entry-share a svg { width: 11px; height: 11px; } + +.single-post-main .entry-featured-image { + width: min(1160px, calc(100% - 6px)); + margin-top: 3px; +} + +.single-post-main .entry-featured-image img { + width: 100%; + max-height: 610px; + object-fit: cover; +} + +.single-post-main .article-layout { + width: min(1160px, calc(100% - 32px)); + padding-block: 18px 70px; + gap: clamp(18px, 4vw, 42px); + grid-template-columns: minmax(0, 1fr) minmax(92px, 0.34fr); +} + +.single-post-main .entry-content { + color: #d7e1e5; + font-family: var(--post-reading-font); + font-size: clamp(0.78rem, 1.1vw, 0.9rem); + line-height: 1.52; +} + +.single-post-main .entry-content h2, +.single-post-main .entry-content h3 { + margin-top: 1.55em; + margin-bottom: 0.55em; + font-family: var(--post-reading-font); + font-size: clamp(1rem, 1.4vw, 1.18rem); + font-weight: 750; + letter-spacing: -0.018em; +} + +.single-post-main .entry-content p, +.single-post-main .entry-content ul, +.single-post-main .entry-content ol { margin-bottom: 0.95em; } + +.single-post-main .sidebar { top: calc(var(--header-height) + 16px); } +.single-post-main .sidebar .widget { + padding: 12px; + background: linear-gradient(145deg, rgba(15, 40, 60, 0.96), rgba(8, 27, 42, 0.96)); + border-color: rgba(116, 213, 199, 0.14); + border-radius: 8px; +} + +.single-post-main .sidebar .widget h2, +.single-post-main .sidebar .widget h3 { + margin-bottom: 8px; + font-family: var(--post-reading-font); + font-size: 0.68rem; +} + +.single-post-main .sidebar .widget p, +.single-post-main .sidebar .widget label, +.single-post-main .sidebar .widget input, +.single-post-main .sidebar .widget button { font-family: var(--post-reading-font); font-size: 0.57rem; } + +@media (max-width: 700px) { + .single-post-main .entry-hero { padding-block: calc(var(--header-height) + 20px) 16px; } + .single-post-main .entry-hero h1 { font-size: clamp(1.52rem, 7vw, 2rem); } + .single-post-main .entry-post-meta { min-height: 38px; } + .single-post-main .article-layout { + width: calc(100% - 16px); + padding-top: 14px; + gap: 14px; + grid-template-columns: minmax(0, 1fr) minmax(88px, 96px); + } + .single-post-main .article-layout .sidebar { display: block; } + .single-post-main .article-layout .sidebar .widget { margin-bottom: 12px; } +} + +@media (max-width: 680px) { + body.single-post { --header-height: 30px; } + body.single-post .site-header__inner { gap: 8px; } + body.single-post .custom-logo { width: 18px; height: 18px; } + body.single-post .site-branding { gap: 5px; } + body.single-post .site-branding__name { font-size: 0.48rem; } + body.single-post .site-branding__tagline--short { display: block; margin-top: 1px; font-size: 0.3rem; } + body.single-post .menu-toggle { display: none; } + body.single-post .primary-navigation { + position: static; + display: flex; + width: auto; + padding: 0; + background: transparent; + border: 0; + box-shadow: none; + visibility: visible; + opacity: 1; + transform: none; + } + body.single-post .primary-navigation ul { flex-direction: row; gap: 7px; } + body.single-post .primary-navigation a { + min-height: 25px; + width: auto; + padding: 0; + border: 0; + font-size: 0.42rem; + } +} + +/* Readable type and fluid stacking on phones. */ +.single-post-main .entry-category-badge { font-size: 0.68rem; } +.single-post-main .entry-post-meta { font-size: 0.72rem; } +.single-post-main .entry-content { + font-size: clamp(0.98rem, 1.2vw, 1.06rem); + line-height: 1.68; +} +.single-post-main .entry-content h2, +.single-post-main .entry-content h3 { font-size: clamp(1.18rem, 1.8vw, 1.35rem); } +.single-post-main .sidebar .widget p, +.single-post-main .sidebar .widget label, +.single-post-main .sidebar .widget input, +.single-post-main .sidebar .widget button { font-size: 0.78rem; } + +/* Sharing, related articles and post-to-post navigation. */ +.single-post-main .entry-post-meta { font-size: 0.82rem; } +.single-post-main .entry-share { gap: 7px; } +.single-post-main .entry-share a { width: 30px; height: 30px; } +.single-post-main .entry-share a svg { width: 17px; height: 17px; } +.single-post-main .entry-share a:first-child { color: #fff; background: #25d366; } + +@media (min-width: 861px) { + .single-post-main .sidebar { + top: calc(var(--header-height) + 42px); + scroll-margin-top: calc(var(--header-height) + 42px); + } +} + +.post-share-panel { + display: flex; + margin-top: 42px; + padding-top: 28px; + align-items: center; + flex-wrap: wrap; + gap: 14px; + border-top: 1px solid var(--color-line-soft); +} +.post-share-panel__title { + margin: 0; + color: var(--color-text); + font-family: var(--post-reading-font); + font-size: 0.84rem; + font-weight: 800; + letter-spacing: 0.075em; + text-transform: uppercase; +} +.post-share-panel__links { + display: flex; + align-items: center; + gap: 9px; +} +.post-share-panel__links a { + display: grid; + width: 44px; + height: 44px; + place-items: center; + color: #fff; + background: var(--color-brand); + border-radius: 50%; + text-decoration: none; + transition: transform 180ms var(--ease-out), filter 180ms ease; +} +.post-share-panel__links a:first-child { background: #25d366; } +.post-share-panel__links a:nth-child(2) { background: #1877f2; } +.post-share-panel__links a:nth-child(3) { background: #050505; } +.post-share-panel__links a:nth-child(4) { background: #0a66c2; } +.post-share-panel__links a:hover { color: #fff; filter: brightness(1.12); transform: translateY(-2px); } +.post-share-panel__links svg { width: 23px; height: 23px; } + +.related-posts { margin-top: 42px; } +.related-posts__heading { + display: grid; + margin-bottom: 24px; + align-items: center; + gap: 12px; + grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr); +} +.related-posts__heading span { height: 1px; background: var(--color-line-soft); } +.related-posts__heading h2 { + margin: 0; + color: var(--color-text-muted); + font-family: var(--post-reading-font); + font-size: 0.82rem; + font-weight: 500; + letter-spacing: 0.04em; +} +.related-posts__grid { display: grid; gap: 16px; grid-template-columns: repeat(3, minmax(0, 1fr)); } +.related-post__link { display: block; color: var(--color-text); text-decoration: none; } +.related-post__image { + width: 100%; + aspect-ratio: 16 / 9; + margin-bottom: 11px; + object-fit: cover; + border-radius: 7px; +} +.related-post__title { + margin: 0; + font-family: var(--post-reading-font); + font-size: 0.92rem; + font-weight: 750; + line-height: 1.35; + letter-spacing: -0.015em; +} +.related-post__link:hover { color: var(--color-brand); } + +.single-post-main .post-navigation--editorial { margin-top: 44px; } +.single-post-main .post-navigation--editorial .nav-links { grid-template-columns: repeat(2, minmax(0, 1fr)); } +.single-post-main .post-navigation--editorial .nav-next { text-align: right; } +.single-post-main .post-navigation--editorial a { min-height: 124px; background: rgba(13, 36, 56, 0.56); } + +@media (max-width: 680px) { + .single-post-main .entry-post-meta { font-size: 0.78rem; } + .single-post-main .entry-share a { width: 34px; height: 34px; } + .single-post-main .entry-share a svg { width: 19px; height: 19px; } + .post-share-panel { margin-top: 34px; gap: 12px; } + .post-share-panel__links { width: 100%; gap: 10px; } + .post-share-panel__links a { width: 46px; height: 46px; } + .post-share-panel__links svg { width: 24px; height: 24px; } + .related-posts { margin-top: 34px; } + .related-posts__grid { gap: 22px; grid-template-columns: minmax(0, 1fr); } + .related-post__title { font-size: 1rem; } + .single-post-main .post-navigation--editorial .nav-links { grid-template-columns: minmax(0, 1fr); } + .single-post-main .post-navigation--editorial .nav-next { text-align: left; } +} + +@media (max-width: 860px) { + .single-post-main .article-layout { + width: min(720px, calc(100% - 32px)); + grid-template-columns: minmax(0, 1fr); + } + .single-post-main .sidebar { + position: static; + display: grid; + gap: 16px; + grid-template-columns: repeat(2, minmax(0, 1fr)); + } + .single-post-main .sidebar .widget { margin-bottom: 0; } +} + +@media (max-width: 680px) { + body.single-post { --header-height: 64px; } + body.single-post .site-header__inner { gap: 12px; } + body.single-post .custom-logo { width: 30px; height: 30px; } + body.single-post .site-branding { gap: 8px; } + body.single-post .site-branding__name { font-size: 0.78rem; } + body.single-post .site-branding__tagline--short { margin-top: 2px; font-size: 0.42rem; } + body.single-post .menu-toggle { display: block; } + body.single-post .primary-navigation { + position: fixed; + z-index: 1001; + top: 0; + right: 0; + bottom: 0; + display: flex; + width: min(88vw, 390px); + padding: calc(var(--header-height) + 36px) 30px 34px; + background: rgba(7, 22, 36, 0.98); + border-left: 1px solid var(--color-line-soft); + box-shadow: -28px 0 70px rgba(0, 0, 0, 0.34); + visibility: hidden; + opacity: 0; + transform: translateX(100%); + } + body.single-post .primary-navigation.is-open, + body.single-post .primary-navigation[aria-hidden="false"] { + visibility: visible; + opacity: 1; + transform: translateX(0); + } + body.single-post .primary-navigation ul { + width: 100%; + align-items: stretch; + flex-direction: column; + gap: 4px; + } + body.single-post .primary-navigation a { + min-height: 52px; + width: 100%; + padding-inline: 12px; + font-size: 1rem; + border-bottom: 1px solid rgba(142, 190, 207, 0.08); + } + .single-post-main .entry-hero { padding-block: calc(var(--header-height) + 18px) 22px; } + .single-post-main .entry-hero h1 { + max-width: 20ch; + font-size: clamp(1.85rem, 8.5vw, 2.3rem); + line-height: 1.1; + } + .single-post-main .entry-post-meta { + min-height: 48px; + align-items: center; + flex-direction: row; + flex-wrap: wrap; + padding-block: 10px; + gap: 10px; + font-size: 0.7rem; + } + .single-post-main .entry-share { margin-left: auto; } + .single-post-main .entry-featured-image { margin-top: 8px; } + .single-post-main .article-layout { + width: calc(100% - 32px); + padding-block: 24px 58px; + gap: 28px; + } + .single-post-main .entry-content { font-size: 1rem; line-height: 1.7; } + .single-post-main .entry-content h2, + .single-post-main .entry-content h3 { font-size: 1.25rem; } + .single-post-main .sidebar { + display: grid; + grid-template-columns: minmax(0, 1fr); + } + .single-post-main .sidebar .widget { padding: 18px; } +} +.page-hero__actions { margin-top: 28px; } +.page-hero__search { max-width: 560px; margin-top: 26px; } +.results-count { color: var(--color-text-muted); } + +.search-form__label { flex: 1; } +.search-form__field { + appearance: none; + width: 100%; + min-height: 48px; + padding: 11px 13px; + color: var(--color-text) !important; + background-color: #071827 !important; + border: 1px solid var(--color-line) !important; + border-radius: 9px; +} +.search-form__field:focus { border-color: var(--color-brand); box-shadow: 0 0 0 3px rgba(116, 213, 199, 0.15); outline: none; } +.search-form__submit { + display: inline-flex; + min-width: 48px; + min-height: 48px; + padding: 10px 15px; + align-items: center; + justify-content: center; + color: var(--color-brand-ink); + background: var(--color-brand); + border: 1px solid var(--color-brand); + border-radius: 9px; + font-weight: 720; +} + +.widget .wp-block-search__label { + display: block; + margin-bottom: 7px; + color: var(--color-text-soft); +} + +.widget .wp-block-search__inside-wrapper { + display: flex; + gap: 8px; +} + +.widget .wp-block-search__input { + appearance: none; + min-width: 0; + min-height: 48px; + padding: 11px 13px; + color: var(--color-text) !important; + background: #071827 !important; + border: 1px solid var(--color-line) !important; + border-radius: 9px; +} + +.widget .wp-block-search__input:focus { + border-color: var(--color-brand) !important; + box-shadow: 0 0 0 3px rgba(116, 213, 199, 0.15); + outline: none; +} + +.widget .wp-block-search__button { + min-height: 48px; + margin-left: 0; + border: 1px solid var(--color-brand); +} + +.error-404 { display: block; min-height: 0; padding: 0; text-align: left; } +.error-404 .page-hero__inner { margin-inline: auto; text-align: center; } +.error-404 .page-hero__actions { display: flex; justify-content: center; } +.error-404 .page-hero__search { margin-inline: auto; } + +.site-footer__main { + display: grid; + margin-bottom: 48px; + gap: clamp(32px, 6vw, 78px); + grid-template-columns: minmax(280px, 1.2fr) minmax(260px, 0.8fr) minmax(150px, 0.45fr); +} +.site-footer__brand { max-width: 470px; } +.site-footer__brand-row { display: flex; margin-bottom: 18px; align-items: center; gap: 12px; } +.site-footer__name { margin: 0; color: var(--color-text); font-weight: 760; } +.site-footer__brand > p { color: var(--color-text-muted); font-size: 0.88rem; } +.site-footer__widgets { display: grid; gap: 18px; grid-template-columns: repeat(2, minmax(0, 1fr)); } +.site-footer__widgets .widget { margin: 0; } +.footer-navigation .footer-menu { margin: 0; padding: 0; list-style: none; } +.site-footer__bottom { + display: flex; + padding-top: 24px; + align-items: center; + justify-content: space-between; + gap: 20px; + color: var(--color-text-muted); + border-top: 1px solid var(--color-line-soft); + font-family: var(--font-mono); + font-size: 0.68rem; +} +.site-footer__bottom p { margin: 0; } +.social-links a { width: auto; min-width: 42px; padding-inline: 12px; font-size: 0.72rem; } + +@media (max-width: 1080px) { + .hero.site-shell { gap: 36px; grid-template-columns: minmax(0, 6fr) minmax(280px, 4fr); } + .site-header__contact { display: none; } + .site-footer__main { grid-template-columns: minmax(260px, 1fr) minmax(260px, 1fr); } + .footer-navigation { grid-column: 1 / -1; } +} + +@media (max-width: 920px) { + :root { --hero-peek: 120px; } + .menu-toggle { z-index: 1002; display: block; } + .primary-navigation { + position: fixed; + z-index: 1001; + top: 0; + right: 0; + bottom: 0; + display: flex; + width: min(88vw, 390px); + padding: calc(var(--header-height) + 36px) 30px 34px; + align-items: stretch; + flex-direction: column; + justify-content: flex-start; + background: rgba(7, 22, 36, 0.98); + border-left: 1px solid var(--color-line-soft); + box-shadow: -28px 0 70px rgba(0, 0, 0, 0.34); + visibility: hidden; + opacity: 0; + transform: translateX(100%); + transition: transform 260ms var(--ease-out), opacity 200ms ease, visibility 0s linear 260ms; + } + .primary-navigation.is-open, + .primary-navigation[aria-hidden="false"] { visibility: visible; opacity: 1; transform: translateX(0); transition-delay: 0s; } + .primary-navigation ul { width: 100%; align-items: stretch; flex-direction: column; gap: 4px; } + .primary-navigation li, + .primary-navigation a { width: 100%; } + .primary-navigation a { min-height: 54px; padding-inline: 12px; font-size: 1rem; border-bottom: 1px solid rgba(142, 190, 207, 0.08); } + .primary-navigation a::after { display: none; } + .hero.site-shell { grid-template-columns: minmax(0, 1fr); } + .hero__visual { position: absolute; right: -7%; bottom: 3%; width: min(43vw, 390px); opacity: 0.25; } + .hero__visual .hero__image { width: 100%; max-height: 45vh; } + .section__header--split { grid-template-columns: minmax(0, 1fr); } + .about-layout__header { position: static; } + .site-footer__main { grid-template-columns: minmax(0, 1fr); } + .footer-navigation { grid-column: auto; } +} + +@media (max-width: 680px) { + :root { --hero-peek: 118px; } + .site-branding__text { display: grid; min-width: 0; } + .site-branding__tagline--full { display: none; } + .site-branding__tagline--short { display: block; max-width: none; overflow: visible; font-size: 0.47rem; letter-spacing: 0.01em; text-overflow: clip; } + .hero { padding-block: 28px 62px; } + .hero__title { font-size: clamp(2.35rem, 11vw, 3.1rem); } + .hero__actions { display: grid; align-items: stretch; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8px; } + .hero__actions .button { width: 100%; padding-inline: 12px; } + .hero__visual { right: -24%; bottom: 1%; width: 74vw; opacity: 0.14; } + .section--about { padding-top: 40px; } + .about-facts, + .site-footer__widgets { grid-template-columns: minmax(0, 1fr); } + .service-card__number { margin-bottom: 30px; } + .site-footer__bottom { align-items: flex-start; flex-direction: column; } + .posts-grid--featured { grid-template-columns: minmax(0, 1fr); } + .article-layout__main .posts-grid { grid-template-columns: minmax(0, 1fr); } +} + +@media print { + .site-header, + .site-footer, + .section--newsletter, + .sidebar, + .whatsapp-float, + .post-navigation, + .comments-area { display: none !important; } +} + +/* Editorial news index and category pages. */ +.news-category-nav { + position: relative; + z-index: 20; + color: #dfe5ee; + background: #0b1c3f; + border-top: 1px solid rgba(255, 255, 255, 0.13); + border-bottom: 1px solid rgba(255, 255, 255, 0.13); +} + +/* Inner editorial pages must reserve space for the regular site header. */ +.site-header:not(.site-header--overlay) { + position: relative; + background: var(--color-bg); + border-bottom-color: var(--color-line-soft); +} + +.news-category-nav__inner { + display: flex; + min-height: 42px; + padding: 0; + align-items: stretch; + flex-wrap: wrap; +} + +.news-category-nav__list { + display: flex; + min-height: 42px; + margin: 0; + padding: 0; + align-items: stretch; + flex-wrap: wrap; + list-style: none; +} + +.news-category-nav__list a { + display: inline-flex; + min-height: 42px; + padding: 0 17px; + align-items: center; + color: inherit; + font-size: 0.75rem; + font-weight: 700; + letter-spacing: 0.055em; + text-decoration: none; + text-transform: uppercase; +} + +.news-category-nav__link { + display: inline-flex; + min-height: 42px; + padding: 0 17px; + align-items: center; + color: inherit; + font-size: 0.75rem; + font-weight: 700; + letter-spacing: 0.055em; + text-decoration: none; + text-transform: uppercase; +} + +.news-category-nav__link:hover, +.news-category-nav__link.is-current { + color: #d6f52a; + background: rgba(255, 255, 255, 0.055); +} + +.news-category-nav__list a:hover, +.news-category-nav__list .current-menu-item > a, +.news-category-nav__list .current-menu-ancestor > a { + color: #d6f52a; + background: rgba(255, 255, 255, 0.055); +} + +.news-index { + min-height: 55vh; + color: #0a1b43; + background: #fff; +} + +.news-page-heading { + padding: 17px 0 16px; + color: #fff; + background: #0b1c3f; + box-shadow: 0 3px 7px rgba(0, 0, 0, 0.25); +} + +.news-page-heading h1 { + display: flex; + margin: 0; + align-items: center; + gap: 10px; + color: #fff; + font-size: clamp(1.7rem, 3vw, 2.25rem); + font-weight: 780; + letter-spacing: -0.025em; +} + +.news-page-heading h1::before { + width: 7px; + height: 21px; + content: ""; + background: #d6f52a; + border-radius: 3px; +} + +.news-page-heading__description { + max-width: 68ch; + margin: 8px 0 0 17px; + color: rgba(255, 255, 255, 0.8); + font-size: 0.94rem; +} + +.news-index__content { + padding-top: clamp(34px, 4vw, 44px); + padding-bottom: clamp(58px, 7vw, 90px); +} + +.latest-news-grid { + display: grid; + gap: 28px 22px; + grid-template-columns: minmax(0, 2fr) repeat(2, minmax(0, 1fr)); +} + +.latest-news-card { + min-width: 0; +} + +.latest-news-card--featured { + grid-row: span 2; +} + +.latest-news-card__media { + display: block; + overflow: hidden; + aspect-ratio: 16 / 9; + background: #dce2e8; + border-radius: 6px; +} + +.latest-news-card--featured .latest-news-card__media { + height: 100%; + max-height: 460px; + aspect-ratio: auto; +} + +.latest-news-card__image, +.latest-news-card__placeholder { + width: 100%; + height: 100%; + object-fit: cover; + transition: transform 240ms var(--ease-out); +} + +.latest-news-card__placeholder { + display: block; + background: linear-gradient(135deg, #bfc9d2, #eff2f4); +} + +.latest-news-card:hover .latest-news-card__image, +.latest-news-card:focus-within .latest-news-card__image { + transform: scale(1.035); +} + +.latest-news-card__body { + padding-top: 10px; +} + +.latest-news-card__category { + display: inline-flex; + margin-bottom: 10px; + padding: 4px 8px; + color: #07142e; + background: #d6f52a; + border-radius: 4px; + font-size: 0.66rem; + font-weight: 800; + letter-spacing: 0.035em; + line-height: 1; + text-decoration: none; + text-transform: uppercase; +} + +.latest-news-card__category:hover { color: #07142e; background: #c5e72b; } + +.latest-news-card__title { + margin: 0 0 10px; + color: #0a1b43; + font-size: clamp(1rem, 1.35vw, 1.13rem); + font-weight: 760; + letter-spacing: -0.025em; + line-height: 1.2; +} + +.latest-news-card--featured .latest-news-card__title { + font-size: clamp(1.6rem, 2.5vw, 2.25rem); + line-height: 1.16; +} + +.latest-news-card__title a { color: inherit; text-decoration: none; } +.latest-news-card__title a:hover { color: #35560d; } + +.latest-news-card__date { + display: block; + color: #0a1b43; + font-size: 0.78rem; + line-height: 1.3; +} + +.latest-news-card__excerpt { + margin: 11px 0 0; + color: #17264a; + font-size: 0.95rem; + line-height: 1.45; +} + +@media (max-width: 860px) { + .latest-news-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } + .latest-news-card--featured { grid-column: 1 / -1; grid-row: auto; } + .latest-news-card--featured .latest-news-card__media { height: auto; aspect-ratio: 16 / 8; } +} + +@media (max-width: 560px) { + .news-category-nav__link { padding-inline: 13px; font-size: 0.68rem; } + .news-page-heading { padding-block: 14px; } + .news-index__content { padding-top: 26px; } + .latest-news-grid { gap: 28px; grid-template-columns: minmax(0, 1fr); } + .latest-news-card--featured { grid-column: auto; } + .latest-news-card--featured .latest-news-card__media { aspect-ratio: 16 / 9; } +} + +/* The reference establishes the editorial composition; these rules retain the theme identity. */ +.news-category-nav { + color: var(--color-text-soft); + background: var(--color-bg-alt); + border-color: var(--color-line-soft); +} + +.news-category-nav__link:hover, +.news-category-nav__link.is-current { + color: var(--color-brand-strong); + background: rgba(116, 213, 199, 0.09); +} + +.news-category-nav__list a:hover, +.news-category-nav__list .current-menu-item > a, +.news-category-nav__list .current-menu-ancestor > a { + color: var(--color-brand-strong); + background: rgba(116, 213, 199, 0.09); +} + +.news-index { + color: var(--color-text); + background: var(--color-bg-deep); +} + +.news-page-heading { + background: + radial-gradient(circle at 78% 35%, rgba(116, 213, 199, 0.11), transparent 28%), + linear-gradient(145deg, var(--color-bg), var(--color-bg-alt)); + box-shadow: 0 3px 7px rgba(0, 0, 0, 0.2); +} + +.news-page-heading h1::before { background: var(--color-brand); } +.news-page-heading__description { color: var(--color-text-soft); } +.latest-news-card__media { background: var(--color-surface); } +.latest-news-card__placeholder { background: linear-gradient(135deg, var(--color-surface-raised), var(--color-bg-alt)); } + +.latest-news-card__category { + color: var(--color-brand-ink); + background: var(--color-brand); +} + +.latest-news-card__category:hover { color: var(--color-brand-ink); background: var(--color-brand-strong); } +.latest-news-card__title, +.latest-news-card__date { color: var(--color-text); } +.latest-news-card__title a:hover { color: var(--color-brand-strong); } +.latest-news-card__excerpt { color: var(--color-text-soft); } + +.latest-news-list { + width: min(100%, 720px); + margin-top: clamp(64px, 8vw, 100px); +} + +.latest-news-list__heading { + display: flex; + margin: 0 0 40px; + align-items: center; + gap: 10px; + color: var(--color-text); + font-size: clamp(1.15rem, 1.8vw, 1.45rem); +} + +.latest-news-list__heading::before { + width: 5px; + height: 21px; + content: ""; + background: var(--color-brand); + border-radius: 4px; +} + +.latest-news-list__items { display: grid; gap: 28px; } + +.latest-news-list__item { + display: grid; + min-width: 0; + align-items: center; + gap: 17px; + grid-template-columns: 120px minmax(0, 1fr); +} + +.latest-news-list__image { + display: block; + overflow: hidden; + aspect-ratio: 16 / 10; + background: var(--color-surface); + border-radius: var(--radius-sm); +} + +.latest-news-list__image img { + width: 100%; + height: 100%; + object-fit: cover; + transition: transform 220ms var(--ease-out); +} + +.latest-news-list__item:hover .latest-news-list__image img { transform: scale(1.05); } +.latest-news-list__body .latest-news-card__category { margin-bottom: 8px; } + +.latest-news-list__body h3 { + margin: 0 0 7px; + color: var(--color-text); + font-size: clamp(0.94rem, 1.4vw, 1.08rem); + line-height: 1.28; +} + +.latest-news-list__body h3 a { color: inherit; text-decoration: none; } +.latest-news-list__body h3 a:hover { color: var(--color-brand-strong); } +.latest-news-list__body time { color: var(--color-text-muted); font-size: 0.76rem; } + +@media (max-width: 560px) { + .latest-news-list { margin-top: 58px; } + .latest-news-list__heading { margin-bottom: 28px; } + .latest-news-list__item { gap: 13px; grid-template-columns: 92px minmax(0, 1fr); } +} + +/* Editorial page header order: site header, categories, then page heading. */ +.site-header--editorial, +.site-header--editorial.is-scrolled { + position: sticky; + top: 0; + right: auto; + left: auto; + background: var(--color-bg); + border-bottom: 1px solid var(--color-line-soft); + box-shadow: none; + backdrop-filter: none; +} + +/* WordPress adds a top offset for the admin bar to fixed headers. This + header is in normal document flow, so that offset would cover the category + navigation below it. */ +.admin-bar .site-header--editorial, +.admin-bar .site-header--editorial.is-scrolled { + /* WordPress already applies a top document margin for #wpadminbar. */ + top: 0; +} + +.site-header--editorial + .news-category-nav { + position: relative; + z-index: 10; +} + +.site-header--editorial + .news-category-nav + .news-index .news-page-heading { + position: relative; + z-index: 1; +} + +.mobile-news-categories { display: none; } + +@media (max-width: 920px) { + /* One solid, scrollable drawer for every page type. */ + .primary-navigation, + .site-header--editorial .primary-navigation { + position: fixed !important; + top: 0 !important; + right: 0 !important; + bottom: 0 !important; + height: 100dvh !important; + min-height: 100dvh !important; + max-height: none !important; + z-index: 100000 !important; + /* Overrides the legacy mobile drawer padding (which left a large gap). */ + padding: 62px 24px 78px !important; + background-color: #071624 !important; + background-image: none !important; + isolation: isolate; + overflow-x: hidden; + overflow-y: auto; + overscroll-behavior: contain; + scrollbar-width: thin; + scrollbar-color: var(--color-brand) transparent; + } + .primary-navigation.is-open, + .primary-navigation[aria-hidden="false"] { + opacity: 1 !important; + } + .menu-toggle { z-index: 100001 !important; } + .menu-drawer-close { + position: absolute; + top: 14px; + right: 18px; + z-index: 3; + display: grid; + width: 46px; + height: 46px; + padding: 0; + place-items: center; + color: var(--color-text); + background: rgba(255, 255, 255, 0.035); + border: 1px solid var(--color-line-soft); + border-radius: 12px; + } + .menu-drawer-close span { + position: absolute; + width: 20px; + height: 2px; + background: currentColor; + border-radius: 999px; + } + .menu-drawer-close span:first-child { transform: rotate(45deg); } + .menu-drawer-close span:last-child { transform: rotate(-45deg); } + .admin-bar .primary-navigation { top: 32px !important; height: calc(100dvh - 32px) !important; min-height: calc(100dvh - 32px) !important; } + .primary-navigation::-webkit-scrollbar, + .site-header--editorial .primary-navigation::-webkit-scrollbar { width: 6px; } + .primary-navigation::-webkit-scrollbar-thumb, + .site-header--editorial .primary-navigation::-webkit-scrollbar-thumb { background: var(--color-brand); border-radius: 999px; } + .primary-navigation::after, + .site-header--editorial .primary-navigation::after { + position: absolute; + right: 22px; + bottom: 17px; + z-index: 2; + display: grid; + width: 34px; + height: 34px; + place-items: center; + color: var(--color-brand-ink); + background: var(--color-brand); + border-radius: 50%; + box-shadow: 0 8px 22px rgba(0, 0, 0, 0.3); + content: "↓"; + font-size: 1.1rem; + font-weight: 800; + opacity: 0; + pointer-events: none; + transform: translateY(8px); + transition: opacity 160ms ease, transform 160ms ease; + } + .primary-navigation.is-open.has-more-content::after, + .site-header--editorial .primary-navigation.is-open.has-more-content::after { + opacity: 1; + transform: translateY(0); + } + .site-header--editorial + .news-category-nav { display: none; } + .primary-navigation .mobile-news-categories { + display: block; + width: 100%; + margin-top: 22px; + padding-top: 18px; + border-top: 1px solid var(--color-line-soft); + } + .mobile-news-categories__title { + margin: 0 0 8px; + color: var(--color-text-muted); + font-family: var(--font-mono); + font-size: 0.7rem; + font-weight: 700; + letter-spacing: 0.1em; + text-transform: uppercase; + } + .primary-navigation .mobile-news-categories__list { + display: grid; + width: 100%; + margin: 0; + padding: 0; + gap: 2px; + list-style: none; + } + .primary-navigation .mobile-news-categories__list a { + min-height: 46px; + color: var(--color-text-soft); + } + .primary-navigation .mobile-news-categories__list a.is-current, + .primary-navigation .mobile-news-categories__list .current-menu-item > a, + .primary-navigation .mobile-news-categories__list .current-menu-ancestor > a { color: var(--color-brand-strong); } + .admin-bar .site-header--editorial .primary-navigation { top: 32px; } +} + +@media (max-width: 782px) { + .admin-bar .site-header--editorial, + .admin-bar .site-header--editorial.is-scrolled { top: 0; } + .admin-bar .site-header--editorial .primary-navigation { top: 46px; } + .admin-bar .primary-navigation { top: 46px !important; height: calc(100dvh - 46px) !important; min-height: calc(100dvh - 46px) !important; } +} + +/* Keep the long professional tagline from stretching the mobile header. */ +@media (max-width: 680px) { + .site-header__inner { gap: 12px; } + .site-header__inner .site-branding { + min-width: 0; + max-width: calc(100% - 64px); + } + .site-header__inner .site-branding__text { min-width: 0; max-width: 100%; } + .site-header__inner .site-branding__tagline--full { display: none !important; } + .site-header__inner .site-branding__tagline--short { + display: block; + max-width: 100%; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } + .site-header__inner .menu-toggle { flex: 0 0 46px; } +} + +@media (max-width: 680px) { + .single-post-main .entry-post-meta { justify-content: center; } + .single-post-main .entry-share { + width: 100%; + margin: 6px auto 0 !important; + justify-content: center; + } +} + +/* The single-post hero used to compensate for an absolute header. The + editorial header is now in normal flow, so that extra top space must not + be added again. */ +.site-header--editorial ~ .single-post-main .entry-hero { + padding-top: clamp(28px, 4vw, 54px); +} + +@media (max-width: 700px) { + .site-header--editorial ~ .single-post-main .entry-hero { + padding-top: 24px; + } +} diff --git a/wp-content/themes/gustavoo-portfolio/assets/fonts/INTER-LICENSE.txt b/wp-content/themes/gustavoo-portfolio/assets/fonts/INTER-LICENSE.txt new file mode 100644 index 0000000..b525cbf --- /dev/null +++ b/wp-content/themes/gustavoo-portfolio/assets/fonts/INTER-LICENSE.txt @@ -0,0 +1,93 @@ +Copyright 2020 The Inter Project Authors (https://github.com/rsms/inter) + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +http://scripts.sil.org/OFL + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/wp-content/themes/gustavoo-portfolio/assets/fonts/inter-variable.woff2 b/wp-content/themes/gustavoo-portfolio/assets/fonts/inter-variable.woff2 new file mode 100644 index 0000000..350bbbc Binary files /dev/null and b/wp-content/themes/gustavoo-portfolio/assets/fonts/inter-variable.woff2 differ diff --git a/wp-content/themes/gustavoo-portfolio/assets/images/favicon-32.png b/wp-content/themes/gustavoo-portfolio/assets/images/favicon-32.png new file mode 100644 index 0000000..0c5b628 Binary files /dev/null and b/wp-content/themes/gustavoo-portfolio/assets/images/favicon-32.png differ diff --git a/wp-content/themes/gustavoo-portfolio/assets/images/hero-art.png b/wp-content/themes/gustavoo-portfolio/assets/images/hero-art.png new file mode 100644 index 0000000..2dd4aae Binary files /dev/null and b/wp-content/themes/gustavoo-portfolio/assets/images/hero-art.png differ diff --git a/wp-content/themes/gustavoo-portfolio/assets/images/hero-art.webp b/wp-content/themes/gustavoo-portfolio/assets/images/hero-art.webp new file mode 100644 index 0000000..de69f53 Binary files /dev/null and b/wp-content/themes/gustavoo-portfolio/assets/images/hero-art.webp differ diff --git a/wp-content/themes/gustavoo-portfolio/assets/images/logo-mark.png b/wp-content/themes/gustavoo-portfolio/assets/images/logo-mark.png new file mode 100644 index 0000000..8b24e47 Binary files /dev/null and b/wp-content/themes/gustavoo-portfolio/assets/images/logo-mark.png differ diff --git a/wp-content/themes/gustavoo-portfolio/assets/images/logo-mark.webp b/wp-content/themes/gustavoo-portfolio/assets/images/logo-mark.webp new file mode 100644 index 0000000..8d3f538 Binary files /dev/null and b/wp-content/themes/gustavoo-portfolio/assets/images/logo-mark.webp differ diff --git a/wp-content/themes/gustavoo-portfolio/assets/images/site-icon-192.png b/wp-content/themes/gustavoo-portfolio/assets/images/site-icon-192.png new file mode 100644 index 0000000..c61a7bf Binary files /dev/null and b/wp-content/themes/gustavoo-portfolio/assets/images/site-icon-192.png differ diff --git a/wp-content/themes/gustavoo-portfolio/assets/images/site-icon-512.png b/wp-content/themes/gustavoo-portfolio/assets/images/site-icon-512.png new file mode 100644 index 0000000..8b24e47 Binary files /dev/null and b/wp-content/themes/gustavoo-portfolio/assets/images/site-icon-512.png differ diff --git a/wp-content/themes/gustavoo-portfolio/assets/js/navigation.js b/wp-content/themes/gustavoo-portfolio/assets/js/navigation.js new file mode 100644 index 0000000..da56876 --- /dev/null +++ b/wp-content/themes/gustavoo-portfolio/assets/js/navigation.js @@ -0,0 +1,273 @@ +(function () { + "use strict"; + + const ready = (callback) => { + if (document.readyState === "loading") { + document.addEventListener("DOMContentLoaded", callback, { once: true }); + return; + } + + callback(); + }; + + ready(function () { + const header = document.querySelector(".site-header"); + const toggle = document.querySelector("[data-nav-toggle], .nav-toggle, .menu-toggle"); + const navigation = document.querySelector("[data-primary-nav], .primary-nav, .primary-navigation"); + const reduceMotion = window.matchMedia("(prefers-reduced-motion: reduce)"); + let lastFocusedElement = null; + + const updateHeader = () => { + if (!header) { + return; + } + + header.classList.toggle("is-scrolled", window.scrollY > 24); + }; + + updateHeader(); + window.addEventListener("scroll", updateHeader, { passive: true }); + + if (toggle && navigation) { + const drawerClose = navigation.querySelector("[data-nav-close]"); + const navigationParent = navigation.parentNode; + const navigationNextSibling = navigation.nextSibling; + let navigationIsDrawer = false; + + const placeNavigation = (asDrawer) => { + if (asDrawer && !navigationIsDrawer) { + document.body.appendChild(navigation); + navigationIsDrawer = true; + } else if (!asDrawer && navigationIsDrawer) { + navigationParent.insertBefore(navigation, navigationNextSibling); + navigationIsDrawer = false; + } + }; + + if (!navigation.id) { + navigation.id = "site-navigation"; + } + + toggle.setAttribute("aria-controls", navigation.id); + toggle.setAttribute("aria-expanded", "false"); + navigation.setAttribute("aria-hidden", "true"); + + const focusableSelector = [ + "a[href]", + "button:not([disabled])", + "input:not([disabled])", + "select:not([disabled])", + "textarea:not([disabled])", + "[tabindex]:not([tabindex='-1'])", + ].join(","); + + const closeMenu = (restoreFocus) => { + toggle.setAttribute("aria-expanded", "false"); + const toggleLabel = toggle.querySelector(".menu-toggle__label"); + if (toggleLabel && window.gustavooPortfolioNavigation) { + toggleLabel.textContent = window.gustavooPortfolioNavigation.expand; + } + navigation.setAttribute("aria-hidden", "true"); + navigation.classList.remove("is-open"); + navigation.classList.remove("has-more-content"); + document.body.classList.remove("nav-open"); + + if (restoreFocus && lastFocusedElement) { + lastFocusedElement.focus(); + } + }; + + const openMenu = () => { + lastFocusedElement = document.activeElement; + toggle.setAttribute("aria-expanded", "true"); + const toggleLabel = toggle.querySelector(".menu-toggle__label"); + if (toggleLabel && window.gustavooPortfolioNavigation) { + toggleLabel.textContent = window.gustavooPortfolioNavigation.collapse; + } + navigation.setAttribute("aria-hidden", "false"); + navigation.classList.add("is-open"); + document.body.classList.add("nav-open"); + + window.requestAnimationFrame(function () { + updateNavigationScrollHint(); + }); + + const firstFocusable = navigation.querySelector(focusableSelector); + if (firstFocusable) { + window.requestAnimationFrame(() => firstFocusable.focus()); + } + }; + + const updateNavigationScrollHint = () => { + const hasMoreContent = navigation.scrollHeight > navigation.clientHeight + 4 + && navigation.scrollTop + navigation.clientHeight < navigation.scrollHeight - 4; + navigation.classList.toggle("has-more-content", hasMoreContent); + }; + + toggle.addEventListener("click", function () { + if (toggle.getAttribute("aria-expanded") === "true") { + closeMenu(false); + } else { + openMenu(); + } + }); + + if (drawerClose) { + drawerClose.addEventListener("click", function () { + closeMenu(true); + }); + } + + navigation.addEventListener("click", function (event) { + if (event.target.closest("a")) { + closeMenu(false); + } + }); + + navigation.addEventListener("scroll", updateNavigationScrollHint, { passive: true }); + window.addEventListener("resize", updateNavigationScrollHint, { passive: true }); + + document.addEventListener("keydown", function (event) { + if (toggle.getAttribute("aria-expanded") !== "true") { + return; + } + + if (event.key === "Escape") { + event.preventDefault(); + closeMenu(true); + return; + } + + if (event.key !== "Tab") { + return; + } + + const focusable = Array.from(navigation.querySelectorAll(focusableSelector)); + focusable.push(toggle); + + if (!focusable.length) { + return; + } + + const first = focusable[0]; + const last = focusable[focusable.length - 1]; + + if (event.shiftKey && document.activeElement === first) { + event.preventDefault(); + last.focus(); + } else if (!event.shiftKey && document.activeElement === last) { + event.preventDefault(); + first.focus(); + } + }); + + const desktopQuery = window.matchMedia("(min-width: 921px)"); + const handleDesktop = (event) => { + placeNavigation(!event.matches); + + if (event.matches) { + closeMenu(false); + navigation.removeAttribute("aria-hidden"); + } else if (toggle.getAttribute("aria-expanded") !== "true") { + navigation.setAttribute("aria-hidden", "true"); + } + }; + + handleDesktop(desktopQuery); + desktopQuery.addEventListener("change", handleDesktop); + } + + if (window.location.hash.length > 1) { + const hashId = decodeURIComponent(window.location.hash.slice(1)); + const hashTarget = document.getElementById(hashId); + + if (hashTarget) { + window.requestAnimationFrame(function () { + window.setTimeout(function () { + hashTarget.scrollIntoView({ behavior: "auto", block: "start" }); + }, 120); + }); + } + } + + document.querySelectorAll('a[href^="#"]:not([href="#"])').forEach(function (link) { + link.addEventListener("click", function (event) { + const id = link.getAttribute("href").slice(1); + const target = document.getElementById(id); + + if (!target) { + return; + } + + event.preventDefault(); + + // The header becomes fixed after the page is scrolled, so + // scrollIntoView() cannot scroll the document when targeting it. + if (id === "masthead") { + window.scrollTo({ + top: 0, + behavior: reduceMotion.matches ? "auto" : "smooth", + }); + } else { + target.scrollIntoView({ + behavior: reduceMotion.matches ? "auto" : "smooth", + block: "start", + }); + } + + if (window.history && window.history.replaceState) { + window.history.replaceState(null, "", `#${id}`); + } + }); + }); + + const revealItems = Array.from(document.querySelectorAll("[data-reveal]")); + + if (revealItems.length && "IntersectionObserver" in window && !reduceMotion.matches) { + document.documentElement.classList.add("has-reveal"); + + const revealObserver = new IntersectionObserver( + function (entries, observer) { + entries.forEach(function (entry) { + if (!entry.isIntersecting) { + return; + } + + const delay = Number.parseInt(entry.target.dataset.revealDelay || "0", 10); + if (delay > 0) { + entry.target.style.transitionDelay = `${Math.min(delay, 500)}ms`; + } + + entry.target.classList.add("is-visible"); + observer.unobserve(entry.target); + }); + }, + { rootMargin: "0px 0px -8%", threshold: 0.12 } + ); + + revealItems.forEach((item) => revealObserver.observe(item)); + } else { + revealItems.forEach((item) => item.classList.add("is-visible")); + } + + const progress = document.querySelector("[data-reading-progress]"); + const article = document.querySelector(".entry-content"); + + if (progress && article) { + const updateProgress = () => { + const articleTop = article.getBoundingClientRect().top + window.scrollY; + const articleHeight = article.offsetHeight; + const viewportHeight = window.innerHeight; + const distance = articleHeight - viewportHeight; + const current = window.scrollY - articleTop; + const percentage = distance > 0 ? Math.min(100, Math.max(0, (current / distance) * 100)) : 100; + progress.style.setProperty("--reading-progress", `${percentage}%`); + progress.setAttribute("aria-valuenow", String(Math.round(percentage))); + }; + + updateProgress(); + window.addEventListener("scroll", updateProgress, { passive: true }); + window.addEventListener("resize", updateProgress, { passive: true }); + } + }); +})(); diff --git a/wp-content/themes/gustavoo-portfolio/category.php b/wp-content/themes/gustavoo-portfolio/category.php new file mode 100644 index 0000000..f78354e --- /dev/null +++ b/wp-content/themes/gustavoo-portfolio/category.php @@ -0,0 +1,34 @@ + +
      +
      +
      +

      +
      +
      +
      +
      + +
      + 0 === $gustavoo_news_index ) ); + $gustavoo_news_index++; + } + ?> +
      + $gustavoo_displayed_post_ids, 'category_id' => get_queried_object_id() ) ); ?> + + + + +
      +
      + diff --git a/wp-content/themes/gustavoo-portfolio/comments.php b/wp-content/themes/gustavoo-portfolio/comments.php new file mode 100644 index 0000000..b164b39 --- /dev/null +++ b/wp-content/themes/gustavoo-portfolio/comments.php @@ -0,0 +1,65 @@ + + +
      + +

      + +

      + +
        + 64, + 'short_ping' => true, + 'style' => 'ol', + ) + ); + ?> +
      + + sprintf( ' %s', esc_html__( 'Comentários anteriores', 'gustavoo-portfolio' ) ), + 'next_text' => sprintf( '%s ', esc_html__( 'Próximos comentários', 'gustavoo-portfolio' ) ), + ) + ); + ?> + + + +

      + + + 'submit button button--primary', + 'label_submit' => __( 'Publicar comentário', 'gustavoo-portfolio' ), + 'title_reply' => __( 'Deixe um comentário', 'gustavoo-portfolio' ), + 'title_reply_before' => '

      ', + 'title_reply_after' => '

      ', + 'comment_notes_before' => '

      ' . esc_html__( 'Seu endereço de e-mail não será publicado. Campos obrigatórios são indicados.', 'gustavoo-portfolio' ) . '

      ', + ) + ); + ?> +
      diff --git a/wp-content/themes/gustavoo-portfolio/footer.php b/wp-content/themes/gustavoo-portfolio/footer.php new file mode 100644 index 0000000..f43773f --- /dev/null +++ b/wp-content/themes/gustavoo-portfolio/footer.php @@ -0,0 +1,66 @@ + + +
      + + + +
      + + + + + + diff --git a/wp-content/themes/gustavoo-portfolio/front-page.php b/wp-content/themes/gustavoo-portfolio/front-page.php new file mode 100644 index 0000000..5fe608e --- /dev/null +++ b/wp-content/themes/gustavoo-portfolio/front-page.php @@ -0,0 +1,41 @@ + + +
      + + + + + + + +
      +
      + +
      +
      + + + + + +
      + + +> + + + + + +> + + + + +
      +
      +
      + + + + + + +
      + + + + + +
      +
      + + + +
      +

      + +
      + +
      + 0 === $gustavoo_news_index ) ); + $gustavoo_news_index++; + } + ?> +
      + $gustavoo_displayed_post_ids ) ); ?> + + + + +
      +
      + + Customize. */ +function gustavoo_portfolio_customize_register( $wp_customize ) { + $wp_customize->add_panel( + 'gustavoo_portfolio_content', + array( + 'title' => __( 'Conteúdo do tema', 'gustavoo-portfolio' ), + 'description' => __( 'Edite os conteúdos exibidos no site e use os lápis na prévia para ir direto à seção desejada.', 'gustavoo-portfolio' ), + 'priority' => 30, + ) + ); + + $sections = array( + 'hero' => __( 'Hero', 'gustavoo-portfolio' ), + 'about' => __( 'Sobre', 'gustavoo-portfolio' ), + 'services' => __( 'Serviços', 'gustavoo-portfolio' ), + 'projects' => __( 'Projetos', 'gustavoo-portfolio' ), + 'blog' => __( 'Blog', 'gustavoo-portfolio' ), + 'contact' => __( 'Contato', 'gustavoo-portfolio' ), + 'newsletter' => __( 'Newsletter', 'gustavoo-portfolio' ), + 'social' => __( 'Redes sociais', 'gustavoo-portfolio' ), + ); + + foreach ( $sections as $id => $title ) { + $wp_customize->add_section( + 'gustavoo_portfolio_' . $id, + array( + 'title' => $title, + 'panel' => 'gustavoo_portfolio_content', + 'priority' => 10, + ) + ); + } + + $fields = array( + 'hero_eyebrow' => array( 'hero', __( 'Sobretítulo', 'gustavoo-portfolio' ), 'text' ), + 'hero_title' => array( 'hero', __( 'Título', 'gustavoo-portfolio' ), 'text' ), + 'hero_description' => array( 'hero', __( 'Descrição', 'gustavoo-portfolio' ), 'textarea' ), + 'hero_primary_label' => array( 'hero', __( 'Botão principal — texto', 'gustavoo-portfolio' ), 'text' ), + 'hero_primary_url' => array( 'hero', __( 'Botão principal — link', 'gustavoo-portfolio' ), 'link' ), + 'hero_secondary_label' => array( 'hero', __( 'Botão secundário — texto', 'gustavoo-portfolio' ), 'text' ), + 'hero_secondary_url' => array( 'hero', __( 'Botão secundário — link', 'gustavoo-portfolio' ), 'link' ), + 'about_eyebrow' => array( 'about', __( 'Sobretítulo', 'gustavoo-portfolio' ), 'text' ), + 'about_title' => array( 'about', __( 'Título', 'gustavoo-portfolio' ), 'text' ), + 'about_text' => array( 'about', __( 'Texto principal', 'gustavoo-portfolio' ), 'textarea' ), + 'about_secondary_text' => array( 'about', __( 'Texto complementar', 'gustavoo-portfolio' ), 'textarea' ), + 'services_eyebrow' => array( 'services', __( 'Sobretítulo', 'gustavoo-portfolio' ), 'text' ), + 'services_title' => array( 'services', __( 'Título', 'gustavoo-portfolio' ), 'text' ), + 'services_description' => array( 'services', __( 'Descrição', 'gustavoo-portfolio' ), 'textarea' ), + 'projects_eyebrow' => array( 'projects', __( 'Sobretítulo', 'gustavoo-portfolio' ), 'text' ), + 'projects_title' => array( 'projects', __( 'Título', 'gustavoo-portfolio' ), 'text' ), + 'projects_text' => array( 'projects', __( 'Descrição', 'gustavoo-portfolio' ), 'textarea' ), + 'projects_limit' => array( 'projects', __( 'Quantidade de projetos', 'gustavoo-portfolio' ), 'number' ), + 'blog_eyebrow' => array( 'blog', __( 'Sobretítulo', 'gustavoo-portfolio' ), 'text' ), + 'blog_title' => array( 'blog', __( 'Título', 'gustavoo-portfolio' ), 'text' ), + 'blog_text' => array( 'blog', __( 'Descrição', 'gustavoo-portfolio' ), 'textarea' ), + 'blog_limit' => array( 'blog', __( 'Quantidade de posts', 'gustavoo-portfolio' ), 'number' ), + 'contact_eyebrow' => array( 'contact', __( 'Sobretítulo', 'gustavoo-portfolio' ), 'text' ), + 'contact_heading' => array( 'contact', __( 'Título', 'gustavoo-portfolio' ), 'text' ), + 'contact_text' => array( 'contact', __( 'Descrição', 'gustavoo-portfolio' ), 'textarea' ), + 'email' => array( 'contact', __( 'E-mail', 'gustavoo-portfolio' ), 'email' ), + 'whatsapp' => array( 'contact', __( 'WhatsApp — número', 'gustavoo-portfolio' ), 'text' ), + 'whatsapp_message' => array( 'contact', __( 'WhatsApp — mensagem inicial', 'gustavoo-portfolio' ), 'textarea' ), + 'newsletter_eyebrow' => array( 'newsletter', __( 'Sobretítulo', 'gustavoo-portfolio' ), 'text' ), + 'newsletter_heading' => array( 'newsletter', __( 'Título', 'gustavoo-portfolio' ), 'text' ), + 'newsletter_text' => array( 'newsletter', __( 'Descrição', 'gustavoo-portfolio' ), 'textarea' ), + 'github_url' => array( 'social', __( 'GitHub', 'gustavoo-portfolio' ), 'url' ), + 'linkedin_url' => array( 'social', __( 'LinkedIn', 'gustavoo-portfolio' ), 'url' ), + 'instagram_url' => array( 'social', __( 'Instagram', 'gustavoo-portfolio' ), 'url' ), + 'availability_label' => array( 'social', __( 'Status de disponibilidade', 'gustavoo-portfolio' ), 'text' ), + ); + + foreach ( $fields as $key => $field ) { + $setting_id = 'gustavoo_portfolio_' . $key; + $sanitize = 'textarea' === $field[2] ? 'sanitize_textarea_field' : ( 'number' === $field[2] ? 'absint' : ( in_array( $field[2], array( 'url', 'link' ), true ) ? 'gustavoo_portfolio_customize_link' : ( 'email' === $field[2] ? 'sanitize_email' : 'sanitize_text_field' ) ) ); + + $wp_customize->add_setting( + $setting_id, + array( + 'default' => gustavoo_portfolio_get_setting( $key ), + 'sanitize_callback' => $sanitize, + // Each front-page section is refreshed by its native Customizer + // partial, allowing WordPress to expose its own edit shortcut. + 'transport' => 'postMessage', + ) + ); + + $control_args = array( + 'label' => $field[1], + 'section' => 'gustavoo_portfolio_' . $field[0], + 'type' => in_array( $field[2], array( 'textarea', 'number', 'email', 'url' ), true ) ? $field[2] : 'text', + ); + + if ( 'number' === $field[2] ) { + $control_args['input_attrs'] = array( 'min' => 1, 'max' => 24 ); + } + + $wp_customize->add_control( $setting_id, $control_args ); + } + + if ( ! isset( $wp_customize->selective_refresh ) ) { + return; + } + + $partials = array( + 'hero' => array( '.hero-stage', 'template-parts/front/hero', array( 'hero_eyebrow', 'hero_title', 'hero_description', 'hero_primary_label', 'hero_primary_url', 'hero_secondary_label', 'hero_secondary_url' ) ), + 'about' => array( '#sobre', 'template-parts/front/about', array( 'about_eyebrow', 'about_title', 'about_text', 'about_secondary_text' ) ), + 'services' => array( '#servicos', 'template-parts/front/services', array( 'services_eyebrow', 'services_title', 'services_description' ) ), + 'projects' => array( '#projetos', 'template-parts/front/projects', array( 'projects_eyebrow', 'projects_title', 'projects_text', 'projects_limit' ) ), + 'blog' => array( '#blog', 'template-parts/front/posts', array( 'blog_eyebrow', 'blog_title', 'blog_text', 'blog_limit' ) ), + 'contact' => array( '#contato', 'template-parts/front/contact', array( 'contact_eyebrow', 'contact_heading', 'contact_text', 'email', 'whatsapp', 'whatsapp_message', 'github_url', 'linkedin_url', 'instagram_url' ) ), + ); + + foreach ( $partials as $id => $partial ) { + $settings = array_map( + static function ( $key ) { + return 'gustavoo_portfolio_' . $key; + }, + $partial[2] + ); + + /* + * A native edit shortcut is tied to the partial's primary setting. + * Using the first actual setting as the partial ID makes the pencil + * focus a real control instead of an unresolvable synthetic partial. + */ + $wp_customize->selective_refresh->add_partial( + $settings[0], + array( + 'selector' => $partial[0], + 'settings' => $settings, + 'primary_setting' => $settings[0], + 'container_inclusive' => true, + 'render_callback' => static function () use ( $partial ) { + return gustavoo_portfolio_customize_render_part( $partial[1] ); + }, + ) + ); + } + + $wp_customize->selective_refresh->add_partial( + 'gustavoo_portfolio_whatsapp_shortcut', + array( + 'selector' => '.whatsapp-float-wrap', + 'settings' => array( 'gustavoo_portfolio_whatsapp', 'gustavoo_portfolio_whatsapp_message' ), + 'primary_setting' => 'gustavoo_portfolio_whatsapp', + 'container_inclusive' => true, + 'render_callback' => static function () { + return gustavoo_portfolio_customize_render_part( 'template-parts/global/whatsapp-float' ); + }, + ) + ); +} +add_action( 'customize_register', 'gustavoo_portfolio_customize_register' ); + +/** Register native Customizer shortcuts for the theme's menu locations. */ +function gustavoo_portfolio_customize_menu_shortcuts( $wp_customize ) { + if ( ! isset( $wp_customize->selective_refresh ) ) { + return; + } + + $menus = array( + 'primary' => '#site-navigation', + 'news_categories' => '.news-category-nav', + ); + + foreach ( $menus as $location => $selector ) { + $setting = 'nav_menu_locations[' . $location . ']'; + + if ( ! $wp_customize->get_setting( $setting ) ) { + continue; + } + + $wp_customize->selective_refresh->add_partial( + 'gustavoo_portfolio_menu_shortcut_' . $location, + array( + 'selector' => $selector, + 'settings' => array( $setting ), + 'primary_setting' => $setting, + ) + ); + } +} +add_action( 'customize_register', 'gustavoo_portfolio_customize_menu_shortcuts', 20 ); diff --git a/wp-content/themes/gustavoo-portfolio/inc/defaults.php b/wp-content/themes/gustavoo-portfolio/inc/defaults.php new file mode 100644 index 0000000..a6b7035 --- /dev/null +++ b/wp-content/themes/gustavoo-portfolio/inc/defaults.php @@ -0,0 +1,173 @@ + + */ +function gustavoo_portfolio_get_defaults() { + $defaults = array( + 'hero_eyebrow' => __( 'Desenvolvimento de ponta a ponta', 'gustavoo-portfolio' ), + 'hero_title' => __( 'Transformo ideias em produtos digitais sólidos.', 'gustavoo-portfolio' ), + 'hero_description' => __( 'Gustavo, Desenvolvedor Full Stack Sênior. Web, mobile, infraestrutura e automação para tirar projetos do papel e fazê-los crescer.', 'gustavoo-portfolio' ), + 'hero_primary_label' => __( 'Ver projetos', 'gustavoo-portfolio' ), + 'hero_primary_url' => '#projetos', + 'hero_secondary_label' => __( 'Fale comigo', 'gustavoo-portfolio' ), + 'hero_secondary_url' => '#contato', + 'about_eyebrow' => __( 'Sobre mim', 'gustavoo-portfolio' ), + 'about_title' => __( 'Responsabilidade técnica do planejamento à produção.', 'gustavoo-portfolio' ), + 'about_text' => __( 'Mais do que escrever código, meu objetivo é garantir que seu projeto saia do papel e funcione exatamente como planejado. Assumo a responsabilidade técnica de ponta a ponta para que você foque no que importa: crescer.', 'gustavoo-portfolio' ), + 'about_secondary_text' => __( 'Já participei do desenvolvimento e lançamento de múltiplos projetos digitais, atuando da arquitetura técnica à divulgação e ao crescimento das plataformas. Essa combinação de visão técnica e entendimento de mercado orienta cada entrega.', 'gustavoo-portfolio' ), + 'services_eyebrow' => __( 'Como posso ajudar', 'gustavoo-portfolio' ), + 'services_title' => __( 'Soluções completas para produtos digitais.', 'gustavoo-portfolio' ), + 'services_description' => __( 'Estratégia, código e infraestrutura trabalhando juntos para criar experiências rápidas, seguras e preparadas para evoluir.', 'gustavoo-portfolio' ), + 'projects_eyebrow' => __( 'Portfólio', 'gustavoo-portfolio' ), + 'projects_title' => __( 'Projetos selecionados', 'gustavoo-portfolio' ), + 'projects_text' => __( 'Uma seleção de produtos, experiências e soluções que ajudei a colocar no mundo.', 'gustavoo-portfolio' ), + 'projects_limit' => 6, + 'blog_eyebrow' => __( 'Blog', 'gustavoo-portfolio' ), + 'blog_title' => __( 'Código, produto e bastidores', 'gustavoo-portfolio' ), + 'blog_text' => __( 'Análises práticas sobre desenvolvimento, infraestrutura, automação e crescimento de produtos digitais.', 'gustavoo-portfolio' ), + 'blog_limit' => 4, + 'contact_eyebrow' => __( 'Vamos conversar', 'gustavoo-portfolio' ), + 'contact_heading' => __( 'Tem um projeto em mente?', 'gustavoo-portfolio' ), + 'contact_text' => __( 'Conte o que você precisa construir ou melhorar. Responderei com os próximos passos para transformar a ideia em uma entrega concreta.', 'gustavoo-portfolio' ), + 'contact_form_id' => 0, + 'newsletter_eyebrow' => __( 'Newsletter', 'gustavoo-portfolio' ), + 'newsletter_heading' => __( 'Ideias úteis, direto na sua caixa de entrada.', 'gustavoo-portfolio' ), + 'newsletter_text' => __( 'Receba conteúdos sobre desenvolvimento, produto, infraestrutura e automação. Sem ruído e sem spam.', 'gustavoo-portfolio' ), + 'newsletter_form_id' => 0, + 'email' => (string) get_option( 'admin_email', '' ), + 'whatsapp' => '', + 'whatsapp_message' => __( 'Olá! Gostaria de falar sobre um projeto.', 'gustavoo-portfolio' ), + 'github_url' => '', + 'linkedin_url' => '', + 'instagram_url' => '', + 'projects_archive_url' => '', + 'availability_label' => __( 'Disponível para novos projetos', 'gustavoo-portfolio' ), + ); + + /** + * Filter theme defaults before saved settings are merged. + * + * @param array $defaults Default settings. + */ + return apply_filters( 'gustavoo_portfolio_defaults', $defaults ); +} + +/** + * Return the saved theme settings merged with safe local defaults. + * + * @return array + */ +function gustavoo_portfolio_get_settings() { + $settings = get_option( 'gustavoo_portfolio_settings', array() ); + + if ( ! is_array( $settings ) ) { + $settings = array(); + } + + return wp_parse_args( $settings, gustavoo_portfolio_get_defaults() ); +} + +/** + * Read one theme setting. A few aliases preserve compatibility with early builds. + * + * @param string $key Setting key. + * @param mixed $fallback Optional fallback. + * @return mixed + */ +function gustavoo_portfolio_get_setting( $key, $fallback = null ) { + $customizer_key = 'gustavoo_portfolio_' . $key; + $customizer_values = get_theme_mods(); + + if ( is_array( $customizer_values ) && array_key_exists( $customizer_key, $customizer_values ) ) { + return $customizer_values[ $customizer_key ]; + } + + $settings = gustavoo_portfolio_get_settings(); + $aliases = array( + 'hero_eyebrow' => array( 'hero_kicker' ), + 'hero_description' => array( 'hero_text' ), + 'contact_form_id' => array( 'fluent_contact_form_id', 'lead_form_id' ), + 'newsletter_form_id' => array( 'fluent_newsletter_form_id', 'fluentcrm_form_id' ), + 'contact_heading' => array( 'contact_title' ), + 'newsletter_heading' => array( 'newsletter_title' ), + ); + + if ( array_key_exists( $key, $settings ) && '' !== $settings[ $key ] && null !== $settings[ $key ] ) { + return $settings[ $key ]; + } + + if ( isset( $aliases[ $key ] ) ) { + foreach ( $aliases[ $key ] as $alias ) { + if ( array_key_exists( $alias, $settings ) && '' !== $settings[ $alias ] && null !== $settings[ $alias ] ) { + return $settings[ $alias ]; + } + } + } + + if ( null !== $fallback ) { + return $fallback; + } + + $defaults = gustavoo_portfolio_get_defaults(); + + return $defaults[ $key ] ?? ''; +} + +/** + * Return the service cards used by the front page. + * + * @return array> + */ +function gustavoo_portfolio_get_services() { + $services = array( + array( + 'number' => '01', + 'title' => __( 'Web, Mobile e E-commerce', 'gustavoo-portfolio' ), + 'description' => __( 'Sites institucionais, aplicações para iOS e Android, softwares desktop, lojas e marketplaces personalizados com foco em experiência e conversão.', 'gustavoo-portfolio' ), + ), + array( + 'number' => '02', + 'title' => __( 'WordPress avançado', 'gustavoo-portfolio' ), + 'description' => __( 'Temas e plugins sob medida, integrações específicas, recuperação de sites e otimização para Core Web Vitals.', 'gustavoo-portfolio' ), + ), + array( + 'number' => '03', + 'title' => __( 'Infraestrutura e servidores', 'gustavoo-portfolio' ), + 'description' => __( 'Ambientes Linux, VPS e servidores dedicados configurados para manter aplicações estáveis, rápidas, seguras e escaláveis.', 'gustavoo-portfolio' ), + ), + array( + 'number' => '04', + 'title' => __( 'Automação e integrações', 'gustavoo-portfolio' ), + 'description' => __( 'APIs e fluxos com n8n para conectar sistemas, organizar dados e eliminar tarefas operacionais repetitivas.', 'gustavoo-portfolio' ), + ), + array( + 'number' => '05', + 'title' => __( 'Games e experiências interativas', 'gustavoo-portfolio' ), + 'description' => __( 'Jogos 2D e 3D para celulares, computadores e navegadores, incluindo ranking, multiplayer e compras dentro do jogo.', 'gustavoo-portfolio' ), + ), + array( + 'number' => '06', + 'title' => __( 'Divulgação e crescimento', 'gustavoo-portfolio' ), + 'description' => __( 'Estratégias de conteúdo, comunidades, parcerias e mídia paga para aproximar produtos digitais do público certo.', 'gustavoo-portfolio' ), + ), + ); + + /** + * Filter the service cards shown on the front page. + * + * @param array> $services Service cards. + */ + return apply_filters( 'gustavoo_portfolio_services', $services ); +} diff --git a/wp-content/themes/gustavoo-portfolio/inc/enqueue.php b/wp-content/themes/gustavoo-portfolio/inc/enqueue.php new file mode 100644 index 0000000..1efa447 --- /dev/null +++ b/wp-content/themes/gustavoo-portfolio/inc/enqueue.php @@ -0,0 +1,75 @@ + __( 'Abrir menu', 'gustavoo-portfolio' ), + 'collapse' => __( 'Fechar menu', 'gustavoo-portfolio' ), + ) + ); + } + + if ( is_singular() && comments_open() && get_option( 'thread_comments' ) ) { + wp_enqueue_script( 'comment-reply' ); + } +} +add_action( 'wp_enqueue_scripts', 'gustavoo_portfolio_enqueue_assets' ); diff --git a/wp-content/themes/gustavoo-portfolio/inc/forms.php b/wp-content/themes/gustavoo-portfolio/inc/forms.php new file mode 100644 index 0000000..4c45a35 --- /dev/null +++ b/wp-content/themes/gustavoo-portfolio/inc/forms.php @@ -0,0 +1,247 @@ +form_fields, true ); + + if ( ! is_array( $structure ) || ! isset( $structure['fields'] ) || ! is_array( $structure['fields'] ) ) { + return; + } + + $defaults = fluentformLoadFile( 'Services/FormBuilder/DefaultElements.php' ); + $message = $defaults['general']['textarea'] ?? array(); + + if ( ! $message ) { + return; + } + + $fields_by_name = array(); + $changed = false; + + foreach ( $structure['fields'] as $field ) { + $name = (string) ( $field['attributes']['name'] ?? '' ); + + if ( ! in_array( $name, array( 'email', 'whatsapp', 'message' ), true ) ) { + $changed = true; + continue; + } + + if ( isset( $fields_by_name[ $name ] ) ) { + $changed = true; + continue; + } + + $fields_by_name[ $name ] = $field; + } + + if ( isset( $fields_by_name['email'] ) ) { + $fields_by_name['email']['settings']['validation_rules']['required']['value'] = true; + } + + if ( isset( $fields_by_name['whatsapp'] ) ) { + $whatsapp = $fields_by_name['whatsapp']; + $whatsapp['settings']['validation_rules']['required']['value'] = true; + $whatsapp['settings']['temp_mask'] = 'custom'; + $whatsapp['settings']['data-mask-reverse'] = 'no'; + $whatsapp['attributes']['data-mask'] = '(00) 00000-0000'; + $fields_by_name['whatsapp'] = $whatsapp; + } + + if ( ! isset( $fields_by_name['whatsapp'] ) ) { + $whatsapp = $defaults['general']['input_mask'] ?? array(); + if ( $whatsapp ) { + $whatsapp['uniqElKey'] = 'el_gso_contact_whatsapp'; + $whatsapp['attributes']['name'] = 'whatsapp'; + $whatsapp['attributes']['placeholder'] = '(31) 99999-9999'; + $whatsapp['attributes']['data-mask'] = '(00) 00000-0000'; + $whatsapp['settings']['label'] = __( 'WhatsApp', 'gustavoo-portfolio' ); + $whatsapp['settings']['admin_field_label'] = __( 'WhatsApp', 'gustavoo-portfolio' ); + $whatsapp['settings']['mobile_keyboard_type'] = 'tel'; + $whatsapp['settings']['temp_mask'] = 'custom'; + $whatsapp['settings']['data-mask-reverse'] = 'no'; + $whatsapp['settings']['validation_rules']['required']['value'] = true; + $fields_by_name['whatsapp'] = $whatsapp; + } + } + + if ( ! isset( $fields_by_name['message'] ) || 'textarea' !== (string) ( $fields_by_name['message']['element'] ?? '' ) ) { + $message['uniqElKey'] = 'el_gso_contact_message'; + $message['attributes']['name'] = 'message'; + $message['attributes']['placeholder'] = __( 'Descreva o motivo do contato e o que você quer construir.', 'gustavoo-portfolio' ); + $message['attributes']['rows'] = 5; + $message['settings']['label'] = __( 'Como posso ajudar?', 'gustavoo-portfolio' ); + $message['settings']['admin_field_label'] = __( 'Motivo do contato', 'gustavoo-portfolio' ); + $message['settings']['validation_rules']['required']['value'] = true; + $fields_by_name['message'] = $message; + } + + $fields = array(); + foreach ( array( 'email', 'whatsapp', 'message' ) as $required_name ) { + if ( isset( $fields_by_name[ $required_name ] ) ) { + $fields[] = $fields_by_name[ $required_name ]; + } + } + $changed = wp_json_encode( $structure['fields'] ) !== wp_json_encode( $fields ); + + if ( ! $changed ) { + return; + } + + $structure['fields'] = $fields; + $form_fields = wp_json_encode( $structure, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES ); + + if ( class_exists( '\FluentForm\App\Services\Form\FormService' ) ) { + ( new \FluentForm\App\Services\Form\FormService() )->update( + array( + 'form_id' => $form->id, + 'title' => $form->title, + 'status' => $form->status, + 'formFields' => $form_fields, + ) + ); + } else { + $form->form_fields = $form_fields; + $form->save(); + } +} +add_action( 'init', 'gustavoo_portfolio_ensure_contact_form_fields', 30 ); + +/** + * Preserve contact fields in submissions while an older form schema is being + * migrated. Fluent Forms only whitelists fields known by its saved schema. + * + * @param array $form_data Sanitized submission data. + * @param int $form_id Submitted form ID. + * @param array $input_configs Fluent Forms input configuration. + * @return array + */ +function gustavoo_portfolio_preserve_contact_submission_fields( $form_data, $form_id, $input_configs ) { + $contact_form_id = absint( gustavoo_portfolio_get_setting( 'contact_form_id' ) ); + + if ( ! $contact_form_id || $contact_form_id !== absint( $form_id ) ) { + return $form_data; + } + + if ( isset( $_POST['whatsapp'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing -- Fluent Forms validates the submission. + $form_data['whatsapp'] = sanitize_text_field( wp_unslash( $_POST['whatsapp'] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Missing + } + + if ( isset( $_POST['message'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing -- Fluent Forms validates the submission. + $form_data['message'] = sanitize_textarea_field( wp_unslash( $_POST['message'] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Missing + } + + return $form_data; +} +add_filter( 'fluentform/insert_response_data', 'gustavoo_portfolio_preserve_contact_submission_fields', 10, 3 ); + +/** + * Render a Fluent Forms form from a numeric, administrator-controlled ID. + * + * The theme deliberately never stores or executes arbitrary shortcodes. Contact + * synchronization is configured through the FluentCRM feed inside Fluent Forms. + * + * @param int $form_id Form ID. + * @param string $context Form context, used for classes and filters. + * @return string + */ +function gustavoo_portfolio_render_fluent_form( $form_id, $context = 'default' ) { + $form_id = absint( $form_id ); + $context = sanitize_html_class( $context ); + + if ( $form_id && shortcode_exists( 'fluentform' ) ) { + if ( 'contact' === $context ) { + gustavoo_portfolio_ensure_contact_form_fields(); + } + + $shortcode = sprintf( '[fluentform id="%d"]', $form_id ); + $markup = do_shortcode( $shortcode ); + + // Older persisted forms may still contain only the e-mail field. Keep the + // requested fields visible inside the real Fluent Forms
      . + if ( 'contact' === $context && false !== stripos( $markup, '
      ' ) ) { + $missing_fields = ''; + + if ( false === stripos( $markup, 'name="whatsapp"' ) ) { + $missing_fields .= '
      '; + } + + if ( false === stripos( $markup, 'name="message"' ) ) { + $missing_fields .= '
      '; + } + + if ( $missing_fields ) { + $markup = preg_replace( '#'; + $fallback .= '

      ' . esc_html__( 'O formulário está sendo configurado. Você ainda pode entrar em contato diretamente.', 'gustavoo-portfolio' ) . '

      '; + $fallback .= '' . esc_html( $button_label ) . ''; + + if ( current_user_can( 'manage_options' ) ) { + $fallback .= '

      '; + $fallback .= esc_html__( 'Configure o ID do Fluent Forms nas opções do portfólio.', 'gustavoo-portfolio' ); + $fallback .= '

      '; + } + + $fallback .= ''; + + return $fallback; +} + +/** + * Print a Fluent Forms form. Output originates either from the trusted plugin + * shortcode or from fully escaped fallback markup built above. + * + * @param int $form_id Form ID. + * @param string $context Form context. + * @return void + */ +function gustavoo_portfolio_the_fluent_form( $form_id, $context = 'default' ) { + echo gustavoo_portfolio_render_fluent_form( $form_id, $context ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Trusted plugin output or escaped local fallback. +} diff --git a/wp-content/themes/gustavoo-portfolio/inc/setup.php b/wp-content/themes/gustavoo-portfolio/inc/setup.php new file mode 100644 index 0000000..9df4a5a --- /dev/null +++ b/wp-content/themes/gustavoo-portfolio/inc/setup.php @@ -0,0 +1,146 @@ + 160, + 'width' => 160, + 'flex-height' => true, + 'flex-width' => true, + ) + ); + + register_nav_menus( + array( + 'primary' => __( 'Menu principal', 'gustavoo-portfolio' ), + 'news_categories' => __( 'Menu de categorias de notícias', 'gustavoo-portfolio' ), + 'footer' => __( 'Menu do rodapé', 'gustavoo-portfolio' ), + ) + ); + + add_image_size( 'gustavoo-project-card', 720, 480, true ); + add_image_size( 'gustavoo-post-card', 720, 460, true ); +} +add_action( 'after_setup_theme', 'gustavoo_portfolio_setup' ); + +/** + * Define a comfortable content width for embeds and media. + * + * @return void + */ +function gustavoo_portfolio_content_width() { + $GLOBALS['content_width'] = apply_filters( 'gustavoo_portfolio_content_width', 780 ); +} +add_action( 'after_setup_theme', 'gustavoo_portfolio_content_width', 0 ); + +/** + * Add useful state classes without coupling templates to presentation logic. + * + * @param string[] $classes Existing body classes. + * @return string[] + */ +function gustavoo_portfolio_body_classes( $classes ) { + if ( is_front_page() ) { + $classes[] = 'has-overlay-header'; + } + + if ( ! is_front_page() && is_active_sidebar( 'sidebar-blog' ) ) { + $classes[] = 'has-blog-sidebar'; + } + + if ( ! is_singular() ) { + $classes[] = 'is-list-view'; + } + + return array_unique( $classes ); +} +add_filter( 'body_class', 'gustavoo_portfolio_body_classes' ); + +/** Set the posts page browser title to the editorial section name. */ +function gustavoo_portfolio_blog_document_title( $title ) { + if ( is_home() ) { + $title['title'] = __( 'Últimas notícias', 'gustavoo-portfolio' ); + } + + return $title; +} +add_filter( 'document_title_parts', 'gustavoo_portfolio_blog_document_title' ); + +/** Keep the editorial top grid focused on the five newest posts. */ +function gustavoo_portfolio_news_posts_per_page( $query ) { + if ( is_admin() || ! $query->is_main_query() || ! ( $query->is_home() || $query->is_category() ) ) { + return; + } + + $query->set( 'posts_per_page', 5 ); +} +add_action( 'pre_get_posts', 'gustavoo_portfolio_news_posts_per_page' ); + +/** Refresh category rewrite rules once after the editorial templates are installed. */ +function gustavoo_portfolio_refresh_editorial_rewrites() { + $version = '1.0.0'; + + if ( $version === get_option( 'gustavoo_portfolio_editorial_rewrite_version' ) ) { + return; + } + + flush_rewrite_rules( false ); + update_option( 'gustavoo_portfolio_editorial_rewrite_version', $version, false ); +} +add_action( 'init', 'gustavoo_portfolio_refresh_editorial_rewrites', 99 ); + +/** + * Print a fallback icon only when WordPress has no configured Site Icon. + * + * @return void + */ +function gustavoo_portfolio_fallback_site_icon() { + if ( has_site_icon() || ! function_exists( 'gustavoo_portfolio_get_image_source' ) ) { + return; + } + + $icon_url = gustavoo_portfolio_get_image_source( 'logo-mark', 'png' ); + + if ( $icon_url ) { + printf( '' . "\n", esc_url( $icon_url ) ); + } +} +add_action( 'wp_head', 'gustavoo_portfolio_fallback_site_icon', 2 ); diff --git a/wp-content/themes/gustavoo-portfolio/inc/template-tags.php b/wp-content/themes/gustavoo-portfolio/inc/template-tags.php new file mode 100644 index 0000000..745a413 --- /dev/null +++ b/wp-content/themes/gustavoo-portfolio/inc/template-tags.php @@ -0,0 +1,569 @@ + $has_webp ? get_theme_file_uri( $webp_path ) : '', + 'png' => $has_png ? get_theme_file_uri( $png_path ) : '', + 'fallback' => $has_png ? get_theme_file_uri( $png_path ) : get_theme_file_uri( $webp_path ), + ); +} + +/** + * Print the configured logo or the bundled brand mark. + * + * @return void + */ +function gustavoo_portfolio_the_brand() { + $site_name = get_bloginfo( 'name' ); + + if ( has_custom_logo() ) { + echo get_custom_logo(); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Core-generated custom logo markup. + return; + } + + $sources = gustavoo_portfolio_get_picture_sources( 'logo-mark' ); + ?> + + + + + + + + + + + true, + 'exclude' => array( absint( get_option( 'default_category' ) ) ), + 'orderby' => 'name', + 'order' => 'ASC', + ) + ); + + if ( empty( $categories ) ) { + return; + } + + $current_category_id = is_category() ? (int) get_queried_object_id() : 0; + ?> + + +
      +

      + 'news_categories', + 'menu_class' => 'mobile-news-categories__list', + 'container' => false, + 'fallback_cb' => false, + 'depth' => 1, + ) + ); + ?> +
      + true, + 'exclude' => array( absint( get_option( 'default_category' ) ) ), + 'orderby' => 'name', + 'order' => 'ASC', + ) + ); + + if ( empty( $categories ) ) { + return; + } + + $current_category_id = is_category() ? (int) get_queried_object_id() : 0; + ?> +
      +

      + +
      + +
        + + + + + +
      + + */ +function gustavoo_portfolio_get_social_links() { + $profiles = array( + 'linkedin' => array( + 'label' => __( 'LinkedIn', 'gustavoo-portfolio' ), + 'url' => (string) gustavoo_portfolio_get_setting( 'linkedin_url' ), + ), + 'instagram' => array( + 'label' => __( 'Instagram', 'gustavoo-portfolio' ), + 'url' => (string) gustavoo_portfolio_get_setting( 'instagram_url' ), + ), + ); + + return array_filter( + $profiles, + static function ( $profile ) { + return ! empty( $profile['url'] ); + } + ); +} + +/** + * Print social links with explicit new-window text for assistive technology. + * + * @param string $class Optional list class. + * @return void + */ +function gustavoo_portfolio_social_links( $class = 'social-links' ) { + $profiles = gustavoo_portfolio_get_social_links(); + + if ( empty( $profiles ) ) { + return; + } + + printf( '
        ', esc_attr( $class ) ); + + foreach ( $profiles as $slug => $profile ) { + printf( + '', + esc_attr( $slug ), + esc_url( $profile['url'] ), + esc_html( $profile['label'] ), + esc_html__( '(abre em uma nova guia)', 'gustavoo-portfolio' ) + ); + } + + echo '
      '; +} + +/** + * Print share links for a blog post using inline brand SVG icons. + * + * @param string $class Optional wrapper class. + * @return void + */ +function gustavoo_portfolio_post_share_links( $class = 'entry-share' ) { + $share_url = rawurlencode( get_permalink() ); + $share_title = rawurlencode( get_the_title() ); + $links = array( + 'WhatsApp' => array( + 'url' => 'https://api.whatsapp.com/send?text=' . $share_title . '%20' . $share_url, + 'icon' => '', + ), + 'Facebook' => array( + 'url' => 'https://www.facebook.com/sharer/sharer.php?u=' . $share_url, + 'icon' => '', + ), + 'X' => array( + 'url' => 'https://twitter.com/intent/tweet?text=' . $share_title . '&url=' . $share_url, + 'icon' => '', + ), + 'LinkedIn' => array( + 'url' => 'https://www.linkedin.com/sharing/share-offsite/?url=' . $share_url, + 'icon' => '', + ), + ); + + printf( '
      ', esc_attr( $class ), esc_attr__( 'Compartilhar postagem', 'gustavoo-portfolio' ) ); + + foreach ( $links as $label => $link ) { + printf( + '%3$s%2$s', + esc_url( $link['url'] ), + esc_attr( $label ), + $link['icon'] // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Static SVG markup. + ); + } + + echo '
      '; +} + +/** + * Convert a configured phone number to a WhatsApp URL. + * + * @param string $phone Raw phone number. + * @param string $message Optional pre-filled conversation message. + * @return string + */ +function gustavoo_portfolio_get_whatsapp_url( $phone, $message = '' ) { + $digits = preg_replace( '/\D+/', '', (string) $phone ); + + if ( ! $digits ) { + return ''; + } + + if ( in_array( strlen( $digits ), array( 10, 11 ), true ) ) { + $digits = '55' . $digits; + } + + $url = 'https://wa.me/' . $digits; + $message = trim( wp_strip_all_tags( (string) $message ) ); + + if ( '' !== $message ) { + $url .= '?text=' . rawurlencode( $message ); + } + + return $url; +} + +/** Print the configured floating WhatsApp contact link. */ +function gustavoo_portfolio_the_whatsapp_float() { + $phone = (string) gustavoo_portfolio_get_setting( 'whatsapp' ); + $message = (string) gustavoo_portfolio_get_setting( 'whatsapp_message' ); + $url = gustavoo_portfolio_get_whatsapp_url( $phone, $message ); + + if ( ! $url ) { + return; + } + ?> +
      > + + + +
      + 'gso_project', + 'post_status' => 'publish', + 'posts_per_page' => $limit * 3, + 'orderby' => array( + 'menu_order' => 'ASC', + 'date' => 'DESC', + ), + 'ignore_sticky_posts' => true, + 'no_found_rows' => true, + 'update_post_term_cache' => true, + ); + + $featured_args = $base_args; + $featured_args['meta_query'] = array( + array( + 'key' => '_gso_project_featured', + 'value' => array( '1', 'yes', 'on', 'true' ), + 'compare' => 'IN', + ), + ); + + $featured = get_posts( $featured_args ); + $others = get_posts( $base_args ); + $projects = array(); + $seen = array(); + + foreach ( array_merge( $featured, $others ) as $project ) { + if ( isset( $seen[ $project->ID ] ) ) { + continue; + } + + $url = gustavoo_portfolio_validate_project_url( get_post_meta( $project->ID, '_gso_project_url', true ) ); + + if ( ! $url ) { + continue; + } + + $seen[ $project->ID ] = true; + $projects[] = $project; + + if ( count( $projects ) >= $limit ) { + break; + } + } + + return $projects; +} + +/** + * Print post publication metadata. + * + * @return void + */ +function gustavoo_portfolio_posted_on() { + $published = sprintf( + '', + esc_attr( get_the_date( DATE_W3C ) ), + esc_html( get_the_date() ) + ); + + printf( + '%1$s %2$s', + esc_html__( 'Publicado em', 'gustavoo-portfolio' ), + $published // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Escaped immediately above. + ); +} + +/** + * Print the post author link. + * + * @return void + */ +function gustavoo_portfolio_posted_by() { + printf( + '', + esc_html__( 'Por', 'gustavoo-portfolio' ), + esc_url( get_author_posts_url( (int) get_the_author_meta( 'ID' ) ) ), + esc_html( get_the_author() ) + ); +} + +/** + * Estimate reading time from the current post content. + * + * @param int|null $post_id Optional post ID. + * @return string + */ +function gustavoo_portfolio_reading_time( $post_id = null ) { + $post_id = $post_id ? absint( $post_id ) : get_the_ID(); + $content = wp_strip_all_tags( strip_shortcodes( (string) get_post_field( 'post_content', $post_id ) ) ); + $words = preg_split( '/\s+/u', trim( $content ), -1, PREG_SPLIT_NO_EMPTY ); + $minutes = max( 1, (int) ceil( count( is_array( $words ) ? $words : array() ) / 220 ) ); + + return sprintf( + /* translators: %d: reading time in minutes. */ + _n( '%d min de leitura', '%d min de leitura', $minutes, 'gustavoo-portfolio' ), + $minutes + ); +} + +/** + * Print category and tag links for a post. + * + * @return void + */ +function gustavoo_portfolio_entry_terms() { + $categories = get_the_category_list( esc_html_x( ', ', 'category list separator', 'gustavoo-portfolio' ) ); + $tags = get_the_tag_list( '', esc_html_x( ', ', 'tag list separator', 'gustavoo-portfolio' ) ); + + if ( $categories ) { + printf( + '
      %1$s %2$s
      ', + esc_html__( 'Categorias:', 'gustavoo-portfolio' ), + wp_kses_post( $categories ) + ); + } + + if ( $tags ) { + printf( + '
      %1$s %2$s
      ', + esc_html__( 'Tags:', 'gustavoo-portfolio' ), + wp_kses_post( $tags ) + ); + } +} + +/** + * Print main-query pagination with accessible labels. + * + * @return void + */ +function gustavoo_portfolio_pagination() { + the_posts_pagination( + array( + 'mid_size' => 1, + 'prev_text' => sprintf( ' %s', esc_html__( 'Anteriores', 'gustavoo-portfolio' ) ), + 'next_text' => sprintf( '%s ', esc_html__( 'Próximos', 'gustavoo-portfolio' ) ), + 'screen_reader_text' => __( 'Navegação entre páginas', 'gustavoo-portfolio' ), + ) + ); +} diff --git a/wp-content/themes/gustavoo-portfolio/inc/widgets.php b/wp-content/themes/gustavoo-portfolio/inc/widgets.php new file mode 100644 index 0000000..533f719 --- /dev/null +++ b/wp-content/themes/gustavoo-portfolio/inc/widgets.php @@ -0,0 +1,204 @@ + '
      ', + 'after_widget' => '
      ', + 'before_title' => '

      ', + 'after_title' => '

      ', + ); + + register_sidebar( + array_merge( + $shared, + array( + 'name' => __( 'Barra lateral do blog', 'gustavoo-portfolio' ), + 'id' => 'sidebar-blog', + 'description' => __( 'Widgets exibidos ao lado de posts, arquivos e buscas.', 'gustavoo-portfolio' ), + ) + ) + ); + + register_sidebar( + array_merge( + $shared, + array( + 'name' => __( 'Rodapé — coluna 1', 'gustavoo-portfolio' ), + 'id' => 'footer-1', + 'description' => __( 'Primeira coluna de widgets do rodapé.', 'gustavoo-portfolio' ), + ) + ) + ); + + register_sidebar( + array_merge( + $shared, + array( + 'name' => __( 'Rodapé — coluna 2', 'gustavoo-portfolio' ), + 'id' => 'footer-2', + 'description' => __( 'Segunda coluna de widgets do rodapé.', 'gustavoo-portfolio' ), + ) + ) + ); +} +add_action( 'widgets_init', 'gustavoo_portfolio_register_sidebars' ); + +/** + * Newsletter widget backed by the same safe Fluent Forms renderer as the CTA. + */ +class Gustavao_Portfolio_Newsletter_Widget extends WP_Widget { + /** + * Register the widget with WordPress. + */ + public function __construct() { + parent::__construct( + 'gustavoo_portfolio_newsletter', + __( 'Gustavo — Newsletter', 'gustavoo-portfolio' ), + array( + 'classname' => 'widget_newsletter', + 'description' => __( 'Exibe a chamada de newsletter integrada ao Fluent Forms e FluentCRM.', 'gustavoo-portfolio' ), + 'customize_selective_refresh' => true, + ) + ); + } + + /** + * Render widget front end. + * + * @param array $args Sidebar wrappers. + * @param array $instance Saved instance. + * @return void + */ + public function widget( $args, $instance ) { + $title = ! empty( $instance['title'] ) ? $instance['title'] : gustavoo_portfolio_get_setting( 'newsletter_heading' ); + $text = ! empty( $instance['text'] ) ? $instance['text'] : gustavoo_portfolio_get_setting( 'newsletter_text' ); + $form_id = ! empty( $instance['form_id'] ) ? absint( $instance['form_id'] ) : absint( gustavoo_portfolio_get_setting( 'newsletter_form_id' ) ); + + echo $args['before_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Registered sidebar wrapper. + + if ( $title ) { + echo $args['before_title']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Registered sidebar wrapper. + echo esc_html( $title ); + echo $args['after_title']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Registered sidebar wrapper. + } + + if ( $text ) { + printf( '

      %s

      ', esc_html( $text ) ); + } + + gustavoo_portfolio_the_fluent_form( $form_id, 'newsletter' ); + + echo $args['after_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Registered sidebar wrapper. + } + + /** + * Render widget controls. + * + * @param array $instance Saved instance. + * @return void + */ + public function form( $instance ) { + $title = isset( $instance['title'] ) ? (string) $instance['title'] : ''; + $text = isset( $instance['text'] ) ? (string) $instance['text'] : ''; + $form_id = isset( $instance['form_id'] ) ? absint( $instance['form_id'] ) : 0; + ?> +

      + + +

      +

      + + +

      +

      + + +

      + $new_instance New values. + * @param array $old_instance Previous values. + * @return array + */ + public function update( $new_instance, $old_instance ) { + unset( $old_instance ); + + return array( + 'title' => isset( $new_instance['title'] ) ? sanitize_text_field( $new_instance['title'] ) : '', + 'text' => isset( $new_instance['text'] ) ? sanitize_textarea_field( $new_instance['text'] ) : '', + 'form_id' => isset( $new_instance['form_id'] ) ? absint( $new_instance['form_id'] ) : 0, + ); + } +} + +/** + * Register the newsletter widget after core has loaded WP_Widget. + * + * @return void + */ +function gustavoo_portfolio_register_widgets() { + register_widget( 'Gustavao_Portfolio_Newsletter_Widget' ); +} +add_action( 'widgets_init', 'gustavoo_portfolio_register_widgets' ); + +/** + * Add one newsletter widget to the blog sidebar on first theme activation. + * + * Existing widget arrangements are preserved and the operation is idempotent. + * + * @return void + */ +function gustavoo_portfolio_seed_newsletter_widget() { + $widget_id_base = 'gustavoo_portfolio_newsletter'; + $instances = get_option( 'widget_' . $widget_id_base, array() ); + $instances = is_array( $instances ) ? $instances : array(); + $sidebars = wp_get_sidebars_widgets(); + $sidebars = is_array( $sidebars ) ? $sidebars : array(); + + foreach ( $sidebars as $widgets ) { + foreach ( (array) $widgets as $widget_id ) { + if ( str_starts_with( (string) $widget_id, $widget_id_base . '-' ) ) { + return; + } + } + } + + $indexes = array_filter( array_keys( $instances ), 'is_int' ); + $index = $indexes ? max( $indexes ) + 1 : 1; + + $instances[ $index ] = array( + 'title' => __( 'Newsletter', 'gustavoo-portfolio' ), + 'text' => __( 'Receba novos artigos sobre desenvolvimento, infraestrutura e automação.', 'gustavoo-portfolio' ), + 'form_id' => 0, + ); + $instances['_multiwidget'] = 1; + + $sidebars['sidebar-blog'] = array_values( + array_merge( + array( $widget_id_base . '-' . $index ), + isset( $sidebars['sidebar-blog'] ) ? (array) $sidebars['sidebar-blog'] : array() + ) + ); + + update_option( 'widget_' . $widget_id_base, $instances, false ); + update_option( 'sidebars_widgets', $sidebars, false ); +} +add_action( 'after_switch_theme', 'gustavoo_portfolio_seed_newsletter_widget' ); diff --git a/wp-content/themes/gustavoo-portfolio/index.php b/wp-content/themes/gustavoo-portfolio/index.php new file mode 100644 index 0000000..d636941 --- /dev/null +++ b/wp-content/themes/gustavoo-portfolio/index.php @@ -0,0 +1,41 @@ + + +
      +
      +
      +

      +

      +
      +
      + +
      +
      + +
      + +
      + + + + +
      + + +
      +
      + + + +
      +
      + +
      +
      + + + +
      + + +
      +
      + +

      + found_posts, 'gustavoo-portfolio' ) ), + esc_html( number_format_i18n( (int) $wp_query->found_posts ) ) + ); + ?> +

      +
      + +
      + + + + +
      + + +
      +
      + + + diff --git a/wp-content/themes/gustavoo-portfolio/sidebar.php b/wp-content/themes/gustavoo-portfolio/sidebar.php new file mode 100644 index 0000000..2d7f3b6 --- /dev/null +++ b/wp-content/themes/gustavoo-portfolio/sidebar.php @@ -0,0 +1,35 @@ + + diff --git a/wp-content/themes/gustavoo-portfolio/single.php b/wp-content/themes/gustavoo-portfolio/single.php new file mode 100644 index 0000000..95b6e47 --- /dev/null +++ b/wp-content/themes/gustavoo-portfolio/single.php @@ -0,0 +1,138 @@ + +
      +
      +
      + + + + + + ', '' ); ?> +
      +
      + + + + +
      + 'entry-featured-image__image' ) ); ?> +
      + + +
      +
      > +
      + + '', + ) + ); + ?> +
      + +
      + +
      + + + +
      +

      + +
      + + + wp_list_pluck( $gustavoo_categories, 'term_id' ), + 'post__not_in' => array( get_the_ID() ), + 'posts_per_page' => 3, + 'ignore_sticky_posts' => true, + 'orderby' => 'date', + 'order' => 'DESC', + ) + ); + ?> + have_posts() ) : ?> + + + + + + + + + + + + +
      + + +
      +
      + + +
      +

      + +

      + + +

      + +
      diff --git a/wp-content/themes/gustavoo-portfolio/template-parts/content/content-page.php b/wp-content/themes/gustavoo-portfolio/template-parts/content/content-page.php new file mode 100644 index 0000000..35e92d6 --- /dev/null +++ b/wp-content/themes/gustavoo-portfolio/template-parts/content/content-page.php @@ -0,0 +1,32 @@ + + +
      > +
      +

      + ', '' ); ?> +
      + + +
      + 'page-article__image' ) ); ?> +
      + + +
      + + '', + ) + ); + ?> +
      +
      diff --git a/wp-content/themes/gustavoo-portfolio/template-parts/front/about.php b/wp-content/themes/gustavoo-portfolio/template-parts/front/about.php new file mode 100644 index 0000000..89692d5 --- /dev/null +++ b/wp-content/themes/gustavoo-portfolio/template-parts/front/about.php @@ -0,0 +1,41 @@ + + +
      +
      +
      +

      +

      +
      + +
      +
      + + +
      + +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      diff --git a/wp-content/themes/gustavoo-portfolio/template-parts/front/contact.php b/wp-content/themes/gustavoo-portfolio/template-parts/front/contact.php new file mode 100644 index 0000000..4a6097a --- /dev/null +++ b/wp-content/themes/gustavoo-portfolio/template-parts/front/contact.php @@ -0,0 +1,46 @@ + + +
      +
      +
      +

      +

      +

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

      + Política de Privacidade.', 'gustavoo-portfolio' ) ), + esc_url( $gustavoo_privacy_url ) + ); + ?> +

      + +
      +
      +
      diff --git a/wp-content/themes/gustavoo-portfolio/template-parts/front/hero.php b/wp-content/themes/gustavoo-portfolio/template-parts/front/hero.php new file mode 100644 index 0000000..23720b8 --- /dev/null +++ b/wp-content/themes/gustavoo-portfolio/template-parts/front/hero.php @@ -0,0 +1,54 @@ + + +
      +
      +
      + +

      + + +

      +

      + +
      + + + + + + + + + + + +
      + +
      + + +
      +
      diff --git a/wp-content/themes/gustavoo-portfolio/template-parts/front/posts.php b/wp-content/themes/gustavoo-portfolio/template-parts/front/posts.php new file mode 100644 index 0000000..56bcd49 --- /dev/null +++ b/wp-content/themes/gustavoo-portfolio/template-parts/front/posts.php @@ -0,0 +1,47 @@ + 'post', + 'post_status' => 'publish', + 'posts_per_page' => $gustavoo_blog_limit, + 'ignore_sticky_posts' => true, + 'no_found_rows' => true, + ) +); +?> + +
      +
      +
      +
      +

      +

      +
      +

      +
      + + have_posts() ) : ?> + + +

      + +

      + + + +
      +
      diff --git a/wp-content/themes/gustavoo-portfolio/template-parts/front/projects.php b/wp-content/themes/gustavoo-portfolio/template-parts/front/projects.php new file mode 100644 index 0000000..a3cbde1 --- /dev/null +++ b/wp-content/themes/gustavoo-portfolio/template-parts/front/projects.php @@ -0,0 +1,49 @@ + + +
      +
      +
      +
      +

      +

      +
      +

      +
      + + +
      + +
      + +
      +

      + + + +
      + + + +

      + +
      +
      diff --git a/wp-content/themes/gustavoo-portfolio/template-parts/front/services.php b/wp-content/themes/gustavoo-portfolio/template-parts/front/services.php new file mode 100644 index 0000000..aa2239c --- /dev/null +++ b/wp-content/themes/gustavoo-portfolio/template-parts/front/services.php @@ -0,0 +1,31 @@ + + +
      +
      +
      +
      +

      +

      +
      +

      +
      + +
      + +
      + +

      +

      +
      + +
      +
      +
      diff --git a/wp-content/themes/gustavoo-portfolio/template-parts/global/latest-news-card.php b/wp-content/themes/gustavoo-portfolio/template-parts/global/latest-news-card.php new file mode 100644 index 0000000..cb4c40e --- /dev/null +++ b/wp-content/themes/gustavoo-portfolio/template-parts/global/latest-news-card.php @@ -0,0 +1,22 @@ + + diff --git a/wp-content/themes/gustavoo-portfolio/template-parts/global/latest-news-list.php b/wp-content/themes/gustavoo-portfolio/template-parts/global/latest-news-list.php new file mode 100644 index 0000000..23cf870 --- /dev/null +++ b/wp-content/themes/gustavoo-portfolio/template-parts/global/latest-news-list.php @@ -0,0 +1,47 @@ + 'post', + 'post_status' => 'publish', + 'posts_per_page' => 4, + 'post__not_in' => array_map( 'absint', $gustavoo_args['exclude'] ?? array() ), + 'ignore_sticky_posts' => true, + 'orderby' => 'date', + 'order' => 'DESC', +); + +if ( ! empty( $gustavoo_args['category_id'] ) ) { + $gustavoo_query_args['cat'] = absint( $gustavoo_args['category_id'] ); +} + +$gustavoo_latest_posts = new WP_Query( $gustavoo_query_args ); + +if ( ! $gustavoo_latest_posts->have_posts() ) { + return; +} +?> +
      +

      +
      + have_posts() ) : $gustavoo_latest_posts->the_post(); ?> + + + +
      +
      + diff --git a/wp-content/themes/gustavoo-portfolio/template-parts/global/newsletter-cta.php b/wp-content/themes/gustavoo-portfolio/template-parts/global/newsletter-cta.php new file mode 100644 index 0000000..7b10d51 --- /dev/null +++ b/wp-content/themes/gustavoo-portfolio/template-parts/global/newsletter-cta.php @@ -0,0 +1,35 @@ + + + diff --git a/wp-content/themes/gustavoo-portfolio/template-parts/global/post-card.php b/wp-content/themes/gustavoo-portfolio/template-parts/global/post-card.php new file mode 100644 index 0000000..006c9e9 --- /dev/null +++ b/wp-content/themes/gustavoo-portfolio/template-parts/global/post-card.php @@ -0,0 +1,45 @@ + + + diff --git a/wp-content/themes/gustavoo-portfolio/template-parts/global/project-card.php b/wp-content/themes/gustavoo-portfolio/template-parts/global/project-card.php new file mode 100644 index 0000000..5d46659 --- /dev/null +++ b/wp-content/themes/gustavoo-portfolio/template-parts/global/project-card.php @@ -0,0 +1,84 @@ + + + diff --git a/wp-content/themes/gustavoo-portfolio/template-parts/global/whatsapp-float.php b/wp-content/themes/gustavoo-portfolio/template-parts/global/whatsapp-float.php new file mode 100644 index 0000000..c0994a1 --- /dev/null +++ b/wp-content/themes/gustavoo-portfolio/template-parts/global/whatsapp-float.php @@ -0,0 +1,8 @@ +